diff --git a/bun.lockb b/bun.lockb index 9652a01..f6a66cd 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 8730fd2..c39ed2b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "calque", "dependencies": { + "@solid-primitives/clipboard": "^1.5.10", "@solid-primitives/i18n": "^2.1.1", "@solid-primitives/storage": "^4.2.1", "@solidjs/meta": "^0.29.4", diff --git a/src/components/table/table.module.css b/src/components/table/table.module.css index ad0f7d4..6defd7f 100644 --- a/src/components/table/table.module.css +++ b/src/components/table/table.module.css @@ -29,7 +29,7 @@ white-space: nowrap; } - & *:is(.cell:first-child, .checkbox + .cell) { + & :is(.cell:first-child, .checkbox + .cell) { position: sticky; inset-inline-start: 1px; padding-inline-start: calc(var(--depth, 0) * (1em + var(--padding-s)) + var(--padding-m)); @@ -212,11 +212,6 @@ & :is(.cell:first-child, .checkbox + .cell) { inset-inline-start: 2em; } - - & details > summary { - inset-inline-start: 2em; - grid-column: 2; - } } } diff --git a/src/features/command/command.ts b/src/features/command/command.ts index d7197ae..99805be 100644 --- a/src/features/command/command.ts +++ b/src/features/command/command.ts @@ -9,7 +9,7 @@ export enum Modifier { } export interface CommandType any = (...args: any[]) => any> { - (...args: Parameters): Promise>; + (...args: Parameters): (ReturnType extends Promise ? ReturnType : Promise>); label: DictionaryKey; shortcut?: { key: string; diff --git a/src/features/command/context.tsx b/src/features/command/context.tsx index 56cca71..8d0f997 100644 --- a/src/features/command/context.tsx +++ b/src/features/command/context.tsx @@ -106,7 +106,7 @@ const Add: Component<{ command: CommandType, commands: undefined } | { commands: return undefined; }; -const Context = any = any>(props: ParentProps<{ for: CommandType, with: Parameters }>): JSX.Element => { +const Context = any = (...args: any[]) => any>(props: ParentProps<{ for: CommandType, with: Parameters }>): JSX.Element => { const resolved = children(() => props.children); const context = useCommands(); const args = createMemo(() => props.with); diff --git a/src/features/command/contextMenu.tsx b/src/features/command/contextMenu.tsx index deea323..c2ea84f 100644 --- a/src/features/command/contextMenu.tsx +++ b/src/features/command/contextMenu.tsx @@ -1,27 +1,28 @@ import { Accessor, Component, createContext, createEffect, createMemo, createSignal, For, JSX, ParentComponent, splitProps, useContext } from "solid-js"; import { CommandType } from "./command"; import css from "./contextMenu.module.css"; +import { useCommands } from "./context"; interface ContextMenuType { readonly commands: Accessor; - readonly target: Accessor; - show(element: HTMLElement): void; + readonly event: Accessor; + show(event: Event): void; hide(): void; } const ContextMenu = createContext() const Root: ParentComponent<{ commands: CommandType[] }> = (props) => { - const [target, setTarget] = createSignal(); + const [event, setEvent] = createSignal(); - const context = { + const context: ContextMenuType = { commands: createMemo(() => props.commands), - target, - show(element: HTMLElement) { - setTarget(element); + event, + show(event) { + setEvent(event); }, hide() { - setTarget(undefined); + setEvent(undefined); }, }; @@ -32,17 +33,18 @@ const Root: ParentComponent<{ commands: CommandType[] }> = (props) => { const Menu: Component<{ children: (command: CommandType) => JSX.Element }> = (props) => { const context = useContext(ContextMenu)!; + const commandContext = useCommands(); const [root, setRoot] = createSignal(); createEffect(() => { - const target = context.target(); + const event = context.event(); const menu = root(); if (!menu) { return; } - if (target) { + if (event) { menu.showPopover(); } else { @@ -57,12 +59,11 @@ const Menu: Component<{ children: (command: CommandType) => JSX.Element }> = (pr }; const onCommand = (command: CommandType) => (e: PointerEvent) => { + commandContext?.execute(command, context.event()!); context.hide(); - - command(); }; - return + return { command =>
  • {props.children(command)}
  • }
    @@ -73,12 +74,11 @@ const Handle: ParentComponent> = (props) => { const [local, rest] = splitProps(props, ['children']); const context = useContext(ContextMenu)!; - const [handle, setHandle] = createSignal(); - return { + return { e.preventDefault(); - context.show(handle()!); + context.show(e); return false; }}>{local.children}; diff --git a/src/features/file/grid.tsx b/src/features/file/grid.tsx index 368cf36..22c7c26 100644 --- a/src/features/file/grid.tsx +++ b/src/features/file/grid.tsx @@ -1,4 +1,4 @@ -import { Accessor, Component, createEffect, createMemo, createSignal } from "solid-js"; +import { Accessor, Component, createEffect, createMemo, createSignal, JSX } from "solid-js"; import { debounce, decode, Mutation } from "~/utilities"; import { Column, GridApi as GridCompApi, Grid as GridComp } from "~/components/grid"; import { createDataSet, DataSetNode, DataSetRowNode } from "~/components/table"; @@ -29,7 +29,7 @@ const groupBy = (rows: DataSetRowNode[]) => { return group(rows.map(r => ({ ...r, _key: r.value.key }))) as any; } -export function Grid(props: { class?: string, rows: Entry[], locales: string[], api?: (api: GridApi) => any }) { +export function Grid(props: { class?: string, rows: Entry[], locales: string[], api?: (api: GridApi) => any, children?: (key: string) => JSX.Element }) { const { t } = useI18n(); const rows = createMemo(() => createDataSet(props.rows, { group: { by: 'key', with: groupBy } })); @@ -38,7 +38,7 @@ export function Grid(props: { class?: string, rows: Entry[], locales: string[], { id: 'key', label: t('feature.file.grid.key'), - renderer: ({ value }) => value.split('.').at(-1), + renderer: ({ value }) => props.children?.(value) ?? value.split('.').at(-1), }, ...locales().map>(lang => ({ id: lang, diff --git a/src/features/i18n/context.tsx b/src/features/i18n/context.tsx index 5780290..1b7bcfb 100644 --- a/src/features/i18n/context.tsx +++ b/src/features/i18n/context.tsx @@ -1,8 +1,8 @@ import { Accessor, createContext, createMemo, createSignal, ParentComponent, Setter, useContext } from 'solid-js'; import { translator, flatten, Translator, Flatten } from "@solid-primitives/i18n"; +import { makePersisted } from '@solid-primitives/storage'; import en from '~/i18n/en-GB.json'; import nl from '~/i18n/nl-NL.json'; -import { makePersisted } from '@solid-primitives/storage'; type RawDictionary = typeof en; export type Dictionary = Flatten; diff --git a/src/i18n/en-GB.json b/src/i18n/en-GB.json index 3d4edee..a76a19d 100644 --- a/src/i18n/en-GB.json +++ b/src/i18n/en-GB.json @@ -28,7 +28,8 @@ "clearSelection": "Clear selection", "insertKey": "Insert new key", "insertLanguage": "Insert new language", - "delete": "Delete selected items" + "delete": "Delete selected items", + "copyKey": "Copy key" }, "prompt": { "newKey": { diff --git a/src/i18n/nl-NL.json b/src/i18n/nl-NL.json index de06ee8..faab271 100644 --- a/src/i18n/nl-NL.json +++ b/src/i18n/nl-NL.json @@ -28,7 +28,8 @@ "clearSelection": "Selectie leeg maken", "insertKey": "Voeg nieuwe sleutel toe", "insertLanguage": "Voeg nieuwe taal toe", - "delete": "Verwijder geselecteerde items" + "delete": "Verwijder geselecteerde items", + "copyKey": "Kopieer sleutel" }, "prompt": { "newKey": { @@ -50,4 +51,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/routes/(editor)/edit.tsx b/src/routes/(editor)/edit.tsx index 184e195..78fac26 100644 --- a/src/routes/(editor)/edit.tsx +++ b/src/routes/(editor)/edit.tsx @@ -12,6 +12,7 @@ import { Prompt, PromptApi } from "~/components/prompt"; import EditBlankImage from '~/assets/edit-blank.svg' import { useI18n } from "~/features/i18n"; import { makePersisted } from "@solid-primitives/storage"; +import { writeClipboard } from "@solid-primitives/clipboard"; import css from "./edit.module.css"; const isInstalledPWA = !isServer && window.matchMedia('(display-mode: standalone)').matches; @@ -449,7 +450,19 @@ const Content: Component<{ directory: FileSystemDirectoryHandle, api?: Setter<(G })(); }); - return ; + const copyKey = createCommand('page.edit.command.copyKey', (key: string) => writeClipboard(key)); + + return { + key => { + return + { + command => + } + + {key.split('.').at(-1)!} + ; + } + }; }; const Blank: Component<{ open: CommandType }> = (props) => {