Skip to content

Commit

Permalink
refactor menu to use dropdown component
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Kruining committed Jan 7, 2025
1 parent 096d4c2 commit 9d943c1
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 79 deletions.
4 changes: 1 addition & 3 deletions src/components/dropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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']();
});
Expand All @@ -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>
Expand Down
3 changes: 2 additions & 1 deletion src/components/select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)} />
Expand Down
24 changes: 4 additions & 20 deletions src/features/menu/index.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
.root {
display: grid;
grid-auto-flow: column;
margin: 0;
padding: 0;

Expand All @@ -7,7 +9,7 @@
}
}

.item {
:is(.item, .child > button) {
padding: var(--padding-m) var(--padding-l);

background-color: inherit;
Expand All @@ -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;
Expand All @@ -62,8 +50,4 @@
background-color: var(--surface-600);
}
}
}

:popover-open + .item {
background-color: var(--surface-500);
}
73 changes: 18 additions & 55 deletions src/features/menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -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>
Expand Down

0 comments on commit 9d943c1

Please sign in to comment.