react-glide-table 0.0.1 → 1.0.1
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 +1691 -0
- package/dist/index.d.cts +146 -0
- package/dist/index.d.ts +146 -0
- package/dist/index.js +1666 -0
- package/dist/style.css +404 -0
- package/package.json +9 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,1666 @@
|
|
|
1
|
+
// src/components/ui/table/components/DataTable/DataTable.tsx
|
|
2
|
+
import {
|
|
3
|
+
flexRender as flexRender2,
|
|
4
|
+
getCoreRowModel,
|
|
5
|
+
useReactTable
|
|
6
|
+
} from "@tanstack/react-table";
|
|
7
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
8
|
+
import { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo2, useRef as useRef4, useState as useState3 } from "react";
|
|
9
|
+
|
|
10
|
+
// src/components/ui/table/components/DataTable/DataTableRow.tsx
|
|
11
|
+
import { flexRender } from "@tanstack/react-table";
|
|
12
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
13
|
+
|
|
14
|
+
// src/components/ui/table/constants.ts
|
|
15
|
+
var CELL_ALIGN_CLASS = {
|
|
16
|
+
left: "cell-align-left",
|
|
17
|
+
center: "cell-align-center",
|
|
18
|
+
right: "cell-align-right"
|
|
19
|
+
};
|
|
20
|
+
var ROW_HOVER_CLASS = "row-hoverable";
|
|
21
|
+
var ROW_HOVERED_BG_CLASS = "row-hovered";
|
|
22
|
+
var CELL_SELECTION_FILL_CLASS = "cell-selection-fill";
|
|
23
|
+
var DATA_TABLE_ROW_HEIGHT = 44;
|
|
24
|
+
var DATA_TABLE_VIRTUAL_OVERSCAN = 8;
|
|
25
|
+
|
|
26
|
+
// src/components/ui/table/DataTableContext.tsx
|
|
27
|
+
import { createContext, use } from "react";
|
|
28
|
+
import { jsx } from "react/jsx-runtime";
|
|
29
|
+
var DataTableContext = createContext(null);
|
|
30
|
+
function useDataTableRowContext() {
|
|
31
|
+
const context = use(DataTableContext);
|
|
32
|
+
if (!context) {
|
|
33
|
+
throw new Error("useDataTableRowContext must be used within a DataTableContextProvider");
|
|
34
|
+
}
|
|
35
|
+
return context;
|
|
36
|
+
}
|
|
37
|
+
function DataTableContextProvider({
|
|
38
|
+
value,
|
|
39
|
+
children
|
|
40
|
+
}) {
|
|
41
|
+
return /* @__PURE__ */ jsx(DataTableContext, { value, children });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/components/ui/table/features/cell-edit/cellEdit.ts
|
|
45
|
+
function getColumnAccessorKey(columnDef) {
|
|
46
|
+
if (columnDef.accessorKey !== void 0 && columnDef.accessorKey !== null) {
|
|
47
|
+
return String(columnDef.accessorKey);
|
|
48
|
+
}
|
|
49
|
+
return columnDef.id;
|
|
50
|
+
}
|
|
51
|
+
function isColumnEditable(columnDef) {
|
|
52
|
+
return Boolean(columnDef.meta?.editable);
|
|
53
|
+
}
|
|
54
|
+
function getColumnEditType(columnDef) {
|
|
55
|
+
return columnDef.meta?.editType ?? "text";
|
|
56
|
+
}
|
|
57
|
+
function parseCellEditValue(raw, editType) {
|
|
58
|
+
if (editType === "text") {
|
|
59
|
+
return { ok: true, value: raw };
|
|
60
|
+
}
|
|
61
|
+
const trimmed = raw.trim();
|
|
62
|
+
if (trimmed === "") {
|
|
63
|
+
return { ok: true, value: null };
|
|
64
|
+
}
|
|
65
|
+
const parsed = Number(trimmed);
|
|
66
|
+
if (Number.isNaN(parsed)) {
|
|
67
|
+
return { ok: false };
|
|
68
|
+
}
|
|
69
|
+
return { ok: true, value: parsed };
|
|
70
|
+
}
|
|
71
|
+
function getCellEditDraftValue(value) {
|
|
72
|
+
if (value === null || value === void 0) return "";
|
|
73
|
+
return String(value);
|
|
74
|
+
}
|
|
75
|
+
function applyCellEdit(data, rows, rowIndex, colIndex, raw) {
|
|
76
|
+
const cell = rows[rowIndex]?.getVisibleCells()[colIndex];
|
|
77
|
+
if (!cell) return null;
|
|
78
|
+
const columnDef = cell.column.columnDef;
|
|
79
|
+
if (!isColumnEditable(columnDef)) return null;
|
|
80
|
+
const accessorKey = getColumnAccessorKey(columnDef);
|
|
81
|
+
if (!accessorKey) return null;
|
|
82
|
+
const parsed = parseCellEditValue(raw, getColumnEditType(columnDef));
|
|
83
|
+
if (!parsed.ok) return null;
|
|
84
|
+
const newData = data.map((row) => ({ ...row }));
|
|
85
|
+
if (!newData[rowIndex]) return null;
|
|
86
|
+
newData[rowIndex][accessorKey] = parsed.value;
|
|
87
|
+
return newData;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/components/ui/table/features/cell-selection/cellSelection.ts
|
|
91
|
+
var INITIAL_DRAG_STATE = {
|
|
92
|
+
isSelecting: false,
|
|
93
|
+
isFillDragging: false,
|
|
94
|
+
start: null,
|
|
95
|
+
end: null,
|
|
96
|
+
fillAnchor: null,
|
|
97
|
+
fillEnd: null
|
|
98
|
+
};
|
|
99
|
+
function getCellSelectionBounds(start, end) {
|
|
100
|
+
if (!start || !end) return null;
|
|
101
|
+
return {
|
|
102
|
+
startRow: Math.min(start.row, end.row),
|
|
103
|
+
endRow: Math.max(start.row, end.row),
|
|
104
|
+
startCol: Math.min(start.col, end.col),
|
|
105
|
+
endCol: Math.max(start.col, end.col)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function getRowIndexInMergedCell(clientY, cellElement, rowIndex, rowSpan) {
|
|
109
|
+
if (rowSpan <= 1) return rowIndex;
|
|
110
|
+
const rect = cellElement.getBoundingClientRect();
|
|
111
|
+
const relativeY = clientY - rect.top;
|
|
112
|
+
const rowHeight = rect.height / rowSpan;
|
|
113
|
+
const offset = Math.min(Math.max(Math.floor(relativeY / rowHeight), 0), rowSpan - 1);
|
|
114
|
+
return rowIndex + offset;
|
|
115
|
+
}
|
|
116
|
+
function isCellInSelection(rowIndex, colIndex, bounds, rowSpan = 1) {
|
|
117
|
+
if (!bounds) return false;
|
|
118
|
+
const cellEndRow = rowIndex + rowSpan - 1;
|
|
119
|
+
return cellEndRow >= bounds.startRow && rowIndex <= bounds.endRow && colIndex >= bounds.startCol && colIndex <= bounds.endCol;
|
|
120
|
+
}
|
|
121
|
+
function getActiveSelectionBounds(dragState, selectionBounds) {
|
|
122
|
+
if (dragState.isFillDragging && dragState.fillAnchor && dragState.fillEnd) {
|
|
123
|
+
return getCellSelectionBounds(dragState.fillAnchor, dragState.fillEnd);
|
|
124
|
+
}
|
|
125
|
+
return selectionBounds;
|
|
126
|
+
}
|
|
127
|
+
var SELECTION_EDGE_WIDTH_PX = 2;
|
|
128
|
+
var SELECTION_EDGE_COLOR = "var(--color-brand-primary)";
|
|
129
|
+
function getCellSelectionEdgeStyle(rowIndex, colIndex, bounds, rowSpan = 1) {
|
|
130
|
+
if (!isCellInSelection(rowIndex, colIndex, bounds, rowSpan) || !bounds) return void 0;
|
|
131
|
+
const cellEndRow = rowIndex + rowSpan - 1;
|
|
132
|
+
const isTopEdge = bounds.startRow >= rowIndex && bounds.startRow <= cellEndRow;
|
|
133
|
+
const isBottomEdge = bounds.endRow >= rowIndex && bounds.endRow <= cellEndRow;
|
|
134
|
+
const isLeftEdge = colIndex === bounds.startCol;
|
|
135
|
+
const isRightEdge = colIndex === bounds.endCol;
|
|
136
|
+
const shadows = [];
|
|
137
|
+
if (isTopEdge) {
|
|
138
|
+
shadows.push(`inset 0 ${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`);
|
|
139
|
+
}
|
|
140
|
+
if (isBottomEdge) {
|
|
141
|
+
shadows.push(`inset 0 -${SELECTION_EDGE_WIDTH_PX}px 0 0 ${SELECTION_EDGE_COLOR}`);
|
|
142
|
+
}
|
|
143
|
+
if (isLeftEdge) {
|
|
144
|
+
shadows.push(`inset ${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`);
|
|
145
|
+
}
|
|
146
|
+
if (isRightEdge) {
|
|
147
|
+
shadows.push(`inset -${SELECTION_EDGE_WIDTH_PX}px 0 0 0 ${SELECTION_EDGE_COLOR}`);
|
|
148
|
+
}
|
|
149
|
+
return shadows.length > 0 ? { boxShadow: shadows.join(", ") } : void 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/components/ui/table/features/row-expand/row-expand.ts
|
|
153
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
154
|
+
function getFieldValue(row, key) {
|
|
155
|
+
return row[key];
|
|
156
|
+
}
|
|
157
|
+
function canExpandRow(row) {
|
|
158
|
+
const children = row.children;
|
|
159
|
+
const level = row.level;
|
|
160
|
+
return Array.isArray(children) && children.length > 0 && (level === 0 || level === void 0);
|
|
161
|
+
}
|
|
162
|
+
function toggleExpandedRowId(rowId, previous) {
|
|
163
|
+
const next = new Set(previous);
|
|
164
|
+
if (next.has(rowId)) {
|
|
165
|
+
next.delete(rowId);
|
|
166
|
+
} else {
|
|
167
|
+
next.add(rowId);
|
|
168
|
+
}
|
|
169
|
+
return next;
|
|
170
|
+
}
|
|
171
|
+
var useConvertTreeData = ({
|
|
172
|
+
data,
|
|
173
|
+
enabled = true,
|
|
174
|
+
toggleField = "materialCode",
|
|
175
|
+
childField = "assemblyCode",
|
|
176
|
+
flattenField = "assemblyMaterials",
|
|
177
|
+
preventExpand = false,
|
|
178
|
+
startIndex = 1,
|
|
179
|
+
expandedRows,
|
|
180
|
+
onExpandedRowsChange
|
|
181
|
+
}) => {
|
|
182
|
+
const onExpandedRowsChangeRef = useRef(onExpandedRowsChange);
|
|
183
|
+
const hasInitializedRef = useRef(false);
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
onExpandedRowsChangeRef.current = onExpandedRowsChange;
|
|
186
|
+
}, [onExpandedRowsChange]);
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
if (!data || data.length === 0) {
|
|
189
|
+
hasInitializedRef.current = false;
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (!enabled || hasInitializedRef.current) return;
|
|
193
|
+
const ids = data.map((item) => getFieldValue(item, toggleField)).filter((value) => typeof value === "string" && value.length > 0);
|
|
194
|
+
onExpandedRowsChangeRef.current?.(new Set(ids));
|
|
195
|
+
hasInitializedRef.current = true;
|
|
196
|
+
}, [enabled, data, toggleField]);
|
|
197
|
+
const processedData = useMemo(() => {
|
|
198
|
+
if (!enabled || !data || data.length === 0) return [];
|
|
199
|
+
const flattenedData = [];
|
|
200
|
+
const flattenItems = (items) => {
|
|
201
|
+
items.forEach((item) => {
|
|
202
|
+
const newItem = { ...item };
|
|
203
|
+
const nested = newItem[flattenField];
|
|
204
|
+
if (Array.isArray(nested)) {
|
|
205
|
+
const children = nested.map(
|
|
206
|
+
(child) => typeof child === "object" && child !== null ? { ...child } : child
|
|
207
|
+
);
|
|
208
|
+
delete newItem[flattenField];
|
|
209
|
+
flattenedData.push(newItem);
|
|
210
|
+
children.forEach((child) => {
|
|
211
|
+
if (typeof child === "object" && child !== null) {
|
|
212
|
+
;
|
|
213
|
+
child[childField] = newItem[toggleField];
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
flattenItems(children);
|
|
217
|
+
} else {
|
|
218
|
+
flattenedData.push(newItem);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
};
|
|
222
|
+
flattenItems(data);
|
|
223
|
+
const dataWithLevels = flattenedData.map((item) => ({
|
|
224
|
+
...item,
|
|
225
|
+
level: 0,
|
|
226
|
+
children: [],
|
|
227
|
+
processed: false
|
|
228
|
+
}));
|
|
229
|
+
const itemMap = /* @__PURE__ */ new Map();
|
|
230
|
+
dataWithLevels.forEach((item) => {
|
|
231
|
+
const key = getFieldValue(item, toggleField);
|
|
232
|
+
if (typeof key !== "string" || !key) return;
|
|
233
|
+
if (!itemMap.has(key)) {
|
|
234
|
+
itemMap.set(key, []);
|
|
235
|
+
}
|
|
236
|
+
itemMap.get(key)?.push(item);
|
|
237
|
+
});
|
|
238
|
+
const rootItems = [];
|
|
239
|
+
dataWithLevels.forEach((item) => {
|
|
240
|
+
if (!getFieldValue(item, childField)) {
|
|
241
|
+
rootItems.push(item);
|
|
242
|
+
item.processed = true;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
dataWithLevels.forEach((item) => {
|
|
246
|
+
const parentKey = getFieldValue(item, childField);
|
|
247
|
+
if (!parentKey || item.processed) return;
|
|
248
|
+
const parentItems = dataWithLevels.filter(
|
|
249
|
+
(parent) => getFieldValue(parent, toggleField) === parentKey && !getFieldValue(parent, childField)
|
|
250
|
+
);
|
|
251
|
+
if (parentItems.length > 0) {
|
|
252
|
+
const parent = parentItems[0];
|
|
253
|
+
item.level = parent.level + 1;
|
|
254
|
+
parent.children.push(item);
|
|
255
|
+
item.processed = true;
|
|
256
|
+
} else {
|
|
257
|
+
const otherParents = itemMap.get(String(parentKey)) || [];
|
|
258
|
+
if (otherParents.length > 0) {
|
|
259
|
+
const parent = otherParents[0];
|
|
260
|
+
item.level = parent.level + 1;
|
|
261
|
+
parent.children.push(item);
|
|
262
|
+
item.processed = true;
|
|
263
|
+
} else {
|
|
264
|
+
rootItems.push(item);
|
|
265
|
+
item.processed = true;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
return rootItems;
|
|
270
|
+
}, [enabled, data, toggleField, childField, flattenField]);
|
|
271
|
+
const flattenTree = useMemo(() => {
|
|
272
|
+
if (!enabled) return [];
|
|
273
|
+
const flatten = (nodes, result = [], level = 0) => {
|
|
274
|
+
nodes.forEach((node, index) => {
|
|
275
|
+
const currentIndex = level === 0 ? `${index + startIndex}` : `${level}-${index + 1}`;
|
|
276
|
+
const toggleValue = getFieldValue(node, toggleField);
|
|
277
|
+
const uniqueId = `${index}-${String(toggleValue ?? "")}`;
|
|
278
|
+
result.push({
|
|
279
|
+
...node,
|
|
280
|
+
treeNo: currentIndex,
|
|
281
|
+
uniqueId,
|
|
282
|
+
processed: true
|
|
283
|
+
});
|
|
284
|
+
const shouldExpandChildren = node.children.length > 0 && (preventExpand || typeof toggleValue === "string" && expandedRows?.has(toggleValue));
|
|
285
|
+
if (shouldExpandChildren) {
|
|
286
|
+
flatten(node.children, result, index + startIndex);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
return result;
|
|
290
|
+
};
|
|
291
|
+
const flattenedData = flatten(processedData, [], 0);
|
|
292
|
+
flattenedData.forEach((item) => {
|
|
293
|
+
if (getFieldValue(item, childField)) {
|
|
294
|
+
const parentItem = flattenedData.find(
|
|
295
|
+
(parent) => getFieldValue(parent, toggleField) === getFieldValue(item, childField)
|
|
296
|
+
);
|
|
297
|
+
const parentAmount = parentItem ? Number(getFieldValue(parentItem, "amount") ?? 1) : 1;
|
|
298
|
+
item.parentCount = parentAmount || 1;
|
|
299
|
+
} else {
|
|
300
|
+
item.parentCount = 1;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
return flattenedData;
|
|
304
|
+
}, [enabled, processedData, startIndex, toggleField, childField, preventExpand, expandedRows]);
|
|
305
|
+
const sortedData = useMemo(() => {
|
|
306
|
+
if (!enabled) {
|
|
307
|
+
return data ?? [];
|
|
308
|
+
}
|
|
309
|
+
return [...flattenTree].sort((a, b) => {
|
|
310
|
+
const aParts = String(a.treeNo ?? "").split("-").map(Number);
|
|
311
|
+
const bParts = String(b.treeNo ?? "").split("-").map(Number);
|
|
312
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
313
|
+
const aVal = aParts[i] || 0;
|
|
314
|
+
const bVal = bParts[i] || 0;
|
|
315
|
+
if (aVal !== bVal) {
|
|
316
|
+
return aVal - bVal;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return 0;
|
|
320
|
+
});
|
|
321
|
+
}, [enabled, data, flattenTree]);
|
|
322
|
+
return sortedData;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// src/components/ui/table/components/icons.tsx
|
|
326
|
+
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
327
|
+
function ChevronDown({ className, "aria-hidden": ariaHidden = true }) {
|
|
328
|
+
return /* @__PURE__ */ jsx2(
|
|
329
|
+
"svg",
|
|
330
|
+
{
|
|
331
|
+
className,
|
|
332
|
+
"aria-hidden": ariaHidden,
|
|
333
|
+
width: "16",
|
|
334
|
+
height: "16",
|
|
335
|
+
viewBox: "0 0 24 24",
|
|
336
|
+
fill: "none",
|
|
337
|
+
stroke: "currentColor",
|
|
338
|
+
strokeWidth: "2",
|
|
339
|
+
strokeLinecap: "round",
|
|
340
|
+
strokeLinejoin: "round",
|
|
341
|
+
children: /* @__PURE__ */ jsx2("path", { d: "m6 9 6 6 6-6" })
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
function ChevronUp({ className, "aria-hidden": ariaHidden = true }) {
|
|
346
|
+
return /* @__PURE__ */ jsx2(
|
|
347
|
+
"svg",
|
|
348
|
+
{
|
|
349
|
+
className,
|
|
350
|
+
"aria-hidden": ariaHidden,
|
|
351
|
+
width: "16",
|
|
352
|
+
height: "16",
|
|
353
|
+
viewBox: "0 0 24 24",
|
|
354
|
+
fill: "none",
|
|
355
|
+
stroke: "currentColor",
|
|
356
|
+
strokeWidth: "2",
|
|
357
|
+
strokeLinecap: "round",
|
|
358
|
+
strokeLinejoin: "round",
|
|
359
|
+
children: /* @__PURE__ */ jsx2("path", { d: "m18 15-6-6-6 6" })
|
|
360
|
+
}
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
function ChevronLeft({ className, "aria-hidden": ariaHidden = true }) {
|
|
364
|
+
return /* @__PURE__ */ jsx2(
|
|
365
|
+
"svg",
|
|
366
|
+
{
|
|
367
|
+
className,
|
|
368
|
+
"aria-hidden": ariaHidden,
|
|
369
|
+
width: "16",
|
|
370
|
+
height: "16",
|
|
371
|
+
viewBox: "0 0 24 24",
|
|
372
|
+
fill: "none",
|
|
373
|
+
stroke: "currentColor",
|
|
374
|
+
strokeWidth: "2",
|
|
375
|
+
strokeLinecap: "round",
|
|
376
|
+
strokeLinejoin: "round",
|
|
377
|
+
children: /* @__PURE__ */ jsx2("path", { d: "m15 18-6-6 6-6" })
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
function ChevronRight({ className, "aria-hidden": ariaHidden = true }) {
|
|
382
|
+
return /* @__PURE__ */ jsx2(
|
|
383
|
+
"svg",
|
|
384
|
+
{
|
|
385
|
+
className,
|
|
386
|
+
"aria-hidden": ariaHidden,
|
|
387
|
+
width: "16",
|
|
388
|
+
height: "16",
|
|
389
|
+
viewBox: "0 0 24 24",
|
|
390
|
+
fill: "none",
|
|
391
|
+
stroke: "currentColor",
|
|
392
|
+
strokeWidth: "2",
|
|
393
|
+
strokeLinecap: "round",
|
|
394
|
+
strokeLinejoin: "round",
|
|
395
|
+
children: /* @__PURE__ */ jsx2("path", { d: "m9 18 6-6-6-6" })
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
function ArrowUp({ className, "aria-hidden": ariaHidden = true }) {
|
|
400
|
+
return /* @__PURE__ */ jsxs(
|
|
401
|
+
"svg",
|
|
402
|
+
{
|
|
403
|
+
className,
|
|
404
|
+
"aria-hidden": ariaHidden,
|
|
405
|
+
width: "14",
|
|
406
|
+
height: "14",
|
|
407
|
+
viewBox: "0 0 24 24",
|
|
408
|
+
fill: "none",
|
|
409
|
+
stroke: "currentColor",
|
|
410
|
+
strokeWidth: "2",
|
|
411
|
+
strokeLinecap: "round",
|
|
412
|
+
strokeLinejoin: "round",
|
|
413
|
+
children: [
|
|
414
|
+
/* @__PURE__ */ jsx2("path", { d: "m18 15-6-6-6 6" }),
|
|
415
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 21V9" })
|
|
416
|
+
]
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
function ArrowDown({ className, "aria-hidden": ariaHidden = true }) {
|
|
421
|
+
return /* @__PURE__ */ jsxs(
|
|
422
|
+
"svg",
|
|
423
|
+
{
|
|
424
|
+
className,
|
|
425
|
+
"aria-hidden": ariaHidden,
|
|
426
|
+
width: "14",
|
|
427
|
+
height: "14",
|
|
428
|
+
viewBox: "0 0 24 24",
|
|
429
|
+
fill: "none",
|
|
430
|
+
stroke: "currentColor",
|
|
431
|
+
strokeWidth: "2",
|
|
432
|
+
strokeLinecap: "round",
|
|
433
|
+
strokeLinejoin: "round",
|
|
434
|
+
children: [
|
|
435
|
+
/* @__PURE__ */ jsx2("path", { d: "m6 9 6 6 6-6" }),
|
|
436
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 3v12" })
|
|
437
|
+
]
|
|
438
|
+
}
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
function ArrowUpDown({ className, "aria-hidden": ariaHidden = true }) {
|
|
442
|
+
return /* @__PURE__ */ jsxs(
|
|
443
|
+
"svg",
|
|
444
|
+
{
|
|
445
|
+
className,
|
|
446
|
+
"aria-hidden": ariaHidden,
|
|
447
|
+
width: "14",
|
|
448
|
+
height: "14",
|
|
449
|
+
viewBox: "0 0 24 24",
|
|
450
|
+
fill: "none",
|
|
451
|
+
stroke: "currentColor",
|
|
452
|
+
strokeWidth: "2",
|
|
453
|
+
strokeLinecap: "round",
|
|
454
|
+
strokeLinejoin: "round",
|
|
455
|
+
children: [
|
|
456
|
+
/* @__PURE__ */ jsx2("path", { d: "m21 16-4 4-4-4" }),
|
|
457
|
+
/* @__PURE__ */ jsx2("path", { d: "M17 20V4" }),
|
|
458
|
+
/* @__PURE__ */ jsx2("path", { d: "m3 8 4-4 4 4" }),
|
|
459
|
+
/* @__PURE__ */ jsx2("path", { d: "M7 4v16" })
|
|
460
|
+
]
|
|
461
|
+
}
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// src/lib/cn.ts
|
|
466
|
+
function cn(...inputs) {
|
|
467
|
+
return inputs.filter(Boolean).join(" ");
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/components/ui/table/components/DataTable/DataTableRow.tsx
|
|
471
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
472
|
+
function resolveExpandCellIndex(cells, toggleField) {
|
|
473
|
+
if (!toggleField) return 0;
|
|
474
|
+
const matchedIndex = cells.findIndex(
|
|
475
|
+
(cell) => cell.column.id === toggleField
|
|
476
|
+
);
|
|
477
|
+
if (matchedIndex >= 0) return matchedIndex;
|
|
478
|
+
const noColumnIndex = cells.findIndex(
|
|
479
|
+
(cell) => cell.column.id === "no" || cell.column.id === "treeNo"
|
|
480
|
+
);
|
|
481
|
+
if (noColumnIndex >= 0 && noColumnIndex + 1 < cells.length) {
|
|
482
|
+
return noColumnIndex + 1;
|
|
483
|
+
}
|
|
484
|
+
return 0;
|
|
485
|
+
}
|
|
486
|
+
function DataTableRow({
|
|
487
|
+
row,
|
|
488
|
+
onToggleSelect,
|
|
489
|
+
virtualIndex,
|
|
490
|
+
measureElement
|
|
491
|
+
}) {
|
|
492
|
+
const { rowSpan, selection, cellSelection, cellEdit, expand } = useDataTableRowContext();
|
|
493
|
+
const {
|
|
494
|
+
enableRowSpan,
|
|
495
|
+
primaryRowSpanKey,
|
|
496
|
+
columnRowSpanMap,
|
|
497
|
+
hoveredRowIndex,
|
|
498
|
+
hoveredGroupKey,
|
|
499
|
+
selectedGroupKeys,
|
|
500
|
+
onRowHover
|
|
501
|
+
} = rowSpan;
|
|
502
|
+
const { rowSelectionMode, selectOnRowClick, onRowClick, getRowClassName } = selection;
|
|
503
|
+
const {
|
|
504
|
+
activeSelectionBounds,
|
|
505
|
+
dragState,
|
|
506
|
+
onCellMouseDown,
|
|
507
|
+
onCellMouseEnter,
|
|
508
|
+
onFillHandleMouseDown
|
|
509
|
+
} = cellSelection;
|
|
510
|
+
const {
|
|
511
|
+
editingCell,
|
|
512
|
+
draftValue,
|
|
513
|
+
onDraftValueChange,
|
|
514
|
+
onStartEdit,
|
|
515
|
+
onCommitEdit,
|
|
516
|
+
onCancelEdit
|
|
517
|
+
} = cellEdit;
|
|
518
|
+
const {
|
|
519
|
+
enableExpand,
|
|
520
|
+
toggleField,
|
|
521
|
+
expandedRows,
|
|
522
|
+
preventExpand,
|
|
523
|
+
onToggleExpand
|
|
524
|
+
} = expand;
|
|
525
|
+
const rowIndex = row.index;
|
|
526
|
+
const rowData = row.original;
|
|
527
|
+
const isRowHovered = hoveredRowIndex === rowIndex;
|
|
528
|
+
const isRowSelected = row.getIsSelected();
|
|
529
|
+
const rowGroupKey = primaryRowSpanKey !== void 0 && rowData[primaryRowSpanKey] !== null && rowData[primaryRowSpanKey] !== void 0 ? String(rowData[primaryRowSpanKey]) : null;
|
|
530
|
+
const isGroupHovered = enableRowSpan && hoveredGroupKey !== null && rowGroupKey === hoveredGroupKey;
|
|
531
|
+
const isGroupSelected = enableRowSpan && rowGroupKey !== null && selectedGroupKeys.has(rowGroupKey);
|
|
532
|
+
const visibleCells = row.getVisibleCells();
|
|
533
|
+
const expandCellIndex = enableExpand ? resolveExpandCellIndex(visibleCells, toggleField) : -1;
|
|
534
|
+
const canExpand = enableExpand && !preventExpand && canExpandRow(rowData);
|
|
535
|
+
const expandKey = toggleField && rowData[toggleField] !== null && rowData[toggleField] !== void 0 ? String(rowData[toggleField]) : null;
|
|
536
|
+
const isExpanded = expandKey !== null && Boolean(expandedRows?.has(expandKey));
|
|
537
|
+
const rowLevel = typeof rowData.level === "number" ? rowData.level : 0;
|
|
538
|
+
const editInputRef = useRef2(null);
|
|
539
|
+
const isRowEditing = editingCell?.rowIndex === rowIndex;
|
|
540
|
+
useEffect2(() => {
|
|
541
|
+
if (!isRowEditing) return;
|
|
542
|
+
editInputRef.current?.focus();
|
|
543
|
+
editInputRef.current?.select();
|
|
544
|
+
}, [isRowEditing, editingCell?.colIndex]);
|
|
545
|
+
return /* @__PURE__ */ jsx3(
|
|
546
|
+
"tr",
|
|
547
|
+
{
|
|
548
|
+
ref: measureElement,
|
|
549
|
+
"data-index": virtualIndex,
|
|
550
|
+
className: cn(
|
|
551
|
+
"DataTableRowJSX",
|
|
552
|
+
!enableRowSpan && ROW_HOVER_CLASS,
|
|
553
|
+
enableRowSpan && isRowHovered && !isRowSelected && ROW_HOVERED_BG_CLASS,
|
|
554
|
+
isRowSelected && "is-selected",
|
|
555
|
+
enableExpand && canExpand && "is-expandable",
|
|
556
|
+
getRowClassName?.(rowData, rowIndex)
|
|
557
|
+
),
|
|
558
|
+
onMouseEnter: () => onRowHover(rowIndex, rowData),
|
|
559
|
+
onClick: () => {
|
|
560
|
+
if (isRowEditing) return;
|
|
561
|
+
onRowClick?.(rowData, rowIndex);
|
|
562
|
+
if (rowSelectionMode !== "none" && selectOnRowClick) {
|
|
563
|
+
onToggleSelect();
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
children: visibleCells.map((cell, cellIndex) => {
|
|
567
|
+
const columnId = cell.column.id;
|
|
568
|
+
const meta = cell.column.columnDef.meta;
|
|
569
|
+
const align = meta?.align ?? "center";
|
|
570
|
+
const cellClassName = meta?.className;
|
|
571
|
+
const isRowSpanColumn = Boolean(enableRowSpan && meta?.rowSpan);
|
|
572
|
+
const isExpandCell = cellIndex === expandCellIndex;
|
|
573
|
+
const editable = isColumnEditable(cell.column.columnDef);
|
|
574
|
+
const editType = getColumnEditType(cell.column.columnDef);
|
|
575
|
+
let rowSpanInfo;
|
|
576
|
+
if (isRowSpanColumn) {
|
|
577
|
+
rowSpanInfo = columnRowSpanMap.get(columnId)?.[rowIndex];
|
|
578
|
+
if (rowSpanInfo && rowSpanInfo.rowSpan === 0) {
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
const isEditing = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === cellIndex;
|
|
583
|
+
const showCellHover = isRowSpanColumn ? isGroupHovered : isRowHovered;
|
|
584
|
+
const showCellSelected = isRowSpanColumn ? isGroupSelected : isRowSelected;
|
|
585
|
+
const cellRowSpan = rowSpanInfo?.rowSpan ?? 1;
|
|
586
|
+
const isCellDragSelected = isCellInSelection(
|
|
587
|
+
rowIndex,
|
|
588
|
+
cellIndex,
|
|
589
|
+
activeSelectionBounds,
|
|
590
|
+
cellRowSpan
|
|
591
|
+
);
|
|
592
|
+
const isBottomRightCell = !isEditing && activeSelectionBounds && !dragState.isSelecting && activeSelectionBounds.endRow >= rowIndex && activeSelectionBounds.endRow <= rowIndex + cellRowSpan - 1 && cellIndex === activeSelectionBounds.endCol;
|
|
593
|
+
const resolveCellRowIndex = (clientY, element) => getRowIndexInMergedCell(clientY, element, rowIndex, cellRowSpan);
|
|
594
|
+
return /* @__PURE__ */ jsxs2(
|
|
595
|
+
"td",
|
|
596
|
+
{
|
|
597
|
+
rowSpan: rowSpanInfo && rowSpanInfo.rowSpan > 1 ? rowSpanInfo.rowSpan : void 0,
|
|
598
|
+
onMouseDown: (event) => {
|
|
599
|
+
if (isEditing) {
|
|
600
|
+
event.stopPropagation();
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
event.preventDefault();
|
|
604
|
+
onCellMouseDown(
|
|
605
|
+
resolveCellRowIndex(event.clientY, event.currentTarget),
|
|
606
|
+
cellIndex
|
|
607
|
+
);
|
|
608
|
+
},
|
|
609
|
+
onMouseEnter: (event) => onCellMouseEnter(
|
|
610
|
+
resolveCellRowIndex(event.clientY, event.currentTarget),
|
|
611
|
+
cellIndex
|
|
612
|
+
),
|
|
613
|
+
onMouseMove: (event) => {
|
|
614
|
+
if (!dragState.isSelecting && !dragState.isFillDragging) return;
|
|
615
|
+
onCellMouseEnter(
|
|
616
|
+
resolveCellRowIndex(event.clientY, event.currentTarget),
|
|
617
|
+
cellIndex
|
|
618
|
+
);
|
|
619
|
+
},
|
|
620
|
+
onDoubleClick: (event) => {
|
|
621
|
+
if (!editable) return;
|
|
622
|
+
event.preventDefault();
|
|
623
|
+
event.stopPropagation();
|
|
624
|
+
onStartEdit(rowIndex, cellIndex);
|
|
625
|
+
},
|
|
626
|
+
style: getCellSelectionEdgeStyle(
|
|
627
|
+
rowIndex,
|
|
628
|
+
cellIndex,
|
|
629
|
+
activeSelectionBounds,
|
|
630
|
+
cellRowSpan
|
|
631
|
+
),
|
|
632
|
+
className: cn(
|
|
633
|
+
"data-table-cell",
|
|
634
|
+
CELL_ALIGN_CLASS[align],
|
|
635
|
+
cellClassName,
|
|
636
|
+
enableRowSpan && showCellSelected && "is-group-selected",
|
|
637
|
+
enableRowSpan && showCellHover && !showCellSelected && "is-group-hovered",
|
|
638
|
+
isCellDragSelected && CELL_SELECTION_FILL_CLASS,
|
|
639
|
+
editable && "is-editable"
|
|
640
|
+
),
|
|
641
|
+
children: [
|
|
642
|
+
isEditing ? /* @__PURE__ */ jsx3(
|
|
643
|
+
"input",
|
|
644
|
+
{
|
|
645
|
+
ref: editInputRef,
|
|
646
|
+
type: editType === "number" ? "number" : "text",
|
|
647
|
+
defaultValue: draftValue,
|
|
648
|
+
className: cn("cell-edit-input", CELL_ALIGN_CLASS[align]),
|
|
649
|
+
onChange: (event) => onDraftValueChange(event.target.value),
|
|
650
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
651
|
+
onClick: (event) => event.stopPropagation(),
|
|
652
|
+
onKeyDown: (event) => {
|
|
653
|
+
if (event.key === "Enter") {
|
|
654
|
+
event.preventDefault();
|
|
655
|
+
onCommitEdit(event.currentTarget.value);
|
|
656
|
+
}
|
|
657
|
+
if (event.key === "Escape") {
|
|
658
|
+
event.preventDefault();
|
|
659
|
+
onCancelEdit();
|
|
660
|
+
}
|
|
661
|
+
},
|
|
662
|
+
onBlur: (event) => {
|
|
663
|
+
onCommitEdit(event.currentTarget.value);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
) : isExpandCell && enableExpand ? /* @__PURE__ */ jsxs2("div", { className: "expand-cell", children: [
|
|
667
|
+
/* @__PURE__ */ jsxs2("div", { className: "expand-cell-content", children: [
|
|
668
|
+
rowLevel > 0 && /* @__PURE__ */ jsx3("span", { className: "expand-cell-indent", children: "\xB7" }),
|
|
669
|
+
/* @__PURE__ */ jsx3("div", { className: "expand-cell-value", children: flexRender(cell.column.columnDef.cell, cell.getContext()) })
|
|
670
|
+
] }),
|
|
671
|
+
canExpand && expandKey && /* @__PURE__ */ jsx3(
|
|
672
|
+
"button",
|
|
673
|
+
{
|
|
674
|
+
type: "button",
|
|
675
|
+
"aria-label": isExpanded ? "\uD589 \uC811\uAE30" : "\uD589 \uD3BC\uCE58\uAE30",
|
|
676
|
+
className: "expand-toggle-button",
|
|
677
|
+
onClick: (event) => {
|
|
678
|
+
event.stopPropagation();
|
|
679
|
+
onToggleExpand?.(expandKey);
|
|
680
|
+
},
|
|
681
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
682
|
+
children: isExpanded ? /* @__PURE__ */ jsx3(ChevronUp, { className: "expand-toggle-icon" }) : /* @__PURE__ */ jsx3(ChevronDown, { className: "expand-toggle-icon" })
|
|
683
|
+
}
|
|
684
|
+
)
|
|
685
|
+
] }) : flexRender(cell.column.columnDef.cell, cell.getContext()),
|
|
686
|
+
isBottomRightCell && /* @__PURE__ */ jsx3(
|
|
687
|
+
"div",
|
|
688
|
+
{
|
|
689
|
+
role: "presentation",
|
|
690
|
+
className: "fill-handle",
|
|
691
|
+
onMouseDown: (event) => {
|
|
692
|
+
event.stopPropagation();
|
|
693
|
+
event.preventDefault();
|
|
694
|
+
onFillHandleMouseDown(rowIndex, cellIndex);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
)
|
|
698
|
+
]
|
|
699
|
+
},
|
|
700
|
+
cell.id
|
|
701
|
+
);
|
|
702
|
+
})
|
|
703
|
+
}
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/components/ui/table/components/DataTable/DataTableToolbar.tsx
|
|
708
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
709
|
+
function DefaultSelectionLabel({ selectedCount }) {
|
|
710
|
+
if (selectedCount <= 0) return null;
|
|
711
|
+
return /* @__PURE__ */ jsxs3("span", { className: "toolbar-selection", children: [
|
|
712
|
+
"\u2713 ",
|
|
713
|
+
selectedCount,
|
|
714
|
+
"\uAC1C \uC120\uD0DD\uB428"
|
|
715
|
+
] });
|
|
716
|
+
}
|
|
717
|
+
function DataTableToolbar({
|
|
718
|
+
filteredCount,
|
|
719
|
+
totalCount,
|
|
720
|
+
summary,
|
|
721
|
+
selectedCount,
|
|
722
|
+
selectionLabel,
|
|
723
|
+
toolbar,
|
|
724
|
+
className
|
|
725
|
+
}) {
|
|
726
|
+
const displayFiltered = filteredCount ?? totalCount;
|
|
727
|
+
const hasCount = displayFiltered !== void 0 || totalCount !== void 0;
|
|
728
|
+
const hasLeftContent = hasCount || Boolean(summary);
|
|
729
|
+
const hasToolbar = Boolean(toolbar);
|
|
730
|
+
const selectionContent = selectionLabel ? selectionLabel(selectedCount) : /* @__PURE__ */ jsx4(DefaultSelectionLabel, { selectedCount });
|
|
731
|
+
const hasSelectionContent = selectionContent !== null && selectionContent !== false;
|
|
732
|
+
if (!hasLeftContent && !hasToolbar && !hasSelectionContent) return null;
|
|
733
|
+
return /* @__PURE__ */ jsxs3("div", { className: cn("DataTableToolbarJSX", className), children: [
|
|
734
|
+
/* @__PURE__ */ jsxs3("div", { className: "toolbar-left", children: [
|
|
735
|
+
hasCount && /* @__PURE__ */ jsx4("span", { className: "toolbar-count", children: displayFiltered !== void 0 && totalCount !== void 0 ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
736
|
+
/* @__PURE__ */ jsx4("span", { className: "toolbar-count-primary", children: displayFiltered }),
|
|
737
|
+
/* @__PURE__ */ jsxs3("span", { className: "toolbar-count-placeholder", children: [
|
|
738
|
+
" / ",
|
|
739
|
+
totalCount
|
|
740
|
+
] })
|
|
741
|
+
] }) : /* @__PURE__ */ jsx4("span", { className: "toolbar-count-primary", children: displayFiltered ?? totalCount }) }),
|
|
742
|
+
summary
|
|
743
|
+
] }),
|
|
744
|
+
/* @__PURE__ */ jsxs3("div", { className: "toolbar-right", children: [
|
|
745
|
+
selectionContent,
|
|
746
|
+
hasToolbar && /* @__PURE__ */ jsx4("div", { className: "toolbar-actions", children: toolbar })
|
|
747
|
+
] })
|
|
748
|
+
] });
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// src/components/ui/table/features/cell-edit/useCellEdit.ts
|
|
752
|
+
import { useCallback, useEffect as useEffect3, useRef as useRef3, useState } from "react";
|
|
753
|
+
function useCellEdit({
|
|
754
|
+
data,
|
|
755
|
+
rows,
|
|
756
|
+
onDataChange
|
|
757
|
+
}) {
|
|
758
|
+
const [editingCell, setEditingCell] = useState(null);
|
|
759
|
+
const [draftValue, setDraftValue] = useState("");
|
|
760
|
+
const draftValueRef = useRef3(draftValue);
|
|
761
|
+
const editingCellRef = useRef3(editingCell);
|
|
762
|
+
useEffect3(() => {
|
|
763
|
+
draftValueRef.current = draftValue;
|
|
764
|
+
}, [draftValue]);
|
|
765
|
+
useEffect3(() => {
|
|
766
|
+
editingCellRef.current = editingCell;
|
|
767
|
+
}, [editingCell]);
|
|
768
|
+
const cancelEdit = useCallback(() => {
|
|
769
|
+
setEditingCell(null);
|
|
770
|
+
setDraftValue("");
|
|
771
|
+
}, []);
|
|
772
|
+
const commitEdit = useCallback(
|
|
773
|
+
(raw) => {
|
|
774
|
+
const current = editingCellRef.current;
|
|
775
|
+
if (!current) return true;
|
|
776
|
+
if (!onDataChange) {
|
|
777
|
+
cancelEdit();
|
|
778
|
+
return true;
|
|
779
|
+
}
|
|
780
|
+
const value = raw ?? draftValueRef.current;
|
|
781
|
+
const next = applyCellEdit(data, rows, current.rowIndex, current.colIndex, value);
|
|
782
|
+
if (!next) return false;
|
|
783
|
+
onDataChange(next);
|
|
784
|
+
cancelEdit();
|
|
785
|
+
return true;
|
|
786
|
+
},
|
|
787
|
+
[cancelEdit, data, onDataChange, rows]
|
|
788
|
+
);
|
|
789
|
+
const startEdit = useCallback(
|
|
790
|
+
(rowIndex, colIndex) => {
|
|
791
|
+
const cell = rows[rowIndex]?.getVisibleCells()[colIndex];
|
|
792
|
+
if (!cell || !isColumnEditable(cell.column.columnDef)) return;
|
|
793
|
+
const current = editingCellRef.current;
|
|
794
|
+
if (current && (current.rowIndex !== rowIndex || current.colIndex !== colIndex) && !commitEdit()) {
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
setEditingCell({ rowIndex, colIndex });
|
|
798
|
+
setDraftValue(getCellEditDraftValue(cell.getValue()));
|
|
799
|
+
},
|
|
800
|
+
[commitEdit, rows]
|
|
801
|
+
);
|
|
802
|
+
return {
|
|
803
|
+
editingCell,
|
|
804
|
+
draftValue,
|
|
805
|
+
setDraftValue,
|
|
806
|
+
startEdit,
|
|
807
|
+
commitEdit,
|
|
808
|
+
cancelEdit
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
813
|
+
import { useCallback as useCallback2, useEffect as useEffect4, useState as useState2 } from "react";
|
|
814
|
+
|
|
815
|
+
// src/components/ui/table/features/cell-selection/fillData.ts
|
|
816
|
+
function getColumnAccessorKey2(columnDef) {
|
|
817
|
+
if ("accessorKey" in columnDef && columnDef.accessorKey) {
|
|
818
|
+
return String(columnDef.accessorKey);
|
|
819
|
+
}
|
|
820
|
+
return columnDef.id;
|
|
821
|
+
}
|
|
822
|
+
function applyFillData(data, rows, sourceBounds, fillBounds) {
|
|
823
|
+
const newData = data.map((row) => ({ ...row }));
|
|
824
|
+
const sourceHeight = sourceBounds.endRow - sourceBounds.startRow + 1;
|
|
825
|
+
const sourceWidth = sourceBounds.endCol - sourceBounds.startCol + 1;
|
|
826
|
+
for (let rowIndex = fillBounds.startRow; rowIndex <= fillBounds.endRow; rowIndex += 1) {
|
|
827
|
+
for (let colIndex = fillBounds.startCol; colIndex <= fillBounds.endCol; colIndex += 1) {
|
|
828
|
+
if (isCellInSelection(rowIndex, colIndex, sourceBounds)) continue;
|
|
829
|
+
const offsetRow = rowIndex - sourceBounds.startRow;
|
|
830
|
+
const offsetCol = colIndex - sourceBounds.startCol;
|
|
831
|
+
const sourceRowIndex = sourceBounds.startRow + (offsetRow % sourceHeight + sourceHeight) % sourceHeight;
|
|
832
|
+
const sourceColIndex = sourceBounds.startCol + (offsetCol % sourceWidth + sourceWidth) % sourceWidth;
|
|
833
|
+
const targetCell = rows[rowIndex]?.getVisibleCells()[colIndex];
|
|
834
|
+
const sourceCell = rows[sourceRowIndex]?.getVisibleCells()[sourceColIndex];
|
|
835
|
+
if (!targetCell || !sourceCell) continue;
|
|
836
|
+
const accessorKey = getColumnAccessorKey2(
|
|
837
|
+
targetCell.column.columnDef
|
|
838
|
+
);
|
|
839
|
+
if (!accessorKey) continue;
|
|
840
|
+
newData[rowIndex][accessorKey] = sourceCell.getValue();
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return newData;
|
|
844
|
+
}
|
|
845
|
+
function hasFillExtension(sourceBounds, fillBounds) {
|
|
846
|
+
if (!sourceBounds) return false;
|
|
847
|
+
return fillBounds.startRow < sourceBounds.startRow || fillBounds.endRow > sourceBounds.endRow || fillBounds.startCol < sourceBounds.startCol || fillBounds.endCol > sourceBounds.endCol;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// src/components/ui/table/features/cell-selection/useCellSelection.ts
|
|
851
|
+
function useCellSelection({
|
|
852
|
+
data,
|
|
853
|
+
rows,
|
|
854
|
+
onDataChange
|
|
855
|
+
}) {
|
|
856
|
+
const [dragState, setDragState] = useState2(INITIAL_DRAG_STATE);
|
|
857
|
+
const cellSelectionBounds = getCellSelectionBounds(dragState.start, dragState.end);
|
|
858
|
+
const activeSelectionBounds = getActiveSelectionBounds(dragState, cellSelectionBounds);
|
|
859
|
+
const handleCellMouseDown = useCallback2((rowIndex, colIndex) => {
|
|
860
|
+
setDragState({
|
|
861
|
+
isSelecting: true,
|
|
862
|
+
isFillDragging: false,
|
|
863
|
+
start: { row: rowIndex, col: colIndex },
|
|
864
|
+
end: { row: rowIndex, col: colIndex },
|
|
865
|
+
fillAnchor: null,
|
|
866
|
+
fillEnd: null
|
|
867
|
+
});
|
|
868
|
+
}, []);
|
|
869
|
+
const handleCellMouseEnter = useCallback2((rowIndex, colIndex) => {
|
|
870
|
+
setDragState((prev) => {
|
|
871
|
+
if (prev.isSelecting) {
|
|
872
|
+
return { ...prev, end: { row: rowIndex, col: colIndex } };
|
|
873
|
+
}
|
|
874
|
+
if (prev.isFillDragging) {
|
|
875
|
+
return { ...prev, fillEnd: { row: rowIndex, col: colIndex } };
|
|
876
|
+
}
|
|
877
|
+
return prev;
|
|
878
|
+
});
|
|
879
|
+
}, []);
|
|
880
|
+
const handleFillHandleMouseDown = useCallback2((rowIndex, colIndex) => {
|
|
881
|
+
setDragState((prev) => {
|
|
882
|
+
const bounds = getCellSelectionBounds(prev.start, prev.end);
|
|
883
|
+
if (!bounds) return prev;
|
|
884
|
+
return {
|
|
885
|
+
...prev,
|
|
886
|
+
isSelecting: false,
|
|
887
|
+
isFillDragging: true,
|
|
888
|
+
fillAnchor: { row: bounds.startRow, col: bounds.startCol },
|
|
889
|
+
fillEnd: { row: rowIndex, col: colIndex }
|
|
890
|
+
};
|
|
891
|
+
});
|
|
892
|
+
}, []);
|
|
893
|
+
useEffect4(() => {
|
|
894
|
+
const handleKeyDown = (e) => {
|
|
895
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "c" && activeSelectionBounds) {
|
|
896
|
+
const { startRow, endRow, startCol, endCol } = activeSelectionBounds;
|
|
897
|
+
const selectedData = rows.slice(startRow, endRow + 1).map((row) => {
|
|
898
|
+
const cells = row.getVisibleCells();
|
|
899
|
+
return cells.slice(startCol, endCol + 1).map((cell) => cell.getValue()).join(" ");
|
|
900
|
+
}).join("\n");
|
|
901
|
+
navigator.clipboard.writeText(selectedData);
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
905
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
906
|
+
}, [activeSelectionBounds, rows]);
|
|
907
|
+
useEffect4(() => {
|
|
908
|
+
const handleMouseUp = () => {
|
|
909
|
+
setDragState((prev) => {
|
|
910
|
+
if (prev.isFillDragging && prev.fillAnchor && prev.fillEnd) {
|
|
911
|
+
const sourceBounds = getCellSelectionBounds(prev.start, prev.end);
|
|
912
|
+
const newBounds = getCellSelectionBounds(prev.fillAnchor, prev.fillEnd);
|
|
913
|
+
if (newBounds) {
|
|
914
|
+
if (hasFillExtension(sourceBounds, newBounds) && sourceBounds && onDataChange) {
|
|
915
|
+
onDataChange(applyFillData(data, rows, sourceBounds, newBounds));
|
|
916
|
+
}
|
|
917
|
+
return {
|
|
918
|
+
isSelecting: false,
|
|
919
|
+
isFillDragging: false,
|
|
920
|
+
start: { row: newBounds.startRow, col: newBounds.startCol },
|
|
921
|
+
end: { row: newBounds.endRow, col: newBounds.endCol },
|
|
922
|
+
fillAnchor: null,
|
|
923
|
+
fillEnd: null
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if (prev.isSelecting) {
|
|
928
|
+
return { ...prev, isSelecting: false };
|
|
929
|
+
}
|
|
930
|
+
if (prev.isFillDragging) {
|
|
931
|
+
return { ...prev, isFillDragging: false, fillAnchor: null, fillEnd: null };
|
|
932
|
+
}
|
|
933
|
+
return prev;
|
|
934
|
+
});
|
|
935
|
+
};
|
|
936
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
937
|
+
return () => window.removeEventListener("mouseup", handleMouseUp);
|
|
938
|
+
}, [data, onDataChange, rows]);
|
|
939
|
+
return {
|
|
940
|
+
dragState,
|
|
941
|
+
activeSelectionBounds,
|
|
942
|
+
handleCellMouseDown,
|
|
943
|
+
handleCellMouseEnter,
|
|
944
|
+
handleFillHandleMouseDown
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/components/ui/table/features/row-selection/rowSelection.ts
|
|
949
|
+
function resolveRowSelection(mode, controlledSelection, internalSelection) {
|
|
950
|
+
if (mode === "none") return {};
|
|
951
|
+
return controlledSelection ?? internalSelection;
|
|
952
|
+
}
|
|
953
|
+
function normalizeSingleSelection(next) {
|
|
954
|
+
const selectedIds = Object.keys(next).filter((id) => next[id]);
|
|
955
|
+
if (selectedIds.length <= 1) return next;
|
|
956
|
+
return { [selectedIds[selectedIds.length - 1]]: true };
|
|
957
|
+
}
|
|
958
|
+
function applySelectionUpdater(mode, updater, previous) {
|
|
959
|
+
const next = typeof updater === "function" ? updater(previous) : updater;
|
|
960
|
+
return mode === "single" ? normalizeSingleSelection(next) : next;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// src/components/ui/table/features/row-span/rowSpan.ts
|
|
964
|
+
function getRowFieldValue(row, key) {
|
|
965
|
+
return row[key];
|
|
966
|
+
}
|
|
967
|
+
function computeRowSpans(data, rowSpanKey) {
|
|
968
|
+
if (data.length === 0) return [];
|
|
969
|
+
const result = [];
|
|
970
|
+
for (let index = 0; index < data.length; index++) {
|
|
971
|
+
const currentValue = getRowFieldValue(data[index], rowSpanKey);
|
|
972
|
+
const previousValue = index > 0 ? getRowFieldValue(data[index - 1], rowSpanKey) : void 0;
|
|
973
|
+
if (index > 0 && currentValue === previousValue) {
|
|
974
|
+
result.push({ rowSpan: 0, isFirstInGroup: false });
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
let span = 1;
|
|
978
|
+
for (let nextIndex = index + 1; nextIndex < data.length; nextIndex++) {
|
|
979
|
+
if (getRowFieldValue(data[nextIndex], rowSpanKey) === currentValue) {
|
|
980
|
+
span++;
|
|
981
|
+
} else {
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
result.push({ rowSpan: span, isFirstInGroup: true });
|
|
986
|
+
}
|
|
987
|
+
return result;
|
|
988
|
+
}
|
|
989
|
+
function buildColumnRowSpanMap(data, columnKeys) {
|
|
990
|
+
const map = /* @__PURE__ */ new Map();
|
|
991
|
+
for (const { columnId, rowSpanKey } of columnKeys) {
|
|
992
|
+
map.set(columnId, computeRowSpans(data, rowSpanKey));
|
|
993
|
+
}
|
|
994
|
+
return map;
|
|
995
|
+
}
|
|
996
|
+
function collectRowSpanColumns(columns) {
|
|
997
|
+
const result = [];
|
|
998
|
+
const visit = (defs) => {
|
|
999
|
+
for (const columnDef of defs) {
|
|
1000
|
+
if ("columns" in columnDef && columnDef.columns?.length) {
|
|
1001
|
+
visit(columnDef.columns);
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
const columnId = columnDef.id ?? ("accessorKey" in columnDef && columnDef.accessorKey ? String(columnDef.accessorKey) : void 0);
|
|
1005
|
+
if (!columnId || !columnDef.meta?.rowSpan) continue;
|
|
1006
|
+
result.push({
|
|
1007
|
+
columnId,
|
|
1008
|
+
rowSpanKey: columnDef.meta.rowSpanKey ?? columnId
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
visit(columns);
|
|
1013
|
+
return result;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// src/components/ui/table/components/DataTable/DataTable.tsx
|
|
1017
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1018
|
+
function DataTable({
|
|
1019
|
+
data,
|
|
1020
|
+
columns,
|
|
1021
|
+
rowSelectionMode = "none",
|
|
1022
|
+
rowSelection: controlledRowSelection,
|
|
1023
|
+
onRowSelectionChange,
|
|
1024
|
+
totalCount,
|
|
1025
|
+
filteredCount,
|
|
1026
|
+
summary,
|
|
1027
|
+
toolbar,
|
|
1028
|
+
selectionLabel,
|
|
1029
|
+
isPending = false,
|
|
1030
|
+
emptyText = "\uB370\uC774\uD130\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
1031
|
+
enableRowSpan = false,
|
|
1032
|
+
getRowId,
|
|
1033
|
+
onRowClick,
|
|
1034
|
+
getRowClassName,
|
|
1035
|
+
getRowCanSelect,
|
|
1036
|
+
selectOnRowClick = true,
|
|
1037
|
+
onDataChange,
|
|
1038
|
+
className,
|
|
1039
|
+
preserveRowSelection = false,
|
|
1040
|
+
toggleField,
|
|
1041
|
+
childField,
|
|
1042
|
+
flattenField,
|
|
1043
|
+
expandedRows: controlledExpandedRows,
|
|
1044
|
+
onExpandedRowsChange,
|
|
1045
|
+
preventExpand = false,
|
|
1046
|
+
enableVirtualization = true,
|
|
1047
|
+
estimateRowHeight = DATA_TABLE_ROW_HEIGHT,
|
|
1048
|
+
virtualOverscan = DATA_TABLE_VIRTUAL_OVERSCAN
|
|
1049
|
+
}) {
|
|
1050
|
+
const enableExpand = Boolean(toggleField);
|
|
1051
|
+
const [internalRowSelection, setInternalRowSelection] = useState3({});
|
|
1052
|
+
const [internalExpandedRows, setInternalExpandedRows] = useState3(() => /* @__PURE__ */ new Set());
|
|
1053
|
+
const [hoveredRowIndex, setHoveredRowIndex] = useState3(null);
|
|
1054
|
+
const [hoveredGroupKey, setHoveredGroupKey] = useState3(null);
|
|
1055
|
+
const scrollRef = useRef4(null);
|
|
1056
|
+
const shouldVirtualize = enableVirtualization && !enableRowSpan;
|
|
1057
|
+
useEffect5(() => {
|
|
1058
|
+
if (enableVirtualization && enableRowSpan) {
|
|
1059
|
+
console.warn(
|
|
1060
|
+
"[DataTable] enableRowSpan\uC774 \uCF1C\uC838 \uC788\uC73C\uBA74 \uC140 \uBCD1\uD569 \uC720\uC9C0\uB97C \uC704\uD574 \uAC00\uC0C1\uD654\uB97C \uBE44\uD65C\uC131\uD654\uD569\uB2C8\uB2E4."
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
}, [enableVirtualization, enableRowSpan]);
|
|
1064
|
+
const rowSelection = resolveRowSelection(
|
|
1065
|
+
rowSelectionMode,
|
|
1066
|
+
controlledRowSelection,
|
|
1067
|
+
internalRowSelection
|
|
1068
|
+
);
|
|
1069
|
+
const expandedRows = controlledExpandedRows ?? internalExpandedRows;
|
|
1070
|
+
const handleExpandedRowsChange = useCallback3(
|
|
1071
|
+
(next) => {
|
|
1072
|
+
if (onExpandedRowsChange) {
|
|
1073
|
+
onExpandedRowsChange(next);
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
setInternalExpandedRows(next);
|
|
1077
|
+
},
|
|
1078
|
+
[onExpandedRowsChange]
|
|
1079
|
+
);
|
|
1080
|
+
const tableData = useConvertTreeData({
|
|
1081
|
+
data,
|
|
1082
|
+
enabled: enableExpand,
|
|
1083
|
+
toggleField,
|
|
1084
|
+
childField,
|
|
1085
|
+
flattenField,
|
|
1086
|
+
expandedRows,
|
|
1087
|
+
onExpandedRowsChange: enableExpand ? handleExpandedRowsChange : void 0,
|
|
1088
|
+
preventExpand
|
|
1089
|
+
});
|
|
1090
|
+
const table = useReactTable({
|
|
1091
|
+
data: tableData,
|
|
1092
|
+
columns,
|
|
1093
|
+
state: {
|
|
1094
|
+
rowSelection: rowSelectionMode === "none" ? {} : rowSelection
|
|
1095
|
+
},
|
|
1096
|
+
enableRowSelection: rowSelectionMode === "none" ? false : getRowCanSelect ? (row) => getRowCanSelect(row.original, row.index) : true,
|
|
1097
|
+
enableMultiRowSelection: rowSelectionMode === "multi",
|
|
1098
|
+
onRowSelectionChange: (updater) => {
|
|
1099
|
+
if (onRowSelectionChange) {
|
|
1100
|
+
onRowSelectionChange(
|
|
1101
|
+
(previous) => applySelectionUpdater(rowSelectionMode, updater, previous)
|
|
1102
|
+
);
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
setInternalRowSelection(
|
|
1106
|
+
(previous) => applySelectionUpdater(rowSelectionMode, updater, previous)
|
|
1107
|
+
);
|
|
1108
|
+
},
|
|
1109
|
+
getCoreRowModel: getCoreRowModel(),
|
|
1110
|
+
getRowId: getRowId ? (originalRow, index) => getRowId(originalRow, index) : (_originalRow, index) => String(index)
|
|
1111
|
+
});
|
|
1112
|
+
const rowSpanColumnKeys = useMemo2(() => {
|
|
1113
|
+
if (!enableRowSpan) return [];
|
|
1114
|
+
return collectRowSpanColumns(columns);
|
|
1115
|
+
}, [enableRowSpan, columns]);
|
|
1116
|
+
const primaryRowSpanKey = rowSpanColumnKeys[0]?.rowSpanKey;
|
|
1117
|
+
const columnRowSpanMap = useMemo2(
|
|
1118
|
+
() => buildColumnRowSpanMap(tableData, rowSpanColumnKeys),
|
|
1119
|
+
[tableData, rowSpanColumnKeys]
|
|
1120
|
+
);
|
|
1121
|
+
const selectedRows = table.getSelectedRowModel().rows;
|
|
1122
|
+
const selectedCount = selectedRows.length;
|
|
1123
|
+
const rows = table.getRowModel().rows;
|
|
1124
|
+
const columnCount = table.getAllLeafColumns().length || 1;
|
|
1125
|
+
const rowVirtualizer = useVirtualizer({
|
|
1126
|
+
count: shouldVirtualize ? rows.length : 0,
|
|
1127
|
+
getScrollElement: () => scrollRef.current,
|
|
1128
|
+
estimateSize: () => estimateRowHeight,
|
|
1129
|
+
overscan: virtualOverscan
|
|
1130
|
+
});
|
|
1131
|
+
const virtualRows = rowVirtualizer.getVirtualItems();
|
|
1132
|
+
const totalSize = rowVirtualizer.getTotalSize();
|
|
1133
|
+
const paddingTop = virtualRows.length > 0 ? virtualRows[0]?.start ?? 0 : 0;
|
|
1134
|
+
const paddingBottom = virtualRows.length > 0 ? totalSize - (virtualRows[virtualRows.length - 1]?.end ?? 0) : 0;
|
|
1135
|
+
const selectedGroupKeys = useMemo2(() => {
|
|
1136
|
+
if (!enableRowSpan || !primaryRowSpanKey) return /* @__PURE__ */ new Set();
|
|
1137
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1138
|
+
for (const selectedRow of selectedRows) {
|
|
1139
|
+
const value = selectedRow.original[primaryRowSpanKey];
|
|
1140
|
+
if (value !== null && value !== void 0) keys.add(String(value));
|
|
1141
|
+
}
|
|
1142
|
+
return keys;
|
|
1143
|
+
}, [enableRowSpan, primaryRowSpanKey, selectedRows]);
|
|
1144
|
+
const {
|
|
1145
|
+
dragState,
|
|
1146
|
+
activeSelectionBounds,
|
|
1147
|
+
handleCellMouseDown,
|
|
1148
|
+
handleCellMouseEnter,
|
|
1149
|
+
handleFillHandleMouseDown
|
|
1150
|
+
} = useCellSelection({ data: tableData, rows, onDataChange });
|
|
1151
|
+
const { editingCell, draftValue, setDraftValue, startEdit, commitEdit, cancelEdit } = useCellEdit(
|
|
1152
|
+
{ data: tableData, rows, onDataChange }
|
|
1153
|
+
);
|
|
1154
|
+
const handleCellMouseDownWithCommit = useCallback3(
|
|
1155
|
+
(rowIndex, colIndex) => {
|
|
1156
|
+
const isSameEditingCell = editingCell?.rowIndex === rowIndex && editingCell?.colIndex === colIndex;
|
|
1157
|
+
if (editingCell && !isSameEditingCell && !commitEdit()) {
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
handleCellMouseDown(rowIndex, colIndex);
|
|
1161
|
+
},
|
|
1162
|
+
[commitEdit, editingCell, handleCellMouseDown]
|
|
1163
|
+
);
|
|
1164
|
+
const clearHover = () => {
|
|
1165
|
+
setHoveredRowIndex(null);
|
|
1166
|
+
setHoveredGroupKey(null);
|
|
1167
|
+
};
|
|
1168
|
+
const handleRowHover = useCallback3(
|
|
1169
|
+
(rowIndex, rowData) => {
|
|
1170
|
+
setHoveredRowIndex(rowIndex);
|
|
1171
|
+
if (!primaryRowSpanKey) {
|
|
1172
|
+
setHoveredGroupKey(null);
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
const groupValue = rowData[primaryRowSpanKey];
|
|
1176
|
+
setHoveredGroupKey(
|
|
1177
|
+
groupValue === null || groupValue === void 0 ? null : String(groupValue)
|
|
1178
|
+
);
|
|
1179
|
+
},
|
|
1180
|
+
[primaryRowSpanKey]
|
|
1181
|
+
);
|
|
1182
|
+
const handleToggleSelect = useCallback3(
|
|
1183
|
+
(row) => {
|
|
1184
|
+
if (!row.getCanSelect()) return;
|
|
1185
|
+
if (preserveRowSelection && row.getIsSelected()) {
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
row.toggleSelected();
|
|
1189
|
+
},
|
|
1190
|
+
[preserveRowSelection]
|
|
1191
|
+
);
|
|
1192
|
+
const handleToggleExpand = useCallback3(
|
|
1193
|
+
(rowKey) => {
|
|
1194
|
+
if (preventExpand) return;
|
|
1195
|
+
handleExpandedRowsChange(toggleExpandedRowId(rowKey, expandedRows));
|
|
1196
|
+
},
|
|
1197
|
+
[preventExpand, handleExpandedRowsChange, expandedRows]
|
|
1198
|
+
);
|
|
1199
|
+
const rowContextValue = useMemo2(() => {
|
|
1200
|
+
return {
|
|
1201
|
+
rowSpan: {
|
|
1202
|
+
enableRowSpan,
|
|
1203
|
+
primaryRowSpanKey,
|
|
1204
|
+
columnRowSpanMap,
|
|
1205
|
+
hoveredRowIndex,
|
|
1206
|
+
hoveredGroupKey,
|
|
1207
|
+
selectedGroupKeys,
|
|
1208
|
+
onRowHover: handleRowHover
|
|
1209
|
+
},
|
|
1210
|
+
selection: {
|
|
1211
|
+
rowSelectionMode,
|
|
1212
|
+
selectOnRowClick,
|
|
1213
|
+
onRowClick,
|
|
1214
|
+
getRowClassName
|
|
1215
|
+
},
|
|
1216
|
+
cellSelection: {
|
|
1217
|
+
activeSelectionBounds,
|
|
1218
|
+
dragState,
|
|
1219
|
+
onCellMouseDown: handleCellMouseDownWithCommit,
|
|
1220
|
+
onCellMouseEnter: handleCellMouseEnter,
|
|
1221
|
+
onFillHandleMouseDown: handleFillHandleMouseDown
|
|
1222
|
+
},
|
|
1223
|
+
cellEdit: {
|
|
1224
|
+
editingCell,
|
|
1225
|
+
draftValue,
|
|
1226
|
+
onDraftValueChange: setDraftValue,
|
|
1227
|
+
onStartEdit: startEdit,
|
|
1228
|
+
onCommitEdit: commitEdit,
|
|
1229
|
+
onCancelEdit: cancelEdit
|
|
1230
|
+
},
|
|
1231
|
+
expand: {
|
|
1232
|
+
enableExpand,
|
|
1233
|
+
toggleField,
|
|
1234
|
+
expandedRows,
|
|
1235
|
+
preventExpand,
|
|
1236
|
+
onToggleExpand: handleToggleExpand
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1239
|
+
}, [
|
|
1240
|
+
enableRowSpan,
|
|
1241
|
+
primaryRowSpanKey,
|
|
1242
|
+
columnRowSpanMap,
|
|
1243
|
+
hoveredRowIndex,
|
|
1244
|
+
hoveredGroupKey,
|
|
1245
|
+
selectedGroupKeys,
|
|
1246
|
+
handleRowHover,
|
|
1247
|
+
rowSelectionMode,
|
|
1248
|
+
selectOnRowClick,
|
|
1249
|
+
onRowClick,
|
|
1250
|
+
getRowClassName,
|
|
1251
|
+
activeSelectionBounds,
|
|
1252
|
+
dragState,
|
|
1253
|
+
handleCellMouseDownWithCommit,
|
|
1254
|
+
handleCellMouseEnter,
|
|
1255
|
+
handleFillHandleMouseDown,
|
|
1256
|
+
editingCell,
|
|
1257
|
+
draftValue,
|
|
1258
|
+
setDraftValue,
|
|
1259
|
+
startEdit,
|
|
1260
|
+
commitEdit,
|
|
1261
|
+
cancelEdit,
|
|
1262
|
+
enableExpand,
|
|
1263
|
+
toggleField,
|
|
1264
|
+
expandedRows,
|
|
1265
|
+
preventExpand,
|
|
1266
|
+
handleToggleExpand
|
|
1267
|
+
]);
|
|
1268
|
+
if (isPending) {
|
|
1269
|
+
return /* @__PURE__ */ jsx5("div", { className: cn("DataTableJSX", "DataTableJSX--pending", className), children: /* @__PURE__ */ jsx5("span", { className: "data-table-loading-text", children: "\uB85C\uB529 \uC911..." }) });
|
|
1270
|
+
}
|
|
1271
|
+
return /* @__PURE__ */ jsxs4("div", { className: cn("DataTableJSX", className), children: [
|
|
1272
|
+
/* @__PURE__ */ jsx5(
|
|
1273
|
+
DataTableToolbar,
|
|
1274
|
+
{
|
|
1275
|
+
filteredCount: filteredCount ?? tableData.length,
|
|
1276
|
+
totalCount,
|
|
1277
|
+
summary,
|
|
1278
|
+
selectedCount,
|
|
1279
|
+
selectionLabel,
|
|
1280
|
+
toolbar
|
|
1281
|
+
}
|
|
1282
|
+
),
|
|
1283
|
+
/* @__PURE__ */ jsx5("div", { ref: scrollRef, className: "data-table-scroll", children: /* @__PURE__ */ jsxs4(
|
|
1284
|
+
"table",
|
|
1285
|
+
{
|
|
1286
|
+
className: "data-table",
|
|
1287
|
+
onDragStart: (event) => event.preventDefault(),
|
|
1288
|
+
children: [
|
|
1289
|
+
/* @__PURE__ */ jsx5("thead", { className: "data-table-head", children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx5("tr", { className: "data-table-head-row", children: headerGroup.headers.map((header) => {
|
|
1290
|
+
const align = header.column.columnDef.meta?.align ?? "center";
|
|
1291
|
+
const headerClassName = header.column.columnDef.meta?.headerClassName;
|
|
1292
|
+
return /* @__PURE__ */ jsx5(
|
|
1293
|
+
"th",
|
|
1294
|
+
{
|
|
1295
|
+
style: { width: header.getSize() !== 150 ? header.getSize() : void 0 },
|
|
1296
|
+
className: cn(
|
|
1297
|
+
"data-table-head-cell",
|
|
1298
|
+
CELL_ALIGN_CLASS[align],
|
|
1299
|
+
headerClassName
|
|
1300
|
+
),
|
|
1301
|
+
children: header.isPlaceholder ? null : flexRender2(header.column.columnDef.header, header.getContext())
|
|
1302
|
+
},
|
|
1303
|
+
header.id
|
|
1304
|
+
);
|
|
1305
|
+
}) }, headerGroup.id)) }),
|
|
1306
|
+
/* @__PURE__ */ jsx5(DataTableContextProvider, { value: rowContextValue, children: /* @__PURE__ */ jsx5("tbody", { onMouseLeave: clearHover, className: "data-table-body", children: rows.length === 0 ? /* @__PURE__ */ jsx5("tr", { children: /* @__PURE__ */ jsx5("td", { colSpan: columnCount, className: "data-table-empty-cell", children: emptyText }) }) : shouldVirtualize ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1307
|
+
paddingTop > 0 && /* @__PURE__ */ jsx5("tr", { "aria-hidden": true, className: "data-table-virtual-spacer", children: /* @__PURE__ */ jsx5(
|
|
1308
|
+
"td",
|
|
1309
|
+
{
|
|
1310
|
+
colSpan: columnCount,
|
|
1311
|
+
style: { height: paddingTop },
|
|
1312
|
+
className: "data-table-virtual-spacer-cell"
|
|
1313
|
+
}
|
|
1314
|
+
) }),
|
|
1315
|
+
virtualRows.map((virtualRow) => {
|
|
1316
|
+
const row = rows[virtualRow.index];
|
|
1317
|
+
if (!row) return null;
|
|
1318
|
+
return /* @__PURE__ */ jsx5(
|
|
1319
|
+
DataTableRow,
|
|
1320
|
+
{
|
|
1321
|
+
row,
|
|
1322
|
+
virtualIndex: virtualRow.index,
|
|
1323
|
+
measureElement: rowVirtualizer.measureElement,
|
|
1324
|
+
onToggleSelect: () => handleToggleSelect(row)
|
|
1325
|
+
},
|
|
1326
|
+
row.id
|
|
1327
|
+
);
|
|
1328
|
+
}),
|
|
1329
|
+
paddingBottom > 0 && /* @__PURE__ */ jsx5("tr", { "aria-hidden": true, className: "data-table-virtual-spacer", children: /* @__PURE__ */ jsx5(
|
|
1330
|
+
"td",
|
|
1331
|
+
{
|
|
1332
|
+
colSpan: columnCount,
|
|
1333
|
+
style: { height: paddingBottom },
|
|
1334
|
+
className: "data-table-virtual-spacer-cell"
|
|
1335
|
+
}
|
|
1336
|
+
) })
|
|
1337
|
+
] }) : rows.map((row) => /* @__PURE__ */ jsx5(
|
|
1338
|
+
DataTableRow,
|
|
1339
|
+
{
|
|
1340
|
+
row,
|
|
1341
|
+
onToggleSelect: () => handleToggleSelect(row)
|
|
1342
|
+
},
|
|
1343
|
+
row.id
|
|
1344
|
+
)) }) })
|
|
1345
|
+
]
|
|
1346
|
+
}
|
|
1347
|
+
) })
|
|
1348
|
+
] });
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// src/components/ui/table/components/Table/Table.tsx
|
|
1352
|
+
import { useCallback as useCallback4, useMemo as useMemo3, useState as useState4 } from "react";
|
|
1353
|
+
|
|
1354
|
+
// src/components/ui/table/components/Table/buildColumnDef.tsx
|
|
1355
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1356
|
+
function SortableHeader({
|
|
1357
|
+
label,
|
|
1358
|
+
field,
|
|
1359
|
+
sort,
|
|
1360
|
+
onSort
|
|
1361
|
+
}) {
|
|
1362
|
+
const isActive = sort?.field === field;
|
|
1363
|
+
const Icon = isActive ? sort.direction === "asc" ? ArrowUp : ArrowDown : ArrowUpDown;
|
|
1364
|
+
return /* @__PURE__ */ jsxs5(
|
|
1365
|
+
"button",
|
|
1366
|
+
{
|
|
1367
|
+
type: "button",
|
|
1368
|
+
className: cn("SortableHeaderJSX", isActive ? "is-active" : "is-inactive"),
|
|
1369
|
+
onClick: () => onSort(field),
|
|
1370
|
+
children: [
|
|
1371
|
+
/* @__PURE__ */ jsx6("span", { children: label }),
|
|
1372
|
+
/* @__PURE__ */ jsx6(Icon, { className: "sortable-header-icon" })
|
|
1373
|
+
]
|
|
1374
|
+
}
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
function buildColumnDef(props, sort, onSort) {
|
|
1378
|
+
const {
|
|
1379
|
+
field,
|
|
1380
|
+
virtual = false,
|
|
1381
|
+
children,
|
|
1382
|
+
sortable = false,
|
|
1383
|
+
width,
|
|
1384
|
+
align,
|
|
1385
|
+
rowSpan,
|
|
1386
|
+
rowSpanKey,
|
|
1387
|
+
editable,
|
|
1388
|
+
editType,
|
|
1389
|
+
className,
|
|
1390
|
+
headerClassName,
|
|
1391
|
+
render
|
|
1392
|
+
} = props;
|
|
1393
|
+
return {
|
|
1394
|
+
id: field,
|
|
1395
|
+
...!virtual ? { accessorKey: field } : {},
|
|
1396
|
+
size: width ?? 150,
|
|
1397
|
+
header: sortable ? () => /* @__PURE__ */ jsx6(SortableHeader, { label: children, field, sort, onSort }) : (
|
|
1398
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
1399
|
+
() => children
|
|
1400
|
+
),
|
|
1401
|
+
...render ? {
|
|
1402
|
+
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
1403
|
+
cell: ({ row, getValue }) => render(
|
|
1404
|
+
getValue(),
|
|
1405
|
+
row,
|
|
1406
|
+
row.index
|
|
1407
|
+
)
|
|
1408
|
+
} : {},
|
|
1409
|
+
meta: {
|
|
1410
|
+
align,
|
|
1411
|
+
rowSpan,
|
|
1412
|
+
rowSpanKey,
|
|
1413
|
+
editable,
|
|
1414
|
+
editType,
|
|
1415
|
+
className,
|
|
1416
|
+
headerClassName
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// src/components/ui/table/components/Table/parseTableChildren.ts
|
|
1422
|
+
import { Children } from "react";
|
|
1423
|
+
|
|
1424
|
+
// src/components/ui/table/components/Table/tableChildTypes.ts
|
|
1425
|
+
import { isValidElement } from "react";
|
|
1426
|
+
var TABLE_HEADER_DISPLAY_NAME = "Table.Header";
|
|
1427
|
+
var TABLE_BODY_DISPLAY_NAME = "Table.Body";
|
|
1428
|
+
var TABLE_COLUMN_DISPLAY_NAME = "Table.Column";
|
|
1429
|
+
var TABLE_PAGINATION_DISPLAY_NAME = "Table.Pagination";
|
|
1430
|
+
function getComponentDisplayName(type) {
|
|
1431
|
+
if (typeof type === "function" || typeof type === "object" && type !== null) {
|
|
1432
|
+
return type.displayName;
|
|
1433
|
+
}
|
|
1434
|
+
return void 0;
|
|
1435
|
+
}
|
|
1436
|
+
function isTableHeaderElement(child) {
|
|
1437
|
+
return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_HEADER_DISPLAY_NAME;
|
|
1438
|
+
}
|
|
1439
|
+
function isTableBodyElement(child) {
|
|
1440
|
+
return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_BODY_DISPLAY_NAME;
|
|
1441
|
+
}
|
|
1442
|
+
function isTableColumnElement(child) {
|
|
1443
|
+
return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_COLUMN_DISPLAY_NAME;
|
|
1444
|
+
}
|
|
1445
|
+
function isTablePaginationElement(child) {
|
|
1446
|
+
return isValidElement(child) && getComponentDisplayName(child.type) === TABLE_PAGINATION_DISPLAY_NAME;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
// src/components/ui/table/components/Table/parseTableChildren.ts
|
|
1450
|
+
function parseTableChildren(children) {
|
|
1451
|
+
const slots = {
|
|
1452
|
+
header: null,
|
|
1453
|
+
body: null,
|
|
1454
|
+
pagination: null
|
|
1455
|
+
};
|
|
1456
|
+
for (const child of Children.toArray(children)) {
|
|
1457
|
+
if (isTableHeaderElement(child)) {
|
|
1458
|
+
slots.header = child;
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
if (isTableBodyElement(child)) {
|
|
1462
|
+
slots.body = child;
|
|
1463
|
+
continue;
|
|
1464
|
+
}
|
|
1465
|
+
if (isTablePaginationElement(child)) {
|
|
1466
|
+
slots.pagination = child;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return slots;
|
|
1470
|
+
}
|
|
1471
|
+
function extractColumnElements(header) {
|
|
1472
|
+
if (!header) return [];
|
|
1473
|
+
const { children } = header.props;
|
|
1474
|
+
return Children.toArray(children).filter(isTableColumnElement);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/components/ui/table/components/Table/TableBody.tsx
|
|
1478
|
+
function TableBody() {
|
|
1479
|
+
return null;
|
|
1480
|
+
}
|
|
1481
|
+
TableBody.displayName = TABLE_BODY_DISPLAY_NAME;
|
|
1482
|
+
|
|
1483
|
+
// src/components/ui/table/components/Table/TableColumn.tsx
|
|
1484
|
+
function TableColumn(props) {
|
|
1485
|
+
void props;
|
|
1486
|
+
return null;
|
|
1487
|
+
}
|
|
1488
|
+
TableColumn.displayName = TABLE_COLUMN_DISPLAY_NAME;
|
|
1489
|
+
|
|
1490
|
+
// src/components/ui/table/components/Table/tableDataPipeline.ts
|
|
1491
|
+
function sortTableData(data, sort) {
|
|
1492
|
+
if (!sort) return data;
|
|
1493
|
+
const { field, direction } = sort;
|
|
1494
|
+
const multiplier = direction === "asc" ? 1 : -1;
|
|
1495
|
+
return [...data].sort((left, right) => {
|
|
1496
|
+
const leftValue = left[field];
|
|
1497
|
+
const rightValue = right[field];
|
|
1498
|
+
if ((leftValue === null || leftValue === void 0) && (rightValue === null || rightValue === void 0)) {
|
|
1499
|
+
return 0;
|
|
1500
|
+
}
|
|
1501
|
+
if (leftValue === null || leftValue === void 0) return 1;
|
|
1502
|
+
if (rightValue === null || rightValue === void 0) return -1;
|
|
1503
|
+
if (typeof leftValue === "number" && typeof rightValue === "number") {
|
|
1504
|
+
return (leftValue - rightValue) * multiplier;
|
|
1505
|
+
}
|
|
1506
|
+
return String(leftValue).localeCompare(String(rightValue), "ko") * multiplier;
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
function paginateTableData(data, page, pageSize) {
|
|
1510
|
+
const safePage = Math.max(1, page);
|
|
1511
|
+
const start = (safePage - 1) * pageSize;
|
|
1512
|
+
return data.slice(start, start + pageSize);
|
|
1513
|
+
}
|
|
1514
|
+
function getTotalPages(totalCount, pageSize) {
|
|
1515
|
+
if (pageSize <= 0) return 1;
|
|
1516
|
+
return Math.max(1, Math.ceil(totalCount / pageSize));
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
// src/components/ui/table/components/Table/TableHeader.tsx
|
|
1520
|
+
function TableHeader(props) {
|
|
1521
|
+
void props;
|
|
1522
|
+
return null;
|
|
1523
|
+
}
|
|
1524
|
+
TableHeader.displayName = TABLE_HEADER_DISPLAY_NAME;
|
|
1525
|
+
|
|
1526
|
+
// src/components/ui/table/components/Table/TablePagination.tsx
|
|
1527
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1528
|
+
function TablePagination({
|
|
1529
|
+
page,
|
|
1530
|
+
pageSize = 10,
|
|
1531
|
+
totalCount = 0,
|
|
1532
|
+
onChange,
|
|
1533
|
+
className
|
|
1534
|
+
}) {
|
|
1535
|
+
const totalPages = getTotalPages(totalCount, pageSize);
|
|
1536
|
+
const safePage = Math.min(Math.max(1, page), totalPages);
|
|
1537
|
+
const canGoPrev = safePage > 1;
|
|
1538
|
+
const canGoNext = safePage < totalPages;
|
|
1539
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn("TablePaginationJSX", className), children: [
|
|
1540
|
+
/* @__PURE__ */ jsx7(
|
|
1541
|
+
"button",
|
|
1542
|
+
{
|
|
1543
|
+
type: "button",
|
|
1544
|
+
className: "pagination-button",
|
|
1545
|
+
disabled: !canGoPrev,
|
|
1546
|
+
onClick: () => onChange(safePage - 1),
|
|
1547
|
+
"aria-label": "\uC774\uC804 \uD398\uC774\uC9C0",
|
|
1548
|
+
children: /* @__PURE__ */ jsx7(ChevronLeft, { className: "pagination-button-icon" })
|
|
1549
|
+
}
|
|
1550
|
+
),
|
|
1551
|
+
/* @__PURE__ */ jsxs6("span", { className: "pagination-label", children: [
|
|
1552
|
+
safePage,
|
|
1553
|
+
" / ",
|
|
1554
|
+
totalPages
|
|
1555
|
+
] }),
|
|
1556
|
+
/* @__PURE__ */ jsx7(
|
|
1557
|
+
"button",
|
|
1558
|
+
{
|
|
1559
|
+
type: "button",
|
|
1560
|
+
className: "pagination-button",
|
|
1561
|
+
disabled: !canGoNext,
|
|
1562
|
+
onClick: () => onChange(safePage + 1),
|
|
1563
|
+
"aria-label": "\uB2E4\uC74C \uD398\uC774\uC9C0",
|
|
1564
|
+
children: /* @__PURE__ */ jsx7(ChevronRight, { className: "pagination-button-icon" })
|
|
1565
|
+
}
|
|
1566
|
+
)
|
|
1567
|
+
] });
|
|
1568
|
+
}
|
|
1569
|
+
TablePagination.displayName = TABLE_PAGINATION_DISPLAY_NAME;
|
|
1570
|
+
|
|
1571
|
+
// src/components/ui/table/components/Table/Table.tsx
|
|
1572
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1573
|
+
function TableRoot({
|
|
1574
|
+
data,
|
|
1575
|
+
children,
|
|
1576
|
+
className,
|
|
1577
|
+
totalCount,
|
|
1578
|
+
filteredCount,
|
|
1579
|
+
...dataTableProps
|
|
1580
|
+
}) {
|
|
1581
|
+
const { header, pagination: paginationElement } = useMemo3(
|
|
1582
|
+
() => parseTableChildren(children),
|
|
1583
|
+
[children]
|
|
1584
|
+
);
|
|
1585
|
+
const [sort, setSort] = useState4(null);
|
|
1586
|
+
const handleSort = useCallback4((field) => {
|
|
1587
|
+
setSort((previous) => {
|
|
1588
|
+
if (previous?.field !== field) {
|
|
1589
|
+
return { field, direction: "asc" };
|
|
1590
|
+
}
|
|
1591
|
+
if (previous.direction === "asc") {
|
|
1592
|
+
return { field, direction: "desc" };
|
|
1593
|
+
}
|
|
1594
|
+
return null;
|
|
1595
|
+
});
|
|
1596
|
+
}, []);
|
|
1597
|
+
const columns = useMemo3(() => {
|
|
1598
|
+
return extractColumnElements(header).map(
|
|
1599
|
+
(columnElement) => buildColumnDef(columnElement.props, sort, handleSort)
|
|
1600
|
+
);
|
|
1601
|
+
}, [header, sort, handleSort]);
|
|
1602
|
+
const paginationProps = paginationElement?.props;
|
|
1603
|
+
const pageSize = paginationProps?.pageSize ?? 10;
|
|
1604
|
+
const page = paginationProps?.page ?? 1;
|
|
1605
|
+
const resolvedTotalCount = paginationProps?.totalCount ?? totalCount ?? data.length;
|
|
1606
|
+
const tableData = useMemo3(() => {
|
|
1607
|
+
const sortedData = sortTableData(data, sort);
|
|
1608
|
+
if (!paginationProps) return sortedData;
|
|
1609
|
+
return paginateTableData(sortedData, page, pageSize);
|
|
1610
|
+
}, [data, sort, paginationProps, page, pageSize]);
|
|
1611
|
+
if (columns.length === 0) {
|
|
1612
|
+
console.warn("[Table] Table.Header \uC548\uC5D0 Table.Column\uC744 \uD558\uB098 \uC774\uC0C1 \uC120\uC5B8\uD574 \uC8FC\uC138\uC694.");
|
|
1613
|
+
}
|
|
1614
|
+
return /* @__PURE__ */ jsxs7("div", { className: "TableJSX", children: [
|
|
1615
|
+
/* @__PURE__ */ jsx8(
|
|
1616
|
+
DataTable,
|
|
1617
|
+
{
|
|
1618
|
+
...dataTableProps,
|
|
1619
|
+
data: tableData,
|
|
1620
|
+
columns,
|
|
1621
|
+
totalCount: paginationProps ? resolvedTotalCount : totalCount,
|
|
1622
|
+
filteredCount: filteredCount ?? data.length,
|
|
1623
|
+
className: cn(className, paginationProps && "DataTableJSX--with-pagination")
|
|
1624
|
+
}
|
|
1625
|
+
),
|
|
1626
|
+
paginationProps && /* @__PURE__ */ jsx8(
|
|
1627
|
+
TablePagination,
|
|
1628
|
+
{
|
|
1629
|
+
page,
|
|
1630
|
+
pageSize,
|
|
1631
|
+
totalCount: resolvedTotalCount,
|
|
1632
|
+
onChange: paginationProps.onChange,
|
|
1633
|
+
className: paginationProps.className
|
|
1634
|
+
}
|
|
1635
|
+
)
|
|
1636
|
+
] });
|
|
1637
|
+
}
|
|
1638
|
+
function createTable() {
|
|
1639
|
+
function Column(props) {
|
|
1640
|
+
void props;
|
|
1641
|
+
return null;
|
|
1642
|
+
}
|
|
1643
|
+
Column.displayName = TABLE_COLUMN_DISPLAY_NAME;
|
|
1644
|
+
return Object.assign(
|
|
1645
|
+
function BoundTable(props) {
|
|
1646
|
+
return /* @__PURE__ */ jsx8(TableRoot, { ...props });
|
|
1647
|
+
},
|
|
1648
|
+
{
|
|
1649
|
+
Header: TableHeader,
|
|
1650
|
+
Column,
|
|
1651
|
+
Body: TableBody,
|
|
1652
|
+
Pagination: TablePagination
|
|
1653
|
+
}
|
|
1654
|
+
);
|
|
1655
|
+
}
|
|
1656
|
+
var Table = Object.assign(TableRoot, {
|
|
1657
|
+
Header: TableHeader,
|
|
1658
|
+
Column: TableColumn,
|
|
1659
|
+
Body: TableBody,
|
|
1660
|
+
Pagination: TablePagination
|
|
1661
|
+
});
|
|
1662
|
+
export {
|
|
1663
|
+
DataTable,
|
|
1664
|
+
Table,
|
|
1665
|
+
createTable
|
|
1666
|
+
};
|