@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
|
@@ -10,11 +10,17 @@ import { styles } from './style'
|
|
|
10
10
|
import { Tooltip } from '../../../components'
|
|
11
11
|
import { getEChartZoomConfig } from '../../echart/utils'
|
|
12
12
|
import type { EchartWidgetState } from '../../echart/types'
|
|
13
|
+
import type { EchartOptionsProps } from '../../echart/types'
|
|
13
14
|
import { useShallow } from 'zustand/shallow'
|
|
14
15
|
|
|
16
|
+
export const ZOOM_TOGGLE_TOOL_ID = 'zoom-toggle'
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* Widget action to toggle EChart zoom functionality.
|
|
17
20
|
*
|
|
21
|
+
* Registers as a config pipeline tool so that zoom configuration is automatically
|
|
22
|
+
* re-applied when the base config is updated (e.g., by WidgetLoader).
|
|
23
|
+
*
|
|
18
24
|
* When zoom is active, displays an inline reset button to disable zoom.
|
|
19
25
|
* Only intended for use in fullscreen ToolbarActions for bar and histogram widgets.
|
|
20
26
|
*
|
|
@@ -41,6 +47,10 @@ export function ZoomToggle({
|
|
|
41
47
|
const theme = useTheme()
|
|
42
48
|
const setWidget = useWidgetStore((state) => state.setWidget)
|
|
43
49
|
const getWidget = useWidgetStore((state) => state.getWidget)
|
|
50
|
+
const registerTool = useWidgetStore((state) => state.registerTool)
|
|
51
|
+
const unregisterTool = useWidgetStore((state) => state.unregisterTool)
|
|
52
|
+
const setToolEnabled = useWidgetStore((state) => state.setToolEnabled)
|
|
53
|
+
const updateToolConfig = useWidgetStore((state) => state.updateToolConfig)
|
|
44
54
|
|
|
45
55
|
const zoom = useWidgetStore(
|
|
46
56
|
useShallow((state) => {
|
|
@@ -49,76 +59,94 @@ export function ZoomToggle({
|
|
|
49
59
|
}),
|
|
50
60
|
)
|
|
51
61
|
|
|
52
|
-
const
|
|
53
|
-
(
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
zoomStart: start,
|
|
65
|
-
zoomEnd: end,
|
|
66
|
-
option: {
|
|
67
|
-
...currentOption,
|
|
68
|
-
...zoomConfig,
|
|
69
|
-
},
|
|
70
|
-
})
|
|
71
|
-
},
|
|
72
|
-
[getWidget, id, setWidget, theme],
|
|
62
|
+
const zoomStart = useWidgetStore(
|
|
63
|
+
useShallow((state) => {
|
|
64
|
+
const widget = state.getWidget<ZoomState>(id)
|
|
65
|
+
return widget?.zoomStart ?? defaultZoomStart
|
|
66
|
+
}),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
const zoomEnd = useWidgetStore(
|
|
70
|
+
useShallow((state) => {
|
|
71
|
+
const widget = state.getWidget<ZoomState>(id)
|
|
72
|
+
return widget?.zoomEnd ?? defaultZoomEnd
|
|
73
|
+
}),
|
|
73
74
|
)
|
|
74
75
|
|
|
76
|
+
// Register config tool on mount
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
registerTool(id, {
|
|
79
|
+
id: ZOOM_TOGGLE_TOOL_ID,
|
|
80
|
+
type: 'config',
|
|
81
|
+
order: 20,
|
|
82
|
+
enabled: zoom,
|
|
83
|
+
fn: (currentConfig, toolConfig) => {
|
|
84
|
+
const config = currentConfig as Record<string, unknown>
|
|
85
|
+
const option = config.option as EchartOptionsProps | undefined
|
|
86
|
+
const enabled = (toolConfig?.enabled as boolean) ?? false
|
|
87
|
+
const start = (toolConfig?.start as number) ?? 0
|
|
88
|
+
const end = (toolConfig?.end as number) ?? 100
|
|
89
|
+
|
|
90
|
+
const zoomConfig = getEChartZoomConfig(
|
|
91
|
+
enabled,
|
|
92
|
+
{ start, end },
|
|
93
|
+
{ inside: true, xSlider: true, ySlider: false },
|
|
94
|
+
theme,
|
|
95
|
+
)
|
|
96
|
+
return { ...config, option: { ...option, ...zoomConfig } }
|
|
97
|
+
},
|
|
98
|
+
config: { enabled: zoom, start: zoomStart, end: zoomEnd },
|
|
99
|
+
})
|
|
100
|
+
return () => unregisterTool(id, ZOOM_TOGGLE_TOOL_ID)
|
|
101
|
+
}, [id, registerTool, unregisterTool, zoom, theme, zoomStart, zoomEnd])
|
|
102
|
+
|
|
103
|
+
// Sync tool enabled/config when state changes
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
setToolEnabled(id, ZOOM_TOGGLE_TOOL_ID, zoom)
|
|
106
|
+
updateToolConfig(id, ZOOM_TOGGLE_TOOL_ID, {
|
|
107
|
+
enabled: zoom,
|
|
108
|
+
start: zoomStart,
|
|
109
|
+
end: zoomEnd,
|
|
110
|
+
})
|
|
111
|
+
}, [id, zoom, zoomStart, zoomEnd, setToolEnabled, updateToolConfig])
|
|
112
|
+
|
|
75
113
|
// Initialize zoom state from defaults only if not already set
|
|
76
114
|
useEffect(() => {
|
|
77
115
|
const existingState = getWidget<ZoomState>(id)
|
|
78
116
|
|
|
79
117
|
if (existingState?.zoom) return
|
|
80
118
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
existingState?.
|
|
84
|
-
existingState?.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
defaultZoomEnd,
|
|
90
|
-
defaultZoomStart,
|
|
91
|
-
getWidget,
|
|
92
|
-
id,
|
|
93
|
-
updateZoomOption,
|
|
94
|
-
])
|
|
95
|
-
|
|
96
|
-
// Cleanup: reset zoom when component unmounts
|
|
119
|
+
setWidget<ZoomState>(id, {
|
|
120
|
+
zoom: existingState?.zoom ?? defaultZoom,
|
|
121
|
+
zoomStart: existingState?.zoomStart ?? defaultZoomStart,
|
|
122
|
+
zoomEnd: existingState?.zoomEnd ?? defaultZoomEnd,
|
|
123
|
+
})
|
|
124
|
+
}, [defaultZoom, defaultZoomEnd, defaultZoomStart, getWidget, id, setWidget])
|
|
125
|
+
|
|
126
|
+
// Cleanup: disable zoom when component unmounts
|
|
97
127
|
useEffect(() => {
|
|
98
128
|
return () => {
|
|
99
|
-
|
|
100
|
-
updateZoomOption(
|
|
101
|
-
false,
|
|
102
|
-
existingState?.zoomStart ?? 0,
|
|
103
|
-
existingState?.zoomEnd ?? 100,
|
|
104
|
-
)
|
|
129
|
+
setWidget<ZoomState>(id, { zoom: false })
|
|
105
130
|
}
|
|
106
|
-
}, [
|
|
131
|
+
}, [id, setWidget])
|
|
107
132
|
|
|
108
133
|
const handleToggle = () => {
|
|
109
134
|
const newZoom = !zoom
|
|
110
|
-
|
|
111
135
|
const existingState = getWidget<ZoomState>(id)
|
|
112
136
|
|
|
113
|
-
|
|
114
|
-
newZoom,
|
|
115
|
-
newZoom ? (existingState?.zoomStart ?? defaultZoomStart) : 0,
|
|
116
|
-
newZoom ? (existingState?.zoomEnd ?? defaultZoomEnd) : 100,
|
|
117
|
-
)
|
|
137
|
+
setWidget<ZoomState>(id, {
|
|
138
|
+
zoom: newZoom,
|
|
139
|
+
zoomStart: newZoom ? (existingState?.zoomStart ?? defaultZoomStart) : 0,
|
|
140
|
+
zoomEnd: newZoom ? (existingState?.zoomEnd ?? defaultZoomEnd) : 100,
|
|
141
|
+
})
|
|
118
142
|
}
|
|
119
143
|
|
|
120
144
|
const handleReset = () => {
|
|
121
|
-
|
|
145
|
+
setWidget<ZoomState>(id, {
|
|
146
|
+
zoom: true,
|
|
147
|
+
zoomStart: defaultZoomStart,
|
|
148
|
+
zoomEnd: defaultZoomEnd,
|
|
149
|
+
})
|
|
122
150
|
}
|
|
123
151
|
|
|
124
152
|
// Handle dataZoom event to update zoom range in store
|
|
@@ -133,10 +161,14 @@ export function ZoomToggle({
|
|
|
133
161
|
const end = zoomEvent.end
|
|
134
162
|
|
|
135
163
|
if (start !== undefined && end !== undefined) {
|
|
136
|
-
|
|
164
|
+
setWidget<ZoomState>(id, {
|
|
165
|
+
zoom: true,
|
|
166
|
+
zoomStart: start,
|
|
167
|
+
zoomEnd: end,
|
|
168
|
+
})
|
|
137
169
|
}
|
|
138
170
|
},
|
|
139
|
-
[
|
|
171
|
+
[id, setWidget],
|
|
140
172
|
)
|
|
141
173
|
|
|
142
174
|
// Register dataZoom event handler when zoom is enabled
|
|
@@ -175,6 +175,7 @@ describe('EchartUI', () => {
|
|
|
175
175
|
|
|
176
176
|
expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
|
|
177
177
|
lazyUpdate: true,
|
|
178
|
+
replaceMerge: ['dataset', 'series'],
|
|
178
179
|
})
|
|
179
180
|
})
|
|
180
181
|
|
|
@@ -199,6 +200,22 @@ describe('EchartUI', () => {
|
|
|
199
200
|
|
|
200
201
|
expect(mockChart.setOption).toHaveBeenCalledWith(newOption, {
|
|
201
202
|
lazyUpdate: true,
|
|
203
|
+
replaceMerge: ['dataset', 'series'],
|
|
204
|
+
})
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test('uses provided replaceMerge values', () => {
|
|
208
|
+
render(
|
|
209
|
+
<EchartUI
|
|
210
|
+
{...defaultProps}
|
|
211
|
+
option={basicOption}
|
|
212
|
+
replaceMerge={['series']}
|
|
213
|
+
/>,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
|
|
217
|
+
lazyUpdate: true,
|
|
218
|
+
replaceMerge: ['series'],
|
|
202
219
|
})
|
|
203
220
|
})
|
|
204
221
|
|
|
@@ -479,6 +496,7 @@ describe('EchartUI', () => {
|
|
|
479
496
|
|
|
480
497
|
expect(mockChart.setOption).toHaveBeenCalledWith(complexOption, {
|
|
481
498
|
lazyUpdate: true,
|
|
499
|
+
replaceMerge: ['dataset', 'series'],
|
|
482
500
|
})
|
|
483
501
|
})
|
|
484
502
|
|
|
@@ -3,15 +3,14 @@ import * as echarts from 'echarts'
|
|
|
3
3
|
import type { EchartUIProps } from './types'
|
|
4
4
|
import { useWidgetRef } from '../../hooks'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
init,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}: EchartUIProps) {
|
|
6
|
+
const DEFAULT_REPLACE_MERGE: string[] = ['dataset', 'series']
|
|
7
|
+
|
|
8
|
+
export function EchartUI(props: EchartUIProps) {
|
|
9
|
+
const { id, ref, init, option, className, style, onEvents } = props
|
|
10
|
+
const replaceMerge = toReplaceMerge(
|
|
11
|
+
(props as { replaceMerge?: unknown }).replaceMerge,
|
|
12
|
+
)
|
|
13
|
+
|
|
15
14
|
const chartRef = useWidgetRef<HTMLDivElement>(id)
|
|
16
15
|
const chartInstance = useRef<echarts.ECharts>(null)
|
|
17
16
|
const resizeObserverRef = useRef<ResizeObserver | null>(null)
|
|
@@ -35,11 +34,16 @@ export function EchartUI({
|
|
|
35
34
|
|
|
36
35
|
// Update chart when options change
|
|
37
36
|
useEffect(() => {
|
|
38
|
-
|
|
37
|
+
const setOptionConfig = {
|
|
39
38
|
lazyUpdate: true,
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
39
|
+
replaceMerge: replaceMerge ?? DEFAULT_REPLACE_MERGE,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
chartInstance.current?.setOption(
|
|
43
|
+
option,
|
|
44
|
+
setOptionConfig as Parameters<echarts.ECharts['setOption']>[1],
|
|
45
|
+
)
|
|
46
|
+
}, [option, replaceMerge])
|
|
43
47
|
|
|
44
48
|
// Handle resize using ResizeObserver
|
|
45
49
|
useEffect(() => {
|
|
@@ -78,3 +82,11 @@ export function EchartUI({
|
|
|
78
82
|
|
|
79
83
|
return <div id={id} ref={chartRef} style={style} className={className} />
|
|
80
84
|
}
|
|
85
|
+
|
|
86
|
+
function toReplaceMerge(value: unknown): string[] | undefined {
|
|
87
|
+
if (!Array.isArray(value)) {
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return value.filter((item): item is string => typeof item === 'string')
|
|
92
|
+
}
|
|
@@ -184,6 +184,7 @@ describe('Echart', () => {
|
|
|
184
184
|
}),
|
|
185
185
|
{
|
|
186
186
|
lazyUpdate: true,
|
|
187
|
+
replaceMerge: ['dataset', 'series'],
|
|
187
188
|
},
|
|
188
189
|
)
|
|
189
190
|
})
|
|
@@ -276,6 +277,7 @@ describe('Echart', () => {
|
|
|
276
277
|
|
|
277
278
|
expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
|
|
278
279
|
lazyUpdate: true,
|
|
280
|
+
replaceMerge: ['dataset', 'series'],
|
|
279
281
|
})
|
|
280
282
|
|
|
281
283
|
const newOption: EChartsOption = {
|
|
@@ -297,6 +299,7 @@ describe('Echart', () => {
|
|
|
297
299
|
|
|
298
300
|
expect(mockChart.setOption).toHaveBeenCalledWith(newOption, {
|
|
299
301
|
lazyUpdate: true,
|
|
302
|
+
replaceMerge: ['dataset', 'series'],
|
|
300
303
|
})
|
|
301
304
|
})
|
|
302
305
|
|
|
@@ -364,6 +367,7 @@ describe('Echart', () => {
|
|
|
364
367
|
|
|
365
368
|
expect(mockChart.setOption).toHaveBeenCalledWith(complexOption, {
|
|
366
369
|
lazyUpdate: true,
|
|
370
|
+
replaceMerge: ['dataset', 'series'],
|
|
367
371
|
})
|
|
368
372
|
})
|
|
369
373
|
|
|
@@ -400,6 +404,7 @@ describe('Echart', () => {
|
|
|
400
404
|
)
|
|
401
405
|
expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
|
|
402
406
|
lazyUpdate: true,
|
|
407
|
+
replaceMerge: ['dataset', 'series'],
|
|
403
408
|
})
|
|
404
409
|
expect(mockChart.on).toHaveBeenCalledWith('click', clickHandler)
|
|
405
410
|
})
|
|
@@ -442,6 +447,7 @@ describe('Echart', () => {
|
|
|
442
447
|
|
|
443
448
|
expect(mockChart.setOption).toHaveBeenCalledWith(emptyOption, {
|
|
444
449
|
lazyUpdate: true,
|
|
450
|
+
replaceMerge: ['dataset', 'series'],
|
|
445
451
|
})
|
|
446
452
|
})
|
|
447
453
|
|
|
@@ -464,6 +470,7 @@ describe('Echart', () => {
|
|
|
464
470
|
}),
|
|
465
471
|
{
|
|
466
472
|
lazyUpdate: true,
|
|
473
|
+
replaceMerge: ['dataset', 'series'],
|
|
467
474
|
},
|
|
468
475
|
)
|
|
469
476
|
})
|
|
@@ -509,6 +516,7 @@ describe('Echart', () => {
|
|
|
509
516
|
|
|
510
517
|
expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
|
|
511
518
|
lazyUpdate: true,
|
|
519
|
+
replaceMerge: ['dataset', 'series'],
|
|
512
520
|
})
|
|
513
521
|
|
|
514
522
|
rerender(<Echart id='chart-b' />)
|
|
@@ -517,10 +525,27 @@ describe('Echart', () => {
|
|
|
517
525
|
{ series: [{ type: 'line', data: [1, 2, 3] }] },
|
|
518
526
|
{
|
|
519
527
|
lazyUpdate: true,
|
|
528
|
+
replaceMerge: ['dataset', 'series'],
|
|
520
529
|
},
|
|
521
530
|
)
|
|
522
531
|
})
|
|
523
532
|
|
|
533
|
+
test('passes replaceMerge from widget to EchartUI', () => {
|
|
534
|
+
useWidgetStore.getState().setWidget('test-echart', {
|
|
535
|
+
id: 'test-echart',
|
|
536
|
+
type: 'echart',
|
|
537
|
+
option: basicOption,
|
|
538
|
+
replaceMerge: ['series'],
|
|
539
|
+
} as EchartWidgetState)
|
|
540
|
+
|
|
541
|
+
render(<Echart id='test-echart' />)
|
|
542
|
+
|
|
543
|
+
expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
|
|
544
|
+
lazyUpdate: true,
|
|
545
|
+
replaceMerge: ['series'],
|
|
546
|
+
})
|
|
547
|
+
})
|
|
548
|
+
|
|
524
549
|
test('disposes chart when component unmounts', () => {
|
|
525
550
|
useWidgetStore.getState().setWidget('test-echart', {
|
|
526
551
|
id: 'test-echart',
|
|
@@ -19,6 +19,7 @@ export function Echart(props: EchartProps) {
|
|
|
19
19
|
option: widget?.option,
|
|
20
20
|
onEvents: widget?.onEvents,
|
|
21
21
|
init: widget?.init,
|
|
22
|
+
replaceMerge: widget?.replaceMerge,
|
|
22
23
|
}
|
|
23
24
|
}),
|
|
24
25
|
)
|
|
@@ -36,9 +37,16 @@ export function Echart(props: EchartProps) {
|
|
|
36
37
|
|
|
37
38
|
const onEvents = widget.onEvents
|
|
38
39
|
const init = widget.init
|
|
40
|
+
const replaceMerge = widget.replaceMerge
|
|
39
41
|
|
|
40
42
|
return (
|
|
41
|
-
<EchartUI
|
|
43
|
+
<EchartUI
|
|
44
|
+
id={props.id}
|
|
45
|
+
option={option}
|
|
46
|
+
onEvents={onEvents}
|
|
47
|
+
init={init}
|
|
48
|
+
replaceMerge={replaceMerge}
|
|
49
|
+
/>
|
|
42
50
|
)
|
|
43
51
|
}
|
|
44
52
|
|
|
@@ -5,12 +5,14 @@ import type { Ref } from 'react'
|
|
|
5
5
|
import { theme as CartoTheme } from '@carto/meridian-ds/theme'
|
|
6
6
|
|
|
7
7
|
export type EchartOptionsProps = EChartsOption
|
|
8
|
+
export type EchartReplaceMerge = string[]
|
|
8
9
|
|
|
9
10
|
export interface EchartUIProps {
|
|
10
11
|
id: string
|
|
11
12
|
option: EchartOptionsProps
|
|
12
13
|
className?: string
|
|
13
14
|
init?: echarts.EChartsInitOpts
|
|
15
|
+
replaceMerge?: EchartReplaceMerge
|
|
14
16
|
style?: React.CSSProperties
|
|
15
17
|
ref?: Ref<echarts.ECharts>
|
|
16
18
|
onEvents?: Record<string, Parameters<echarts.ECharts['on']>[2]>
|
|
@@ -26,6 +28,7 @@ export type EchartWidgetState = BaseWidgetState<{
|
|
|
26
28
|
option: EchartUIProps['option']
|
|
27
29
|
onEvents?: EchartUIProps['onEvents']
|
|
28
30
|
init?: EchartUIProps['init']
|
|
31
|
+
replaceMerge?: EchartReplaceMerge
|
|
29
32
|
}>
|
|
30
33
|
|
|
31
34
|
export interface EchartWidgetOptionProps<D> {
|
|
@@ -38,4 +41,5 @@ export interface EchartWidgetProps {
|
|
|
38
41
|
type: string
|
|
39
42
|
option: EchartUIProps['option']
|
|
40
43
|
onEvents?: EchartUIProps['onEvents']
|
|
44
|
+
replaceMerge?: EchartReplaceMerge
|
|
41
45
|
}
|
|
@@ -3,11 +3,14 @@ import type { WidgetLoaderProps } from './types'
|
|
|
3
3
|
import { useWidgetStore } from '../stores/widget-store'
|
|
4
4
|
import type { WrapperState } from '../wrapper'
|
|
5
5
|
|
|
6
|
-
export function WidgetLoader<T>(props: WidgetLoaderProps<T>) {
|
|
6
|
+
export function WidgetLoader<T extends Record<string, unknown> = Record<string, unknown>>(props: WidgetLoaderProps<T>) {
|
|
7
7
|
const setWidget = useWidgetStore((state) => state.setWidget)
|
|
8
8
|
const executeToolPipeline = useWidgetStore(
|
|
9
9
|
(state) => state.executeToolPipeline,
|
|
10
10
|
)
|
|
11
|
+
const executeConfigPipeline = useWidgetStore(
|
|
12
|
+
(state) => state.executeConfigPipeline,
|
|
13
|
+
)
|
|
11
14
|
|
|
12
15
|
// Split into 3 effects for metadata and 1 for data pipeline:
|
|
13
16
|
// Each property that can be modified independently gets its own effect to avoid
|
|
@@ -35,21 +38,19 @@ export function WidgetLoader<T>(props: WidgetLoaderProps<T>) {
|
|
|
35
38
|
})
|
|
36
39
|
}, [props.id, props.isLoading, props.isFetching, props.error, setWidget])
|
|
37
40
|
|
|
38
|
-
// Effect 3: Config updates
|
|
41
|
+
// Effect 3: Config updates — run through config pipeline
|
|
39
42
|
useEffect(() => {
|
|
40
43
|
if (props.config) {
|
|
41
|
-
|
|
42
|
-
...props.config,
|
|
43
|
-
})
|
|
44
|
+
void executeConfigPipeline(props.id, props.config)
|
|
44
45
|
}
|
|
45
|
-
}, [props.id, props.config,
|
|
46
|
+
}, [props.id, props.config, executeConfigPipeline])
|
|
46
47
|
|
|
47
48
|
// Effect 4: Execute tool pipeline when props.data changes
|
|
48
49
|
useEffect(() => {
|
|
49
50
|
void executeToolPipeline(props.id, props.data)
|
|
50
51
|
}, [props.id, props.data, executeToolPipeline])
|
|
51
52
|
|
|
52
|
-
// Effect 5: Re-execute
|
|
53
|
+
// Effect 5: Re-execute pipelines when tool state changes (enabled/config)
|
|
53
54
|
useEffect(() => {
|
|
54
55
|
let prevTools = useWidgetStore.getState().widgets[props.id]?.registeredTools
|
|
55
56
|
|
|
@@ -60,11 +61,20 @@ export function WidgetLoader<T>(props: WidgetLoaderProps<T>) {
|
|
|
60
61
|
if (currentTools !== prevTools) {
|
|
61
62
|
prevTools = currentTools
|
|
62
63
|
void executeToolPipeline(props.id, props.data)
|
|
64
|
+
if (props.config) {
|
|
65
|
+
void executeConfigPipeline(props.id, props.config)
|
|
66
|
+
}
|
|
63
67
|
}
|
|
64
68
|
})
|
|
65
69
|
|
|
66
70
|
return unsubscribe
|
|
67
|
-
}, [
|
|
71
|
+
}, [
|
|
72
|
+
props.id,
|
|
73
|
+
props.data,
|
|
74
|
+
props.config,
|
|
75
|
+
executeToolPipeline,
|
|
76
|
+
executeConfigPipeline,
|
|
77
|
+
])
|
|
68
78
|
|
|
69
79
|
return props.children
|
|
70
80
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
2
|
import type { WidgetsStoreProps, WidgetState } from '../stores/types'
|
|
3
3
|
|
|
4
|
-
export interface WidgetLoaderProps<T> extends WidgetsStoreProps {
|
|
4
|
+
export interface WidgetLoaderProps<T extends Record<string, unknown> = Record<string, unknown>> extends WidgetsStoreProps {
|
|
5
5
|
children: ReactNode
|
|
6
6
|
config?: T
|
|
7
7
|
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { RefObject } from 'react'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Tool type determines which pipeline a tool participates in.
|
|
5
|
+
* - 'data': transforms widget data (default)
|
|
6
|
+
* - 'config': transforms widget config/option
|
|
7
|
+
*/
|
|
8
|
+
export type ToolType = 'data' | 'config'
|
|
9
|
+
|
|
3
10
|
export interface WidgetsStoreProps {
|
|
4
11
|
/** Unique identifier for the widget */
|
|
5
12
|
id: string
|
|
@@ -77,6 +84,8 @@ export interface ToolRegistration {
|
|
|
77
84
|
fn: ToolTransformFunction
|
|
78
85
|
/** Whether tool is currently enabled */
|
|
79
86
|
enabled: boolean
|
|
87
|
+
/** 'data' (default) transforms data, 'config' transforms widget config/option */
|
|
88
|
+
type?: ToolType
|
|
80
89
|
/** Tool-specific configuration */
|
|
81
90
|
config?: Record<string, unknown>
|
|
82
91
|
/**
|
|
@@ -184,6 +193,17 @@ export interface WidgetStoreActions {
|
|
|
184
193
|
* @param sourceData - Original data to transform
|
|
185
194
|
*/
|
|
186
195
|
executeToolPipeline: (widgetId: string, sourceData: unknown) => Promise<void>
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Execute the config transformation pipeline
|
|
199
|
+
* Applies config-type tools to the base config, then sets the result on the widget
|
|
200
|
+
* @param widgetId - Widget ID
|
|
201
|
+
* @param baseConfig - Base config to transform
|
|
202
|
+
*/
|
|
203
|
+
executeConfigPipeline: (
|
|
204
|
+
widgetId: string,
|
|
205
|
+
baseConfig: Record<string, unknown>,
|
|
206
|
+
) => Promise<void>
|
|
187
207
|
}
|
|
188
208
|
|
|
189
209
|
/**
|