refactor menu to use dropdown component
This commit is contained in:
parent
096d4c2651
commit
9d943c1182
4 changed files with 25 additions and 79 deletions
|
@ -21,8 +21,6 @@ export function Dropdown(props: DropdownProps) {
|
|||
const [dialog, setDialog] = createSignal<HTMLDialogElement>();
|
||||
const [open, setOpen] = createSignal<boolean>(props.open ?? false);
|
||||
|
||||
const showCaret = createMemo(() => props.showCaret ?? true);
|
||||
|
||||
createEffect(() => {
|
||||
dialog()?.[open() ? 'showPopover' : 'hidePopover']();
|
||||
});
|
||||
|
@ -42,7 +40,7 @@ export function Dropdown(props: DropdownProps) {
|
|||
<button id={`${props.id}_button`} popoverTarget={`${props.id}_dialog`} class={css.button}>
|
||||
{props.text}
|
||||
|
||||
<Show when={showCaret()}>
|
||||
<Show when={props.showCaret}>
|
||||
<FaSolidAngleDown class={css.caret} />
|
||||
</Show>
|
||||
</button>
|
||||
|
|
|
@ -19,6 +19,7 @@ export function Select<T, K extends string>(props: SelectProps<T, K>) {
|
|||
const [key, setKey] = createSignal<K>(props.value);
|
||||
const [query, setQuery] = createSignal<string>('');
|
||||
|
||||
const showCaret = createMemo(() => props.showCaret ?? true);
|
||||
const values = createMemo(() => {
|
||||
let entries = Object.entries<T>(props.values) as [K, T][];
|
||||
const filter = props.filter;
|
||||
|
@ -43,7 +44,7 @@ export function Select<T, K extends string>(props: SelectProps<T, K>) {
|
|||
}
|
||||
}</Show>
|
||||
|
||||
return <Dropdown api={setDropdown} id={props.id} class={`${css.box} ${props.class}`} showCaret={props.showCaret} open={props.open} text={text}>
|
||||
return <Dropdown api={setDropdown} id={props.id} class={`${css.box} ${props.class}`} showCaret={showCaret()} open={props.open} text={text}>
|
||||
<Show when={props.filter !== undefined}>
|
||||
<header>
|
||||
<input value={query()} onInput={e => setQuery(e.target.value)} />
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
.root {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
|
@ -7,7 +9,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
:is(.item, .child > button) {
|
||||
padding: var(--padding-m) var(--padding-l);
|
||||
|
||||
background-color: inherit;
|
||||
|
@ -22,26 +24,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.child {
|
||||
position: fixed;
|
||||
inset-inline-start: anchor(self-start);
|
||||
inset-block-start: anchor(end);
|
||||
|
||||
.child > dialog {
|
||||
grid-template-columns: auto auto;
|
||||
place-content: start;
|
||||
|
||||
gap: var(--padding-m);
|
||||
padding: var(--padding-m) 0;
|
||||
inline-size: max-content;
|
||||
|
||||
background-color: var(--surface-500);
|
||||
border: 1px solid var(--surface-300);
|
||||
border-block-start-width: 0;
|
||||
margin: unset;
|
||||
|
||||
&:popover-open {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
& > .separator {
|
||||
grid-column: span 2;
|
||||
|
@ -62,8 +50,4 @@
|
|||
background-color: var(--surface-600);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:popover-open + .item {
|
||||
background-color: var(--surface-500);
|
||||
}
|
|
@ -3,6 +3,7 @@ import { Portal } from "solid-js/web";
|
|||
import { createStore } from "solid-js/store";
|
||||
import { CommandType, Command, useCommands } from "../command";
|
||||
import css from "./index.module.css";
|
||||
import { Dropdown, DropdownApi } from "~/components/dropdown";
|
||||
|
||||
export interface MenuContextType {
|
||||
ref: Accessor<Node | undefined>;
|
||||
|
@ -102,77 +103,39 @@ const Separator: Component = (props) => {
|
|||
const Root: ParentComponent<{}> = (props) => {
|
||||
const menuContext = useMenu();
|
||||
const commandContext = useCommands();
|
||||
const [current, setCurrent] = createSignal<HTMLElement>();
|
||||
const items = children(() => props.children).toArray() as unknown as (Item | ItemWithChildren)[];
|
||||
|
||||
menuContext.addItems(items)
|
||||
|
||||
const close = () => {
|
||||
const el = current();
|
||||
|
||||
if (el) {
|
||||
el.hidePopover();
|
||||
|
||||
setCurrent(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const onExecute = (command?: CommandType) => {
|
||||
return command
|
||||
? (e: Event) => {
|
||||
close();
|
||||
|
||||
return commandContext?.execute(command, e);
|
||||
}
|
||||
: () => { }
|
||||
};
|
||||
|
||||
const Child: Component<{ command: CommandType }> = (props) => {
|
||||
return <button class={css.item} type="button" onpointerdown={onExecute(props.command)}>
|
||||
<Command.Handle command={props.command} />
|
||||
</button>
|
||||
};
|
||||
menuContext.addItems(items);
|
||||
|
||||
return <Portal mount={menuContext.ref()}>
|
||||
<For each={items}>{
|
||||
item => <Switch>
|
||||
<Match when={item.kind === 'node' ? item as ItemWithChildren : undefined}>{
|
||||
item => <>
|
||||
<div
|
||||
class={css.child}
|
||||
id={`child-${item().id}`}
|
||||
style={`position-anchor: --menu-${item().id};`}
|
||||
popover
|
||||
on:toggle={(e: ToggleEvent) => {
|
||||
if (e.newState === 'open' && e.target !== null) {
|
||||
return setCurrent(e.target as HTMLElement);
|
||||
}
|
||||
}}
|
||||
>
|
||||
item => {
|
||||
const [dropdown, setDropdown] = createSignal<DropdownApi>();
|
||||
|
||||
return <Dropdown api={setDropdown} class={css.child} id={`child-${item().id}`} text={item().label}>
|
||||
<For each={item().children}>{
|
||||
child => <Switch>
|
||||
<Match when={child.kind === 'leaf' ? child as Item : undefined}>{
|
||||
item => <Child command={item().command} />
|
||||
item => <button class={css.item} type="button" onpointerdown={e => {
|
||||
commandContext?.execute(item().command, e);
|
||||
dropdown()?.hide();
|
||||
}}>
|
||||
<Command.Handle command={item().command} />
|
||||
</button>
|
||||
}</Match>
|
||||
|
||||
<Match when={child.kind === 'separator'}><hr class={css.separator} /></Match>
|
||||
</Switch>
|
||||
}</For>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class={css.item}
|
||||
type="button"
|
||||
popovertarget={`child-${item().id}`}
|
||||
style={`anchor-name: --menu-${item().id};`}
|
||||
>
|
||||
{item().label}
|
||||
</button>
|
||||
</>
|
||||
</Switch>}</For>
|
||||
</Dropdown>;
|
||||
}
|
||||
}</Match>
|
||||
|
||||
<Match when={item.kind === 'leaf' ? item as Item : undefined}>{
|
||||
item => <Child command={item().command} />
|
||||
item => <button class={css.item} type="button" onpointerdown={e => commandContext?.execute(item().command, e)}>
|
||||
<Command.Handle command={item().command} />
|
||||
</button>
|
||||
}</Match>
|
||||
</Switch>
|
||||
}</For>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue