lynote-ui 0.0.9 → 0.0.11

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.
@@ -0,0 +1,1216 @@
1
+ const require_chunk = require("./chunk-8l464Juk.js");
2
+ const require_utils = require("./utils-DrngEK1K.js");
3
+ const require_button = require("./button/index.js");
4
+ const require_input = require("./input/index.js");
5
+ const require_dropdown_menu = require("./dropdown-menu/index.js");
6
+ const require_native_select = require("./native-select/index.js");
7
+ const require_skeleton = require("./skeleton/index.js");
8
+ let lucide_react = require("lucide-react");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ let react = require("react");
11
+ react = require_chunk.__toESM(react);
12
+ let _dnd_kit_core = require("@dnd-kit/core");
13
+ let _dnd_kit_modifiers = require("@dnd-kit/modifiers");
14
+ let _dnd_kit_sortable = require("@dnd-kit/sortable");
15
+ let _dnd_kit_utilities = require("@dnd-kit/utilities");
16
+ let _tanstack_react_table = require("@tanstack/react-table");
17
+ let _tanstack_react_virtual = require("@tanstack/react-virtual");
18
+ //#region src/table/components/base-table.tsx
19
+ function Table({ className, ...props }) {
20
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
21
+ "data-slot": "table-container",
22
+ className: "relative w-full overflow-x-auto",
23
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("table", {
24
+ "data-slot": "table",
25
+ className: require_utils.cn("w-full caption-bottom text-sm", className),
26
+ ...props
27
+ })
28
+ });
29
+ }
30
+ function TableHeader({ className, ...props }) {
31
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", {
32
+ "data-slot": "table-header",
33
+ className: require_utils.cn("[&_tr]:border-b", className),
34
+ ...props
35
+ });
36
+ }
37
+ function TableBody({ className, ...props }) {
38
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", {
39
+ "data-slot": "table-body",
40
+ className: require_utils.cn("[&_tr:last-child]:border-0", className),
41
+ ...props
42
+ });
43
+ }
44
+ function TableFooter({ className, ...props }) {
45
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tfoot", {
46
+ "data-slot": "table-footer",
47
+ className: require_utils.cn("bg-muted/50 border-t font-medium [&>tr]:last:border-b-0", className),
48
+ ...props
49
+ });
50
+ }
51
+ function TableRow({ className, ...props }) {
52
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
53
+ "data-slot": "table-row",
54
+ className: require_utils.cn("hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors", className),
55
+ ...props
56
+ });
57
+ }
58
+ function TableHead({ className, ...props }) {
59
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
60
+ "data-slot": "table-head",
61
+ className: require_utils.cn("text-foreground h-10 whitespace-nowrap px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0", className),
62
+ ...props
63
+ });
64
+ }
65
+ function TableCell({ className, ...props }) {
66
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
67
+ "data-slot": "table-cell",
68
+ className: require_utils.cn("whitespace-nowrap p-2 align-middle [&:has([role=checkbox])]:pr-0", className),
69
+ ...props
70
+ });
71
+ }
72
+ function TableCaption({ className, ...props }) {
73
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("caption", {
74
+ "data-slot": "table-caption",
75
+ className: require_utils.cn("text-muted-foreground mt-4 text-sm", className),
76
+ ...props
77
+ });
78
+ }
79
+ //#endregion
80
+ //#region src/table/hooks/use-data-table.ts
81
+ var DEFAULT_FEATURES = {
82
+ sorting: true,
83
+ multiSort: false,
84
+ filtering: true,
85
+ globalFilter: false,
86
+ pagination: true,
87
+ rowSelection: false,
88
+ columnVisibility: true,
89
+ columnOrdering: false,
90
+ columnPinning: false,
91
+ columnResizing: false,
92
+ rowDnd: false,
93
+ expanding: false
94
+ };
95
+ function applyUpdater(updater, prev) {
96
+ return typeof updater === "function" ? updater(prev) : updater;
97
+ }
98
+ /**
99
+ * 选取受控值,若未传则回退到非受控
100
+ *
101
+ * setter 同时支持值和函数式 updater,以避免一帧内多次更新拿到旧 snapshot
102
+ */
103
+ function useControlled(controlled, initial, onChange) {
104
+ const isControlled = controlled !== void 0;
105
+ const [internal, setInternal] = (0, react.useState)(initial);
106
+ const value = isControlled ? controlled : internal;
107
+ const setValue = (updater) => {
108
+ if (isControlled) {
109
+ const next = applyUpdater(updater, controlled);
110
+ onChange?.(next);
111
+ return;
112
+ }
113
+ setInternal((prev) => {
114
+ const next = applyUpdater(updater, prev);
115
+ onChange?.(next);
116
+ return next;
117
+ });
118
+ };
119
+ return [value, setValue];
120
+ }
121
+ /**
122
+ * 构造 TanStack 表格实例,并按需开启各项功能
123
+ */
124
+ function useDataTable({ data, columns, features, state, initialState, onStateChange, tableOptions }) {
125
+ const mergedFeatures = {
126
+ ...DEFAULT_FEATURES,
127
+ ...features
128
+ };
129
+ const [sorting, setSorting] = useControlled(state?.sorting, initialState?.sorting ?? [], onStateChange?.onSortingChange);
130
+ const [columnFilters, setColumnFilters] = useControlled(state?.columnFilters, initialState?.columnFilters ?? [], onStateChange?.onColumnFiltersChange);
131
+ const [columnVisibility, setColumnVisibility] = useControlled(state?.columnVisibility, initialState?.columnVisibility ?? {}, onStateChange?.onColumnVisibilityChange);
132
+ const [columnOrder, setColumnOrder] = useControlled(state?.columnOrder, initialState?.columnOrder ?? [], onStateChange?.onColumnOrderChange);
133
+ const [columnPinning, setColumnPinning] = useControlled(state?.columnPinning, initialState?.columnPinning ?? {}, onStateChange?.onColumnPinningChange);
134
+ const [rowSelection, setRowSelection] = useControlled(state?.rowSelection, initialState?.rowSelection ?? {}, onStateChange?.onRowSelectionChange);
135
+ const [pagination, setPagination] = useControlled(state?.pagination, initialState?.pagination ?? {
136
+ pageIndex: 0,
137
+ pageSize: 10
138
+ }, onStateChange?.onPaginationChange);
139
+ const [globalFilter, setGlobalFilter] = useControlled(state?.globalFilter, initialState?.globalFilter ?? "", onStateChange?.onGlobalFilterChange);
140
+ const [expanded, setExpanded] = useControlled(state?.expanded, initialState?.expanded ?? {}, onStateChange?.onExpandedChange);
141
+ return {
142
+ table: (0, _tanstack_react_table.useReactTable)({
143
+ data,
144
+ columns,
145
+ state: {
146
+ sorting,
147
+ columnFilters,
148
+ columnVisibility,
149
+ columnOrder,
150
+ columnPinning,
151
+ rowSelection,
152
+ pagination,
153
+ globalFilter,
154
+ expanded
155
+ },
156
+ enableSorting: mergedFeatures.sorting,
157
+ enableMultiSort: mergedFeatures.multiSort,
158
+ enableFilters: mergedFeatures.filtering,
159
+ enableGlobalFilter: mergedFeatures.globalFilter,
160
+ enableRowSelection: mergedFeatures.rowSelection,
161
+ enableHiding: mergedFeatures.columnVisibility,
162
+ enableColumnPinning: mergedFeatures.columnPinning,
163
+ enableColumnResizing: mergedFeatures.columnResizing,
164
+ columnResizeMode: "onChange",
165
+ onSortingChange: setSorting,
166
+ onColumnFiltersChange: setColumnFilters,
167
+ onColumnVisibilityChange: setColumnVisibility,
168
+ onColumnOrderChange: setColumnOrder,
169
+ onColumnPinningChange: setColumnPinning,
170
+ onRowSelectionChange: setRowSelection,
171
+ onPaginationChange: setPagination,
172
+ onGlobalFilterChange: setGlobalFilter,
173
+ onExpandedChange: setExpanded,
174
+ getCoreRowModel: (0, _tanstack_react_table.getCoreRowModel)(),
175
+ getSortedRowModel: mergedFeatures.sorting ? (0, _tanstack_react_table.getSortedRowModel)() : void 0,
176
+ getFilteredRowModel: mergedFeatures.filtering || mergedFeatures.globalFilter ? (0, _tanstack_react_table.getFilteredRowModel)() : void 0,
177
+ getPaginationRowModel: mergedFeatures.pagination && !tableOptions?.manualPagination ? (0, _tanstack_react_table.getPaginationRowModel)() : void 0,
178
+ getExpandedRowModel: mergedFeatures.expanding ? (0, _tanstack_react_table.getExpandedRowModel)() : void 0,
179
+ ...tableOptions
180
+ }),
181
+ features: mergedFeatures
182
+ };
183
+ }
184
+ /**
185
+ * 监听容器横向滚动,返回左右阴影显示状态
186
+ *
187
+ * 用于固定列阴影提示
188
+ */
189
+ function useScrollShadow(wrapperRef, containerSelector = "[data-slot=\"table-container\"]") {
190
+ const [scrollShadow, setScrollShadow] = (0, react.useState)({
191
+ left: false,
192
+ right: false
193
+ });
194
+ (0, react.useEffect)(() => {
195
+ const scrollContainer = wrapperRef.current?.querySelector(containerSelector);
196
+ if (!scrollContainer) return;
197
+ const update = () => {
198
+ const scrollLeft = scrollContainer.scrollLeft;
199
+ const maxScrollLeft = Math.max(0, scrollContainer.scrollWidth - scrollContainer.clientWidth);
200
+ const next = {
201
+ left: scrollLeft > 1,
202
+ right: maxScrollLeft - scrollLeft > 1
203
+ };
204
+ setScrollShadow((current) => current.left === next.left && current.right === next.right ? current : next);
205
+ };
206
+ update();
207
+ scrollContainer.addEventListener("scroll", update, { passive: true });
208
+ const resizeObserver = new ResizeObserver(update);
209
+ resizeObserver.observe(scrollContainer);
210
+ const tableElement = scrollContainer.querySelector("table");
211
+ if (tableElement) resizeObserver.observe(tableElement);
212
+ return () => {
213
+ scrollContainer.removeEventListener("scroll", update);
214
+ resizeObserver.disconnect();
215
+ };
216
+ }, [wrapperRef, containerSelector]);
217
+ return scrollShadow;
218
+ }
219
+ //#endregion
220
+ //#region src/table/utils.ts
221
+ /**
222
+ * 展开面板 row 的 DOM id 协议
223
+ *
224
+ * `DataTable` 渲染面板 row 时把这个 id 设到 `<tr id>` 上,
225
+ * `RowExpandToggle` 把它写进按钮的 `aria-controls`,二者必须保持一致
226
+ */
227
+ var panelIdFor = (rowId) => `data-table-row-${rowId}-panel`;
228
+ /**
229
+ * 计算固定列(sticky)需要的样式
230
+ */
231
+ function getCommonPinningStyles(column) {
232
+ const isPinned = column.getIsPinned();
233
+ return {
234
+ left: isPinned === "left" ? `${column.getStart("left")}px` : void 0,
235
+ opacity: isPinned ? .98 : 1,
236
+ position: isPinned ? "sticky" : "relative",
237
+ right: isPinned === "right" ? `${column.getAfter("right")}px` : void 0,
238
+ width: `${column.getSize()}px`,
239
+ zIndex: isPinned ? 1 : 0
240
+ };
241
+ }
242
+ /**
243
+ * 计算固定列阴影 className
244
+ */
245
+ function getColumnPinningClassName(column, scrollShadow) {
246
+ const isPinned = column.getIsPinned();
247
+ const isLastLeftPinnedColumn = isPinned === "left" && column.getIsLastColumn("left");
248
+ const isFirstRightPinnedColumn = isPinned === "right" && column.getIsFirstColumn("right");
249
+ let styles = "bg-background after:transition-all";
250
+ if (isLastLeftPinnedColumn) styles = require_utils.cn(styles, "after:absolute after:bottom-0 after:top-0 after:right-0 after:w-[30px] after:translate-x-full after:content-[''] after:shadow-[inset_15px_0_8px_-8px_#0000000d] dark:after:shadow-[inset_15px_0_8px_-8px_#ffffff0d]");
251
+ if (isLastLeftPinnedColumn && !scrollShadow.left) styles = require_utils.cn(styles, "after:shadow-[inset_15px_0_8px_-8px_#00000000]");
252
+ if (isFirstRightPinnedColumn) styles = require_utils.cn(styles, "after:absolute after:bottom-0 after:top-0 after:left-0 after:w-[30px] after:-translate-x-full after:content-[''] after:shadow-[inset_-15px_0_8px_-8px_#0000000d] dark:after:shadow-[inset_-15px_0_8px_-8px_#ffffff0d]");
253
+ if (isFirstRightPinnedColumn && !scrollShadow.right) styles = require_utils.cn(styles, "after:shadow-[inset_-15px_0_8px_-8px_#00000000]");
254
+ return styles;
255
+ }
256
+ function ensureRow(map, rowIndex) {
257
+ let entry = map.get(rowIndex);
258
+ if (!entry) {
259
+ entry = /* @__PURE__ */ new Map();
260
+ map.set(rowIndex, entry);
261
+ }
262
+ return entry;
263
+ }
264
+ function setSpan(map, rowIndex, columnId, next) {
265
+ const row = ensureRow(map, rowIndex);
266
+ const previous = row.get(columnId) ?? {
267
+ rowSpan: 1,
268
+ colSpan: 1
269
+ };
270
+ row.set(columnId, {
271
+ ...previous,
272
+ ...next
273
+ });
274
+ }
275
+ /**
276
+ * 根据每列 `meta.rowSpan` / `meta.colSpan` 计算每行每列的合并跨度
277
+ *
278
+ * `0` 表示该单元格不应渲染(被合并掉了)
279
+ */
280
+ function computeCellSpans(rows, columns) {
281
+ const map = /* @__PURE__ */ new Map();
282
+ if (rows.length === 0) return map;
283
+ columns.forEach((column) => {
284
+ const meta = column.columnDef.meta;
285
+ const rowSpanCfg = meta?.rowSpan;
286
+ if (!rowSpanCfg) return;
287
+ if (rowSpanCfg === true || rowSpanCfg === "auto") {
288
+ const parentKey = meta?.rowSpanParent;
289
+ let index = 0;
290
+ while (index < rows.length) {
291
+ const value = rows[index].getValue(column.id);
292
+ const parentValue = parentKey ? rows[index].getValue(parentKey) : void 0;
293
+ let span = 1;
294
+ while (index + span < rows.length) {
295
+ const nextValue = rows[index + span].getValue(column.id);
296
+ const nextParent = parentKey ? rows[index + span].getValue(parentKey) : void 0;
297
+ if (Object.is(nextValue, value) && (!parentKey || Object.is(nextParent, parentValue))) span += 1;
298
+ else break;
299
+ }
300
+ if (span > 1) {
301
+ setSpan(map, index, column.id, { rowSpan: span });
302
+ for (let offset = 1; offset < span; offset += 1) setSpan(map, index + offset, column.id, {
303
+ rowSpan: 0,
304
+ colSpan: 0
305
+ });
306
+ }
307
+ index += span;
308
+ }
309
+ } else if (typeof rowSpanCfg === "function") rows.forEach((row, rowIndex) => {
310
+ const span = rowSpanCfg({
311
+ row,
312
+ rows,
313
+ rowIndex,
314
+ value: row.getValue(column.id)
315
+ });
316
+ const existing = map.get(rowIndex)?.get(column.id);
317
+ if (existing && existing.rowSpan === 0) return;
318
+ if (span === 0) setSpan(map, rowIndex, column.id, {
319
+ rowSpan: 0,
320
+ colSpan: 0
321
+ });
322
+ else if (span > 1) {
323
+ setSpan(map, rowIndex, column.id, { rowSpan: span });
324
+ for (let offset = 1; offset < span; offset += 1) setSpan(map, rowIndex + offset, column.id, {
325
+ rowSpan: 0,
326
+ colSpan: 0
327
+ });
328
+ }
329
+ });
330
+ });
331
+ rows.forEach((row, rowIndex) => {
332
+ const cells = row.getVisibleCells();
333
+ let skipUntil = -1;
334
+ cells.forEach((cell, cellIndex) => {
335
+ const columnId = cell.column.id;
336
+ const rowMap = ensureRow(map, rowIndex);
337
+ const existing = rowMap.get(columnId);
338
+ if (existing && existing.rowSpan === 0) return;
339
+ if (cellIndex <= skipUntil) {
340
+ rowMap.set(columnId, {
341
+ rowSpan: existing?.rowSpan ?? 1,
342
+ colSpan: 0
343
+ });
344
+ return;
345
+ }
346
+ const colSpanCfg = cell.column.columnDef.meta?.colSpan;
347
+ if (typeof colSpanCfg === "function") {
348
+ const colSpan = colSpanCfg({
349
+ row,
350
+ rows,
351
+ rowIndex,
352
+ column: cell.column
353
+ });
354
+ if (colSpan === 0) rowMap.set(columnId, {
355
+ rowSpan: existing?.rowSpan ?? 1,
356
+ colSpan: 0
357
+ });
358
+ else if (colSpan > 1) {
359
+ rowMap.set(columnId, {
360
+ rowSpan: existing?.rowSpan ?? 1,
361
+ colSpan
362
+ });
363
+ skipUntil = cellIndex + colSpan - 1;
364
+ }
365
+ }
366
+ });
367
+ });
368
+ return map;
369
+ }
370
+ //#endregion
371
+ //#region src/table/components/data-table-column-header.tsx
372
+ /**
373
+ * 表头单元格,封装排序、列宽拖拽、固定列阴影
374
+ */
375
+ function DataTableColumnHeader({ header, resizable, scrollShadow }) {
376
+ const canSort = header.column.getCanSort();
377
+ const isSorted = header.column.getIsSorted();
378
+ const canResize = resizable && header.column.getCanResize();
379
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableHead, {
380
+ "data-slot": "data-table-column-header",
381
+ colSpan: header.colSpan,
382
+ style: getCommonPinningStyles(header.column),
383
+ className: getColumnPinningClassName(header.column, scrollShadow),
384
+ children: header.isPlaceholder ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
385
+ className: "flex items-center gap-1",
386
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
387
+ className: require_utils.cn("flex flex-1 items-center gap-1", canSort && "cursor-pointer select-none"),
388
+ onClick: canSort ? header.column.getToggleSortingHandler() : void 0,
389
+ children: [(0, _tanstack_react_table.flexRender)(header.column.columnDef.header, header.getContext()), canSort && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
390
+ className: "flex flex-col items-center",
391
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronUpIcon, { className: require_utils.cn("text-muted-foreground -mb-1 size-[1em] transition", isSorted === "asc" && "text-foreground") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDownIcon, { className: require_utils.cn("text-muted-foreground -mt-1 size-[1em] transition", isSorted === "desc" && "text-foreground") })]
392
+ })]
393
+ }), canResize && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
394
+ onDoubleClick: header.column.resetSize,
395
+ onMouseDown: header.getResizeHandler(),
396
+ onTouchStart: header.getResizeHandler(),
397
+ className: require_utils.cn("ml-auto h-[stretch] w-px cursor-col-resize touch-none select-none", "hover:bg-primary/40 data-[resizing=true]:bg-primary"),
398
+ "data-resizing": header.column.getIsResizing() || void 0
399
+ })]
400
+ })
401
+ });
402
+ }
403
+ //#endregion
404
+ //#region src/table/components/data-table-pagination.tsx
405
+ var DEFAULT_PAGE_SIZE_OPTIONS = [
406
+ 10,
407
+ 20,
408
+ 30,
409
+ 50,
410
+ 100
411
+ ];
412
+ /**
413
+ * 表格分页栏:总行数 / 每页数量 / 页码跳转
414
+ */
415
+ function DataTablePagination({ table, pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS, showSelectionCount = true }) {
416
+ const pagination = table.getState().pagination;
417
+ const selectedCount = Object.keys(table.getState().rowSelection).length;
418
+ const totalRows = table.getRowCount();
419
+ const pageCount = table.getPageCount();
420
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
421
+ className: "text-muted-foreground flex flex-wrap items-center justify-between gap-2 py-2 text-sm",
422
+ children: [
423
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
424
+ className: "flex-1",
425
+ children: showSelectionCount && selectedCount > 0 ? `已选择 ${selectedCount} / ${totalRows} 条` : `共 ${totalRows} 条`
426
+ }),
427
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
428
+ className: "flex items-center gap-2",
429
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "每页" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_native_select.NativeSelect, {
430
+ size: "sm",
431
+ value: pagination.pageSize,
432
+ onChange: (event) => table.setPageSize(Number(event.target.value)),
433
+ children: pageSizeOptions.map((size) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_native_select.NativeSelectOption, {
434
+ value: size,
435
+ children: size
436
+ }, size))
437
+ })]
438
+ }),
439
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
440
+ className: "flex items-center gap-1",
441
+ children: [
442
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
443
+ className: "px-2",
444
+ children: [
445
+ "第 ",
446
+ pagination.pageIndex + 1,
447
+ " / ",
448
+ Math.max(pageCount, 1),
449
+ " 页"
450
+ ]
451
+ }),
452
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
453
+ variant: "outline",
454
+ size: "icon-sm",
455
+ "aria-label": "第一页",
456
+ onClick: () => table.setPageIndex(0),
457
+ disabled: !table.getCanPreviousPage(),
458
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronsLeftIcon, {})
459
+ }),
460
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
461
+ variant: "outline",
462
+ size: "icon-sm",
463
+ "aria-label": "上一页",
464
+ onClick: () => table.previousPage(),
465
+ disabled: !table.getCanPreviousPage(),
466
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronLeftIcon, {})
467
+ }),
468
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
469
+ variant: "outline",
470
+ size: "icon-sm",
471
+ "aria-label": "下一页",
472
+ onClick: () => table.nextPage(),
473
+ disabled: !table.getCanNextPage(),
474
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRightIcon, {})
475
+ }),
476
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
477
+ variant: "outline",
478
+ size: "icon-sm",
479
+ "aria-label": "最后一页",
480
+ onClick: () => table.setPageIndex(pageCount - 1),
481
+ disabled: !table.getCanNextPage(),
482
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronsRightIcon, {})
483
+ })
484
+ ]
485
+ })
486
+ ]
487
+ });
488
+ }
489
+ //#endregion
490
+ //#region src/table/components/data-table-virtual-body.tsx
491
+ var DEFAULT_ROW_HEIGHT = 36;
492
+ var DEFAULT_MAX_HEIGHT = 480;
493
+ var DEFAULT_OVERSCAN = 10;
494
+ /**
495
+ * 行虚拟滚动表格:使用 grid 布局 + `useVirtualizer` 仅渲染可视区域
496
+ *
497
+ * 因表格采用 grid 布局,固定列 / 多级表头暂不支持
498
+ */
499
+ function DataTableVirtualBody({ table, loading, empty = "暂无数据", options, bordered = false, className }) {
500
+ const estimateRowHeight = options?.estimateRowHeight ?? DEFAULT_ROW_HEIGHT;
501
+ const maxHeight = options?.maxHeight ?? DEFAULT_MAX_HEIGHT;
502
+ const overscan = options?.overscan ?? DEFAULT_OVERSCAN;
503
+ const containerRef = (0, react.useRef)(null);
504
+ const rows = table.getRowModel().rows;
505
+ const totalWidth = table.getTotalSize();
506
+ const virtualizer = (0, _tanstack_react_virtual.useVirtualizer)({
507
+ count: rows.length,
508
+ getScrollElement: () => containerRef.current,
509
+ estimateSize: () => estimateRowHeight,
510
+ overscan
511
+ });
512
+ const virtualItems = virtualizer.getVirtualItems();
513
+ const showEmpty = !loading && rows.length === 0;
514
+ const skeletonRowCount = Math.max(1, Math.floor((typeof maxHeight === "number" ? maxHeight : 480) / estimateRowHeight));
515
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
516
+ ref: containerRef,
517
+ "data-slot": "table-container",
518
+ className: require_utils.cn("bg-background relative overflow-auto rounded-md", bordered && "border", className),
519
+ style: { height: typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight },
520
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
521
+ "data-slot": "table",
522
+ className: "grid text-sm",
523
+ style: {
524
+ width: `${totalWidth}px`,
525
+ minWidth: "100%"
526
+ },
527
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", {
528
+ "data-slot": "table-header",
529
+ className: "bg-background sticky top-0 z-10 grid border-b",
530
+ children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
531
+ className: "flex w-full",
532
+ children: headerGroup.headers.map((header, headerIndex) => {
533
+ const canSort = header.column.getCanSort();
534
+ const isSorted = header.column.getIsSorted();
535
+ const isLast = headerIndex === headerGroup.headers.length - 1;
536
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("th", {
537
+ "data-slot": "table-head",
538
+ className: require_utils.cn("text-foreground flex h-10 shrink-0 items-center gap-1 px-2 text-left font-medium", canSort && "cursor-pointer select-none", bordered && !isLast && "border-r"),
539
+ style: { width: `${header.getSize()}px` },
540
+ onClick: canSort ? header.column.getToggleSortingHandler() : void 0,
541
+ children: [header.isPlaceholder ? null : (0, _tanstack_react_table.flexRender)(header.column.columnDef.header, header.getContext()), canSort && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
542
+ className: "ml-1 flex flex-col items-center",
543
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronUpIcon, { className: require_utils.cn("text-muted-foreground -mb-1 size-3 transition", isSorted === "asc" && "text-foreground") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDownIcon, { className: require_utils.cn("text-muted-foreground -mt-1 size-3 transition", isSorted === "desc" && "text-foreground") })]
544
+ })]
545
+ }, header.id);
546
+ })
547
+ }, headerGroup.id))
548
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", {
549
+ "data-slot": "table-body",
550
+ className: "relative grid",
551
+ style: { height: loading ? `${skeletonRowCount * estimateRowHeight}px` : `${virtualizer.getTotalSize()}px` },
552
+ children: loading ? Array.from({ length: skeletonRowCount }).map((_, rowIndex) => {
553
+ const leafColumns = table.getVisibleLeafColumns();
554
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
555
+ className: "flex w-full border-b",
556
+ style: { height: `${estimateRowHeight}px` },
557
+ children: leafColumns.map((column, columnIndex) => {
558
+ const isLast = columnIndex === leafColumns.length - 1;
559
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
560
+ className: require_utils.cn("flex items-center px-2", bordered && !isLast && "border-r"),
561
+ style: { width: `${column.getSize()}px` },
562
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_skeleton.Skeleton, { className: "h-4 w-full" })
563
+ }, column.id);
564
+ })
565
+ }, `loading-${rowIndex}`);
566
+ }) : showEmpty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
567
+ className: "flex w-full",
568
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
569
+ colSpan: table.getVisibleLeafColumns().length,
570
+ className: "text-muted-foreground flex h-24 w-full items-center justify-center",
571
+ children: empty
572
+ })
573
+ }) : virtualItems.map((virtualRow) => {
574
+ const row = rows[virtualRow.index];
575
+ const cells = row.getVisibleCells();
576
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
577
+ "data-index": virtualRow.index,
578
+ "data-state": row.getIsSelected() ? "selected" : void 0,
579
+ ref: (node) => virtualizer.measureElement(node),
580
+ className: "hover:bg-muted/50 data-[state=selected]:bg-muted absolute flex w-full border-b",
581
+ style: { transform: `translateY(${virtualRow.start}px)` },
582
+ children: cells.map((cell, cellIndex) => {
583
+ const isLast = cellIndex === cells.length - 1;
584
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
585
+ "data-slot": "table-cell",
586
+ className: require_utils.cn("text-foreground flex items-center px-2", bordered && !isLast && "border-r"),
587
+ style: {
588
+ width: `${cell.column.getSize()}px`,
589
+ height: `${estimateRowHeight}px`
590
+ },
591
+ children: (0, _tanstack_react_table.flexRender)(cell.column.columnDef.cell, cell.getContext())
592
+ }, cell.id);
593
+ })
594
+ }, row.id);
595
+ })
596
+ })]
597
+ })
598
+ });
599
+ }
600
+ //#endregion
601
+ //#region src/table/components/row-drag-handle.tsx
602
+ var RowDragHandleContext = (0, react.createContext)(null);
603
+ /** 在 cell 内任意位置放此组件即可成为行拖拽手柄 */
604
+ function RowDragHandle({ className, ...props }) {
605
+ const handle = (0, react.useContext)(RowDragHandleContext);
606
+ if (!handle) return null;
607
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
608
+ type: "button",
609
+ ref: handle.setActivatorNodeRef,
610
+ "aria-label": "拖动排序",
611
+ className: require_utils.cn("text-muted-foreground hover:text-foreground inline-flex size-6 cursor-grab items-center justify-center rounded-md focus-visible:outline-none active:cursor-grabbing", className),
612
+ ...handle.attributes,
613
+ ...handle.listeners,
614
+ ...props,
615
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.GripVerticalIcon, { className: "size-4" })
616
+ });
617
+ }
618
+ //#endregion
619
+ //#region src/table/components/data-table.tsx
620
+ var ROW_DND_HANDLE_COLUMN_ID = "__row_dnd_handle__";
621
+ var DEFAULT_SKELETON_ROWS = 8;
622
+ /** ReactNode 但语义上等同于"不渲染"的值(null / undefined / boolean) */
623
+ function isRenderablePanel(value) {
624
+ return value !== null && value !== void 0 && typeof value !== "boolean";
625
+ }
626
+ /**
627
+ * 渲染一行 cells —— 处理列合并 / 跨列 / 固定列样式
628
+ *
629
+ * 抽成共享函数,sortable 与非 sortable 两条路径都用同一份单元格生成逻辑
630
+ */
631
+ function renderRowCells(row, rowIndex, cellSpans, scrollShadow) {
632
+ return row.getVisibleCells().map((cell) => {
633
+ const span = cellSpans.get(rowIndex)?.get(cell.column.id);
634
+ const rowSpan = span?.rowSpan ?? 1;
635
+ const colSpan = span?.colSpan ?? 1;
636
+ if (rowSpan === 0 || colSpan === 0) return null;
637
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableCell, {
638
+ rowSpan: rowSpan > 1 ? rowSpan : void 0,
639
+ colSpan: colSpan > 1 ? colSpan : void 0,
640
+ style: getCommonPinningStyles(cell.column),
641
+ className: getColumnPinningClassName(cell.column, scrollShadow),
642
+ children: (0, _tanstack_react_table.flexRender)(cell.column.columnDef.cell, cell.getContext())
643
+ }, cell.id);
644
+ });
645
+ }
646
+ /** 渲染展开面板 row —— 跨满全列的单格 TableRow */
647
+ function renderExpandedPanel({ rowId, panel, visibleColumnCount, isSelected, style, isDragging }) {
648
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableRow, {
649
+ id: panelIdFor(rowId),
650
+ "data-slot": "data-table-expanded-row",
651
+ "data-state": isSelected ? "selected" : void 0,
652
+ style,
653
+ className: require_utils.cn("bg-muted/30 hover:bg-muted/30", isDragging && "shadow-md"),
654
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableCell, {
655
+ colSpan: visibleColumnCount,
656
+ className: "p-4",
657
+ children: panel
658
+ })
659
+ });
660
+ }
661
+ function buildRowDndColumn() {
662
+ return {
663
+ id: ROW_DND_HANDLE_COLUMN_ID,
664
+ size: 36,
665
+ enableSorting: false,
666
+ enableHiding: false,
667
+ enableResizing: false,
668
+ enableColumnFilter: false,
669
+ enablePinning: false,
670
+ header: "",
671
+ cell: () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowDragHandle, {})
672
+ };
673
+ }
674
+ /** 单行 sortable 包装,把 dnd-kit 属性透传到 TableRow + Context */
675
+ function SortableTableRow({ row, rowIndex, cellSpans, scrollShadow, onRowClick, expandedPanel, visibleColumnCount }) {
676
+ const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = (0, _dnd_kit_sortable.useSortable)({ id: row.id });
677
+ const style = {
678
+ transform: _dnd_kit_utilities.CSS.Transform.toString(transform),
679
+ transition,
680
+ position: isDragging ? "relative" : void 0,
681
+ zIndex: isDragging ? 1 : void 0
682
+ };
683
+ const isSelected = row.getIsSelected();
684
+ const showPanel = expandedPanel !== void 0;
685
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(RowDragHandleContext.Provider, {
686
+ value: {
687
+ attributes,
688
+ listeners,
689
+ setActivatorNodeRef,
690
+ isDragging
691
+ },
692
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableRow, {
693
+ ref: setNodeRef,
694
+ style,
695
+ "data-state": isSelected ? "selected" : void 0,
696
+ className: require_utils.cn(isDragging && "bg-muted/70 shadow-md", onRowClick && "cursor-pointer"),
697
+ onClick: onRowClick ? () => onRowClick(row.original) : void 0,
698
+ children: renderRowCells(row, rowIndex, cellSpans, scrollShadow)
699
+ }), showPanel ? renderExpandedPanel({
700
+ rowId: row.id,
701
+ panel: expandedPanel,
702
+ visibleColumnCount,
703
+ isSelected,
704
+ style,
705
+ isDragging
706
+ }) : null]
707
+ });
708
+ }
709
+ /**
710
+ * 全功能数据表格
711
+ *
712
+ * 在 `Table` 基础组件之上,基于 `@tanstack/react-table`
713
+ * 提供排序、筛选、分页、行选择、列显隐 / 拖拽 / 固定 / 调宽、行虚拟滚动、行拖拽排序等能力
714
+ *
715
+ * 可通过 `ref` 拿到底层 TanStack `table` 实例 ([[DataTableHandle]]) 进行命令式操作
716
+ */
717
+ function DataTableInner({ columns, data, loading, state, initialState, onStateChange, features, renderToolbar, renderPagination, empty = "暂无数据", pageSizeOptions, className, tableClassName, onRowClick, onRowOrderChange, rowDndHandle = "auto", tableOptions, virtual = false, bordered = false, expandedRowRender }, ref) {
718
+ const virtualEnabled = Boolean(virtual);
719
+ const virtualOptions = virtual && typeof virtual === "object" ? virtual : void 0;
720
+ const rowDndEnabled = !virtualEnabled && Boolean(features?.rowDnd);
721
+ const autoHandle = rowDndEnabled && rowDndHandle === "auto";
722
+ const expandedRowRenderEffective = virtualEnabled ? void 0 : expandedRowRender;
723
+ const virtualConflicts = virtualEnabled && Boolean(features?.rowDnd || features?.expanding || expandedRowRender);
724
+ const rowDndMissingGetRowId = rowDndEnabled && !tableOptions?.getRowId;
725
+ (0, react.useEffect)(() => {
726
+ if (virtualConflicts) console.warn("[DataTable] virtual 模式与 rowDnd / expanding / expandedRowRender 不兼容,已自动忽略相关功能");
727
+ if (rowDndMissingGetRowId) console.warn("[DataTable] 启用 features.rowDnd 时,请通过 tableOptions.getRowId 提供稳定的行 id,否则拖拽顺序会错乱");
728
+ }, [virtualConflicts, rowDndMissingGetRowId]);
729
+ const effectiveFeatures = virtualEnabled ? {
730
+ ...features,
731
+ pagination: false,
732
+ rowDnd: false,
733
+ expanding: false
734
+ } : features;
735
+ const { table, features: mergedFeatures } = useDataTable({
736
+ data,
737
+ columns: (0, react.useMemo)(() => autoHandle && !columns.some((c) => c.id === ROW_DND_HANDLE_COLUMN_ID) ? [buildRowDndColumn(), ...columns] : columns, [columns, autoHandle]),
738
+ features: effectiveFeatures,
739
+ state,
740
+ initialState,
741
+ onStateChange,
742
+ tableOptions: (0, react.useMemo)(() => {
743
+ if (!expandedRowRenderEffective) return tableOptions;
744
+ if (tableOptions?.getRowCanExpand) return tableOptions;
745
+ return {
746
+ ...tableOptions,
747
+ getRowCanExpand: () => true
748
+ };
749
+ }, [tableOptions, expandedRowRenderEffective])
750
+ });
751
+ (0, react.useImperativeHandle)(ref, () => table, [table]);
752
+ const wrapperRef = (0, react.useRef)(null);
753
+ const scrollShadow = useScrollShadow(wrapperRef);
754
+ const visibleLeafColumns = table.getVisibleLeafColumns();
755
+ const visibleColumnCount = visibleLeafColumns.length;
756
+ const rows = table.getRowModel().rows;
757
+ const cellSpans = computeCellSpans(rows, visibleLeafColumns);
758
+ const rowIds = rows.map((row) => row.id);
759
+ const sensors = (0, _dnd_kit_core.useSensors)((0, _dnd_kit_core.useSensor)(_dnd_kit_core.PointerSensor, { activationConstraint: { distance: 5 } }), (0, _dnd_kit_core.useSensor)(_dnd_kit_core.KeyboardSensor, { coordinateGetter: _dnd_kit_sortable.sortableKeyboardCoordinates }));
760
+ const handleDragEnd = (event) => {
761
+ const { active, over } = event;
762
+ if (!over || active.id === over.id) return;
763
+ const oldIndex = rows.findIndex((row) => row.id === String(active.id));
764
+ const newIndex = rows.findIndex((row) => row.id === String(over.id));
765
+ if (oldIndex === -1 || newIndex === -1) return;
766
+ const nextData = (0, _dnd_kit_sortable.arrayMove)(data, oldIndex, newIndex);
767
+ onRowOrderChange?.(nextData, {
768
+ oldIndex,
769
+ newIndex,
770
+ activeId: String(active.id),
771
+ overId: String(over.id)
772
+ });
773
+ };
774
+ const toolbarNode = renderToolbar?.(table);
775
+ const skeletonRowCount = mergedFeatures.pagination ? table.getState().pagination.pageSize : DEFAULT_SKELETON_ROWS;
776
+ const bodyContent = loading ? Array.from({ length: skeletonRowCount }).map((_, rowIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableRow, { children: table.getVisibleLeafColumns().map((column) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableCell, {
777
+ style: getCommonPinningStyles(column),
778
+ className: getColumnPinningClassName(column, scrollShadow),
779
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_skeleton.Skeleton, { className: "h-4 w-full" })
780
+ }, column.id)) }, `loading-${rowIndex}`)) : rows.length ? rows.map((row, rowIndex) => {
781
+ const rawPanel = expandedRowRenderEffective && row.getIsExpanded() ? expandedRowRenderEffective(row) : void 0;
782
+ const expandedPanel = isRenderablePanel(rawPanel) ? rawPanel : void 0;
783
+ const isSelected = row.getIsSelected();
784
+ if (rowDndEnabled) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SortableTableRow, {
785
+ row,
786
+ rowIndex,
787
+ cellSpans,
788
+ scrollShadow,
789
+ onRowClick,
790
+ expandedPanel,
791
+ visibleColumnCount
792
+ }, row.id);
793
+ if (expandedPanel === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableRow, {
794
+ "data-state": isSelected ? "selected" : void 0,
795
+ className: onRowClick ? "cursor-pointer" : void 0,
796
+ onClick: onRowClick ? () => onRowClick(row.original) : void 0,
797
+ children: renderRowCells(row, rowIndex, cellSpans, scrollShadow)
798
+ }, row.id);
799
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableRow, {
800
+ "data-state": isSelected ? "selected" : void 0,
801
+ className: onRowClick ? "cursor-pointer" : void 0,
802
+ onClick: onRowClick ? () => onRowClick(row.original) : void 0,
803
+ children: renderRowCells(row, rowIndex, cellSpans, scrollShadow)
804
+ }), renderExpandedPanel({
805
+ rowId: row.id,
806
+ panel: expandedPanel,
807
+ visibleColumnCount,
808
+ isSelected
809
+ })] }, row.id);
810
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableRow, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableCell, {
811
+ colSpan: visibleColumnCount,
812
+ className: "text-muted-foreground h-24 text-center",
813
+ children: empty
814
+ }) });
815
+ const standardBody = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
816
+ ref: wrapperRef,
817
+ className: require_utils.cn("rounded-md", bordered && "border [&_td]:border-r [&_th]:border-r [&_tr>*:last-child]:border-r-0", tableClassName),
818
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Table, {
819
+ className: "min-w-max",
820
+ style: mergedFeatures.columnResizing ? { width: table.getTotalSize() } : void 0,
821
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableRow, { children: headerGroup.headers.map((header) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableColumnHeader, {
822
+ header,
823
+ resizable: mergedFeatures.columnResizing,
824
+ scrollShadow
825
+ }, header.id)) }, headerGroup.id)) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TableBody, { children: rowDndEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_dnd_kit_sortable.SortableContext, {
826
+ items: rowIds,
827
+ strategy: _dnd_kit_sortable.verticalListSortingStrategy,
828
+ children: bodyContent
829
+ }) : bodyContent })]
830
+ })
831
+ });
832
+ const bodyNode = virtualEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableVirtualBody, {
833
+ table,
834
+ loading,
835
+ empty,
836
+ options: virtualOptions,
837
+ bordered,
838
+ className: tableClassName
839
+ }) : rowDndEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_dnd_kit_core.DndContext, {
840
+ sensors,
841
+ collisionDetection: _dnd_kit_core.closestCenter,
842
+ modifiers: [_dnd_kit_modifiers.restrictToVerticalAxis],
843
+ onDragEnd: handleDragEnd,
844
+ children: standardBody
845
+ }) : standardBody;
846
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
847
+ "data-slot": "data-table",
848
+ className: require_utils.cn("flex flex-col", className),
849
+ children: [
850
+ toolbarNode,
851
+ bodyNode,
852
+ mergedFeatures.pagination && (renderPagination !== void 0 ? renderPagination(table) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTablePagination, {
853
+ table,
854
+ pageSizeOptions
855
+ }))
856
+ ]
857
+ });
858
+ }
859
+ /**
860
+ * forwardRef 包装,泛型用 cast 保留 —— 否则会被 forwardRef 的签名擦除成 unknown
861
+ */
862
+ var DataTable = (0, react.forwardRef)(DataTableInner);
863
+ //#endregion
864
+ //#region src/table/components/data-table-view-options.tsx
865
+ /**
866
+ * 列显隐 / 排序 / 固定 设置面板
867
+ *
868
+ * 通过 DropdownMenu 弹出列表,内置 DnD-Kit 排序与固定按钮
869
+ */
870
+ function DataTableViewOptions({ table, enableOrdering = false, enablePinning = false, trigger }) {
871
+ const draggableColumns = table.getAllLeafColumns().filter((column) => column.getCanHide());
872
+ const draggableColumnIds = draggableColumns.map((column) => column.id);
873
+ const sensors = (0, _dnd_kit_core.useSensors)((0, _dnd_kit_core.useSensor)(_dnd_kit_core.PointerSensor, { activationConstraint: { distance: 5 } }), (0, _dnd_kit_core.useSensor)(_dnd_kit_core.KeyboardSensor, { coordinateGetter: _dnd_kit_sortable.sortableKeyboardCoordinates }));
874
+ const handleDragEnd = ({ active, over }) => {
875
+ if (!over || active.id === over.id) return;
876
+ const currentOrder = table.getState().columnOrder;
877
+ const allColumnIds = table.getAllLeafColumns().map((column) => column.id);
878
+ const orderedColumnIds = currentOrder.length ? currentOrder.filter((columnId) => allColumnIds.includes(columnId)) : allColumnIds;
879
+ const nextOrder = [...orderedColumnIds, ...allColumnIds.filter((columnId) => !orderedColumnIds.includes(columnId))];
880
+ const oldIndex = nextOrder.indexOf(String(active.id));
881
+ const newIndex = nextOrder.indexOf(String(over.id));
882
+ if (oldIndex === -1 || newIndex === -1) return;
883
+ table.setColumnOrder((0, _dnd_kit_sortable.arrayMove)(nextOrder, oldIndex, newIndex));
884
+ };
885
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_dropdown_menu.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dropdown_menu.DropdownMenuTrigger, { render: trigger ?? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_button.Button, {
886
+ variant: "outline",
887
+ size: "sm",
888
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Settings2Icon, {}), "视图"]
889
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_dropdown_menu.DropdownMenuContent, {
890
+ align: "end",
891
+ className: "min-w-56",
892
+ children: [
893
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dropdown_menu.DropdownMenuGroup, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dropdown_menu.DropdownMenuLabel, { children: "显示的列" }) }),
894
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dropdown_menu.DropdownMenuSeparator, {}),
895
+ enableOrdering ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_dnd_kit_core.DndContext, {
896
+ sensors,
897
+ collisionDetection: _dnd_kit_core.closestCenter,
898
+ modifiers: [_dnd_kit_modifiers.restrictToVerticalAxis],
899
+ onDragEnd: handleDragEnd,
900
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_dnd_kit_sortable.SortableContext, {
901
+ items: enableOrdering ? draggableColumnIds : [],
902
+ strategy: _dnd_kit_sortable.verticalListSortingStrategy,
903
+ children: draggableColumns.map((column) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SortableColumnItem, {
904
+ column,
905
+ enablePinning
906
+ }, column.id))
907
+ })
908
+ }) : draggableColumns.map((column) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColumnVisibilityItem, {
909
+ column,
910
+ enablePinning
911
+ }, column.id))
912
+ ]
913
+ })] });
914
+ }
915
+ /**
916
+ * 列设置项内部统一渲染:复选 + 列名 + 可选固定操作
917
+ */
918
+ function ColumnItemBody({ column, enablePinning, dragHandle }) {
919
+ const pinned = column.getIsPinned();
920
+ const canPin = enablePinning && column.getCanPin();
921
+ const handlePinClick = (event, position) => {
922
+ event.preventDefault();
923
+ event.stopPropagation();
924
+ column.pin(position);
925
+ };
926
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_dropdown_menu.DropdownMenuCheckboxItem, {
927
+ className: "capitalize",
928
+ checked: column.getIsVisible(),
929
+ onCheckedChange: (value) => column.toggleVisibility(!!value),
930
+ children: [
931
+ dragHandle,
932
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
933
+ className: "min-w-20 flex-1",
934
+ children: column.id
935
+ }),
936
+ canPin && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
937
+ className: "ml-auto mr-5 flex items-center gap-1 normal-case",
938
+ onClick: (event) => event.stopPropagation(),
939
+ onPointerDown: (event) => event.stopPropagation(),
940
+ children: [
941
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
942
+ type: "button",
943
+ variant: pinned === "left" ? "default" : "ghost",
944
+ size: "xs",
945
+ onClick: (event) => handlePinClick(event, "left"),
946
+ children: "左"
947
+ }),
948
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
949
+ type: "button",
950
+ variant: pinned ? "ghost" : "default",
951
+ size: "xs",
952
+ onClick: (event) => handlePinClick(event, false),
953
+ children: "无"
954
+ }),
955
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
956
+ type: "button",
957
+ variant: pinned === "right" ? "default" : "ghost",
958
+ size: "xs",
959
+ onClick: (event) => handlePinClick(event, "right"),
960
+ children: "右"
961
+ })
962
+ ]
963
+ })
964
+ ]
965
+ });
966
+ }
967
+ function ColumnVisibilityItem({ column, enablePinning }) {
968
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColumnItemBody, {
969
+ column,
970
+ enablePinning
971
+ });
972
+ }
973
+ function SortableColumnItem({ column, enablePinning }) {
974
+ const { attributes, listeners, setActivatorNodeRef, setNodeRef, transform, transition, isDragging } = (0, _dnd_kit_sortable.useSortable)({ id: column.id });
975
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
976
+ ref: setNodeRef,
977
+ style: {
978
+ transform: _dnd_kit_utilities.CSS.Transform.toString(transform),
979
+ transition
980
+ },
981
+ className: require_utils.cn(isDragging && "relative z-10 opacity-80"),
982
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ColumnItemBody, {
983
+ column,
984
+ enablePinning,
985
+ dragHandle: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
986
+ ref: setActivatorNodeRef,
987
+ className: "cursor-grab active:cursor-grabbing",
988
+ "aria-label": `Reorder ${column.id} column`,
989
+ onClick: (event) => event.stopPropagation(),
990
+ ...attributes,
991
+ ...listeners,
992
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.GripVerticalIcon, { className: "size-[1em]" })
993
+ })
994
+ })
995
+ });
996
+ }
997
+ //#endregion
998
+ //#region src/table/components/data-table-toolbar.tsx
999
+ /**
1000
+ * 表格工具栏:搜索 + 视图配置 + 自定义扩展
1001
+ */
1002
+ function DataTableToolbar({ table, searchColumnId, searchPlaceholder = "搜索...", showViewOptions = true, enableColumnOrdering, enableColumnPinning, extra, className }) {
1003
+ const searchColumn = searchColumnId ? table.getColumn(searchColumnId) : void 0;
1004
+ const useGlobalFilter = !searchColumn;
1005
+ const value = useGlobalFilter ? table.getState().globalFilter : searchColumn?.getFilterValue();
1006
+ const hasValue = Boolean(value);
1007
+ const handleChange = (next) => {
1008
+ if (useGlobalFilter) table.setGlobalFilter(next);
1009
+ else searchColumn?.setFilterValue(next || void 0);
1010
+ };
1011
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1012
+ "data-slot": "data-table-toolbar",
1013
+ className: require_utils.cn("flex flex-wrap items-center gap-2 py-3", className),
1014
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1015
+ className: "relative max-w-sm flex-1",
1016
+ children: [
1017
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.SearchIcon, { className: "text-muted-foreground pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2" }),
1018
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_input.Input, {
1019
+ placeholder: searchPlaceholder,
1020
+ value: value ?? "",
1021
+ onChange: (event) => handleChange(event.target.value),
1022
+ className: "pl-8 pr-8"
1023
+ }),
1024
+ hasValue && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
1025
+ type: "button",
1026
+ variant: "ghost",
1027
+ size: "icon-xs",
1028
+ className: "absolute right-1 top-1/2 -translate-y-1/2",
1029
+ "aria-label": "清除搜索",
1030
+ onClick: () => handleChange(""),
1031
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XIcon, {})
1032
+ })
1033
+ ]
1034
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1035
+ className: "ml-auto flex items-center gap-2",
1036
+ children: [extra, showViewOptions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableViewOptions, {
1037
+ table,
1038
+ enableOrdering: enableColumnOrdering,
1039
+ enablePinning: enableColumnPinning
1040
+ })]
1041
+ })]
1042
+ });
1043
+ }
1044
+ //#endregion
1045
+ //#region src/table/components/row-expand-toggle.tsx
1046
+ /**
1047
+ * 行展开切换按钮:基于 `row.getToggleExpandedHandler` + `row.depth` 自动缩进
1048
+ *
1049
+ * 配合 `features.expanding` 与 `tableOptions.getSubRows`(嵌套树),或 `expandedRowRender`(展开面板)使用
1050
+ */
1051
+ function RowExpandToggle({ row, indent = 16, placeholder = true, className, ...props }) {
1052
+ const paddingLeft = indent > 0 ? row.depth * indent : 0;
1053
+ const canExpand = row.getCanExpand();
1054
+ const isExpanded = row.getIsExpanded();
1055
+ if (!canExpand) {
1056
+ if (!placeholder) return null;
1057
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1058
+ "aria-hidden": true,
1059
+ className: "inline-flex size-6 shrink-0",
1060
+ style: paddingLeft ? { marginLeft: paddingLeft } : void 0
1061
+ });
1062
+ }
1063
+ const hasPanelTarget = row.subRows.length === 0;
1064
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1065
+ type: "button",
1066
+ "aria-label": isExpanded ? "收起" : "展开",
1067
+ "aria-expanded": isExpanded,
1068
+ "aria-controls": isExpanded && hasPanelTarget ? panelIdFor(row.id) : void 0,
1069
+ onClick: row.getToggleExpandedHandler(),
1070
+ className: require_utils.cn("text-muted-foreground hover:text-foreground inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-colors focus-visible:outline-none", className),
1071
+ style: paddingLeft ? { marginLeft: paddingLeft } : void 0,
1072
+ ...props,
1073
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRightIcon, { className: require_utils.cn("size-4 transition-transform", isExpanded ? "rotate-90" : "rotate-0") })
1074
+ });
1075
+ }
1076
+ //#endregion
1077
+ Object.defineProperty(exports, "DataTable", {
1078
+ enumerable: true,
1079
+ get: function() {
1080
+ return DataTable;
1081
+ }
1082
+ });
1083
+ Object.defineProperty(exports, "DataTableColumnHeader", {
1084
+ enumerable: true,
1085
+ get: function() {
1086
+ return DataTableColumnHeader;
1087
+ }
1088
+ });
1089
+ Object.defineProperty(exports, "DataTablePagination", {
1090
+ enumerable: true,
1091
+ get: function() {
1092
+ return DataTablePagination;
1093
+ }
1094
+ });
1095
+ Object.defineProperty(exports, "DataTableToolbar", {
1096
+ enumerable: true,
1097
+ get: function() {
1098
+ return DataTableToolbar;
1099
+ }
1100
+ });
1101
+ Object.defineProperty(exports, "DataTableViewOptions", {
1102
+ enumerable: true,
1103
+ get: function() {
1104
+ return DataTableViewOptions;
1105
+ }
1106
+ });
1107
+ Object.defineProperty(exports, "DataTableVirtualBody", {
1108
+ enumerable: true,
1109
+ get: function() {
1110
+ return DataTableVirtualBody;
1111
+ }
1112
+ });
1113
+ Object.defineProperty(exports, "RowDragHandle", {
1114
+ enumerable: true,
1115
+ get: function() {
1116
+ return RowDragHandle;
1117
+ }
1118
+ });
1119
+ Object.defineProperty(exports, "RowDragHandleContext", {
1120
+ enumerable: true,
1121
+ get: function() {
1122
+ return RowDragHandleContext;
1123
+ }
1124
+ });
1125
+ Object.defineProperty(exports, "RowExpandToggle", {
1126
+ enumerable: true,
1127
+ get: function() {
1128
+ return RowExpandToggle;
1129
+ }
1130
+ });
1131
+ Object.defineProperty(exports, "Table", {
1132
+ enumerable: true,
1133
+ get: function() {
1134
+ return Table;
1135
+ }
1136
+ });
1137
+ Object.defineProperty(exports, "TableBody", {
1138
+ enumerable: true,
1139
+ get: function() {
1140
+ return TableBody;
1141
+ }
1142
+ });
1143
+ Object.defineProperty(exports, "TableCaption", {
1144
+ enumerable: true,
1145
+ get: function() {
1146
+ return TableCaption;
1147
+ }
1148
+ });
1149
+ Object.defineProperty(exports, "TableCell", {
1150
+ enumerable: true,
1151
+ get: function() {
1152
+ return TableCell;
1153
+ }
1154
+ });
1155
+ Object.defineProperty(exports, "TableFooter", {
1156
+ enumerable: true,
1157
+ get: function() {
1158
+ return TableFooter;
1159
+ }
1160
+ });
1161
+ Object.defineProperty(exports, "TableHead", {
1162
+ enumerable: true,
1163
+ get: function() {
1164
+ return TableHead;
1165
+ }
1166
+ });
1167
+ Object.defineProperty(exports, "TableHeader", {
1168
+ enumerable: true,
1169
+ get: function() {
1170
+ return TableHeader;
1171
+ }
1172
+ });
1173
+ Object.defineProperty(exports, "TableRow", {
1174
+ enumerable: true,
1175
+ get: function() {
1176
+ return TableRow;
1177
+ }
1178
+ });
1179
+ Object.defineProperty(exports, "computeCellSpans", {
1180
+ enumerable: true,
1181
+ get: function() {
1182
+ return computeCellSpans;
1183
+ }
1184
+ });
1185
+ Object.defineProperty(exports, "getColumnPinningClassName", {
1186
+ enumerable: true,
1187
+ get: function() {
1188
+ return getColumnPinningClassName;
1189
+ }
1190
+ });
1191
+ Object.defineProperty(exports, "getCommonPinningStyles", {
1192
+ enumerable: true,
1193
+ get: function() {
1194
+ return getCommonPinningStyles;
1195
+ }
1196
+ });
1197
+ Object.defineProperty(exports, "panelIdFor", {
1198
+ enumerable: true,
1199
+ get: function() {
1200
+ return panelIdFor;
1201
+ }
1202
+ });
1203
+ Object.defineProperty(exports, "useDataTable", {
1204
+ enumerable: true,
1205
+ get: function() {
1206
+ return useDataTable;
1207
+ }
1208
+ });
1209
+ Object.defineProperty(exports, "useScrollShadow", {
1210
+ enumerable: true,
1211
+ get: function() {
1212
+ return useScrollShadow;
1213
+ }
1214
+ });
1215
+
1216
+ //# sourceMappingURL=table-FocfdaF7.js.map