@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 +6 -4
- package/dist/codes.cjs +0 -1
- package/dist/codes.cjs.map +1 -1
- package/dist/codes.js +0 -2
- package/dist/codes.js.map +1 -1
- package/dist/data-table.cjs +192 -83
- package/dist/data-table.cjs.map +1 -1
- package/dist/data-table.js +194 -83
- package/dist/data-table.js.map +1 -1
- package/dist/index.cjs +0 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/render-codes.cjs +6 -5
- package/dist/render-codes.cjs.map +1 -1
- package/dist/render-codes.js +6 -6
- package/dist/render-codes.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.d.cts +2 -0
- package/dist/styles.css.d.ts +2 -0
- package/dist/use-data-table.cjs +26 -25
- package/dist/use-data-table.cjs.map +1 -1
- package/dist/use-data-table.js +26 -24
- package/dist/use-data-table.js.map +1 -1
- package/package.json +21 -5
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`.
|
|
16
|
-
router treats
|
|
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
|
-
|
|
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
package/dist/codes.cjs.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":"
|
|
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
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":"
|
|
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":[]}
|
package/dist/data-table.cjs
CHANGED
|
@@ -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 = "
|
|
88
|
+
const fallback = "cells-warn";
|
|
88
89
|
try {
|
|
89
|
-
if (first.
|
|
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
|
-
|
|
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
|
|
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)(
|
|
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
|
|
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
|
|
195
|
-
const
|
|
196
|
-
|
|
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:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
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: (
|
|
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
|
-
|
|
367
|
+
Boundary,
|
|
282
368
|
{
|
|
283
369
|
table,
|
|
284
370
|
resetKey: rowModel,
|
|
285
|
-
fallback: (retry) => stateRow(
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
|
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
|
-
|
|
344
|
-
def: this.props.col
|
|
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
|
-
|
|
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 ?
|
|
454
|
+
return this.state.failed ? this.props.fallback(this.retry) : this.props.children;
|
|
373
455
|
}
|
|
374
456
|
};
|
|
375
|
-
|
|
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":
|
|
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") &&
|
|
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":
|
|
395
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
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)(
|
|
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" });
|