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

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) {
@@ -6266,6 +6352,7 @@ function ReactListTableInner(props, ref) {
6266
6352
  ReactListTableExportNameDialog,
6267
6353
  {
6268
6354
  request: exportNameRequest,
6355
+ storageKey: storageKey ? `list:${storageKey}` : void 0,
6269
6356
  onClose: closeExportNameRequest,
6270
6357
  renderTable: (tableProps) => /* @__PURE__ */ jsxRuntime.jsx(ReactListTable, { ...tableProps })
6271
6358
  }
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) {
@@ -6265,6 +6351,7 @@ function ReactListTableInner(props, ref) {
6265
6351
  ReactListTableExportNameDialog,
6266
6352
  {
6267
6353
  request: exportNameRequest,
6354
+ storageKey: storageKey ? `list:${storageKey}` : void 0,
6268
6355
  onClose: closeExportNameRequest,
6269
6356
  renderTable: (tableProps) => /* @__PURE__ */ jsx(ReactListTable, { ...tableProps })
6270
6357
  }
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.38",
4
4
  "description": "React adapter for the list table core engine.",
5
5
  "license": "MIT",
6
6
  "type": "module",