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 [dialog, setDialog] = createSignal<HTMLDialogElement>();
|
||||||
const [open, setOpen] = createSignal<boolean>(props.open ?? false);
|
const [open, setOpen] = createSignal<boolean>(props.open ?? false);
|
||||||
|
|
||||||
const showCaret = createMemo(() => props.showCaret ?? true);
|
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
dialog()?.[open() ? 'showPopover' : 'hidePopover']();
|
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}>
|
<button id={`${props.id}_button`} popoverTarget={`${props.id}_dialog`} class={css.button}>
|
||||||
{props.text}
|
{props.text}
|
||||||
|
|
||||||
<Show when={showCaret()}>
|
<Show when={props.showCaret}>
|
||||||
<FaSolidAngleDown class={css.caret} />
|
<FaSolidAngleDown class={css.caret} />
|
||||||
</Show>
|
</Show>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -19,6 +19,7 @@ export function Select<T, K extends string>(props: SelectProps<T, K>) {
|
||||||
const [key, setKey] = createSignal<K>(props.value);
|
const [key, setKey] = createSignal<K>(props.value);
|
||||||
const [query, setQuery] = createSignal<string>('');
|
const [query, setQuery] = createSignal<string>('');
|
||||||
|
|
||||||
|
const showCaret = createMemo(() => props.showCaret ?? true);
|
||||||
const values = createMemo(() => {
|
const values = createMemo(() => {
|
||||||
let entries = Object.entries<T>(props.values) as [K, T][];
|
let entries = Object.entries<T>(props.values) as [K, T][];
|
||||||
const filter = props.filter;
|
const filter = props.filter;
|
||||||
|
@ -43,7 +44,7 @@ export function Select<T, K extends string>(props: SelectProps<T, K>) {
|
||||||
}
|
}
|
||||||
}</Show>
|
}</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}>
|
<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)} />
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
.root {
|
.root {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
@ -7,7 +9,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item {
|
:is(.item, .child > button) {
|
||||||
padding: var(--padding-m) var(--padding-l);
|
padding: var(--padding-m) var(--padding-l);
|
||||||
|
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
|
@ -22,26 +24,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.child {
|
.child > dialog {
|
||||||
position: fixed;
|
|
||||||
inset-inline-start: anchor(self-start);
|
|
||||||
inset-block-start: anchor(end);
|
|
||||||
|
|
||||||
grid-template-columns: auto auto;
|
grid-template-columns: auto auto;
|
||||||
place-content: start;
|
place-content: start;
|
||||||
|
|
||||||
gap: var(--padding-m);
|
gap: var(--padding-m);
|
||||||
padding: var(--padding-m) 0;
|
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 {
|
& > .separator {
|
||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
|
@ -63,7 +51,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
: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 { createStore } from "solid-js/store";
|
||||||
import { CommandType, Command, useCommands } from "../command";
|
import { CommandType, Command, useCommands } from "../command";
|
||||||
import css from "./index.module.css";
|
import css from "./index.module.css";
|
||||||
|
import { Dropdown, DropdownApi } from "~/components/dropdown";
|
||||||
|
|
||||||
export interface MenuContextType {
|
export interface MenuContextType {
|
||||||
ref: Accessor<Node | undefined>;
|
ref: Accessor<Node | undefined>;
|
||||||
|
@ -102,77 +103,39 @@ const Separator: Component = (props) => {
|
||||||
const Root: ParentComponent<{}> = (props) => {
|
const Root: ParentComponent<{}> = (props) => {
|
||||||
const menuContext = useMenu();
|
const menuContext = useMenu();
|
||||||
const commandContext = useCommands();
|
const commandContext = useCommands();
|
||||||
const [current, setCurrent] = createSignal<HTMLElement>();
|
|
||||||
const items = children(() => props.children).toArray() as unknown as (Item | ItemWithChildren)[];
|
const items = children(() => props.children).toArray() as unknown as (Item | ItemWithChildren)[];
|
||||||
|
|
||||||
menuContext.addItems(items)
|
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>
|
|
||||||
};
|
|
||||||
|
|
||||||
return <Portal mount={menuContext.ref()}>
|
return <Portal mount={menuContext.ref()}>
|
||||||
<For each={items}>{
|
<For each={items}>{
|
||||||
item => <Switch>
|
item => <Switch>
|
||||||
<Match when={item.kind === 'node' ? item as ItemWithChildren : undefined}>{
|
<Match when={item.kind === 'node' ? item as ItemWithChildren : undefined}>{
|
||||||
item => <>
|
item => {
|
||||||
<div
|
const [dropdown, setDropdown] = createSignal<DropdownApi>();
|
||||||
class={css.child}
|
|
||||||
id={`child-${item().id}`}
|
return <Dropdown api={setDropdown} class={css.child} id={`child-${item().id}`} text={item().label}>
|
||||||
style={`position-anchor: --menu-${item().id};`}
|
|
||||||
popover
|
|
||||||
on:toggle={(e: ToggleEvent) => {
|
|
||||||
if (e.newState === 'open' && e.target !== null) {
|
|
||||||
return setCurrent(e.target as HTMLElement);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<For each={item().children}>{
|
<For each={item().children}>{
|
||||||
child => <Switch>
|
child => <Switch>
|
||||||
<Match when={child.kind === 'leaf' ? child as Item : undefined}>{
|
<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>
|
||||||
|
|
||||||
<Match when={child.kind === 'separator'}><hr class={css.separator} /></Match>
|
<Match when={child.kind === 'separator'}><hr class={css.separator} /></Match>
|
||||||
</Switch>
|
</Switch>}</For>
|
||||||
}</For>
|
</Dropdown>;
|
||||||
</div>
|
}
|
||||||
|
|
||||||
<button
|
|
||||||
class={css.item}
|
|
||||||
type="button"
|
|
||||||
popovertarget={`child-${item().id}`}
|
|
||||||
style={`anchor-name: --menu-${item().id};`}
|
|
||||||
>
|
|
||||||
{item().label}
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
}</Match>
|
}</Match>
|
||||||
|
|
||||||
<Match when={item.kind === 'leaf' ? item as Item : undefined}>{
|
<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>
|
}</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
}</For>
|
}</For>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue