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