@canvas-components/react-pivot-table 0.2.3 → 0.2.5

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
@@ -8,74 +8,237 @@ var reactDom = require('react-dom');
8
8
  var chart = require('@canvas-components/chart');
9
9
 
10
10
  // src/components/react-pivot-table.tsx
11
+
12
+ // #style-inject:#style-inject
13
+ function styleInject(css, { insertAt } = {}) {
14
+ if (!css || typeof document === "undefined") return;
15
+ const head = document.head || document.getElementsByTagName("head")[0];
16
+ const style = document.createElement("style");
17
+ style.type = "text/css";
18
+ if (insertAt === "top") {
19
+ if (head.firstChild) {
20
+ head.insertBefore(style, head.firstChild);
21
+ } else {
22
+ head.appendChild(style);
23
+ }
24
+ } else {
25
+ head.appendChild(style);
26
+ }
27
+ if (style.styleSheet) {
28
+ style.styleSheet.cssText = css;
29
+ } else {
30
+ style.appendChild(document.createTextNode(css));
31
+ }
32
+ }
33
+
34
+ // 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
+ var CONTEXT_MENU_SURFACE_STYLE = {
37
+ padding: 8,
38
+ background: "#ffffff",
39
+ boxShadow: "0 8px 24px rgba(15, 23, 42, 0.16)"
40
+ };
11
41
  function PivotContextMenu(props) {
12
42
  const { request } = props;
43
+ const [menuOpen, setMenuOpen] = react.useState(true);
44
+ react.useEffect(() => {
45
+ if (request) setMenuOpen(true);
46
+ }, [request]);
13
47
  if (!request) return null;
14
48
  return /* @__PURE__ */ jsxRuntime.jsx(
15
49
  reactComponents.ReactPopup,
16
50
  {
17
- open: true,
51
+ open: menuOpen,
18
52
  anchorRect: { x: request.x, y: request.y, width: 0, height: 0 },
19
53
  offset: 4,
20
54
  matchAnchorWidth: false,
21
- minWidth: 180,
55
+ minWidth: 260,
22
56
  onOpenChange: (open) => {
57
+ setMenuOpen(open);
23
58
  if (!open) request.onClose();
24
59
  },
25
- surfaceStyle: { padding: 6 },
26
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "grid", minWidth: 180 }, children: [
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,
27
63
  /* @__PURE__ */ jsxRuntime.jsx(
28
- MenuButton,
64
+ MenuSubmenu,
29
65
  {
30
- label: "\u590D\u5236\u5355\u5143\u683C",
31
- onClick: request.onCopy,
32
- close: request.onClose
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
77
+ }
78
+ ],
79
+ children: request.target !== "header" ? /* @__PURE__ */ jsxRuntime.jsx(CustomSelectionMenu, { request }) : null
33
80
  }
34
81
  ),
35
- request.onCopySelection ? /* @__PURE__ */ jsxRuntime.jsx(
36
- MenuButton,
37
- {
38
- label: "\u590D\u5236\u9009\u533A",
39
- onClick: request.onCopySelection,
40
- close: request.onClose
41
- }
42
- ) : null,
43
- /* @__PURE__ */ jsxRuntime.jsx(
44
- CustomSelectionMenu,
82
+ request.target === "header" ? /* @__PURE__ */ jsxRuntime.jsx(HeaderMenu, { request }) : null,
83
+ request.target === "summary" ? /* @__PURE__ */ jsxRuntime.jsx(
84
+ SummaryMenu,
45
85
  {
46
86
  request
47
87
  },
48
- `${request.x}-${request.y}`
49
- ),
50
- /* @__PURE__ */ jsxRuntime.jsx(
51
- MenuButton,
52
- {
53
- label: "\u5BFC\u51FA CSV",
54
- onClick: request.onExportCsv,
55
- close: request.onClose
56
- }
57
- ),
88
+ `${request.indicatorKey}-${request.x}-${request.y}`
89
+ ) : null,
90
+ /* @__PURE__ */ jsxRuntime.jsx(DataFormatMenu, { request }),
91
+ /* @__PURE__ */ jsxRuntime.jsx(DensityMenu, { request }),
58
92
  /* @__PURE__ */ jsxRuntime.jsx(
59
- MenuButton,
93
+ MenuSubmenu,
60
94
  {
61
- label: "\u5BFC\u51FA XLSX",
62
- onClick: request.onExportXlsx,
63
- close: request.onClose
95
+ label: "\u5BFC\u51FA",
96
+ items: [
97
+ {
98
+ key: "export-csv",
99
+ label: "\u5BFC\u51FA CSV",
100
+ onClick: request.onExportCsv
101
+ },
102
+ {
103
+ key: "export-xlsx",
104
+ label: "\u5BFC\u51FA XLSX",
105
+ onClick: request.onExportXlsx
106
+ }
107
+ ]
64
108
  }
65
109
  ),
66
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "8px 10px", color: "#8f959e", fontSize: 12 }, children: [
110
+ request.recordCount != null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "8px 10px", color: "#8f959e", fontSize: 12 }, children: [
67
111
  "\u4E0B\u94BB\u8BB0\u5F55 ",
68
112
  request.recordCount,
69
113
  " \u6761"
70
- ] })
114
+ ] }) : null
71
115
  ] })
72
116
  }
73
117
  );
74
118
  }
119
+ function HeaderMenu(props) {
120
+ const { request } = props;
121
+ const hasSort = request.onSortAsc || request.onSortDesc || request.onClearSort;
122
+ const hasExpand = request.onExpand || request.onCollapse || request.onExpandAll || request.onCollapseAll;
123
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
124
+ hasSort ? /* @__PURE__ */ jsxRuntime.jsx(
125
+ MenuSubmenu,
126
+ {
127
+ label: "\u6392\u5E8F",
128
+ items: [
129
+ {
130
+ key: "sort-asc",
131
+ label: "\u5347\u5E8F",
132
+ selected: request.sortDirection === "asc",
133
+ onClick: request.onSortAsc
134
+ },
135
+ {
136
+ key: "sort-desc",
137
+ label: "\u964D\u5E8F",
138
+ selected: request.sortDirection === "desc",
139
+ onClick: request.onSortDesc
140
+ },
141
+ {
142
+ key: "sort-clear",
143
+ label: "\u53D6\u6D88\u6392\u5E8F",
144
+ onClick: request.onClearSort
145
+ }
146
+ ]
147
+ }
148
+ ) : null,
149
+ request.onOpenFilter ? /* @__PURE__ */ jsxRuntime.jsx(MenuAction, { label: "\u6253\u5F00\u7B5B\u9009\u9762\u677F", onClick: request.onOpenFilter }) : null,
150
+ hasExpand ? /* @__PURE__ */ jsxRuntime.jsx(
151
+ MenuSubmenu,
152
+ {
153
+ label: "\u5C55\u5F00/\u6298\u53E0",
154
+ items: [
155
+ {
156
+ key: "toggle-current",
157
+ label: request.expanded ? "\u6298\u53E0\u5F53\u524D\u9879" : "\u5C55\u5F00\u5F53\u524D\u9879",
158
+ onClick: request.expanded ? request.onCollapse : request.onExpand
159
+ },
160
+ {
161
+ key: "expand-all",
162
+ label: "\u5168\u90E8\u5C55\u5F00",
163
+ onClick: request.onExpandAll
164
+ },
165
+ {
166
+ key: "collapse-all",
167
+ label: "\u5168\u90E8\u6298\u53E0",
168
+ onClick: request.onCollapseAll
169
+ }
170
+ ]
171
+ }
172
+ ) : null
173
+ ] });
174
+ }
175
+ function SummaryMenu(props) {
176
+ const { request } = props;
177
+ const [precision, setPrecision] = react.useState(
178
+ request.summaryPrecision ?? null
179
+ );
180
+ const aggregators = [
181
+ ["sum", "\u6C42\u548C"],
182
+ ["avg", "\u5E73\u5747\u503C"],
183
+ ["count", "\u8BA1\u6570"],
184
+ ["distinctCount", "\u53BB\u91CD\u8BA1\u6570"],
185
+ ["min", "\u6700\u5C0F\u503C"],
186
+ ["max", "\u6700\u5927\u503C"]
187
+ ];
188
+ if (!request.onChangeSummaryAggregator) return null;
189
+ return /* @__PURE__ */ jsxRuntime.jsx(
190
+ MenuSubmenu,
191
+ {
192
+ 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();
200
+ }
201
+ })),
202
+ footer: request.onChangeSummaryPrecision ? /* @__PURE__ */ jsxRuntime.jsxs(
203
+ "div",
204
+ {
205
+ style: {
206
+ display: "grid",
207
+ gridTemplateColumns: "72px 1fr",
208
+ gap: 8,
209
+ alignItems: "center",
210
+ marginTop: 4,
211
+ padding: "10px 10px 4px",
212
+ borderTop: "1px solid #e5e7eb"
213
+ },
214
+ children: [
215
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#374151", fontSize: 13 }, children: "\u5C0F\u6570\u4F4D\u6570" }),
216
+ /* @__PURE__ */ jsxRuntime.jsx(
217
+ reactComponents.ReactInputNumber,
218
+ {
219
+ value: precision,
220
+ min: 0,
221
+ max: 20,
222
+ step: 1,
223
+ precision: 0,
224
+ placeholder: "\u81EA\u52A8",
225
+ onChange: (value) => {
226
+ setPrecision(value);
227
+ request.onChangeSummaryPrecision?.(value ?? void 0);
228
+ }
229
+ }
230
+ )
231
+ ]
232
+ }
233
+ ) : null
234
+ }
235
+ );
236
+ }
237
+ function MenuDivider() {
238
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 1, margin: "6px 0", background: "#f0f0f0" } });
239
+ }
75
240
  function CustomSelectionMenu(props) {
76
241
  const { request } = props;
77
- const triggerRef = react.useRef(null);
78
- const [open, setOpen] = react.useState(false);
79
242
  const [range, setRange] = react.useState(
80
243
  request.customSelection ?? {
81
244
  startRow: 1,
@@ -93,104 +256,79 @@ function CustomSelectionMenu(props) {
93
256
  [key]: value == null ? 1 : Math.floor(value)
94
257
  }));
95
258
  };
96
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
259
+ return /* @__PURE__ */ jsxRuntime.jsx(MenuSubmenu, { label: "\u590D\u5236\u81EA\u5B9A\u4E49\u9009\u533A", items: [], minWidth: 250, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: 6 }, children: [
260
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#6b7280", fontSize: 12, marginBottom: 6 }, children: "\u8F93\u5165\u8D77\u59CB\u4F4D\u7F6E\u548C\u7ED3\u675F\u4F4D\u7F6E" }),
261
+ /* @__PURE__ */ jsxRuntime.jsxs(
262
+ "div",
263
+ {
264
+ style: {
265
+ display: "grid",
266
+ gridTemplateColumns: "38px 1fr 1fr",
267
+ gap: 6,
268
+ marginBottom: 4,
269
+ color: "#6b7280",
270
+ fontSize: 12,
271
+ textAlign: "center"
272
+ },
273
+ children: [
274
+ /* @__PURE__ */ jsxRuntime.jsx("span", {}),
275
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u884C" }),
276
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u5217" })
277
+ ]
278
+ }
279
+ ),
97
280
  /* @__PURE__ */ jsxRuntime.jsx(
98
- "button",
281
+ CustomSelectionRow,
99
282
  {
100
- ref: triggerRef,
101
- type: "button",
102
- onClick: () => setOpen((value) => !value),
103
- style: menuButtonStyle,
104
- children: "\u590D\u5236\u81EA\u5B9A\u4E49\u9009\u533A"
283
+ label: "\u8D77\u59CB",
284
+ row: range.startRow,
285
+ column: range.startColumn,
286
+ maxRow: range.rowCount,
287
+ maxColumn: range.columnCount,
288
+ onRowChange: (value) => update("startRow", value),
289
+ onColumnChange: (value) => update("startColumn", value)
105
290
  }
106
291
  ),
107
292
  /* @__PURE__ */ jsxRuntime.jsx(
108
- reactComponents.ReactPopup,
293
+ CustomSelectionRow,
109
294
  {
110
- open,
111
- anchorRef: triggerRef,
112
- placement: "rightTop",
113
- offset: 2,
114
- minWidth: 250,
115
- onOpenChange: setOpen,
116
- surfaceStyle: { padding: 6, width: 250 },
117
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: 6 }, children: [
118
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#6b7280", fontSize: 12, marginBottom: 6 }, children: "\u8F93\u5165\u8D77\u59CB\u4F4D\u7F6E\u548C\u7ED3\u675F\u4F4D\u7F6E" }),
119
- /* @__PURE__ */ jsxRuntime.jsxs(
120
- "div",
121
- {
122
- style: {
123
- display: "grid",
124
- gridTemplateColumns: "38px 1fr 1fr",
125
- gap: 6,
126
- marginBottom: 4,
127
- color: "#6b7280",
128
- fontSize: 12,
129
- textAlign: "center"
130
- },
131
- children: [
132
- /* @__PURE__ */ jsxRuntime.jsx("span", {}),
133
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u884C" }),
134
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u5217" })
135
- ]
136
- }
137
- ),
138
- /* @__PURE__ */ jsxRuntime.jsx(
139
- CustomSelectionRow,
140
- {
141
- label: "\u8D77\u59CB",
142
- row: range.startRow,
143
- column: range.startColumn,
144
- maxRow: range.rowCount,
145
- maxColumn: range.columnCount,
146
- onRowChange: (value) => update("startRow", value),
147
- onColumnChange: (value) => update("startColumn", value)
148
- }
149
- ),
150
- /* @__PURE__ */ jsxRuntime.jsx(
151
- CustomSelectionRow,
152
- {
153
- label: "\u7ED3\u675F",
154
- row: range.endRow,
155
- column: range.endColumn,
156
- maxRow: range.rowCount,
157
- maxColumn: range.columnCount,
158
- onRowChange: (value) => update("endRow", value),
159
- onColumnChange: (value) => update("endColumn", value)
160
- }
161
- ),
162
- /* @__PURE__ */ jsxRuntime.jsx(
163
- "button",
164
- {
165
- type: "button",
166
- style: {
167
- display: "block",
168
- width: "100%",
169
- height: 32,
170
- marginTop: 6,
171
- border: "none",
172
- borderRadius: 4,
173
- background: "#1677ff",
174
- color: "#fff",
175
- cursor: "pointer"
176
- },
177
- onClick: () => {
178
- request.onCopyCustomSelection?.({
179
- startRow: range.startRow,
180
- startColumn: range.startColumn,
181
- endRow: range.endRow,
182
- endColumn: range.endColumn
183
- });
184
- setOpen(false);
185
- request.onClose();
186
- },
187
- children: "\u590D\u5236"
188
- }
189
- )
190
- ] })
295
+ label: "\u7ED3\u675F",
296
+ row: range.endRow,
297
+ column: range.endColumn,
298
+ maxRow: range.rowCount,
299
+ maxColumn: range.columnCount,
300
+ onRowChange: (value) => update("endRow", value),
301
+ onColumnChange: (value) => update("endColumn", value)
302
+ }
303
+ ),
304
+ /* @__PURE__ */ jsxRuntime.jsx(
305
+ "button",
306
+ {
307
+ type: "button",
308
+ style: {
309
+ display: "block",
310
+ width: "100%",
311
+ height: 32,
312
+ marginTop: 6,
313
+ border: "none",
314
+ borderRadius: 4,
315
+ background: "#1677ff",
316
+ color: "#fff",
317
+ cursor: "pointer"
318
+ },
319
+ onClick: () => {
320
+ request.onCopyCustomSelection?.({
321
+ startRow: range.startRow,
322
+ startColumn: range.startColumn,
323
+ endRow: range.endRow,
324
+ endColumn: range.endColumn
325
+ });
326
+ request.onClose();
327
+ },
328
+ children: "\u590D\u5236"
191
329
  }
192
330
  )
193
- ] });
331
+ ] }) });
194
332
  }
195
333
  function CustomSelectionRow(props) {
196
334
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -233,27 +371,324 @@ function CustomSelectionRow(props) {
233
371
  }
234
372
  );
235
373
  }
236
- var menuButtonStyle = {
237
- height: 34,
238
- padding: "0 10px",
239
- border: 0,
240
- borderRadius: 4,
241
- background: "transparent",
242
- color: "#1f2329",
243
- textAlign: "left",
244
- cursor: "pointer"
245
- };
246
- function MenuButton(props) {
247
- return /* @__PURE__ */ jsxRuntime.jsx(
374
+ function MenuAction(props) {
375
+ const { label, preview, onClick, selected, disabled, reserveSelectionIcon } = props;
376
+ if (!onClick && !disabled) return null;
377
+ return /* @__PURE__ */ jsxRuntime.jsxs(
248
378
  "button",
249
379
  {
250
380
  type: "button",
251
- onClick: () => {
252
- props.onClick();
253
- props.close();
381
+ onClick: disabled ? void 0 : onClick,
382
+ disabled,
383
+ className: "react-pivot-table-menu-action",
384
+ style: {
385
+ display: "flex",
386
+ alignItems: "center",
387
+ width: "100%",
388
+ height: 32,
389
+ padding: "0 10px",
390
+ border: "none",
391
+ borderRadius: 8,
392
+ background: "transparent",
393
+ color: disabled ? "#9ca3af" : "#374151",
394
+ textAlign: "left",
395
+ cursor: disabled ? "default" : "pointer"
254
396
  },
255
- style: menuButtonStyle,
256
- children: props.label
397
+ children: [
398
+ reserveSelectionIcon ? /* @__PURE__ */ jsxRuntime.jsx(
399
+ "span",
400
+ {
401
+ "aria-hidden": "true",
402
+ style: {
403
+ width: 16,
404
+ flex: "0 0 16px",
405
+ marginRight: 6,
406
+ textAlign: "center"
407
+ },
408
+ children: selected ? "\u2713" : ""
409
+ }
410
+ ) : null,
411
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1, whiteSpace: "nowrap" }, children: label }),
412
+ preview ? /* @__PURE__ */ jsxRuntime.jsx(
413
+ "span",
414
+ {
415
+ style: { marginLeft: 16, color: "#9ca3af", whiteSpace: "nowrap" },
416
+ children: preview
417
+ }
418
+ ) : null
419
+ ]
420
+ }
421
+ );
422
+ }
423
+ function MenuSubmenu(props) {
424
+ const { label, items, footer, children, minWidth = 220 } = props;
425
+ const menuItems = items.filter((item) => item.onClick || item.disabled);
426
+ const reserveSelectionIcon = menuItems.some(
427
+ (item) => typeof item.selected === "boolean"
428
+ );
429
+ const triggerRef = react.useRef(null);
430
+ const [open, setOpen] = react.useState(false);
431
+ const closeTimerRef = react.useRef(null);
432
+ if (!menuItems.length && !footer && !children) return null;
433
+ const clearCloseTimer = () => {
434
+ if (closeTimerRef.current != null) {
435
+ window.clearTimeout(closeTimerRef.current);
436
+ closeTimerRef.current = null;
437
+ }
438
+ };
439
+ const scheduleClose = () => {
440
+ clearCloseTimer();
441
+ closeTimerRef.current = window.setTimeout(() => {
442
+ setOpen(false);
443
+ closeTimerRef.current = null;
444
+ }, 120);
445
+ };
446
+ return /* @__PURE__ */ jsxRuntime.jsxs(
447
+ "div",
448
+ {
449
+ onMouseEnter: () => {
450
+ clearCloseTimer();
451
+ setOpen(true);
452
+ },
453
+ onMouseLeave: scheduleClose,
454
+ children: [
455
+ /* @__PURE__ */ jsxRuntime.jsxs(
456
+ "button",
457
+ {
458
+ ref: triggerRef,
459
+ type: "button",
460
+ onClick: () => setOpen((value) => !value),
461
+ className: "react-pivot-table-menu-action react-pivot-table-menu-action-submenu",
462
+ style: {
463
+ display: "flex",
464
+ alignItems: "center",
465
+ justifyContent: "space-between",
466
+ width: "100%",
467
+ height: 32,
468
+ padding: "0 10px",
469
+ border: "none",
470
+ borderRadius: 8,
471
+ background: "transparent",
472
+ color: "#374151",
473
+ textAlign: "left",
474
+ cursor: "pointer"
475
+ },
476
+ children: [
477
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
478
+ /* @__PURE__ */ jsxRuntime.jsx(
479
+ "span",
480
+ {
481
+ className: `react-pivot-table-menu-chevron${open ? " react-pivot-table-menu-chevron-active" : ""}`,
482
+ "aria-hidden": "true"
483
+ }
484
+ )
485
+ ]
486
+ }
487
+ ),
488
+ /* @__PURE__ */ jsxRuntime.jsx(
489
+ reactComponents.ReactPopup,
490
+ {
491
+ open,
492
+ anchorRef: triggerRef,
493
+ placement: "rightTop",
494
+ offset: 2,
495
+ minWidth,
496
+ onOpenChange: setOpen,
497
+ surfaceStyle: { ...CONTEXT_MENU_SURFACE_STYLE, width: minWidth },
498
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { onMouseEnter: clearCloseTimer, onMouseLeave: scheduleClose, children: [
499
+ menuItems.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
500
+ item.separatorBefore ? /* @__PURE__ */ jsxRuntime.jsx(MenuDivider, {}) : null,
501
+ /* @__PURE__ */ jsxRuntime.jsx(
502
+ MenuAction,
503
+ {
504
+ label: item.label,
505
+ preview: item.preview,
506
+ selected: item.selected,
507
+ disabled: item.disabled,
508
+ onClick: item.onClick,
509
+ reserveSelectionIcon
510
+ }
511
+ )
512
+ ] }, item.key)),
513
+ children,
514
+ footer
515
+ ] })
516
+ }
517
+ )
518
+ ]
519
+ }
520
+ );
521
+ }
522
+ function DensityMenu(props) {
523
+ const { request } = props;
524
+ if (!request.onChangeDensity) return null;
525
+ return /* @__PURE__ */ jsxRuntime.jsx(
526
+ MenuSubmenu,
527
+ {
528
+ label: "\u8868\u683C\u5BC6\u5EA6",
529
+ items: [
530
+ {
531
+ key: "density-comfortable",
532
+ label: "\u5BBD\u677E",
533
+ selected: request.density === "comfortable",
534
+ onClick: () => request.onChangeDensity?.("comfortable")
535
+ },
536
+ {
537
+ key: "density-middle",
538
+ label: "\u4E2D\u7B49",
539
+ selected: request.density === "middle",
540
+ onClick: () => request.onChangeDensity?.("middle")
541
+ },
542
+ {
543
+ key: "density-compact",
544
+ label: "\u7D27\u51D1",
545
+ selected: request.density === "compact",
546
+ onClick: () => request.onChangeDensity?.("compact")
547
+ },
548
+ {
549
+ key: "density-ultra-compact",
550
+ label: "\u8D85\u7D27\u51D1",
551
+ selected: request.density === "ultra-compact",
552
+ onClick: () => request.onChangeDensity?.("ultra-compact")
553
+ },
554
+ {
555
+ key: "density-auto",
556
+ label: "\u81EA\u9002\u5E94",
557
+ selected: request.density === "auto",
558
+ onClick: () => request.onChangeDensity?.("auto")
559
+ }
560
+ ]
561
+ }
562
+ );
563
+ }
564
+ function DataFormatMenu(props) {
565
+ const { request } = props;
566
+ if (!request.onChangeDataFormat) return null;
567
+ const formats = [
568
+ { key: "default", label: "\u5E38\u89C4", value: "default" },
569
+ { key: "text", label: "\u7EAF\u6587\u672C", value: "text" },
570
+ {
571
+ key: "number",
572
+ label: "\u6570\u5B57",
573
+ preview: "1024",
574
+ value: "number",
575
+ separatorBefore: true
576
+ },
577
+ {
578
+ key: "thousands",
579
+ label: "\u6570\u5B57\uFF08\u5343\u5206\u4F4D\uFF09",
580
+ preview: "1,024",
581
+ value: "thousands"
582
+ },
583
+ {
584
+ key: "decimal",
585
+ label: "\u6570\u5B57\uFF08\u5343\u5206\u4F4D\uFF0C\u5C0F\u6570\u70B9\uFF09",
586
+ preview: "1,024.56",
587
+ value: "decimal"
588
+ },
589
+ {
590
+ key: "percent",
591
+ label: "\u767E\u5206\u6BD4",
592
+ preview: "10%",
593
+ value: "percent",
594
+ separatorBefore: true
595
+ },
596
+ {
597
+ key: "percentDecimal",
598
+ label: "\u767E\u5206\u6BD4\uFF08\u5C0F\u6570\u70B9\uFF09",
599
+ preview: "10.24%",
600
+ value: "percentDecimal"
601
+ },
602
+ {
603
+ key: "scientific",
604
+ label: "\u79D1\u5B66\u8BB0\u6570",
605
+ preview: "1.02E+003",
606
+ value: "scientific"
607
+ },
608
+ {
609
+ key: "numberToChineseUpper",
610
+ label: "\u6570\u5B57\u8F6C\u5927\u5199",
611
+ preview: "\u7396\u62FE\u58F9\u4E07\u634C\u4EDF",
612
+ value: "numberToChineseUpper"
613
+ },
614
+ {
615
+ key: "chineseUpperToNumber",
616
+ label: "\u5927\u5199\u8F6C\u6570\u5B57",
617
+ preview: "918000",
618
+ value: "chineseUpperToNumber"
619
+ },
620
+ {
621
+ key: "cny",
622
+ label: "\u4EBA\u6C11\u5E01",
623
+ preview: "\xA51,024",
624
+ value: "cny",
625
+ separatorBefore: true
626
+ },
627
+ {
628
+ key: "cnyDecimal",
629
+ label: "\u4EBA\u6C11\u5E01\uFF08\u5C0F\u6570\u70B9\uFF09",
630
+ preview: "\xA51,024.56",
631
+ value: "cnyDecimal"
632
+ },
633
+ { key: "usd", label: "\u7F8E\u5143", preview: "$1,024", value: "usd" },
634
+ {
635
+ key: "usdDecimal",
636
+ label: "\u7F8E\u5143\uFF08\u5C0F\u6570\u70B9\uFF09",
637
+ preview: "$1,024.56",
638
+ value: "usdDecimal"
639
+ },
640
+ {
641
+ key: "dateSlash",
642
+ label: "\u65E5\u671F\uFF08\u659C\u6760\uFF09",
643
+ preview: "2017/08/01",
644
+ value: "dateSlash",
645
+ separatorBefore: true
646
+ },
647
+ {
648
+ key: "dateDash",
649
+ label: "\u65E5\u671F\uFF08\u77ED\u6A2A\u7EBF\uFF09",
650
+ preview: "2017-08-01",
651
+ value: "dateDash"
652
+ },
653
+ {
654
+ key: "dateFullCN",
655
+ label: "\u65E5\u671F\uFF08\u4E2D\u6587\uFF09",
656
+ preview: "2017\u5E748\u67081\u65E5",
657
+ value: "dateFullCN"
658
+ },
659
+ { key: "time", label: "\u65F6\u95F4", preview: "23:24:25", value: "time" },
660
+ {
661
+ key: "timeShort",
662
+ label: "\u65F6\u95F4\uFF08\u5206\uFF09",
663
+ preview: "23:24",
664
+ value: "timeShort"
665
+ },
666
+ {
667
+ key: "datetime",
668
+ label: "\u65E5\u671F\u65F6\u95F4",
669
+ preview: "2017/08/01 23:24:25",
670
+ value: "datetime"
671
+ },
672
+ {
673
+ key: "datetime12",
674
+ label: "\u65E5\u671F\u65F6\u95F4\uFF08\u4E0A\u4E0B\uFF09",
675
+ preview: "2017/08/01 \u4E0B\u534811:24",
676
+ value: "datetime12"
677
+ }
678
+ ];
679
+ return /* @__PURE__ */ jsxRuntime.jsx(
680
+ MenuSubmenu,
681
+ {
682
+ label: "\u683C\u5F0F",
683
+ minWidth: 340,
684
+ items: formats.map((item) => ({
685
+ key: `data-format-${item.key}`,
686
+ label: item.label,
687
+ preview: "preview" in item ? item.preview : void 0,
688
+ separatorBefore: "separatorBefore" in item ? item.separatorBefore : void 0,
689
+ selected: request.dataFormat === item.value,
690
+ onClick: () => request.onChangeDataFormat?.(item.value)
691
+ }))
257
692
  }
258
693
  );
259
694
  }
@@ -288,28 +723,6 @@ function PivotTooltip(props) {
288
723
  );
289
724
  }
290
725
 
291
- // #style-inject:#style-inject
292
- function styleInject(css, { insertAt } = {}) {
293
- if (!css || typeof document === "undefined") return;
294
- const head = document.head || document.getElementsByTagName("head")[0];
295
- const style = document.createElement("style");
296
- style.type = "text/css";
297
- if (insertAt === "top") {
298
- if (head.firstChild) {
299
- head.insertBefore(style, head.firstChild);
300
- } else {
301
- head.appendChild(style);
302
- }
303
- } else {
304
- head.appendChild(style);
305
- }
306
- if (style.styleSheet) {
307
- style.styleSheet.cssText = css;
308
- } else {
309
- style.appendChild(document.createTextNode(css));
310
- }
311
- }
312
-
313
726
  // src/features/field-panel/pivot-field-panel.css
314
727
  styleInject('.react-pivot-table-customization {\n position: relative;\n overflow: hidden;\n}\n.react-pivot-table-customization__table {\n position: relative;\n min-width: 0;\n min-height: 0;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n.react-pivot-table-customization__panel-column {\n flex-grow: 0 !important;\n overflow: hidden;\n}\n.react-pivot-table-customization__chart-column {\n flex-grow: 0 !important;\n overflow: hidden;\n}\n.react-pivot-field-panel {\n display: flex;\n width: 100%;\n min-width: 280px;\n height: 100%;\n flex-direction: column;\n overflow: hidden;\n background: #fff;\n color: #1f2329;\n font-family:\n "Segoe UI",\n "Microsoft YaHei",\n sans-serif;\n font-size: 13px;\n box-sizing: border-box;\n}\n.react-pivot-field-panel button,\n.react-pivot-field-panel input,\n.react-pivot-field-panel select {\n font: inherit;\n}\n.react-pivot-field-panel__header {\n display: flex;\n height: 42px;\n flex: 0 0 42px;\n align-items: center;\n justify-content: space-between;\n padding: 0 10px 0 14px;\n border-bottom: 1px solid #edf0f2;\n background: #fff;\n}\n.react-pivot-field-panel__icon-button {\n display: inline-flex;\n width: 24px;\n height: 24px;\n align-items: center;\n justify-content: center;\n padding: 0;\n border: 0;\n background: transparent;\n color: #646a73;\n cursor: pointer;\n font: 18px/24px "Segoe UI", sans-serif;\n}\n.react-pivot-field-panel__icon-button:hover {\n background: #f2f3f5;\n color: #d14343;\n}\n.react-pivot-field-panel__layout {\n width: 100%;\n height: auto;\n min-height: 0;\n flex: 1 1 0;\n overflow: hidden;\n}\n.react-pivot-field-panel__layout > .canvas-react-resizable-panels-item > .canvas-react-resizable-panel {\n overflow: hidden;\n}\n.react-pivot-field-panel__section {\n height: 100%;\n padding: 10px 12px 0;\n box-sizing: border-box;\n}\n.react-pivot-field-panel__section--fields,\n.react-pivot-field-panel__section--areas {\n display: flex;\n min-height: 0;\n flex-direction: column;\n}\n.react-pivot-field-panel__section--fields {\n padding-bottom: 6px;\n}\n.react-pivot-field-panel__section--areas {\n padding-bottom: 12px;\n}\n.react-pivot-field-panel__section-title {\n margin-bottom: 5px;\n font-weight: 600;\n}\n.react-pivot-field-panel__hint {\n margin-bottom: 8px;\n color: #646a73;\n font-size: 12px;\n}\n.react-pivot-field-panel__search {\n display: flex;\n height: 32px;\n align-items: center;\n gap: 8px;\n padding: 0 9px;\n border: 1px solid #d5d9de;\n border-radius: 3px;\n background: #fff;\n box-sizing: border-box;\n}\n.react-pivot-field-panel__search > span {\n width: 12px;\n height: 12px;\n border: 1.5px solid #4f5660;\n border-radius: 50%;\n position: relative;\n}\n.react-pivot-field-panel__search > span::after {\n position: absolute;\n right: -4px;\n bottom: -3px;\n width: 5px;\n height: 1.5px;\n background: #4f5660;\n content: "";\n transform: rotate(45deg);\n}\n.react-pivot-field-panel__search input {\n min-width: 0;\n flex: 1;\n border: 0;\n outline: 0;\n color: #1f2329;\n}\n.react-pivot-field-panel__field-list {\n min-height: 0;\n flex: 1;\n margin-top: 6px;\n overflow: auto;\n border: 1px solid #dfe3e7;\n border-radius: 3px;\n background: #fff;\n}\n.react-pivot-field-panel__field,\n.react-pivot-field-panel__area-field {\n display: grid;\n height: 28px;\n align-items: center;\n column-gap: 6px;\n box-sizing: border-box;\n}\n.react-pivot-field-panel__field {\n grid-template-columns: 10px 16px minmax(0, 1fr);\n padding: 0 8px 0 6px;\n}\n.react-pivot-field-panel__field:hover {\n background: #f5f8fb;\n}\n.react-pivot-field-panel__field[data-dragging] {\n opacity: 0.45;\n}\n.react-pivot-field-panel__field > span:nth-of-type(2),\n.react-pivot-field-panel__area-field > span:nth-of-type(2) {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.react-pivot-field-panel__drag {\n width: 8px;\n height: 12px;\n cursor: grab;\n background-image:\n radial-gradient(\n circle at 2px 2px,\n #8f959e 1px,\n transparent 1.2px);\n background-position: 0 0;\n background-size: 4px 4px;\n background-repeat: repeat;\n}\n.react-pivot-field-panel__empty,\n.react-pivot-field-panel__area-empty {\n display: grid;\n height: 100%;\n place-items: center;\n color: #8f959e;\n font-size: 12px;\n}\n.react-pivot-field-panel__areas {\n display: grid;\n min-height: 0;\n flex: 1;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);\n gap: 8px;\n}\n.react-pivot-field-panel__area-wrap {\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n}\n.react-pivot-field-panel__area-title {\n display: grid;\n height: 25px;\n grid-template-columns: minmax(0, 1fr);\n align-items: center;\n gap: 5px;\n color: #3f454d;\n}\n.react-pivot-field-panel__area {\n min-height: 0;\n flex: 1;\n overflow: auto;\n border: 1px solid #dfe3e7;\n border-radius: 3px;\n background: #fff;\n}\n.react-pivot-field-panel__area[data-dragging] {\n border-color: #2f78d0;\n background: #f7faff;\n}\n.react-pivot-field-panel__area-field {\n grid-template-columns: 9px minmax(0, 1fr) 18px;\n padding: 0 7px 0 5px;\n}\n.react-pivot-field-panel__area-field:hover {\n background: #f5f8fb;\n}\n.react-pivot-field-panel__area-field-menu {\n display: grid;\n width: 18px;\n height: 22px;\n padding: 0;\n place-items: center;\n border: 0;\n background: transparent;\n color: #646a73;\n cursor: pointer;\n}\n.react-pivot-field-panel__area-field-menu:hover {\n color: #1f2329;\n background: #e9edf2;\n}\n.react-pivot-field-panel__area-field-menu > span {\n width: 0;\n height: 0;\n border-top: 5px solid currentColor;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.react-pivot-field-menu {\n min-width: 176px;\n padding: 5px 0;\n color: #1f2329;\n font:\n 13px "Segoe UI",\n "Microsoft YaHei",\n sans-serif;\n}\n.react-pivot-field-menu__item {\n display: block;\n width: 100%;\n height: 30px;\n padding: 0 12px;\n border: 0;\n background: transparent;\n color: inherit;\n text-align: left;\n cursor: pointer;\n}\n.react-pivot-field-menu__item:hover:not(:disabled) {\n background: #e9f2fc;\n}\n.react-pivot-field-menu__item:disabled {\n color: #a7abb2;\n cursor: default;\n}\n.react-pivot-field-menu__item[data-danger] {\n color: #c9372c;\n}\n.react-pivot-field-menu__separator {\n height: 1px;\n margin: 4px 0;\n background: #e1e4e8;\n}\n.react-pivot-field-settings {\n display: grid;\n min-width: 260px;\n gap: 12px;\n padding: 14px;\n color: #1f2329;\n font:\n 13px "Segoe UI",\n "Microsoft YaHei",\n sans-serif;\n}\n.react-pivot-field-settings label {\n display: grid;\n gap: 5px;\n color: #515761;\n}\n.react-pivot-field-settings__name-input,\n.react-pivot-field-settings__aggregator {\n height: 30px;\n font: inherit;\n}\n.react-pivot-field-settings__actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.react-pivot-field-settings__actions button {\n height: 30px;\n padding: 0 14px;\n border: 1px solid #bfc4cb;\n border-radius: 3px;\n background: #fff;\n cursor: pointer;\n}\n.react-pivot-field-settings__actions button.is-primary {\n border-color: #2f78d0;\n background: #2f78d0;\n color: #fff;\n}\n');
315
728
  function PivotFieldPanel(props) {
@@ -575,7 +988,7 @@ function FieldContextMenu(props) {
575
988
  },
576
989
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-pivot-field-menu", role: "menu", children: [
577
990
  /* @__PURE__ */ jsxRuntime.jsx(
578
- MenuAction,
991
+ MenuAction2,
579
992
  {
580
993
  label: "\u4E0A\u79FB",
581
994
  disabled: index <= 0,
@@ -584,7 +997,7 @@ function FieldContextMenu(props) {
584
997
  }
585
998
  ),
586
999
  /* @__PURE__ */ jsxRuntime.jsx(
587
- MenuAction,
1000
+ MenuAction2,
588
1001
  {
589
1002
  label: "\u4E0B\u79FB",
590
1003
  disabled: index < 0 || index >= keys.length - 1,
@@ -593,7 +1006,7 @@ function FieldContextMenu(props) {
593
1006
  }
594
1007
  ),
595
1008
  /* @__PURE__ */ jsxRuntime.jsx(
596
- MenuAction,
1009
+ MenuAction2,
597
1010
  {
598
1011
  label: "\u79FB\u81F3\u5F00\u5934",
599
1012
  disabled: index <= 0,
@@ -602,7 +1015,7 @@ function FieldContextMenu(props) {
602
1015
  }
603
1016
  ),
604
1017
  /* @__PURE__ */ jsxRuntime.jsx(
605
- MenuAction,
1018
+ MenuAction2,
606
1019
  {
607
1020
  label: "\u79FB\u81F3\u672B\u5C3E",
608
1021
  disabled: index === keys.length - 1,
@@ -612,7 +1025,7 @@ function FieldContextMenu(props) {
612
1025
  ),
613
1026
  /* @__PURE__ */ jsxRuntime.jsx(MenuSeparator, {}),
614
1027
  /* @__PURE__ */ jsxRuntime.jsx(
615
- MenuAction,
1028
+ MenuAction2,
616
1029
  {
617
1030
  label: "\u6DFB\u52A0\u5230\u62A5\u8868\u7B5B\u9009",
618
1031
  disabled: state.area === "filter",
@@ -621,7 +1034,7 @@ function FieldContextMenu(props) {
621
1034
  }
622
1035
  ),
623
1036
  /* @__PURE__ */ jsxRuntime.jsx(
624
- MenuAction,
1037
+ MenuAction2,
625
1038
  {
626
1039
  label: "\u6DFB\u52A0\u5230\u884C\u6807\u7B7E",
627
1040
  disabled: state.area === "row",
@@ -630,7 +1043,7 @@ function FieldContextMenu(props) {
630
1043
  }
631
1044
  ),
632
1045
  /* @__PURE__ */ jsxRuntime.jsx(
633
- MenuAction,
1046
+ MenuAction2,
634
1047
  {
635
1048
  label: "\u6DFB\u52A0\u5230\u5217\u6807\u7B7E",
636
1049
  disabled: state.area === "column",
@@ -639,7 +1052,7 @@ function FieldContextMenu(props) {
639
1052
  }
640
1053
  ),
641
1054
  /* @__PURE__ */ jsxRuntime.jsx(
642
- MenuAction,
1055
+ MenuAction2,
643
1056
  {
644
1057
  label: "\u79FB\u52A8\u5230\u503C",
645
1058
  disabled: state.area === "value",
@@ -649,7 +1062,7 @@ function FieldContextMenu(props) {
649
1062
  ),
650
1063
  /* @__PURE__ */ jsxRuntime.jsx(MenuSeparator, {}),
651
1064
  /* @__PURE__ */ jsxRuntime.jsx(
652
- MenuAction,
1065
+ MenuAction2,
653
1066
  {
654
1067
  label: "\u5220\u9664\u5B57\u6BB5",
655
1068
  danger: true,
@@ -658,7 +1071,7 @@ function FieldContextMenu(props) {
658
1071
  }
659
1072
  ),
660
1073
  /* @__PURE__ */ jsxRuntime.jsx(
661
- MenuAction,
1074
+ MenuAction2,
662
1075
  {
663
1076
  label: "\u5B57\u6BB5\u8BBE\u7F6E...",
664
1077
  onClick: () => props.onSettings(state)
@@ -741,7 +1154,7 @@ function FieldSettingsPopup(props) {
741
1154
  }
742
1155
  );
743
1156
  }
744
- function MenuAction(props) {
1157
+ function MenuAction2(props) {
745
1158
  return /* @__PURE__ */ jsxRuntime.jsx(
746
1159
  "button",
747
1160
  {
@@ -1268,10 +1681,12 @@ function ReactPivotTableInner(props, ref) {
1268
1681
  null
1269
1682
  );
1270
1683
  const chartIdRef = react.useRef(0);
1684
+ const layoutRevisionRef = react.useRef(0);
1271
1685
  const [internalLayout, setInternalLayout] = react.useState(
1272
1686
  () => customizationOptions?.defaultLayout ?? pivotTable.createEmptyPivotFieldLayout()
1273
1687
  );
1274
1688
  const [fieldOverrides, setFieldOverrides] = react.useState({});
1689
+ const [densityOverride, setDensityOverride] = react.useState(null);
1275
1690
  const [filterRequest, setFilterRequest] = react.useState(null);
1276
1691
  const [contextMenuRequest, setContextMenuRequest] = react.useState(null);
1277
1692
  const [tooltipRequest, setTooltipRequest] = react.useState(null);
@@ -1315,7 +1730,9 @@ function ReactPivotTableInner(props, ref) {
1315
1730
  indicator: {
1316
1731
  ...field.indicator,
1317
1732
  title: override.title ?? field.indicator?.title,
1318
- aggregator: override.aggregator ?? field.indicator?.aggregator
1733
+ aggregator: override.aggregator ?? field.indicator?.aggregator,
1734
+ precision: "precision" in override ? override.precision : field.indicator?.precision,
1735
+ dataFormat: "dataFormat" in override ? override.dataFormat : field.indicator?.dataFormat
1319
1736
  }
1320
1737
  };
1321
1738
  }),
@@ -1325,6 +1742,20 @@ function ReactPivotTableInner(props, ref) {
1325
1742
  () => customizationEnabled ? pivotTable.resolvePivotFieldLayoutOptions(fields, fieldLayout) : null,
1326
1743
  [customizationEnabled, fieldLayout, fields]
1327
1744
  );
1745
+ const overriddenIndicators = react.useMemo(
1746
+ () => tableOptions.indicators.map((indicator) => {
1747
+ const override = fieldOverrides[indicator.key];
1748
+ if (!override) return indicator;
1749
+ return {
1750
+ ...indicator,
1751
+ title: override.title ?? indicator.title,
1752
+ aggregator: override.aggregator ?? indicator.aggregator,
1753
+ precision: "precision" in override ? override.precision : indicator.precision,
1754
+ dataFormat: "dataFormat" in override ? override.dataFormat : indicator.dataFormat
1755
+ };
1756
+ }),
1757
+ [fieldOverrides, tableOptions.indicators]
1758
+ );
1328
1759
  const refreshChartModel = () => {
1329
1760
  const nextModel = tableRef.current?.getPivotModel() ?? null;
1330
1761
  setChartModel((current) => current === nextModel ? current : nextModel);
@@ -1332,9 +1763,31 @@ function ReactPivotTableInner(props, ref) {
1332
1763
  const options = {
1333
1764
  ...tableOptions,
1334
1765
  ...customizedOptions,
1766
+ rowHeight: densityOverride == null ? tableOptions.rowHeight : resolveDensityRowHeight(densityOverride),
1767
+ indicators: customizedOptions?.indicators ?? overriddenIndicators,
1335
1768
  ui: {
1336
1769
  onFilterPanelRequest: setFilterRequest,
1337
- onContextMenuRequest: setContextMenuRequest,
1770
+ onContextMenuRequest: (request) => {
1771
+ if (request) {
1772
+ const nativeChangeDensity = request.onChangeDensity;
1773
+ const fieldKey = request.indicatorKey;
1774
+ const enhancedRequest = {
1775
+ ...request,
1776
+ onChangeDensity: (density) => {
1777
+ nativeChangeDensity?.(density);
1778
+ setDensityOverride(density);
1779
+ },
1780
+ onChangeDataFormat: fieldKey == null ? void 0 : (dataFormat) => updateField(fieldKey, { dataFormat })
1781
+ };
1782
+ if (request.target === "summary" && fieldKey != null) {
1783
+ enhancedRequest.onChangeSummaryAggregator = (aggregator) => updateField(fieldKey, { aggregator });
1784
+ enhancedRequest.onChangeSummaryPrecision = (precision) => updateField(fieldKey, { precision });
1785
+ }
1786
+ setContextMenuRequest(enhancedRequest);
1787
+ return;
1788
+ }
1789
+ setContextMenuRequest(null);
1790
+ },
1338
1791
  onTooltipRequest: setTooltipRequest
1339
1792
  },
1340
1793
  onFilterChange: (event) => {
@@ -1373,7 +1826,13 @@ function ReactPivotTableInner(props, ref) {
1373
1826
  setCharts((current) => current.filter((chart) => chart.id !== chartId));
1374
1827
  };
1375
1828
  const applyLayout = (next) => {
1376
- if (customizationOptions?.layout === void 0) setInternalLayout(next);
1829
+ if (customizationOptions?.layout === void 0) {
1830
+ layoutRevisionRef.current += 1;
1831
+ setInternalLayout(next);
1832
+ if (customizationOptions?.storageKey) {
1833
+ void pivotTable.savePivotFieldLayout(customizationOptions.storageKey, next);
1834
+ }
1835
+ }
1377
1836
  customizationOptions?.onLayoutChange?.(next);
1378
1837
  };
1379
1838
  const moveField = (fieldKey, area, index, sourceArea) => {
@@ -1382,7 +1841,7 @@ function ReactPivotTableInner(props, ref) {
1382
1841
  const removeField = (fieldKey) => {
1383
1842
  applyLayout(pivotTable.removePivotField(fieldLayout, fieldKey));
1384
1843
  };
1385
- const updateField = (fieldKey, patch) => {
1844
+ function updateField(fieldKey, patch) {
1386
1845
  setFieldOverrides((current) => ({
1387
1846
  ...current,
1388
1847
  [fieldKey]: { ...current[fieldKey], ...patch }
@@ -1397,11 +1856,13 @@ function ReactPivotTableInner(props, ref) {
1397
1856
  indicator: {
1398
1857
  ...field.indicator,
1399
1858
  title: patch.title ?? field.indicator?.title,
1400
- aggregator: patch.aggregator ?? field.indicator?.aggregator
1859
+ aggregator: patch.aggregator ?? field.indicator?.aggregator,
1860
+ precision: "precision" in patch ? patch.precision : field.indicator?.precision,
1861
+ dataFormat: "dataFormat" in patch ? patch.dataFormat : field.indicator?.dataFormat
1401
1862
  }
1402
1863
  });
1403
1864
  }
1404
- };
1865
+ }
1405
1866
  react.useEffect(() => {
1406
1867
  const container = containerRef.current;
1407
1868
  if (!container) return;
@@ -1421,6 +1882,22 @@ function ReactPivotTableInner(props, ref) {
1421
1882
  table.updateOptions(options);
1422
1883
  refreshChartModel();
1423
1884
  }, [options]);
1885
+ react.useEffect(() => {
1886
+ const storageKey = customizationOptions?.storageKey;
1887
+ if (!storageKey || customizationOptions?.layout !== void 0) return;
1888
+ let active = true;
1889
+ const revision = layoutRevisionRef.current;
1890
+ void pivotTable.loadPivotFieldLayout(storageKey).then((storedLayout) => {
1891
+ if (!active || !storedLayout || layoutRevisionRef.current !== revision) {
1892
+ return;
1893
+ }
1894
+ setInternalLayout(storedLayout);
1895
+ customizationOptions?.onLayoutChange?.(storedLayout);
1896
+ });
1897
+ return () => {
1898
+ active = false;
1899
+ };
1900
+ }, [customizationOptions?.layout, customizationOptions?.storageKey]);
1424
1901
  react.useEffect(() => {
1425
1902
  if (customizationOptions?.panelWidth != null) {
1426
1903
  setFieldPanelWidth(customizationOptions.panelWidth);
@@ -1567,6 +2044,13 @@ function ReactPivotTableInner(props, ref) {
1567
2044
  /* @__PURE__ */ jsxRuntime.jsx(PivotTooltip, { request: tooltipRequest })
1568
2045
  ] });
1569
2046
  }
2047
+ function resolveDensityRowHeight(density) {
2048
+ if (density === "comfortable") return 56;
2049
+ if (density === "middle") return 48;
2050
+ if (density === "compact") return 32;
2051
+ if (density === "ultra-compact") return 26;
2052
+ return void 0;
2053
+ }
1570
2054
  var ReactPivotTable = react.forwardRef(ReactPivotTableInner);
1571
2055
  function toSharedFilterRequest(request, onValueChange) {
1572
2056
  if (!request) return null;