@skygraph/react 0.0.0-placeholder.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/dist/Table-BqN2u5ma.d.cts +1015 -0
  3. package/dist/Table-IvijKCyd.d.ts +1015 -0
  4. package/dist/chunk-2OCEO636.js +91 -0
  5. package/dist/chunk-2OCEO636.js.map +1 -0
  6. package/dist/chunk-6SHHUUAE.js +1667 -0
  7. package/dist/chunk-6SHHUUAE.js.map +1 -0
  8. package/dist/chunk-BNMJSYI2.js +54 -0
  9. package/dist/chunk-BNMJSYI2.js.map +1 -0
  10. package/dist/chunk-DRBAZI46.js +57 -0
  11. package/dist/chunk-DRBAZI46.js.map +1 -0
  12. package/dist/chunk-GJDDPZH7.js +226 -0
  13. package/dist/chunk-GJDDPZH7.js.map +1 -0
  14. package/dist/chunk-KCFWFDSP.js +847 -0
  15. package/dist/chunk-KCFWFDSP.js.map +1 -0
  16. package/dist/chunk-MJAEAFPN.js +3791 -0
  17. package/dist/chunk-MJAEAFPN.js.map +1 -0
  18. package/dist/chunk-MLEBVELO.js +1412 -0
  19. package/dist/chunk-MLEBVELO.js.map +1 -0
  20. package/dist/chunk-SEQI65CF.js +61 -0
  21. package/dist/chunk-SEQI65CF.js.map +1 -0
  22. package/dist/chunk-ZJF6SJLP.js +200 -0
  23. package/dist/chunk-ZJF6SJLP.js.map +1 -0
  24. package/dist/common-CdpocIEz.d.cts +27 -0
  25. package/dist/common-CdpocIEz.d.ts +27 -0
  26. package/dist/datagrid-B6hg5yJh.d.cts +200 -0
  27. package/dist/datagrid-B6hg5yJh.d.ts +200 -0
  28. package/dist/datagrid.cjs +1052 -0
  29. package/dist/datagrid.cjs.map +1 -0
  30. package/dist/datagrid.d.cts +2 -0
  31. package/dist/datagrid.d.ts +2 -0
  32. package/dist/datagrid.js +10 -0
  33. package/dist/datagrid.js.map +1 -0
  34. package/dist/devtools.cjs +253 -0
  35. package/dist/devtools.cjs.map +1 -0
  36. package/dist/devtools.d.cts +29 -0
  37. package/dist/devtools.d.ts +29 -0
  38. package/dist/devtools.js +9 -0
  39. package/dist/devtools.js.map +1 -0
  40. package/dist/form.cjs +1723 -0
  41. package/dist/form.cjs.map +1 -0
  42. package/dist/form.d.cts +530 -0
  43. package/dist/form.d.ts +530 -0
  44. package/dist/form.js +49 -0
  45. package/dist/form.js.map +1 -0
  46. package/dist/index.cjs +22419 -0
  47. package/dist/index.cjs.map +1 -0
  48. package/dist/index.d.cts +3671 -0
  49. package/dist/index.d.ts +3671 -0
  50. package/dist/index.js +14087 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/table.cjs +4130 -0
  53. package/dist/table.cjs.map +1 -0
  54. package/dist/table.d.cts +31 -0
  55. package/dist/table.d.ts +31 -0
  56. package/dist/table.js +26 -0
  57. package/dist/table.js.map +1 -0
  58. package/dist/tree.cjs +1821 -0
  59. package/dist/tree.cjs.map +1 -0
  60. package/dist/tree.d.cts +348 -0
  61. package/dist/tree.d.ts +348 -0
  62. package/dist/tree.js +13 -0
  63. package/dist/tree.js.map +1 -0
  64. package/dist/virtual.cjs +145 -0
  65. package/dist/virtual.cjs.map +1 -0
  66. package/dist/virtual.d.cts +50 -0
  67. package/dist/virtual.d.ts +50 -0
  68. package/dist/virtual.js +11 -0
  69. package/dist/virtual.js.map +1 -0
  70. package/package.json +108 -18
  71. package/README.md +0 -12
  72. package/index.js +0 -3
@@ -0,0 +1,3791 @@
1
+ import {
2
+ Button,
3
+ Input,
4
+ Transition
5
+ } from "./chunk-ZJF6SJLP.js";
6
+ import {
7
+ Checkbox
8
+ } from "./chunk-DRBAZI46.js";
9
+ import {
10
+ Spin,
11
+ useConfig
12
+ } from "./chunk-2OCEO636.js";
13
+
14
+ // src/components/ui/Pagination.tsx
15
+ import { useState } from "react";
16
+ import { jsx, jsxs } from "react/jsx-runtime";
17
+ function Pagination({
18
+ current,
19
+ total,
20
+ pageSize = 10,
21
+ onChange,
22
+ onPageSizeChange,
23
+ showTotal,
24
+ disabled: disabledProp,
25
+ simple,
26
+ showSizeChanger,
27
+ pageSizeOptions = [10, 20, 50, 100],
28
+ showQuickJumper,
29
+ className,
30
+ style,
31
+ unstyled
32
+ }) {
33
+ const config = useConfig();
34
+ const disabled = disabledProp ?? config.disabled ?? false;
35
+ const locale = config.locale?.pagination;
36
+ const totalPrefix = locale?.totalPrefix ?? "Total";
37
+ const itemsPerPageLabel = locale?.itemsPerPage ?? "/ page";
38
+ const jumpLabel = locale?.jump ?? "Go to";
39
+ const ariaLabel = locale?.ariaLabel ?? "Pagination";
40
+ const [jumpValue, setJumpValue] = useState("");
41
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
42
+ const safeCurrentPage = Math.min(Math.max(1, current), totalPages);
43
+ const rangeStart = (safeCurrentPage - 1) * pageSize + 1;
44
+ const rangeEnd = Math.min(safeCurrentPage * pageSize, total);
45
+ const go = (page) => {
46
+ if (disabled || page < 1 || page > totalPages || page === safeCurrentPage) return;
47
+ onChange?.(page);
48
+ };
49
+ const handleJump = () => {
50
+ const num = parseInt(jumpValue, 10);
51
+ if (!isNaN(num)) {
52
+ go(num);
53
+ }
54
+ setJumpValue("");
55
+ };
56
+ const pages = buildPages(safeCurrentPage, totalPages);
57
+ const renderTotal = () => {
58
+ if (!showTotal) return null;
59
+ if (typeof showTotal === "function") {
60
+ return /* @__PURE__ */ jsx("span", { className: "sg-pagination-total", children: showTotal(total, [rangeStart, rangeEnd]) });
61
+ }
62
+ return /* @__PURE__ */ jsxs("span", { className: "sg-pagination-total", children: [
63
+ totalPrefix,
64
+ " ",
65
+ total
66
+ ] });
67
+ };
68
+ const renderSizeChanger = () => {
69
+ if (!showSizeChanger) return null;
70
+ return /* @__PURE__ */ jsx(
71
+ "select",
72
+ {
73
+ className: "sg-pagination-size-changer",
74
+ value: pageSize,
75
+ onChange: (e) => onPageSizeChange?.(Number(e.target.value)),
76
+ disabled,
77
+ children: pageSizeOptions.map((opt) => /* @__PURE__ */ jsxs("option", { value: opt, children: [
78
+ opt,
79
+ " ",
80
+ itemsPerPageLabel
81
+ ] }, opt))
82
+ }
83
+ );
84
+ };
85
+ const renderQuickJumper = () => {
86
+ if (!showQuickJumper) return null;
87
+ return /* @__PURE__ */ jsxs("span", { className: "sg-pagination-quick-jumper", children: [
88
+ jumpLabel,
89
+ /* @__PURE__ */ jsx(
90
+ "input",
91
+ {
92
+ type: "text",
93
+ value: jumpValue,
94
+ onChange: (e) => setJumpValue(e.target.value),
95
+ onKeyDown: (e) => {
96
+ if (e.key === "Enter") handleJump();
97
+ },
98
+ onBlur: handleJump,
99
+ disabled,
100
+ className: "sg-pagination-jumper-input"
101
+ }
102
+ )
103
+ ] });
104
+ };
105
+ if (unstyled) {
106
+ return /* @__PURE__ */ jsxs("nav", { className, style, "aria-label": ariaLabel, children: [
107
+ /* @__PURE__ */ jsx("button", { disabled: safeCurrentPage <= 1, onClick: () => go(safeCurrentPage - 1), children: "\u2039" }),
108
+ pages.map(
109
+ (p, i) => p === "..." ? /* @__PURE__ */ jsx("span", { children: "\u2026" }, `e${i}`) : /* @__PURE__ */ jsx(
110
+ "button",
111
+ {
112
+ onClick: () => go(p),
113
+ "aria-current": p === safeCurrentPage ? "page" : void 0,
114
+ children: p
115
+ },
116
+ p
117
+ )
118
+ ),
119
+ /* @__PURE__ */ jsx("button", { disabled: safeCurrentPage >= totalPages, onClick: () => go(safeCurrentPage + 1), children: "\u203A" })
120
+ ] });
121
+ }
122
+ if (simple) {
123
+ return /* @__PURE__ */ jsxs(
124
+ "nav",
125
+ {
126
+ className: [
127
+ "sg-pagination sg-pagination-simple",
128
+ disabled ? "sg-pagination-disabled" : "",
129
+ className
130
+ ].filter(Boolean).join(" "),
131
+ style,
132
+ "aria-label": ariaLabel,
133
+ children: [
134
+ /* @__PURE__ */ jsx(
135
+ "button",
136
+ {
137
+ className: "sg-pagination-item sg-pagination-prev",
138
+ disabled: safeCurrentPage <= 1,
139
+ onClick: () => go(safeCurrentPage - 1),
140
+ children: "\u2039"
141
+ }
142
+ ),
143
+ /* @__PURE__ */ jsxs("span", { className: "sg-pagination-simple-pager", children: [
144
+ safeCurrentPage,
145
+ " / ",
146
+ totalPages
147
+ ] }),
148
+ /* @__PURE__ */ jsx(
149
+ "button",
150
+ {
151
+ className: "sg-pagination-item sg-pagination-next",
152
+ disabled: safeCurrentPage >= totalPages,
153
+ onClick: () => go(safeCurrentPage + 1),
154
+ children: "\u203A"
155
+ }
156
+ )
157
+ ]
158
+ }
159
+ );
160
+ }
161
+ return /* @__PURE__ */ jsxs(
162
+ "nav",
163
+ {
164
+ className: ["sg-pagination", disabled ? "sg-pagination-disabled" : "", className].filter(Boolean).join(" "),
165
+ style,
166
+ "aria-label": ariaLabel,
167
+ children: [
168
+ renderTotal(),
169
+ /* @__PURE__ */ jsx(
170
+ "button",
171
+ {
172
+ className: "sg-pagination-item sg-pagination-prev",
173
+ disabled: safeCurrentPage <= 1,
174
+ onClick: () => go(safeCurrentPage - 1),
175
+ children: "\u2039"
176
+ }
177
+ ),
178
+ pages.map(
179
+ (p, i) => p === "..." ? /* @__PURE__ */ jsx("span", { className: "sg-pagination-ellipsis", children: "\u2026" }, `e${i}`) : /* @__PURE__ */ jsx(
180
+ "button",
181
+ {
182
+ className: `sg-pagination-item${p === safeCurrentPage ? " sg-pagination-item-active" : ""}`,
183
+ onClick: () => go(p),
184
+ "aria-current": p === safeCurrentPage ? "page" : void 0,
185
+ children: p
186
+ },
187
+ p
188
+ )
189
+ ),
190
+ /* @__PURE__ */ jsx(
191
+ "button",
192
+ {
193
+ className: "sg-pagination-item sg-pagination-next",
194
+ disabled: safeCurrentPage >= totalPages,
195
+ onClick: () => go(safeCurrentPage + 1),
196
+ children: "\u203A"
197
+ }
198
+ ),
199
+ renderSizeChanger(),
200
+ renderQuickJumper()
201
+ ]
202
+ }
203
+ );
204
+ }
205
+ function buildPages(current, total) {
206
+ if (total <= 7) {
207
+ return Array.from({ length: total }, (_, i) => i + 1);
208
+ }
209
+ if (current <= 4) {
210
+ return [1, 2, 3, 4, 5, "...", total];
211
+ }
212
+ if (current >= total - 3) {
213
+ return [1, "...", total - 4, total - 3, total - 2, total - 1, total];
214
+ }
215
+ return [1, "...", current - 1, current, current + 1, "...", total];
216
+ }
217
+
218
+ // src/components/ui/Select.tsx
219
+ import { useState as useState2, useRef, useEffect, useId } from "react";
220
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
221
+ function Select(props) {
222
+ const {
223
+ options,
224
+ placeholder = "Select...",
225
+ disabled: disabledProp,
226
+ loading,
227
+ size: sizeProp,
228
+ onBlur,
229
+ className,
230
+ style,
231
+ unstyled,
232
+ "aria-label": ariaLabel,
233
+ "aria-labelledby": ariaLabelledBy
234
+ } = props;
235
+ const isMultiple = props.multiple === true;
236
+ const config = useConfig();
237
+ const size = sizeProp ?? config.size ?? "middle";
238
+ const disabled = disabledProp ?? config.disabled ?? false;
239
+ const initialSingle = isMultiple ? void 0 : props.value ?? props.defaultValue;
240
+ const initialMultiple = isMultiple ? props.value ?? props.defaultValue ?? [] : [];
241
+ const [internalSingle, setInternalSingle] = useState2(initialSingle);
242
+ const [internalMultiple, setInternalMultiple] = useState2(initialMultiple);
243
+ const [open, setOpen] = useState2(false);
244
+ const [focusedIndex, setFocusedIndex] = useState2(-1);
245
+ const ref = useRef(null);
246
+ const listboxId = useId();
247
+ const currentSingle = isMultiple ? void 0 : props.value ?? internalSingle;
248
+ const currentMultiple = isMultiple ? props.value ?? internalMultiple : [];
249
+ const selectedOption = isMultiple ? void 0 : options.find((o) => o.value === currentSingle);
250
+ const selectedOptions = isMultiple ? options.filter((o) => currentMultiple.includes(o.value)) : [];
251
+ useEffect(() => {
252
+ const handleClickOutside = (e) => {
253
+ if (ref.current && !ref.current.contains(e.target)) {
254
+ setOpen(false);
255
+ }
256
+ };
257
+ document.addEventListener("mousedown", handleClickOutside);
258
+ return () => document.removeEventListener("mousedown", handleClickOutside);
259
+ }, []);
260
+ useEffect(() => {
261
+ if (!open) setFocusedIndex(-1);
262
+ }, [open]);
263
+ const emitSingle = (val) => {
264
+ setInternalSingle(val);
265
+ if (!isMultiple) props.onChange?.(val);
266
+ };
267
+ const emitMultiple = (vals) => {
268
+ setInternalMultiple(vals);
269
+ if (isMultiple) props.onChange?.(vals);
270
+ };
271
+ const handleSelect = (opt) => {
272
+ if (opt.disabled || opt.loading) return;
273
+ if (isMultiple) {
274
+ const exists = currentMultiple.includes(opt.value);
275
+ const next = exists ? currentMultiple.filter((v) => v !== opt.value) : [...currentMultiple, opt.value];
276
+ emitMultiple(next);
277
+ } else {
278
+ emitSingle(opt.value);
279
+ setOpen(false);
280
+ }
281
+ };
282
+ const handleRemoveTag = (val, e) => {
283
+ e.stopPropagation();
284
+ if (disabled || loading) return;
285
+ emitMultiple(currentMultiple.filter((v) => v !== val));
286
+ };
287
+ if (unstyled) {
288
+ if (isMultiple) {
289
+ return /* @__PURE__ */ jsx2(
290
+ "select",
291
+ {
292
+ className,
293
+ style,
294
+ multiple: true,
295
+ value: currentMultiple.map(String),
296
+ disabled: disabled || loading,
297
+ "aria-label": ariaLabel,
298
+ "aria-labelledby": ariaLabelledBy,
299
+ onChange: (e) => {
300
+ const picked = Array.from(e.target.selectedOptions).map((o) => {
301
+ const v = o.value;
302
+ const numVal = Number(v);
303
+ return options.some((opt) => opt.value === numVal) ? numVal : v;
304
+ });
305
+ emitMultiple(picked);
306
+ },
307
+ onBlur,
308
+ children: options.map((opt) => /* @__PURE__ */ jsx2("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))
309
+ }
310
+ );
311
+ }
312
+ return /* @__PURE__ */ jsxs2(
313
+ "select",
314
+ {
315
+ className,
316
+ style,
317
+ value: currentSingle,
318
+ disabled: disabled || loading,
319
+ "aria-label": ariaLabel,
320
+ "aria-labelledby": ariaLabelledBy,
321
+ onChange: (e) => {
322
+ const v = e.target.value;
323
+ const numVal = Number(v);
324
+ const finalVal = options.some((o) => o.value === numVal) ? numVal : v;
325
+ emitSingle(finalVal);
326
+ },
327
+ onBlur,
328
+ children: [
329
+ !selectedOption && /* @__PURE__ */ jsx2("option", { value: "", children: placeholder }),
330
+ options.map((opt) => /* @__PURE__ */ jsx2("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))
331
+ ]
332
+ }
333
+ );
334
+ }
335
+ const wrapperClass = [
336
+ "sg-select",
337
+ `sg-select-${size}`,
338
+ isMultiple ? "sg-select-multiple" : "",
339
+ open ? "sg-select-open" : "",
340
+ disabled || loading ? "sg-select-disabled" : "",
341
+ loading ? "sg-select-loading" : "",
342
+ className
343
+ ].filter(Boolean).join(" ");
344
+ const isOptionSelected = (opt) => isMultiple ? currentMultiple.includes(opt.value) : opt.value === currentSingle;
345
+ const renderTrigger = () => {
346
+ if (isMultiple) {
347
+ if (selectedOptions.length === 0) {
348
+ return /* @__PURE__ */ jsx2("span", { className: "sg-select-placeholder", children: placeholder });
349
+ }
350
+ return /* @__PURE__ */ jsx2("span", { className: "sg-select-tags", children: selectedOptions.map((opt) => /* @__PURE__ */ jsxs2("span", { className: "sg-select-tag", children: [
351
+ /* @__PURE__ */ jsx2("span", { className: "sg-select-tag-label", children: opt.label }),
352
+ /* @__PURE__ */ jsx2(
353
+ "button",
354
+ {
355
+ type: "button",
356
+ className: "sg-select-tag-remove",
357
+ "aria-label": `Remove ${opt.label}`,
358
+ tabIndex: -1,
359
+ onClick: (e) => handleRemoveTag(opt.value, e),
360
+ children: "\xD7"
361
+ }
362
+ )
363
+ ] }, opt.value)) });
364
+ }
365
+ return /* @__PURE__ */ jsx2(
366
+ "span",
367
+ {
368
+ className: selectedOption ? "sg-select-selection-item" : "sg-select-placeholder",
369
+ children: selectedOption?.label ?? placeholder
370
+ }
371
+ );
372
+ };
373
+ return /* @__PURE__ */ jsxs2("div", { className: wrapperClass, ref, style, onBlur, children: [
374
+ /* @__PURE__ */ jsxs2(
375
+ "div",
376
+ {
377
+ className: "sg-select-selector",
378
+ role: "combobox",
379
+ "aria-expanded": open,
380
+ "aria-haspopup": "listbox",
381
+ "aria-controls": listboxId,
382
+ "aria-multiselectable": isMultiple || void 0,
383
+ "aria-label": ariaLabel,
384
+ "aria-labelledby": ariaLabelledBy,
385
+ "aria-disabled": disabled || loading || void 0,
386
+ tabIndex: disabled || loading ? -1 : 0,
387
+ onClick: () => !disabled && !loading && setOpen(!open),
388
+ onKeyDown: (e) => {
389
+ if (disabled || loading) return;
390
+ switch (e.key) {
391
+ case "Enter":
392
+ case " ":
393
+ e.preventDefault();
394
+ if (open && focusedIndex >= 0) {
395
+ handleSelect(options[focusedIndex]);
396
+ } else {
397
+ setOpen(!open);
398
+ }
399
+ break;
400
+ case "Escape":
401
+ e.preventDefault();
402
+ setOpen(false);
403
+ break;
404
+ case "ArrowDown":
405
+ e.preventDefault();
406
+ if (!open) setOpen(true);
407
+ else setFocusedIndex((i) => (i + 1) % options.length);
408
+ break;
409
+ case "ArrowUp":
410
+ e.preventDefault();
411
+ if (!open) setOpen(true);
412
+ else
413
+ setFocusedIndex(
414
+ (i) => (i - 1 + options.length) % options.length
415
+ );
416
+ break;
417
+ case "Backspace":
418
+ if (isMultiple && currentMultiple.length > 0) {
419
+ emitMultiple(currentMultiple.slice(0, -1));
420
+ }
421
+ break;
422
+ }
423
+ },
424
+ children: [
425
+ renderTrigger(),
426
+ loading ? /* @__PURE__ */ jsx2(Spin, { size: "small", unstyled }) : /* @__PURE__ */ jsx2("span", { className: "sg-select-arrow", children: open ? "\u25B2" : "\u25BC" })
427
+ ]
428
+ }
429
+ ),
430
+ /* @__PURE__ */ jsx2(Transition, { visible: open, name: "sg-slide-up", unmountOnExit: true, children: /* @__PURE__ */ jsx2("div", { className: "sg-select-dropdown", role: "listbox", id: listboxId, "aria-multiselectable": isMultiple || void 0, children: options.map((opt, idx) => /* @__PURE__ */ jsxs2(
431
+ "div",
432
+ {
433
+ role: "option",
434
+ "aria-selected": isOptionSelected(opt),
435
+ className: [
436
+ "sg-select-option",
437
+ isOptionSelected(opt) ? "sg-select-option-selected" : "",
438
+ idx === focusedIndex ? "sg-select-option-focused" : "",
439
+ opt.disabled || opt.loading ? "sg-select-option-disabled" : "",
440
+ opt.loading ? "sg-select-option-loading" : ""
441
+ ].filter(Boolean).join(" "),
442
+ onClick: () => handleSelect(opt),
443
+ children: [
444
+ /* @__PURE__ */ jsx2("span", { children: opt.label }),
445
+ opt.loading && /* @__PURE__ */ jsx2(Spin, { size: "small", unstyled })
446
+ ]
447
+ },
448
+ opt.value
449
+ )) }) })
450
+ ] });
451
+ }
452
+
453
+ // src/components/complex/Table/export.ts
454
+ function toCSVString(data) {
455
+ return data.map(
456
+ (row) => row.map((cell) => {
457
+ if (cell.includes(",") || cell.includes('"') || cell.includes("\n")) {
458
+ return `"${cell.replace(/"/g, '""')}"`;
459
+ }
460
+ return cell;
461
+ }).join(",")
462
+ ).join("\n");
463
+ }
464
+ function toTSVString(data) {
465
+ return data.map((row) => row.join(" ")).join("\n");
466
+ }
467
+ function toJSONString(headers, rows, pretty = true) {
468
+ const objects = rows.map((row) => {
469
+ const obj = {};
470
+ headers.forEach((h, i) => {
471
+ obj[h] = row[i] ?? "";
472
+ });
473
+ return obj;
474
+ });
475
+ return pretty ? JSON.stringify(objects, null, 2) : JSON.stringify(objects);
476
+ }
477
+ function downloadCSV(content, filename = "export.csv") {
478
+ const blob = new Blob([content], { type: "text/csv;charset=utf-8;" });
479
+ const url = URL.createObjectURL(blob);
480
+ const a = document.createElement("a");
481
+ a.href = url;
482
+ a.download = filename;
483
+ a.click();
484
+ URL.revokeObjectURL(url);
485
+ }
486
+ function downloadJSON(content, filename = "export.json") {
487
+ const blob = new Blob([content], { type: "application/json;charset=utf-8;" });
488
+ const url = URL.createObjectURL(blob);
489
+ const a = document.createElement("a");
490
+ a.href = url;
491
+ a.download = filename;
492
+ a.click();
493
+ URL.revokeObjectURL(url);
494
+ }
495
+ async function copyToClipboard(text) {
496
+ if (navigator.clipboard) {
497
+ await navigator.clipboard.writeText(text);
498
+ }
499
+ }
500
+ function printTable(tableEl) {
501
+ if (!tableEl) return;
502
+ const printWindow = window.open("", "_blank");
503
+ if (!printWindow) return;
504
+ const styles = Array.from(document.querySelectorAll('style, link[rel="stylesheet"]')).map((el) => el.outerHTML).join("\n");
505
+ printWindow.document.write(`<!DOCTYPE html><html><head>${styles}
506
+ <style>
507
+ body { padding: 20px; }
508
+ .sg-table-toolbar, .sg-table-pagination, .sg-table-selection-bar { display: none !important; }
509
+ .sg-table-wrapper { overflow: visible !important; }
510
+ .sg-table-scroll { overflow: visible !important; max-height: none !important; }
511
+ @media print { @page { size: landscape; margin: 1cm; } }
512
+ </style>
513
+ </head><body>${tableEl.outerHTML}</body></html>`);
514
+ printWindow.document.close();
515
+ printWindow.focus();
516
+ setTimeout(() => {
517
+ printWindow.print();
518
+ printWindow.close();
519
+ }, 300);
520
+ }
521
+
522
+ // src/hooks/useTable.ts
523
+ import { useState as useState3, useCallback, useMemo, useEffect as useEffect2, useRef as useRef2 } from "react";
524
+ import { createCore, createTable } from "@skygraph/core";
525
+ function useTable(options) {
526
+ const [{ core, table }] = useState3(() => {
527
+ const c = createCore();
528
+ const t = createTable(c, options);
529
+ if (options?.data) {
530
+ t.addRows(options.data);
531
+ }
532
+ return { core: c, table: t };
533
+ });
534
+ const [visibleRows, setVisibleRows] = useState3(() => table.getVisibleRows());
535
+ const [tableState, setTableState] = useState3(() => table.getTableState());
536
+ const [pinnedColumns, setPinnedColumns] = useState3(() => table.getPinnedColumns());
537
+ const [columnWidths, setColumnWidthsState] = useState3(
538
+ () => table.getColumnWidths()
539
+ );
540
+ const prevDataRef = useRef2(options?.data);
541
+ useEffect2(() => {
542
+ const newData = options?.data;
543
+ if (newData === prevDataRef.current) return;
544
+ prevDataRef.current = newData;
545
+ table.reset();
546
+ if (newData) {
547
+ table.addRows(newData);
548
+ }
549
+ setVisibleRows(table.getVisibleRows());
550
+ setTableState(table.getTableState());
551
+ }, [options?.data, table]);
552
+ const refresh = useCallback(() => {
553
+ setVisibleRows(table.getVisibleRows());
554
+ setTableState(table.getTableState());
555
+ setPinnedColumns(table.getPinnedColumns());
556
+ setColumnWidthsState(table.getColumnWidths());
557
+ }, [table]);
558
+ const pinColumn = useCallback(
559
+ (column, side) => {
560
+ table.pinColumn(column, side);
561
+ setPinnedColumns(table.getPinnedColumns());
562
+ },
563
+ [table]
564
+ );
565
+ const clearPinned = useCallback(() => {
566
+ table.clearPinned();
567
+ setPinnedColumns(table.getPinnedColumns());
568
+ }, [table]);
569
+ const setColumnWidth = useCallback(
570
+ (column, width) => {
571
+ table.setColumnWidth(column, width);
572
+ setColumnWidthsState(table.getColumnWidths());
573
+ },
574
+ [table]
575
+ );
576
+ const clearColumnWidths = useCallback(() => {
577
+ table.clearColumnWidths();
578
+ setColumnWidthsState(table.getColumnWidths());
579
+ }, [table]);
580
+ const setSort = useCallback(
581
+ (column, direction) => {
582
+ table.setSort(column, direction);
583
+ refresh();
584
+ },
585
+ [table, refresh]
586
+ );
587
+ const setSorts = useCallback(
588
+ (sorts) => {
589
+ table.setSorts(sorts);
590
+ refresh();
591
+ },
592
+ [table, refresh]
593
+ );
594
+ const clearSort = useCallback(() => {
595
+ table.clearSort();
596
+ refresh();
597
+ }, [table, refresh]);
598
+ const addFilter = useCallback(
599
+ (filter) => {
600
+ table.addFilter(filter);
601
+ refresh();
602
+ },
603
+ [table, refresh]
604
+ );
605
+ const removeFilter = useCallback(
606
+ (column) => {
607
+ table.removeFilter(column);
608
+ refresh();
609
+ },
610
+ [table, refresh]
611
+ );
612
+ const clearFilters = useCallback(() => {
613
+ table.clearFilters();
614
+ refresh();
615
+ }, [table, refresh]);
616
+ const setFilterFn = useCallback(
617
+ (fn) => {
618
+ table.setFilterFn(fn);
619
+ refresh();
620
+ },
621
+ [table, refresh]
622
+ );
623
+ const setPage = useCallback(
624
+ (page) => {
625
+ table.setPage(page);
626
+ refresh();
627
+ },
628
+ [table, refresh]
629
+ );
630
+ const nextPage = useCallback(() => {
631
+ table.nextPage();
632
+ refresh();
633
+ }, [table, refresh]);
634
+ const prevPage = useCallback(() => {
635
+ table.prevPage();
636
+ refresh();
637
+ }, [table, refresh]);
638
+ return useMemo(
639
+ () => ({
640
+ core,
641
+ table,
642
+ visibleRows,
643
+ tableState,
644
+ pinnedColumns,
645
+ columnWidths,
646
+ setSort,
647
+ setSorts,
648
+ clearSort,
649
+ addFilter,
650
+ removeFilter,
651
+ clearFilters,
652
+ setFilterFn,
653
+ setPage,
654
+ nextPage,
655
+ prevPage,
656
+ pinColumn,
657
+ clearPinned,
658
+ setColumnWidth,
659
+ clearColumnWidths,
660
+ refresh
661
+ }),
662
+ [
663
+ core,
664
+ table,
665
+ visibleRows,
666
+ tableState,
667
+ pinnedColumns,
668
+ columnWidths,
669
+ setSort,
670
+ setSorts,
671
+ clearSort,
672
+ addFilter,
673
+ removeFilter,
674
+ clearFilters,
675
+ setFilterFn,
676
+ setPage,
677
+ nextPage,
678
+ prevPage,
679
+ pinColumn,
680
+ clearPinned,
681
+ setColumnWidth,
682
+ clearColumnWidths,
683
+ refresh
684
+ ]
685
+ );
686
+ }
687
+
688
+ // src/utils/print/printElement.ts
689
+ function buildPageRule(opts = {}) {
690
+ const orientation = opts.orientation ?? "portrait";
691
+ const pageSize = opts.pageSize ?? "A4";
692
+ const margins = opts.margins ?? "1cm";
693
+ return `@page { size: ${pageSize} ${orientation}; margin: ${margins}; }`;
694
+ }
695
+ function buildPrintHtml(fragment, opts = {}, documentRef = typeof document !== "undefined" ? document : null) {
696
+ const styles = documentRef === null ? "" : Array.from(documentRef.querySelectorAll('style, link[rel="stylesheet"]')).map((el) => el.outerHTML).join("\n");
697
+ const pageRule = buildPageRule(opts);
698
+ const scale = opts.scale && opts.scale !== 1 ? opts.scale : null;
699
+ const title = escapeHtml(opts.fileName ?? "SkyGraph print");
700
+ const customStyles = opts.customStyles ?? "";
701
+ const wrapperOpen = scale ? `<div class="sg-print-root" style="transform: scale(${scale}); transform-origin: 0 0;">` : `<div class="sg-print-root">`;
702
+ return `<!DOCTYPE html>
703
+ <html>
704
+ <head>
705
+ <meta charset="utf-8" />
706
+ <title>${title}</title>
707
+ ${styles}
708
+ <style>
709
+ ${pageRule}
710
+ html, body { margin: 0; padding: 0; background: #fff; }
711
+ body { padding: 0; }
712
+ .sg-print-root { width: 100%; }
713
+ ${customStyles}
714
+ </style>
715
+ </head>
716
+ <body>
717
+ ${wrapperOpen}
718
+ ${fragment}
719
+ </div>
720
+ </body>
721
+ </html>`;
722
+ }
723
+ function escapeHtml(s) {
724
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
725
+ }
726
+ function printElement(node, opts = {}) {
727
+ if (!node) return null;
728
+ if (typeof window === "undefined") return null;
729
+ const popup = window.open("", "_blank");
730
+ if (!popup) return null;
731
+ const html = buildPrintHtml(node.outerHTML, opts);
732
+ popup.document.open();
733
+ popup.document.write(html);
734
+ popup.document.close();
735
+ popup.focus();
736
+ if (opts.skipPrint) return popup;
737
+ const trigger = () => {
738
+ try {
739
+ popup.print();
740
+ } catch {
741
+ }
742
+ if (!opts.keepOpen) {
743
+ try {
744
+ popup.close();
745
+ } catch {
746
+ }
747
+ }
748
+ };
749
+ if (popup.document.readyState === "complete") {
750
+ setTimeout(trigger, 0);
751
+ } else {
752
+ popup.addEventListener("load", () => setTimeout(trigger, 0), { once: true });
753
+ setTimeout(trigger, 300);
754
+ }
755
+ return popup;
756
+ }
757
+
758
+ // src/components/complex/Table/Table.tsx
759
+ import React5, { forwardRef, useCallback as useCallback4, useImperativeHandle, useMemo as useMemo4, useRef as useRef7 } from "react";
760
+
761
+ // src/components/complex/Table/FilterDropdown.tsx
762
+ import React from "react";
763
+ import { createPortal } from "react-dom";
764
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
765
+ var TEXT_OPS = [
766
+ "eq",
767
+ "neq",
768
+ "contains",
769
+ "startsWith",
770
+ "endsWith",
771
+ "in",
772
+ "notIn",
773
+ "isEmpty",
774
+ "isNotEmpty"
775
+ ];
776
+ var NUMBER_OPS = [
777
+ "eq",
778
+ "neq",
779
+ "lt",
780
+ "lte",
781
+ "gt",
782
+ "gte",
783
+ "between",
784
+ "in",
785
+ "notIn",
786
+ "isEmpty",
787
+ "isNotEmpty"
788
+ ];
789
+ var NO_VALUE_OPS = /* @__PURE__ */ new Set(["isEmpty", "isNotEmpty"]);
790
+ function FilterTrigger({ col, t, state, actions }) {
791
+ const hasFilter = col.filterDropdown || col.filterMode === "search" || col.filterMode === "advanced" || col.filters && col.filters.length > 0;
792
+ if (!hasFilter) return null;
793
+ const filtered = col.filterMode === "advanced" ? state.advancedFilters[col.key] != null : (state.activeFilters[col.key]?.length ?? 0) > 0;
794
+ const iconNode = col.filterIcon ? typeof col.filterIcon === "function" ? col.filterIcon(filtered) : col.filterIcon : col.filterMode === "search" ? t.searchIcon : t.filterIcon;
795
+ return /* @__PURE__ */ jsx3(
796
+ "span",
797
+ {
798
+ className: `sg-table-filter-trigger${filtered ? " sg-table-filter-active" : ""}`,
799
+ onClick: (e) => {
800
+ e.stopPropagation();
801
+ const isOpen = state.openFilterDropdown === col.key;
802
+ if (isOpen) {
803
+ actions.setOpenFilterDropdown(null);
804
+ actions.setFilterAnchor(null);
805
+ } else {
806
+ actions.setFilterAnchor(e.currentTarget);
807
+ actions.setOpenFilterDropdown(col.key);
808
+ }
809
+ },
810
+ children: iconNode
811
+ }
812
+ );
813
+ }
814
+ function FilterPortal({
815
+ anchor,
816
+ children
817
+ }) {
818
+ const [coords, setCoords] = React.useState(null);
819
+ React.useLayoutEffect(() => {
820
+ if (!anchor) return;
821
+ const update = () => {
822
+ const r = anchor.getBoundingClientRect();
823
+ setCoords({ top: r.bottom + 4, left: r.left });
824
+ };
825
+ update();
826
+ window.addEventListener("scroll", update, true);
827
+ window.addEventListener("resize", update);
828
+ return () => {
829
+ window.removeEventListener("scroll", update, true);
830
+ window.removeEventListener("resize", update);
831
+ };
832
+ }, [anchor]);
833
+ if (!anchor || !coords || typeof document === "undefined") return null;
834
+ return createPortal(
835
+ /* @__PURE__ */ jsx3(
836
+ "div",
837
+ {
838
+ className: "sg-table-filter-portal",
839
+ style: { position: "fixed", top: coords.top, left: coords.left, zIndex: 1100 },
840
+ children
841
+ }
842
+ ),
843
+ document.body
844
+ );
845
+ }
846
+ function FilterDropdownContent({ col, t, state, actions, filterRef }) {
847
+ if (state.openFilterDropdown !== col.key) return null;
848
+ if (col.filterDropdown) {
849
+ const props = actions.getFilterDropdownProps(col);
850
+ const content = typeof col.filterDropdown === "function" ? col.filterDropdown(props) : col.filterDropdown;
851
+ return /* @__PURE__ */ jsx3(FilterPortal, { anchor: state.filterAnchor, children: /* @__PURE__ */ jsx3(
852
+ "div",
853
+ {
854
+ className: "sg-table-filter-dropdown",
855
+ ref: filterRef,
856
+ onClick: (e) => e.stopPropagation(),
857
+ children: content
858
+ }
859
+ ) });
860
+ }
861
+ if (col.filterMode === "advanced") {
862
+ return /* @__PURE__ */ jsx3(FilterPortal, { anchor: state.filterAnchor, children: /* @__PURE__ */ jsx3(
863
+ AdvancedFilterPanel,
864
+ {
865
+ col,
866
+ t,
867
+ state,
868
+ actions,
869
+ filterRef
870
+ }
871
+ ) });
872
+ }
873
+ if (col.filterMode === "search") {
874
+ const searchVal = state.activeFilters[col.key]?.[0] ?? "";
875
+ return /* @__PURE__ */ jsx3(FilterPortal, { anchor: state.filterAnchor, children: /* @__PURE__ */ jsxs3(
876
+ "div",
877
+ {
878
+ className: "sg-table-filter-dropdown sg-table-filter-search-mode",
879
+ ref: filterRef,
880
+ onClick: (e) => e.stopPropagation(),
881
+ children: [
882
+ /* @__PURE__ */ jsx3(
883
+ Input,
884
+ {
885
+ placeholder: `${t.searchPlaceholder.replace("...", "")} ${typeof col.title === "string" ? col.title : ""}...`,
886
+ size: "small",
887
+ value: searchVal,
888
+ onChange: (v) => actions.handleSearchFilterChange(col.key, v)
889
+ }
890
+ ),
891
+ /* @__PURE__ */ jsxs3("div", { className: "sg-table-filter-actions", children: [
892
+ /* @__PURE__ */ jsx3(
893
+ Button,
894
+ {
895
+ size: "small",
896
+ onClick: () => {
897
+ actions.handleFilterReset(col.key);
898
+ actions.handleFilterClose(col.key);
899
+ },
900
+ children: t.filterReset
901
+ }
902
+ ),
903
+ /* @__PURE__ */ jsx3(
904
+ Button,
905
+ {
906
+ size: "small",
907
+ type: "primary",
908
+ onClick: () => actions.handleFilterConfirm(col.key),
909
+ children: t.filterConfirm
910
+ }
911
+ )
912
+ ] })
913
+ ]
914
+ }
915
+ ) });
916
+ }
917
+ if (!col.filters || col.filters.length === 0) return null;
918
+ const multiple = col.filterMultiple !== false;
919
+ const currentVals = state.activeFilters[col.key] ?? [];
920
+ const searchTxt = state.filterSearchText[col.key] ?? "";
921
+ let visibleFilters = col.filters;
922
+ if (col.filterSearch && searchTxt) {
923
+ visibleFilters = col.filters.filter((f) => {
924
+ if (typeof col.filterSearch === "function") {
925
+ return col.filterSearch(searchTxt, f);
926
+ }
927
+ return f.text.toLowerCase().includes(searchTxt.toLowerCase());
928
+ });
929
+ }
930
+ return /* @__PURE__ */ jsx3(FilterPortal, { anchor: state.filterAnchor, children: /* @__PURE__ */ jsxs3(
931
+ "div",
932
+ {
933
+ className: "sg-table-filter-dropdown",
934
+ ref: filterRef,
935
+ onClick: (e) => e.stopPropagation(),
936
+ children: [
937
+ col.filterSearch !== void 0 && col.filterSearch !== false && /* @__PURE__ */ jsx3("div", { className: "sg-table-filter-search", children: /* @__PURE__ */ jsx3(
938
+ Input,
939
+ {
940
+ placeholder: t.filterSearchPlaceholder,
941
+ size: "small",
942
+ value: searchTxt,
943
+ onChange: (v) => actions.setFilterSearchText((prev) => ({ ...prev, [col.key]: v }))
944
+ }
945
+ ) }),
946
+ /* @__PURE__ */ jsxs3("div", { className: "sg-table-filter-list", children: [
947
+ visibleFilters.map((f) => {
948
+ const checked = currentVals.includes(f.value);
949
+ return /* @__PURE__ */ jsxs3("label", { className: "sg-table-filter-item", children: [
950
+ multiple ? /* @__PURE__ */ jsx3(
951
+ Checkbox,
952
+ {
953
+ checked,
954
+ onChange: () => actions.handleFilterToggle(col.key, f.value, true)
955
+ }
956
+ ) : /* @__PURE__ */ jsx3(
957
+ "input",
958
+ {
959
+ type: "radio",
960
+ checked,
961
+ onChange: () => actions.handleFilterToggle(col.key, f.value, false)
962
+ }
963
+ ),
964
+ /* @__PURE__ */ jsx3("span", { children: f.text })
965
+ ] }, String(f.value));
966
+ }),
967
+ visibleFilters.length === 0 && /* @__PURE__ */ jsx3("div", { className: "sg-table-filter-empty", children: t.filterEmptyText })
968
+ ] }),
969
+ /* @__PURE__ */ jsxs3("div", { className: "sg-table-filter-actions", children: [
970
+ /* @__PURE__ */ jsx3(
971
+ Button,
972
+ {
973
+ size: "small",
974
+ onClick: () => {
975
+ actions.handleFilterReset(col.key);
976
+ actions.handleFilterClose(col.key);
977
+ },
978
+ children: t.filterReset
979
+ }
980
+ ),
981
+ /* @__PURE__ */ jsx3(Button, { size: "small", type: "primary", onClick: () => actions.handleFilterConfirm(col.key), children: t.filterConfirm })
982
+ ] })
983
+ ]
984
+ }
985
+ ) });
986
+ }
987
+ function AdvancedFilterPanel({ col, t, state, actions, filterRef }) {
988
+ const isNumeric = col.advancedFilterType === "number";
989
+ const defaultOps = isNumeric ? NUMBER_OPS : TEXT_OPS;
990
+ const ops = col.advancedFilterOperators ?? defaultOps;
991
+ const current = state.advancedFilters[col.key];
992
+ const [draftOp, setDraftOp] = React.useState(current?.op ?? ops[0]);
993
+ const [draftValue, setDraftValue] = React.useState(() => formatDraftValue(current?.value));
994
+ const [draftValue2, setDraftValue2] = React.useState(() => {
995
+ const v = current?.value;
996
+ if (Array.isArray(v) && v.length === 2) return String(v[1] ?? "");
997
+ return "";
998
+ });
999
+ React.useEffect(() => {
1000
+ if (current) {
1001
+ setDraftOp(current.op);
1002
+ setDraftValue(formatDraftValue(current.value));
1003
+ if (Array.isArray(current.value) && current.value.length === 2) {
1004
+ setDraftValue2(String(current.value[1] ?? ""));
1005
+ }
1006
+ }
1007
+ }, [state.openFilterDropdown]);
1008
+ const reset = () => {
1009
+ setDraftOp(ops[0]);
1010
+ setDraftValue("");
1011
+ setDraftValue2("");
1012
+ actions.setAdvancedFilter(col.key, null);
1013
+ actions.handleFilterClose(col.key);
1014
+ };
1015
+ const confirm = () => {
1016
+ if (NO_VALUE_OPS.has(draftOp)) {
1017
+ actions.setAdvancedFilter(col.key, { op: draftOp });
1018
+ } else if (draftOp === "between") {
1019
+ const a = parseValue(draftValue, isNumeric);
1020
+ const b = parseValue(draftValue2, isNumeric);
1021
+ actions.setAdvancedFilter(col.key, { op: "between", value: [a, b] });
1022
+ } else if (draftOp === "in" || draftOp === "notIn") {
1023
+ const list = draftValue.split(",").map((s) => s.trim()).filter((s) => s.length > 0).map((s) => parseValue(s, isNumeric));
1024
+ actions.setAdvancedFilter(col.key, { op: draftOp, value: list });
1025
+ } else {
1026
+ actions.setAdvancedFilter(col.key, {
1027
+ op: draftOp,
1028
+ value: parseValue(draftValue, isNumeric)
1029
+ });
1030
+ }
1031
+ actions.setOpenFilterDropdown(null);
1032
+ };
1033
+ const showValueInput = !NO_VALUE_OPS.has(draftOp);
1034
+ const showSecondInput = draftOp === "between";
1035
+ const isValueRequired = showValueInput;
1036
+ const valueIsEmpty = isValueRequired && draftValue.trim() === "";
1037
+ const secondValueIsEmpty = showSecondInput && draftValue2.trim() === "";
1038
+ const confirmDisabled = valueIsEmpty || secondValueIsEmpty;
1039
+ return /* @__PURE__ */ jsxs3(
1040
+ "div",
1041
+ {
1042
+ className: "sg-table-filter-dropdown sg-table-filter-advanced-mode",
1043
+ ref: filterRef,
1044
+ onClick: (e) => e.stopPropagation(),
1045
+ children: [
1046
+ /* @__PURE__ */ jsx3("div", { className: "sg-table-filter-advanced-row", children: /* @__PURE__ */ jsx3(
1047
+ Select,
1048
+ {
1049
+ size: "small",
1050
+ value: draftOp,
1051
+ onChange: (v) => setDraftOp(v),
1052
+ options: ops.map((op) => ({ label: operatorLabel(op, t), value: op })),
1053
+ "aria-label": "Filter operator"
1054
+ }
1055
+ ) }),
1056
+ showValueInput && /* @__PURE__ */ jsx3("div", { className: "sg-table-filter-advanced-row", children: /* @__PURE__ */ jsx3(
1057
+ Input,
1058
+ {
1059
+ size: "small",
1060
+ type: isNumeric && draftOp !== "in" && draftOp !== "notIn" && draftOp !== "between" ? "number" : "text",
1061
+ value: draftValue,
1062
+ onChange: setDraftValue,
1063
+ placeholder: draftOp === "in" || draftOp === "notIn" ? t.filterAdvancedInPlaceholder : t.filterAdvancedValuePlaceholder
1064
+ }
1065
+ ) }),
1066
+ showSecondInput && /* @__PURE__ */ jsx3("div", { className: "sg-table-filter-advanced-row", children: /* @__PURE__ */ jsx3(
1067
+ Input,
1068
+ {
1069
+ size: "small",
1070
+ type: isNumeric ? "number" : "text",
1071
+ value: draftValue2,
1072
+ onChange: setDraftValue2,
1073
+ placeholder: t.filterAdvancedBetweenMaxPlaceholder
1074
+ }
1075
+ ) }),
1076
+ /* @__PURE__ */ jsxs3("div", { className: "sg-table-filter-actions", children: [
1077
+ /* @__PURE__ */ jsx3(Button, { size: "small", onClick: reset, children: t.filterReset }),
1078
+ /* @__PURE__ */ jsx3(Button, { size: "small", type: "primary", disabled: confirmDisabled, onClick: confirm, children: t.filterConfirm })
1079
+ ] })
1080
+ ]
1081
+ }
1082
+ );
1083
+ }
1084
+ function formatDraftValue(value) {
1085
+ if (value == null) return "";
1086
+ if (Array.isArray(value)) return value.map((v) => String(v ?? "")).join(", ");
1087
+ return String(value);
1088
+ }
1089
+ function parseValue(raw, numeric) {
1090
+ const trimmed = raw.trim();
1091
+ if (trimmed === "") return "";
1092
+ if (numeric) {
1093
+ const n = Number(trimmed);
1094
+ return Number.isNaN(n) ? trimmed : n;
1095
+ }
1096
+ return trimmed;
1097
+ }
1098
+ function operatorLabel(op, t) {
1099
+ const map = {
1100
+ eq: t.filterOpEq,
1101
+ neq: t.filterOpNeq,
1102
+ lt: t.filterOpLt,
1103
+ lte: t.filterOpLte,
1104
+ gt: t.filterOpGt,
1105
+ gte: t.filterOpGte,
1106
+ between: t.filterOpBetween,
1107
+ in: t.filterOpIn,
1108
+ notIn: t.filterOpNotIn,
1109
+ contains: t.filterOpContains,
1110
+ startsWith: t.filterOpStartsWith,
1111
+ endsWith: t.filterOpEndsWith,
1112
+ isEmpty: t.filterOpIsEmpty,
1113
+ isNotEmpty: t.filterOpIsNotEmpty
1114
+ };
1115
+ return map[op];
1116
+ }
1117
+
1118
+ // src/components/complex/Table/TableHeader.tsx
1119
+ import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1120
+ function SortIndicator({
1121
+ col,
1122
+ sorts,
1123
+ getSortIndex,
1124
+ getSortDirection,
1125
+ t
1126
+ }) {
1127
+ if (!col.sortable) return null;
1128
+ const dir = getSortDirection(col.key);
1129
+ const idx = getSortIndex(col.key);
1130
+ const isActive = dir !== null;
1131
+ return /* @__PURE__ */ jsxs4("span", { className: `sg-table-sort-arrows${isActive ? " sg-table-sort-active" : ""}`, children: [
1132
+ /* @__PURE__ */ jsx4("span", { className: `sg-table-sort-asc${dir === "asc" ? " sg-table-sort-current" : ""}`, children: t.sortAsc }),
1133
+ /* @__PURE__ */ jsx4("span", { className: `sg-table-sort-desc${dir === "desc" ? " sg-table-sort-current" : ""}`, children: t.sortDesc }),
1134
+ idx >= 0 && sorts.length > 1 && /* @__PURE__ */ jsx4("span", { className: "sg-table-sort-index", children: idx + 1 })
1135
+ ] });
1136
+ }
1137
+ function ColumnTooltip({ content }) {
1138
+ if (!content) return null;
1139
+ return /* @__PURE__ */ jsx4("span", { className: "sg-table-th-tooltip", title: typeof content === "string" ? content : void 0, children: "\u24D8" });
1140
+ }
1141
+ function TableHeader(props) {
1142
+ const {
1143
+ columns,
1144
+ headerRows,
1145
+ hasColumnGroups,
1146
+ rowSelection,
1147
+ expandable,
1148
+ draggable,
1149
+ allSelected,
1150
+ someSelected,
1151
+ dragOver,
1152
+ t,
1153
+ fixedStyle,
1154
+ onSort,
1155
+ onSelectAll,
1156
+ onDragStart,
1157
+ onDragOver,
1158
+ onDragLeave,
1159
+ onDrop,
1160
+ onDragEnd,
1161
+ onResizeStart,
1162
+ filterState,
1163
+ filterActions,
1164
+ filterRef,
1165
+ sorts,
1166
+ getSortIndex,
1167
+ getSortDirection,
1168
+ headerContextMenu,
1169
+ onContextMenu,
1170
+ showRowNumber,
1171
+ rowDraggable,
1172
+ columnPinning,
1173
+ onPinColumn,
1174
+ onAutoResize,
1175
+ slotClassNames,
1176
+ slotStyles
1177
+ } = props;
1178
+ const sCls = slotClassNames ?? {};
1179
+ const sSty = slotStyles ?? {};
1180
+ const filterProps = (col) => ({
1181
+ col,
1182
+ t,
1183
+ state: filterState,
1184
+ actions: filterActions,
1185
+ filterRef
1186
+ });
1187
+ const sortProps = {
1188
+ sorts,
1189
+ getSortIndex,
1190
+ getSortDirection,
1191
+ t
1192
+ };
1193
+ const buildContextMenu = (col, e) => {
1194
+ e.preventDefault();
1195
+ const items = [];
1196
+ if (headerContextMenu) {
1197
+ items.push(...headerContextMenu(col));
1198
+ }
1199
+ if (columnPinning && onPinColumn) {
1200
+ if (items.length > 0) {
1201
+ items.push({ key: "pin-divider", label: "", divider: true });
1202
+ }
1203
+ if (col.fixed !== "left") {
1204
+ items.push({
1205
+ key: "pin-left",
1206
+ label: t.pinLeft,
1207
+ onClick: () => onPinColumn(col.key, "left")
1208
+ });
1209
+ }
1210
+ if (col.fixed !== "right") {
1211
+ items.push({
1212
+ key: "pin-right",
1213
+ label: t.pinRight,
1214
+ onClick: () => onPinColumn(col.key, "right")
1215
+ });
1216
+ }
1217
+ if (col.fixed) {
1218
+ items.push({
1219
+ key: "unpin",
1220
+ label: t.unpin,
1221
+ onClick: () => onPinColumn(col.key, void 0)
1222
+ });
1223
+ }
1224
+ }
1225
+ if (items.length > 0) {
1226
+ onContextMenu({ x: e.clientX, y: e.clientY, items });
1227
+ }
1228
+ };
1229
+ if (hasColumnGroups && headerRows) {
1230
+ return /* @__PURE__ */ jsx4(Fragment, { children: headerRows.map((row, ri) => /* @__PURE__ */ jsxs4(
1231
+ "div",
1232
+ {
1233
+ className: ["sg-table-header-row", sCls.headerRow].filter(Boolean).join(" "),
1234
+ role: "row",
1235
+ style: { display: "contents", ...sSty.headerRow },
1236
+ children: [
1237
+ ri === 0 && rowDraggable && /* @__PURE__ */ jsx4(
1238
+ "div",
1239
+ {
1240
+ className: "sg-table-th",
1241
+ role: "columnheader",
1242
+ style: { gridRow: `span ${headerRows.length}` }
1243
+ }
1244
+ ),
1245
+ ri === 0 && showRowNumber && /* @__PURE__ */ jsx4(
1246
+ "div",
1247
+ {
1248
+ className: "sg-table-th sg-table-cell-row-number",
1249
+ role: "columnheader",
1250
+ style: { gridRow: `span ${headerRows.length}` },
1251
+ children: t.rowNumber
1252
+ }
1253
+ ),
1254
+ ri === 0 && rowSelection && /* @__PURE__ */ jsx4(
1255
+ "div",
1256
+ {
1257
+ className: "sg-table-th sg-table-cell-selection",
1258
+ role: "columnheader",
1259
+ style: { gridRow: `span ${headerRows.length}` },
1260
+ children: rowSelection.type !== "radio" && /* @__PURE__ */ jsx4(
1261
+ Checkbox,
1262
+ {
1263
+ checked: allSelected,
1264
+ indeterminate: someSelected,
1265
+ onChange: onSelectAll
1266
+ }
1267
+ )
1268
+ }
1269
+ ),
1270
+ ri === 0 && expandable && /* @__PURE__ */ jsx4(
1271
+ "div",
1272
+ {
1273
+ className: "sg-table-th sg-table-cell-expand",
1274
+ role: "columnheader",
1275
+ style: { gridRow: `span ${headerRows.length}` }
1276
+ }
1277
+ ),
1278
+ row.map((cell, ci) => {
1279
+ const isLeaf = !cell.col.children || cell.col.children.length === 0;
1280
+ return /* @__PURE__ */ jsxs4(
1281
+ "div",
1282
+ {
1283
+ className: [
1284
+ "sg-table-th",
1285
+ isLeaf && cell.col.sortable ? "sg-table-th-sortable" : "",
1286
+ cell.col.fixed ? "sg-table-cell-fixed" : "",
1287
+ cell.col.fixed ? `sg-table-cell-fixed-${cell.col.fixed}` : "",
1288
+ cell.colSpan > 1 ? "sg-table-th-group" : "",
1289
+ sCls.headerCell,
1290
+ cell.col.headerClassName
1291
+ ].filter(Boolean).join(" "),
1292
+ style: {
1293
+ ...isLeaf ? fixedStyle(cell.col, true) : {},
1294
+ gridColumn: cell.colSpan > 1 ? `span ${cell.colSpan}` : void 0,
1295
+ gridRow: cell.rowSpan > 1 ? `span ${cell.rowSpan}` : void 0,
1296
+ justifyContent: cell.colSpan > 1 ? "center" : void 0,
1297
+ ...sSty.headerCell
1298
+ },
1299
+ role: "columnheader",
1300
+ onClick: isLeaf ? (e) => onSort(cell.col, e.shiftKey) : void 0,
1301
+ onContextMenu: isLeaf ? (e) => buildContextMenu(cell.col, e) : void 0,
1302
+ children: [
1303
+ /* @__PURE__ */ jsxs4(
1304
+ "span",
1305
+ {
1306
+ className: ["sg-table-th-content", sCls.headerCellContent].filter(Boolean).join(" "),
1307
+ style: sSty.headerCellContent,
1308
+ children: [
1309
+ cell.col.title,
1310
+ isLeaf && cell.col.tooltip && /* @__PURE__ */ jsx4(ColumnTooltip, { content: cell.col.tooltip }),
1311
+ isLeaf && /* @__PURE__ */ jsx4(SortIndicator, { col: cell.col, ...sortProps })
1312
+ ]
1313
+ }
1314
+ ),
1315
+ isLeaf && /* @__PURE__ */ jsx4(FilterTrigger, { ...filterProps(cell.col) }),
1316
+ isLeaf && /* @__PURE__ */ jsx4(FilterDropdownContent, { ...filterProps(cell.col) }),
1317
+ isLeaf && cell.col.resizable && /* @__PURE__ */ jsx4(
1318
+ "div",
1319
+ {
1320
+ className: "sg-table-resize-handle",
1321
+ onMouseDown: (e) => onResizeStart(e, cell.col.key),
1322
+ onDoubleClick: onAutoResize ? (e) => {
1323
+ e.stopPropagation();
1324
+ onAutoResize(cell.col.key);
1325
+ } : void 0
1326
+ }
1327
+ )
1328
+ ]
1329
+ },
1330
+ cell.col.key ?? `g-${ri}-${ci}`
1331
+ );
1332
+ })
1333
+ ]
1334
+ },
1335
+ `hdr-${ri}`
1336
+ )) });
1337
+ }
1338
+ return /* @__PURE__ */ jsxs4(
1339
+ "div",
1340
+ {
1341
+ className: ["sg-table-header-row", sCls.headerRow].filter(Boolean).join(" "),
1342
+ role: "row",
1343
+ style: { display: "contents", ...sSty.headerRow },
1344
+ children: [
1345
+ rowDraggable && /* @__PURE__ */ jsx4("div", { className: "sg-table-th", role: "columnheader" }),
1346
+ showRowNumber && /* @__PURE__ */ jsx4("div", { className: "sg-table-th sg-table-cell-row-number", role: "columnheader", children: t.rowNumber }),
1347
+ rowSelection && /* @__PURE__ */ jsx4("div", { className: "sg-table-th sg-table-cell-selection", role: "columnheader", children: rowSelection.type !== "radio" && /* @__PURE__ */ jsx4(Checkbox, { checked: allSelected, indeterminate: someSelected, onChange: onSelectAll }) }),
1348
+ expandable && /* @__PURE__ */ jsx4("div", { className: "sg-table-th sg-table-cell-expand", role: "columnheader" }),
1349
+ columns.map((col) => /* @__PURE__ */ jsxs4(
1350
+ "div",
1351
+ {
1352
+ className: [
1353
+ "sg-table-th",
1354
+ col.sortable ? "sg-table-th-sortable" : "",
1355
+ dragOver === col.key ? "sg-table-th-drag-over" : "",
1356
+ col.fixed ? "sg-table-cell-fixed" : "",
1357
+ col.fixed ? `sg-table-cell-fixed-${col.fixed}` : "",
1358
+ sCls.headerCell,
1359
+ col.headerClassName
1360
+ ].filter(Boolean).join(" "),
1361
+ style: { ...fixedStyle(col, true), ...sSty.headerCell },
1362
+ role: "columnheader",
1363
+ onClick: (e) => onSort(col, e.shiftKey),
1364
+ onContextMenu: (e) => buildContextMenu(col, e),
1365
+ draggable: draggable && !col.fixed,
1366
+ onDragStart: draggable ? (e) => onDragStart(e, col.key) : void 0,
1367
+ onDragOver: draggable ? (e) => onDragOver(e, col.key) : void 0,
1368
+ onDragLeave: draggable ? () => onDragLeave() : void 0,
1369
+ onDrop: draggable ? (e) => onDrop(e, col.key) : void 0,
1370
+ onDragEnd: draggable ? () => onDragEnd() : void 0,
1371
+ children: [
1372
+ /* @__PURE__ */ jsxs4(
1373
+ "span",
1374
+ {
1375
+ className: ["sg-table-th-content", sCls.headerCellContent].filter(Boolean).join(" "),
1376
+ style: sSty.headerCellContent,
1377
+ children: [
1378
+ col.title,
1379
+ col.tooltip && /* @__PURE__ */ jsx4(ColumnTooltip, { content: col.tooltip }),
1380
+ /* @__PURE__ */ jsx4(SortIndicator, { col, ...sortProps })
1381
+ ]
1382
+ }
1383
+ ),
1384
+ /* @__PURE__ */ jsx4(FilterTrigger, { ...filterProps(col) }),
1385
+ /* @__PURE__ */ jsx4(FilterDropdownContent, { ...filterProps(col) }),
1386
+ col.resizable && /* @__PURE__ */ jsx4(
1387
+ "div",
1388
+ {
1389
+ className: "sg-table-resize-handle",
1390
+ onMouseDown: (e) => onResizeStart(e, col.key),
1391
+ onDoubleClick: onAutoResize ? (e) => {
1392
+ e.stopPropagation();
1393
+ onAutoResize(col.key);
1394
+ } : void 0
1395
+ }
1396
+ )
1397
+ ]
1398
+ },
1399
+ col.key
1400
+ ))
1401
+ ]
1402
+ }
1403
+ );
1404
+ }
1405
+
1406
+ // src/components/complex/Table/TableBody.tsx
1407
+ import React2, { useRef as useRef3, useState as useState4, useEffect as useEffect3, useCallback as useCallback2 } from "react";
1408
+
1409
+ // src/components/complex/Table/TableGroupRow.tsx
1410
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1411
+ function TableGroupRow({ group, groupBy, totalCols, t, onToggle }) {
1412
+ return /* @__PURE__ */ jsx5(
1413
+ "div",
1414
+ {
1415
+ className: "sg-table-group-row",
1416
+ role: "row",
1417
+ style: { display: "contents" },
1418
+ children: /* @__PURE__ */ jsxs5(
1419
+ "div",
1420
+ {
1421
+ className: "sg-table-td sg-table-group-cell",
1422
+ role: "cell",
1423
+ style: { gridColumn: `1 / ${totalCols + 1}` },
1424
+ onClick: () => onToggle(group.groupKey),
1425
+ children: [
1426
+ /* @__PURE__ */ jsx5(
1427
+ "button",
1428
+ {
1429
+ type: "button",
1430
+ className: `sg-table-expand-btn${group.expanded ? " sg-table-expand-open" : ""}`,
1431
+ "aria-label": group.expanded ? t.groupCollapse : t.groupExpand,
1432
+ "aria-expanded": group.expanded,
1433
+ onClick: (e) => {
1434
+ e.stopPropagation();
1435
+ onToggle(group.groupKey);
1436
+ },
1437
+ children: t.expandIcon
1438
+ }
1439
+ ),
1440
+ /* @__PURE__ */ jsxs5("span", { className: "sg-table-group-title", children: [
1441
+ groupBy ? `${groupBy}: ` : null,
1442
+ /* @__PURE__ */ jsx5("strong", { children: String(group.groupValue) })
1443
+ ] }),
1444
+ /* @__PURE__ */ jsxs5("span", { className: "sg-table-group-count", children: [
1445
+ "(",
1446
+ group.count,
1447
+ ")"
1448
+ ] }),
1449
+ Object.keys(group.aggregates).length > 0 && /* @__PURE__ */ jsx5("span", { className: "sg-table-group-aggregates", children: Object.entries(group.aggregates).map(([key, val]) => /* @__PURE__ */ jsxs5("span", { className: "sg-table-group-agg-item", children: [
1450
+ key,
1451
+ ": ",
1452
+ String(val)
1453
+ ] }, key)) })
1454
+ ]
1455
+ }
1456
+ )
1457
+ }
1458
+ );
1459
+ }
1460
+
1461
+ // src/components/complex/Table/TableBody.tsx
1462
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1463
+ function TableBody(props) {
1464
+ const {
1465
+ columns,
1466
+ flatRows,
1467
+ rowSelection,
1468
+ expandable,
1469
+ isTreeMode,
1470
+ indentSize,
1471
+ treeExpanded,
1472
+ expandedKeys,
1473
+ totalCols,
1474
+ spanMap,
1475
+ hiddenCells,
1476
+ t,
1477
+ rowClassName,
1478
+ fixedStyle,
1479
+ onRowClick,
1480
+ onCellEdit,
1481
+ onSelectRow,
1482
+ onToggleExpand,
1483
+ onToggleTreeExpand,
1484
+ rowDraggable,
1485
+ onRowOrderChange,
1486
+ cellContextMenu,
1487
+ onContextMenu,
1488
+ striped,
1489
+ showRowNumber,
1490
+ groupBy,
1491
+ onToggleGroup,
1492
+ focusedCell,
1493
+ onFocusCell,
1494
+ keyboardNavigation,
1495
+ emptyContent,
1496
+ slotClassNames,
1497
+ slotStyles,
1498
+ isPinnedSection,
1499
+ rowIndexOffset
1500
+ } = props;
1501
+ const sCls = slotClassNames ?? {};
1502
+ const sSty = slotStyles ?? {};
1503
+ const [editingCell, setEditingCell] = useState4(null);
1504
+ const [editValue, setEditValue] = useState4("");
1505
+ const editRef = useRef3(null);
1506
+ const [dragRowIdx, setDragRowIdx] = useState4(null);
1507
+ const [dragOverRowIdx, setDragOverRowIdx] = useState4(null);
1508
+ const [copiedCell, setCopiedCell] = useState4(null);
1509
+ useEffect3(() => {
1510
+ if (editingCell && editRef.current) editRef.current.focus();
1511
+ }, [editingCell]);
1512
+ const handleEditStart = (rowId, col, val) => {
1513
+ setEditingCell({ rowId, col });
1514
+ setEditValue(String(val ?? ""));
1515
+ };
1516
+ const handleEditSave = () => {
1517
+ if (editingCell && onCellEdit) {
1518
+ onCellEdit(editingCell.rowId, editingCell.col, editValue);
1519
+ }
1520
+ setEditingCell(null);
1521
+ };
1522
+ const handleEditCancel = () => setEditingCell(null);
1523
+ const handleRowDragStart = (e, idx) => {
1524
+ setDragRowIdx(idx);
1525
+ e.dataTransfer.effectAllowed = "move";
1526
+ e.dataTransfer.setData("text/plain", String(idx));
1527
+ };
1528
+ const handleRowDragOver = (e, idx) => {
1529
+ e.preventDefault();
1530
+ e.dataTransfer.dropEffect = "move";
1531
+ setDragOverRowIdx(idx);
1532
+ };
1533
+ const handleRowDrop = (_e, toIdx) => {
1534
+ if (dragRowIdx !== null && dragRowIdx !== toIdx) {
1535
+ onRowOrderChange?.(dragRowIdx, toIdx);
1536
+ }
1537
+ setDragRowIdx(null);
1538
+ setDragOverRowIdx(null);
1539
+ };
1540
+ const handleRowDragEnd = () => {
1541
+ setDragRowIdx(null);
1542
+ setDragOverRowIdx(null);
1543
+ };
1544
+ const handleCellContext = (e, id, colKey, data) => {
1545
+ if (!cellContextMenu) return;
1546
+ e.preventDefault();
1547
+ const items = cellContextMenu(id, colKey, data);
1548
+ if (items.length > 0) {
1549
+ onContextMenu({ x: e.clientX, y: e.clientY, items });
1550
+ }
1551
+ };
1552
+ const handleCellCopy = useCallback2(
1553
+ async (rowId, colKey, value) => {
1554
+ await copyToClipboard(String(value ?? ""));
1555
+ setCopiedCell(`${rowId}:${colKey}`);
1556
+ setTimeout(() => setCopiedCell(null), 1500);
1557
+ },
1558
+ []
1559
+ );
1560
+ if (flatRows.length === 0) {
1561
+ return /* @__PURE__ */ jsx6(
1562
+ "div",
1563
+ {
1564
+ className: ["sg-table-empty", sCls.empty].filter(Boolean).join(" "),
1565
+ style: { gridColumn: `1 / ${totalCols + 1}`, ...sSty.empty },
1566
+ role: "row",
1567
+ children: /* @__PURE__ */ jsx6("span", { role: "cell", children: emptyContent ?? t.emptyText })
1568
+ }
1569
+ );
1570
+ }
1571
+ let dataRowIndex = 0;
1572
+ return /* @__PURE__ */ jsx6(Fragment2, { children: flatRows.map((row, rowIdx) => {
1573
+ if (row.__groupRow) {
1574
+ return /* @__PURE__ */ jsx6(
1575
+ TableGroupRow,
1576
+ {
1577
+ group: row.__groupRow,
1578
+ groupBy,
1579
+ totalCols,
1580
+ t,
1581
+ onToggle: onToggleGroup ?? (() => {
1582
+ })
1583
+ },
1584
+ row.id
1585
+ );
1586
+ }
1587
+ const currentDataIdx = dataRowIndex++;
1588
+ const isExpanded = expandedKeys.has(row.id);
1589
+ const isSelected = rowSelection?.selectedKeys.includes(row.id);
1590
+ const canExpand = expandable?.rowExpandable ? expandable.rowExpandable(row.data) : !!expandable;
1591
+ const rowCls = typeof rowClassName === "function" ? rowClassName(row.data, row.id) : rowClassName ?? "";
1592
+ const isZebra = striped && currentDataIdx % 2 === 1;
1593
+ const isDragOver = dragOverRowIdx === rowIdx && dragRowIdx !== rowIdx;
1594
+ const isPinned = isPinnedSection;
1595
+ const virtualIndexAttr = rowIndexOffset !== void 0 ? rowIndexOffset + rowIdx : void 0;
1596
+ return /* @__PURE__ */ jsxs6(React2.Fragment, { children: [
1597
+ /* @__PURE__ */ jsxs6(
1598
+ "div",
1599
+ {
1600
+ className: [
1601
+ "sg-table-row",
1602
+ isSelected ? "sg-table-row-selected" : "",
1603
+ onRowClick ? "sg-table-row-clickable" : "",
1604
+ isZebra ? "sg-table-row-striped" : "",
1605
+ isDragOver ? "sg-table-row-drag-over" : "",
1606
+ isPinned ? "sg-table-row-pinned" : "",
1607
+ sCls.row,
1608
+ rowCls
1609
+ ].filter(Boolean).join(" "),
1610
+ role: "row",
1611
+ style: { display: "contents", ...sSty.row },
1612
+ "data-sg-virtual-row-index": virtualIndexAttr,
1613
+ draggable: rowDraggable,
1614
+ onDragStart: rowDraggable ? (e) => handleRowDragStart(e, rowIdx) : void 0,
1615
+ onDragOver: rowDraggable ? (e) => handleRowDragOver(e, rowIdx) : void 0,
1616
+ onDrop: rowDraggable ? (e) => handleRowDrop(e, rowIdx) : void 0,
1617
+ onDragEnd: rowDraggable ? handleRowDragEnd : void 0,
1618
+ children: [
1619
+ rowDraggable && /* @__PURE__ */ jsx6(
1620
+ "div",
1621
+ {
1622
+ className: "sg-table-td sg-table-cell-drag-handle",
1623
+ role: "cell",
1624
+ style: { cursor: "grab", userSelect: "none", display: "flex", alignItems: "center", justifyContent: "center" },
1625
+ children: "\u283F"
1626
+ }
1627
+ ),
1628
+ showRowNumber && /* @__PURE__ */ jsx6("div", { className: "sg-table-td sg-table-cell-row-number", role: "cell", children: currentDataIdx + 1 }),
1629
+ rowSelection && /* @__PURE__ */ jsx6(
1630
+ "div",
1631
+ {
1632
+ className: "sg-table-td sg-table-cell-selection",
1633
+ role: "cell",
1634
+ onClick: (e) => {
1635
+ e.stopPropagation();
1636
+ onSelectRow(row.id, row.data);
1637
+ },
1638
+ children: rowSelection.type === "radio" ? /* @__PURE__ */ jsx6("input", { type: "radio", checked: !!isSelected, readOnly: true }) : /* @__PURE__ */ jsx6(Checkbox, { checked: !!isSelected, onChange: () => {
1639
+ } })
1640
+ }
1641
+ ),
1642
+ expandable && /* @__PURE__ */ jsx6("div", { className: "sg-table-td sg-table-cell-expand", role: "cell", children: canExpand && /* @__PURE__ */ jsx6(
1643
+ "button",
1644
+ {
1645
+ className: `sg-table-expand-btn${isExpanded ? " sg-table-expand-open" : ""}`,
1646
+ onClick: (e) => {
1647
+ e.stopPropagation();
1648
+ onToggleExpand(row.id);
1649
+ },
1650
+ children: t.expandIcon
1651
+ }
1652
+ ) }),
1653
+ columns.map((col, colIndex) => {
1654
+ const cellKey = `${row.id}:${col.key}`;
1655
+ if (hiddenCells.has(cellKey)) return null;
1656
+ const span = spanMap.get(cellKey);
1657
+ const val = row.data[col.key];
1658
+ const isEditing = editingCell?.rowId === row.id && editingCell?.col === col.key;
1659
+ const isFocused = keyboardNavigation && focusedCell?.row === rowIdx && focusedCell?.col === colIndex;
1660
+ const isCopied = copiedCell === cellKey;
1661
+ const cellStyle = { ...fixedStyle(col) };
1662
+ if (span?.colSpan && span.colSpan > 1)
1663
+ cellStyle.gridColumn = `span ${span.colSpan}`;
1664
+ if (span?.rowSpan && span.rowSpan > 1)
1665
+ cellStyle.gridRow = `span ${span.rowSpan}`;
1666
+ const isFirstCol = colIndex === 0;
1667
+ const treeIndent = isTreeMode && isFirstCol ? row.depth * indentSize : 0;
1668
+ const cellClsExtra = typeof col.cellClassName === "function" ? col.cellClassName(val, row.data, row.id) : col.cellClassName ?? "";
1669
+ return /* @__PURE__ */ jsxs6(
1670
+ "div",
1671
+ {
1672
+ className: [
1673
+ "sg-table-td",
1674
+ col.fixed ? "sg-table-cell-fixed" : "",
1675
+ col.fixed ? `sg-table-cell-fixed-${col.fixed}` : "",
1676
+ col.editable ? "sg-table-td-editable" : "",
1677
+ isFocused ? "sg-table-cell-focused" : "",
1678
+ isCopied ? "sg-table-cell-copied" : "",
1679
+ col.copyable ? "sg-table-cell-copyable" : "",
1680
+ sCls.bodyCell,
1681
+ cellClsExtra
1682
+ ].filter(Boolean).join(" "),
1683
+ style: { ...cellStyle, ...sSty.bodyCell },
1684
+ role: "cell",
1685
+ tabIndex: keyboardNavigation ? -1 : void 0,
1686
+ onClick: () => {
1687
+ onRowClick?.(row.id, row.data);
1688
+ if (keyboardNavigation) {
1689
+ onFocusCell?.({ row: rowIdx, col: colIndex });
1690
+ }
1691
+ },
1692
+ onContextMenu: (e) => handleCellContext(e, row.id, col.key, row.data),
1693
+ onDoubleClick: col.editable && onCellEdit ? (e) => {
1694
+ e.stopPropagation();
1695
+ handleEditStart(row.id, col.key, val);
1696
+ } : void 0,
1697
+ children: [
1698
+ isTreeMode && isFirstCol && /* @__PURE__ */ jsxs6(
1699
+ "span",
1700
+ {
1701
+ style: {
1702
+ display: "inline-flex",
1703
+ alignItems: "center",
1704
+ minWidth: treeIndent + 20,
1705
+ flexShrink: 0
1706
+ },
1707
+ children: [
1708
+ /* @__PURE__ */ jsx6("span", { style: { width: treeIndent } }),
1709
+ row.hasChildren ? /* @__PURE__ */ jsx6(
1710
+ "button",
1711
+ {
1712
+ className: `sg-table-expand-btn${treeExpanded.has(row.id) ? " sg-table-expand-open" : ""}`,
1713
+ onClick: (e) => {
1714
+ e.stopPropagation();
1715
+ onToggleTreeExpand(row.id);
1716
+ },
1717
+ children: t.expandIcon
1718
+ }
1719
+ ) : /* @__PURE__ */ jsx6("span", { style: { width: 20 } })
1720
+ ]
1721
+ }
1722
+ ),
1723
+ /* @__PURE__ */ jsx6("span", { className: "sg-table-cell-content", children: isEditing ? /* @__PURE__ */ jsx6(
1724
+ "input",
1725
+ {
1726
+ ref: editRef,
1727
+ className: "sg-table-edit-input",
1728
+ value: editValue,
1729
+ onChange: (e) => setEditValue(e.target.value),
1730
+ onBlur: handleEditSave,
1731
+ onKeyDown: (e) => {
1732
+ if (e.key === "Enter") handleEditSave();
1733
+ if (e.key === "Escape") handleEditCancel();
1734
+ }
1735
+ }
1736
+ ) : col.render ? col.render(val, row.data, row.id) : String(val ?? "") }),
1737
+ col.copyable && !isEditing && /* @__PURE__ */ jsx6(
1738
+ "button",
1739
+ {
1740
+ className: "sg-table-copy-btn",
1741
+ onClick: (e) => {
1742
+ e.stopPropagation();
1743
+ handleCellCopy(row.id, col.key, val);
1744
+ },
1745
+ title: "Copy",
1746
+ children: isCopied ? "\u2713" : "\u2398"
1747
+ }
1748
+ )
1749
+ ]
1750
+ },
1751
+ col.key
1752
+ );
1753
+ })
1754
+ ]
1755
+ }
1756
+ ),
1757
+ expandable && isExpanded && /* @__PURE__ */ jsx6(
1758
+ "div",
1759
+ {
1760
+ className: "sg-table-expanded-row",
1761
+ style: { gridColumn: `1 / ${totalCols + 1}` },
1762
+ role: "row",
1763
+ children: /* @__PURE__ */ jsx6("div", { role: "cell", children: expandable.expandedRowRender(row.data, row.id) })
1764
+ }
1765
+ )
1766
+ ] }, row.id);
1767
+ }) });
1768
+ }
1769
+
1770
+ // src/components/complex/Table/VirtualTableBody.tsx
1771
+ import { useEffect as useEffect4, useLayoutEffect, useMemo as useMemo2, useReducer, useRef as useRef4, useState as useState5 } from "react";
1772
+ import { createVirtual } from "@skygraph/core";
1773
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1774
+ var DEFAULT_ROW_HEIGHT = 40;
1775
+ function buildEstimate(rowHeight, estimate, flatRows) {
1776
+ if (typeof rowHeight === "number" && estimate == null) {
1777
+ return rowHeight;
1778
+ }
1779
+ const fallback = typeof rowHeight === "number" ? rowHeight : DEFAULT_ROW_HEIGHT;
1780
+ const fn = estimate ?? (typeof rowHeight === "function" ? rowHeight : void 0);
1781
+ if (!fn) return fallback;
1782
+ return (index) => {
1783
+ const row = flatRows[index];
1784
+ if (!row) return fallback;
1785
+ const v = fn(row.data, row.id);
1786
+ return Number.isFinite(v) && v >= 0 ? v : fallback;
1787
+ };
1788
+ }
1789
+ function VirtualTableBody(props) {
1790
+ const {
1791
+ rowHeight,
1792
+ estimateRowHeight,
1793
+ overscan,
1794
+ scrollContainerRef,
1795
+ flatRows,
1796
+ totalCols,
1797
+ ...bodyProps
1798
+ } = props;
1799
+ const isDeclarative = typeof rowHeight === "function";
1800
+ const isDynamic = isDeclarative || estimateRowHeight != null;
1801
+ const virtual = useMemo2(
1802
+ () => createVirtual({
1803
+ itemCount: flatRows.length,
1804
+ itemHeight: buildEstimate(rowHeight, estimateRowHeight, flatRows),
1805
+ overscan
1806
+ }),
1807
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1808
+ []
1809
+ );
1810
+ const [, forceUpdate] = useReducer((n) => n + 1, 0);
1811
+ useEffect4(() => virtual.subscribe(() => forceUpdate()), [virtual]);
1812
+ useEffect4(() => {
1813
+ virtual.setItemCount(flatRows.length);
1814
+ virtual.setItemHeight(buildEstimate(rowHeight, estimateRowHeight, flatRows));
1815
+ }, [virtual, flatRows, rowHeight, estimateRowHeight]);
1816
+ useEffect4(() => {
1817
+ if (!isDeclarative) return;
1818
+ const fn = rowHeight;
1819
+ for (let i = 0; i < flatRows.length; i++) {
1820
+ const row = flatRows[i];
1821
+ if (!row) continue;
1822
+ const h = fn(row.data, row.id);
1823
+ if (Number.isFinite(h) && h >= 0) {
1824
+ virtual.setMeasuredHeight(i, Math.round(h));
1825
+ }
1826
+ }
1827
+ }, [virtual, flatRows, rowHeight, isDeclarative]);
1828
+ const [scrollTop, setScrollTop] = useState5(0);
1829
+ const [viewportHeight, setViewportHeight] = useState5(0);
1830
+ useEffect4(() => {
1831
+ const el = scrollContainerRef.current;
1832
+ if (!el) return;
1833
+ setViewportHeight(el.clientHeight);
1834
+ setScrollTop(el.scrollTop);
1835
+ const onScroll = () => setScrollTop(el.scrollTop);
1836
+ el.addEventListener("scroll", onScroll, { passive: true });
1837
+ let ro = null;
1838
+ if (typeof ResizeObserver !== "undefined") {
1839
+ ro = new ResizeObserver(() => setViewportHeight(el.clientHeight));
1840
+ ro.observe(el);
1841
+ }
1842
+ return () => {
1843
+ el.removeEventListener("scroll", onScroll);
1844
+ ro?.disconnect();
1845
+ };
1846
+ }, [scrollContainerRef]);
1847
+ const range = virtual.getRange(scrollTop, viewportHeight || 400);
1848
+ const visibleRows = flatRows.slice(range.startIndex, range.endIndex + 1);
1849
+ const totalHeight = range.totalHeight;
1850
+ const topPad = range.offsetTop;
1851
+ const visibleSize = visibleRows.reduce(
1852
+ (sum, _row, idx) => sum + virtual.getItemSize(range.startIndex + idx),
1853
+ 0
1854
+ );
1855
+ const bottomPad = Math.max(0, totalHeight - topPad - visibleSize);
1856
+ const normalizeHeight = (h) => Math.round(h);
1857
+ const roRef = useRef4(null);
1858
+ const observedCellsRef = useRef4(/* @__PURE__ */ new Set());
1859
+ useEffect4(() => {
1860
+ if (!isDynamic) return;
1861
+ if (isDeclarative) return;
1862
+ if (typeof ResizeObserver === "undefined") return;
1863
+ const ro = new ResizeObserver((entries) => {
1864
+ for (const entry of entries) {
1865
+ const target = entry.target;
1866
+ const rowEl = target.closest("[data-sg-virtual-row-index]");
1867
+ if (!rowEl) continue;
1868
+ const idx = Number(rowEl.getAttribute("data-sg-virtual-row-index"));
1869
+ virtual.setMeasuredHeight(idx, normalizeHeight(entry.contentRect.height));
1870
+ }
1871
+ });
1872
+ roRef.current = ro;
1873
+ return () => {
1874
+ ro.disconnect();
1875
+ roRef.current = null;
1876
+ observedCellsRef.current.clear();
1877
+ };
1878
+ }, [isDynamic, isDeclarative, virtual]);
1879
+ useLayoutEffect(() => {
1880
+ if (!isDynamic) return;
1881
+ const grid = scrollContainerRef.current?.querySelector(".sg-table-grid");
1882
+ if (!grid) return;
1883
+ const rowEls = grid.querySelectorAll("[data-sg-virtual-row-index]");
1884
+ const ro = roRef.current;
1885
+ const currentCells = /* @__PURE__ */ new Set();
1886
+ rowEls.forEach((rowEl) => {
1887
+ const firstCell = rowEl.firstElementChild;
1888
+ if (!firstCell) return;
1889
+ const idx = Number(rowEl.getAttribute("data-sg-virtual-row-index"));
1890
+ currentCells.add(firstCell);
1891
+ if (isDeclarative) {
1892
+ const row = flatRows[idx];
1893
+ if (row) {
1894
+ const declared = rowHeight(row.data, row.id);
1895
+ if (Number.isFinite(declared) && declared >= 0) {
1896
+ virtual.setMeasuredHeight(idx, Math.round(declared));
1897
+ }
1898
+ }
1899
+ } else if (!virtual.hasMeasured(idx)) {
1900
+ virtual.setMeasuredHeight(idx, normalizeHeight(firstCell.getBoundingClientRect().height));
1901
+ }
1902
+ if (isDeclarative || virtual.hasMeasured(idx)) {
1903
+ const h = virtual.getItemSize(idx);
1904
+ const px = `${h}px`;
1905
+ const children = rowEl.children;
1906
+ for (let c = 0; c < children.length; c++) {
1907
+ const cell = children[c];
1908
+ if (cell.style.height !== px) cell.style.height = px;
1909
+ if (cell.style.minHeight !== "0px") cell.style.minHeight = "0px";
1910
+ if (cell.style.overflow !== "hidden") cell.style.overflow = "hidden";
1911
+ }
1912
+ }
1913
+ if (ro && !observedCellsRef.current.has(firstCell)) {
1914
+ ro.observe(firstCell);
1915
+ }
1916
+ });
1917
+ if (ro) {
1918
+ for (const cell of observedCellsRef.current) {
1919
+ if (!currentCells.has(cell)) ro.unobserve(cell);
1920
+ }
1921
+ }
1922
+ observedCellsRef.current = currentCells;
1923
+ if (!isDeclarative && typeof ResizeObserver === "undefined") {
1924
+ rowEls.forEach((rowEl) => {
1925
+ const firstCell = rowEl.firstElementChild;
1926
+ if (!firstCell) return;
1927
+ const idx = Number(rowEl.getAttribute("data-sg-virtual-row-index"));
1928
+ virtual.setMeasuredHeight(idx, normalizeHeight(firstCell.getBoundingClientRect().height));
1929
+ });
1930
+ }
1931
+ }, [isDynamic, isDeclarative, virtual, scrollContainerRef, visibleRows, flatRows, rowHeight]);
1932
+ const spacerColumn = `1 / ${totalCols + 1}`;
1933
+ return /* @__PURE__ */ jsxs7(Fragment3, { children: [
1934
+ topPad > 0 && /* @__PURE__ */ jsx7("div", { style: { gridColumn: spacerColumn, height: topPad }, role: "presentation", "aria-hidden": true }),
1935
+ /* @__PURE__ */ jsx7(
1936
+ TableBody,
1937
+ {
1938
+ ...bodyProps,
1939
+ flatRows: visibleRows,
1940
+ totalCols,
1941
+ rowIndexOffset: range.startIndex
1942
+ }
1943
+ ),
1944
+ bottomPad > 0 && /* @__PURE__ */ jsx7(
1945
+ "div",
1946
+ {
1947
+ style: { gridColumn: spacerColumn, height: bottomPad },
1948
+ role: "presentation",
1949
+ "aria-hidden": true
1950
+ }
1951
+ )
1952
+ ] });
1953
+ }
1954
+
1955
+ // src/components/complex/Table/ContextMenu.tsx
1956
+ import { useEffect as useEffect5, useRef as useRef5, useState as useState6 } from "react";
1957
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1958
+ function ContextMenu({ state, onClose }) {
1959
+ const ref = useRef5(null);
1960
+ const [subMenu, setSubMenu] = useState6(null);
1961
+ useEffect5(() => {
1962
+ if (!state) return;
1963
+ const handle = (e) => {
1964
+ if (ref.current && !ref.current.contains(e.target)) {
1965
+ onClose();
1966
+ }
1967
+ };
1968
+ const handleKey = (e) => {
1969
+ if (e.key === "Escape") onClose();
1970
+ };
1971
+ const handleScroll = () => onClose();
1972
+ document.addEventListener("mousedown", handle);
1973
+ document.addEventListener("keydown", handleKey);
1974
+ window.addEventListener("scroll", handleScroll, true);
1975
+ return () => {
1976
+ document.removeEventListener("mousedown", handle);
1977
+ document.removeEventListener("keydown", handleKey);
1978
+ window.removeEventListener("scroll", handleScroll, true);
1979
+ };
1980
+ }, [state, onClose]);
1981
+ if (!state) return null;
1982
+ const rootStyle = {
1983
+ position: "fixed",
1984
+ top: state.y,
1985
+ left: state.x
1986
+ };
1987
+ const submenuStyle = {
1988
+ position: "absolute",
1989
+ top: 0,
1990
+ left: "100%"
1991
+ };
1992
+ const itemClass = (item) => {
1993
+ const classes = ["sg-context-menu-item"];
1994
+ if (item.danger) classes.push("sg-context-menu-item--danger");
1995
+ if (item.disabled) classes.push("sg-context-menu-item--disabled");
1996
+ return classes.join(" ");
1997
+ };
1998
+ const renderItems = (items, isSubmenu = false) => /* @__PURE__ */ jsx8(
1999
+ "div",
2000
+ {
2001
+ ref: isSubmenu ? void 0 : ref,
2002
+ className: "sg-context-menu",
2003
+ style: isSubmenu ? submenuStyle : rootStyle,
2004
+ role: "menu",
2005
+ children: items.map((item) => {
2006
+ if (item.divider) {
2007
+ return /* @__PURE__ */ jsx8("div", { className: "sg-context-menu-divider" }, item.key);
2008
+ }
2009
+ return /* @__PURE__ */ jsxs8(
2010
+ "div",
2011
+ {
2012
+ className: itemClass(item),
2013
+ role: "menuitem",
2014
+ onClick: () => {
2015
+ if (item.disabled) return;
2016
+ item.onClick?.();
2017
+ if (!item.children) onClose();
2018
+ },
2019
+ onMouseEnter: (e) => {
2020
+ if (item.children) {
2021
+ const rect = e.currentTarget.getBoundingClientRect();
2022
+ setSubMenu({ key: item.key, x: rect.right, y: rect.top, items: item.children });
2023
+ } else {
2024
+ setSubMenu(null);
2025
+ }
2026
+ },
2027
+ children: [
2028
+ item.icon && /* @__PURE__ */ jsx8("span", { className: "sg-context-menu-icon", children: item.icon }),
2029
+ /* @__PURE__ */ jsx8("span", { className: "sg-context-menu-label", children: item.label }),
2030
+ item.children && /* @__PURE__ */ jsx8("span", { className: "sg-context-menu-submenu-arrow", children: "\u203A" }),
2031
+ subMenu?.key === item.key && item.children && renderItems(item.children, true)
2032
+ ]
2033
+ },
2034
+ item.key
2035
+ );
2036
+ })
2037
+ }
2038
+ );
2039
+ return renderItems(state.items);
2040
+ }
2041
+
2042
+ // src/components/complex/Table/useTableState.ts
2043
+ import { useState as useState7, useRef as useRef6, useEffect as useEffect6, useMemo as useMemo3, useCallback as useCallback3 } from "react";
2044
+
2045
+ // src/components/complex/Table/helpers.ts
2046
+ function flattenLeafColumns(cols) {
2047
+ const result = [];
2048
+ for (const col of cols) {
2049
+ if (col.children && col.children.length > 0) {
2050
+ result.push(...flattenLeafColumns(col.children));
2051
+ } else {
2052
+ result.push(col);
2053
+ }
2054
+ }
2055
+ return result;
2056
+ }
2057
+ function buildHeaderRows(cols) {
2058
+ function getDepth(c) {
2059
+ if (!c.children || c.children.length === 0) return 1;
2060
+ return 1 + Math.max(...c.children.map(getDepth));
2061
+ }
2062
+ const maxDepth = cols.length > 0 ? Math.max(...cols.map(getDepth)) : 1;
2063
+ function getLeafCount(c) {
2064
+ if (!c.children || c.children.length === 0) return 1;
2065
+ return c.children.reduce((sum, ch) => sum + getLeafCount(ch), 0);
2066
+ }
2067
+ const rows = Array.from({ length: maxDepth }, () => []);
2068
+ function fill(list, depth) {
2069
+ for (const c of list) {
2070
+ if (c.children && c.children.length > 0) {
2071
+ rows[depth].push({ col: c, colSpan: getLeafCount(c), rowSpan: 1 });
2072
+ fill(c.children, depth + 1);
2073
+ } else {
2074
+ rows[depth].push({ col: c, colSpan: 1, rowSpan: maxDepth - depth });
2075
+ }
2076
+ }
2077
+ }
2078
+ fill(cols, 0);
2079
+ return rows;
2080
+ }
2081
+ function flattenTreeRows(rows, childrenKey, expandedSet, depth = 0) {
2082
+ const result = [];
2083
+ for (const row of rows) {
2084
+ const children = row.data[childrenKey];
2085
+ const hasChildren = Array.isArray(children) && children.length > 0;
2086
+ result.push({ id: row.id, data: row.data, depth, hasChildren });
2087
+ if (hasChildren && expandedSet.has(row.id)) {
2088
+ result.push(...flattenTreeRows(children, childrenKey, expandedSet, depth + 1));
2089
+ }
2090
+ }
2091
+ return result;
2092
+ }
2093
+ function collectAllTreeIds(rows, childrenKey) {
2094
+ const ids = [];
2095
+ for (const row of rows) {
2096
+ const children = row.data[childrenKey];
2097
+ if (Array.isArray(children) && children.length > 0) {
2098
+ ids.push(row.id);
2099
+ ids.push(...collectAllTreeIds(children, childrenKey));
2100
+ }
2101
+ }
2102
+ return ids;
2103
+ }
2104
+ function computeSpanMap(columns, flatRows) {
2105
+ const map = /* @__PURE__ */ new Map();
2106
+ const hasOnCell = columns.some((c) => c.onCell);
2107
+ if (!hasOnCell) return map;
2108
+ flatRows.forEach((row, rowIndex) => {
2109
+ for (const col of columns) {
2110
+ if (col.onCell) {
2111
+ const span = col.onCell(row.data, rowIndex);
2112
+ if (span && (span.rowSpan !== void 0 || span.colSpan !== void 0)) {
2113
+ map.set(`${row.id}:${col.key}`, span);
2114
+ }
2115
+ }
2116
+ }
2117
+ });
2118
+ return map;
2119
+ }
2120
+ function computeHiddenCells(columns, flatRows, spanMap) {
2121
+ const hidden = /* @__PURE__ */ new Set();
2122
+ const hasOnCell = columns.some((c) => c.onCell);
2123
+ if (!hasOnCell) return hidden;
2124
+ flatRows.forEach((row, rowIndex) => {
2125
+ for (let ci = 0; ci < columns.length; ci++) {
2126
+ const key = `${row.id}:${columns[ci].key}`;
2127
+ const span = spanMap.get(key);
2128
+ if (!span) continue;
2129
+ if (span.rowSpan === 0 || span.colSpan === 0) {
2130
+ hidden.add(key);
2131
+ continue;
2132
+ }
2133
+ const rs = span.rowSpan ?? 1;
2134
+ const cs = span.colSpan ?? 1;
2135
+ for (let dr = 0; dr < rs; dr++) {
2136
+ for (let dc = 0; dc < cs; dc++) {
2137
+ if (dr === 0 && dc === 0) continue;
2138
+ const targetRow = flatRows[rowIndex + dr];
2139
+ const targetCol = columns[ci + dc];
2140
+ if (targetRow && targetCol) {
2141
+ hidden.add(`${targetRow.id}:${targetCol.key}`);
2142
+ }
2143
+ }
2144
+ }
2145
+ }
2146
+ });
2147
+ return hidden;
2148
+ }
2149
+ function getFixedStyle(col, fixedLeftOffsets, fixedRightOffsets, isHeader) {
2150
+ const s = {};
2151
+ if (col.align) s.textAlign = col.align;
2152
+ if (col.ellipsis) {
2153
+ s.overflow = "hidden";
2154
+ s.textOverflow = "ellipsis";
2155
+ s.whiteSpace = "nowrap";
2156
+ }
2157
+ if (col.fixed === "left") {
2158
+ s.position = "sticky";
2159
+ s.left = fixedLeftOffsets[col.key];
2160
+ s.zIndex = isHeader ? 4 : 2;
2161
+ }
2162
+ if (col.fixed === "right") {
2163
+ s.position = "sticky";
2164
+ s.right = fixedRightOffsets[col.key];
2165
+ s.zIndex = isHeader ? 4 : 2;
2166
+ }
2167
+ return s;
2168
+ }
2169
+ function computeAggregate(values, type) {
2170
+ const nums = values.map(Number).filter((n) => !isNaN(n));
2171
+ if (nums.length === 0) return type === "count" ? values.length : "-";
2172
+ switch (type) {
2173
+ case "sum":
2174
+ return Math.round(nums.reduce((a, b) => a + b, 0) * 100) / 100;
2175
+ case "avg":
2176
+ return Math.round(nums.reduce((a, b) => a + b, 0) / nums.length * 100) / 100;
2177
+ case "count":
2178
+ return values.length;
2179
+ case "min":
2180
+ return Math.min(...nums);
2181
+ case "max":
2182
+ return Math.max(...nums);
2183
+ }
2184
+ }
2185
+ function groupByColumn(flatRows, groupByKey, columns, expandedGroups) {
2186
+ const groups = /* @__PURE__ */ new Map();
2187
+ const groupOrder = [];
2188
+ for (const row of flatRows) {
2189
+ const val = String(row.data[groupByKey] ?? "Other");
2190
+ if (!groups.has(val)) {
2191
+ groups.set(val, []);
2192
+ groupOrder.push(val);
2193
+ }
2194
+ groups.get(val).push(row);
2195
+ }
2196
+ const result = [];
2197
+ for (const gKey of groupOrder) {
2198
+ const rows = groups.get(gKey);
2199
+ const expanded = expandedGroups.has(gKey);
2200
+ const aggregates = {};
2201
+ for (const col of columns) {
2202
+ if (col.aggregate) {
2203
+ const values = rows.map((r) => r.data[col.key]);
2204
+ if (typeof col.aggregate === "function") {
2205
+ aggregates[col.key] = col.aggregate(values);
2206
+ } else {
2207
+ aggregates[col.key] = computeAggregate(values, col.aggregate);
2208
+ }
2209
+ }
2210
+ }
2211
+ const groupRow = {
2212
+ __isGroup: true,
2213
+ groupKey: gKey,
2214
+ groupValue: gKey,
2215
+ count: rows.length,
2216
+ expanded,
2217
+ aggregates
2218
+ };
2219
+ result.push({
2220
+ id: `__group_${gKey}`,
2221
+ data: {},
2222
+ depth: 0,
2223
+ hasChildren: false,
2224
+ __groupRow: groupRow
2225
+ });
2226
+ if (expanded) {
2227
+ result.push(...rows);
2228
+ }
2229
+ }
2230
+ return result;
2231
+ }
2232
+ function separatePinnedRows(flatRows, topIds, bottomIds) {
2233
+ const topSet = new Set(topIds ?? []);
2234
+ const bottomSet = new Set(bottomIds ?? []);
2235
+ const top = [];
2236
+ const middle = [];
2237
+ const bottom = [];
2238
+ for (const row of flatRows) {
2239
+ if (topSet.has(row.id)) top.push(row);
2240
+ else if (bottomSet.has(row.id)) bottom.push(row);
2241
+ else middle.push(row);
2242
+ }
2243
+ if (topIds) {
2244
+ top.sort((a, b) => topIds.indexOf(a.id) - topIds.indexOf(b.id));
2245
+ }
2246
+ if (bottomIds) {
2247
+ bottom.sort((a, b) => bottomIds.indexOf(a.id) - bottomIds.indexOf(b.id));
2248
+ }
2249
+ return { top, middle, bottom };
2250
+ }
2251
+ function computeColumnAggregates(columns, flatRows) {
2252
+ const hasAny = columns.some((c) => c.aggregate);
2253
+ if (!hasAny) return null;
2254
+ const result = {};
2255
+ for (const col of columns) {
2256
+ if (!col.aggregate) continue;
2257
+ const values = flatRows.filter((r) => !r.__groupRow).map((r) => r.data[col.key]);
2258
+ if (typeof col.aggregate === "function") {
2259
+ result[col.key] = col.aggregate(values);
2260
+ } else {
2261
+ result[col.key] = computeAggregate(values, col.aggregate);
2262
+ }
2263
+ }
2264
+ return result;
2265
+ }
2266
+
2267
+ // src/components/complex/Table/defaultIcons.tsx
2268
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2269
+ var FilterIcon = /* @__PURE__ */ jsx9(
2270
+ "svg",
2271
+ {
2272
+ width: "12",
2273
+ height: "12",
2274
+ viewBox: "0 0 16 16",
2275
+ fill: "none",
2276
+ stroke: "currentColor",
2277
+ strokeWidth: "1.5",
2278
+ strokeLinejoin: "round",
2279
+ strokeLinecap: "round",
2280
+ "aria-hidden": "true",
2281
+ focusable: "false",
2282
+ children: /* @__PURE__ */ jsx9("path", { d: "M2 3h12l-5 6.5V14l-2 1V9.5L2 3z" })
2283
+ }
2284
+ );
2285
+ var SearchIcon = /* @__PURE__ */ jsxs9(
2286
+ "svg",
2287
+ {
2288
+ width: "12",
2289
+ height: "12",
2290
+ viewBox: "0 0 16 16",
2291
+ fill: "none",
2292
+ stroke: "currentColor",
2293
+ strokeWidth: "1.5",
2294
+ strokeLinecap: "round",
2295
+ "aria-hidden": "true",
2296
+ focusable: "false",
2297
+ children: [
2298
+ /* @__PURE__ */ jsx9("circle", { cx: "7", cy: "7", r: "4.5" }),
2299
+ /* @__PURE__ */ jsx9("path", { d: "M10.5 10.5L14 14" })
2300
+ ]
2301
+ }
2302
+ );
2303
+
2304
+ // src/components/complex/Table/types.ts
2305
+ var DEFAULT_LOCALE = {
2306
+ emptyText: "No data",
2307
+ filterReset: "Reset",
2308
+ filterConfirm: "OK",
2309
+ filterSearchPlaceholder: "Search filters...",
2310
+ filterEmptyText: "No filters",
2311
+ searchPlaceholder: "Search...",
2312
+ sortAsc: "\u25B2",
2313
+ sortDesc: "\u25BC",
2314
+ expandIcon: "\u203A",
2315
+ filterIcon: FilterIcon,
2316
+ searchIcon: SearchIcon,
2317
+ totalRows: (count) => `${count} rows`,
2318
+ selectedRows: (count) => `${count} selected`,
2319
+ exportCSV: "Export CSV",
2320
+ exportJSON: "Export JSON",
2321
+ copyTable: "Copy",
2322
+ pinLeft: "Pin Left",
2323
+ pinRight: "Pin Right",
2324
+ unpin: "Unpin",
2325
+ showColumns: "Show Columns",
2326
+ print: "Print",
2327
+ fullscreen: "Fullscreen",
2328
+ exitFullscreen: "Exit Fullscreen",
2329
+ density: "Density",
2330
+ densitySmall: "Compact",
2331
+ densityMiddle: "Default",
2332
+ densityLarge: "Comfortable",
2333
+ groupCollapse: "Collapse All",
2334
+ groupExpand: "Expand All",
2335
+ sum: "Sum",
2336
+ avg: "Avg",
2337
+ count: "Count",
2338
+ min: "Min",
2339
+ max: "Max",
2340
+ copiedToClipboard: "Copied!",
2341
+ rowNumber: "#",
2342
+ groupByLabel: "Group by",
2343
+ groupByNone: "No grouping",
2344
+ filterAdvancedValuePlaceholder: "Value",
2345
+ filterAdvancedInPlaceholder: "value1, value2, ...",
2346
+ filterAdvancedBetweenMaxPlaceholder: "Max",
2347
+ filterOpEq: "=",
2348
+ filterOpNeq: "!=",
2349
+ filterOpLt: "<",
2350
+ filterOpLte: "<=",
2351
+ filterOpGt: ">",
2352
+ filterOpGte: ">=",
2353
+ filterOpBetween: "between",
2354
+ filterOpIn: "in",
2355
+ filterOpNotIn: "not in",
2356
+ filterOpContains: "contains",
2357
+ filterOpStartsWith: "starts with",
2358
+ filterOpEndsWith: "ends with",
2359
+ filterOpIsEmpty: "is empty",
2360
+ filterOpIsNotEmpty: "is not empty"
2361
+ };
2362
+ var DEFAULT_COL_WIDTH = 150;
2363
+ var MIN_COL_WIDTH = 50;
2364
+ var SELECTION_COL_WIDTH = "40px";
2365
+
2366
+ // src/components/complex/Table/useTableState.ts
2367
+ function useTableState(props) {
2368
+ const {
2369
+ columns: columnsProp,
2370
+ rowSelection,
2371
+ expandable,
2372
+ tree,
2373
+ draggable: _draggable,
2374
+ searchable,
2375
+ locale,
2376
+ onColumnOrderChange,
2377
+ onCellEdit: _onCellEdit,
2378
+ multiSort,
2379
+ sorts: controlledSorts,
2380
+ onSortsChange,
2381
+ hiddenColumns: hiddenColumnsProp,
2382
+ onHiddenColumnsChange,
2383
+ onColumnPinChange,
2384
+ groupBy,
2385
+ defaultGroupExpanded,
2386
+ onGroupExpandChange,
2387
+ pinnedRows: pinnedRowsConfig,
2388
+ keyboardNavigation,
2389
+ scroll,
2390
+ footer: _footer,
2391
+ showRowNumber: _showRowNumber,
2392
+ fullscreenable: _fullscreenable,
2393
+ densityToggle: _densityToggle,
2394
+ emptyContent: _emptyContent,
2395
+ printable: _printable,
2396
+ exportJSON: _exportJSON,
2397
+ highlightOnHover: _highlightOnHover,
2398
+ columnAutoResize: _columnAutoResize,
2399
+ ...tableOptions
2400
+ } = props;
2401
+ const hasScrollX = scroll?.x != null;
2402
+ const {
2403
+ table,
2404
+ visibleRows,
2405
+ tableState,
2406
+ columnWidths: persistedColWidths,
2407
+ setSort,
2408
+ setSorts,
2409
+ clearSort,
2410
+ setPage,
2411
+ setFilterFn,
2412
+ setColumnWidth: persistColumnWidth,
2413
+ refresh
2414
+ } = useTable(tableOptions);
2415
+ const t = useMemo3(() => ({ ...DEFAULT_LOCALE, ...locale }), [locale]);
2416
+ const hasColumnGroups = columnsProp.some((c) => c.children && c.children.length > 0);
2417
+ const leafColumns = useMemo3(() => flattenLeafColumns(columnsProp), [columnsProp]);
2418
+ const headerRows = useMemo3(
2419
+ () => hasColumnGroups ? buildHeaderRows(columnsProp) : null,
2420
+ [columnsProp, hasColumnGroups]
2421
+ );
2422
+ const [internalSorts, setInternalSorts] = useState7([]);
2423
+ const sorts = controlledSorts ?? internalSorts;
2424
+ const [colWidths, setColWidths] = useState7(() => {
2425
+ const w = {};
2426
+ for (const c of leafColumns) {
2427
+ w[c.key] = persistedColWidths[c.key] ?? c.width ?? DEFAULT_COL_WIDTH;
2428
+ }
2429
+ return w;
2430
+ });
2431
+ const [userResizedCols, setUserResizedCols] = useState7(
2432
+ () => new Set(Object.keys(persistedColWidths))
2433
+ );
2434
+ const [colOrder, setColOrder] = useState7(() => leafColumns.map((c) => c.key));
2435
+ const [dragCol, setDragCol] = useState7(null);
2436
+ const [dragOver, setDragOver] = useState7(null);
2437
+ const [internalHidden, setInternalHidden] = useState7(
2438
+ () => new Set(hiddenColumnsProp ?? [])
2439
+ );
2440
+ const hiddenSet = useMemo3(
2441
+ () => hiddenColumnsProp ? new Set(hiddenColumnsProp) : internalHidden,
2442
+ [hiddenColumnsProp, internalHidden]
2443
+ );
2444
+ const toggleColumnVisibility = useCallback3(
2445
+ (key) => {
2446
+ if (onHiddenColumnsChange) {
2447
+ const current = hiddenColumnsProp ?? [];
2448
+ const next = current.includes(key) ? current.filter((k) => k !== key) : [...current, key];
2449
+ onHiddenColumnsChange(next);
2450
+ } else {
2451
+ setInternalHidden((prev) => {
2452
+ const next = new Set(prev);
2453
+ if (next.has(key)) {
2454
+ next.delete(key);
2455
+ } else {
2456
+ next.add(key);
2457
+ }
2458
+ return next;
2459
+ });
2460
+ }
2461
+ },
2462
+ [hiddenColumnsProp, onHiddenColumnsChange]
2463
+ );
2464
+ const [internalExpanded, setInternalExpanded] = useState7(() => {
2465
+ if (tree?.defaultExpandAllRows) {
2466
+ const childrenKey2 = tree.childrenColumnName ?? "children";
2467
+ return new Set(collectAllTreeIds(props.data ?? [], childrenKey2));
2468
+ }
2469
+ return new Set(expandable?.defaultExpandedRowKeys ?? []);
2470
+ });
2471
+ const expandedKeys = expandable?.expandedKeys ? new Set(expandable.expandedKeys) : internalExpanded;
2472
+ const [treeExpanded, setTreeExpanded] = useState7(() => {
2473
+ if (tree?.defaultExpandAllRows) {
2474
+ const childrenKey2 = tree.childrenColumnName ?? "children";
2475
+ return new Set(collectAllTreeIds(props.data ?? [], childrenKey2));
2476
+ }
2477
+ return /* @__PURE__ */ new Set();
2478
+ });
2479
+ const [searchText, setSearchText] = useState7("");
2480
+ const [activeFilters, setActiveFilters] = useState7(() => {
2481
+ const init = {};
2482
+ for (const col of leafColumns) {
2483
+ if (col.filteredValue) {
2484
+ init[col.key] = [...col.filteredValue];
2485
+ } else if (col.defaultFilteredValue) {
2486
+ init[col.key] = [...col.defaultFilteredValue];
2487
+ }
2488
+ }
2489
+ return init;
2490
+ });
2491
+ const [filterSearchText, setFilterSearchText] = useState7({});
2492
+ const [openFilterDropdown, setOpenFilterDropdown] = useState7(null);
2493
+ const [advancedFilters, setAdvancedFiltersState] = useState7({});
2494
+ const filterRef = useRef6(null);
2495
+ const [filterAnchor, setFilterAnchor] = useState7(null);
2496
+ const setAdvancedFilter = useCallback3(
2497
+ (colKey, filter) => {
2498
+ setAdvancedFiltersState((prev) => {
2499
+ const next = { ...prev };
2500
+ if (filter == null) delete next[colKey];
2501
+ else next[colKey] = filter;
2502
+ return next;
2503
+ });
2504
+ table.setColumnFilter(colKey, filter);
2505
+ refresh();
2506
+ },
2507
+ [table, refresh]
2508
+ );
2509
+ const resizeRef = useRef6(null);
2510
+ const [contextMenu, setContextMenu] = useState7(null);
2511
+ const columns = useMemo3(() => {
2512
+ const base = hasColumnGroups ? leafColumns : (() => {
2513
+ const map = new Map(leafColumns.map((c) => [c.key, c]));
2514
+ return colOrder.filter((k) => map.has(k)).map((k) => map.get(k));
2515
+ })();
2516
+ return base.filter((c) => !hiddenSet.has(c.key) && !c.hidden);
2517
+ }, [leafColumns, colOrder, hasColumnGroups, hiddenSet]);
2518
+ useEffect6(() => {
2519
+ const keys = leafColumns.map((c) => c.key);
2520
+ setColOrder((prev) => {
2521
+ const prevSet = new Set(prev);
2522
+ const added = keys.filter((k) => !prevSet.has(k));
2523
+ const valid = prev.filter((k) => keys.includes(k));
2524
+ return [...valid, ...added];
2525
+ });
2526
+ }, [leafColumns]);
2527
+ useEffect6(() => {
2528
+ for (const col of leafColumns) {
2529
+ if (col.filteredValue !== void 0) {
2530
+ setActiveFilters((prev) => {
2531
+ if (JSON.stringify(prev[col.key]) === JSON.stringify(col.filteredValue)) return prev;
2532
+ return { ...prev, [col.key]: [...col.filteredValue] };
2533
+ });
2534
+ }
2535
+ }
2536
+ }, [leafColumns]);
2537
+ useEffect6(() => {
2538
+ const colFilters = columns.filter((c) => c.onFilter && activeFilters[c.key]?.length);
2539
+ const hasSearch = searchable && searchText.length > 0;
2540
+ if (colFilters.length === 0 && !hasSearch) {
2541
+ setFilterFn(null);
2542
+ return;
2543
+ }
2544
+ const lower = hasSearch ? searchText.toLowerCase() : "";
2545
+ setFilterFn((row) => {
2546
+ for (const col of colFilters) {
2547
+ const vals = activeFilters[col.key];
2548
+ const fn = col.onFilter;
2549
+ if (!vals.some((v) => fn(v, row))) return false;
2550
+ }
2551
+ if (hasSearch) {
2552
+ const match = Object.values(row).some(
2553
+ (v) => v != null && String(v).toLowerCase().includes(lower)
2554
+ );
2555
+ if (!match) return false;
2556
+ }
2557
+ return true;
2558
+ });
2559
+ }, [activeFilters, searchText, searchable, setFilterFn, columns]);
2560
+ useEffect6(() => {
2561
+ if (!openFilterDropdown) return;
2562
+ const handle = (e) => {
2563
+ if (filterRef.current && !filterRef.current.contains(e.target)) {
2564
+ setOpenFilterDropdown(null);
2565
+ }
2566
+ };
2567
+ document.addEventListener("mousedown", handle);
2568
+ return () => document.removeEventListener("mousedown", handle);
2569
+ }, [openFilterDropdown]);
2570
+ const handleSort = useCallback3(
2571
+ (col, shiftKey) => {
2572
+ if (!col.sortable) return;
2573
+ if (multiSort && shiftKey) {
2574
+ const existing = sorts.find((s) => s.column === col.key);
2575
+ let next;
2576
+ if (!existing) {
2577
+ next = [...sorts, { column: col.key, direction: "asc" }];
2578
+ } else if (existing.direction === "asc") {
2579
+ next = sorts.map(
2580
+ (s) => s.column === col.key ? { ...s, direction: "desc" } : s
2581
+ );
2582
+ } else {
2583
+ next = sorts.filter((s) => s.column !== col.key);
2584
+ }
2585
+ if (onSortsChange) {
2586
+ onSortsChange(next);
2587
+ } else {
2588
+ setInternalSorts(next);
2589
+ }
2590
+ if (next.length === 0) {
2591
+ clearSort();
2592
+ } else {
2593
+ setSorts(next);
2594
+ }
2595
+ } else {
2596
+ const current = sorts.length === 1 ? sorts[0] : null;
2597
+ let next;
2598
+ if (!current || current.column !== col.key) {
2599
+ next = [{ column: col.key, direction: "asc" }];
2600
+ } else if (current.direction === "asc") {
2601
+ next = [{ column: col.key, direction: "desc" }];
2602
+ } else {
2603
+ next = [];
2604
+ }
2605
+ if (onSortsChange) {
2606
+ onSortsChange(next);
2607
+ } else {
2608
+ setInternalSorts(next);
2609
+ }
2610
+ if (next.length === 0) {
2611
+ clearSort();
2612
+ } else {
2613
+ setSort(next[0].column, next[0].direction);
2614
+ }
2615
+ }
2616
+ },
2617
+ [multiSort, sorts, onSortsChange, setSort, setSorts, clearSort]
2618
+ );
2619
+ const getSortIndex = useCallback3(
2620
+ (colKey) => {
2621
+ if (!multiSort || sorts.length <= 1) return -1;
2622
+ return sorts.findIndex((s) => s.column === colKey);
2623
+ },
2624
+ [multiSort, sorts]
2625
+ );
2626
+ const getSortDirection = useCallback3(
2627
+ (colKey) => {
2628
+ const found = sorts.find((s) => s.column === colKey);
2629
+ return found?.direction ?? null;
2630
+ },
2631
+ [sorts]
2632
+ );
2633
+ const handleToggleExpand = (id) => {
2634
+ const open = expandedKeys.has(id);
2635
+ if (expandable?.onExpand) {
2636
+ expandable.onExpand(!open, id);
2637
+ } else {
2638
+ setInternalExpanded((prev) => {
2639
+ const next = new Set(prev);
2640
+ if (open) {
2641
+ next.delete(id);
2642
+ } else {
2643
+ next.add(id);
2644
+ }
2645
+ return next;
2646
+ });
2647
+ }
2648
+ };
2649
+ const handleToggleTreeExpand = (id) => {
2650
+ setTreeExpanded((prev) => {
2651
+ const next = new Set(prev);
2652
+ if (next.has(id)) {
2653
+ next.delete(id);
2654
+ } else {
2655
+ next.add(id);
2656
+ }
2657
+ return next;
2658
+ });
2659
+ };
2660
+ const handleSelectRow = (id, row) => {
2661
+ if (!rowSelection) return;
2662
+ const { selectedKeys, onChange, type = "checkbox" } = rowSelection;
2663
+ if (type === "radio") {
2664
+ onChange([id], [row]);
2665
+ return;
2666
+ }
2667
+ const next = selectedKeys.includes(id) ? selectedKeys.filter((k) => k !== id) : [...selectedKeys, id];
2668
+ onChange(
2669
+ next,
2670
+ visibleRows.filter((r) => next.includes(r.id)).map((r) => r.data)
2671
+ );
2672
+ };
2673
+ const handleSelectAll = () => {
2674
+ if (!rowSelection) return;
2675
+ const ids = visibleRows.map((r) => r.id);
2676
+ const all = ids.every((id) => rowSelection.selectedKeys.includes(id));
2677
+ rowSelection.onChange(all ? [] : ids, all ? [] : visibleRows.map((r) => r.data));
2678
+ };
2679
+ const handleFilterToggle = (colKey, value, multiple) => {
2680
+ setActiveFilters((prev) => {
2681
+ const current = prev[colKey] ?? [];
2682
+ if (!multiple) return { ...prev, [colKey]: [value] };
2683
+ const idx = current.indexOf(value);
2684
+ const next = idx >= 0 ? current.filter((_, i) => i !== idx) : [...current, value];
2685
+ return { ...prev, [colKey]: next };
2686
+ });
2687
+ };
2688
+ const handleFilterConfirm = (colKey) => {
2689
+ setOpenFilterDropdown(null);
2690
+ setFilterSearchText((prev) => ({ ...prev, [colKey]: "" }));
2691
+ };
2692
+ const handleFilterReset = (colKey) => {
2693
+ setActiveFilters((prev) => ({ ...prev, [colKey]: [] }));
2694
+ setFilterSearchText((prev) => ({ ...prev, [colKey]: "" }));
2695
+ };
2696
+ const handleFilterClose = (colKey) => {
2697
+ setOpenFilterDropdown(null);
2698
+ setFilterSearchText((prev) => ({ ...prev, [colKey]: "" }));
2699
+ };
2700
+ const handleSearchFilterChange = (colKey, text) => {
2701
+ setActiveFilters((prev) => ({ ...prev, [colKey]: text ? [text] : [] }));
2702
+ };
2703
+ const getFilterDropdownProps = (col) => ({
2704
+ selectedKeys: activeFilters[col.key] ?? [],
2705
+ setSelectedKeys: (keys) => setActiveFilters((prev) => ({ ...prev, [col.key]: keys })),
2706
+ confirm: () => handleFilterConfirm(col.key),
2707
+ clearFilters: () => handleFilterReset(col.key),
2708
+ close: () => handleFilterClose(col.key)
2709
+ });
2710
+ const handleResizeStart = (e, colKey) => {
2711
+ e.preventDefault();
2712
+ e.stopPropagation();
2713
+ resizeRef.current = {
2714
+ col: colKey,
2715
+ startX: e.clientX,
2716
+ startW: colWidths[colKey] ?? DEFAULT_COL_WIDTH
2717
+ };
2718
+ const onMove = (ev) => {
2719
+ if (!resizeRef.current) return;
2720
+ const diff = ev.clientX - resizeRef.current.startX;
2721
+ const min = leafColumns.find((c) => c.key === resizeRef.current.col)?.minWidth ?? MIN_COL_WIDTH;
2722
+ setColWidths((prev) => ({
2723
+ ...prev,
2724
+ [resizeRef.current.col]: Math.max(min, resizeRef.current.startW + diff)
2725
+ }));
2726
+ setUserResizedCols(
2727
+ (prev) => prev.has(resizeRef.current.col) ? prev : new Set(prev).add(resizeRef.current.col)
2728
+ );
2729
+ };
2730
+ const onUp = () => {
2731
+ const ref = resizeRef.current;
2732
+ resizeRef.current = null;
2733
+ document.removeEventListener("mousemove", onMove);
2734
+ document.removeEventListener("mouseup", onUp);
2735
+ if (!ref) return;
2736
+ setColWidths((prev) => {
2737
+ const w = prev[ref.col];
2738
+ if (typeof w === "number") persistColumnWidth(ref.col, w);
2739
+ return prev;
2740
+ });
2741
+ };
2742
+ document.addEventListener("mousemove", onMove);
2743
+ document.addEventListener("mouseup", onUp);
2744
+ };
2745
+ const handleDragStart = (e, key) => {
2746
+ setDragCol(key);
2747
+ e.dataTransfer.effectAllowed = "move";
2748
+ e.dataTransfer.setData("text/plain", key);
2749
+ };
2750
+ const handleDragOver = (e, key) => {
2751
+ e.preventDefault();
2752
+ e.dataTransfer.dropEffect = "move";
2753
+ setDragOver(key);
2754
+ };
2755
+ const handleDrop = (e, targetKey) => {
2756
+ e.preventDefault();
2757
+ if (!dragCol || dragCol === targetKey) {
2758
+ setDragCol(null);
2759
+ setDragOver(null);
2760
+ return;
2761
+ }
2762
+ setColOrder((prev) => {
2763
+ const next = prev.filter((k) => k !== dragCol);
2764
+ const idx = next.indexOf(targetKey);
2765
+ next.splice(idx, 0, dragCol);
2766
+ onColumnOrderChange?.(next);
2767
+ return next;
2768
+ });
2769
+ setDragCol(null);
2770
+ setDragOver(null);
2771
+ };
2772
+ const handleDragEnd = () => {
2773
+ setDragCol(null);
2774
+ setDragOver(null);
2775
+ };
2776
+ const handlePinColumn = useCallback3(
2777
+ (key, fixed) => {
2778
+ onColumnPinChange?.(key, fixed);
2779
+ },
2780
+ [onColumnPinChange]
2781
+ );
2782
+ const [isFullscreen, setIsFullscreen] = useState7(false);
2783
+ const wrapperRef = useRef6(null);
2784
+ const toggleFullscreen = useCallback3(() => {
2785
+ setIsFullscreen((prev) => !prev);
2786
+ }, []);
2787
+ const [density, setDensity] = useState7("middle");
2788
+ useEffect6(() => {
2789
+ if (!groupBy) {
2790
+ table.clearGroupBy();
2791
+ return;
2792
+ }
2793
+ const aggregates2 = leafColumns.filter((c) => c.aggregate).map((c) => ({
2794
+ column: c.key,
2795
+ type: typeof c.aggregate === "function" ? c.aggregate : c.aggregate
2796
+ }));
2797
+ table.groupBy(groupBy, aggregates2);
2798
+ }, [groupBy, leafColumns, table]);
2799
+ const [expandedGroups, setExpandedGroups] = useState7(() => {
2800
+ return defaultGroupExpanded ? /* @__PURE__ */ new Set(["__all__"]) : /* @__PURE__ */ new Set();
2801
+ });
2802
+ const toggleGroupExpand = useCallback3(
2803
+ (groupKey) => {
2804
+ setExpandedGroups((prev) => {
2805
+ const next = new Set(prev);
2806
+ const wasExpanded = next.has(groupKey);
2807
+ if (wasExpanded) {
2808
+ next.delete(groupKey);
2809
+ } else {
2810
+ next.add(groupKey);
2811
+ }
2812
+ onGroupExpandChange?.(groupKey, !wasExpanded);
2813
+ return next;
2814
+ });
2815
+ },
2816
+ [onGroupExpandChange]
2817
+ );
2818
+ const expandAllGroups = useCallback3(() => {
2819
+ setExpandedGroups((prev) => {
2820
+ const next = new Set(prev);
2821
+ next.add("__all__");
2822
+ return next;
2823
+ });
2824
+ }, []);
2825
+ const collapseAllGroups = useCallback3(() => {
2826
+ setExpandedGroups(/* @__PURE__ */ new Set());
2827
+ }, []);
2828
+ const [focusedCell, setFocusedCell] = useState7(null);
2829
+ const handleKeyNav = useCallback3(
2830
+ (e) => {
2831
+ if (!keyboardNavigation || !focusedCell) return;
2832
+ const maxRow = visibleRows.length - 1;
2833
+ const maxCol = columns.length - 1;
2834
+ let { row, col } = focusedCell;
2835
+ switch (e.key) {
2836
+ case "ArrowUp":
2837
+ e.preventDefault();
2838
+ row = Math.max(0, row - 1);
2839
+ break;
2840
+ case "ArrowDown":
2841
+ e.preventDefault();
2842
+ row = Math.min(maxRow, row + 1);
2843
+ break;
2844
+ case "ArrowLeft":
2845
+ e.preventDefault();
2846
+ col = Math.max(0, col - 1);
2847
+ break;
2848
+ case "ArrowRight":
2849
+ e.preventDefault();
2850
+ col = Math.min(maxCol, col + 1);
2851
+ break;
2852
+ case "Tab":
2853
+ e.preventDefault();
2854
+ if (e.shiftKey) {
2855
+ col -= 1;
2856
+ if (col < 0) {
2857
+ col = maxCol;
2858
+ row = Math.max(0, row - 1);
2859
+ }
2860
+ } else {
2861
+ col += 1;
2862
+ if (col > maxCol) {
2863
+ col = 0;
2864
+ row = Math.min(maxRow, row + 1);
2865
+ }
2866
+ }
2867
+ break;
2868
+ case "Home":
2869
+ e.preventDefault();
2870
+ col = 0;
2871
+ if (e.ctrlKey) row = 0;
2872
+ break;
2873
+ case "End":
2874
+ e.preventDefault();
2875
+ col = maxCol;
2876
+ if (e.ctrlKey) row = maxRow;
2877
+ break;
2878
+ default:
2879
+ return;
2880
+ }
2881
+ setFocusedCell({ row, col });
2882
+ },
2883
+ [keyboardNavigation, focusedCell, visibleRows.length, columns.length]
2884
+ );
2885
+ const handleAutoResize = useCallback3(
2886
+ (colKey) => {
2887
+ const maxLen = visibleRows.reduce((max, row) => {
2888
+ const val = String(row.data[colKey] ?? "");
2889
+ return Math.max(max, val.length);
2890
+ }, 0);
2891
+ const headerCol = leafColumns.find((c) => c.key === colKey);
2892
+ const headerLen = typeof headerCol?.title === "string" ? headerCol.title.length : 6;
2893
+ const charWidth = 8;
2894
+ const padding = 32;
2895
+ const min = headerCol?.minWidth ?? MIN_COL_WIDTH;
2896
+ const optimal = Math.max(min, Math.max(maxLen, headerLen) * charWidth + padding);
2897
+ setColWidths((prev) => ({ ...prev, [colKey]: optimal }));
2898
+ setUserResizedCols((prev) => prev.has(colKey) ? prev : new Set(prev).add(colKey));
2899
+ },
2900
+ [visibleRows, leafColumns]
2901
+ );
2902
+ const isTreeMode = !!tree;
2903
+ const childrenKey = tree?.childrenColumnName ?? "children";
2904
+ const indentSize = tree?.indentSize ?? 20;
2905
+ const baseFlatRows = useMemo3(() => {
2906
+ if (!isTreeMode) {
2907
+ return visibleRows.map((r) => ({
2908
+ id: r.id,
2909
+ data: r.data,
2910
+ depth: 0,
2911
+ hasChildren: false
2912
+ }));
2913
+ }
2914
+ return flattenTreeRows(visibleRows, childrenKey, treeExpanded);
2915
+ }, [visibleRows, isTreeMode, childrenKey, treeExpanded]);
2916
+ const groupedRows = useMemo3(() => {
2917
+ if (!groupBy) return baseFlatRows;
2918
+ const allExpanded = expandedGroups.has("__all__");
2919
+ const effectiveExpanded = allExpanded ? /* @__PURE__ */ new Set([...expandedGroups, ...baseFlatRows.map((r) => String(r.data[groupBy] ?? "Other"))]) : expandedGroups;
2920
+ return groupByColumn(baseFlatRows, groupBy, columns, effectiveExpanded);
2921
+ }, [baseFlatRows, groupBy, columns, expandedGroups]);
2922
+ const {
2923
+ top: pinnedTop,
2924
+ middle: middleRows,
2925
+ bottom: pinnedBottom
2926
+ } = useMemo3(
2927
+ () => separatePinnedRows(groupedRows, pinnedRowsConfig?.top, pinnedRowsConfig?.bottom),
2928
+ [groupedRows, pinnedRowsConfig]
2929
+ );
2930
+ const flatRows = groupedRows;
2931
+ const aggregates = useMemo3(
2932
+ () => computeColumnAggregates(columns, baseFlatRows),
2933
+ [columns, baseFlatRows]
2934
+ );
2935
+ const extraBefore = [];
2936
+ if (rowSelection) extraBefore.push(SELECTION_COL_WIDTH);
2937
+ if (expandable) extraBefore.push(SELECTION_COL_WIDTH);
2938
+ const hasAutoColumns = columns.some(
2939
+ (c) => !c.fixed && !userResizedCols.has(c.key) && c.width == null
2940
+ );
2941
+ const shouldFlexExplicitWidths = !hasScrollX && !hasAutoColumns;
2942
+ const scrollXMinWidth = typeof scroll?.x === "number" ? scroll.x : typeof scroll?.x === "string" && scroll.x.trim().endsWith("px") ? Number.parseFloat(scroll.x) : null;
2943
+ const declaredGridWidth = extraBefore.reduce((sum, w) => sum + Number.parseFloat(w), 0) + columns.reduce((sum, c) => sum + (colWidths[c.key] ?? c.width ?? DEFAULT_COL_WIDTH), 0);
2944
+ const shouldFillScrollX = hasScrollX && scrollXMinWidth !== null && Number.isFinite(scrollXMinWidth) && scrollXMinWidth > declaredGridWidth;
2945
+ const colTemplates = columns.map((c) => {
2946
+ const resized = userResizedCols.has(c.key);
2947
+ const w = colWidths[c.key] ?? c.width ?? DEFAULT_COL_WIDTH;
2948
+ if (hasScrollX) {
2949
+ if (c.fixed || resized) {
2950
+ return `${w}px`;
2951
+ }
2952
+ return shouldFillScrollX ? `minmax(${w}px, ${w}fr)` : `${w}px`;
2953
+ }
2954
+ if (c.fixed || resized) {
2955
+ return `${w}px`;
2956
+ }
2957
+ if (c.width == null) {
2958
+ return `minmax(${MIN_COL_WIDTH}px, 1fr)`;
2959
+ }
2960
+ if (shouldFlexExplicitWidths) {
2961
+ return `minmax(${MIN_COL_WIDTH}px, ${w}fr)`;
2962
+ }
2963
+ return `minmax(${MIN_COL_WIDTH}px, ${w}px)`;
2964
+ });
2965
+ const gridTemplate = [...extraBefore, ...colTemplates].join(" ");
2966
+ const totalCols = extraBefore.length + columns.length;
2967
+ const fixedLeftOffsets = useMemo3(() => {
2968
+ const o = {};
2969
+ const selW = parseInt(SELECTION_COL_WIDTH);
2970
+ let left = (rowSelection ? selW : 0) + (expandable ? selW : 0);
2971
+ for (const col of columns) {
2972
+ if (col.fixed === "left") {
2973
+ o[col.key] = left;
2974
+ left += colWidths[col.key] ?? col.width ?? DEFAULT_COL_WIDTH;
2975
+ }
2976
+ }
2977
+ return o;
2978
+ }, [columns, colWidths, rowSelection, expandable]);
2979
+ const fixedRightOffsets = useMemo3(() => {
2980
+ const o = {};
2981
+ let right = 0;
2982
+ for (let i = columns.length - 1; i >= 0; i--) {
2983
+ if (columns[i].fixed === "right") {
2984
+ o[columns[i].key] = right;
2985
+ right += colWidths[columns[i].key] ?? columns[i].width ?? DEFAULT_COL_WIDTH;
2986
+ }
2987
+ }
2988
+ return o;
2989
+ }, [columns, colWidths]);
2990
+ const fixedStyle = (col, isHeader) => getFixedStyle(col, fixedLeftOffsets, fixedRightOffsets, isHeader);
2991
+ const allSelected = rowSelection ? visibleRows.length > 0 && visibleRows.every((r) => rowSelection.selectedKeys.includes(r.id)) : false;
2992
+ const someSelected = rowSelection ? visibleRows.some((r) => rowSelection.selectedKeys.includes(r.id)) && !allSelected : false;
2993
+ const spanMap = useMemo3(() => computeSpanMap(columns, flatRows), [columns, flatRows]);
2994
+ const hiddenCells = useMemo3(
2995
+ () => computeHiddenCells(columns, flatRows, spanMap),
2996
+ [columns, flatRows, spanMap]
2997
+ );
2998
+ return {
2999
+ visibleRows,
3000
+ tableState,
3001
+ columns,
3002
+ leafColumns,
3003
+ flatRows,
3004
+ headerRows,
3005
+ hasColumnGroups,
3006
+ t,
3007
+ // Multi-sort
3008
+ sorts,
3009
+ getSortIndex,
3010
+ getSortDirection,
3011
+ handleSort,
3012
+ // Selection
3013
+ allSelected,
3014
+ someSelected,
3015
+ handleSelectAll,
3016
+ handleSelectRow,
3017
+ // Expand
3018
+ expandedKeys,
3019
+ handleToggleExpand,
3020
+ // Tree
3021
+ isTreeMode,
3022
+ indentSize,
3023
+ treeExpanded,
3024
+ handleToggleTreeExpand,
3025
+ // Filters
3026
+ filterState: {
3027
+ activeFilters,
3028
+ filterSearchText,
3029
+ openFilterDropdown,
3030
+ advancedFilters,
3031
+ filterAnchor
3032
+ },
3033
+ filterActions: {
3034
+ setOpenFilterDropdown,
3035
+ handleFilterToggle,
3036
+ handleFilterConfirm,
3037
+ handleFilterReset,
3038
+ handleFilterClose,
3039
+ handleSearchFilterChange,
3040
+ setFilterSearchText,
3041
+ getFilterDropdownProps,
3042
+ setAdvancedFilter,
3043
+ setFilterAnchor
3044
+ },
3045
+ filterRef,
3046
+ // Drag columns
3047
+ dragOver,
3048
+ handleDragStart,
3049
+ handleDragOver,
3050
+ handleDragEnd,
3051
+ handleDrop,
3052
+ setDragOver: () => setDragOver(null),
3053
+ // Resize
3054
+ colWidths,
3055
+ handleResizeStart,
3056
+ // Grid
3057
+ gridTemplate,
3058
+ totalCols,
3059
+ // Fixed
3060
+ fixedStyle,
3061
+ // Span
3062
+ spanMap,
3063
+ hiddenCells,
3064
+ // Search
3065
+ searchText,
3066
+ setSearchText,
3067
+ // Pagination
3068
+ setPage,
3069
+ // Column visibility
3070
+ hiddenSet,
3071
+ toggleColumnVisibility,
3072
+ // Column pin
3073
+ handlePinColumn,
3074
+ // Context menu
3075
+ contextMenu,
3076
+ setContextMenu,
3077
+ // Fullscreen
3078
+ isFullscreen,
3079
+ toggleFullscreen,
3080
+ wrapperRef,
3081
+ // Density
3082
+ density,
3083
+ setDensity,
3084
+ // Group by
3085
+ expandedGroups,
3086
+ toggleGroupExpand,
3087
+ expandAllGroups,
3088
+ collapseAllGroups,
3089
+ // Keyboard
3090
+ focusedCell,
3091
+ setFocusedCell,
3092
+ handleKeyNav,
3093
+ // Auto-resize
3094
+ handleAutoResize,
3095
+ // Aggregation
3096
+ aggregates,
3097
+ // Pinned
3098
+ pinnedTop,
3099
+ pinnedBottom,
3100
+ middleRows
3101
+ };
3102
+ }
3103
+
3104
+ // src/types/slots.ts
3105
+ function slotClass(...parts) {
3106
+ const filtered = parts.filter((p) => typeof p === "string" && p.length > 0);
3107
+ return filtered.length ? filtered.join(" ") : void 0;
3108
+ }
3109
+ function slotStyle(base, custom) {
3110
+ if (!base && !custom) return void 0;
3111
+ if (!base) return custom;
3112
+ if (!custom) return base;
3113
+ return { ...base, ...custom };
3114
+ }
3115
+
3116
+ // src/components/complex/Table/Table.tsx
3117
+ import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3118
+ var DRAG_HANDLE_WIDTH = "32px";
3119
+ var ROW_NUMBER_WIDTH = "48px";
3120
+ function TableInner(props, forwardedRef) {
3121
+ const {
3122
+ rowSelection,
3123
+ expandable,
3124
+ sticky,
3125
+ draggable,
3126
+ searchable,
3127
+ showPagination = true,
3128
+ bordered: borderedProp,
3129
+ size: sizeProp,
3130
+ scroll,
3131
+ summary,
3132
+ onRowClick,
3133
+ onCellEdit,
3134
+ loading: loadingProp,
3135
+ rowClassName,
3136
+ className,
3137
+ style,
3138
+ unstyled,
3139
+ rowDraggable,
3140
+ onRowOrderChange,
3141
+ headerContextMenu,
3142
+ cellContextMenu,
3143
+ columnVisibility,
3144
+ virtual: virtualProp,
3145
+ exportable,
3146
+ onExport,
3147
+ selectionSummary,
3148
+ selectionActions,
3149
+ striped,
3150
+ showSizeChanger,
3151
+ pageSizeOptions,
3152
+ showQuickJumper,
3153
+ showTotal,
3154
+ onPageSizeChange,
3155
+ showRowNumber,
3156
+ footer,
3157
+ fullscreenable,
3158
+ densityToggle,
3159
+ groupBy,
3160
+ groupByOptions,
3161
+ onGroupByChange,
3162
+ keyboardNavigation,
3163
+ emptyContent,
3164
+ printable,
3165
+ exportJSON,
3166
+ highlightOnHover,
3167
+ columnAutoResize,
3168
+ columnPinning,
3169
+ classNames: slotCls,
3170
+ styles: slotSty
3171
+ } = props;
3172
+ const sCls = slotCls ?? {};
3173
+ const sSty = slotSty ?? {};
3174
+ const config = useConfig();
3175
+ const sizeFromProp = sizeProp ?? config.size ?? "middle";
3176
+ const bordered = borderedProp ?? config.bordered ?? false;
3177
+ const loading = loadingProp ?? false;
3178
+ const state = useTableState(props);
3179
+ const tableRef = useRef7(null);
3180
+ const scrollRef = useRef7(null);
3181
+ const effectiveSize = densityToggle ? state.density : sizeFromProp;
3182
+ const rowNumConfig = useMemo4(
3183
+ () => showRowNumber ? typeof showRowNumber === "object" ? showRowNumber : {} : null,
3184
+ [showRowNumber]
3185
+ );
3186
+ const getExportData = useCallback4(() => {
3187
+ const cols = state.columns.map((c) => c.key);
3188
+ const headers = state.columns.map((c) => typeof c.title === "string" ? c.title : c.key);
3189
+ const rows = state.flatRows.filter((r) => !r.__groupRow).map(
3190
+ (row) => cols.map((k) => {
3191
+ const v = row.data[k];
3192
+ return v == null ? "" : String(v);
3193
+ })
3194
+ );
3195
+ return { cols, headers, rows };
3196
+ }, [state.columns, state.flatRows]);
3197
+ const handleExportCSV = useCallback4(() => {
3198
+ const { headers, rows } = getExportData();
3199
+ const data = [headers, ...rows];
3200
+ if (onExport) {
3201
+ onExport(data);
3202
+ } else {
3203
+ downloadCSV(toCSVString(data));
3204
+ }
3205
+ }, [getExportData, onExport]);
3206
+ const handleExportJSON = useCallback4(() => {
3207
+ const { headers, rows } = getExportData();
3208
+ downloadJSON(toJSONString(headers, rows));
3209
+ }, [getExportData]);
3210
+ const handleCopy = useCallback4(async () => {
3211
+ const { headers, rows } = getExportData();
3212
+ await copyToClipboard(toTSVString([headers, ...rows]));
3213
+ }, [getExportData]);
3214
+ const printOptionsFromProp = useMemo4(() => {
3215
+ if (typeof printable === "object" && printable !== null) {
3216
+ return { fileName: printable.fileName };
3217
+ }
3218
+ return {};
3219
+ }, [printable]);
3220
+ const doPrint = useCallback4(
3221
+ (opts) => {
3222
+ printElement(tableRef.current, { ...printOptionsFromProp, ...opts ?? {} });
3223
+ },
3224
+ [printOptionsFromProp]
3225
+ );
3226
+ const handlePrint = useCallback4(() => {
3227
+ doPrint();
3228
+ }, [doPrint]);
3229
+ useImperativeHandle(forwardedRef, () => ({ print: doPrint }), [doPrint]);
3230
+ const gridTemplate = useMemo4(() => {
3231
+ const parts = [];
3232
+ if (rowDraggable) parts.push(DRAG_HANDLE_WIDTH);
3233
+ if (rowNumConfig) parts.push(rowNumConfig.width ? `${rowNumConfig.width}px` : ROW_NUMBER_WIDTH);
3234
+ parts.push(state.gridTemplate);
3235
+ return parts.join(" ");
3236
+ }, [rowDraggable, rowNumConfig, state.gridTemplate]);
3237
+ const totalCols = state.totalCols + (rowDraggable ? 1 : 0) + (rowNumConfig ? 1 : 0);
3238
+ const virtualConfig = virtualProp ? {
3239
+ rowHeight: typeof virtualProp === "object" ? virtualProp.rowHeight ?? 40 : 40,
3240
+ estimateRowHeight: typeof virtualProp === "object" ? virtualProp.estimateRowHeight : void 0,
3241
+ overscan: typeof virtualProp === "object" ? virtualProp.overscan ?? 5 : 5,
3242
+ height: typeof virtualProp === "object" ? virtualProp.height : void 0
3243
+ } : null;
3244
+ const virtualFixedRowHeight = virtualConfig && typeof virtualConfig.rowHeight === "number" && virtualConfig.estimateRowHeight == null ? virtualConfig.rowHeight : null;
3245
+ if (unstyled) {
3246
+ return /* @__PURE__ */ jsxs10("div", { className, style, role: "table", children: [
3247
+ /* @__PURE__ */ jsx10("div", { role: "rowgroup", children: /* @__PURE__ */ jsx10("div", { role: "row", children: state.columns.map((col) => /* @__PURE__ */ jsx10(
3248
+ "span",
3249
+ {
3250
+ role: "columnheader",
3251
+ onClick: (e) => state.handleSort(col, e.shiftKey),
3252
+ children: col.title
3253
+ },
3254
+ col.key
3255
+ )) }) }),
3256
+ /* @__PURE__ */ jsx10("div", { role: "rowgroup", children: state.flatRows.map((row) => /* @__PURE__ */ jsx10("div", { role: "row", children: state.columns.map((col) => /* @__PURE__ */ jsx10("span", { role: "cell", children: col.render ? col.render(row.data[col.key], row.data, row.id) : String(row.data[col.key] ?? "") }, col.key)) }, row.id)) })
3257
+ ] });
3258
+ }
3259
+ const hasToolbar = searchable || columnVisibility || exportable || exportJSON || fullscreenable || densityToggle || printable || groupBy || groupByOptions && groupByOptions.length > 0;
3260
+ const wrapperCls = [
3261
+ "sg-table-wrapper",
3262
+ `sg-table-${effectiveSize}`,
3263
+ bordered ? "sg-table-bordered" : "",
3264
+ sticky || virtualConfig ? "sg-table-sticky" : "",
3265
+ loading ? "sg-table-loading" : "",
3266
+ striped ? "sg-table-striped" : "",
3267
+ state.isFullscreen ? "sg-table-fullscreen" : "",
3268
+ highlightOnHover === false ? "" : "sg-table-hoverable",
3269
+ sCls.root,
3270
+ className
3271
+ ].filter(Boolean).join(" ");
3272
+ const scrollStyle = {};
3273
+ if (scroll?.x) scrollStyle.overflowX = "auto";
3274
+ if (virtualConfig) {
3275
+ scrollStyle.overflowY = "auto";
3276
+ scrollStyle.height = virtualConfig.height ?? scroll?.y ?? 400;
3277
+ } else if (scroll?.y) {
3278
+ scrollStyle.overflowY = "auto";
3279
+ scrollStyle.maxHeight = scroll.y;
3280
+ }
3281
+ const selectedCount = rowSelection?.selectedKeys.length ?? 0;
3282
+ return /* @__PURE__ */ jsxs10(
3283
+ "div",
3284
+ {
3285
+ className: wrapperCls,
3286
+ style: slotStyle(sSty.root, style),
3287
+ ref: (el) => {
3288
+ ;
3289
+ tableRef.current = el;
3290
+ state.wrapperRef.current = el;
3291
+ },
3292
+ onKeyDown: keyboardNavigation ? state.handleKeyNav : void 0,
3293
+ tabIndex: keyboardNavigation ? 0 : void 0,
3294
+ children: [
3295
+ hasToolbar && /* @__PURE__ */ jsxs10("div", { className: slotClass("sg-table-toolbar", sCls.toolbar), style: sSty.toolbar, children: [
3296
+ searchable && /* @__PURE__ */ jsx10(
3297
+ Input,
3298
+ {
3299
+ placeholder: state.t.searchPlaceholder,
3300
+ size: "small",
3301
+ value: state.searchText,
3302
+ onChange: state.setSearchText,
3303
+ style: { maxWidth: 240 }
3304
+ }
3305
+ ),
3306
+ /* @__PURE__ */ jsxs10("div", { className: "sg-table-toolbar-right", children: [
3307
+ groupByOptions && groupByOptions.length > 0 && /* @__PURE__ */ jsx10(
3308
+ GroupByDropdown,
3309
+ {
3310
+ value: groupBy ?? null,
3311
+ options: groupByOptions,
3312
+ onChange: onGroupByChange ?? (() => {
3313
+ }),
3314
+ t: state.t
3315
+ }
3316
+ ),
3317
+ groupBy && /* @__PURE__ */ jsxs10("div", { className: "sg-table-toolbar-group", children: [
3318
+ /* @__PURE__ */ jsx10("button", { className: "sg-table-toolbar-btn", onClick: state.expandAllGroups, children: state.t.groupExpand }),
3319
+ /* @__PURE__ */ jsx10("button", { className: "sg-table-toolbar-btn", onClick: state.collapseAllGroups, children: state.t.groupCollapse })
3320
+ ] }),
3321
+ densityToggle && /* @__PURE__ */ jsx10(DensityDropdown, { value: state.density, onChange: state.setDensity, t: state.t }),
3322
+ columnVisibility && /* @__PURE__ */ jsx10(
3323
+ ColumnVisibilityDropdown,
3324
+ {
3325
+ leafColumns: state.leafColumns,
3326
+ hiddenSet: state.hiddenSet,
3327
+ onToggle: state.toggleColumnVisibility,
3328
+ label: state.t.showColumns
3329
+ }
3330
+ ),
3331
+ exportable && /* @__PURE__ */ jsxs10(Fragment4, { children: [
3332
+ /* @__PURE__ */ jsx10("button", { className: "sg-table-toolbar-btn", onClick: handleExportCSV, children: state.t.exportCSV }),
3333
+ /* @__PURE__ */ jsx10("button", { className: "sg-table-toolbar-btn", onClick: handleCopy, children: state.t.copyTable })
3334
+ ] }),
3335
+ exportJSON && /* @__PURE__ */ jsx10("button", { className: "sg-table-toolbar-btn", onClick: handleExportJSON, children: state.t.exportJSON }),
3336
+ printable && /* @__PURE__ */ jsx10("button", { className: "sg-table-toolbar-btn", onClick: handlePrint, children: state.t.print }),
3337
+ fullscreenable && /* @__PURE__ */ jsx10("button", { className: "sg-table-toolbar-btn", onClick: state.toggleFullscreen, children: state.isFullscreen ? state.t.exitFullscreen : state.t.fullscreen })
3338
+ ] })
3339
+ ] }),
3340
+ selectionSummary && selectedCount > 0 && /* @__PURE__ */ jsxs10("div", { className: "sg-table-selection-bar", children: [
3341
+ /* @__PURE__ */ jsx10("span", { className: "sg-table-selection-count", children: state.t.selectedRows(selectedCount) }),
3342
+ selectionActions?.map((action) => /* @__PURE__ */ jsx10(
3343
+ "button",
3344
+ {
3345
+ className: `sg-table-selection-action${action.danger ? " sg-table-selection-action-danger" : ""}`,
3346
+ onClick: () => action.onClick(
3347
+ rowSelection.selectedKeys,
3348
+ state.visibleRows.filter((r) => rowSelection.selectedKeys.includes(r.id)).map((r) => r.data)
3349
+ ),
3350
+ children: action.label
3351
+ },
3352
+ action.key
3353
+ )),
3354
+ /* @__PURE__ */ jsx10(
3355
+ "button",
3356
+ {
3357
+ className: "sg-table-selection-clear",
3358
+ onClick: () => rowSelection?.onChange([], []),
3359
+ children: "\u2715"
3360
+ }
3361
+ )
3362
+ ] }),
3363
+ /* @__PURE__ */ jsxs10(
3364
+ "div",
3365
+ {
3366
+ ref: scrollRef,
3367
+ className: slotClass("sg-table-scroll", scroll?.x ? "sg-table-scroll-x" : "", sCls.scroll),
3368
+ style: slotStyle(
3369
+ Object.keys(scrollStyle).length > 0 ? scrollStyle : void 0,
3370
+ sSty.scroll
3371
+ ),
3372
+ children: [
3373
+ state.pinnedTop.length > 0 && /* @__PURE__ */ jsx10(
3374
+ "div",
3375
+ {
3376
+ className: "sg-table-pinned-top",
3377
+ style: { gridTemplateColumns: gridTemplate, minWidth: scroll?.x },
3378
+ children: /* @__PURE__ */ jsx10(
3379
+ TableBody,
3380
+ {
3381
+ columns: state.columns,
3382
+ flatRows: state.pinnedTop,
3383
+ rowSelection,
3384
+ expandable,
3385
+ isTreeMode: state.isTreeMode,
3386
+ indentSize: state.indentSize,
3387
+ treeExpanded: state.treeExpanded,
3388
+ expandedKeys: state.expandedKeys,
3389
+ totalCols,
3390
+ spanMap: state.spanMap,
3391
+ hiddenCells: state.hiddenCells,
3392
+ t: state.t,
3393
+ rowClassName,
3394
+ fixedStyle: state.fixedStyle,
3395
+ onRowClick,
3396
+ onCellEdit,
3397
+ onSelectRow: state.handleSelectRow,
3398
+ onToggleExpand: state.handleToggleExpand,
3399
+ onToggleTreeExpand: state.handleToggleTreeExpand,
3400
+ onContextMenu: state.setContextMenu,
3401
+ striped: false,
3402
+ isPinnedSection: true,
3403
+ slotClassNames: sCls,
3404
+ slotStyles: sSty
3405
+ }
3406
+ )
3407
+ }
3408
+ ),
3409
+ /* @__PURE__ */ jsxs10(
3410
+ "div",
3411
+ {
3412
+ className: slotClass(
3413
+ "sg-table-grid",
3414
+ virtualFixedRowHeight != null ? "sg-table-grid-virtual-fixed" : "",
3415
+ sCls.grid
3416
+ ),
3417
+ style: slotStyle(
3418
+ {
3419
+ gridTemplateColumns: gridTemplate,
3420
+ minWidth: scroll?.x,
3421
+ ...virtualFixedRowHeight != null ? {
3422
+ ["--sg-virtual-row-height"]: `${virtualFixedRowHeight}px`
3423
+ } : null
3424
+ },
3425
+ sSty.grid
3426
+ ),
3427
+ role: "table",
3428
+ children: [
3429
+ /* @__PURE__ */ jsx10(
3430
+ TableHeader,
3431
+ {
3432
+ columns: state.columns,
3433
+ headerRows: state.headerRows,
3434
+ hasColumnGroups: state.hasColumnGroups,
3435
+ rowSelection,
3436
+ expandable,
3437
+ draggable,
3438
+ allSelected: state.allSelected,
3439
+ someSelected: state.someSelected,
3440
+ dragOver: state.dragOver,
3441
+ t: state.t,
3442
+ fixedStyle: state.fixedStyle,
3443
+ onSort: state.handleSort,
3444
+ onSelectAll: state.handleSelectAll,
3445
+ onDragStart: state.handleDragStart,
3446
+ onDragOver: state.handleDragOver,
3447
+ onDragLeave: state.setDragOver,
3448
+ onDrop: state.handleDrop,
3449
+ onDragEnd: state.handleDragEnd,
3450
+ onResizeStart: state.handleResizeStart,
3451
+ filterState: state.filterState,
3452
+ filterActions: state.filterActions,
3453
+ filterRef: state.filterRef,
3454
+ sorts: state.sorts,
3455
+ getSortIndex: state.getSortIndex,
3456
+ getSortDirection: state.getSortDirection,
3457
+ headerContextMenu,
3458
+ onContextMenu: state.setContextMenu,
3459
+ showRowNumber: !!rowNumConfig,
3460
+ rowDraggable,
3461
+ columnPinning,
3462
+ onPinColumn: state.handlePinColumn,
3463
+ onAutoResize: columnAutoResize ? state.handleAutoResize : void 0,
3464
+ slotClassNames: sCls,
3465
+ slotStyles: sSty
3466
+ }
3467
+ ),
3468
+ virtualConfig ? /* @__PURE__ */ jsx10(
3469
+ VirtualTableBody,
3470
+ {
3471
+ columns: state.columns,
3472
+ flatRows: state.flatRows,
3473
+ rowHeight: virtualConfig.rowHeight,
3474
+ estimateRowHeight: virtualConfig.estimateRowHeight,
3475
+ overscan: virtualConfig.overscan,
3476
+ scrollContainerRef: scrollRef,
3477
+ rowSelection,
3478
+ expandable,
3479
+ isTreeMode: state.isTreeMode,
3480
+ indentSize: state.indentSize,
3481
+ treeExpanded: state.treeExpanded,
3482
+ expandedKeys: state.expandedKeys,
3483
+ totalCols,
3484
+ spanMap: state.spanMap,
3485
+ hiddenCells: state.hiddenCells,
3486
+ t: state.t,
3487
+ rowClassName,
3488
+ fixedStyle: state.fixedStyle,
3489
+ onRowClick,
3490
+ onCellEdit,
3491
+ onSelectRow: state.handleSelectRow,
3492
+ onToggleExpand: state.handleToggleExpand,
3493
+ onToggleTreeExpand: state.handleToggleTreeExpand,
3494
+ rowDraggable,
3495
+ showRowNumber: !!rowNumConfig,
3496
+ cellContextMenu,
3497
+ onContextMenu: state.setContextMenu,
3498
+ striped,
3499
+ groupBy,
3500
+ onToggleGroup: state.toggleGroupExpand,
3501
+ focusedCell: state.focusedCell,
3502
+ onFocusCell: state.setFocusedCell,
3503
+ keyboardNavigation,
3504
+ emptyContent,
3505
+ slotClassNames: sCls,
3506
+ slotStyles: sSty
3507
+ }
3508
+ ) : /* @__PURE__ */ jsx10(
3509
+ TableBody,
3510
+ {
3511
+ columns: state.columns,
3512
+ flatRows: state.flatRows,
3513
+ rowSelection,
3514
+ expandable,
3515
+ isTreeMode: state.isTreeMode,
3516
+ indentSize: state.indentSize,
3517
+ treeExpanded: state.treeExpanded,
3518
+ expandedKeys: state.expandedKeys,
3519
+ totalCols,
3520
+ spanMap: state.spanMap,
3521
+ hiddenCells: state.hiddenCells,
3522
+ t: state.t,
3523
+ rowClassName,
3524
+ fixedStyle: state.fixedStyle,
3525
+ onRowClick,
3526
+ onCellEdit,
3527
+ onSelectRow: state.handleSelectRow,
3528
+ onToggleExpand: state.handleToggleExpand,
3529
+ onToggleTreeExpand: state.handleToggleTreeExpand,
3530
+ rowDraggable,
3531
+ onRowOrderChange,
3532
+ cellContextMenu,
3533
+ onContextMenu: state.setContextMenu,
3534
+ striped,
3535
+ showRowNumber: !!rowNumConfig,
3536
+ groupBy,
3537
+ onToggleGroup: state.toggleGroupExpand,
3538
+ focusedCell: state.focusedCell,
3539
+ onFocusCell: state.setFocusedCell,
3540
+ keyboardNavigation,
3541
+ emptyContent,
3542
+ slotClassNames: sCls,
3543
+ slotStyles: sSty
3544
+ }
3545
+ ),
3546
+ footer && state.aggregates && /* @__PURE__ */ jsxs10(
3547
+ "div",
3548
+ {
3549
+ className: ["sg-table-footer-row", sCls.footer].filter(Boolean).join(" "),
3550
+ role: "row",
3551
+ style: { display: "contents", ...sSty.footer },
3552
+ children: [
3553
+ rowDraggable && /* @__PURE__ */ jsx10("div", { className: "sg-table-td sg-table-footer-cell", role: "cell" }),
3554
+ rowNumConfig && /* @__PURE__ */ jsx10("div", { className: "sg-table-td sg-table-footer-cell", role: "cell" }),
3555
+ rowSelection && /* @__PURE__ */ jsx10("div", { className: "sg-table-td sg-table-footer-cell", role: "cell" }),
3556
+ expandable && /* @__PURE__ */ jsx10("div", { className: "sg-table-td sg-table-footer-cell", role: "cell" }),
3557
+ state.columns.map((col) => /* @__PURE__ */ jsx10(
3558
+ "div",
3559
+ {
3560
+ className: "sg-table-td sg-table-footer-cell",
3561
+ role: "cell",
3562
+ style: state.fixedStyle(col),
3563
+ children: state.aggregates[col.key] != null ? /* @__PURE__ */ jsxs10("span", { className: "sg-table-aggregate-value", children: [
3564
+ /* @__PURE__ */ jsx10("span", { className: "sg-table-aggregate-label", children: typeof col.aggregate === "string" ? state.t[col.aggregate] : "" }),
3565
+ " ",
3566
+ String(state.aggregates[col.key])
3567
+ ] }) : null
3568
+ },
3569
+ col.key
3570
+ ))
3571
+ ]
3572
+ }
3573
+ ),
3574
+ summary && state.visibleRows && summary(state.visibleRows).map((cells, ri) => /* @__PURE__ */ jsx10(
3575
+ "div",
3576
+ {
3577
+ className: "sg-table-summary-row",
3578
+ role: "row",
3579
+ style: { display: "contents" },
3580
+ children: cells.map((cell, ci) => /* @__PURE__ */ jsx10(
3581
+ "div",
3582
+ {
3583
+ className: "sg-table-td sg-table-summary-cell",
3584
+ role: "cell",
3585
+ style: {
3586
+ gridColumn: cell.colSpan ? `span ${cell.colSpan}` : void 0,
3587
+ textAlign: cell.align
3588
+ },
3589
+ children: cell.content
3590
+ },
3591
+ ci
3592
+ ))
3593
+ },
3594
+ `sum-${ri}`
3595
+ ))
3596
+ ]
3597
+ }
3598
+ ),
3599
+ state.pinnedBottom.length > 0 && /* @__PURE__ */ jsx10(
3600
+ "div",
3601
+ {
3602
+ className: "sg-table-pinned-bottom",
3603
+ style: { gridTemplateColumns: gridTemplate, minWidth: scroll?.x },
3604
+ children: /* @__PURE__ */ jsx10(
3605
+ TableBody,
3606
+ {
3607
+ columns: state.columns,
3608
+ flatRows: state.pinnedBottom,
3609
+ rowSelection,
3610
+ expandable,
3611
+ isTreeMode: state.isTreeMode,
3612
+ indentSize: state.indentSize,
3613
+ treeExpanded: state.treeExpanded,
3614
+ expandedKeys: state.expandedKeys,
3615
+ totalCols,
3616
+ spanMap: state.spanMap,
3617
+ hiddenCells: state.hiddenCells,
3618
+ t: state.t,
3619
+ rowClassName,
3620
+ fixedStyle: state.fixedStyle,
3621
+ onRowClick,
3622
+ onCellEdit,
3623
+ onSelectRow: state.handleSelectRow,
3624
+ onToggleExpand: state.handleToggleExpand,
3625
+ onToggleTreeExpand: state.handleToggleTreeExpand,
3626
+ onContextMenu: state.setContextMenu,
3627
+ striped: false,
3628
+ isPinnedSection: true,
3629
+ slotClassNames: sCls,
3630
+ slotStyles: sSty
3631
+ }
3632
+ )
3633
+ }
3634
+ )
3635
+ ]
3636
+ }
3637
+ ),
3638
+ /* @__PURE__ */ jsx10(ContextMenu, { state: state.contextMenu, onClose: () => state.setContextMenu(null) }),
3639
+ loading && /* @__PURE__ */ jsx10("div", { className: "sg-table-loading-mask", children: /* @__PURE__ */ jsx10(Spin, { size: "large" }) }),
3640
+ showPagination && state.tableState.totalPages > 1 && /* @__PURE__ */ jsxs10("div", { className: slotClass("sg-table-pagination", sCls.pagination), style: sSty.pagination, children: [
3641
+ /* @__PURE__ */ jsx10("span", { className: "sg-table-pagination-total", children: state.t.totalRows(state.tableState.filteredRows) }),
3642
+ /* @__PURE__ */ jsx10(
3643
+ Pagination,
3644
+ {
3645
+ current: state.tableState.page,
3646
+ total: state.tableState.filteredRows,
3647
+ pageSize: state.tableState.pageSize,
3648
+ onChange: state.setPage,
3649
+ showSizeChanger,
3650
+ pageSizeOptions,
3651
+ showQuickJumper,
3652
+ showTotal,
3653
+ onPageSizeChange
3654
+ }
3655
+ )
3656
+ ] })
3657
+ ]
3658
+ }
3659
+ );
3660
+ }
3661
+ var Table = forwardRef(TableInner);
3662
+ Table.displayName = "Table";
3663
+ function ColumnVisibilityDropdown({
3664
+ leafColumns,
3665
+ hiddenSet,
3666
+ onToggle,
3667
+ label
3668
+ }) {
3669
+ const [open, setOpen] = React5.useState(false);
3670
+ const ref = React5.useRef(null);
3671
+ React5.useEffect(() => {
3672
+ if (!open) return;
3673
+ const h = (e) => {
3674
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
3675
+ };
3676
+ document.addEventListener("mousedown", h);
3677
+ return () => document.removeEventListener("mousedown", h);
3678
+ }, [open]);
3679
+ return /* @__PURE__ */ jsxs10("div", { ref, style: { position: "relative" }, children: [
3680
+ /* @__PURE__ */ jsx10("button", { className: "sg-table-toolbar-btn", onClick: () => setOpen(!open), children: label }),
3681
+ open && /* @__PURE__ */ jsx10("div", { className: "sg-table-col-visibility-dropdown", children: leafColumns.map((col) => /* @__PURE__ */ jsxs10("label", { className: "sg-table-col-visibility-item", children: [
3682
+ /* @__PURE__ */ jsx10(Checkbox, { checked: !hiddenSet.has(col.key), onChange: () => onToggle(col.key) }),
3683
+ /* @__PURE__ */ jsx10("span", { children: typeof col.title === "string" ? col.title : col.key })
3684
+ ] }, col.key)) })
3685
+ ] });
3686
+ }
3687
+ function GroupByDropdown({
3688
+ value,
3689
+ options,
3690
+ onChange,
3691
+ t
3692
+ }) {
3693
+ const [open, setOpen] = React5.useState(false);
3694
+ const ref = React5.useRef(null);
3695
+ React5.useEffect(() => {
3696
+ if (!open) return;
3697
+ const h = (e) => {
3698
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
3699
+ };
3700
+ document.addEventListener("mousedown", h);
3701
+ return () => document.removeEventListener("mousedown", h);
3702
+ }, [open]);
3703
+ const current = value ? options.find((o) => o.key === value) : null;
3704
+ return /* @__PURE__ */ jsxs10("div", { ref, style: { position: "relative" }, children: [
3705
+ /* @__PURE__ */ jsxs10("button", { className: "sg-table-toolbar-btn", onClick: () => setOpen(!open), children: [
3706
+ t.groupByLabel,
3707
+ current ? `: ${typeof current.label === "string" ? current.label : current.key}` : ""
3708
+ ] }),
3709
+ open && /* @__PURE__ */ jsxs10("div", { className: "sg-table-group-by-dropdown", role: "menu", children: [
3710
+ /* @__PURE__ */ jsx10(
3711
+ "button",
3712
+ {
3713
+ type: "button",
3714
+ className: `sg-table-group-by-option${value === null ? " sg-table-group-by-active" : ""}`,
3715
+ onClick: () => {
3716
+ onChange(null);
3717
+ setOpen(false);
3718
+ },
3719
+ children: t.groupByNone
3720
+ }
3721
+ ),
3722
+ options.map((opt) => /* @__PURE__ */ jsx10(
3723
+ "button",
3724
+ {
3725
+ type: "button",
3726
+ className: `sg-table-group-by-option${value === opt.key ? " sg-table-group-by-active" : ""}`,
3727
+ onClick: () => {
3728
+ onChange(opt.key);
3729
+ setOpen(false);
3730
+ },
3731
+ children: opt.label
3732
+ },
3733
+ opt.key
3734
+ ))
3735
+ ] })
3736
+ ] });
3737
+ }
3738
+ function DensityDropdown({
3739
+ value,
3740
+ onChange,
3741
+ t
3742
+ }) {
3743
+ const [open, setOpen] = React5.useState(false);
3744
+ const ref = React5.useRef(null);
3745
+ React5.useEffect(() => {
3746
+ if (!open) return;
3747
+ const h = (e) => {
3748
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
3749
+ };
3750
+ document.addEventListener("mousedown", h);
3751
+ return () => document.removeEventListener("mousedown", h);
3752
+ }, [open]);
3753
+ const options = [
3754
+ { key: "small", label: t.densitySmall },
3755
+ { key: "middle", label: t.densityMiddle },
3756
+ { key: "large", label: t.densityLarge }
3757
+ ];
3758
+ return /* @__PURE__ */ jsxs10("div", { ref, style: { position: "relative" }, children: [
3759
+ /* @__PURE__ */ jsx10("button", { className: "sg-table-toolbar-btn", onClick: () => setOpen(!open), children: t.density }),
3760
+ open && /* @__PURE__ */ jsx10("div", { className: "sg-table-density-dropdown", children: options.map((opt) => /* @__PURE__ */ jsx10(
3761
+ "button",
3762
+ {
3763
+ className: `sg-table-density-option${value === opt.key ? " sg-table-density-active" : ""}`,
3764
+ onClick: () => {
3765
+ onChange(opt.key);
3766
+ setOpen(false);
3767
+ },
3768
+ children: opt.label
3769
+ },
3770
+ opt.key
3771
+ )) })
3772
+ ] });
3773
+ }
3774
+
3775
+ export {
3776
+ Pagination,
3777
+ Select,
3778
+ toCSVString,
3779
+ toTSVString,
3780
+ toJSONString,
3781
+ downloadCSV,
3782
+ downloadJSON,
3783
+ copyToClipboard,
3784
+ printTable,
3785
+ useTable,
3786
+ buildPageRule,
3787
+ buildPrintHtml,
3788
+ printElement,
3789
+ Table
3790
+ };
3791
+ //# sourceMappingURL=chunk-MJAEAFPN.js.map