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

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 } 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",
@@ -2334,6 +2786,8 @@ function ReactTableEditorOverlay(props) {
2334
2786
  }
2335
2787
  function renderEditorByType(request, style) {
2336
2788
  switch (request.config.valueType ?? "text") {
2789
+ case "cascader":
2790
+ return /* @__PURE__ */ jsx(CascaderEditor, { request, style });
2337
2791
  case "number":
2338
2792
  return /* @__PURE__ */ jsx(NumberEditor, { request, style });
2339
2793
  case "input-range":
@@ -2622,6 +3076,50 @@ function DateEditor(props) {
2622
3076
  }
2623
3077
  ) });
2624
3078
  }
3079
+ function CascaderEditor({
3080
+ request,
3081
+ style
3082
+ }) {
3083
+ const [value, setValue] = useState(
3084
+ Array.isArray(request.value) ? request.value : []
3085
+ );
3086
+ const skipBlurCommitRef = useRef(false);
3087
+ useEffect(() => {
3088
+ setValue(Array.isArray(request.value) ? request.value : []);
3089
+ }, [request.value]);
3090
+ return /* @__PURE__ */ jsx("div", { style, children: /* @__PURE__ */ jsx(
3091
+ ReactCascader,
3092
+ {
3093
+ value,
3094
+ options: request.config.options,
3095
+ autoFocus: true,
3096
+ placeholder: request.config.placeholder,
3097
+ showSearch: request.config.showSearch,
3098
+ allowClear: request.config.allowClear,
3099
+ changeOnSelect: request.config.changeOnSelect,
3100
+ expandTrigger: request.config.expandTrigger,
3101
+ displayRender: request.config.displayRender,
3102
+ style: getEditorInputStyle(request),
3103
+ onChange: (next, _options, info) => {
3104
+ setValue(next);
3105
+ request.onChange(next);
3106
+ if (info.complete && info.trigger !== "clear") {
3107
+ skipBlurCommitRef.current = true;
3108
+ request.onCommit();
3109
+ }
3110
+ },
3111
+ onBlur: (event) => handleEditorBlur(event, skipBlurCommitRef, request),
3112
+ onKeyDown: (event) => {
3113
+ if (event.key === "Tab")
3114
+ handleEditorNavigationKey(event, request, skipBlurCommitRef);
3115
+ if (event.key === "Escape") {
3116
+ skipBlurCommitRef.current = true;
3117
+ handleEditorEscapeKey(event, request);
3118
+ }
3119
+ }
3120
+ }
3121
+ ) });
3122
+ }
2625
3123
  function SelectEditor(props) {
2626
3124
  const { request, style } = props;
2627
3125
  const options = request.config.options ?? [];
@@ -3448,7 +3946,7 @@ function resolveSortableTransform(index, sourceIndex, targetIndex, height) {
3448
3946
  }
3449
3947
 
3450
3948
  // 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');
3949
+ 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
3950
  function ReactTableToolbar(props) {
3453
3951
  const { toolbar, title } = props;
3454
3952
  const [densityOpen, setDensityOpen] = useState(false);
@@ -3565,6 +4063,18 @@ function ReactTableToolbar(props) {
3565
4063
  children: [
3566
4064
  /* @__PURE__ */ jsxs("div", { className: "react-list-table-column-setting-head", children: [
3567
4065
  /* @__PURE__ */ jsx("strong", { children: "\u5217\u8BBE\u7F6E" }),
4066
+ /* @__PURE__ */ jsx(
4067
+ ReactCheckbox,
4068
+ {
4069
+ checked: props.cacheColumnSettings === true,
4070
+ disabled: !props.columnSettingsCacheAvailable,
4071
+ 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",
4072
+ onChange: (event) => props.onColumnSettingsCacheChange?.(
4073
+ event.target.checked
4074
+ ),
4075
+ children: "\u7F13\u5B58\u5230\u672C\u5730"
4076
+ }
4077
+ ),
3568
4078
  /* @__PURE__ */ jsx("span", { style: { marginLeft: "auto" }, children: config.columnSettingHeaderExtra }),
3569
4079
  /* @__PURE__ */ jsx("button", { type: "button", onClick: props.onResetColumns, children: "\u91CD\u7F6E" })
3570
4080
  ] }),
@@ -3900,6 +4410,8 @@ function ReactListTableInner(props, ref) {
3900
4410
  stretchColumns,
3901
4411
  resizable,
3902
4412
  storageKey,
4413
+ cacheColumnSettings,
4414
+ cachePreferences,
3903
4415
  draggable,
3904
4416
  tableId,
3905
4417
  rowDrag,
@@ -3921,6 +4433,10 @@ function ReactListTableInner(props, ref) {
3921
4433
  );
3922
4434
  const [columnConfig, setColumnConfig] = useState(() => /* @__PURE__ */ new Map());
3923
4435
  const [columnConfigRevision, setColumnConfigRevision] = useState(0);
4436
+ const [columnSettingsCacheOverride, setColumnSettingsCacheOverride] = useState(null);
4437
+ const resolvedColumnSettingsCache = (columnSettingsCacheOverride?.storageKey === storageKey ? columnSettingsCacheOverride?.value : void 0) ?? cacheColumnSettings ?? true;
4438
+ const [cachePreferencesOverride, setCachePreferencesOverride] = useState();
4439
+ const resolvedCachePreferences = (cachePreferencesOverride?.storageKey === storageKey ? cachePreferencesOverride?.value : void 0) ?? cachePreferences;
3924
4440
  const [columnConfigResetRevision, setColumnConfigResetRevision] = useState(0);
3925
4441
  const resolvedColumns = useMemo(
3926
4442
  () => applyColumnManagerState(
@@ -3974,6 +4490,8 @@ function ReactListTableInner(props, ref) {
3974
4490
  stretchColumns,
3975
4491
  resizable,
3976
4492
  storageKey,
4493
+ cacheColumnSettings: resolvedColumnSettingsCache,
4494
+ cachePreferences: resolvedCachePreferences,
3977
4495
  draggable,
3978
4496
  tableId,
3979
4497
  rowDrag,
@@ -3991,7 +4509,13 @@ function ReactListTableInner(props, ref) {
3991
4509
  columnConfigResetRevision,
3992
4510
  ui: {
3993
4511
  ...nextOptions.ui,
3994
- onDensityChange: setDensityOverride
4512
+ onDensityChange: setDensityOverride,
4513
+ onColumnSettingsCacheChange: (value) => setColumnSettingsCacheOverride(
4514
+ (current) => current?.storageKey === storageKey && current?.value === value ? current : { storageKey, value }
4515
+ ),
4516
+ onCachePreferencesChange: (next) => setCachePreferencesOverride(
4517
+ (current) => current?.storageKey === storageKey && JSON.stringify(current?.value ?? {}) === JSON.stringify(next) ? current : { storageKey, value: next }
4518
+ )
3995
4519
  }
3996
4520
  };
3997
4521
  }, [
@@ -4027,6 +4551,8 @@ function ReactListTableInner(props, ref) {
4027
4551
  stretchColumns,
4028
4552
  resizable,
4029
4553
  storageKey,
4554
+ resolvedColumnSettingsCache,
4555
+ resolvedCachePreferences,
4030
4556
  draggable,
4031
4557
  tableId,
4032
4558
  rowDrag,
@@ -4095,6 +4621,9 @@ function ReactListTableInner(props, ref) {
4095
4621
  {
4096
4622
  title,
4097
4623
  toolbar,
4624
+ cacheColumnSettings: Boolean(storageKey) && resolvedColumnSettingsCache,
4625
+ columnSettingsCacheAvailable: Boolean(storageKey),
4626
+ onColumnSettingsCacheChange: (enabled) => tableRef.current?.setColumnSettingsCache(enabled),
4098
4627
  columns: resolvedColumns,
4099
4628
  density: resolvedRowHeight ?? 32,
4100
4629
  hostRef: shellRef,