portal is the answer I was looking for

This commit is contained in:
Chris Kruining 2024-09-26 15:47:19 +02:00
parent 2ec83e2ccb
commit 4b3d91d6cd
3 changed files with 76 additions and 80 deletions

View file

@ -1,9 +1,9 @@
import { Component, JSX, ParentComponent, children, createContext, createRenderEffect, createUniqueId, mergeProps, onCleanup, useContext } from "solid-js"; import { Accessor, Component, For, JSX, ParentComponent, Setter, Show, children, createContext, createRenderEffect, createSignal, createUniqueId, mergeProps, onCleanup, useContext } from "solid-js";
import { isServer, ssr, useAssets } from "solid-js/web"; import { Portal, isServer, ssr, useAssets } from "solid-js/web";
export interface MenuContextType { export interface MenuContextType {
add(item: Item): number; ref(): JSX.Element|undefined;
remove(index: number): void; // setRef(ref: JSX.Element|undefined): void;
}; };
export interface Item { export interface Item {
@ -15,35 +15,48 @@ export interface Item {
const MenuContext = createContext<MenuContextType>(); const MenuContext = createContext<MenuContextType>();
const initClientProvider = () => { // const initClientProvider = (): MenuContextType => {
console.log(document.querySelector('[data-app-menu="root"]')); // const root = document.querySelector('[data-app-menu="root"]');
console.log(document.querySelector('[data-app-menu="ssr-items"]')); // const items = JSON.parse(document.querySelector('[data-app-menu="ssr-items"]')?.textContent ?? '[]');
return { // console.log(items);
add(item: Item) {
return -1;
},
remove(index: number) {},
};
};
const initServerProvider = () => { // let _ref!: JSX.Element;
const items: Item[] = [];
useAssets(() => ssr(`<div data-app-menu="ssr-items">${JSON.stringify(items)}</div>`) as any); // // useAssets(() => ssr(`<script type="application/json" data-app-menu="ssr-items">${JSON.stringify(items)}</script>`) as any);
return { // return {
add(item: Item) { // ref() {
return items.push(item); // return _ref;
}, // },
remove(index: number) {}, // setRef(ref: JSX.Element) {
}; // _ref = ref;
}; // },
// };
// };
export const MenuProvider: ParentComponent = (props) => { // const initServerProvider = (): MenuContextType => {
const ctx = isServer ? initServerProvider() : initClientProvider(); // let _ref!: JSX.Element;
return <MenuContext.Provider value={ctx}>{props.children}</MenuContext.Provider>; // // useAssets(() => ssr(`<script type="application/json" data-app-menu="ssr-items">${JSON.stringify(items)}</script>`) 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<JSX.Element>();
// const ctx = {ref, setRef};
return <MenuContext.Provider value={{ ref: () => props.root }}>{props.children}</MenuContext.Provider>;
} }
const useMenu = () => { const useMenu = () => {
@ -56,7 +69,7 @@ const useMenu = () => {
return context; return context;
} }
export const MenuItem: ParentComponent<{ label: string }> = (props) => { const Item: ParentComponent<{ label: string }> = (props) => {
const childItems = children(() => props.children); const childItems = children(() => props.children);
return mergeProps(props, { return mergeProps(props, {
@ -66,48 +79,28 @@ export const MenuItem: ParentComponent<{ label: string }> = (props) => {
}) as unknown as JSX.Element; }) as unknown as JSX.Element;
} }
export const Menu: ParentComponent<{}> = (props) => { const Root: ParentComponent<{}> = (props) => {
const menu = useMenu(); const menu = useMenu();
const items: { label: string, children?: { label: string }[] }[] = (isServer const items: { label: string, children?: { label: string }[] }[] = (isServer
? props.children ? props.children
: props.children?.map(c => c())) ?? []; : props.children?.map(c => c())) ?? [];
createRenderEffect(() => { return <Portal mount={menu.ref()}>
const indices = items.map(({ label, children }) => <For each={items}>
menu.add({ {(item) => <>
id: createUniqueId(), <button {...(item.children ? { popovertarget: item.label } : {})}>{item.label}</button>
label,
children: children?.map(({ label }) => ({ id: createUniqueId(), label }))
})
);
onCleanup(() => { <Show when={item.children}>
for(const index of indices){ <div id={item.label} popover>
menu.remove(index); <For each={item.children}>
{(child) => <span>{child.label}</span>}
</For>
</div>
</Show>
</>
} }
}); </For>
}); </Portal>
return null;
}; };
export const MenuRoot: Component = () => { export const Menu = { Root, Item } as const;
const menu = useMenu();
return <div data-app-menu="root"></div>
// return <For each={menu?.items()}>
// {(item) => <>
// <button {...(item.children ? { popovertarget: item.label } : {})}>{item.label}</button>
// <Show when={item.children}>
// <div id={item.label} popover>
// <For each={item.children}>
// {(child) => <span>{child.label}</span>}
// </For>
// </div>
// </Show>
// </>
// }
// </For>;
};

View file

@ -1,17 +1,20 @@
import { Title } from "@solidjs/meta"; import { Title } from "@solidjs/meta";
import { JSX, createSignal } from "solid-js";
import { FilesProvider } from "~/features/file"; import { FilesProvider } from "~/features/file";
import { MenuRoot, MenuProvider } from "~/features/menu"; import { MenuProvider, Menu } from "~/features/menu";
export default function Editor(props) { export default function Editor(props) {
return <MenuProvider> const [ref, setRef] = createSignal<JSX.Element>();
<nav>
<Title>Translation-Tool</Title> return <MenuProvider root={ref()}>
<Title>Translation-Tool</Title>
<nav ref={setRef}>
<a href="/">Index</a> <a href="/">Index</a>
<a href="/about">About</a> <a href="/about">About</a>
<MenuRoot />
</nav> </nav>
<main> <main>
<FilesProvider> <FilesProvider>
{props.children} {props.children}

View file

@ -1,21 +1,21 @@
import { Menu, MenuItem } from "~/features/menu"; import { Menu } from "~/features/menu";
export default function Index() { export default function Index() {
return ( return (
<> <>
<Menu> <Menu.Root>
<MenuItem label="file"> <Menu.Item label="file">
<MenuItem label="open" /> <Menu.Item label="open" />
<MenuItem label="save" /> <Menu.Item label="save" />
</MenuItem> </Menu.Item>
<MenuItem label="edit" /> <Menu.Item label="edit" />
<MenuItem label="selection" /> <Menu.Item label="selection" />
<MenuItem label="view" /> <Menu.Item label="view" />
</Menu> </Menu.Root>
</> </>
); );
} }