@carto/ps-react-ui 4.3.6 → 4.3.7

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 (119) 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/index.d.ts +2 -2
  20. package/dist/types/widgets/actions/lock-selection/types.d.ts +0 -13
  21. package/dist/types/widgets/actions/relative-data/types.d.ts +0 -4
  22. package/dist/types/widgets/actions/searcher/types.d.ts +0 -2
  23. package/dist/types/widgets/actions/stack-toggle/types.d.ts +0 -4
  24. package/dist/types/widgets/echart/types.d.ts +0 -4
  25. package/dist/types/widgets/echart/utils.d.ts +2 -1
  26. package/dist/types/widgets/error/error.d.ts +1 -1
  27. package/dist/types/widgets/error/types.d.ts +8 -0
  28. package/dist/types/widgets/loader/loader.d.ts +1 -1
  29. package/dist/types/widgets/loader/types.d.ts +1 -1
  30. package/dist/types/widgets/stores/types.d.ts +1 -1
  31. package/dist/{use-widget-ref-B8x4sHIj.js → use-widget-ref-P-2i0MJG.js} +2 -2
  32. package/dist/{use-widget-ref-B8x4sHIj.js.map → use-widget-ref-P-2i0MJG.js.map} +1 -1
  33. package/dist/{utils-D3-eQyDR.js → utils-idmvq0Oa.js} +17 -16
  34. package/dist/utils-idmvq0Oa.js.map +1 -0
  35. package/dist/{widget-store-Dn0Bnc4h.js → widget-store-CzDt8oSK.js} +31 -46
  36. package/dist/widget-store-CzDt8oSK.js.map +1 -0
  37. package/dist/widgets/actions.js +690 -716
  38. package/dist/widgets/actions.js.map +1 -1
  39. package/dist/widgets/bar.js +67 -63
  40. package/dist/widgets/bar.js.map +1 -1
  41. package/dist/widgets/category.js +250 -241
  42. package/dist/widgets/category.js.map +1 -1
  43. package/dist/widgets/echart.js +93 -100
  44. package/dist/widgets/echart.js.map +1 -1
  45. package/dist/widgets/error.js +1 -1
  46. package/dist/widgets/formula.js +64 -72
  47. package/dist/widgets/formula.js.map +1 -1
  48. package/dist/widgets/histogram.js +75 -73
  49. package/dist/widgets/histogram.js.map +1 -1
  50. package/dist/widgets/loader.js +1 -1
  51. package/dist/widgets/loader.js.map +1 -1
  52. package/dist/widgets/markdown.js +2 -2
  53. package/dist/widgets/no-data.js +1 -1
  54. package/dist/widgets/pie.js +4 -4
  55. package/dist/widgets/range.js +97 -105
  56. package/dist/widgets/range.js.map +1 -1
  57. package/dist/widgets/scatterplot.js +8 -8
  58. package/dist/widgets/skeleton-loader.js +1 -1
  59. package/dist/widgets/spread.js +84 -100
  60. package/dist/widgets/spread.js.map +1 -1
  61. package/dist/widgets/stores.js +1 -1
  62. package/dist/widgets/table.js +493 -485
  63. package/dist/widgets/table.js.map +1 -1
  64. package/dist/widgets/timeseries.js +4 -4
  65. package/dist/widgets/wrapper.js +156 -156
  66. package/dist/widgets/wrapper.js.map +1 -1
  67. package/dist/widgets.js +4 -4
  68. package/package.json +3 -3
  69. package/src/components/lasso-tool/lasso-tool-inline.tsx +19 -17
  70. package/src/components/lasso-tool/lasso-tool.tsx +22 -20
  71. package/src/components/lasso-tool/types.ts +4 -3
  72. package/src/widgets/_shared/chart-config/index.ts +1 -0
  73. package/src/widgets/_shared/chart-config/option-builders.test.ts +40 -0
  74. package/src/widgets/_shared/chart-config/option-builders.ts +12 -0
  75. package/src/widgets/actions/fullscreen/fullscreen.tsx +5 -8
  76. package/src/widgets/actions/index.ts +2 -5
  77. package/src/widgets/actions/lock-selection/lock-selection.test.tsx +28 -30
  78. package/src/widgets/actions/lock-selection/lock-selection.tsx +25 -26
  79. package/src/widgets/actions/lock-selection/types.ts +0 -17
  80. package/src/widgets/actions/relative-data/relative-data.test.tsx +13 -13
  81. package/src/widgets/actions/relative-data/relative-data.tsx +18 -21
  82. package/src/widgets/actions/relative-data/types.ts +0 -7
  83. package/src/widgets/actions/searcher/searcher.tsx +40 -22
  84. package/src/widgets/actions/searcher/types.ts +0 -2
  85. package/src/widgets/actions/stack-toggle/stack-toggle.test.tsx +19 -9
  86. package/src/widgets/actions/stack-toggle/stack-toggle.tsx +32 -22
  87. package/src/widgets/actions/stack-toggle/types.ts +0 -8
  88. package/src/widgets/actions/zoom-toggle/zoom-toggle.tsx +113 -95
  89. package/src/widgets/bar/config.ts +37 -28
  90. package/src/widgets/category/category-ui.tsx +25 -22
  91. package/src/widgets/echart/echart-ui.test.tsx +3 -18
  92. package/src/widgets/echart/echart-ui.tsx +4 -22
  93. package/src/widgets/echart/echart.test.tsx +9 -25
  94. package/src/widgets/echart/echart.tsx +36 -29
  95. package/src/widgets/echart/types.ts +0 -4
  96. package/src/widgets/echart/utils.ts +3 -1
  97. package/src/widgets/error/error.tsx +17 -14
  98. package/src/widgets/error/types.ts +10 -0
  99. package/src/widgets/formula/components/value.tsx +13 -13
  100. package/src/widgets/histogram/config.ts +36 -29
  101. package/src/widgets/loader/loader.tsx +3 -1
  102. package/src/widgets/loader/types.ts +3 -1
  103. package/src/widgets/no-data/no-data.tsx +8 -11
  104. package/src/widgets/range/components/range-item.tsx +9 -13
  105. package/src/widgets/spread/components/max-value.tsx +13 -13
  106. package/src/widgets/spread/components/min-value.tsx +13 -13
  107. package/src/widgets/stores/types.ts +1 -4
  108. package/src/widgets/stores/widget-store.ts +1 -27
  109. package/src/widgets/table/hooks/use-pagination.ts +44 -35
  110. package/src/widgets/table/hooks/use-sort.ts +25 -23
  111. package/src/widgets/wrapper/wrapper-ui.tsx +16 -17
  112. package/dist/error-piB8FwYO.js +0 -38
  113. package/dist/error-piB8FwYO.js.map +0 -1
  114. package/dist/lasso-tool-BctzdzBu.js.map +0 -1
  115. package/dist/no-data-jdlbMef0.js +0 -61
  116. package/dist/no-data-jdlbMef0.js.map +0 -1
  117. package/dist/styles-CCZnY17y.js.map +0 -1
  118. package/dist/utils-D3-eQyDR.js.map +0 -1
  119. package/dist/widget-store-Dn0Bnc4h.js.map +0 -1
@@ -3,13 +3,8 @@ import * as echarts from 'echarts'
3
3
  import type { EchartUIProps } from './types'
4
4
  import { useWidgetRef } from '../../hooks'
5
5
 
6
- const DEFAULT_REPLACE_MERGE: string[] = ['dataset', 'series']
7
-
8
6
  export function EchartUI(props: EchartUIProps) {
9
7
  const { id, ref, init, option, className, style, onEvents } = props
10
- const replaceMerge = toReplaceMerge(
11
- (props as { replaceMerge?: unknown }).replaceMerge,
12
- )
13
8
 
14
9
  const chartRef = useWidgetRef<HTMLDivElement>(id)
15
10
  const chartInstance = useRef<echarts.ECharts>(null)
@@ -34,16 +29,11 @@ export function EchartUI(props: EchartUIProps) {
34
29
 
35
30
  // Update chart when options change
36
31
  useEffect(() => {
37
- const setOptionConfig = {
32
+ chartInstance.current?.setOption(option, {
38
33
  lazyUpdate: true,
39
- replaceMerge: replaceMerge ?? DEFAULT_REPLACE_MERGE,
40
- }
41
-
42
- chartInstance.current?.setOption(
43
- option,
44
- setOptionConfig as Parameters<echarts.ECharts['setOption']>[1],
45
- )
46
- }, [option, replaceMerge])
34
+ notMerge: true,
35
+ })
36
+ }, [option])
47
37
 
48
38
  // Handle resize using ResizeObserver
49
39
  useEffect(() => {
@@ -82,11 +72,3 @@ export function EchartUI(props: EchartUIProps) {
82
72
 
83
73
  return <div id={id} ref={chartRef} style={style} className={className} />
84
74
  }
85
-
86
- function toReplaceMerge(value: unknown): string[] | undefined {
87
- if (!Array.isArray(value)) {
88
- return undefined
89
- }
90
-
91
- return value.filter((item): item is string => typeof item === 'string')
92
- }
@@ -184,7 +184,7 @@ describe('Echart', () => {
184
184
  }),
185
185
  {
186
186
  lazyUpdate: true,
187
- replaceMerge: ['dataset', 'series'],
187
+ notMerge: true,
188
188
  },
189
189
  )
190
190
  })
@@ -277,7 +277,7 @@ describe('Echart', () => {
277
277
 
278
278
  expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
279
279
  lazyUpdate: true,
280
- replaceMerge: ['dataset', 'series'],
280
+ notMerge: true,
281
281
  })
282
282
 
283
283
  const newOption: EChartsOption = {
@@ -299,7 +299,7 @@ describe('Echart', () => {
299
299
 
300
300
  expect(mockChart.setOption).toHaveBeenCalledWith(newOption, {
301
301
  lazyUpdate: true,
302
- replaceMerge: ['dataset', 'series'],
302
+ notMerge: true,
303
303
  })
304
304
  })
305
305
 
@@ -367,7 +367,7 @@ describe('Echart', () => {
367
367
 
368
368
  expect(mockChart.setOption).toHaveBeenCalledWith(complexOption, {
369
369
  lazyUpdate: true,
370
- replaceMerge: ['dataset', 'series'],
370
+ notMerge: true,
371
371
  })
372
372
  })
373
373
 
@@ -404,7 +404,7 @@ describe('Echart', () => {
404
404
  )
405
405
  expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
406
406
  lazyUpdate: true,
407
- replaceMerge: ['dataset', 'series'],
407
+ notMerge: true,
408
408
  })
409
409
  expect(mockChart.on).toHaveBeenCalledWith('click', clickHandler)
410
410
  })
@@ -447,7 +447,7 @@ describe('Echart', () => {
447
447
 
448
448
  expect(mockChart.setOption).toHaveBeenCalledWith(emptyOption, {
449
449
  lazyUpdate: true,
450
- replaceMerge: ['dataset', 'series'],
450
+ notMerge: true,
451
451
  })
452
452
  })
453
453
 
@@ -470,7 +470,7 @@ describe('Echart', () => {
470
470
  }),
471
471
  {
472
472
  lazyUpdate: true,
473
- replaceMerge: ['dataset', 'series'],
473
+ notMerge: true,
474
474
  },
475
475
  )
476
476
  })
@@ -516,7 +516,7 @@ describe('Echart', () => {
516
516
 
517
517
  expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
518
518
  lazyUpdate: true,
519
- replaceMerge: ['dataset', 'series'],
519
+ notMerge: true,
520
520
  })
521
521
 
522
522
  rerender(<Echart id='chart-b' />)
@@ -525,27 +525,11 @@ describe('Echart', () => {
525
525
  { series: [{ type: 'line', data: [1, 2, 3] }] },
526
526
  {
527
527
  lazyUpdate: true,
528
- replaceMerge: ['dataset', 'series'],
528
+ notMerge: true,
529
529
  },
530
530
  )
531
531
  })
532
532
 
533
- test('passes replaceMerge from widget to EchartUI', () => {
534
- useWidgetStore.getState().setWidget('test-echart', {
535
- id: 'test-echart',
536
- type: 'echart',
537
- option: basicOption,
538
- replaceMerge: ['series'],
539
- } as EchartWidgetState)
540
-
541
- render(<Echart id='test-echart' />)
542
-
543
- expect(mockChart.setOption).toHaveBeenCalledWith(basicOption, {
544
- lazyUpdate: true,
545
- replaceMerge: ['series'],
546
- })
547
- })
548
-
549
533
  test('disposes chart when component unmounts', () => {
550
534
  useWidgetStore.getState().setWidget('test-echart', {
551
535
  id: 'test-echart',
@@ -10,43 +10,50 @@ import { useShallow } from 'zustand/shallow'
10
10
  import { useMemo } from 'react'
11
11
 
12
12
  export function Echart(props: EchartProps) {
13
- const widget = useWidgetStore(
14
- useShallow((state) => {
15
- const widget = state.getWidget<EchartWidgetState>(props.id)
16
- return {
17
- id: widget?.id,
18
- data: widget?.data as EchartWidgetData | undefined,
19
- option: widget?.option,
20
- onEvents: widget?.onEvents,
21
- init: widget?.init,
22
- replaceMerge: widget?.replaceMerge,
23
- }
24
- }),
13
+ const id = useWidgetStore(
14
+ (state) => state.getWidget<EchartWidgetState>(props.id)?.id,
15
+ )
16
+
17
+ const data = useWidgetStore(
18
+ useShallow(
19
+ (state) =>
20
+ state.getWidget<EchartWidgetState>(props.id)?.data as
21
+ | EchartWidgetData
22
+ | undefined,
23
+ ),
24
+ )
25
+
26
+ const widgetOption = useWidgetStore(
27
+ useShallow((state) => state.getWidget<EchartWidgetState>(props.id)?.option),
28
+ )
29
+
30
+ const onEvents = useWidgetStore(
31
+ useShallow(
32
+ (state) => state.getWidget<EchartWidgetState>(props.id)?.onEvents,
33
+ ),
34
+ )
35
+
36
+ const init = useWidgetStore(
37
+ (state) => state.getWidget<EchartWidgetState>(props.id)?.init,
25
38
  )
26
39
 
27
40
  // Memoize dataset transformation to avoid re-computing on every render
28
- const dataset = useMemo(() => buildDataset(widget.data), [widget.data])
41
+ const dataset = useMemo(() => buildDataset(data), [data])
29
42
 
30
- if (!widget.id) {
43
+ const option = useMemo<EchartOptionsProps>(
44
+ () => ({
45
+ ...widgetOption,
46
+ ...(dataset && { dataset }),
47
+ }),
48
+ [widgetOption, dataset],
49
+ )
50
+
51
+ if (!id) {
31
52
  return null
32
53
  }
33
- const option: EchartOptionsProps = {
34
- ...widget.option,
35
- ...(dataset && { dataset }),
36
- }
37
-
38
- const onEvents = widget.onEvents
39
- const init = widget.init
40
- const replaceMerge = widget.replaceMerge
41
54
 
42
55
  return (
43
- <EchartUI
44
- id={props.id}
45
- option={option}
46
- onEvents={onEvents}
47
- init={init}
48
- replaceMerge={replaceMerge}
49
- />
56
+ <EchartUI id={props.id} option={option} onEvents={onEvents} init={init} />
50
57
  )
51
58
  }
52
59
 
@@ -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) => {
@@ -3,7 +3,9 @@ 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,
@@ -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) {