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