| 1 | "use client"; |
| 2 | |
| 3 | import { useEffect } from "react"; |
| 4 | |
| 5 | import { Grid } from "./grid"; |
| 6 | import { Toolbar } from "./toolbar"; |
| 7 | import { |
| 8 | type ChartDataMode, |
| 9 | type ChartDataType, |
| 10 | type SeriesChartType, |
| 11 | } from "./types"; |
| 12 | import { useChartEditor } from "./use-chart-editor"; |
| 13 | |
| 14 | interface ChartDataEditorProps { |
| 15 | data: ChartDataType; |
| 16 | chartType: ChartDataMode; |
| 17 | onDataChange: (data: ChartDataType) => void; |
| 18 | isComposedChart?: boolean; |
| 19 | seriesChartTypes?: Record<string, SeriesChartType>; |
| 20 | onSeriesChartTypesChange?: (types: Record<string, SeriesChartType>) => void; |
| 21 | } |
| 22 | |
| 23 | export function ChartDataEditor({ |
| 24 | data: initialData, |
| 25 | chartType, |
| 26 | onDataChange, |
| 27 | isComposedChart = false, |
| 28 | seriesChartTypes: initialSeriesChartTypes, |
| 29 | onSeriesChartTypesChange, |
| 30 | }: ChartDataEditorProps) { |
| 31 | const { |
| 32 | data, |
| 33 | seriesNames, |
| 34 | seriesChartTypes, |
| 35 | focusedCell, |
| 36 | setFocusedCell, |
| 37 | updateCell, |
| 38 | addRow, |
| 39 | removeRow, |
| 40 | addSeries, |
| 41 | removeSeries, |
| 42 | renameSeries, |
| 43 | updateSeriesChartType, |
| 44 | addZColumn, |
| 45 | removeZColumn, |
| 46 | hasZColumn, |
| 47 | fields, |
| 48 | schema, |
| 49 | labelKey, |
| 50 | } = useChartEditor(initialData, chartType, initialSeriesChartTypes); |
| 51 | |
| 52 | // Auto-save on data change (non-blocking) |
| 53 | useEffect(() => { |
| 54 | onDataChange(data); |
| 55 | }, [data, onDataChange]); |
| 56 | |
| 57 | // Auto-save on series chart types change |
| 58 | useEffect(() => { |
| 59 | if (isComposedChart && onSeriesChartTypesChange) { |
| 60 | onSeriesChartTypesChange(seriesChartTypes); |
| 61 | } |
| 62 | }, [seriesChartTypes, isComposedChart, onSeriesChartTypesChange]); |
| 63 | |
| 64 | const handleFocusCell = (row: number, col: number) => { |
| 65 | setFocusedCell({ row, col }); |
| 66 | }; |
| 67 | |
| 68 | return ( |
| 69 | <div className="flex flex-col gap-3"> |
| 70 | <Toolbar |
| 71 | schema={schema} |
| 72 | rowCount={Array.isArray(data) ? data.length : 0} |
| 73 | seriesCount={seriesNames.length} |
| 74 | hasZColumn={hasZColumn} |
| 75 | onAddRow={addRow} |
| 76 | onAddSeries={addSeries} |
| 77 | onAddZColumn={addZColumn} |
| 78 | onRemoveZColumn={removeZColumn} |
| 79 | /> |
| 80 | |
| 81 | <Grid |
| 82 | data={data} |
| 83 | fields={fields} |
| 84 | schema={schema} |
| 85 | seriesNames={seriesNames} |
| 86 | focusedCell={focusedCell} |
| 87 | onUpdateCell={updateCell} |
| 88 | onRemoveRow={removeRow} |
| 89 | onRenameSeries={renameSeries} |
| 90 | onRemoveSeries={removeSeries} |
| 91 | onAddRow={addRow} |
| 92 | onFocusCell={handleFocusCell} |
| 93 | setFocusedCell={setFocusedCell} |
| 94 | seriesChartTypes={seriesChartTypes} |
| 95 | onSeriesChartTypeChange={updateSeriesChartType} |
| 96 | isComposedChart={isComposedChart} |
| 97 | labelKey={labelKey} |
| 98 | /> |
| 99 | |
| 100 | {Array.isArray(data) && data.length === 0 && ( |
| 101 | <div className="flex flex-col items-center justify-center py-12 text-muted-foreground"> |
| 102 | <p className="text-sm">No data points yet</p> |
| 103 | <p className="mt-1 text-xs"> |
| 104 | Click{" "} |
| 105 | <kbd className="rounded bg-muted px-1.5 py-0.5 font-mono text-[10px]"> |
| 106 | Add Row |
| 107 | </kbd>{" "} |
| 108 | or press{" "} |
| 109 | <kbd className="rounded bg-muted px-1.5 py-0.5 font-mono text-[10px]"> |
| 110 | Enter |
| 111 | </kbd>{" "} |
| 112 | to get started |
| 113 | </p> |
| 114 | </div> |
| 115 | )} |
| 116 | </div> |
| 117 | ); |
| 118 | } |
| 119 | |
| 120 | export * from "./types"; |
| 121 |