@carto/ps-react-ui 4.5.1 → 4.6.1
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/{download-config-DemuQ3Jm.js → download-config-C3I0jWIL.js} +2 -2
- package/dist/{download-config-DemuQ3Jm.js.map → download-config-C3I0jWIL.js.map} +1 -1
- package/dist/{row-D4VOhcNI.js → row-DZSP99LW.js} +2 -2
- package/dist/{row-D4VOhcNI.js.map → row-DZSP99LW.js.map} +1 -1
- package/dist/{series-Bola3CmD.js → series-DLNHDWs0.js} +3 -3
- package/dist/{series-Bola3CmD.js.map → series-DLNHDWs0.js.map} +1 -1
- package/dist/types/hooks/index.d.ts +0 -1
- package/dist/types/widgets/actions/brush-toggle/brush-toggle.d.ts +3 -0
- package/dist/types/widgets/actions/index.d.ts +4 -4
- package/dist/types/widgets/actions/lock-selection/types.d.ts +2 -0
- package/dist/types/widgets/actions/relative-data/relative-data.d.ts +7 -2
- package/dist/types/widgets/actions/relative-data/types.d.ts +2 -0
- package/dist/types/widgets/actions/zoom-toggle/zoom-toggle.d.ts +4 -0
- package/dist/types/widgets/category/index.d.ts +10 -2
- package/dist/types/widgets/no-data/no-data.d.ts +3 -2
- package/dist/types/widgets/no-data/types.d.ts +5 -1
- package/dist/types/widgets/stores/index.d.ts +1 -1
- package/dist/types/widgets/stores/types.d.ts +10 -10
- package/dist/types/widgets/stores/widget-store.d.ts +2 -3
- package/dist/types/widgets/table/index.d.ts +6 -2
- package/dist/{use-widget-ref-BFazQvJK.js → use-widget-ref-Ddr_SlJJ.js} +2 -2
- package/dist/{use-widget-ref-BFazQvJK.js.map → use-widget-ref-Ddr_SlJJ.js.map} +1 -1
- package/dist/{use-widget-selector-DqRmWQ1K.js → use-widget-selector-DFl2hW0R.js} +2 -2
- package/dist/{use-widget-selector-DqRmWQ1K.js.map → use-widget-selector-DFl2hW0R.js.map} +1 -1
- package/dist/{widget-store-CIrb9RKP.js → widget-store-Bw5zRUGg.js} +93 -95
- package/dist/widget-store-Bw5zRUGg.js.map +1 -0
- package/dist/widgets/actions.js +770 -755
- package/dist/widgets/actions.js.map +1 -1
- package/dist/widgets/bar.js +2 -2
- package/dist/widgets/category.js +3 -3
- package/dist/widgets/category.js.map +1 -1
- package/dist/widgets/echart.js +2 -2
- package/dist/widgets/error.js +37 -2
- package/dist/widgets/error.js.map +1 -1
- package/dist/widgets/formula.js +5 -5
- package/dist/widgets/histogram.js +1 -1
- package/dist/widgets/loader.js +1 -1
- package/dist/widgets/markdown.js +2 -2
- package/dist/widgets/no-data.js +58 -2
- package/dist/widgets/no-data.js.map +1 -1
- package/dist/widgets/note.js +121 -2
- package/dist/widgets/note.js.map +1 -1
- package/dist/widgets/pie.js +2 -2
- package/dist/widgets/range.js +3 -3
- 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 +2 -2
- package/dist/widgets/table.js +3 -3
- package/dist/widgets/timeseries.js +2 -2
- package/dist/widgets/utils.js +1 -1
- package/dist/widgets/wrapper.js +2 -2
- package/package.json +2 -6
- package/src/hooks/index.ts +0 -1
- package/src/widgets/actions/brush-toggle/brush-toggle.tsx +18 -22
- package/src/widgets/actions/change-column/change-column.test.tsx +1 -1
- package/src/widgets/actions/download/download.test.tsx +1 -1
- package/src/widgets/actions/index.ts +11 -2
- package/src/widgets/actions/lock-selection/lock-selection.test.tsx +14 -0
- package/src/widgets/actions/lock-selection/lock-selection.tsx +18 -11
- package/src/widgets/actions/lock-selection/types.ts +2 -0
- package/src/widgets/actions/relative-data/relative-data.test.tsx +211 -20
- package/src/widgets/actions/relative-data/relative-data.tsx +65 -34
- package/src/widgets/actions/relative-data/types.ts +2 -0
- package/src/widgets/actions/searcher/searcher.tsx +28 -30
- package/src/widgets/actions/stack-toggle/stack-toggle.tsx +11 -2
- package/src/widgets/actions/zoom-toggle/zoom-toggle.tsx +53 -45
- package/src/widgets/category/category-ui.tsx +4 -6
- package/src/widgets/category/index.ts +13 -14
- package/src/widgets/no-data/no-data.test.tsx +90 -40
- package/src/widgets/no-data/no-data.tsx +7 -5
- package/src/widgets/no-data/types.ts +5 -1
- package/src/widgets/stores/index.ts +2 -0
- package/src/widgets/stores/types.ts +10 -18
- package/src/widgets/stores/widget-store.test.ts +132 -13
- package/src/widgets/stores/widget-store.ts +29 -35
- package/src/widgets/table/index.ts +6 -4
- package/dist/error-Cj8eUMrl.js +0 -40
- package/dist/error-Cj8eUMrl.js.map +0 -1
- package/dist/no-data-DkIt7Qt1.js +0 -61
- package/dist/no-data-DkIt7Qt1.js.map +0 -1
- package/dist/note-t51drNe0.js +0 -124
- package/dist/note-t51drNe0.js.map +0 -1
- package/dist/types/hooks/use-debounce.d.ts +0 -19
- package/dist/types/widgets/category/components/index.d.ts +0 -10
- package/dist/types/widgets/index.d.ts +0 -9
- package/dist/types/widgets/table/hooks/index.d.ts +0 -6
- package/dist/widget-store-CIrb9RKP.js.map +0 -1
- package/dist/widgets.js +0 -13
- package/dist/widgets.js.map +0 -1
- package/src/hooks/use-debounce.ts +0 -55
- package/src/widgets/category/components/index.ts +0 -14
- package/src/widgets/index.ts +0 -25
- package/src/widgets/table/hooks/index.ts +0 -7
|
@@ -3,7 +3,7 @@ import { PercentOutlined } from '@mui/icons-material'
|
|
|
3
3
|
import { useCallback, useEffect, useRef } from 'react'
|
|
4
4
|
import { widgetStoreActions } from '../../stores/widget-store'
|
|
5
5
|
import { useWidgetSelector } from '../../stores/use-widget-selector'
|
|
6
|
-
import type { RelativeDataProps } from './types'
|
|
6
|
+
import type { RelativeDataProps, RelativeDataState } from './types'
|
|
7
7
|
import { actionButtonStyles } from '../shared/styles'
|
|
8
8
|
import { Tooltip } from '../../../components'
|
|
9
9
|
import { calculateTotal, toRelativeData } from './utils'
|
|
@@ -15,8 +15,13 @@ export const RELATIVE_DATA_CONFIG_TOOL_ID = 'relative-data-config'
|
|
|
15
15
|
/**
|
|
16
16
|
* Widget action to toggle between relative (percentage) and absolute data display.
|
|
17
17
|
*
|
|
18
|
-
* Registers
|
|
19
|
-
*
|
|
18
|
+
* Registers two transformation tools in the widget pipeline when mounted:
|
|
19
|
+
* - A data tool that converts values to percentages (enabled/disabled via store)
|
|
20
|
+
* - A config tool that is **always enabled** and reads `isRelative` from the store
|
|
21
|
+
* to decide whether to apply the percentage formatter or restore the original one.
|
|
22
|
+
* The config tool must always participate because the original formatter may have
|
|
23
|
+
* been set via `setWidget` (not in the base config), so disabling the tool would
|
|
24
|
+
* leave the percentage formatter stuck on the widget.
|
|
20
25
|
*
|
|
21
26
|
* @example
|
|
22
27
|
* ```tsx
|
|
@@ -39,15 +44,21 @@ export function RelativeData({
|
|
|
39
44
|
undefined,
|
|
40
45
|
)
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
// Read isRelative from widget store root — single source of truth
|
|
48
|
+
const { isRelative } = useWidgetSelector(id, (w) => ({
|
|
49
|
+
isRelative:
|
|
50
|
+
(w as RelativeDataState | undefined)?.isRelative ?? defaultIsRelative,
|
|
46
51
|
}))
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
// Initialize store with default value on mount
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const current = widgetStoreActions.getWidget<RelativeDataState>(id)
|
|
56
|
+
if (current?.isRelative === undefined) {
|
|
57
|
+
widgetStoreActions.setWidget(id, { isRelative: defaultIsRelative })
|
|
58
|
+
}
|
|
59
|
+
}, [id, defaultIsRelative])
|
|
49
60
|
|
|
50
|
-
// Register data tool
|
|
61
|
+
// Register data tool once — fn has no closure deps
|
|
51
62
|
useEffect(() => {
|
|
52
63
|
widgetStoreActions.registerTool(id, {
|
|
53
64
|
id: RELATIVE_DATA_TOOL_ID,
|
|
@@ -63,18 +74,35 @@ export function RelativeData({
|
|
|
63
74
|
return () => widgetStoreActions.unregisterTool(id, RELATIVE_DATA_TOOL_ID)
|
|
64
75
|
}, [id, order, defaultIsRelative])
|
|
65
76
|
|
|
66
|
-
//
|
|
77
|
+
// Sync data tool enabled — lightweight, no re-registration
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
widgetStoreActions.setToolEnabled(id, RELATIVE_DATA_TOOL_ID, isRelative)
|
|
80
|
+
}, [id, isRelative])
|
|
81
|
+
|
|
82
|
+
// Register config tool — ALWAYS enabled.
|
|
83
|
+
// Reads isRelative, originalFormatter, originalMax from the store at execution time:
|
|
84
|
+
// - isRelative=true → applies percentage formatter and max=100
|
|
85
|
+
// - isRelative=false → restores original formatter/max from store
|
|
67
86
|
useEffect(() => {
|
|
68
87
|
widgetStoreActions.registerTool(id, {
|
|
69
88
|
id: RELATIVE_DATA_CONFIG_TOOL_ID,
|
|
70
89
|
type: 'config',
|
|
71
90
|
order,
|
|
72
91
|
enabled: true,
|
|
73
|
-
fn: (currentConfig: unknown
|
|
92
|
+
fn: (currentConfig: unknown) => {
|
|
74
93
|
const config = currentConfig as Record<string, unknown>
|
|
75
|
-
|
|
94
|
+
const widget = widgetStoreActions.getWidget<RelativeDataState>(id)
|
|
95
|
+
|
|
96
|
+
const hasSourceData =
|
|
97
|
+
widget?.sourceData != null &&
|
|
98
|
+
!(Array.isArray(widget.sourceData) && widget.sourceData.length === 0)
|
|
99
|
+
|
|
100
|
+
if (widget?.isRelative) {
|
|
101
|
+
// Don't apply percentage formatter when there's no source data
|
|
102
|
+
if (!hasSourceData) return config
|
|
103
|
+
|
|
76
104
|
if (!percentFormatterRef.current) {
|
|
77
|
-
const locale =
|
|
105
|
+
const locale = widget?.locale
|
|
78
106
|
percentFormatterRef.current = (value: number) =>
|
|
79
107
|
new Intl.NumberFormat(locale, {
|
|
80
108
|
style: 'percent',
|
|
@@ -84,46 +112,49 @@ export function RelativeData({
|
|
|
84
112
|
}
|
|
85
113
|
return { ...config, formatter: percentFormatterRef.current, max: 100 }
|
|
86
114
|
}
|
|
87
|
-
|
|
115
|
+
|
|
116
|
+
// Not in relative mode — restore originals from store if captured.
|
|
117
|
+
// Use `in` because originalFormatter may have been captured as undefined
|
|
118
|
+
// (widget had no formatter before toggling relative on).
|
|
88
119
|
percentFormatterRef.current = undefined
|
|
89
|
-
if (
|
|
120
|
+
if (widget != null && 'originalFormatter' in widget) {
|
|
90
121
|
return {
|
|
91
122
|
...config,
|
|
92
|
-
formatter:
|
|
93
|
-
max:
|
|
123
|
+
formatter: widget.originalFormatter,
|
|
124
|
+
max: widget.originalMax,
|
|
94
125
|
}
|
|
95
126
|
}
|
|
96
127
|
return config
|
|
97
128
|
},
|
|
98
|
-
config: {
|
|
99
|
-
isRelative: defaultIsRelative,
|
|
100
|
-
},
|
|
101
129
|
})
|
|
102
|
-
return () =>
|
|
130
|
+
return () => {
|
|
131
|
+
// Restore original formatter/max if unmounting while in relative mode
|
|
132
|
+
const widget = widgetStoreActions.getWidget<RelativeDataState>(id)
|
|
133
|
+
if (widget?.isRelative && 'originalFormatter' in widget) {
|
|
134
|
+
widgetStoreActions.setWidget(id, {
|
|
135
|
+
formatter: widget.originalFormatter,
|
|
136
|
+
max: widget.originalMax,
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
percentFormatterRef.current = undefined
|
|
103
140
|
widgetStoreActions.unregisterTool(id, RELATIVE_DATA_CONFIG_TOOL_ID)
|
|
141
|
+
}
|
|
104
142
|
}, [id, order, defaultIsRelative])
|
|
105
143
|
|
|
106
144
|
const handleToggle = useCallback(() => {
|
|
107
145
|
const newIsRelative = !isRelative
|
|
108
|
-
|
|
146
|
+
percentFormatterRef.current = undefined
|
|
109
147
|
|
|
110
148
|
if (newIsRelative) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
max?: number
|
|
115
|
-
}
|
|
116
|
-
widgetStoreActions.updateToolConfig(id, RELATIVE_DATA_CONFIG_TOOL_ID, {
|
|
149
|
+
// Capture current formatter/max in store before switching to relative
|
|
150
|
+
const widget = widgetStoreActions.getWidget(id)
|
|
151
|
+
widgetStoreActions.setWidget(id, {
|
|
117
152
|
isRelative: true,
|
|
118
153
|
originalFormatter: widget?.formatter,
|
|
119
|
-
originalMax: widget?.max,
|
|
120
|
-
locale: widget?.locale,
|
|
154
|
+
originalMax: (widget as unknown as Record<string, unknown>)?.max,
|
|
121
155
|
})
|
|
122
156
|
} else {
|
|
123
|
-
|
|
124
|
-
widgetStoreActions.updateToolConfig(id, RELATIVE_DATA_CONFIG_TOOL_ID, {
|
|
125
|
-
isRelative: false,
|
|
126
|
-
})
|
|
157
|
+
widgetStoreActions.setWidget(id, { isRelative: false })
|
|
127
158
|
}
|
|
128
159
|
}, [isRelative, id])
|
|
129
160
|
|
|
@@ -57,42 +57,44 @@ export function Searcher({
|
|
|
57
57
|
[id],
|
|
58
58
|
)
|
|
59
59
|
|
|
60
|
-
// Register tool
|
|
60
|
+
// Register tool once — fn reads searchText from the store at execution time.
|
|
61
|
+
// Enabled is synced separately to avoid full re-registration on toggle.
|
|
61
62
|
useEffect(() => {
|
|
62
63
|
widgetStoreActions.registerTool(id, {
|
|
63
64
|
id: SEARCHER_TOOL_ID,
|
|
64
65
|
order,
|
|
65
|
-
enabled,
|
|
66
|
-
fn: async (data
|
|
67
|
-
const
|
|
66
|
+
enabled: false,
|
|
67
|
+
fn: async (data) => {
|
|
68
|
+
const widget = widgetStoreActions.getWidget<SearcherState>(id)
|
|
69
|
+
const currentSearchText = widget?.searchText ?? ''
|
|
68
70
|
|
|
69
71
|
// Execute filter (can be sync or async)
|
|
70
|
-
const result = filter(data as EchartWidgetData,
|
|
72
|
+
const result = filter(data as EchartWidgetData, currentSearchText)
|
|
71
73
|
|
|
72
74
|
// Return result directly (pipeline will handle Promise)
|
|
73
75
|
return result
|
|
74
76
|
},
|
|
75
|
-
config: { searchText },
|
|
76
77
|
disables: [LOCK_SELECTION_TOOL_ID],
|
|
77
78
|
})
|
|
78
79
|
|
|
79
80
|
return () => widgetStoreActions.unregisterTool(id, SEARCHER_TOOL_ID)
|
|
80
|
-
}, [id, order,
|
|
81
|
+
}, [id, order, filter])
|
|
81
82
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
// Sync enabled from store — lightweight, no re-registration
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
widgetStoreActions.setToolEnabled(id, SEARCHER_TOOL_ID, enabled)
|
|
86
|
+
}, [id, enabled])
|
|
87
|
+
|
|
88
|
+
// Trigger pipeline re-execution when search text changes (debounced).
|
|
89
|
+
// The fn reads searchText from the store, so we just need to trigger the pipeline.
|
|
90
|
+
const debouncedTriggerPipeline = useCallback(() => {
|
|
91
|
+
if (debounceTimeoutRef.current) {
|
|
92
|
+
clearTimeout(debounceTimeoutRef.current)
|
|
93
|
+
}
|
|
94
|
+
debounceTimeoutRef.current = setTimeout(() => {
|
|
95
|
+
widgetStoreActions.triggerToolPipeline(id)
|
|
96
|
+
}, debounceDelay)
|
|
97
|
+
}, [id, debounceDelay])
|
|
96
98
|
|
|
97
99
|
// Auto-focus when enabled becomes true
|
|
98
100
|
useEffect(() => {
|
|
@@ -117,16 +119,14 @@ export function Searcher({
|
|
|
117
119
|
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
118
120
|
const newValue = event.target.value
|
|
119
121
|
setSearchText(newValue)
|
|
120
|
-
|
|
122
|
+
debouncedTriggerPipeline()
|
|
121
123
|
},
|
|
122
|
-
[
|
|
124
|
+
[debouncedTriggerPipeline, setSearchText],
|
|
123
125
|
)
|
|
124
126
|
|
|
125
127
|
const handleClear = useCallback(() => {
|
|
126
128
|
setSearchText('')
|
|
127
|
-
widgetStoreActions.
|
|
128
|
-
searchText: '',
|
|
129
|
-
})
|
|
129
|
+
widgetStoreActions.triggerToolPipeline(id)
|
|
130
130
|
if (inputRef.current) {
|
|
131
131
|
inputRef.current.focus()
|
|
132
132
|
}
|
|
@@ -187,10 +187,8 @@ const defaultFilterFn: SearcherFilterFn = (
|
|
|
187
187
|
return Promise.resolve(
|
|
188
188
|
data.map((series) =>
|
|
189
189
|
series.filter((item) =>
|
|
190
|
-
Object.values(item).some(
|
|
191
|
-
(value)
|
|
192
|
-
typeof value === 'string' &&
|
|
193
|
-
value.toLowerCase().includes(lowerSearch),
|
|
190
|
+
Object.values(item).some((value) =>
|
|
191
|
+
String(value).toLowerCase().includes(lowerSearch),
|
|
194
192
|
),
|
|
195
193
|
),
|
|
196
194
|
),
|
|
@@ -54,13 +54,13 @@ export function StackToggle({
|
|
|
54
54
|
const effectiveDefaultIsStacked = hasStackInSeries || defaultIsStacked
|
|
55
55
|
const isStacked = storeIsStacked ?? effectiveDefaultIsStacked
|
|
56
56
|
|
|
57
|
-
// Register config tool
|
|
57
|
+
// Register config tool once — fn has no closure deps
|
|
58
58
|
useEffect(() => {
|
|
59
59
|
widgetStoreActions.registerTool(id, {
|
|
60
60
|
id: STACK_TOGGLE_TOOL_ID,
|
|
61
61
|
type: 'config',
|
|
62
62
|
order: 10,
|
|
63
|
-
enabled:
|
|
63
|
+
enabled: false,
|
|
64
64
|
fn: (currentConfig: unknown) => {
|
|
65
65
|
const config = currentConfig as Record<string, unknown>
|
|
66
66
|
const option = config.option as EchartOptionsProps | undefined
|
|
@@ -82,6 +82,15 @@ export function StackToggle({
|
|
|
82
82
|
},
|
|
83
83
|
})
|
|
84
84
|
return () => widgetStoreActions.unregisterTool(id, STACK_TOGGLE_TOOL_ID)
|
|
85
|
+
}, [id])
|
|
86
|
+
|
|
87
|
+
// Sync enabled from store — lightweight, no re-registration
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
widgetStoreActions.setToolEnabled(
|
|
90
|
+
id,
|
|
91
|
+
STACK_TOGGLE_TOOL_ID,
|
|
92
|
+
isStacked && hasMultiSeries,
|
|
93
|
+
)
|
|
85
94
|
}, [id, isStacked, hasMultiSeries])
|
|
86
95
|
|
|
87
96
|
// Initialize store with default value only if not already configured
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from '@mui/icons-material'
|
|
6
6
|
import { useEffect, useCallback } from 'react'
|
|
7
7
|
import { widgetStoreActions } from '../../stores/widget-store'
|
|
8
|
-
import type { ZoomToggleProps } from './types'
|
|
8
|
+
import type { ZoomToggleProps, ZoomState } from './types'
|
|
9
9
|
import { styles } from './style'
|
|
10
10
|
import { Tooltip } from '../../../components'
|
|
11
11
|
import { getEChartZoomConfig } from '../../echart/utils'
|
|
@@ -20,6 +20,10 @@ export const ZOOM_TOGGLE_TOOL_ID = 'zoom-toggle'
|
|
|
20
20
|
* Registers as a config pipeline tool so that zoom configuration is automatically
|
|
21
21
|
* re-applied when the base config is updated (e.g., by WidgetLoader).
|
|
22
22
|
*
|
|
23
|
+
* Zoom state (enabled, range) is stored in the widget store root, and the tool
|
|
24
|
+
* derives its `enabled` flag from the store. This keeps state accessible across
|
|
25
|
+
* component instances.
|
|
26
|
+
*
|
|
23
27
|
* When zoom is active, displays an inline reset button to disable zoom.
|
|
24
28
|
* Only intended for use in fullscreen ToolbarActions for bar and histogram widgets.
|
|
25
29
|
*
|
|
@@ -45,19 +49,26 @@ export function ZoomToggle({
|
|
|
45
49
|
}: ZoomToggleProps) {
|
|
46
50
|
const theme = useTheme()
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
// Read zoom state from widget store root — single source of truth
|
|
53
|
+
const { zoom, zoomStart, zoomEnd } = useWidgetSelector(id, (w) => ({
|
|
54
|
+
zoom: (w as ZoomState | undefined)?.zoom ?? defaultZoom,
|
|
55
|
+
zoomStart: (w as ZoomState | undefined)?.zoomStart ?? defaultZoomStart,
|
|
56
|
+
zoomEnd: (w as ZoomState | undefined)?.zoomEnd ?? defaultZoomEnd,
|
|
57
|
+
}))
|
|
58
|
+
|
|
59
|
+
// Initialize store with default values on mount
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const current = widgetStoreActions.getWidget<ZoomState>(id)
|
|
62
|
+
if (current?.zoom === undefined) {
|
|
63
|
+
widgetStoreActions.setWidget(id, {
|
|
64
|
+
zoom: defaultZoom,
|
|
65
|
+
zoomStart: defaultZoomStart,
|
|
66
|
+
zoomEnd: defaultZoomEnd,
|
|
67
|
+
})
|
|
57
68
|
}
|
|
58
|
-
})
|
|
69
|
+
}, [id, defaultZoom, defaultZoomStart, defaultZoomEnd])
|
|
59
70
|
|
|
60
|
-
// Handle dataZoom event
|
|
71
|
+
// Handle dataZoom event — update store (source of truth)
|
|
61
72
|
const handleDataZoom = useCallback(
|
|
62
73
|
(event: unknown) => {
|
|
63
74
|
const zoomEvent = event as {
|
|
@@ -73,41 +84,32 @@ export function ZoomToggle({
|
|
|
73
84
|
const end = zoomEvent.end ?? zoomEvent.batch?.[0]?.end
|
|
74
85
|
|
|
75
86
|
if (start !== undefined && end !== undefined) {
|
|
76
|
-
widgetStoreActions.
|
|
77
|
-
|
|
78
|
-
start,
|
|
79
|
-
end,
|
|
87
|
+
widgetStoreActions.setWidget(id, {
|
|
88
|
+
zoom: true,
|
|
89
|
+
zoomStart: start,
|
|
90
|
+
zoomEnd: end,
|
|
80
91
|
})
|
|
81
92
|
}
|
|
82
93
|
},
|
|
83
94
|
[id],
|
|
84
95
|
)
|
|
85
96
|
|
|
86
|
-
// Register config tool
|
|
97
|
+
// Register config tool once — fn reads zoom range from the store at execution time.
|
|
87
98
|
useEffect(() => {
|
|
88
|
-
const existingTool = widgetStoreActions
|
|
89
|
-
.getWidget(id)
|
|
90
|
-
?.registeredTools?.find((tool) => tool.id === ZOOM_TOGGLE_TOOL_ID)
|
|
91
|
-
|
|
92
|
-
const initialEnabled = existingTool?.enabled ?? defaultZoom
|
|
93
|
-
const initialStart =
|
|
94
|
-
(existingTool?.config?.start as number | undefined) ?? defaultZoomStart
|
|
95
|
-
const initialEnd =
|
|
96
|
-
(existingTool?.config?.end as number | undefined) ?? defaultZoomEnd
|
|
97
|
-
|
|
98
99
|
widgetStoreActions.registerTool(id, {
|
|
99
100
|
id: ZOOM_TOGGLE_TOOL_ID,
|
|
100
101
|
type: 'config',
|
|
101
102
|
order: 20,
|
|
102
|
-
enabled:
|
|
103
|
-
fn: (currentConfig
|
|
103
|
+
enabled: defaultZoom,
|
|
104
|
+
fn: (currentConfig) => {
|
|
104
105
|
const config = currentConfig as Record<string, unknown>
|
|
105
106
|
const option = config.option as EchartOptionsProps | undefined
|
|
106
107
|
const currentOnEvents =
|
|
107
108
|
(config.onEvents as Record<string, unknown> | undefined) ?? {}
|
|
108
109
|
|
|
109
|
-
const
|
|
110
|
-
const
|
|
110
|
+
const widget = widgetStoreActions.getWidget<ZoomState>(id)
|
|
111
|
+
const start = widget?.zoomStart ?? 0
|
|
112
|
+
const end = widget?.zoomEnd ?? 100
|
|
111
113
|
|
|
112
114
|
const legend = option?.legend as { show?: boolean } | undefined
|
|
113
115
|
const hasLegend = legend?.show !== false && legend !== undefined
|
|
@@ -135,8 +137,6 @@ export function ZoomToggle({
|
|
|
135
137
|
|
|
136
138
|
const gridBottom = currentGridBottom + sliderHeight + sliderGap
|
|
137
139
|
|
|
138
|
-
const onEventsWithoutDataZoom = { ...currentOnEvents }
|
|
139
|
-
delete onEventsWithoutDataZoom.dataZoom
|
|
140
140
|
const onEvents = { ...currentOnEvents, dataZoom: handleDataZoom }
|
|
141
141
|
|
|
142
142
|
return {
|
|
@@ -149,28 +149,36 @@ export function ZoomToggle({
|
|
|
149
149
|
onEvents,
|
|
150
150
|
}
|
|
151
151
|
},
|
|
152
|
-
config: {
|
|
153
|
-
start: initialStart,
|
|
154
|
-
end: initialEnd,
|
|
155
|
-
},
|
|
156
152
|
})
|
|
157
153
|
return () => widgetStoreActions.unregisterTool(id, ZOOM_TOGGLE_TOOL_ID)
|
|
158
154
|
}, [id, theme, handleDataZoom, defaultZoom, defaultZoomStart, defaultZoomEnd])
|
|
159
155
|
|
|
156
|
+
// Sync enabled from store — lightweight, no re-registration
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
widgetStoreActions.setToolEnabled(id, ZOOM_TOGGLE_TOOL_ID, zoom)
|
|
159
|
+
}, [id, zoom])
|
|
160
|
+
|
|
161
|
+
// Trigger pipeline when zoom range changes — fn reads from store at execution time,
|
|
162
|
+
// but the pipeline only re-runs when registeredTools reference changes.
|
|
163
|
+
// setToolEnabled handles the zoom on/off case, this handles range-only changes.
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
widgetStoreActions.triggerToolPipeline(id)
|
|
166
|
+
}, [id, zoomStart, zoomEnd])
|
|
167
|
+
|
|
160
168
|
const handleToggle = () => {
|
|
161
169
|
const newZoom = !zoom
|
|
162
|
-
widgetStoreActions.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
170
|
+
widgetStoreActions.setWidget(id, {
|
|
171
|
+
zoom: newZoom,
|
|
172
|
+
zoomStart: newZoom ? zoomStart : 0,
|
|
173
|
+
zoomEnd: newZoom ? zoomEnd : 100,
|
|
166
174
|
})
|
|
167
175
|
}
|
|
168
176
|
|
|
169
177
|
const handleReset = () => {
|
|
170
|
-
widgetStoreActions.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
widgetStoreActions.setWidget(id, {
|
|
179
|
+
zoom: true,
|
|
180
|
+
zoomStart: defaultZoomStart,
|
|
181
|
+
zoomEnd: defaultZoomEnd,
|
|
174
182
|
})
|
|
175
183
|
}
|
|
176
184
|
|
|
@@ -2,12 +2,10 @@ import { Box, useTheme } from '@mui/material'
|
|
|
2
2
|
import { useWidgetSelector } from '../stores/use-widget-selector'
|
|
3
3
|
import { styles } from './style'
|
|
4
4
|
import type { CategoryUIProps, CategoryWidgetState } from './types'
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
CategoryLegend,
|
|
10
|
-
} from './components'
|
|
5
|
+
import { CategoryRowSingle } from './components/category-row-single'
|
|
6
|
+
import { CategoryRowMulti } from './components/category-row-multi'
|
|
7
|
+
import { CategoryRowOther } from './components/category-row-other'
|
|
8
|
+
import { CategoryLegend } from './components/category-legend'
|
|
11
9
|
import { useState } from 'react'
|
|
12
10
|
import { defaultFormatter, defaultLabelFormatter } from '../utils/formatter'
|
|
13
11
|
import { useWidgetRef } from '../../hooks'
|
|
@@ -2,21 +2,20 @@ export { CategoryUI } from './category-ui'
|
|
|
2
2
|
export { CategorySkeleton } from './skeleton'
|
|
3
3
|
export { categoryConfig, categoryDownloadConfig } from './config'
|
|
4
4
|
|
|
5
|
-
export {
|
|
6
|
-
|
|
7
|
-
CategoryRowSingle,
|
|
8
|
-
CategoryRowMulti,
|
|
9
|
-
CategoryLegend,
|
|
10
|
-
CategoryRowOther,
|
|
11
|
-
} from './components'
|
|
5
|
+
export { CategoryBar } from './components/category-bar'
|
|
6
|
+
export type { CategoryBarProps } from './components/category-bar'
|
|
12
7
|
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from './components'
|
|
8
|
+
export { CategoryRowSingle } from './components/category-row-single'
|
|
9
|
+
export type { CategoryRowSingleProps } from './components/category-row-single'
|
|
10
|
+
|
|
11
|
+
export { CategoryRowMulti } from './components/category-row-multi'
|
|
12
|
+
export type { CategoryRowMultiProps } from './components/category-row-multi'
|
|
13
|
+
|
|
14
|
+
export { CategoryRowOther } from './components/category-row-other'
|
|
15
|
+
export type { CategoryRowOtherProps } from './components/category-row-other'
|
|
16
|
+
|
|
17
|
+
export { CategoryLegend } from './components/category-legend'
|
|
18
|
+
export type { CategoryLegendProps } from './components/category-legend'
|
|
20
19
|
|
|
21
20
|
export type {
|
|
22
21
|
CategoryUIProps,
|