@thebes/cadmea 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +133 -0
- package/dist/RichTextEditor-BPilh7Pw.js +36 -0
- package/dist/RichTextEditor-BPilh7Pw.js.map +1 -0
- package/dist/RichTextEditor-DcLqdFY7.js +15 -0
- package/dist/RichTextEditor-DcLqdFY7.js.map +1 -0
- package/dist/index/index.d.ts +147 -0
- package/dist/index/index.js +740 -0
- package/dist/index/index.js.map +1 -0
- package/dist/index/server.js +508 -0
- package/dist/index/server.js.map +1 -0
- package/dist/tanstack-start/index.d.ts +180 -0
- package/dist/tanstack-start/index.js +897 -0
- package/dist/tanstack-start/index.js.map +1 -0
- package/dist/tanstack-start/server.js +730 -0
- package/dist/tanstack-start/server.js.map +1 -0
- package/package.json +138 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["CollectionConfig","FieldConfig","createEffect","createSignal","For","lazy","Show","Suspense","CollectionCapabilities","RichTextEditor","then","mod","default","editableFields","config","Object","entries","fields","filter","key","RelationshipOption","id","label","DraftActions","onSaveDraft","values","Record","Promise","onPublish","onPreview","saving","publishing","previewing","canPublish","canPreview","saveDraftLabel","publishLabel","previewLabel","CollectionEditProps","initialValues","onSubmit","submitLabel","error","onUploadFile","file","File","url","relationshipOptions","Partial","onDirtyChange","dirty","draftActions","capabilities","RenderContext","CollectionEdit","props","initialSnapshot","JSON","stringify","setValues","setField","value","prev","editablePayload","fromEntries","type","handleSubmit","event","SubmitEvent","preventDefault","ctx","versioned","versions","drafts","_el$","_tmpl$6","_el$3","firstChild","addEventListener","_$insert","_$createComponent","when","children","_el$2","_tmpl$","each","field","_el$0","_tmpl$8","_el$1","_$setAttribute","required","_tmpl$7","renderInput","fallback","canUpdate","_el$11","_tmpl$9","_tmpl$2","_$effect","disabled","_el$4","_tmpl$3","$$click","_el$6","_tmpl$4","_el$8","_tmpl$5","_el$13","_tmpl$0","$$input","e","currentTarget","_el$14","_tmpl$1","options","option","_el$15","_tmpl$10","_el$16","_tmpl$11","valueAsNumber","_el$17","_tmpl$12","formatDateValue","_el$18","_tmpl$13","checked","renderUploadInput","renderRelationshipInput","renderArrayInput","content","onChange","doc","uploading","setUploading","uploadError","setUploadError","handleFileChange","Event","HTMLInputElement","files","undefined","err","Error","message","_el$20","_tmpl$16","_el$22","_el$21","_tmpl$14","_el$24","_tmpl$15","_p$","_v$","_v$2","t","hasMany","relationTo","_el$25","_tmpl$17","_el$26","Number","_el$27","String","items","Array","isArray","updateItem","index","itemKey","itemValue","next","slice","addItem","removeItem","_","i","fieldsForItem","item","base","discriminator","variantValue","variantFields","variants","_el$28","_tmpl$18","_el$29","_el$30","_el$31","_tmpl$19","_el$32","itemField","inputId","_el$33","_el$34","v","date","Date","isNaN","getTime","toLocaleString","_$delegateEvents","CollectionConfig","FieldConfig","createSignal","For","Show","listableFields","config","excluded","Set","Object","entries","fields","filter","key","field","has","type","formatCellValue","value","undefined","Date","toLocaleDateString","String","rowId","row","Record","id","CollectionListProps","rows","onRowClick","page","pageSize","totalCount","onPageChange","sortField","sortDirection","onSortChange","direction","selectable","selectedIds","ReadonlySet","onSelectionChange","CollectionList","props","columns","selectMode","setSelectMode","toggleSelected","next","delete","add","handleRowActivate","_el$","_tmpl$7","_el$2","firstChild","_$insert","_$createComponent","when","children","_el$3","_tmpl$","$$click","v","_el$4","_tmpl$2","_el$5","_el$6","nextSibling","addEventListener","e","currentTarget","each","_el$16","_tmpl$8","_$effect","length","fallback","_el$17","_tmpl$9","_el$18","_el$20","_el$19","slug","_el$7","_tmpl$4","_el$8","_el$9","_el$1","_tmpl$3","_el$21","_el$22","_tmpl$1","_el$23","_tmpl$0","_el$24","stopPropagation","checked","_el$25","_tmpl$10","_$className","_el$10","_tmpl$5","_el$26","_tmpl$12","_el$27","_el$29","$$keydown","preventDefault","_el$28","_tmpl$11","_el$30","_tmpl$13","_el$31","_el$32","_$memo","_el$11","_tmpl$6","_el$12","_el$13","_el$14","_el$15","_p$","_v$","_v$2","disabled","t","_$delegateEvents","createEffect","createSignal","For","JSX","onCleanup","Show","SearchPaletteResult","collection","id","label","SearchPaletteProps","onSearch","query","Promise","onSelect","result","DEBOUNCE_MS","capitalize","value","length","toUpperCase","slice","SearchPalette","props","Element","open","setOpen","setQuery","results","setResults","activeIndex","setActiveIndex","inputRef","HTMLInputElement","triggeredBy","HTMLElement","debounceTimer","ReturnType","setTimeout","latestQueryToken","close","focus","runSearch","token","trim","then","found","onInput","clearTimeout","selectResult","onGlobalKeyDown","event","KeyboardEvent","metaKey","ctrlKey","key","toLowerCase","preventDefault","document","activeElement","addEventListener","removeEventListener","onDialogKeyDown","i","Math","min","max","_$createComponent","when","children","_el$","_tmpl$2","_el$2","firstChild","_el$3","_el$4","_el$5","nextSibling","$$click","$$keydown","stopPropagation","$$input","currentTarget","_ref$","_$use","_$insert","fallback","_el$7","_tmpl$3","_el$6","_tmpl$","each","index","_el$8","_tmpl$4","_el$9","_el$0","_el$1","_$effect","classList","toggle","_$delegateEvents"],"sources":["../../src/CollectionEdit.tsx","../../src/CollectionList.tsx","../../src/SearchPalette.tsx"],"sourcesContent":["import type { CollectionConfig, FieldConfig } from \"@thebes/cadmus/cms\";\nimport {\n createEffect,\n createSignal,\n For,\n lazy,\n Show,\n Suspense,\n} from \"solid-js\";\nimport type { CollectionCapabilities } from \"./capabilities.js\";\n\n// Dynamic import, not a static one — @tiptap/core + @tiptap/starter-kit\n// are a large dependency (pushed a consuming route's bundle from ~9KB to\n// ~800KB when statically imported, even for collections with zero\n// richText fields). Lazy-loading means only forms that actually render a\n// richText field pull this chunk in at runtime.\nconst RichTextEditor = lazy(() =>\n import(\"./RichTextEditor.js\").then((mod) => ({\n default: mod.RichTextEditor,\n })),\n);\n\n// Fields the generic form can actually render today. `id` is never\n// user-editable. `date` fields (e.g. createdAt) are server-defaulted and\n// shown read-only rather than editable in this step.\nfunction editableFields(config: CollectionConfig): [string, FieldConfig][] {\n return Object.entries(config.fields).filter(([key]) => key !== \"id\");\n}\n\nexport interface RelationshipOption {\n id: number;\n label: string;\n}\n\n/**\n * Replaces the generic \"Save\" button with \"Save draft\"/\"Publish\" when the\n * collection has `versions: { drafts: true }` — a separate privilege from\n * a plain update, matching `access.publish` in `@thebes/cadmus/cms`.\n * `onPublish` takes no values: publishing acts on whatever was last saved\n * as a draft (the consuming route tracks which version that is), not on\n * the live form state.\n */\nexport interface DraftActions {\n onSaveDraft: (values: Record<string, unknown>) => void | Promise<void>;\n onPublish?: () => void | Promise<void>;\n /**\n * Opens a live preview of the last saved draft (issue #28) — like\n * `onPublish`, acts on whatever was last saved as a draft, not the live\n * form state. Omit to not render the Preview button at all.\n */\n onPreview?: () => void | Promise<void>;\n saving?: boolean;\n publishing?: boolean;\n previewing?: boolean;\n /** Disables Publish — e.g. until a draft has been saved at least once. */\n canPublish?: boolean;\n /** Disables Preview — same gating as canPublish, a draft must exist first. */\n canPreview?: boolean;\n saveDraftLabel?: string;\n publishLabel?: string;\n previewLabel?: string;\n}\n\nexport interface CollectionEditProps {\n config: CollectionConfig;\n initialValues?: Record<string, unknown>;\n onSubmit: (values: Record<string, unknown>) => void | Promise<void>;\n submitLabel?: string;\n error?: string;\n /** Disables the Save button and shows a spinner in its place. */\n saving?: boolean;\n /**\n * Resolves an `upload` field's selected file to a stored URL. Required\n * if the collection has any `upload` fields — `CollectionEdit` never\n * talks to storage directly (stays agnostic of R2/cadmus/storage), so\n * the consuming route wires this to a server function that calls an\n * `ImageService`'s `upload()`.\n */\n onUploadFile?: (file: File) => Promise<{ url: string }>;\n /**\n * Options for `relationship` fields (hasMany:false only — see\n * RelationshipFieldConfig's `hasMany` caveat), keyed by the field's\n * `relationTo` collection slug. `CollectionEdit` can't query another\n * collection itself, so the consuming route fetches the related rows\n * and passes them in.\n */\n relationshipOptions?: Partial<Record<string, RelationshipOption[]>>;\n /**\n * Fired whenever the dirty (unsaved-changes) state changes — wire this\n * to a router-level navigation guard (e.g. `useBlocker` in the\n * consuming route) since `CollectionEdit` has no router access itself.\n */\n onDirtyChange?: (dirty: boolean) => void;\n /** Only rendered when `config.versions?.drafts` is also true. */\n draftActions?: DraftActions;\n /**\n * Hides the Save button when `canUpdate` is `false` — see issue #26's\n * RBAC-aware admin UI. Undefined (the default — most collections don't\n * wire this up) reads as \"allowed\", same as `@thebes/cadmus/cms`'s own\n * \"no access fn = allowed\" default.\n */\n capabilities?: CollectionCapabilities;\n}\n\ninterface RenderContext {\n onUploadFile?: (file: File) => Promise<{ url: string }>;\n relationshipOptions?: Partial<Record<string, RelationshipOption[]>>;\n}\n\nexport function CollectionEdit(props: CollectionEditProps) {\n const initialSnapshot = JSON.stringify(props.initialValues ?? {});\n const [values, setValues] = createSignal<Record<string, unknown>>(\n props.initialValues ?? {},\n );\n\n // Reported via `onDirtyChange` rather than tracked by the consuming\n // route itself — only this component sees every field edit as it\n // happens. A plain JSON.stringify comparison is enough: form values are\n // already plain JSON-shaped data (TipTap docs, array-field items), so\n // there's no Date/Map/Set edge case to special-case here.\n createEffect(() => {\n props.onDirtyChange?.(JSON.stringify(values()) !== initialSnapshot);\n });\n\n function setField(key: string, value: unknown) {\n setValues((prev) => ({ ...prev, [key]: value }));\n }\n\n // date fields are read-only — never include them in a submitted/draft payload\n function editablePayload(): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(values()).filter(\n ([key]) => props.config.fields[key]?.type !== \"date\",\n ),\n );\n }\n\n function handleSubmit(event: SubmitEvent) {\n event.preventDefault();\n void props.onSubmit(editablePayload());\n }\n\n const ctx: RenderContext = {\n onUploadFile: props.onUploadFile,\n relationshipOptions: props.relationshipOptions,\n };\n\n const versioned = () => props.config.versions?.drafts && props.draftActions;\n\n return (\n <form class=\"flex flex-col gap-4\" onSubmit={handleSubmit}>\n <Show when={props.error}>\n {/* role=\"alert\" so assistive tech announces submit failures the\n moment they appear, not only if the user happens to navigate to\n them. */}\n <p class=\"text-sm text-error\" role=\"alert\">\n {props.error}\n </p>\n </Show>\n <For each={editableFields(props.config)}>\n {([key, field]) => (\n <div class=\"form-control\">\n {/* The \" *\" stays inside the label's accessible name (it reads\n as \"required\" alongside each input's `required` attribute);\n the span only colors it, it does not change the text. */}\n <label class=\"label\" for={key}>\n {key}\n <Show when={field.required}>\n <span class=\"text-error\">{\" *\"}</span>\n </Show>\n </label>\n {renderInput(key, field, values()[key], setField, ctx)}\n </div>\n )}\n </For>\n {/* Bottom-anchored, full-width action bar — not a top toolbar, per\n issue #25's mobile-first note. */}\n <div class=\"bg-base-100 sticky bottom-0 flex gap-2 border-t py-3\">\n <Show\n when={versioned()}\n fallback={\n <Show when={props.capabilities?.canUpdate !== false}>\n <button\n type=\"submit\"\n class=\"btn btn-primary flex-1\"\n disabled={props.saving}\n >\n <Show\n when={props.saving}\n fallback={props.submitLabel ?? \"Save\"}\n >\n <span class=\"loading loading-spinner loading-sm\" />\n </Show>\n </button>\n </Show>\n }\n >\n <button\n type=\"button\"\n class=\"btn flex-1\"\n disabled={props.draftActions?.saving}\n onClick={() =>\n void props.draftActions?.onSaveDraft(editablePayload())\n }\n >\n <Show\n when={props.draftActions?.saving}\n fallback={props.draftActions?.saveDraftLabel ?? \"Save draft\"}\n >\n <span class=\"loading loading-spinner loading-sm\" />\n </Show>\n </button>\n <button\n type=\"button\"\n class=\"btn btn-primary flex-1\"\n disabled={\n !props.draftActions?.canPublish || props.draftActions?.publishing\n }\n onClick={() => void props.draftActions?.onPublish?.()}\n >\n <Show\n when={props.draftActions?.publishing}\n fallback={props.draftActions?.publishLabel ?? \"Publish\"}\n >\n <span class=\"loading loading-spinner loading-sm\" />\n </Show>\n </button>\n <Show when={props.draftActions?.onPreview}>\n <button\n type=\"button\"\n class=\"btn btn-outline flex-1\"\n disabled={\n !props.draftActions?.canPreview ||\n props.draftActions?.previewing\n }\n onClick={() => void props.draftActions?.onPreview?.()}\n >\n <Show\n when={props.draftActions?.previewing}\n fallback={props.draftActions?.previewLabel ?? \"Preview\"}\n >\n <span class=\"loading loading-spinner loading-sm\" />\n </Show>\n </button>\n </Show>\n </Show>\n </div>\n </form>\n );\n}\n\nfunction renderInput(\n key: string,\n field: FieldConfig,\n value: unknown,\n setField: (key: string, value: unknown) => void,\n ctx: RenderContext,\n) {\n switch (field.type) {\n case \"text\":\n return (\n <input\n id={key}\n class=\"input\"\n type=\"text\"\n value={(value as string) ?? \"\"}\n required={field.required}\n onInput={(e) => setField(key, e.currentTarget.value)}\n />\n );\n case \"select\":\n return (\n <select\n id={key}\n class=\"select\"\n value={(value as string) ?? \"\"}\n required={field.required}\n onChange={(e) => setField(key, e.currentTarget.value)}\n >\n <For each={field.options}>\n {(option) => <option value={option}>{option}</option>}\n </For>\n </select>\n );\n case \"number\":\n return (\n <input\n id={key}\n class=\"input\"\n type=\"number\"\n value={(value as number) ?? \"\"}\n required={field.required}\n onInput={(e) => setField(key, e.currentTarget.valueAsNumber)}\n />\n );\n case \"date\":\n return (\n <input\n id={key}\n class=\"input\"\n type=\"text\"\n readOnly\n value={formatDateValue(value)}\n />\n );\n case \"checkbox\":\n return (\n <input\n id={key}\n class=\"checkbox\"\n type=\"checkbox\"\n checked={(value as boolean) ?? false}\n onChange={(e) => setField(key, e.currentTarget.checked)}\n />\n );\n case \"upload\":\n return renderUploadInput(key, field, value, setField, ctx);\n case \"relationship\":\n return renderRelationshipInput(key, field, value, setField, ctx);\n case \"array\":\n return renderArrayInput(key, field, value, setField, ctx);\n case \"richText\":\n return (\n <Suspense\n fallback={<span class=\"loading loading-spinner loading-sm\" />}\n >\n <RichTextEditor\n id={key}\n content={value as object | undefined}\n onChange={(doc) => setField(key, doc)}\n />\n </Suspense>\n );\n default:\n return null;\n }\n}\n\nfunction renderUploadInput(\n key: string,\n field: FieldConfig & { type: \"upload\" },\n value: unknown,\n setField: (key: string, value: unknown) => void,\n ctx: RenderContext,\n) {\n const [uploading, setUploading] = createSignal(false);\n const [uploadError, setUploadError] = createSignal<string>();\n\n async function handleFileChange(\n e: Event & { currentTarget: HTMLInputElement },\n ) {\n const file = e.currentTarget.files?.[0];\n if (!file) return;\n if (!ctx.onUploadFile) {\n setUploadError(\"No upload handler configured for this form.\");\n return;\n }\n setUploading(true);\n setUploadError(undefined);\n try {\n const { url } = await ctx.onUploadFile(file);\n setField(key, url);\n } catch (err) {\n setUploadError(err instanceof Error ? err.message : \"Upload failed\");\n } finally {\n setUploading(false);\n }\n }\n\n return (\n <div class=\"flex flex-col gap-2\">\n <Show when={value}>\n <p class=\"text-sm opacity-70 break-all\">{value as string}</p>\n </Show>\n <input\n id={key}\n class=\"file-input\"\n type=\"file\"\n required={field.required && !value}\n disabled={uploading()}\n onChange={handleFileChange}\n />\n <Show when={uploading()}>\n <span class=\"loading loading-spinner loading-sm\" />\n </Show>\n <Show when={uploadError()}>\n <p class=\"text-sm text-error\">{uploadError()}</p>\n </Show>\n </div>\n );\n}\n\nfunction renderRelationshipInput(\n key: string,\n field: FieldConfig & { type: \"relationship\" },\n value: unknown,\n setField: (key: string, value: unknown) => void,\n ctx: RenderContext,\n) {\n // hasMany:true relationships are join-table-backed (no plain FK column\n // to bind a single <select> to) — not supported by this generic form\n // yet. See RelationshipFieldConfig's `hasMany` doc.\n if (field.hasMany) return null;\n\n const options = ctx.relationshipOptions?.[field.relationTo] ?? [];\n\n return (\n <select\n id={key}\n class=\"select\"\n value={value != null ? String(value) : \"\"}\n required={field.required}\n onChange={(e) =>\n setField(\n key,\n e.currentTarget.value === \"\" ? null : Number(e.currentTarget.value),\n )\n }\n >\n <option value=\"\">—</option>\n <For each={options}>\n {(option) => <option value={option.id}>{option.label}</option>}\n </For>\n </select>\n );\n}\n\nfunction renderArrayInput(\n key: string,\n field: FieldConfig & { type: \"array\" },\n value: unknown,\n setField: (key: string, value: unknown) => void,\n ctx: RenderContext,\n) {\n const items = () =>\n Array.isArray(value) ? (value as Record<string, unknown>[]) : [];\n\n function updateItem(index: number, itemKey: string, itemValue: unknown) {\n const next = items().slice();\n next[index] = { ...next[index], [itemKey]: itemValue };\n setField(key, next);\n }\n\n function addItem() {\n setField(key, [...items(), {}]);\n }\n\n function removeItem(index: number) {\n setField(\n key,\n items().filter((_, i) => i !== index),\n );\n }\n\n function fieldsForItem(\n item: Record<string, unknown>,\n ): [string, FieldConfig][] {\n const base = Object.entries(field.fields);\n const discriminator = field.discriminator;\n if (!discriminator) return base;\n\n const variantValue = item[discriminator.key];\n const variantFields =\n typeof variantValue === \"string\"\n ? discriminator.variants[variantValue]\n : undefined;\n return variantFields ? [...base, ...Object.entries(variantFields)] : base;\n }\n\n return (\n <div class=\"flex flex-col gap-3\">\n <For each={items()}>\n {(item, index) => (\n <div class=\"card bg-base-200 flex flex-col gap-2 p-3\">\n <For each={fieldsForItem(item)}>\n {([itemKey, itemField]) => {\n const inputId = `${key}.${index()}.${itemKey}`;\n return (\n <div class=\"form-control\">\n <label class=\"label\" for={inputId}>\n {itemKey}\n <Show when={itemField.required}>\n <span class=\"text-error\">{\" *\"}</span>\n </Show>\n </label>\n {renderInput(\n inputId,\n itemField,\n item[itemKey],\n (_, v) => updateItem(index(), itemKey, v),\n ctx,\n )}\n </div>\n );\n }}\n </For>\n <button\n type=\"button\"\n class=\"btn btn-error btn-outline btn-sm self-start\"\n onClick={() => removeItem(index())}\n >\n Remove\n </button>\n </div>\n )}\n </For>\n <button\n type=\"button\"\n class=\"btn btn-outline btn-sm self-start\"\n onClick={addItem}\n >\n Add {key}\n </button>\n </div>\n );\n}\n\nfunction formatDateValue(value: unknown): string {\n if (!value) return \"—\";\n const date = value instanceof Date ? value : new Date(value as string);\n return Number.isNaN(date.getTime()) ? \"—\" : date.toLocaleString();\n}\n","import type { CollectionConfig, FieldConfig } from \"@thebes/cadmus/cms\";\nimport { createSignal, For, Show } from \"solid-js\";\n\n// Field types that can be rendered as a plain table cell today.\n// `id` is intentionally excluded — it's never a useful list column.\n// `richText`/`array` are structured content, not a sensible table cell;\n// `relationship` has no resolved label available here (CollectionList\n// only receives raw row data, not the related collection's rows) so it'd\n// show a bare numeric id — excluded until that's worth solving.\nfunction listableFields(config: CollectionConfig): [string, FieldConfig][] {\n const excluded = new Set([\"richText\", \"array\", \"relationship\"]);\n return Object.entries(config.fields).filter(\n ([key, field]) => key !== \"id\" && !excluded.has(field.type),\n );\n}\n\nfunction formatCellValue(value: unknown): string {\n if (value === null || value === undefined) return \"—\";\n if (value instanceof Date) return value.toLocaleDateString();\n return String(value);\n}\n\nfunction rowId(row: Record<string, unknown>): number | undefined {\n return typeof row.id === \"number\" ? row.id : undefined;\n}\n\nexport interface CollectionListProps {\n config: CollectionConfig;\n rows: Record<string, unknown>[];\n onRowClick?: (row: Record<string, unknown>) => void;\n\n /**\n * 1-based current page. Omit (along with `pageSize`) to render without\n * the pagination bar entirely — list views with no `find()` paging\n * wired up yet still render correctly.\n */\n page?: number;\n pageSize?: number;\n /** Total row count across all pages — see `LocalApi.count()`. Enables\n * disabling \"Next\" exactly at the last page; omit to fall back to a\n * `rows.length < pageSize` heuristic. */\n totalCount?: number;\n onPageChange?: (page: number) => void;\n\n /** Field key currently sorted on. Omit to hide the sort control. */\n sortField?: string;\n sortDirection?: \"asc\" | \"desc\";\n onSortChange?: (field: string, direction: \"asc\" | \"desc\") => void;\n\n /** Shows the \"Select\" bulk-select mode toggle. */\n selectable?: boolean;\n selectedIds?: ReadonlySet<number>;\n onSelectionChange?: (selectedIds: Set<number>) => void;\n}\n\nexport function CollectionList(props: CollectionListProps) {\n const columns = () => listableFields(props.config);\n const [selectMode, setSelectMode] = createSignal(false);\n\n function toggleSelected(id: number) {\n const next = new Set(props.selectedIds ?? []);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n props.onSelectionChange?.(next);\n }\n\n function handleRowActivate(row: Record<string, unknown>) {\n if (selectMode()) {\n const id = rowId(row);\n if (id !== undefined) toggleSelected(id);\n return;\n }\n props.onRowClick?.(row);\n }\n\n return (\n <div class=\"flex flex-col gap-3\">\n <div class=\"flex flex-wrap items-center justify-between gap-2\">\n <Show when={props.selectable}>\n <button\n type=\"button\"\n class=\"btn btn-outline btn-sm\"\n onClick={() => setSelectMode((v) => !v)}\n >\n {selectMode() ? \"Done\" : \"Select\"}\n </button>\n </Show>\n {/* Dropdown picker, not clickable column headers — sort works the\n same on touch and desktop, see issue #25's mobile-first note. */}\n <Show when={props.onSortChange}>\n <div class=\"join\">\n <select\n aria-label=\"Sort by\"\n class=\"select select-sm join-item\"\n value={props.sortField ?? \"\"}\n onChange={(e) =>\n props.onSortChange?.(\n e.currentTarget.value,\n props.sortDirection ?? \"asc\",\n )\n }\n >\n <For each={columns()}>\n {([key]) => <option value={key}>{key}</option>}\n </For>\n </select>\n <select\n aria-label=\"Sort direction\"\n class=\"select select-sm join-item\"\n value={props.sortDirection ?? \"asc\"}\n onChange={(e) =>\n props.onSortChange?.(\n props.sortField ?? columns()[0]?.[0] ?? \"\",\n e.currentTarget.value as \"asc\" | \"desc\",\n )\n }\n >\n <option value=\"asc\">Ascending</option>\n <option value=\"desc\">Descending</option>\n </select>\n </div>\n </Show>\n </div>\n\n <Show\n when={props.rows.length > 0}\n fallback={<p class=\"text-sm opacity-70\">No {props.config.slug} yet.</p>}\n >\n {/* Table on desktop — hidden below md per the mobile-first card\n layout below, not the other way around. */}\n <table class=\"table hidden md:table\">\n <thead>\n <tr>\n <Show when={selectMode()}>\n <th />\n </Show>\n <For each={columns()}>{([key]) => <th>{key}</th>}</For>\n </tr>\n </thead>\n <tbody>\n <For each={props.rows}>\n {(row) => (\n <tr\n class={\n props.onRowClick || selectMode()\n ? \"cursor-pointer hover\"\n : undefined\n }\n onClick={() => handleRowActivate(row)}\n >\n <Show when={selectMode()}>\n <td>\n <input\n type=\"checkbox\"\n class=\"checkbox checkbox-sm\"\n onClick={(e) => e.stopPropagation()}\n checked={\n rowId(row) !== undefined &&\n (props.selectedIds?.has(rowId(row) as number) ??\n false)\n }\n onChange={() => {\n const id = rowId(row);\n if (id !== undefined) toggleSelected(id);\n }}\n />\n </td>\n </Show>\n <For each={columns()}>\n {([key]) => <td>{formatCellValue(row[key])}</td>}\n </For>\n </tr>\n )}\n </For>\n </tbody>\n </table>\n\n {/* Stacked card list on mobile/tablet — tap-to-select via an\n always-visible checkbox in select mode, never hover-revealed. */}\n <div class=\"flex flex-col gap-2 md:hidden\">\n <For each={props.rows}>\n {(row) => (\n // biome-ignore lint/a11y/useSemanticElements: a native <button> can't contain interactive content (the select-mode checkbox below); role=\"button\" + tabIndex/onKeyDown is the standard fallback.\n <div\n class=\"card bg-base-200 cursor-pointer p-3\"\n role=\"button\"\n tabIndex={0}\n onClick={() => handleRowActivate(row)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n handleRowActivate(row);\n }\n }}\n >\n <div class=\"flex items-start gap-3\">\n <Show when={selectMode()}>\n <input\n type=\"checkbox\"\n class=\"checkbox checkbox-sm mt-1\"\n onClick={(e) => e.stopPropagation()}\n checked={\n rowId(row) !== undefined &&\n (props.selectedIds?.has(rowId(row) as number) ?? false)\n }\n onChange={() => {\n const id = rowId(row);\n if (id !== undefined) toggleSelected(id);\n }}\n />\n </Show>\n <div class=\"flex flex-1 flex-col gap-1\">\n <For each={columns()}>\n {([key]) => (\n <div class=\"flex justify-between gap-2 text-sm\">\n <span class=\"opacity-60\">{key}</span>\n <span class=\"text-right\">\n {formatCellValue(row[key])}\n </span>\n </div>\n )}\n </For>\n </div>\n </div>\n </div>\n )}\n </For>\n </div>\n </Show>\n\n {/* Bottom-anchored prev/next bar — no page numbers, per issue #25's\n mobile-first note. Renders only when pagination is wired up. */}\n <Show when={props.page !== undefined && props.pageSize !== undefined}>\n <div class=\"bg-base-100 sticky bottom-0 flex items-center justify-between gap-2 border-t py-2\">\n <button\n type=\"button\"\n class=\"btn btn-sm\"\n disabled={(props.page ?? 1) <= 1}\n onClick={() => props.onPageChange?.((props.page ?? 1) - 1)}\n >\n Prev\n </button>\n <span class=\"text-sm opacity-70\">Page {props.page}</span>\n <button\n type=\"button\"\n class=\"btn btn-sm\"\n disabled={\n props.totalCount !== undefined\n ? (props.page ?? 1) * (props.pageSize ?? 0) >= props.totalCount\n : props.rows.length < (props.pageSize ?? 0)\n }\n onClick={() => props.onPageChange?.((props.page ?? 1) + 1)}\n >\n Next\n </button>\n </div>\n </Show>\n </div>\n );\n}\n","import {\n createEffect,\n createSignal,\n For,\n type JSX,\n onCleanup,\n Show,\n} from \"solid-js\";\n\nexport interface SearchPaletteResult {\n collection: string;\n id: number;\n label: string;\n}\n\nexport interface SearchPaletteProps {\n /** Runs a query against `LocalApi.search()` for every searchable collection — see `@thebes/cadmus/cms`'s `getCollectionsMeta`. */\n onSearch: (query: string) => Promise<SearchPaletteResult[]>;\n /** Navigates to the chosen result; the palette closes itself afterward. */\n onSelect: (result: SearchPaletteResult) => void;\n}\n\nconst DEBOUNCE_MS = 200;\n\nfunction capitalize(value: string): string {\n return value.length === 0 ? value : value[0].toUpperCase() + value.slice(1);\n}\n\n/**\n * Self-contained Cmd+K (Ctrl+K on non-Mac) search palette — issue #29.\n * Owns its own open/closed state and keyboard listener; the host app only\n * supplies `onSearch` (wired to a server function that fans out across\n * every collection with `search` configured) and `onSelect` (navigation).\n * Mirrors PanelNav's focus-trap-on-open / restore-focus-on-close pattern\n * rather than introducing a second one.\n */\nexport function SearchPalette(props: SearchPaletteProps): JSX.Element {\n const [open, setOpen] = createSignal(false);\n const [query, setQuery] = createSignal(\"\");\n const [results, setResults] = createSignal<SearchPaletteResult[]>([]);\n const [activeIndex, setActiveIndex] = createSignal(0);\n let inputRef: HTMLInputElement | undefined;\n let triggeredBy: HTMLElement | null = null;\n let debounceTimer: ReturnType<typeof setTimeout> | undefined;\n // Guards a result fetch that resolves after a newer one was already\n // kicked off (or after the palette closed) from clobbering fresher\n // results with stale ones.\n let latestQueryToken = 0;\n\n function close() {\n setOpen(false);\n setQuery(\"\");\n setResults([]);\n triggeredBy?.focus();\n }\n\n function runSearch(value: string) {\n const token = ++latestQueryToken;\n if (value.trim().length < 2) {\n setResults([]);\n return;\n }\n props.onSearch(value).then((found) => {\n if (token !== latestQueryToken) return;\n setResults(found);\n setActiveIndex(0);\n });\n }\n\n function onInput(value: string) {\n setQuery(value);\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => runSearch(value), DEBOUNCE_MS);\n }\n\n function selectResult(result: SearchPaletteResult | undefined) {\n if (!result) return;\n props.onSelect(result);\n close();\n }\n\n // Global Cmd+K / Ctrl+K listener — registered once, independent of\n // `open` (unlike PanelNav's Escape/Tab handling, which only needs to\n // exist while open).\n createEffect(() => {\n function onGlobalKeyDown(event: KeyboardEvent) {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === \"k\") {\n event.preventDefault();\n triggeredBy = document.activeElement as HTMLElement | null;\n setOpen(true);\n }\n }\n document.addEventListener(\"keydown\", onGlobalKeyDown);\n onCleanup(() => document.removeEventListener(\"keydown\", onGlobalKeyDown));\n });\n\n createEffect(() => {\n if (open()) inputRef?.focus();\n });\n\n function onDialogKeyDown(event: KeyboardEvent) {\n if (event.key === \"Escape\") {\n close();\n return;\n }\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n setActiveIndex((i) => Math.min(i + 1, results().length - 1));\n return;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((i) => Math.max(i - 1, 0));\n return;\n }\n if (event.key === \"Enter\") {\n event.preventDefault();\n selectResult(results()[activeIndex()]);\n }\n }\n\n return (\n <Show when={open()}>\n <div\n aria-hidden=\"true\"\n class=\"fixed inset-0 z-50 flex items-start justify-center bg-[var(--color-backdrop)] px-4 pt-[15vh]\"\n onClick={close}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Search\"\n class=\"w-full max-w-lg overflow-hidden rounded-2xl border border-[var(--line)] bg-[var(--surface-strong)] shadow-2xl\"\n onClick={(event) => event.stopPropagation()}\n onKeyDown={onDialogKeyDown}\n >\n <div class=\"flex items-center gap-2 border-b border-[var(--line)] px-4 py-3\">\n <i\n class=\"ph ph-magnifying-glass text-lg text-[var(--sea-ink-soft)]\"\n aria-hidden=\"true\"\n />\n <input\n ref={inputRef}\n type=\"text\"\n value={query()}\n onInput={(event) => onInput(event.currentTarget.value)}\n placeholder=\"Search…\"\n aria-label=\"Search\"\n class=\"flex-1 bg-transparent text-base text-[var(--sea-ink)] outline-none placeholder:text-[var(--sea-ink-soft)]\"\n />\n <kbd class=\"rounded border border-[var(--chip-line)] bg-[var(--chip-bg)] px-1.5 py-0.5 text-xs text-[var(--sea-ink-soft)]\">\n Esc\n </kbd>\n </div>\n\n <Show\n when={results().length > 0}\n fallback={\n <p class=\"px-4 py-6 text-center text-sm text-[var(--sea-ink-soft)]\">\n {query().trim().length < 2\n ? \"Keep typing to search…\"\n : \"No results\"}\n </p>\n }\n >\n <ul class=\"max-h-80 overflow-y-auto py-2\">\n <For each={results()}>\n {(result, index) => (\n <li>\n <button\n type=\"button\"\n onClick={() => selectResult(result)}\n onMouseEnter={() => setActiveIndex(index())}\n class=\"flex w-full items-center justify-between gap-3 px-4 py-2 text-left text-sm text-[var(--sea-ink)] hover:bg-[var(--link-bg-hover)]\"\n classList={{\n \"bg-[var(--link-bg-hover)]\": activeIndex() === index(),\n }}\n >\n <span class=\"truncate\">{result.label}</span>\n <span class=\"shrink-0 text-xs text-[var(--sea-ink-soft)]\">\n {capitalize(result.collection)}\n </span>\n </button>\n </li>\n )}\n </For>\n </ul>\n </Show>\n </div>\n </div>\n </Show>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
import { createComponent, escape, ssr, ssrAttribute } from "solid-js/web";
|
|
2
|
+
import { For, Show, Suspense, createEffect, createSignal, lazy, onCleanup } from "solid-js";
|
|
3
|
+
//#region src/CollectionEdit.tsx
|
|
4
|
+
var _tmpl$$2 = ["<p class=\"text-sm text-error\" role=\"alert\">", "</p>"], _tmpl$2$2 = "<span class=\"loading loading-spinner loading-sm\"></span>", _tmpl$3$2 = [
|
|
5
|
+
"<button type=\"button\" class=\"btn flex-1\"",
|
|
6
|
+
">",
|
|
7
|
+
"</button>"
|
|
8
|
+
], _tmpl$4$2 = [
|
|
9
|
+
"<button type=\"button\" class=\"btn btn-primary flex-1\"",
|
|
10
|
+
">",
|
|
11
|
+
"</button>"
|
|
12
|
+
], _tmpl$5$1 = [
|
|
13
|
+
"<button type=\"button\" class=\"btn btn-outline flex-1\"",
|
|
14
|
+
">",
|
|
15
|
+
"</button>"
|
|
16
|
+
], _tmpl$6$1 = [
|
|
17
|
+
"<form class=\"flex flex-col gap-4\">",
|
|
18
|
+
"",
|
|
19
|
+
"<div class=\"bg-base-100 sticky bottom-0 flex gap-2 border-t py-3\">",
|
|
20
|
+
"</div></form>"
|
|
21
|
+
], _tmpl$7$1 = "<span class=\"text-error\"> *</span>", _tmpl$8$1 = [
|
|
22
|
+
"<div class=\"form-control\"><label class=\"label\"",
|
|
23
|
+
">",
|
|
24
|
+
"",
|
|
25
|
+
"</label>",
|
|
26
|
+
"</div>"
|
|
27
|
+
], _tmpl$9$1 = [
|
|
28
|
+
"<button type=\"submit\" class=\"btn btn-primary flex-1\"",
|
|
29
|
+
">",
|
|
30
|
+
"</button>"
|
|
31
|
+
], _tmpl$0$1 = [
|
|
32
|
+
"<input",
|
|
33
|
+
" class=\"input\" type=\"text\"",
|
|
34
|
+
"",
|
|
35
|
+
">"
|
|
36
|
+
], _tmpl$1$1 = [
|
|
37
|
+
"<select",
|
|
38
|
+
" class=\"select\"",
|
|
39
|
+
"",
|
|
40
|
+
">",
|
|
41
|
+
"</select>"
|
|
42
|
+
], _tmpl$10$1 = [
|
|
43
|
+
"<option",
|
|
44
|
+
">",
|
|
45
|
+
"</option>"
|
|
46
|
+
], _tmpl$11$1 = [
|
|
47
|
+
"<input",
|
|
48
|
+
" class=\"input\" type=\"number\"",
|
|
49
|
+
"",
|
|
50
|
+
">"
|
|
51
|
+
], _tmpl$12$1 = [
|
|
52
|
+
"<input",
|
|
53
|
+
" class=\"input\" type=\"text\" readonly",
|
|
54
|
+
">"
|
|
55
|
+
], _tmpl$13$1 = [
|
|
56
|
+
"<input",
|
|
57
|
+
" class=\"checkbox\" type=\"checkbox\"",
|
|
58
|
+
">"
|
|
59
|
+
], _tmpl$14$1 = ["<p class=\"text-sm opacity-70 break-all\">", "</p>"], _tmpl$15 = ["<p class=\"text-sm text-error\">", "</p>"], _tmpl$16 = [
|
|
60
|
+
"<div class=\"flex flex-col gap-2\">",
|
|
61
|
+
"<input",
|
|
62
|
+
" class=\"file-input\" type=\"file\"",
|
|
63
|
+
"",
|
|
64
|
+
">",
|
|
65
|
+
"",
|
|
66
|
+
"</div>"
|
|
67
|
+
], _tmpl$17 = [
|
|
68
|
+
"<select",
|
|
69
|
+
" class=\"select\"",
|
|
70
|
+
"",
|
|
71
|
+
"><option value>—</option>",
|
|
72
|
+
"</select>"
|
|
73
|
+
], _tmpl$18 = [
|
|
74
|
+
"<div class=\"flex flex-col gap-3\">",
|
|
75
|
+
"<button type=\"button\" class=\"btn btn-outline btn-sm self-start\">Add ",
|
|
76
|
+
"</button></div>"
|
|
77
|
+
], _tmpl$19 = ["<div class=\"card bg-base-200 flex flex-col gap-2 p-3\">", "<button type=\"button\" class=\"btn btn-error btn-outline btn-sm self-start\">Remove</button></div>"];
|
|
78
|
+
const RichTextEditor = lazy(() => import("../RichTextEditor-DcLqdFY7.js").then((mod) => ({ default: mod.RichTextEditor })));
|
|
79
|
+
function editableFields(config) {
|
|
80
|
+
return Object.entries(config.fields).filter(([key]) => key !== "id");
|
|
81
|
+
}
|
|
82
|
+
function CollectionEdit(props) {
|
|
83
|
+
const initialSnapshot = JSON.stringify(props.initialValues ?? {});
|
|
84
|
+
const [values, setValues] = createSignal(props.initialValues ?? {});
|
|
85
|
+
createEffect(() => {
|
|
86
|
+
props.onDirtyChange?.(JSON.stringify(values()) !== initialSnapshot);
|
|
87
|
+
});
|
|
88
|
+
function setField(key, value) {
|
|
89
|
+
setValues((prev) => ({
|
|
90
|
+
...prev,
|
|
91
|
+
[key]: value
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
const ctx = {
|
|
95
|
+
onUploadFile: props.onUploadFile,
|
|
96
|
+
relationshipOptions: props.relationshipOptions
|
|
97
|
+
};
|
|
98
|
+
const versioned = () => props.config.versions?.drafts && props.draftActions;
|
|
99
|
+
return ssr(_tmpl$6$1, escape(createComponent(Show, {
|
|
100
|
+
get when() {
|
|
101
|
+
return props.error;
|
|
102
|
+
},
|
|
103
|
+
get children() {
|
|
104
|
+
return ssr(_tmpl$$2, escape(props.error));
|
|
105
|
+
}
|
|
106
|
+
})), escape(createComponent(For, {
|
|
107
|
+
get each() {
|
|
108
|
+
return editableFields(props.config);
|
|
109
|
+
},
|
|
110
|
+
children: ([key, field]) => ssr(_tmpl$8$1, ssrAttribute("for", escape(key, true), false), escape(key), escape(createComponent(Show, {
|
|
111
|
+
get when() {
|
|
112
|
+
return field.required;
|
|
113
|
+
},
|
|
114
|
+
get children() {
|
|
115
|
+
return ssr(_tmpl$7$1);
|
|
116
|
+
}
|
|
117
|
+
})), escape(renderInput(key, field, values()[key], setField, ctx)))
|
|
118
|
+
})), escape(createComponent(Show, {
|
|
119
|
+
get when() {
|
|
120
|
+
return versioned();
|
|
121
|
+
},
|
|
122
|
+
get fallback() {
|
|
123
|
+
return createComponent(Show, {
|
|
124
|
+
get when() {
|
|
125
|
+
return props.capabilities?.canUpdate !== false;
|
|
126
|
+
},
|
|
127
|
+
get children() {
|
|
128
|
+
return ssr(_tmpl$9$1, ssrAttribute("disabled", props.saving, true), escape(createComponent(Show, {
|
|
129
|
+
get when() {
|
|
130
|
+
return props.saving;
|
|
131
|
+
},
|
|
132
|
+
get fallback() {
|
|
133
|
+
return props.submitLabel ?? "Save";
|
|
134
|
+
},
|
|
135
|
+
get children() {
|
|
136
|
+
return ssr(_tmpl$2$2);
|
|
137
|
+
}
|
|
138
|
+
})));
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
get children() {
|
|
143
|
+
return [
|
|
144
|
+
ssr(_tmpl$3$2, ssrAttribute("disabled", props.draftActions?.saving, true), escape(createComponent(Show, {
|
|
145
|
+
get when() {
|
|
146
|
+
return props.draftActions?.saving;
|
|
147
|
+
},
|
|
148
|
+
get fallback() {
|
|
149
|
+
return props.draftActions?.saveDraftLabel ?? "Save draft";
|
|
150
|
+
},
|
|
151
|
+
get children() {
|
|
152
|
+
return ssr(_tmpl$2$2);
|
|
153
|
+
}
|
|
154
|
+
}))),
|
|
155
|
+
ssr(_tmpl$4$2, ssrAttribute("disabled", !props.draftActions?.canPublish || props.draftActions?.publishing, true), escape(createComponent(Show, {
|
|
156
|
+
get when() {
|
|
157
|
+
return props.draftActions?.publishing;
|
|
158
|
+
},
|
|
159
|
+
get fallback() {
|
|
160
|
+
return props.draftActions?.publishLabel ?? "Publish";
|
|
161
|
+
},
|
|
162
|
+
get children() {
|
|
163
|
+
return ssr(_tmpl$2$2);
|
|
164
|
+
}
|
|
165
|
+
}))),
|
|
166
|
+
createComponent(Show, {
|
|
167
|
+
get when() {
|
|
168
|
+
return props.draftActions?.onPreview;
|
|
169
|
+
},
|
|
170
|
+
get children() {
|
|
171
|
+
return ssr(_tmpl$5$1, ssrAttribute("disabled", !props.draftActions?.canPreview || props.draftActions?.previewing, true), escape(createComponent(Show, {
|
|
172
|
+
get when() {
|
|
173
|
+
return props.draftActions?.previewing;
|
|
174
|
+
},
|
|
175
|
+
get fallback() {
|
|
176
|
+
return props.draftActions?.previewLabel ?? "Preview";
|
|
177
|
+
},
|
|
178
|
+
get children() {
|
|
179
|
+
return ssr(_tmpl$2$2);
|
|
180
|
+
}
|
|
181
|
+
})));
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
];
|
|
185
|
+
}
|
|
186
|
+
})));
|
|
187
|
+
}
|
|
188
|
+
function renderInput(key, field, value, setField, ctx) {
|
|
189
|
+
switch (field.type) {
|
|
190
|
+
case "text": return ssr(_tmpl$0$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(value ?? "", true), false), ssrAttribute("required", field.required, true));
|
|
191
|
+
case "select": return ssr(_tmpl$1$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(value ?? "", true), false), ssrAttribute("required", field.required, true), escape(createComponent(For, {
|
|
192
|
+
get each() {
|
|
193
|
+
return field.options;
|
|
194
|
+
},
|
|
195
|
+
children: (option) => ssr(_tmpl$10$1, ssrAttribute("value", escape(option, true), false), escape(option))
|
|
196
|
+
})));
|
|
197
|
+
case "number": return ssr(_tmpl$11$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(value ?? "", true), false), ssrAttribute("required", field.required, true));
|
|
198
|
+
case "date": return ssr(_tmpl$12$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", escape(formatDateValue(value), true), false));
|
|
199
|
+
case "checkbox": return ssr(_tmpl$13$1, ssrAttribute("id", escape(key, true), false), ssrAttribute("checked", value ?? false, true));
|
|
200
|
+
case "upload": return renderUploadInput(key, field, value, setField, ctx);
|
|
201
|
+
case "relationship": return renderRelationshipInput(key, field, value, setField, ctx);
|
|
202
|
+
case "array": return renderArrayInput(key, field, value, setField, ctx);
|
|
203
|
+
case "richText": return createComponent(Suspense, {
|
|
204
|
+
get fallback() {
|
|
205
|
+
return ssr(_tmpl$2$2);
|
|
206
|
+
},
|
|
207
|
+
get children() {
|
|
208
|
+
return createComponent(RichTextEditor, {
|
|
209
|
+
id: key,
|
|
210
|
+
content: value,
|
|
211
|
+
onChange: (doc) => setField(key, doc)
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
default: return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function renderUploadInput(key, field, value, setField, ctx) {
|
|
219
|
+
const [uploading, setUploading] = createSignal(false);
|
|
220
|
+
const [uploadError, setUploadError] = createSignal();
|
|
221
|
+
return ssr(_tmpl$16, escape(createComponent(Show, {
|
|
222
|
+
when: value,
|
|
223
|
+
get children() {
|
|
224
|
+
return ssr(_tmpl$14$1, escape(value));
|
|
225
|
+
}
|
|
226
|
+
})), ssrAttribute("id", escape(key, true), false), ssrAttribute("required", field.required && !value, true), ssrAttribute("disabled", uploading(), true), escape(createComponent(Show, {
|
|
227
|
+
get when() {
|
|
228
|
+
return uploading();
|
|
229
|
+
},
|
|
230
|
+
get children() {
|
|
231
|
+
return ssr(_tmpl$2$2);
|
|
232
|
+
}
|
|
233
|
+
})), escape(createComponent(Show, {
|
|
234
|
+
get when() {
|
|
235
|
+
return uploadError();
|
|
236
|
+
},
|
|
237
|
+
get children() {
|
|
238
|
+
return ssr(_tmpl$15, escape(uploadError()));
|
|
239
|
+
}
|
|
240
|
+
})));
|
|
241
|
+
}
|
|
242
|
+
function renderRelationshipInput(key, field, value, setField, ctx) {
|
|
243
|
+
if (field.hasMany) return null;
|
|
244
|
+
const options = ctx.relationshipOptions?.[field.relationTo] ?? [];
|
|
245
|
+
return ssr(_tmpl$17, ssrAttribute("id", escape(key, true), false), ssrAttribute("value", value != null ? escape(String(value), true) : "", false), ssrAttribute("required", field.required, true), escape(createComponent(For, {
|
|
246
|
+
each: options,
|
|
247
|
+
children: (option) => ssr(_tmpl$10$1, ssrAttribute("value", escape(option.id, true), false), escape(option.label))
|
|
248
|
+
})));
|
|
249
|
+
}
|
|
250
|
+
function renderArrayInput(key, field, value, setField, ctx) {
|
|
251
|
+
const items = () => Array.isArray(value) ? value : [];
|
|
252
|
+
function updateItem(index, itemKey, itemValue) {
|
|
253
|
+
const next = items().slice();
|
|
254
|
+
next[index] = {
|
|
255
|
+
...next[index],
|
|
256
|
+
[itemKey]: itemValue
|
|
257
|
+
};
|
|
258
|
+
setField(key, next);
|
|
259
|
+
}
|
|
260
|
+
function fieldsForItem(item) {
|
|
261
|
+
const base = Object.entries(field.fields);
|
|
262
|
+
const discriminator = field.discriminator;
|
|
263
|
+
if (!discriminator) return base;
|
|
264
|
+
const variantValue = item[discriminator.key];
|
|
265
|
+
const variantFields = typeof variantValue === "string" ? discriminator.variants[variantValue] : void 0;
|
|
266
|
+
return variantFields ? [...base, ...Object.entries(variantFields)] : base;
|
|
267
|
+
}
|
|
268
|
+
return ssr(_tmpl$18, escape(createComponent(For, {
|
|
269
|
+
get each() {
|
|
270
|
+
return items();
|
|
271
|
+
},
|
|
272
|
+
children: (item, index) => ssr(_tmpl$19, escape(createComponent(For, {
|
|
273
|
+
get each() {
|
|
274
|
+
return fieldsForItem(item);
|
|
275
|
+
},
|
|
276
|
+
children: ([itemKey, itemField]) => {
|
|
277
|
+
const inputId = `${key}.${index()}.${itemKey}`;
|
|
278
|
+
return ssr(_tmpl$8$1, ssrAttribute("for", escape(inputId, true), false), escape(itemKey), escape(createComponent(Show, {
|
|
279
|
+
get when() {
|
|
280
|
+
return itemField.required;
|
|
281
|
+
},
|
|
282
|
+
get children() {
|
|
283
|
+
return ssr(_tmpl$7$1);
|
|
284
|
+
}
|
|
285
|
+
})), escape(renderInput(inputId, itemField, item[itemKey], (_, v) => updateItem(index(), itemKey, v), ctx)));
|
|
286
|
+
}
|
|
287
|
+
})))
|
|
288
|
+
})), escape(key));
|
|
289
|
+
}
|
|
290
|
+
function formatDateValue(value) {
|
|
291
|
+
if (!value) return "—";
|
|
292
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
293
|
+
return Number.isNaN(date.getTime()) ? "—" : date.toLocaleString();
|
|
294
|
+
}
|
|
295
|
+
//#endregion
|
|
296
|
+
//#region src/CollectionList.tsx
|
|
297
|
+
var _tmpl$$1 = ["<button type=\"button\" class=\"btn btn-outline btn-sm\">", "</button>"], _tmpl$2$1 = [
|
|
298
|
+
"<div class=\"join\"><select aria-label=\"Sort by\" class=\"select select-sm join-item\"",
|
|
299
|
+
">",
|
|
300
|
+
"</select><select aria-label=\"Sort direction\" class=\"select select-sm join-item\"",
|
|
301
|
+
"><option value=\"asc\">Ascending</option><option value=\"desc\">Descending</option></select></div>"
|
|
302
|
+
], _tmpl$3$1 = "<th></th>", _tmpl$4$1 = [
|
|
303
|
+
"<table class=\"table hidden md:table\"><thead><tr>",
|
|
304
|
+
"",
|
|
305
|
+
"</tr></thead><tbody>",
|
|
306
|
+
"</tbody></table>"
|
|
307
|
+
], _tmpl$5 = ["<div class=\"flex flex-col gap-2 md:hidden\">", "</div>"], _tmpl$6 = [
|
|
308
|
+
"<div class=\"bg-base-100 sticky bottom-0 flex items-center justify-between gap-2 border-t py-2\"><button type=\"button\" class=\"btn btn-sm\"",
|
|
309
|
+
">Prev</button><span class=\"text-sm opacity-70\">Page ",
|
|
310
|
+
"</span><button type=\"button\" class=\"btn btn-sm\"",
|
|
311
|
+
">Next</button></div>"
|
|
312
|
+
], _tmpl$7 = [
|
|
313
|
+
"<div class=\"flex flex-col gap-3\"><div class=\"flex flex-wrap items-center justify-between gap-2\">",
|
|
314
|
+
"",
|
|
315
|
+
"</div>",
|
|
316
|
+
"",
|
|
317
|
+
"</div>"
|
|
318
|
+
], _tmpl$8 = [
|
|
319
|
+
"<option",
|
|
320
|
+
">",
|
|
321
|
+
"</option>"
|
|
322
|
+
], _tmpl$9 = ["<p class=\"text-sm opacity-70\">No ", " yet.</p>"], _tmpl$0 = ["<th>", "</th>"], _tmpl$1 = ["<td><input type=\"checkbox\" class=\"checkbox checkbox-sm\"", "></td>"], _tmpl$10 = [
|
|
323
|
+
"<tr",
|
|
324
|
+
">",
|
|
325
|
+
"",
|
|
326
|
+
"</tr>"
|
|
327
|
+
], _tmpl$11 = ["<td>", "</td>"], _tmpl$12 = ["<input type=\"checkbox\" class=\"checkbox checkbox-sm mt-1\"", ">"], _tmpl$13 = [
|
|
328
|
+
"<div class=\"card bg-base-200 cursor-pointer p-3\" role=\"button\" tabindex=\"0\"><div class=\"flex items-start gap-3\">",
|
|
329
|
+
"<div class=\"flex flex-1 flex-col gap-1\">",
|
|
330
|
+
"</div></div></div>"
|
|
331
|
+
], _tmpl$14 = [
|
|
332
|
+
"<div class=\"flex justify-between gap-2 text-sm\"><span class=\"opacity-60\">",
|
|
333
|
+
"</span><span class=\"text-right\">",
|
|
334
|
+
"</span></div>"
|
|
335
|
+
];
|
|
336
|
+
function listableFields(config) {
|
|
337
|
+
const excluded = new Set([
|
|
338
|
+
"richText",
|
|
339
|
+
"array",
|
|
340
|
+
"relationship"
|
|
341
|
+
]);
|
|
342
|
+
return Object.entries(config.fields).filter(([key, field]) => key !== "id" && !excluded.has(field.type));
|
|
343
|
+
}
|
|
344
|
+
function formatCellValue(value) {
|
|
345
|
+
if (value === null || value === void 0) return "—";
|
|
346
|
+
if (value instanceof Date) return value.toLocaleDateString();
|
|
347
|
+
return String(value);
|
|
348
|
+
}
|
|
349
|
+
function rowId(row) {
|
|
350
|
+
return typeof row.id === "number" ? row.id : void 0;
|
|
351
|
+
}
|
|
352
|
+
function CollectionList(props) {
|
|
353
|
+
const columns = () => listableFields(props.config);
|
|
354
|
+
const [selectMode, setSelectMode] = createSignal(false);
|
|
355
|
+
return ssr(_tmpl$7, escape(createComponent(Show, {
|
|
356
|
+
get when() {
|
|
357
|
+
return props.selectable;
|
|
358
|
+
},
|
|
359
|
+
get children() {
|
|
360
|
+
return ssr(_tmpl$$1, selectMode() ? "Done" : "Select");
|
|
361
|
+
}
|
|
362
|
+
})), escape(createComponent(Show, {
|
|
363
|
+
get when() {
|
|
364
|
+
return props.onSortChange;
|
|
365
|
+
},
|
|
366
|
+
get children() {
|
|
367
|
+
return ssr(_tmpl$2$1, ssrAttribute("value", escape(props.sortField ?? "", true), false), escape(createComponent(For, {
|
|
368
|
+
get each() {
|
|
369
|
+
return columns();
|
|
370
|
+
},
|
|
371
|
+
children: ([key]) => ssr(_tmpl$8, ssrAttribute("value", escape(key, true), false), escape(key))
|
|
372
|
+
})), ssrAttribute("value", escape(props.sortDirection ?? "asc", true), false));
|
|
373
|
+
}
|
|
374
|
+
})), escape(createComponent(Show, {
|
|
375
|
+
get when() {
|
|
376
|
+
return props.rows.length > 0;
|
|
377
|
+
},
|
|
378
|
+
get fallback() {
|
|
379
|
+
return ssr(_tmpl$9, escape(props.config.slug));
|
|
380
|
+
},
|
|
381
|
+
get children() {
|
|
382
|
+
return [ssr(_tmpl$4$1, escape(createComponent(Show, {
|
|
383
|
+
get when() {
|
|
384
|
+
return selectMode();
|
|
385
|
+
},
|
|
386
|
+
get children() {
|
|
387
|
+
return ssr(_tmpl$3$1);
|
|
388
|
+
}
|
|
389
|
+
})), escape(createComponent(For, {
|
|
390
|
+
get each() {
|
|
391
|
+
return columns();
|
|
392
|
+
},
|
|
393
|
+
children: ([key]) => ssr(_tmpl$0, escape(key))
|
|
394
|
+
})), escape(createComponent(For, {
|
|
395
|
+
get each() {
|
|
396
|
+
return props.rows;
|
|
397
|
+
},
|
|
398
|
+
children: (row) => ssr(_tmpl$10, ssrAttribute("class", props.onRowClick || selectMode() ? "cursor-pointer hover" : escape(void 0, true), false), escape(createComponent(Show, {
|
|
399
|
+
get when() {
|
|
400
|
+
return selectMode();
|
|
401
|
+
},
|
|
402
|
+
get children() {
|
|
403
|
+
return ssr(_tmpl$1, ssrAttribute("checked", rowId(row) !== void 0 && (props.selectedIds?.has(rowId(row)) ?? false), true));
|
|
404
|
+
}
|
|
405
|
+
})), escape(createComponent(For, {
|
|
406
|
+
get each() {
|
|
407
|
+
return columns();
|
|
408
|
+
},
|
|
409
|
+
children: ([key]) => ssr(_tmpl$11, escape(formatCellValue(row[key])))
|
|
410
|
+
})))
|
|
411
|
+
}))), ssr(_tmpl$5, escape(createComponent(For, {
|
|
412
|
+
get each() {
|
|
413
|
+
return props.rows;
|
|
414
|
+
},
|
|
415
|
+
children: (row) => ssr(_tmpl$13, escape(createComponent(Show, {
|
|
416
|
+
get when() {
|
|
417
|
+
return selectMode();
|
|
418
|
+
},
|
|
419
|
+
get children() {
|
|
420
|
+
return ssr(_tmpl$12, ssrAttribute("checked", rowId(row) !== void 0 && (props.selectedIds?.has(rowId(row)) ?? false), true));
|
|
421
|
+
}
|
|
422
|
+
})), escape(createComponent(For, {
|
|
423
|
+
get each() {
|
|
424
|
+
return columns();
|
|
425
|
+
},
|
|
426
|
+
children: ([key]) => ssr(_tmpl$14, escape(key), escape(formatCellValue(row[key])))
|
|
427
|
+
})))
|
|
428
|
+
})))];
|
|
429
|
+
}
|
|
430
|
+
})), escape(createComponent(Show, {
|
|
431
|
+
get when() {
|
|
432
|
+
return props.page !== void 0 && props.pageSize !== void 0;
|
|
433
|
+
},
|
|
434
|
+
get children() {
|
|
435
|
+
return ssr(_tmpl$6, ssrAttribute("disabled", (props.page ?? 1) <= 1, true), escape(props.page), ssrAttribute("disabled", props.totalCount !== void 0 ? (props.page ?? 1) * (props.pageSize ?? 0) >= props.totalCount : props.rows.length < (props.pageSize ?? 0), true));
|
|
436
|
+
}
|
|
437
|
+
})));
|
|
438
|
+
}
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region src/SearchPalette.tsx
|
|
441
|
+
var _tmpl$ = ["<ul class=\"max-h-80 overflow-y-auto py-2\">", "</ul>"], _tmpl$2 = [
|
|
442
|
+
"<div aria-hidden=\"true\" class=\"fixed inset-0 z-50 flex items-start justify-center bg-[var(--color-backdrop)] px-4 pt-[15vh]\"><div role=\"dialog\" aria-modal=\"true\" aria-label=\"Search\" class=\"w-full max-w-lg overflow-hidden rounded-2xl border border-[var(--line)] bg-[var(--surface-strong)] shadow-2xl\"><div class=\"flex items-center gap-2 border-b border-[var(--line)] px-4 py-3\"><i class=\"ph ph-magnifying-glass text-lg text-[var(--sea-ink-soft)]\" aria-hidden=\"true\"></i><input type=\"text\"",
|
|
443
|
+
" placeholder=\"Search…\" aria-label=\"Search\" class=\"flex-1 bg-transparent text-base text-[var(--sea-ink)] outline-none placeholder:text-[var(--sea-ink-soft)]\"><kbd class=\"rounded border border-[var(--chip-line)] bg-[var(--chip-bg)] px-1.5 py-0.5 text-xs text-[var(--sea-ink-soft)]\">Esc</kbd></div>",
|
|
444
|
+
"</div></div>"
|
|
445
|
+
], _tmpl$3 = ["<p class=\"px-4 py-6 text-center text-sm text-[var(--sea-ink-soft)]\">", "</p>"], _tmpl$4 = [
|
|
446
|
+
"<li><button type=\"button\" class=\"",
|
|
447
|
+
"\"><span class=\"truncate\">",
|
|
448
|
+
"</span><span class=\"shrink-0 text-xs text-[var(--sea-ink-soft)]\">",
|
|
449
|
+
"</span></button></li>"
|
|
450
|
+
];
|
|
451
|
+
function capitalize(value) {
|
|
452
|
+
return value.length === 0 ? value : value[0].toUpperCase() + value.slice(1);
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Self-contained Cmd+K (Ctrl+K on non-Mac) search palette — issue #29.
|
|
456
|
+
* Owns its own open/closed state and keyboard listener; the host app only
|
|
457
|
+
* supplies `onSearch` (wired to a server function that fans out across
|
|
458
|
+
* every collection with `search` configured) and `onSelect` (navigation).
|
|
459
|
+
* Mirrors PanelNav's focus-trap-on-open / restore-focus-on-close pattern
|
|
460
|
+
* rather than introducing a second one.
|
|
461
|
+
*/
|
|
462
|
+
function SearchPalette(props) {
|
|
463
|
+
const [open, setOpen] = createSignal(false);
|
|
464
|
+
const [query, setQuery] = createSignal("");
|
|
465
|
+
const [results, setResults] = createSignal([]);
|
|
466
|
+
const [activeIndex, setActiveIndex] = createSignal(0);
|
|
467
|
+
createEffect(() => {
|
|
468
|
+
function onGlobalKeyDown(event) {
|
|
469
|
+
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
|
470
|
+
event.preventDefault();
|
|
471
|
+
document.activeElement;
|
|
472
|
+
setOpen(true);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
document.addEventListener("keydown", onGlobalKeyDown);
|
|
476
|
+
onCleanup(() => document.removeEventListener("keydown", onGlobalKeyDown));
|
|
477
|
+
});
|
|
478
|
+
createEffect(() => {
|
|
479
|
+
if (open());
|
|
480
|
+
});
|
|
481
|
+
return createComponent(Show, {
|
|
482
|
+
get when() {
|
|
483
|
+
return open();
|
|
484
|
+
},
|
|
485
|
+
get children() {
|
|
486
|
+
return ssr(_tmpl$2, ssrAttribute("value", escape(query(), true), false), escape(createComponent(Show, {
|
|
487
|
+
get when() {
|
|
488
|
+
return results().length > 0;
|
|
489
|
+
},
|
|
490
|
+
get fallback() {
|
|
491
|
+
return ssr(_tmpl$3, query().trim().length < 2 ? "Keep typing to search…" : "No results");
|
|
492
|
+
},
|
|
493
|
+
get children() {
|
|
494
|
+
return ssr(_tmpl$, escape(createComponent(For, {
|
|
495
|
+
get each() {
|
|
496
|
+
return results();
|
|
497
|
+
},
|
|
498
|
+
children: (result, index) => ssr(_tmpl$4, `flex w-full items-center justify-between gap-3 px-4 py-2 text-left text-sm text-[var(--sea-ink)] hover:bg-[var(--link-bg-hover)] ${activeIndex() === index() ? "bg-[var(--link-bg-hover)]" : ""}`, escape(result.label), escape(capitalize(result.collection)))
|
|
499
|
+
})));
|
|
500
|
+
}
|
|
501
|
+
})));
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
//#endregion
|
|
506
|
+
export { CollectionEdit, CollectionList, SearchPalette };
|
|
507
|
+
|
|
508
|
+
//# sourceMappingURL=server.js.map
|