@canvas-components/react-pivot-table 0.1.7

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 ADDED
@@ -0,0 +1,1705 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var pivotTable = require('@canvas-components/pivot-table');
5
+ var reactComponents = require('@canvas-components/react-components');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+ var reactDom = require('react-dom');
8
+ var chart = require('@canvas-components/chart');
9
+
10
+ // src/components/react-pivot-table.tsx
11
+ function PivotContextMenu(props) {
12
+ const { request } = props;
13
+ if (!request) return null;
14
+ return /* @__PURE__ */ jsxRuntime.jsx(
15
+ reactComponents.ReactPopup,
16
+ {
17
+ open: true,
18
+ anchorRect: { x: request.x, y: request.y, width: 0, height: 0 },
19
+ offset: 4,
20
+ matchAnchorWidth: false,
21
+ minWidth: 180,
22
+ onOpenChange: (open) => {
23
+ if (!open) request.onClose();
24
+ },
25
+ surfaceStyle: { padding: 6 },
26
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "grid", minWidth: 180 }, children: [
27
+ /* @__PURE__ */ jsxRuntime.jsx(
28
+ MenuButton,
29
+ {
30
+ label: "\u590D\u5236\u5355\u5143\u683C",
31
+ onClick: request.onCopy,
32
+ close: request.onClose
33
+ }
34
+ ),
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,
45
+ {
46
+ request
47
+ },
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
+ ),
58
+ /* @__PURE__ */ jsxRuntime.jsx(
59
+ MenuButton,
60
+ {
61
+ label: "\u5BFC\u51FA XLSX",
62
+ onClick: request.onExportXlsx,
63
+ close: request.onClose
64
+ }
65
+ ),
66
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "8px 10px", color: "#8f959e", fontSize: 12 }, children: [
67
+ "\u4E0B\u94BB\u8BB0\u5F55 ",
68
+ request.recordCount,
69
+ " \u6761"
70
+ ] })
71
+ ] })
72
+ }
73
+ );
74
+ }
75
+ function CustomSelectionMenu(props) {
76
+ const { request } = props;
77
+ const triggerRef = react.useRef(null);
78
+ const [open, setOpen] = react.useState(false);
79
+ const [range, setRange] = react.useState(
80
+ request.customSelection ?? {
81
+ startRow: 1,
82
+ startColumn: 1,
83
+ endRow: 1,
84
+ endColumn: 1,
85
+ rowCount: 0,
86
+ columnCount: 0
87
+ }
88
+ );
89
+ if (!request.customSelection || !request.onCopyCustomSelection) return null;
90
+ const update = (key, value) => {
91
+ setRange((current) => ({
92
+ ...current,
93
+ [key]: value == null ? 1 : Math.floor(value)
94
+ }));
95
+ };
96
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
97
+ /* @__PURE__ */ jsxRuntime.jsx(
98
+ "button",
99
+ {
100
+ ref: triggerRef,
101
+ type: "button",
102
+ onClick: () => setOpen((value) => !value),
103
+ style: menuButtonStyle,
104
+ children: "\u590D\u5236\u81EA\u5B9A\u4E49\u9009\u533A"
105
+ }
106
+ ),
107
+ /* @__PURE__ */ jsxRuntime.jsx(
108
+ reactComponents.ReactPopup,
109
+ {
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
+ ] })
191
+ }
192
+ )
193
+ ] });
194
+ }
195
+ function CustomSelectionRow(props) {
196
+ return /* @__PURE__ */ jsxRuntime.jsxs(
197
+ "div",
198
+ {
199
+ style: {
200
+ display: "grid",
201
+ gridTemplateColumns: "38px 1fr 1fr",
202
+ gap: 6,
203
+ alignItems: "center",
204
+ marginBottom: 6
205
+ },
206
+ children: [
207
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#374151", fontSize: 12 }, children: props.label }),
208
+ /* @__PURE__ */ jsxRuntime.jsx(
209
+ reactComponents.ReactInputNumber,
210
+ {
211
+ value: props.row,
212
+ min: 1,
213
+ max: props.maxRow,
214
+ step: 1,
215
+ precision: 0,
216
+ placeholder: "\u884C",
217
+ onChange: props.onRowChange
218
+ }
219
+ ),
220
+ /* @__PURE__ */ jsxRuntime.jsx(
221
+ reactComponents.ReactInputNumber,
222
+ {
223
+ value: props.column,
224
+ min: 1,
225
+ max: props.maxColumn,
226
+ step: 1,
227
+ precision: 0,
228
+ placeholder: "\u5217",
229
+ onChange: props.onColumnChange
230
+ }
231
+ )
232
+ ]
233
+ }
234
+ );
235
+ }
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(
248
+ "button",
249
+ {
250
+ type: "button",
251
+ onClick: () => {
252
+ props.onClick();
253
+ props.close();
254
+ },
255
+ style: menuButtonStyle,
256
+ children: props.label
257
+ }
258
+ );
259
+ }
260
+ function PivotTooltip(props) {
261
+ const { request } = props;
262
+ if (!request) return null;
263
+ if (typeof document === "undefined") return null;
264
+ return reactDom.createPortal(
265
+ /* @__PURE__ */ jsxRuntime.jsx(
266
+ "div",
267
+ {
268
+ role: "tooltip",
269
+ style: {
270
+ position: "fixed",
271
+ left: request.x + 10,
272
+ top: request.y + 14,
273
+ zIndex: 1100,
274
+ maxWidth: 360,
275
+ padding: "6px 10px",
276
+ borderRadius: 4,
277
+ background: "rgba(31, 35, 41, 0.92)",
278
+ color: "#fff",
279
+ fontSize: 12,
280
+ lineHeight: 1.5,
281
+ overflowWrap: "anywhere",
282
+ pointerEvents: "none"
283
+ },
284
+ children: request.text
285
+ }
286
+ ),
287
+ document.body
288
+ );
289
+ }
290
+
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
+ // src/features/field-panel/pivot-field-panel.css
314
+ 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
+ function PivotFieldPanel(props) {
316
+ const [keyword, setKeyword] = react.useState("");
317
+ const [draggingKey, setDraggingKey] = react.useState(null);
318
+ const [dragOverArea, setDragOverArea] = react.useState(null);
319
+ const [contextMenu, setContextMenu] = react.useState(null);
320
+ const [settings, setSettings] = react.useState(null);
321
+ const normalizedKeyword = keyword.trim().toLocaleLowerCase();
322
+ const visibleFields = props.fields.filter(
323
+ (field) => pivotTable.resolvePivotDimensionTitle(field).toLocaleLowerCase().includes(normalizedKeyword)
324
+ );
325
+ const settingsField = settings ? findField(props.fields, settings.fieldKey) : void 0;
326
+ const handleContextMenu = (event, fieldKey, area) => {
327
+ event.preventDefault();
328
+ setContextMenu({ fieldKey, area, x: event.clientX, y: event.clientY });
329
+ };
330
+ const handleMenuClick = (event, fieldKey, area) => {
331
+ event.stopPropagation();
332
+ const rect = event.currentTarget.getBoundingClientRect();
333
+ setContextMenu({ fieldKey, area, x: rect.left, y: rect.bottom });
334
+ };
335
+ return /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "react-pivot-field-panel", "aria-label": "\u6570\u636E\u900F\u89C6\u8868\u5B57\u6BB5", children: [
336
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "react-pivot-field-panel__header", children: [
337
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "\u6570\u636E\u900F\u89C6\u8868\u5B57\u6BB5" }),
338
+ /* @__PURE__ */ jsxRuntime.jsx(
339
+ "button",
340
+ {
341
+ type: "button",
342
+ className: "react-pivot-field-panel__icon-button",
343
+ "aria-label": "\u5173\u95ED\u5B57\u6BB5\u9762\u677F",
344
+ title: "\u5173\u95ED",
345
+ onClick: props.onClose,
346
+ children: "\xD7"
347
+ }
348
+ )
349
+ ] }),
350
+ /* @__PURE__ */ jsxRuntime.jsx(
351
+ reactComponents.ReactResizablePanels,
352
+ {
353
+ direction: "vertical",
354
+ className: "react-pivot-field-panel__layout",
355
+ panels: [
356
+ {
357
+ id: "field-list",
358
+ minSize: 140,
359
+ content: /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "react-pivot-field-panel__section react-pivot-field-panel__section--fields", children: [
360
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-field-panel__section-title", children: "\u5B57\u6BB5\u5217\u8868" }),
361
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-field-panel__hint", children: "\u5C06\u5B57\u6BB5\u62D6\u52A8\u81F3\u6570\u636E\u900F\u89C6\u8868\u533A\u57DF" }),
362
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "react-pivot-field-panel__search", children: [
363
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true" }),
364
+ /* @__PURE__ */ jsxRuntime.jsx(
365
+ "input",
366
+ {
367
+ value: keyword,
368
+ placeholder: "\u641C\u7D22\u5B57\u6BB5",
369
+ onChange: (event) => setKeyword(event.target.value)
370
+ }
371
+ )
372
+ ] }),
373
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-pivot-field-panel__field-list", children: [
374
+ visibleFields.map((field) => {
375
+ const fieldKey = pivotTable.resolvePivotDimensionKey(field);
376
+ const area = pivotTable.findPivotFieldArea(props.layout, fieldKey);
377
+ return /* @__PURE__ */ jsxRuntime.jsxs(
378
+ "div",
379
+ {
380
+ className: "react-pivot-field-panel__field",
381
+ draggable: true,
382
+ "data-dragging": draggingKey === fieldKey || void 0,
383
+ onDragStart: (event) => startDrag(event, fieldKey, setDraggingKey),
384
+ onDragEnd: () => {
385
+ setDraggingKey(null);
386
+ setDragOverArea(null);
387
+ },
388
+ children: [
389
+ /* @__PURE__ */ jsxRuntime.jsx(
390
+ "span",
391
+ {
392
+ className: "react-pivot-field-panel__drag",
393
+ "aria-hidden": "true"
394
+ }
395
+ ),
396
+ /* @__PURE__ */ jsxRuntime.jsx(
397
+ reactComponents.ReactCheckbox,
398
+ {
399
+ checked: area != null,
400
+ "aria-label": `${pivotTable.resolvePivotDimensionTitle(field)}\u542F\u7528\u72B6\u6001`,
401
+ onChange: (event) => {
402
+ if (event.target.checked) {
403
+ props.onMove(
404
+ fieldKey,
405
+ field.defaultArea ?? "row"
406
+ );
407
+ } else {
408
+ props.onRemove(fieldKey);
409
+ }
410
+ }
411
+ }
412
+ ),
413
+ /* @__PURE__ */ jsxRuntime.jsx("span", { title: pivotTable.resolvePivotDimensionTitle(field), children: pivotTable.resolvePivotDimensionTitle(field) })
414
+ ]
415
+ },
416
+ fieldKey
417
+ );
418
+ }),
419
+ visibleFields.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-field-panel__empty", children: "\u672A\u627E\u5230\u5B57\u6BB5" }) : null
420
+ ] })
421
+ ] })
422
+ },
423
+ {
424
+ id: "pivot-areas",
425
+ minSize: 140,
426
+ content: /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "react-pivot-field-panel__section react-pivot-field-panel__section--areas", children: [
427
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-field-panel__section-title", children: "\u6570\u636E\u900F\u89C6\u8868\u533A\u57DF" }),
428
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-field-panel__hint", children: "\u5728\u4E0B\u65B9\u533A\u57DF\u4E2D\u62D6\u52A8\u5B57\u6BB5" }),
429
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-field-panel__areas", children: areaDefinitions.map(({ area, label }) => /* @__PURE__ */ jsxRuntime.jsx(
430
+ FieldArea,
431
+ {
432
+ area,
433
+ label,
434
+ fields: props.fields,
435
+ fieldKeys: layoutArea(props.layout, area),
436
+ dragging: dragOverArea === area,
437
+ onDragEnter: () => setDragOverArea(area),
438
+ onDragLeave: () => setDragOverArea(
439
+ (current) => current === area ? null : current
440
+ ),
441
+ onDrop: (event, index) => {
442
+ event.preventDefault();
443
+ const dragField = readDragField(event, draggingKey);
444
+ if (dragField) {
445
+ props.onMove(
446
+ dragField.fieldKey,
447
+ area,
448
+ index,
449
+ dragField.sourceArea
450
+ );
451
+ }
452
+ setDraggingKey(null);
453
+ setDragOverArea(null);
454
+ },
455
+ onContextMenu: handleContextMenu,
456
+ onMenuClick: handleMenuClick
457
+ },
458
+ area
459
+ )) })
460
+ ] })
461
+ }
462
+ ]
463
+ }
464
+ ),
465
+ /* @__PURE__ */ jsxRuntime.jsx(
466
+ FieldContextMenu,
467
+ {
468
+ state: contextMenu,
469
+ layout: props.layout,
470
+ onClose: () => setContextMenu(null),
471
+ onMove: props.onMove,
472
+ onRemove: props.onRemove,
473
+ onSettings: (next) => {
474
+ setContextMenu(null);
475
+ setSettings(next);
476
+ }
477
+ }
478
+ ),
479
+ settings && settingsField ? /* @__PURE__ */ jsxRuntime.jsx(
480
+ FieldSettingsPopup,
481
+ {
482
+ state: settings,
483
+ field: settingsField,
484
+ onClose: () => setSettings(null),
485
+ onApply: (patch) => {
486
+ props.onFieldChange(settings.fieldKey, patch);
487
+ setSettings(null);
488
+ }
489
+ },
490
+ `${settings.fieldKey}:${settings.x}:${settings.y}`
491
+ ) : null
492
+ ] });
493
+ }
494
+ function FieldArea(props) {
495
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-pivot-field-panel__area-wrap", children: [
496
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-field-panel__area-title", children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: props.label }) }),
497
+ /* @__PURE__ */ jsxRuntime.jsxs(
498
+ "div",
499
+ {
500
+ className: "react-pivot-field-panel__area",
501
+ "data-dragging": props.dragging || void 0,
502
+ onDragOver: (event) => event.preventDefault(),
503
+ onDragEnter: props.onDragEnter,
504
+ onDragLeave: (event) => {
505
+ if (!event.currentTarget.contains(event.relatedTarget)) {
506
+ props.onDragLeave();
507
+ }
508
+ },
509
+ onDrop: (event) => props.onDrop(event),
510
+ children: [
511
+ props.fieldKeys.map((fieldKey, index) => {
512
+ const field = findField(props.fields, fieldKey);
513
+ if (!field) return null;
514
+ const fieldTitle = props.area === "value" ? pivotTable.resolvePivotValueFieldTitle(field) : pivotTable.resolvePivotDimensionTitle(field);
515
+ return /* @__PURE__ */ jsxRuntime.jsxs(
516
+ "div",
517
+ {
518
+ className: "react-pivot-field-panel__area-field",
519
+ draggable: true,
520
+ onDragStart: (event) => startDrag(event, fieldKey, void 0, props.area),
521
+ onDragOver: (event) => event.preventDefault(),
522
+ onDrop: (event) => {
523
+ event.stopPropagation();
524
+ props.onDrop(event, index);
525
+ },
526
+ onContextMenu: (event) => props.onContextMenu(event, fieldKey, props.area),
527
+ children: [
528
+ /* @__PURE__ */ jsxRuntime.jsx(
529
+ "span",
530
+ {
531
+ className: "react-pivot-field-panel__drag",
532
+ "aria-hidden": "true"
533
+ }
534
+ ),
535
+ /* @__PURE__ */ jsxRuntime.jsx("span", { title: fieldTitle, children: fieldTitle }),
536
+ /* @__PURE__ */ jsxRuntime.jsx(
537
+ "button",
538
+ {
539
+ type: "button",
540
+ className: "react-pivot-field-panel__area-field-menu",
541
+ "aria-label": `${fieldTitle}\u5B57\u6BB5\u83DC\u5355`,
542
+ title: "\u5B57\u6BB5\u64CD\u4F5C",
543
+ onPointerDown: (event) => event.stopPropagation(),
544
+ onClick: (event) => props.onMenuClick(event, fieldKey, props.area),
545
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true" })
546
+ }
547
+ )
548
+ ]
549
+ },
550
+ fieldKey
551
+ );
552
+ }),
553
+ props.fieldKeys.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-field-panel__area-empty", children: "\u62D6\u52A8\u5B57\u6BB5\u5230\u6B64\u5904" }) : null
554
+ ]
555
+ }
556
+ )
557
+ ] });
558
+ }
559
+ function FieldContextMenu(props) {
560
+ const state = props.state;
561
+ if (!state) return null;
562
+ const keys = layoutArea(props.layout, state.area);
563
+ const index = keys.indexOf(state.fieldKey);
564
+ return /* @__PURE__ */ jsxRuntime.jsx(
565
+ reactComponents.ReactPopup,
566
+ {
567
+ open: true,
568
+ anchorRect: { x: state.x, y: state.y, width: 0, height: 0 },
569
+ offset: 2,
570
+ minWidth: 176,
571
+ zIndex: 10020,
572
+ surfaceStyle: popupStyle,
573
+ onOpenChange: (open) => {
574
+ if (!open) props.onClose();
575
+ },
576
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-pivot-field-menu", role: "menu", children: [
577
+ /* @__PURE__ */ jsxRuntime.jsx(
578
+ MenuAction,
579
+ {
580
+ label: "\u4E0A\u79FB",
581
+ disabled: index <= 0,
582
+ onClick: () => props.onMove(state.fieldKey, state.area, index - 1, state.area),
583
+ close: props.onClose
584
+ }
585
+ ),
586
+ /* @__PURE__ */ jsxRuntime.jsx(
587
+ MenuAction,
588
+ {
589
+ label: "\u4E0B\u79FB",
590
+ disabled: index < 0 || index >= keys.length - 1,
591
+ onClick: () => props.onMove(state.fieldKey, state.area, index + 1, state.area),
592
+ close: props.onClose
593
+ }
594
+ ),
595
+ /* @__PURE__ */ jsxRuntime.jsx(
596
+ MenuAction,
597
+ {
598
+ label: "\u79FB\u81F3\u5F00\u5934",
599
+ disabled: index <= 0,
600
+ onClick: () => props.onMove(state.fieldKey, state.area, 0, state.area),
601
+ close: props.onClose
602
+ }
603
+ ),
604
+ /* @__PURE__ */ jsxRuntime.jsx(
605
+ MenuAction,
606
+ {
607
+ label: "\u79FB\u81F3\u672B\u5C3E",
608
+ disabled: index === keys.length - 1,
609
+ onClick: () => props.onMove(state.fieldKey, state.area, void 0, state.area),
610
+ close: props.onClose
611
+ }
612
+ ),
613
+ /* @__PURE__ */ jsxRuntime.jsx(MenuSeparator, {}),
614
+ /* @__PURE__ */ jsxRuntime.jsx(
615
+ MenuAction,
616
+ {
617
+ label: "\u6DFB\u52A0\u5230\u62A5\u8868\u7B5B\u9009",
618
+ disabled: state.area === "filter",
619
+ onClick: () => props.onMove(state.fieldKey, "filter", void 0, state.area),
620
+ close: props.onClose
621
+ }
622
+ ),
623
+ /* @__PURE__ */ jsxRuntime.jsx(
624
+ MenuAction,
625
+ {
626
+ label: "\u6DFB\u52A0\u5230\u884C\u6807\u7B7E",
627
+ disabled: state.area === "row",
628
+ onClick: () => props.onMove(state.fieldKey, "row", void 0, state.area),
629
+ close: props.onClose
630
+ }
631
+ ),
632
+ /* @__PURE__ */ jsxRuntime.jsx(
633
+ MenuAction,
634
+ {
635
+ label: "\u6DFB\u52A0\u5230\u5217\u6807\u7B7E",
636
+ disabled: state.area === "column",
637
+ onClick: () => props.onMove(state.fieldKey, "column", void 0, state.area),
638
+ close: props.onClose
639
+ }
640
+ ),
641
+ /* @__PURE__ */ jsxRuntime.jsx(
642
+ MenuAction,
643
+ {
644
+ label: "\u79FB\u52A8\u5230\u503C",
645
+ disabled: state.area === "value",
646
+ onClick: () => props.onMove(state.fieldKey, "value", void 0, state.area),
647
+ close: props.onClose
648
+ }
649
+ ),
650
+ /* @__PURE__ */ jsxRuntime.jsx(MenuSeparator, {}),
651
+ /* @__PURE__ */ jsxRuntime.jsx(
652
+ MenuAction,
653
+ {
654
+ label: "\u5220\u9664\u5B57\u6BB5",
655
+ danger: true,
656
+ onClick: () => props.onRemove(state.fieldKey),
657
+ close: props.onClose
658
+ }
659
+ ),
660
+ /* @__PURE__ */ jsxRuntime.jsx(
661
+ MenuAction,
662
+ {
663
+ label: "\u5B57\u6BB5\u8BBE\u7F6E...",
664
+ onClick: () => props.onSettings(state)
665
+ }
666
+ )
667
+ ] })
668
+ }
669
+ );
670
+ }
671
+ function FieldSettingsPopup(props) {
672
+ const field = props.field;
673
+ const state = props.state;
674
+ const [draft, setDraft] = react.useState(null);
675
+ const fieldKey = pivotTable.resolvePivotDimensionKey(field);
676
+ const current = draft?.key === fieldKey ? draft : {
677
+ key: fieldKey,
678
+ title: pivotTable.resolvePivotDimensionTitle(field),
679
+ aggregator: resolveAggregator(field)
680
+ };
681
+ return /* @__PURE__ */ jsxRuntime.jsx(
682
+ reactComponents.ReactPopup,
683
+ {
684
+ open: true,
685
+ anchorRect: { x: state.x, y: state.y, width: 0, height: 0 },
686
+ offset: 2,
687
+ minWidth: 260,
688
+ zIndex: 10021,
689
+ surfaceStyle: popupStyle,
690
+ onOpenChange: (open) => {
691
+ if (!open) props.onClose();
692
+ },
693
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
694
+ "form",
695
+ {
696
+ className: "react-pivot-field-settings",
697
+ onSubmit: (event) => {
698
+ event.preventDefault();
699
+ props.onApply({
700
+ title: current.title.trim() || void 0,
701
+ aggregator: current.aggregator
702
+ });
703
+ },
704
+ children: [
705
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "\u5B57\u6BB5\u8BBE\u7F6E" }),
706
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
707
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u81EA\u5B9A\u4E49\u540D\u79F0" }),
708
+ /* @__PURE__ */ jsxRuntime.jsx(
709
+ reactComponents.ReactInput,
710
+ {
711
+ wrapperClassName: "react-pivot-field-settings__name-input",
712
+ value: current.title,
713
+ onChange: (event) => setDraft({ ...current, title: event.target.value })
714
+ }
715
+ )
716
+ ] }),
717
+ state.area === "value" ? /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
718
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u6C47\u603B\u65B9\u5F0F" }),
719
+ /* @__PURE__ */ jsxRuntime.jsx(
720
+ reactComponents.ReactSelect,
721
+ {
722
+ className: "react-pivot-field-settings__aggregator",
723
+ value: current.aggregator,
724
+ options: aggregatorOptions,
725
+ allowClear: false,
726
+ popupZIndex: 10022,
727
+ onChange: (value) => setDraft({
728
+ ...current,
729
+ aggregator: value
730
+ })
731
+ }
732
+ )
733
+ ] }) : null,
734
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-pivot-field-settings__actions", children: [
735
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: props.onClose, children: "\u53D6\u6D88" }),
736
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "is-primary", children: "\u786E\u5B9A" })
737
+ ] })
738
+ ]
739
+ }
740
+ )
741
+ }
742
+ );
743
+ }
744
+ function MenuAction(props) {
745
+ return /* @__PURE__ */ jsxRuntime.jsx(
746
+ "button",
747
+ {
748
+ type: "button",
749
+ role: "menuitem",
750
+ className: "react-pivot-field-menu__item",
751
+ "data-danger": props.danger || void 0,
752
+ disabled: props.disabled,
753
+ onClick: () => {
754
+ props.onClick();
755
+ props.close?.();
756
+ },
757
+ children: props.label
758
+ }
759
+ );
760
+ }
761
+ function MenuSeparator() {
762
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-field-menu__separator", role: "separator" });
763
+ }
764
+ function startDrag(event, fieldKey, setDraggingKey, sourceArea) {
765
+ event.dataTransfer.effectAllowed = "move";
766
+ event.dataTransfer.setData("application/x-pivot-field", fieldKey);
767
+ event.dataTransfer.setData("text/plain", fieldKey);
768
+ if (sourceArea) {
769
+ event.dataTransfer.setData("application/x-pivot-field-area", sourceArea);
770
+ }
771
+ setDraggingKey?.(fieldKey);
772
+ }
773
+ function readDragField(event, fallback) {
774
+ const fieldKey = event.dataTransfer.getData("application/x-pivot-field") || event.dataTransfer.getData("text/plain") || fallback;
775
+ if (!fieldKey) return null;
776
+ const sourceArea = event.dataTransfer.getData(
777
+ "application/x-pivot-field-area"
778
+ );
779
+ return { fieldKey, sourceArea: sourceArea || void 0 };
780
+ }
781
+ function findField(fields, fieldKey) {
782
+ return fields.find((field) => pivotTable.resolvePivotDimensionKey(field) === fieldKey);
783
+ }
784
+ function layoutArea(layout, area) {
785
+ if (area === "filter") return layout.filters;
786
+ if (area === "column") return layout.columns;
787
+ if (area === "row") return layout.rows;
788
+ return layout.values;
789
+ }
790
+ function resolveAggregator(field) {
791
+ const aggregator = field.indicator?.aggregator;
792
+ return typeof aggregator === "string" && builtInAggregators.includes(aggregator) ? aggregator : "sum";
793
+ }
794
+ var areaDefinitions = [
795
+ { area: "filter", label: "\u7B5B\u9009\u5668" },
796
+ { area: "column", label: "\u5217" },
797
+ { area: "row", label: "\u884C" },
798
+ { area: "value", label: "\u503C" }
799
+ ];
800
+ var builtInAggregators = [
801
+ "sum",
802
+ "avg",
803
+ "count",
804
+ "distinctCount",
805
+ "min",
806
+ "max"
807
+ ];
808
+ var aggregatorOptions = [
809
+ { label: "\u6C42\u548C", value: "sum" },
810
+ { label: "\u8BA1\u6570", value: "count" },
811
+ { label: "\u5E73\u5747\u503C", value: "avg" },
812
+ { label: "\u6700\u5927\u503C", value: "max" },
813
+ { label: "\u6700\u5C0F\u503C", value: "min" },
814
+ { label: "\u53BB\u91CD\u8BA1\u6570", value: "distinctCount" }
815
+ ];
816
+ var popupStyle = {
817
+ padding: 0,
818
+ background: "#ffffff",
819
+ border: "1px solid #c8cdd4",
820
+ borderRadius: 4,
821
+ boxShadow: "0 8px 24px rgba(31, 35, 41, 0.16)"
822
+ };
823
+ var pivotChartTypeLabels = {
824
+ pie: "\u997C\u56FE",
825
+ "grouped-horizontal-bar": "\u7C07\u72B6\u6761\u5F62\u56FE",
826
+ "grouped-column": "\u7C07\u72B6\u67F1\u5F62\u56FE",
827
+ "stacked-area": "\u5806\u79EF\u9762\u79EF\u56FE",
828
+ "stacked-horizontal-bar": "\u5806\u79EF\u6761\u5F62\u56FE",
829
+ "stacked-column": "\u5806\u79EF\u67F1\u5F62\u56FE",
830
+ area: "\u9762\u79EF\u56FE",
831
+ line: "\u6298\u7EBF\u56FE"
832
+ };
833
+ var SERIES_COLORS = [
834
+ "#1677ff",
835
+ "#36a269",
836
+ "#e89b29",
837
+ "#7b61c8",
838
+ "#d05a6e",
839
+ "#278f9d"
840
+ ];
841
+ function buildPivotChartOption(params) {
842
+ const { columns, indicators, model, rows, type } = params;
843
+ if (indicators.length === 0) return null;
844
+ const rowPaths = Array.from(model.rowPaths.values());
845
+ const columnPaths = Array.from(model.columnPaths.values());
846
+ const labels = (rows.length > 0 ? rowPaths : columnPaths).map(
847
+ resolvePathLabel
848
+ );
849
+ const rawSeries = buildRawSeries({
850
+ columnPaths,
851
+ columns,
852
+ indicators,
853
+ model,
854
+ rowPaths,
855
+ rows
856
+ });
857
+ if (rawSeries.length === 0) return null;
858
+ if (type === "pie") {
859
+ const source = rawSeries[0];
860
+ return {
861
+ rendererType: "pie",
862
+ option: {
863
+ color: SERIES_COLORS,
864
+ tooltip: createTooltip(),
865
+ series: {
866
+ type: "pie",
867
+ name: source.name,
868
+ center: ["34%", "50%"],
869
+ radius: "52%",
870
+ itemStyle: { borderColor: "#ffffff", borderWidth: 1 },
871
+ label: {
872
+ show: true,
873
+ position: "outside",
874
+ color: "#515761",
875
+ fontSize: 11
876
+ },
877
+ labelLine: {
878
+ show: true,
879
+ length: 10,
880
+ length2: 8,
881
+ lineStyle: { width: 1, opacity: 0.72 }
882
+ },
883
+ emphasis: {
884
+ scale: true,
885
+ scaleSize: 7
886
+ },
887
+ data: labels.map((name, index) => ({
888
+ name,
889
+ value: source.data[index] ?? 0
890
+ }))
891
+ },
892
+ legend: {
893
+ show: true,
894
+ orient: "vertical",
895
+ left: "right",
896
+ top: "middle",
897
+ itemWidth: 9,
898
+ itemHeight: 9,
899
+ itemGap: 7,
900
+ textStyle: { color: "#515761", fontSize: 11 }
901
+ }
902
+ }
903
+ };
904
+ }
905
+ const horizontal = type === "grouped-horizontal-bar" || type === "stacked-horizontal-bar";
906
+ const line = type === "line" || type === "area" || type === "stacked-area";
907
+ const stacked = type === "stacked-area" || type === "stacked-horizontal-bar" || type === "stacked-column";
908
+ const area = type === "area" || type === "stacked-area";
909
+ const series = rawSeries.map(
910
+ (item, index) => {
911
+ const color = SERIES_COLORS[index % SERIES_COLORS.length];
912
+ return line ? {
913
+ type: "line",
914
+ name: item.name,
915
+ data: item.data,
916
+ stack: stacked ? "pivot-values" : void 0,
917
+ smooth: false,
918
+ showSymbol: true,
919
+ symbolSize: 5,
920
+ lineStyle: { color, width: 2 },
921
+ areaStyle: area ? { color, opacity: stacked ? 0.7 : 0.22 } : void 0,
922
+ itemStyle: { color }
923
+ } : {
924
+ type: "bar",
925
+ name: item.name,
926
+ data: item.data,
927
+ stack: stacked ? "pivot-values" : void 0,
928
+ barWidth: stacked ? 24 : 18,
929
+ itemStyle: { color }
930
+ };
931
+ }
932
+ );
933
+ const hasNegative = rawSeries.some(
934
+ (item) => item.data.some((value) => value < 0)
935
+ );
936
+ const categoryAxis = {
937
+ type: "category",
938
+ data: labels,
939
+ axisLabel: { color: "#646a73", fontSize: 11, margin: 8 },
940
+ axisTick: { show: false }
941
+ };
942
+ const valueAxis = {
943
+ type: "value",
944
+ min: hasNegative ? "dataMin" : 0,
945
+ splitNumber: 4,
946
+ axisLabel: { color: "#8f959e", fontSize: 10 },
947
+ axisTick: { show: false },
948
+ splitLine: { show: true, color: "#edf0f2" }
949
+ };
950
+ return {
951
+ rendererType: "sparkline",
952
+ option: {
953
+ grid: {
954
+ show: true,
955
+ left: 12,
956
+ right: 14,
957
+ top: 14,
958
+ bottom: 18,
959
+ containLabel: true,
960
+ backgroundColor: "#ffffff",
961
+ borderColor: "#e6e9ed",
962
+ borderWidth: 1
963
+ },
964
+ xAxis: horizontal ? valueAxis : categoryAxis,
965
+ yAxis: horizontal ? categoryAxis : valueAxis,
966
+ tooltip: {
967
+ ...createTooltip(),
968
+ trigger: "axis",
969
+ axisPointer: {
970
+ type: line ? "line" : "shadow",
971
+ lineStyle: { color: "#8f959e", width: 1 },
972
+ shadowStyle: { color: "#1f2329", opacity: 0.05 }
973
+ }
974
+ },
975
+ series
976
+ }
977
+ };
978
+ }
979
+ function createTooltip() {
980
+ return {
981
+ backgroundColor: "rgba(255, 255, 255, 0.98)",
982
+ borderColor: "#dfe3e7",
983
+ borderWidth: 1,
984
+ borderRadius: 4,
985
+ padding: [8, 10],
986
+ textStyle: { color: "#1f2329", fontSize: 12 }
987
+ };
988
+ }
989
+ function buildRawSeries(params) {
990
+ const { columnPaths, columns, indicators, model, rowPaths, rows } = params;
991
+ const result = [];
992
+ if (rows.length > 0) {
993
+ columnPaths.forEach(
994
+ (columnPath) => indicators.forEach(
995
+ (indicator) => result.push({
996
+ name: resolveSeriesName(
997
+ columns.length > 0 ? resolvePathLabel(columnPath) : "",
998
+ indicator
999
+ ),
1000
+ data: rowPaths.map(
1001
+ (rowPath2) => resolveCellNumber(
1002
+ model,
1003
+ rowPath2.key,
1004
+ columnPath.key,
1005
+ indicator.key
1006
+ )
1007
+ )
1008
+ })
1009
+ )
1010
+ );
1011
+ return result;
1012
+ }
1013
+ const rowPath = rowPaths[0];
1014
+ if (!rowPath) return result;
1015
+ indicators.forEach(
1016
+ (indicator) => result.push({
1017
+ name: indicator.title ?? indicator.key,
1018
+ data: columnPaths.map(
1019
+ (columnPath) => resolveCellNumber(model, rowPath.key, columnPath.key, indicator.key)
1020
+ )
1021
+ })
1022
+ );
1023
+ return result;
1024
+ }
1025
+ function resolvePathLabel(path) {
1026
+ return path.values.map((item) => item.label).join(" / ") || "\u603B\u8BA1";
1027
+ }
1028
+ function resolveSeriesName(pathLabel, indicator) {
1029
+ const indicatorLabel = indicator.title ?? indicator.key;
1030
+ return pathLabel ? `${pathLabel} \xB7 ${indicatorLabel}` : indicatorLabel;
1031
+ }
1032
+ function resolveCellNumber(model, rowPathKey, columnPathKey, indicatorKey) {
1033
+ const value = model.cells.get(
1034
+ pivotTable.createPivotCellKey(rowPathKey, columnPathKey, indicatorKey)
1035
+ )?.value;
1036
+ const numericValue = typeof value === "number" ? value : Number(value);
1037
+ return Number.isFinite(numericValue) ? numericValue : 0;
1038
+ }
1039
+
1040
+ // src/features/titlebar/pivot-titlebar.css
1041
+ styleInject('.react-pivot-table {\n position: relative;\n display: flex;\n min-width: 0;\n min-height: 0;\n flex-direction: column;\n overflow: hidden;\n box-sizing: border-box;\n}\n.react-pivot-table-titlebar {\n display: flex;\n height: 44px;\n flex: 0 0 44px;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0;\n background: #fff;\n box-sizing: border-box;\n}\n.react-pivot-table-title {\n min-width: 0;\n overflow: hidden;\n color: #1f2937;\n font-size: 14px;\n font-weight: 600;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.react-pivot-table-titlebar__actions {\n display: flex;\n flex: none;\n align-items: center;\n gap: 14px;\n color: #475569;\n}\n.react-pivot-table-titlebar__tool {\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: inherit;\n cursor: pointer;\n font: 16px/24px sans-serif;\n}\n.react-pivot-table-titlebar__tool:hover,\n.react-pivot-table-titlebar__tool[aria-pressed=true],\n.react-pivot-table-titlebar__tool[aria-expanded=true] {\n color: #1677ff;\n}\n.react-pivot-table-titlebar__tool:focus-visible {\n outline: 2px solid rgba(22, 119, 255, 0.28);\n outline-offset: 1px;\n}\n.react-pivot-chart-picker-surface {\n overflow: hidden;\n border-radius: 4px;\n}\n.react-pivot-chart-picker {\n display: grid;\n gap: 2px;\n padding: 5px;\n background: #fff;\n}\n.react-pivot-chart-picker__item {\n display: grid;\n width: 100%;\n min-height: 34px;\n grid-template-columns: 24px minmax(0, 1fr);\n align-items: center;\n gap: 8px;\n padding: 4px 8px;\n border: 0;\n border-radius: 3px;\n background: transparent;\n color: #1f2329;\n cursor: pointer;\n font:\n 13px/20px "Segoe UI",\n "Microsoft YaHei",\n sans-serif;\n text-align: left;\n}\n.react-pivot-chart-picker__item:hover,\n.react-pivot-chart-picker__item:focus-visible {\n background: #f2f6fb;\n outline: none;\n}\n.react-pivot-chart-picker__item:disabled {\n background: transparent;\n color: #a7abb2;\n cursor: default;\n}\n.react-pivot-chart-picker__item svg {\n width: 22px;\n height: 22px;\n fill: #6b7785;\n stroke: #6b7785;\n stroke-width: 1.8;\n}\n.react-pivot-chart-picker__item:hover svg,\n.react-pivot-chart-picker__item:focus-visible svg {\n fill: #1677ff;\n stroke: #1677ff;\n}\n.react-pivot-chart-picker__item:disabled svg {\n fill: #a7abb2;\n stroke: #a7abb2;\n}\n.react-pivot-table__content {\n position: relative;\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n.react-pivot-table__table {\n position: relative;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n');
1042
+ function PivotTitlebar(props) {
1043
+ const chartButtonRef = react.useRef(null);
1044
+ const [chartMenuOpen, setChartMenuOpen] = react.useState(false);
1045
+ const settingsLabel = props.fieldPanelOpen ? "\u5173\u95ED\u5B57\u6BB5\u9762\u677F" : "\u6253\u5F00\u5B57\u6BB5\u9762\u677F";
1046
+ return /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "react-pivot-table-titlebar", children: [
1047
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-table-title", children: props.title }),
1048
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "react-pivot-table-titlebar__actions", children: [
1049
+ /* @__PURE__ */ jsxRuntime.jsx(
1050
+ "button",
1051
+ {
1052
+ ref: chartButtonRef,
1053
+ type: "button",
1054
+ className: "react-pivot-table-titlebar__tool",
1055
+ title: "\u6DFB\u52A0\u56FE\u8868",
1056
+ "aria-label": "\u6DFB\u52A0\u56FE\u8868",
1057
+ "aria-haspopup": "menu",
1058
+ "aria-expanded": chartMenuOpen,
1059
+ onClick: () => setChartMenuOpen((open) => !open),
1060
+ children: /* @__PURE__ */ jsxRuntime.jsx(ChartIcon, {})
1061
+ }
1062
+ ),
1063
+ props.showFieldSettings ? /* @__PURE__ */ jsxRuntime.jsx(
1064
+ "button",
1065
+ {
1066
+ type: "button",
1067
+ className: "react-pivot-table-titlebar__tool",
1068
+ title: settingsLabel,
1069
+ "aria-label": settingsLabel,
1070
+ "aria-pressed": props.fieldPanelOpen,
1071
+ onClick: props.onToggleFieldPanel,
1072
+ children: /* @__PURE__ */ jsxRuntime.jsx(SettingsIcon, {})
1073
+ }
1074
+ ) : null
1075
+ ] }),
1076
+ /* @__PURE__ */ jsxRuntime.jsx(
1077
+ reactComponents.ReactPopup,
1078
+ {
1079
+ open: chartMenuOpen,
1080
+ anchorRef: chartButtonRef,
1081
+ placement: "bottomRight",
1082
+ offset: 6,
1083
+ minWidth: 176,
1084
+ surfaceClassName: "react-pivot-chart-picker-surface",
1085
+ onOpenChange: setChartMenuOpen,
1086
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-chart-picker", role: "menu", children: Object.keys(pivotChartTypeLabels).map(
1087
+ (type) => {
1088
+ const added = props.chartTypes.includes(type);
1089
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1090
+ "button",
1091
+ {
1092
+ type: "button",
1093
+ className: "react-pivot-chart-picker__item",
1094
+ role: "menuitem",
1095
+ disabled: added,
1096
+ title: added ? "\u5DF2\u6DFB\u52A0\u8BE5\u56FE\u8868" : void 0,
1097
+ onClick: () => {
1098
+ props.onAddChart(type);
1099
+ setChartMenuOpen(false);
1100
+ },
1101
+ children: [
1102
+ /* @__PURE__ */ jsxRuntime.jsx(ChartTypeIcon, { type }),
1103
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: pivotChartTypeLabels[type] })
1104
+ ]
1105
+ },
1106
+ type
1107
+ );
1108
+ }
1109
+ ) })
1110
+ }
1111
+ )
1112
+ ] });
1113
+ }
1114
+ function ChartIcon() {
1115
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1116
+ "svg",
1117
+ {
1118
+ viewBox: "0 0 24 24",
1119
+ width: "1em",
1120
+ height: "1em",
1121
+ fill: "none",
1122
+ stroke: "currentColor",
1123
+ strokeWidth: "1.8",
1124
+ "aria-hidden": "true",
1125
+ focusable: "false",
1126
+ children: [
1127
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 19V5M4 19h16" }),
1128
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m7 15 3-4 3 2 4-6" })
1129
+ ]
1130
+ }
1131
+ );
1132
+ }
1133
+ function ChartTypeIcon(props) {
1134
+ if (props.type === "pie") {
1135
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: [
1136
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 3a9 9 0 1 0 9 9h-9z" }),
1137
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14 3.2V10h6.8A9 9 0 0 0 14 3.2z", fill: "none" })
1138
+ ] });
1139
+ }
1140
+ if (props.type === "grouped-horizontal-bar" || props.type === "stacked-horizontal-bar") {
1141
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: props.type === "stacked-horizontal-bar" ? /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 4h7v4H3zM10 4h9v4h-9zM3 10h11v4H3zM14 10h7v4h-7zM3 16h5v4H3zM8 16h10v4H8z" }) : /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 4h13v3H3zM3 9h18v3H3zM3 14h10v3H3zM3 19h16v3H3z" }) });
1142
+ }
1143
+ if (props.type === "area" || props.type === "stacked-area") {
1144
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: props.type === "stacked-area" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1145
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 17 8 11l4 3 5-7 4 3v10H3z" }),
1146
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m3 13 5-5 4 3 5-7 4 3", fill: "none" })
1147
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 18 8 11l4 3 6-9 3 3v12H3z" }) });
1148
+ }
1149
+ if (props.type === "grouped-column" || props.type === "stacked-column") {
1150
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: props.type === "stacked-column" ? /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 12h5v8H3zM3 5h5v7H3zM10 9h5v11h-5zM10 4h5v5h-5zM17 14h4v6h-4zM17 7h4v7h-4z" }) : /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 11h3v9H3zM7 6h3v14H7zM13 8h3v12h-3zM17 4h3v16h-3z" }) });
1151
+ }
1152
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", focusable: "false", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m3 17 5-6 4 3 6-8 3 2", fill: "none" }) });
1153
+ }
1154
+ function SettingsIcon() {
1155
+ return /* @__PURE__ */ jsxRuntime.jsx(
1156
+ "svg",
1157
+ {
1158
+ viewBox: "64 64 896 896",
1159
+ width: "1em",
1160
+ height: "1em",
1161
+ fill: "currentColor",
1162
+ "aria-hidden": "true",
1163
+ focusable: "false",
1164
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z" })
1165
+ }
1166
+ );
1167
+ }
1168
+
1169
+ // src/features/chart/pivot-chart-panel.css
1170
+ styleInject('.react-pivot-chart-panel {\n display: flex;\n width: 100%;\n min-width: 0;\n height: 100%;\n min-height: 0;\n flex-direction: column;\n gap: 8px;\n padding: 0;\n overflow: auto;\n background: #fff;\n box-sizing: border-box;\n}\n.react-pivot-chart-card {\n display: flex;\n min-width: 0;\n min-height: 240px;\n flex: 0 0 260px;\n flex-direction: column;\n overflow: hidden;\n border-radius: 0;\n background: #fff;\n box-sizing: border-box;\n}\n.react-pivot-chart-card:only-child {\n min-height: 240px;\n flex: 1 1 auto;\n}\n.react-pivot-chart-card__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 color: #1f2329;\n font-size: 13px;\n box-sizing: border-box;\n}\n.react-pivot-chart-card__remove {\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-chart-card__remove:hover {\n background: #f2f3f5;\n color: #d14343;\n}\n.react-pivot-chart-card__canvas {\n min-width: 0;\n min-height: 0;\n flex: 1 1 auto;\n}\n.react-pivot-chart-card__empty {\n display: grid;\n min-height: 0;\n flex: 1 1 auto;\n place-items: center;\n color: #8f959e;\n font-size: 12px;\n}\n');
1171
+ function PivotChartPanel(props) {
1172
+ return /* @__PURE__ */ jsxRuntime.jsx("section", { className: "react-pivot-chart-panel", "aria-label": "\u900F\u89C6\u56FE\u8868", children: props.charts.map((chart, index) => /* @__PURE__ */ jsxRuntime.jsx(
1173
+ PivotChartCard,
1174
+ {
1175
+ chart,
1176
+ index,
1177
+ model: props.model,
1178
+ rows: props.rows,
1179
+ columns: props.columns,
1180
+ indicators: props.indicators,
1181
+ onRemove: props.onRemove
1182
+ },
1183
+ chart.id
1184
+ )) });
1185
+ }
1186
+ function PivotChartCard(props) {
1187
+ const option = react.useMemo(
1188
+ () => props.model ? buildPivotChartOption({
1189
+ type: props.chart.type,
1190
+ model: props.model,
1191
+ rows: props.rows,
1192
+ columns: props.columns,
1193
+ indicators: props.indicators
1194
+ }) : null,
1195
+ [
1196
+ props.chart.type,
1197
+ props.columns,
1198
+ props.indicators,
1199
+ props.model,
1200
+ props.rows
1201
+ ]
1202
+ );
1203
+ const title = `${pivotChartTypeLabels[props.chart.type]} ${props.index + 1}`;
1204
+ return /* @__PURE__ */ jsxRuntime.jsxs("article", { className: "react-pivot-chart-card", children: [
1205
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "react-pivot-chart-card__header", children: [
1206
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: title }),
1207
+ /* @__PURE__ */ jsxRuntime.jsx(
1208
+ "button",
1209
+ {
1210
+ type: "button",
1211
+ className: "react-pivot-chart-card__remove",
1212
+ "aria-label": `\u79FB\u9664${title}`,
1213
+ title: "\u79FB\u9664\u56FE\u8868",
1214
+ onClick: () => props.onRemove(props.chart.id),
1215
+ children: "\xD7"
1216
+ }
1217
+ )
1218
+ ] }),
1219
+ option ? /* @__PURE__ */ jsxRuntime.jsx(PivotChartCanvas, { chartOption: option }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-chart-card__empty", children: "\u8BF7\u5148\u5728\u503C\u533A\u57DF\u6DFB\u52A0\u6307\u6807" })
1220
+ ] });
1221
+ }
1222
+ function PivotChartCanvas(props) {
1223
+ const containerRef = react.useRef(null);
1224
+ react.useEffect(() => {
1225
+ const container = containerRef.current;
1226
+ if (!container) {
1227
+ return;
1228
+ }
1229
+ const chart$1 = new chart.CanvasChart(container, {
1230
+ type: props.chartOption.rendererType,
1231
+ option: props.chartOption.option
1232
+ });
1233
+ chart$1.mount();
1234
+ return () => chart$1.destroy();
1235
+ }, [props.chartOption]);
1236
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "react-pivot-chart-card__canvas" });
1237
+ }
1238
+ function ReactPivotTableInner(props, ref) {
1239
+ const { className, style, customization, title, ...tableOptions } = props;
1240
+ const containerRef = react.useRef(null);
1241
+ const tableRef = react.useRef(null);
1242
+ const customizationOptions = customization && typeof customization === "object" ? customization : void 0;
1243
+ const customizationEnabled = customization === true || customizationOptions != null;
1244
+ const [internalPanelOpen, setInternalPanelOpen] = react.useState(
1245
+ customizationOptions?.defaultOpen ?? false
1246
+ );
1247
+ const [fieldPanelWidth, setFieldPanelWidth] = react.useState(
1248
+ () => customizationOptions?.panelWidth ?? 320
1249
+ );
1250
+ const [chartPanelWidth, setChartPanelWidth] = react.useState(360);
1251
+ const [charts, setCharts] = react.useState([]);
1252
+ const [chartModel, setChartModel] = react.useState(
1253
+ null
1254
+ );
1255
+ const chartIdRef = react.useRef(0);
1256
+ const [internalLayout, setInternalLayout] = react.useState(
1257
+ () => customizationOptions?.defaultLayout ?? pivotTable.createEmptyPivotFieldLayout()
1258
+ );
1259
+ const [fieldOverrides, setFieldOverrides] = react.useState({});
1260
+ const [filterRequest, setFilterRequest] = react.useState(null);
1261
+ const [contextMenuRequest, setContextMenuRequest] = react.useState(null);
1262
+ const [tooltipRequest, setTooltipRequest] = react.useState(null);
1263
+ const panelOpen = customizationOptions?.open ?? internalPanelOpen;
1264
+ const fieldLayout = customizationOptions?.layout ?? internalLayout;
1265
+ const baseFields = react.useMemo(() => {
1266
+ const catalog = pivotTable.createPivotFieldCatalog({
1267
+ fields: customizationOptions?.fields,
1268
+ filterDimensions: tableOptions.filterDimensions,
1269
+ columns: tableOptions.columns,
1270
+ rows: tableOptions.rows,
1271
+ indicators: tableOptions.indicators
1272
+ });
1273
+ return catalog.map(
1274
+ (field) => field.indicator ? field : {
1275
+ ...field,
1276
+ indicator: {
1277
+ aggregator: inferFieldAggregator(
1278
+ tableOptions.dataSource,
1279
+ field.dataIndex
1280
+ )
1281
+ }
1282
+ }
1283
+ );
1284
+ }, [
1285
+ customizationOptions?.fields,
1286
+ tableOptions.filterDimensions,
1287
+ tableOptions.columns,
1288
+ tableOptions.rows,
1289
+ tableOptions.indicators,
1290
+ tableOptions.dataSource
1291
+ ]);
1292
+ const fields = react.useMemo(
1293
+ () => baseFields.map((field) => {
1294
+ const fieldKey = pivotTable.resolvePivotDimensionKey(field);
1295
+ const override = fieldOverrides[fieldKey];
1296
+ if (!override) return field;
1297
+ return {
1298
+ ...field,
1299
+ title: override.title ?? field.title,
1300
+ indicator: {
1301
+ ...field.indicator,
1302
+ title: override.title ?? field.indicator?.title,
1303
+ aggregator: override.aggregator ?? field.indicator?.aggregator
1304
+ }
1305
+ };
1306
+ }),
1307
+ [baseFields, fieldOverrides]
1308
+ );
1309
+ const customizedOptions = react.useMemo(
1310
+ () => customizationEnabled ? pivotTable.resolvePivotFieldLayoutOptions(fields, fieldLayout) : null,
1311
+ [customizationEnabled, fieldLayout, fields]
1312
+ );
1313
+ const refreshChartModel = () => {
1314
+ const nextModel = tableRef.current?.getPivotModel() ?? null;
1315
+ setChartModel((current) => current === nextModel ? current : nextModel);
1316
+ };
1317
+ const options = {
1318
+ ...tableOptions,
1319
+ ...customizedOptions,
1320
+ ui: {
1321
+ onFilterPanelRequest: setFilterRequest,
1322
+ onContextMenuRequest: setContextMenuRequest,
1323
+ onTooltipRequest: setTooltipRequest
1324
+ },
1325
+ onFilterChange: (event) => {
1326
+ tableOptions.onFilterChange?.(event);
1327
+ refreshChartModel();
1328
+ },
1329
+ onSortChange: (event) => {
1330
+ tableOptions.onSortChange?.(event);
1331
+ refreshChartModel();
1332
+ }
1333
+ };
1334
+ const sharedFilterRequest = react.useMemo(() => {
1335
+ if (!filterRequest) return null;
1336
+ const { axis, dimensionKey } = filterRequest;
1337
+ return toSharedFilterRequest(filterRequest, (value) => {
1338
+ setFilterRequest(
1339
+ (current) => current?.axis === axis && current.dimensionKey === dimensionKey ? { ...current, value } : current
1340
+ );
1341
+ });
1342
+ }, [filterRequest]);
1343
+ const setPanelOpen = (open) => {
1344
+ if (customizationOptions?.open === void 0) setInternalPanelOpen(open);
1345
+ customizationOptions?.onOpenChange?.(open);
1346
+ };
1347
+ const addChart = (type) => {
1348
+ setCharts((current) => {
1349
+ if (current.some((chart) => chart.type === type)) {
1350
+ return current;
1351
+ }
1352
+ chartIdRef.current += 1;
1353
+ return [...current, { id: `pivot-chart-${chartIdRef.current}`, type }];
1354
+ });
1355
+ refreshChartModel();
1356
+ };
1357
+ const removeChart = (chartId) => {
1358
+ setCharts((current) => current.filter((chart) => chart.id !== chartId));
1359
+ };
1360
+ const applyLayout = (next) => {
1361
+ if (customizationOptions?.layout === void 0) setInternalLayout(next);
1362
+ customizationOptions?.onLayoutChange?.(next);
1363
+ };
1364
+ const moveField = (fieldKey, area, index, sourceArea) => {
1365
+ applyLayout(pivotTable.movePivotField(fieldLayout, fieldKey, area, index, sourceArea));
1366
+ };
1367
+ const removeField = (fieldKey) => {
1368
+ applyLayout(pivotTable.removePivotField(fieldLayout, fieldKey));
1369
+ };
1370
+ const updateField = (fieldKey, patch) => {
1371
+ setFieldOverrides((current) => ({
1372
+ ...current,
1373
+ [fieldKey]: { ...current[fieldKey], ...patch }
1374
+ }));
1375
+ const field = fields.find(
1376
+ (item) => pivotTable.resolvePivotDimensionKey(item) === fieldKey
1377
+ );
1378
+ if (field) {
1379
+ customizationOptions?.onFieldChange?.({
1380
+ ...field,
1381
+ title: patch.title ?? field.title,
1382
+ indicator: {
1383
+ ...field.indicator,
1384
+ title: patch.title ?? field.indicator?.title,
1385
+ aggregator: patch.aggregator ?? field.indicator?.aggregator
1386
+ }
1387
+ });
1388
+ }
1389
+ };
1390
+ react.useEffect(() => {
1391
+ const container = containerRef.current;
1392
+ if (!container) return;
1393
+ const table = new pivotTable.PivotTable(container, options);
1394
+ table.mount();
1395
+ tableRef.current = table;
1396
+ refreshChartModel();
1397
+ return () => {
1398
+ table.destroy();
1399
+ tableRef.current = null;
1400
+ setChartModel(null);
1401
+ };
1402
+ }, []);
1403
+ react.useEffect(() => {
1404
+ const table = tableRef.current;
1405
+ if (!table) return;
1406
+ table.updateOptions(options);
1407
+ refreshChartModel();
1408
+ }, [options]);
1409
+ react.useEffect(() => {
1410
+ if (customizationOptions?.panelWidth != null) {
1411
+ setFieldPanelWidth(customizationOptions.panelWidth);
1412
+ }
1413
+ }, [customizationOptions?.panelWidth]);
1414
+ react.useImperativeHandle(
1415
+ ref,
1416
+ () => ({
1417
+ getPivotModel: () => tableRef.current?.getPivotModel() ?? null,
1418
+ getVisibleRowNodes: () => tableRef.current?.getVisibleRowNodes() ?? [],
1419
+ getVisibleColumnNodes: () => tableRef.current?.getVisibleColumnNodes() ?? [],
1420
+ getCellRecords: (params) => tableRef.current?.getCellRecords(params) ?? [],
1421
+ getSelection: () => tableRef.current?.getSelection() ?? null,
1422
+ setSelection: (selection) => tableRef.current?.setSelection(selection) ?? null,
1423
+ getFilters: () => tableRef.current?.getFilters() ?? null,
1424
+ getDebugSnapshot: () => tableRef.current?.getDebugSnapshot() ?? null,
1425
+ getSorts: () => tableRef.current?.getSorts() ?? [],
1426
+ resetSorters: () => tableRef.current?.resetSorters() ?? [],
1427
+ selectCell: (address, extend) => tableRef.current?.selectCell(address, extend) ?? null,
1428
+ selectNode: (axis, nodeId) => tableRef.current?.selectNode(axis, nodeId) ?? null,
1429
+ scrollToCell: (address) => tableRef.current?.scrollToCell(address) ?? false,
1430
+ getCellValue: (address) => tableRef.current?.getCellValue(address),
1431
+ expandRowNode: (nodeId) => tableRef.current?.expandRowNode(nodeId) ?? false,
1432
+ collapseRowNode: (nodeId) => tableRef.current?.collapseRowNode(nodeId) ?? false,
1433
+ expandColumnNode: (nodeId) => tableRef.current?.expandColumnNode(nodeId) ?? false,
1434
+ collapseColumnNode: (nodeId) => tableRef.current?.collapseColumnNode(nodeId) ?? false,
1435
+ expandAll: (axis) => tableRef.current?.expandAll(axis) ?? 0,
1436
+ collapseAll: (axis) => tableRef.current?.collapseAll(axis) ?? 0,
1437
+ setDimensionFilter: (axis, dimensionKey, filter) => tableRef.current?.setDimensionFilter(axis, dimensionKey, filter) ?? null,
1438
+ getDimensionFilterOptions: (axis, dimensionKey) => tableRef.current?.getDimensionFilterOptions(axis, dimensionKey) ?? [],
1439
+ resetFilters: () => tableRef.current?.resetFilters() ?? null,
1440
+ setDimensionSort: (axis, dimensionKey, direction) => tableRef.current?.setDimensionSort(axis, dimensionKey, direction) ?? [],
1441
+ setIndicatorSort: (params) => tableRef.current?.setIndicatorSort(params) ?? [],
1442
+ exportCsv: (exportOptions) => tableRef.current?.exportCsv(exportOptions) ?? "",
1443
+ exportXlsx: (exportOptions) => tableRef.current?.exportXlsx(exportOptions) ?? new Uint8Array(),
1444
+ getState: () => tableRef.current?.getState() ?? null,
1445
+ restoreState: (snapshot) => tableRef.current?.restoreState(snapshot) ?? false
1446
+ }),
1447
+ []
1448
+ );
1449
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1450
+ /* @__PURE__ */ jsxRuntime.jsxs(
1451
+ "div",
1452
+ {
1453
+ className: `react-pivot-table${className ? ` ${className}` : ""}`,
1454
+ style: {
1455
+ width: props.width ?? "100%",
1456
+ height: props.height ?? "100%",
1457
+ ...style
1458
+ },
1459
+ children: [
1460
+ /* @__PURE__ */ jsxRuntime.jsx(
1461
+ PivotTitlebar,
1462
+ {
1463
+ title,
1464
+ showFieldSettings: customizationEnabled,
1465
+ fieldPanelOpen: panelOpen,
1466
+ onToggleFieldPanel: () => setPanelOpen(!panelOpen),
1467
+ onAddChart: addChart,
1468
+ chartTypes: charts.map((chart) => chart.type)
1469
+ }
1470
+ ),
1471
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "react-pivot-table__content", children: /* @__PURE__ */ jsxRuntime.jsx(
1472
+ reactComponents.ReactResizablePanels,
1473
+ {
1474
+ animated: true,
1475
+ className: "react-pivot-table-customization",
1476
+ style: { width: "100%", height: "100%" },
1477
+ sizes: {
1478
+ "chart-panel": chartPanelWidth,
1479
+ "field-panel": fieldPanelWidth
1480
+ },
1481
+ onSizesChange: (nextSizes) => {
1482
+ const nextChartSize = nextSizes["chart-panel"];
1483
+ if (typeof nextChartSize === "number") {
1484
+ setChartPanelWidth(nextChartSize);
1485
+ }
1486
+ const nextFieldSize = nextSizes["field-panel"];
1487
+ if (panelOpen && typeof nextFieldSize === "number") {
1488
+ setFieldPanelWidth(nextFieldSize);
1489
+ }
1490
+ },
1491
+ panels: [
1492
+ {
1493
+ id: "pivot-table",
1494
+ minSize: 320,
1495
+ content: /* @__PURE__ */ jsxRuntime.jsx(
1496
+ "div",
1497
+ {
1498
+ ref: containerRef,
1499
+ className: "react-pivot-table-customization__table"
1500
+ }
1501
+ )
1502
+ },
1503
+ ...charts.length > 0 ? [
1504
+ {
1505
+ id: "chart-panel",
1506
+ size: chartPanelWidth,
1507
+ minSize: 300,
1508
+ maxSize: 640,
1509
+ className: "react-pivot-table-customization__chart-column",
1510
+ content: /* @__PURE__ */ jsxRuntime.jsx(
1511
+ PivotChartPanel,
1512
+ {
1513
+ charts,
1514
+ model: chartModel,
1515
+ rows: options.rows,
1516
+ columns: options.columns,
1517
+ indicators: options.indicators,
1518
+ onRemove: removeChart
1519
+ }
1520
+ )
1521
+ }
1522
+ ] : [],
1523
+ ...customizationEnabled ? [
1524
+ {
1525
+ id: "field-panel",
1526
+ collapsed: !panelOpen,
1527
+ size: fieldPanelWidth,
1528
+ minSize: 280,
1529
+ maxSize: 520,
1530
+ className: "react-pivot-table-customization__panel-column",
1531
+ content: /* @__PURE__ */ jsxRuntime.jsx(
1532
+ PivotFieldPanel,
1533
+ {
1534
+ fields,
1535
+ layout: fieldLayout,
1536
+ onClose: () => setPanelOpen(false),
1537
+ onMove: moveField,
1538
+ onRemove: removeField,
1539
+ onFieldChange: updateField
1540
+ }
1541
+ )
1542
+ }
1543
+ ] : []
1544
+ ]
1545
+ }
1546
+ ) })
1547
+ ]
1548
+ }
1549
+ ),
1550
+ /* @__PURE__ */ jsxRuntime.jsx(reactComponents.ReactFilterPanel, { request: sharedFilterRequest }),
1551
+ /* @__PURE__ */ jsxRuntime.jsx(PivotContextMenu, { request: contextMenuRequest }),
1552
+ /* @__PURE__ */ jsxRuntime.jsx(PivotTooltip, { request: tooltipRequest })
1553
+ ] });
1554
+ }
1555
+ var ReactPivotTable = react.forwardRef(ReactPivotTableInner);
1556
+ function toSharedFilterRequest(request, onValueChange) {
1557
+ if (!request) return null;
1558
+ return {
1559
+ columnTitle: request.title,
1560
+ anchorRect: {
1561
+ x: request.x,
1562
+ y: request.y,
1563
+ width: request.width,
1564
+ height: 40
1565
+ },
1566
+ value: toSharedValue(request.value),
1567
+ filterOptionImmediateApply: request.filterOptionImmediateApply,
1568
+ options: request.options,
1569
+ texts: defaultFilterTexts,
1570
+ onApply: (value) => {
1571
+ const next = toPivotFilter(value);
1572
+ onValueChange?.(next);
1573
+ request.onApply(next);
1574
+ },
1575
+ onReset: () => {
1576
+ onValueChange?.({});
1577
+ request.onReset();
1578
+ },
1579
+ onClose: request.onCancel
1580
+ };
1581
+ }
1582
+ function toSharedValue(value) {
1583
+ return {
1584
+ keyword: value.searchKeyword ?? "",
1585
+ mode: value.searchExact ? "exact" : "fuzzy",
1586
+ searchExact: value.searchExact,
1587
+ selectedKeys: value.selectedKeys ?? [],
1588
+ filterType: value.condition && value.condition.operator !== "none" ? "condition" : "value",
1589
+ conditionOperator: toSharedOperator(value.condition?.operator),
1590
+ conditionValue: stringifyConditionValue(value.condition?.value),
1591
+ conditionSecondValue: stringifyConditionValue(value.condition?.secondValue),
1592
+ conditionRelation: value.conditionRelation,
1593
+ secondConditionOperator: toSharedOperator(value.secondCondition?.operator),
1594
+ secondConditionValue: stringifyConditionValue(value.secondCondition?.value),
1595
+ secondConditionSecondValue: stringifyConditionValue(
1596
+ value.secondCondition?.secondValue
1597
+ )
1598
+ };
1599
+ }
1600
+ function toPivotFilter(value) {
1601
+ if (value.filterType === "condition") {
1602
+ return {
1603
+ condition: toPivotCondition(
1604
+ value.conditionOperator,
1605
+ value.conditionValue,
1606
+ value.conditionSecondValue
1607
+ ),
1608
+ secondCondition: toPivotCondition(
1609
+ value.secondConditionOperator,
1610
+ value.secondConditionValue,
1611
+ value.secondConditionSecondValue
1612
+ ),
1613
+ conditionRelation: value.conditionRelation
1614
+ };
1615
+ }
1616
+ return {
1617
+ selectedKeys: value.selectedKeys ?? [],
1618
+ searchKeyword: value.keyword,
1619
+ searchExact: value.searchExact
1620
+ };
1621
+ }
1622
+ function toPivotCondition(operator, value, secondValue) {
1623
+ return {
1624
+ operator: toPivotOperator(operator),
1625
+ value,
1626
+ secondValue
1627
+ };
1628
+ }
1629
+ function toSharedOperator(operator) {
1630
+ const map = {
1631
+ none: "none",
1632
+ equals: "eq",
1633
+ notEquals: "neq",
1634
+ contains: "includes",
1635
+ notContains: "notIncludes",
1636
+ startsWith: "startsWith",
1637
+ endsWith: "endsWith",
1638
+ greaterThan: "gt",
1639
+ greaterThanOrEqual: "gte",
1640
+ lessThan: "lt",
1641
+ lessThanOrEqual: "lte",
1642
+ between: "between",
1643
+ blank: "blank",
1644
+ notBlank: "notBlank",
1645
+ notBetween: "notBetween",
1646
+ notStartsWith: "notStartsWith",
1647
+ notEndsWith: "notEndsWith"
1648
+ };
1649
+ return map[operator ?? "none"];
1650
+ }
1651
+ function toPivotOperator(operator) {
1652
+ const map = {
1653
+ none: "none",
1654
+ eq: "equals",
1655
+ neq: "notEquals",
1656
+ includes: "contains",
1657
+ notIncludes: "notContains",
1658
+ startsWith: "startsWith",
1659
+ endsWith: "endsWith",
1660
+ gt: "greaterThan",
1661
+ gte: "greaterThanOrEqual",
1662
+ lt: "lessThan",
1663
+ lte: "lessThanOrEqual",
1664
+ between: "between",
1665
+ blank: "blank",
1666
+ notBlank: "notBlank",
1667
+ notBetween: "notBetween",
1668
+ notStartsWith: "notStartsWith",
1669
+ notEndsWith: "notEndsWith"
1670
+ };
1671
+ return map[operator ?? "none"] ?? "none";
1672
+ }
1673
+ function stringifyConditionValue(value) {
1674
+ return value == null ? "" : String(value);
1675
+ }
1676
+ var defaultFilterTexts = {
1677
+ title: "\u8FC7\u6EE4",
1678
+ valueFilterTab: "\u6309\u503C",
1679
+ conditionFilterTab: "\u6309\u6761\u4EF6",
1680
+ searchPlaceholder: "\u641C\u7D22\u8FC7\u6EE4\u9879",
1681
+ optionsLabel: "\u8FC7\u6EE4\u9879",
1682
+ emptyOptions: "\u6682\u65E0\u8FC7\u6EE4\u9879",
1683
+ loadingOptions: "\u52A0\u8F7D\u4E2D",
1684
+ exactToggleLabel: "\u7CBE\u786E\u5339\u914D",
1685
+ allOptionLabel: "\u5168\u90E8",
1686
+ blankOptionLabel: "\u7A7A",
1687
+ notBlankOptionLabel: "\u975E\u7A7A",
1688
+ relationLabel: "\u6761\u4EF6\u5173\u7CFB",
1689
+ firstConditionLabel: "\u6761\u4EF6\u4E00",
1690
+ secondConditionLabel: "\u6761\u4EF6\u4E8C",
1691
+ firstValuePlaceholder: "\u8BF7\u8F93\u5165\u6761\u4EF6\u503C",
1692
+ secondValuePlaceholder: "\u8BF7\u8F93\u5165\u7B2C\u4E8C\u4E2A\u503C",
1693
+ relationAndLabel: "\u4E14",
1694
+ relationOrLabel: "\u6216",
1695
+ resetAction: "\u91CD\u7F6E",
1696
+ applyAction: "\u786E\u5B9A"
1697
+ };
1698
+ function inferFieldAggregator(records, dataIndex) {
1699
+ const value = records.map((record) => pivotTable.getValueByDataIndex(record, dataIndex)).find((item) => item != null);
1700
+ return typeof value === "number" ? "sum" : "count";
1701
+ }
1702
+
1703
+ exports.ReactPivotTable = ReactPivotTable;
1704
+ //# sourceMappingURL=index.cjs.map
1705
+ //# sourceMappingURL=index.cjs.map