@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.
package/README.md CHANGED
@@ -12,9 +12,11 @@ preset; this package installs **no** features unless you pass them.
12
12
  npm install @datakable/react @datakable/core react
13
13
  ```
14
14
 
15
- React `>=18`. Every module of the bundle carries a `"use client"` directive, so the Next.js app
16
- router treats `<DataTable />` and `useDataTable` imported from here as client code. Like core,
15
+ React `>=18`. The modules of `<DataTable />` and `useDataTable` carry a `"use client"` directive,
16
+ so the Next.js app router treats them as client code even when a server component imports them
17
+ from here; the rest (core's `createTable`, …) stays usable on the server. Like core,
17
18
  it needs a bundler that replaces `process.env.NODE_ENV` (see [core](https://github.com/Ink01101011/datakable/tree/main/packages/core)).
19
+ In Node.js (SSR, tests) it needs Node `>=18.18`. The types need TypeScript `>=5.4`.
18
20
 
19
21
  ## useDataTable
20
22
 
@@ -108,8 +110,8 @@ for the features installed (this package installs none by default). Pass `table=
108
110
  to render a table you set up yourself, `components` to replace the search input, pager or states,
109
111
  and style it through the `data-*` hooks (`data-status`, `data-initial-load`, `data-sort`,
110
112
  `data-invalid`), or import the optional `@datakable/react/styles.css` (zero-specificity rules,
111
- themed through `--dk-*` custom properties). A `cell` that throws renders `⚠` in that cell
112
- (`DK_CALLBACK_FAILED`); so does a component it returns that throws while rendering — each such cell
113
+ themed through `--dk-*` custom properties). A `cell` that throws renders `⚠` in that cell (named
114
+ "Could not display this value" for screen readers, `DK_CALLBACK_FAILED`); so does a component it returns that throws while rendering — each such cell
113
115
  has its own error boundary, and the other cells keep rendering (`DK_RENDER_FAILED`). A component
114
116
  that throws elsewhere in the body shows the Error state with a retry.
115
117
 
package/dist/codes.cjs CHANGED
@@ -1,4 +1,3 @@
1
- "use client";
2
1
  "use strict";
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/codes.ts"],"sourcesContent":["import { defineDiagnostic } from '@datakable/core'\n\ndeclare module '@datakable/core' {\n interface DiagnosticCatalog {\n DK_UNSTABLE_COLUMNS: {\n /** Consecutive renders that passed a new `columns` array with the same shape. */\n renders: number\n }\n DK_UNSTABLE_OPTION: {\n /** `data`, `fetcher`, `rowKey`, or a feature's function option: `features[2].searchFn`. */\n option: string\n /** Consecutive renders that passed a new reference. */\n renders: number\n }\n }\n}\n\n// No @types/node here: declare just what is read.\ndeclare const process: { env: { NODE_ENV?: string } }\n\nconst isFeatureOption = (option: string) => option.startsWith('features[')\n\n/**\n * Codes owned by `@datakable/react` (§8.3, react, dev only). Like core's dev checks, the\n * definitions exist only in development: the NODE_ENV check is written out here (not an imported\n * flag) so a consumer's bundler folds it while parsing and drops the text from production bundles\n * (§8.2). They are never emitted in production, so `datakable/messages` has no text for them.\n */\nexport const reactDiagnostics =\n process.env.NODE_ENV !== 'production'\n ? [\n defineDiagnostic({\n code: 'DK_UNSTABLE_COLUMNS',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'columns' }),\n problem: (d) =>\n `useDataTable received a new columns array with the same shape on ${d.renders} renders ` +\n 'in a row (columns are compared by reference)',\n impact: () =>\n 'each new array rebuilds the column cache and re-runs the pipeline; in a re-render caused ' +\n 'by the table itself, the columns the table already has are kept',\n fix: () => [\n 'define the columns outside the component, or memoise them: ' +\n 'useMemo(() => [...columns], [the values they read])',\n 'a new columns array is how you tell the table the columns changed (accessor / format / ' +\n 'cell), so create one only when they do',\n ],\n }),\n defineDiagnostic({\n code: 'DK_UNSTABLE_OPTION',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: d.option }),\n problem: (d) =>\n `useDataTable received a new ${d.option} reference on ${d.renders} renders in a row`,\n impact: (d) =>\n d.option === 'fetcher'\n ? 'the table always calls the latest fetcher, but a new fetcher does not refetch: ' +\n 'values it reads that are not table state do not reload the data'\n : d.option === 'data'\n ? 'each new array is ingested again (schema, data checks, pipeline); in a re-render ' +\n 'caused by the table itself an array with equal rows is skipped'\n : isFeatureOption(d.option)\n ? 'features are read once, when the table is created: the table keeps calling the ' +\n `${d.option} of the first render (a stale closure)`\n : 'each new rowKey function recomputes every row key',\n fix: (d) =>\n d.option === 'fetcher'\n ? [\n 'call table.refetch() when a value the fetcher reads changes (or keep that value in ' +\n 'a server-mode feature state, which refetches by itself)',\n 'wrap the fetcher in useCallback to silence this warning',\n ]\n : d.option === 'data'\n ? [\n 'memoise derived data: useMemo(() => rows.filter(...), [rows])',\n 'or keep the data in state and pass the same array until it changes',\n ]\n : isFeatureOption(d.option)\n ? [\n 'create the features once: useMemo(() => [search({ searchFn })], []) or outside ' +\n 'the component',\n 'read changing values inside the function through a ref (ref.current) instead ' +\n 'of the render closure',\n ]\n : [\n \"use a key path (rowKey: 'id') or a function defined outside the component\",\n 'or wrap it in useCallback',\n ],\n }),\n ]\n : []\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAiC;AAoBjC,IAAM,kBAAkB,CAAC,WAAmB,OAAO,WAAW,WAAW;AAQlE,IAAM,mBACX,QAAQ,IAAI,aAAa,eACrB;AAAA,MACE,8BAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,IAChD,SAAS,CAAC,MACR,oEAAoE,EAAE,OAAO;AAAA,IAE/E,QAAQ,MACN;AAAA,IAEF,KAAK,MAAM;AAAA,MACT;AAAA,MAEA;AAAA,IAEF;AAAA,EACF,CAAC;AAAA,MACD,8BAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,OAAO,EAAE,MAAM,UAAU,MAAM,EAAE,OAAO;AAAA,IAChD,SAAS,CAAC,MACR,+BAA+B,EAAE,MAAM,iBAAiB,EAAE,OAAO;AAAA,IACnE,QAAQ,CAAC,MACP,EAAE,WAAW,YACT,mJAEA,EAAE,WAAW,SACX,oJAEA,gBAAgB,EAAE,MAAM,IACtB,kFACG,EAAE,MAAM,2CACX;AAAA,IACV,KAAK,CAAC,MACJ,EAAE,WAAW,YACT;AAAA,MACE;AAAA,MAEA;AAAA,IACF,IACA,EAAE,WAAW,SACX;AAAA,MACE;AAAA,MACA;AAAA,IACF,IACA,gBAAgB,EAAE,MAAM,IACtB;AAAA,MACE;AAAA,MAEA;AAAA,IAEF,IACA;AAAA,MACE;AAAA,MACA;AAAA,IACF;AAAA,EACZ,CAAC;AACH,IACA,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/codes.ts"],"sourcesContent":["import { defineDiagnostic } from '@datakable/core'\n\ndeclare module '@datakable/core' {\n interface DiagnosticCatalog {\n DK_UNSTABLE_COLUMNS: {\n /** Consecutive renders that passed a new `columns` array with the same shape. */\n renders: number\n }\n DK_UNSTABLE_OPTION: {\n /** `data`, `fetcher`, `rowKey`, or a feature's function option: `features[2].searchFn`. */\n option: string\n /** Consecutive renders that passed a new reference. */\n renders: number\n }\n }\n}\n\n// No @types/node here: declare just what is read.\ndeclare const process: { env: { NODE_ENV?: string } }\n\nconst isFeatureOption = (option: string) => option.startsWith('features[')\n\n/**\n * Codes owned by `@datakable/react` (§8.3, react, dev only). Like core's dev checks, the\n * definitions exist only in development: the NODE_ENV check is written out here (not an imported\n * flag) so a consumer's bundler folds it while parsing and drops the text from production bundles\n * (§8.2). They are never emitted in production, so `datakable/messages` has no text for them.\n */\nexport const reactDiagnostics =\n process.env.NODE_ENV !== 'production'\n ? [\n defineDiagnostic({\n code: 'DK_UNSTABLE_COLUMNS',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'columns' }),\n problem: (d) =>\n `useDataTable received a new columns array with the same shape on ${d.renders} renders ` +\n 'in a row (columns are compared by reference)',\n impact: () =>\n 'each new array rebuilds the column cache and re-runs the pipeline; in a re-render caused ' +\n 'by the table itself, the columns the table already has are kept',\n fix: () => [\n 'define the columns outside the component, or memoise them: ' +\n 'useMemo(() => [...columns], [the values they read])',\n 'a new columns array is how you tell the table the columns changed (accessor / format / ' +\n 'cell), so create one only when they do',\n ],\n }),\n defineDiagnostic({\n code: 'DK_UNSTABLE_OPTION',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: d.option }),\n problem: (d) =>\n `useDataTable received a new ${d.option} reference on ${d.renders} renders in a row`,\n impact: (d) =>\n d.option === 'fetcher'\n ? 'the table always calls the latest fetcher, but a new fetcher does not refetch: ' +\n 'values it reads that are not table state do not reload the data'\n : d.option === 'data'\n ? 'each new array is ingested again (schema, data checks, pipeline); in a re-render ' +\n 'caused by the table itself an array with equal rows is skipped'\n : isFeatureOption(d.option)\n ? 'features are read once, when the table is created: the table keeps calling the ' +\n `${d.option} of the first render (a stale closure)`\n : 'each new rowKey function recomputes every row key',\n fix: (d) =>\n d.option === 'fetcher'\n ? [\n 'call table.refetch() when a value the fetcher reads changes (or keep that value in ' +\n 'a server-mode feature state, which refetches by itself)',\n 'wrap the fetcher in useCallback to silence this warning',\n ]\n : d.option === 'data'\n ? [\n 'memoise derived data: useMemo(() => rows.filter(...), [rows])',\n 'or keep the data in state and pass the same array until it changes',\n ]\n : isFeatureOption(d.option)\n ? [\n 'create the features once: useMemo(() => [search({ searchFn })], []) or outside ' +\n 'the component',\n 'read changing values inside the function through a ref (ref.current) instead ' +\n 'of the render closure',\n ]\n : [\n \"use a key path (rowKey: 'id') or a function defined outside the component\",\n 'or wrap it in useCallback',\n ],\n }),\n ]\n : []\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAiC;AAoBjC,IAAM,kBAAkB,CAAC,WAAmB,OAAO,WAAW,WAAW;AAQlE,IAAM,mBACX,QAAQ,IAAI,aAAa,eACrB;AAAA,MACE,8BAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,IAChD,SAAS,CAAC,MACR,oEAAoE,EAAE,OAAO;AAAA,IAE/E,QAAQ,MACN;AAAA,IAEF,KAAK,MAAM;AAAA,MACT;AAAA,MAEA;AAAA,IAEF;AAAA,EACF,CAAC;AAAA,MACD,8BAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,OAAO,EAAE,MAAM,UAAU,MAAM,EAAE,OAAO;AAAA,IAChD,SAAS,CAAC,MACR,+BAA+B,EAAE,MAAM,iBAAiB,EAAE,OAAO;AAAA,IACnE,QAAQ,CAAC,MACP,EAAE,WAAW,YACT,mJAEA,EAAE,WAAW,SACX,oJAEA,gBAAgB,EAAE,MAAM,IACtB,kFACG,EAAE,MAAM,2CACX;AAAA,IACV,KAAK,CAAC,MACJ,EAAE,WAAW,YACT;AAAA,MACE;AAAA,MAEA;AAAA,IACF,IACA,EAAE,WAAW,SACX;AAAA,MACE;AAAA,MACA;AAAA,IACF,IACA,gBAAgB,EAAE,MAAM,IACtB;AAAA,MACE;AAAA,MAEA;AAAA,IAEF,IACA;AAAA,MACE;AAAA,MACA;AAAA,IACF;AAAA,EACZ,CAAC;AACH,IACA,CAAC;","names":[]}
package/dist/codes.js CHANGED
@@ -1,5 +1,3 @@
1
- "use client";
2
-
3
1
  // src/codes.ts
4
2
  import { defineDiagnostic } from "@datakable/core";
5
3
  var isFeatureOption = (option) => option.startsWith("features[");
package/dist/codes.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/codes.ts"],"sourcesContent":["import { defineDiagnostic } from '@datakable/core'\n\ndeclare module '@datakable/core' {\n interface DiagnosticCatalog {\n DK_UNSTABLE_COLUMNS: {\n /** Consecutive renders that passed a new `columns` array with the same shape. */\n renders: number\n }\n DK_UNSTABLE_OPTION: {\n /** `data`, `fetcher`, `rowKey`, or a feature's function option: `features[2].searchFn`. */\n option: string\n /** Consecutive renders that passed a new reference. */\n renders: number\n }\n }\n}\n\n// No @types/node here: declare just what is read.\ndeclare const process: { env: { NODE_ENV?: string } }\n\nconst isFeatureOption = (option: string) => option.startsWith('features[')\n\n/**\n * Codes owned by `@datakable/react` (§8.3, react, dev only). Like core's dev checks, the\n * definitions exist only in development: the NODE_ENV check is written out here (not an imported\n * flag) so a consumer's bundler folds it while parsing and drops the text from production bundles\n * (§8.2). They are never emitted in production, so `datakable/messages` has no text for them.\n */\nexport const reactDiagnostics =\n process.env.NODE_ENV !== 'production'\n ? [\n defineDiagnostic({\n code: 'DK_UNSTABLE_COLUMNS',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'columns' }),\n problem: (d) =>\n `useDataTable received a new columns array with the same shape on ${d.renders} renders ` +\n 'in a row (columns are compared by reference)',\n impact: () =>\n 'each new array rebuilds the column cache and re-runs the pipeline; in a re-render caused ' +\n 'by the table itself, the columns the table already has are kept',\n fix: () => [\n 'define the columns outside the component, or memoise them: ' +\n 'useMemo(() => [...columns], [the values they read])',\n 'a new columns array is how you tell the table the columns changed (accessor / format / ' +\n 'cell), so create one only when they do',\n ],\n }),\n defineDiagnostic({\n code: 'DK_UNSTABLE_OPTION',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: d.option }),\n problem: (d) =>\n `useDataTable received a new ${d.option} reference on ${d.renders} renders in a row`,\n impact: (d) =>\n d.option === 'fetcher'\n ? 'the table always calls the latest fetcher, but a new fetcher does not refetch: ' +\n 'values it reads that are not table state do not reload the data'\n : d.option === 'data'\n ? 'each new array is ingested again (schema, data checks, pipeline); in a re-render ' +\n 'caused by the table itself an array with equal rows is skipped'\n : isFeatureOption(d.option)\n ? 'features are read once, when the table is created: the table keeps calling the ' +\n `${d.option} of the first render (a stale closure)`\n : 'each new rowKey function recomputes every row key',\n fix: (d) =>\n d.option === 'fetcher'\n ? [\n 'call table.refetch() when a value the fetcher reads changes (or keep that value in ' +\n 'a server-mode feature state, which refetches by itself)',\n 'wrap the fetcher in useCallback to silence this warning',\n ]\n : d.option === 'data'\n ? [\n 'memoise derived data: useMemo(() => rows.filter(...), [rows])',\n 'or keep the data in state and pass the same array until it changes',\n ]\n : isFeatureOption(d.option)\n ? [\n 'create the features once: useMemo(() => [search({ searchFn })], []) or outside ' +\n 'the component',\n 'read changing values inside the function through a ref (ref.current) instead ' +\n 'of the render closure',\n ]\n : [\n \"use a key path (rowKey: 'id') or a function defined outside the component\",\n 'or wrap it in useCallback',\n ],\n }),\n ]\n : []\n"],"mappings":";;;AAAA,SAAS,wBAAwB;AAoBjC,IAAM,kBAAkB,CAAC,WAAmB,OAAO,WAAW,WAAW;AAQlE,IAAM,mBACX,QAAQ,IAAI,aAAa,eACrB;AAAA,EACE,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,IAChD,SAAS,CAAC,MACR,oEAAoE,EAAE,OAAO;AAAA,IAE/E,QAAQ,MACN;AAAA,IAEF,KAAK,MAAM;AAAA,MACT;AAAA,MAEA;AAAA,IAEF;AAAA,EACF,CAAC;AAAA,EACD,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,OAAO,EAAE,MAAM,UAAU,MAAM,EAAE,OAAO;AAAA,IAChD,SAAS,CAAC,MACR,+BAA+B,EAAE,MAAM,iBAAiB,EAAE,OAAO;AAAA,IACnE,QAAQ,CAAC,MACP,EAAE,WAAW,YACT,mJAEA,EAAE,WAAW,SACX,oJAEA,gBAAgB,EAAE,MAAM,IACtB,kFACG,EAAE,MAAM,2CACX;AAAA,IACV,KAAK,CAAC,MACJ,EAAE,WAAW,YACT;AAAA,MACE;AAAA,MAEA;AAAA,IACF,IACA,EAAE,WAAW,SACX;AAAA,MACE;AAAA,MACA;AAAA,IACF,IACA,gBAAgB,EAAE,MAAM,IACtB;AAAA,MACE;AAAA,MAEA;AAAA,IAEF,IACA;AAAA,MACE;AAAA,MACA;AAAA,IACF;AAAA,EACZ,CAAC;AACH,IACA,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/codes.ts"],"sourcesContent":["import { defineDiagnostic } from '@datakable/core'\n\ndeclare module '@datakable/core' {\n interface DiagnosticCatalog {\n DK_UNSTABLE_COLUMNS: {\n /** Consecutive renders that passed a new `columns` array with the same shape. */\n renders: number\n }\n DK_UNSTABLE_OPTION: {\n /** `data`, `fetcher`, `rowKey`, or a feature's function option: `features[2].searchFn`. */\n option: string\n /** Consecutive renders that passed a new reference. */\n renders: number\n }\n }\n}\n\n// No @types/node here: declare just what is read.\ndeclare const process: { env: { NODE_ENV?: string } }\n\nconst isFeatureOption = (option: string) => option.startsWith('features[')\n\n/**\n * Codes owned by `@datakable/react` (§8.3, react, dev only). Like core's dev checks, the\n * definitions exist only in development: the NODE_ENV check is written out here (not an imported\n * flag) so a consumer's bundler folds it while parsing and drops the text from production bundles\n * (§8.2). They are never emitted in production, so `datakable/messages` has no text for them.\n */\nexport const reactDiagnostics =\n process.env.NODE_ENV !== 'production'\n ? [\n defineDiagnostic({\n code: 'DK_UNSTABLE_COLUMNS',\n level: 'warn',\n where: () => ({ kind: 'option', path: 'columns' }),\n problem: (d) =>\n `useDataTable received a new columns array with the same shape on ${d.renders} renders ` +\n 'in a row (columns are compared by reference)',\n impact: () =>\n 'each new array rebuilds the column cache and re-runs the pipeline; in a re-render caused ' +\n 'by the table itself, the columns the table already has are kept',\n fix: () => [\n 'define the columns outside the component, or memoise them: ' +\n 'useMemo(() => [...columns], [the values they read])',\n 'a new columns array is how you tell the table the columns changed (accessor / format / ' +\n 'cell), so create one only when they do',\n ],\n }),\n defineDiagnostic({\n code: 'DK_UNSTABLE_OPTION',\n level: 'warn',\n where: (d) => ({ kind: 'option', path: d.option }),\n problem: (d) =>\n `useDataTable received a new ${d.option} reference on ${d.renders} renders in a row`,\n impact: (d) =>\n d.option === 'fetcher'\n ? 'the table always calls the latest fetcher, but a new fetcher does not refetch: ' +\n 'values it reads that are not table state do not reload the data'\n : d.option === 'data'\n ? 'each new array is ingested again (schema, data checks, pipeline); in a re-render ' +\n 'caused by the table itself an array with equal rows is skipped'\n : isFeatureOption(d.option)\n ? 'features are read once, when the table is created: the table keeps calling the ' +\n `${d.option} of the first render (a stale closure)`\n : 'each new rowKey function recomputes every row key',\n fix: (d) =>\n d.option === 'fetcher'\n ? [\n 'call table.refetch() when a value the fetcher reads changes (or keep that value in ' +\n 'a server-mode feature state, which refetches by itself)',\n 'wrap the fetcher in useCallback to silence this warning',\n ]\n : d.option === 'data'\n ? [\n 'memoise derived data: useMemo(() => rows.filter(...), [rows])',\n 'or keep the data in state and pass the same array until it changes',\n ]\n : isFeatureOption(d.option)\n ? [\n 'create the features once: useMemo(() => [search({ searchFn })], []) or outside ' +\n 'the component',\n 'read changing values inside the function through a ref (ref.current) instead ' +\n 'of the render closure',\n ]\n : [\n \"use a key path (rowKey: 'id') or a function defined outside the component\",\n 'or wrap it in useCallback',\n ],\n }),\n ]\n : []\n"],"mappings":";AAAA,SAAS,wBAAwB;AAoBjC,IAAM,kBAAkB,CAAC,WAAmB,OAAO,WAAW,WAAW;AAQlE,IAAM,mBACX,QAAQ,IAAI,aAAa,eACrB;AAAA,EACE,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,OAAO,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,IAChD,SAAS,CAAC,MACR,oEAAoE,EAAE,OAAO;AAAA,IAE/E,QAAQ,MACN;AAAA,IAEF,KAAK,MAAM;AAAA,MACT;AAAA,MAEA;AAAA,IAEF;AAAA,EACF,CAAC;AAAA,EACD,iBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,OAAO,EAAE,MAAM,UAAU,MAAM,EAAE,OAAO;AAAA,IAChD,SAAS,CAAC,MACR,+BAA+B,EAAE,MAAM,iBAAiB,EAAE,OAAO;AAAA,IACnE,QAAQ,CAAC,MACP,EAAE,WAAW,YACT,mJAEA,EAAE,WAAW,SACX,oJAEA,gBAAgB,EAAE,MAAM,IACtB,kFACG,EAAE,MAAM,2CACX;AAAA,IACV,KAAK,CAAC,MACJ,EAAE,WAAW,YACT;AAAA,MACE;AAAA,MAEA;AAAA,IACF,IACA,EAAE,WAAW,SACX;AAAA,MACE;AAAA,MACA;AAAA,IACF,IACA,gBAAgB,EAAE,MAAM,IACtB;AAAA,MACE;AAAA,MAEA;AAAA,IAEF,IACA;AAAA,MACE;AAAA,MACA;AAAA,IACF;AAAA,EACZ,CAAC;AACH,IACA,CAAC;","names":[]}
@@ -1,5 +1,5 @@
1
- "use client";
2
1
  "use strict";
2
+ "use client";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -29,7 +29,7 @@ var import_react = require("react");
29
29
  var import_render_codes = require("./render-codes.cjs");
30
30
  var import_use_data_table = require("./use-data-table.cjs");
31
31
  var import_jsx_runtime = require("react/jsx-runtime");
32
- (0, import_core.keepDiagnostics)(import_render_codes.renderDiagnostics, import_core.boundaryDiagnostics, import_core.slotDiagnostics);
32
+ (0, import_core.keepDiagnostics)(import_render_codes.renderDiagnostics, import_core.boundaryDiagnostics, import_core.slotDiagnostics, import_core.pipelineDiagnostics);
33
33
  (0, import_render_codes.registerRenderOperations)();
34
34
  var useIsomorphicLayoutEffect = typeof window === "undefined" ? import_react.useEffect : import_react.useLayoutEffect;
35
35
  function DataTable(props) {
@@ -44,6 +44,7 @@ function ZeroConfig(props) {
44
44
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DataTableView, { table: view, components, caption });
45
45
  }
46
46
  var WARN = "\u26A0";
47
+ var FAILED = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { role: "img", "aria-label": "Could not display this value", children: WARN });
47
48
  var BUSY = ["validating", "pending", "searching", "fetching"];
48
49
  var LOADING = ["validating", "searching", "fetching"];
49
50
  function hasAsyncSource(table) {
@@ -76,7 +77,7 @@ var tableOf = (view) => Object.hasOwn(view, "rowModel") ? Object.getPrototypeOf(
76
77
  function report(table, failures) {
77
78
  const bySlot = /* @__PURE__ */ new Map();
78
79
  for (const f of failures) {
79
- const k = `${f.slot}\0${f.expected === void 0 ? "throw" : "return"}`;
80
+ const k = `${f.slot}\0${f.lost ? "" : f.expected === void 0 ? "throw" : "return"}`;
80
81
  const list = bySlot.get(k);
81
82
  if (list) list.push(f);
82
83
  else bySlot.set(k, [f]);
@@ -84,9 +85,17 @@ function report(table, failures) {
84
85
  for (const list of bySlot.values()) {
85
86
  const first = list[0];
86
87
  const rows = list.slice(0, 3).map((f) => f.row);
87
- const fallback = "those cells rendered \u26A0";
88
+ const fallback = "cells-warn";
88
89
  try {
89
- if (first.expected === void 0) {
90
+ if (first.lost !== void 0) {
91
+ (0, import_core.emitTableDiagnostic)(table, "DK_UNRENDERABLE_VALUE", {
92
+ column: first.column,
93
+ index: first.index,
94
+ count: list.length,
95
+ rows,
96
+ valueType: first.lost
97
+ });
98
+ } else if (first.expected === void 0) {
90
99
  const e = first.error;
91
100
  (0, import_core.emitTableDiagnostic)(
92
101
  table,
@@ -116,6 +125,10 @@ function report(table, failures) {
116
125
  }
117
126
  }
118
127
  }
128
+ var lost;
129
+ var onLost = (v) => {
130
+ lost = v;
131
+ };
119
132
  function cellContent(col, row, failures, table) {
120
133
  const def = col.def;
121
134
  const value = row.getValue(col.id);
@@ -147,15 +160,33 @@ function cellContent(col, row, failures, table) {
147
160
  bad(failures, col, row, slot, "a string", out);
148
161
  return (0, import_core.defaultFormat)(value);
149
162
  }
150
- return (0, import_core.defaultFormat)(value);
163
+ const text = (0, import_core.defaultFormat)(value, onLost);
164
+ if (lost !== void 0) {
165
+ const { id: column, index } = col;
166
+ const type = Array.isArray(lost) ? "array" : typeof lost;
167
+ failures.push({ slot: `columns[${index}]`, column, index, row: row.index, lost: type });
168
+ lost = void 0;
169
+ }
170
+ return text;
151
171
  } catch (error) {
152
172
  failures.push({ slot: `columns[${col.index}].${slot}`, column: col.id, row: row.index, error });
153
- return WARN;
173
+ return FAILED;
154
174
  }
155
175
  }
156
176
  function guard(out, table, col, row, slot) {
157
177
  if (typeof out !== "object" || out === null) return out;
158
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CellBoundary, { table, col, slot, row: row.index, original: row.original, children: out });
178
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
179
+ Boundary,
180
+ {
181
+ table,
182
+ col,
183
+ slot,
184
+ row: row.index,
185
+ resetKey: row.original,
186
+ fallback: failedCell,
187
+ children: out
188
+ }
189
+ );
159
190
  }
160
191
  function bad(failures, col, row, slot, expected, out) {
161
192
  failures.push({
@@ -165,7 +196,31 @@ function bad(failures, col, row, slot, expected, out) {
165
196
  expected,
166
197
  received: (0, import_core.describeReturn)(out)
167
198
  });
168
- return WARN;
199
+ return FAILED;
200
+ }
201
+ var HIDDEN = {
202
+ position: "absolute",
203
+ width: 1,
204
+ height: 1,
205
+ overflow: "hidden",
206
+ clipPath: "inset(50%)",
207
+ whiteSpace: "nowrap"
208
+ };
209
+ var serverPaged = (view) => view.getOptions().features?.some(
210
+ (f) => f?.stage === "paginate" && f.mode === "server"
211
+ ) === true;
212
+ function pages(view, page, count, server) {
213
+ if (server) {
214
+ return {
215
+ ...page,
216
+ pageCount: view.getPageCount?.() ?? 0,
217
+ canPrev: view.canPrevPage?.() ?? false,
218
+ canNext: view.canNextPage?.() ?? false
219
+ };
220
+ }
221
+ const pageCount = Math.ceil(count / page.pageSize);
222
+ const index = Math.min(page.pageIndex, Math.max(pageCount - 1, 0));
223
+ return { ...page, pageCount, canPrev: index > 0, canNext: index + 1 < pageCount };
169
224
  }
170
225
  var ariaSort = (dir) => dir === "asc" ? "ascending" : dir === "desc" ? "descending" : "none";
171
226
  function DataTableView(props) {
@@ -186,14 +241,45 @@ function DataTableView(props) {
186
241
  if (failures.length > 0) report(table, failures);
187
242
  });
188
243
  const sortable = typeof view.toggleSort === "function";
244
+ const sortBy = state.sorting?.by;
189
245
  const searchState = state.search;
190
246
  const paged = typeof view.getPageCount === "function";
191
247
  const loading = isInitialLoad && (LOADING.includes(status) || status === "pending" && hasAsyncSource(view));
192
248
  const failed = isInitialLoad && status === "error";
193
249
  const span = Math.max(columns.length, 1);
194
- const retryLoad = () => void view.refetch();
195
- const Search = parts.SearchInput ?? DefaultSearchInput;
196
- const Pager = parts.Pagination ?? DefaultPagination;
250
+ const tableEl = (0, import_react.useRef)(null);
251
+ const keepFocus = () => {
252
+ const t = tableEl.current;
253
+ const from = document.activeElement?.closest("[data-dk-state],[data-dk-refresh]");
254
+ if (t && from && t.parentElement?.contains(from)) {
255
+ t.tabIndex = -1;
256
+ t.focus();
257
+ }
258
+ };
259
+ const retryLoad = () => {
260
+ keepFocus();
261
+ void view.refetch();
262
+ };
263
+ const server = paged && serverPaged(view);
264
+ const pager = paged ? pages(view, state.pagination, rowModel.filteredCount, server) : null;
265
+ const n = rowModel.filteredCount;
266
+ const count = `${n} row${n === 1 ? "" : "s"}`;
267
+ const page = pager ? `${pager.pageIndex + 1} of ${Math.max(pager.pageCount, 1)}` : "";
268
+ let said = "";
269
+ if (searchState?.status === "pending" || searchState?.status === "searching") {
270
+ said = "Searching rows\u2026";
271
+ } else if (status === "idle" && !loading) {
272
+ said = server && n ? `Page ${page}` : page ? `${count}, page ${page}` : count;
273
+ }
274
+ const liveEl = (0, import_react.useRef)(null);
275
+ const saidBefore = (0, import_react.useRef)(null);
276
+ (0, import_react.useEffect)(() => {
277
+ const el = liveEl.current;
278
+ if (el && saidBefore.current !== null && saidBefore.current !== said) el.textContent = said;
279
+ saidBefore.current = said;
280
+ });
281
+ const cap = props.caption;
282
+ const named = typeof cap === "string" || typeof cap === "number" ? `${cap}` : "";
197
283
  const Empty = parts.Empty ?? DefaultEmpty;
198
284
  const Loading = parts.Loading ?? DefaultLoading;
199
285
  const ErrorState = parts.Error ?? DefaultError;
@@ -232,6 +318,7 @@ function DataTableView(props) {
232
318
  );
233
319
  });
234
320
  }
321
+ if (typeof window === "undefined" && failures.length > 0) report(table, failures);
235
322
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
236
323
  "div",
237
324
  {
@@ -240,22 +327,21 @@ function DataTableView(props) {
240
327
  "data-initial-load": isInitialLoad ? "true" : "false",
241
328
  "aria-busy": BUSY.includes(status) ? "true" : void 0,
242
329
  children: [
243
- searchState && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-dk-search": "", "data-status": searchState.status, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
244
- Search,
245
- {
246
- value: searchState.input,
247
- status: searchState.status,
248
- onChange: (q) => void view.setSearch?.(q),
249
- onSubmit: () => void view.flushSearch?.(),
250
- onClear: () => view.clearSearch?.()
251
- }
252
- ) }),
330
+ searchState && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-dk-search": "", "data-status": searchState.status, children: part(table, "SearchInput", parts.SearchInput, DefaultSearchInput, {
331
+ value: searchState.input,
332
+ status: searchState.status,
333
+ onChange: (q) => void view.setSearch?.(q),
334
+ onSubmit: () => void view.flushSearch?.(),
335
+ onClear: () => view.clearSearch?.(),
336
+ label: named ? `Search ${named}` : "Search"
337
+ }) }),
253
338
  !loading && !failed && status !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RefreshIndicator, { status, retry: retryLoad }),
254
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { children: [
339
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { ref: tableEl, children: [
255
340
  props.caption != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("caption", { children: props.caption }),
256
341
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: columns.map((col) => {
257
342
  const canSort = sortable && col.def.sortable !== false;
258
- const dir = canSort ? view.getSortDirection?.(col.id) ?? false : false;
343
+ const e = canSort ? sortBy?.find((x) => x.id === col.id) : void 0;
344
+ const dir = e ? e.desc ? "desc" : "asc" : false;
259
345
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
260
346
  "th",
261
347
  {
@@ -266,7 +352,7 @@ function DataTableView(props) {
266
352
  "button",
267
353
  {
268
354
  type: "button",
269
- onClick: (e) => view.toggleSort?.(col.id, { multi: e.shiftKey }),
355
+ onClick: (e2) => view.toggleSort?.(col.id, { multi: e2.shiftKey }),
270
356
  children: [
271
357
  col.header,
272
358
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "data-dk-sort-indicator": "", "aria-hidden": "true", children: dir === "asc" ? " \u25B2" : dir === "desc" ? " \u25BC" : "" })
@@ -278,77 +364,74 @@ function DataTableView(props) {
278
364
  );
279
365
  }) }) }),
280
366
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
281
- BodyBoundary,
367
+ Boundary,
282
368
  {
283
369
  table,
284
370
  resetKey: rowModel,
285
- fallback: (retry) => stateRow(span, "error", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorState, { status, reason: "render", retry })),
371
+ fallback: (retry) => stateRow(
372
+ span,
373
+ "error",
374
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
375
+ ErrorState,
376
+ {
377
+ status,
378
+ reason: "render",
379
+ retry: () => {
380
+ keepFocus();
381
+ retry();
382
+ }
383
+ }
384
+ )
385
+ ),
286
386
  children: body
287
387
  }
288
388
  ) })
289
389
  ] }),
290
- paged && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
291
- Pager,
292
- {
293
- pageIndex: state.pagination.pageIndex,
294
- pageSize: state.pagination.pageSize,
295
- pageCount: view.getPageCount?.() ?? 0,
296
- canPrev: view.canPrevPage?.() ?? false,
297
- canNext: view.canNextPage?.() ?? false,
298
- prev: () => view.prevPage?.(),
299
- next: () => view.nextPage?.(),
300
- setPageIndex: (i) => view.setPageIndex?.(i),
301
- setPageSize: (n) => view.setPageSize?.(n)
302
- }
303
- )
390
+ pager && part(table, "Pagination", parts.Pagination, DefaultPagination, {
391
+ ...pager,
392
+ prev: () => view.prevPage?.(),
393
+ next: () => view.nextPage?.(),
394
+ setPageIndex: (i) => view.setPageIndex?.(i),
395
+ setPageSize: (n2) => view.setPageSize?.(n2),
396
+ label: named ? `${named} pagination` : "Pagination"
397
+ }),
398
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "aria-live": "polite", style: HIDDEN, ref: liveEl })
304
399
  ]
305
400
  }
306
401
  );
307
402
  }
403
+ function part(table, name, Custom, Default, props) {
404
+ if (!Custom) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Default, { ...props });
405
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
406
+ Boundary,
407
+ {
408
+ table,
409
+ slot: `components.${name}`,
410
+ resetKey: Custom,
411
+ fallback: () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Default, { ...props }),
412
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Custom, { ...props })
413
+ }
414
+ );
415
+ }
308
416
  function stateRow(span, kind, content) {
309
417
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { "data-dk-state": kind, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { colSpan: span, children: content }) });
310
418
  }
311
- var BodyBoundary = class extends import_react.Component {
312
- constructor() {
313
- super(...arguments);
314
- this.state = { failed: false, key: this.props.resetKey };
315
- this.retry = () => this.setState({ failed: false });
316
- }
317
- static getDerivedStateFromError() {
318
- return { failed: true };
319
- }
320
- static getDerivedStateFromProps(props, state) {
321
- return props.resetKey === state.key ? null : { failed: false, key: props.resetKey };
322
- }
323
- componentDidCatch(error) {
324
- try {
325
- (0, import_core.emitTableDiagnostic)(
326
- this.props.table,
327
- "DK_RENDER_FAILED",
328
- { op: "render:table", ...error instanceof Error ? { errorName: error.name } : {} },
329
- { cause: error }
330
- );
331
- } catch {
332
- }
333
- }
334
- render() {
335
- return this.state.failed ? this.props.fallback(this.retry) : this.props.children;
336
- }
337
- };
338
- var CellBoundary = class extends import_react.Component {
419
+ var Boundary = class extends import_react.Component {
339
420
  constructor() {
340
421
  super(...arguments);
341
422
  this.state = {
342
423
  failed: false,
343
- original: this.props.original,
344
- def: this.props.col.def
424
+ key: this.props.resetKey,
425
+ def: this.props.col?.def
345
426
  };
427
+ this.retry = () => this.setState({ failed: false });
346
428
  }
347
429
  static getDerivedStateFromError() {
348
430
  return { failed: true };
349
431
  }
350
432
  static getDerivedStateFromProps(props, state) {
351
- return props.original === state.original && props.col.def === state.def ? null : { failed: false, original: props.original, def: props.col.def };
433
+ const def = props.col?.def;
434
+ return props.resetKey === state.key && def === state.def ? null : { failed: false, key: props.resetKey, def };
352
435
  }
353
436
  componentDidCatch(error) {
354
437
  const { table, col, slot, row } = this.props;
@@ -357,10 +440,9 @@ var CellBoundary = class extends import_react.Component {
357
440
  table,
358
441
  "DK_RENDER_FAILED",
359
442
  {
360
- op: "render:cell",
361
- slot: `columns[${col.index}].${slot}`,
362
- column: col.id,
363
- row,
443
+ op: col ? "render:cell" : "render:table",
444
+ ...slot ? { slot: col ? `columns[${col.index}].${slot}` : slot } : {},
445
+ ...col ? { column: col.id, row } : {},
364
446
  ...error instanceof Error ? { errorName: error.name } : {}
365
447
  },
366
448
  { cause: error }
@@ -369,16 +451,24 @@ var CellBoundary = class extends import_react.Component {
369
451
  }
370
452
  }
371
453
  render() {
372
- return this.state.failed ? WARN : this.props.children;
454
+ return this.state.failed ? this.props.fallback(this.retry) : this.props.children;
373
455
  }
374
456
  };
375
- function DefaultSearchInput({ value, status, onChange, onSubmit, onClear }) {
457
+ var failedCell = () => FAILED;
458
+ function DefaultSearchInput({
459
+ value,
460
+ status,
461
+ onChange,
462
+ onSubmit,
463
+ onClear,
464
+ label
465
+ }) {
376
466
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
377
467
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
378
468
  "input",
379
469
  {
380
470
  type: "search",
381
- "aria-label": "Search",
471
+ "aria-label": label,
382
472
  value,
383
473
  onChange: (e) => onChange(e.target.value),
384
474
  onKeyDown: (e) => {
@@ -387,19 +477,38 @@ function DefaultSearchInput({ value, status, onChange, onSubmit, onClear }) {
387
477
  }
388
478
  }
389
479
  ),
390
- (status === "pending" || status === "searching") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { role: "status", "data-dk-spinner": "", "aria-label": "Searching" })
480
+ (status === "pending" || status === "searching") && // Decorative: the live region says "Searching…" (an empty status announces nothing).
481
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "data-dk-spinner": "", "aria-hidden": "true" })
391
482
  ] });
392
483
  }
393
484
  function DefaultPagination(p) {
394
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("nav", { "aria-label": "Pagination", "data-dk-pagination": "", children: [
395
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-label": "Previous page", disabled: !p.canPrev, onClick: p.prev, children: "\u2039" }),
485
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("nav", { "aria-label": p.label, "data-dk-pagination": "", children: [
486
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
487
+ "button",
488
+ {
489
+ type: "button",
490
+ "aria-label": "Previous page",
491
+ "aria-disabled": !p.canPrev || void 0,
492
+ onClick: p.canPrev ? p.prev : void 0,
493
+ children: "\u2039"
494
+ }
495
+ ),
396
496
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { "data-dk-page": "", children: [
397
497
  "Page ",
398
498
  p.pageIndex + 1,
399
499
  " of ",
400
500
  Math.max(p.pageCount, 1)
401
501
  ] }),
402
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-label": "Next page", disabled: !p.canNext, onClick: p.next, children: "\u203A" })
502
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
503
+ "button",
504
+ {
505
+ type: "button",
506
+ "aria-label": "Next page",
507
+ "aria-disabled": !p.canNext || void 0,
508
+ onClick: p.canNext ? p.next : void 0,
509
+ children: "\u203A"
510
+ }
511
+ )
403
512
  ] });
404
513
  }
405
514
  var DefaultEmpty = ({ filtered }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: filtered ? "No matching rows" : "No rows" });