@canvas-components/react-pivot-table 0.2.17 → 0.2.19

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,6 +4,7 @@ var react = require('react');
4
4
  var pivotTable = require('@canvas-components/pivot-table');
5
5
  var reactComponents = require('@canvas-components/react-components');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
+ var reactListTable = require('@canvas-components/react-list-table');
7
8
  var reactDom = require('react-dom');
8
9
  var chart = require('@canvas-components/chart');
9
10
 
@@ -32,89 +33,276 @@ function styleInject(css, { insertAt } = {}) {
32
33
  }
33
34
 
34
35
  // src/features/context-menu/pivot-context-menu.css
35
- styleInject(".react-pivot-table-menu-action {\n transition: background-color 0.2s ease, color 0.2s ease;\n}\n.react-pivot-table-menu-action:hover {\n background: #f3f4f6 !important;\n}\n.react-pivot-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-pivot-table-menu-action:hover .react-pivot-table-menu-chevron,\n.react-pivot-table-menu-chevron-active {\n border-color: #6b7280;\n transform: rotate(135deg);\n}\n");
36
+ styleInject(".react-pivot-table-menu-action {\n font-size: inherit;\n transition: background-color 0.2s ease, color 0.2s ease;\n}\n.react-pivot-table-menu-action:hover {\n background: #f3f4f6 !important;\n}\n.react-pivot-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-pivot-table-menu-action:hover .react-pivot-table-menu-chevron,\n.react-pivot-table-menu-chevron-active {\n border-color: #6b7280;\n transform: rotate(135deg);\n}\n");
36
37
  var CONTEXT_MENU_SURFACE_STYLE = {
37
38
  padding: 8,
38
39
  background: "#ffffff",
39
- boxShadow: "0 8px 24px rgba(15, 23, 42, 0.16)"
40
+ boxShadow: "0 8px 24px rgba(15, 23, 42, 0.16)",
41
+ fontSize: 14
40
42
  };
43
+ function ScrollbarModeMenu(props) {
44
+ const { request, onOpenSettings } = props;
45
+ return /* @__PURE__ */ jsxRuntime.jsx(
46
+ MenuSubmenu,
47
+ {
48
+ label: "\u6EDA\u52A8\u6761\u6A21\u5F0F",
49
+ items: [
50
+ ["scrollbar", "\u6EDA\u52A8\u6761"],
51
+ ["button", "\u6309\u94AE"]
52
+ ].map(([mode, label]) => ({
53
+ key: `scrollbar-mode-${mode}`,
54
+ label,
55
+ selected: (request.scrollbar?.mode ?? "scrollbar") === mode,
56
+ onClick: () => {
57
+ request.onChangeScrollbar?.({
58
+ ...request.scrollbar,
59
+ mode,
60
+ horizontal: request.scrollbar?.horizontal === false ? false : { ...request.scrollbar?.horizontal, mode },
61
+ vertical: request.scrollbar?.vertical === false ? false : { ...request.scrollbar?.vertical, mode }
62
+ });
63
+ request.onClose();
64
+ }
65
+ })),
66
+ footer: /* @__PURE__ */ jsxRuntime.jsx(reactComponents.ReactButton, { onClick: onOpenSettings, children: "\u914D\u7F6E\u6EDA\u52A8\u6761\u5C5E\u6027" })
67
+ }
68
+ );
69
+ }
70
+ function ScrollbarCacheMenu(props) {
71
+ const { request } = props;
72
+ return /* @__PURE__ */ jsxRuntime.jsx(
73
+ MenuSubmenu,
74
+ {
75
+ label: "\u7F13\u5B58\u5230\u672C\u5730",
76
+ items: [
77
+ {
78
+ key: "cache-scrollbar",
79
+ label: "\u6EDA\u52A8\u6761",
80
+ selected: request.scrollbarCacheAvailable === true && request.cacheScrollbar !== false,
81
+ disabled: request.scrollbarCacheAvailable !== true,
82
+ onClick: () => request.onChangeCacheScrollbar?.(request.cacheScrollbar === false)
83
+ }
84
+ ]
85
+ }
86
+ );
87
+ }
41
88
  function PivotContextMenu(props) {
42
89
  const { request } = props;
43
90
  const [menuOpen, setMenuOpen] = react.useState(true);
91
+ const [scrollbarSettingsRequest, setScrollbarSettingsRequest] = react.useState(null);
92
+ const [scrollbarPositionRequest, setScrollbarPositionRequest] = react.useState(null);
44
93
  react.useEffect(() => {
45
94
  if (request) setMenuOpen(true);
46
95
  }, [request]);
47
- if (!request) return null;
48
- return /* @__PURE__ */ jsxRuntime.jsx(
49
- reactComponents.ReactPopup,
96
+ const scrollbarDialog = scrollbarSettingsRequest ? /* @__PURE__ */ jsxRuntime.jsx(
97
+ reactComponents.ReactScrollbarSettingsDialog,
50
98
  {
51
- open: menuOpen,
52
- anchorRect: { x: request.x, y: request.y, width: 0, height: 0 },
53
- offset: 4,
54
- matchAnchorWidth: false,
55
- minWidth: 260,
56
- onOpenChange: (open) => {
57
- setMenuOpen(open);
58
- if (!open) request.onClose();
99
+ value: scrollbarSettingsRequest.scrollbar,
100
+ onConfirm: (value) => {
101
+ scrollbarSettingsRequest.onChangeScrollbar?.({
102
+ ...scrollbarSettingsRequest.scrollbar,
103
+ ...value
104
+ });
105
+ setScrollbarSettingsRequest(null);
59
106
  },
60
- surfaceStyle: { ...CONTEXT_MENU_SURFACE_STYLE, width: 260 },
61
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
62
- request.title ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "6px 10px", color: "#8f959e", fontSize: 12 }, children: request.title }) : null,
63
- /* @__PURE__ */ jsxRuntime.jsx(
64
- MenuSubmenu,
65
- {
66
- label: "\u590D\u5236",
67
- items: [
68
- {
69
- key: "copy-cell",
70
- label: request.target === "header" ? "\u590D\u5236\u8868\u5934" : "\u590D\u5236\u5355\u5143\u683C",
71
- onClick: request.onCopy
72
- },
73
- {
74
- key: "copy-selection",
75
- label: "\u590D\u5236\u9009\u533A",
76
- onClick: request.target === "header" ? void 0 : request.onCopySelection
107
+ onClose: () => setScrollbarSettingsRequest(null)
108
+ }
109
+ ) : null;
110
+ const scrollbarPositionDialog = scrollbarPositionRequest ? /* @__PURE__ */ jsxRuntime.jsx(
111
+ reactComponents.ReactScrollbarPositionDialog,
112
+ {
113
+ rowCount: scrollbarPositionRequest.scrollbarRowCount ?? 0,
114
+ columnCount: scrollbarPositionRequest.scrollbarColumnCount ?? 0,
115
+ onConfirm: (row, column) => scrollbarPositionRequest.onScrollToPosition?.(row, column),
116
+ onClose: () => setScrollbarPositionRequest(null)
117
+ }
118
+ ) : null;
119
+ if (!request)
120
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
121
+ scrollbarDialog,
122
+ scrollbarPositionDialog
123
+ ] });
124
+ const openScrollbarSettings = () => {
125
+ setScrollbarSettingsRequest(request);
126
+ request.onClose();
127
+ };
128
+ const exportAndClose = (action) => action ? () => {
129
+ setMenuOpen(false);
130
+ request.onClose();
131
+ action();
132
+ } : void 0;
133
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
134
+ scrollbarDialog,
135
+ scrollbarPositionDialog,
136
+ /* @__PURE__ */ jsxRuntime.jsx(
137
+ reactComponents.ReactPopup,
138
+ {
139
+ open: menuOpen,
140
+ anchorRect: { x: request.x, y: request.y, width: 0, height: 0 },
141
+ offset: 4,
142
+ matchAnchorWidth: false,
143
+ minWidth: 260,
144
+ onOpenChange: (open) => {
145
+ setMenuOpen(open);
146
+ if (!open) request.onClose();
147
+ },
148
+ surfaceStyle: { ...CONTEXT_MENU_SURFACE_STYLE, width: 260 },
149
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { children: request.target === "scrollbar" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
150
+ /* @__PURE__ */ jsxRuntime.jsx(
151
+ ScrollbarModeMenu,
152
+ {
153
+ request,
154
+ onOpenSettings: openScrollbarSettings
155
+ }
156
+ ),
157
+ /* @__PURE__ */ jsxRuntime.jsx(
158
+ MenuAction,
159
+ {
160
+ label: "\u6EDA\u52A8\u5230\u6307\u5B9A\u884C / \u5217",
161
+ onClick: () => {
162
+ setScrollbarPositionRequest(request);
163
+ request.onClose();
77
164
  }
78
- ],
79
- children: request.target !== "header" ? /* @__PURE__ */ jsxRuntime.jsx(CustomSelectionMenu, { request }) : null
80
- }
81
- ),
82
- request.target === "header" ? /* @__PURE__ */ jsxRuntime.jsx(HeaderMenu, { request }) : null,
83
- request.target === "summary" ? /* @__PURE__ */ jsxRuntime.jsx(
84
- SummaryMenu,
85
- {
86
- request
87
- },
88
- `${request.indicatorKey}-${request.x}-${request.y}`
89
- ) : null,
90
- /* @__PURE__ */ jsxRuntime.jsx(DataFormatMenu, { request }),
91
- /* @__PURE__ */ jsxRuntime.jsx(DensityMenu, { request }),
92
- /* @__PURE__ */ jsxRuntime.jsx(
93
- MenuSubmenu,
94
- {
95
- label: "\u5BFC\u51FA",
96
- items: [
97
- {
98
- key: "export-csv",
99
- label: "\u5BFC\u51FA CSV",
100
- onClick: request.onExportCsv
165
+ }
166
+ ),
167
+ /* @__PURE__ */ jsxRuntime.jsx(
168
+ MenuAction,
169
+ {
170
+ label: "\u91CD\u7F6E\u6EDA\u52A8\u6761",
171
+ onClick: () => {
172
+ request.onResetScrollbar?.();
173
+ request.onClose();
174
+ }
175
+ }
176
+ ),
177
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollbarCacheMenu, { request })
178
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
179
+ request.title ? /* @__PURE__ */ jsxRuntime.jsx(
180
+ "div",
181
+ {
182
+ style: {
183
+ padding: "6px 10px",
184
+ color: "#8f959e",
185
+ fontSize: 12
101
186
  },
102
- {
103
- key: "export-xlsx",
104
- label: "\u5BFC\u51FA XLSX",
105
- onClick: request.onExportXlsx
187
+ children: request.title
188
+ }
189
+ ) : null,
190
+ /* @__PURE__ */ jsxRuntime.jsx(
191
+ MenuSubmenu,
192
+ {
193
+ label: "\u590D\u5236",
194
+ items: [
195
+ {
196
+ key: "copy-cell",
197
+ label: request.target === "header" ? "\u590D\u5236\u8868\u5934" : "\u590D\u5236\u5355\u5143\u683C",
198
+ onClick: request.onCopy
199
+ },
200
+ {
201
+ key: "copy-selection",
202
+ label: "\u590D\u5236\u9009\u533A",
203
+ onClick: request.target === "header" ? void 0 : request.onCopySelection
204
+ },
205
+ ...["all", "selection", "viewport"].map(
206
+ (scope) => ({
207
+ key: `copy-image-${scope}`,
208
+ label: `\u590D\u5236\u4E3A\u56FE\u7247 \xB7 ${{ all: "\u6240\u6709\u533A\u57DF", selection: "\u9009\u4E2D\u533A\u57DF", viewport: "\u89C6\u53E3\u533A\u57DF" }[scope]}`,
209
+ onClick: scope === "selection" && !request.hasImageSelection ? void 0 : request.onCopyImage ? () => request.onCopyImage?.(scope) : void 0
210
+ })
211
+ )
212
+ ],
213
+ children: request.target !== "header" ? /* @__PURE__ */ jsxRuntime.jsx(CustomSelectionMenu, { request }) : null
214
+ }
215
+ ),
216
+ request.target === "header" ? /* @__PURE__ */ jsxRuntime.jsx(HeaderMenu, { request }) : null,
217
+ request.target === "summary" ? /* @__PURE__ */ jsxRuntime.jsx(
218
+ SummaryMenu,
219
+ {
220
+ request
221
+ },
222
+ `${request.indicatorKey}-${request.x}-${request.y}`
223
+ ) : null,
224
+ /* @__PURE__ */ jsxRuntime.jsx(DataFormatMenu, { request }),
225
+ /* @__PURE__ */ jsxRuntime.jsx(DensityMenu, { request }),
226
+ /* @__PURE__ */ jsxRuntime.jsx(
227
+ ScrollbarModeMenu,
228
+ {
229
+ request,
230
+ onOpenSettings: openScrollbarSettings
231
+ }
232
+ ),
233
+ /* @__PURE__ */ jsxRuntime.jsx(
234
+ MenuAction,
235
+ {
236
+ label: "\u6EDA\u52A8\u5230\u6307\u5B9A\u884C / \u5217",
237
+ onClick: () => {
238
+ setScrollbarPositionRequest(request);
239
+ request.onClose();
106
240
  }
107
- ]
108
- }
109
- ),
110
- request.recordCount != null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "8px 10px", color: "#8f959e", fontSize: 12 }, children: [
111
- "\u4E0B\u94BB\u8BB0\u5F55 ",
112
- request.recordCount,
113
- " \u6761"
114
- ] }) : null
115
- ] })
116
- }
117
- );
241
+ }
242
+ ),
243
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollbarCacheMenu, { request }),
244
+ /* @__PURE__ */ jsxRuntime.jsx(
245
+ MenuSubmenu,
246
+ {
247
+ label: "\u91CD\u7F6E",
248
+ items: [
249
+ {
250
+ key: "reset-scrollbar",
251
+ label: "\u91CD\u7F6E\u6EDA\u52A8\u6761",
252
+ onClick: () => {
253
+ request.onResetScrollbar?.();
254
+ request.onClose();
255
+ }
256
+ }
257
+ ]
258
+ }
259
+ ),
260
+ /* @__PURE__ */ jsxRuntime.jsx(
261
+ MenuSubmenu,
262
+ {
263
+ label: "\u5BFC\u51FA",
264
+ items: [
265
+ {
266
+ key: "export-csv",
267
+ label: "\u5BFC\u51FA CSV",
268
+ onClick: exportAndClose(request.onExportCsv)
269
+ },
270
+ {
271
+ key: "export-xlsx",
272
+ label: "\u5BFC\u51FA XLSX",
273
+ onClick: exportAndClose(request.onExportXlsx)
274
+ },
275
+ ...["all", "selection", "viewport"].map(
276
+ (scope) => ({
277
+ key: `export-image-${scope}`,
278
+ label: `\u5BFC\u51FA\u56FE\u7247 \xB7 ${{ all: "\u6240\u6709\u533A\u57DF", selection: "\u9009\u4E2D\u533A\u57DF", viewport: "\u89C6\u53E3\u533A\u57DF" }[scope]}`,
279
+ onClick: exportAndClose(
280
+ scope === "selection" && !request.hasImageSelection ? void 0 : request.onExportImage ? () => request.onExportImage?.(scope) : void 0
281
+ )
282
+ })
283
+ )
284
+ ]
285
+ }
286
+ ),
287
+ request.recordCount != null ? /* @__PURE__ */ jsxRuntime.jsxs(
288
+ "div",
289
+ {
290
+ style: {
291
+ padding: "8px 10px",
292
+ color: "#8f959e",
293
+ fontSize: 12
294
+ },
295
+ children: [
296
+ "\u4E0B\u94BB\u8BB0\u5F55 ",
297
+ request.recordCount,
298
+ " \u6761"
299
+ ]
300
+ }
301
+ ) : null
302
+ ] }) })
303
+ }
304
+ )
305
+ ] });
118
306
  }
119
307
  function HeaderMenu(props) {
120
308
  const { request } = props;
@@ -146,7 +334,29 @@ function HeaderMenu(props) {
146
334
  ]
147
335
  }
148
336
  ) : null,
149
- request.onOpenFilter ? /* @__PURE__ */ jsxRuntime.jsx(MenuAction, { label: "\u6253\u5F00\u7B5B\u9009\u9762\u677F", onClick: request.onOpenFilter }) : null,
337
+ request.onAutoFitCurrentColumn || request.onAutoFitAllColumns ? /* @__PURE__ */ jsxRuntime.jsx(
338
+ MenuSubmenu,
339
+ {
340
+ label: "\u81EA\u52A8\u8C03\u6574\u5217\u5BBD",
341
+ items: [
342
+ {
343
+ key: "auto-fit-current",
344
+ label: "\u5F53\u524D\u5217",
345
+ onClick: request.onAutoFitCurrentColumn
346
+ },
347
+ {
348
+ key: "auto-fit-all",
349
+ label: "\u5168\u90E8\u5217",
350
+ onClick: request.onAutoFitAllColumns
351
+ },
352
+ {
353
+ key: "auto-fit-fill-container",
354
+ label: "\u81EA\u52A8\u94FA\u6EE1\u5BB9\u5668",
355
+ onClick: request.onAutoFitFillContainer
356
+ }
357
+ ]
358
+ }
359
+ ) : null,
150
360
  hasExpand ? /* @__PURE__ */ jsxRuntime.jsx(
151
361
  MenuSubmenu,
152
362
  {
@@ -174,14 +384,35 @@ function HeaderMenu(props) {
174
384
  }
175
385
  function SummaryMenu(props) {
176
386
  const { request } = props;
387
+ const [title, setTitle] = react.useState(request.summaryTitle ?? "");
177
388
  const [precision, setPrecision] = react.useState(
178
389
  request.summaryPrecision ?? null
179
390
  );
391
+ const [roundingMode, setRoundingMode] = react.useState(
392
+ request.summaryRoundingMode ?? "round"
393
+ );
394
+ const [preserveTrailingZeros, setPreserveTrailingZeros] = react.useState(
395
+ request.summaryPreserveTrailingZeros ?? false
396
+ );
397
+ react.useEffect(() => {
398
+ setTitle(request.summaryTitle ?? "");
399
+ setPrecision(request.summaryPrecision ?? null);
400
+ setRoundingMode(request.summaryRoundingMode ?? "round");
401
+ setPreserveTrailingZeros(request.summaryPreserveTrailingZeros ?? false);
402
+ }, [
403
+ request.indicatorKey,
404
+ request.summaryTitle,
405
+ request.summaryPrecision,
406
+ request.summaryRoundingMode,
407
+ request.summaryPreserveTrailingZeros
408
+ ]);
180
409
  const aggregators = [
181
410
  ["sum", "\u6C42\u548C"],
182
411
  ["avg", "\u5E73\u5747\u503C"],
183
412
  ["count", "\u8BA1\u6570"],
184
413
  ["distinctCount", "\u53BB\u91CD\u8BA1\u6570"],
414
+ ["rowCount", "\u884C\u6570"],
415
+ ["mergedRowCount", "\u5408\u5E76\u884C\u6570"],
185
416
  ["min", "\u6700\u5C0F\u503C"],
186
417
  ["max", "\u6700\u5927\u503C"]
187
418
  ];
@@ -190,15 +421,62 @@ function SummaryMenu(props) {
190
421
  MenuSubmenu,
191
422
  {
192
423
  label: "\u6C47\u603B\u65B9\u5F0F",
193
- items: aggregators.map(([value, label]) => ({
194
- key: `summary-${value}`,
195
- label,
196
- selected: request.summaryAggregator === value,
197
- onClick: () => {
198
- request.onChangeSummaryAggregator?.(value);
199
- request.onClose();
424
+ minWidth: 268,
425
+ header: request.onChangeSummaryTitle ? /* @__PURE__ */ jsxRuntime.jsxs(
426
+ "div",
427
+ {
428
+ style: {
429
+ display: "grid",
430
+ gridTemplateColumns: "56px 1fr",
431
+ gap: 8,
432
+ alignItems: "center",
433
+ padding: "2px 10px 10px",
434
+ marginBottom: 4,
435
+ borderBottom: "1px solid #e5e7eb"
436
+ },
437
+ children: [
438
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#374151", fontSize: 14 }, children: "\u6807\u9898\uFF1A" }),
439
+ /* @__PURE__ */ jsxRuntime.jsx(
440
+ reactComponents.ReactInput,
441
+ {
442
+ value: title,
443
+ placeholder: "\u8BF7\u8F93\u5165\u6807\u9898",
444
+ onChange: (event) => {
445
+ setTitle(event.target.value);
446
+ request.onChangeSummaryTitle?.(event.target.value);
447
+ }
448
+ }
449
+ )
450
+ ]
200
451
  }
201
- })),
452
+ ) : void 0,
453
+ items: [
454
+ ...aggregators.map(([value, label]) => ({
455
+ key: `summary-${value}`,
456
+ label,
457
+ selected: !request.summaryDisabled && request.summaryAggregator === value,
458
+ onClick: () => {
459
+ request.onChangeSummaryAggregator?.(value);
460
+ request.onClose();
461
+ }
462
+ })),
463
+ {
464
+ key: "summary-prefix",
465
+ label: "\u5C55\u793A\u6C47\u603B\u65B9\u5F0F\u524D\u7F00",
466
+ separatorBefore: true,
467
+ selected: request.summaryShowTypePrefix !== false,
468
+ disabled: request.summaryDisabled,
469
+ onClick: () => request.onChangeSummaryShowTypePrefix?.(
470
+ request.summaryShowTypePrefix === false
471
+ )
472
+ },
473
+ {
474
+ key: "summary-clear",
475
+ label: "\u53D6\u6D88\u6C47\u603B",
476
+ separatorBefore: true,
477
+ onClick: request.onClearSummary
478
+ }
479
+ ],
202
480
  footer: request.onChangeSummaryPrecision ? /* @__PURE__ */ jsxRuntime.jsxs(
203
481
  "div",
204
482
  {
@@ -212,7 +490,7 @@ function SummaryMenu(props) {
212
490
  borderTop: "1px solid #e5e7eb"
213
491
  },
214
492
  children: [
215
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#374151", fontSize: 13 }, children: "\u5C0F\u6570\u4F4D\u6570" }),
493
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#374151", fontSize: 14 }, children: "\u5C0F\u6570\u4F4D\u6570" }),
216
494
  /* @__PURE__ */ jsxRuntime.jsx(
217
495
  reactComponents.ReactInputNumber,
218
496
  {
@@ -221,10 +499,42 @@ function SummaryMenu(props) {
221
499
  max: 20,
222
500
  step: 1,
223
501
  precision: 0,
224
- placeholder: "\u81EA\u52A8",
502
+ placeholder: "\u9ED8\u8BA4",
503
+ onChange: setPrecision,
504
+ onCommit: (value) => request.onChangeSummaryPrecision?.(value ?? void 0)
505
+ }
506
+ ),
507
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#374151", fontSize: 14 }, children: "\u53D6\u6574\u65B9\u5F0F" }),
508
+ /* @__PURE__ */ jsxRuntime.jsx(
509
+ reactComponents.ReactSelect,
510
+ {
511
+ value: roundingMode,
512
+ options: [
513
+ { label: "\u56DB\u820D\u4E94\u5165", value: "round" },
514
+ { label: "\u5411\u4E0B\u53D6\u6574", value: "floor" },
515
+ { label: "\u5411\u4E0A\u53D6\u6574", value: "ceil" },
516
+ { label: "\u820D\u53BB\u4E0D\u5904\u7406", value: "truncate" }
517
+ ],
518
+ onChange: (value) => {
519
+ const mode = value;
520
+ setRoundingMode(mode);
521
+ request.onChangeSummaryRoundingMode?.(mode);
522
+ }
523
+ }
524
+ ),
525
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#374151", fontSize: 14 }, children: "\u4FDD\u7559\u65E0\u6548\u96F6" }),
526
+ /* @__PURE__ */ jsxRuntime.jsx(
527
+ reactComponents.ReactSelect,
528
+ {
529
+ value: preserveTrailingZeros ? "true" : "false",
530
+ options: [
531
+ { label: "\u4E0D\u4FDD\u7559", value: "false" },
532
+ { label: "\u4FDD\u7559", value: "true" }
533
+ ],
225
534
  onChange: (value) => {
226
- setPrecision(value);
227
- request.onChangeSummaryPrecision?.(value ?? void 0);
535
+ const enabled = value === "true";
536
+ setPreserveTrailingZeros(enabled);
537
+ request.onChangeSummaryPreserveTrailingZeros?.(enabled);
228
538
  }
229
539
  }
230
540
  )
@@ -421,7 +731,7 @@ function MenuAction(props) {
421
731
  );
422
732
  }
423
733
  function MenuSubmenu(props) {
424
- const { label, items, footer, children, minWidth = 220 } = props;
734
+ const { label, items, footer, header, children, minWidth = 220 } = props;
425
735
  const menuItems = items.filter((item) => item.onClick || item.disabled);
426
736
  const reserveSelectionIcon = menuItems.some(
427
737
  (item) => typeof item.selected === "boolean"
@@ -429,7 +739,7 @@ function MenuSubmenu(props) {
429
739
  const triggerRef = react.useRef(null);
430
740
  const [open, setOpen] = react.useState(false);
431
741
  const closeTimerRef = react.useRef(null);
432
- if (!menuItems.length && !footer && !children) return null;
742
+ if (!menuItems.length && !footer && !header && !children) return null;
433
743
  const clearCloseTimer = () => {
434
744
  if (closeTimerRef.current != null) {
435
745
  window.clearTimeout(closeTimerRef.current);
@@ -496,6 +806,7 @@ function MenuSubmenu(props) {
496
806
  onOpenChange: setOpen,
497
807
  surfaceStyle: { ...CONTEXT_MENU_SURFACE_STYLE, width: minWidth },
498
808
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { onMouseEnter: clearCloseTimer, onMouseLeave: scheduleClose, children: [
809
+ header,
499
810
  menuItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
500
811
  item.separatorBefore ? /* @__PURE__ */ jsxRuntime.jsx(MenuDivider, {}) : null,
501
812
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -693,54 +1004,15 @@ function DataFormatMenu(props) {
693
1004
  );
694
1005
  }
695
1006
  function PivotExportNameDialog({
696
- request
1007
+ request,
1008
+ onClose
697
1009
  }) {
698
- const extension = request.fileName.match(/\.(xlsx|csv)$/i)?.[0] ?? "";
699
- const [fileName, setFileName] = react.useState(
700
- extension ? request.fileName.slice(0, -extension.length) : request.fileName
701
- );
702
- const [invalid, setInvalid] = react.useState(false);
703
- const confirm = () => {
704
- if (!fileName.trim()) {
705
- setInvalid(true);
706
- return;
707
- }
708
- request.onConfirm(`${fileName.trim()}${extension}`);
709
- };
710
- return /* @__PURE__ */ jsxRuntime.jsxs(
711
- reactComponents.ReactModal,
1010
+ return /* @__PURE__ */ jsxRuntime.jsx(
1011
+ reactListTable.ReactListTableExportNameDialog,
712
1012
  {
713
- open: true,
714
- title: "\u5BFC\u51FA\u6587\u4EF6",
715
- width: 420,
716
- fullscreenable: false,
717
- onCancel: request.onCancel,
718
- onOk: confirm,
719
- okText: "\u5BFC\u51FA",
720
- cancelText: "\u53D6\u6D88",
721
- children: [
722
- /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
723
- "\u6587\u4EF6\u540D\u79F0",
724
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
725
- /* @__PURE__ */ jsxRuntime.jsx(
726
- reactComponents.ReactInput,
727
- {
728
- autoFocus: true,
729
- value: fileName,
730
- placeholder: "\u8BF7\u8F93\u5165\u6587\u4EF6\u540D\u79F0",
731
- onFocus: (event) => event.target.select(),
732
- onChange: (event) => {
733
- setFileName(event.target.value);
734
- setInvalid(false);
735
- },
736
- onPressEnter: confirm
737
- }
738
- ),
739
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-label": "\u6587\u4EF6\u540E\u7F00", style: { flexShrink: 0 }, children: extension })
740
- ] })
741
- ] }),
742
- invalid ? /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", style: { color: "#ff4d4f", marginTop: 8 }, children: "\u8BF7\u8F93\u5165\u6587\u4EF6\u540D\u79F0" }) : null
743
- ]
1013
+ request,
1014
+ onClose,
1015
+ renderTable: (props) => /* @__PURE__ */ jsxRuntime.jsx(reactListTable.ReactListTable, { ...props })
744
1016
  }
745
1017
  );
746
1018
  }
@@ -1276,7 +1548,9 @@ var aggregatorOptions = [
1276
1548
  { label: "\u5E73\u5747\u503C", value: "avg" },
1277
1549
  { label: "\u6700\u5927\u503C", value: "max" },
1278
1550
  { label: "\u6700\u5C0F\u503C", value: "min" },
1279
- { label: "\u53BB\u91CD\u8BA1\u6570", value: "distinctCount" }
1551
+ { label: "\u53BB\u91CD\u8BA1\u6570", value: "distinctCount" },
1552
+ { label: "\u884C\u6570", value: "rowCount" },
1553
+ { label: "\u5408\u5E76\u884C\u6570", value: "mergedRowCount" }
1280
1554
  ];
1281
1555
  var popupStyle = {
1282
1556
  padding: 0,
@@ -1731,7 +2005,14 @@ function resolveReactPivotTableDebugOptions(debug, inheritedEnabled) {
1731
2005
  };
1732
2006
  }
1733
2007
  function ReactPivotTableInner(props, ref) {
1734
- const { className, style, customization, title, ...tableOptions } = props;
2008
+ const {
2009
+ className,
2010
+ style,
2011
+ customization,
2012
+ pivotAreas,
2013
+ title,
2014
+ ...tableOptions
2015
+ } = props;
1735
2016
  const inheritedDebugEnabled = useReactPivotTableDebugEnabled();
1736
2017
  const resolvedDebug = react.useMemo(
1737
2018
  () => resolveReactPivotTableDebugOptions(
@@ -1744,6 +2025,7 @@ function ReactPivotTableInner(props, ref) {
1744
2025
  const tableRef = react.useRef(null);
1745
2026
  const customizationOptions = customization && typeof customization === "object" ? customization : void 0;
1746
2027
  const customizationEnabled = customization === true || customizationOptions != null;
2028
+ const pivotLayoutEnabled = customizationEnabled || pivotAreas != null;
1747
2029
  const [internalPanelOpen, setInternalPanelOpen] = react.useState(
1748
2030
  customizationOptions?.defaultOpen ?? false
1749
2031
  );
@@ -1758,13 +2040,35 @@ function ReactPivotTableInner(props, ref) {
1758
2040
  const chartIdRef = react.useRef(0);
1759
2041
  const layoutRevisionRef = react.useRef(0);
1760
2042
  const [internalLayout, setInternalLayout] = react.useState(
1761
- () => customizationOptions?.defaultLayout ?? pivotTable.createEmptyPivotFieldLayout()
2043
+ () => pivotAreas ?? customizationOptions?.defaultLayout ?? pivotTable.createPivotFieldLayout({
2044
+ filterDimensions: tableOptions.filterDimensions,
2045
+ columns: tableOptions.columns,
2046
+ rows: tableOptions.rows,
2047
+ indicators: tableOptions.indicators
2048
+ })
1762
2049
  );
1763
2050
  const [fieldOverrides, setFieldOverrides] = react.useState({});
1764
2051
  const [densityOverride, setDensityOverride] = react.useState(null);
2052
+ const [scrollbarOverride, setScrollbarOverride] = react.useState(null);
2053
+ const [sharedScrollbar, setSharedScrollbar] = react.useState(
2054
+ reactComponents.getSharedScrollbarSettings
2055
+ );
2056
+ react.useEffect(
2057
+ () => reactComponents.subscribeSharedScrollbarSettings(
2058
+ () => setSharedScrollbar(reactComponents.getSharedScrollbarSettings())
2059
+ ),
2060
+ []
2061
+ );
2062
+ const [scrollbarCacheEnabled, setScrollbarCacheEnabled] = react.useState(true);
2063
+ const scrollbarRevisionRef = react.useRef(0);
2064
+ const scrollbarStorageKey = customizationOptions?.storageKey;
2065
+ const effectiveScrollbar = sharedScrollbar.shared ? sharedScrollbar.value : scrollbarOverride ?? tableOptions.scrollbar;
1765
2066
  const [filterRequest, setFilterRequest] = react.useState(null);
1766
2067
  const [contextMenuRequest, setContextMenuRequest] = react.useState(null);
1767
2068
  const [exportNameRequest, setExportNameRequest] = react.useState(null);
2069
+ const [exportLoadingText, setExportLoadingText] = react.useState(
2070
+ null
2071
+ );
1768
2072
  const [tooltipRequest, setTooltipRequest] = react.useState(null);
1769
2073
  const panelOpen = customizationOptions?.open ?? internalPanelOpen;
1770
2074
  const fieldLayout = customizationOptions?.layout ?? internalLayout;
@@ -1805,6 +2109,7 @@ function ReactPivotTableInner(props, ref) {
1805
2109
  title: override.title ?? field.title,
1806
2110
  indicator: {
1807
2111
  ...field.indicator,
2112
+ ...override,
1808
2113
  title: override.title ?? field.indicator?.title,
1809
2114
  aggregator: override.aggregator ?? field.indicator?.aggregator,
1810
2115
  precision: "precision" in override ? override.precision : field.indicator?.precision,
@@ -1815,8 +2120,8 @@ function ReactPivotTableInner(props, ref) {
1815
2120
  [baseFields, fieldOverrides]
1816
2121
  );
1817
2122
  const customizedOptions = react.useMemo(
1818
- () => customizationEnabled ? pivotTable.resolvePivotFieldLayoutOptions(fields, fieldLayout) : null,
1819
- [customizationEnabled, fieldLayout, fields]
2123
+ () => pivotLayoutEnabled ? pivotTable.resolvePivotFieldLayoutOptions(fields, fieldLayout) : null,
2124
+ [pivotLayoutEnabled, fieldLayout, fields]
1820
2125
  );
1821
2126
  const overriddenIndicators = react.useMemo(
1822
2127
  () => tableOptions.indicators.map((indicator) => {
@@ -1824,6 +2129,7 @@ function ReactPivotTableInner(props, ref) {
1824
2129
  if (!override) return indicator;
1825
2130
  return {
1826
2131
  ...indicator,
2132
+ ...override,
1827
2133
  title: override.title ?? indicator.title,
1828
2134
  aggregator: override.aggregator ?? indicator.aggregator,
1829
2135
  precision: "precision" in override ? override.precision : indicator.precision,
@@ -1839,10 +2145,12 @@ function ReactPivotTableInner(props, ref) {
1839
2145
  const options = {
1840
2146
  ...tableOptions,
1841
2147
  ...customizedOptions,
2148
+ scrollbar: effectiveScrollbar,
1842
2149
  debug: resolvedDebug,
1843
2150
  rowHeight: densityOverride == null ? tableOptions.rowHeight : resolveDensityRowHeight(densityOverride),
1844
2151
  indicators: customizedOptions?.indicators ?? overriddenIndicators,
1845
2152
  ui: {
2153
+ onLoadingRequest: (request) => setExportLoadingText(request ? request.text ?? "\u6B63\u5728\u5BFC\u51FA\u2026" : null),
1846
2154
  onExportNameRequest: (request) => {
1847
2155
  setExportNameRequest(request);
1848
2156
  },
@@ -1853,6 +2161,39 @@ function ReactPivotTableInner(props, ref) {
1853
2161
  const fieldKey = request.indicatorKey;
1854
2162
  const enhancedRequest = {
1855
2163
  ...request,
2164
+ scrollbar: effectiveScrollbar,
2165
+ cacheScrollbar: scrollbarCacheEnabled,
2166
+ scrollbarCacheAvailable: Boolean(scrollbarStorageKey),
2167
+ onChangeCacheScrollbar: (enabled) => {
2168
+ setScrollbarCacheEnabled(enabled);
2169
+ scrollbarRevisionRef.current += 1;
2170
+ if (scrollbarStorageKey)
2171
+ void pivotTable.savePivotScrollbarConfig(scrollbarStorageKey, {
2172
+ enabled,
2173
+ scrollbar: enabled ? effectiveScrollbar : void 0
2174
+ });
2175
+ },
2176
+ onChangeScrollbar: (value) => {
2177
+ setScrollbarOverride(value);
2178
+ if (reactComponents.getSharedScrollbarSettings().shared)
2179
+ reactComponents.setSharedScrollbarSettings({ shared: true, value });
2180
+ scrollbarRevisionRef.current += 1;
2181
+ if (scrollbarStorageKey && scrollbarCacheEnabled)
2182
+ void pivotTable.savePivotScrollbarConfig(scrollbarStorageKey, {
2183
+ enabled: true,
2184
+ scrollbar: value
2185
+ });
2186
+ },
2187
+ onResetScrollbar: () => {
2188
+ setScrollbarOverride(null);
2189
+ if (reactComponents.getSharedScrollbarSettings().shared)
2190
+ reactComponents.setSharedScrollbarSettings({ shared: true, value: {} });
2191
+ scrollbarRevisionRef.current += 1;
2192
+ if (scrollbarStorageKey && scrollbarCacheEnabled)
2193
+ void pivotTable.savePivotScrollbarConfig(scrollbarStorageKey, {
2194
+ enabled: true
2195
+ });
2196
+ },
1856
2197
  onChangeDensity: (density) => {
1857
2198
  nativeChangeDensity?.(density);
1858
2199
  setDensityOverride(density);
@@ -1860,8 +2201,35 @@ function ReactPivotTableInner(props, ref) {
1860
2201
  onChangeDataFormat: fieldKey == null ? void 0 : (dataFormat) => updateField(fieldKey, { dataFormat })
1861
2202
  };
1862
2203
  if (request.target === "summary" && fieldKey != null) {
1863
- enhancedRequest.onChangeSummaryAggregator = (aggregator) => updateField(fieldKey, { aggregator });
1864
- enhancedRequest.onChangeSummaryPrecision = (precision) => updateField(fieldKey, { precision });
2204
+ const changeSummary = (patch, menuPatch) => {
2205
+ updateField(fieldKey, patch);
2206
+ setContextMenuRequest(
2207
+ (current) => current?.target === "summary" && current.indicatorKey === fieldKey ? { ...current, ...menuPatch } : current
2208
+ );
2209
+ };
2210
+ enhancedRequest.onChangeSummaryAggregator = (aggregator) => changeSummary(
2211
+ {
2212
+ aggregator,
2213
+ summaryAggregator: aggregator,
2214
+ summaryDisabled: false
2215
+ },
2216
+ { summaryAggregator: aggregator, summaryDisabled: false }
2217
+ );
2218
+ enhancedRequest.onChangeSummaryPrecision = (precision) => changeSummary({ precision }, { summaryPrecision: precision });
2219
+ enhancedRequest.onChangeSummaryTitle = (summaryTitle) => changeSummary({ summaryTitle }, { summaryTitle });
2220
+ enhancedRequest.onChangeSummaryShowTypePrefix = (summaryShowTypePrefix) => changeSummary(
2221
+ { summaryShowTypePrefix },
2222
+ { summaryShowTypePrefix }
2223
+ );
2224
+ enhancedRequest.onChangeSummaryRoundingMode = (summaryRoundingMode) => changeSummary({ summaryRoundingMode }, { summaryRoundingMode });
2225
+ enhancedRequest.onChangeSummaryPreserveTrailingZeros = (summaryPreserveTrailingZeros) => changeSummary(
2226
+ { summaryPreserveTrailingZeros },
2227
+ { summaryPreserveTrailingZeros }
2228
+ );
2229
+ enhancedRequest.onClearSummary = () => changeSummary(
2230
+ { summaryDisabled: true },
2231
+ { summaryDisabled: true }
2232
+ );
1865
2233
  }
1866
2234
  setContextMenuRequest(enhancedRequest);
1867
2235
  return;
@@ -1881,10 +2249,10 @@ function ReactPivotTableInner(props, ref) {
1881
2249
  };
1882
2250
  const sharedFilterRequest = react.useMemo(() => {
1883
2251
  if (!filterRequest) return null;
1884
- const { axis, dimensionKey } = filterRequest;
2252
+ const { axis, dimensionKey, columnPathKey } = filterRequest;
1885
2253
  return toSharedFilterRequest(filterRequest, (value) => {
1886
2254
  setFilterRequest(
1887
- (current) => current?.axis === axis && current.dimensionKey === dimensionKey ? { ...current, value } : current
2255
+ (current) => current?.axis === axis && current.dimensionKey === dimensionKey && current.columnPathKey === columnPathKey ? { ...current, value } : current
1888
2256
  );
1889
2257
  });
1890
2258
  }, [filterRequest]);
@@ -1935,6 +2303,7 @@ function ReactPivotTableInner(props, ref) {
1935
2303
  title: patch.title ?? field.title,
1936
2304
  indicator: {
1937
2305
  ...field.indicator,
2306
+ ...patch,
1938
2307
  title: patch.title ?? field.indicator?.title,
1939
2308
  aggregator: patch.aggregator ?? field.indicator?.aggregator,
1940
2309
  precision: "precision" in patch ? patch.precision : field.indicator?.precision,
@@ -1962,6 +2331,21 @@ function ReactPivotTableInner(props, ref) {
1962
2331
  table.updateOptions(options);
1963
2332
  refreshChartModel();
1964
2333
  }, [options]);
2334
+ react.useEffect(() => {
2335
+ if (!scrollbarStorageKey) return;
2336
+ let active = true;
2337
+ const revision = scrollbarRevisionRef.current;
2338
+ void pivotTable.loadPivotScrollbarConfig(scrollbarStorageKey).then((stored) => {
2339
+ if (!active || scrollbarRevisionRef.current !== revision || !stored)
2340
+ return;
2341
+ setScrollbarCacheEnabled(stored.enabled);
2342
+ if (stored.enabled && stored.scrollbar)
2343
+ setScrollbarOverride(stored.scrollbar);
2344
+ });
2345
+ return () => {
2346
+ active = false;
2347
+ };
2348
+ }, [scrollbarStorageKey]);
1965
2349
  react.useEffect(() => {
1966
2350
  const storageKey = customizationOptions?.storageKey;
1967
2351
  if (!storageKey || customizationOptions?.layout !== void 0) return;
@@ -1986,6 +2370,7 @@ function ReactPivotTableInner(props, ref) {
1986
2370
  react.useImperativeHandle(
1987
2371
  ref,
1988
2372
  () => ({
2373
+ autoFitColumnWidths: (target, fillContainer) => tableRef.current?.autoFitColumnWidths(target, fillContainer) ?? false,
1989
2374
  getPivotModel: () => tableRef.current?.getPivotModel() ?? null,
1990
2375
  getVisibleRowNodes: () => tableRef.current?.getVisibleRowNodes() ?? [],
1991
2376
  getVisibleColumnNodes: () => tableRef.current?.getVisibleColumnNodes() ?? [],
@@ -2007,6 +2392,15 @@ function ReactPivotTableInner(props, ref) {
2007
2392
  expandAll: (axis) => tableRef.current?.expandAll(axis) ?? 0,
2008
2393
  collapseAll: (axis) => tableRef.current?.collapseAll(axis) ?? 0,
2009
2394
  setDimensionFilter: (axis, dimensionKey, filter) => tableRef.current?.setDimensionFilter(axis, dimensionKey, filter) ?? null,
2395
+ setIndicatorFilter: (indicatorKey, columnPathKey, filter) => tableRef.current?.setIndicatorFilter(
2396
+ indicatorKey,
2397
+ columnPathKey,
2398
+ filter
2399
+ ) ?? null,
2400
+ getIndicatorFilterOptions: (indicatorKey, columnPathKey) => tableRef.current?.getIndicatorFilterOptions(
2401
+ indicatorKey,
2402
+ columnPathKey
2403
+ ) ?? [],
2010
2404
  getDimensionFilterOptions: (axis, dimensionKey) => tableRef.current?.getDimensionFilterOptions(axis, dimensionKey) ?? [],
2011
2405
  resetFilters: () => tableRef.current?.resetFilters() ?? null,
2012
2406
  setDimensionSort: (axis, dimensionKey, direction) => tableRef.current?.setDimensionSort(axis, dimensionKey, direction) ?? [],
@@ -2024,11 +2418,27 @@ function ReactPivotTableInner(props, ref) {
2024
2418
  {
2025
2419
  className: `react-pivot-table${className ? ` ${className}` : ""}`,
2026
2420
  style: {
2421
+ position: "relative",
2027
2422
  width: props.width ?? "100%",
2028
2423
  height: props.height ?? "100%",
2029
2424
  ...style
2030
2425
  },
2031
2426
  children: [
2427
+ exportLoadingText ? /* @__PURE__ */ jsxRuntime.jsx(
2428
+ "div",
2429
+ {
2430
+ role: "status",
2431
+ style: {
2432
+ position: "absolute",
2433
+ inset: 0,
2434
+ zIndex: 10,
2435
+ display: "grid",
2436
+ placeItems: "center",
2437
+ background: "rgba(255,255,255,0.75)"
2438
+ },
2439
+ children: exportLoadingText
2440
+ }
2441
+ ) : null,
2032
2442
  /* @__PURE__ */ jsxRuntime.jsx(
2033
2443
  PivotTitlebar,
2034
2444
  {
@@ -2121,7 +2531,13 @@ function ReactPivotTableInner(props, ref) {
2121
2531
  ),
2122
2532
  /* @__PURE__ */ jsxRuntime.jsx(reactComponents.ReactFilterPanel, { request: sharedFilterRequest }),
2123
2533
  /* @__PURE__ */ jsxRuntime.jsx(PivotContextMenu, { request: contextMenuRequest }),
2124
- exportNameRequest ? /* @__PURE__ */ jsxRuntime.jsx(PivotExportNameDialog, { request: exportNameRequest }) : null,
2534
+ exportNameRequest ? /* @__PURE__ */ jsxRuntime.jsx(
2535
+ PivotExportNameDialog,
2536
+ {
2537
+ request: exportNameRequest,
2538
+ onClose: () => setExportNameRequest(null)
2539
+ }
2540
+ ) : null,
2125
2541
  /* @__PURE__ */ jsxRuntime.jsx(PivotTooltip, { request: tooltipRequest })
2126
2542
  ] });
2127
2543
  }