@carto/ps-react-ui 4.4.3 → 4.5.0
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-Dqu78h2a.js → download-config-DemuQ3Jm.js} +9 -10
- package/dist/{download-config-Dqu78h2a.js.map → download-config-DemuQ3Jm.js.map} +1 -1
- package/dist/error-Cj8eUMrl.js +40 -0
- package/dist/error-Cj8eUMrl.js.map +1 -0
- package/dist/no-data-DkIt7Qt1.js +61 -0
- package/dist/no-data-DkIt7Qt1.js.map +1 -0
- package/dist/row-D4VOhcNI.js +34 -0
- package/dist/row-D4VOhcNI.js.map +1 -0
- package/dist/series-Bola3CmD.js +90 -0
- package/dist/series-Bola3CmD.js.map +1 -0
- package/dist/types/widgets/echart/shared-resize-observer.d.ts +12 -0
- package/dist/types/widgets/stores/index.d.ts +2 -1
- package/dist/types/widgets/stores/use-widget-selector.d.ts +35 -0
- package/dist/types/widgets/stores/widget-store-performance.test.d.ts +1 -0
- package/dist/types/widgets/stores/widget-store.d.ts +49 -27
- package/dist/types/widgets/table/types.d.ts +1 -1
- package/dist/use-widget-ref-BFazQvJK.js +22 -0
- package/dist/use-widget-ref-BFazQvJK.js.map +1 -0
- package/dist/use-widget-selector-DqRmWQ1K.js +12 -0
- package/dist/use-widget-selector-DqRmWQ1K.js.map +1 -0
- package/dist/widget-store-CIrb9RKP.js +263 -0
- package/dist/widget-store-CIrb9RKP.js.map +1 -0
- package/dist/widgets/actions.js +783 -817
- package/dist/widgets/actions.js.map +1 -1
- package/dist/widgets/bar.js +2 -2
- package/dist/widgets/category.js +254 -257
- package/dist/widgets/category.js.map +1 -1
- package/dist/widgets/echart.js +109 -99
- package/dist/widgets/echart.js.map +1 -1
- package/dist/widgets/error.js +1 -1
- package/dist/widgets/formula.js +71 -63
- package/dist/widgets/formula.js.map +1 -1
- package/dist/widgets/histogram.js +7 -8
- package/dist/widgets/histogram.js.map +1 -1
- package/dist/widgets/loader.js +53 -60
- package/dist/widgets/loader.js.map +1 -1
- package/dist/widgets/markdown.js +51 -50
- package/dist/widgets/markdown.js.map +1 -1
- package/dist/widgets/no-data.js +1 -1
- package/dist/widgets/pie.js +2 -2
- package/dist/widgets/range.js +146 -144
- package/dist/widgets/range.js.map +1 -1
- package/dist/widgets/scatterplot.js +2 -2
- package/dist/widgets/skeleton-loader.js +18 -17
- package/dist/widgets/skeleton-loader.js.map +1 -1
- package/dist/widgets/spread.js +110 -94
- package/dist/widgets/spread.js.map +1 -1
- package/dist/widgets/stores.js +5 -2
- package/dist/widgets/stores.js.map +1 -1
- package/dist/widgets/table.js +422 -436
- package/dist/widgets/table.js.map +1 -1
- package/dist/widgets/timeseries.js +2 -2
- package/dist/widgets/utils.js +1 -1
- package/dist/widgets/wrapper.js +156 -158
- package/dist/widgets/wrapper.js.map +1 -1
- package/dist/widgets.js +4 -4
- package/package.json +1 -1
- package/src/hooks/use-widget-ref.ts +3 -4
- package/src/widgets/actions/brush-toggle/brush-toggle.tsx +18 -32
- package/src/widgets/actions/change-column/change-column.tsx +15 -15
- package/src/widgets/actions/change-column/sortable-column-item.tsx +3 -1
- package/src/widgets/actions/download/download.tsx +4 -3
- package/src/widgets/actions/fullscreen/fullscreen.tsx +7 -11
- package/src/widgets/actions/lock-selection/lock-selection.tsx +12 -15
- package/src/widgets/actions/relative-data/relative-data.tsx +22 -26
- package/src/widgets/actions/searcher/searcher-toggle.tsx +11 -12
- package/src/widgets/actions/searcher/searcher.tsx +20 -21
- package/src/widgets/actions/stack-toggle/stack-toggle.tsx +15 -21
- package/src/widgets/actions/zoom-toggle/zoom-toggle.tsx +27 -43
- package/src/widgets/category/category-ui.tsx +27 -31
- package/src/widgets/echart/echart-ui.test.tsx +20 -16
- package/src/widgets/echart/echart-ui.tsx +6 -12
- package/src/widgets/echart/echart.tsx +13 -27
- package/src/widgets/echart/shared-resize-observer.ts +45 -0
- package/src/widgets/error/error.tsx +7 -9
- package/src/widgets/formula/components/prefix.tsx +4 -6
- package/src/widgets/formula/components/row.tsx +4 -4
- package/src/widgets/formula/components/series.tsx +4 -6
- package/src/widgets/formula/components/suffix.tsx +4 -6
- package/src/widgets/formula/components/value.tsx +9 -16
- package/src/widgets/loader/loader.tsx +31 -44
- package/src/widgets/markdown/markdown.tsx +4 -7
- package/src/widgets/no-data/no-data.tsx +7 -10
- package/src/widgets/range/components/range-item.tsx +20 -18
- package/src/widgets/skeleton-loader/skeleton-loader.tsx +2 -5
- package/src/widgets/spread/components/max-value.tsx +14 -16
- package/src/widgets/spread/components/min-value.tsx +14 -16
- package/src/widgets/stores/index.ts +2 -1
- package/src/widgets/stores/use-widget-selector.ts +47 -0
- package/src/widgets/stores/widget-store-performance.test.ts +750 -0
- package/src/widgets/stores/widget-store.test.ts +81 -0
- package/src/widgets/stores/widget-store.ts +225 -44
- package/src/widgets/table/config.ts +0 -1
- package/src/widgets/table/hooks/use-pagination.ts +28 -52
- package/src/widgets/table/hooks/use-selection.ts +20 -24
- package/src/widgets/table/hooks/use-sort.ts +22 -39
- package/src/widgets/table/types.ts +1 -1
- package/src/widgets/wrapper/wrapper-ui.tsx +12 -13
- package/src/widgets/wrapper/wrapper.tsx +4 -6
- package/dist/error-CEkRPccv.js +0 -39
- package/dist/error-CEkRPccv.js.map +0 -1
- package/dist/no-data-hR3KcJ-_.js +0 -60
- package/dist/no-data-hR3KcJ-_.js.map +0 -1
- package/dist/row-DTCV0Ocm.js +0 -35
- package/dist/row-DTCV0Ocm.js.map +0 -1
- package/dist/series-CYNOu2Ju.js +0 -91
- package/dist/series-CYNOu2Ju.js.map +0 -1
- package/dist/use-widget-ref-wtFLDFCD.js +0 -25
- package/dist/use-widget-ref-wtFLDFCD.js.map +0 -1
- package/dist/widget-store-CzDt8oSK.js +0 -163
- package/dist/widget-store-CzDt8oSK.js.map +0 -1
|
@@ -21,14 +21,14 @@ import {
|
|
|
21
21
|
useState,
|
|
22
22
|
type MouseEvent,
|
|
23
23
|
} from 'react'
|
|
24
|
-
import {
|
|
24
|
+
import { widgetStoreActions } from '../../stores/widget-store'
|
|
25
25
|
import type { ChangeColumnProps } from './types'
|
|
26
26
|
import { actionButtonStyles } from '../shared/styles'
|
|
27
27
|
import { Tooltip } from '../../../components'
|
|
28
28
|
import type { TableColumn, TableWidgetState } from '../../table/types'
|
|
29
29
|
import { ChangeColumnIcon } from './change-column-icon'
|
|
30
30
|
import { SortableColumnItem } from './sortable-column-item'
|
|
31
|
-
import {
|
|
31
|
+
import { useWidgetSelector } from '../../stores/use-widget-selector'
|
|
32
32
|
|
|
33
33
|
export const CHANGE_COLUMN_TOOL_ID = 'change-column'
|
|
34
34
|
|
|
@@ -57,12 +57,9 @@ export function ChangeColumn({
|
|
|
57
57
|
MenuProps,
|
|
58
58
|
}: ChangeColumnProps) {
|
|
59
59
|
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const columns = useWidgetStore(
|
|
64
|
-
useShallow((state) => state.getWidget<TableWidgetState>(id)?.columns),
|
|
65
|
-
)
|
|
60
|
+
const { columns } = useWidgetSelector(id, (w) => ({
|
|
61
|
+
columns: (w as TableWidgetState | undefined)?.columns,
|
|
62
|
+
}))
|
|
66
63
|
|
|
67
64
|
/**
|
|
68
65
|
* Config tool function that reorders columns to match the current widget state.
|
|
@@ -72,9 +69,7 @@ export function ChangeColumn({
|
|
|
72
69
|
*/
|
|
73
70
|
const reorderFn = useCallback(
|
|
74
71
|
(currentConfig: unknown): unknown => {
|
|
75
|
-
const widgetState =
|
|
76
|
-
.getState()
|
|
77
|
-
.getWidget<TableWidgetState>(id)
|
|
72
|
+
const widgetState = widgetStoreActions.getWidget<TableWidgetState>(id)
|
|
78
73
|
const currentColumns = widgetState?.columns
|
|
79
74
|
if (!currentColumns || currentColumns.length === 0) return currentConfig
|
|
80
75
|
|
|
@@ -130,15 +125,15 @@ export function ChangeColumn({
|
|
|
130
125
|
|
|
131
126
|
// Register config tool on mount
|
|
132
127
|
useEffect(() => {
|
|
133
|
-
registerTool(id, {
|
|
128
|
+
widgetStoreActions.registerTool(id, {
|
|
134
129
|
id: CHANGE_COLUMN_TOOL_ID,
|
|
135
130
|
type: 'config',
|
|
136
131
|
order: 100,
|
|
137
132
|
enabled: true,
|
|
138
133
|
fn: reorderFn,
|
|
139
134
|
})
|
|
140
|
-
return () => unregisterTool(id, CHANGE_COLUMN_TOOL_ID)
|
|
141
|
-
}, [id,
|
|
135
|
+
return () => widgetStoreActions.unregisterTool(id, CHANGE_COLUMN_TOOL_ID)
|
|
136
|
+
}, [id, reorderFn])
|
|
142
137
|
|
|
143
138
|
const handleToggle = useCallback((event: MouseEvent<HTMLElement>) => {
|
|
144
139
|
event.stopPropagation()
|
|
@@ -156,7 +151,7 @@ export function ChangeColumn({
|
|
|
156
151
|
const newIndex = columns.findIndex((col) => col.id === over.id)
|
|
157
152
|
if (oldIndex !== -1 && newIndex !== -1) {
|
|
158
153
|
const newColumns = arrayMove(columns, oldIndex, newIndex)
|
|
159
|
-
setWidget(id, { columns: newColumns })
|
|
154
|
+
widgetStoreActions.setWidget(id, { columns: newColumns })
|
|
160
155
|
}
|
|
161
156
|
}
|
|
162
157
|
|
|
@@ -201,6 +196,11 @@ export function ChangeColumn({
|
|
|
201
196
|
onClose={handleClose}
|
|
202
197
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
|
203
198
|
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
|
|
199
|
+
slotProps={{
|
|
200
|
+
paper: {
|
|
201
|
+
sx: { overflow: 'hidden' },
|
|
202
|
+
},
|
|
203
|
+
}}
|
|
204
204
|
{...MenuProps}
|
|
205
205
|
>
|
|
206
206
|
<SortableContext
|
|
@@ -22,7 +22,9 @@ export function SortableColumnItem({ column }: SortableColumnItemProps) {
|
|
|
22
22
|
} = useSortable({ id: column.id })
|
|
23
23
|
|
|
24
24
|
const style = {
|
|
25
|
-
transform: CSS.Transform.toString(
|
|
25
|
+
transform: CSS.Transform.toString(
|
|
26
|
+
transform ? { ...transform, x: 0 } : null,
|
|
27
|
+
),
|
|
26
28
|
transition,
|
|
27
29
|
opacity: isDragging ? 0.5 : 1,
|
|
28
30
|
cursor: isDragging ? 'grabbing' : 'grab',
|
|
@@ -9,8 +9,7 @@ import {
|
|
|
9
9
|
import type { DownloadItem, DownloadProps } from './types'
|
|
10
10
|
import { FileDownloadOutlined } from '@mui/icons-material'
|
|
11
11
|
import { useState, type MouseEvent } from 'react'
|
|
12
|
-
import {
|
|
13
|
-
import { useShallow } from 'zustand/shallow'
|
|
12
|
+
import { useWidgetSelector } from '../../stores/use-widget-selector'
|
|
14
13
|
|
|
15
14
|
const EMPTY_LABELS: NonNullable<DownloadProps['labels']> = {}
|
|
16
15
|
|
|
@@ -32,7 +31,9 @@ export function Download({
|
|
|
32
31
|
Icon,
|
|
33
32
|
IconButtonProps,
|
|
34
33
|
}: DownloadProps) {
|
|
35
|
-
const data =
|
|
34
|
+
const { data } = useWidgetSelector(id, (w) => ({
|
|
35
|
+
data: w?.data,
|
|
36
|
+
}))
|
|
36
37
|
|
|
37
38
|
const [isDownloading, setIsDownloading] = useState(false)
|
|
38
39
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
IconButton,
|
|
6
6
|
Typography,
|
|
7
7
|
} from '@mui/material'
|
|
8
|
-
import {
|
|
8
|
+
import { widgetStoreActions } from '../../stores/widget-store'
|
|
9
|
+
import { useWidgetSelector } from '../../stores/use-widget-selector'
|
|
9
10
|
import { Close, FullscreenOutlined } from '@mui/icons-material'
|
|
10
11
|
import type {
|
|
11
12
|
FullScreenConfig,
|
|
@@ -13,7 +14,6 @@ import type {
|
|
|
13
14
|
FullScreenState,
|
|
14
15
|
} from './types'
|
|
15
16
|
import { styles } from './styles'
|
|
16
|
-
import { useShallow } from 'zustand/shallow'
|
|
17
17
|
|
|
18
18
|
const EMPTY_DIALOG_CONTENT_PROPS: NonNullable<
|
|
19
19
|
FullScreenProps['DialogContentProps']
|
|
@@ -44,16 +44,12 @@ export function FullScreen({
|
|
|
44
44
|
} = EMPTY_DIALOG_CONTENT_PROPS,
|
|
45
45
|
DialogProps,
|
|
46
46
|
}: FullScreenProps) {
|
|
47
|
-
const isFullScreen =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
useShallow((state) => state.getWidget<FullScreenState>(id)?.title),
|
|
52
|
-
)
|
|
53
|
-
const setWidget = useWidgetStore((state) => state.setWidget)
|
|
54
|
-
|
|
47
|
+
const { isFullScreen, title } = useWidgetSelector(id, (w) => ({
|
|
48
|
+
isFullScreen: (w as FullScreenState | undefined)?.isFullScreen,
|
|
49
|
+
title: (w as FullScreenState | undefined)?.title,
|
|
50
|
+
}))
|
|
55
51
|
const updateFullScreenConfig = (updates: Partial<FullScreenConfig>) => {
|
|
56
|
-
setWidget<FullScreenState>(id, {
|
|
52
|
+
widgetStoreActions.setWidget<FullScreenState>(id, {
|
|
57
53
|
isFullScreen: updates.isFullScreen,
|
|
58
54
|
})
|
|
59
55
|
}
|
|
@@ -4,8 +4,8 @@ import { useCallback, useEffect, useMemo } from 'react'
|
|
|
4
4
|
import type { LockSelectionProps, LockSelectionState } from './types'
|
|
5
5
|
import { actionButtonStyles } from '../shared/styles'
|
|
6
6
|
import { Tooltip } from '../../../components'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { widgetStoreActions } from '../../stores/widget-store'
|
|
8
|
+
import { useWidgetSelector } from '../../stores/use-widget-selector'
|
|
9
9
|
import type { EchartWidgetData } from '../../echart/types'
|
|
10
10
|
|
|
11
11
|
export const LOCK_SELECTION_TOOL_ID = 'lock-selection'
|
|
@@ -34,12 +34,9 @@ export function LockSelection({
|
|
|
34
34
|
Icon,
|
|
35
35
|
IconButtonProps,
|
|
36
36
|
}: LockSelectionProps) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const storeIsLocked = useWidgetStore(
|
|
41
|
-
useShallow((state) => state.getWidget<LockSelectionState>(id)?.isLocked),
|
|
42
|
-
)
|
|
37
|
+
const { storeIsLocked } = useWidgetSelector(id, (w) => ({
|
|
38
|
+
storeIsLocked: (w as LockSelectionState | undefined)?.isLocked,
|
|
39
|
+
}))
|
|
43
40
|
|
|
44
41
|
const isLocked = storeIsLocked ?? false
|
|
45
42
|
const lockedItems = useMemo(
|
|
@@ -47,9 +44,9 @@ export function LockSelection({
|
|
|
47
44
|
[isLocked, selectedItems],
|
|
48
45
|
)
|
|
49
46
|
|
|
50
|
-
// Register tool
|
|
47
|
+
// Register tool with all reactive deps — store's no-op detection handles performance
|
|
51
48
|
useEffect(() => {
|
|
52
|
-
registerTool(id, {
|
|
49
|
+
widgetStoreActions.registerTool(id, {
|
|
53
50
|
id: LOCK_SELECTION_TOOL_ID,
|
|
54
51
|
order,
|
|
55
52
|
enabled: isLocked,
|
|
@@ -62,24 +59,24 @@ export function LockSelection({
|
|
|
62
59
|
config: { lockedItems },
|
|
63
60
|
})
|
|
64
61
|
|
|
65
|
-
return () => unregisterTool(id, LOCK_SELECTION_TOOL_ID)
|
|
66
|
-
}, [id, order,
|
|
62
|
+
return () => widgetStoreActions.unregisterTool(id, LOCK_SELECTION_TOOL_ID)
|
|
63
|
+
}, [id, order, isLocked, lockedItems])
|
|
67
64
|
|
|
68
65
|
const handleToggle = useCallback(() => {
|
|
69
66
|
if (isLocked) {
|
|
70
67
|
// Unlock: clear locked items and disable tool
|
|
71
|
-
setWidget(id, {
|
|
68
|
+
widgetStoreActions.setWidget(id, {
|
|
72
69
|
isLocked: false,
|
|
73
70
|
lockedItems: [],
|
|
74
71
|
})
|
|
75
72
|
} else {
|
|
76
73
|
// Lock: save selected items and enable tool
|
|
77
|
-
setWidget(id, {
|
|
74
|
+
widgetStoreActions.setWidget(id, {
|
|
78
75
|
isLocked: true,
|
|
79
76
|
lockedItems: selectedItems,
|
|
80
77
|
})
|
|
81
78
|
}
|
|
82
|
-
}, [id, isLocked, selectedItems
|
|
79
|
+
}, [id, isLocked, selectedItems])
|
|
83
80
|
|
|
84
81
|
// Don't render if no selections
|
|
85
82
|
if (selectedItems.length === 0) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { IconButton } from '@mui/material'
|
|
2
2
|
import { PercentOutlined } from '@mui/icons-material'
|
|
3
3
|
import { useCallback, useEffect, useRef } from 'react'
|
|
4
|
-
import {
|
|
4
|
+
import { widgetStoreActions } from '../../stores/widget-store'
|
|
5
|
+
import { useWidgetSelector } from '../../stores/use-widget-selector'
|
|
5
6
|
import type { RelativeDataProps } from './types'
|
|
6
7
|
import { actionButtonStyles } from '../shared/styles'
|
|
7
8
|
import { Tooltip } from '../../../components'
|
|
@@ -37,24 +38,18 @@ export function RelativeData({
|
|
|
37
38
|
const percentFormatterRef = useRef<((value: number) => string) | undefined>(
|
|
38
39
|
undefined,
|
|
39
40
|
)
|
|
40
|
-
const getWidget = useWidgetStore((state) => state.getWidget)
|
|
41
|
-
const registerTool = useWidgetStore((state) => state.registerTool)
|
|
42
|
-
const unregisterTool = useWidgetStore((state) => state.unregisterTool)
|
|
43
|
-
const setToolEnabled = useWidgetStore((state) => state.setToolEnabled)
|
|
44
|
-
const updateToolConfig = useWidgetStore((state) => state.updateToolConfig)
|
|
45
41
|
|
|
46
|
-
const storeIsRelative =
|
|
47
|
-
(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
)
|
|
42
|
+
const { storeIsRelative } = useWidgetSelector(id, (w) => ({
|
|
43
|
+
storeIsRelative: w?.registeredTools?.find(
|
|
44
|
+
(t) => t.id === RELATIVE_DATA_CONFIG_TOOL_ID,
|
|
45
|
+
)?.config?.isRelative as boolean | undefined,
|
|
46
|
+
}))
|
|
52
47
|
|
|
53
48
|
const isRelative = storeIsRelative ?? defaultIsRelative
|
|
54
49
|
|
|
55
|
-
// Register tool
|
|
50
|
+
// Register data tool with all reactive deps — store's no-op detection handles performance
|
|
56
51
|
useEffect(() => {
|
|
57
|
-
registerTool(id, {
|
|
52
|
+
widgetStoreActions.registerTool(id, {
|
|
58
53
|
id: RELATIVE_DATA_TOOL_ID,
|
|
59
54
|
order,
|
|
60
55
|
enabled: defaultIsRelative,
|
|
@@ -65,17 +60,17 @@ export function RelativeData({
|
|
|
65
60
|
},
|
|
66
61
|
})
|
|
67
62
|
|
|
68
|
-
return () => unregisterTool(id, RELATIVE_DATA_TOOL_ID)
|
|
69
|
-
}, [id, order,
|
|
63
|
+
return () => widgetStoreActions.unregisterTool(id, RELATIVE_DATA_TOOL_ID)
|
|
64
|
+
}, [id, order, defaultIsRelative])
|
|
70
65
|
|
|
71
|
-
// Register config tool
|
|
66
|
+
// Register config tool with all reactive deps — store's no-op detection handles performance
|
|
72
67
|
useEffect(() => {
|
|
73
|
-
registerTool(id, {
|
|
68
|
+
widgetStoreActions.registerTool(id, {
|
|
74
69
|
id: RELATIVE_DATA_CONFIG_TOOL_ID,
|
|
75
70
|
type: 'config',
|
|
76
71
|
order,
|
|
77
72
|
enabled: true,
|
|
78
|
-
fn: (currentConfig, toolConfig) => {
|
|
73
|
+
fn: (currentConfig: unknown, toolConfig?: Record<string, unknown>) => {
|
|
79
74
|
const config = currentConfig as Record<string, unknown>
|
|
80
75
|
if (toolConfig?.isRelative) {
|
|
81
76
|
if (!percentFormatterRef.current) {
|
|
@@ -104,20 +99,21 @@ export function RelativeData({
|
|
|
104
99
|
isRelative: defaultIsRelative,
|
|
105
100
|
},
|
|
106
101
|
})
|
|
107
|
-
return () =>
|
|
108
|
-
|
|
102
|
+
return () =>
|
|
103
|
+
widgetStoreActions.unregisterTool(id, RELATIVE_DATA_CONFIG_TOOL_ID)
|
|
104
|
+
}, [id, order, defaultIsRelative])
|
|
109
105
|
|
|
110
106
|
const handleToggle = useCallback(() => {
|
|
111
107
|
const newIsRelative = !isRelative
|
|
112
|
-
setToolEnabled(id, RELATIVE_DATA_TOOL_ID, newIsRelative)
|
|
108
|
+
widgetStoreActions.setToolEnabled(id, RELATIVE_DATA_TOOL_ID, newIsRelative)
|
|
113
109
|
|
|
114
110
|
if (newIsRelative) {
|
|
115
|
-
const widget = getWidget(id) as {
|
|
111
|
+
const widget = widgetStoreActions.getWidget(id) as {
|
|
116
112
|
formatter?: (value: number) => string
|
|
117
113
|
locale?: string
|
|
118
114
|
max?: number
|
|
119
115
|
}
|
|
120
|
-
updateToolConfig(id, RELATIVE_DATA_CONFIG_TOOL_ID, {
|
|
116
|
+
widgetStoreActions.updateToolConfig(id, RELATIVE_DATA_CONFIG_TOOL_ID, {
|
|
121
117
|
isRelative: true,
|
|
122
118
|
originalFormatter: widget?.formatter,
|
|
123
119
|
originalMax: widget?.max,
|
|
@@ -125,11 +121,11 @@ export function RelativeData({
|
|
|
125
121
|
})
|
|
126
122
|
} else {
|
|
127
123
|
percentFormatterRef.current = undefined
|
|
128
|
-
updateToolConfig(id, RELATIVE_DATA_CONFIG_TOOL_ID, {
|
|
124
|
+
widgetStoreActions.updateToolConfig(id, RELATIVE_DATA_CONFIG_TOOL_ID, {
|
|
129
125
|
isRelative: false,
|
|
130
126
|
})
|
|
131
127
|
}
|
|
132
|
-
}, [isRelative, id
|
|
128
|
+
}, [isRelative, id])
|
|
133
129
|
|
|
134
130
|
const tooltipLabel = isRelative
|
|
135
131
|
? (labels?.absolute ?? 'Show absolute values')
|
|
@@ -4,8 +4,8 @@ import { useCallback, useEffect } from 'react'
|
|
|
4
4
|
import type { SearcherToggleProps, SearcherState } from './types'
|
|
5
5
|
import { actionButtonStyles } from '../shared/styles'
|
|
6
6
|
import { Tooltip } from '../../../components'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { widgetStoreActions } from '../../stores/widget-store'
|
|
8
|
+
import { useWidgetSelector } from '../../stores/use-widget-selector'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Widget action button to toggle search functionality.
|
|
@@ -30,25 +30,24 @@ export function SearcherToggle({
|
|
|
30
30
|
Icon,
|
|
31
31
|
IconButtonProps,
|
|
32
32
|
}: SearcherToggleProps) {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
useShallow((state) => state.getWidget<SearcherState>(id)?.isSearchEnabled),
|
|
37
|
-
)
|
|
33
|
+
const { storeIsEnabled } = useWidgetSelector(id, (w) => ({
|
|
34
|
+
storeIsEnabled: (w as SearcherState | undefined)?.isSearchEnabled,
|
|
35
|
+
}))
|
|
38
36
|
|
|
39
37
|
const isEnabled = storeIsEnabled ?? defaultEnabled
|
|
40
38
|
|
|
41
39
|
// Initialize store with default value on mount
|
|
42
40
|
useEffect(() => {
|
|
43
|
-
const currentValue =
|
|
41
|
+
const currentValue =
|
|
42
|
+
widgetStoreActions.getWidget<SearcherState>(id)?.isSearchEnabled
|
|
44
43
|
if (currentValue === undefined) {
|
|
45
|
-
setWidget(id, { isSearchEnabled: defaultEnabled })
|
|
44
|
+
widgetStoreActions.setWidget(id, { isSearchEnabled: defaultEnabled })
|
|
46
45
|
}
|
|
47
|
-
}, [defaultEnabled,
|
|
46
|
+
}, [defaultEnabled, id])
|
|
48
47
|
|
|
49
48
|
const handleToggle = useCallback(() => {
|
|
50
|
-
setWidget(id, { isSearchEnabled: !isEnabled })
|
|
51
|
-
}, [id, isEnabled
|
|
49
|
+
widgetStoreActions.setWidget(id, { isSearchEnabled: !isEnabled })
|
|
50
|
+
}, [id, isEnabled])
|
|
52
51
|
|
|
53
52
|
const tooltipLabel = isEnabled
|
|
54
53
|
? (labels?.disable ?? 'Disable search')
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { TextField, InputAdornment, IconButton } from '@mui/material'
|
|
2
2
|
import { ClearOutlined, SearchOutlined } from '@mui/icons-material'
|
|
3
3
|
import { useEffect, useRef, useCallback } from 'react'
|
|
4
|
-
import {
|
|
4
|
+
import { widgetStoreActions } from '../../stores/widget-store'
|
|
5
|
+
import { useWidgetSelector } from '../../stores/use-widget-selector'
|
|
5
6
|
import type { SearcherProps, SearcherFilterFn, SearcherState } from './types'
|
|
6
7
|
import type { EchartWidgetData } from '../../echart/types'
|
|
7
8
|
import { LOCK_SELECTION_TOOL_ID } from '../lock-selection/lock-selection'
|
|
@@ -41,30 +42,24 @@ export function Searcher({
|
|
|
41
42
|
const debounceTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
42
43
|
|
|
43
44
|
// Read enabled state and search text from widget store
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const searchText = widgetState?.searchText ?? ''
|
|
45
|
+
const { enabled, searchText } = useWidgetSelector(id, (w) => ({
|
|
46
|
+
enabled: (w as SearcherState | undefined)?.isSearchEnabled ?? false,
|
|
47
|
+
searchText: (w as SearcherState | undefined)?.searchText ?? '',
|
|
48
|
+
}))
|
|
49
49
|
const prevEnabledRef = useRef(enabled)
|
|
50
50
|
|
|
51
51
|
const filter = filterFn ?? defaultFilterFn
|
|
52
52
|
|
|
53
|
-
const setWidget = useWidgetStore((state) => state.setWidget)
|
|
54
|
-
const registerTool = useWidgetStore((state) => state.registerTool)
|
|
55
|
-
const unregisterTool = useWidgetStore((state) => state.unregisterTool)
|
|
56
|
-
const updateToolConfig = useWidgetStore((state) => state.updateToolConfig)
|
|
57
|
-
|
|
58
53
|
const setSearchText = useCallback(
|
|
59
54
|
(text: string) => {
|
|
60
|
-
setWidget(id, { searchText: text })
|
|
55
|
+
widgetStoreActions.setWidget(id, { searchText: text })
|
|
61
56
|
},
|
|
62
|
-
[id
|
|
57
|
+
[id],
|
|
63
58
|
)
|
|
64
59
|
|
|
65
|
-
// Register tool
|
|
60
|
+
// Register tool with all reactive deps — store's no-op detection handles performance
|
|
66
61
|
useEffect(() => {
|
|
67
|
-
registerTool(id, {
|
|
62
|
+
widgetStoreActions.registerTool(id, {
|
|
68
63
|
id: SEARCHER_TOOL_ID,
|
|
69
64
|
order,
|
|
70
65
|
enabled,
|
|
@@ -81,8 +76,8 @@ export function Searcher({
|
|
|
81
76
|
disables: [LOCK_SELECTION_TOOL_ID],
|
|
82
77
|
})
|
|
83
78
|
|
|
84
|
-
return () => unregisterTool(id, SEARCHER_TOOL_ID)
|
|
85
|
-
}, [id, order,
|
|
79
|
+
return () => widgetStoreActions.unregisterTool(id, SEARCHER_TOOL_ID)
|
|
80
|
+
}, [id, order, enabled, searchText, filter])
|
|
86
81
|
|
|
87
82
|
// Update config when search text changes (debounced)
|
|
88
83
|
const debouncedUpdateConfig = useCallback(
|
|
@@ -91,10 +86,12 @@ export function Searcher({
|
|
|
91
86
|
clearTimeout(debounceTimeoutRef.current)
|
|
92
87
|
}
|
|
93
88
|
debounceTimeoutRef.current = setTimeout(() => {
|
|
94
|
-
updateToolConfig(id, SEARCHER_TOOL_ID, {
|
|
89
|
+
widgetStoreActions.updateToolConfig(id, SEARCHER_TOOL_ID, {
|
|
90
|
+
searchText: text,
|
|
91
|
+
})
|
|
95
92
|
}, debounceDelay)
|
|
96
93
|
},
|
|
97
|
-
[id,
|
|
94
|
+
[id, debounceDelay],
|
|
98
95
|
)
|
|
99
96
|
|
|
100
97
|
// Auto-focus when enabled becomes true
|
|
@@ -127,11 +124,13 @@ export function Searcher({
|
|
|
127
124
|
|
|
128
125
|
const handleClear = useCallback(() => {
|
|
129
126
|
setSearchText('')
|
|
130
|
-
updateToolConfig(id, SEARCHER_TOOL_ID, {
|
|
127
|
+
widgetStoreActions.updateToolConfig(id, SEARCHER_TOOL_ID, {
|
|
128
|
+
searchText: '',
|
|
129
|
+
})
|
|
131
130
|
if (inputRef.current) {
|
|
132
131
|
inputRef.current.focus()
|
|
133
132
|
}
|
|
134
|
-
}, [id, setSearchText
|
|
133
|
+
}, [id, setSearchText])
|
|
135
134
|
|
|
136
135
|
if (!enabled) {
|
|
137
136
|
return null
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IconButton } from '@mui/material'
|
|
2
2
|
import { useCallback, useEffect, useMemo } from 'react'
|
|
3
|
-
import {
|
|
3
|
+
import { widgetStoreActions } from '../../stores/widget-store'
|
|
4
4
|
import type { StackToggleProps, StackToggleState } from './types'
|
|
5
5
|
import { actionButtonStyles } from '../shared/styles'
|
|
6
6
|
import { Tooltip } from '../../../components'
|
|
@@ -9,7 +9,7 @@ import { getEChartStackConfig } from '../../echart/utils'
|
|
|
9
9
|
import { DEFAULT_STACK_GROUP } from '../../echart/const'
|
|
10
10
|
import type { EchartWidgetState } from '../../echart/types'
|
|
11
11
|
import type { EchartOptionsProps } from '../../echart/types'
|
|
12
|
-
import {
|
|
12
|
+
import { useWidgetSelector } from '../../stores/use-widget-selector'
|
|
13
13
|
|
|
14
14
|
export const STACK_TOGGLE_TOOL_ID = 'stack-toggle'
|
|
15
15
|
|
|
@@ -34,16 +34,10 @@ export function StackToggle({
|
|
|
34
34
|
Icon,
|
|
35
35
|
IconButtonProps,
|
|
36
36
|
}: StackToggleProps) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
useShallow((state) => state.getWidget<StackToggleState>(id)?.isStacked),
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
const option = useWidgetStore(
|
|
45
|
-
useShallow((state) => state.getWidget<EchartWidgetState>(id)?.option),
|
|
46
|
-
)
|
|
37
|
+
const { storeIsStacked, option } = useWidgetSelector(id, (w) => ({
|
|
38
|
+
storeIsStacked: (w as StackToggleState | undefined)?.isStacked,
|
|
39
|
+
option: (w as EchartWidgetState | undefined)?.option,
|
|
40
|
+
}))
|
|
47
41
|
|
|
48
42
|
const { hasMultiSeries, hasStackInSeries } = useMemo(() => {
|
|
49
43
|
if (!option) return { hasMultiSeries: false, hasStackInSeries: false }
|
|
@@ -60,14 +54,14 @@ export function StackToggle({
|
|
|
60
54
|
const effectiveDefaultIsStacked = hasStackInSeries || defaultIsStacked
|
|
61
55
|
const isStacked = storeIsStacked ?? effectiveDefaultIsStacked
|
|
62
56
|
|
|
63
|
-
// Register config tool
|
|
57
|
+
// Register config tool with all reactive deps — store's no-op detection handles performance
|
|
64
58
|
useEffect(() => {
|
|
65
|
-
registerTool(id, {
|
|
59
|
+
widgetStoreActions.registerTool(id, {
|
|
66
60
|
id: STACK_TOGGLE_TOOL_ID,
|
|
67
61
|
type: 'config',
|
|
68
62
|
order: 10,
|
|
69
63
|
enabled: isStacked && hasMultiSeries,
|
|
70
|
-
fn: (currentConfig) => {
|
|
64
|
+
fn: (currentConfig: unknown) => {
|
|
71
65
|
const config = currentConfig as Record<string, unknown>
|
|
72
66
|
const option = config.option as EchartOptionsProps | undefined
|
|
73
67
|
if (!option) return currentConfig
|
|
@@ -87,18 +81,18 @@ export function StackToggle({
|
|
|
87
81
|
return { ...config, option: { ...option, series: updatedSeries } }
|
|
88
82
|
},
|
|
89
83
|
})
|
|
90
|
-
return () => unregisterTool(id, STACK_TOGGLE_TOOL_ID)
|
|
91
|
-
}, [id,
|
|
84
|
+
return () => widgetStoreActions.unregisterTool(id, STACK_TOGGLE_TOOL_ID)
|
|
85
|
+
}, [id, isStacked, hasMultiSeries])
|
|
92
86
|
|
|
93
87
|
// Initialize store with default value only if not already configured
|
|
94
88
|
useEffect(() => {
|
|
95
89
|
if (storeIsStacked !== undefined) return
|
|
96
|
-
setWidget(id, { isStacked: effectiveDefaultIsStacked })
|
|
97
|
-
}, [effectiveDefaultIsStacked, id,
|
|
90
|
+
widgetStoreActions.setWidget(id, { isStacked: effectiveDefaultIsStacked })
|
|
91
|
+
}, [effectiveDefaultIsStacked, id, storeIsStacked])
|
|
98
92
|
|
|
99
93
|
const handleToggle = useCallback(() => {
|
|
100
|
-
setWidget(id, { isStacked: !isStacked })
|
|
101
|
-
}, [isStacked, id
|
|
94
|
+
widgetStoreActions.setWidget(id, { isStacked: !isStacked })
|
|
95
|
+
}, [isStacked, id])
|
|
102
96
|
|
|
103
97
|
const tooltipLabel = isStacked
|
|
104
98
|
? (labels?.unstacked ?? 'Disable stacking')
|