@carto/ps-react-ui 4.3.6 → 4.3.8

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 (117) 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-BctzdzBu.js → lasso-tool-jl4YK02H.js} +19 -19
  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-D3uVFImu.js → row-BKmVAUN5.js} +2 -2
  10. package/dist/{row-D3uVFImu.js.map → row-BKmVAUN5.js.map} +1 -1
  11. package/dist/{series-BAImrSBo.js → series-D1pynfeh.js} +3 -3
  12. package/dist/{series-BAImrSBo.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/change-column/change-column.d.ts +4 -0
  20. package/dist/types/widgets/actions/fullscreen/fullscreen.d.ts +1 -1
  21. package/dist/types/widgets/actions/fullscreen/types.d.ts +2 -1
  22. package/dist/types/widgets/actions/index.d.ts +1 -1
  23. package/dist/types/widgets/actions/lock-selection/types.d.ts +7 -7
  24. package/dist/types/widgets/actions/relative-data/types.d.ts +1 -1
  25. package/dist/types/widgets/echart/types.d.ts +0 -4
  26. package/dist/types/widgets/echart/utils.d.ts +2 -1
  27. package/dist/types/widgets/error/error.d.ts +1 -1
  28. package/dist/types/widgets/error/types.d.ts +8 -0
  29. package/dist/types/widgets/loader/loader.d.ts +1 -1
  30. package/dist/types/widgets/loader/types.d.ts +1 -1
  31. package/dist/types/widgets/stores/types.d.ts +1 -1
  32. package/dist/{use-widget-ref-B8x4sHIj.js → use-widget-ref-P-2i0MJG.js} +2 -2
  33. package/dist/{use-widget-ref-B8x4sHIj.js.map → use-widget-ref-P-2i0MJG.js.map} +1 -1
  34. package/dist/{utils-D3-eQyDR.js → utils-idmvq0Oa.js} +17 -16
  35. package/dist/utils-idmvq0Oa.js.map +1 -0
  36. package/dist/{widget-store-Dn0Bnc4h.js → widget-store-CzDt8oSK.js} +31 -46
  37. package/dist/widget-store-CzDt8oSK.js.map +1 -0
  38. package/dist/widgets/actions.js +714 -697
  39. package/dist/widgets/actions.js.map +1 -1
  40. package/dist/widgets/bar.js +67 -63
  41. package/dist/widgets/bar.js.map +1 -1
  42. package/dist/widgets/category.js +250 -241
  43. package/dist/widgets/category.js.map +1 -1
  44. package/dist/widgets/echart.js +93 -100
  45. package/dist/widgets/echart.js.map +1 -1
  46. package/dist/widgets/error.js +1 -1
  47. package/dist/widgets/formula.js +64 -72
  48. package/dist/widgets/formula.js.map +1 -1
  49. package/dist/widgets/histogram.js +75 -73
  50. package/dist/widgets/histogram.js.map +1 -1
  51. package/dist/widgets/loader.js +58 -49
  52. package/dist/widgets/loader.js.map +1 -1
  53. package/dist/widgets/markdown.js +2 -2
  54. package/dist/widgets/no-data.js +1 -1
  55. package/dist/widgets/pie.js +4 -4
  56. package/dist/widgets/range.js +97 -105
  57. package/dist/widgets/range.js.map +1 -1
  58. package/dist/widgets/scatterplot.js +8 -8
  59. package/dist/widgets/skeleton-loader.js +1 -1
  60. package/dist/widgets/spread.js +84 -100
  61. package/dist/widgets/spread.js.map +1 -1
  62. package/dist/widgets/stores.js +1 -1
  63. package/dist/widgets/table.js +493 -485
  64. package/dist/widgets/table.js.map +1 -1
  65. package/dist/widgets/timeseries.js +4 -4
  66. package/dist/widgets/wrapper.js +156 -156
  67. package/dist/widgets/wrapper.js.map +1 -1
  68. package/dist/widgets.js +4 -4
  69. package/package.json +1 -1
  70. package/src/components/lasso-tool/lasso-tool-inline.tsx +19 -17
  71. package/src/components/lasso-tool/lasso-tool.tsx +22 -20
  72. package/src/components/lasso-tool/types.ts +4 -3
  73. package/src/widgets/_shared/chart-config/index.ts +1 -0
  74. package/src/widgets/_shared/chart-config/option-builders.test.ts +40 -0
  75. package/src/widgets/_shared/chart-config/option-builders.ts +12 -0
  76. package/src/widgets/actions/change-column/change-column.test.tsx +129 -2
  77. package/src/widgets/actions/change-column/change-column.tsx +79 -2
  78. package/src/widgets/actions/fullscreen/fullscreen.tsx +8 -8
  79. package/src/widgets/actions/fullscreen/types.ts +6 -1
  80. package/src/widgets/actions/index.ts +4 -1
  81. package/src/widgets/actions/lock-selection/lock-selection.test.tsx +28 -30
  82. package/src/widgets/actions/lock-selection/types.ts +8 -8
  83. package/src/widgets/actions/relative-data/relative-data.test.tsx +13 -13
  84. package/src/widgets/actions/relative-data/types.ts +1 -1
  85. package/src/widgets/actions/stack-toggle/stack-toggle.test.tsx +19 -9
  86. package/src/widgets/actions/zoom-toggle/zoom-toggle.tsx +113 -95
  87. package/src/widgets/bar/config.ts +37 -28
  88. package/src/widgets/category/category-ui.tsx +25 -22
  89. package/src/widgets/echart/echart-ui.test.tsx +3 -18
  90. package/src/widgets/echart/echart-ui.tsx +4 -22
  91. package/src/widgets/echart/echart.test.tsx +9 -25
  92. package/src/widgets/echart/echart.tsx +36 -29
  93. package/src/widgets/echart/types.ts +0 -4
  94. package/src/widgets/echart/utils.ts +3 -1
  95. package/src/widgets/error/error.tsx +17 -14
  96. package/src/widgets/error/types.ts +10 -0
  97. package/src/widgets/formula/components/value.tsx +13 -13
  98. package/src/widgets/histogram/config.ts +36 -29
  99. package/src/widgets/loader/loader.tsx +28 -25
  100. package/src/widgets/loader/types.ts +3 -1
  101. package/src/widgets/no-data/no-data.tsx +8 -11
  102. package/src/widgets/range/components/range-item.tsx +9 -13
  103. package/src/widgets/spread/components/max-value.tsx +13 -13
  104. package/src/widgets/spread/components/min-value.tsx +13 -13
  105. package/src/widgets/stores/types.ts +1 -4
  106. package/src/widgets/stores/widget-store.ts +1 -27
  107. package/src/widgets/table/hooks/use-pagination.ts +44 -35
  108. package/src/widgets/table/hooks/use-sort.ts +25 -23
  109. package/src/widgets/wrapper/wrapper-ui.tsx +16 -17
  110. package/dist/error-piB8FwYO.js +0 -38
  111. package/dist/error-piB8FwYO.js.map +0 -1
  112. package/dist/lasso-tool-BctzdzBu.js.map +0 -1
  113. package/dist/no-data-jdlbMef0.js +0 -61
  114. package/dist/no-data-jdlbMef0.js.map +0 -1
  115. package/dist/styles-CCZnY17y.js.map +0 -1
  116. package/dist/utils-D3-eQyDR.js.map +0 -1
  117. package/dist/widget-store-Dn0Bnc4h.js.map +0 -1
@@ -5,11 +5,10 @@ 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'
13
12
  import type { EchartOptionsProps } from '../../echart/types'
14
13
  import { useShallow } from 'zustand/shallow'
15
14
 
@@ -45,143 +44,162 @@ export function ZoomToggle({
45
44
  IconButtonProps,
46
45
  }: ZoomToggleProps) {
47
46
  const theme = useTheme()
48
- const setWidget = useWidgetStore((state) => state.setWidget)
49
47
  const getWidget = useWidgetStore((state) => state.getWidget)
50
48
  const registerTool = useWidgetStore((state) => state.registerTool)
51
49
  const unregisterTool = useWidgetStore((state) => state.unregisterTool)
52
50
  const setToolEnabled = useWidgetStore((state) => state.setToolEnabled)
53
51
  const updateToolConfig = useWidgetStore((state) => state.updateToolConfig)
54
52
 
55
- const zoom = useWidgetStore(
53
+ const zoomTool = useWidgetStore(
56
54
  useShallow((state) => {
57
- const widget = state.getWidget<ZoomState>(id)
58
- return widget?.zoom ?? false
55
+ const tools = state.getWidget(id)?.registeredTools ?? []
56
+ return tools.find((tool) => tool.id === ZOOM_TOGGLE_TOOL_ID)
59
57
  }),
60
58
  )
61
59
 
62
- const zoomStart = useWidgetStore(
63
- useShallow((state) => {
64
- const widget = state.getWidget<ZoomState>(id)
65
- return widget?.zoomStart ?? defaultZoomStart
66
- }),
67
- )
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
68
65
 
69
- const zoomEnd = useWidgetStore(
70
- useShallow((state) => {
71
- const widget = state.getWidget<ZoomState>(id)
72
- return widget?.zoomEnd ?? defaultZoomEnd
73
- }),
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
+ }
89
+ },
90
+ [id, setToolEnabled, updateToolConfig],
74
91
  )
75
92
 
76
93
  // Register config tool on mount
77
94
  useEffect(() => {
95
+ const existingTool = getWidget(id)?.registeredTools?.find(
96
+ (tool) => tool.id === ZOOM_TOGGLE_TOOL_ID,
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
+
78
105
  registerTool(id, {
79
106
  id: ZOOM_TOGGLE_TOOL_ID,
80
107
  type: 'config',
81
108
  order: 20,
82
- enabled: zoom,
109
+ enabled: initialEnabled,
83
110
  fn: (currentConfig, toolConfig) => {
84
111
  const config = currentConfig as Record<string, unknown>
85
112
  const option = config.option as EchartOptionsProps | undefined
113
+ const currentOnEvents =
114
+ (config.onEvents as Record<string, unknown> | undefined) ?? {}
115
+
86
116
  const enabled = (toolConfig?.enabled as boolean) ?? false
87
117
  const start = (toolConfig?.start as number) ?? 0
88
118
  const end = (toolConfig?.end as number) ?? 100
89
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
+
90
127
  const zoomConfig = getEChartZoomConfig(
91
128
  enabled,
92
129
  { start, end },
93
- { inside: true, xSlider: true, ySlider: false },
130
+ {
131
+ inside: true,
132
+ xSlider: true,
133
+ ySlider: false,
134
+ bottomOffset: legendBottomOffset,
135
+ },
94
136
  theme,
95
137
  )
96
- return { ...config, option: { ...option, ...zoomConfig } }
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,
97
169
  },
98
- config: { enabled: zoom, start: zoomStart, end: zoomEnd },
99
170
  })
100
171
  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
-
113
- // Initialize zoom state from defaults only if not already set
114
- useEffect(() => {
115
- const existingState = getWidget<ZoomState>(id)
116
-
117
- if (existingState?.zoom) return
118
-
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
127
- useEffect(() => {
128
- return () => {
129
- setWidget<ZoomState>(id, { zoom: false })
130
- }
131
- }, [id, setWidget])
172
+ }, [
173
+ defaultZoom,
174
+ defaultZoomEnd,
175
+ defaultZoomStart,
176
+ getWidget,
177
+ handleDataZoom,
178
+ id,
179
+ registerTool,
180
+ theme,
181
+ unregisterTool,
182
+ ])
132
183
 
133
184
  const handleToggle = () => {
134
185
  const newZoom = !zoom
135
- const existingState = getWidget<ZoomState>(id)
136
-
137
- setWidget<ZoomState>(id, {
138
- zoom: newZoom,
139
- zoomStart: newZoom ? (existingState?.zoomStart ?? defaultZoomStart) : 0,
140
- zoomEnd: newZoom ? (existingState?.zoomEnd ?? defaultZoomEnd) : 100,
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,
141
191
  })
142
192
  }
143
193
 
144
194
  const handleReset = () => {
145
- setWidget<ZoomState>(id, {
146
- zoom: true,
147
- zoomStart: defaultZoomStart,
148
- zoomEnd: 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,
149
200
  })
150
201
  }
151
202
 
152
- // Handle dataZoom event to update zoom range in store
153
- const handleDataZoom = useCallback(
154
- (event: unknown) => {
155
- const zoomEvent = event as {
156
- start: number
157
- end: number
158
- }
159
-
160
- const start = zoomEvent.start
161
- const end = zoomEvent.end
162
-
163
- if (start !== undefined && end !== undefined) {
164
- setWidget<ZoomState>(id, {
165
- zoom: true,
166
- zoomStart: start,
167
- zoomEnd: end,
168
- })
169
- }
170
- },
171
- [id, setWidget],
172
- )
173
-
174
- // Register dataZoom event handler when zoom is enabled
175
- useEffect(() => {
176
- if (zoom) {
177
- setWidget<EchartWidgetState>(id, {
178
- onEvents: {
179
- dataZoom: handleDataZoom,
180
- },
181
- })
182
- }
183
- }, [id, zoom, handleDataZoom, setWidget])
184
-
185
203
  const enableLabel = labels?.enable ?? 'Enable zoom'
186
204
  const disableLabel = labels?.disable ?? 'Disable zoom'
187
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