refactor select to use dropdown
This commit is contained in:
parent
6d69566e1a
commit
4a62406de9
2 changed files with 55 additions and 97 deletions
|
@ -1,56 +1,46 @@
|
||||||
import { createMemo, createSignal, For, JSX, Setter, createEffect, Show } from "solid-js";
|
import { createMemo, createSignal, For, JSX, Setter, createEffect, Show, ParentProps, children } from "solid-js";
|
||||||
import { FaSolidAngleDown } from "solid-icons/fa";
|
import { FaSolidAngleDown } from "solid-icons/fa";
|
||||||
import css from './index.module.css';
|
import css from './index.module.css';
|
||||||
|
|
||||||
interface DropdownProps<T, K extends string> {
|
export interface DropdownApi {
|
||||||
|
show(): void;
|
||||||
|
hide(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DropdownProps {
|
||||||
|
api?: (api: DropdownApi) => any,
|
||||||
id: string;
|
id: string;
|
||||||
class?: string;
|
class?: string;
|
||||||
value?: K;
|
|
||||||
setValue?: Setter<K | undefined>;
|
|
||||||
values: Record<K, T>;
|
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
showCaret?: boolean;
|
showCaret?: boolean;
|
||||||
children: (key: K, value: T) => JSX.Element;
|
text: JSX.Element;
|
||||||
filter?: (query: string, key: K, value: T) => boolean;
|
children: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Dropdown<T, K extends string>(props: DropdownProps<T, K>) {
|
export function Dropdown(props: DropdownProps) {
|
||||||
const [dialog, setDialog] = createSignal<HTMLDialogElement>();
|
const [dialog, setDialog] = createSignal<HTMLDialogElement>();
|
||||||
const [key, setKey] = createSignal<K | undefined>(props.value);
|
|
||||||
const [open, setOpen] = createSignal<boolean>(props.open ?? false);
|
const [open, setOpen] = createSignal<boolean>(props.open ?? false);
|
||||||
const [query, setQuery] = createSignal<string>('');
|
|
||||||
|
|
||||||
const values = createMemo(() => {
|
|
||||||
let entries = Object.entries<T>(props.values) as [K, T][];
|
|
||||||
const filter = props.filter;
|
|
||||||
const q = query();
|
|
||||||
|
|
||||||
if (filter) {
|
|
||||||
entries = entries.filter(([k, v]) => filter(q, k, v));
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries;
|
|
||||||
});
|
|
||||||
|
|
||||||
const showCaret = createMemo(() => props.showCaret ?? true);
|
const showCaret = createMemo(() => props.showCaret ?? true);
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
props.setValue?.(() => key());
|
|
||||||
});
|
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
dialog()?.[open() ? 'showPopover' : 'hidePopover']();
|
dialog()?.[open() ? 'showPopover' : 'hidePopover']();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
props.api?.({
|
||||||
|
show() {
|
||||||
|
dialog()?.showPopover();
|
||||||
|
},
|
||||||
|
hide() {
|
||||||
|
dialog()?.hidePopover();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return <section class={`${css.box} ${props.class}`}>
|
return <section class={`${css.box} ${props.class}`}>
|
||||||
<button id={`${props.id}_button`} popoverTarget={`${props.id}_dialog`} class={css.button}>
|
<button id={`${props.id}_button`} popoverTarget={`${props.id}_dialog`} class={css.button}>
|
||||||
<Show when={key()}>{
|
{props.text}
|
||||||
key => {
|
|
||||||
const value = createMemo(() => props.values[key()]);
|
|
||||||
|
|
||||||
return <>{props.children(key(), value())}</>;
|
|
||||||
}
|
|
||||||
}</Show>
|
|
||||||
|
|
||||||
<Show when={showCaret()}>
|
<Show when={showCaret()}>
|
||||||
<FaSolidAngleDown class={css.caret} />
|
<FaSolidAngleDown class={css.caret} />
|
||||||
|
@ -58,24 +48,7 @@ export function Dropdown<T, K extends string>(props: DropdownProps<T, K>) {
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<dialog ref={setDialog} id={`${props.id}_dialog`} anchor={`${props.id}_button`} popover class={css.dialog} onToggle={e => setOpen(e.newState === 'open')}>
|
<dialog ref={setDialog} id={`${props.id}_dialog`} anchor={`${props.id}_button`} popover class={css.dialog} onToggle={e => setOpen(e.newState === 'open')}>
|
||||||
<Show when={props.filter !== undefined}>
|
{props.children}
|
||||||
<header>
|
|
||||||
<input value={query()} onInput={e => setQuery(e.target.value)} />
|
|
||||||
</header>
|
|
||||||
</Show>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<For each={values()}>{
|
|
||||||
([k, v]) => {
|
|
||||||
const selected = createMemo(() => key() === k);
|
|
||||||
|
|
||||||
return <span class={`${css.option} ${selected() ? css.selected : ''}`} onpointerdown={() => {
|
|
||||||
setKey(() => k);
|
|
||||||
dialog()?.hidePopover();
|
|
||||||
}}>{props.children(k, v)}</span>;
|
|
||||||
}
|
|
||||||
}</For>
|
|
||||||
</main>
|
|
||||||
</dialog>
|
</dialog>
|
||||||
</section>;
|
</section>;
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import { createMemo, createSignal, For, JSX, Setter, createEffect, Show } from "solid-js";
|
import { createMemo, createSignal, For, JSX, Setter, createEffect, Show } from "solid-js";
|
||||||
import { FaSolidAngleDown } from "solid-icons/fa";
|
import { Dropdown, DropdownApi } from "../dropdown";
|
||||||
import css from './index.module.css';
|
import css from './index.module.css';
|
||||||
|
|
||||||
interface SelectProps<T, K extends string> {
|
interface SelectProps<T, K extends string> {
|
||||||
|
@ -15,9 +15,8 @@ interface SelectProps<T, K extends string> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Select<T, K extends string>(props: SelectProps<T, K>) {
|
export function Select<T, K extends string>(props: SelectProps<T, K>) {
|
||||||
const [dialog, setDialog] = createSignal<HTMLDialogElement>();
|
const [dropdown, setDropdown] = createSignal<DropdownApi>();
|
||||||
const [key, setKey] = createSignal<K>(props.value);
|
const [key, setKey] = createSignal<K>(props.value);
|
||||||
const [open, setOpen] = createSignal<boolean>(props.open ?? false);
|
|
||||||
const [query, setQuery] = createSignal<string>('');
|
const [query, setQuery] = createSignal<string>('');
|
||||||
|
|
||||||
const values = createMemo(() => {
|
const values = createMemo(() => {
|
||||||
|
@ -32,19 +31,11 @@ export function Select<T, K extends string>(props: SelectProps<T, K>) {
|
||||||
return entries;
|
return entries;
|
||||||
});
|
});
|
||||||
|
|
||||||
const showCaret = createMemo(() => props.showCaret ?? true);
|
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
props.setValue?.(() => key());
|
props.setValue?.(() => key());
|
||||||
});
|
});
|
||||||
|
|
||||||
createEffect(() => {
|
const text = <Show when={key()}>{
|
||||||
dialog()?.[open() ? 'showPopover' : 'hidePopover']();
|
|
||||||
});
|
|
||||||
|
|
||||||
return <section class={`${css.box} ${props.class}`}>
|
|
||||||
<button id={`${props.id}_button`} popoverTarget={`${props.id}_dialog`} class={css.button}>
|
|
||||||
<Show when={key()}>{
|
|
||||||
key => {
|
key => {
|
||||||
const value = createMemo(() => props.values[key()]);
|
const value = createMemo(() => props.values[key()]);
|
||||||
|
|
||||||
|
@ -52,12 +43,7 @@ export function Select<T, K extends string>(props: SelectProps<T, K>) {
|
||||||
}
|
}
|
||||||
}</Show>
|
}</Show>
|
||||||
|
|
||||||
<Show when={showCaret()}>
|
return <Dropdown api={setDropdown} id={props.id} class={`${css.box} ${props.class}`} showCaret={props.showCaret} open={props.open} text={text}>
|
||||||
<FaSolidAngleDown class={css.caret} />
|
|
||||||
</Show>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<dialog ref={setDialog} id={`${props.id}_dialog`} anchor={`${props.id}_button`} popover class={css.dialog} onToggle={e => setOpen(e.newState === 'open')}>
|
|
||||||
<Show when={props.filter !== undefined}>
|
<Show when={props.filter !== undefined}>
|
||||||
<header>
|
<header>
|
||||||
<input value={query()} onInput={e => setQuery(e.target.value)} />
|
<input value={query()} onInput={e => setQuery(e.target.value)} />
|
||||||
|
@ -71,11 +57,10 @@ export function Select<T, K extends string>(props: SelectProps<T, K>) {
|
||||||
|
|
||||||
return <span class={`${css.option} ${selected() ? css.selected : ''}`} onpointerdown={() => {
|
return <span class={`${css.option} ${selected() ? css.selected : ''}`} onpointerdown={() => {
|
||||||
setKey(() => k);
|
setKey(() => k);
|
||||||
dialog()?.hidePopover();
|
dropdown()?.hide();
|
||||||
}}>{props.children(k, v)}</span>;
|
}}>{props.children(k, v)}</span>;
|
||||||
}
|
}
|
||||||
}</For>
|
}</For>
|
||||||
</main>
|
</main>
|
||||||
</dialog>
|
</Dropdown>
|
||||||
</section>;
|
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue