@crispy-seed/data-table 0.1.1 → 0.1.3
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/dist/index.cjs +4570 -2
- package/dist/index.js +4554 -1
- package/package.json +1 -1
- package/dist/DataTable-12s-D02v6Qj5.js +0 -1523
- package/dist/DataTable-12s-Gpv6I4J8.cjs +0 -1574
- package/dist/DataTable-12s-Zml1x9Kr-12s-B-mwMBhn.cjs +0 -161
- package/dist/DataTable-12s-Zml1x9Kr-12s-BR-yHfob.js +0 -153
- package/dist/Menu-12s-A4lEc__n.cjs +0 -103
- package/dist/Menu-12s-v9vnaq8F.js +0 -77
- package/dist/Select-12s-CE3SMzFp.js +0 -218
- package/dist/Select-12s-CNQC9z8t.cjs +0 -249
- package/dist/body-cells-12s-2sTwdliH.cjs +0 -461
- package/dist/body-cells-12s-Ba2uF4T-.js +0 -452
- package/dist/column-calc-12s-C1-I2KdV.js +0 -129
- package/dist/column-calc-12s-CJ26VuDJ.cjs +0 -149
- package/dist/column-layout-12s-BhQmNTJG.js +0 -99
- package/dist/column-layout-12s-k2fZYeKl.cjs +0 -120
- package/dist/column-menu-12s-6Yvu4SZ0.js +0 -406
- package/dist/column-menu-12s-BnknuXGh.cjs +0 -426
- package/dist/editors-12s-C0RxmTcI.cjs +0 -554
- package/dist/editors-12s-COixUnI2.js +0 -524
- package/dist/resize-12s-CajyNXFB.js +0 -58
- package/dist/resize-12s-ChLpxb4i.cjs +0 -58
- package/dist/states-12s-BB0PT_bq.cjs +0 -125
- package/dist/states-12s-m5mBR90e.js +0 -122
- package/dist/toolbar-12s-B1xiS-Zu.js +0 -68
- package/dist/toolbar-12s-DzLbghpK.cjs +0 -70
- package/dist/useDataTable-12s-BHiDUubw-12s-CBeCwTe7.js +0 -61
- package/dist/useDataTable-12s-BHiDUubw-12s-Dgvt7XDk.cjs +0 -61
- package/dist/useDataTableColumnCalc-12s-DTQQTtEJ-12s-CvD14JYo.cjs +0 -207
- package/dist/useDataTableColumnCalc-12s-DTQQTtEJ-12s-DsEse3uq.js +0 -183
- package/dist/useDataTableColumns-12s-Cn2ZAzFL-12s-CD6goR59.js +0 -289
- package/dist/useDataTableColumns-12s-Cn2ZAzFL-12s-CUk_CpYT.cjs +0 -292
- package/dist/useDataTableEdit-12s-9Wb_UCrN-12s-BoOHq3GB.js +0 -98
- package/dist/useDataTableEdit-12s-9Wb_UCrN-12s-De6YHBba.cjs +0 -98
- package/dist/useDataTableFilter-12s-ZzbmWMin-12s-CWyBeFna.js +0 -74
- package/dist/useDataTableFilter-12s-ZzbmWMin-12s-lpjzZY3_.cjs +0 -74
- package/dist/useDataTableInfiniteScroll-12s-8ZG31URd-12s-BexjP6iO.js +0 -104
- package/dist/useDataTableInfiniteScroll-12s-8ZG31URd-12s-Bt8gojr9.cjs +0 -104
- package/dist/useDataTableRow-12s-CPYaDa1p-12s-4qkGoz-j.js +0 -30
- package/dist/useDataTableRow-12s-CPYaDa1p-12s-DG64uYjn.cjs +0 -30
- package/dist/useDataTableScroll-12s-BKDCr0en-12s-C7f2vHeG.js +0 -61
- package/dist/useDataTableScroll-12s-BKDCr0en-12s-CUvUYMtt.cjs +0 -61
- package/dist/useDataTableSearch-12s-Ck3iWcpX-12s-B-65V6gH.js +0 -37
- package/dist/useDataTableSearch-12s-Ck3iWcpX-12s-DhVfN2Hq.cjs +0 -37
- package/dist/useDataTableSelection-12s-BIDtiRQq-12s-IS__VAnU.cjs +0 -71
- package/dist/useDataTableSelection-12s-BIDtiRQq-12s-O6M_BhrH.js +0 -71
- package/dist/useDataTableSort-12s-DvJYtmeS-12s-C5VGnsq4.cjs +0 -78
- package/dist/useDataTableSort-12s-DvJYtmeS-12s-DABSNt6U.js +0 -78
|
@@ -1,207 +0,0 @@
|
|
|
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;
|
|
@@ -1,183 +0,0 @@
|
|
|
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 };
|
|
@@ -1,289 +0,0 @@
|
|
|
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 { d as useControllableState } from './useDataTableColumnCalc-12s-DTQQTtEJ-12s-DsEse3uq.js';
|
|
12
|
-
import { dataAttr } from '@seed-design/dom-utils';
|
|
13
|
-
import { useMemo, useCallback, useState, useRef } from 'react';
|
|
14
|
-
|
|
15
|
-
// Select's `dom.ts` reads state off REAL rendered elements (aria-disabled, data-value)
|
|
16
|
-
// because its options register asynchronously through a floating-ui list and the DOM
|
|
17
|
-
// is the one thing always in sync with `elementsRef`. DataTable's selection has no
|
|
18
|
-
// such registry -- it is two plain id arrays (`rowIds`, `selectedIds`) the root already
|
|
19
|
-
// holds -- so there is nothing to inspect on an element. What's isolated here instead is
|
|
20
|
-
// the same KIND of thing Select isolates: small pure functions the state hooks would
|
|
21
|
-
// otherwise inline, kept out of the hooks so they can be unit-tested (and mutation-
|
|
22
|
-
// tested) on their own.
|
|
23
|
-
/**
|
|
24
|
-
* Tri-state selection summary of `selectedIds` against the CURRENTLY RENDERED
|
|
25
|
-
* `rowIds` -- never against `selectedIds` alone, so a stale id that scrolled out of a
|
|
26
|
-
* filtered/sorted view can't quietly count toward "all" or "some".
|
|
27
|
-
*/ function computeSelectionState(rowIds, selectedIds) {
|
|
28
|
-
if (rowIds.length === 0) return "none";
|
|
29
|
-
const selectedIdSet = new Set(selectedIds);
|
|
30
|
-
const selectedRowCount = rowIds.filter((id)=>selectedIdSet.has(id)).length;
|
|
31
|
-
if (selectedRowCount === 0) return "none";
|
|
32
|
-
return selectedRowCount === rowIds.length ? "all" : "some";
|
|
33
|
-
}
|
|
34
|
-
/** Adds `id` if absent, removes it if present. Never mutates `ids`. */ function toggleMembership(ids, id) {
|
|
35
|
-
return ids.includes(id) ? ids.filter((existing)=>existing !== id) : [
|
|
36
|
-
...ids,
|
|
37
|
-
id
|
|
38
|
-
];
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Reconciles a persisted/controlled column ORDER against the table's current column
|
|
42
|
-
* KEYS -- used by useDataTableColumns so a consumer's `columnOrder` never silently drops
|
|
43
|
-
* a column the table actually renders (a key added to `columns` after the order was
|
|
44
|
-
* first captured) nor keeps a ghost entry for one that was removed. Known keys keep
|
|
45
|
-
* their position from `order`; any key in `keys` missing from `order` is appended, in
|
|
46
|
-
* `keys`' own order; any entry in `order` no longer in `keys` is dropped.
|
|
47
|
-
*/ function reconcileColumnOrder(order, keys) {
|
|
48
|
-
const keySet = new Set(keys);
|
|
49
|
-
const known = order.filter((key)=>keySet.has(key));
|
|
50
|
-
const knownSet = new Set(known);
|
|
51
|
-
const added = keys.filter((key)=>!knownSet.has(key));
|
|
52
|
-
return [
|
|
53
|
-
...known,
|
|
54
|
-
...added
|
|
55
|
-
];
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Moves `key` one step toward `direction` in `order` -- a no-op (same array, well,
|
|
59
|
-
* an equal-valued copy) at the edge it would otherwise walk off. Column-menu's
|
|
60
|
-
* "Move left"/"Move right" (DATATABLE-ATTIO-SPEC.md's column menu) is one call of
|
|
61
|
-
* this per click; drag-to-reorder is `reorderColumn` below instead, which can jump
|
|
62
|
-
* more than one position in a single move.
|
|
63
|
-
*/ function moveColumnKey(order, key, direction) {
|
|
64
|
-
const index = order.indexOf(key);
|
|
65
|
-
if (index === -1) return [
|
|
66
|
-
...order
|
|
67
|
-
];
|
|
68
|
-
const targetIndex = direction === "left" ? index - 1 : index + 1;
|
|
69
|
-
if (targetIndex < 0 || targetIndex >= order.length) return [
|
|
70
|
-
...order
|
|
71
|
-
];
|
|
72
|
-
const next = [
|
|
73
|
-
...order
|
|
74
|
-
];
|
|
75
|
-
[next[index], next[targetIndex]] = [
|
|
76
|
-
next[targetIndex],
|
|
77
|
-
next[index]
|
|
78
|
-
];
|
|
79
|
-
return next;
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Drag-to-reorder: removes `dragKey` from `order` and reinserts it immediately before
|
|
83
|
-
* `dropKey`'s CURRENT position (i.e. its position after the removal, so dropping a
|
|
84
|
-
* column just after its own old slot doesn't appear to do nothing). A no-op (an
|
|
85
|
-
* equal-valued copy) when either key is missing or they're the same key.
|
|
86
|
-
*/ function reorderColumnKey(order, dragKey, dropKey) {
|
|
87
|
-
if (dragKey === dropKey || !order.includes(dragKey) || !order.includes(dropKey)) {
|
|
88
|
-
return [
|
|
89
|
-
...order
|
|
90
|
-
];
|
|
91
|
-
}
|
|
92
|
-
const withoutDrag = order.filter((key)=>key !== dragKey);
|
|
93
|
-
const dropIndex = withoutDrag.indexOf(dropKey);
|
|
94
|
-
return [
|
|
95
|
-
...withoutDrag.slice(0, dropIndex),
|
|
96
|
-
dragKey,
|
|
97
|
-
...withoutDrag.slice(dropIndex)
|
|
98
|
-
];
|
|
99
|
-
}
|
|
100
|
-
/** Clamps a drag-resized column width to a sane range -- crisp's own datatable.tsx
|
|
101
|
-
* resize handler clamps the same way (a column can neither collapse to unreadable
|
|
102
|
-
* nor balloon past a sane cap from one fast mouse move). */ function clampColumnWidth(width, min = 80, max = 640) {
|
|
103
|
-
return Math.min(max, Math.max(min, width));
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Infinite scroll's own gate: fire `onLoadMore` only when the sentinel is ACTUALLY
|
|
107
|
-
* intersecting (not e.g. an observer's initial `false` callback, which fires once on
|
|
108
|
-
* `observe()` before any real intersection), there IS a next page (`hasMore`), and no
|
|
109
|
-
* fetch is already in flight (`loadingMore`) -- either of the latter two alone must
|
|
110
|
-
* suppress a trigger, which is why this is a pure `&&` rather than three separate
|
|
111
|
-
* call sites that could each drift. Isolated the same way `computeSelectionState`/
|
|
112
|
-
* `toggleMembership` are: the real IntersectionObserver callback (useDataTable
|
|
113
|
-
* InfiniteScroll.ts) is unit-testable only through a live browser API, but the
|
|
114
|
-
* DECISION it makes on every callback is plain data in, boolean out.
|
|
115
|
-
*/ function shouldTriggerLoadMore(isIntersecting, hasMore, loadingMore) {
|
|
116
|
-
return hasMore && !loadingMore;
|
|
117
|
-
}
|
|
118
|
-
const EMPTY_STRINGS = [];
|
|
119
|
-
const EMPTY_RECORD = {};
|
|
120
|
-
/**
|
|
121
|
-
* Owns everything DATATABLE-ATTIO-SPEC.md's column menu + reorder + resize tranche
|
|
122
|
-
* needs -- order, visibility, a per-column label override, and a per-column width --
|
|
123
|
-
* the way useDataTableSelection owns the one selected-id SET rather than one hook per
|
|
124
|
-
* row. All four are "one value per column key" maps/lists a consumer may want to
|
|
125
|
-
* persist (a saved view), so they share this hook instead of splitting into four
|
|
126
|
-
* separate ones; drag-to-reorder and drag-to-resize (below) are the two INTERACTIONS
|
|
127
|
-
* built on top of the order/width state this hook already owns, not independent state
|
|
128
|
-
* of their own.
|
|
129
|
-
*/ function useDataTableColumns(props) {
|
|
130
|
-
const { columnKeys = EMPTY_STRINGS, columnOrder: columnOrderProp, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys: hiddenProp, defaultHiddenColumnKeys = EMPTY_STRINGS, onHiddenColumnKeysChange, columnWidths: widthsProp, defaultColumnWidths = EMPTY_RECORD, onColumnWidthsChange, columnLabels: labelsProp, defaultColumnLabels = EMPTY_RECORD, onColumnLabelsChange } = props;
|
|
131
|
-
const [rawOrder, setRawOrder] = useControllableState({
|
|
132
|
-
prop: columnOrderProp,
|
|
133
|
-
defaultProp: defaultColumnOrder ?? columnKeys,
|
|
134
|
-
onChange: onColumnOrderChange
|
|
135
|
-
});
|
|
136
|
-
// Reconciled EVERY render against the table's current `columnKeys` (dom.ts's
|
|
137
|
-
// `reconcileColumnOrder`), not just once at mount -- a controlled `columnOrder` a
|
|
138
|
-
// consumer persisted before a new column existed must still show that new column
|
|
139
|
-
// (appended), and one holding a column that got removed from `columns` must not
|
|
140
|
-
// render a ghost header for it.
|
|
141
|
-
const columnOrder = useMemo(()=>reconcileColumnOrder(rawOrder ?? columnKeys, columnKeys), [
|
|
142
|
-
rawOrder,
|
|
143
|
-
columnKeys
|
|
144
|
-
]);
|
|
145
|
-
const [hiddenColumnKeys = EMPTY_STRINGS, setHiddenColumnKeys] = useControllableState({
|
|
146
|
-
prop: hiddenProp,
|
|
147
|
-
defaultProp: defaultHiddenColumnKeys,
|
|
148
|
-
onChange: onHiddenColumnKeysChange
|
|
149
|
-
});
|
|
150
|
-
const hiddenSet = useMemo(()=>new Set(hiddenColumnKeys), [
|
|
151
|
-
hiddenColumnKeys
|
|
152
|
-
]);
|
|
153
|
-
const [columnWidths = EMPTY_RECORD, setColumnWidths] = useControllableState({
|
|
154
|
-
prop: widthsProp,
|
|
155
|
-
defaultProp: defaultColumnWidths,
|
|
156
|
-
onChange: onColumnWidthsChange
|
|
157
|
-
});
|
|
158
|
-
const [columnLabels = EMPTY_RECORD, setColumnLabels] = useControllableState({
|
|
159
|
-
prop: labelsProp,
|
|
160
|
-
defaultProp: defaultColumnLabels,
|
|
161
|
-
onChange: onColumnLabelsChange
|
|
162
|
-
});
|
|
163
|
-
const visibleColumnOrder = useMemo(()=>columnOrder.filter((key)=>!hiddenSet.has(key)), [
|
|
164
|
-
columnOrder,
|
|
165
|
-
hiddenSet
|
|
166
|
-
]);
|
|
167
|
-
const isColumnHidden = useCallback((key)=>hiddenSet.has(key), [
|
|
168
|
-
hiddenSet
|
|
169
|
-
]);
|
|
170
|
-
const hideColumn = useCallback((key)=>setHiddenColumnKeys((current = EMPTY_STRINGS)=>[
|
|
171
|
-
...current,
|
|
172
|
-
key
|
|
173
|
-
]), [
|
|
174
|
-
setHiddenColumnKeys
|
|
175
|
-
]);
|
|
176
|
-
const showColumn = useCallback((key)=>setHiddenColumnKeys((current = EMPTY_STRINGS)=>current.filter((existing)=>existing !== key)), [
|
|
177
|
-
setHiddenColumnKeys
|
|
178
|
-
]);
|
|
179
|
-
const toggleColumnVisibility = useCallback((key)=>isColumnHidden(key) ? showColumn(key) : hideColumn(key), [
|
|
180
|
-
isColumnHidden,
|
|
181
|
-
showColumn,
|
|
182
|
-
hideColumn
|
|
183
|
-
]);
|
|
184
|
-
const getColumnLabel = useCallback((key, fallback)=>columnLabels[key] ?? fallback, [
|
|
185
|
-
columnLabels
|
|
186
|
-
]);
|
|
187
|
-
const renameColumn = useCallback((key, label)=>setColumnLabels((current = EMPTY_RECORD)=>({
|
|
188
|
-
...current,
|
|
189
|
-
[key]: label
|
|
190
|
-
})), [
|
|
191
|
-
setColumnLabels
|
|
192
|
-
]);
|
|
193
|
-
const getColumnWidth = useCallback((key)=>columnWidths[key], [
|
|
194
|
-
columnWidths
|
|
195
|
-
]);
|
|
196
|
-
const setColumnWidth = useCallback((key, width)=>setColumnWidths((current = EMPTY_RECORD)=>({
|
|
197
|
-
...current,
|
|
198
|
-
[key]: clampColumnWidth(width)
|
|
199
|
-
})), [
|
|
200
|
-
setColumnWidths
|
|
201
|
-
]);
|
|
202
|
-
const moveColumn = useCallback((key, direction)=>setRawOrder((current)=>moveColumnKey(current ?? columnKeys, key, direction)), [
|
|
203
|
-
setRawOrder,
|
|
204
|
-
columnKeys
|
|
205
|
-
]);
|
|
206
|
-
const reorderColumn = useCallback((dragKey, dropKey)=>setRawOrder((current)=>reorderColumnKey(current ?? columnKeys, dragKey, dropKey)), [
|
|
207
|
-
setRawOrder,
|
|
208
|
-
columnKeys
|
|
209
|
-
]);
|
|
210
|
-
// -- drag-to-reorder ---------------------------------------------------------------
|
|
211
|
-
// Live state only (which column is currently being dragged) -- the styled layer
|
|
212
|
-
// drives it by calling `dragOverColumn` as the pointer crosses OTHER header cells
|
|
213
|
-
// (via `document.elementFromPoint`, since a captured pointer stops native
|
|
214
|
-
// `pointerenter` from firing on siblings), which reorders immediately rather than
|
|
215
|
-
// waiting for a drop. That is exactly what Attio's own header drag does (the column
|
|
216
|
-
// being passed visibly swaps live), and it means `endColumnDrag` never has to also
|
|
217
|
-
// commit a move -- there is nothing left to commit.
|
|
218
|
-
const [draggingColumnKey, setDraggingColumnKey] = useState(null);
|
|
219
|
-
const beginColumnDrag = useCallback((key)=>setDraggingColumnKey(key), []);
|
|
220
|
-
const dragOverColumn = useCallback((hoverKey)=>{
|
|
221
|
-
setDraggingColumnKey((current)=>{
|
|
222
|
-
if (!current || current === hoverKey) return current;
|
|
223
|
-
reorderColumn(current, hoverKey);
|
|
224
|
-
return current;
|
|
225
|
-
});
|
|
226
|
-
}, [
|
|
227
|
-
reorderColumn
|
|
228
|
-
]);
|
|
229
|
-
const endColumnDrag = useCallback(()=>setDraggingColumnKey(null), []);
|
|
230
|
-
// -- drag-to-resize ------------------------------------------------------------------
|
|
231
|
-
// `resizeStartRef` holds the ONE thing that must survive across pointermove events
|
|
232
|
-
// without triggering a re-render on every pixel (the drag's origin) -- state
|
|
233
|
-
// (`resizingColumnKey`) is only for what the render actually needs: which column's
|
|
234
|
-
// handle should show its active/dragging look.
|
|
235
|
-
const resizeStartRef = useRef(null);
|
|
236
|
-
const [resizingColumnKey, setResizingColumnKey] = useState(null);
|
|
237
|
-
const beginColumnResize = useCallback((key, clientX, currentWidth)=>{
|
|
238
|
-
resizeStartRef.current = {
|
|
239
|
-
key,
|
|
240
|
-
startX: clientX,
|
|
241
|
-
startWidth: currentWidth
|
|
242
|
-
};
|
|
243
|
-
setResizingColumnKey(key);
|
|
244
|
-
}, []);
|
|
245
|
-
const updateColumnResize = useCallback((clientX)=>{
|
|
246
|
-
const start = resizeStartRef.current;
|
|
247
|
-
if (!start) return;
|
|
248
|
-
setColumnWidth(start.key, start.startWidth + (clientX - start.startX));
|
|
249
|
-
}, [
|
|
250
|
-
setColumnWidth
|
|
251
|
-
]);
|
|
252
|
-
const endColumnResize = useCallback(()=>{
|
|
253
|
-
resizeStartRef.current = null;
|
|
254
|
-
setResizingColumnKey(null);
|
|
255
|
-
}, []);
|
|
256
|
-
return {
|
|
257
|
-
columnOrder,
|
|
258
|
-
visibleColumnOrder,
|
|
259
|
-
hiddenColumnKeys,
|
|
260
|
-
columnWidths,
|
|
261
|
-
columnLabels,
|
|
262
|
-
isColumnHidden,
|
|
263
|
-
hideColumn,
|
|
264
|
-
showColumn,
|
|
265
|
-
toggleColumnVisibility,
|
|
266
|
-
getColumnLabel,
|
|
267
|
-
renameColumn,
|
|
268
|
-
getColumnWidth,
|
|
269
|
-
setColumnWidth,
|
|
270
|
-
moveColumn,
|
|
271
|
-
reorderColumn,
|
|
272
|
-
draggingColumnKey,
|
|
273
|
-
beginColumnDrag,
|
|
274
|
-
dragOverColumn,
|
|
275
|
-
endColumnDrag,
|
|
276
|
-
resizingColumnKey,
|
|
277
|
-
beginColumnResize,
|
|
278
|
-
updateColumnResize,
|
|
279
|
-
endColumnResize,
|
|
280
|
-
// Exposed for the styled layer's header cell -- `data-dragging`/`data-resizing`
|
|
281
|
-
// read directly off state above already cover most of it; `dataAttr` re-exported
|
|
282
|
-
// via this hook's own module keeps every other sub-hook's habit of building its
|
|
283
|
-
// own data-attribute values rather than the styled layer re-deriving booleans.
|
|
284
|
-
isColumnDragging: (key)=>dataAttr(draggingColumnKey === key),
|
|
285
|
-
isColumnResizing: (key)=>dataAttr(resizingColumnKey === key)
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export { computeSelectionState as c, shouldTriggerLoadMore as s, toggleMembership as t, useDataTableColumns as u };
|