@crispy-seed/data-table 0.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.
Files changed (56) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +30 -0
  3. package/README.md +103 -0
  4. package/dist/DataTable-12s-8LMBtS2u.cjs +1532 -0
  5. package/dist/DataTable-12s-Bd20OuMs.js +1482 -0
  6. package/dist/DataTable-12s-Zml1x9Kr-12s-CTRzgvMT.cjs +161 -0
  7. package/dist/DataTable-12s-Zml1x9Kr-12s-Coi-3lUa.js +153 -0
  8. package/dist/Menu-12s-Db_zr1AP.cjs +104 -0
  9. package/dist/Menu-12s-Di5JADW4.js +78 -0
  10. package/dist/Select-12s-1Uh7XbkG.js +219 -0
  11. package/dist/Select-12s-D8DdKT-U.cjs +250 -0
  12. package/dist/body-cells-12s-BnOVUTKN.js +453 -0
  13. package/dist/body-cells-12s-BoOTcg-l.cjs +462 -0
  14. package/dist/column-calc-12s-C6IPSOaF.js +129 -0
  15. package/dist/column-calc-12s-CKuSpe-l.cjs +149 -0
  16. package/dist/column-layout-12s-CTu5yT_D.cjs +120 -0
  17. package/dist/column-layout-12s-s7wnxkMN.js +99 -0
  18. package/dist/column-menu-12s-BPE9N1gs.cjs +427 -0
  19. package/dist/column-menu-12s-mRBk-4oa.js +407 -0
  20. package/dist/data-table-recipe.css +442 -0
  21. package/dist/data-table-recipe.layered.css +505 -0
  22. package/dist/editors-12s-BO9Mp034.js +525 -0
  23. package/dist/editors-12s-DO8TZkYV.cjs +555 -0
  24. package/dist/index.cjs +14 -0
  25. package/dist/index.d.ts +323 -0
  26. package/dist/index.js +10 -0
  27. package/dist/resize-12s-jQY-PX67.cjs +58 -0
  28. package/dist/resize-12s-nsMvrmLt.js +58 -0
  29. package/dist/states-12s-CLO_uVue.cjs +125 -0
  30. package/dist/states-12s-DEFSxJCO.js +122 -0
  31. package/dist/styles.css +14660 -0
  32. package/dist/toolbar-12s-AVU-sbDW.js +69 -0
  33. package/dist/toolbar-12s-CaLgJrDo.cjs +71 -0
  34. package/dist/useDataTable-12s-BHiDUubw-12s-CBeCwTe7.js +61 -0
  35. package/dist/useDataTable-12s-BHiDUubw-12s-Dgvt7XDk.cjs +61 -0
  36. package/dist/useDataTableColumnCalc-12s-DTQQTtEJ-12s-CvD14JYo.cjs +207 -0
  37. package/dist/useDataTableColumnCalc-12s-DTQQTtEJ-12s-DsEse3uq.js +183 -0
  38. package/dist/useDataTableColumns-12s-Cn2ZAzFL-12s-CD6goR59.js +289 -0
  39. package/dist/useDataTableColumns-12s-Cn2ZAzFL-12s-CUk_CpYT.cjs +292 -0
  40. package/dist/useDataTableEdit-12s-9Wb_UCrN-12s-BoOHq3GB.js +98 -0
  41. package/dist/useDataTableEdit-12s-9Wb_UCrN-12s-De6YHBba.cjs +98 -0
  42. package/dist/useDataTableFilter-12s-ZzbmWMin-12s-CWyBeFna.js +74 -0
  43. package/dist/useDataTableFilter-12s-ZzbmWMin-12s-lpjzZY3_.cjs +74 -0
  44. package/dist/useDataTableInfiniteScroll-12s-8ZG31URd-12s-BexjP6iO.js +104 -0
  45. package/dist/useDataTableInfiniteScroll-12s-8ZG31URd-12s-Bt8gojr9.cjs +104 -0
  46. package/dist/useDataTableRow-12s-CPYaDa1p-12s-BmUSAnPe.js +30 -0
  47. package/dist/useDataTableRow-12s-CPYaDa1p-12s-D-DRjwAe.cjs +30 -0
  48. package/dist/useDataTableScroll-12s-BKDCr0en-12s-C7f2vHeG.js +61 -0
  49. package/dist/useDataTableScroll-12s-BKDCr0en-12s-CUvUYMtt.cjs +61 -0
  50. package/dist/useDataTableSearch-12s-Ck3iWcpX-12s-B-65V6gH.js +37 -0
  51. package/dist/useDataTableSearch-12s-Ck3iWcpX-12s-DhVfN2Hq.cjs +37 -0
  52. package/dist/useDataTableSelection-12s-BIDtiRQq-12s-IS__VAnU.cjs +71 -0
  53. package/dist/useDataTableSelection-12s-BIDtiRQq-12s-O6M_BhrH.js +71 -0
  54. package/dist/useDataTableSort-12s-DvJYtmeS-12s-C5VGnsq4.cjs +78 -0
  55. package/dist/useDataTableSort-12s-DvJYtmeS-12s-DABSNt6U.js +78 -0
  56. package/package.json +78 -0
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+ function __insertCSS(code) {
3
+ if (!code || typeof document == 'undefined') return
4
+ let head = document.head || document.getElementsByTagName('head')[0]
5
+ let style = document.createElement('style')
6
+ style.type = 'text/css'
7
+ head.appendChild(style)
8
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
9
+ }
10
+
11
+ import { jsxs, jsx } from 'react/jsx-runtime';
12
+ import 'react';
13
+ import { z as withContext, A as dataTable, B as IconMagnifyingglassLine_default } from './DataTable-12s-Bd20OuMs.js';
14
+ import { Primitive } from '@seed-design/react-primitive';
15
+ import { c as TextFieldRoot, d as TextFieldPrefixIcon, e as TextFieldInput } from './editors-12s-BO9Mp034.js';
16
+ import { u as useDataTableContext } from './DataTable-12s-Zml1x9Kr-12s-Coi-3lUa.js';
17
+
18
+ const DataTableToolbar = withContext(Primitive.div, "toolbar");
19
+ const DataTableCount = withContext(Primitive.span, "count");
20
+ /**
21
+ * Fix (recurring bug): the search box is seed's own DOCUMENTED `TextField`
22
+ * composition -- `TextFieldRoot` (`variant="outline" size="medium"`) +
23
+ * `TextFieldPrefixIcon` + `TextFieldInput`, exactly `docs/examples/react/
24
+ * text-field-input/prefix.tsx` -- not that same trio fought with a custom
25
+ * `classNames.search` overriding its height/padding/icon position with `!important`.
26
+ * That override (this file's own previous state) is what kept mis-placing the icon:
27
+ * every fix landed on ANOTHER property the override was still fighting (the icon's own
28
+ * intrinsic SVG size, its inherited margin, the root's own min-height beating a plain
29
+ * `height` override) because the override was never the right layer to fix it at.
30
+ * `size="medium"`'s built-in prefix-icon spacing (a flex `gap` + edge margins, not
31
+ * absolute positioning) is what `text-input.ts`'s own variant CSS already gets right,
32
+ * so it needs no help from this component at all -- `search` (recipe.ts) is now just
33
+ * this box's own layout constraint (`flex`, `max-width`) within the toolbar row, not
34
+ * its internal chrome.
35
+ */ function SearchBox({ placeholder, label }) {
36
+ const { query, setQuery } = useDataTableContext();
37
+ const classNames = dataTable();
38
+ return /*#__PURE__*/ jsxs(TextFieldRoot, {
39
+ variant: "outline",
40
+ size: "medium",
41
+ className: classNames.search,
42
+ children: [
43
+ /*#__PURE__*/ jsx(TextFieldPrefixIcon, {
44
+ svg: /*#__PURE__*/ jsx(IconMagnifyingglassLine_default, {})
45
+ }),
46
+ /*#__PURE__*/ jsx(TextFieldInput, {
47
+ type: "text",
48
+ value: query,
49
+ onChange: (event)=>setQuery(event.target.value),
50
+ placeholder: placeholder,
51
+ "aria-label": label
52
+ })
53
+ ]
54
+ });
55
+ }
56
+ /** crisp's own count format (`datatable.tsx`'s `resolvedCountLabel`): unfiltered reads
57
+ * "N rows" ("N row" for exactly one), filtered/searched reads "N of M" with no
58
+ * trailing word at all -- reproduced literally rather than reinvented, since this is
59
+ * the one piece of copy DATATABLE-ATTIO-SPEC.md calls out by example. */ function defaultCountLabel(shown, total) {
60
+ if (shown === total) return `${total} ${total === 1 ? "row" : "rows"}`;
61
+ return `${shown} of ${total}`;
62
+ }
63
+ function RowCount({ shown, total, countLabel }) {
64
+ return /*#__PURE__*/ jsx(DataTableCount, {
65
+ children: (countLabel ?? defaultCountLabel)(shown, total)
66
+ });
67
+ }
68
+
69
+ export { DataTableToolbar as D, RowCount as R, SearchBox as S };
@@ -0,0 +1,71 @@
1
+ 'use client';
2
+ function __insertCSS(code) {
3
+ if (!code || typeof document == 'undefined') return
4
+ let head = document.head || document.getElementsByTagName('head')[0]
5
+ let style = document.createElement('style')
6
+ style.type = 'text/css'
7
+ head.appendChild(style)
8
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
9
+ }
10
+
11
+ var jsxRuntime = require('react/jsx-runtime');
12
+ require('react');
13
+ var DataTable12s = require('./DataTable-12s-8LMBtS2u.cjs');
14
+ var reactPrimitive = require('@seed-design/react-primitive');
15
+ var editors12s = require('./editors-12s-DO8TZkYV.cjs');
16
+ var DataTable12sZml1x9Kr12s = require('./DataTable-12s-Zml1x9Kr-12s-CTRzgvMT.cjs');
17
+
18
+ const DataTableToolbar = DataTable12s.withContext(reactPrimitive.Primitive.div, "toolbar");
19
+ const DataTableCount = DataTable12s.withContext(reactPrimitive.Primitive.span, "count");
20
+ /**
21
+ * Fix (recurring bug): the search box is seed's own DOCUMENTED `TextField`
22
+ * composition -- `TextFieldRoot` (`variant="outline" size="medium"`) +
23
+ * `TextFieldPrefixIcon` + `TextFieldInput`, exactly `docs/examples/react/
24
+ * text-field-input/prefix.tsx` -- not that same trio fought with a custom
25
+ * `classNames.search` overriding its height/padding/icon position with `!important`.
26
+ * That override (this file's own previous state) is what kept mis-placing the icon:
27
+ * every fix landed on ANOTHER property the override was still fighting (the icon's own
28
+ * intrinsic SVG size, its inherited margin, the root's own min-height beating a plain
29
+ * `height` override) because the override was never the right layer to fix it at.
30
+ * `size="medium"`'s built-in prefix-icon spacing (a flex `gap` + edge margins, not
31
+ * absolute positioning) is what `text-input.ts`'s own variant CSS already gets right,
32
+ * so it needs no help from this component at all -- `search` (recipe.ts) is now just
33
+ * this box's own layout constraint (`flex`, `max-width`) within the toolbar row, not
34
+ * its internal chrome.
35
+ */ function SearchBox({ placeholder, label }) {
36
+ const { query, setQuery } = DataTable12sZml1x9Kr12s.useDataTableContext();
37
+ const classNames = DataTable12s.dataTable();
38
+ return /*#__PURE__*/ jsxRuntime.jsxs(editors12s.TextFieldRoot, {
39
+ variant: "outline",
40
+ size: "medium",
41
+ className: classNames.search,
42
+ children: [
43
+ /*#__PURE__*/ jsxRuntime.jsx(editors12s.TextFieldPrefixIcon, {
44
+ svg: /*#__PURE__*/ jsxRuntime.jsx(DataTable12s.IconMagnifyingglassLine_default, {})
45
+ }),
46
+ /*#__PURE__*/ jsxRuntime.jsx(editors12s.TextFieldInput, {
47
+ type: "text",
48
+ value: query,
49
+ onChange: (event)=>setQuery(event.target.value),
50
+ placeholder: placeholder,
51
+ "aria-label": label
52
+ })
53
+ ]
54
+ });
55
+ }
56
+ /** crisp's own count format (`datatable.tsx`'s `resolvedCountLabel`): unfiltered reads
57
+ * "N rows" ("N row" for exactly one), filtered/searched reads "N of M" with no
58
+ * trailing word at all -- reproduced literally rather than reinvented, since this is
59
+ * the one piece of copy DATATABLE-ATTIO-SPEC.md calls out by example. */ function defaultCountLabel(shown, total) {
60
+ if (shown === total) return `${total} ${total === 1 ? "row" : "rows"}`;
61
+ return `${shown} of ${total}`;
62
+ }
63
+ function RowCount({ shown, total, countLabel }) {
64
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableCount, {
65
+ children: (countLabel ?? defaultCountLabel)(shown, total)
66
+ });
67
+ }
68
+
69
+ exports.DataTableToolbar = DataTableToolbar;
70
+ exports.RowCount = RowCount;
71
+ exports.SearchBox = SearchBox;
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+ function __insertCSS(code) {
3
+ if (!code || typeof document == 'undefined') return
4
+ let head = document.head || document.getElementsByTagName('head')[0]
5
+ let style = document.createElement('style')
6
+ style.type = 'text/css'
7
+ head.appendChild(style)
8
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
9
+ }
10
+
11
+ import { elementProps } from '@seed-design/dom-utils';
12
+ import { u as useDataTableColumnCalc } from './useDataTableColumnCalc-12s-DTQQTtEJ-12s-DsEse3uq.js';
13
+ import { u as useDataTableColumns } from './useDataTableColumns-12s-Cn2ZAzFL-12s-CD6goR59.js';
14
+ import { u as useDataTableEdit } from './useDataTableEdit-12s-9Wb_UCrN-12s-BoOHq3GB.js';
15
+ import { u as useDataTableFilter } from './useDataTableFilter-12s-ZzbmWMin-12s-CWyBeFna.js';
16
+ import { u as useDataTableInfiniteScroll } from './useDataTableInfiniteScroll-12s-8ZG31URd-12s-BexjP6iO.js';
17
+ import { u as useDataTableScroll } from './useDataTableScroll-12s-BKDCr0en-12s-C7f2vHeG.js';
18
+ import { u as useDataTableSearch } from './useDataTableSearch-12s-Ck3iWcpX-12s-B-65V6gH.js';
19
+ import { u as useDataTableSelection } from './useDataTableSelection-12s-BIDtiRQq-12s-O6M_BhrH.js';
20
+ import { u as useDataTableSort } from './useDataTableSort-12s-DvJYtmeS-12s-DABSNt6U.js';
21
+
22
+ // Composes the eight independent concerns a table root owns -- which column is
23
+ // sorted, which rows are selected, the toolbar's free-text query, each column's own
24
+ // filter, column order/visibility/width/label, each column's ColumnCalc footer op, the
25
+ // horizontal scroll-shadow edge state, and which single cell is mid-edit/pending/
26
+ // erroring -- the way useSelect composes useSelectHighlight + useSelectOptions. No
27
+ // sub-hook knows about any other; a future concern (row grouping) is a ninth sibling
28
+ // call here, not a branch inside an existing one.
29
+ function useDataTable(props) {
30
+ const sort = useDataTableSort(props);
31
+ const selection = useDataTableSelection(props);
32
+ const search = useDataTableSearch(props);
33
+ const filter = useDataTableFilter(props);
34
+ const columns = useDataTableColumns(props);
35
+ const columnCalc = useDataTableColumnCalc(props);
36
+ const scroll = useDataTableScroll();
37
+ const edit = useDataTableEdit(props);
38
+ // Wired to `scroll`'s own scroll-container ref (not a fresh one of its own) so a
39
+ // BOUNDED table's auto-load triggers off its own internal scroll, not the
40
+ // browsing viewport -- see useDataTableInfiniteScroll.ts's own doc comment.
41
+ const infiniteScroll = useDataTableInfiniteScroll({
42
+ ...props,
43
+ scrollRootRef: scroll.elementRef
44
+ });
45
+ return {
46
+ ...sort,
47
+ ...selection,
48
+ ...search,
49
+ ...filter,
50
+ ...columns,
51
+ ...columnCalc,
52
+ ...scroll,
53
+ ...edit,
54
+ ...infiniteScroll,
55
+ rootProps: elementProps({
56
+ role: "table"
57
+ })
58
+ };
59
+ }
60
+
61
+ export { useDataTable as u };
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+ function __insertCSS(code) {
3
+ if (!code || typeof document == 'undefined') return
4
+ let head = document.head || document.getElementsByTagName('head')[0]
5
+ let style = document.createElement('style')
6
+ style.type = 'text/css'
7
+ head.appendChild(style)
8
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
9
+ }
10
+
11
+ var domUtils = require('@seed-design/dom-utils');
12
+ var useDataTableColumnCalc12sDTQQTtEJ12s = require('./useDataTableColumnCalc-12s-DTQQTtEJ-12s-CvD14JYo.cjs');
13
+ var useDataTableColumns12sCn2ZAzFL12s = require('./useDataTableColumns-12s-Cn2ZAzFL-12s-CUk_CpYT.cjs');
14
+ var useDataTableEdit12s9Wb_UCrN12s = require('./useDataTableEdit-12s-9Wb_UCrN-12s-De6YHBba.cjs');
15
+ var useDataTableFilter12sZzbmWMin12s = require('./useDataTableFilter-12s-ZzbmWMin-12s-lpjzZY3_.cjs');
16
+ var useDataTableInfiniteScroll12s8ZG31URd12s = require('./useDataTableInfiniteScroll-12s-8ZG31URd-12s-Bt8gojr9.cjs');
17
+ var useDataTableScroll12sBKDCr0en12s = require('./useDataTableScroll-12s-BKDCr0en-12s-CUvUYMtt.cjs');
18
+ var useDataTableSearch12sCk3iWcpX12s = require('./useDataTableSearch-12s-Ck3iWcpX-12s-DhVfN2Hq.cjs');
19
+ var useDataTableSelection12sBIDtiRQq12s = require('./useDataTableSelection-12s-BIDtiRQq-12s-IS__VAnU.cjs');
20
+ var useDataTableSort12sDvJYtmeS12s = require('./useDataTableSort-12s-DvJYtmeS-12s-C5VGnsq4.cjs');
21
+
22
+ // Composes the eight independent concerns a table root owns -- which column is
23
+ // sorted, which rows are selected, the toolbar's free-text query, each column's own
24
+ // filter, column order/visibility/width/label, each column's ColumnCalc footer op, the
25
+ // horizontal scroll-shadow edge state, and which single cell is mid-edit/pending/
26
+ // erroring -- the way useSelect composes useSelectHighlight + useSelectOptions. No
27
+ // sub-hook knows about any other; a future concern (row grouping) is a ninth sibling
28
+ // call here, not a branch inside an existing one.
29
+ function useDataTable(props) {
30
+ const sort = useDataTableSort12sDvJYtmeS12s.useDataTableSort(props);
31
+ const selection = useDataTableSelection12sBIDtiRQq12s.useDataTableSelection(props);
32
+ const search = useDataTableSearch12sCk3iWcpX12s.useDataTableSearch(props);
33
+ const filter = useDataTableFilter12sZzbmWMin12s.useDataTableFilter(props);
34
+ const columns = useDataTableColumns12sCn2ZAzFL12s.useDataTableColumns(props);
35
+ const columnCalc = useDataTableColumnCalc12sDTQQTtEJ12s.useDataTableColumnCalc(props);
36
+ const scroll = useDataTableScroll12sBKDCr0en12s.useDataTableScroll();
37
+ const edit = useDataTableEdit12s9Wb_UCrN12s.useDataTableEdit(props);
38
+ // Wired to `scroll`'s own scroll-container ref (not a fresh one of its own) so a
39
+ // BOUNDED table's auto-load triggers off its own internal scroll, not the
40
+ // browsing viewport -- see useDataTableInfiniteScroll.ts's own doc comment.
41
+ const infiniteScroll = useDataTableInfiniteScroll12s8ZG31URd12s.useDataTableInfiniteScroll({
42
+ ...props,
43
+ scrollRootRef: scroll.elementRef
44
+ });
45
+ return {
46
+ ...sort,
47
+ ...selection,
48
+ ...search,
49
+ ...filter,
50
+ ...columns,
51
+ ...columnCalc,
52
+ ...scroll,
53
+ ...edit,
54
+ ...infiniteScroll,
55
+ rootProps: domUtils.elementProps({
56
+ role: "table"
57
+ })
58
+ };
59
+ }
60
+
61
+ exports.useDataTable = useDataTable;
@@ -0,0 +1,207 @@
1
+ 'use client';
2
+ function __insertCSS(code) {
3
+ if (!code || typeof document == 'undefined') return
4
+ let head = document.head || document.getElementsByTagName('head')[0]
5
+ let style = document.createElement('style')
6
+ style.type = 'text/css'
7
+ head.appendChild(style)
8
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
9
+ }
10
+
11
+ var React = require('react');
12
+
13
+ function _interopNamespace(e) {
14
+ if (e && e.__esModule) return e;
15
+ var n = Object.create(null);
16
+ if (e) {
17
+ Object.keys(e).forEach(function (k) {
18
+ if (k !== 'default') {
19
+ var d = Object.getOwnPropertyDescriptor(e, k);
20
+ Object.defineProperty(n, k, d.get ? d : {
21
+ enumerable: true,
22
+ get: function () { return e[k]; }
23
+ });
24
+ }
25
+ });
26
+ }
27
+ n.default = e;
28
+ return n;
29
+ }
30
+
31
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
32
+
33
+ // packages/react/use-layout-effect/src/use-layout-effect.tsx
34
+ var useLayoutEffect2 = globalThis?.document ? React__namespace.useLayoutEffect : ()=>{};
35
+
36
+ // src/use-controllable-state.tsx
37
+ var useInsertionEffect = React__namespace[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
38
+ function useControllableState({ prop, defaultProp, onChange = ()=>{}, caller }) {
39
+ const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
40
+ defaultProp,
41
+ onChange
42
+ });
43
+ const isControlled = prop !== void 0;
44
+ const value = isControlled ? prop : uncontrolledProp;
45
+ {
46
+ const isControlledRef = React__namespace.useRef(prop !== void 0);
47
+ React__namespace.useEffect(()=>{
48
+ const wasControlled = isControlledRef.current;
49
+ if (wasControlled !== isControlled) {
50
+ const from = wasControlled ? "controlled" : "uncontrolled";
51
+ const to = isControlled ? "controlled" : "uncontrolled";
52
+ console.warn(`${caller} is changing from ${from} to ${to}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`);
53
+ }
54
+ isControlledRef.current = isControlled;
55
+ }, [
56
+ isControlled,
57
+ caller
58
+ ]);
59
+ }
60
+ const setValue = React__namespace.useCallback((nextValue)=>{
61
+ if (isControlled) {
62
+ const value2 = isFunction(nextValue) ? nextValue(prop) : nextValue;
63
+ if (value2 !== prop) {
64
+ onChangeRef.current?.(value2);
65
+ }
66
+ } else {
67
+ setUncontrolledProp(nextValue);
68
+ }
69
+ }, [
70
+ isControlled,
71
+ prop,
72
+ setUncontrolledProp,
73
+ onChangeRef
74
+ ]);
75
+ return [
76
+ value,
77
+ setValue
78
+ ];
79
+ }
80
+ function useUncontrolledState({ defaultProp, onChange }) {
81
+ const [value, setValue] = React__namespace.useState(defaultProp);
82
+ const prevValueRef = React__namespace.useRef(value);
83
+ const onChangeRef = React__namespace.useRef(onChange);
84
+ useInsertionEffect(()=>{
85
+ onChangeRef.current = onChange;
86
+ }, [
87
+ onChange
88
+ ]);
89
+ React__namespace.useEffect(()=>{
90
+ if (prevValueRef.current !== value) {
91
+ onChangeRef.current?.(value);
92
+ prevValueRef.current = value;
93
+ }
94
+ }, [
95
+ value,
96
+ prevValueRef
97
+ ]);
98
+ return [
99
+ value,
100
+ setValue,
101
+ onChangeRef
102
+ ];
103
+ }
104
+ function isFunction(value) {
105
+ return typeof value === "function";
106
+ }
107
+
108
+ const DATA_TABLE_UNIVERSAL_CALC_OPS = [
109
+ "count",
110
+ "countFilled"
111
+ ];
112
+ const DATA_TABLE_NUMERIC_CALC_OPS = [
113
+ "sum",
114
+ "average",
115
+ "min",
116
+ "max"
117
+ ];
118
+ const DATA_TABLE_CALC_LABEL = {
119
+ count: "Count",
120
+ countFilled: "Count filled",
121
+ sum: "Sum",
122
+ average: "Average",
123
+ min: "Min",
124
+ max: "Max"
125
+ };
126
+ /** A value counts as empty when it is null/undefined, an empty string, or an empty
127
+ * array -- crisp's own `isEmptyValue` (columncalc.tsx), reused verbatim. */ function isEmptyValueForCalc(value) {
128
+ if (value == null) return true;
129
+ if (typeof value === "string") return value.trim() === "";
130
+ if (Array.isArray(value)) return value.length === 0;
131
+ return false;
132
+ }
133
+ /**
134
+ * Pure so the arithmetic is independently (and mutation-) testable without rendering a
135
+ * table at all. `sum`/`average`/`min`/`max` skip empty values and non-numeric ones
136
+ * (coerced via `Number`, dropping `NaN`); they return `null` -- render nothing, not a
137
+ * misleading 0 -- when there is nothing numeric left to reduce over.
138
+ */ function computeColumnCalc(values, op) {
139
+ switch(op){
140
+ case "count":
141
+ return values.length;
142
+ case "countFilled":
143
+ return values.filter((value)=>!isEmptyValueForCalc(value)).length;
144
+ }
145
+ const numbers = values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>typeof value === "number" ? value : Number(value)).filter((value)=>Number.isFinite(value));
146
+ if (numbers.length === 0) return null;
147
+ switch(op){
148
+ case "sum":
149
+ return numbers.reduce((total, value)=>total + value, 0);
150
+ case "average":
151
+ return numbers.reduce((total, value)=>total + value, 0) / numbers.length;
152
+ case "min":
153
+ return Math.min(...numbers);
154
+ case "max":
155
+ return Math.max(...numbers);
156
+ default:
157
+ return null;
158
+ }
159
+ }
160
+ const EMPTY_OPS = {};
161
+ // Owns which aggregation (if any) each column's ColumnCalc footer cell currently
162
+ // shows -- the way useDataTableFilter owns one filter per column key. The actual
163
+ // REDUCE over row values needs `TRow` (a type this headless package never sees), so
164
+ // -- same split as sort/search/filter -- this hook only tracks WHICH op is active per
165
+ // column; the styled layer (DataTable.tsx's `ColumnCalcCell`) calls `computeColumnCalc`
166
+ // with the column's own resolved values.
167
+ function useDataTableColumnCalc(props) {
168
+ const { columnCalcOps: opsProp, defaultColumnCalcOps = EMPTY_OPS, onColumnCalcOpsChange } = props;
169
+ const [columnCalcOps = EMPTY_OPS, setColumnCalcOps] = useControllableState({
170
+ prop: opsProp,
171
+ defaultProp: defaultColumnCalcOps,
172
+ onChange: onColumnCalcOpsChange
173
+ });
174
+ const getColumnCalcOp = React.useCallback((columnKey)=>columnCalcOps[columnKey], [
175
+ columnCalcOps
176
+ ]);
177
+ const setColumnCalcOp = React.useCallback((columnKey, op)=>{
178
+ setColumnCalcOps((current = EMPTY_OPS)=>{
179
+ if (op === undefined) {
180
+ if (!(columnKey in current)) return current;
181
+ const next = {
182
+ ...current
183
+ };
184
+ delete next[columnKey];
185
+ return next;
186
+ }
187
+ return {
188
+ ...current,
189
+ [columnKey]: op
190
+ };
191
+ });
192
+ }, [
193
+ setColumnCalcOps
194
+ ]);
195
+ return {
196
+ columnCalcOps,
197
+ getColumnCalcOp,
198
+ setColumnCalcOp
199
+ };
200
+ }
201
+
202
+ exports.DATA_TABLE_CALC_LABEL = DATA_TABLE_CALC_LABEL;
203
+ exports.DATA_TABLE_NUMERIC_CALC_OPS = DATA_TABLE_NUMERIC_CALC_OPS;
204
+ exports.DATA_TABLE_UNIVERSAL_CALC_OPS = DATA_TABLE_UNIVERSAL_CALC_OPS;
205
+ exports.computeColumnCalc = computeColumnCalc;
206
+ exports.useControllableState = useControllableState;
207
+ exports.useDataTableColumnCalc = useDataTableColumnCalc;
@@ -0,0 +1,183 @@
1
+ 'use client';
2
+ function __insertCSS(code) {
3
+ if (!code || typeof document == 'undefined') return
4
+ let head = document.head || document.getElementsByTagName('head')[0]
5
+ let style = document.createElement('style')
6
+ style.type = 'text/css'
7
+ head.appendChild(style)
8
+ ;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
9
+ }
10
+
11
+ import * as React from 'react';
12
+ import { useCallback } from 'react';
13
+
14
+ // packages/react/use-layout-effect/src/use-layout-effect.tsx
15
+ var useLayoutEffect2 = globalThis?.document ? React.useLayoutEffect : ()=>{};
16
+
17
+ // src/use-controllable-state.tsx
18
+ var useInsertionEffect = React[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
19
+ function useControllableState({ prop, defaultProp, onChange = ()=>{}, caller }) {
20
+ const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
21
+ defaultProp,
22
+ onChange
23
+ });
24
+ const isControlled = prop !== void 0;
25
+ const value = isControlled ? prop : uncontrolledProp;
26
+ {
27
+ const isControlledRef = React.useRef(prop !== void 0);
28
+ React.useEffect(()=>{
29
+ const wasControlled = isControlledRef.current;
30
+ if (wasControlled !== isControlled) {
31
+ const from = wasControlled ? "controlled" : "uncontrolled";
32
+ const to = isControlled ? "controlled" : "uncontrolled";
33
+ console.warn(`${caller} is changing from ${from} to ${to}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`);
34
+ }
35
+ isControlledRef.current = isControlled;
36
+ }, [
37
+ isControlled,
38
+ caller
39
+ ]);
40
+ }
41
+ const setValue = React.useCallback((nextValue)=>{
42
+ if (isControlled) {
43
+ const value2 = isFunction(nextValue) ? nextValue(prop) : nextValue;
44
+ if (value2 !== prop) {
45
+ onChangeRef.current?.(value2);
46
+ }
47
+ } else {
48
+ setUncontrolledProp(nextValue);
49
+ }
50
+ }, [
51
+ isControlled,
52
+ prop,
53
+ setUncontrolledProp,
54
+ onChangeRef
55
+ ]);
56
+ return [
57
+ value,
58
+ setValue
59
+ ];
60
+ }
61
+ function useUncontrolledState({ defaultProp, onChange }) {
62
+ const [value, setValue] = React.useState(defaultProp);
63
+ const prevValueRef = React.useRef(value);
64
+ const onChangeRef = React.useRef(onChange);
65
+ useInsertionEffect(()=>{
66
+ onChangeRef.current = onChange;
67
+ }, [
68
+ onChange
69
+ ]);
70
+ React.useEffect(()=>{
71
+ if (prevValueRef.current !== value) {
72
+ onChangeRef.current?.(value);
73
+ prevValueRef.current = value;
74
+ }
75
+ }, [
76
+ value,
77
+ prevValueRef
78
+ ]);
79
+ return [
80
+ value,
81
+ setValue,
82
+ onChangeRef
83
+ ];
84
+ }
85
+ function isFunction(value) {
86
+ return typeof value === "function";
87
+ }
88
+
89
+ const DATA_TABLE_UNIVERSAL_CALC_OPS = [
90
+ "count",
91
+ "countFilled"
92
+ ];
93
+ const DATA_TABLE_NUMERIC_CALC_OPS = [
94
+ "sum",
95
+ "average",
96
+ "min",
97
+ "max"
98
+ ];
99
+ const DATA_TABLE_CALC_LABEL = {
100
+ count: "Count",
101
+ countFilled: "Count filled",
102
+ sum: "Sum",
103
+ average: "Average",
104
+ min: "Min",
105
+ max: "Max"
106
+ };
107
+ /** A value counts as empty when it is null/undefined, an empty string, or an empty
108
+ * array -- crisp's own `isEmptyValue` (columncalc.tsx), reused verbatim. */ function isEmptyValueForCalc(value) {
109
+ if (value == null) return true;
110
+ if (typeof value === "string") return value.trim() === "";
111
+ if (Array.isArray(value)) return value.length === 0;
112
+ return false;
113
+ }
114
+ /**
115
+ * Pure so the arithmetic is independently (and mutation-) testable without rendering a
116
+ * table at all. `sum`/`average`/`min`/`max` skip empty values and non-numeric ones
117
+ * (coerced via `Number`, dropping `NaN`); they return `null` -- render nothing, not a
118
+ * misleading 0 -- when there is nothing numeric left to reduce over.
119
+ */ function computeColumnCalc(values, op) {
120
+ switch(op){
121
+ case "count":
122
+ return values.length;
123
+ case "countFilled":
124
+ return values.filter((value)=>!isEmptyValueForCalc(value)).length;
125
+ }
126
+ const numbers = values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>typeof value === "number" ? value : Number(value)).filter((value)=>Number.isFinite(value));
127
+ if (numbers.length === 0) return null;
128
+ switch(op){
129
+ case "sum":
130
+ return numbers.reduce((total, value)=>total + value, 0);
131
+ case "average":
132
+ return numbers.reduce((total, value)=>total + value, 0) / numbers.length;
133
+ case "min":
134
+ return Math.min(...numbers);
135
+ case "max":
136
+ return Math.max(...numbers);
137
+ default:
138
+ return null;
139
+ }
140
+ }
141
+ const EMPTY_OPS = {};
142
+ // Owns which aggregation (if any) each column's ColumnCalc footer cell currently
143
+ // shows -- the way useDataTableFilter owns one filter per column key. The actual
144
+ // REDUCE over row values needs `TRow` (a type this headless package never sees), so
145
+ // -- same split as sort/search/filter -- this hook only tracks WHICH op is active per
146
+ // column; the styled layer (DataTable.tsx's `ColumnCalcCell`) calls `computeColumnCalc`
147
+ // with the column's own resolved values.
148
+ function useDataTableColumnCalc(props) {
149
+ const { columnCalcOps: opsProp, defaultColumnCalcOps = EMPTY_OPS, onColumnCalcOpsChange } = props;
150
+ const [columnCalcOps = EMPTY_OPS, setColumnCalcOps] = useControllableState({
151
+ prop: opsProp,
152
+ defaultProp: defaultColumnCalcOps,
153
+ onChange: onColumnCalcOpsChange
154
+ });
155
+ const getColumnCalcOp = useCallback((columnKey)=>columnCalcOps[columnKey], [
156
+ columnCalcOps
157
+ ]);
158
+ const setColumnCalcOp = useCallback((columnKey, op)=>{
159
+ setColumnCalcOps((current = EMPTY_OPS)=>{
160
+ if (op === undefined) {
161
+ if (!(columnKey in current)) return current;
162
+ const next = {
163
+ ...current
164
+ };
165
+ delete next[columnKey];
166
+ return next;
167
+ }
168
+ return {
169
+ ...current,
170
+ [columnKey]: op
171
+ };
172
+ });
173
+ }, [
174
+ setColumnCalcOps
175
+ ]);
176
+ return {
177
+ columnCalcOps,
178
+ getColumnCalcOp,
179
+ setColumnCalcOp
180
+ };
181
+ }
182
+
183
+ export { DATA_TABLE_CALC_LABEL as D, DATA_TABLE_UNIVERSAL_CALC_OPS as a, DATA_TABLE_NUMERIC_CALC_OPS as b, computeColumnCalc as c, useControllableState as d, useDataTableColumnCalc as u };