@carto/ps-react-ui 4.3.3 → 4.3.5
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 +3 -3
- package/dist/components.js.map +1 -1
- package/dist/{lasso-tool-BwRzEW7k.js → lasso-tool-wFqOD6wk.js} +13 -13
- package/dist/lasso-tool-wFqOD6wk.js.map +1 -0
- package/dist/types/components/common-types.d.ts +41 -0
- package/dist/types/components/types.d.ts +1 -1
- package/dist/types/widgets/echart/echart-ui.d.ts +1 -1
- package/dist/types/widgets/echart/types.d.ts +4 -0
- package/dist/widgets/actions.js +1 -1
- package/dist/widgets/bar.js +1 -1
- package/dist/widgets/category.js +1 -1
- package/dist/widgets/echart.js +96 -85
- package/dist/widgets/echart.js.map +1 -1
- package/dist/widgets/formula.js +1 -1
- package/dist/widgets/histogram.js +1 -1
- package/dist/widgets/markdown.js +1 -1
- package/dist/widgets/pie.js +1 -1
- package/dist/widgets/scatterplot.js +1 -1
- package/dist/widgets/spread.js +1 -1
- package/dist/widgets/table.js +1 -1
- package/dist/widgets/timeseries.js +1 -1
- package/dist/widgets/toolbar-actions.js.map +1 -1
- package/dist/widgets/wrapper.js +1 -1
- package/package.json +8 -3
- package/src/components/basemaps/basemaps.test.tsx +196 -0
- package/src/components/basemaps/basemaps.tsx +128 -0
- package/src/components/basemaps/const.ts +13 -0
- package/src/components/basemaps/group-wrapper.test.tsx +38 -0
- package/src/components/basemaps/group-wrapper.tsx +28 -0
- package/src/components/basemaps/group.test.tsx +52 -0
- package/src/components/basemaps/group.tsx +42 -0
- package/src/components/basemaps/header.test.tsx +54 -0
- package/src/components/basemaps/header.tsx +36 -0
- package/src/components/basemaps/styles.ts +76 -0
- package/src/components/basemaps/types.ts +30 -0
- package/src/components/common-types.ts +1 -0
- package/src/components/geolocation-controls/const.ts +6 -0
- package/src/components/geolocation-controls/geolocation-controls.test.tsx +133 -0
- package/src/components/geolocation-controls/geolocation-controls.tsx +95 -0
- package/src/components/geolocation-controls/types.ts +17 -0
- package/src/components/index.ts +64 -0
- package/src/components/lasso-tool/chip.tsx +37 -0
- package/src/components/lasso-tool/const.tsx +70 -0
- package/src/components/lasso-tool/icons.tsx +91 -0
- package/src/components/lasso-tool/lasso-tool-inline.test.tsx +168 -0
- package/src/components/lasso-tool/lasso-tool-inline.tsx +245 -0
- package/src/components/lasso-tool/lasso-tool.test.tsx +212 -0
- package/src/components/lasso-tool/lasso-tool.tsx +479 -0
- package/src/components/lasso-tool/styles.ts +143 -0
- package/src/components/lasso-tool/types.ts +114 -0
- package/src/components/list-data/list-data-skeleton.test.tsx +10 -0
- package/src/components/list-data/list-data-skeleton.tsx +40 -0
- package/src/components/list-data/list-data.test.tsx +94 -0
- package/src/components/list-data/list-data.tsx +106 -0
- package/src/components/list-data/styles.ts +37 -0
- package/src/components/list-data/types.ts +25 -0
- package/src/components/measurement-tools/const.tsx +108 -0
- package/src/components/measurement-tools/icons.tsx +54 -0
- package/src/components/measurement-tools/measurement-tools.test.tsx +165 -0
- package/src/components/measurement-tools/measurement-tools.tsx +443 -0
- package/src/components/measurement-tools/styles.ts +91 -0
- package/src/components/measurement-tools/types.ts +77 -0
- package/src/components/no-data-alert/no-data-alert.test.tsx +31 -0
- package/src/components/no-data-alert/no-data-alert.tsx +59 -0
- package/src/components/responsive-drawer/responsive-drawer.test.tsx +91 -0
- package/src/components/responsive-drawer/responsive-drawer.tsx +53 -0
- package/src/components/smart-tooltip/smart-tooltip.test.tsx +168 -0
- package/src/components/smart-tooltip/smart-tooltip.tsx +40 -0
- package/src/components/tooltip/tooltip.test.tsx +86 -0
- package/src/components/tooltip/tooltip.tsx +30 -0
- package/src/components/types.ts +1 -0
- package/src/components/zoom-controls/styles.ts +27 -0
- package/src/components/zoom-controls/types.ts +19 -0
- package/src/components/zoom-controls/zoom-controls.test.tsx +101 -0
- package/src/components/zoom-controls/zoom-controls.tsx +114 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/use-debounce.ts +55 -0
- package/src/hooks/use-skeleton.test.tsx +32 -0
- package/src/hooks/use-skeleton.ts +19 -0
- package/src/hooks/use-widget-ref.ts +33 -0
- package/src/widgets/README.md +277 -0
- package/src/widgets/_shared/chart-config/config-factory.ts +67 -0
- package/src/widgets/_shared/chart-config/csv-modifiers.ts +56 -0
- package/src/widgets/_shared/chart-config/index.ts +21 -0
- package/src/widgets/_shared/chart-config/option-builders.ts +203 -0
- package/src/widgets/_shared/skeleton/index.ts +5 -0
- package/src/widgets/_shared/skeleton/styles.ts +20 -0
- package/src/widgets/actions/change-column/change-column-icon.tsx +10 -0
- package/src/widgets/actions/change-column/change-column.test.tsx +163 -0
- package/src/widgets/actions/change-column/change-column.tsx +141 -0
- package/src/widgets/actions/change-column/sortable-column-item.tsx +49 -0
- package/src/widgets/actions/change-column/types.ts +20 -0
- package/src/widgets/actions/download/download.test.tsx +322 -0
- package/src/widgets/actions/download/download.tsx +118 -0
- package/src/widgets/actions/download/exports.test.tsx +275 -0
- package/src/widgets/actions/download/exports.tsx +103 -0
- package/src/widgets/actions/download/types.ts +21 -0
- package/src/widgets/actions/fullscreen/fullscreen.test.tsx +269 -0
- package/src/widgets/actions/fullscreen/fullscreen.tsx +82 -0
- package/src/widgets/actions/fullscreen/styles.ts +17 -0
- package/src/widgets/actions/fullscreen/types.ts +27 -0
- package/src/widgets/actions/index.ts +51 -0
- package/src/widgets/actions/lock-selection/lock-selection.test.tsx +186 -0
- package/src/widgets/actions/lock-selection/lock-selection.tsx +133 -0
- package/src/widgets/actions/lock-selection/types.ts +41 -0
- package/src/widgets/actions/relative-data/relative-data.test.tsx +267 -0
- package/src/widgets/actions/relative-data/relative-data.tsx +133 -0
- package/src/widgets/actions/relative-data/style.ts +9 -0
- package/src/widgets/actions/relative-data/types.ts +31 -0
- package/src/widgets/actions/relative-data/utils.test.ts +223 -0
- package/src/widgets/actions/relative-data/utils.ts +58 -0
- package/src/widgets/actions/searcher/searcher-toggle.test.tsx +354 -0
- package/src/widgets/actions/searcher/searcher-toggle.tsx +73 -0
- package/src/widgets/actions/searcher/searcher.tsx +205 -0
- package/src/widgets/actions/searcher/types.ts +72 -0
- package/src/widgets/actions/shared/styles.ts +12 -0
- package/src/widgets/actions/stack-toggle/grouped-bar-chart-icon.tsx +14 -0
- package/src/widgets/actions/stack-toggle/stack-toggle.test.tsx +270 -0
- package/src/widgets/actions/stack-toggle/stack-toggle.tsx +146 -0
- package/src/widgets/actions/stack-toggle/types.ts +29 -0
- package/src/widgets/actions/zoom-toggle/index.ts +2 -0
- package/src/widgets/actions/zoom-toggle/style.ts +14 -0
- package/src/widgets/actions/zoom-toggle/types.ts +44 -0
- package/src/widgets/actions/zoom-toggle/zoom-toggle.tsx +186 -0
- package/src/widgets/bar/config.ts +122 -0
- package/src/widgets/bar/index.ts +9 -0
- package/src/widgets/bar/skeleton.tsx +60 -0
- package/src/widgets/bar/style.ts +33 -0
- package/src/widgets/bar/types.ts +16 -0
- package/src/widgets/category/category-ui.test.tsx +399 -0
- package/src/widgets/category/category-ui.tsx +156 -0
- package/src/widgets/category/components/category-bar.tsx +28 -0
- package/src/widgets/category/components/category-legend.tsx +30 -0
- package/src/widgets/category/components/category-row-multi.tsx +50 -0
- package/src/widgets/category/components/category-row-other.tsx +23 -0
- package/src/widgets/category/components/category-row-single.tsx +47 -0
- package/src/widgets/category/components/index.ts +14 -0
- package/src/widgets/category/config.ts +85 -0
- package/src/widgets/category/index.ts +30 -0
- package/src/widgets/category/skeleton.tsx +24 -0
- package/src/widgets/category/style.ts +133 -0
- package/src/widgets/category/types.ts +40 -0
- package/src/widgets/echart/const.ts +1 -0
- package/src/widgets/echart/echart-ui.test.tsx +537 -0
- package/src/widgets/echart/echart-ui.tsx +92 -0
- package/src/widgets/echart/echart.test.tsx +562 -0
- package/src/widgets/echart/echart.tsx +68 -0
- package/src/widgets/echart/index.ts +16 -0
- package/src/widgets/echart/options.ts +53 -0
- package/src/widgets/echart/types.ts +45 -0
- package/src/widgets/echart/utils.ts +169 -0
- package/src/widgets/error/error.test.tsx +331 -0
- package/src/widgets/error/error.tsx +40 -0
- package/src/widgets/error/index.ts +2 -0
- package/src/widgets/error/types.ts +14 -0
- package/src/widgets/formula/components/item.test.tsx +249 -0
- package/src/widgets/formula/components/item.tsx +18 -0
- package/src/widgets/formula/components/prefix.test.tsx +341 -0
- package/src/widgets/formula/components/prefix.tsx +18 -0
- package/src/widgets/formula/components/row.test.tsx +364 -0
- package/src/widgets/formula/components/row.tsx +21 -0
- package/src/widgets/formula/components/series.tsx +34 -0
- package/src/widgets/formula/components/suffix.test.tsx +383 -0
- package/src/widgets/formula/components/suffix.tsx +28 -0
- package/src/widgets/formula/components/value.test.tsx +329 -0
- package/src/widgets/formula/components/value.tsx +29 -0
- package/src/widgets/formula/config.ts +27 -0
- package/src/widgets/formula/formula-ui.test.tsx +399 -0
- package/src/widgets/formula/formula-ui.tsx +27 -0
- package/src/widgets/formula/index.ts +24 -0
- package/src/widgets/formula/serializer.test.tsx +144 -0
- package/src/widgets/formula/serializer.ts +28 -0
- package/src/widgets/formula/skeleton.tsx +10 -0
- package/src/widgets/formula/style.ts +23 -0
- package/src/widgets/formula/types.ts +50 -0
- package/src/widgets/histogram/config.ts +143 -0
- package/src/widgets/histogram/index.ts +8 -0
- package/src/widgets/histogram/skeleton.tsx +52 -0
- package/src/widgets/histogram/style.ts +8 -0
- package/src/widgets/histogram/types.ts +17 -0
- package/src/widgets/index.ts +25 -0
- package/src/widgets/loader/index.ts +4 -0
- package/src/widgets/loader/loader.tsx +70 -0
- package/src/widgets/loader/types.ts +11 -0
- package/src/widgets/loader/utils.test.ts +112 -0
- package/src/widgets/loader/utils.ts +35 -0
- package/src/widgets/markdown/config.ts +18 -0
- package/src/widgets/markdown/index.ts +14 -0
- package/src/widgets/markdown/markdown-ui.test.tsx +341 -0
- package/src/widgets/markdown/markdown-ui.tsx +52 -0
- package/src/widgets/markdown/markdown.tsx +20 -0
- package/src/widgets/markdown/skeleton.tsx +12 -0
- package/src/widgets/markdown/style.ts +28 -0
- package/src/widgets/markdown/types.ts +28 -0
- package/src/widgets/no-data/index.ts +2 -0
- package/src/widgets/no-data/no-data.test.tsx +447 -0
- package/src/widgets/no-data/no-data.tsx +116 -0
- package/src/widgets/no-data/style.ts +18 -0
- package/src/widgets/no-data/types.ts +72 -0
- package/src/widgets/note/index.ts +2 -0
- package/src/widgets/note/note.test.tsx +391 -0
- package/src/widgets/note/note.tsx +114 -0
- package/src/widgets/note/style.ts +29 -0
- package/src/widgets/note/types.ts +9 -0
- package/src/widgets/pie/config.ts +177 -0
- package/src/widgets/pie/index.ts +8 -0
- package/src/widgets/pie/skeleton.tsx +70 -0
- package/src/widgets/pie/style.ts +8 -0
- package/src/widgets/pie/types.ts +16 -0
- package/src/widgets/range/components/range-item.tsx +213 -0
- package/src/widgets/range/config.ts +10 -0
- package/src/widgets/range/index.ts +16 -0
- package/src/widgets/range/range-ui.test.tsx +203 -0
- package/src/widgets/range/range-ui.tsx +11 -0
- package/src/widgets/range/serializer.test.ts +70 -0
- package/src/widgets/range/serializer.ts +27 -0
- package/src/widgets/range/skeleton.tsx +14 -0
- package/src/widgets/range/style.ts +37 -0
- package/src/widgets/range/types.ts +39 -0
- package/src/widgets/scatterplot/config.ts +138 -0
- package/src/widgets/scatterplot/index.ts +8 -0
- package/src/widgets/scatterplot/skeleton.tsx +59 -0
- package/src/widgets/scatterplot/style.ts +21 -0
- package/src/widgets/scatterplot/types.ts +17 -0
- package/src/widgets/selection-summary/index.ts +6 -0
- package/src/widgets/selection-summary/selection-summary.tsx +46 -0
- package/src/widgets/selection-summary/style.ts +10 -0
- package/src/widgets/selection-summary/types.ts +14 -0
- package/src/widgets/skeleton-loader/index.ts +2 -0
- package/src/widgets/skeleton-loader/skeleton-loader.test.tsx +139 -0
- package/src/widgets/skeleton-loader/skeleton-loader.tsx +28 -0
- package/src/widgets/skeleton-loader/types.ts +8 -0
- package/src/widgets/spread/components/max-value.tsx +29 -0
- package/src/widgets/spread/components/min-value.tsx +29 -0
- package/src/widgets/spread/components/separator.tsx +6 -0
- package/src/widgets/spread/config.ts +34 -0
- package/src/widgets/spread/index.ts +23 -0
- package/src/widgets/spread/skeleton.tsx +10 -0
- package/src/widgets/spread/spread-ui.test.tsx +368 -0
- package/src/widgets/spread/spread-ui.tsx +29 -0
- package/src/widgets/spread/style.ts +22 -0
- package/src/widgets/spread/types.ts +47 -0
- package/src/widgets/stores/index.ts +9 -0
- package/src/widgets/stores/types.ts +192 -0
- package/src/widgets/stores/widget-store.test.ts +601 -0
- package/src/widgets/stores/widget-store.ts +239 -0
- package/src/widgets/subheader/index.ts +3 -0
- package/src/widgets/subheader/style.ts +20 -0
- package/src/widgets/subheader/subheader.test.tsx +45 -0
- package/src/widgets/subheader/subheader.tsx +16 -0
- package/src/widgets/subheader/types.ts +11 -0
- package/src/widgets/table/components/cell-header.tsx +58 -0
- package/src/widgets/table/components/cell.tsx +80 -0
- package/src/widgets/table/components/index.ts +4 -0
- package/src/widgets/table/components/pagination-actions.tsx +67 -0
- package/src/widgets/table/components/pagination.tsx +41 -0
- package/src/widgets/table/components/row.tsx +60 -0
- package/src/widgets/table/config.ts +71 -0
- package/src/widgets/table/helpers.test.ts +244 -0
- package/src/widgets/table/helpers.ts +107 -0
- package/src/widgets/table/hooks/index.ts +7 -0
- package/src/widgets/table/hooks/use-pagination.test.ts +294 -0
- package/src/widgets/table/hooks/use-pagination.ts +155 -0
- package/src/widgets/table/hooks/use-selection.test.ts +504 -0
- package/src/widgets/table/hooks/use-selection.ts +189 -0
- package/src/widgets/table/hooks/use-sort.test.ts +296 -0
- package/src/widgets/table/hooks/use-sort.ts +138 -0
- package/src/widgets/table/index.ts +53 -0
- package/src/widgets/table/serializer.ts +54 -0
- package/src/widgets/table/skeleton.tsx +48 -0
- package/src/widgets/table/style.ts +34 -0
- package/src/widgets/table/table-ui.tsx +64 -0
- package/src/widgets/table/types.ts +223 -0
- package/src/widgets/timeseries/config.ts +135 -0
- package/src/widgets/timeseries/index.ts +8 -0
- package/src/widgets/timeseries/skeleton.tsx +55 -0
- package/src/widgets/timeseries/style.ts +36 -0
- package/src/widgets/timeseries/types.ts +17 -0
- package/src/widgets/toolbar-actions/index.ts +6 -0
- package/src/widgets/toolbar-actions/styles.ts +38 -0
- package/src/widgets/toolbar-actions/toolbar-actions.test.tsx +691 -0
- package/src/widgets/toolbar-actions/toolbar-actions.tsx +145 -0
- package/src/widgets/toolbar-actions/types.ts +60 -0
- package/src/widgets/wrapper/components/actions.test.tsx +101 -0
- package/src/widgets/wrapper/components/actions.tsx +30 -0
- package/src/widgets/wrapper/components/options.test.tsx +323 -0
- package/src/widgets/wrapper/components/options.tsx +73 -0
- package/src/widgets/wrapper/components/title.test.tsx +126 -0
- package/src/widgets/wrapper/components/title.tsx +32 -0
- package/src/widgets/wrapper/index.ts +16 -0
- package/src/widgets/wrapper/styles.ts +98 -0
- package/src/widgets/wrapper/types.ts +55 -0
- package/src/widgets/wrapper/wrapper-ui.test.tsx +232 -0
- package/src/widgets/wrapper/wrapper-ui.tsx +57 -0
- package/src/widgets/wrapper/wrapper.test.tsx +365 -0
- package/src/widgets/wrapper/wrapper.tsx +50 -0
- package/dist/lasso-tool-BwRzEW7k.js.map +0 -1
- package/dist/types/common/common.d.ts +0 -3
- package/dist/types/common/index.d.ts +0 -26
- package/dist/types/common/lasso-tools.d.ts +0 -36
- package/dist/types/common/measurement-tools.d.ts +0 -65
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { create } from 'zustand'
|
|
2
|
+
import type { WidgetState, WidgetStore, ToolRegistration } from './types'
|
|
3
|
+
|
|
4
|
+
// Track active pipeline executions for cancellation
|
|
5
|
+
const activePipelines = new Map<string, number>()
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Zustand store for managing widget state across the application.
|
|
9
|
+
*
|
|
10
|
+
* This store provides centralized state management for all widget UI components
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* // Import the store
|
|
15
|
+
* import { useWidgetStore } from '@carto/ps-react-ui/widgets'
|
|
16
|
+
*
|
|
17
|
+
* // Use in a component
|
|
18
|
+
* function MyWidget() {
|
|
19
|
+
* const setWidget = useWidgetStore((state) => state.setWidget)
|
|
20
|
+
* const widget = useWidgetStore((state) => state.widgets['my-widget'])
|
|
21
|
+
*
|
|
22
|
+
* useEffect(() => {
|
|
23
|
+
* setWidget({
|
|
24
|
+
* id: 'my-widget',
|
|
25
|
+
* type: 'formula',
|
|
26
|
+
* title: 'Total Sales',
|
|
27
|
+
* isLoading: false,
|
|
28
|
+
* visible: true,
|
|
29
|
+
* data: { value: 1000, prefix: '$' }
|
|
30
|
+
* })
|
|
31
|
+
* }, [setWidget])
|
|
32
|
+
*
|
|
33
|
+
* return <div>{widget?.data?.value}</div>
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* // Get widgets by type
|
|
40
|
+
* const getWidgetsByType = useWidgetStore((state) => state.getWidgetsByType)
|
|
41
|
+
* const formulaWidgets = getWidgetsByType('formula')
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export const useWidgetStore = create<WidgetStore>()((set, get) => ({
|
|
45
|
+
// State
|
|
46
|
+
widgets: {},
|
|
47
|
+
|
|
48
|
+
// Actions
|
|
49
|
+
setWidget: (id, widget) =>
|
|
50
|
+
set((state) => {
|
|
51
|
+
const current =
|
|
52
|
+
state.widgets[id] ?? ({} as WidgetStore['widgets'][string])
|
|
53
|
+
return {
|
|
54
|
+
widgets: {
|
|
55
|
+
...state.widgets,
|
|
56
|
+
[id]: {
|
|
57
|
+
...current,
|
|
58
|
+
...widget,
|
|
59
|
+
id,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
}),
|
|
64
|
+
|
|
65
|
+
removeWidget: (id) =>
|
|
66
|
+
set((state) => {
|
|
67
|
+
const widgets = { ...state.widgets }
|
|
68
|
+
|
|
69
|
+
delete widgets[id]
|
|
70
|
+
|
|
71
|
+
return { widgets }
|
|
72
|
+
}),
|
|
73
|
+
|
|
74
|
+
clearWidgets: () =>
|
|
75
|
+
set({
|
|
76
|
+
widgets: {},
|
|
77
|
+
}),
|
|
78
|
+
|
|
79
|
+
getWidget: <T extends WidgetState>(id: string) => {
|
|
80
|
+
return get().widgets[id] as T | undefined
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
registerTool: (widgetId: string, tool: ToolRegistration) =>
|
|
84
|
+
set((state) => {
|
|
85
|
+
const current = state.widgets[widgetId] ?? ({} as WidgetState)
|
|
86
|
+
const registeredTools = current.registeredTools ?? []
|
|
87
|
+
|
|
88
|
+
// Remove existing tool with same id if present
|
|
89
|
+
const filteredTools = registeredTools.filter(
|
|
90
|
+
(t: ToolRegistration) => t.id !== tool.id,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
widgets: {
|
|
95
|
+
...state.widgets,
|
|
96
|
+
[widgetId]: {
|
|
97
|
+
...current,
|
|
98
|
+
id: widgetId,
|
|
99
|
+
registeredTools: [...filteredTools, tool],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
}),
|
|
104
|
+
|
|
105
|
+
unregisterTool: (widgetId: string, toolId: string) =>
|
|
106
|
+
set((state) => {
|
|
107
|
+
const current = state.widgets[widgetId]
|
|
108
|
+
if (!current) return state
|
|
109
|
+
|
|
110
|
+
const registeredTools = current.registeredTools ?? []
|
|
111
|
+
const filteredTools = registeredTools.filter(
|
|
112
|
+
(t: ToolRegistration) => t.id !== toolId,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
widgets: {
|
|
117
|
+
...state.widgets,
|
|
118
|
+
[widgetId]: {
|
|
119
|
+
...current,
|
|
120
|
+
registeredTools: filteredTools,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
}),
|
|
125
|
+
|
|
126
|
+
updateToolConfig: (
|
|
127
|
+
widgetId: string,
|
|
128
|
+
toolId: string,
|
|
129
|
+
config: Record<string, unknown>,
|
|
130
|
+
) =>
|
|
131
|
+
set((state) => {
|
|
132
|
+
const current = state.widgets[widgetId]
|
|
133
|
+
if (!current) return state
|
|
134
|
+
|
|
135
|
+
const registeredTools = current.registeredTools ?? []
|
|
136
|
+
const updatedTools = registeredTools.map((tool: ToolRegistration) =>
|
|
137
|
+
tool.id === toolId
|
|
138
|
+
? {
|
|
139
|
+
...tool,
|
|
140
|
+
config: { ...tool.config, ...config },
|
|
141
|
+
}
|
|
142
|
+
: tool,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
widgets: {
|
|
147
|
+
...state.widgets,
|
|
148
|
+
[widgetId]: {
|
|
149
|
+
...current,
|
|
150
|
+
registeredTools: updatedTools,
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
}),
|
|
155
|
+
|
|
156
|
+
setToolEnabled: (widgetId: string, toolId: string, enabled: boolean) =>
|
|
157
|
+
set((state) => {
|
|
158
|
+
const current = state.widgets[widgetId]
|
|
159
|
+
if (!current) return state
|
|
160
|
+
|
|
161
|
+
const registeredTools = current.registeredTools ?? []
|
|
162
|
+
const updatedTools = registeredTools.map((tool: ToolRegistration) =>
|
|
163
|
+
tool.id === toolId ? { ...tool, enabled } : tool,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
widgets: {
|
|
168
|
+
...state.widgets,
|
|
169
|
+
[widgetId]: {
|
|
170
|
+
...current,
|
|
171
|
+
registeredTools: updatedTools,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
}),
|
|
176
|
+
|
|
177
|
+
executeToolPipeline: async (widgetId: string, sourceData: unknown) => {
|
|
178
|
+
const widget = get().widgets[widgetId]
|
|
179
|
+
if (!widget) return
|
|
180
|
+
|
|
181
|
+
// Cancel any in-progress pipeline for this widget
|
|
182
|
+
const currentExecution = (activePipelines.get(widgetId) ?? 0) + 1
|
|
183
|
+
activePipelines.set(widgetId, currentExecution)
|
|
184
|
+
|
|
185
|
+
const widgetWithTools = widget
|
|
186
|
+
|
|
187
|
+
// Build set of tool IDs that should be disabled
|
|
188
|
+
const disabledToolIds = new Set<string>()
|
|
189
|
+
for (const tool of widgetWithTools.registeredTools ?? []) {
|
|
190
|
+
if (tool.enabled && tool.disables) {
|
|
191
|
+
tool.disables.forEach((id) => disabledToolIds.add(id))
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Sort tools by order and filter enabled only, excluding disabled tools
|
|
196
|
+
const sortedTools = [...(widgetWithTools.registeredTools ?? [])]
|
|
197
|
+
.filter((tool) => tool.enabled && !disabledToolIds.has(tool.id))
|
|
198
|
+
.sort((a, b) => a.order - b.order)
|
|
199
|
+
|
|
200
|
+
// Execute pipeline - handle both sync and async tools
|
|
201
|
+
let transformedData = sourceData
|
|
202
|
+
for (const tool of sortedTools) {
|
|
203
|
+
// Check if this execution was cancelled
|
|
204
|
+
if (activePipelines.get(widgetId) !== currentExecution) {
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
// Call tool function (may return Promise or direct value)
|
|
210
|
+
transformedData = await tool.fn(transformedData, tool.config)
|
|
211
|
+
} catch (error) {
|
|
212
|
+
// eslint-disable-next-line no-console
|
|
213
|
+
console.error(`Tool ${tool.id} failed for widget ${widgetId}:`, error)
|
|
214
|
+
// Continue with current data to prevent one tool from breaking all
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Single store update with final transformed data
|
|
219
|
+
set((state) => {
|
|
220
|
+
const currentWidget = state.widgets[widgetId]
|
|
221
|
+
if (!currentWidget) return state
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
widgets: {
|
|
225
|
+
...state.widgets,
|
|
226
|
+
[widgetId]: {
|
|
227
|
+
...currentWidget,
|
|
228
|
+
data: transformedData,
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
// Clean up tracking
|
|
235
|
+
if (activePipelines.get(widgetId) === currentExecution) {
|
|
236
|
+
activePipelines.delete(widgetId)
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
}))
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material'
|
|
2
|
+
|
|
3
|
+
export const styles = {
|
|
4
|
+
root: {
|
|
5
|
+
display: 'flex',
|
|
6
|
+
alignItems: 'center',
|
|
7
|
+
gap: ({ spacing }) => spacing(1),
|
|
8
|
+
minHeight: ({ spacing }) => spacing(3),
|
|
9
|
+
},
|
|
10
|
+
slotLeft: {
|
|
11
|
+
flexShrink: 0,
|
|
12
|
+
},
|
|
13
|
+
slotRight: {
|
|
14
|
+
flexGrow: 1,
|
|
15
|
+
display: 'flex',
|
|
16
|
+
justifyContent: 'flex-end',
|
|
17
|
+
alignItems: 'center',
|
|
18
|
+
gap: ({ spacing }) => spacing(1),
|
|
19
|
+
},
|
|
20
|
+
} satisfies Record<string, SxProps<Theme>>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, test, expect } from 'vitest'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import { WidgetSubHeader } from './subheader'
|
|
4
|
+
|
|
5
|
+
describe('WidgetSubHeader', () => {
|
|
6
|
+
test('renders empty right slot by default', () => {
|
|
7
|
+
const { container } = render(<WidgetSubHeader />)
|
|
8
|
+
expect(container.firstChild).toBeTruthy()
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
test('renders slotLeft content', () => {
|
|
12
|
+
render(<WidgetSubHeader slotLeft={<span>Left Content</span>} />)
|
|
13
|
+
expect(screen.getByText('Left Content')).toBeTruthy()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('renders slotRight content', () => {
|
|
17
|
+
render(<WidgetSubHeader slotRight={<span>Right Content</span>} />)
|
|
18
|
+
expect(screen.getByText('Right Content')).toBeTruthy()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('renders both slots when provided', () => {
|
|
22
|
+
render(
|
|
23
|
+
<WidgetSubHeader
|
|
24
|
+
slotLeft={<span>Left</span>}
|
|
25
|
+
slotRight={<span>Right</span>}
|
|
26
|
+
/>,
|
|
27
|
+
)
|
|
28
|
+
expect(screen.getByText('Left')).toBeTruthy()
|
|
29
|
+
expect(screen.getByText('Right')).toBeTruthy()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('right slot takes full width when left is empty (flex layout)', () => {
|
|
33
|
+
const { container } = render(<WidgetSubHeader />)
|
|
34
|
+
// The right slot should have flexGrow: 1 for full width behavior
|
|
35
|
+
const rightSlot = container.querySelector('[class*="MuiBox-root"]')
|
|
36
|
+
expect(rightSlot).toBeTruthy()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('renders with custom sx prop', () => {
|
|
40
|
+
const { container } = render(
|
|
41
|
+
<WidgetSubHeader sx={{ backgroundColor: 'red' }} />,
|
|
42
|
+
)
|
|
43
|
+
expect(container.firstChild).toBeTruthy()
|
|
44
|
+
})
|
|
45
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Box } from '@mui/material'
|
|
2
|
+
import { styles } from './style'
|
|
3
|
+
import type { WidgetSubHeaderProps } from './types'
|
|
4
|
+
|
|
5
|
+
export function WidgetSubHeader({
|
|
6
|
+
slotLeft,
|
|
7
|
+
slotRight,
|
|
8
|
+
sx,
|
|
9
|
+
}: WidgetSubHeaderProps) {
|
|
10
|
+
return (
|
|
11
|
+
<Box sx={{ ...styles.root, ...sx }}>
|
|
12
|
+
{slotLeft && <Box sx={styles.slotLeft}>{slotLeft}</Box>}
|
|
13
|
+
<Box sx={styles.slotRight}>{slotRight}</Box>
|
|
14
|
+
</Box>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SxProps, Theme } from '@mui/material'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
export interface WidgetSubHeaderProps {
|
|
5
|
+
/** Content for the left slot */
|
|
6
|
+
slotLeft?: ReactNode
|
|
7
|
+
/** Content for the right slot*/
|
|
8
|
+
slotRight?: ReactNode
|
|
9
|
+
/** Custom styles */
|
|
10
|
+
sx?: SxProps<Theme>
|
|
11
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { TableCell, TableSortLabel, Checkbox } from '@mui/material'
|
|
2
|
+
import type { CellHeaderProps } from '../types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Table cell header component with optional sorting and select-all checkbox
|
|
6
|
+
*/
|
|
7
|
+
export function CellHeader({
|
|
8
|
+
column,
|
|
9
|
+
sortState,
|
|
10
|
+
onSort,
|
|
11
|
+
isSelectAll,
|
|
12
|
+
isAllSelected,
|
|
13
|
+
isIndeterminate,
|
|
14
|
+
onSelectAll,
|
|
15
|
+
}: CellHeaderProps) {
|
|
16
|
+
// Render select-all checkbox
|
|
17
|
+
if (isSelectAll) {
|
|
18
|
+
return (
|
|
19
|
+
<TableCell padding='checkbox'>
|
|
20
|
+
<Checkbox
|
|
21
|
+
checked={isAllSelected}
|
|
22
|
+
indeterminate={isIndeterminate}
|
|
23
|
+
onChange={onSelectAll}
|
|
24
|
+
inputProps={{ 'aria-label': 'select all rows' }}
|
|
25
|
+
/>
|
|
26
|
+
</TableCell>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const isSorted = sortState?.columnId === column.id
|
|
31
|
+
const sortDirection = isSorted ? sortState.direction : undefined
|
|
32
|
+
|
|
33
|
+
// Render sortable column header
|
|
34
|
+
if (column.sortable && onSort) {
|
|
35
|
+
return (
|
|
36
|
+
<TableCell
|
|
37
|
+
align={column.align}
|
|
38
|
+
style={{ width: column.width }}
|
|
39
|
+
sortDirection={sortDirection}
|
|
40
|
+
>
|
|
41
|
+
<TableSortLabel
|
|
42
|
+
active={isSorted}
|
|
43
|
+
direction={sortDirection ?? 'asc'}
|
|
44
|
+
onClick={() => onSort(column.id)}
|
|
45
|
+
>
|
|
46
|
+
{column.label}
|
|
47
|
+
</TableSortLabel>
|
|
48
|
+
</TableCell>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Render non-sortable column header
|
|
53
|
+
return (
|
|
54
|
+
<TableCell align={column.align} style={{ width: column.width }}>
|
|
55
|
+
{column.label}
|
|
56
|
+
</TableCell>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { TableCell as MuiTableCell, Link, Typography } from '@mui/material'
|
|
2
|
+
import ReactMarkdown, { type Components } from 'react-markdown'
|
|
3
|
+
import type { TableColumn } from '../types'
|
|
4
|
+
import { getCellValue } from '../helpers'
|
|
5
|
+
import type { ReactNode } from 'react'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Props for Cell component
|
|
9
|
+
*/
|
|
10
|
+
export interface CellProps {
|
|
11
|
+
/** Column definition */
|
|
12
|
+
column: TableColumn
|
|
13
|
+
/** Cell value */
|
|
14
|
+
value: unknown
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const DEFAULT_P = ({ children }: { children?: ReactNode }) => (
|
|
18
|
+
<Typography component='span' variant='body2' color='inherit'>
|
|
19
|
+
{children}
|
|
20
|
+
</Typography>
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Markdown components for cell content
|
|
25
|
+
* Uses compact styling suitable for table cells (reusing Note widget pattern)
|
|
26
|
+
*/
|
|
27
|
+
const CELL_MARKDOWN_COMPONENTS: Components = {
|
|
28
|
+
h1: DEFAULT_P,
|
|
29
|
+
h2: DEFAULT_P,
|
|
30
|
+
h3: DEFAULT_P,
|
|
31
|
+
p: DEFAULT_P,
|
|
32
|
+
a: ({ children, href }) => {
|
|
33
|
+
const isExternal = href?.startsWith('http')
|
|
34
|
+
return (
|
|
35
|
+
<Link
|
|
36
|
+
href={href}
|
|
37
|
+
target={isExternal ? '_blank' : undefined}
|
|
38
|
+
rel={isExternal ? 'noopener noreferrer' : undefined}
|
|
39
|
+
color='text.primary'
|
|
40
|
+
underline='always'
|
|
41
|
+
>
|
|
42
|
+
{children}
|
|
43
|
+
</Link>
|
|
44
|
+
)
|
|
45
|
+
},
|
|
46
|
+
img: () => null,
|
|
47
|
+
ul: DEFAULT_P,
|
|
48
|
+
ol: DEFAULT_P,
|
|
49
|
+
li: DEFAULT_P,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Table cell component with automatic markdown support for string values
|
|
54
|
+
* Markdown is rendered when the raw value is a string and no formatter is defined.
|
|
55
|
+
* If a formatter is used, its output is rendered directly without markdown parsing.
|
|
56
|
+
*/
|
|
57
|
+
export function Cell({ column, value }: CellProps) {
|
|
58
|
+
// If formatter is defined, use it directly without markdown
|
|
59
|
+
if (column.formatter) {
|
|
60
|
+
return (
|
|
61
|
+
<MuiTableCell align={column.align}>
|
|
62
|
+
{column.formatter(value)}
|
|
63
|
+
</MuiTableCell>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// For string values without formatter, render as markdown
|
|
68
|
+
if (typeof value === 'string') {
|
|
69
|
+
return (
|
|
70
|
+
<MuiTableCell align={column.align}>
|
|
71
|
+
<ReactMarkdown components={CELL_MARKDOWN_COMPONENTS}>
|
|
72
|
+
{value}
|
|
73
|
+
</ReactMarkdown>
|
|
74
|
+
</MuiTableCell>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// For non-string values, render directly
|
|
79
|
+
return <MuiTableCell align={column.align}>{getCellValue(value)}</MuiTableCell>
|
|
80
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Box, IconButton } from '@mui/material'
|
|
2
|
+
import FirstPageIcon from '@mui/icons-material/FirstPage'
|
|
3
|
+
import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft'
|
|
4
|
+
import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight'
|
|
5
|
+
import LastPageIcon from '@mui/icons-material/LastPage'
|
|
6
|
+
import type { PaginationActionsProps } from '../types'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Custom pagination actions with first/prev/next/last buttons
|
|
10
|
+
*/
|
|
11
|
+
export function PaginationActions({
|
|
12
|
+
count,
|
|
13
|
+
page,
|
|
14
|
+
rowsPerPage,
|
|
15
|
+
onPageChange,
|
|
16
|
+
}: PaginationActionsProps) {
|
|
17
|
+
const lastPage = Math.max(0, Math.ceil(count / rowsPerPage) - 1)
|
|
18
|
+
|
|
19
|
+
const handleFirstPage = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
20
|
+
onPageChange(event, 0)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const handlePreviousPage = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
24
|
+
onPageChange(event, page - 1)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const handleNextPage = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
28
|
+
onPageChange(event, page + 1)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const handleLastPage = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
32
|
+
onPageChange(event, lastPage)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Box sx={{ flexShrink: 0, ml: 2.5 }}>
|
|
37
|
+
<IconButton
|
|
38
|
+
onClick={handleFirstPage}
|
|
39
|
+
disabled={page === 0}
|
|
40
|
+
aria-label='first page'
|
|
41
|
+
>
|
|
42
|
+
<FirstPageIcon />
|
|
43
|
+
</IconButton>
|
|
44
|
+
<IconButton
|
|
45
|
+
onClick={handlePreviousPage}
|
|
46
|
+
disabled={page === 0}
|
|
47
|
+
aria-label='previous page'
|
|
48
|
+
>
|
|
49
|
+
<KeyboardArrowLeft />
|
|
50
|
+
</IconButton>
|
|
51
|
+
<IconButton
|
|
52
|
+
onClick={handleNextPage}
|
|
53
|
+
disabled={page >= lastPage}
|
|
54
|
+
aria-label='next page'
|
|
55
|
+
>
|
|
56
|
+
<KeyboardArrowRight />
|
|
57
|
+
</IconButton>
|
|
58
|
+
<IconButton
|
|
59
|
+
onClick={handleLastPage}
|
|
60
|
+
disabled={page >= lastPage}
|
|
61
|
+
aria-label='last page'
|
|
62
|
+
>
|
|
63
|
+
<LastPageIcon />
|
|
64
|
+
</IconButton>
|
|
65
|
+
</Box>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { TablePagination as MuiTablePagination } from '@mui/material'
|
|
2
|
+
import type { PaginationProps } from '../types'
|
|
3
|
+
import { PaginationActions } from './pagination-actions'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Table pagination component
|
|
7
|
+
*/
|
|
8
|
+
export function Pagination({
|
|
9
|
+
page,
|
|
10
|
+
rowsPerPage,
|
|
11
|
+
total,
|
|
12
|
+
rowsPerPageOptions = [5, 10, 25, 50],
|
|
13
|
+
onPageChange,
|
|
14
|
+
onRowsPerPageChange,
|
|
15
|
+
}: PaginationProps) {
|
|
16
|
+
const handlePageChange = (
|
|
17
|
+
_event: React.MouseEvent<HTMLButtonElement> | null,
|
|
18
|
+
newPage: number,
|
|
19
|
+
) => {
|
|
20
|
+
onPageChange(newPage)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const handleRowsPerPageChange = (
|
|
24
|
+
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
|
25
|
+
) => {
|
|
26
|
+
onRowsPerPageChange(parseInt(event.target.value, 10))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<MuiTablePagination
|
|
31
|
+
component='div'
|
|
32
|
+
count={total}
|
|
33
|
+
page={page}
|
|
34
|
+
rowsPerPage={rowsPerPage}
|
|
35
|
+
rowsPerPageOptions={rowsPerPageOptions}
|
|
36
|
+
onPageChange={handlePageChange}
|
|
37
|
+
onRowsPerPageChange={handleRowsPerPageChange}
|
|
38
|
+
ActionsComponent={PaginationActions}
|
|
39
|
+
/>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { TableRow as MuiTableRow, TableCell, Checkbox } from '@mui/material'
|
|
2
|
+
import type { RowProps } from '../types'
|
|
3
|
+
import { Cell } from './cell'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Table row component with selection and hover support
|
|
7
|
+
*/
|
|
8
|
+
export function Row({
|
|
9
|
+
row,
|
|
10
|
+
columns,
|
|
11
|
+
isSelected = false,
|
|
12
|
+
selectable = false,
|
|
13
|
+
onClick,
|
|
14
|
+
onSelect,
|
|
15
|
+
onMouseEnter,
|
|
16
|
+
onMouseLeave,
|
|
17
|
+
}: RowProps) {
|
|
18
|
+
const handleClick = () => {
|
|
19
|
+
onClick?.(row)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const handleCheckboxClick = (event: React.MouseEvent) => {
|
|
23
|
+
event.stopPropagation()
|
|
24
|
+
onSelect?.(row)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const handleMouseEnter = () => {
|
|
28
|
+
onMouseEnter?.(row)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const handleMouseLeave = () => {
|
|
32
|
+
onMouseLeave?.()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<MuiTableRow
|
|
37
|
+
hover={!!onMouseEnter || !!onMouseLeave || !!onClick}
|
|
38
|
+
selected={isSelected}
|
|
39
|
+
onClick={handleClick}
|
|
40
|
+
onMouseEnter={handleMouseEnter}
|
|
41
|
+
onMouseLeave={handleMouseLeave}
|
|
42
|
+
sx={{
|
|
43
|
+
cursor: onClick ? 'pointer' : 'default',
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
{selectable && (
|
|
47
|
+
<TableCell padding='checkbox'>
|
|
48
|
+
<Checkbox
|
|
49
|
+
checked={isSelected}
|
|
50
|
+
onClick={handleCheckboxClick}
|
|
51
|
+
inputProps={{ 'aria-label': `select row ${row.id}` }}
|
|
52
|
+
/>
|
|
53
|
+
</TableCell>
|
|
54
|
+
)}
|
|
55
|
+
{columns.map((column) => (
|
|
56
|
+
<Cell key={column.id} column={column} value={row[column.id]} />
|
|
57
|
+
))}
|
|
58
|
+
</MuiTableRow>
|
|
59
|
+
)
|
|
60
|
+
}
|