@marktiderman/genesis-ui 2.8.0 → 2.10.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/SKILL.md +75 -4
- package/dist/{AppShell-LXQykEqe.d.ts → AppShell-BVr4V0UK.d.ts} +1 -110
- package/dist/PageHeader-V9lVkZtF.d.ts +114 -0
- package/dist/{chunk-S7NXD37O.js → chunk-25XEXYGC.js} +4 -4
- package/dist/chunk-25XEXYGC.js.map +1 -0
- package/dist/chunk-C7PQ54JI.js +763 -0
- package/dist/chunk-C7PQ54JI.js.map +1 -0
- package/dist/chunk-DIV4BYXX.js +58 -0
- package/dist/chunk-DIV4BYXX.js.map +1 -0
- package/dist/chunk-I4NBE2KL.js +16 -0
- package/dist/chunk-I4NBE2KL.js.map +1 -0
- package/dist/{chunk-DWCAC2E7.js → chunk-RKPS7ERS.js} +5 -4
- package/dist/chunk-RKPS7ERS.js.map +1 -0
- package/dist/components/data/index.d.ts +42 -949
- package/dist/components/data/index.js +962 -777
- package/dist/components/data/index.js.map +1 -1
- package/dist/components/layout/index.d.ts +2 -1
- package/dist/components/layout/index.js +7 -7
- package/dist/components/patterns/index.js +2 -1
- package/dist/components/patterns/sortable-list.js +2 -1
- package/dist/hooks/index.d.ts +8 -1
- package/dist/hooks/index.js +4 -1
- package/dist/hooks/use-resource-page.d.ts +1 -1
- package/dist/hooks/use-resource-page.js +1 -1
- package/dist/hooks/use-resource-url-state.d.ts +68 -0
- package/dist/hooks/use-resource-url-state.js +3 -0
- package/dist/hooks/use-resource-url-state.js.map +1 -0
- package/dist/index.js +21 -20
- package/dist/{use-resource-page-D4byuwAs.d.ts → use-resource-page-DaJzuq3M.d.ts} +63 -2
- package/dist/use-resource-table-DCVf_lEG.d.ts +1113 -0
- package/package.json +4 -8
- package/dist/chunk-DWCAC2E7.js.map +0 -1
- package/dist/chunk-S7NXD37O.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/data/undo-toast.ts","../src/components/data/use-resource-table.ts"],"names":[],"mappings":"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","file":"chunk-C7PQ54JI.js","sourcesContent":["import type { MutationMode, UndoableWrite } from \"@marktiderman/genesis-core/hooks\";\nimport { toast } from \"../ui/sonner\";\n\nconst DONE: Record<UndoableWrite[\"kind\"], string> = {\n create: \"Created.\",\n update: \"Saved.\",\n delete: \"Deleted.\",\n};\n\n/**\n * The undo toast for an `undoable` write (core's `onUndoable`): a sonner\n * toast with an Undo button, shown for as long as the undo is offered.\n * Needs a mounted `<Toaster />`.\n */\nexport function showUndoToast(write: UndoableWrite): void {\n toast(DONE[write.kind], {\n duration: write.delayMs,\n action: {\n label: \"Undo\",\n onClick: () => {\n void write.undo().then((undone) => {\n // Sonner pauses on hover, so the button can outlive the window.\n if (!undone && write.strategy === \"delay\") {\n toast(\"Already saved. It can no longer be undone.\");\n }\n });\n },\n },\n });\n}\n\n/** The default error report for writes that fail after the form has closed. */\nexport function showWriteErrorToast(error: Error): void {\n toast.error(error.message || \"The change could not be saved.\");\n}\n\n/**\n * ResourcePage's write-mode options for its `useResource`: the page's\n * `mutationMode` unless `resourceOptions` sets one, the undo toast, and an\n * error toast for a write that fails after it was shown as done.\n */\nexport function writeModeOptions(\n resourceOptions: { mutationMode?: MutationMode; onUndoable?: (write: UndoableWrite) => void; onError?: (error: Error) => void } | undefined,\n mutationMode: MutationMode | undefined,\n): { mutationMode: MutationMode; onUndoable: (write: UndoableWrite) => void; onError?: (error: Error) => void } {\n const mode = resourceOptions?.mutationMode ?? mutationMode ?? \"pessimistic\";\n return {\n mutationMode: mode,\n onUndoable: resourceOptions?.onUndoable ?? showUndoToast,\n onError: resourceOptions?.onError ?? (mode === \"pessimistic\" ? undefined : showWriteErrorToast),\n };\n}\n","\"use client\";\n\n/**\n * `useResourceTable`: ResourcePage's behaviour without its markup. Lives\n * apart from ResourcePage.tsx so importing the hook (e.g. from `/hooks`)\n * does not pull in the page's components and icons.\n */\nimport { useMemo, useState, useCallback, useEffect, useRef } from \"react\";\nimport type { DataTableColumn, DataTableSort } from \"./DataTable\";\nimport type {\n ResourceAction,\n ResourceDeleteConfirmCopy,\n ResourcePageProps,\n} from \"./ResourcePage\";\nimport { useViewSettings } from \"../../hooks/use-view-settings\";\nimport { useKeyboardNavigation } from \"../../hooks/use-keyboard-navigation\";\nimport { useResourceUrlState } from \"../../hooks/use-resource-url-state\";\nimport {\n useResourcePage,\n type ResourceActions,\n} from \"../../hooks/use-resource-page\";\nimport {\n useCapabilities,\n useOne,\n useResource,\n} from \"@marktiderman/genesis-core/hooks\";\nimport type {\n BaseRecord,\n Identifier,\n} from \"@marktiderman/genesis-core/provider\";\nimport { writeModeOptions } from \"./undo-toast\";\nimport { toast } from \"../ui/sonner\";\nimport { defaultNavigate } from \"../../navigation\";\n\n/**\n * Everything ResourcePage's markup reads. Internal: its names may change in\n * any release. Apps use {@link useResourceTable}.\n *\n * @internal\n */\nexport function useResourceTableModel<\n T extends Record<string, unknown> = Record<string, unknown>,\n>(props: ResourcePageProps<T>) {\n const {\n resource: resourceName,\n data: dataProp,\n total: totalProp,\n pagination = \"client\",\n clientRowLimit = 1000,\n title,\n subtitle,\n icon,\n columns: columnsProp,\n getKey: getKeyProp,\n detail = \"panel\",\n detailWidth = \"md\",\n titleField: titleFieldProp,\n subtitleField: subtitleFieldProp,\n getTitle,\n getSubtitle,\n renderDetail: renderDetailProp,\n detailFields,\n statusFilter,\n searchFields,\n searchPlaceholder,\n initialFilters,\n onFiltersChange,\n filters: filtersProp,\n page: pageProp,\n perPage: perPageProp,\n onPageChange,\n onPerPageChange,\n onSortChange,\n onSearchChange,\n loading: loadingProp,\n error: errorProp,\n errorMessage,\n onRetry: onRetryProp,\n savedViews,\n onLoadView,\n onSaveView,\n onDeleteView,\n canSaveViews,\n columnVisibility,\n columnVisibilityKey,\n resizableColumns,\n resizeKey,\n stickyHeader,\n stickyFirstColumn,\n pageSizeOptions,\n onCreate,\n onEdit,\n onDelete,\n can,\n canEditItem,\n createLabel = \"New\",\n canEdit,\n canDelete,\n allowDelete,\n confirmDelete,\n onDeleted,\n secondaryActions,\n allowCreate,\n allowEdit,\n formFields,\n renderForm: renderFormProp,\n formMode,\n formSteps,\n formLayout: formLayoutProp,\n mutationMode: mutationModeProp,\n bulkActions,\n renderCard: renderCardProp,\n openOnCardClick = false,\n renderTable: renderTableProp,\n gridCols: gridColsProp,\n keyboardNavigation = true,\n capabilities,\n toolbar,\n emptyState,\n emptyIcon,\n emptyMessage,\n emptyDescription,\n header: headerMode = \"page\",\n resourceOptions,\n urlState: urlStateProp,\n defaultView = \"table\",\n onNavigate = defaultNavigate,\n viewSettingsKey: viewSettingsKeyProp,\n slots,\n slotProps,\n className,\n } = props;\n\n // ── Determine mode ──\n const isClientMode = !!resourceName;\n\n /**\n * Server-controlled mode is entered by supplying ANY of the controlled\n * props — presence is the switch, so a caller that only pages (no server\n * search) still gets its `data` left alone.\n */\n // `resource` mode paging through the provider: ResourcePage owns both the\n // fetch and the page state, and reuses the server-controlled path below\n // (no client-side transform, the pager under the list) with useResource's\n // setters standing in for the caller's callbacks.\n const resourceServerPaging = isClientMode && pagination === \"server\";\n\n // ── URL state (`urlState`): the open record, search, sort and page ──\n // An adapter has `setSearchParams`; the options object wraps one.\n const url = useResourceUrlState(\n urlStateProp\n ? \"setSearchParams\" in urlStateProp\n ? { searchParams: urlStateProp }\n : urlStateProp\n : undefined,\n );\n // Read once: the URL seeds the list when it mounts, then follows it.\n const [urlSeed] = useState(() => {\n if (!url) return null;\n // A URL sort only counts when it names a sortable column: a hand-edited\n // or stale link must not send an arbitrary field to the provider.\n const sortable =\n !url.sort || !columnsProp\n ? true\n : columnsProp.some((col) =>\n typeof col === \"string\"\n ? col === url.sort!.field\n : col.key === url.sort!.field && col.sortable !== false,\n );\n return { q: url.q, sort: sortable ? url.sort : null, page: url.page };\n });\n // The URL names the open record only where there is a peek to open.\n const urlPeek = detail === \"panel\" || detail === \"modal\" ? url : undefined;\n const urlSortKey = urlSeed?.sort\n ? `${urlSeed.sort.field}:${urlSeed.sort.order}`\n : \"\";\n\n const isServerControlled =\n resourceServerPaging ||\n pageProp !== undefined ||\n perPageProp !== undefined ||\n onPageChange !== undefined ||\n onPerPageChange !== undefined ||\n onSortChange !== undefined ||\n onSearchChange !== undefined;\n\n const viewSettingsKey =\n viewSettingsKeyProp ??\n resourceName ??\n title.toLowerCase().replace(/\\s+/g, \"-\");\n const { settings, updateSetting } = useViewSettings(viewSettingsKey);\n\n // Server search needs the columns to match. Without them the provider\n // would get a query it ignores, so the search box hides instead of lying.\n const serverSearchFields = resourceOptions?.searchFields ?? searchFields;\n\n // ── Client-side mode: call useResource (hook is always called for rules-of-hooks) ──\n // Core's useResource constrains its row type to BaseRecord (rows carry an\n // `id`), which is stricter than ResourcePage's public `T extends\n // Record<string, unknown>`. Intersect at the call site so the public\n // ResourcePageProps<T> generic stays unchanged while satisfying the hook.\n const resourceHook = useResource<T & BaseRecord>(\n resourceName ?? \"__noop__\",\n isClientMode\n ? {\n ...resourceOptions,\n // Client paging fetches everything it can in one request; server\n // paging starts at the user's stored page size. An explicit\n // `defaultPerPage` wins over both.\n defaultPerPage:\n resourceOptions?.defaultPerPage ??\n (resourceServerPaging\n ? (perPageProp ?? settings.pageSize)\n : clientRowLimit),\n searchFields: resourceServerPaging\n ? serverSearchFields\n : resourceOptions?.searchFields,\n // Paging, searching and sorting change the query key; keep the\n // current page on screen while the next one loads.\n keepPreviousData:\n resourceOptions?.keepPreviousData ?? resourceServerPaging,\n // Start where the URL says, so restoring a link doesn't push a\n // search or sort that resets the page to 1.\n ...(resourceServerPaging && urlSeed\n ? {\n defaultSearch: urlSeed.q,\n ...(urlSeed.page ? { defaultPage: urlSeed.page } : {}),\n ...(urlSeed.sort ? { defaultSort: urlSeed.sort } : {}),\n }\n : {}),\n ...writeModeOptions(resourceOptions, mutationModeProp),\n }\n : { enabled: false },\n );\n\n /** Explicit prop beats the per-user stored preference. */\n const effectivePageSize = resourceServerPaging\n ? (resourceHook.params.pagination?.perPage ?? settings.pageSize)\n : (perPageProp ?? settings.pageSize);\n\n // ── Resolve display data ──\n const displayData: T[] = isClientMode\n ? (resourceHook.list.data?.data ?? [])\n : (dataProp ?? []);\n const displayTotal: number = isClientMode\n ? (resourceHook.list.data?.total ?? 0)\n : (totalProp ?? 0);\n // Client-side mode reads the hook it owns; server-data mode reads the\n // caller's `loading` / `error` props, defaulting to false so a page that\n // supplies neither renders exactly as it did before they existed.\n const isLoading = isClientMode\n ? resourceHook.list.isLoading\n : (loadingProp ?? false);\n const hasError = isClientMode\n ? resourceHook.list.isError\n : (errorProp ?? false);\n\n // ── Call useResourcePage ──\n const page = useResourcePage<T>({\n resource: resourceName,\n data: displayData,\n total: displayTotal,\n columns: columnsProp,\n statusFilter,\n searchFields,\n initialFilters:\n initialFilters ??\n (urlSeed && (urlSeed.q || urlSeed.sort)\n ? [\n ...(urlSeed.q\n ? [{ field: \"__search__\", operator: \"contains\" as const, value: urlSeed.q }]\n : []),\n ...(urlSeed.sort\n ? [{ field: \"__sort__\", operator: \"eq\" as const, value: urlSortKey }]\n : []),\n ]\n : undefined),\n onFiltersChange,\n defaultView,\n // Each page remembers its own table/cards choice, under the same key as\n // its density and page size — not one key shared by every data page.\n viewKey: viewSettingsKey,\n detail,\n titleField: titleFieldProp,\n subtitleField: subtitleFieldProp,\n controlled: isServerControlled,\n });\n\n // ── `pagination=\"server\"`: push the filter row's state into useResource ──\n // Effects rather than the change handlers, so `initialFilters` and \"Clear\n // all\" reach the provider by the same path as a keystroke. Each push is\n // skipped while its value still equals what the hook already has, so the\n // first render does not reset `defaultSort` or refetch for nothing.\n const {\n setSearch: setResourceSearch,\n setSort: setResourceSort,\n setFilters: setResourceFilters,\n } = resourceHook;\n // Seeded from the URL, which useResource already starts on (see above).\n const pushedSearchRef = useRef(resourceServerPaging ? (urlSeed?.q ?? \"\") : \"\");\n const searchEffectRanRef = useRef(false);\n useEffect(() => {\n if (!resourceServerPaging) return;\n const firstRun = !searchEffectRanRef.current;\n searchEffectRanRef.current = true;\n if (page.search === pushedSearchRef.current) return;\n const push = () => {\n pushedSearchRef.current = page.search;\n setResourceSearch(page.search);\n };\n // One request per pause in typing, not per keystroke — but an\n // `initialFilters` search (first run) or a cleared box (\"Clear all\")\n // goes out at once, landing with the sort / status pushes instead of\n // 300ms behind them.\n if (firstRun || page.search === \"\") {\n push();\n return;\n }\n const timer = setTimeout(push, 300);\n return () => clearTimeout(timer);\n }, [resourceServerPaging, page.search, setResourceSearch]);\n\n const defaultSort = resourceOptions?.defaultSort;\n const pushedSortRef = useRef<string>(resourceServerPaging ? urlSortKey : \"\");\n useEffect(() => {\n if (!resourceServerPaging) return;\n const key = page.sort ? `${page.sort.field}:${page.sort.order}` : \"\";\n if (key === pushedSortRef.current) return;\n pushedSortRef.current = key;\n // Clearing the sort returns to the resource's default order, not to\n // whatever order the provider happens to use.\n setResourceSort(page.sort ?? defaultSort);\n }, [resourceServerPaging, page.sort, defaultSort, setResourceSort]);\n\n const statusField = statusFilter\n ? typeof statusFilter === \"string\"\n ? statusFilter\n : statusFilter.field\n : undefined;\n const defaultFilters = resourceOptions?.defaultFilters;\n const pushedStatusRef = useRef<string>(\"\");\n useEffect(() => {\n if (!resourceServerPaging || !statusField) return;\n // `defaultFilters` is part of the key: useResource re-syncs its filters\n // to a changed `defaultFilters` (dropping the status filter), so a\n // change there must re-push even when the chips did not move.\n const key = JSON.stringify([page.statusValue, defaultFilters ?? null]);\n if (key === pushedStatusRef.current) return;\n pushedStatusRef.current = key;\n setResourceFilters([\n ...(defaultFilters ?? []),\n ...(page.statusValue.length > 0\n ? [{ field: statusField, operator: \"in\" as const, value: page.statusValue }]\n : []),\n ]);\n }, [\n resourceServerPaging,\n statusField,\n page.statusValue,\n defaultFilters,\n setResourceFilters,\n ]);\n\n // ── `pagination=\"client\"`: say so when the resource outgrew the fetch ──\n const clientTruncated =\n isClientMode &&\n !resourceServerPaging &&\n !isLoading &&\n !hasError &&\n displayTotal > displayData.length;\n const warnedTruncatedRef = useRef(false);\n useEffect(() => {\n if (!clientTruncated || warnedTruncatedRef.current) return;\n warnedTruncatedRef.current = true;\n console.warn(\n `ResourcePage: \"${resourceName}\" has ${displayTotal} rows but only ${displayData.length} were fetched. ` +\n `Pass \\`pagination=\"server\"\\` to page through all of them, or raise \\`clientRowLimit\\` ` +\n `(no higher than the provider's \\`maxPerPage\\`, which refuses a larger page).`,\n );\n }, [clientTruncated, resourceName, displayTotal, displayData.length]);\n // ── Build ResourceActions for column render functions ──\n const actions: ResourceActions<T> = useMemo(() => {\n if (isClientMode) {\n return {\n update: resourceHook.update as ResourceActions<T>[\"update\"],\n remove: resourceHook.remove,\n refetch: () => resourceHook.list.refetch(),\n };\n }\n return {\n update: async () => {\n throw new Error(\n \"ResourceActions.update not available in server-data mode\",\n );\n },\n remove: async () => {\n throw new Error(\n \"ResourceActions.remove not available in server-data mode\",\n );\n },\n refetch: () => {\n /* no-op in server-data mode */\n },\n };\n }, [\n isClientMode,\n resourceHook.update,\n resourceHook.remove,\n resourceHook.list,\n ]);\n\n // ── Row identity ──\n const keyOf = useCallback(\n (item: T): string =>\n getKeyProp ? getKeyProp(item) : String((item as Record<string, unknown>).id ?? \"\"),\n [getKeyProp],\n );\n\n // ── The peek's row, kept live ──\n // `page.selectedItem` is a snapshot taken at click time. Re-resolving it by\n // key against the current rows means a save (which refetches) shows its\n // new values in the open peek instead of the pre-edit copy. A row missing\n // from the current rows keeps the snapshot: paging, sorting, search and\n // filters all drop rows that still exist. A confirmed delete closes the\n // peek itself (`performDelete`).\n // An empty key (rows with no `id` and no `getKey`) identifies nothing, so\n // it keeps the snapshot rather than matching the first keyless row.\n // With `urlState` the URL names the open record (`?id=`): a link opens it,\n // and Back closes it. The click-time snapshot still covers a record that\n // isn't in the current rows.\n const snapshotKey = page.selectedItem ? keyOf(page.selectedItem) : null;\n const selectedKey = urlPeek ? urlPeek.detailId : snapshotKey;\n const snapshot =\n page.selectedItem && snapshotKey === selectedKey ? page.selectedItem : null;\n const rowOrSnapshot: T | null = !selectedKey\n ? snapshot\n : (displayData.find((row) => keyOf(row) === selectedKey) ?? snapshot);\n // A linked record that isn't in the loaded rows (another page, outside the\n // search) is fetched on its own; one that can't be read shows as missing.\n const linkedId = urlPeek && selectedKey && !rowOrSnapshot ? selectedKey : null;\n const linked = useOne<T & BaseRecord>(resourceName ?? \"__noop__\", linkedId, {\n enabled: isClientMode && !!linkedId,\n });\n const selectedItem: T | null =\n rowOrSnapshot ?? (linkedId && linked.data ? (linked.data as T) : null);\n // Missing only when that is known: the fetch failed (resource mode), or\n // `data` is the whole set (data mode without server paging). A record\n // absent from one server-controlled page may be on another.\n const linkMissing =\n !!linkedId &&\n !selectedItem &&\n !isLoading &&\n (isClientMode ? linked.isError : !isServerControlled);\n const detailOpen = urlPeek ? selectedItem !== null || linkMissing : page.detailOpen;\n\n // ── Bulk selection ──\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n\n const handleToggleSelect = useCallback((id: string) => {\n setSelectedIds((prev) => {\n const next = new Set(prev);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n }, []);\n\n const handleToggleAll = useCallback(() => {\n setSelectedIds((prev) => {\n if (prev.size === page.data.length) return new Set();\n return new Set(page.data.map(keyOf));\n });\n }, [page.data, keyOf]);\n\n const handleClearSelection = useCallback(() => {\n setSelectedIds(new Set());\n }, []);\n\n // ── Built-in form state ──\n const [formOpen, setFormOpen] = useState(false);\n const [formAction, setFormAction] = useState<\"create\" | \"edit\">(\"create\");\n const [formItem, setFormItem] = useState<T | null>(null);\n\n // Determine if built-in form should handle create/edit\n const useBuiltInCreate = allowCreate && !onCreate && isClientMode;\n const useBuiltInEdit = allowEdit && !onEdit && isClientMode;\n\n const handleCreate = useCallback(() => {\n if (useBuiltInCreate) {\n setFormAction(\"create\");\n setFormItem(null);\n setFormOpen(true);\n } else if (onCreate) {\n onCreate();\n }\n }, [useBuiltInCreate, onCreate]);\n\n // One permission check: `can`, then the deprecated per-row predicates.\n const allows = useCallback(\n (action: ResourceAction, item?: T): boolean => {\n if (can && !can(action, item)) return false;\n if (!item) return true;\n if (canEditItem && !canEditItem(item)) return false;\n if (action === \"edit\" && canEdit && !canEdit(item)) return false;\n if (action === \"delete\" && canDelete && !canDelete(item)) return false;\n return true;\n },\n [can, canEditItem, canEdit, canDelete],\n );\n\n const handleEdit = useCallback(\n (item: T) => {\n if (!allows(\"edit\", item)) return;\n if (useBuiltInEdit) {\n setFormAction(\"edit\");\n setFormItem(item);\n setFormOpen(true);\n } else if (onEdit) {\n onEdit(item);\n }\n },\n [useBuiltInEdit, onEdit, allows],\n );\n\n const closeDetail = useCallback(() => {\n page.setDetailOpen(false);\n page.setSelectedItem(null);\n urlPeek?.setDetailId(null);\n }, [page, urlPeek]);\n\n // ── Delete: built-in, confirmed, or the caller's as-is ──\n const useBuiltInDelete = !!allowDelete && !onDelete && isClientMode;\n const deleteConfirmed =\n confirmDelete === undefined ? useBuiltInDelete : !!confirmDelete;\n const deleteCopy =\n typeof confirmDelete === \"object\" ? confirmDelete : undefined;\n // The row being confirmed, kept after the dialog closes so its copy does\n // not flip to the defaults during the exit animation; `deleteOpen` is what\n // actually opens and closes it.\n const [pendingDelete, setPendingDelete] = useState<T | null>(null);\n const [deleteOpen, setDeleteOpen] = useState(false);\n const { remove: removeRecord } = resourceHook;\n // A recycle-bin resource says so in the confirm step instead of \"can't be undone\".\n const { data: providerCaps } = useCapabilities(resourceName, {\n enabled: useBuiltInDelete && deleteConfirmed,\n });\n const deleteKind: \"recycle\" | \"permanent\" = providerCaps?.softDelete ? \"recycle\" : \"permanent\";\n\n const performDelete = useCallback(\n async (item: T) => {\n if (useBuiltInDelete) {\n // A caller-supplied `getKey` (e.g. `encodeRecordKey` for a composite\n // key) is the row's identity to the provider too; otherwise `id`.\n await removeRecord(\n (getKeyProp\n ? keyOf(item)\n : (item as Record<string, unknown>).id) as Identifier,\n );\n } else if (onDelete) {\n await onDelete(item);\n }\n closeDetail();\n onDeleted?.(item);\n },\n [\n useBuiltInDelete,\n removeRecord,\n getKeyProp,\n keyOf,\n onDelete,\n closeDetail,\n onDeleted,\n ],\n );\n\n const deleteHandler: ((item: T) => void) | undefined = !(\n useBuiltInDelete || onDelete\n )\n ? undefined\n : deleteConfirmed\n ? (item: T) => {\n setPendingDelete(item);\n setDeleteOpen(true);\n }\n : useBuiltInDelete\n ? (item: T) => {\n performDelete(item).catch((err: unknown) => {\n // useResource has already handed the failure to\n // `resourceOptions.onError`; without one, the user is told.\n if (!resourceOptions?.onError) {\n toast.error(err instanceof Error ? err.message : \"The delete failed.\");\n }\n });\n }\n : // Unconfirmed `onDelete` keeps its original contract exactly:\n // called straight from the peek, nothing closed for it.\n (item: T) => {\n // A throw or a rejected promise is reported, never left unhandled,\n // even when `onError` itself throws (the toast is the fallback).\n const report = (err: unknown) => {\n const error = err instanceof Error ? err : new Error(String(err));\n if (resourceOptions?.onError) {\n try {\n resourceOptions.onError(error);\n return;\n } catch {\n // fall through to the toast\n }\n }\n toast.error(error.message || \"The delete failed.\");\n };\n // Called right away, as before; a throw or a rejection is reported.\n try {\n Promise.resolve(onDelete?.(item)).catch(report);\n } catch (err) {\n report(err);\n }\n };\n\n // Per-row rights, evaluated against the row the peek is showing.\n const selectedEditable = !!selectedItem && allows(\"edit\", selectedItem);\n const selectedDeletable = !!selectedItem && allows(\"delete\", selectedItem);\n\n // ── Patch tableColumns to use real actions (client-side mode) ──\n const tableColumns = useMemo(() => {\n const base = isClientMode\n ? page.columns.map((col) => ({\n key: col.key,\n header: col.header ?? col.key,\n sortable: col.sortable ?? true,\n hideBelow: col.hideBelow,\n width: col.width,\n minWidth: col.minWidth,\n defaultVisible: col.defaultVisible,\n editable: col.editable,\n accessor: col.accessor,\n render: col.render\n ? (item: T, value: unknown) => col.render!(item, value, actions)\n : undefined,\n }))\n : page.tableColumns;\n if (!isServerControlled) return base;\n // The server owns the ordering. DataTable's header sort is client-side\n // and would only reorder the rows of the current page — a visibly wrong\n // answer presented as a sort — so the affordance is withdrawn rather\n // than left to lie. The DataFilters sort control drives `onSortChange`.\n return base.map((col) => ({ ...col, sortable: false }));\n }, [\n isClientMode,\n isServerControlled,\n page.columns,\n page.tableColumns,\n actions,\n ]);\n\n // ── Client mode: DataTable's header sort is `page.sort` ──\n // The primary header sort is the page's sort (and so the URL's); Shift-\n // click tie-breakers are kept here while that primary still matches, and\n // dropped when the sort changes elsewhere (the sort select, the URL).\n const [tableTieBreaks, setTableTieBreaks] = useState<{\n primary: string;\n rest: DataTableSort[];\n } | null>(null);\n const pageSort = page.sort;\n const setPageSort = page.setSort;\n const tableSort = useMemo<DataTableSort[]>(() => {\n if (!pageSort) return [];\n const primary: DataTableSort = { key: pageSort.field, dir: pageSort.order };\n const rest =\n tableTieBreaks && tableTieBreaks.primary === `${primary.key}:${primary.dir}`\n ? tableTieBreaks.rest\n : [];\n // Only the table's own columns: a sort-select option or a URL sort on a\n // field the table doesn't show still orders the rows (page.sort), but is\n // not a header sort.\n return [primary, ...rest].filter((s) => tableColumns.some((c) => c.key === s.key));\n }, [pageSort, tableTieBreaks, tableColumns]);\n const handleTableSort = useCallback(\n (next: DataTableSort[]) => {\n const [primary, ...rest] = next;\n setTableTieBreaks(primary ? { primary: `${primary.key}:${primary.dir}`, rest } : null);\n setPageSort(primary ? { field: primary.key, order: primary.dir } : null);\n },\n [setPageSort],\n );\n\n // ── Server-driven pager state, needed above for handleSearchChange's\n // already-on-page-1 check. ──\n // The pager's inputs: the caller's controlled props, or — for\n // `pagination=\"server\"` — useResource's own page state.\n const pagerPage = resourceServerPaging\n ? (resourceHook.params.pagination?.page ?? 1)\n : pageProp;\n const pagerPerPage = resourceServerPaging\n ? resourceHook.params.pagination?.perPage\n : perPageProp;\n const pagerTotal = resourceServerPaging ? displayTotal : totalProp;\n const pagerOnPageChange = resourceServerPaging\n ? resourceHook.setPage\n : onPageChange;\n const currentPage = pagerPage ?? 1;\n\n // ── `urlState`: write search, sort and (server paging) page back ──\n // Replace-only, so Back steps through opened records rather than every\n // keystroke; an unchanged URL is not rewritten.\n const writeUrl = url?.write;\n useEffect(() => {\n writeUrl?.({\n q: page.search,\n sort: page.sort,\n ...(resourceServerPaging ? { page: currentPage } : {}),\n });\n }, [writeUrl, page.search, page.sort, currentPage, resourceServerPaging]);\n\n // ── Filter changes: keep the controlled UI state, and tell the server ──\n // In client-side mode these are exactly the hook's own setters; in\n // server-controlled mode they additionally notify the caller and reset to\n // page 1, since the previous page number is meaningless against a new\n // query. The reset is skipped when already on page 1 — nothing changed,\n // so there is nothing for the caller to do.\n const handleSearchChange = useCallback(\n (value: string) => {\n page.filterConfig.onSearchChange(value);\n if (!isServerControlled) return;\n onSearchChange?.(value);\n // `pagination=\"server\"`: useResource's setSearch resets the page\n // itself once the debounce settles; resetting here too would fetch\n // page 1 of the OLD query first.\n if (currentPage !== 1 && !resourceServerPaging) onPageChange?.(1);\n },\n [\n page.filterConfig,\n isServerControlled,\n onSearchChange,\n onPageChange,\n resourceServerPaging,\n currentPage,\n ],\n );\n\n const handleSortSelect = useCallback(\n (value: string) => {\n page.filterConfig.onSortChange(value);\n if (!isServerControlled) return;\n const [field, order] = value.split(\":\");\n onSortChange?.(\n field && (order === \"asc\" || order === \"desc\")\n ? { field, order }\n : null,\n );\n // See handleSearchChange: useResource's setSort resets the page.\n if (!resourceServerPaging) onPageChange?.(1);\n },\n [\n page.filterConfig,\n isServerControlled,\n onSortChange,\n onPageChange,\n resourceServerPaging,\n ],\n );\n\n const handleClearFilters = useCallback(() => {\n page.clearFilters();\n if (!isServerControlled) return;\n // Clearing has to reach the server too — otherwise \"Clear all\" empties\n // the controls and leaves the same rows on screen.\n onSearchChange?.(\"\");\n onSortChange?.(null);\n if (!resourceServerPaging) onPageChange?.(1);\n }, [\n page,\n isServerControlled,\n onSearchChange,\n onSortChange,\n onPageChange,\n resourceServerPaging,\n ]);\n\n // ── Status-chip changes: same page-1 reset as search/sort ──\n // In server-controlled mode `useResourcePage` skips client-side status\n // filtering, so a status change only reaches the caller through\n // `onFiltersChange`. Nothing else calls `onPageChange(1)` for it, so\n // selecting a more restrictive status while on page 3 could otherwise\n // fetch page 3 of the new result set and render an empty page while the\n // matching rows sit on page 1. Guarded the same way as\n // `handleSearchChange`, and not reachable from `handleClearFilters`\n // (which already does its own single reset).\n const handleStatusChange = useCallback(\n (values: string[]) => {\n page.filterConfig.statusChips?.onChange(values);\n if (!isServerControlled) return;\n if (currentPage !== 1 && !resourceServerPaging) onPageChange?.(1);\n },\n [\n page.filterConfig,\n isServerControlled,\n onPageChange,\n resourceServerPaging,\n currentPage,\n ],\n );\n\n const statusChipsConfig = useMemo(() => {\n const base = page.filterConfig.statusChips;\n if (!base) return undefined;\n return { ...base, onChange: handleStatusChange };\n }, [page.filterConfig.statusChips, handleStatusChange]);\n\n const handlePageSizeChange = useCallback(\n (size: number) => {\n updateSetting(\"pageSize\", size);\n onPerPageChange?.(size);\n // useResource's setPerPage resets to page 1 itself.\n if (resourceServerPaging) resourceHook.setPerPage(size);\n else if (isServerControlled) onPageChange?.(1);\n },\n [\n updateSetting,\n onPerPageChange,\n resourceServerPaging,\n resourceHook.setPerPage,\n isServerControlled,\n onPageChange,\n ],\n );\n\n // ── Pagination-config error ──\n // `perPage` AND `total` are both required whenever `onPageChange` is\n // supplied. Neither can be derived from the other, and neither can be\n // derived from the rows:\n // - `perPage`: see the prop doc on `perPage`. A short final page has\n // fewer rows than the page size, so the row count of a page cannot\n // stand in for the page size.\n // - `total`: `displayTotal` falls back to `0` (Line 453). With `total`\n // omitted the range span reads \"No records\" and `Next` stays\n // disabled, while `page.data` renders rows directly below it. A\n // surface that says \"no records\" above visible records is a lie, not\n // a degraded view.\n // Rather than silently guess either one, the pager below refuses to\n // render and shows the error instead. Logged once per mount (not on\n // every render) so it's visible in a console without spamming it.\n const missingPerPage =\n isServerControlled && !!pagerOnPageChange && pagerPerPage === undefined;\n const missingTotal =\n isServerControlled && !!pagerOnPageChange && pagerTotal === undefined;\n const pagerMisconfigured = missingPerPage || missingTotal;\n const missingPagerProps = [\n ...(missingPerPage ? [\"perPage\"] : []),\n ...(missingTotal ? [\"total\"] : []),\n ];\n const loggedMissingPerPageRef = useRef(false);\n useEffect(() => {\n if (!pagerMisconfigured) return;\n if (loggedMissingPerPageRef.current) return;\n loggedMissingPerPageRef.current = true;\n console.error(\n `ResourcePage: ${missingPagerProps\n .map((name) => `\\`${name}\\``)\n .join(\" and \")} ${\n missingPagerProps.length > 1 ? \"are\" : \"is\"\n } required when \\`onPageChange\\` is supplied.`,\n );\n // `missingPagerProps` is derived from `pagerMisconfigured`'s own inputs\n // and the ref makes this run at most once, so it is not a dependency.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [pagerMisconfigured]);\n\n // ── Server-driven pager math ──\n // By the time the pager renders, `pagerMisconfigured` has already gated\n // it off, so `perPageProp` and `totalProp` are both present here.\n const pagerPageSize = pagerPerPage ?? effectivePageSize;\n const totalPages = Math.max(\n 1,\n Math.ceil(displayTotal / Math.max(1, pagerPageSize)),\n );\n const rangeFrom =\n page.data.length === 0 ? 0 : (currentPage - 1) * pagerPageSize + 1;\n const rangeTo = rangeFrom === 0 ? 0 : rangeFrom + page.data.length - 1;\n\n // ── `pagination=\"server\"`: never strand the user past the last page ──\n // Deleting the last row of the last page (or any shrink in `total`) would\n // otherwise leave `page` beyond the end: the list comes back empty, the\n // shell swaps in its empty state — pager included — and there is no way\n // back. Step back to the real last page instead.\n const { setPage: setResourcePage } = resourceHook;\n const settledPage =\n !isLoading && !hasError && !resourceHook.list.isPlaceholderData;\n useEffect(() => {\n if (!resourceServerPaging || !settledPage) return;\n if (currentPage > totalPages) setResourcePage(totalPages);\n }, [resourceServerPaging, settledPage, currentPage, totalPages, setResourcePage]);\n\n // ── Detail title/subtitle values ──\n const titleOf = (item: T): string =>\n getTitle\n ? getTitle(item)\n : String((item as Record<string, unknown>)[page.titleField] ?? \"\");\n const detailTitle = selectedItem ? titleOf(selectedItem) : \"\";\n const detailSubtitle = !selectedItem\n ? undefined\n : getSubtitle\n ? getSubtitle(selectedItem)\n : page.subtitleField\n ? String(\n (selectedItem as Record<string, unknown>)[page.subtitleField] ?? \"\",\n )\n : undefined;\n\n // ── Row click handler (route mode navigates) ──\n const handleRowClick = (item: T) => {\n if (detail === \"route\") {\n // Navigate to /{resource}/{id}\n const id = (item as Record<string, unknown>).id;\n if (resourceName && id) {\n onNavigate(`/${resourceName}/${id}`);\n }\n return;\n }\n page.handleRowClick(item);\n urlPeek?.setDetailId(keyOf(item));\n };\n\n // ── Keyboard navigation (j/k, Enter, Backspace, x) ──\n // Only engaged for grid/list views: table view runs its own independent\n // client-side sort + pagination (DataTable), so a flat index into\n // `page.data` can't reliably be mapped back onto its currently-visible,\n // possibly differently-ordered rows.\n const hasBulkSelect = !!bulkActions && bulkActions.length > 0;\n // A detail panel/modal or the built-in form is covering the list. Cursor\n // keys suspend (they'd otherwise drive the list behind the overlay, and\n // Enter would re-open the row instead of activating the focused control);\n // ⌫ stays live so it can dismiss the overlay.\n const overlayOpen = detailOpen || formOpen || deleteOpen;\n const keyboardNav = useKeyboardNavigation({\n itemCount: page.data.length,\n enabled: keyboardNavigation && page.viewMode !== \"table\",\n overlayOpen,\n onOpen: (index) => {\n const item = page.data[index];\n if (item) handleRowClick(item);\n },\n onToggleSelect: hasBulkSelect\n ? (index) => {\n const item = page.data[index];\n if (item) {\n handleToggleSelect(keyOf(item));\n }\n }\n : undefined,\n // Supplied only while there is an open detail to close — the hook\n // suppresses Backspace's default whenever this is present, so leaving it\n // wired up permanently would swallow the browser/app's Backspace on\n // every grid/list page (including detail=\"none\" and detail=\"route\",\n // which never open one). `detailOpen` is only ever true for the panel\n // and modal modes, so it is the precise precondition here.\n onBack: detailOpen ? closeDetail : undefined,\n });\n\n // ── Create button ──\n return {\n actions,\n allows,\n clientTruncated,\n closeDetail,\n currentPage,\n deleteConfirmed,\n deleteCopy,\n deleteHandler,\n deleteKind,\n deleteOpen,\n detailOpen,\n detailSubtitle,\n detailTitle,\n displayData,\n displayTotal,\n effectivePageSize,\n formAction,\n formItem,\n formOpen,\n handleClearFilters,\n handleClearSelection,\n handleCreate,\n handleEdit,\n handlePageSizeChange,\n handleRowClick,\n handleSearchChange,\n handleSortSelect,\n handleTableSort,\n handleToggleAll,\n handleToggleSelect,\n hasError,\n isClientMode,\n isLoading,\n isServerControlled,\n keyOf,\n keyboardNav,\n linkMissing,\n linked,\n missingPagerProps,\n page,\n pagerMisconfigured,\n pagerOnPageChange,\n pendingDelete,\n performDelete,\n rangeFrom,\n rangeTo,\n resourceHook,\n resourceServerPaging,\n selectedDeletable,\n selectedEditable,\n selectedIds,\n selectedItem,\n serverSearchFields,\n setDeleteOpen,\n setFormOpen,\n settings,\n statusChipsConfig,\n tableColumns,\n tableSort,\n titleOf,\n totalPages,\n updateSetting,\n useBuiltInCreate,\n useBuiltInDelete,\n useBuiltInEdit,\n };\n}\n\n/**\n * What {@link useResourceTable} returns: the list's rows and the handlers a\n * custom layout wires to its controls.\n *\n * @stability Beta\n */\nexport interface ResourceTableState<\n T extends Record<string, unknown> = Record<string, unknown>,\n> {\n // ── Rows ──\n /**\n * The rows to show: filtered and sorted. With `pagination=\"client\"` this\n * is every matching row (page it yourself); with server paging it is the\n * current page.\n */\n rows: T[];\n /** How many rows match, across all pages. */\n total: number;\n loading: boolean;\n /** The read failed. */\n error: boolean;\n /** True when more than `clientRowLimit` rows exist and only that many were loaded. */\n truncated: boolean;\n /** The columns, resolved to DataTable's format. */\n columns: DataTableColumn<T>[];\n /** Write actions for cell renderers (update, remove, …). */\n actions: ResourceActions<T>;\n /** A row's identity: `getKey`, else its `id`. */\n getKey: (item: T) => string;\n /** A row's title: `getTitle`, else its title field. */\n getTitle: (item: T) => string;\n /** The page's permission check (`can`, then the deprecated per-row predicates). */\n can: (action: ResourceAction, item?: T) => boolean;\n\n // ── Search, sort and filters ──\n search: string;\n setSearch: (value: string) => void;\n /** Sort keys, primary first. */\n sort: DataTableSort[];\n setSort: (sort: DataTableSort[]) => void;\n /** The status filter's choices, its selected values and its setter, when the list has one. */\n statusFilter:\n | { options: string[]; selected: string[]; onChange: (values: string[]) => void }\n | undefined;\n hasActiveFilters: boolean;\n clearFilters: () => void;\n\n // ── Paging (server or caller-controlled paging) ──\n /** The current page, from 1. */\n page: number;\n pageCount: number;\n pageSize: number;\n setPageSize: (size: number) => void;\n /** Go to a page; undefined when the page is not controlled (client paging). */\n setPage: ((page: number) => void) | undefined;\n /** The 1-based range of the rows shown, for \"21–40 of 95\"; 0 when empty. */\n rangeFrom: number;\n rangeTo: number;\n\n // ── Selection ──\n selectedIds: Set<string>;\n toggleSelected: (id: string) => void;\n toggleAll: () => void;\n clearSelection: () => void;\n\n // ── The open record ──\n /** The open row, kept current as the data changes. */\n openItem: T | null;\n detailOpen: boolean;\n /** What clicking a row does: opens the peek, modal or route. */\n openRow: (item: T) => void;\n closeDetail: () => void;\n /** A record linked by URL that does not exist (or was deleted). */\n openItemMissing: boolean;\n\n // ── Create and edit ──\n /** Undefined when the page cannot create. */\n create: (() => void) | undefined;\n edit: (item: T) => void;\n /** The built-in form's state, for rendering your own form. */\n form: {\n open: boolean;\n setOpen: (open: boolean) => void;\n action: \"create\" | \"edit\";\n /** The row being edited; null when creating. */\n item: T | null;\n };\n\n // ── Delete ──\n /** Delete a row: asks first when the delete is confirmed. Undefined when the page cannot delete. */\n remove: ((item: T) => void) | undefined;\n /** The confirm step's state, when `remove` asks first. */\n deleteConfirm: {\n open: boolean;\n setOpen: (open: boolean) => void;\n /** The row being confirmed (kept through the close animation). */\n item: T | null;\n /** Run the delete; rejects with the failure. */\n confirm: () => Promise<void>;\n /** \"recycle\" when the provider soft-deletes, so the copy can say it can be restored. */\n kind: \"recycle\" | \"permanent\";\n copy: ResourceDeleteConfirmCopy<T> | undefined;\n };\n}\n\n/**\n * The list behind {@link ResourcePage}, without any markup: where the rows\n * come from (a `resource` through the data provider, or `data` you pass),\n * search, sort, status filter and paging, selection, the open record (and\n * its URL state), the built-in create/edit form and confirmed delete, and\n * the row handlers. Takes the same options as `ResourcePage`.\n *\n * Use it to build a different layout over the same behaviour; `ResourcePage`\n * itself is this behaviour plus Genesis's markup.\n *\n * @stability Beta\n */\nexport function useResourceTable<\n T extends Record<string, unknown> = Record<string, unknown>,\n>(props: ResourcePageProps<T>): ResourceTableState<T> {\n const m = useResourceTableModel<T>(props);\n const { pendingDelete, performDelete } = m;\n const confirm = useCallback(\n () => (pendingDelete ? performDelete(pendingDelete) : Promise.resolve()),\n [pendingDelete, performDelete],\n );\n return {\n rows: m.page.data,\n total: m.page.total,\n loading: m.isLoading,\n error: m.hasError,\n truncated: m.clientTruncated,\n columns: m.tableColumns,\n actions: m.actions,\n getKey: m.keyOf,\n getTitle: m.titleOf,\n can: m.allows,\n search: m.page.search,\n setSearch: m.handleSearchChange,\n sort: m.tableSort,\n setSort: m.handleTableSort,\n statusFilter: m.statusChipsConfig,\n hasActiveFilters: m.page.hasActiveFilters,\n clearFilters: m.handleClearFilters,\n page: m.currentPage,\n pageCount: m.totalPages,\n pageSize: m.effectivePageSize,\n setPageSize: m.handlePageSizeChange,\n setPage: m.pagerOnPageChange,\n rangeFrom: m.rangeFrom,\n rangeTo: m.rangeTo,\n selectedIds: m.selectedIds,\n toggleSelected: m.handleToggleSelect,\n toggleAll: m.handleToggleAll,\n clearSelection: m.handleClearSelection,\n openItem: m.selectedItem,\n detailOpen: m.detailOpen,\n openRow: m.handleRowClick,\n closeDetail: m.closeDetail,\n openItemMissing: m.linkMissing,\n create: m.useBuiltInCreate || props.onCreate ? m.handleCreate : undefined,\n edit: m.handleEdit,\n form: {\n open: m.formOpen,\n setOpen: m.setFormOpen,\n action: m.formAction,\n item: m.formItem,\n },\n remove: m.deleteHandler,\n deleteConfirm: {\n open: m.deleteOpen,\n setOpen: m.setDeleteOpen,\n item: pendingDelete,\n confirm,\n kind: m.deleteKind,\n copy: m.deleteCopy,\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
|
+
|
|
4
|
+
// src/hooks/use-resource-url-state.ts
|
|
5
|
+
var DEFAULTS = { detail: "id", page: "page", sort: "sort", q: "q" };
|
|
6
|
+
function parseSort(raw) {
|
|
7
|
+
if (!raw) return null;
|
|
8
|
+
const at = raw.lastIndexOf(":");
|
|
9
|
+
const field = at > 0 ? raw.slice(0, at) : "";
|
|
10
|
+
const order = raw.slice(at + 1);
|
|
11
|
+
return field && (order === "asc" || order === "desc") ? { field, order } : null;
|
|
12
|
+
}
|
|
13
|
+
function useResourceUrlState(options) {
|
|
14
|
+
const adapter = options?.searchParams;
|
|
15
|
+
const names = { ...DEFAULTS, ...options?.params };
|
|
16
|
+
const sp = adapter?.searchParams;
|
|
17
|
+
const setSearchParams = adapter?.setSearchParams;
|
|
18
|
+
const detailId = sp?.get(names.detail) || null;
|
|
19
|
+
const rawPage = sp?.get(names.page);
|
|
20
|
+
const pageNum = rawPage && /^\d+$/.test(rawPage) ? Number(rawPage) : null;
|
|
21
|
+
const page = pageNum !== null && Number.isSafeInteger(pageNum) && pageNum > 0 ? pageNum : null;
|
|
22
|
+
const sortRaw = sp?.get(names.sort) ?? null;
|
|
23
|
+
const q = sp?.get(names.q) ?? "";
|
|
24
|
+
const setDetailId = useCallback(
|
|
25
|
+
(id) => {
|
|
26
|
+
if (!sp || !setSearchParams) return;
|
|
27
|
+
const next = new URLSearchParams(sp);
|
|
28
|
+
if (id) next.set(names.detail, id);
|
|
29
|
+
else next.delete(names.detail);
|
|
30
|
+
if (next.toString() === sp.toString()) return;
|
|
31
|
+
setSearchParams(next, { replace: !id });
|
|
32
|
+
},
|
|
33
|
+
[sp, setSearchParams, names.detail]
|
|
34
|
+
);
|
|
35
|
+
const write = useCallback(
|
|
36
|
+
(values) => {
|
|
37
|
+
if (!sp || !setSearchParams) return;
|
|
38
|
+
const next = new URLSearchParams(sp);
|
|
39
|
+
const put = (key, value) => {
|
|
40
|
+
if (value) next.set(key, value);
|
|
41
|
+
else next.delete(key);
|
|
42
|
+
};
|
|
43
|
+
if (values.page !== void 0) put(names.page, values.page && values.page > 1 ? String(values.page) : null);
|
|
44
|
+
if (values.sort !== void 0) put(names.sort, values.sort ? `${values.sort.field}:${values.sort.order}` : null);
|
|
45
|
+
if (values.q !== void 0) put(names.q, values.q || null);
|
|
46
|
+
if (next.toString() === sp.toString()) return;
|
|
47
|
+
setSearchParams(next, { replace: true });
|
|
48
|
+
},
|
|
49
|
+
[sp, setSearchParams, names.page, names.sort, names.q]
|
|
50
|
+
);
|
|
51
|
+
const sort = useMemo(() => parseSort(sortRaw), [sortRaw]);
|
|
52
|
+
if (!adapter) return void 0;
|
|
53
|
+
return { detailId, setDetailId, page, sort, q, write };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { useResourceUrlState };
|
|
57
|
+
//# sourceMappingURL=chunk-DIV4BYXX.js.map
|
|
58
|
+
//# sourceMappingURL=chunk-DIV4BYXX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-resource-url-state.ts"],"names":[],"mappings":"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","file":"chunk-DIV4BYXX.js","sourcesContent":["\"use client\";\n\nimport { useCallback, useMemo } from \"react\";\nimport type { SearchParamsAdapter } from \"../navigation\";\nimport type { SortParam } from \"@marktiderman/genesis-core/provider\";\n\n/**\n * The query-string names a list keeps its state under. Rename one when two\n * lists share a page, or when `id` already means something in your routes.\n *\n * @stability Beta\n */\nexport interface ResourceUrlParams {\n /** The open record's key. Default `\"id\"`. */\n detail?: string;\n /** The page number (from 1). Default `\"page\"`. */\n page?: string;\n /** `field:asc` or `field:desc`. Default `\"sort\"`. */\n sort?: string;\n /** The search text. Default `\"q\"`. */\n q?: string;\n}\n\n/**\n * Where a list's state lives in the URL: the router's search params, plus\n * optional names for each value.\n *\n * @stability Beta\n */\nexport interface ResourceUrlStateOptions {\n searchParams: SearchParamsAdapter;\n params?: ResourceUrlParams;\n}\n\n/** @stability Beta */\nexport interface ResourceUrlState {\n /** The open record's key, or null when no record is open. */\n detailId: string | null;\n /** Open a record (a new history entry, so Back closes it) or close it (null; replaces). */\n setDetailId: (id: string | null) => void;\n /** The page from the URL, or null when absent or not a positive integer. */\n page: number | null;\n /** The sort from the URL, or null. */\n sort: SortParam | null;\n /** The search text from the URL (\"\" when absent). */\n q: string;\n /**\n * Write page, sort and search. Replaces the history entry (typing a search\n * shouldn't fill Back with every keystroke). A default value (page 1, no\n * sort, empty search) is dropped from the URL; `undefined` leaves a value as is.\n */\n write: (next: { page?: number | null; sort?: SortParam | null; q?: string }) => void;\n}\n\nconst DEFAULTS: Required<ResourceUrlParams> = { detail: \"id\", page: \"page\", sort: \"sort\", q: \"q\" };\n\nfunction parseSort(raw: string | null): SortParam | null {\n if (!raw) return null;\n const at = raw.lastIndexOf(\":\");\n const field = at > 0 ? raw.slice(0, at) : \"\";\n const order = raw.slice(at + 1);\n return field && (order === \"asc\" || order === \"desc\") ? { field, order } : null;\n}\n\n/**\n * A list's peek, page, sort and search, kept in the URL through any\n * {@link SearchParamsAdapter} (the browser's, React Router's or TanStack\n * Router's). The URL is the source of truth: a copied link reopens the same\n * record on the same page, and Back closes a record that was opened.\n *\n * `ResourcePage` uses it when given `urlState`; a server-controlled page\n * that owns its own page/sort/search can call it directly.\n *\n * @stability Beta\n */\nexport function useResourceUrlState(options: ResourceUrlStateOptions): ResourceUrlState;\nexport function useResourceUrlState(options: ResourceUrlStateOptions | undefined): ResourceUrlState | undefined;\nexport function useResourceUrlState(options: ResourceUrlStateOptions | undefined): ResourceUrlState | undefined {\n const adapter = options?.searchParams;\n const names = { ...DEFAULTS, ...options?.params };\n const sp = adapter?.searchParams;\n const setSearchParams = adapter?.setSearchParams;\n\n const detailId = sp?.get(names.detail) || null;\n const rawPage = sp?.get(names.page);\n const pageNum = rawPage && /^\\d+$/.test(rawPage) ? Number(rawPage) : null;\n // A positive safe integer only: hundreds of digits parse to Infinity.\n const page = pageNum !== null && Number.isSafeInteger(pageNum) && pageNum > 0 ? pageNum : null;\n const sortRaw = sp?.get(names.sort) ?? null;\n const q = sp?.get(names.q) ?? \"\";\n\n const setDetailId = useCallback(\n (id: string | null) => {\n if (!sp || !setSearchParams) return;\n const next = new URLSearchParams(sp);\n if (id) next.set(names.detail, id);\n else next.delete(names.detail);\n if (next.toString() === sp.toString()) return;\n // Opening adds an entry so Back closes the record; closing replaces.\n setSearchParams(next, { replace: !id });\n },\n [sp, setSearchParams, names.detail],\n );\n\n const write = useCallback<ResourceUrlState[\"write\"]>(\n (values) => {\n if (!sp || !setSearchParams) return;\n const next = new URLSearchParams(sp);\n const put = (key: string, value: string | null) => {\n if (value) next.set(key, value);\n else next.delete(key);\n };\n if (values.page !== undefined) put(names.page, values.page && values.page > 1 ? String(values.page) : null);\n if (values.sort !== undefined) put(names.sort, values.sort ? `${values.sort.field}:${values.sort.order}` : null);\n if (values.q !== undefined) put(names.q, values.q || null);\n if (next.toString() === sp.toString()) return;\n setSearchParams(next, { replace: true });\n },\n [sp, setSearchParams, names.page, names.sort, names.q],\n );\n\n const sort = useMemo(() => parseSort(sortRaw), [sortRaw]);\n\n if (!adapter) return undefined;\n return { detailId, setDetailId, page, sort, q, write };\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
// src/components/patterns/use-announcer.ts
|
|
5
|
+
function useAnnouncer() {
|
|
6
|
+
const [message, setMessage] = React.useState("");
|
|
7
|
+
const announce = React.useCallback(
|
|
8
|
+
(next) => setMessage((previous) => previous === next ? `${next}\xA0` : next),
|
|
9
|
+
[]
|
|
10
|
+
);
|
|
11
|
+
return { message, announce };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { useAnnouncer };
|
|
15
|
+
//# sourceMappingURL=chunk-I4NBE2KL.js.map
|
|
16
|
+
//# sourceMappingURL=chunk-I4NBE2KL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/patterns/use-announcer.ts"],"names":[],"mappings":";;;AAaO,SAAS,YAAA,GAAe;AAC7B,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAU,eAAS,EAAE,CAAA;AAC/C,EAAA,MAAM,QAAA,GAAiB,KAAA,CAAA,WAAA;AAAA,IACrB,CAAC,IAAA,KAAiB,UAAA,CAAW,CAAC,QAAA,KAAc,aAAa,IAAA,GAAO,CAAA,EAAG,IAAI,CAAA,IAAA,CAAA,GAAW,IAAK,CAAA;AAAA,IACvF;AAAC,GACH;AACA,EAAA,OAAO,EAAE,SAAS,QAAA,EAAS;AAC7B","file":"chunk-I4NBE2KL.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\n/**\n * A polite live-region message for drag and drop (SortableList,\n * KanbanBoard). Render `message` in an `aria-live=\"polite\"` element.\n *\n * A live region only speaks when its text changes, so the same message\n * twice in a row is told apart by a trailing no-break space.\n *\n * @internal\n */\nexport function useAnnouncer() {\n const [message, setMessage] = React.useState(\"\");\n const announce = React.useCallback(\n (next: string) => setMessage((previous) => (previous === next ? `${next}\\u00a0` : next)),\n [],\n );\n return { message, announce };\n}\n"]}
|
|
@@ -78,10 +78,11 @@ function useResourcePage(props) {
|
|
|
78
78
|
subtitleField: subtitleFieldProp,
|
|
79
79
|
initialFilters,
|
|
80
80
|
onFiltersChange,
|
|
81
|
-
controlled = false
|
|
81
|
+
controlled = false,
|
|
82
|
+
viewKey
|
|
82
83
|
} = props;
|
|
83
84
|
const [viewMode, setViewMode] = useViewPreference(
|
|
84
|
-
resource ?? "resource-page",
|
|
85
|
+
viewKey ?? resource ?? "resource-page",
|
|
85
86
|
defaultView
|
|
86
87
|
);
|
|
87
88
|
const initialSearch = useMemo(() => {
|
|
@@ -293,5 +294,5 @@ function useResourcePage(props) {
|
|
|
293
294
|
}
|
|
294
295
|
|
|
295
296
|
export { titleCase, useResourcePage };
|
|
296
|
-
//# sourceMappingURL=chunk-
|
|
297
|
-
//# sourceMappingURL=chunk-
|
|
297
|
+
//# sourceMappingURL=chunk-RKPS7ERS.js.map
|
|
298
|
+
//# sourceMappingURL=chunk-RKPS7ERS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-resource-page.ts"],"names":[],"mappings":"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","file":"chunk-RKPS7ERS.js","sourcesContent":["\"use client\";\n\nimport { useState, useMemo, useCallback, useEffect, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { DataTableColumn } from \"../components/data/DataTable\";\nimport type { DataFilterConfig } from \"../components/data/DataFilters\";\nimport type { ViewMode } from \"../components/patterns/view-toggle\";\nimport type { Identifier, FilterParam, SortParam } from \"@marktiderman/genesis-core/provider\";\nimport { useViewPreference, type ViewModeDefault } from \"./use-view-preference\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Actions available inside column render functions. */\nexport interface ResourceActions<T> {\n update: (id: Identifier, data: Partial<T>) => Promise<T>;\n remove: (id: Identifier) => Promise<void>;\n refetch: () => void;\n}\n\n/** Column definition — full config form. */\nexport interface ResourceColumnDef<T> {\n key: string;\n header?: string;\n sortable?: boolean;\n hideBelow?: \"sm\" | \"md\" | \"lg\" | \"xl\";\n /** Starting width, passed straight through to `DataTable` (a number is px). */\n width?: DataTableColumn<T>[\"width\"];\n /** Smallest width the user can resize to, in px. Passed through to `DataTable`. */\n minWidth?: number;\n /** Whether the column shows before the user picks columns. Default true. */\n defaultVisible?: boolean;\n render?: (\n item: T,\n value: unknown,\n actions: ResourceActions<T>,\n ) => ReactNode;\n /** In-place cell editing, passed straight through to `DataTable`. */\n editable?: DataTableColumn<T>[\"editable\"];\n /**\n * The cell's value, when it isn't `item[key]`. Passed straight through to\n * `DataTable`, which also starts an edit from `String(accessor(item))`.\n */\n accessor?: (item: T) => unknown;\n}\n\nexport interface UseResourcePageProps<T> {\n /** Resource name — used for view-preference storage key. */\n resource?: string;\n /** The data to display (pre-fetched or from useResource). */\n data: T[];\n /** Total count of records (before client-side filtering). */\n total: number;\n /** Column definitions. Strings are expanded to `{ key, header, sortable }`. Omit to auto-generate from data keys. */\n columns?: Array<string | ResourceColumnDef<T>>;\n /** Status filter config. String = field name (options auto-derived). Object = explicit field + options. */\n statusFilter?: string | { field: string; options?: string[] };\n /** Fields to search across. Defaults to all string-valued fields in data. */\n searchFields?: string[];\n /** Initial filter params applied on mount. */\n initialFilters?: FilterParam[];\n /** Callback when filters change. */\n onFiltersChange?: (filters: FilterParam[]) => void;\n /**\n * Default view mode, used until the user picks one. Pass\n * `{ mobile, desktop }` for a viewport-dependent default (cards on phones,\n * a table on desktops); only an explicit pick is persisted.\n */\n defaultView?: ViewModeDefault;\n /**\n * Where the table/cards/list choice is remembered. Default: `resource`,\n * else one key shared by every keyless page — so pages without a\n * `resource` (rows passed in) should each set their own.\n */\n viewKey?: string;\n /** Detail interaction mode. */\n detail?: \"panel\" | \"modal\" | \"route\" | \"none\";\n /** Field used for the detail title. Defaults to first column key. */\n titleField?: string;\n /** Field used for the detail subtitle. Defaults to second column key. */\n subtitleField?: string;\n /**\n * Server-controlled mode. The `data` handed in has ALREADY been searched,\n * sorted and paginated by the server, so this hook must not do any of it a\n * second time — `data` is returned untouched and `total` is the server's\n * `total` rather than the length of a client-filtered slice.\n *\n * Search / sort / status state is still tracked (the filter UI stays\n * controlled and `onFiltersChange` still fires) — only the client-side\n * transform of `data` is skipped. Defaults to `false`, so every existing\n * caller keeps its current client-side behavior.\n */\n controlled?: boolean;\n}\n\nexport interface UseResourcePageReturn<T> {\n /** Filtered and sorted data for display. */\n data: T[];\n /** Total count (of the filtered set; the server's `total` when `controlled`). */\n total: number;\n /** Resolved column definitions. */\n columns: ResourceColumnDef<T>[];\n /** Columns mapped to DataTable format. */\n tableColumns: DataTableColumn<T>[];\n /** Ready-to-spread filter config for DataFilters. */\n filterConfig: {\n search: string;\n onSearchChange: (value: string) => void;\n sort: string;\n onSortChange: (sort: string) => void;\n sortOptions: Array<{ value: string; label: string }>;\n statusChips?: {\n options: string[];\n selected: string[];\n onChange: (values: string[]) => void;\n };\n };\n viewMode: ViewMode;\n setViewMode: (mode: ViewMode) => void;\n search: string;\n setSearch: (value: string) => void;\n sort: SortParam | null;\n setSort: (sort: SortParam | null) => void;\n statusValue: string[];\n setStatusValue: (values: string[]) => void;\n selectedItem: T | null;\n setSelectedItem: (item: T | null) => void;\n detailOpen: boolean;\n setDetailOpen: (open: boolean) => void;\n handleRowClick: (item: T) => void;\n titleField: string;\n subtitleField: string;\n statusOptions: string[];\n hasActiveFilters: boolean;\n clearFilters: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a key string to title case.\n * \"created_at\" → \"Created At\"\n * \"firstName\" → \"First Name\"\n */\nexport function titleCase(str: string): string {\n // Split on underscores, hyphens, and camelCase boundaries\n const words = str\n .replace(/([a-z])([A-Z])/g, \"$1 $2\") // camelCase → camel Case\n .replace(/[_-]/g, \" \") // snake_case / kebab-case → spaces\n .split(/\\s+/)\n .filter(Boolean);\n\n return words\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())\n .join(\" \");\n}\n\n/**\n * Resolve column shorthand into full ResourceColumnDef objects.\n * - undefined + data present → auto-generate from Object.keys(data[0]), skip \"id\"\n * - undefined + empty data → empty array\n * - string items → { key, header: titleCase(key), sortable: true }\n * - object items → defaults applied\n */\nfunction resolveColumns<T>(\n columns: Array<string | ResourceColumnDef<T>> | undefined,\n data: T[],\n): ResourceColumnDef<T>[] {\n if (columns === undefined) {\n if (data.length === 0) return [];\n const keys = Object.keys(data[0] as Record<string, unknown>).filter(\n (k) => k !== \"id\",\n );\n return keys.map((key) => ({\n key,\n header: titleCase(key),\n sortable: true,\n }));\n }\n\n return columns.map((col) => {\n if (typeof col === \"string\") {\n return { key: col, header: titleCase(col), sortable: true };\n }\n return {\n ...col,\n header: col.header ?? titleCase(col.key),\n sortable: col.sortable ?? true,\n };\n });\n}\n\n/**\n * Derive the list of string fields in a data set (for default search).\n */\nfunction deriveStringFields<T>(data: T[]): string[] {\n if (data.length === 0) return [];\n const first = data[0] as Record<string, unknown>;\n return Object.keys(first).filter((k) => typeof first[k] === \"string\");\n}\n\n/**\n * Derive unique status options from a data array.\n */\nfunction deriveStatusOptions<T>(data: T[], field: string): string[] {\n const values = new Set<string>();\n for (const item of data) {\n const val = (item as Record<string, unknown>)[field];\n if (val != null) values.add(String(val));\n }\n return Array.from(values).sort();\n}\n\n/**\n * Client-side text search across multiple fields.\n */\nfunction matchesSearch<T>(\n item: T,\n query: string,\n fields: string[],\n): boolean {\n if (!query) return true;\n const lower = query.toLowerCase();\n const record = item as Record<string, unknown>;\n return fields.some((f) => {\n const val = record[f];\n return val != null && String(val).toLowerCase().includes(lower);\n });\n}\n\n/**\n * Client-side sort comparator.\n */\nfunction compareItems<T>(a: T, b: T, sort: SortParam): number {\n const ra = a as Record<string, unknown>;\n const rb = b as Record<string, unknown>;\n const va = ra[sort.field];\n const vb = rb[sort.field];\n const sa = va == null ? \"\" : String(va);\n const sb = vb == null ? \"\" : String(vb);\n const cmp = sa.localeCompare(sb, undefined, {\n numeric: true,\n sensitivity: \"base\",\n });\n return sort.order === \"asc\" ? cmp : -cmp;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useResourcePage<T = Record<string, unknown>>(\n props: UseResourcePageProps<T>,\n): UseResourcePageReturn<T> {\n const {\n resource,\n data: rawData,\n total: rawTotal,\n columns: columnsProp,\n statusFilter: statusFilterProp,\n searchFields: searchFieldsProp,\n defaultView = \"table\",\n detail = \"panel\",\n titleField: titleFieldProp,\n subtitleField: subtitleFieldProp,\n initialFilters,\n onFiltersChange,\n controlled = false,\n viewKey,\n } = props;\n\n // -- View mode --\n const [viewMode, setViewMode] = useViewPreference(\n viewKey ?? resource ?? \"resource-page\",\n defaultView,\n );\n\n // -- Derive initial values from initialFilters --\n const initialSearch = useMemo(() => {\n if (!initialFilters) return \"\";\n const searchFilter = initialFilters.find(\n (f) => f.operator === \"contains\" && f.field === \"__search__\",\n );\n return searchFilter && \"value\" in searchFilter ? String(searchFilter.value ?? \"\") : \"\";\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n const initialSort = useMemo(() => {\n if (!initialFilters) return null;\n const sortFilter = initialFilters.find(\n (f) => f.field === \"__sort__\",\n );\n if (!sortFilter || !(\"value\" in sortFilter) || typeof sortFilter.value !== \"string\") return null;\n const [field, order] = sortFilter.value.split(\":\");\n if (!field || (order !== \"asc\" && order !== \"desc\")) return null;\n return { field, order } as SortParam;\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n const initialStatus = useMemo(() => {\n if (!initialFilters) return [];\n const statusFilter = initialFilters.find(\n (f) => f.operator === \"in\" && f.field === \"__status__\",\n );\n if (!statusFilter || statusFilter.operator !== \"in\") return [];\n return statusFilter.value.map((v) => String(v));\n }, []); // eslint-disable-line react-hooks/exhaustive-deps -- only on mount\n\n // -- Search --\n const [search, setSearch] = useState(initialSearch);\n\n // -- Sort --\n const [sort, setSort] = useState<SortParam | null>(initialSort);\n\n // -- Status filter --\n const [statusValue, setStatusValue] = useState<string[]>(initialStatus);\n\n // -- Detail panel --\n const [selectedItem, setSelectedItem] = useState<T | null>(null);\n const [detailOpen, setDetailOpen] = useState(false);\n\n // -- Resolve columns --\n const resolvedColumns = useMemo(\n () => resolveColumns(columnsProp, rawData),\n [columnsProp, rawData],\n );\n\n // -- Title / subtitle fields --\n const titleField =\n titleFieldProp ?? resolvedColumns[0]?.key ?? \"name\";\n const subtitleField =\n subtitleFieldProp ?? resolvedColumns[1]?.key ?? \"\";\n\n // -- Status filter config --\n const statusFieldName = useMemo(() => {\n if (!statusFilterProp) return null;\n return typeof statusFilterProp === \"string\"\n ? statusFilterProp\n : statusFilterProp.field;\n }, [statusFilterProp]);\n\n const statusOptions = useMemo(() => {\n if (!statusFilterProp) return [];\n if (typeof statusFilterProp === \"object\" && statusFilterProp.options) {\n return statusFilterProp.options;\n }\n if (!statusFieldName) return [];\n return deriveStatusOptions(rawData, statusFieldName);\n }, [statusFilterProp, statusFieldName, rawData]);\n\n // -- Search fields --\n const searchFields = useMemo(\n () => searchFieldsProp ?? deriveStringFields(rawData),\n [searchFieldsProp, rawData],\n );\n\n // -- Client-side filtering + sorting --\n // Skipped entirely in controlled mode: the server already applied the\n // search, the sort and the page window, so re-running them here would\n // filter the current page against a query the server has already honoured\n // (and re-sort a slice of rows that is not the full result set).\n const filteredData = useMemo(() => {\n if (controlled) return rawData;\n\n let result = rawData;\n\n // Text search\n if (search) {\n result = result.filter((item) =>\n matchesSearch(item, search, searchFields),\n );\n }\n\n // Status filter\n if (statusFieldName && statusValue.length > 0) {\n result = result.filter((item) => {\n const val = (item as Record<string, unknown>)[statusFieldName];\n return val != null && statusValue.includes(String(val));\n });\n }\n\n // Sort\n if (sort) {\n result = [...result].sort((a, b) => compareItems(a, b, sort));\n }\n\n return result;\n }, [controlled, rawData, search, searchFields, statusFieldName, statusValue, sort]);\n\n // -- Sort options (built from resolved columns) --\n const sortOptions = useMemo(() => {\n const opts: Array<{ value: string; label: string }> = [];\n for (const col of resolvedColumns) {\n if (col.sortable !== false) {\n opts.push({\n value: `${col.key}:asc`,\n label: `${col.header ?? titleCase(col.key)} (A-Z)`,\n });\n opts.push({\n value: `${col.key}:desc`,\n label: `${col.header ?? titleCase(col.key)} (Z-A)`,\n });\n }\n }\n return opts;\n }, [resolvedColumns]);\n\n // -- Sort string for DataFilters select --\n const sortString = sort ? `${sort.field}:${sort.order}` : \"\";\n\n const handleSortChange = useCallback((value: string) => {\n if (!value) {\n setSort(null);\n return;\n }\n const [field, order] = value.split(\":\");\n setSort({ field, order: order as \"asc\" | \"desc\" });\n }, []);\n\n // -- Map to DataTableColumn --\n const tableColumns: DataTableColumn<T>[] = useMemo(\n () =>\n resolvedColumns.map((col) => ({\n key: col.key,\n header: col.header ?? titleCase(col.key),\n sortable: col.sortable ?? true,\n hideBelow: col.hideBelow,\n width: col.width,\n minWidth: col.minWidth,\n defaultVisible: col.defaultVisible,\n editable: col.editable,\n accessor: col.accessor,\n render: col.render\n ? (item: T, value: unknown) =>\n col.render!(item, value, {\n update: async () => {\n throw new Error(\n \"ResourceActions.update not available in server-data mode\",\n );\n },\n remove: async () => {\n throw new Error(\n \"ResourceActions.remove not available in server-data mode\",\n );\n },\n refetch: () => {\n /* no-op in server-data mode */\n },\n })\n : undefined,\n })),\n [resolvedColumns],\n );\n\n // -- Active filters check --\n const hasActiveFilters =\n search !== \"\" || statusValue.length > 0 || sort !== null;\n\n const clearFilters = useCallback(() => {\n setSearch(\"\");\n setStatusValue([]);\n setSort(null);\n }, []);\n\n // -- Notify parent of filter changes --\n const onFiltersChangeRef = useRef(onFiltersChange);\n onFiltersChangeRef.current = onFiltersChange;\n\n useEffect(() => {\n if (!onFiltersChangeRef.current) return;\n const filters: FilterParam[] = [];\n if (search) {\n filters.push({ field: \"__search__\", operator: \"contains\", value: search });\n }\n if (sort) {\n filters.push({ field: \"__sort__\", operator: \"eq\", value: `${sort.field}:${sort.order}` });\n }\n if (statusValue.length > 0) {\n filters.push({ field: \"__status__\", operator: \"in\", value: statusValue });\n }\n onFiltersChangeRef.current(filters);\n }, [search, sort, statusValue]);\n\n // -- Row click --\n const handleRowClick = useCallback(\n (item: T) => {\n if (detail === \"none\") return;\n setSelectedItem(item);\n setDetailOpen(true);\n },\n [detail],\n );\n\n // -- Filter config (ready to spread into DataFilters) --\n const filterConfig = useMemo(\n () => ({\n search,\n onSearchChange: setSearch,\n sort: sortString,\n onSortChange: handleSortChange,\n sortOptions,\n ...(statusFieldName && statusOptions.length > 0\n ? {\n statusChips: {\n options: statusOptions,\n selected: statusValue,\n onChange: setStatusValue,\n },\n }\n : {}),\n }),\n [\n search,\n sortString,\n handleSortChange,\n sortOptions,\n statusFieldName,\n statusOptions,\n statusValue,\n ],\n );\n\n return {\n data: filteredData,\n // Controlled mode: `filteredData` is one server page, so its length is\n // not the record count — the server's total is.\n total: controlled ? rawTotal : filteredData.length,\n columns: resolvedColumns,\n tableColumns,\n filterConfig,\n viewMode,\n setViewMode,\n search,\n setSearch,\n sort,\n setSort,\n statusValue,\n setStatusValue,\n selectedItem,\n setSelectedItem,\n detailOpen,\n setDetailOpen,\n handleRowClick,\n titleField,\n subtitleField,\n statusOptions,\n hasActiveFilters,\n clearFilters,\n };\n}\n"]}
|