feat: Cluster creation modal improvements
This commit is contained in:
parent
a95513b6b9
commit
8664610560
17 changed files with 354 additions and 67 deletions
|
|
@ -29,6 +29,7 @@
|
|||
"@tauri-apps/plugin-clipboard-manager": "2.1.0-beta.5",
|
||||
"@tauri-apps/plugin-shell": "github:tauri-apps/tauri-plugin-shell#v2",
|
||||
"@thisbeyond/solid-dnd": "^0.7.5",
|
||||
"@thisbeyond/solid-select": "^0.15.0",
|
||||
"@unocss/reset": "^0.61.4",
|
||||
"@untitled-theme/icons-solid": "^0.10.4",
|
||||
"overlayscrollbars": "^2.9.2",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ fn main() {
|
|||
"get_settings",
|
||||
"set_settings",
|
||||
"get_program_info",
|
||||
"get_minecraft_versions"
|
||||
]),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,5 +13,6 @@ permissions = [
|
|||
"allow-get-manifest",
|
||||
"allow-get-settings",
|
||||
"allow-set-settings",
|
||||
"allow-get-program-info"
|
||||
"allow-get-program-info",
|
||||
"allow-get-minecraft-versions"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ use std::collections::HashMap;
|
|||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use chrono::DateTime;
|
||||
use interpulse::api::minecraft::Version;
|
||||
use onelauncher::constants::{NATIVE_ARCH, TARGET_OS, VERSION};
|
||||
use onelauncher::data::{ClusterMeta, Loader, MinecraftCredentials, PackageData, Settings};
|
||||
use onelauncher::store::{Cluster, ClusterPath, MinecraftLogin};
|
||||
|
|
@ -31,7 +33,8 @@ macro_rules! collect_commands {
|
|||
get_clusters,
|
||||
get_settings,
|
||||
set_settings,
|
||||
get_program_info
|
||||
get_program_info,
|
||||
get_minecraft_versions
|
||||
])
|
||||
}};
|
||||
}
|
||||
|
|
@ -132,16 +135,22 @@ pub async fn get_clusters() -> Result<HashMap<ClusterPath, Cluster>, String> {
|
|||
// cluster::list(None).await.map_err(|op| op.into())
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn get_minecraft_versions() -> Result<Vec<Version>, String> {
|
||||
Ok(onelauncher::api::metadata::get_minecraft_versions().await?.versions)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn get_settings() -> Result<Settings, String> {
|
||||
settings::get().await.map_err(|err| err.into())
|
||||
Ok(settings::get().await?)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn set_settings(settings: Settings) -> Result<(), String> {
|
||||
settings::set(settings).await.map_err(|err| err.into())
|
||||
Ok(settings::set(settings).await?)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Type)]
|
||||
|
|
@ -174,19 +183,19 @@ pub fn get_program_info() -> ProgramInfo {
|
|||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn get_users() -> Result<Vec<MinecraftCredentials>, String> {
|
||||
minecraft::users().await.map_err(|err| err.into())
|
||||
Ok(minecraft::users().await?)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn get_user(uuid: Uuid) -> Result<MinecraftCredentials, String> {
|
||||
minecraft::get_user(uuid).await.map_err(|err| err.into())
|
||||
Ok(minecraft::get_user(uuid).await?)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn begin_msa() -> Result<MinecraftLogin, String> {
|
||||
minecraft::begin().await.map_err(|err| err.into())
|
||||
Ok(minecraft::begin().await?)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
|
|
@ -195,13 +204,11 @@ pub async fn finish_msa(
|
|||
code: String,
|
||||
login: MinecraftLogin,
|
||||
) -> Result<MinecraftCredentials, String> {
|
||||
minecraft::finish(code.as_str(), login)
|
||||
.await
|
||||
.map_err(|err| err.into())
|
||||
Ok(minecraft::finish(code.as_str(), login).await?)
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn remove_user(uuid: Uuid) -> Result<(), String> {
|
||||
minecraft::remove_user(uuid).await.map_err(|err| err.into())
|
||||
Ok(minecraft::remove_user(uuid).await?)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
@apply py-1.5 px-3 rounded-lg flex flex-row justify-center items-center gap-x-2.5;
|
||||
|
||||
&__primary {
|
||||
@apply text-onbrand hover:text-onbrand-hover active:text-onbrand-pressed disabled:text-onbrand-disabled;
|
||||
@apply text-onbrand hover:text-onbrand-hover active:text-onbrand-pressed disabled:text-onbrand-disabled;
|
||||
@apply bg-brand hover:bg-brand-hover active:bg-brand-pressed disabled:bg-brand-disabled;
|
||||
}
|
||||
|
||||
|
|
@ -17,11 +17,11 @@
|
|||
}
|
||||
|
||||
&__ghost {
|
||||
@apply hover:bg-gray-05 active:bg-gray-10;
|
||||
@apply hover:bg-gray-05 [&[aria-checked="true"]]:bg-gray-10 active:bg-gray-10;
|
||||
}
|
||||
|
||||
&__icon, &__iconSecondary {
|
||||
@apply p-1.5 hover:bg-gray-05 active:bg-gray-10 aspect-square box-border w-8 h-8;
|
||||
@apply p-1.5 hover:bg-gray-05 [&[aria-checked="true"]]:bg-gray-10 active:bg-gray-10 aspect-square box-border w-8 h-8;
|
||||
|
||||
&.large {
|
||||
@apply w-9 h-9;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { JSX, ParentProps } from 'solid-js';
|
||||
import { mergeProps, splitProps } from 'solid-js';
|
||||
import { createSignal, mergeProps, splitProps } from 'solid-js';
|
||||
import styles from './Button.module.scss';
|
||||
|
||||
type ButtonProps = JSX.ButtonHTMLAttributes<HTMLButtonElement> & ParentProps & {
|
||||
|
|
@ -22,4 +22,32 @@ function Button(props: ButtonProps) {
|
|||
);
|
||||
}
|
||||
|
||||
type ButtonToggleProps = ButtonProps & {
|
||||
defaultChecked?: boolean;
|
||||
onChecked?: (checked: boolean) => any;
|
||||
};
|
||||
|
||||
Button.Toggle = (props: ButtonToggleProps) => {
|
||||
const [checked, setChecked] = createSignal(props.defaultChecked || false);
|
||||
const [split, rest] = splitProps(props, ['defaultChecked', 'onChecked', 'onClick', 'class']);
|
||||
|
||||
function toggle() {
|
||||
const newValue = !checked();
|
||||
setChecked(newValue);
|
||||
props.onChecked?.(newValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
toggle();
|
||||
// @ts-expect-error -- type error which i cba to resolve
|
||||
split.onClick?.(e);
|
||||
}}
|
||||
aria-checked={checked()}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
||||
|
|
|
|||
20
apps/desktop/src/ui/components/base/Checkbox.module.scss
Normal file
20
apps/desktop/src/ui/components/base/Checkbox.module.scss
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
.checkbox {
|
||||
@apply flex flex-row gap-x-1 items-center;
|
||||
|
||||
.box {
|
||||
@apply w-[18px] h-[18px] flex flex-col justify-center items-center rounded-md bg-component-bg hover:bg-component-bg-hover active:bg-component-bg-pressed border border-gray-05 transition-all;
|
||||
|
||||
> svg {
|
||||
@apply w-[18px] h-[18px] mt-px opacity-0 transition-opacity;
|
||||
}
|
||||
|
||||
&.checked {
|
||||
@apply bg-brand hover:bg-brand-hover active:bg-brand-pressed border-transparent;
|
||||
|
||||
> svg {
|
||||
@apply opacity-100;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
34
apps/desktop/src/ui/components/base/Checkbox.tsx
Normal file
34
apps/desktop/src/ui/components/base/Checkbox.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { type JSX, createSignal, splitProps } from 'solid-js';
|
||||
import { CheckIcon } from '@untitled-theme/icons-solid';
|
||||
import styles from './Checkbox.module.scss';
|
||||
|
||||
type CheckboxProps = JSX.HTMLAttributes<HTMLDivElement> & {
|
||||
defaultChecked?: boolean;
|
||||
onChecked?: (checked: boolean) => void;
|
||||
};
|
||||
|
||||
function Checkbox(props: CheckboxProps) {
|
||||
const [checked, setChecked] = createSignal(props.defaultChecked || false);
|
||||
const [split, rest] = splitProps(props, ['class', 'defaultChecked', 'onChecked', 'text']);
|
||||
|
||||
function toggle() {
|
||||
const newValue = !checked();
|
||||
setChecked(newValue);
|
||||
props.onChecked?.(newValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => toggle()}
|
||||
class={`${styles.checkbox} ${` ${split.class}` ?? ''}`}
|
||||
{...rest}
|
||||
>
|
||||
<div class={`${styles.box} ${checked() ? styles.checked : ''}`}>
|
||||
<CheckIcon />
|
||||
</div>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Checkbox;
|
||||
|
|
@ -1,15 +1,22 @@
|
|||
.dropdown {
|
||||
@apply relative h-full;
|
||||
|
||||
.list {
|
||||
@apply bg-secondary rounded-lg border border-gray-05 p-1 shadow-md shadow-black/30;
|
||||
@apply flex-col gap-2;
|
||||
@apply max-h-46 overflow-auto;
|
||||
@apply hidden;
|
||||
.dropdown_elements_container {
|
||||
@apply flex flex-col gap-1;
|
||||
}
|
||||
|
||||
&.visible {
|
||||
@apply flex;
|
||||
}
|
||||
.dropdown_element {
|
||||
@apply bg-secondary rounded-lg border border-gray-05 p-1 shadow-md shadow-black/30 h-full;
|
||||
}
|
||||
|
||||
.list {
|
||||
@apply max-h-46 overflow-auto;
|
||||
@apply flex-col gap-1;
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
.visible .list {
|
||||
@apply flex;
|
||||
}
|
||||
|
||||
.row {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { type Accessor, Index, type JSX, Match, type ParentProps, type ResolvedJSXElement, type Setter, Switch, children, createSignal, splitProps } from 'solid-js';
|
||||
import { type Accessor, Index, type JSX, Match, type ParentProps, type ResolvedJSXElement, type Setter, Show, Switch, children, createSignal, splitProps } from 'solid-js';
|
||||
import { ChevronDownIcon, ChevronUpIcon } from '@untitled-theme/icons-solid';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid';
|
||||
import Popup from '../overlay/Popup';
|
||||
|
|
@ -10,6 +10,7 @@ export type DropdownProps = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'onChange'>
|
|||
text?: string;
|
||||
selected?: number;
|
||||
dropdownClass?: string;
|
||||
listToolRow?: () => JSX.Element;
|
||||
component?: (
|
||||
props: {
|
||||
visible: Accessor<boolean>;
|
||||
|
|
@ -20,7 +21,7 @@ export type DropdownProps = Omit<JSX.HTMLAttributes<HTMLDivElement>, 'onChange'>
|
|||
};
|
||||
|
||||
function Dropdown(props: DropdownProps) {
|
||||
const [split, rest] = splitProps(props, ['children', 'class', 'text', 'onChange', 'selected', 'component', 'dropdownClass']);
|
||||
const [split, rest] = splitProps(props, ['children', 'class', 'text', 'onChange', 'selected', 'component', 'dropdownClass', 'listToolRow']);
|
||||
const [visible, setVisible] = createSignal(false);
|
||||
|
||||
// eslint-disable-next-line solid/reactivity -- todo
|
||||
|
|
@ -72,18 +73,26 @@ function Dropdown(props: DropdownProps) {
|
|||
ref.classList.add(...split.dropdownClass.split(' '));
|
||||
}}
|
||||
>
|
||||
<OverlayScrollbarsComponent class={`${styles.list} ${visible() ? styles.visible : ''}`}>
|
||||
<Index each={items()}>
|
||||
{(item, index) => (
|
||||
<div onClick={() => select(index)}>
|
||||
<div class="hover:bg-gray-05 p-2 rounded-lg flex flex-row gap-2 justify-between items-center">
|
||||
{item()}
|
||||
<div class={styles.selected! + (selected() === index ? ` ${styles.visible}` : '')} />
|
||||
<div class={`${styles.dropdown_elements_container} ${visible() ? styles.visible : ''}`}>
|
||||
<OverlayScrollbarsComponent class={`${styles.list} ${styles.dropdown_element}`}>
|
||||
{/* TODO(perf): Optimise */}
|
||||
<Index each={items()}>
|
||||
{(item, index) => (
|
||||
<div onClick={() => select(index)}>
|
||||
<div class="hover:bg-gray-05 p-2 rounded-lg flex flex-row gap-2 justify-between items-center">
|
||||
{item()}
|
||||
<div class={styles.selected! + (selected() === index ? ` ${styles.visible}` : '')} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Index>
|
||||
</OverlayScrollbarsComponent>
|
||||
)}
|
||||
</Index>
|
||||
</OverlayScrollbarsComponent>
|
||||
<Show when={split.listToolRow !== undefined}>
|
||||
<div class={styles.dropdown_element}>
|
||||
{split.listToolRow!()}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Popup>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
12
apps/desktop/src/ui/components/base/SelectList.module.scss
Normal file
12
apps/desktop/src/ui/components/base/SelectList.module.scss
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
.select_list {
|
||||
@apply bg-component-bg rounded-lg overflow-hidden border border-gray-05;
|
||||
|
||||
.row {
|
||||
@apply min-h-4 py-1 px-2 text-left;
|
||||
@apply [&:nth-child(even)]:bg-white/01 hover:bg-component-bg-hover active:bg-component-bg-pressed;
|
||||
|
||||
&.selected {
|
||||
@apply bg-white/10;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
apps/desktop/src/ui/components/base/SelectList.tsx
Normal file
66
apps/desktop/src/ui/components/base/SelectList.tsx
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid';
|
||||
import { type Context, type JSX, type ParentProps, type Signal, createContext, createSignal, splitProps, useContext } from 'solid-js';
|
||||
import styles from './SelectList.module.scss';
|
||||
|
||||
type SelectListContextHelpers = Signal<number | undefined>;
|
||||
const SelectListContext = createContext<SelectListContextHelpers>() as Context<SelectListContextHelpers>;
|
||||
|
||||
function SelectListContextProvider(props: ParentProps) {
|
||||
const [selected, setSelected] = createSignal<number>();
|
||||
|
||||
return (
|
||||
<SelectListContext.Provider value={[selected, setSelected]}>
|
||||
{props.children}
|
||||
</SelectListContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function useSelectListContext() {
|
||||
return useContext(SelectListContext);
|
||||
}
|
||||
|
||||
export type SelectListProps = JSX.HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
function SelectList(props: SelectListProps) {
|
||||
const [split, rest] = splitProps(props, ['class']);
|
||||
return (
|
||||
<SelectListContextProvider>
|
||||
<div {...rest} class={`${styles.select_list} ${split.class || ''}`}>
|
||||
<OverlayScrollbarsComponent class="max-h-full">
|
||||
<div>
|
||||
{props.children}
|
||||
</div>
|
||||
</OverlayScrollbarsComponent>
|
||||
</div>
|
||||
</SelectListContextProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export type SelectListRowProps = JSX.HTMLAttributes<HTMLDivElement> & {
|
||||
index?: number;
|
||||
};
|
||||
|
||||
SelectList.Row = (props: SelectListRowProps) => {
|
||||
const [selected, setSelected] = useSelectListContext();
|
||||
const [split, rest] = splitProps(props, ['index', 'class', 'onClick']);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...rest}
|
||||
tabIndex={0}
|
||||
onClick={(e) => {
|
||||
if (props.index !== undefined)
|
||||
setSelected(props.index);
|
||||
|
||||
if (split.onClick)
|
||||
// @ts-expect-error -- la
|
||||
split.onClick(e);
|
||||
}}
|
||||
class={`${styles.row} ${props.index !== undefined && selected() === props.index ? styles.selected : ''} ${split.class || ''}`}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectList;
|
||||
|
|
@ -3,7 +3,6 @@ import { type JSX, createSignal, splitProps } from 'solid-js';
|
|||
type ToggleProps = JSX.HTMLAttributes<HTMLDivElement> & {
|
||||
defaultChecked?: boolean;
|
||||
onChecked?: (checked: boolean) => void;
|
||||
showText?: 'left' | 'right';
|
||||
};
|
||||
|
||||
function Toggle(props: ToggleProps) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { TextInputIcon } from '@untitled-theme/icons-solid';
|
||||
import { For, type JSX, createEffect, createSignal, on, splitProps } from 'solid-js';
|
||||
import { Camera01Icon, FilterFunnel01Icon, TextInputIcon } from '@untitled-theme/icons-solid';
|
||||
import { For, Index, type JSX, Show, createEffect, createMemo, createSignal, on, onMount, splitProps } from 'solid-js';
|
||||
import { Select } from '@thisbeyond/solid-select';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid';
|
||||
import type { ClusterStepProps } from './ClusterCreationModal';
|
||||
import Dropdown from '~ui/components/base/Dropdown';
|
||||
import TextField from '~ui/components/base/TextField';
|
||||
|
|
@ -7,6 +9,15 @@ import VanillaImage from '~assets/logos/vanilla.png';
|
|||
import FabricImage from '~assets/logos/fabric.png';
|
||||
import ForgeImage from '~assets/logos/forge.png';
|
||||
import QuiltImage from '~assets/logos/quilt.png';
|
||||
import useCommand from '~ui/hooks/useCommand';
|
||||
import { bridge } from '~imports';
|
||||
import Button from '~ui/components/base/Button';
|
||||
import Tooltip from '~ui/components/base/Tooltip';
|
||||
import SelectList from '~ui/components/base/SelectList';
|
||||
import Checkbox from '~ui/components/base/Checkbox';
|
||||
import { getEnumMembers } from '~utils/primitives';
|
||||
import type { VersionType } from '~bindings';
|
||||
import { formatVersionRelease } from '~utils/helpers';
|
||||
|
||||
const loaders: {
|
||||
name: string;
|
||||
|
|
@ -55,32 +66,92 @@ export function ClusterStepTwo(props: ClusterStepProps) {
|
|||
/>
|
||||
</Option>
|
||||
|
||||
<div class="flex flex-row gap-x-2">
|
||||
<Option class="flex-1" header="Versions">
|
||||
<Dropdown>
|
||||
<For each={Array.from({ length: 300 }, (_, i) => i).reverse()}>
|
||||
{version => (
|
||||
<Dropdown.Row>{version}</Dropdown.Row>
|
||||
)}
|
||||
</For>
|
||||
</Dropdown>
|
||||
</Option>
|
||||
<Option class="w-32" header="Loader">
|
||||
<Dropdown>
|
||||
<For each={loaders}>
|
||||
{loader => (
|
||||
<Dropdown.Row>
|
||||
<div class="flex flex-row gap-x-2">
|
||||
<div class="w-4 h-4">
|
||||
<loader.icon />
|
||||
</div>
|
||||
{loader.name}
|
||||
<Option header="Versions">
|
||||
<VersionSelector />
|
||||
</Option>
|
||||
|
||||
<Option header="Loader">
|
||||
<Dropdown>
|
||||
<For each={loaders}>
|
||||
{loader => (
|
||||
<Dropdown.Row>
|
||||
<div class="flex flex-row gap-x-2">
|
||||
<div class="w-4 h-4">
|
||||
<loader.icon />
|
||||
</div>
|
||||
</Dropdown.Row>
|
||||
)}
|
||||
</For>
|
||||
</Dropdown>
|
||||
</Option>
|
||||
{loader.name}
|
||||
</div>
|
||||
</Dropdown.Row>
|
||||
)}
|
||||
</For>
|
||||
</Dropdown>
|
||||
</Option>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type VersionReleaseFilters = {
|
||||
[key in VersionType]: boolean;
|
||||
};
|
||||
|
||||
function VersionSelector() {
|
||||
const [versions] = useCommand(bridge.commands.getMinecraftVersions);
|
||||
const [filteredVersions, setFilteredVersions] = createSignal<bridge.Version[]>([]);
|
||||
const [filters, setFilters] = createSignal<VersionReleaseFilters>({
|
||||
old_alpha: false,
|
||||
old_beta: false,
|
||||
release: false,
|
||||
snapshot: false,
|
||||
});
|
||||
|
||||
function refresh(filters: VersionReleaseFilters, versions: bridge.Version[]) {
|
||||
setFilteredVersions(() => {
|
||||
if (Object.values(filters).every(value => value === false))
|
||||
return versions;
|
||||
|
||||
return versions.filter((version) => {
|
||||
return filters[version.type];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toggleFilter(name: string) {
|
||||
setFilters((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
[name]: !prev[name as keyof VersionReleaseFilters],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
const list = versions();
|
||||
if (list !== undefined)
|
||||
refresh(filters(), list);
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
setFilteredVersions(versions()!);
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="flex flex-row flex-1 gap-2">
|
||||
<SelectList class="max-h-40 min-w-3/5">
|
||||
<Show when={filteredVersions() !== undefined}>
|
||||
<Index each={filteredVersions()}>
|
||||
{(version, index) => (
|
||||
<SelectList.Row index={index}>{version().id}</SelectList.Row>
|
||||
)}
|
||||
</Index>
|
||||
</Show>
|
||||
</SelectList>
|
||||
|
||||
<div class="bg-component-bg rounded-lg overflow-hidden border border-gray-05 flex-1 p-2 flex flex-col gap-y-2">
|
||||
<For each={Object.keys(filters())}>
|
||||
{name => (
|
||||
<Checkbox onChecked={() => toggleFilter(name)}>{formatVersionRelease(name as VersionType)}</Checkbox>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -96,9 +167,9 @@ function Option(props: OptionProps) {
|
|||
return (
|
||||
<div {...rest} class={`flex flex-col gap-y-2 items-stretch ${split.class || ''}`}>
|
||||
<h3 class="text-md font-semibold uppercase text-fg-secondary text-left">{props.header}</h3>
|
||||
<div class="max-h-8">
|
||||
{props.children}
|
||||
</div>
|
||||
{/* <div class="max-h-8"> */}
|
||||
{props.children}
|
||||
{/* </div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
12
apps/desktop/src/utils/helpers.ts
Normal file
12
apps/desktop/src/utils/helpers.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import type { VersionType } from '~bindings';
|
||||
|
||||
export function formatVersionRelease(release: VersionType): string {
|
||||
const mapping: { [key in VersionType]: string } = {
|
||||
old_alpha: 'Alpha',
|
||||
old_beta: 'Beta',
|
||||
release: 'Release',
|
||||
snapshot: 'Snapshot',
|
||||
};
|
||||
|
||||
return mapping[release];
|
||||
}
|
||||
|
|
@ -17,6 +17,12 @@ export function abbreviateNumber(n: number) {
|
|||
return `${n}`;
|
||||
};
|
||||
|
||||
export function getEnumMembers(obj: any): string[] {
|
||||
return Object.keys(obj).filter((item) => {
|
||||
return Number.isNaN(Number(item));
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
upperFirst,
|
||||
abbreviateNumber,
|
||||
|
|
|
|||
13
pnpm-lock.yaml
generated
13
pnpm-lock.yaml
generated
|
|
@ -86,6 +86,9 @@ importers:
|
|||
'@thisbeyond/solid-dnd':
|
||||
specifier: ^0.7.5
|
||||
version: 0.7.5(solid-js@1.8.18)
|
||||
'@thisbeyond/solid-select':
|
||||
specifier: ^0.15.0
|
||||
version: 0.15.0(solid-js@1.8.18)
|
||||
'@unocss/reset':
|
||||
specifier: ^0.61.4
|
||||
version: 0.61.4
|
||||
|
|
@ -1026,6 +1029,12 @@ packages:
|
|||
peerDependencies:
|
||||
solid-js: ^1.5
|
||||
|
||||
'@thisbeyond/solid-select@0.15.0':
|
||||
resolution: {integrity: sha512-L3QnA5vm09JWNqLR4QqEIi7cgfs9/1sJbFjyEnPskjKpxEvb4G+Iy5cyd1qhZ2vAIZ9vaLd3udts3kefayPsMg==}
|
||||
engines: {node: '>=18.0.0', pnpm: '>=9.0.0'}
|
||||
peerDependencies:
|
||||
solid-js: ^1.8
|
||||
|
||||
'@trysound/sax@0.2.0':
|
||||
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
|
@ -4104,6 +4113,10 @@ snapshots:
|
|||
dependencies:
|
||||
solid-js: 1.8.18
|
||||
|
||||
'@thisbeyond/solid-select@0.15.0(solid-js@1.8.18)':
|
||||
dependencies:
|
||||
solid-js: 1.8.18
|
||||
|
||||
'@trysound/sax@0.2.0': {}
|
||||
|
||||
'@tsconfig/node10@1.0.11': {}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue