@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,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: 0,
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({ id, children }: WidgetErrorProps) {
7
- const widget = useWidgetStore(
8
- useShallow((state) => {
9
- const w = state.widgets[id]
10
- return {
11
- isLoading: w?.isLoading,
12
- isFetching: w?.isFetching,
13
- error: w?.error,
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 (widget?.isLoading || widget?.isFetching) {
21
+ if (isLoading || isFetching) {
20
22
  return children
21
23
  }
22
24
 
23
25
  // Show error UI if error exists
24
- if (widget?.error) {
25
- const errorTitle = widget.error.title ?? 'Error'
26
+ if (error) {
27
+ const errorTitle = titleProp ?? error.title ?? 'Error'
26
28
  const errorMessage =
27
- widget.error.message ??
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
- value,
11
- color,
12
- formatter = defaultFormatter,
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
- applyYAxisFormatter,
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
- const yAxis = {
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: applyYAxisFormatter(yAxis, formatter),
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 Record<string, unknown> = Record<string, unknown>>(props: WidgetLoaderProps<T>) {
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 tool state changes (enabled/config)
69
+ // Effect 5: Re-execute pipelines when registered tools change
54
70
  useEffect(() => {
55
- let prevTools = useWidgetStore.getState().widgets[props.id]?.registeredTools
56
-
57
- const unsubscribe = useWidgetStore.subscribe((state) => {
58
- const currentTools = state.widgets[props.id]?.registeredTools
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
- return unsubscribe
71
- }, [
72
- props.id,
73
- props.data,
74
- props.config,
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<T extends Record<string, unknown> = Record<string, unknown>> extends WidgetsStoreProps {
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 widget = useWidgetStore(
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 (widget?.isLoading || widget?.isFetching) {
58
+ if (isLoading || isFetching) {
62
59
  return children
63
60
  }
64
61
 
65
62
  // Check if data is empty
66
- if (isEmpty(widget?.data)) {
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
- item,
15
- onChange,
16
- formatter = defaultFormatter,
17
- } = useWidgetStore(
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
- max,
11
- color,
12
- formatter = defaultFormatter,
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
- min,
11
- color,
12
- formatter = defaultFormatter,
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 paginationState = useWidgetStore(
52
- useShallow((state) => {
53
- const widget = state.widgets[widgetId] as TableWidgetState | undefined
54
- return {
55
- paginationEnabled: !!widget?.pagination,
56
- page: widget?.pagination?.page ?? DEFAULT_PAGE,
57
- rowsPerPage: widget?.pagination?.rowsPerPage ?? DEFAULT_ROWS_PER_PAGE,
58
- rowsPerPageOptions:
59
- widget?.pagination?.rowsPerPageOptions ??
60
- DEFAULT_ROWS_PER_PAGE_OPTIONS,
61
- total: widget?.pagination?.total ?? data.length,
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
- () => setPage(paginationState.page + 1),
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' || !paginationState.paginationEnabled) {
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, paginationState.page, paginationState.rowsPerPage)
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
- ...paginationState,
151
+ paginationEnabled,
152
+ page,
153
+ rowsPerPage,
154
+ rowsPerPageOptions,
155
+ total,
147
156
  paginatedData,
148
157
  setPage,
149
158
  setRowsPerPage,