@canvas-components/react-list-table 0.3.19 → 0.3.21

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.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { createContext, forwardRef, useMemo, useRef, useState, useImperativeHandle, useContext, useEffect, useLayoutEffect, Fragment as Fragment$1 } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
- import { ReactImagePreview, ReactNumberInput, ReactSelect, ReactPagination, ReactFilterPanel, ReactPopup, ReactModal, ReactInput, ReactPopconfirm, ReactContentScaleProvider, ReactInputNumber, ReactCheckbox, ReactAutoComplete, ReactColorPicker, ReactTimeRangePicker, ReactDateTimeRangePicker, ReactDateTimePicker, ReactTimePicker, ReactDateRangePicker, ReactDatePicker, ReactMonthPicker, ReactQuarterPicker, ReactYearPicker, ReactInputRange } from '@canvas-components/react-components';
3
+ import { ReactImagePreview, ReactCheckbox, ReactNumberInput, ReactSelect, ReactPagination, ReactFilterPanel, ReactPopup, ReactModal, ReactInput, ReactPopconfirm, ReactContentScaleProvider, ReactButton, ReactColorPicker, ReactInputNumber, ReactAutoComplete, ReactTimeRangePicker, ReactDateTimeRangePicker, ReactDateTimePicker, ReactTimePicker, ReactDateRangePicker, ReactDatePicker, ReactMonthPicker, ReactQuarterPicker, ReactYearPicker, ReactInputRange, ReactCascader, ReactTreeSelect } from '@canvas-components/react-components';
4
4
  import { EXPAND_COLUMN, SELECTION_COLUMN, ROW_SELECTION_COLUMN, resetListTableStoredColumnConfig, ListTable } from '@canvas-components/list-table';
5
5
  export { EXPAND_COLUMN, ROW_SELECTION_COLUMN, SELECTION_COLUMN, getListTableStoredPageSize } from '@canvas-components/list-table';
6
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
6
7
  import { formatDateTimeValue, isDateTimeFormatPattern } from '@canvas-components/utils/date';
7
8
  import { formatNumberValue, formatPercentValue, formatScientificValue, formatCustomNumberPattern } from '@canvas-components/utils/number';
8
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
9
9
 
10
10
  // #style-inject:#style-inject
11
11
  function styleInject(css, { insertAt } = {}) {
@@ -31,6 +31,328 @@ function styleInject(css, { insertAt } = {}) {
31
31
 
32
32
  // src/styles/table-overrides.css
33
33
  styleInject(".react-list-table-filter-option {\n transition: background-color 0.2s ease;\n}\n.react-list-table-filter-option:hover {\n background: #f3f4f6;\n}\n.react-list-table-menu-action,\n.react-list-table-menu-row {\n transition: background-color 0.2s ease, color 0.2s ease;\n}\n.react-list-table-menu-action:hover,\n.react-list-table-menu-row:hover {\n background: #f3f4f6 !important;\n}\n.react-list-table-custom-selection-copy {\n transition: background-color 0.2s ease;\n}\n.react-list-table-custom-selection-copy:hover {\n background: #4096ff !important;\n}\n.react-list-table-custom-selection-input > .canvas-react-input-number-handler-down {\n display: none;\n}\n.react-list-table-menu-chevron {\n width: 7px;\n height: 7px;\n flex: 0 0 auto;\n margin-left: 8px;\n border-top: 1.5px solid #9ca3af;\n border-right: 1.5px solid #9ca3af;\n transform: rotate(45deg);\n transform-origin: center;\n transition: transform 0.18s ease, border-color 0.18s ease;\n}\n.react-list-table-menu-action:hover .react-list-table-menu-chevron,\n.react-list-table-menu-row:hover .react-list-table-menu-chevron {\n border-color: #6b7280;\n transform: rotate(135deg);\n}\n.react-list-table-menu-chevron-active {\n border-color: #6b7280;\n transform: rotate(135deg);\n}\n");
34
+ var conditionalOperators = {
35
+ eq: "\u7B49\u4E8E",
36
+ neq: "\u4E0D\u7B49\u4E8E",
37
+ gt: "\u5927\u4E8E",
38
+ gte: "\u5927\u4E8E\u7B49\u4E8E",
39
+ lt: "\u5C0F\u4E8E",
40
+ lte: "\u5C0F\u4E8E\u7B49\u4E8E",
41
+ between: "\u4ECB\u4E8E\uFF08\u542B\u8FB9\u754C\uFF09",
42
+ contains: "\u5305\u542B\u6587\u672C",
43
+ empty: "\u4E3A\u7A7A",
44
+ notEmpty: "\u4E0D\u4E3A\u7A7A"
45
+ };
46
+ function describeConditionalRule(rule) {
47
+ return resolveConditions(rule).map(
48
+ (condition, index) => `${index === 0 ? "" : (condition.relation ?? rule.conditionRelation ?? "and") === "or" ? " \u6216 " : " \u4E14 "}${conditionalOperators[condition.operator]}${condition.operator === "empty" || condition.operator === "notEmpty" ? "" : ` ${condition.value ?? ""}`}${condition.operator === "between" ? ` \uFF5E ${condition.endValue ?? ""}` : ""}`
49
+ ).join("");
50
+ }
51
+ function resolveConditions(rule) {
52
+ return rule.conditions ?? (rule.operator ? [
53
+ {
54
+ operator: rule.operator,
55
+ value: rule.value,
56
+ endValue: rule.endValue
57
+ }
58
+ ] : []);
59
+ }
60
+ function validateConditionalRule(rule) {
61
+ const conditions = resolveConditions(rule);
62
+ if (!conditions.length) return "\u8BF7\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u6761\u4EF6\u3002";
63
+ for (const [index, condition] of conditions.entries()) {
64
+ const error = validateCondition(condition);
65
+ if (error) return `\u6761\u4EF6 ${index + 1}\uFF1A${error}`;
66
+ }
67
+ return null;
68
+ }
69
+ function validateCondition(rule) {
70
+ if (rule.operator === "empty" || rule.operator === "notEmpty") return null;
71
+ if (String(rule.value ?? "").trim() === "")
72
+ return "\u8BF7\u8F93\u5165\u6BD4\u8F83\u503C\uFF1B\u7A7A\u503C\u8BF7\u4F7F\u7528\u201C\u4E3A\u7A7A\u201D\u6761\u4EF6\u3002";
73
+ if (["gt", "gte", "lt", "lte", "between"].includes(rule.operator)) {
74
+ if (!Number.isFinite(Number(rule.value))) return "\u8BF7\u8F93\u5165\u6709\u6548\u6570\u503C\u3002";
75
+ if (rule.operator === "between" && (String(rule.endValue ?? "").trim() === "" || !Number.isFinite(Number(rule.endValue)) || Number(rule.endValue) < Number(rule.value)))
76
+ return "\u8BF7\u8F93\u5165\u4E0D\u5C0F\u4E8E\u8D77\u59CB\u503C\u7684\u7ED3\u675F\u6570\u503C\u3002";
77
+ }
78
+ return null;
79
+ }
80
+ function ConditionalFormatDialog(props) {
81
+ const rules = props.request.conditionalFormats ?? [];
82
+ const [rule, setRule] = useState(
83
+ () => props.index == null ? {
84
+ id: `rule-${Date.now()}-${Math.random().toString(36).slice(2)}`,
85
+ operator: "gt",
86
+ value: "",
87
+ backgroundColor: "#fff1b8",
88
+ textColor: "#1f2329"
89
+ } : { ...rules[props.index] }
90
+ );
91
+ const [error, setError] = useState(null);
92
+ const conditions = resolveConditions(rule);
93
+ const patch = (value) => {
94
+ setRule((current) => ({ ...current, ...value }));
95
+ setError(null);
96
+ };
97
+ const apply = (next) => {
98
+ props.request.onChangeConditionalFormats?.(next);
99
+ props.onClose();
100
+ };
101
+ const patchCondition = (index, value) => {
102
+ patch({
103
+ conditions: conditions.map(
104
+ (condition, i) => i === index ? { ...condition, ...value } : condition
105
+ )
106
+ });
107
+ };
108
+ return /* @__PURE__ */ jsx(
109
+ ReactModal,
110
+ {
111
+ open: true,
112
+ title: props.index == null ? "\u65B0\u589E\u6761\u4EF6\u683C\u5F0F\u89C4\u5219" : "\u7F16\u8F91\u6761\u4EF6\u683C\u5F0F\u89C4\u5219",
113
+ width: 460,
114
+ onCancel: props.onClose,
115
+ onOk: () => {
116
+ const message = validateConditionalRule(rule);
117
+ if (message) {
118
+ setError(message);
119
+ return;
120
+ }
121
+ const next = rules.map((item) => ({ ...item }));
122
+ if (props.index == null) next.push(rule);
123
+ else next[props.index] = rule;
124
+ apply(next);
125
+ },
126
+ okText: "\u4FDD\u5B58\u89C4\u5219",
127
+ cancelText: "\u53D6\u6D88",
128
+ children: /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 14, fontSize: 14 }, children: [
129
+ /* @__PURE__ */ jsx("div", { style: { color: "#64748b" }, children: "\u4EC5\u5F53\u524D\u5217\u751F\u6548\uFF1B\u4ECE\u4E0A\u5230\u4E0B\u5339\u914D\uFF0C\u9996\u6761\u547D\u4E2D\u7684\u80CC\u666F\u751F\u6548\u3002 \u6761\u4EF6\u4E4B\u95F4\u53EF\u5206\u522B\u8BBE\u7F6E\u201C\u4E14 / \u6216\u201D\uFF0C\u4E14\u4F18\u5148\u4E8E\u6216\u3002" }),
130
+ /* @__PURE__ */ jsxs("label", { children: [
131
+ "\u89C4\u5219\u540D\u79F0\uFF08\u53EF\u9009\uFF09",
132
+ /* @__PURE__ */ jsx(
133
+ ReactInput,
134
+ {
135
+ value: rule.name ?? "",
136
+ onChange: (event) => patch({ name: event.target.value })
137
+ }
138
+ )
139
+ ] }),
140
+ /* @__PURE__ */ jsx(
141
+ "div",
142
+ {
143
+ style: {
144
+ display: "grid",
145
+ gap: 12,
146
+ maxHeight: "35vh",
147
+ overflowY: "auto"
148
+ },
149
+ children: conditions.map((condition, index) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
150
+ index > 0 && /* @__PURE__ */ jsx(
151
+ ReactSelect,
152
+ {
153
+ "aria-label": `\u6761\u4EF6 ${index} \u4E0E\u6761\u4EF6 ${index + 1} \u7684\u5173\u7CFB`,
154
+ value: condition.relation ?? rule.conditionRelation ?? "and",
155
+ options: [
156
+ { value: "and", label: "\u4E14" },
157
+ { value: "or", label: "\u6216" }
158
+ ],
159
+ onChange: (value) => patchCondition(index, { relation: value }),
160
+ style: { width: 88, justifySelf: "center" }
161
+ }
162
+ ),
163
+ /* @__PURE__ */ jsxs(
164
+ "div",
165
+ {
166
+ style: {
167
+ display: "grid",
168
+ gap: 8,
169
+ padding: 12,
170
+ border: "1px solid #d9e1ec"
171
+ },
172
+ children: [
173
+ /* @__PURE__ */ jsxs(
174
+ "div",
175
+ {
176
+ style: {
177
+ display: "flex",
178
+ justifyContent: "space-between",
179
+ alignItems: "center"
180
+ },
181
+ children: [
182
+ /* @__PURE__ */ jsxs("span", { children: [
183
+ "\u6761\u4EF6 ",
184
+ index + 1
185
+ ] }),
186
+ /* @__PURE__ */ jsx(
187
+ ReactButton,
188
+ {
189
+ type: "text",
190
+ size: "small",
191
+ disabled: conditions.length === 1,
192
+ "aria-label": `\u5220\u9664\u6761\u4EF6 ${index + 1}`,
193
+ onClick: () => patch({
194
+ conditions: conditions.filter((_, i) => i !== index)
195
+ }),
196
+ children: "\u5220\u9664\u6761\u4EF6"
197
+ }
198
+ )
199
+ ]
200
+ }
201
+ ),
202
+ /* @__PURE__ */ jsx(
203
+ ReactSelect,
204
+ {
205
+ "aria-label": `\u6761\u4EF6 ${index + 1} \u6BD4\u8F83\u65B9\u5F0F`,
206
+ value: condition.operator,
207
+ options: Object.entries(conditionalOperators).map(
208
+ ([value, label]) => ({ value, label })
209
+ ),
210
+ onChange: (value) => patchCondition(index, {
211
+ operator: value
212
+ })
213
+ }
214
+ ),
215
+ condition.operator !== "empty" && condition.operator !== "notEmpty" && /* @__PURE__ */ jsxs("label", { children: [
216
+ "\u6BD4\u8F83\u503C",
217
+ /* @__PURE__ */ jsx(
218
+ ReactInput,
219
+ {
220
+ value: String(condition.value ?? ""),
221
+ onChange: (event) => patchCondition(index, { value: event.target.value })
222
+ }
223
+ )
224
+ ] }),
225
+ condition.operator === "between" && /* @__PURE__ */ jsxs("label", { children: [
226
+ "\u7ED3\u675F\u503C",
227
+ /* @__PURE__ */ jsx(
228
+ ReactInput,
229
+ {
230
+ value: String(condition.endValue ?? ""),
231
+ onChange: (event) => patchCondition(index, { endValue: event.target.value })
232
+ }
233
+ )
234
+ ] })
235
+ ]
236
+ }
237
+ )
238
+ ] }, index))
239
+ }
240
+ ),
241
+ /* @__PURE__ */ jsx(
242
+ ReactButton,
243
+ {
244
+ onClick: () => patch({
245
+ conditions: [
246
+ ...conditions,
247
+ { operator: "gt", value: "", relation: "and" }
248
+ ]
249
+ }),
250
+ children: "\u65B0\u589E\u6761\u4EF6"
251
+ }
252
+ ),
253
+ /* @__PURE__ */ jsxs("label", { style: { display: "grid", gap: 4 }, children: [
254
+ "\u5355\u5143\u683C\u80CC\u666F",
255
+ /* @__PURE__ */ jsx(
256
+ ReactColorPicker,
257
+ {
258
+ "aria-label": "\u5355\u5143\u683C\u80CC\u666F",
259
+ value: rule.backgroundColor,
260
+ onChange: (backgroundColor) => patch({ backgroundColor })
261
+ }
262
+ )
263
+ ] }),
264
+ /* @__PURE__ */ jsxs("label", { style: { display: "grid", gap: 4 }, children: [
265
+ "\u6587\u672C\u989C\u8272",
266
+ /* @__PURE__ */ jsx(
267
+ ReactColorPicker,
268
+ {
269
+ "aria-label": "\u6587\u672C\u989C\u8272",
270
+ value: rule.textColor,
271
+ onChange: (textColor) => patch({ textColor })
272
+ }
273
+ )
274
+ ] }),
275
+ /* @__PURE__ */ jsxs(
276
+ "div",
277
+ {
278
+ style: {
279
+ padding: "8px 12px",
280
+ backgroundColor: rule.backgroundColor,
281
+ color: rule.textColor,
282
+ border: "1px solid #d9e1ec"
283
+ },
284
+ children: [
285
+ "\u6837\u5F0F\u9884\u89C8\uFF1A",
286
+ describeConditionalRule(rule)
287
+ ]
288
+ }
289
+ ),
290
+ error && /* @__PURE__ */ jsx("div", { role: "alert", style: { color: "#cf1322" }, children: error }),
291
+ props.index != null && /* @__PURE__ */ jsx(
292
+ ReactButton,
293
+ {
294
+ type: "text",
295
+ danger: true,
296
+ onClick: () => apply(rules.filter((_, index) => index !== props.index)),
297
+ children: "\u5220\u9664\u6B64\u89C4\u5219"
298
+ }
299
+ )
300
+ ] })
301
+ }
302
+ );
303
+ }
304
+
305
+ // src/features/context-menu/cache-menu.ts
306
+ function buildCacheMenuItems(request) {
307
+ const available = request.columnSettingsCacheAvailable === true;
308
+ const items = [];
309
+ const state = {
310
+ disabled: !available,
311
+ title: available ? void 0 : "\u914D\u7F6E storageKey \u540E\u53EF\u7F13\u5B58\u914D\u7F6E"
312
+ };
313
+ if (request.onChangeColumnSettingsCache)
314
+ items.push({
315
+ ...state,
316
+ key: "cache-columns",
317
+ label: "\u5217\u8BBE\u7F6E",
318
+ selected: available && request.cacheColumnSettings === true,
319
+ onClick: () => request.onChangeColumnSettingsCache?.(!request.cacheColumnSettings)
320
+ });
321
+ if (request.onChangeCachePreference) {
322
+ const categories = [
323
+ ["sort", "\u6392\u5E8F"],
324
+ ["filter", "\u7B5B\u9009"],
325
+ ["summary", "\u6C47\u603B"],
326
+ ["alignment", "\u5BF9\u9F50"],
327
+ ["dataFormat", "\u683C\u5F0F"],
328
+ ["density", "\u8868\u683C\u5BC6\u5EA6"],
329
+ ["zoom", "\u7F29\u653E"]
330
+ ];
331
+ for (const [category, label] of categories)
332
+ items.push({
333
+ ...state,
334
+ key: `cache-${category}`,
335
+ label,
336
+ selected: available && request.cachePreferences?.[category] !== false,
337
+ onClick: () => request.onChangeCachePreference?.(
338
+ category,
339
+ request.cachePreferences?.[category] === false
340
+ )
341
+ });
342
+ }
343
+ if (request.onChangeConditionalFormats)
344
+ items.push({
345
+ ...state,
346
+ key: "cache-conditional",
347
+ label: "\u5F53\u524D\u5217\u6761\u4EF6\u683C\u5F0F",
348
+ selected: available && request.cacheConditionalFormats !== false,
349
+ onClick: () => request.onChangeConditionalFormats?.(
350
+ request.conditionalFormats ?? [],
351
+ request.cacheConditionalFormats === false
352
+ )
353
+ });
354
+ return items;
355
+ }
34
356
  var CONTEXT_MENU_SURFACE_STYLE = {
35
357
  padding: 8,
36
358
  background: "#ffffff",
@@ -39,10 +361,22 @@ var CONTEXT_MENU_SURFACE_STYLE = {
39
361
  function ReactTableContextMenuView(props) {
40
362
  const { request } = props;
41
363
  const [menuOpen, setMenuOpen] = useState(true);
364
+ const [conditionalEditor, setConditionalEditor] = useState(null);
365
+ const conditionalDialog = conditionalEditor ? /* @__PURE__ */ jsx(
366
+ ConditionalFormatDialog,
367
+ {
368
+ ...conditionalEditor,
369
+ onClose: () => {
370
+ setConditionalEditor(null);
371
+ conditionalEditor.request.onClose();
372
+ }
373
+ }
374
+ ) : null;
42
375
  const [moreFormatRequest, setMoreFormatRequest] = useState(null);
43
376
  const [customSelectionRequest, setCustomSelectionRequest] = useState(null);
44
377
  if (!request) {
45
378
  return /* @__PURE__ */ jsxs(Fragment, { children: [
379
+ conditionalDialog,
46
380
  moreFormatRequest ? /* @__PURE__ */ jsx(
47
381
  MoreFormatDialog,
48
382
  {
@@ -67,6 +401,7 @@ function ReactTableContextMenuView(props) {
67
401
  width: 260
68
402
  };
69
403
  return /* @__PURE__ */ jsxs(Fragment, { children: [
404
+ conditionalDialog,
70
405
  /* @__PURE__ */ jsx(
71
406
  ReactPopup,
72
407
  {
@@ -76,7 +411,7 @@ function ReactTableContextMenuView(props) {
76
411
  minWidth: 260,
77
412
  onOpenChange: (open) => {
78
413
  setMenuOpen(open);
79
- if (!open && !moreFormatRequest && !customSelectionRequest) {
414
+ if (!open && !moreFormatRequest && !customSelectionRequest && !conditionalEditor) {
80
415
  request.onClose();
81
416
  }
82
417
  },
@@ -111,7 +446,28 @@ function ReactTableContextMenuView(props) {
111
446
  key: "copy-selection",
112
447
  label: "\u590D\u5236\u9009\u533A",
113
448
  onClick: request.onCopySelection
114
- }
449
+ },
450
+ ...request.onCopyImage ? [
451
+ {
452
+ key: "copy-image-all",
453
+ label: "\u590D\u5236\u4E3A\u56FE\u7247 \xB7 \u6240\u6709\u533A\u57DF",
454
+ separatorBefore: true,
455
+ title: "\u5F53\u524D\u5DF2\u52A0\u8F7D\u5E76\u7ECF\u8FC7\u6392\u5E8F\u8FC7\u6EE4\u7684\u5168\u90E8\u6570\u636E\uFF0C\u4E0D\u8BF7\u6C42\u672A\u52A0\u8F7D\u7684\u670D\u52A1\u7AEF\u6570\u636E",
456
+ onClick: () => request.onCopyImage?.("all")
457
+ },
458
+ {
459
+ key: "copy-image-selection",
460
+ label: "\u590D\u5236\u4E3A\u56FE\u7247 \xB7 \u9009\u4E2D\u533A\u57DF",
461
+ disabled: !request.canCopySelectionImage,
462
+ onClick: () => request.onCopyImage?.("selection")
463
+ },
464
+ {
465
+ key: "copy-image-viewport",
466
+ label: "\u590D\u5236\u4E3A\u56FE\u7247 \xB7 \u89C6\u53E3\u533A\u57DF",
467
+ title: "\u5F53\u524D\u53EF\u89C1\u8868\u683C\uFF0C\u5305\u542B\u8868\u5934\u548C\u6C47\u603B\u884C\uFF0C\u4E0D\u5305\u542B\u5206\u9875\u5668\u548C\u83DC\u5355",
468
+ onClick: () => request.onCopyImage?.("viewport")
469
+ }
470
+ ] : []
115
471
  ],
116
472
  children: /* @__PURE__ */ jsx(
117
473
  CustomSelectionMenu,
@@ -150,7 +506,26 @@ function ReactTableContextMenuView(props) {
150
506
  key: "export-json",
151
507
  label: "\u5BFC\u51FA JSON",
152
508
  onClick: request.onExportJson
153
- }
509
+ },
510
+ ...request.onExportImage ? [
511
+ {
512
+ key: "export-image-all",
513
+ label: "\u5BFC\u51FA\u56FE\u7247 \xB7 \u6240\u6709\u533A\u57DF",
514
+ separatorBefore: true,
515
+ onClick: () => request.onExportImage?.("all")
516
+ },
517
+ {
518
+ key: "export-image-selection",
519
+ label: "\u5BFC\u51FA\u56FE\u7247 \xB7 \u9009\u4E2D\u533A\u57DF",
520
+ disabled: !request.canCopySelectionImage,
521
+ onClick: () => request.onExportImage?.("selection")
522
+ },
523
+ {
524
+ key: "export-image-viewport",
525
+ label: "\u5BFC\u51FA\u56FE\u7247 \xB7 \u89C6\u53E3\u533A\u57DF",
526
+ onClick: () => request.onExportImage?.("viewport")
527
+ }
528
+ ] : []
154
529
  ]
155
530
  }
156
531
  ),
@@ -223,6 +598,41 @@ function ReactTableContextMenuView(props) {
223
598
  ),
224
599
  /* @__PURE__ */ jsx(SummaryMenu, { request }),
225
600
  /* @__PURE__ */ jsx(AlignmentMenu, { request }),
601
+ request.onChangeConditionalFormats && /* @__PURE__ */ jsx(
602
+ MenuSubmenu,
603
+ {
604
+ label: "\u6761\u4EF6\u683C\u5F0F",
605
+ items: [
606
+ {
607
+ key: "add-rule",
608
+ label: "\u65B0\u589E\u89C4\u5219",
609
+ onClick: () => {
610
+ setConditionalEditor({ request, index: null });
611
+ setMenuOpen(false);
612
+ }
613
+ },
614
+ {
615
+ key: "reset-rules",
616
+ label: "\u91CD\u7F6E\u6761\u4EF6\u683C\u5F0F",
617
+ disabled: !request.conditionalFormats?.length,
618
+ onClick: () => request.onChangeConditionalFormats?.([])
619
+ }
620
+ ],
621
+ header: /* @__PURE__ */ jsx("div", { style: { display: "grid", gap: 4, paddingBottom: 8 }, children: request.conditionalFormats?.length ? request.conditionalFormats.map((rule, index) => /* @__PURE__ */ jsx(
622
+ MenuAction,
623
+ {
624
+ label: rule.name?.trim() || `\u89C4\u5219 ${index + 1}`,
625
+ colorSwatch: rule.backgroundColor,
626
+ title: `${rule.name?.trim() || `\u89C4\u5219 ${index + 1}`}\uFF1A${describeConditionalRule(rule)}`,
627
+ onClick: () => {
628
+ setConditionalEditor({ request, index });
629
+ setMenuOpen(false);
630
+ }
631
+ },
632
+ rule.id
633
+ )) : /* @__PURE__ */ jsx("span", { style: { color: "#64748b", padding: 8 }, children: "\u6682\u65E0\u89C4\u5219" }) })
634
+ }
635
+ ),
226
636
  /* @__PURE__ */ jsx(
227
637
  DataFormatMenu,
228
638
  {
@@ -353,6 +763,13 @@ function ReactTableContextMenuView(props) {
353
763
  }
354
764
  ),
355
765
  /* @__PURE__ */ jsx(ZoomMenu, { value: request.zoom ?? 1, onChange: request.onChangeZoom }),
766
+ /* @__PURE__ */ jsx(
767
+ MenuSubmenu,
768
+ {
769
+ label: "\u7F13\u5B58\u5230\u672C\u5730",
770
+ items: buildCacheMenuItems(request)
771
+ }
772
+ ),
356
773
  request.columns?.length && request.onToggleColumn ? /* @__PURE__ */ jsxs(Fragment, { children: [
357
774
  /* @__PURE__ */ jsx(MenuDivider, {}),
358
775
  /* @__PURE__ */ jsx(
@@ -399,6 +816,7 @@ function MenuAction(props) {
399
816
  type: "button",
400
817
  onClick: disabled ? void 0 : onClick,
401
818
  disabled,
819
+ title: props.title,
402
820
  className: "react-list-table-menu-action",
403
821
  style: {
404
822
  display: "flex",
@@ -427,7 +845,36 @@ function MenuAction(props) {
427
845
  children: selected ? "\u2713" : ""
428
846
  }
429
847
  ) : null,
430
- /* @__PURE__ */ jsx("span", { style: { flex: 1, whiteSpace: "nowrap" }, children: label }),
848
+ props.colorSwatch ? /* @__PURE__ */ jsx(
849
+ "span",
850
+ {
851
+ "aria-hidden": "true",
852
+ style: {
853
+ width: 14,
854
+ height: 14,
855
+ flex: "0 0 14px",
856
+ marginRight: 8,
857
+ borderRadius: 2,
858
+ backgroundColor: props.colorSwatch,
859
+ boxShadow: "inset 0 0 0 1px rgba(0, 0, 0, 0.12)"
860
+ }
861
+ }
862
+ ) : null,
863
+ /* @__PURE__ */ jsx(
864
+ "span",
865
+ {
866
+ style: {
867
+ flex: 1,
868
+ whiteSpace: "nowrap",
869
+ ...props.colorSwatch ? {
870
+ minWidth: 0,
871
+ overflow: "hidden",
872
+ textOverflow: "ellipsis"
873
+ } : null
874
+ },
875
+ children: label
876
+ }
877
+ ),
431
878
  preview ? /* @__PURE__ */ jsx(
432
879
  "span",
433
880
  {
@@ -538,6 +985,7 @@ function MenuSubmenu(props) {
538
985
  MenuAction,
539
986
  {
540
987
  label: item.label,
988
+ title: item.title,
541
989
  preview: item.preview,
542
990
  onClick: item.onClick,
543
991
  selected: item.selected,
@@ -1027,13 +1475,7 @@ function CustomSelectionMenu(props) {
1027
1475
  if (!request.onCopyCustomSelection || !request.customSelection) {
1028
1476
  return null;
1029
1477
  }
1030
- return /* @__PURE__ */ jsx(
1031
- MenuAction,
1032
- {
1033
- label: "\u590D\u5236\u81EA\u5B9A\u4E49\u9009\u533A",
1034
- onClick: props.onOpen
1035
- }
1036
- );
1478
+ return /* @__PURE__ */ jsx(MenuAction, { label: "\u590D\u5236\u81EA\u5B9A\u4E49\u9009\u533A", onClick: props.onOpen });
1037
1479
  }
1038
1480
  function CustomSelectionDialog(props) {
1039
1481
  const { request } = props;
@@ -1246,6 +1688,16 @@ function SummaryMenu(props) {
1246
1688
  selected: request.summaryType === item.value,
1247
1689
  onClick: () => request.onChangeSummaryType?.(item.value)
1248
1690
  })),
1691
+ {
1692
+ key: "show-summary-type-prefix",
1693
+ label: "\u5C55\u793A\u6C47\u603B\u65B9\u5F0F\u524D\u7F00",
1694
+ separatorBefore: true,
1695
+ selected: request.summaryShowTypePrefix !== false,
1696
+ disabled: !request.summaryType,
1697
+ onClick: request.onChangeSummaryShowTypePrefix ? () => request.onChangeSummaryShowTypePrefix?.(
1698
+ request.summaryShowTypePrefix === false
1699
+ ) : void 0
1700
+ },
1249
1701
  {
1250
1702
  key: "clear-summary",
1251
1703
  label: "\u53D6\u6D88\u6C47\u603B",
@@ -2227,6 +2679,58 @@ var toolButtonStyle = {
2227
2679
  lineHeight: 1,
2228
2680
  cursor: "pointer"
2229
2681
  };
2682
+ function ReactListTableExportNameDialog({
2683
+ request
2684
+ }) {
2685
+ const extension = request.fileName.match(/\.(xlsx|csv|txt|json)$/i)?.[0] ?? "";
2686
+ const [fileName, setFileName] = useState(
2687
+ extension ? request.fileName.slice(0, -extension.length) : request.fileName
2688
+ );
2689
+ const [invalid, setInvalid] = useState(false);
2690
+ const confirm = () => {
2691
+ if (!fileName.trim()) {
2692
+ setInvalid(true);
2693
+ return;
2694
+ }
2695
+ request.onConfirm(`${fileName.trim()}${extension}`);
2696
+ };
2697
+ return /* @__PURE__ */ jsxs(
2698
+ ReactModal,
2699
+ {
2700
+ open: true,
2701
+ title: "\u5BFC\u51FA\u6587\u4EF6",
2702
+ width: 420,
2703
+ fullscreenable: false,
2704
+ onCancel: request.onCancel,
2705
+ onOk: confirm,
2706
+ okText: "\u5BFC\u51FA",
2707
+ cancelText: "\u53D6\u6D88",
2708
+ children: [
2709
+ /* @__PURE__ */ jsxs("label", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
2710
+ "\u6587\u4EF6\u540D\u79F0",
2711
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
2712
+ /* @__PURE__ */ jsx(
2713
+ ReactInput,
2714
+ {
2715
+ autoFocus: true,
2716
+ value: fileName,
2717
+ placeholder: "\u8BF7\u8F93\u5165\u6587\u4EF6\u540D\u79F0",
2718
+ onFocus: (event) => event.target.select(),
2719
+ onChange: (event) => {
2720
+ setFileName(event.target.value);
2721
+ setInvalid(false);
2722
+ },
2723
+ onPressEnter: confirm
2724
+ }
2725
+ ),
2726
+ /* @__PURE__ */ jsx("span", { "aria-label": "\u6587\u4EF6\u540E\u7F00", style: { flexShrink: 0 }, children: extension })
2727
+ ] })
2728
+ ] }),
2729
+ invalid ? /* @__PURE__ */ jsx("div", { role: "alert", style: { color: "#ff4d4f", marginTop: 8 }, children: "\u8BF7\u8F93\u5165\u6587\u4EF6\u540D\u79F0" }) : null
2730
+ ]
2731
+ }
2732
+ );
2733
+ }
2230
2734
 
2231
2735
  // src/features/editor/utils.ts
2232
2736
  function toNumberValue(value) {
@@ -2328,12 +2832,16 @@ function ReactTableEditorOverlay(props) {
2328
2832
  if (!input) {
2329
2833
  return;
2330
2834
  }
2331
- focusAndSelectEditorInput(input);
2835
+ focusAndSelectEditorInput(input, request.selectOnFocus);
2332
2836
  }, [request]);
2333
2837
  return /* @__PURE__ */ jsx(ReactContentScaleProvider, { scale: contentZoom, children: /* @__PURE__ */ jsx("div", { ref: editorHostRef, children: renderEditorByType(request, style) }, request.editorKey) });
2334
2838
  }
2335
2839
  function renderEditorByType(request, style) {
2336
2840
  switch (request.config.valueType ?? "text") {
2841
+ case "tree-select":
2842
+ return /* @__PURE__ */ jsx(TreeSelectEditor, { request, style });
2843
+ case "cascader":
2844
+ return /* @__PURE__ */ jsx(CascaderEditor, { request, style });
2337
2845
  case "number":
2338
2846
  return /* @__PURE__ */ jsx(NumberEditor, { request, style });
2339
2847
  case "input-range":
@@ -2373,7 +2881,7 @@ function TextEditor(props) {
2373
2881
  const skipBlurCommitRef = useRef(false);
2374
2882
  useLayoutEffect(() => {
2375
2883
  const input = hostRef.current?.querySelector("input");
2376
- focusAndSelectEditorInput(input);
2884
+ focusAndSelectEditorInput(input, request.selectOnFocus);
2377
2885
  }, [request]);
2378
2886
  useEffect(() => {
2379
2887
  setValue(String(request.value ?? ""));
@@ -2413,7 +2921,7 @@ function NumberEditor(props) {
2413
2921
  const skipBlurCommitRef = useRef(false);
2414
2922
  useLayoutEffect(() => {
2415
2923
  const input = hostRef.current?.querySelector("input");
2416
- focusAndSelectEditorInput(input);
2924
+ focusAndSelectEditorInput(input, request.selectOnFocus);
2417
2925
  }, [request]);
2418
2926
  useEffect(() => {
2419
2927
  setValue(toNumberValue(request.value));
@@ -2622,6 +3130,94 @@ function DateEditor(props) {
2622
3130
  }
2623
3131
  ) });
2624
3132
  }
3133
+ function TreeSelectEditor({
3134
+ request,
3135
+ style
3136
+ }) {
3137
+ const [value, setValue] = useState(
3138
+ request.value ?? null
3139
+ );
3140
+ const skipBlurCommitRef = useRef(false);
3141
+ useEffect(() => {
3142
+ setValue(request.value ?? null);
3143
+ }, [request.value]);
3144
+ return /* @__PURE__ */ jsx("div", { style, children: /* @__PURE__ */ jsx(
3145
+ ReactTreeSelect,
3146
+ {
3147
+ autoFocus: true,
3148
+ value,
3149
+ treeData: request.config.treeData,
3150
+ multiple: request.config.multiple,
3151
+ showSearch: request.config.showSearch,
3152
+ treeDefaultExpandAll: request.config.treeDefaultExpandAll,
3153
+ treeDefaultExpandedKeys: request.config.treeDefaultExpandedKeys,
3154
+ allowClear: request.config.allowClear,
3155
+ placeholder: request.config.placeholder,
3156
+ style: getEditorInputStyle(request),
3157
+ onChange: (next, _nodes, info) => {
3158
+ setValue(next);
3159
+ request.onChange(next);
3160
+ if (info.complete && info.trigger !== "clear") {
3161
+ skipBlurCommitRef.current = true;
3162
+ request.onCommit();
3163
+ }
3164
+ },
3165
+ onBlur: (event) => handleEditorBlur(event, skipBlurCommitRef, request),
3166
+ onKeyDown: (event) => {
3167
+ if (event.key === "Tab")
3168
+ handleEditorNavigationKey(event, request, skipBlurCommitRef);
3169
+ if (event.key === "Escape") {
3170
+ skipBlurCommitRef.current = true;
3171
+ handleEditorEscapeKey(event, request);
3172
+ }
3173
+ }
3174
+ }
3175
+ ) });
3176
+ }
3177
+ function CascaderEditor({
3178
+ request,
3179
+ style
3180
+ }) {
3181
+ const [value, setValue] = useState(Array.isArray(request.value) ? request.value : []);
3182
+ const skipBlurCommitRef = useRef(false);
3183
+ useEffect(() => {
3184
+ setValue(Array.isArray(request.value) ? request.value : []);
3185
+ }, [request.value]);
3186
+ return /* @__PURE__ */ jsx("div", { style, children: /* @__PURE__ */ jsx(
3187
+ ReactCascader,
3188
+ {
3189
+ value,
3190
+ options: request.config.options,
3191
+ autoFocus: true,
3192
+ placeholder: request.config.placeholder,
3193
+ showSearch: request.config.showSearch,
3194
+ allowClear: request.config.allowClear,
3195
+ changeOnSelect: request.config.changeOnSelect,
3196
+ multiple: request.config.multiple,
3197
+ showCheckedStrategy: request.config.showCheckedStrategy,
3198
+ expandTrigger: request.config.expandTrigger,
3199
+ displayRender: request.config.displayRender,
3200
+ style: getEditorInputStyle(request),
3201
+ onChange: (next, _options, info) => {
3202
+ setValue(next);
3203
+ request.onChange(next);
3204
+ if (info.complete && info.trigger !== "clear") {
3205
+ skipBlurCommitRef.current = true;
3206
+ request.onCommit();
3207
+ }
3208
+ },
3209
+ onBlur: (event) => handleEditorBlur(event, skipBlurCommitRef, request),
3210
+ onKeyDown: (event) => {
3211
+ if (event.key === "Tab")
3212
+ handleEditorNavigationKey(event, request, skipBlurCommitRef);
3213
+ if (event.key === "Escape") {
3214
+ skipBlurCommitRef.current = true;
3215
+ handleEditorEscapeKey(event, request);
3216
+ }
3217
+ }
3218
+ }
3219
+ ) });
3220
+ }
2625
3221
  function SelectEditor(props) {
2626
3222
  const { request, style } = props;
2627
3223
  const options = request.config.options ?? [];
@@ -2952,13 +3548,14 @@ function handleRangeInputTab(event) {
2952
3548
  focusAndSelectEditorInput(nextInput);
2953
3549
  return true;
2954
3550
  }
2955
- function focusAndSelectEditorInput(input) {
3551
+ function focusAndSelectEditorInput(input, selectOnFocus = true) {
2956
3552
  if (!input) {
2957
3553
  return;
2958
3554
  }
2959
3555
  input.focus();
2960
3556
  try {
2961
- input.select();
3557
+ if (selectOnFocus) input.select();
3558
+ else input.setSelectionRange(input.value.length, input.value.length);
2962
3559
  } catch {
2963
3560
  }
2964
3561
  }
@@ -3448,7 +4045,7 @@ function resolveSortableTransform(index, sourceIndex, targetIndex, height) {
3448
4045
  }
3449
4046
 
3450
4047
  // src/features/toolbar/style.css
3451
- 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, .14);\n}\n.react-list-table-tool-menu button {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 6px 10px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n text-align: left;\n cursor: pointer;\n}\n.react-list-table-tool-menu button.active::after {\n content: "\\2713";\n margin-left: 12px;\n font-size: 14px;\n}\n.react-list-table-tool-menu button:hover,\n.react-list-table-tool-menu button.active {\n color: #1677ff;\n background: #e6f4ff;\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 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-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 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');
4048
+ 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, .14);\n}\n.react-list-table-tool-menu button {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 6px 10px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n text-align: left;\n cursor: pointer;\n}\n.react-list-table-tool-menu button.active::after {\n content: "\\2713";\n margin-left: 12px;\n font-size: 14px;\n}\n.react-list-table-tool-menu button:hover,\n.react-list-table-tool-menu button.active {\n color: #1677ff;\n background: #e6f4ff;\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-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 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');
3452
4049
  function ReactTableToolbar(props) {
3453
4050
  const { toolbar, title } = props;
3454
4051
  const [densityOpen, setDensityOpen] = useState(false);
@@ -3565,6 +4162,18 @@ function ReactTableToolbar(props) {
3565
4162
  children: [
3566
4163
  /* @__PURE__ */ jsxs("div", { className: "react-list-table-column-setting-head", children: [
3567
4164
  /* @__PURE__ */ jsx("strong", { children: "\u5217\u8BBE\u7F6E" }),
4165
+ /* @__PURE__ */ jsx(
4166
+ ReactCheckbox,
4167
+ {
4168
+ checked: props.cacheColumnSettings === true,
4169
+ disabled: !props.columnSettingsCacheAvailable,
4170
+ title: props.columnSettingsCacheAvailable ? "\u7F13\u5B58\u5217\u5BBD\u3001\u663E\u9690\u3001\u56FA\u5B9A\u548C\u987A\u5E8F" : "\u914D\u7F6E storageKey \u540E\u53EF\u7F13\u5B58\u5217\u8BBE\u7F6E",
4171
+ onChange: (event) => props.onColumnSettingsCacheChange?.(
4172
+ event.target.checked
4173
+ ),
4174
+ children: "\u7F13\u5B58\u5230\u672C\u5730"
4175
+ }
4176
+ ),
3568
4177
  /* @__PURE__ */ jsx("span", { style: { marginLeft: "auto" }, children: config.columnSettingHeaderExtra }),
3569
4178
  /* @__PURE__ */ jsx("button", { type: "button", onClick: props.onResetColumns, children: "\u91CD\u7F6E" })
3570
4179
  ] }),
@@ -3712,9 +4321,14 @@ function useReactListTableState(options) {
3712
4321
  const [searchRequest, setSearchRequest] = useState(null);
3713
4322
  const [imagePreviewRequest, setImagePreviewRequest] = useState(null);
3714
4323
  const [dangerConfirmRequest, setDangerConfirmRequest] = useState(null);
4324
+ const [exportNameRequest, setExportNameRequest] = useState(null);
3715
4325
  const uiOptions = useMemo(
3716
4326
  () => ({
3717
4327
  ...options.ui,
4328
+ onExportNameRequest: (request) => {
4329
+ setExportNameRequest(request);
4330
+ options.ui?.onExportNameRequest?.(request);
4331
+ },
3718
4332
  onEditorRequest: (request) => {
3719
4333
  setEditorRequest(request);
3720
4334
  options.ui?.onEditorRequest?.(request);
@@ -3782,6 +4396,7 @@ function useReactListTableState(options) {
3782
4396
  setSearchRequest(null);
3783
4397
  setImagePreviewRequest(null);
3784
4398
  setDangerConfirmRequest(null);
4399
+ setExportNameRequest(null);
3785
4400
  };
3786
4401
  }, []);
3787
4402
  useEffect(() => {
@@ -3803,6 +4418,7 @@ function useReactListTableState(options) {
3803
4418
  searchRequest,
3804
4419
  imagePreviewRequest,
3805
4420
  dangerConfirmRequest,
4421
+ exportNameRequest,
3806
4422
  closeDangerConfirmRequest: () => {
3807
4423
  setDangerConfirmRequest(null);
3808
4424
  options.ui?.onDangerConfirmRequest?.(null);
@@ -3900,12 +4516,15 @@ function ReactListTableInner(props, ref) {
3900
4516
  stretchColumns,
3901
4517
  resizable,
3902
4518
  storageKey,
4519
+ cacheColumnSettings,
4520
+ cachePreferences,
3903
4521
  draggable,
3904
4522
  tableId,
3905
4523
  rowDrag,
3906
4524
  debug,
3907
4525
  title,
3908
- toolbar
4526
+ toolbar,
4527
+ exportOptions
3909
4528
  } = props;
3910
4529
  const inheritedDebugEnabled = useReactListTableDebugEnabled();
3911
4530
  const resolvedDebug = useMemo(
@@ -3921,6 +4540,10 @@ function ReactListTableInner(props, ref) {
3921
4540
  );
3922
4541
  const [columnConfig, setColumnConfig] = useState(() => /* @__PURE__ */ new Map());
3923
4542
  const [columnConfigRevision, setColumnConfigRevision] = useState(0);
4543
+ const [columnSettingsCacheOverride, setColumnSettingsCacheOverride] = useState(null);
4544
+ const resolvedColumnSettingsCache = (columnSettingsCacheOverride?.storageKey === storageKey ? columnSettingsCacheOverride?.value : void 0) ?? cacheColumnSettings ?? true;
4545
+ const [cachePreferencesOverride, setCachePreferencesOverride] = useState();
4546
+ const resolvedCachePreferences = (cachePreferencesOverride?.storageKey === storageKey ? cachePreferencesOverride?.value : void 0) ?? cachePreferences;
3924
4547
  const [columnConfigResetRevision, setColumnConfigResetRevision] = useState(0);
3925
4548
  const resolvedColumns = useMemo(
3926
4549
  () => applyColumnManagerState(
@@ -3942,6 +4565,7 @@ function ReactListTableInner(props, ref) {
3942
4565
  );
3943
4566
  const options = useMemo(() => {
3944
4567
  const nextOptions = buildListTableOptions({
4568
+ exportOptions,
3945
4569
  width,
3946
4570
  height,
3947
4571
  emptyText,
@@ -3974,6 +4598,8 @@ function ReactListTableInner(props, ref) {
3974
4598
  stretchColumns,
3975
4599
  resizable,
3976
4600
  storageKey,
4601
+ cacheColumnSettings: resolvedColumnSettingsCache,
4602
+ cachePreferences: resolvedCachePreferences,
3977
4603
  draggable,
3978
4604
  tableId,
3979
4605
  rowDrag,
@@ -3991,10 +4617,17 @@ function ReactListTableInner(props, ref) {
3991
4617
  columnConfigResetRevision,
3992
4618
  ui: {
3993
4619
  ...nextOptions.ui,
3994
- onDensityChange: setDensityOverride
4620
+ onDensityChange: setDensityOverride,
4621
+ onColumnSettingsCacheChange: (value) => setColumnSettingsCacheOverride(
4622
+ (current) => current?.storageKey === storageKey && current?.value === value ? current : { storageKey, value }
4623
+ ),
4624
+ onCachePreferencesChange: (next) => setCachePreferencesOverride(
4625
+ (current) => current?.storageKey === storageKey && JSON.stringify(current?.value ?? {}) === JSON.stringify(next) ? current : { storageKey, value: next }
4626
+ )
3995
4627
  }
3996
4628
  };
3997
4629
  }, [
4630
+ exportOptions,
3998
4631
  width,
3999
4632
  height,
4000
4633
  emptyText,
@@ -4027,6 +4660,8 @@ function ReactListTableInner(props, ref) {
4027
4660
  stretchColumns,
4028
4661
  resizable,
4029
4662
  storageKey,
4663
+ resolvedColumnSettingsCache,
4664
+ resolvedCachePreferences,
4030
4665
  draggable,
4031
4666
  tableId,
4032
4667
  rowDrag,
@@ -4048,6 +4683,7 @@ function ReactListTableInner(props, ref) {
4048
4683
  searchRequest,
4049
4684
  imagePreviewRequest,
4050
4685
  dangerConfirmRequest,
4686
+ exportNameRequest,
4051
4687
  closeDangerConfirmRequest
4052
4688
  } = useReactListTableState(options);
4053
4689
  const hasPagination = paginationConfig != null;
@@ -4095,6 +4731,9 @@ function ReactListTableInner(props, ref) {
4095
4731
  {
4096
4732
  title,
4097
4733
  toolbar,
4734
+ cacheColumnSettings: Boolean(storageKey) && resolvedColumnSettingsCache,
4735
+ columnSettingsCacheAvailable: Boolean(storageKey),
4736
+ onColumnSettingsCacheChange: (enabled) => tableRef.current?.setColumnSettingsCache(enabled),
4098
4737
  columns: resolvedColumns,
4099
4738
  density: resolvedRowHeight ?? 32,
4100
4739
  hostRef: shellRef,
@@ -4224,6 +4863,10 @@ function ReactListTableInner(props, ref) {
4224
4863
  /* @__PURE__ */ jsx(ReactListTableSearchDialog, { request: searchRequest }),
4225
4864
  document.body
4226
4865
  ) : null,
4866
+ exportNameRequest ? createPortal(
4867
+ /* @__PURE__ */ jsx(ReactListTableExportNameDialog, { request: exportNameRequest }),
4868
+ document.body
4869
+ ) : null,
4227
4870
  dangerConfirmRequest ? createPortal(
4228
4871
  /* @__PURE__ */ jsx(
4229
4872
  ReactTableDangerConfirmView,