@canvas-components/react-list-table 0.3.37 → 0.3.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3573,6 +3573,37 @@ var toolButtonStyle = {
3573
3573
  lineHeight: 1,
3574
3574
  cursor: "pointer"
3575
3575
  };
3576
+ function readExportCache(storageKey) {
3577
+ if (!storageKey) return null;
3578
+ try {
3579
+ const value = JSON.parse(localStorage.getItem(`canvas-table-export:${storageKey}`) ?? "null");
3580
+ if (value?.version !== 1 || !Array.isArray(value.rows) || !Array.isArray(value.selectedKeys) || !value.selectedKeys.every((key) => typeof key === "string") || typeof value.includeSummary !== "boolean") return null;
3581
+ if (!value.rows.every((row) => row && typeof row.key === "string" && ["default", "ascend", "descend"].includes(row.sortOrder) && summaryOptions.some((option) => option.value === row.summaryType) && roundingOptions.some((option) => option.value === row.roundingMode) && ["yes", "no"].includes(row.showTypePrefix) && ["yes", "no"].includes(row.preserveTrailingZeros) && (row.precision == null || Number.isInteger(row.precision) && row.precision >= 0 && row.precision <= 20) && (row.sortIndex == null || Number.isFinite(row.sortIndex)) && (row.sortPriority == null || Number.isFinite(row.sortPriority)))) return null;
3582
+ return value;
3583
+ } catch {
3584
+ return null;
3585
+ }
3586
+ }
3587
+ function writeExportCache(storageKey, value) {
3588
+ if (!storageKey) return;
3589
+ try {
3590
+ const key = `canvas-table-export:${storageKey}`;
3591
+ if (value) localStorage.setItem(key, JSON.stringify(value));
3592
+ else localStorage.removeItem(key);
3593
+ } catch {
3594
+ }
3595
+ }
3596
+ function restoreExportRows(columns, cache) {
3597
+ const current = new Map(createExportDialogRows(columns).map((row) => [row.key, row]));
3598
+ const rows = [];
3599
+ cache?.rows.forEach((saved) => {
3600
+ const row = current.get(saved.key);
3601
+ if (!row) return;
3602
+ rows.push({ ...saved, key: row.key, title: row.title });
3603
+ current.delete(saved.key);
3604
+ });
3605
+ return [...rows, ...current.values()];
3606
+ }
3576
3607
  var sortOptions = [
3577
3608
  { value: "default", label: "\u9ED8\u8BA4" },
3578
3609
  { value: "ascend", label: "\u5347\u5E8F" },
@@ -3644,18 +3675,34 @@ function buildExportDialogSettings(rows, selectedKeys, includeSummary) {
3644
3675
  function ReactListTableExportNameDialog({
3645
3676
  request,
3646
3677
  onClose,
3647
- renderTable
3678
+ renderTable,
3679
+ storageKey
3648
3680
  }) {
3649
3681
  const tableId = react.useId();
3682
+ const tableHostRef = react.useRef(null);
3683
+ const [tableWidth, setTableWidth] = react.useState(0);
3684
+ react.useEffect(() => {
3685
+ const host = tableHostRef.current;
3686
+ if (!host || typeof ResizeObserver === "undefined") return;
3687
+ const update = () => setTableWidth(host.clientWidth);
3688
+ update();
3689
+ const observer = new ResizeObserver(update);
3690
+ observer.observe(host);
3691
+ return () => observer.disconnect();
3692
+ }, []);
3650
3693
  const extension = request.fileName.match(/\.(xlsx|csv|txt|json)$/i)?.[0] ?? "";
3651
3694
  const [fileName, setFileName] = react.useState(
3652
3695
  extension ? request.fileName.slice(0, -extension.length) : request.fileName
3653
3696
  );
3654
- const [rows, setRows] = react.useState(() => createExportDialogRows(request.columns ?? []));
3697
+ const [cached] = react.useState(() => readExportCache(storageKey));
3698
+ const [cacheEnabled, setCacheEnabled] = react.useState(Boolean(storageKey));
3699
+ const [rows, setRows] = react.useState(() => restoreExportRows(request.columns ?? [], cached));
3655
3700
  const [selectedKeys, setSelectedKeys] = react.useState(
3656
- () => (request.columns ?? []).filter((column) => column.selected !== false).map((column) => column.key)
3701
+ () => (request.columns ?? []).filter((column) => cached?.rows.some((row) => row.key === column.key) ? cached.selectedKeys.includes(column.key) : column.selected !== false).map((column) => column.key)
3702
+ );
3703
+ const [includeSummary, setIncludeSummary] = react.useState(
3704
+ () => cached?.includeSummary ?? (request.includeSummary === true || (request.columns?.some((column) => column.summary.type !== "none") ?? false))
3657
3705
  );
3658
- const [includeSummary, setIncludeSummary] = react.useState(request.includeSummary ?? false);
3659
3706
  const [error, setError] = react.useState("");
3660
3707
  const columns = react.useMemo(
3661
3708
  () => [
@@ -3733,9 +3780,16 @@ function ReactListTableExportNameDialog({
3733
3780
  ],
3734
3781
  [includeSummary, request.columns]
3735
3782
  );
3783
+ const fittedColumns = react.useMemo(
3784
+ () => fitExportDialogColumns(columns, tableWidth),
3785
+ [columns, tableWidth]
3786
+ );
3736
3787
  const confirm = () => {
3737
3788
  if (!fileName.trim()) return setError("\u8BF7\u8F93\u5165\u6587\u4EF6\u540D\u79F0");
3738
3789
  if (request.columns && !selectedKeys.length) return setError("\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u5217");
3790
+ if (request.columns && cacheEnabled) {
3791
+ writeExportCache(storageKey, { version: 1, rows, selectedKeys: selectedKeys.map(String), includeSummary });
3792
+ }
3739
3793
  request.onConfirm(
3740
3794
  `${fileName.trim()}${extension}`,
3741
3795
  request.columns ? buildExportDialogSettings(rows, selectedKeys, includeSummary) : void 0
@@ -3747,7 +3801,7 @@ function ReactListTableExportNameDialog({
3747
3801
  {
3748
3802
  open: true,
3749
3803
  title: "\u5BFC\u51FA\u6587\u4EF6",
3750
- width: request.columns ? 1180 : 420,
3804
+ width: request.columns ? includeSummary ? 1320 : 1180 : 420,
3751
3805
  fullscreenable: false,
3752
3806
  onOk: confirm,
3753
3807
  okText: "\u5BFC\u51FA",
@@ -3780,6 +3834,27 @@ function ReactListTableExportNameDialog({
3780
3834
  request.columns ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 16 }, children: [
3781
3835
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 16, marginBottom: 8 }, children: [
3782
3836
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600 }, children: "\u5BFC\u51FA\u8868\u683C\u5217" }),
3837
+ storageKey ? /* @__PURE__ */ jsxRuntime.jsxs(
3838
+ "label",
3839
+ {
3840
+ style: { display: "inline-flex", alignItems: "center", gap: 6 },
3841
+ title: "\u5BFC\u51FA\u65F6\u4FDD\u5B58\u914D\u7F6E\uFF0C\u4E0B\u6B21\u6253\u5F00\u81EA\u52A8\u6062\u590D\uFF1B\u53D6\u6D88\u52FE\u9009\u6E05\u9664\u7F13\u5B58",
3842
+ children: [
3843
+ /* @__PURE__ */ jsxRuntime.jsx(
3844
+ reactComponents.ReactCheckbox,
3845
+ {
3846
+ checked: cacheEnabled,
3847
+ "aria-label": "\u7F13\u5B58\u5BFC\u51FA\u914D\u7F6E",
3848
+ onChange: (event) => {
3849
+ setCacheEnabled(event.target.checked);
3850
+ if (!event.target.checked) writeExportCache(storageKey, null);
3851
+ }
3852
+ }
3853
+ ),
3854
+ "\u7F13\u5B58\u5BFC\u51FA\u914D\u7F6E"
3855
+ ]
3856
+ }
3857
+ ) : null,
3783
3858
  /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
3784
3859
  /* @__PURE__ */ jsxRuntime.jsx(
3785
3860
  reactComponents.ReactCheckbox,
@@ -3792,9 +3867,9 @@ function ReactListTableExportNameDialog({
3792
3867
  "\u5BFC\u51FA\u6C47\u603B\u884C"
3793
3868
  ] })
3794
3869
  ] }),
3795
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "min(45vh, 520px)", minHeight: 250, overflow: "hidden" }, children: renderTable({
3870
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: tableHostRef, style: { height: "min(45vh, 520px)", minHeight: 250, overflow: "hidden" }, children: renderTable({
3796
3871
  tableId,
3797
- columns,
3872
+ columns: fittedColumns,
3798
3873
  dataSource: rows,
3799
3874
  rowKey: "key",
3800
3875
  rowHeight: 42,
@@ -3839,6 +3914,17 @@ function ReactListTableExportNameDialog({
3839
3914
  }
3840
3915
  );
3841
3916
  }
3917
+ function fitExportDialogColumns(columns, availableWidth) {
3918
+ if (availableWidth <= 0) return columns;
3919
+ const totalWidth = (items) => items.reduce((sum, column) => sum + (column.key === listTable.ROW_SELECTION_COLUMN.key ? 0 : column.children?.length ? totalWidth(column.children) : Number(column.width) || 160), 0);
3920
+ const ratio = Math.min(1, Math.max(availableWidth - 54 - 32, 1) / totalWidth(columns));
3921
+ const fit = (items) => items.map((column) => {
3922
+ if (column.key === listTable.ROW_SELECTION_COLUMN.key) return column;
3923
+ if (column.children?.length) return { ...column, children: fit(column.children) };
3924
+ return { ...column, width: Math.max(1, Math.floor((Number(column.width) || 160) * ratio)), minWidth: 1 };
3925
+ });
3926
+ return fit(columns);
3927
+ }
3842
3928
 
3843
3929
  // src/features/editor/utils.ts
3844
3930
  function toNumberValue(value) {
@@ -4359,6 +4445,10 @@ function SelectEditor(props) {
4359
4445
  handleEditorBlur(event, skipBlurCommitRef, request);
4360
4446
  },
4361
4447
  onKeyDown: (event) => {
4448
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
4449
+ event.stopPropagation();
4450
+ return;
4451
+ }
4362
4452
  if (handleEditorNavigationKey(event, request, skipBlurCommitRef)) {
4363
4453
  return;
4364
4454
  }
@@ -5222,7 +5312,7 @@ function resolveSortableTransform(index, sourceIndex, targetIndex, height) {
5222
5312
  }
5223
5313
 
5224
5314
  // src/features/toolbar/style.css
5225
- styleInject(".react-list-table-shell {\n background: #fff;\n font-size: 14px;\n}\n.react-list-table-titlebar {\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0;\n box-sizing: border-box;\n background: #fff;\n}\n.react-list-table-shell-fullscreen {\n width: 100% !important;\n height: 100% !important;\n}\n.react-list-table-shell-fullscreen .canvas-list-table-root {\n height: 100% !important;\n}\n.react-list-table-title {\n min-width: 0;\n color: #1f2937;\n font-size: 14px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-tools {\n display: flex;\n align-items: center;\n gap: 14px;\n color: #475569;\n}\n.react-list-table-tool-wrap {\n position: relative;\n display: inline-flex;\n}\n.react-list-table-tool {\n width: 20px;\n height: 24px;\n padding: 0;\n border: 0;\n color: inherit;\n background: transparent;\n font: 16px/24px sans-serif;\n cursor: pointer;\n}\n.react-list-table-tool:hover {\n color: #1677ff;\n}\n.react-list-table-tool-menu,\n.react-list-table-column-setting {\n position: absolute;\n z-index: 120;\n top: 28px;\n right: 0;\n min-width: 112px;\n padding: 6px;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n background: #fff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.14);\n}\n.react-list-table-tool-menu button {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: #374151;\n font-size: 14px;\n text-align: left;\n cursor: pointer;\n}\n.react-list-table-tool-menu {\n width: max-content;\n min-width: 0;\n padding: 8px;\n border-radius: 8px;\n}\n.react-list-table-tool-menu-check {\n width: 16px;\n flex: 0 0 16px;\n margin-right: 6px;\n text-align: center;\n}\n.react-list-table-tool-menu button:hover {\n background: #f3f4f6;\n}\n.react-list-table-column-setting {\n width: 328px;\n max-height: 360px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.react-list-table-column-setting-head {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 4px 6px 8px;\n border-bottom: 1px solid #f0f0f0;\n flex: 0 0 auto;\n}\n.react-list-table-column-setting-head button {\n border: 0;\n color: #1677ff;\n background: transparent;\n cursor: pointer;\n}\n.react-list-table-column-setting-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n.react-list-table-column-item {\n display: flex;\n align-items: center;\n gap: 8px;\n justify-content: space-between;\n padding: 7px 6px;\n box-sizing: border-box;\n cursor: pointer;\n}\n.react-list-table-column-main,\n.react-list-table-column-actions {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.react-list-table-column-main {\n min-width: 0;\n}\n.react-list-table-column-actions {\n flex: 0 0 auto;\n}\n.react-list-table-column-actions > .canvas-react-select-wrap {\n flex: 0 0 96px;\n width: 96px;\n height: 24px;\n}\n.react-list-table-column-label {\n min-width: 64px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-column-width-input {\n height: 22px;\n padding-inline: 5px;\n}\n.react-list-table-column-fixed-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-sort-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-setting label.dragging {\n opacity: 0.45;\n background: #e6f4ff;\n}\n.react-list-table-column-setting [data-sortable-key].dragging {\n opacity: 0.28;\n}\n.react-list-table-column-drag {\n touch-action: none;\n color: #94a3b8;\n display: inline-flex;\n font-size: 14px;\n cursor: grab;\n user-select: none;\n}\nbody.react-list-table-sorting,\nbody.react-list-table-sorting * {\n cursor: grabbing !important;\n}\n.react-list-table-sort-overlay {\n position: fixed;\n z-index: 1400;\n display: block;\n padding: 0;\n box-sizing: border-box;\n color: #334155;\n background: #fff;\n border: 1px solid #91caff;\n border-radius: 6px;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);\n pointer-events: none;\n}\n.react-list-table-sort-overlay .react-list-table-column-item {\n width: 100%;\n height: 100%;\n}\n");
5315
+ styleInject(".react-list-table-shell {\n background: #fff;\n font-size: 14px;\n}\n.react-list-table-titlebar {\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0;\n box-sizing: border-box;\n background: #fff;\n}\n.react-list-table-shell-fullscreen {\n width: 100% !important;\n height: 100% !important;\n}\n.react-list-table-shell-fullscreen .canvas-list-table-root {\n height: 100% !important;\n}\n.react-list-table-title {\n min-width: 0;\n color: #1f2937;\n font-size: 14px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-tools {\n display: flex;\n align-items: center;\n gap: 14px;\n color: #475569;\n}\n.react-list-table-tool-wrap {\n position: relative;\n display: inline-flex;\n}\n.react-list-table-tool {\n width: 20px;\n height: 24px;\n padding: 0;\n border: 0;\n color: inherit;\n background: transparent;\n font: 16px/24px sans-serif;\n cursor: pointer;\n}\n.react-list-table-tool:hover {\n color: #1677ff;\n}\n.react-list-table-tool-menu,\n.react-list-table-column-setting {\n position: absolute;\n z-index: 120;\n top: 28px;\n right: 0;\n min-width: 112px;\n padding: 6px;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n background: #fff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.14);\n}\n.react-list-table-tool-menu button {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: #374151;\n font-size: 14px;\n text-align: left;\n cursor: pointer;\n}\n.react-list-table-tool-menu {\n width: max-content;\n min-width: 0;\n padding: 8px;\n border-radius: 8px;\n}\n.react-list-table-tool-menu-check {\n width: 16px;\n flex: 0 0 16px;\n margin-right: 6px;\n text-align: center;\n}\n.react-list-table-tool-menu button:hover {\n background: #f3f4f6;\n}\n.react-list-table-column-setting {\n width: 328px;\n max-height: 360px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.react-list-table-column-setting-head {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 4px 6px 8px;\n border-bottom: 1px solid #f0f0f0;\n flex: 0 0 auto;\n}\n.react-list-table-column-setting-head button {\n border: 0;\n color: #1677ff;\n background: transparent;\n cursor: pointer;\n}\n.react-list-table-column-setting-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n.react-list-table-column-item {\n display: flex;\n align-items: center;\n gap: 8px;\n justify-content: space-between;\n padding: 7px 6px;\n box-sizing: border-box;\n cursor: pointer;\n}\n.react-list-table-column-position {\n flex: 0 0 28px;\n text-align: center;\n color: #64748b;\n font-variant-numeric: tabular-nums;\n}\n.react-list-table-column-main,\n.react-list-table-column-actions {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.react-list-table-column-main {\n min-width: 0;\n}\n.react-list-table-column-actions {\n flex: 0 0 auto;\n}\n.react-list-table-column-actions > .canvas-react-select-wrap {\n flex: 0 0 96px;\n width: 96px;\n height: 24px;\n}\n.react-list-table-column-label {\n min-width: 64px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-column-width-input {\n height: 22px;\n padding-inline: 5px;\n}\n.react-list-table-column-fixed-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-sort-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-setting label.dragging {\n opacity: 0.45;\n background: #e6f4ff;\n}\n.react-list-table-column-setting [data-sortable-key].dragging {\n opacity: 0.28;\n}\n.react-list-table-column-drag {\n touch-action: none;\n color: #94a3b8;\n display: inline-flex;\n font-size: 14px;\n cursor: grab;\n user-select: none;\n}\nbody.react-list-table-sorting,\nbody.react-list-table-sorting * {\n cursor: grabbing !important;\n}\n.react-list-table-sort-overlay {\n position: fixed;\n z-index: 1400;\n display: block;\n padding: 0;\n box-sizing: border-box;\n color: #334155;\n background: #fff;\n border: 1px solid #91caff;\n border-radius: 6px;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);\n pointer-events: none;\n}\n.react-list-table-sort-overlay .react-list-table-column-item {\n width: 100%;\n height: 100%;\n}\n");
5226
5316
  function ReactTableToolbar(props) {
5227
5317
  const { toolbar, title } = props;
5228
5318
  const [densityOpen, setDensityOpen] = react.useState(false);
@@ -5349,7 +5439,7 @@ function ReactTableToolbar(props) {
5349
5439
  "div",
5350
5440
  {
5351
5441
  className: "react-list-table-column-setting",
5352
- style: { width: config.columnSettingWidth ?? 430 },
5442
+ style: { width: config.columnSettingWidth ?? 470 },
5353
5443
  children: [
5354
5444
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-list-table-column-setting-head", children: [
5355
5445
  /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "\u5217\u8BBE\u7F6E" }),
@@ -5377,9 +5467,21 @@ function ReactTableToolbar(props) {
5377
5467
  SortableList,
5378
5468
  {
5379
5469
  scrollContainerRef: columnSettingsBodyRef,
5380
- items: flattenColumns(props.columns),
5470
+ items: flattenColumns(props.columns).map((column, index) => ({
5471
+ ...column,
5472
+ position: index + 1
5473
+ })),
5381
5474
  onOrderChange: props.onColumnOrderChange,
5382
5475
  renderItem: (column, handleProps) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-list-table-column-item", children: [
5476
+ /* @__PURE__ */ jsxRuntime.jsx(
5477
+ "span",
5478
+ {
5479
+ className: "react-list-table-column-position",
5480
+ "aria-label": `\u5E8F\u53F7 ${column.position}`,
5481
+ title: "\u5E8F\u53F7",
5482
+ children: column.position
5483
+ }
5484
+ ),
5383
5485
  /* @__PURE__ */ jsxRuntime.jsxs(
5384
5486
  "span",
5385
5487
  {
@@ -6266,6 +6368,7 @@ function ReactListTableInner(props, ref) {
6266
6368
  ReactListTableExportNameDialog,
6267
6369
  {
6268
6370
  request: exportNameRequest,
6371
+ storageKey: storageKey ? `list:${storageKey}` : void 0,
6269
6372
  onClose: closeExportNameRequest,
6270
6373
  renderTable: (tableProps) => /* @__PURE__ */ jsxRuntime.jsx(ReactListTable, { ...tableProps })
6271
6374
  }
package/dist/index.d.cts CHANGED
@@ -111,9 +111,11 @@ interface ExportDialogRow {
111
111
  preserveTrailingZeros: "yes" | "no";
112
112
  }
113
113
  /** 使用 ReactListTable 承载大量导出列的弹窗。 */
114
- declare function ReactListTableExportNameDialog({ request, onClose, renderTable, }: {
114
+ declare function ReactListTableExportNameDialog({ request, onClose, renderTable, storageKey, }: {
115
115
  request: ListTableExportNameRequest;
116
116
  onClose?: () => void;
117
+ /** 用于隔离不同表格的导出配置缓存。 */
118
+ storageKey?: string;
117
119
  renderTable: (props: ReactListTableProps<ExportDialogRow>) => ReactNode;
118
120
  }): react.JSX.Element;
119
121
 
package/dist/index.d.ts CHANGED
@@ -111,9 +111,11 @@ interface ExportDialogRow {
111
111
  preserveTrailingZeros: "yes" | "no";
112
112
  }
113
113
  /** 使用 ReactListTable 承载大量导出列的弹窗。 */
114
- declare function ReactListTableExportNameDialog({ request, onClose, renderTable, }: {
114
+ declare function ReactListTableExportNameDialog({ request, onClose, renderTable, storageKey, }: {
115
115
  request: ListTableExportNameRequest;
116
116
  onClose?: () => void;
117
+ /** 用于隔离不同表格的导出配置缓存。 */
118
+ storageKey?: string;
117
119
  renderTable: (props: ReactListTableProps<ExportDialogRow>) => ReactNode;
118
120
  }): react.JSX.Element;
119
121
 
package/dist/index.js CHANGED
@@ -3572,6 +3572,37 @@ var toolButtonStyle = {
3572
3572
  lineHeight: 1,
3573
3573
  cursor: "pointer"
3574
3574
  };
3575
+ function readExportCache(storageKey) {
3576
+ if (!storageKey) return null;
3577
+ try {
3578
+ const value = JSON.parse(localStorage.getItem(`canvas-table-export:${storageKey}`) ?? "null");
3579
+ if (value?.version !== 1 || !Array.isArray(value.rows) || !Array.isArray(value.selectedKeys) || !value.selectedKeys.every((key) => typeof key === "string") || typeof value.includeSummary !== "boolean") return null;
3580
+ if (!value.rows.every((row) => row && typeof row.key === "string" && ["default", "ascend", "descend"].includes(row.sortOrder) && summaryOptions.some((option) => option.value === row.summaryType) && roundingOptions.some((option) => option.value === row.roundingMode) && ["yes", "no"].includes(row.showTypePrefix) && ["yes", "no"].includes(row.preserveTrailingZeros) && (row.precision == null || Number.isInteger(row.precision) && row.precision >= 0 && row.precision <= 20) && (row.sortIndex == null || Number.isFinite(row.sortIndex)) && (row.sortPriority == null || Number.isFinite(row.sortPriority)))) return null;
3581
+ return value;
3582
+ } catch {
3583
+ return null;
3584
+ }
3585
+ }
3586
+ function writeExportCache(storageKey, value) {
3587
+ if (!storageKey) return;
3588
+ try {
3589
+ const key = `canvas-table-export:${storageKey}`;
3590
+ if (value) localStorage.setItem(key, JSON.stringify(value));
3591
+ else localStorage.removeItem(key);
3592
+ } catch {
3593
+ }
3594
+ }
3595
+ function restoreExportRows(columns, cache) {
3596
+ const current = new Map(createExportDialogRows(columns).map((row) => [row.key, row]));
3597
+ const rows = [];
3598
+ cache?.rows.forEach((saved) => {
3599
+ const row = current.get(saved.key);
3600
+ if (!row) return;
3601
+ rows.push({ ...saved, key: row.key, title: row.title });
3602
+ current.delete(saved.key);
3603
+ });
3604
+ return [...rows, ...current.values()];
3605
+ }
3575
3606
  var sortOptions = [
3576
3607
  { value: "default", label: "\u9ED8\u8BA4" },
3577
3608
  { value: "ascend", label: "\u5347\u5E8F" },
@@ -3643,18 +3674,34 @@ function buildExportDialogSettings(rows, selectedKeys, includeSummary) {
3643
3674
  function ReactListTableExportNameDialog({
3644
3675
  request,
3645
3676
  onClose,
3646
- renderTable
3677
+ renderTable,
3678
+ storageKey
3647
3679
  }) {
3648
3680
  const tableId = useId();
3681
+ const tableHostRef = useRef(null);
3682
+ const [tableWidth, setTableWidth] = useState(0);
3683
+ useEffect(() => {
3684
+ const host = tableHostRef.current;
3685
+ if (!host || typeof ResizeObserver === "undefined") return;
3686
+ const update = () => setTableWidth(host.clientWidth);
3687
+ update();
3688
+ const observer = new ResizeObserver(update);
3689
+ observer.observe(host);
3690
+ return () => observer.disconnect();
3691
+ }, []);
3649
3692
  const extension = request.fileName.match(/\.(xlsx|csv|txt|json)$/i)?.[0] ?? "";
3650
3693
  const [fileName, setFileName] = useState(
3651
3694
  extension ? request.fileName.slice(0, -extension.length) : request.fileName
3652
3695
  );
3653
- const [rows, setRows] = useState(() => createExportDialogRows(request.columns ?? []));
3696
+ const [cached] = useState(() => readExportCache(storageKey));
3697
+ const [cacheEnabled, setCacheEnabled] = useState(Boolean(storageKey));
3698
+ const [rows, setRows] = useState(() => restoreExportRows(request.columns ?? [], cached));
3654
3699
  const [selectedKeys, setSelectedKeys] = useState(
3655
- () => (request.columns ?? []).filter((column) => column.selected !== false).map((column) => column.key)
3700
+ () => (request.columns ?? []).filter((column) => cached?.rows.some((row) => row.key === column.key) ? cached.selectedKeys.includes(column.key) : column.selected !== false).map((column) => column.key)
3701
+ );
3702
+ const [includeSummary, setIncludeSummary] = useState(
3703
+ () => cached?.includeSummary ?? (request.includeSummary === true || (request.columns?.some((column) => column.summary.type !== "none") ?? false))
3656
3704
  );
3657
- const [includeSummary, setIncludeSummary] = useState(request.includeSummary ?? false);
3658
3705
  const [error, setError] = useState("");
3659
3706
  const columns = useMemo(
3660
3707
  () => [
@@ -3732,9 +3779,16 @@ function ReactListTableExportNameDialog({
3732
3779
  ],
3733
3780
  [includeSummary, request.columns]
3734
3781
  );
3782
+ const fittedColumns = useMemo(
3783
+ () => fitExportDialogColumns(columns, tableWidth),
3784
+ [columns, tableWidth]
3785
+ );
3735
3786
  const confirm = () => {
3736
3787
  if (!fileName.trim()) return setError("\u8BF7\u8F93\u5165\u6587\u4EF6\u540D\u79F0");
3737
3788
  if (request.columns && !selectedKeys.length) return setError("\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u5217");
3789
+ if (request.columns && cacheEnabled) {
3790
+ writeExportCache(storageKey, { version: 1, rows, selectedKeys: selectedKeys.map(String), includeSummary });
3791
+ }
3738
3792
  request.onConfirm(
3739
3793
  `${fileName.trim()}${extension}`,
3740
3794
  request.columns ? buildExportDialogSettings(rows, selectedKeys, includeSummary) : void 0
@@ -3746,7 +3800,7 @@ function ReactListTableExportNameDialog({
3746
3800
  {
3747
3801
  open: true,
3748
3802
  title: "\u5BFC\u51FA\u6587\u4EF6",
3749
- width: request.columns ? 1180 : 420,
3803
+ width: request.columns ? includeSummary ? 1320 : 1180 : 420,
3750
3804
  fullscreenable: false,
3751
3805
  onOk: confirm,
3752
3806
  okText: "\u5BFC\u51FA",
@@ -3779,6 +3833,27 @@ function ReactListTableExportNameDialog({
3779
3833
  request.columns ? /* @__PURE__ */ jsxs("div", { style: { marginTop: 16 }, children: [
3780
3834
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 16, marginBottom: 8 }, children: [
3781
3835
  /* @__PURE__ */ jsx("span", { style: { fontWeight: 600 }, children: "\u5BFC\u51FA\u8868\u683C\u5217" }),
3836
+ storageKey ? /* @__PURE__ */ jsxs(
3837
+ "label",
3838
+ {
3839
+ style: { display: "inline-flex", alignItems: "center", gap: 6 },
3840
+ title: "\u5BFC\u51FA\u65F6\u4FDD\u5B58\u914D\u7F6E\uFF0C\u4E0B\u6B21\u6253\u5F00\u81EA\u52A8\u6062\u590D\uFF1B\u53D6\u6D88\u52FE\u9009\u6E05\u9664\u7F13\u5B58",
3841
+ children: [
3842
+ /* @__PURE__ */ jsx(
3843
+ ReactCheckbox,
3844
+ {
3845
+ checked: cacheEnabled,
3846
+ "aria-label": "\u7F13\u5B58\u5BFC\u51FA\u914D\u7F6E",
3847
+ onChange: (event) => {
3848
+ setCacheEnabled(event.target.checked);
3849
+ if (!event.target.checked) writeExportCache(storageKey, null);
3850
+ }
3851
+ }
3852
+ ),
3853
+ "\u7F13\u5B58\u5BFC\u51FA\u914D\u7F6E"
3854
+ ]
3855
+ }
3856
+ ) : null,
3782
3857
  /* @__PURE__ */ jsxs("label", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
3783
3858
  /* @__PURE__ */ jsx(
3784
3859
  ReactCheckbox,
@@ -3791,9 +3866,9 @@ function ReactListTableExportNameDialog({
3791
3866
  "\u5BFC\u51FA\u6C47\u603B\u884C"
3792
3867
  ] })
3793
3868
  ] }),
3794
- /* @__PURE__ */ jsx("div", { style: { height: "min(45vh, 520px)", minHeight: 250, overflow: "hidden" }, children: renderTable({
3869
+ /* @__PURE__ */ jsx("div", { ref: tableHostRef, style: { height: "min(45vh, 520px)", minHeight: 250, overflow: "hidden" }, children: renderTable({
3795
3870
  tableId,
3796
- columns,
3871
+ columns: fittedColumns,
3797
3872
  dataSource: rows,
3798
3873
  rowKey: "key",
3799
3874
  rowHeight: 42,
@@ -3838,6 +3913,17 @@ function ReactListTableExportNameDialog({
3838
3913
  }
3839
3914
  );
3840
3915
  }
3916
+ function fitExportDialogColumns(columns, availableWidth) {
3917
+ if (availableWidth <= 0) return columns;
3918
+ const totalWidth = (items) => items.reduce((sum, column) => sum + (column.key === ROW_SELECTION_COLUMN.key ? 0 : column.children?.length ? totalWidth(column.children) : Number(column.width) || 160), 0);
3919
+ const ratio = Math.min(1, Math.max(availableWidth - 54 - 32, 1) / totalWidth(columns));
3920
+ const fit = (items) => items.map((column) => {
3921
+ if (column.key === ROW_SELECTION_COLUMN.key) return column;
3922
+ if (column.children?.length) return { ...column, children: fit(column.children) };
3923
+ return { ...column, width: Math.max(1, Math.floor((Number(column.width) || 160) * ratio)), minWidth: 1 };
3924
+ });
3925
+ return fit(columns);
3926
+ }
3841
3927
 
3842
3928
  // src/features/editor/utils.ts
3843
3929
  function toNumberValue(value) {
@@ -4358,6 +4444,10 @@ function SelectEditor(props) {
4358
4444
  handleEditorBlur(event, skipBlurCommitRef, request);
4359
4445
  },
4360
4446
  onKeyDown: (event) => {
4447
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
4448
+ event.stopPropagation();
4449
+ return;
4450
+ }
4361
4451
  if (handleEditorNavigationKey(event, request, skipBlurCommitRef)) {
4362
4452
  return;
4363
4453
  }
@@ -5221,7 +5311,7 @@ function resolveSortableTransform(index, sourceIndex, targetIndex, height) {
5221
5311
  }
5222
5312
 
5223
5313
  // src/features/toolbar/style.css
5224
- styleInject(".react-list-table-shell {\n background: #fff;\n font-size: 14px;\n}\n.react-list-table-titlebar {\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0;\n box-sizing: border-box;\n background: #fff;\n}\n.react-list-table-shell-fullscreen {\n width: 100% !important;\n height: 100% !important;\n}\n.react-list-table-shell-fullscreen .canvas-list-table-root {\n height: 100% !important;\n}\n.react-list-table-title {\n min-width: 0;\n color: #1f2937;\n font-size: 14px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-tools {\n display: flex;\n align-items: center;\n gap: 14px;\n color: #475569;\n}\n.react-list-table-tool-wrap {\n position: relative;\n display: inline-flex;\n}\n.react-list-table-tool {\n width: 20px;\n height: 24px;\n padding: 0;\n border: 0;\n color: inherit;\n background: transparent;\n font: 16px/24px sans-serif;\n cursor: pointer;\n}\n.react-list-table-tool:hover {\n color: #1677ff;\n}\n.react-list-table-tool-menu,\n.react-list-table-column-setting {\n position: absolute;\n z-index: 120;\n top: 28px;\n right: 0;\n min-width: 112px;\n padding: 6px;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n background: #fff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.14);\n}\n.react-list-table-tool-menu button {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: #374151;\n font-size: 14px;\n text-align: left;\n cursor: pointer;\n}\n.react-list-table-tool-menu {\n width: max-content;\n min-width: 0;\n padding: 8px;\n border-radius: 8px;\n}\n.react-list-table-tool-menu-check {\n width: 16px;\n flex: 0 0 16px;\n margin-right: 6px;\n text-align: center;\n}\n.react-list-table-tool-menu button:hover {\n background: #f3f4f6;\n}\n.react-list-table-column-setting {\n width: 328px;\n max-height: 360px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.react-list-table-column-setting-head {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 4px 6px 8px;\n border-bottom: 1px solid #f0f0f0;\n flex: 0 0 auto;\n}\n.react-list-table-column-setting-head button {\n border: 0;\n color: #1677ff;\n background: transparent;\n cursor: pointer;\n}\n.react-list-table-column-setting-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n.react-list-table-column-item {\n display: flex;\n align-items: center;\n gap: 8px;\n justify-content: space-between;\n padding: 7px 6px;\n box-sizing: border-box;\n cursor: pointer;\n}\n.react-list-table-column-main,\n.react-list-table-column-actions {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.react-list-table-column-main {\n min-width: 0;\n}\n.react-list-table-column-actions {\n flex: 0 0 auto;\n}\n.react-list-table-column-actions > .canvas-react-select-wrap {\n flex: 0 0 96px;\n width: 96px;\n height: 24px;\n}\n.react-list-table-column-label {\n min-width: 64px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-column-width-input {\n height: 22px;\n padding-inline: 5px;\n}\n.react-list-table-column-fixed-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-sort-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-setting label.dragging {\n opacity: 0.45;\n background: #e6f4ff;\n}\n.react-list-table-column-setting [data-sortable-key].dragging {\n opacity: 0.28;\n}\n.react-list-table-column-drag {\n touch-action: none;\n color: #94a3b8;\n display: inline-flex;\n font-size: 14px;\n cursor: grab;\n user-select: none;\n}\nbody.react-list-table-sorting,\nbody.react-list-table-sorting * {\n cursor: grabbing !important;\n}\n.react-list-table-sort-overlay {\n position: fixed;\n z-index: 1400;\n display: block;\n padding: 0;\n box-sizing: border-box;\n color: #334155;\n background: #fff;\n border: 1px solid #91caff;\n border-radius: 6px;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);\n pointer-events: none;\n}\n.react-list-table-sort-overlay .react-list-table-column-item {\n width: 100%;\n height: 100%;\n}\n");
5314
+ styleInject(".react-list-table-shell {\n background: #fff;\n font-size: 14px;\n}\n.react-list-table-titlebar {\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0;\n box-sizing: border-box;\n background: #fff;\n}\n.react-list-table-shell-fullscreen {\n width: 100% !important;\n height: 100% !important;\n}\n.react-list-table-shell-fullscreen .canvas-list-table-root {\n height: 100% !important;\n}\n.react-list-table-title {\n min-width: 0;\n color: #1f2937;\n font-size: 14px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-tools {\n display: flex;\n align-items: center;\n gap: 14px;\n color: #475569;\n}\n.react-list-table-tool-wrap {\n position: relative;\n display: inline-flex;\n}\n.react-list-table-tool {\n width: 20px;\n height: 24px;\n padding: 0;\n border: 0;\n color: inherit;\n background: transparent;\n font: 16px/24px sans-serif;\n cursor: pointer;\n}\n.react-list-table-tool:hover {\n color: #1677ff;\n}\n.react-list-table-tool-menu,\n.react-list-table-column-setting {\n position: absolute;\n z-index: 120;\n top: 28px;\n right: 0;\n min-width: 112px;\n padding: 6px;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n background: #fff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.14);\n}\n.react-list-table-tool-menu button {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: #374151;\n font-size: 14px;\n text-align: left;\n cursor: pointer;\n}\n.react-list-table-tool-menu {\n width: max-content;\n min-width: 0;\n padding: 8px;\n border-radius: 8px;\n}\n.react-list-table-tool-menu-check {\n width: 16px;\n flex: 0 0 16px;\n margin-right: 6px;\n text-align: center;\n}\n.react-list-table-tool-menu button:hover {\n background: #f3f4f6;\n}\n.react-list-table-column-setting {\n width: 328px;\n max-height: 360px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.react-list-table-column-setting-head {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 4px 6px 8px;\n border-bottom: 1px solid #f0f0f0;\n flex: 0 0 auto;\n}\n.react-list-table-column-setting-head button {\n border: 0;\n color: #1677ff;\n background: transparent;\n cursor: pointer;\n}\n.react-list-table-column-setting-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n.react-list-table-column-item {\n display: flex;\n align-items: center;\n gap: 8px;\n justify-content: space-between;\n padding: 7px 6px;\n box-sizing: border-box;\n cursor: pointer;\n}\n.react-list-table-column-position {\n flex: 0 0 28px;\n text-align: center;\n color: #64748b;\n font-variant-numeric: tabular-nums;\n}\n.react-list-table-column-main,\n.react-list-table-column-actions {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.react-list-table-column-main {\n min-width: 0;\n}\n.react-list-table-column-actions {\n flex: 0 0 auto;\n}\n.react-list-table-column-actions > .canvas-react-select-wrap {\n flex: 0 0 96px;\n width: 96px;\n height: 24px;\n}\n.react-list-table-column-label {\n min-width: 64px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-column-width-input {\n height: 22px;\n padding-inline: 5px;\n}\n.react-list-table-column-fixed-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-sort-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-setting label.dragging {\n opacity: 0.45;\n background: #e6f4ff;\n}\n.react-list-table-column-setting [data-sortable-key].dragging {\n opacity: 0.28;\n}\n.react-list-table-column-drag {\n touch-action: none;\n color: #94a3b8;\n display: inline-flex;\n font-size: 14px;\n cursor: grab;\n user-select: none;\n}\nbody.react-list-table-sorting,\nbody.react-list-table-sorting * {\n cursor: grabbing !important;\n}\n.react-list-table-sort-overlay {\n position: fixed;\n z-index: 1400;\n display: block;\n padding: 0;\n box-sizing: border-box;\n color: #334155;\n background: #fff;\n border: 1px solid #91caff;\n border-radius: 6px;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);\n pointer-events: none;\n}\n.react-list-table-sort-overlay .react-list-table-column-item {\n width: 100%;\n height: 100%;\n}\n");
5225
5315
  function ReactTableToolbar(props) {
5226
5316
  const { toolbar, title } = props;
5227
5317
  const [densityOpen, setDensityOpen] = useState(false);
@@ -5348,7 +5438,7 @@ function ReactTableToolbar(props) {
5348
5438
  "div",
5349
5439
  {
5350
5440
  className: "react-list-table-column-setting",
5351
- style: { width: config.columnSettingWidth ?? 430 },
5441
+ style: { width: config.columnSettingWidth ?? 470 },
5352
5442
  children: [
5353
5443
  /* @__PURE__ */ jsxs("div", { className: "react-list-table-column-setting-head", children: [
5354
5444
  /* @__PURE__ */ jsx("strong", { children: "\u5217\u8BBE\u7F6E" }),
@@ -5376,9 +5466,21 @@ function ReactTableToolbar(props) {
5376
5466
  SortableList,
5377
5467
  {
5378
5468
  scrollContainerRef: columnSettingsBodyRef,
5379
- items: flattenColumns(props.columns),
5469
+ items: flattenColumns(props.columns).map((column, index) => ({
5470
+ ...column,
5471
+ position: index + 1
5472
+ })),
5380
5473
  onOrderChange: props.onColumnOrderChange,
5381
5474
  renderItem: (column, handleProps) => /* @__PURE__ */ jsxs("div", { className: "react-list-table-column-item", children: [
5475
+ /* @__PURE__ */ jsx(
5476
+ "span",
5477
+ {
5478
+ className: "react-list-table-column-position",
5479
+ "aria-label": `\u5E8F\u53F7 ${column.position}`,
5480
+ title: "\u5E8F\u53F7",
5481
+ children: column.position
5482
+ }
5483
+ ),
5382
5484
  /* @__PURE__ */ jsxs(
5383
5485
  "span",
5384
5486
  {
@@ -6265,6 +6367,7 @@ function ReactListTableInner(props, ref) {
6265
6367
  ReactListTableExportNameDialog,
6266
6368
  {
6267
6369
  request: exportNameRequest,
6370
+ storageKey: storageKey ? `list:${storageKey}` : void 0,
6268
6371
  onClose: closeExportNameRequest,
6269
6372
  renderTable: (tableProps) => /* @__PURE__ */ jsx(ReactListTable, { ...tableProps })
6270
6373
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canvas-components/react-list-table",
3
- "version": "0.3.37",
3
+ "version": "0.3.39",
4
4
  "description": "React adapter for the list table core engine.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "@canvas-components/chart": "0.3.7",
32
- "@canvas-components/list-table": "0.3.32",
33
- "@canvas-components/react-components": "0.3.16"
32
+ "@canvas-components/react-components": "0.3.17",
33
+ "@canvas-components/list-table": "0.3.33"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "react": ">=16.8.0",