@carto/ps-react-ui 4.3.5 → 4.3.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.
Files changed (125) hide show
  1. package/dist/components.js +123 -123
  2. package/dist/components.js.map +1 -1
  3. package/dist/error-CEkRPccv.js +39 -0
  4. package/dist/error-CEkRPccv.js.map +1 -0
  5. package/dist/{lasso-tool-wFqOD6wk.js → lasso-tool-jl4YK02H.js} +184 -159
  6. package/dist/lasso-tool-jl4YK02H.js.map +1 -0
  7. package/dist/no-data-hR3KcJ-_.js +60 -0
  8. package/dist/no-data-hR3KcJ-_.js.map +1 -0
  9. package/dist/{row-DrHwXNvF.js → row-BKmVAUN5.js} +2 -2
  10. package/dist/{row-DrHwXNvF.js.map → row-BKmVAUN5.js.map} +1 -1
  11. package/dist/{series-D3Pc-kYX.js → series-D1pynfeh.js} +3 -3
  12. package/dist/{series-D3Pc-kYX.js.map → series-D1pynfeh.js.map} +1 -1
  13. package/dist/{styles-CCZnY17y.js → styles-DrPyd0y5.js} +28 -22
  14. package/dist/styles-DrPyd0y5.js.map +1 -0
  15. package/dist/types/components/lasso-tool/types.d.ts +1 -1
  16. package/dist/types/widgets/_shared/chart-config/index.d.ts +1 -1
  17. package/dist/types/widgets/_shared/chart-config/option-builders.d.ts +7 -0
  18. package/dist/types/widgets/_shared/chart-config/option-builders.test.d.ts +1 -0
  19. package/dist/types/widgets/actions/index.d.ts +4 -4
  20. package/dist/types/widgets/actions/lock-selection/types.d.ts +0 -13
  21. package/dist/types/widgets/actions/relative-data/types.d.ts +0 -4
  22. package/dist/types/widgets/actions/searcher/types.d.ts +0 -2
  23. package/dist/types/widgets/actions/stack-toggle/stack-toggle.d.ts +3 -2
  24. package/dist/types/widgets/actions/stack-toggle/types.d.ts +0 -4
  25. package/dist/types/widgets/actions/zoom-toggle/zoom-toggle.d.ts +4 -0
  26. package/dist/types/widgets/echart/types.d.ts +0 -4
  27. package/dist/types/widgets/echart/utils.d.ts +2 -1
  28. package/dist/types/widgets/error/error.d.ts +1 -1
  29. package/dist/types/widgets/error/types.d.ts +8 -0
  30. package/dist/types/widgets/loader/loader.d.ts +1 -1
  31. package/dist/types/widgets/loader/types.d.ts +1 -1
  32. package/dist/types/widgets/stores/index.d.ts +1 -1
  33. package/dist/types/widgets/stores/types.d.ts +15 -0
  34. package/dist/{use-widget-ref-B0aNCANx.js → use-widget-ref-P-2i0MJG.js} +2 -2
  35. package/dist/{use-widget-ref-B0aNCANx.js.map → use-widget-ref-P-2i0MJG.js.map} +1 -1
  36. package/dist/{utils-D3-eQyDR.js → utils-idmvq0Oa.js} +17 -16
  37. package/dist/utils-idmvq0Oa.js.map +1 -0
  38. package/dist/widget-store-CzDt8oSK.js +163 -0
  39. package/dist/widget-store-CzDt8oSK.js.map +1 -0
  40. package/dist/widgets/actions.js +714 -659
  41. package/dist/widgets/actions.js.map +1 -1
  42. package/dist/widgets/bar.js +67 -63
  43. package/dist/widgets/bar.js.map +1 -1
  44. package/dist/widgets/category.js +250 -241
  45. package/dist/widgets/category.js.map +1 -1
  46. package/dist/widgets/echart.js +93 -100
  47. package/dist/widgets/echart.js.map +1 -1
  48. package/dist/widgets/error.js +1 -1
  49. package/dist/widgets/formula.js +64 -72
  50. package/dist/widgets/formula.js.map +1 -1
  51. package/dist/widgets/histogram.js +75 -73
  52. package/dist/widgets/histogram.js.map +1 -1
  53. package/dist/widgets/loader.js +41 -40
  54. package/dist/widgets/loader.js.map +1 -1
  55. package/dist/widgets/markdown.js +2 -2
  56. package/dist/widgets/no-data.js +1 -1
  57. package/dist/widgets/pie.js +4 -4
  58. package/dist/widgets/range.js +97 -105
  59. package/dist/widgets/range.js.map +1 -1
  60. package/dist/widgets/scatterplot.js +8 -8
  61. package/dist/widgets/skeleton-loader.js +1 -1
  62. package/dist/widgets/spread.js +84 -100
  63. package/dist/widgets/spread.js.map +1 -1
  64. package/dist/widgets/stores.js +1 -1
  65. package/dist/widgets/table.js +493 -485
  66. package/dist/widgets/table.js.map +1 -1
  67. package/dist/widgets/timeseries.js +4 -4
  68. package/dist/widgets/wrapper.js +156 -156
  69. package/dist/widgets/wrapper.js.map +1 -1
  70. package/dist/widgets.js +4 -4
  71. package/package.json +3 -3
  72. package/src/components/lasso-tool/lasso-tool-inline.tsx +19 -17
  73. package/src/components/lasso-tool/lasso-tool.tsx +27 -22
  74. package/src/components/lasso-tool/types.ts +4 -3
  75. package/src/widgets/_shared/chart-config/index.ts +1 -0
  76. package/src/widgets/_shared/chart-config/option-builders.test.ts +40 -0
  77. package/src/widgets/_shared/chart-config/option-builders.ts +12 -0
  78. package/src/widgets/actions/fullscreen/fullscreen.tsx +5 -8
  79. package/src/widgets/actions/index.ts +4 -7
  80. package/src/widgets/actions/lock-selection/lock-selection.test.tsx +28 -30
  81. package/src/widgets/actions/lock-selection/lock-selection.tsx +25 -26
  82. package/src/widgets/actions/lock-selection/types.ts +0 -17
  83. package/src/widgets/actions/relative-data/relative-data.test.tsx +13 -13
  84. package/src/widgets/actions/relative-data/relative-data.tsx +18 -21
  85. package/src/widgets/actions/relative-data/types.ts +0 -7
  86. package/src/widgets/actions/searcher/searcher.tsx +40 -22
  87. package/src/widgets/actions/searcher/types.ts +0 -2
  88. package/src/widgets/actions/stack-toggle/stack-toggle.test.tsx +160 -16
  89. package/src/widgets/actions/stack-toggle/stack-toggle.tsx +79 -78
  90. package/src/widgets/actions/stack-toggle/types.ts +0 -8
  91. package/src/widgets/actions/zoom-toggle/zoom-toggle.tsx +137 -87
  92. package/src/widgets/bar/config.ts +37 -28
  93. package/src/widgets/category/category-ui.tsx +25 -22
  94. package/src/widgets/echart/echart-ui.test.tsx +3 -18
  95. package/src/widgets/echart/echart-ui.tsx +4 -22
  96. package/src/widgets/echart/echart.test.tsx +9 -25
  97. package/src/widgets/echart/echart.tsx +36 -29
  98. package/src/widgets/echart/types.ts +0 -4
  99. package/src/widgets/echart/utils.ts +3 -1
  100. package/src/widgets/error/error.tsx +17 -14
  101. package/src/widgets/error/types.ts +10 -0
  102. package/src/widgets/formula/components/value.tsx +13 -13
  103. package/src/widgets/histogram/config.ts +36 -29
  104. package/src/widgets/loader/loader.tsx +20 -8
  105. package/src/widgets/loader/types.ts +3 -1
  106. package/src/widgets/no-data/no-data.tsx +8 -11
  107. package/src/widgets/range/components/range-item.tsx +9 -13
  108. package/src/widgets/spread/components/max-value.tsx +13 -13
  109. package/src/widgets/spread/components/min-value.tsx +13 -13
  110. package/src/widgets/stores/index.ts +1 -0
  111. package/src/widgets/stores/types.ts +17 -0
  112. package/src/widgets/stores/widget-store.test.ts +141 -0
  113. package/src/widgets/stores/widget-store.ts +73 -2
  114. package/src/widgets/table/hooks/use-pagination.ts +44 -35
  115. package/src/widgets/table/hooks/use-sort.ts +25 -23
  116. package/src/widgets/wrapper/wrapper-ui.tsx +16 -17
  117. package/dist/error-B2IJ9d2h.js +0 -38
  118. package/dist/error-B2IJ9d2h.js.map +0 -1
  119. package/dist/lasso-tool-wFqOD6wk.js.map +0 -1
  120. package/dist/no-data-C54XJt13.js +0 -61
  121. package/dist/no-data-C54XJt13.js.map +0 -1
  122. package/dist/styles-CCZnY17y.js.map +0 -1
  123. package/dist/utils-D3-eQyDR.js.map +0 -1
  124. package/dist/widget-store-CB6Trp_0.js +0 -131
  125. package/dist/widget-store-CB6Trp_0.js.map +0 -1
@@ -5,16 +5,21 @@ import {
5
5
  } from '@mui/icons-material'
6
6
  import { useEffect, useCallback } from 'react'
7
7
  import { useWidgetStore } from '../../stores/widget-store'
8
- import type { ZoomToggleProps, ZoomState } from './types'
8
+ import type { ZoomToggleProps } from './types'
9
9
  import { styles } from './style'
10
10
  import { Tooltip } from '../../../components'
11
11
  import { getEChartZoomConfig } from '../../echart/utils'
12
- import type { EchartWidgetState } from '../../echart/types'
12
+ import type { EchartOptionsProps } from '../../echart/types'
13
13
  import { useShallow } from 'zustand/shallow'
14
14
 
15
+ export const ZOOM_TOGGLE_TOOL_ID = 'zoom-toggle'
16
+
15
17
  /**
16
18
  * Widget action to toggle EChart zoom functionality.
17
19
  *
20
+ * Registers as a config pipeline tool so that zoom configuration is automatically
21
+ * re-applied when the base config is updated (e.g., by WidgetLoader).
22
+ *
18
23
  * When zoom is active, displays an inline reset button to disable zoom.
19
24
  * Only intended for use in fullscreen ToolbarActions for bar and histogram widgets.
20
25
  *
@@ -39,117 +44,162 @@ export function ZoomToggle({
39
44
  IconButtonProps,
40
45
  }: ZoomToggleProps) {
41
46
  const theme = useTheme()
42
- const setWidget = useWidgetStore((state) => state.setWidget)
43
47
  const getWidget = useWidgetStore((state) => state.getWidget)
48
+ const registerTool = useWidgetStore((state) => state.registerTool)
49
+ const unregisterTool = useWidgetStore((state) => state.unregisterTool)
50
+ const setToolEnabled = useWidgetStore((state) => state.setToolEnabled)
51
+ const updateToolConfig = useWidgetStore((state) => state.updateToolConfig)
44
52
 
45
- const zoom = useWidgetStore(
53
+ const zoomTool = useWidgetStore(
46
54
  useShallow((state) => {
47
- const widget = state.getWidget<ZoomState>(id)
48
- return widget?.zoom ?? false
55
+ const tools = state.getWidget(id)?.registeredTools ?? []
56
+ return tools.find((tool) => tool.id === ZOOM_TOGGLE_TOOL_ID)
49
57
  }),
50
58
  )
51
59
 
52
- const updateZoomOption = useCallback(
53
- (enabled: boolean, start: number, end: number) => {
54
- const zoomConfig = getEChartZoomConfig(
55
- enabled,
56
- { start, end },
57
- { inside: true, xSlider: true, ySlider: false },
58
- theme,
59
- )
60
- const currentOption = getWidget<EchartWidgetState>(id)?.option
61
-
62
- setWidget<EchartWidgetState & ZoomState>(id, {
63
- zoom: enabled,
64
- zoomStart: start,
65
- zoomEnd: end,
66
- option: {
67
- ...currentOption,
68
- ...zoomConfig,
69
- },
70
- })
60
+ const zoom = zoomTool?.enabled ?? defaultZoom
61
+ const zoomStart =
62
+ (zoomTool?.config?.start as number | undefined) ?? defaultZoomStart
63
+ const zoomEnd =
64
+ (zoomTool?.config?.end as number | undefined) ?? defaultZoomEnd
65
+
66
+ // Handle dataZoom event to update zoom range in tool config
67
+ const handleDataZoom = useCallback(
68
+ (event: unknown) => {
69
+ const zoomEvent = event as {
70
+ start?: number
71
+ end?: number
72
+ batch?: {
73
+ start?: number
74
+ end?: number
75
+ }[]
76
+ }
77
+
78
+ const start = zoomEvent.start ?? zoomEvent.batch?.[0]?.start
79
+ const end = zoomEvent.end ?? zoomEvent.batch?.[0]?.end
80
+
81
+ if (start !== undefined && end !== undefined) {
82
+ setToolEnabled(id, ZOOM_TOGGLE_TOOL_ID, true)
83
+ updateToolConfig(id, ZOOM_TOGGLE_TOOL_ID, {
84
+ enabled: true,
85
+ start,
86
+ end,
87
+ })
88
+ }
71
89
  },
72
- [getWidget, id, setWidget, theme],
90
+ [id, setToolEnabled, updateToolConfig],
73
91
  )
74
92
 
75
- // Initialize zoom state from defaults only if not already set
93
+ // Register config tool on mount
76
94
  useEffect(() => {
77
- const existingState = getWidget<ZoomState>(id)
78
-
79
- if (existingState?.zoom) return
80
-
81
- // Apply initial zoom config
82
- updateZoomOption(
83
- existingState?.zoom ?? defaultZoom,
84
- existingState?.zoomStart ?? defaultZoomStart,
85
- existingState?.zoomEnd ?? defaultZoomEnd,
95
+ const existingTool = getWidget(id)?.registeredTools?.find(
96
+ (tool) => tool.id === ZOOM_TOGGLE_TOOL_ID,
86
97
  )
98
+
99
+ const initialEnabled = existingTool?.enabled ?? defaultZoom
100
+ const initialStart =
101
+ (existingTool?.config?.start as number | undefined) ?? defaultZoomStart
102
+ const initialEnd =
103
+ (existingTool?.config?.end as number | undefined) ?? defaultZoomEnd
104
+
105
+ registerTool(id, {
106
+ id: ZOOM_TOGGLE_TOOL_ID,
107
+ type: 'config',
108
+ order: 20,
109
+ enabled: initialEnabled,
110
+ fn: (currentConfig, toolConfig) => {
111
+ const config = currentConfig as Record<string, unknown>
112
+ const option = config.option as EchartOptionsProps | undefined
113
+ const currentOnEvents =
114
+ (config.onEvents as Record<string, unknown> | undefined) ?? {}
115
+
116
+ const enabled = (toolConfig?.enabled as boolean) ?? false
117
+ const start = (toolConfig?.start as number) ?? 0
118
+ const end = (toolConfig?.end as number) ?? 100
119
+
120
+ const legend = option?.legend as { show?: boolean } | undefined
121
+ const hasLegend = legend?.show !== false && legend !== undefined
122
+
123
+ const sliderHeight = parseInt(theme?.spacing?.(4) ?? '32')
124
+ const sliderGap = 8
125
+ const legendBottomOffset = hasLegend ? 28 : 0
126
+
127
+ const zoomConfig = getEChartZoomConfig(
128
+ enabled,
129
+ { start, end },
130
+ {
131
+ inside: true,
132
+ xSlider: true,
133
+ ySlider: false,
134
+ bottomOffset: legendBottomOffset,
135
+ },
136
+ theme,
137
+ )
138
+
139
+ const grid = option?.grid as { bottom?: number | string } | undefined
140
+ const currentGridBottom =
141
+ typeof grid?.bottom === 'number'
142
+ ? grid.bottom
143
+ : parseInt(grid?.bottom ?? '24')
144
+
145
+ const gridBottom = enabled
146
+ ? currentGridBottom + sliderHeight + sliderGap
147
+ : currentGridBottom
148
+
149
+ const onEventsWithoutDataZoom = { ...currentOnEvents }
150
+ delete onEventsWithoutDataZoom.dataZoom
151
+ const onEvents = enabled
152
+ ? { ...currentOnEvents, dataZoom: handleDataZoom }
153
+ : onEventsWithoutDataZoom
154
+
155
+ return {
156
+ ...config,
157
+ option: {
158
+ ...option,
159
+ ...zoomConfig,
160
+ grid: { ...grid, bottom: gridBottom },
161
+ },
162
+ onEvents,
163
+ }
164
+ },
165
+ config: {
166
+ enabled: initialEnabled,
167
+ start: initialStart,
168
+ end: initialEnd,
169
+ },
170
+ })
171
+ return () => unregisterTool(id, ZOOM_TOGGLE_TOOL_ID)
87
172
  }, [
88
173
  defaultZoom,
89
174
  defaultZoomEnd,
90
175
  defaultZoomStart,
91
176
  getWidget,
177
+ handleDataZoom,
92
178
  id,
93
- updateZoomOption,
179
+ registerTool,
180
+ theme,
181
+ unregisterTool,
94
182
  ])
95
183
 
96
- // Cleanup: reset zoom when component unmounts
97
- useEffect(() => {
98
- return () => {
99
- const existingState = getWidget<ZoomState>(id)
100
- updateZoomOption(
101
- false,
102
- existingState?.zoomStart ?? 0,
103
- existingState?.zoomEnd ?? 100,
104
- )
105
- }
106
- }, [getWidget, id, updateZoomOption])
107
-
108
184
  const handleToggle = () => {
109
185
  const newZoom = !zoom
110
-
111
- const existingState = getWidget<ZoomState>(id)
112
-
113
- updateZoomOption(
114
- newZoom,
115
- newZoom ? (existingState?.zoomStart ?? defaultZoomStart) : 0,
116
- newZoom ? (existingState?.zoomEnd ?? defaultZoomEnd) : 100,
117
- )
186
+ setToolEnabled(id, ZOOM_TOGGLE_TOOL_ID, newZoom)
187
+ updateToolConfig(id, ZOOM_TOGGLE_TOOL_ID, {
188
+ enabled: newZoom,
189
+ start: newZoom ? zoomStart : 0,
190
+ end: newZoom ? zoomEnd : 100,
191
+ })
118
192
  }
119
193
 
120
194
  const handleReset = () => {
121
- updateZoomOption(true, defaultZoomStart, defaultZoomEnd)
195
+ setToolEnabled(id, ZOOM_TOGGLE_TOOL_ID, true)
196
+ updateToolConfig(id, ZOOM_TOGGLE_TOOL_ID, {
197
+ enabled: true,
198
+ start: defaultZoomStart,
199
+ end: defaultZoomEnd,
200
+ })
122
201
  }
123
202
 
124
- // Handle dataZoom event to update zoom range in store
125
- const handleDataZoom = useCallback(
126
- (event: unknown) => {
127
- const zoomEvent = event as {
128
- start: number
129
- end: number
130
- }
131
-
132
- const start = zoomEvent.start
133
- const end = zoomEvent.end
134
-
135
- if (start !== undefined && end !== undefined) {
136
- updateZoomOption(true, start, end)
137
- }
138
- },
139
- [updateZoomOption],
140
- )
141
-
142
- // Register dataZoom event handler when zoom is enabled
143
- useEffect(() => {
144
- if (zoom) {
145
- setWidget<EchartWidgetState>(id, {
146
- onEvents: {
147
- dataZoom: handleDataZoom,
148
- },
149
- })
150
- }
151
- }, [id, zoom, handleDataZoom, setWidget])
152
-
153
203
  const enableLabel = labels?.enable ?? 'Enable zoom'
154
204
  const disableLabel = labels?.disable ?? 'Disable zoom'
155
205
  const resetLabel = labels?.reset ?? 'Reset zoom'
@@ -10,7 +10,7 @@ import {
10
10
  buildGridConfig,
11
11
  createTooltipPositioner,
12
12
  createTooltipFormatter,
13
- applyYAxisFormatter,
13
+ niceNum,
14
14
  } from '../_shared/chart-config'
15
15
  import { downloadToCSV, downloadToPNG, type DownloadItem } from '../actions'
16
16
  import type { ConfigProps } from '../loader/types'
@@ -47,32 +47,7 @@ function getOption({
47
47
  }: BarConfig): EchartOptionsProps {
48
48
  const hasLegend = (data?.length ?? 0) > 1
49
49
 
50
- const yAxis = {
51
- type: 'value' as const,
52
- splitNumber: 1,
53
- axisLabel: {
54
- fontSize: theme.typography.overlineDelicate.fontSize,
55
- fontFamily: theme.typography.overlineDelicate.fontFamily,
56
- margin: parseInt(theme.spacing(1)),
57
- show: true,
58
- showMaxLabel: true,
59
- showMinLabel: false,
60
- verticalAlign: 'bottom' as const,
61
- inside: true,
62
- },
63
- axisLine: {
64
- show: false,
65
- },
66
- axisTick: {
67
- show: false,
68
- },
69
- splitLine: {
70
- show: true,
71
- lineStyle: {
72
- color: theme.palette.black[4],
73
- },
74
- },
75
- }
50
+ let niceMax = 1
76
51
 
77
52
  return {
78
53
  legend: buildLegendConfig(hasLegend),
@@ -90,7 +65,41 @@ function getOption({
90
65
  margin: 0,
91
66
  },
92
67
  },
93
- yAxis: applyYAxisFormatter(yAxis, formatter),
68
+ yAxis: {
69
+ type: 'value' as const,
70
+ min: 0,
71
+ max: (extent: { min: number; max: number }) => {
72
+ niceMax = extent.max <= 0 ? 1 : niceNum(extent.max)
73
+ return niceMax
74
+ },
75
+ splitNumber: 1,
76
+ axisLabel: {
77
+ fontSize: theme.typography.overlineDelicate.fontSize,
78
+ fontFamily: theme.typography.overlineDelicate.fontFamily,
79
+ margin: parseInt(theme.spacing(1)),
80
+ show: true,
81
+ showMaxLabel: true,
82
+ showMinLabel: false,
83
+ verticalAlign: 'bottom' as const,
84
+ inside: true,
85
+ formatter: (value: number) => {
86
+ if (value !== niceMax) return ''
87
+ return formatter ? formatter(value) : String(value)
88
+ },
89
+ },
90
+ axisLine: {
91
+ show: false,
92
+ },
93
+ axisTick: {
94
+ show: false,
95
+ },
96
+ splitLine: {
97
+ show: true,
98
+ lineStyle: {
99
+ color: theme.palette.black[4],
100
+ },
101
+ },
102
+ },
94
103
  tooltip: {
95
104
  position: createTooltipPositioner(theme),
96
105
  formatter: createTooltipFormatter((item) => {
@@ -15,30 +15,33 @@ const defaultFormatter = (value: number) => value.toString()
15
15
 
16
16
  export function CategoryUI({ id }: CategoryUIProps) {
17
17
  const theme = useTheme()
18
- const widget = useWidgetStore(
19
- useShallow((state) => {
20
- const widget = state.getWidget<CategoryWidgetState>(id)
21
- return {
22
- formatter: widget?.formatter,
23
- series: widget?.series,
24
- data: widget?.data,
25
- maxItems: widget?.maxItems,
26
- labels: widget?.labels,
27
- onRowClick: widget?.onRowClick,
28
- selected: widget?.selected,
29
- max: widget?.max,
30
- }
31
- }),
18
+ const _formatter = useWidgetStore(
19
+ useShallow((state) => state.getWidget<CategoryWidgetState>(id)?.formatter),
20
+ )
21
+ const _series = useWidgetStore(
22
+ useShallow((state) => state.getWidget<CategoryWidgetState>(id)?.series),
23
+ )
24
+ const data = useWidgetStore(
25
+ useShallow((state) => state.getWidget<CategoryWidgetState>(id)?.data),
26
+ )
27
+ const maxItems = useWidgetStore(
28
+ useShallow((state) => state.getWidget<CategoryWidgetState>(id)?.maxItems),
29
+ )
30
+ const labels = useWidgetStore(
31
+ useShallow((state) => state.getWidget<CategoryWidgetState>(id)?.labels),
32
+ )
33
+ const onRowClick = useWidgetStore(
34
+ useShallow((state) => state.getWidget<CategoryWidgetState>(id)?.onRowClick),
35
+ )
36
+ const selected = useWidgetStore(
37
+ useShallow((state) => state.getWidget<CategoryWidgetState>(id)?.selected),
38
+ )
39
+ const max = useWidgetStore(
40
+ useShallow((state) => state.getWidget<CategoryWidgetState>(id)?.max),
32
41
  )
33
42
 
34
- const formatter = widget?.formatter ?? defaultFormatter
35
- const series = widget?.series ?? []
36
- const data = widget?.data
37
- const maxItems = widget?.maxItems
38
- const labels = widget?.labels
39
- const onRowClick = widget?.onRowClick
40
- const selected = widget?.selected
41
- const max = widget?.max
43
+ const formatter = _formatter ?? defaultFormatter
44
+ const series = _series ?? []
42
45
 
43
46
  const [maxHeight] = useState<string | number | undefined>(
44
47
  maxItems ? 40 * (series.length || 1) * maxItems : undefined,
@@ -175,7 +175,7 @@ describe('EchartUI', () => {
175
175
 
176
176
  expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
177
177
  lazyUpdate: true,
178
- replaceMerge: ['dataset', 'series'],
178
+ notMerge: true,
179
179
  })
180
180
  })
181
181
 
@@ -200,22 +200,7 @@ describe('EchartUI', () => {
200
200
 
201
201
  expect(mockChart.setOption).toHaveBeenCalledWith(newOption, {
202
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'],
203
+ notMerge: true,
219
204
  })
220
205
  })
221
206
 
@@ -496,7 +481,7 @@ describe('EchartUI', () => {
496
481
 
497
482
  expect(mockChart.setOption).toHaveBeenCalledWith(complexOption, {
498
483
  lazyUpdate: true,
499
- replaceMerge: ['dataset', 'series'],
484
+ notMerge: true,
500
485
  })
501
486
  })
502
487
 
@@ -3,13 +3,8 @@ import * as echarts from 'echarts'
3
3
  import type { EchartUIProps } from './types'
4
4
  import { useWidgetRef } from '../../hooks'
5
5
 
6
- const DEFAULT_REPLACE_MERGE: string[] = ['dataset', 'series']
7
-
8
6
  export function EchartUI(props: EchartUIProps) {
9
7
  const { id, ref, init, option, className, style, onEvents } = props
10
- const replaceMerge = toReplaceMerge(
11
- (props as { replaceMerge?: unknown }).replaceMerge,
12
- )
13
8
 
14
9
  const chartRef = useWidgetRef<HTMLDivElement>(id)
15
10
  const chartInstance = useRef<echarts.ECharts>(null)
@@ -34,16 +29,11 @@ export function EchartUI(props: EchartUIProps) {
34
29
 
35
30
  // Update chart when options change
36
31
  useEffect(() => {
37
- const setOptionConfig = {
32
+ chartInstance.current?.setOption(option, {
38
33
  lazyUpdate: true,
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])
34
+ notMerge: true,
35
+ })
36
+ }, [option])
47
37
 
48
38
  // Handle resize using ResizeObserver
49
39
  useEffect(() => {
@@ -82,11 +72,3 @@ export function EchartUI(props: EchartUIProps) {
82
72
 
83
73
  return <div id={id} ref={chartRef} style={style} className={className} />
84
74
  }
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,7 +184,7 @@ describe('Echart', () => {
184
184
  }),
185
185
  {
186
186
  lazyUpdate: true,
187
- replaceMerge: ['dataset', 'series'],
187
+ notMerge: true,
188
188
  },
189
189
  )
190
190
  })
@@ -277,7 +277,7 @@ describe('Echart', () => {
277
277
 
278
278
  expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
279
279
  lazyUpdate: true,
280
- replaceMerge: ['dataset', 'series'],
280
+ notMerge: true,
281
281
  })
282
282
 
283
283
  const newOption: EChartsOption = {
@@ -299,7 +299,7 @@ describe('Echart', () => {
299
299
 
300
300
  expect(mockChart.setOption).toHaveBeenCalledWith(newOption, {
301
301
  lazyUpdate: true,
302
- replaceMerge: ['dataset', 'series'],
302
+ notMerge: true,
303
303
  })
304
304
  })
305
305
 
@@ -367,7 +367,7 @@ describe('Echart', () => {
367
367
 
368
368
  expect(mockChart.setOption).toHaveBeenCalledWith(complexOption, {
369
369
  lazyUpdate: true,
370
- replaceMerge: ['dataset', 'series'],
370
+ notMerge: true,
371
371
  })
372
372
  })
373
373
 
@@ -404,7 +404,7 @@ describe('Echart', () => {
404
404
  )
405
405
  expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
406
406
  lazyUpdate: true,
407
- replaceMerge: ['dataset', 'series'],
407
+ notMerge: true,
408
408
  })
409
409
  expect(mockChart.on).toHaveBeenCalledWith('click', clickHandler)
410
410
  })
@@ -447,7 +447,7 @@ describe('Echart', () => {
447
447
 
448
448
  expect(mockChart.setOption).toHaveBeenCalledWith(emptyOption, {
449
449
  lazyUpdate: true,
450
- replaceMerge: ['dataset', 'series'],
450
+ notMerge: true,
451
451
  })
452
452
  })
453
453
 
@@ -470,7 +470,7 @@ describe('Echart', () => {
470
470
  }),
471
471
  {
472
472
  lazyUpdate: true,
473
- replaceMerge: ['dataset', 'series'],
473
+ notMerge: true,
474
474
  },
475
475
  )
476
476
  })
@@ -516,7 +516,7 @@ describe('Echart', () => {
516
516
 
517
517
  expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
518
518
  lazyUpdate: true,
519
- replaceMerge: ['dataset', 'series'],
519
+ notMerge: true,
520
520
  })
521
521
 
522
522
  rerender(<Echart id='chart-b' />)
@@ -525,27 +525,11 @@ describe('Echart', () => {
525
525
  { series: [{ type: 'line', data: [1, 2, 3] }] },
526
526
  {
527
527
  lazyUpdate: true,
528
- replaceMerge: ['dataset', 'series'],
528
+ notMerge: true,
529
529
  },
530
530
  )
531
531
  })
532
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
-
549
533
  test('disposes chart when component unmounts', () => {
550
534
  useWidgetStore.getState().setWidget('test-echart', {
551
535
  id: 'test-echart',
@@ -10,43 +10,50 @@ import { useShallow } from 'zustand/shallow'
10
10
  import { useMemo } from 'react'
11
11
 
12
12
  export function Echart(props: EchartProps) {
13
- const widget = useWidgetStore(
14
- useShallow((state) => {
15
- const widget = state.getWidget<EchartWidgetState>(props.id)
16
- return {
17
- id: widget?.id,
18
- data: widget?.data as EchartWidgetData | undefined,
19
- option: widget?.option,
20
- onEvents: widget?.onEvents,
21
- init: widget?.init,
22
- replaceMerge: widget?.replaceMerge,
23
- }
24
- }),
13
+ const id = useWidgetStore(
14
+ (state) => state.getWidget<EchartWidgetState>(props.id)?.id,
15
+ )
16
+
17
+ const data = useWidgetStore(
18
+ useShallow(
19
+ (state) =>
20
+ state.getWidget<EchartWidgetState>(props.id)?.data as
21
+ | EchartWidgetData
22
+ | undefined,
23
+ ),
24
+ )
25
+
26
+ const widgetOption = useWidgetStore(
27
+ useShallow((state) => state.getWidget<EchartWidgetState>(props.id)?.option),
28
+ )
29
+
30
+ const onEvents = useWidgetStore(
31
+ useShallow(
32
+ (state) => state.getWidget<EchartWidgetState>(props.id)?.onEvents,
33
+ ),
34
+ )
35
+
36
+ const init = useWidgetStore(
37
+ (state) => state.getWidget<EchartWidgetState>(props.id)?.init,
25
38
  )
26
39
 
27
40
  // Memoize dataset transformation to avoid re-computing on every render
28
- const dataset = useMemo(() => buildDataset(widget.data), [widget.data])
41
+ const dataset = useMemo(() => buildDataset(data), [data])
29
42
 
30
- if (!widget.id) {
43
+ const option = useMemo<EchartOptionsProps>(
44
+ () => ({
45
+ ...widgetOption,
46
+ ...(dataset && { dataset }),
47
+ }),
48
+ [widgetOption, dataset],
49
+ )
50
+
51
+ if (!id) {
31
52
  return null
32
53
  }
33
- const option: EchartOptionsProps = {
34
- ...widget.option,
35
- ...(dataset && { dataset }),
36
- }
37
-
38
- const onEvents = widget.onEvents
39
- const init = widget.init
40
- const replaceMerge = widget.replaceMerge
41
54
 
42
55
  return (
43
- <EchartUI
44
- id={props.id}
45
- option={option}
46
- onEvents={onEvents}
47
- init={init}
48
- replaceMerge={replaceMerge}
49
- />
56
+ <EchartUI id={props.id} option={option} onEvents={onEvents} init={init} />
50
57
  )
51
58
  }
52
59