@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.
- package/dist/components.js +123 -123
- package/dist/components.js.map +1 -1
- package/dist/error-CEkRPccv.js +39 -0
- package/dist/error-CEkRPccv.js.map +1 -0
- package/dist/{lasso-tool-BctzdzBu.js → lasso-tool-jl4YK02H.js} +19 -19
- package/dist/lasso-tool-jl4YK02H.js.map +1 -0
- package/dist/no-data-hR3KcJ-_.js +60 -0
- package/dist/no-data-hR3KcJ-_.js.map +1 -0
- package/dist/{row-D3uVFImu.js → row-BKmVAUN5.js} +2 -2
- package/dist/{row-D3uVFImu.js.map → row-BKmVAUN5.js.map} +1 -1
- package/dist/{series-BAImrSBo.js → series-D1pynfeh.js} +3 -3
- package/dist/{series-BAImrSBo.js.map → series-D1pynfeh.js.map} +1 -1
- package/dist/{styles-CCZnY17y.js → styles-DrPyd0y5.js} +28 -22
- package/dist/styles-DrPyd0y5.js.map +1 -0
- package/dist/types/components/lasso-tool/types.d.ts +1 -1
- package/dist/types/widgets/_shared/chart-config/index.d.ts +1 -1
- package/dist/types/widgets/_shared/chart-config/option-builders.d.ts +7 -0
- package/dist/types/widgets/_shared/chart-config/option-builders.test.d.ts +1 -0
- package/dist/types/widgets/actions/change-column/change-column.d.ts +4 -0
- package/dist/types/widgets/actions/fullscreen/fullscreen.d.ts +1 -1
- package/dist/types/widgets/actions/fullscreen/types.d.ts +2 -1
- package/dist/types/widgets/actions/index.d.ts +1 -1
- package/dist/types/widgets/actions/lock-selection/types.d.ts +7 -7
- package/dist/types/widgets/actions/relative-data/types.d.ts +1 -1
- package/dist/types/widgets/echart/types.d.ts +0 -4
- package/dist/types/widgets/echart/utils.d.ts +2 -1
- package/dist/types/widgets/error/error.d.ts +1 -1
- package/dist/types/widgets/error/types.d.ts +8 -0
- package/dist/types/widgets/loader/loader.d.ts +1 -1
- package/dist/types/widgets/loader/types.d.ts +1 -1
- package/dist/types/widgets/stores/types.d.ts +1 -1
- package/dist/{use-widget-ref-B8x4sHIj.js → use-widget-ref-P-2i0MJG.js} +2 -2
- package/dist/{use-widget-ref-B8x4sHIj.js.map → use-widget-ref-P-2i0MJG.js.map} +1 -1
- package/dist/{utils-D3-eQyDR.js → utils-idmvq0Oa.js} +17 -16
- package/dist/utils-idmvq0Oa.js.map +1 -0
- package/dist/{widget-store-Dn0Bnc4h.js → widget-store-CzDt8oSK.js} +31 -46
- package/dist/widget-store-CzDt8oSK.js.map +1 -0
- package/dist/widgets/actions.js +714 -697
- package/dist/widgets/actions.js.map +1 -1
- package/dist/widgets/bar.js +67 -63
- package/dist/widgets/bar.js.map +1 -1
- package/dist/widgets/category.js +250 -241
- package/dist/widgets/category.js.map +1 -1
- package/dist/widgets/echart.js +93 -100
- package/dist/widgets/echart.js.map +1 -1
- package/dist/widgets/error.js +1 -1
- package/dist/widgets/formula.js +64 -72
- package/dist/widgets/formula.js.map +1 -1
- package/dist/widgets/histogram.js +75 -73
- package/dist/widgets/histogram.js.map +1 -1
- package/dist/widgets/loader.js +58 -49
- package/dist/widgets/loader.js.map +1 -1
- package/dist/widgets/markdown.js +2 -2
- package/dist/widgets/no-data.js +1 -1
- package/dist/widgets/pie.js +4 -4
- package/dist/widgets/range.js +97 -105
- package/dist/widgets/range.js.map +1 -1
- package/dist/widgets/scatterplot.js +8 -8
- package/dist/widgets/skeleton-loader.js +1 -1
- package/dist/widgets/spread.js +84 -100
- package/dist/widgets/spread.js.map +1 -1
- package/dist/widgets/stores.js +1 -1
- package/dist/widgets/table.js +493 -485
- package/dist/widgets/table.js.map +1 -1
- package/dist/widgets/timeseries.js +4 -4
- package/dist/widgets/wrapper.js +156 -156
- package/dist/widgets/wrapper.js.map +1 -1
- package/dist/widgets.js +4 -4
- package/package.json +1 -1
- package/src/components/lasso-tool/lasso-tool-inline.tsx +19 -17
- package/src/components/lasso-tool/lasso-tool.tsx +22 -20
- package/src/components/lasso-tool/types.ts +4 -3
- package/src/widgets/_shared/chart-config/index.ts +1 -0
- package/src/widgets/_shared/chart-config/option-builders.test.ts +40 -0
- package/src/widgets/_shared/chart-config/option-builders.ts +12 -0
- package/src/widgets/actions/change-column/change-column.test.tsx +129 -2
- package/src/widgets/actions/change-column/change-column.tsx +79 -2
- package/src/widgets/actions/fullscreen/fullscreen.tsx +8 -8
- package/src/widgets/actions/fullscreen/types.ts +6 -1
- package/src/widgets/actions/index.ts +4 -1
- package/src/widgets/actions/lock-selection/lock-selection.test.tsx +28 -30
- package/src/widgets/actions/lock-selection/types.ts +8 -8
- package/src/widgets/actions/relative-data/relative-data.test.tsx +13 -13
- package/src/widgets/actions/relative-data/types.ts +1 -1
- package/src/widgets/actions/stack-toggle/stack-toggle.test.tsx +19 -9
- package/src/widgets/actions/zoom-toggle/zoom-toggle.tsx +113 -95
- package/src/widgets/bar/config.ts +37 -28
- package/src/widgets/category/category-ui.tsx +25 -22
- package/src/widgets/echart/echart-ui.test.tsx +3 -18
- package/src/widgets/echart/echart-ui.tsx +4 -22
- package/src/widgets/echart/echart.test.tsx +9 -25
- package/src/widgets/echart/echart.tsx +36 -29
- package/src/widgets/echart/types.ts +0 -4
- package/src/widgets/echart/utils.ts +3 -1
- package/src/widgets/error/error.tsx +17 -14
- package/src/widgets/error/types.ts +10 -0
- package/src/widgets/formula/components/value.tsx +13 -13
- package/src/widgets/histogram/config.ts +36 -29
- package/src/widgets/loader/loader.tsx +28 -25
- package/src/widgets/loader/types.ts +3 -1
- package/src/widgets/no-data/no-data.tsx +8 -11
- package/src/widgets/range/components/range-item.tsx +9 -13
- package/src/widgets/spread/components/max-value.tsx +13 -13
- package/src/widgets/spread/components/min-value.tsx +13 -13
- package/src/widgets/stores/types.ts +1 -4
- package/src/widgets/stores/widget-store.ts +1 -27
- package/src/widgets/table/hooks/use-pagination.ts +44 -35
- package/src/widgets/table/hooks/use-sort.ts +25 -23
- package/src/widgets/wrapper/wrapper-ui.tsx +16 -17
- package/dist/error-piB8FwYO.js +0 -38
- package/dist/error-piB8FwYO.js.map +0 -1
- package/dist/lasso-tool-BctzdzBu.js.map +0 -1
- package/dist/no-data-jdlbMef0.js +0 -61
- package/dist/no-data-jdlbMef0.js.map +0 -1
- package/dist/styles-CCZnY17y.js.map +0 -1
- package/dist/utils-D3-eQyDR.js.map +0 -1
- package/dist/widget-store-Dn0Bnc4h.js.map +0 -1
|
@@ -5,14 +5,12 @@ import type { Ref } from 'react'
|
|
|
5
5
|
import { theme as CartoTheme } from '@carto/meridian-ds/theme'
|
|
6
6
|
|
|
7
7
|
export type EchartOptionsProps = EChartsOption
|
|
8
|
-
export type EchartReplaceMerge = string[]
|
|
9
8
|
|
|
10
9
|
export interface EchartUIProps {
|
|
11
10
|
id: string
|
|
12
11
|
option: EchartOptionsProps
|
|
13
12
|
className?: string
|
|
14
13
|
init?: echarts.EChartsInitOpts
|
|
15
|
-
replaceMerge?: EchartReplaceMerge
|
|
16
14
|
style?: React.CSSProperties
|
|
17
15
|
ref?: Ref<echarts.ECharts>
|
|
18
16
|
onEvents?: Record<string, Parameters<echarts.ECharts['on']>[2]>
|
|
@@ -28,7 +26,6 @@ export type EchartWidgetState = BaseWidgetState<{
|
|
|
28
26
|
option: EchartUIProps['option']
|
|
29
27
|
onEvents?: EchartUIProps['onEvents']
|
|
30
28
|
init?: EchartUIProps['init']
|
|
31
|
-
replaceMerge?: EchartReplaceMerge
|
|
32
29
|
}>
|
|
33
30
|
|
|
34
31
|
export interface EchartWidgetOptionProps<D> {
|
|
@@ -41,5 +38,4 @@ export interface EchartWidgetProps {
|
|
|
41
38
|
type: string
|
|
42
39
|
option: EchartUIProps['option']
|
|
43
40
|
onEvents?: EchartUIProps['onEvents']
|
|
44
|
-
replaceMerge?: EchartReplaceMerge
|
|
45
41
|
}
|
|
@@ -35,12 +35,14 @@ export function getEChartZoomConfig(
|
|
|
35
35
|
ySlider = false,
|
|
36
36
|
showSliders = true,
|
|
37
37
|
xAxisLabelFormatter,
|
|
38
|
+
bottomOffset = 0,
|
|
38
39
|
} = {} as {
|
|
39
40
|
inside?: boolean
|
|
40
41
|
xSlider?: boolean
|
|
41
42
|
ySlider?: boolean
|
|
42
43
|
showSliders?: boolean
|
|
43
44
|
xAxisLabelFormatter?: (value: number) => string
|
|
45
|
+
bottomOffset?: number
|
|
44
46
|
},
|
|
45
47
|
theme?: Theme,
|
|
46
48
|
) {
|
|
@@ -72,7 +74,7 @@ export function getEChartZoomConfig(
|
|
|
72
74
|
throttle: 0,
|
|
73
75
|
type: 'slider',
|
|
74
76
|
xAxisIndex: [0],
|
|
75
|
-
bottom:
|
|
77
|
+
bottom: bottomOffset,
|
|
76
78
|
height: parseInt(theme?.spacing?.(4) ?? '32'),
|
|
77
79
|
show: zoom && showSliders,
|
|
78
80
|
zoomLock: !zoom,
|
|
@@ -3,28 +3,31 @@ import { useWidgetStore } from '../stores/widget-store'
|
|
|
3
3
|
import { useShallow } from 'zustand/shallow'
|
|
4
4
|
import type { WidgetErrorProps } from './types'
|
|
5
5
|
|
|
6
|
-
export function WidgetError({
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
}),
|
|
6
|
+
export function WidgetError({
|
|
7
|
+
id,
|
|
8
|
+
children,
|
|
9
|
+
title: titleProp,
|
|
10
|
+
description,
|
|
11
|
+
}: WidgetErrorProps) {
|
|
12
|
+
const isLoading = useWidgetStore(
|
|
13
|
+
useShallow((state) => state.widgets[id]?.isLoading),
|
|
16
14
|
)
|
|
15
|
+
const isFetching = useWidgetStore(
|
|
16
|
+
useShallow((state) => state.widgets[id]?.isFetching),
|
|
17
|
+
)
|
|
18
|
+
const error = useWidgetStore(useShallow((state) => state.widgets[id]?.error))
|
|
17
19
|
|
|
18
20
|
// Don't show error during loading/fetching states
|
|
19
|
-
if (
|
|
21
|
+
if (isLoading || isFetching) {
|
|
20
22
|
return children
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
// Show error UI if error exists
|
|
24
|
-
if (
|
|
25
|
-
const errorTitle =
|
|
26
|
+
if (error) {
|
|
27
|
+
const errorTitle = titleProp ?? error.title ?? 'Error'
|
|
26
28
|
const errorMessage =
|
|
27
|
-
|
|
29
|
+
description ??
|
|
30
|
+
error.message ??
|
|
28
31
|
'An error occurred while loading the widget. Please try again.'
|
|
29
32
|
|
|
30
33
|
return (
|
|
@@ -11,4 +11,14 @@ export interface WidgetErrorProps {
|
|
|
11
11
|
* Children to render when no error exists
|
|
12
12
|
*/
|
|
13
13
|
children: ReactNode
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Override error title
|
|
17
|
+
*/
|
|
18
|
+
title?: string
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Override error description/message
|
|
22
|
+
*/
|
|
23
|
+
description?: string
|
|
14
24
|
}
|
|
@@ -6,20 +6,20 @@ import { useShallow } from 'zustand/shallow'
|
|
|
6
6
|
const defaultFormatter = (value: number) => value.toString()
|
|
7
7
|
|
|
8
8
|
export function Value({ id, index = 0, ...props }: ValueProps) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} = useWidgetStore(
|
|
14
|
-
useShallow((state) => {
|
|
15
|
-
const widget = state.getWidget<FormulaWidgetState>(id)
|
|
16
|
-
return {
|
|
17
|
-
value: widget?.data?.[index]?.value,
|
|
18
|
-
color: widget?.data?.[index]?.color,
|
|
19
|
-
formatter: widget?.formatter,
|
|
20
|
-
}
|
|
21
|
-
}),
|
|
9
|
+
const value = useWidgetStore(
|
|
10
|
+
useShallow(
|
|
11
|
+
(state) => state.getWidget<FormulaWidgetState>(id)?.data?.[index]?.value,
|
|
12
|
+
),
|
|
22
13
|
)
|
|
14
|
+
const color = useWidgetStore(
|
|
15
|
+
useShallow(
|
|
16
|
+
(state) => state.getWidget<FormulaWidgetState>(id)?.data?.[index]?.color,
|
|
17
|
+
),
|
|
18
|
+
)
|
|
19
|
+
const formatter =
|
|
20
|
+
useWidgetStore(
|
|
21
|
+
useShallow((state) => state.getWidget<FormulaWidgetState>(id)?.formatter),
|
|
22
|
+
) ?? defaultFormatter
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
25
|
<Item TypographyProps={{ color }} {...props}>
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
buildGridConfig,
|
|
15
15
|
createTooltipPositioner,
|
|
16
16
|
createTooltipFormatter,
|
|
17
|
-
|
|
17
|
+
niceNum,
|
|
18
18
|
} from '../_shared/chart-config'
|
|
19
19
|
import { downloadToCSV, downloadToPNG, type DownloadItem } from '../actions'
|
|
20
20
|
import type { ConfigProps } from '../loader/types'
|
|
@@ -49,33 +49,7 @@ function getOption({
|
|
|
49
49
|
}: HistogramConfig): EchartOptionsProps {
|
|
50
50
|
const hasLegend = (data?.length ?? 0) > 1
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
type: 'value' as const,
|
|
54
|
-
showMaxLabel: true,
|
|
55
|
-
showMinLabel: true,
|
|
56
|
-
splitNumber: 1,
|
|
57
|
-
axisLabel: {
|
|
58
|
-
fontSize: theme.typography.overlineDelicate.fontSize,
|
|
59
|
-
fontFamily: theme.typography.overlineDelicate.fontFamily,
|
|
60
|
-
margin: parseInt(theme.spacing(1)),
|
|
61
|
-
show: true,
|
|
62
|
-
showMaxLabel: true,
|
|
63
|
-
showMinLabel: true,
|
|
64
|
-
verticalAlign: 'bottom' as const,
|
|
65
|
-
},
|
|
66
|
-
axisLine: {
|
|
67
|
-
show: false,
|
|
68
|
-
},
|
|
69
|
-
axisTick: {
|
|
70
|
-
show: false,
|
|
71
|
-
},
|
|
72
|
-
splitLine: {
|
|
73
|
-
show: true,
|
|
74
|
-
lineStyle: {
|
|
75
|
-
color: theme.palette.black[4],
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
}
|
|
52
|
+
let niceMax = 1
|
|
79
53
|
|
|
80
54
|
return {
|
|
81
55
|
legend: buildLegendConfig(hasLegend),
|
|
@@ -110,7 +84,40 @@ function getOption({
|
|
|
110
84
|
},
|
|
111
85
|
},
|
|
112
86
|
},
|
|
113
|
-
yAxis:
|
|
87
|
+
yAxis: {
|
|
88
|
+
type: 'value' as const,
|
|
89
|
+
min: 0,
|
|
90
|
+
max: (extent: { min: number; max: number }) => {
|
|
91
|
+
niceMax = extent.max <= 0 ? 1 : niceNum(extent.max)
|
|
92
|
+
return niceMax
|
|
93
|
+
},
|
|
94
|
+
splitNumber: 1,
|
|
95
|
+
axisLabel: {
|
|
96
|
+
fontSize: theme.typography.overlineDelicate.fontSize,
|
|
97
|
+
fontFamily: theme.typography.overlineDelicate.fontFamily,
|
|
98
|
+
margin: parseInt(theme.spacing(1)),
|
|
99
|
+
show: true,
|
|
100
|
+
showMaxLabel: true,
|
|
101
|
+
showMinLabel: true,
|
|
102
|
+
verticalAlign: 'bottom' as const,
|
|
103
|
+
formatter: (value: number) => {
|
|
104
|
+
if (value !== niceMax) return ''
|
|
105
|
+
return formatter ? formatter(value) : String(value)
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
axisLine: {
|
|
109
|
+
show: false,
|
|
110
|
+
},
|
|
111
|
+
axisTick: {
|
|
112
|
+
show: false,
|
|
113
|
+
},
|
|
114
|
+
splitLine: {
|
|
115
|
+
show: true,
|
|
116
|
+
lineStyle: {
|
|
117
|
+
color: theme.palette.black[4],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
114
121
|
tooltip: {
|
|
115
122
|
position: createTooltipPositioner(theme),
|
|
116
123
|
formatter: createTooltipFormatter((item) => {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { useEffect } from 'react'
|
|
1
|
+
import { useEffect, useRef, useSyncExternalStore } from 'react'
|
|
2
2
|
import type { WidgetLoaderProps } from './types'
|
|
3
3
|
import { useWidgetStore } from '../stores/widget-store'
|
|
4
4
|
import type { WrapperState } from '../wrapper'
|
|
5
5
|
|
|
6
|
-
export function WidgetLoader<T extends
|
|
6
|
+
export function WidgetLoader<T extends object = Record<string, unknown>>(
|
|
7
|
+
props: WidgetLoaderProps<T>,
|
|
8
|
+
) {
|
|
7
9
|
const setWidget = useWidgetStore((state) => state.setWidget)
|
|
8
10
|
const executeToolPipeline = useWidgetStore(
|
|
9
11
|
(state) => state.executeToolPipeline,
|
|
@@ -12,6 +14,20 @@ export function WidgetLoader<T extends Record<string, unknown> = Record<string,
|
|
|
12
14
|
(state) => state.executeConfigPipeline,
|
|
13
15
|
)
|
|
14
16
|
|
|
17
|
+
const registeredTools = useSyncExternalStore(
|
|
18
|
+
useWidgetStore.subscribe,
|
|
19
|
+
() => useWidgetStore.getState().widgets[props.id]?.registeredTools,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
const dataRef = useRef(props.data)
|
|
23
|
+
const configRef = useRef(props.config)
|
|
24
|
+
const isMountedRef = useRef(false)
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
dataRef.current = props.data
|
|
28
|
+
configRef.current = props.config
|
|
29
|
+
})
|
|
30
|
+
|
|
15
31
|
// Split into 3 effects for metadata and 1 for data pipeline:
|
|
16
32
|
// Each property that can be modified independently gets its own effect to avoid
|
|
17
33
|
// accidentally resetting other properties.
|
|
@@ -50,31 +66,18 @@ export function WidgetLoader<T extends Record<string, unknown> = Record<string,
|
|
|
50
66
|
void executeToolPipeline(props.id, props.data)
|
|
51
67
|
}, [props.id, props.data, executeToolPipeline])
|
|
52
68
|
|
|
53
|
-
// Effect 5: Re-execute pipelines when
|
|
69
|
+
// Effect 5: Re-execute pipelines when registered tools change
|
|
54
70
|
useEffect(() => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// Only re-execute if tools array changed
|
|
61
|
-
if (currentTools !== prevTools) {
|
|
62
|
-
prevTools = currentTools
|
|
63
|
-
void executeToolPipeline(props.id, props.data)
|
|
64
|
-
if (props.config) {
|
|
65
|
-
void executeConfigPipeline(props.id, props.config)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
})
|
|
71
|
+
if (!isMountedRef.current) {
|
|
72
|
+
isMountedRef.current = true
|
|
73
|
+
return
|
|
74
|
+
}
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
executeToolPipeline,
|
|
76
|
-
executeConfigPipeline,
|
|
77
|
-
])
|
|
76
|
+
void executeToolPipeline(props.id, dataRef.current)
|
|
77
|
+
if (configRef.current) {
|
|
78
|
+
void executeConfigPipeline(props.id, configRef.current)
|
|
79
|
+
}
|
|
80
|
+
}, [registeredTools, props.id, executeToolPipeline, executeConfigPipeline])
|
|
78
81
|
|
|
79
82
|
return props.children
|
|
80
83
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
2
|
import type { WidgetsStoreProps, WidgetState } from '../stores/types'
|
|
3
3
|
|
|
4
|
-
export interface WidgetLoaderProps<
|
|
4
|
+
export interface WidgetLoaderProps<
|
|
5
|
+
T extends object = Record<string, unknown>,
|
|
6
|
+
> extends WidgetsStoreProps {
|
|
5
7
|
children: ReactNode
|
|
6
8
|
config?: T
|
|
7
9
|
}
|
|
@@ -45,25 +45,22 @@ export function WidgetNoData({
|
|
|
45
45
|
isEmpty = defaultIsEmpty,
|
|
46
46
|
}: WidgetNoDataProps) {
|
|
47
47
|
// Subscribe to widget store with selective subscription for optimal performance
|
|
48
|
-
const
|
|
49
|
-
useShallow((state) =>
|
|
50
|
-
const w = state.widgets[id]
|
|
51
|
-
return {
|
|
52
|
-
isLoading: w?.isLoading,
|
|
53
|
-
isFetching: w?.isFetching,
|
|
54
|
-
data: w?.data,
|
|
55
|
-
}
|
|
56
|
-
}),
|
|
48
|
+
const isLoading = useWidgetStore(
|
|
49
|
+
useShallow((state) => state.widgets[id]?.isLoading),
|
|
57
50
|
)
|
|
51
|
+
const isFetching = useWidgetStore(
|
|
52
|
+
useShallow((state) => state.widgets[id]?.isFetching),
|
|
53
|
+
)
|
|
54
|
+
const data = useWidgetStore(useShallow((state) => state.widgets[id]?.data))
|
|
58
55
|
|
|
59
56
|
// If loading or fetching, show children
|
|
60
57
|
// SkeletonLoader handles loading state, this allows proper composition
|
|
61
|
-
if (
|
|
58
|
+
if (isLoading || isFetching) {
|
|
62
59
|
return children
|
|
63
60
|
}
|
|
64
61
|
|
|
65
62
|
// Check if data is empty
|
|
66
|
-
if (isEmpty(
|
|
63
|
+
if (isEmpty(data)) {
|
|
67
64
|
return (
|
|
68
65
|
<Box sx={styles.root}>
|
|
69
66
|
<Typography variant='body2' color='text.primary'>
|
|
@@ -10,20 +10,16 @@ type EditingState = '' | 'min' | 'max'
|
|
|
10
10
|
const defaultFormatter = (value: number) => value.toString()
|
|
11
11
|
|
|
12
12
|
export function RangeItem({ id, index }: RangeItemProps) {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
useShallow((state) => {
|
|
19
|
-
const widget = state.getWidget<RangeWidgetState>(id)
|
|
20
|
-
return {
|
|
21
|
-
item: widget?.data[index],
|
|
22
|
-
onChange: widget?.onChange,
|
|
23
|
-
formatter: widget?.formatter,
|
|
24
|
-
}
|
|
25
|
-
}),
|
|
13
|
+
const item = useWidgetStore(
|
|
14
|
+
useShallow((state) => state.getWidget<RangeWidgetState>(id)?.data[index]),
|
|
15
|
+
)
|
|
16
|
+
const onChange = useWidgetStore(
|
|
17
|
+
useShallow((state) => state.getWidget<RangeWidgetState>(id)?.onChange),
|
|
26
18
|
)
|
|
19
|
+
const formatter =
|
|
20
|
+
useWidgetStore(
|
|
21
|
+
useShallow((state) => state.getWidget<RangeWidgetState>(id)?.formatter),
|
|
22
|
+
) ?? defaultFormatter
|
|
27
23
|
const getWidget = useWidgetStore((store) => store.getWidget)
|
|
28
24
|
const setWidget = useWidgetStore((store) => store.setWidget)
|
|
29
25
|
|
|
@@ -6,20 +6,20 @@ import { useShallow } from 'zustand/shallow'
|
|
|
6
6
|
const defaultFormatter = (value: number) => value.toString()
|
|
7
7
|
|
|
8
8
|
export function MaxValue({ id, index = 0, ...props }: ValueProps) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} = useWidgetStore(
|
|
14
|
-
useShallow((state) => {
|
|
15
|
-
const widget = state.getWidget<SpreadWidgetState>(id)
|
|
16
|
-
return {
|
|
17
|
-
max: widget?.data[index]?.max,
|
|
18
|
-
color: widget?.data[index]?.color,
|
|
19
|
-
formatter: widget?.formatter,
|
|
20
|
-
}
|
|
21
|
-
}),
|
|
9
|
+
const max = useWidgetStore(
|
|
10
|
+
useShallow(
|
|
11
|
+
(state) => state.getWidget<SpreadWidgetState>(id)?.data[index]?.max,
|
|
12
|
+
),
|
|
22
13
|
)
|
|
14
|
+
const color = useWidgetStore(
|
|
15
|
+
useShallow(
|
|
16
|
+
(state) => state.getWidget<SpreadWidgetState>(id)?.data[index]?.color,
|
|
17
|
+
),
|
|
18
|
+
)
|
|
19
|
+
const formatter =
|
|
20
|
+
useWidgetStore(
|
|
21
|
+
useShallow((state) => state.getWidget<SpreadWidgetState>(id)?.formatter),
|
|
22
|
+
) ?? defaultFormatter
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
25
|
<Item TypographyProps={{ color }} {...props}>
|
|
@@ -6,20 +6,20 @@ import { useShallow } from 'zustand/shallow'
|
|
|
6
6
|
const defaultFormatter = (value: number) => value.toString()
|
|
7
7
|
|
|
8
8
|
export function MinValue({ id, index = 0, ...props }: ValueProps) {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} = useWidgetStore(
|
|
14
|
-
useShallow((state) => {
|
|
15
|
-
const widget = state.getWidget<SpreadWidgetState>(id)
|
|
16
|
-
return {
|
|
17
|
-
min: widget?.data[index]?.min,
|
|
18
|
-
color: widget?.data[index]?.color,
|
|
19
|
-
formatter: widget?.formatter,
|
|
20
|
-
}
|
|
21
|
-
}),
|
|
9
|
+
const min = useWidgetStore(
|
|
10
|
+
useShallow(
|
|
11
|
+
(state) => state.getWidget<SpreadWidgetState>(id)?.data[index]?.min,
|
|
12
|
+
),
|
|
22
13
|
)
|
|
14
|
+
const color = useWidgetStore(
|
|
15
|
+
useShallow(
|
|
16
|
+
(state) => state.getWidget<SpreadWidgetState>(id)?.data[index]?.color,
|
|
17
|
+
),
|
|
18
|
+
)
|
|
19
|
+
const formatter =
|
|
20
|
+
useWidgetStore(
|
|
21
|
+
useShallow((state) => state.getWidget<SpreadWidgetState>(id)?.formatter),
|
|
22
|
+
) ?? defaultFormatter
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
25
|
<Item TypographyProps={{ color }} {...props}>
|
|
@@ -200,10 +200,7 @@ export interface WidgetStoreActions {
|
|
|
200
200
|
* @param widgetId - Widget ID
|
|
201
201
|
* @param baseConfig - Base config to transform
|
|
202
202
|
*/
|
|
203
|
-
executeConfigPipeline: (
|
|
204
|
-
widgetId: string,
|
|
205
|
-
baseConfig: Record<string, unknown>,
|
|
206
|
-
) => Promise<void>
|
|
203
|
+
executeConfigPipeline: (widgetId: string, baseConfig: object) => Promise<void>
|
|
207
204
|
}
|
|
208
205
|
|
|
209
206
|
/**
|
|
@@ -243,10 +243,7 @@ export const useWidgetStore = create<WidgetStore>()((set, get) => ({
|
|
|
243
243
|
}
|
|
244
244
|
},
|
|
245
245
|
|
|
246
|
-
executeConfigPipeline: async (
|
|
247
|
-
widgetId: string,
|
|
248
|
-
baseConfig: Record<string, unknown>,
|
|
249
|
-
) => {
|
|
246
|
+
executeConfigPipeline: async (widgetId: string, baseConfig: object) => {
|
|
250
247
|
const widget = get().widgets[widgetId]
|
|
251
248
|
if (!widget) return
|
|
252
249
|
|
|
@@ -272,29 +269,6 @@ export const useWidgetStore = create<WidgetStore>()((set, get) => ({
|
|
|
272
269
|
)
|
|
273
270
|
.sort((a, b) => a.order - b.order)
|
|
274
271
|
|
|
275
|
-
// If no config tools, just set the base config directly
|
|
276
|
-
if (sortedTools.length === 0) {
|
|
277
|
-
set((state) => {
|
|
278
|
-
const currentWidget = state.widgets[widgetId]
|
|
279
|
-
if (!currentWidget) return state
|
|
280
|
-
|
|
281
|
-
return {
|
|
282
|
-
widgets: {
|
|
283
|
-
...state.widgets,
|
|
284
|
-
[widgetId]: {
|
|
285
|
-
...currentWidget,
|
|
286
|
-
...baseConfig,
|
|
287
|
-
},
|
|
288
|
-
},
|
|
289
|
-
}
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
if (activeConfigPipelines.get(widgetId) === currentExecution) {
|
|
293
|
-
activeConfigPipelines.delete(widgetId)
|
|
294
|
-
}
|
|
295
|
-
return
|
|
296
|
-
}
|
|
297
|
-
|
|
298
272
|
// Chain config tools
|
|
299
273
|
let transformedConfig: unknown = baseConfig
|
|
300
274
|
for (const tool of sortedTools) {
|
|
@@ -48,19 +48,39 @@ export function usePagination<T extends TableRow>(
|
|
|
48
48
|
// Get store actions and state
|
|
49
49
|
const setWidget = useWidgetStore((state) => state.setWidget)
|
|
50
50
|
const getWidget = useWidgetStore((state) => state.getWidget)
|
|
51
|
-
const
|
|
52
|
-
useShallow(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
51
|
+
const paginationEnabled = useWidgetStore(
|
|
52
|
+
useShallow(
|
|
53
|
+
(state) =>
|
|
54
|
+
!!(state.widgets[widgetId] as TableWidgetState | undefined)?.pagination,
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
const page = useWidgetStore(
|
|
58
|
+
useShallow(
|
|
59
|
+
(state) =>
|
|
60
|
+
(state.widgets[widgetId] as TableWidgetState | undefined)?.pagination
|
|
61
|
+
?.page ?? DEFAULT_PAGE,
|
|
62
|
+
),
|
|
63
|
+
)
|
|
64
|
+
const rowsPerPage = useWidgetStore(
|
|
65
|
+
useShallow(
|
|
66
|
+
(state) =>
|
|
67
|
+
(state.widgets[widgetId] as TableWidgetState | undefined)?.pagination
|
|
68
|
+
?.rowsPerPage ?? DEFAULT_ROWS_PER_PAGE,
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
const rowsPerPageOptions = useWidgetStore(
|
|
72
|
+
useShallow(
|
|
73
|
+
(state) =>
|
|
74
|
+
(state.widgets[widgetId] as TableWidgetState | undefined)?.pagination
|
|
75
|
+
?.rowsPerPageOptions ?? DEFAULT_ROWS_PER_PAGE_OPTIONS,
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
const total = useWidgetStore(
|
|
79
|
+
useShallow(
|
|
80
|
+
(state) =>
|
|
81
|
+
(state.widgets[widgetId] as TableWidgetState | undefined)?.pagination
|
|
82
|
+
?.total ?? data.length,
|
|
83
|
+
),
|
|
64
84
|
)
|
|
65
85
|
|
|
66
86
|
const mode = useWidgetStore(
|
|
@@ -71,10 +91,7 @@ export function usePagination<T extends TableRow>(
|
|
|
71
91
|
)
|
|
72
92
|
|
|
73
93
|
// Calculate max page
|
|
74
|
-
const maxPage = Math.max(
|
|
75
|
-
0,
|
|
76
|
-
Math.ceil(paginationState.total / (paginationState?.rowsPerPage ?? 1)) - 1,
|
|
77
|
-
)
|
|
94
|
+
const maxPage = Math.max(0, Math.ceil(total / (rowsPerPage ?? 1)) - 1)
|
|
78
95
|
|
|
79
96
|
// Set page with bounds checking
|
|
80
97
|
const setPage = useCallback(
|
|
@@ -118,32 +135,24 @@ export function usePagination<T extends TableRow>(
|
|
|
118
135
|
// Navigation helpers
|
|
119
136
|
const goToFirstPage = useCallback(() => setPage(0), [setPage])
|
|
120
137
|
const goToLastPage = useCallback(() => setPage(maxPage), [setPage, maxPage])
|
|
121
|
-
const goToNextPage = useCallback(
|
|
122
|
-
|
|
123
|
-
[setPage, paginationState.page],
|
|
124
|
-
)
|
|
125
|
-
const goToPreviousPage = useCallback(
|
|
126
|
-
() => setPage(paginationState.page - 1),
|
|
127
|
-
[setPage, paginationState.page],
|
|
128
|
-
)
|
|
138
|
+
const goToNextPage = useCallback(() => setPage(page + 1), [setPage, page])
|
|
139
|
+
const goToPreviousPage = useCallback(() => setPage(page - 1), [setPage, page])
|
|
129
140
|
|
|
130
141
|
// Paginated data for local mode
|
|
131
142
|
const paginatedData = useMemo(() => {
|
|
132
|
-
if (mode === 'remote' || !
|
|
143
|
+
if (mode === 'remote' || !paginationEnabled) {
|
|
133
144
|
// For remote mode, data is already paginated from server
|
|
134
145
|
return data
|
|
135
146
|
}
|
|
136
|
-
return paginateData(data,
|
|
137
|
-
}, [
|
|
138
|
-
data,
|
|
139
|
-
mode,
|
|
140
|
-
paginationState.page,
|
|
141
|
-
paginationState.paginationEnabled,
|
|
142
|
-
paginationState.rowsPerPage,
|
|
143
|
-
])
|
|
147
|
+
return paginateData(data, page, rowsPerPage)
|
|
148
|
+
}, [data, mode, page, paginationEnabled, rowsPerPage])
|
|
144
149
|
|
|
145
150
|
return {
|
|
146
|
-
|
|
151
|
+
paginationEnabled,
|
|
152
|
+
page,
|
|
153
|
+
rowsPerPage,
|
|
154
|
+
rowsPerPageOptions,
|
|
155
|
+
total,
|
|
147
156
|
paginatedData,
|
|
148
157
|
setPage,
|
|
149
158
|
setRowsPerPage,
|