@carto/ps-react-ui 4.3.4 → 4.3.6
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/components.js +2 -2
- package/dist/{error-B2IJ9d2h.js → error-piB8FwYO.js} +2 -2
- package/dist/{error-B2IJ9d2h.js.map → error-piB8FwYO.js.map} +1 -1
- package/dist/{lasso-tool-wFqOD6wk.js → lasso-tool-BctzdzBu.js} +185 -160
- package/dist/lasso-tool-BctzdzBu.js.map +1 -0
- package/dist/{no-data-C54XJt13.js → no-data-jdlbMef0.js} +2 -2
- package/dist/{no-data-C54XJt13.js.map → no-data-jdlbMef0.js.map} +1 -1
- package/dist/{row-DrHwXNvF.js → row-D3uVFImu.js} +2 -2
- package/dist/{row-DrHwXNvF.js.map → row-D3uVFImu.js.map} +1 -1
- package/dist/{series-D3Pc-kYX.js → series-BAImrSBo.js} +3 -3
- package/dist/{series-D3Pc-kYX.js.map → series-BAImrSBo.js.map} +1 -1
- package/dist/types/widgets/actions/index.d.ts +2 -2
- package/dist/types/widgets/actions/stack-toggle/stack-toggle.d.ts +3 -2
- package/dist/types/widgets/actions/zoom-toggle/zoom-toggle.d.ts +4 -0
- package/dist/types/widgets/echart/echart-ui.d.ts +1 -1
- package/dist/types/widgets/echart/types.d.ts +4 -0
- package/dist/types/widgets/loader/loader.d.ts +1 -1
- package/dist/types/widgets/loader/types.d.ts +1 -1
- package/dist/types/widgets/stores/index.d.ts +1 -1
- package/dist/types/widgets/stores/types.d.ts +15 -0
- package/dist/{use-widget-ref-B0aNCANx.js → use-widget-ref-B8x4sHIj.js} +2 -2
- package/dist/{use-widget-ref-B0aNCANx.js.map → use-widget-ref-B8x4sHIj.js.map} +1 -1
- package/dist/widget-store-Dn0Bnc4h.js +178 -0
- package/dist/widget-store-Dn0Bnc4h.js.map +1 -0
- package/dist/widgets/actions.js +698 -617
- package/dist/widgets/actions.js.map +1 -1
- package/dist/widgets/bar.js +2 -2
- package/dist/widgets/category.js +2 -2
- package/dist/widgets/echart.js +96 -85
- package/dist/widgets/echart.js.map +1 -1
- package/dist/widgets/error.js +1 -1
- package/dist/widgets/formula.js +5 -5
- package/dist/widgets/histogram.js +2 -2
- package/dist/widgets/loader.js +41 -40
- package/dist/widgets/loader.js.map +1 -1
- package/dist/widgets/markdown.js +2 -2
- package/dist/widgets/no-data.js +1 -1
- package/dist/widgets/pie.js +2 -2
- package/dist/widgets/range.js +2 -2
- package/dist/widgets/scatterplot.js +2 -2
- package/dist/widgets/skeleton-loader.js +1 -1
- package/dist/widgets/spread.js +5 -5
- package/dist/widgets/stores.js +1 -1
- package/dist/widgets/table.js +3 -3
- package/dist/widgets/timeseries.js +2 -2
- package/dist/widgets/wrapper.js +2 -2
- package/dist/widgets.js +4 -4
- package/package.json +1 -1
- package/src/components/lasso-tool/lasso-tool.tsx +5 -2
- package/src/widgets/actions/index.ts +2 -2
- package/src/widgets/actions/stack-toggle/stack-toggle.test.tsx +143 -9
- package/src/widgets/actions/stack-toggle/stack-toggle.tsx +61 -70
- package/src/widgets/actions/zoom-toggle/zoom-toggle.tsx +85 -53
- package/src/widgets/echart/echart-ui.test.tsx +18 -0
- package/src/widgets/echart/echart-ui.tsx +25 -13
- package/src/widgets/echart/echart.test.tsx +25 -0
- package/src/widgets/echart/echart.tsx +9 -1
- package/src/widgets/echart/types.ts +4 -0
- package/src/widgets/loader/loader.tsx +18 -8
- package/src/widgets/loader/types.ts +1 -1
- package/src/widgets/stores/index.ts +1 -0
- package/src/widgets/stores/types.ts +20 -0
- package/src/widgets/stores/widget-store.test.ts +141 -0
- package/src/widgets/stores/widget-store.ts +99 -2
- package/dist/lasso-tool-wFqOD6wk.js.map +0 -1
- package/dist/widget-store-CB6Trp_0.js +0 -131
- package/dist/widget-store-CB6Trp_0.js.map +0 -1
|
@@ -2,7 +2,7 @@ import { jsxs as c, jsx as n } from "react/jsx-runtime";
|
|
|
2
2
|
import { c as b } from "react/compiler-runtime";
|
|
3
3
|
import "react";
|
|
4
4
|
import "echarts";
|
|
5
|
-
import "../widget-store-
|
|
5
|
+
import "../widget-store-Dn0Bnc4h.js";
|
|
6
6
|
import "zustand/shallow";
|
|
7
7
|
import { g as k } from "../options-D9wflre6.js";
|
|
8
8
|
import { m as w } from "../utils-D3-eQyDR.js";
|
|
@@ -11,7 +11,7 @@ import { Box as s, Skeleton as o } from "@mui/material";
|
|
|
11
11
|
import "@mui/icons-material";
|
|
12
12
|
import "react-markdown";
|
|
13
13
|
import { d as g, a as d } from "../exports-Cr43OCul.js";
|
|
14
|
-
import "../lasso-tool-
|
|
14
|
+
import "../lasso-tool-BctzdzBu.js";
|
|
15
15
|
import "../cjs-D4KH3azB.js";
|
|
16
16
|
import "@dnd-kit/core";
|
|
17
17
|
import "@dnd-kit/sortable";
|
package/dist/widgets/wrapper.js
CHANGED
|
@@ -3,10 +3,10 @@ import { c as _ } from "react/compiler-runtime";
|
|
|
3
3
|
import { Box as O, IconButton as L, MenuItem as P, ListItemIcon as j, ListItemText as z, Menu as D, Typography as H, LinearProgress as N, AccordionSummary as G, AccordionDetails as U, Accordion as V } from "@mui/material";
|
|
4
4
|
import { MoreVert as q } from "@mui/icons-material";
|
|
5
5
|
import { useState as J, useEffect as K } from "react";
|
|
6
|
-
import "../lasso-tool-
|
|
6
|
+
import "../lasso-tool-BctzdzBu.js";
|
|
7
7
|
import "../cjs-D4KH3azB.js";
|
|
8
8
|
import { S as Q } from "../smart-tooltip-BEtBaIdz.js";
|
|
9
|
-
import { u as T } from "../widget-store-
|
|
9
|
+
import { u as T } from "../widget-store-Dn0Bnc4h.js";
|
|
10
10
|
import { useShallow as R } from "zustand/shallow";
|
|
11
11
|
const A = {
|
|
12
12
|
root: {
|
package/dist/widgets.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { u as r } from "./widget-store-
|
|
2
|
-
import { u as W } from "./use-widget-ref-
|
|
3
|
-
import { W as s } from "./no-data-
|
|
4
|
-
import { W as d } from "./error-
|
|
1
|
+
import { u as r } from "./widget-store-Dn0Bnc4h.js";
|
|
2
|
+
import { u as W } from "./use-widget-ref-B8x4sHIj.js";
|
|
3
|
+
import { W as s } from "./no-data-jdlbMef0.js";
|
|
4
|
+
import { W as d } from "./error-piB8FwYO.js";
|
|
5
5
|
import { W as i } from "./note-t51drNe0.js";
|
|
6
6
|
export {
|
|
7
7
|
d as WidgetError,
|
package/package.json
CHANGED
|
@@ -131,7 +131,7 @@ function LassoToolsUIAction({
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
function Options({
|
|
134
|
-
TriggerProps
|
|
134
|
+
TriggerProps = {},
|
|
135
135
|
MenuProps,
|
|
136
136
|
children,
|
|
137
137
|
}: {
|
|
@@ -142,6 +142,9 @@ function Options({
|
|
|
142
142
|
MenuProps?: Partial<ComponentProps<typeof Menu>>
|
|
143
143
|
children: (props: OptionsChildrenProps) => JSX.Element
|
|
144
144
|
}) {
|
|
145
|
+
const { Icon, sx } = TriggerProps
|
|
146
|
+
const IconNode = Icon ?? <ArrowDropDown />
|
|
147
|
+
|
|
145
148
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
|
|
146
149
|
|
|
147
150
|
const open = Boolean(anchorEl)
|
|
@@ -163,7 +166,7 @@ function Options({
|
|
|
163
166
|
}}
|
|
164
167
|
onClick={handleToggle}
|
|
165
168
|
>
|
|
166
|
-
{
|
|
169
|
+
{IconNode}
|
|
167
170
|
</IconButton>
|
|
168
171
|
<Menu
|
|
169
172
|
id='lasso-menu'
|
|
@@ -15,7 +15,7 @@ export {
|
|
|
15
15
|
export type { RelativeDataProps } from './relative-data/types'
|
|
16
16
|
|
|
17
17
|
/* Zoom Toggle Widget */
|
|
18
|
-
export { ZoomToggle } from './zoom-toggle/zoom-toggle'
|
|
18
|
+
export { ZoomToggle, ZOOM_TOGGLE_TOOL_ID } from './zoom-toggle/zoom-toggle'
|
|
19
19
|
export type {
|
|
20
20
|
ZoomToggleProps,
|
|
21
21
|
ZoomState,
|
|
@@ -23,7 +23,7 @@ export type {
|
|
|
23
23
|
} from './zoom-toggle/types'
|
|
24
24
|
|
|
25
25
|
/* Stack Toggle Widget */
|
|
26
|
-
export { StackToggle } from './stack-toggle/stack-toggle'
|
|
26
|
+
export { StackToggle, STACK_TOGGLE_TOOL_ID } from './stack-toggle/stack-toggle'
|
|
27
27
|
export type { StackToggleProps, StackToggleState } from './stack-toggle/types'
|
|
28
28
|
|
|
29
29
|
/* Searcher Toggle Widget */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, test, expect, beforeEach } from 'vitest'
|
|
2
2
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
3
|
-
import { StackToggle } from './stack-toggle'
|
|
3
|
+
import { StackToggle, STACK_TOGGLE_TOOL_ID } from './stack-toggle'
|
|
4
4
|
import { useWidgetStore } from '../../stores/widget-store'
|
|
5
5
|
import { DEFAULT_STACK_GROUP } from '../../echart/const'
|
|
6
6
|
|
|
@@ -113,19 +113,28 @@ describe('StackToggle', () => {
|
|
|
113
113
|
expect((widget as { isStacked?: boolean })?.isStacked).toBe(true)
|
|
114
114
|
})
|
|
115
115
|
|
|
116
|
-
test('
|
|
117
|
-
// setupMultiSeriesWidget already ran in beforeEach
|
|
116
|
+
test('applies stack to EChart series via config pipeline when toggling to stacked', async () => {
|
|
118
117
|
render(<StackToggle id={widgetId} />)
|
|
119
118
|
const button = screen.getByRole('button')
|
|
120
119
|
fireEvent.click(button)
|
|
121
120
|
|
|
121
|
+
// Run the config pipeline to apply the stack tool's transformation
|
|
122
|
+
await useWidgetStore.getState().executeConfigPipeline(widgetId, {
|
|
123
|
+
option: {
|
|
124
|
+
series: [
|
|
125
|
+
{ name: 'Series 1', type: 'bar' },
|
|
126
|
+
{ name: 'Series 2', type: 'bar' },
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
})
|
|
130
|
+
|
|
122
131
|
const widget = useWidgetStore.getState().getWidget(widgetId)
|
|
123
132
|
const series = (widget as { option?: { series?: { stack?: string }[] } })
|
|
124
133
|
?.option?.series?.[0]
|
|
125
134
|
expect(series?.stack).toBe(DEFAULT_STACK_GROUP)
|
|
126
135
|
})
|
|
127
136
|
|
|
128
|
-
test('removes stack from EChart series when toggling to unstacked', () => {
|
|
137
|
+
test('removes stack from EChart series via config pipeline when toggling to unstacked', async () => {
|
|
129
138
|
useWidgetStore.getState().setWidget(widgetId, {
|
|
130
139
|
option: {
|
|
131
140
|
series: [
|
|
@@ -141,13 +150,23 @@ describe('StackToggle', () => {
|
|
|
141
150
|
// First click unstacks (since series has stack, it starts stacked)
|
|
142
151
|
fireEvent.click(button)
|
|
143
152
|
|
|
153
|
+
// Run the config pipeline — tool is disabled when unstacked, so stack is removed
|
|
154
|
+
await useWidgetStore.getState().executeConfigPipeline(widgetId, {
|
|
155
|
+
option: {
|
|
156
|
+
series: [
|
|
157
|
+
{ name: 'Series 1', type: 'bar' },
|
|
158
|
+
{ name: 'Series 2', type: 'bar' },
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
|
|
144
163
|
const widget = useWidgetStore.getState().getWidget(widgetId)
|
|
145
164
|
const series = (widget as { option?: { series?: { stack?: string }[] } })
|
|
146
165
|
?.option?.series?.[0]
|
|
147
166
|
expect(series?.stack).toBeUndefined()
|
|
148
167
|
})
|
|
149
168
|
|
|
150
|
-
test('
|
|
169
|
+
test('applies stack to multiple series via config pipeline', async () => {
|
|
151
170
|
useWidgetStore.getState().setWidget(widgetId, {
|
|
152
171
|
option: {
|
|
153
172
|
series: [
|
|
@@ -162,6 +181,16 @@ describe('StackToggle', () => {
|
|
|
162
181
|
const button = screen.getByRole('button')
|
|
163
182
|
fireEvent.click(button)
|
|
164
183
|
|
|
184
|
+
await useWidgetStore.getState().executeConfigPipeline(widgetId, {
|
|
185
|
+
option: {
|
|
186
|
+
series: [
|
|
187
|
+
{ name: 'Series 1', type: 'bar' },
|
|
188
|
+
{ name: 'Series 2', type: 'bar' },
|
|
189
|
+
{ name: 'Series 3', type: 'bar' },
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
|
|
165
194
|
const widget = useWidgetStore.getState().getWidget(widgetId)
|
|
166
195
|
const seriesArray = (
|
|
167
196
|
widget as { option?: { series?: { stack?: string }[] } }
|
|
@@ -173,7 +202,7 @@ describe('StackToggle', () => {
|
|
|
173
202
|
})
|
|
174
203
|
})
|
|
175
204
|
|
|
176
|
-
test('
|
|
205
|
+
test('preserves custom stack group via config pipeline', async () => {
|
|
177
206
|
const customStackGroup = 'custom-group'
|
|
178
207
|
useWidgetStore.getState().setWidget(widgetId, {
|
|
179
208
|
option: {
|
|
@@ -187,11 +216,20 @@ describe('StackToggle', () => {
|
|
|
187
216
|
render(<StackToggle id={widgetId} />)
|
|
188
217
|
const button = screen.getByRole('button')
|
|
189
218
|
|
|
190
|
-
// Toggle off then on
|
|
219
|
+
// Toggle off then on
|
|
191
220
|
fireEvent.click(button) // unstacked
|
|
192
221
|
fireEvent.click(button) // stacked again
|
|
193
222
|
|
|
194
|
-
|
|
223
|
+
await useWidgetStore.getState().executeConfigPipeline(widgetId, {
|
|
224
|
+
option: {
|
|
225
|
+
series: [
|
|
226
|
+
{ name: 'Series 1', type: 'bar', stack: customStackGroup },
|
|
227
|
+
{ name: 'Series 2', type: 'bar', stack: customStackGroup },
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
await waitFor(() => {
|
|
195
233
|
const widget = useWidgetStore.getState().getWidget(widgetId)
|
|
196
234
|
const series = (widget as { option?: { series?: { stack?: string }[] } })
|
|
197
235
|
?.option?.series?.[0]
|
|
@@ -238,7 +276,75 @@ describe('StackToggle', () => {
|
|
|
238
276
|
expect(container.firstChild).toBeNull()
|
|
239
277
|
})
|
|
240
278
|
|
|
241
|
-
test('
|
|
279
|
+
test('config tool re-applies stack when config pipeline re-runs', async () => {
|
|
280
|
+
// Start with stacked widget
|
|
281
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
282
|
+
isStacked: true,
|
|
283
|
+
option: {
|
|
284
|
+
series: [
|
|
285
|
+
{ name: 'Series 1', type: 'bar', stack: DEFAULT_STACK_GROUP },
|
|
286
|
+
{ name: 'Series 2', type: 'bar', stack: DEFAULT_STACK_GROUP },
|
|
287
|
+
],
|
|
288
|
+
},
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
render(<StackToggle id={widgetId} defaultIsStacked />)
|
|
292
|
+
|
|
293
|
+
// Simulate loader running config pipeline with base config (no stack)
|
|
294
|
+
await useWidgetStore.getState().executeConfigPipeline(widgetId, {
|
|
295
|
+
option: {
|
|
296
|
+
series: [
|
|
297
|
+
{ name: 'Series 1', type: 'bar' },
|
|
298
|
+
{ name: 'Series 2', type: 'bar' },
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
// Stack should be re-applied by the config tool
|
|
304
|
+
await waitFor(() => {
|
|
305
|
+
const widget = useWidgetStore.getState().getWidget(widgetId)
|
|
306
|
+
const series = (widget as { option?: { series?: { stack?: string }[] } })
|
|
307
|
+
?.option?.series
|
|
308
|
+
expect(series?.[0]?.stack).toBe(DEFAULT_STACK_GROUP)
|
|
309
|
+
expect(series?.[1]?.stack).toBe(DEFAULT_STACK_GROUP)
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
test('config tool does not apply stack when unstacked and pipeline re-runs', async () => {
|
|
314
|
+
// Start with unstacked widget
|
|
315
|
+
useWidgetStore.getState().setWidget(widgetId, {
|
|
316
|
+
isStacked: false,
|
|
317
|
+
option: {
|
|
318
|
+
series: [
|
|
319
|
+
{ name: 'Series 1', type: 'bar' },
|
|
320
|
+
{ name: 'Series 2', type: 'bar' },
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
render(<StackToggle id={widgetId} />)
|
|
326
|
+
|
|
327
|
+
// Run config pipeline with base config
|
|
328
|
+
await useWidgetStore.getState().executeConfigPipeline(widgetId, {
|
|
329
|
+
option: {
|
|
330
|
+
series: [
|
|
331
|
+
{ name: 'Series 1', type: 'bar' },
|
|
332
|
+
{ name: 'Series 2', type: 'bar' },
|
|
333
|
+
],
|
|
334
|
+
},
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
// Stack should NOT be applied since isStacked is false (tool disabled)
|
|
338
|
+
await waitFor(() => {
|
|
339
|
+
const widget = useWidgetStore.getState().getWidget(widgetId)
|
|
340
|
+
const series = (widget as { option?: { series?: { stack?: string }[] } })
|
|
341
|
+
?.option?.series
|
|
342
|
+
expect(series?.[0]?.stack).toBeUndefined()
|
|
343
|
+
expect(series?.[1]?.stack).toBeUndefined()
|
|
344
|
+
})
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
test('preserves other option properties via config pipeline', async () => {
|
|
242
348
|
useWidgetStore.getState().setWidget(widgetId, {
|
|
243
349
|
option: {
|
|
244
350
|
title: { text: 'My Chart' },
|
|
@@ -254,6 +360,17 @@ describe('StackToggle', () => {
|
|
|
254
360
|
const button = screen.getByRole('button')
|
|
255
361
|
fireEvent.click(button)
|
|
256
362
|
|
|
363
|
+
await useWidgetStore.getState().executeConfigPipeline(widgetId, {
|
|
364
|
+
option: {
|
|
365
|
+
title: { text: 'My Chart' },
|
|
366
|
+
xAxis: { type: 'category' },
|
|
367
|
+
series: [
|
|
368
|
+
{ name: 'Series 1', type: 'bar' },
|
|
369
|
+
{ name: 'Series 2', type: 'bar' },
|
|
370
|
+
],
|
|
371
|
+
},
|
|
372
|
+
})
|
|
373
|
+
|
|
257
374
|
const widget = useWidgetStore.getState().getWidget(widgetId)
|
|
258
375
|
const option = (
|
|
259
376
|
widget as {
|
|
@@ -267,4 +384,21 @@ describe('StackToggle', () => {
|
|
|
267
384
|
expect(option?.title?.text).toBe('My Chart')
|
|
268
385
|
expect(option?.xAxis?.type).toBe('category')
|
|
269
386
|
})
|
|
387
|
+
|
|
388
|
+
test('registers config tool on mount and unregisters on unmount', () => {
|
|
389
|
+
const { unmount } = render(<StackToggle id={widgetId} />)
|
|
390
|
+
|
|
391
|
+
const widget = useWidgetStore.getState().getWidget(widgetId)
|
|
392
|
+
const tools = widget?.registeredTools ?? []
|
|
393
|
+
const stackTool = tools.find((t) => t.id === STACK_TOGGLE_TOOL_ID)
|
|
394
|
+
expect(stackTool).toBeDefined()
|
|
395
|
+
expect(stackTool?.type).toBe('config')
|
|
396
|
+
|
|
397
|
+
unmount()
|
|
398
|
+
|
|
399
|
+
const widgetAfter = useWidgetStore.getState().getWidget(widgetId)
|
|
400
|
+
const toolsAfter = widgetAfter?.registeredTools ?? []
|
|
401
|
+
const stackToolAfter = toolsAfter.find((t) => t.id === STACK_TOGGLE_TOOL_ID)
|
|
402
|
+
expect(stackToolAfter).toBeUndefined()
|
|
403
|
+
})
|
|
270
404
|
})
|
|
@@ -8,13 +8,16 @@ import { GroupedBarChartIcon } from './grouped-bar-chart-icon'
|
|
|
8
8
|
import { getEChartStackConfig } from '../../echart/utils'
|
|
9
9
|
import { DEFAULT_STACK_GROUP } from '../../echart/const'
|
|
10
10
|
import type { EchartWidgetState } from '../../echart/types'
|
|
11
|
+
import type { EchartOptionsProps } from '../../echart/types'
|
|
11
12
|
import { useShallow } from 'zustand/shallow'
|
|
12
13
|
|
|
14
|
+
export const STACK_TOGGLE_TOOL_ID = 'stack-toggle'
|
|
15
|
+
|
|
13
16
|
/**
|
|
14
17
|
* Widget action to toggle stacking behavior in ECharts bar and histogram widgets.
|
|
15
18
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
19
|
+
* Registers as a config pipeline tool so that stack configuration is automatically
|
|
20
|
+
* re-applied when the base config is updated (e.g., by WidgetLoader).
|
|
18
21
|
*
|
|
19
22
|
* @example
|
|
20
23
|
* ```tsx
|
|
@@ -32,93 +35,81 @@ export function StackToggle({
|
|
|
32
35
|
IconButtonProps,
|
|
33
36
|
}: StackToggleProps) {
|
|
34
37
|
const setWidget = useWidgetStore((state) => state.setWidget)
|
|
38
|
+
const registerTool = useWidgetStore((state) => state.registerTool)
|
|
39
|
+
const unregisterTool = useWidgetStore((state) => state.unregisterTool)
|
|
40
|
+
const setToolEnabled = useWidgetStore((state) => state.setToolEnabled)
|
|
41
|
+
const updateToolConfig = useWidgetStore((state) => state.updateToolConfig)
|
|
42
|
+
|
|
35
43
|
const storeIsStacked = useWidgetStore(
|
|
36
44
|
useShallow((state) => state.getWidget<StackToggleState>(id)?.isStacked),
|
|
37
45
|
)
|
|
38
46
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* Checks if there are multiple series in the chart
|
|
43
|
-
*/
|
|
44
|
-
const hasMultiSeries = useMemo(() => {
|
|
45
|
-
const option = getWidget<EchartWidgetState>(id)?.option
|
|
46
|
-
if (!option) return false
|
|
47
|
-
|
|
48
|
-
const series = Array.isArray(option.series)
|
|
49
|
-
? option.series
|
|
50
|
-
: [option.series]
|
|
51
|
-
|
|
52
|
-
return series.length > 1
|
|
53
|
-
}, [getWidget, id])
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Checks if any series in the option has a stack property defined
|
|
57
|
-
*/
|
|
58
|
-
const hasStackInSeries = useMemo(() => {
|
|
59
|
-
const option = getWidget<EchartWidgetState>(id)?.option
|
|
60
|
-
if (!option) return false
|
|
47
|
+
const option = useWidgetStore(
|
|
48
|
+
useShallow((state) => state.getWidget<EchartWidgetState>(id)?.option),
|
|
49
|
+
)
|
|
61
50
|
|
|
51
|
+
const { hasMultiSeries, hasStackInSeries } = useMemo(() => {
|
|
52
|
+
if (!option) return { hasMultiSeries: false, hasStackInSeries: false }
|
|
62
53
|
const series = Array.isArray(option.series)
|
|
63
54
|
? option.series
|
|
64
55
|
: [option.series]
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
return {
|
|
57
|
+
hasMultiSeries: series.length > 1,
|
|
58
|
+
hasStackInSeries: series.some((s) => (s as { stack?: string })?.stack),
|
|
59
|
+
}
|
|
60
|
+
}, [option])
|
|
68
61
|
|
|
69
62
|
// If series already has stack defined, default to stacked=true
|
|
70
63
|
const effectiveDefaultIsStacked = hasStackInSeries || defaultIsStacked
|
|
71
64
|
const isStacked = storeIsStacked ?? effectiveDefaultIsStacked
|
|
72
65
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
)
|
|
66
|
+
// Register config tool on mount
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
registerTool(id, {
|
|
69
|
+
id: STACK_TOGGLE_TOOL_ID,
|
|
70
|
+
type: 'config',
|
|
71
|
+
order: 10,
|
|
72
|
+
enabled: isStacked && hasMultiSeries,
|
|
73
|
+
fn: (currentConfig, toolConfig) => {
|
|
74
|
+
const config = currentConfig as Record<string, unknown>
|
|
75
|
+
const option = config.option as EchartOptionsProps | undefined
|
|
76
|
+
if (!option) return currentConfig
|
|
77
|
+
|
|
78
|
+
const stacked = (toolConfig?.stacked as boolean) ?? false
|
|
79
|
+
const series = Array.isArray(option.series)
|
|
80
|
+
? option.series
|
|
81
|
+
: [option.series]
|
|
82
|
+
const updatedSeries = series.map((s) => {
|
|
83
|
+
const existingStack = (s as { stack?: string })?.stack
|
|
84
|
+
const stackGroup =
|
|
85
|
+
typeof existingStack === 'string'
|
|
86
|
+
? existingStack
|
|
87
|
+
: DEFAULT_STACK_GROUP
|
|
88
|
+
return { ...s, ...getEChartStackConfig(stacked, stackGroup) }
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
return { ...config, option: { ...option, series: updatedSeries } }
|
|
92
|
+
},
|
|
93
|
+
config: { stacked: isStacked },
|
|
94
|
+
})
|
|
95
|
+
return () => unregisterTool(id, STACK_TOGGLE_TOOL_ID)
|
|
96
|
+
}, [id, registerTool, unregisterTool, isStacked, hasMultiSeries])
|
|
97
|
+
|
|
98
|
+
// Sync tool enabled/config when state changes
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
setToolEnabled(id, STACK_TOGGLE_TOOL_ID, isStacked && hasMultiSeries)
|
|
101
|
+
updateToolConfig(id, STACK_TOGGLE_TOOL_ID, { stacked: isStacked })
|
|
102
|
+
}, [id, isStacked, hasMultiSeries, setToolEnabled, updateToolConfig])
|
|
110
103
|
|
|
111
104
|
// Initialize store with default value only if not already configured
|
|
112
105
|
useEffect(() => {
|
|
106
|
+
if (storeIsStacked !== undefined) return
|
|
113
107
|
setWidget(id, { isStacked: effectiveDefaultIsStacked })
|
|
114
|
-
}, [effectiveDefaultIsStacked, id, setWidget])
|
|
108
|
+
}, [effectiveDefaultIsStacked, id, setWidget, storeIsStacked])
|
|
115
109
|
|
|
116
110
|
const handleToggle = useCallback(() => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
setWidget(id, { isStacked: newIsStacked })
|
|
120
|
-
updateOptions(newIsStacked)
|
|
121
|
-
}, [isStacked, id, setWidget, updateOptions])
|
|
111
|
+
setWidget(id, { isStacked: !isStacked })
|
|
112
|
+
}, [isStacked, id, setWidget])
|
|
122
113
|
|
|
123
114
|
const tooltipLabel = isStacked
|
|
124
115
|
? (labels?.unstacked ?? 'Disable stacking')
|