@canvas-components/react-list-table 0.3.18 → 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,15 +402,17 @@ 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
  {
409
+ constrainHeight: true,
74
410
  open: menuOpen,
75
411
  anchorRect: { x: request.x, y: request.y, width: 0, height: 0 },
76
412
  minWidth: 260,
77
413
  onOpenChange: (open) => {
78
414
  setMenuOpen(open);
79
- if (!open && !moreFormatRequest && !customSelectionRequest) {
415
+ if (!open && !moreFormatRequest && !customSelectionRequest && !conditionalEditor) {
80
416
  request.onClose();
81
417
  }
82
418
  },
@@ -111,7 +447,28 @@ function ReactTableContextMenuView(props) {
111
447
  key: "copy-selection",
112
448
  label: "\u590D\u5236\u9009\u533A",
113
449
  onClick: request.onCopySelection
114
- }
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
+ ] : []
115
472
  ],
116
473
  children: /* @__PURE__ */ jsxRuntime.jsx(
117
474
  CustomSelectionMenu,
@@ -150,7 +507,26 @@ function ReactTableContextMenuView(props) {
150
507
  key: "export-json",
151
508
  label: "\u5BFC\u51FA JSON",
152
509
  onClick: request.onExportJson
153
- }
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
+ ] : []
154
530
  ]
155
531
  }
156
532
  ),
@@ -223,6 +599,41 @@ function ReactTableContextMenuView(props) {
223
599
  ),
224
600
  /* @__PURE__ */ jsxRuntime.jsx(SummaryMenu, { request }),
225
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
+ ),
226
637
  /* @__PURE__ */ jsxRuntime.jsx(
227
638
  DataFormatMenu,
228
639
  {
@@ -353,6 +764,13 @@ function ReactTableContextMenuView(props) {
353
764
  }
354
765
  ),
355
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
+ ),
356
774
  request.columns?.length && request.onToggleColumn ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
357
775
  /* @__PURE__ */ jsxRuntime.jsx(MenuDivider, {}),
358
776
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -399,6 +817,7 @@ function MenuAction(props) {
399
817
  type: "button",
400
818
  onClick: disabled ? void 0 : onClick,
401
819
  disabled,
820
+ title: props.title,
402
821
  className: "react-list-table-menu-action",
403
822
  style: {
404
823
  display: "flex",
@@ -427,7 +846,36 @@ function MenuAction(props) {
427
846
  children: selected ? "\u2713" : ""
428
847
  }
429
848
  ) : null,
430
- /* @__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
+ ),
431
879
  preview ? /* @__PURE__ */ jsxRuntime.jsx(
432
880
  "span",
433
881
  {
@@ -510,6 +958,7 @@ function MenuSubmenu(props) {
510
958
  /* @__PURE__ */ jsxRuntime.jsx(
511
959
  reactComponents.ReactPopup,
512
960
  {
961
+ constrainHeight: true,
513
962
  open,
514
963
  anchorRef: triggerRef,
515
964
  placement: "rightTop",
@@ -537,6 +986,7 @@ function MenuSubmenu(props) {
537
986
  MenuAction,
538
987
  {
539
988
  label: item.label,
989
+ title: item.title,
540
990
  preview: item.preview,
541
991
  onClick: item.onClick,
542
992
  selected: item.selected,
@@ -699,6 +1149,7 @@ function ZoomMenu(props) {
699
1149
  /* @__PURE__ */ jsxRuntime.jsx(
700
1150
  reactComponents.ReactPopup,
701
1151
  {
1152
+ constrainHeight: true,
702
1153
  open,
703
1154
  anchorRef: triggerRef,
704
1155
  placement: "rightTop",
@@ -863,6 +1314,7 @@ function ColumnVisibilityMenu(props) {
863
1314
  /* @__PURE__ */ jsxRuntime.jsx(
864
1315
  reactComponents.ReactPopup,
865
1316
  {
1317
+ constrainHeight: true,
866
1318
  open,
867
1319
  anchorRef: triggerRef,
868
1320
  placement: "rightTop",
@@ -969,6 +1421,7 @@ function ColumnVisibilityItem(props) {
969
1421
  hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
970
1422
  reactComponents.ReactPopup,
971
1423
  {
1424
+ constrainHeight: true,
972
1425
  open,
973
1426
  anchorRef: triggerRef,
974
1427
  placement: "rightTop",
@@ -1023,13 +1476,7 @@ function CustomSelectionMenu(props) {
1023
1476
  if (!request.onCopyCustomSelection || !request.customSelection) {
1024
1477
  return null;
1025
1478
  }
1026
- return /* @__PURE__ */ jsxRuntime.jsx(
1027
- MenuAction,
1028
- {
1029
- label: "\u590D\u5236\u81EA\u5B9A\u4E49\u9009\u533A",
1030
- onClick: props.onOpen
1031
- }
1032
- );
1479
+ return /* @__PURE__ */ jsxRuntime.jsx(MenuAction, { label: "\u590D\u5236\u81EA\u5B9A\u4E49\u9009\u533A", onClick: props.onOpen });
1033
1480
  }
1034
1481
  function CustomSelectionDialog(props) {
1035
1482
  const { request } = props;
@@ -1242,6 +1689,16 @@ function SummaryMenu(props) {
1242
1689
  selected: request.summaryType === item.value,
1243
1690
  onClick: () => request.onChangeSummaryType?.(item.value)
1244
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
+ },
1245
1702
  {
1246
1703
  key: "clear-summary",
1247
1704
  label: "\u53D6\u6D88\u6C47\u603B",
@@ -2330,6 +2787,8 @@ function ReactTableEditorOverlay(props) {
2330
2787
  }
2331
2788
  function renderEditorByType(request, style) {
2332
2789
  switch (request.config.valueType ?? "text") {
2790
+ case "cascader":
2791
+ return /* @__PURE__ */ jsxRuntime.jsx(CascaderEditor, { request, style });
2333
2792
  case "number":
2334
2793
  return /* @__PURE__ */ jsxRuntime.jsx(NumberEditor, { request, style });
2335
2794
  case "input-range":
@@ -2618,6 +3077,50 @@ function DateEditor(props) {
2618
3077
  }
2619
3078
  ) });
2620
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
+ }
2621
3124
  function SelectEditor(props) {
2622
3125
  const { request, style } = props;
2623
3126
  const options = request.config.options ?? [];
@@ -3444,7 +3947,7 @@ function resolveSortableTransform(index, sourceIndex, targetIndex, height) {
3444
3947
  }
3445
3948
 
3446
3949
  // src/features/toolbar/style.css
3447
- 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');
3448
3951
  function ReactTableToolbar(props) {
3449
3952
  const { toolbar, title } = props;
3450
3953
  const [densityOpen, setDensityOpen] = react.useState(false);
@@ -3561,6 +4064,18 @@ function ReactTableToolbar(props) {
3561
4064
  children: [
3562
4065
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-list-table-column-setting-head", children: [
3563
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
+ ),
3564
4079
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: { marginLeft: "auto" }, children: config.columnSettingHeaderExtra }),
3565
4080
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: props.onResetColumns, children: "\u91CD\u7F6E" })
3566
4081
  ] }),
@@ -3896,6 +4411,8 @@ function ReactListTableInner(props, ref) {
3896
4411
  stretchColumns,
3897
4412
  resizable,
3898
4413
  storageKey,
4414
+ cacheColumnSettings,
4415
+ cachePreferences,
3899
4416
  draggable,
3900
4417
  tableId,
3901
4418
  rowDrag,
@@ -3917,6 +4434,10 @@ function ReactListTableInner(props, ref) {
3917
4434
  );
3918
4435
  const [columnConfig, setColumnConfig] = react.useState(() => /* @__PURE__ */ new Map());
3919
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;
3920
4441
  const [columnConfigResetRevision, setColumnConfigResetRevision] = react.useState(0);
3921
4442
  const resolvedColumns = react.useMemo(
3922
4443
  () => applyColumnManagerState(
@@ -3970,6 +4491,8 @@ function ReactListTableInner(props, ref) {
3970
4491
  stretchColumns,
3971
4492
  resizable,
3972
4493
  storageKey,
4494
+ cacheColumnSettings: resolvedColumnSettingsCache,
4495
+ cachePreferences: resolvedCachePreferences,
3973
4496
  draggable,
3974
4497
  tableId,
3975
4498
  rowDrag,
@@ -3987,7 +4510,13 @@ function ReactListTableInner(props, ref) {
3987
4510
  columnConfigResetRevision,
3988
4511
  ui: {
3989
4512
  ...nextOptions.ui,
3990
- 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
+ )
3991
4520
  }
3992
4521
  };
3993
4522
  }, [
@@ -4023,6 +4552,8 @@ function ReactListTableInner(props, ref) {
4023
4552
  stretchColumns,
4024
4553
  resizable,
4025
4554
  storageKey,
4555
+ resolvedColumnSettingsCache,
4556
+ resolvedCachePreferences,
4026
4557
  draggable,
4027
4558
  tableId,
4028
4559
  rowDrag,
@@ -4091,6 +4622,9 @@ function ReactListTableInner(props, ref) {
4091
4622
  {
4092
4623
  title,
4093
4624
  toolbar,
4625
+ cacheColumnSettings: Boolean(storageKey) && resolvedColumnSettingsCache,
4626
+ columnSettingsCacheAvailable: Boolean(storageKey),
4627
+ onColumnSettingsCacheChange: (enabled) => tableRef.current?.setColumnSettingsCache(enabled),
4094
4628
  columns: resolvedColumns,
4095
4629
  density: resolvedRowHeight ?? 32,
4096
4630
  hostRef: shellRef,