From 99509c9ba132273aeaa8bc3d6d54762f7aa8174d Mon Sep 17 00:00:00 2001 From: Chris Kruining Date: Tue, 1 Oct 2024 17:08:26 +0200 Subject: [PATCH] started implementation of commands --- src/features/menu/index.tsx | 192 +++++++++++++++++++++++++--------- src/features/menu/style.css | 28 +++++ src/routes/(editor).tsx | 13 +-- src/routes/(editor)/index.tsx | 29 +++-- 4 files changed, 197 insertions(+), 65 deletions(-) create mode 100644 src/features/menu/style.css diff --git a/src/features/menu/index.tsx b/src/features/menu/index.tsx index a8759ee..12dfccf 100644 --- a/src/features/menu/index.tsx +++ b/src/features/menu/index.tsx @@ -1,62 +1,89 @@ -import { Accessor, Component, For, JSX, ParentComponent, Setter, Show, children, createContext, createRenderEffect, createSignal, createUniqueId, mergeProps, onCleanup, useContext } from "solid-js"; -import { Portal, isServer, ssr, useAssets } from "solid-js/web"; +import { Accessor, Component, For, JSX, ParentComponent, Setter, Show, children, createContext, createMemo, createSignal, createUniqueId, mergeProps, onCleanup, onMount, splitProps, useContext } from "solid-js"; +import { Portal, isServer } from "solid-js/web"; +import './style.css'; +import { createStore } from "solid-js/store"; export interface MenuContextType { - ref(): JSX.Element|undefined; - // setRef(ref: JSX.Element|undefined): void; + ref: Accessor; + setRef: Setter; + + addItems(items: (Item|ItemWithChildren)[]): void; + items: Accessor<(Item|ItemWithChildren)[]>; + commands(): Command[]; }; +export enum Modifier { + Shift = 1 << 0, + Control = 1 << 1, + Meta = 1 << 2, + Alt = 1 << 3, +} + +export interface Command { + (): any; + shortcut?: { + key: string; + modifier?: Modifier; + }; +} + export interface Item { - ref?: Element; id: string; label: string; - children?: Omit[]; + command: Command; +} + +export interface ItemWithChildren { + id: string; + label: string; + children: Item[]; } const MenuContext = createContext(); -// const initClientProvider = (): MenuContextType => { -// const root = document.querySelector('[data-app-menu="root"]'); -// const items = JSON.parse(document.querySelector('[data-app-menu="ssr-items"]')?.textContent ?? '[]'); +export const createCommand = (command: () => any, shortcut?: Command['shortcut']): Command => { + if(shortcut) { + (command as Command).shortcut = { key: shortcut.key.toLowerCase(), modifier: shortcut.modifier }; + } + + return command; +}; -// console.log(items); +export const MenuProvider: ParentComponent = (props) => { + const [ ref, setRef ] = createSignal(); + const [ _items, setItems ] = createSignal }>>(new Map()); -// let _ref!: JSX.Element; + const [ store, setStore ] = createStore<{ items: Record }>({ items: {} }); -// // useAssets(() => ssr(``) as any); + const addItems = (items: (Item|ItemWithChildren)[]) => setStore('items', values => { + for (const item of items) { + // const existing = values.get(item.id); + + // if(item.children && existing?.children instanceof Map) { + // for (const child of item.children) { + // existing.children.set(child.id, child); + // } + // } + // else if (item.children && existing === undefined){ + // values.set(item.id, { ...item, children: new Map(item.children.map(c => [ c.id, c ])) }); + // } + // else { + // values.set(item.id, item as Item); + // } + values[item.id] = item; + } -// return { -// ref() { -// return _ref; -// }, -// setRef(ref: JSX.Element) { -// _ref = ref; -// }, -// }; -// }; + return values; + }); + const items = createMemo<(Item|ItemWithChildren)[]>(() => + Array.from( + Object.values(store.items), + // item => item.children instanceof Map ? { ...item, children: Array.from(item.children.values()) } : item + ) + ); + const commands = createMemo(() => items().map(item => item.children instanceof Array ? item.children.map(c => c.command) : item.command).flat()); -// const initServerProvider = (): MenuContextType => { -// let _ref!: JSX.Element; - -// // useAssets(() => ssr(``) as any); - -// return { -// ref() { -// return _ref; -// }, -// setRef(ref: JSX.Element) { -// _ref = ref; -// }, -// }; -// }; - -export const MenuProvider: ParentComponent<{ root?: JSX.Element }> = (props) => { - // const ctx = isServer ? initServerProvider() : initClientProvider(); - - // const [ ref, setRef ] = createSignal(); - // const ctx = {ref, setRef}; - - return props.root }}>{props.children}; + return {props.children}; } const useMenu = () => { @@ -69,10 +96,19 @@ const useMenu = () => { return context; } -const Item: ParentComponent<{ label: string }> = (props) => { +type ItemProps = { label: string, children: JSX.Element }|{ label: string, command: Command }; + +const Item: Component = (props) => { + const id = createUniqueId(); + + if(props.command) { + return mergeProps(props, { id }) as unknown as JSX.Element; + } + const childItems = children(() => props.children); return mergeProps(props, { + id, get children() { return childItems(); } @@ -81,19 +117,25 @@ const Item: ParentComponent<{ label: string }> = (props) => { const Root: ParentComponent<{}> = (props) => { const menu = useMenu(); - const items: { label: string, children?: { label: string }[] }[] = (isServer + + menu.addItems((isServer ? props.children - : props.children?.map(c => c())) ?? []; + : props.children?.map(c => c())) ?? []) + + const Button: Component<{ label: string, command: Command }|{ [key: string]: any }> = (props) => { + const [ local, rest ] = splitProps(props, ['label', 'command']); + return ; + }; return - + {(item) => <> - +