finished the menu and started implementing file IO
This commit is contained in:
parent
99509c9ba1
commit
f860ba70f1
10 changed files with 408 additions and 92 deletions
|
@ -1,12 +1,67 @@
|
|||
import { createContext, useContext } from "solid-js";
|
||||
import Dexie, { EntityTable } from "dexie";
|
||||
import { Component, createContext, useContext } from "solid-js";
|
||||
import { isServer } from "solid-js/web";
|
||||
|
||||
const FilesContext = createContext();
|
||||
type Handle = FileSystemFileHandle|FileSystemDirectoryHandle;
|
||||
|
||||
export const FilesProvider = (props) => {
|
||||
return <FilesContext.Provider value={undefined}>{props.children}</FilesContext.Provider>;
|
||||
interface File {
|
||||
name: string;
|
||||
handle: Handle;
|
||||
}
|
||||
|
||||
export const useFiles = () => useContext(FilesContext);
|
||||
type Store = Dexie & {
|
||||
files: EntityTable<File, 'name'>;
|
||||
};
|
||||
|
||||
interface FilesContextType {
|
||||
set(name: string, handle: Handle): Promise<void>;
|
||||
get(name: string): Promise<Handle|undefined>;
|
||||
list(): Promise<Handle[]>;
|
||||
}
|
||||
|
||||
const FilesContext = createContext<FilesContextType>();
|
||||
|
||||
const clientContext = (): FilesContextType => {
|
||||
const db = new Dexie('Files') as Store;
|
||||
|
||||
db.version(1).stores({
|
||||
files: 'name, handle'
|
||||
});
|
||||
|
||||
return {
|
||||
async set(name: string, handle: Handle) {
|
||||
await db.files.put({ name, handle });
|
||||
},
|
||||
async get(name: string) {
|
||||
return (await db.files.get(name))?.handle;
|
||||
},
|
||||
async list() {
|
||||
const files = await db.files.toArray();
|
||||
|
||||
return files.map(f => f.handle)
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const serverContext = (): FilesContextType => ({
|
||||
set(){
|
||||
return Promise.resolve();
|
||||
},
|
||||
get(name: string){
|
||||
return Promise.resolve(undefined);
|
||||
},
|
||||
list(){
|
||||
return Promise.resolve<Handle[]>([]);
|
||||
},
|
||||
});
|
||||
|
||||
export const FilesProvider = (props) => {
|
||||
const ctx = isServer ? serverContext() : clientContext();
|
||||
|
||||
return <FilesContext.Provider value={ctx}>{props.children}</FilesContext.Provider>;
|
||||
}
|
||||
|
||||
export const useFiles = () => useContext(FilesContext)!;
|
||||
|
||||
export const open = () => {
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Accessor, Component, For, JSX, ParentComponent, Setter, Show, children, createContext, createMemo, createSignal, createUniqueId, mergeProps, onCleanup, onMount, splitProps, useContext } from "solid-js";
|
||||
import { Accessor, Component, For, JSX, ParentComponent, Setter, Show, children, createContext, createEffect, 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 {
|
||||
|
@ -57,31 +56,13 @@ export const MenuProvider: ParentComponent = (props) => {
|
|||
|
||||
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 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 items = () => Object.values(store.items);
|
||||
const commands = () => Object.values(store.items).map(item => item.children?.map(c => c.command) ?? item.command).flat();
|
||||
|
||||
return <MenuContext.Provider value={{ ref, setRef, addItems, items, commands }}>{props.children}</MenuContext.Provider>;
|
||||
}
|
||||
|
@ -90,7 +71,7 @@ const useMenu = () => {
|
|||
const context = useContext(MenuContext);
|
||||
|
||||
if(context === undefined) {
|
||||
throw new Error('<Menu /> is called outside of a <MenuProvider />');
|
||||
throw new Error(`MenuContext is called outside of a <MenuProvider />`);
|
||||
}
|
||||
|
||||
return context;
|
||||
|
@ -117,31 +98,81 @@ const Item: Component<ItemProps> = (props) => {
|
|||
|
||||
const Root: ParentComponent<{}> = (props) => {
|
||||
const menu = useMenu();
|
||||
|
||||
menu.addItems((isServer
|
||||
const [ current, setCurrent ] = createSignal<HTMLElement>();
|
||||
const items = (isServer
|
||||
? props.children
|
||||
: props.children?.map(c => c())) ?? [])
|
||||
: props.children?.map(c => c())) ?? [];
|
||||
|
||||
menu.addItems(items)
|
||||
|
||||
const close = () => {
|
||||
const el = current();
|
||||
|
||||
if(el) {
|
||||
el.hidePopover();
|
||||
|
||||
setCurrent(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const onExecute = (command: Command) => {
|
||||
return async () => {
|
||||
await command?.();
|
||||
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
const Button: Component<{ label: string, command: Command }|{ [key: string]: any }> = (props) => {
|
||||
const [ local, rest ] = splitProps(props, ['label', 'command']);
|
||||
return <button class="item" on:pointerDown={local.command} {...rest}>{local.label}</button>;
|
||||
return <button class="menu-item" type="button" on:pointerdown={onExecute(local.command)} {...rest}>{local.label}</button>;
|
||||
};
|
||||
|
||||
return <Portal mount={menu.ref()}>
|
||||
<For each={menu.items()}>
|
||||
{(item) => <>
|
||||
<Button label={item.label} {...(item.children ? { popovertarget: `child-${item.id}`, id: `menu-${item.id}`, command: item.command } : {})} />
|
||||
<For each={items}>{
|
||||
item => {
|
||||
const [] = createSignal();
|
||||
|
||||
<Show when={item.children}>
|
||||
<div class="child" id={`child-${item.id}`} anchor={`menu-${item.id}`} style="inset: unset;" popover>
|
||||
<For each={item.children}>
|
||||
{(child) => <Button label={child.label} command={child.command} />}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</>
|
||||
return <>
|
||||
<Show when={item.children}>
|
||||
<div
|
||||
class="menu-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);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<For each={item.children}>
|
||||
{(child) => <Button label={child.label} command={child.command} />}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Button
|
||||
label={item.label}
|
||||
on:pointerenter={(e) => {
|
||||
if(!item.children) {
|
||||
return;
|
||||
}
|
||||
|
||||
const el = current();
|
||||
|
||||
if(!el){
|
||||
return;
|
||||
}
|
||||
|
||||
el.hidePopover();
|
||||
|
||||
}}
|
||||
{...(item.children ? { popovertarget: `child-${item.id}`, style: `anchor-name: --menu-${item.id};`, command: item.command } : {})}
|
||||
/>
|
||||
</>;
|
||||
}
|
||||
</For>
|
||||
}</For>
|
||||
</Portal>
|
||||
};
|
||||
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
|
||||
.menu-root {
|
||||
& > div {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.item {
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.child {
|
||||
position: fixed;
|
||||
grid-auto-flow: row;
|
||||
gap: .5em;
|
||||
padding: .5em 0;
|
||||
|
||||
inset-inline-start: anchor(start);
|
||||
inset-block-start: anchor(end);
|
||||
|
||||
border: none;
|
||||
margin: unset;
|
||||
|
||||
&:popover-open {
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue