@datakable/react 1.0.0 → 1.1.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/data-table.tsx"],"sourcesContent":["import {\n type AnyFeature,\n boundaryDiagnostics,\n defaultFormat,\n deriveColumns,\n describeReturn,\n emitTableDiagnostic,\n type InvalidRowsMode,\n keepDiagnostics,\n matchesSlot,\n type RowIssue,\n type RowModelStatus,\n type SearchStatus,\n slotDiagnostics,\n type TableSchema,\n type TableState,\n} from '@datakable/core'\nimport {\n Component,\n type ComponentType,\n type ReactNode,\n useEffect,\n useLayoutEffect,\n useMemo,\n} from 'react'\nimport {\n cellSlot,\n invalidCellSlot,\n registerRenderOperations,\n renderDiagnostics,\n} from './render-codes'\nimport { type UseDataTableOptions, useDataTable } from './use-data-table'\n\n// The codes this module emits (bundlers keep the definitions; see core's `keepDiagnostics`).\nkeepDiagnostics(renderDiagnostics, boundaryDiagnostics, slotDiagnostics)\n\n// The ops its boundaries stand for (§3.4: `render:table`, `render:cell`). Registered at import:\n// the dist is one file per module (T37), so hook-only bundles (`useDataTable` without\n// `<DataTable />`) never include this module, and this call ships only with `<DataTable />`.\nregisterRenderOperations()\n\n// No @types/node here: declare just what is read.\ndeclare const process: { env: { NODE_ENV?: string } }\n\n/** No layout effects on the server (React 18 warns about them there). */\nconst useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect\n\n// --- public types --------------------------------------------------------------------------------\n\n/** Props of `components.SearchInput`: the search feature's `input`, bound both ways. */\nexport interface SearchInputProps {\n /** `state.search.input` (updates as the user types). */\n value: string\n /** `state.search.status`: `pending` while a debounce waits, `searching` while a request runs. */\n status: SearchStatus\n /** `setSearch(value)`. */\n onChange(value: string): void\n /** `flushSearch()` (Enter). */\n onSubmit(): void\n /** `clearSearch()` (Esc). */\n onClear(): void\n}\n\n/** Props of `components.Pagination` (the pagination feature's state and API). */\nexport interface PaginationProps {\n pageIndex: number\n pageSize: number\n pageCount: number\n canPrev: boolean\n canNext: boolean\n prev(): void\n next(): void\n setPageIndex(index: number): void\n setPageSize(size: number): void\n}\n\n/** Props of `components.Empty`: shown when the table has loaded and has no rows to show. */\nexport interface EmptyProps {\n /** `true` when there is data but filters / search removed every row. */\n filtered: boolean\n}\n\n/** Props of `components.Loading`: the first load (`isInitialLoad`, nothing to show yet). */\nexport interface LoadingProps {\n status: RowModelStatus\n}\n\n/** Props of `components.Error`: the first load failed, or the body failed to render. */\nexport interface ErrorProps {\n status: RowModelStatus\n /**\n * `'load'`: `status === 'error'` on the first load. `'render'`: a component in the body outside\n * the cells threw (a component inside a cell renders `⚠` in that cell only).\n */\n reason: 'load' | 'render'\n /** `'load'`: `refetch()`. `'render'`: renders the body again. */\n retry(): void\n}\n\n/** `<DataTable components={...} />`: replaces the default parts (unstyled, `data-*` hooks only). */\nexport interface DataTableComponents {\n SearchInput?: ComponentType<SearchInputProps>\n Pagination?: ComponentType<PaginationProps>\n Empty?: ComponentType<EmptyProps>\n Loading?: ComponentType<LoadingProps>\n Error?: ComponentType<ErrorProps>\n}\n\n/** What `<DataTable table={...} />` reads: the value `useDataTable` returns. */\nexport interface DataTableSource {\n readonly state: TableState\n readonly rowModel: {\n readonly rows: readonly DataTableRow[]\n readonly filteredCount: number\n readonly totalCount: number\n readonly status: RowModelStatus\n readonly isInitialLoad: boolean\n }\n getOptions(): { readonly columns?: readonly unknown[] | undefined }\n getData(): readonly unknown[]\n refetch(): Promise<unknown>\n}\n\n/** The part of a `Row` (§6.6) `<DataTable />` renders from. */\ninterface DataTableRow {\n readonly key: string | number\n readonly index: number\n readonly original: unknown\n readonly valid: boolean\n readonly issues: readonly RowIssue[]\n getValue(id: string): unknown\n getCellIssues(id: string): readonly RowIssue[]\n}\n\n/** `<DataTable />`'s own props, next to `table` or the `useDataTable` options. */\nexport interface DataTableOwnProps {\n components?: DataTableComponents | undefined\n caption?: ReactNode\n}\n\n/**\n * `<DataTable table={useDataTable(...)} />`, or `<DataTable data={rows} />` with the\n * `useDataTable` options as props (zero-config: columns derived from the data).\n */\nexport type DataTableProps<\n T = unknown,\n Sch = undefined,\n M extends InvalidRowsMode = InvalidRowsMode,\n> = DataTableOwnProps &\n (\n | { table: DataTableSource }\n | (UseDataTableOptions<T, TableState, Sch, M> & {\n features?: readonly AnyFeature[]\n table?: undefined\n })\n )\n\n// --- component -----------------------------------------------------------------------------------\n\n/**\n * The zero-config table (§10). Renders what the installed features provide: sort buttons with\n * `aria-sort`, a search input bound to `input` (Enter = `flushSearch`, Esc = `clearSearch`),\n * pagination, and the Loading / Error / Empty states from `status` + `isInitialLoad` (§6.6).\n * Unstyled: style it through the `data-*` hooks (`data-status`, `data-initial-load`,\n * `data-invalid`, `data-sort`, …) or replace parts with `components`.\n *\n * `@datakable/react`'s `DataTable` installs no features with `data`; `datakable`'s installs the\n * default preset.\n */\nexport function DataTable<\n T,\n const Sch extends TableSchema | undefined = undefined,\n const M extends InvalidRowsMode = 'drop',\n>(props: DataTableProps<T, Sch, M>): ReactNode\nexport function DataTable(\n props: DataTableOwnProps & { table?: DataTableSource | undefined },\n): ReactNode {\n if (props.table !== undefined) {\n return (\n <DataTableView table={props.table} components={props.components} caption={props.caption} />\n )\n }\n return <ZeroConfig {...(props as DataTableOwnProps & { table?: undefined })} />\n}\n\nfunction ZeroConfig(props: DataTableOwnProps & { table?: undefined }) {\n const { components, caption, table: _, ...options } = props\n const view = useDataTable(options as never) as unknown as DataTableSource\n return <DataTableView table={view} components={components} caption={caption} />\n}\n\n/** Loose access to the installed features' APIs (the table is typed per feature set). */\ntype Api = DataTableSource & {\n toggleSort?(id: string, opts?: { multi?: boolean }): void\n getSortDirection?(id: string): 'asc' | 'desc' | false\n setSearch?(q: string): Promise<unknown>\n flushSearch?(): Promise<unknown>\n clearSearch?(): void\n setPageIndex?(i: number): void\n setPageSize?(n: number): void\n nextPage?(): void\n prevPage?(): void\n getPageCount?(): number\n canNextPage?(): boolean\n canPrevPage?(): boolean\n}\n\n/** The column fields `<DataTable />` reads (types are checked where the columns are written). */\ninterface ColumnLike {\n key?: unknown\n id?: unknown\n header?: unknown\n sortable?: unknown\n cell?: (ctx: { row: unknown; value: unknown }) => ReactNode\n format?: (value: unknown) => unknown\n invalidCell?: (ctx: {\n original: unknown\n value: unknown\n issues: readonly RowIssue[]\n }) => ReactNode\n}\n\ninterface Column {\n readonly def: ColumnLike\n readonly index: number\n readonly id: string\n readonly header: ReactNode\n}\n\n/** A cell callback that threw or broke its contract during this render (reported after commit). */\ninterface Failure {\n slot: string\n column: string\n row: number\n error?: unknown\n /** Bad return: what the contract expects and the type received. */\n expected?: string\n received?: string\n}\n\nconst WARN = '⚠'\nconst BUSY: readonly RowModelStatus[] = ['validating', 'pending', 'searching', 'fetching']\nconst LOADING: readonly RowModelStatus[] = ['validating', 'searching', 'fetching']\n\n/**\n * The table loads its rows asynchronously: a `fetcher`, or a feature with a `searchFn` (its\n * `defineOptions` values). With nothing applied yet, its `'pending'` is a load waiting to start\n * (deferred until the client, §7.2), so `<DataTable />` shows Loading — also in the server HTML.\n */\nfunction hasAsyncSource(table: DataTableSource): boolean {\n const options = table.getOptions() as { fetcher?: unknown; features?: unknown }\n if (typeof options.fetcher === 'function') return true\n if (!Array.isArray(options.features)) return false\n for (const f of options.features as readonly {\n options?: { values?: { searchFn?: unknown } }\n }[]) {\n if (typeof f?.options?.values?.searchFn === 'function') return true\n }\n return false\n}\n\nfunction resolveColumns(defs: readonly unknown[]): Column[] {\n const out: Column[] = []\n for (let index = 0; index < defs.length; index++) {\n const def = defs[index] as ColumnLike | null | undefined\n if (def === null || typeof def !== 'object') continue\n const id = typeof def.key === 'string' ? def.key : def.id\n if (typeof id !== 'string') continue\n out.push({ def, index, id, header: typeof def.header === 'string' ? def.header : id })\n }\n return out\n}\n\nconst segment = (s: PropertyKey | { readonly key: PropertyKey }) =>\n String(typeof s === 'object' ? s.key : s)\n\n/**\n * The redacted issue text for `title` (§5.1): the issue paths only. Schema messages embed the\n * received value, so they are never shown.\n */\nfunction issueTitle(issues: readonly RowIssue[]): string {\n const paths = new Set<string>()\n for (const i of issues) paths.add(i.path?.length ? i.path.map(segment).join('.') : '(row)')\n return `Invalid: ${[...paths].join(', ')}`\n}\n\n/** The table behind a `useDataTable` view (its prototype), for reporting diagnostics. */\nconst tableOf = (view: object): object =>\n Object.hasOwn(view, 'rowModel') ? (Object.getPrototypeOf(view) as object) : view\n\nfunction report(table: object, failures: readonly Failure[]) {\n const bySlot = new Map<string, Failure[]>()\n for (const f of failures) {\n const k = `${f.slot}\\0${f.expected === undefined ? 'throw' : 'return'}`\n const list = bySlot.get(k)\n if (list) list.push(f)\n else bySlot.set(k, [f])\n }\n for (const list of bySlot.values()) {\n const first = list[0] as Failure\n const rows = list.slice(0, 3).map((f) => f.row)\n const fallback = 'those cells rendered ⚠'\n try {\n if (first.expected === undefined) {\n const e = first.error\n emitTableDiagnostic(\n table,\n 'DK_CALLBACK_FAILED',\n {\n slot: first.slot,\n column: first.column,\n rows,\n count: list.length,\n fallback,\n ...(e instanceof Error ? { errorName: e.name } : {}),\n },\n { cause: e },\n )\n } else if (process.env.NODE_ENV !== 'production') {\n emitTableDiagnostic(table, 'DK_CALLBACK_BAD_RETURN', {\n slot: first.slot,\n column: first.column,\n expected: first.expected,\n received: first.received ?? 'undefined',\n count: list.length,\n rows,\n fallback,\n })\n }\n } catch {\n // Not a table made by createTable (a hand-made `table` prop): nothing to report to.\n }\n }\n}\n\n/**\n * Renders one cell's content; a throwing or contract-breaking callback renders `⚠`. An object\n * returned by `cell` / `invalidCell` (an element, an array) may hold components that throw while\n * React renders them, so it gets its own boundary; primitives cannot, so they get none (no cost).\n */\nfunction cellContent(\n col: Column,\n row: DataTableRow,\n failures: Failure[],\n table: object,\n): ReactNode {\n const def = col.def\n const value = row.getValue(col.id)\n const issues = row.valid ? undefined : row.getCellIssues(col.id)\n let slot = 'cell'\n try {\n if (issues !== undefined && issues.length > 0) {\n if (def.invalidCell) {\n slot = 'invalidCell'\n const out = def.invalidCell({ original: row.original, value, issues })\n if (matchesSlot(invalidCellSlot, out)) return guard(out, table, col, row, 'invalidCell')\n return bad(failures, col, row, slot, 'a value (not a Promise)', out)\n }\n return (\n <>\n {defaultFormat(value)}{' '}\n <span data-dk-invalid-marker=\"\" aria-hidden=\"true\">\n {WARN}\n </span>\n </>\n )\n }\n if (def.cell) {\n const out = def.cell({ row: row.original, value })\n if (matchesSlot(cellSlot, out)) return guard(out, table, col, row, 'cell')\n return bad(failures, col, row, slot, 'a value (not a Promise)', out)\n }\n if (def.format) {\n slot = 'format'\n const out = def.format(value)\n if (typeof out === 'string') return out\n bad(failures, col, row, slot, 'a string', out)\n return defaultFormat(value)\n }\n return defaultFormat(value)\n } catch (error) {\n failures.push({ slot: `columns[${col.index}].${slot}`, column: col.id, row: row.index, error })\n return WARN\n }\n}\n\n/** Wraps object content in a per-cell boundary (`render:cell`); primitives pass through as-is. */\nfunction guard(\n out: ReactNode,\n table: object,\n col: Column,\n row: DataTableRow,\n slot: 'cell' | 'invalidCell',\n): ReactNode {\n if (typeof out !== 'object' || out === null) return out\n return (\n <CellBoundary table={table} col={col} slot={slot} row={row.index} original={row.original}>\n {out}\n </CellBoundary>\n )\n}\n\nfunction bad(\n failures: Failure[],\n col: Column,\n row: DataTableRow,\n slot: string,\n expected: string,\n out: unknown,\n): ReactNode {\n failures.push({\n slot: `columns[${col.index}].${slot}`,\n column: col.id,\n row: row.index,\n expected,\n received: describeReturn(out),\n })\n return WARN\n}\n\nconst ariaSort = (dir: 'asc' | 'desc' | false) =>\n dir === 'asc' ? 'ascending' : dir === 'desc' ? 'descending' : 'none'\n\nfunction DataTableView(props: {\n table: DataTableSource\n components: DataTableComponents | undefined\n caption: ReactNode\n}) {\n const view = props.table as Api\n const parts = props.components ?? {}\n const { state, rowModel } = view\n const { rows, status, isInitialLoad } = rowModel\n const defs = view.getOptions().columns\n const data = view.getData()\n // Zero-config columns follow the data; given columns follow their reference (§10).\n const derivedFrom = defs === undefined ? data : undefined\n const columns = useMemo(\n () => resolveColumns(defs ?? (derivedFrom ? deriveColumns(derivedFrom) : [])),\n [defs, derivedFrom],\n )\n const failures: Failure[] = []\n const table = tableOf(view)\n // After commit, never during render: a sink may set state.\n useIsomorphicLayoutEffect(() => {\n if (failures.length > 0) report(table, failures)\n })\n\n const sortable = typeof view.toggleSort === 'function'\n const searchState = state.search as { input: string; status: SearchStatus } | undefined\n const paged = typeof view.getPageCount === 'function'\n // Loading (§6.6, §10): nothing to show yet and rows on their way — in flight, or deferred async\n // work that has not started (SSR, §7.2: 'pending').\n const loading =\n isInitialLoad && (LOADING.includes(status) || (status === 'pending' && hasAsyncSource(view)))\n const failed = isInitialLoad && status === 'error'\n const span = Math.max(columns.length, 1)\n const retryLoad = () => void view.refetch()\n\n const Search = parts.SearchInput ?? DefaultSearchInput\n const Pager = parts.Pagination ?? DefaultPagination\n const Empty = parts.Empty ?? DefaultEmpty\n const Loading = parts.Loading ?? DefaultLoading\n const ErrorState = parts.Error ?? DefaultError\n\n let body: ReactNode\n if (loading) {\n body = stateRow(span, 'loading', <Loading status={status} />)\n } else if (failed) {\n body = stateRow(span, 'error', <ErrorState status={status} reason=\"load\" retry={retryLoad} />)\n } else if (rows.length === 0) {\n body = stateRow(span, 'empty', <Empty filtered={rowModel.totalCount > 0} />)\n } else {\n body = rows.map((row) => {\n const valid = row.valid\n return (\n <tr\n key={row.key}\n data-invalid={valid ? undefined : 'true'}\n aria-invalid={valid ? undefined : 'true'}\n title={valid ? undefined : issueTitle(row.issues)}\n >\n {columns.map((col) => {\n const issues = valid ? undefined : row.getCellIssues(col.id)\n const broken = issues !== undefined && issues.length > 0\n return (\n <td\n key={col.index}\n data-invalid={broken ? 'true' : undefined}\n aria-invalid={broken ? 'true' : undefined}\n title={broken ? issueTitle(issues) : undefined}\n >\n {cellContent(col, row, failures, table)}\n </td>\n )\n })}\n </tr>\n )\n })\n }\n\n return (\n <div\n data-datakable=\"\"\n data-status={status}\n data-initial-load={isInitialLoad ? 'true' : 'false'}\n aria-busy={BUSY.includes(status) ? 'true' : undefined}\n >\n {searchState && (\n <div data-dk-search=\"\" data-status={searchState.status}>\n <Search\n value={searchState.input}\n status={searchState.status}\n onChange={(q) => void view.setSearch?.(q)}\n onSubmit={() => void view.flushSearch?.()}\n onClear={() => view.clearSearch?.()}\n />\n </div>\n )}\n {!loading && !failed && status !== 'idle' && (\n <RefreshIndicator status={status} retry={retryLoad} />\n )}\n <table>\n {props.caption != null && <caption>{props.caption}</caption>}\n <thead>\n <tr>\n {columns.map((col) => {\n const canSort = sortable && col.def.sortable !== false\n const dir = canSort ? (view.getSortDirection?.(col.id) ?? false) : false\n return (\n <th\n key={col.index}\n scope=\"col\"\n aria-sort={canSort ? ariaSort(dir) : undefined}\n data-sort={dir || undefined}\n >\n {canSort ? (\n <button\n type=\"button\"\n onClick={(e) => view.toggleSort?.(col.id, { multi: e.shiftKey })}\n >\n {col.header}\n <span data-dk-sort-indicator=\"\" aria-hidden=\"true\">\n {dir === 'asc' ? ' ▲' : dir === 'desc' ? ' ▼' : ''}\n </span>\n </button>\n ) : (\n col.header\n )}\n </th>\n )\n })}\n </tr>\n </thead>\n <tbody>\n <BodyBoundary\n table={table}\n resetKey={rowModel}\n fallback={(retry) =>\n stateRow(span, 'error', <ErrorState status={status} reason=\"render\" retry={retry} />)\n }\n >\n {body}\n </BodyBoundary>\n </tbody>\n </table>\n {paged && (\n <Pager\n pageIndex={(state.pagination as { pageIndex: number }).pageIndex}\n pageSize={(state.pagination as { pageSize: number }).pageSize}\n pageCount={view.getPageCount?.() ?? 0}\n canPrev={view.canPrevPage?.() ?? false}\n canNext={view.canNextPage?.() ?? false}\n prev={() => view.prevPage?.()}\n next={() => view.nextPage?.()}\n setPageIndex={(i) => view.setPageIndex?.(i)}\n setPageSize={(n) => view.setPageSize?.(n)}\n />\n )}\n </div>\n )\n}\n\nfunction stateRow(span: number, kind: string, content: ReactNode) {\n return (\n <tr data-dk-state={kind}>\n <td colSpan={span}>{content}</td>\n </tr>\n )\n}\n\n// --- error boundaries (DK_RENDER_FAILED) ---------------------------------------------------------\n\ninterface BoundaryProps {\n table: object\n /** A new value (the next row model) renders the body again. */\n resetKey: unknown\n fallback(retry: () => void): ReactNode\n children: ReactNode\n}\n\n/**\n * Catches a component that throws inside the body outside any cell boundary (§10: a\n * `components.Empty` / `Loading` / `Error` part): reports `DK_RENDER_FAILED` (`render:table`) and\n * shows the Error state; the header, search and pagination keep working.\n */\nclass BodyBoundary extends Component<BoundaryProps, { failed: boolean; key: unknown }> {\n override state = { failed: false, key: this.props.resetKey }\n\n static getDerivedStateFromError() {\n return { failed: true }\n }\n\n static getDerivedStateFromProps(props: BoundaryProps, state: { key: unknown }) {\n return props.resetKey === state.key ? null : { failed: false, key: props.resetKey }\n }\n\n override componentDidCatch(error: unknown) {\n try {\n emitTableDiagnostic(\n this.props.table,\n 'DK_RENDER_FAILED',\n { op: 'render:table', ...(error instanceof Error ? { errorName: error.name } : {}) },\n { cause: error },\n )\n } catch {\n // Not a table made by createTable: nothing to report to.\n }\n }\n\n retry = () => this.setState({ failed: false })\n\n override render() {\n return this.state.failed ? this.props.fallback(this.retry) : this.props.children\n }\n}\n\ninterface CellBoundaryProps {\n table: object\n col: Column\n slot: 'cell' | 'invalidCell'\n /** The row's index in the data (`row.index`). */\n row: number\n /** The row's data: a new object (or new columns) renders a failed cell again. */\n original: unknown\n children: ReactNode\n}\n\ninterface CellBoundaryState {\n failed: boolean\n original: unknown\n def: ColumnLike\n}\n\n/**\n * One cell's boundary (`render:cell`, §3.4): a component returned by `cell` / `invalidCell` that\n * throws renders `⚠` in that cell only and reports `DK_RENDER_FAILED` (deduped per column); the\n * other cells, rows and the table keep working. Only wraps object content (see `guard`).\n */\nclass CellBoundary extends Component<CellBoundaryProps, CellBoundaryState> {\n override state: CellBoundaryState = {\n failed: false,\n original: this.props.original,\n def: this.props.col.def,\n }\n\n static getDerivedStateFromError() {\n return { failed: true }\n }\n\n static getDerivedStateFromProps(props: CellBoundaryProps, state: CellBoundaryState) {\n return props.original === state.original && props.col.def === state.def\n ? null\n : { failed: false, original: props.original, def: props.col.def }\n }\n\n override componentDidCatch(error: unknown) {\n const { table, col, slot, row } = this.props\n try {\n emitTableDiagnostic(\n table,\n 'DK_RENDER_FAILED',\n {\n op: 'render:cell',\n slot: `columns[${col.index}].${slot}`,\n column: col.id,\n row,\n ...(error instanceof Error ? { errorName: error.name } : {}),\n },\n { cause: error },\n )\n } catch {\n // Not a table made by createTable: nothing to report to.\n }\n }\n\n override render() {\n return this.state.failed ? WARN : this.props.children\n }\n}\n\n// --- default parts (unstyled) --------------------------------------------------------------------\n\nfunction DefaultSearchInput({ value, status, onChange, onSubmit, onClear }: SearchInputProps) {\n return (\n <>\n <input\n type=\"search\"\n aria-label=\"Search\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') onSubmit()\n else if (e.key === 'Escape') onClear()\n }}\n />\n {(status === 'pending' || status === 'searching') && (\n <span role=\"status\" data-dk-spinner=\"\" aria-label=\"Searching\" />\n )}\n </>\n )\n}\n\nfunction DefaultPagination(p: PaginationProps) {\n return (\n <nav aria-label=\"Pagination\" data-dk-pagination=\"\">\n <button type=\"button\" aria-label=\"Previous page\" disabled={!p.canPrev} onClick={p.prev}>\n ‹\n </button>\n <span data-dk-page=\"\">\n Page {p.pageIndex + 1} of {Math.max(p.pageCount, 1)}\n </span>\n <button type=\"button\" aria-label=\"Next page\" disabled={!p.canNext} onClick={p.next}>\n ›\n </button>\n </nav>\n )\n}\n\nconst DefaultEmpty = ({ filtered }: EmptyProps) => <>{filtered ? 'No matching rows' : 'No rows'}</>\n\nconst DefaultLoading = (_: LoadingProps) => <span role=\"status\">Loading…</span>\n\nfunction DefaultError({ reason, retry }: ErrorProps) {\n return (\n <div role=\"alert\">\n {reason === 'load' ? 'Could not load the rows.' : 'Could not display the rows.'}{' '}\n <button type=\"button\" onClick={retry}>\n Retry\n </button>\n </div>\n )\n}\n\n/** Over stale rows (§6.6): the table is busy, or a later update failed. */\nfunction RefreshIndicator({ status, retry }: { status: RowModelStatus; retry(): void }) {\n if (status === 'error') {\n return (\n <div data-dk-refresh=\"\" data-status={status} role=\"alert\">\n Could not refresh; showing the previous rows.{' '}\n <button type=\"button\" onClick={retry}>\n Retry\n </button>\n </div>\n )\n }\n return (\n <div data-dk-refresh=\"\" data-status={status} role=\"status\">\n Updating…\n </div>\n )\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/data-table.tsx"],"sourcesContent":["'use client'\n\nimport {\n type AnyFeature,\n boundaryDiagnostics,\n defaultFormat,\n deriveColumns,\n describeReturn,\n emitTableDiagnostic,\n type InvalidRowsMode,\n keepDiagnostics,\n matchesSlot,\n pipelineDiagnostics,\n type RowIssue,\n type RowModelStatus,\n type SearchStatus,\n slotDiagnostics,\n type TableSchema,\n type TableState,\n} from '@datakable/core'\nimport {\n Component,\n type ComponentType,\n type CSSProperties,\n type ReactNode,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n} from 'react'\nimport {\n cellSlot,\n invalidCellSlot,\n registerRenderOperations,\n renderDiagnostics,\n} from './render-codes'\nimport { type UseDataTableOptions, useDataTable } from './use-data-table'\n\n// The codes this module emits (bundlers keep the definitions; see core's `keepDiagnostics`).\nkeepDiagnostics(renderDiagnostics, boundaryDiagnostics, slotDiagnostics, pipelineDiagnostics)\n\n// The ops its boundaries stand for (§3.4: `render:table`, `render:cell`). Registered at import:\n// the dist is one file per module (T37), so hook-only bundles (`useDataTable` without\n// `<DataTable />`) never include this module, and this call ships only with `<DataTable />`.\nregisterRenderOperations()\n\n// No @types/node here: declare just what is read.\ndeclare const process: { env: { NODE_ENV?: string } }\n\n/** No layout effects on the server (React 18 warns about them there). */\nconst useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect\n\n// --- public types --------------------------------------------------------------------------------\n\n/** Props of `components.SearchInput`: the search feature's `input`, bound both ways. */\nexport interface SearchInputProps {\n /** `state.search.input` (updates as the user types). */\n value: string\n /** `state.search.status`: `pending` while a debounce waits, `searching` while a request runs. */\n status: SearchStatus\n /** `setSearch(value)`. */\n onChange(value: string): void\n /** `flushSearch()` (Enter). */\n onSubmit(): void\n /** `clearSearch()` (Esc). */\n onClear(): void\n /** The input's accessible name: `'Search <caption>'` with a text `caption`, else `'Search'`. */\n label: string\n}\n\n/** Props of `components.Pagination` (the pagination feature's state and API). */\nexport interface PaginationProps {\n pageIndex: number\n pageSize: number\n pageCount: number\n canPrev: boolean\n canNext: boolean\n prev(): void\n next(): void\n setPageIndex(index: number): void\n setPageSize(size: number): void\n /** The pager's accessible name: `'<caption> pagination'` with a text `caption`, else `'Pagination'`. */\n label: string\n}\n\n/** Props of `components.Empty`: shown when the table has loaded and has no rows to show. */\nexport interface EmptyProps {\n /** `true` when there is data but filters / search removed every row. */\n filtered: boolean\n}\n\n/** Props of `components.Loading`: the first load (`isInitialLoad`, nothing to show yet). */\nexport interface LoadingProps {\n status: RowModelStatus\n}\n\n/** Props of `components.Error`: the first load failed, or the body failed to render. */\nexport interface ErrorProps {\n status: RowModelStatus\n /**\n * `'load'`: `status === 'error'` on the first load. `'render'`: a component in the body outside\n * the cells threw (a component inside a cell renders `⚠` in that cell only).\n */\n reason: 'load' | 'render'\n /** `'load'`: `refetch()`. `'render'`: renders the body again. */\n retry(): void\n}\n\n/** `<DataTable components={...} />`: replaces the default parts (unstyled, `data-*` hooks only). */\nexport interface DataTableComponents {\n SearchInput?: ComponentType<SearchInputProps>\n Pagination?: ComponentType<PaginationProps>\n Empty?: ComponentType<EmptyProps>\n Loading?: ComponentType<LoadingProps>\n Error?: ComponentType<ErrorProps>\n}\n\n/** What `<DataTable table={...} />` reads: the value `useDataTable` returns. */\nexport interface DataTableSource {\n readonly state: TableState\n readonly rowModel: {\n readonly rows: readonly DataTableRow[]\n readonly filteredCount: number\n readonly totalCount: number\n readonly status: RowModelStatus\n readonly isInitialLoad: boolean\n }\n getOptions(): { readonly columns?: readonly unknown[] | undefined }\n getData(): readonly unknown[]\n refetch(): Promise<unknown>\n}\n\n/** The part of a `Row` (§6.6) `<DataTable />` renders from. */\ninterface DataTableRow {\n readonly key: string | number\n readonly index: number\n readonly original: unknown\n readonly valid: boolean\n readonly issues: readonly RowIssue[]\n getValue(id: string): unknown\n getCellIssues(id: string): readonly RowIssue[]\n}\n\n/** `<DataTable />`'s own props, next to `table` or the `useDataTable` options. */\nexport interface DataTableOwnProps {\n components?: DataTableComponents | undefined\n caption?: ReactNode\n}\n\n/**\n * `<DataTable table={useDataTable(...)} />`, or `<DataTable data={rows} />` with the\n * `useDataTable` options as props (zero-config: columns derived from the data).\n */\nexport type DataTableProps<\n T = unknown,\n Sch = undefined,\n M extends InvalidRowsMode = InvalidRowsMode,\n> = DataTableOwnProps &\n (\n | { table: DataTableSource }\n | (UseDataTableOptions<T, TableState, Sch, M> & {\n features?: readonly AnyFeature[]\n table?: undefined\n })\n )\n\n// --- component -----------------------------------------------------------------------------------\n\n/**\n * The zero-config table (§10). Renders what the installed features provide: sort buttons with\n * `aria-sort`, a search input bound to `input` (Enter = `flushSearch`, Esc = `clearSearch`),\n * pagination, and the Loading / Error / Empty states from `status` + `isInitialLoad` (§6.6).\n * Unstyled: style it through the `data-*` hooks (`data-status`, `data-initial-load`,\n * `data-invalid`, `data-sort`, …) or replace parts with `components`.\n *\n * `@datakable/react`'s `DataTable` installs no features with `data`; `datakable`'s installs the\n * default preset.\n */\nexport function DataTable<\n T,\n const Sch extends TableSchema | undefined = undefined,\n const M extends InvalidRowsMode = 'drop',\n>(props: DataTableProps<T, Sch, M>): ReactNode\nexport function DataTable(\n props: DataTableOwnProps & { table?: DataTableSource | undefined },\n): ReactNode {\n if (props.table !== undefined) {\n return (\n <DataTableView table={props.table} components={props.components} caption={props.caption} />\n )\n }\n return <ZeroConfig {...(props as DataTableOwnProps & { table?: undefined })} />\n}\n\nfunction ZeroConfig(props: DataTableOwnProps & { table?: undefined }) {\n const { components, caption, table: _, ...options } = props\n const view = useDataTable(options as never) as unknown as DataTableSource\n return <DataTableView table={view} components={components} caption={caption} />\n}\n\n/** Loose access to the installed features' APIs (the table is typed per feature set). */\ntype Api = DataTableSource & {\n toggleSort?(id: string, opts?: { multi?: boolean }): void\n setSearch?(q: string): Promise<unknown>\n flushSearch?(): Promise<unknown>\n clearSearch?(): void\n setPageIndex?(i: number): void\n setPageSize?(n: number): void\n nextPage?(): void\n prevPage?(): void\n getPageCount?(): number\n canNextPage?(): boolean\n canPrevPage?(): boolean\n}\n\n/** The column fields `<DataTable />` reads (types are checked where the columns are written). */\ninterface ColumnLike {\n key?: unknown\n id?: unknown\n header?: unknown\n sortable?: unknown\n cell?: (ctx: { row: unknown; value: unknown }) => ReactNode\n format?: (value: unknown) => unknown\n invalidCell?: (ctx: {\n original: unknown\n value: unknown\n issues: readonly RowIssue[]\n }) => ReactNode\n}\n\ninterface Column {\n readonly def: ColumnLike\n readonly index: number\n readonly id: string\n readonly header: ReactNode\n}\n\n/** A cell callback that threw or broke its contract during this render (reported after commit). */\ninterface Failure {\n slot: string\n column: string\n row: number\n error?: unknown\n /** Bad return: what the contract expects and the type received. */\n expected?: string\n received?: string\n /** A value the default formatter cannot render (`DK_UNRENDERABLE_VALUE`): its type. */\n lost?: string\n index?: number\n}\n\nconst WARN = '⚠'\n/** A failed cell's content: the `⚠` with its text alternative (#60). One element for every cell. */\nconst FAILED = (\n <span role=\"img\" aria-label=\"Could not display this value\">\n {WARN}\n </span>\n)\nconst BUSY: readonly RowModelStatus[] = ['validating', 'pending', 'searching', 'fetching']\nconst LOADING: readonly RowModelStatus[] = ['validating', 'searching', 'fetching']\n\n/**\n * The table loads its rows asynchronously: a `fetcher`, or a feature with a `searchFn` (its\n * `defineOptions` values). With nothing applied yet, its `'pending'` is a load waiting to start\n * (deferred until the client, §7.2), so `<DataTable />` shows Loading — also in the server HTML.\n */\nfunction hasAsyncSource(table: DataTableSource): boolean {\n const options = table.getOptions() as { fetcher?: unknown; features?: unknown }\n if (typeof options.fetcher === 'function') return true\n if (!Array.isArray(options.features)) return false\n for (const f of options.features as readonly {\n options?: { values?: { searchFn?: unknown } }\n }[]) {\n if (typeof f?.options?.values?.searchFn === 'function') return true\n }\n return false\n}\n\nfunction resolveColumns(defs: readonly unknown[]): Column[] {\n const out: Column[] = []\n for (let index = 0; index < defs.length; index++) {\n const def = defs[index] as ColumnLike | null | undefined\n if (def === null || typeof def !== 'object') continue\n const id = typeof def.key === 'string' ? def.key : def.id\n if (typeof id !== 'string') continue\n out.push({ def, index, id, header: typeof def.header === 'string' ? def.header : id })\n }\n return out\n}\n\nconst segment = (s: PropertyKey | { readonly key: PropertyKey }) =>\n String(typeof s === 'object' ? s.key : s)\n\n/**\n * The redacted issue text for `title` (§5.1): the issue paths only. Schema messages embed the\n * received value, so they are never shown.\n */\nfunction issueTitle(issues: readonly RowIssue[]): string {\n const paths = new Set<string>()\n for (const i of issues) paths.add(i.path?.length ? i.path.map(segment).join('.') : '(row)')\n return `Invalid: ${[...paths].join(', ')}`\n}\n\n/** The table behind a `useDataTable` view (its prototype), for reporting diagnostics. */\nconst tableOf = (view: object): object =>\n Object.hasOwn(view, 'rowModel') ? (Object.getPrototypeOf(view) as object) : view\n\nfunction report(table: object, failures: readonly Failure[]) {\n const bySlot = new Map<string, Failure[]>()\n for (const f of failures) {\n const k = `${f.slot}\\0${f.lost ? '' : f.expected === undefined ? 'throw' : 'return'}`\n const list = bySlot.get(k)\n if (list) list.push(f)\n else bySlot.set(k, [f])\n }\n for (const list of bySlot.values()) {\n const first = list[0] as Failure\n const rows = list.slice(0, 3).map((f) => f.row)\n const fallback = 'cells-warn' // prose key: 'those cells rendered ⚠'\n try {\n if (first.lost !== undefined) {\n // Deduped by the table per column and data reference, like the column cache's report.\n emitTableDiagnostic(table, 'DK_UNRENDERABLE_VALUE', {\n column: first.column,\n index: first.index as number,\n count: list.length,\n rows,\n valueType: first.lost,\n })\n } else if (first.expected === undefined) {\n const e = first.error\n emitTableDiagnostic(\n table,\n 'DK_CALLBACK_FAILED',\n {\n slot: first.slot,\n column: first.column,\n rows,\n count: list.length,\n fallback,\n ...(e instanceof Error ? { errorName: e.name } : {}),\n },\n { cause: e },\n )\n } else if (process.env.NODE_ENV !== 'production') {\n emitTableDiagnostic(table, 'DK_CALLBACK_BAD_RETURN', {\n slot: first.slot,\n column: first.column,\n expected: first.expected,\n received: first.received ?? 'undefined',\n count: list.length,\n rows,\n fallback,\n })\n }\n } catch {\n // Not a table made by createTable (a hand-made `table` prop): nothing to report to.\n }\n }\n}\n\n/**\n * The value `defaultFormat` could not render in the current cell (an object, array or function,\n * never `undefined`). One module-level callback: no closure per cell.\n */\nlet lost: unknown\nconst onLost = (v: unknown) => {\n lost = v\n}\n\n/**\n * Renders one cell's content; a throwing or contract-breaking callback renders `⚠`. An object\n * returned by `cell` / `invalidCell` (an element, an array) may hold components that throw while\n * React renders them, so it gets its own boundary; primitives cannot, so they get none (no cost).\n */\nfunction cellContent(\n col: Column,\n row: DataTableRow,\n failures: Failure[],\n table: object,\n): ReactNode {\n const def = col.def\n const value = row.getValue(col.id)\n const issues = row.valid ? undefined : row.getCellIssues(col.id)\n let slot = 'cell'\n try {\n if (issues !== undefined && issues.length > 0) {\n if (def.invalidCell) {\n slot = 'invalidCell'\n const out = def.invalidCell({ original: row.original, value, issues })\n if (matchesSlot(invalidCellSlot, out)) return guard(out, table, col, row, 'invalidCell')\n return bad(failures, col, row, slot, 'a value (not a Promise)', out)\n }\n return (\n <>\n {defaultFormat(value)}{' '}\n <span data-dk-invalid-marker=\"\" aria-hidden=\"true\">\n {WARN}\n </span>\n </>\n )\n }\n if (def.cell) {\n const out = def.cell({ row: row.original, value })\n if (matchesSlot(cellSlot, out)) return guard(out, table, col, row, 'cell')\n return bad(failures, col, row, slot, 'a value (not a Promise)', out)\n }\n if (def.format) {\n slot = 'format'\n const out = def.format(value)\n if (typeof out === 'string') return out\n bad(failures, col, row, slot, 'a string', out)\n return defaultFormat(value)\n }\n const text = defaultFormat(value, onLost)\n if (lost !== undefined) {\n const { id: column, index } = col\n const type = Array.isArray(lost) ? 'array' : typeof lost\n failures.push({ slot: `columns[${index}]`, column, index, row: row.index, lost: type })\n lost = undefined\n }\n return text\n } catch (error) {\n failures.push({ slot: `columns[${col.index}].${slot}`, column: col.id, row: row.index, error })\n return FAILED\n }\n}\n\n/** Wraps object content in a per-cell boundary (`render:cell`); primitives pass through as-is. */\nfunction guard(\n out: ReactNode,\n table: object,\n col: Column,\n row: DataTableRow,\n slot: 'cell' | 'invalidCell',\n): ReactNode {\n if (typeof out !== 'object' || out === null) return out\n return (\n <Boundary\n table={table}\n col={col}\n slot={slot}\n row={row.index}\n resetKey={row.original}\n fallback={failedCell}\n >\n {out}\n </Boundary>\n )\n}\n\nfunction bad(\n failures: Failure[],\n col: Column,\n row: DataTableRow,\n slot: string,\n expected: string,\n out: unknown,\n): ReactNode {\n failures.push({\n slot: `columns[${col.index}].${slot}`,\n column: col.id,\n row: row.index,\n expected,\n received: describeReturn(out),\n })\n return FAILED\n}\n\ntype Page = { pageIndex: number; pageSize: number }\n\n/** Visually hidden, still read by screen readers (inline: the table ships no CSS). */\nconst HIDDEN: CSSProperties = {\n position: 'absolute',\n width: 1,\n height: 1,\n overflow: 'hidden',\n clipPath: 'inset(50%)',\n whiteSpace: 'nowrap',\n}\n\nconst serverPaged = (view: Api) =>\n (view.getOptions() as { features?: readonly { stage?: string; mode?: string }[] }).features?.some(\n (f) => f?.stage === 'paginate' && f.mode === 'server',\n ) === true\n\n/**\n * The pager of the rendered snapshot, like the pagination API computes it live: the hydrating\n * render must match the server HTML even when the table changed meanwhile (#16). Client mode\n * pages over the row model's `filteredCount`; server mode over the fetched `rowCount`, which only\n * the table knows (and no render changes).\n */\nfunction pages(view: Api, page: Page, count: number, server: boolean) {\n if (server) {\n return {\n ...page,\n pageCount: view.getPageCount?.() ?? 0,\n canPrev: view.canPrevPage?.() ?? false,\n canNext: view.canNextPage?.() ?? false,\n }\n }\n const pageCount = Math.ceil(count / page.pageSize)\n const index = Math.min(page.pageIndex, Math.max(pageCount - 1, 0))\n return { ...page, pageCount, canPrev: index > 0, canNext: index + 1 < pageCount }\n}\n\nconst ariaSort = (dir: 'asc' | 'desc' | false) =>\n dir === 'asc' ? 'ascending' : dir === 'desc' ? 'descending' : 'none'\n\nfunction DataTableView(props: {\n table: DataTableSource\n components: DataTableComponents | undefined\n caption: ReactNode\n}) {\n const view = props.table as Api\n const parts = props.components ?? {}\n const { state, rowModel } = view\n const { rows, status, isInitialLoad } = rowModel\n const defs = view.getOptions().columns\n const data = view.getData()\n // Zero-config columns follow the data; given columns follow their reference (§10).\n const derivedFrom = defs === undefined ? data : undefined\n const columns = useMemo(\n () => resolveColumns(defs ?? (derivedFrom ? deriveColumns(derivedFrom) : [])),\n [defs, derivedFrom],\n )\n const failures: Failure[] = []\n const table = tableOf(view)\n // After commit, never during render: a sink may set state.\n useIsomorphicLayoutEffect(() => {\n if (failures.length > 0) report(table, failures)\n })\n\n const sortable = typeof view.toggleSort === 'function'\n const sortBy = (state.sorting as { by: readonly { id: string; desc?: boolean }[] } | undefined)\n ?.by\n const searchState = state.search as { input: string; status: SearchStatus } | undefined\n const paged = typeof view.getPageCount === 'function'\n // Loading (§6.6, §10): nothing to show yet and rows on their way — in flight, or deferred async\n // work that has not started (SSR, §7.2: 'pending').\n const loading =\n isInitialLoad && (LOADING.includes(status) || (status === 'pending' && hasAsyncSource(view)))\n const failed = isInitialLoad && status === 'error'\n const span = Math.max(columns.length, 1)\n const tableEl = useRef<HTMLTableElement>(null)\n /**\n * Retry unmounts the focused Retry button (Error → Loading → rows, or the refresh error): focus\n * the table first, else it falls to `<body>` (#56). Only when focus is in a state row or the\n * refresh indicator, so a retry called from elsewhere leaves focus alone.\n */\n const keepFocus = () => {\n const t = tableEl.current\n const from = document.activeElement?.closest('[data-dk-state],[data-dk-refresh]')\n if (t && from && t.parentElement?.contains(from)) {\n t.tabIndex = -1\n t.focus()\n }\n }\n const retryLoad = () => {\n keepFocus()\n void view.refetch()\n }\n const server = paged && serverPaged(view)\n const pager = paged ? pages(view, state.pagination as Page, rowModel.filteredCount, server) : null\n\n // Status messages (#54): one polite live region, in the markup from the first render (a region\n // inserted with its text is often not announced). Written after commit, only when the message\n // changes, so the first render (and the hydrating one) matches the server HTML.\n // Worded unlike the visible parts (\"No rows\", \"Searching…\"), so a text query finds one element.\n const n = rowModel.filteredCount\n const count = `${n} row${n === 1 ? '' : 's'}`\n const page = pager ? `${pager.pageIndex + 1} of ${Math.max(pager.pageCount, 1)}` : ''\n let said = ''\n if (searchState?.status === 'pending' || searchState?.status === 'searching') {\n said = 'Searching rows…'\n } else if (status === 'idle' && !loading) {\n // Server mode: `filteredCount` is the page's rows, so only the page (or \"0 rows\").\n said = server && n ? `Page ${page}` : page ? `${count}, page ${page}` : count\n }\n const liveEl = useRef<HTMLDivElement>(null)\n const saidBefore = useRef<string | null>(null)\n useEffect(() => {\n const el = liveEl.current\n if (el && saidBefore.current !== null && saidBefore.current !== said) el.textContent = said\n saidBefore.current = said\n })\n\n // A text caption names the search box and the pager, so two tables get distinct names (#59).\n const cap = props.caption\n const named = typeof cap === 'string' || typeof cap === 'number' ? `${cap}` : ''\n\n const Empty = parts.Empty ?? DefaultEmpty\n const Loading = parts.Loading ?? DefaultLoading\n const ErrorState = parts.Error ?? DefaultError\n\n let body: ReactNode\n if (loading) {\n body = stateRow(span, 'loading', <Loading status={status} />)\n } else if (failed) {\n body = stateRow(span, 'error', <ErrorState status={status} reason=\"load\" retry={retryLoad} />)\n } else if (rows.length === 0) {\n body = stateRow(span, 'empty', <Empty filtered={rowModel.totalCount > 0} />)\n } else {\n body = rows.map((row) => {\n const valid = row.valid\n return (\n <tr\n key={row.key}\n data-invalid={valid ? undefined : 'true'}\n aria-invalid={valid ? undefined : 'true'}\n title={valid ? undefined : issueTitle(row.issues)}\n >\n {columns.map((col) => {\n const issues = valid ? undefined : row.getCellIssues(col.id)\n const broken = issues !== undefined && issues.length > 0\n return (\n <td\n key={col.index}\n data-invalid={broken ? 'true' : undefined}\n aria-invalid={broken ? 'true' : undefined}\n title={broken ? issueTitle(issues) : undefined}\n >\n {cellContent(col, row, failures, table)}\n </td>\n )\n })}\n </tr>\n )\n })\n }\n\n // The server runs no effects: report now (setting state is a no-op there).\n if (typeof window === 'undefined' && failures.length > 0) report(table, failures)\n\n return (\n <div\n data-datakable=\"\"\n data-status={status}\n data-initial-load={isInitialLoad ? 'true' : 'false'}\n aria-busy={BUSY.includes(status) ? 'true' : undefined}\n >\n {searchState && (\n <div data-dk-search=\"\" data-status={searchState.status}>\n {part(table, 'SearchInput', parts.SearchInput, DefaultSearchInput, {\n value: searchState.input,\n status: searchState.status,\n onChange: (q) => void view.setSearch?.(q),\n onSubmit: () => void view.flushSearch?.(),\n onClear: () => view.clearSearch?.(),\n label: named ? `Search ${named}` : 'Search',\n })}\n </div>\n )}\n {!loading && !failed && status !== 'idle' && (\n <RefreshIndicator status={status} retry={retryLoad} />\n )}\n <table ref={tableEl}>\n {props.caption != null && <caption>{props.caption}</caption>}\n <thead>\n <tr>\n {columns.map((col) => {\n const canSort = sortable && col.def.sortable !== false\n // From the rendered state, not the live table (the hydrating render, #16).\n const e = canSort ? sortBy?.find((x) => x.id === col.id) : undefined\n const dir = e ? (e.desc ? 'desc' : 'asc') : false\n return (\n <th\n key={col.index}\n scope=\"col\"\n aria-sort={canSort ? ariaSort(dir) : undefined}\n data-sort={dir || undefined}\n >\n {canSort ? (\n <button\n type=\"button\"\n onClick={(e) => view.toggleSort?.(col.id, { multi: e.shiftKey })}\n >\n {col.header}\n <span data-dk-sort-indicator=\"\" aria-hidden=\"true\">\n {dir === 'asc' ? ' ▲' : dir === 'desc' ? ' ▼' : ''}\n </span>\n </button>\n ) : (\n col.header\n )}\n </th>\n )\n })}\n </tr>\n </thead>\n <tbody>\n <Boundary\n table={table}\n resetKey={rowModel}\n fallback={(retry) =>\n stateRow(\n span,\n 'error',\n <ErrorState\n status={status}\n reason=\"render\"\n retry={() => {\n keepFocus()\n retry()\n }}\n />,\n )\n }\n >\n {body}\n </Boundary>\n </tbody>\n </table>\n {pager &&\n part(table, 'Pagination', parts.Pagination, DefaultPagination, {\n ...pager,\n prev: () => view.prevPage?.(),\n next: () => view.nextPage?.(),\n setPageIndex: (i) => view.setPageIndex?.(i),\n setPageSize: (n) => view.setPageSize?.(n),\n label: named ? `${named} pagination` : 'Pagination',\n })}\n <div aria-live=\"polite\" style={HIDDEN} ref={liveEl} />\n </div>\n )\n}\n\n/**\n * A search / pager part (outside the body): a custom one that throws is replaced by the default\n * part (`DK_RENDER_FAILED` at `components.<name>`) until another component is passed.\n */\nfunction part<P extends object>(\n table: object,\n name: 'SearchInput' | 'Pagination',\n Custom: ComponentType<P> | undefined,\n Default: ComponentType<P>,\n props: P,\n): ReactNode {\n if (!Custom) return <Default {...props} />\n return (\n <Boundary\n table={table}\n slot={`components.${name}`}\n resetKey={Custom}\n fallback={() => <Default {...props} />}\n >\n <Custom {...props} />\n </Boundary>\n )\n}\n\nfunction stateRow(span: number, kind: string, content: ReactNode) {\n return (\n <tr data-dk-state={kind}>\n <td colSpan={span}>{content}</td>\n </tr>\n )\n}\n\n// --- error boundaries (DK_RENDER_FAILED) ---------------------------------------------------------\n\ninterface BoundaryProps {\n table: object\n /**\n * A part outside the body: `components.SearchInput` (reported as the slot). A cell boundary:\n * `cell` / `invalidCell` (reported as `columns[<index>].<slot>`).\n */\n slot?: string\n /** A cell boundary (`render:cell`): the cell's column. A new `def` renders a failed cell again. */\n col?: Column\n /** A cell boundary: the row's index in the data (`row.index`). */\n row?: number\n /** A new value (the next row model; a cell's row data) renders the children again. */\n resetKey: unknown\n fallback(retry: () => void): ReactNode\n children: ReactNode\n}\n\ninterface BoundaryState {\n failed: boolean\n key: unknown\n def: ColumnLike | undefined\n}\n\n/**\n * Catches a component that throws (`DK_RENDER_FAILED`) and renders `fallback` instead; the rest of\n * the table keeps working. Three uses: the body outside any cell (`render:table`, §10: a\n * `components.Empty` / `Loading` / `Error` part) shows the Error state; a custom search / pager\n * part (`render:table` at `components.<name>`) is replaced by the default part; and one cell\n * (`render:cell`, §3.4, with `col`): a component returned by `cell` / `invalidCell` renders `⚠` in\n * that cell only (deduped per column). A cell boundary only wraps object content (see `guard`).\n */\nclass Boundary extends Component<BoundaryProps, BoundaryState> {\n override state: BoundaryState = {\n failed: false,\n key: this.props.resetKey,\n def: this.props.col?.def,\n }\n\n static getDerivedStateFromError() {\n return { failed: true }\n }\n\n static getDerivedStateFromProps(props: BoundaryProps, state: BoundaryState) {\n const def = props.col?.def\n return props.resetKey === state.key && def === state.def\n ? null\n : { failed: false, key: props.resetKey, def }\n }\n\n override componentDidCatch(error: unknown) {\n const { table, col, slot, row } = this.props\n try {\n emitTableDiagnostic(\n table,\n 'DK_RENDER_FAILED',\n {\n op: col ? 'render:cell' : 'render:table',\n ...(slot ? { slot: col ? `columns[${col.index}].${slot}` : slot } : {}),\n ...(col ? { column: col.id, row: row as number } : {}),\n ...(error instanceof Error ? { errorName: error.name } : {}),\n },\n { cause: error },\n )\n } catch {\n // Not a table made by createTable: nothing to report to.\n }\n }\n\n retry = () => this.setState({ failed: false })\n\n override render() {\n return this.state.failed ? this.props.fallback(this.retry) : this.props.children\n }\n}\n\n/** A failed cell's fallback (one function for every cell boundary: no closure per cell). */\nconst failedCell = () => FAILED\n\n// --- default parts (unstyled) --------------------------------------------------------------------\n\nfunction DefaultSearchInput({\n value,\n status,\n onChange,\n onSubmit,\n onClear,\n label,\n}: SearchInputProps) {\n return (\n <>\n <input\n type=\"search\"\n aria-label={label}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') onSubmit()\n else if (e.key === 'Escape') onClear()\n }}\n />\n {(status === 'pending' || status === 'searching') && (\n // Decorative: the live region says \"Searching…\" (an empty status announces nothing).\n <span data-dk-spinner=\"\" aria-hidden=\"true\" />\n )}\n </>\n )\n}\n\n/**\n * `aria-disabled`, not `disabled`, at the first / last page: a disabled button loses focus (to\n * `<body>`) the moment paging makes it unavailable (#55).\n */\nfunction DefaultPagination(p: PaginationProps) {\n return (\n <nav aria-label={p.label} data-dk-pagination=\"\">\n <button\n type=\"button\"\n aria-label=\"Previous page\"\n aria-disabled={!p.canPrev || undefined}\n onClick={p.canPrev ? p.prev : undefined}\n >\n ‹\n </button>\n <span data-dk-page=\"\">\n Page {p.pageIndex + 1} of {Math.max(p.pageCount, 1)}\n </span>\n <button\n type=\"button\"\n aria-label=\"Next page\"\n aria-disabled={!p.canNext || undefined}\n onClick={p.canNext ? p.next : undefined}\n >\n ›\n </button>\n </nav>\n )\n}\n\nconst DefaultEmpty = ({ filtered }: EmptyProps) => <>{filtered ? 'No matching rows' : 'No rows'}</>\n\nconst DefaultLoading = (_: LoadingProps) => <span role=\"status\">Loading…</span>\n\nfunction DefaultError({ reason, retry }: ErrorProps) {\n return (\n <div role=\"alert\">\n {reason === 'load' ? 'Could not load the rows.' : 'Could not display the rows.'}{' '}\n <button type=\"button\" onClick={retry}>\n Retry\n </button>\n </div>\n )\n}\n\n/** Over stale rows (§6.6): the table is busy, or a later update failed. */\nfunction RefreshIndicator({ status, retry }: { status: RowModelStatus; retry(): void }) {\n if (status === 'error') {\n return (\n <div data-dk-refresh=\"\" data-status={status} role=\"alert\">\n Could not refresh; showing the previous rows.{' '}\n <button type=\"button\" onClick={retry}>\n Retry\n </button>\n </div>\n )\n }\n return (\n <div data-dk-refresh=\"\" data-status={status} role=\"status\">\n Updating…\n </div>\n )\n}\n"],"mappings":"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","names":["e","n"]}
@@ -9,13 +9,15 @@ import {
9
9
  emitTableDiagnostic,
10
10
  keepDiagnostics,
11
11
  matchesSlot,
12
+ pipelineDiagnostics,
12
13
  slotDiagnostics
13
14
  } from "@datakable/core";
14
15
  import {
15
16
  Component,
16
17
  useEffect,
17
18
  useLayoutEffect,
18
- useMemo
19
+ useMemo,
20
+ useRef
19
21
  } from "react";
20
22
  import {
21
23
  cellSlot,
@@ -25,7 +27,7 @@ import {
25
27
  } from "./render-codes.js";
26
28
  import { useDataTable } from "./use-data-table.js";
27
29
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
28
- keepDiagnostics(renderDiagnostics, boundaryDiagnostics, slotDiagnostics);
30
+ keepDiagnostics(renderDiagnostics, boundaryDiagnostics, slotDiagnostics, pipelineDiagnostics);
29
31
  registerRenderOperations();
30
32
  var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
31
33
  function DataTable(props) {
@@ -40,6 +42,7 @@ function ZeroConfig(props) {
40
42
  return /* @__PURE__ */ jsx(DataTableView, { table: view, components, caption });
41
43
  }
42
44
  var WARN = "\u26A0";
45
+ var FAILED = /* @__PURE__ */ jsx("span", { role: "img", "aria-label": "Could not display this value", children: WARN });
43
46
  var BUSY = ["validating", "pending", "searching", "fetching"];
44
47
  var LOADING = ["validating", "searching", "fetching"];
45
48
  function hasAsyncSource(table) {
@@ -72,7 +75,7 @@ var tableOf = (view) => Object.hasOwn(view, "rowModel") ? Object.getPrototypeOf(
72
75
  function report(table, failures) {
73
76
  const bySlot = /* @__PURE__ */ new Map();
74
77
  for (const f of failures) {
75
- const k = `${f.slot}\0${f.expected === void 0 ? "throw" : "return"}`;
78
+ const k = `${f.slot}\0${f.lost ? "" : f.expected === void 0 ? "throw" : "return"}`;
76
79
  const list = bySlot.get(k);
77
80
  if (list) list.push(f);
78
81
  else bySlot.set(k, [f]);
@@ -80,9 +83,17 @@ function report(table, failures) {
80
83
  for (const list of bySlot.values()) {
81
84
  const first = list[0];
82
85
  const rows = list.slice(0, 3).map((f) => f.row);
83
- const fallback = "those cells rendered \u26A0";
86
+ const fallback = "cells-warn";
84
87
  try {
85
- if (first.expected === void 0) {
88
+ if (first.lost !== void 0) {
89
+ emitTableDiagnostic(table, "DK_UNRENDERABLE_VALUE", {
90
+ column: first.column,
91
+ index: first.index,
92
+ count: list.length,
93
+ rows,
94
+ valueType: first.lost
95
+ });
96
+ } else if (first.expected === void 0) {
86
97
  const e = first.error;
87
98
  emitTableDiagnostic(
88
99
  table,
@@ -112,6 +123,10 @@ function report(table, failures) {
112
123
  }
113
124
  }
114
125
  }
126
+ var lost;
127
+ var onLost = (v) => {
128
+ lost = v;
129
+ };
115
130
  function cellContent(col, row, failures, table) {
116
131
  const def = col.def;
117
132
  const value = row.getValue(col.id);
@@ -143,15 +158,33 @@ function cellContent(col, row, failures, table) {
143
158
  bad(failures, col, row, slot, "a string", out);
144
159
  return defaultFormat(value);
145
160
  }
146
- return defaultFormat(value);
161
+ const text = defaultFormat(value, onLost);
162
+ if (lost !== void 0) {
163
+ const { id: column, index } = col;
164
+ const type = Array.isArray(lost) ? "array" : typeof lost;
165
+ failures.push({ slot: `columns[${index}]`, column, index, row: row.index, lost: type });
166
+ lost = void 0;
167
+ }
168
+ return text;
147
169
  } catch (error) {
148
170
  failures.push({ slot: `columns[${col.index}].${slot}`, column: col.id, row: row.index, error });
149
- return WARN;
171
+ return FAILED;
150
172
  }
151
173
  }
152
174
  function guard(out, table, col, row, slot) {
153
175
  if (typeof out !== "object" || out === null) return out;
154
- return /* @__PURE__ */ jsx(CellBoundary, { table, col, slot, row: row.index, original: row.original, children: out });
176
+ return /* @__PURE__ */ jsx(
177
+ Boundary,
178
+ {
179
+ table,
180
+ col,
181
+ slot,
182
+ row: row.index,
183
+ resetKey: row.original,
184
+ fallback: failedCell,
185
+ children: out
186
+ }
187
+ );
155
188
  }
156
189
  function bad(failures, col, row, slot, expected, out) {
157
190
  failures.push({
@@ -161,7 +194,31 @@ function bad(failures, col, row, slot, expected, out) {
161
194
  expected,
162
195
  received: describeReturn(out)
163
196
  });
164
- return WARN;
197
+ return FAILED;
198
+ }
199
+ var HIDDEN = {
200
+ position: "absolute",
201
+ width: 1,
202
+ height: 1,
203
+ overflow: "hidden",
204
+ clipPath: "inset(50%)",
205
+ whiteSpace: "nowrap"
206
+ };
207
+ var serverPaged = (view) => view.getOptions().features?.some(
208
+ (f) => f?.stage === "paginate" && f.mode === "server"
209
+ ) === true;
210
+ function pages(view, page, count, server) {
211
+ if (server) {
212
+ return {
213
+ ...page,
214
+ pageCount: view.getPageCount?.() ?? 0,
215
+ canPrev: view.canPrevPage?.() ?? false,
216
+ canNext: view.canNextPage?.() ?? false
217
+ };
218
+ }
219
+ const pageCount = Math.ceil(count / page.pageSize);
220
+ const index = Math.min(page.pageIndex, Math.max(pageCount - 1, 0));
221
+ return { ...page, pageCount, canPrev: index > 0, canNext: index + 1 < pageCount };
165
222
  }
166
223
  var ariaSort = (dir) => dir === "asc" ? "ascending" : dir === "desc" ? "descending" : "none";
167
224
  function DataTableView(props) {
@@ -182,14 +239,45 @@ function DataTableView(props) {
182
239
  if (failures.length > 0) report(table, failures);
183
240
  });
184
241
  const sortable = typeof view.toggleSort === "function";
242
+ const sortBy = state.sorting?.by;
185
243
  const searchState = state.search;
186
244
  const paged = typeof view.getPageCount === "function";
187
245
  const loading = isInitialLoad && (LOADING.includes(status) || status === "pending" && hasAsyncSource(view));
188
246
  const failed = isInitialLoad && status === "error";
189
247
  const span = Math.max(columns.length, 1);
190
- const retryLoad = () => void view.refetch();
191
- const Search = parts.SearchInput ?? DefaultSearchInput;
192
- const Pager = parts.Pagination ?? DefaultPagination;
248
+ const tableEl = useRef(null);
249
+ const keepFocus = () => {
250
+ const t = tableEl.current;
251
+ const from = document.activeElement?.closest("[data-dk-state],[data-dk-refresh]");
252
+ if (t && from && t.parentElement?.contains(from)) {
253
+ t.tabIndex = -1;
254
+ t.focus();
255
+ }
256
+ };
257
+ const retryLoad = () => {
258
+ keepFocus();
259
+ void view.refetch();
260
+ };
261
+ const server = paged && serverPaged(view);
262
+ const pager = paged ? pages(view, state.pagination, rowModel.filteredCount, server) : null;
263
+ const n = rowModel.filteredCount;
264
+ const count = `${n} row${n === 1 ? "" : "s"}`;
265
+ const page = pager ? `${pager.pageIndex + 1} of ${Math.max(pager.pageCount, 1)}` : "";
266
+ let said = "";
267
+ if (searchState?.status === "pending" || searchState?.status === "searching") {
268
+ said = "Searching rows\u2026";
269
+ } else if (status === "idle" && !loading) {
270
+ said = server && n ? `Page ${page}` : page ? `${count}, page ${page}` : count;
271
+ }
272
+ const liveEl = useRef(null);
273
+ const saidBefore = useRef(null);
274
+ useEffect(() => {
275
+ const el = liveEl.current;
276
+ if (el && saidBefore.current !== null && saidBefore.current !== said) el.textContent = said;
277
+ saidBefore.current = said;
278
+ });
279
+ const cap = props.caption;
280
+ const named = typeof cap === "string" || typeof cap === "number" ? `${cap}` : "";
193
281
  const Empty = parts.Empty ?? DefaultEmpty;
194
282
  const Loading = parts.Loading ?? DefaultLoading;
195
283
  const ErrorState = parts.Error ?? DefaultError;
@@ -228,6 +316,7 @@ function DataTableView(props) {
228
316
  );
229
317
  });
230
318
  }
319
+ if (typeof window === "undefined" && failures.length > 0) report(table, failures);
231
320
  return /* @__PURE__ */ jsxs(
232
321
  "div",
233
322
  {
@@ -236,22 +325,21 @@ function DataTableView(props) {
236
325
  "data-initial-load": isInitialLoad ? "true" : "false",
237
326
  "aria-busy": BUSY.includes(status) ? "true" : void 0,
238
327
  children: [
239
- searchState && /* @__PURE__ */ jsx("div", { "data-dk-search": "", "data-status": searchState.status, children: /* @__PURE__ */ jsx(
240
- Search,
241
- {
242
- value: searchState.input,
243
- status: searchState.status,
244
- onChange: (q) => void view.setSearch?.(q),
245
- onSubmit: () => void view.flushSearch?.(),
246
- onClear: () => view.clearSearch?.()
247
- }
248
- ) }),
328
+ searchState && /* @__PURE__ */ jsx("div", { "data-dk-search": "", "data-status": searchState.status, children: part(table, "SearchInput", parts.SearchInput, DefaultSearchInput, {
329
+ value: searchState.input,
330
+ status: searchState.status,
331
+ onChange: (q) => void view.setSearch?.(q),
332
+ onSubmit: () => void view.flushSearch?.(),
333
+ onClear: () => view.clearSearch?.(),
334
+ label: named ? `Search ${named}` : "Search"
335
+ }) }),
249
336
  !loading && !failed && status !== "idle" && /* @__PURE__ */ jsx(RefreshIndicator, { status, retry: retryLoad }),
250
- /* @__PURE__ */ jsxs("table", { children: [
337
+ /* @__PURE__ */ jsxs("table", { ref: tableEl, children: [
251
338
  props.caption != null && /* @__PURE__ */ jsx("caption", { children: props.caption }),
252
339
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((col) => {
253
340
  const canSort = sortable && col.def.sortable !== false;
254
- const dir = canSort ? view.getSortDirection?.(col.id) ?? false : false;
341
+ const e = canSort ? sortBy?.find((x) => x.id === col.id) : void 0;
342
+ const dir = e ? e.desc ? "desc" : "asc" : false;
255
343
  return /* @__PURE__ */ jsx(
256
344
  "th",
257
345
  {
@@ -262,7 +350,7 @@ function DataTableView(props) {
262
350
  "button",
263
351
  {
264
352
  type: "button",
265
- onClick: (e) => view.toggleSort?.(col.id, { multi: e.shiftKey }),
353
+ onClick: (e2) => view.toggleSort?.(col.id, { multi: e2.shiftKey }),
266
354
  children: [
267
355
  col.header,
268
356
  /* @__PURE__ */ jsx("span", { "data-dk-sort-indicator": "", "aria-hidden": "true", children: dir === "asc" ? " \u25B2" : dir === "desc" ? " \u25BC" : "" })
@@ -274,77 +362,74 @@ function DataTableView(props) {
274
362
  );
275
363
  }) }) }),
276
364
  /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx(
277
- BodyBoundary,
365
+ Boundary,
278
366
  {
279
367
  table,
280
368
  resetKey: rowModel,
281
- fallback: (retry) => stateRow(span, "error", /* @__PURE__ */ jsx(ErrorState, { status, reason: "render", retry })),
369
+ fallback: (retry) => stateRow(
370
+ span,
371
+ "error",
372
+ /* @__PURE__ */ jsx(
373
+ ErrorState,
374
+ {
375
+ status,
376
+ reason: "render",
377
+ retry: () => {
378
+ keepFocus();
379
+ retry();
380
+ }
381
+ }
382
+ )
383
+ ),
282
384
  children: body
283
385
  }
284
386
  ) })
285
387
  ] }),
286
- paged && /* @__PURE__ */ jsx(
287
- Pager,
288
- {
289
- pageIndex: state.pagination.pageIndex,
290
- pageSize: state.pagination.pageSize,
291
- pageCount: view.getPageCount?.() ?? 0,
292
- canPrev: view.canPrevPage?.() ?? false,
293
- canNext: view.canNextPage?.() ?? false,
294
- prev: () => view.prevPage?.(),
295
- next: () => view.nextPage?.(),
296
- setPageIndex: (i) => view.setPageIndex?.(i),
297
- setPageSize: (n) => view.setPageSize?.(n)
298
- }
299
- )
388
+ pager && part(table, "Pagination", parts.Pagination, DefaultPagination, {
389
+ ...pager,
390
+ prev: () => view.prevPage?.(),
391
+ next: () => view.nextPage?.(),
392
+ setPageIndex: (i) => view.setPageIndex?.(i),
393
+ setPageSize: (n2) => view.setPageSize?.(n2),
394
+ label: named ? `${named} pagination` : "Pagination"
395
+ }),
396
+ /* @__PURE__ */ jsx("div", { "aria-live": "polite", style: HIDDEN, ref: liveEl })
300
397
  ]
301
398
  }
302
399
  );
303
400
  }
401
+ function part(table, name, Custom, Default, props) {
402
+ if (!Custom) return /* @__PURE__ */ jsx(Default, { ...props });
403
+ return /* @__PURE__ */ jsx(
404
+ Boundary,
405
+ {
406
+ table,
407
+ slot: `components.${name}`,
408
+ resetKey: Custom,
409
+ fallback: () => /* @__PURE__ */ jsx(Default, { ...props }),
410
+ children: /* @__PURE__ */ jsx(Custom, { ...props })
411
+ }
412
+ );
413
+ }
304
414
  function stateRow(span, kind, content) {
305
415
  return /* @__PURE__ */ jsx("tr", { "data-dk-state": kind, children: /* @__PURE__ */ jsx("td", { colSpan: span, children: content }) });
306
416
  }
307
- var BodyBoundary = class extends Component {
308
- constructor() {
309
- super(...arguments);
310
- this.state = { failed: false, key: this.props.resetKey };
311
- this.retry = () => this.setState({ failed: false });
312
- }
313
- static getDerivedStateFromError() {
314
- return { failed: true };
315
- }
316
- static getDerivedStateFromProps(props, state) {
317
- return props.resetKey === state.key ? null : { failed: false, key: props.resetKey };
318
- }
319
- componentDidCatch(error) {
320
- try {
321
- emitTableDiagnostic(
322
- this.props.table,
323
- "DK_RENDER_FAILED",
324
- { op: "render:table", ...error instanceof Error ? { errorName: error.name } : {} },
325
- { cause: error }
326
- );
327
- } catch {
328
- }
329
- }
330
- render() {
331
- return this.state.failed ? this.props.fallback(this.retry) : this.props.children;
332
- }
333
- };
334
- var CellBoundary = class extends Component {
417
+ var Boundary = class extends Component {
335
418
  constructor() {
336
419
  super(...arguments);
337
420
  this.state = {
338
421
  failed: false,
339
- original: this.props.original,
340
- def: this.props.col.def
422
+ key: this.props.resetKey,
423
+ def: this.props.col?.def
341
424
  };
425
+ this.retry = () => this.setState({ failed: false });
342
426
  }
343
427
  static getDerivedStateFromError() {
344
428
  return { failed: true };
345
429
  }
346
430
  static getDerivedStateFromProps(props, state) {
347
- return props.original === state.original && props.col.def === state.def ? null : { failed: false, original: props.original, def: props.col.def };
431
+ const def = props.col?.def;
432
+ return props.resetKey === state.key && def === state.def ? null : { failed: false, key: props.resetKey, def };
348
433
  }
349
434
  componentDidCatch(error) {
350
435
  const { table, col, slot, row } = this.props;
@@ -353,10 +438,9 @@ var CellBoundary = class extends Component {
353
438
  table,
354
439
  "DK_RENDER_FAILED",
355
440
  {
356
- op: "render:cell",
357
- slot: `columns[${col.index}].${slot}`,
358
- column: col.id,
359
- row,
441
+ op: col ? "render:cell" : "render:table",
442
+ ...slot ? { slot: col ? `columns[${col.index}].${slot}` : slot } : {},
443
+ ...col ? { column: col.id, row } : {},
360
444
  ...error instanceof Error ? { errorName: error.name } : {}
361
445
  },
362
446
  { cause: error }
@@ -365,16 +449,24 @@ var CellBoundary = class extends Component {
365
449
  }
366
450
  }
367
451
  render() {
368
- return this.state.failed ? WARN : this.props.children;
452
+ return this.state.failed ? this.props.fallback(this.retry) : this.props.children;
369
453
  }
370
454
  };
371
- function DefaultSearchInput({ value, status, onChange, onSubmit, onClear }) {
455
+ var failedCell = () => FAILED;
456
+ function DefaultSearchInput({
457
+ value,
458
+ status,
459
+ onChange,
460
+ onSubmit,
461
+ onClear,
462
+ label
463
+ }) {
372
464
  return /* @__PURE__ */ jsxs(Fragment, { children: [
373
465
  /* @__PURE__ */ jsx(
374
466
  "input",
375
467
  {
376
468
  type: "search",
377
- "aria-label": "Search",
469
+ "aria-label": label,
378
470
  value,
379
471
  onChange: (e) => onChange(e.target.value),
380
472
  onKeyDown: (e) => {
@@ -383,19 +475,38 @@ function DefaultSearchInput({ value, status, onChange, onSubmit, onClear }) {
383
475
  }
384
476
  }
385
477
  ),
386
- (status === "pending" || status === "searching") && /* @__PURE__ */ jsx("span", { role: "status", "data-dk-spinner": "", "aria-label": "Searching" })
478
+ (status === "pending" || status === "searching") && // Decorative: the live region says "Searching…" (an empty status announces nothing).
479
+ /* @__PURE__ */ jsx("span", { "data-dk-spinner": "", "aria-hidden": "true" })
387
480
  ] });
388
481
  }
389
482
  function DefaultPagination(p) {
390
- return /* @__PURE__ */ jsxs("nav", { "aria-label": "Pagination", "data-dk-pagination": "", children: [
391
- /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Previous page", disabled: !p.canPrev, onClick: p.prev, children: "\u2039" }),
483
+ return /* @__PURE__ */ jsxs("nav", { "aria-label": p.label, "data-dk-pagination": "", children: [
484
+ /* @__PURE__ */ jsx(
485
+ "button",
486
+ {
487
+ type: "button",
488
+ "aria-label": "Previous page",
489
+ "aria-disabled": !p.canPrev || void 0,
490
+ onClick: p.canPrev ? p.prev : void 0,
491
+ children: "\u2039"
492
+ }
493
+ ),
392
494
  /* @__PURE__ */ jsxs("span", { "data-dk-page": "", children: [
393
495
  "Page ",
394
496
  p.pageIndex + 1,
395
497
  " of ",
396
498
  Math.max(p.pageCount, 1)
397
499
  ] }),
398
- /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Next page", disabled: !p.canNext, onClick: p.next, children: "\u203A" })
500
+ /* @__PURE__ */ jsx(
501
+ "button",
502
+ {
503
+ type: "button",
504
+ "aria-label": "Next page",
505
+ "aria-disabled": !p.canNext || void 0,
506
+ onClick: p.canNext ? p.next : void 0,
507
+ children: "\u203A"
508
+ }
509
+ )
399
510
  ] });
400
511
  }
401
512
  var DefaultEmpty = ({ filtered }) => /* @__PURE__ */ jsx(Fragment, { children: filtered ? "No matching rows" : "No rows" });