@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/LICENSE +21 -0
- package/dist/index.cjs +1705 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +81 -0
- package/dist/index.d.ts +81 -0
- package/dist/index.js +1703 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
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
|