@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.cjs CHANGED
@@ -4,9 +4,9 @@ var react = require('react');
4
4
  var reactDom = require('react-dom');
5
5
  var reactComponents = require('@canvas-components/react-components');
6
6
  var listTable = require('@canvas-components/list-table');
7
+ var jsxRuntime = require('react/jsx-runtime');
7
8
  var date = require('@canvas-components/utils/date');
8
9
  var number = require('@canvas-components/utils/number');
9
- var jsxRuntime = require('react/jsx-runtime');
10
10
 
11
11
  // #style-inject:#style-inject
12
12
  function styleInject(css, { insertAt } = {}) {
@@ -32,6 +32,328 @@ function styleInject(css, { insertAt } = {}) {
32
32
 
33
33
  // src/styles/table-overrides.css
34
34
  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");
35
+ var conditionalOperators = {
36
+ eq: "\u7B49\u4E8E",
37
+ neq: "\u4E0D\u7B49\u4E8E",
38
+ gt: "\u5927\u4E8E",
39
+ gte: "\u5927\u4E8E\u7B49\u4E8E",
40
+ lt: "\u5C0F\u4E8E",
41
+ lte: "\u5C0F\u4E8E\u7B49\u4E8E",
42
+ between: "\u4ECB\u4E8E\uFF08\u542B\u8FB9\u754C\uFF09",
43
+ contains: "\u5305\u542B\u6587\u672C",
44
+ empty: "\u4E3A\u7A7A",
45
+ notEmpty: "\u4E0D\u4E3A\u7A7A"
46
+ };
47
+ function describeConditionalRule(rule) {
48
+ return resolveConditions(rule).map(
49
+ (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 ?? ""}` : ""}`
50
+ ).join("");
51
+ }
52
+ function resolveConditions(rule) {
53
+ return rule.conditions ?? (rule.operator ? [
54
+ {
55
+ operator: rule.operator,
56
+ value: rule.value,
57
+ endValue: rule.endValue
58
+ }
59
+ ] : []);
60
+ }
61
+ function validateConditionalRule(rule) {
62
+ const conditions = resolveConditions(rule);
63
+ if (!conditions.length) return "\u8BF7\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u6761\u4EF6\u3002";
64
+ for (const [index, condition] of conditions.entries()) {
65
+ const error = validateCondition(condition);
66
+ if (error) return `\u6761\u4EF6 ${index + 1}\uFF1A${error}`;
67
+ }
68
+ return null;
69
+ }
70
+ function validateCondition(rule) {
71
+ if (rule.operator === "empty" || rule.operator === "notEmpty") return null;
72
+ if (String(rule.value ?? "").trim() === "")
73
+ return "\u8BF7\u8F93\u5165\u6BD4\u8F83\u503C\uFF1B\u7A7A\u503C\u8BF7\u4F7F\u7528\u201C\u4E3A\u7A7A\u201D\u6761\u4EF6\u3002";
74
+ if (["gt", "gte", "lt", "lte", "between"].includes(rule.operator)) {
75
+ if (!Number.isFinite(Number(rule.value))) return "\u8BF7\u8F93\u5165\u6709\u6548\u6570\u503C\u3002";
76
+ if (rule.operator === "between" && (String(rule.endValue ?? "").trim() === "" || !Number.isFinite(Number(rule.endValue)) || Number(rule.endValue) < Number(rule.value)))
77
+ return "\u8BF7\u8F93\u5165\u4E0D\u5C0F\u4E8E\u8D77\u59CB\u503C\u7684\u7ED3\u675F\u6570\u503C\u3002";
78
+ }
79
+ return null;
80
+ }
81
+ function ConditionalFormatDialog(props) {
82
+ const rules = props.request.conditionalFormats ?? [];
83
+ const [rule, setRule] = react.useState(
84
+ () => props.index == null ? {
85
+ id: `rule-${Date.now()}-${Math.random().toString(36).slice(2)}`,
86
+ operator: "gt",
87
+ value: "",
88
+ backgroundColor: "#fff1b8",
89
+ textColor: "#1f2329"
90
+ } : { ...rules[props.index] }
91
+ );
92
+ const [error, setError] = react.useState(null);
93
+ const conditions = resolveConditions(rule);
94
+ const patch = (value) => {
95
+ setRule((current) => ({ ...current, ...value }));
96
+ setError(null);
97
+ };
98
+ const apply = (next) => {
99
+ props.request.onChangeConditionalFormats?.(next);
100
+ props.onClose();
101
+ };
102
+ const patchCondition = (index, value) => {
103
+ patch({
104
+ conditions: conditions.map(
105
+ (condition, i) => i === index ? { ...condition, ...value } : condition
106
+ )
107
+ });
108
+ };
109
+ return /* @__PURE__ */ jsxRuntime.jsx(
110
+ reactComponents.ReactModal,
111
+ {
112
+ open: true,
113
+ title: props.index == null ? "\u65B0\u589E\u6761\u4EF6\u683C\u5F0F\u89C4\u5219" : "\u7F16\u8F91\u6761\u4EF6\u683C\u5F0F\u89C4\u5219",
114
+ width: 460,
115
+ onCancel: props.onClose,
116
+ onOk: () => {
117
+ const message = validateConditionalRule(rule);
118
+ if (message) {
119
+ setError(message);
120
+ return;
121
+ }
122
+ const next = rules.map((item) => ({ ...item }));
123
+ if (props.index == null) next.push(rule);
124
+ else next[props.index] = rule;
125
+ apply(next);
126
+ },
127
+ okText: "\u4FDD\u5B58\u89C4\u5219",
128
+ cancelText: "\u53D6\u6D88",
129
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "grid", gap: 14, fontSize: 14 }, children: [
130
+ /* @__PURE__ */ jsxRuntime.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" }),
131
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
132
+ "\u89C4\u5219\u540D\u79F0\uFF08\u53EF\u9009\uFF09",
133
+ /* @__PURE__ */ jsxRuntime.jsx(
134
+ reactComponents.ReactInput,
135
+ {
136
+ value: rule.name ?? "",
137
+ onChange: (event) => patch({ name: event.target.value })
138
+ }
139
+ )
140
+ ] }),
141
+ /* @__PURE__ */ jsxRuntime.jsx(
142
+ "div",
143
+ {
144
+ style: {
145
+ display: "grid",
146
+ gap: 12,
147
+ maxHeight: "35vh",
148
+ overflowY: "auto"
149
+ },
150
+ children: conditions.map((condition, index) => /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
151
+ index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
152
+ reactComponents.ReactSelect,
153
+ {
154
+ "aria-label": `\u6761\u4EF6 ${index} \u4E0E\u6761\u4EF6 ${index + 1} \u7684\u5173\u7CFB`,
155
+ value: condition.relation ?? rule.conditionRelation ?? "and",
156
+ options: [
157
+ { value: "and", label: "\u4E14" },
158
+ { value: "or", label: "\u6216" }
159
+ ],
160
+ onChange: (value) => patchCondition(index, { relation: value }),
161
+ style: { width: 88, justifySelf: "center" }
162
+ }
163
+ ),
164
+ /* @__PURE__ */ jsxRuntime.jsxs(
165
+ "div",
166
+ {
167
+ style: {
168
+ display: "grid",
169
+ gap: 8,
170
+ padding: 12,
171
+ border: "1px solid #d9e1ec"
172
+ },
173
+ children: [
174
+ /* @__PURE__ */ jsxRuntime.jsxs(
175
+ "div",
176
+ {
177
+ style: {
178
+ display: "flex",
179
+ justifyContent: "space-between",
180
+ alignItems: "center"
181
+ },
182
+ children: [
183
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
184
+ "\u6761\u4EF6 ",
185
+ index + 1
186
+ ] }),
187
+ /* @__PURE__ */ jsxRuntime.jsx(
188
+ reactComponents.ReactButton,
189
+ {
190
+ type: "text",
191
+ size: "small",
192
+ disabled: conditions.length === 1,
193
+ "aria-label": `\u5220\u9664\u6761\u4EF6 ${index + 1}`,
194
+ onClick: () => patch({
195
+ conditions: conditions.filter((_, i) => i !== index)
196
+ }),
197
+ children: "\u5220\u9664\u6761\u4EF6"
198
+ }
199
+ )
200
+ ]
201
+ }
202
+ ),
203
+ /* @__PURE__ */ jsxRuntime.jsx(
204
+ reactComponents.ReactSelect,
205
+ {
206
+ "aria-label": `\u6761\u4EF6 ${index + 1} \u6BD4\u8F83\u65B9\u5F0F`,
207
+ value: condition.operator,
208
+ options: Object.entries(conditionalOperators).map(
209
+ ([value, label]) => ({ value, label })
210
+ ),
211
+ onChange: (value) => patchCondition(index, {
212
+ operator: value
213
+ })
214
+ }
215
+ ),
216
+ condition.operator !== "empty" && condition.operator !== "notEmpty" && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
217
+ "\u6BD4\u8F83\u503C",
218
+ /* @__PURE__ */ jsxRuntime.jsx(
219
+ reactComponents.ReactInput,
220
+ {
221
+ value: String(condition.value ?? ""),
222
+ onChange: (event) => patchCondition(index, { value: event.target.value })
223
+ }
224
+ )
225
+ ] }),
226
+ condition.operator === "between" && /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
227
+ "\u7ED3\u675F\u503C",
228
+ /* @__PURE__ */ jsxRuntime.jsx(
229
+ reactComponents.ReactInput,
230
+ {
231
+ value: String(condition.endValue ?? ""),
232
+ onChange: (event) => patchCondition(index, { endValue: event.target.value })
233
+ }
234
+ )
235
+ ] })
236
+ ]
237
+ }
238
+ )
239
+ ] }, index))
240
+ }
241
+ ),
242
+ /* @__PURE__ */ jsxRuntime.jsx(
243
+ reactComponents.ReactButton,
244
+ {
245
+ onClick: () => patch({
246
+ conditions: [
247
+ ...conditions,
248
+ { operator: "gt", value: "", relation: "and" }
249
+ ]
250
+ }),
251
+ children: "\u65B0\u589E\u6761\u4EF6"
252
+ }
253
+ ),
254
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "grid", gap: 4 }, children: [
255
+ "\u5355\u5143\u683C\u80CC\u666F",
256
+ /* @__PURE__ */ jsxRuntime.jsx(
257
+ reactComponents.ReactColorPicker,
258
+ {
259
+ "aria-label": "\u5355\u5143\u683C\u80CC\u666F",
260
+ value: rule.backgroundColor,
261
+ onChange: (backgroundColor) => patch({ backgroundColor })
262
+ }
263
+ )
264
+ ] }),
265
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "grid", gap: 4 }, children: [
266
+ "\u6587\u672C\u989C\u8272",
267
+ /* @__PURE__ */ jsxRuntime.jsx(
268
+ reactComponents.ReactColorPicker,
269
+ {
270
+ "aria-label": "\u6587\u672C\u989C\u8272",
271
+ value: rule.textColor,
272
+ onChange: (textColor) => patch({ textColor })
273
+ }
274
+ )
275
+ ] }),
276
+ /* @__PURE__ */ jsxRuntime.jsxs(
277
+ "div",
278
+ {
279
+ style: {
280
+ padding: "8px 12px",
281
+ backgroundColor: rule.backgroundColor,
282
+ color: rule.textColor,
283
+ border: "1px solid #d9e1ec"
284
+ },
285
+ children: [
286
+ "\u6837\u5F0F\u9884\u89C8\uFF1A",
287
+ describeConditionalRule(rule)
288
+ ]
289
+ }
290
+ ),
291
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", style: { color: "#cf1322" }, children: error }),
292
+ props.index != null && /* @__PURE__ */ jsxRuntime.jsx(
293
+ reactComponents.ReactButton,
294
+ {
295
+ type: "text",
296
+ danger: true,
297
+ onClick: () => apply(rules.filter((_, index) => index !== props.index)),
298
+ children: "\u5220\u9664\u6B64\u89C4\u5219"
299
+ }
300
+ )
301
+ ] })
302
+ }
303
+ );
304
+ }
305
+
306
+ // src/features/context-menu/cache-menu.ts
307
+ function buildCacheMenuItems(request) {
308
+ const available = request.columnSettingsCacheAvailable === true;
309
+ const items = [];
310
+ const state = {
311
+ disabled: !available,
312
+ title: available ? void 0 : "\u914D\u7F6E storageKey \u540E\u53EF\u7F13\u5B58\u914D\u7F6E"
313
+ };
314
+ if (request.onChangeColumnSettingsCache)
315
+ items.push({
316
+ ...state,
317
+ key: "cache-columns",
318
+ label: "\u5217\u8BBE\u7F6E",
319
+ selected: available && request.cacheColumnSettings === true,
320
+ onClick: () => request.onChangeColumnSettingsCache?.(!request.cacheColumnSettings)
321
+ });
322
+ if (request.onChangeCachePreference) {
323
+ const categories = [
324
+ ["sort", "\u6392\u5E8F"],
325
+ ["filter", "\u7B5B\u9009"],
326
+ ["summary", "\u6C47\u603B"],
327
+ ["alignment", "\u5BF9\u9F50"],
328
+ ["dataFormat", "\u683C\u5F0F"],
329
+ ["density", "\u8868\u683C\u5BC6\u5EA6"],
330
+ ["zoom", "\u7F29\u653E"]
331
+ ];
332
+ for (const [category, label] of categories)
333
+ items.push({
334
+ ...state,
335
+ key: `cache-${category}`,
336
+ label,
337
+ selected: available && request.cachePreferences?.[category] !== false,
338
+ onClick: () => request.onChangeCachePreference?.(
339
+ category,
340
+ request.cachePreferences?.[category] === false
341
+ )
342
+ });
343
+ }
344
+ if (request.onChangeConditionalFormats)
345
+ items.push({
346
+ ...state,
347
+ key: "cache-conditional",
348
+ label: "\u5F53\u524D\u5217\u6761\u4EF6\u683C\u5F0F",
349
+ selected: available && request.cacheConditionalFormats !== false,
350
+ onClick: () => request.onChangeConditionalFormats?.(
351
+ request.conditionalFormats ?? [],
352
+ request.cacheConditionalFormats === false
353
+ )
354
+ });
355
+ return items;
356
+ }
35
357
  var CONTEXT_MENU_SURFACE_STYLE = {
36
358
  padding: 8,
37
359
  background: "#ffffff",
@@ -40,10 +362,22 @@ var CONTEXT_MENU_SURFACE_STYLE = {
40
362
  function ReactTableContextMenuView(props) {
41
363
  const { request } = props;
42
364
  const [menuOpen, setMenuOpen] = react.useState(true);
365
+ const [conditionalEditor, setConditionalEditor] = react.useState(null);
366
+ const conditionalDialog = conditionalEditor ? /* @__PURE__ */ jsxRuntime.jsx(
367
+ ConditionalFormatDialog,
368
+ {
369
+ ...conditionalEditor,
370
+ onClose: () => {
371
+ setConditionalEditor(null);
372
+ conditionalEditor.request.onClose();
373
+ }
374
+ }
375
+ ) : null;
43
376
  const [moreFormatRequest, setMoreFormatRequest] = react.useState(null);
44
377
  const [customSelectionRequest, setCustomSelectionRequest] = react.useState(null);
45
378
  if (!request) {
46
379
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
380
+ conditionalDialog,
47
381
  moreFormatRequest ? /* @__PURE__ */ jsxRuntime.jsx(
48
382
  MoreFormatDialog,
49
383
  {
@@ -68,6 +402,7 @@ function ReactTableContextMenuView(props) {
68
402
  width: 260
69
403
  };
70
404
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
405
+ conditionalDialog,
71
406
  /* @__PURE__ */ jsxRuntime.jsx(
72
407
  reactComponents.ReactPopup,
73
408
  {
@@ -77,7 +412,7 @@ function ReactTableContextMenuView(props) {
77
412
  minWidth: 260,
78
413
  onOpenChange: (open) => {
79
414
  setMenuOpen(open);
80
- if (!open && !moreFormatRequest && !customSelectionRequest) {
415
+ if (!open && !moreFormatRequest && !customSelectionRequest && !conditionalEditor) {
81
416
  request.onClose();
82
417
  }
83
418
  },
@@ -112,7 +447,28 @@ function ReactTableContextMenuView(props) {
112
447
  key: "copy-selection",
113
448
  label: "\u590D\u5236\u9009\u533A",
114
449
  onClick: request.onCopySelection
115
- }
450
+ },
451
+ ...request.onCopyImage ? [
452
+ {
453
+ key: "copy-image-all",
454
+ label: "\u590D\u5236\u4E3A\u56FE\u7247 \xB7 \u6240\u6709\u533A\u57DF",
455
+ separatorBefore: true,
456
+ 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",
457
+ onClick: () => request.onCopyImage?.("all")
458
+ },
459
+ {
460
+ key: "copy-image-selection",
461
+ label: "\u590D\u5236\u4E3A\u56FE\u7247 \xB7 \u9009\u4E2D\u533A\u57DF",
462
+ disabled: !request.canCopySelectionImage,
463
+ onClick: () => request.onCopyImage?.("selection")
464
+ },
465
+ {
466
+ key: "copy-image-viewport",
467
+ label: "\u590D\u5236\u4E3A\u56FE\u7247 \xB7 \u89C6\u53E3\u533A\u57DF",
468
+ 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",
469
+ onClick: () => request.onCopyImage?.("viewport")
470
+ }
471
+ ] : []
116
472
  ],
117
473
  children: /* @__PURE__ */ jsxRuntime.jsx(
118
474
  CustomSelectionMenu,
@@ -151,7 +507,26 @@ function ReactTableContextMenuView(props) {
151
507
  key: "export-json",
152
508
  label: "\u5BFC\u51FA JSON",
153
509
  onClick: request.onExportJson
154
- }
510
+ },
511
+ ...request.onExportImage ? [
512
+ {
513
+ key: "export-image-all",
514
+ label: "\u5BFC\u51FA\u56FE\u7247 \xB7 \u6240\u6709\u533A\u57DF",
515
+ separatorBefore: true,
516
+ onClick: () => request.onExportImage?.("all")
517
+ },
518
+ {
519
+ key: "export-image-selection",
520
+ label: "\u5BFC\u51FA\u56FE\u7247 \xB7 \u9009\u4E2D\u533A\u57DF",
521
+ disabled: !request.canCopySelectionImage,
522
+ onClick: () => request.onExportImage?.("selection")
523
+ },
524
+ {
525
+ key: "export-image-viewport",
526
+ label: "\u5BFC\u51FA\u56FE\u7247 \xB7 \u89C6\u53E3\u533A\u57DF",
527
+ onClick: () => request.onExportImage?.("viewport")
528
+ }
529
+ ] : []
155
530
  ]
156
531
  }
157
532
  ),
@@ -224,6 +599,41 @@ function ReactTableContextMenuView(props) {
224
599
  ),
225
600
  /* @__PURE__ */ jsxRuntime.jsx(SummaryMenu, { request }),
226
601
  /* @__PURE__ */ jsxRuntime.jsx(AlignmentMenu, { request }),
602
+ request.onChangeConditionalFormats && /* @__PURE__ */ jsxRuntime.jsx(
603
+ MenuSubmenu,
604
+ {
605
+ label: "\u6761\u4EF6\u683C\u5F0F",
606
+ items: [
607
+ {
608
+ key: "add-rule",
609
+ label: "\u65B0\u589E\u89C4\u5219",
610
+ onClick: () => {
611
+ setConditionalEditor({ request, index: null });
612
+ setMenuOpen(false);
613
+ }
614
+ },
615
+ {
616
+ key: "reset-rules",
617
+ label: "\u91CD\u7F6E\u6761\u4EF6\u683C\u5F0F",
618
+ disabled: !request.conditionalFormats?.length,
619
+ onClick: () => request.onChangeConditionalFormats?.([])
620
+ }
621
+ ],
622
+ header: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gap: 4, paddingBottom: 8 }, children: request.conditionalFormats?.length ? request.conditionalFormats.map((rule, index) => /* @__PURE__ */ jsxRuntime.jsx(
623
+ MenuAction,
624
+ {
625
+ label: rule.name?.trim() || `\u89C4\u5219 ${index + 1}`,
626
+ colorSwatch: rule.backgroundColor,
627
+ title: `${rule.name?.trim() || `\u89C4\u5219 ${index + 1}`}\uFF1A${describeConditionalRule(rule)}`,
628
+ onClick: () => {
629
+ setConditionalEditor({ request, index });
630
+ setMenuOpen(false);
631
+ }
632
+ },
633
+ rule.id
634
+ )) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#64748b", padding: 8 }, children: "\u6682\u65E0\u89C4\u5219" }) })
635
+ }
636
+ ),
227
637
  /* @__PURE__ */ jsxRuntime.jsx(
228
638
  DataFormatMenu,
229
639
  {
@@ -354,6 +764,13 @@ function ReactTableContextMenuView(props) {
354
764
  }
355
765
  ),
356
766
  /* @__PURE__ */ jsxRuntime.jsx(ZoomMenu, { value: request.zoom ?? 1, onChange: request.onChangeZoom }),
767
+ /* @__PURE__ */ jsxRuntime.jsx(
768
+ MenuSubmenu,
769
+ {
770
+ label: "\u7F13\u5B58\u5230\u672C\u5730",
771
+ items: buildCacheMenuItems(request)
772
+ }
773
+ ),
357
774
  request.columns?.length && request.onToggleColumn ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
358
775
  /* @__PURE__ */ jsxRuntime.jsx(MenuDivider, {}),
359
776
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -400,6 +817,7 @@ function MenuAction(props) {
400
817
  type: "button",
401
818
  onClick: disabled ? void 0 : onClick,
402
819
  disabled,
820
+ title: props.title,
403
821
  className: "react-list-table-menu-action",
404
822
  style: {
405
823
  display: "flex",
@@ -428,7 +846,36 @@ function MenuAction(props) {
428
846
  children: selected ? "\u2713" : ""
429
847
  }
430
848
  ) : null,
431
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1, whiteSpace: "nowrap" }, children: label }),
849
+ props.colorSwatch ? /* @__PURE__ */ jsxRuntime.jsx(
850
+ "span",
851
+ {
852
+ "aria-hidden": "true",
853
+ style: {
854
+ width: 14,
855
+ height: 14,
856
+ flex: "0 0 14px",
857
+ marginRight: 8,
858
+ borderRadius: 2,
859
+ backgroundColor: props.colorSwatch,
860
+ boxShadow: "inset 0 0 0 1px rgba(0, 0, 0, 0.12)"
861
+ }
862
+ }
863
+ ) : null,
864
+ /* @__PURE__ */ jsxRuntime.jsx(
865
+ "span",
866
+ {
867
+ style: {
868
+ flex: 1,
869
+ whiteSpace: "nowrap",
870
+ ...props.colorSwatch ? {
871
+ minWidth: 0,
872
+ overflow: "hidden",
873
+ textOverflow: "ellipsis"
874
+ } : null
875
+ },
876
+ children: label
877
+ }
878
+ ),
432
879
  preview ? /* @__PURE__ */ jsxRuntime.jsx(
433
880
  "span",
434
881
  {
@@ -539,6 +986,7 @@ function MenuSubmenu(props) {
539
986
  MenuAction,
540
987
  {
541
988
  label: item.label,
989
+ title: item.title,
542
990
  preview: item.preview,
543
991
  onClick: item.onClick,
544
992
  selected: item.selected,
@@ -1028,13 +1476,7 @@ function CustomSelectionMenu(props) {
1028
1476
  if (!request.onCopyCustomSelection || !request.customSelection) {
1029
1477
  return null;
1030
1478
  }
1031
- return /* @__PURE__ */ jsxRuntime.jsx(
1032
- MenuAction,
1033
- {
1034
- label: "\u590D\u5236\u81EA\u5B9A\u4E49\u9009\u533A",
1035
- onClick: props.onOpen
1036
- }
1037
- );
1479
+ return /* @__PURE__ */ jsxRuntime.jsx(MenuAction, { label: "\u590D\u5236\u81EA\u5B9A\u4E49\u9009\u533A", onClick: props.onOpen });
1038
1480
  }
1039
1481
  function CustomSelectionDialog(props) {
1040
1482
  const { request } = props;
@@ -1247,6 +1689,16 @@ function SummaryMenu(props) {
1247
1689
  selected: request.summaryType === item.value,
1248
1690
  onClick: () => request.onChangeSummaryType?.(item.value)
1249
1691
  })),
1692
+ {
1693
+ key: "show-summary-type-prefix",
1694
+ label: "\u5C55\u793A\u6C47\u603B\u65B9\u5F0F\u524D\u7F00",
1695
+ separatorBefore: true,
1696
+ selected: request.summaryShowTypePrefix !== false,
1697
+ disabled: !request.summaryType,
1698
+ onClick: request.onChangeSummaryShowTypePrefix ? () => request.onChangeSummaryShowTypePrefix?.(
1699
+ request.summaryShowTypePrefix === false
1700
+ ) : void 0
1701
+ },
1250
1702
  {
1251
1703
  key: "clear-summary",
1252
1704
  label: "\u53D6\u6D88\u6C47\u603B",
@@ -2335,6 +2787,8 @@ function ReactTableEditorOverlay(props) {
2335
2787
  }
2336
2788
  function renderEditorByType(request, style) {
2337
2789
  switch (request.config.valueType ?? "text") {
2790
+ case "cascader":
2791
+ return /* @__PURE__ */ jsxRuntime.jsx(CascaderEditor, { request, style });
2338
2792
  case "number":
2339
2793
  return /* @__PURE__ */ jsxRuntime.jsx(NumberEditor, { request, style });
2340
2794
  case "input-range":
@@ -2623,6 +3077,50 @@ function DateEditor(props) {
2623
3077
  }
2624
3078
  ) });
2625
3079
  }
3080
+ function CascaderEditor({
3081
+ request,
3082
+ style
3083
+ }) {
3084
+ const [value, setValue] = react.useState(
3085
+ Array.isArray(request.value) ? request.value : []
3086
+ );
3087
+ const skipBlurCommitRef = react.useRef(false);
3088
+ react.useEffect(() => {
3089
+ setValue(Array.isArray(request.value) ? request.value : []);
3090
+ }, [request.value]);
3091
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style, children: /* @__PURE__ */ jsxRuntime.jsx(
3092
+ reactComponents.ReactCascader,
3093
+ {
3094
+ value,
3095
+ options: request.config.options,
3096
+ autoFocus: true,
3097
+ placeholder: request.config.placeholder,
3098
+ showSearch: request.config.showSearch,
3099
+ allowClear: request.config.allowClear,
3100
+ changeOnSelect: request.config.changeOnSelect,
3101
+ expandTrigger: request.config.expandTrigger,
3102
+ displayRender: request.config.displayRender,
3103
+ style: getEditorInputStyle(request),
3104
+ onChange: (next, _options, info) => {
3105
+ setValue(next);
3106
+ request.onChange(next);
3107
+ if (info.complete && info.trigger !== "clear") {
3108
+ skipBlurCommitRef.current = true;
3109
+ request.onCommit();
3110
+ }
3111
+ },
3112
+ onBlur: (event) => handleEditorBlur(event, skipBlurCommitRef, request),
3113
+ onKeyDown: (event) => {
3114
+ if (event.key === "Tab")
3115
+ handleEditorNavigationKey(event, request, skipBlurCommitRef);
3116
+ if (event.key === "Escape") {
3117
+ skipBlurCommitRef.current = true;
3118
+ handleEditorEscapeKey(event, request);
3119
+ }
3120
+ }
3121
+ }
3122
+ ) });
3123
+ }
2626
3124
  function SelectEditor(props) {
2627
3125
  const { request, style } = props;
2628
3126
  const options = request.config.options ?? [];
@@ -3449,7 +3947,7 @@ function resolveSortableTransform(index, sourceIndex, targetIndex, height) {
3449
3947
  }
3450
3948
 
3451
3949
  // src/features/toolbar/style.css
3452
- 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');
3950
+ 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');
3453
3951
  function ReactTableToolbar(props) {
3454
3952
  const { toolbar, title } = props;
3455
3953
  const [densityOpen, setDensityOpen] = react.useState(false);
@@ -3566,6 +4064,18 @@ function ReactTableToolbar(props) {
3566
4064
  children: [
3567
4065
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-list-table-column-setting-head", children: [
3568
4066
  /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "\u5217\u8BBE\u7F6E" }),
4067
+ /* @__PURE__ */ jsxRuntime.jsx(
4068
+ reactComponents.ReactCheckbox,
4069
+ {
4070
+ checked: props.cacheColumnSettings === true,
4071
+ disabled: !props.columnSettingsCacheAvailable,
4072
+ 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",
4073
+ onChange: (event) => props.onColumnSettingsCacheChange?.(
4074
+ event.target.checked
4075
+ ),
4076
+ children: "\u7F13\u5B58\u5230\u672C\u5730"
4077
+ }
4078
+ ),
3569
4079
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { marginLeft: "auto" }, children: config.columnSettingHeaderExtra }),
3570
4080
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: props.onResetColumns, children: "\u91CD\u7F6E" })
3571
4081
  ] }),
@@ -3901,6 +4411,8 @@ function ReactListTableInner(props, ref) {
3901
4411
  stretchColumns,
3902
4412
  resizable,
3903
4413
  storageKey,
4414
+ cacheColumnSettings,
4415
+ cachePreferences,
3904
4416
  draggable,
3905
4417
  tableId,
3906
4418
  rowDrag,
@@ -3922,6 +4434,10 @@ function ReactListTableInner(props, ref) {
3922
4434
  );
3923
4435
  const [columnConfig, setColumnConfig] = react.useState(() => /* @__PURE__ */ new Map());
3924
4436
  const [columnConfigRevision, setColumnConfigRevision] = react.useState(0);
4437
+ const [columnSettingsCacheOverride, setColumnSettingsCacheOverride] = react.useState(null);
4438
+ const resolvedColumnSettingsCache = (columnSettingsCacheOverride?.storageKey === storageKey ? columnSettingsCacheOverride?.value : void 0) ?? cacheColumnSettings ?? true;
4439
+ const [cachePreferencesOverride, setCachePreferencesOverride] = react.useState();
4440
+ const resolvedCachePreferences = (cachePreferencesOverride?.storageKey === storageKey ? cachePreferencesOverride?.value : void 0) ?? cachePreferences;
3925
4441
  const [columnConfigResetRevision, setColumnConfigResetRevision] = react.useState(0);
3926
4442
  const resolvedColumns = react.useMemo(
3927
4443
  () => applyColumnManagerState(
@@ -3975,6 +4491,8 @@ function ReactListTableInner(props, ref) {
3975
4491
  stretchColumns,
3976
4492
  resizable,
3977
4493
  storageKey,
4494
+ cacheColumnSettings: resolvedColumnSettingsCache,
4495
+ cachePreferences: resolvedCachePreferences,
3978
4496
  draggable,
3979
4497
  tableId,
3980
4498
  rowDrag,
@@ -3992,7 +4510,13 @@ function ReactListTableInner(props, ref) {
3992
4510
  columnConfigResetRevision,
3993
4511
  ui: {
3994
4512
  ...nextOptions.ui,
3995
- onDensityChange: setDensityOverride
4513
+ onDensityChange: setDensityOverride,
4514
+ onColumnSettingsCacheChange: (value) => setColumnSettingsCacheOverride(
4515
+ (current) => current?.storageKey === storageKey && current?.value === value ? current : { storageKey, value }
4516
+ ),
4517
+ onCachePreferencesChange: (next) => setCachePreferencesOverride(
4518
+ (current) => current?.storageKey === storageKey && JSON.stringify(current?.value ?? {}) === JSON.stringify(next) ? current : { storageKey, value: next }
4519
+ )
3996
4520
  }
3997
4521
  };
3998
4522
  }, [
@@ -4028,6 +4552,8 @@ function ReactListTableInner(props, ref) {
4028
4552
  stretchColumns,
4029
4553
  resizable,
4030
4554
  storageKey,
4555
+ resolvedColumnSettingsCache,
4556
+ resolvedCachePreferences,
4031
4557
  draggable,
4032
4558
  tableId,
4033
4559
  rowDrag,
@@ -4096,6 +4622,9 @@ function ReactListTableInner(props, ref) {
4096
4622
  {
4097
4623
  title,
4098
4624
  toolbar,
4625
+ cacheColumnSettings: Boolean(storageKey) && resolvedColumnSettingsCache,
4626
+ columnSettingsCacheAvailable: Boolean(storageKey),
4627
+ onColumnSettingsCacheChange: (enabled) => tableRef.current?.setColumnSettingsCache(enabled),
4099
4628
  columns: resolvedColumns,
4100
4629
  density: resolvedRowHeight ?? 32,
4101
4630
  hostRef: shellRef,