@sqlrooms/cells 0.29.0-rc.1 → 0.29.0-rc.2

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 (158) hide show
  1. package/dist/cellsSlice.d.ts.map +1 -1
  2. package/dist/cellsSlice.js +191 -73
  3. package/dist/cellsSlice.js.map +1 -1
  4. package/dist/components/AggregationSelector.d.ts +8 -0
  5. package/dist/components/AggregationSelector.d.ts.map +1 -0
  6. package/dist/components/AggregationSelector.js +13 -0
  7. package/dist/components/AggregationSelector.js.map +1 -0
  8. package/dist/components/CellSourceSelector.d.ts +11 -0
  9. package/dist/components/CellSourceSelector.d.ts.map +1 -0
  10. package/dist/components/CellSourceSelector.js +35 -0
  11. package/dist/components/CellSourceSelector.js.map +1 -0
  12. package/dist/components/ChartTypeSelector.d.ts +8 -0
  13. package/dist/components/ChartTypeSelector.d.ts.map +1 -0
  14. package/dist/components/ChartTypeSelector.js +13 -0
  15. package/dist/components/ChartTypeSelector.js.map +1 -0
  16. package/dist/components/CollapsibleSectionButton.d.ts +10 -0
  17. package/dist/components/CollapsibleSectionButton.d.ts.map +1 -0
  18. package/dist/components/CollapsibleSectionButton.js +10 -0
  19. package/dist/components/CollapsibleSectionButton.js.map +1 -0
  20. package/dist/components/ColorSelector.d.ts +24 -0
  21. package/dist/components/ColorSelector.d.ts.map +1 -0
  22. package/dist/components/ColorSelector.js +17 -0
  23. package/dist/components/ColorSelector.js.map +1 -0
  24. package/dist/components/FieldSelector.d.ts +11 -0
  25. package/dist/components/FieldSelector.d.ts.map +1 -0
  26. package/dist/components/FieldSelector.js +11 -0
  27. package/dist/components/FieldSelector.js.map +1 -0
  28. package/dist/components/FieldTypeIcon.d.ts +8 -0
  29. package/dist/components/FieldTypeIcon.d.ts.map +1 -0
  30. package/dist/components/FieldTypeIcon.js +27 -0
  31. package/dist/components/FieldTypeIcon.js.map +1 -0
  32. package/dist/components/InputCellContent.d.ts.map +1 -1
  33. package/dist/components/InputCellContent.js +3 -3
  34. package/dist/components/InputCellContent.js.map +1 -1
  35. package/dist/components/SelectionListener.d.ts +15 -0
  36. package/dist/components/SelectionListener.d.ts.map +1 -0
  37. package/dist/components/SelectionListener.js +74 -0
  38. package/dist/components/SelectionListener.js.map +1 -0
  39. package/dist/components/SheetsTabBar.d.ts.map +1 -1
  40. package/dist/components/SheetsTabBar.js +2 -1
  41. package/dist/components/SheetsTabBar.js.map +1 -1
  42. package/dist/components/SqlCellBody.d.ts +6 -0
  43. package/dist/components/SqlCellBody.d.ts.map +1 -0
  44. package/dist/components/SqlCellBody.js +12 -0
  45. package/dist/components/SqlCellBody.js.map +1 -0
  46. package/dist/components/SqlCellConnectionSelector.d.ts +9 -0
  47. package/dist/components/SqlCellConnectionSelector.d.ts.map +1 -0
  48. package/dist/components/SqlCellConnectionSelector.js +21 -0
  49. package/dist/components/SqlCellConnectionSelector.js.map +1 -0
  50. package/dist/components/SqlCellContent.d.ts.map +1 -1
  51. package/dist/components/SqlCellContent.js +29 -154
  52. package/dist/components/SqlCellContent.js.map +1 -1
  53. package/dist/components/SqlCellDependentsMenu.d.ts +11 -0
  54. package/dist/components/SqlCellDependentsMenu.d.ts.map +1 -0
  55. package/dist/components/SqlCellDependentsMenu.js +62 -0
  56. package/dist/components/SqlCellDependentsMenu.js.map +1 -0
  57. package/dist/components/SqlCellEditor.d.ts +14 -0
  58. package/dist/components/SqlCellEditor.d.ts.map +1 -0
  59. package/dist/components/SqlCellEditor.js +17 -0
  60. package/dist/components/SqlCellEditor.js.map +1 -0
  61. package/dist/components/SqlCellResultNameEditor.d.ts +8 -0
  62. package/dist/components/SqlCellResultNameEditor.d.ts.map +1 -0
  63. package/dist/components/SqlCellResultNameEditor.js +38 -0
  64. package/dist/components/SqlCellResultNameEditor.js.map +1 -0
  65. package/dist/components/SqlCellResults.d.ts +17 -0
  66. package/dist/components/SqlCellResults.d.ts.map +1 -0
  67. package/dist/components/SqlCellResults.js +45 -0
  68. package/dist/components/SqlCellResults.js.map +1 -0
  69. package/dist/components/SqlCellRunButton.d.ts +5 -6
  70. package/dist/components/SqlCellRunButton.d.ts.map +1 -1
  71. package/dist/components/SqlCellRunButton.js +11 -7
  72. package/dist/components/SqlCellRunButton.js.map +1 -1
  73. package/dist/components/SqlQueryPreview.d.ts +9 -0
  74. package/dist/components/SqlQueryPreview.d.ts.map +1 -0
  75. package/dist/components/SqlQueryPreview.js +14 -0
  76. package/dist/components/SqlQueryPreview.js.map +1 -0
  77. package/dist/components/TextCellContent.d.ts.map +1 -1
  78. package/dist/components/TextCellContent.js +24 -7
  79. package/dist/components/TextCellContent.js.map +1 -1
  80. package/dist/components/VegaCellContent.d.ts +0 -1
  81. package/dist/components/VegaCellContent.d.ts.map +1 -1
  82. package/dist/components/VegaCellContent.js +61 -47
  83. package/dist/components/VegaCellContent.js.map +1 -1
  84. package/dist/components/VegaCellHeader.d.ts +11 -0
  85. package/dist/components/VegaCellHeader.d.ts.map +1 -0
  86. package/dist/components/VegaCellHeader.js +31 -0
  87. package/dist/components/VegaCellHeader.js.map +1 -0
  88. package/dist/components/VegaConfigPanel.d.ts +5 -0
  89. package/dist/components/VegaConfigPanel.d.ts.map +1 -1
  90. package/dist/components/VegaConfigPanel.js +53 -63
  91. package/dist/components/VegaConfigPanel.js.map +1 -1
  92. package/dist/crdt.d.ts +61 -0
  93. package/dist/crdt.d.ts.map +1 -0
  94. package/dist/crdt.js +82 -0
  95. package/dist/crdt.js.map +1 -0
  96. package/dist/dagSlice.d.ts +3 -0
  97. package/dist/dagSlice.d.ts.map +1 -0
  98. package/dist/dagSlice.js +51 -0
  99. package/dist/dagSlice.js.map +1 -0
  100. package/dist/dagUtils.d.ts +2 -1
  101. package/dist/dagUtils.d.ts.map +1 -1
  102. package/dist/dagUtils.js +35 -11
  103. package/dist/dagUtils.js.map +1 -1
  104. package/dist/defaultCellRegistry.d.ts.map +1 -1
  105. package/dist/defaultCellRegistry.js +63 -24
  106. package/dist/defaultCellRegistry.js.map +1 -1
  107. package/dist/execution.d.ts.map +1 -1
  108. package/dist/execution.js +2 -1
  109. package/dist/execution.js.map +1 -1
  110. package/dist/helpers.d.ts +14 -2
  111. package/dist/helpers.d.ts.map +1 -1
  112. package/dist/helpers.js +56 -2
  113. package/dist/helpers.js.map +1 -1
  114. package/dist/hooks/useVegaCellDataSource.d.ts +18 -0
  115. package/dist/hooks/useVegaCellDataSource.d.ts.map +1 -0
  116. package/dist/hooks/useVegaCellDataSource.js +40 -0
  117. package/dist/hooks/useVegaCellDataSource.js.map +1 -0
  118. package/dist/hooks/useVegaCellQuery.d.ts +16 -0
  119. package/dist/hooks/useVegaCellQuery.d.ts.map +1 -0
  120. package/dist/hooks/useVegaCellQuery.js +25 -0
  121. package/dist/hooks/useVegaCellQuery.js.map +1 -0
  122. package/dist/index.d.ts +3 -2
  123. package/dist/index.d.ts.map +1 -1
  124. package/dist/index.js +3 -2
  125. package/dist/index.js.map +1 -1
  126. package/dist/sqlHelpers.d.ts +3 -3
  127. package/dist/sqlHelpers.d.ts.map +1 -1
  128. package/dist/sqlHelpers.js +9 -5
  129. package/dist/sqlHelpers.js.map +1 -1
  130. package/dist/typeGuards.d.ts +2 -0
  131. package/dist/typeGuards.d.ts.map +1 -0
  132. package/dist/typeGuards.js +4 -0
  133. package/dist/typeGuards.js.map +1 -0
  134. package/dist/types.d.ts +97 -26
  135. package/dist/types.d.ts.map +1 -1
  136. package/dist/types.js +17 -2
  137. package/dist/types.js.map +1 -1
  138. package/dist/useValidateResultName.d.ts +34 -0
  139. package/dist/useValidateResultName.d.ts.map +1 -0
  140. package/dist/useValidateResultName.js +111 -0
  141. package/dist/useValidateResultName.js.map +1 -0
  142. package/dist/utils.d.ts +8 -0
  143. package/dist/utils.d.ts.map +1 -1
  144. package/dist/utils.js +16 -0
  145. package/dist/utils.js.map +1 -1
  146. package/dist/validation.d.ts +16 -0
  147. package/dist/validation.d.ts.map +1 -0
  148. package/dist/validation.js +40 -0
  149. package/dist/validation.js.map +1 -0
  150. package/dist/vegaSelectionUtils.d.ts +14 -0
  151. package/dist/vegaSelectionUtils.d.ts.map +1 -0
  152. package/dist/vegaSelectionUtils.js +97 -0
  153. package/dist/vegaSelectionUtils.js.map +1 -0
  154. package/dist/vegaSpecBuilder.d.ts +34 -0
  155. package/dist/vegaSpecBuilder.d.ts.map +1 -0
  156. package/dist/vegaSpecBuilder.js +90 -0
  157. package/dist/vegaSpecBuilder.js.map +1 -0
  158. package/package.json +12 -13
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import type { CellContainerProps, VegaCell } from '../types';
3
2
  export type VegaCellContentProps = {
4
3
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"VegaCellContent.d.ts","sourceRoot":"","sources":["../../src/components/VegaCellContent.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAE5D,OAAO,KAAK,EAAO,kBAAkB,EAAE,QAAQ,EAAC,MAAM,UAAU,CAAC;AAGjE,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;IACf,eAAe,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,KAAK,CAAC,YAAY,CAAC;CACpE,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAyI1D,CAAC"}
1
+ {"version":3,"file":"VegaCellContent.d.ts","sourceRoot":"","sources":["../../src/components/VegaCellContent.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,kBAAkB,EAAE,QAAQ,EAAC,MAAM,UAAU,CAAC;AAa3D,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,CAAC;IACf,eAAe,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,KAAK,CAAC,YAAY,CAAC;CACpE,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA6J1D,CAAC"}
@@ -1,63 +1,77 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@sqlrooms/ui';
2
+ import { useDebouncedValue } from '@sqlrooms/ui';
3
3
  import { VegaLiteChart } from '@sqlrooms/vega';
4
4
  import { produce } from 'immer';
5
- import { useCallback, useMemo, useState } from 'react';
5
+ import { useCallback, useState } from 'react';
6
6
  import { useCellsStore } from '../hooks';
7
+ import { useVegaCellQuery } from '../hooks/useVegaCellQuery';
8
+ import { useVegaCellDataSource } from '../hooks/useVegaCellDataSource';
9
+ import { SelectionListener } from './SelectionListener';
7
10
  import { VegaConfigPanel } from './VegaConfigPanel';
11
+ import { VegaCellHeader } from './VegaCellHeader';
12
+ import { toDataSourceCell, toDataSourceTable } from '../helpers';
13
+ const DEFAULT_ASPECT_RATIO = 3 / 2;
14
+ const DEFAULT_SPEC = {
15
+ data: { name: 'queryResult' },
16
+ mark: 'bar',
17
+ padding: 20,
18
+ };
8
19
  export const VegaCellContent = ({ id, cell, renderContainer, }) => {
9
20
  const updateCell = useCellsStore((s) => s.cells.updateCell);
10
- const cellsData = useCellsStore((s) => s.cells.config.data);
11
- const cellsStatus = useCellsStore((s) => s.cells.status);
12
- // Find available SQL cells in the same sheet
13
- const currentSheetId = useCellsStore((s) => s.cells.config.currentSheetId);
14
- const sheetCellIds = useCellsStore((s) => currentSheetId ? s.cells.config.sheets[currentSheetId]?.cellIds : undefined);
15
- const availableSqlCells = useMemo(() => {
16
- if (!sheetCellIds)
17
- return [];
18
- return sheetCellIds
19
- .map((cid) => cellsData[cid])
20
- .filter((c) => c?.type === 'sql');
21
- }, [sheetCellIds, cellsData]);
22
- const selectedSqlId = cell.data.sqlId;
23
- const selectedSqlStatus = selectedSqlId
24
- ? cellsStatus[selectedSqlId]
25
- : undefined;
26
- const [draftSpec, setDraftSpec] = useState(cell.data.vegaSpec ?? {
27
- data: { name: 'queryResult' },
28
- mark: 'bar',
29
- padding: 20,
30
- });
21
+ const crossFilterEnabled = cell.data.crossFilter?.enabled !== false;
22
+ const brushField = cell.data.crossFilter?.brushField;
23
+ const brushFieldType = cell.data.crossFilter?.brushFieldType;
31
24
  const [isEditing, setIsEditing] = useState(false);
32
- const selectedSqlQuery = selectedSqlStatus?.type === 'sql' && selectedSqlStatus.resultView
33
- ? `SELECT * FROM ${selectedSqlStatus.resultView}`
34
- : selectedSqlId && cellsData[selectedSqlId]?.type === 'sql'
35
- ? cellsData[selectedSqlId].data.sql
36
- : '';
37
- const lastRunTime = selectedSqlStatus?.type === 'sql'
38
- ? selectedSqlStatus.lastRunTime
39
- : undefined;
40
- const handleSqlIdChange = (value) => {
25
+ // Local state with debounced persistence
26
+ const [localSpec, handleSpecChange] = useDebouncedValue(cell.data.vegaSpec ?? DEFAULT_SPEC, (spec) => {
41
27
  updateCell(id, (c) => produce(c, (draft) => {
42
28
  if (draft.type === 'vega')
43
- draft.data.sqlId = value;
29
+ draft.data.vegaSpec = spec;
44
30
  }));
45
- };
46
- const saveSpec = useCallback(() => {
31
+ }, 300);
32
+ const { selectedSqlId, selectedTableRef, selectedSqlStatus, baseSqlQuery, lastRunTime, hasDataSource, } = useVegaCellDataSource(cell);
33
+ const { selectedSqlQuery, crossFilterPredicate } = useVegaCellQuery({
34
+ cellId: id,
35
+ baseSqlQuery,
36
+ selectedSqlId,
37
+ crossFilterEnabled,
38
+ });
39
+ // Encode selection value: "cell:<id>" or "table:<schema.table>"
40
+ const selectValue = selectedSqlId
41
+ ? toDataSourceCell(selectedSqlId)
42
+ : selectedTableRef
43
+ ? toDataSourceTable(selectedTableRef)
44
+ : undefined;
45
+ const handleCrossFilterToggle = useCallback((enabled) => {
47
46
  updateCell(id, (c) => produce(c, (draft) => {
48
- if (draft.type === 'vega')
49
- draft.data.vegaSpec = draftSpec;
47
+ if (draft.type === 'vega') {
48
+ if (!draft.data.crossFilter)
49
+ draft.data.crossFilter = {};
50
+ draft.data.crossFilter.enabled = enabled;
51
+ }
52
+ }));
53
+ }, [id, updateCell]);
54
+ const handleBrushFieldChange = useCallback((field) => {
55
+ updateCell(id, (c) => produce(c, (draft) => {
56
+ if (draft.type === 'vega') {
57
+ if (!draft.data.crossFilter)
58
+ draft.data.crossFilter = {};
59
+ draft.data.crossFilter.brushField = field;
60
+ }
61
+ }));
62
+ }, [id, updateCell]);
63
+ const handleBrushFieldTypeChange = useCallback((fieldType) => {
64
+ updateCell(id, (c) => produce(c, (draft) => {
65
+ if (draft.type === 'vega') {
66
+ if (!draft.data.crossFilter)
67
+ draft.data.crossFilter = {};
68
+ draft.data.crossFilter.brushFieldType = fieldType;
69
+ }
50
70
  }));
51
- setIsEditing(false);
52
- }, [id, draftSpec, updateCell]);
53
- const header = (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Select, { value: selectedSqlId, onValueChange: handleSqlIdChange, children: [_jsx(SelectTrigger, { className: "h-6 w-[150px] text-xs shadow-none", children: _jsx(SelectValue, { placeholder: "Select data source" }) }), _jsx(SelectContent, { onCloseAutoFocus: (e) => e.preventDefault(), children: availableSqlCells.map((sql) => (_jsx(SelectItem, { className: "text-xs", value: sql.id, children: sql.data.title }, sql.id))) })] }), _jsx(Button, { size: "xs", variant: "secondary", className: "h-6", onClick: () => (isEditing ? saveSpec() : setIsEditing(true)), disabled: !selectedSqlId, children: isEditing ? 'Save' : 'Edit chart' }), _jsx("span", { className: "text-[10px] font-bold uppercase text-gray-400", children: "Vega" })] }));
54
- const content = (_jsxs("div", { className: "flex h-[400px]", children: [isEditing && (_jsx(VegaConfigPanel, { sqlQuery: selectedSqlQuery, lastRunTime: lastRunTime, spec: draftSpec, onSpecChange: setDraftSpec })), _jsx("div", { onDoubleClick: () => selectedSqlId && setIsEditing((prev) => !prev), className: "flex h-full w-full p-2", children: !selectedSqlId ? (_jsx("div", { className: "flex h-full w-full items-center justify-center text-gray-400", children: "Please select a SQL data source first." })) : !selectedSqlStatus ? (_jsx("div", { className: "flex h-full w-full items-center justify-center text-gray-400", children: "No chart data available, please run the SQL cell first." })) : (_jsx(VegaLiteChart, { sqlQuery: selectedSqlQuery,
55
- // isLoading={
56
- // selectedSqlStatus?.type === 'sql' &&
57
- // selectedSqlStatus.status === 'running'
58
- // }
59
- // lastRunTime={lastRunTime}
60
- spec: draftSpec, className: "h-full w-full", aspectRatio: isEditing ? undefined : 3 / 2 })) })] }));
71
+ }, [id, updateCell]);
72
+ const header = (_jsx(VegaCellHeader, { cellId: id, selectValue: selectValue, onEditToggle: () => setIsEditing((prev) => !prev), hasDataSource: hasDataSource, crossFilterEnabled: crossFilterEnabled, brushField: brushField, crossFilterPredicate: crossFilterPredicate }));
73
+ const showSelectionListener = crossFilterEnabled && brushField && selectedSqlId && selectedSqlStatus;
74
+ const content = (_jsxs("div", { className: "flex h-[400px]", children: [isEditing && (_jsx(VegaConfigPanel, { sqlQuery: baseSqlQuery, lastRunTime: lastRunTime, spec: localSpec, crossFilterEnabled: crossFilterEnabled, onSpecChange: handleSpecChange, onCrossFilterToggle: handleCrossFilterToggle, onBrushFieldChange: handleBrushFieldChange, onBrushFieldTypeChange: handleBrushFieldTypeChange })), _jsx("div", { onDoubleClick: () => hasDataSource && setIsEditing((prev) => !prev), className: "flex h-full w-full p-2", children: !hasDataSource ? (_jsx("div", { className: "flex h-full w-full items-center justify-center text-gray-400", children: "Please select a data source first." })) : !selectedSqlStatus && !selectedTableRef ? (_jsx("div", { className: "flex h-full w-full items-center justify-center text-gray-400", children: "No chart data available, please run the SQL cell first." })) : (_jsx(VegaLiteChart, { sqlQuery: selectedSqlQuery, spec: localSpec, className: "h-full w-full", aspectRatio: isEditing ? undefined : DEFAULT_ASPECT_RATIO, children: showSelectionListener && (_jsx(SelectionListener, { cellId: id, sqlId: selectedSqlId, brushField: brushField, brushFieldType: brushFieldType })) })) })] }));
61
75
  return renderContainer({
62
76
  header,
63
77
  content,
@@ -1 +1 @@
1
- {"version":3,"file":"VegaCellContent.js","sourceRoot":"","sources":["../../src/components/VegaCellContent.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Button,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@sqlrooms/ui';\nimport {VegaLiteChart} from '@sqlrooms/vega';\nimport {produce} from 'immer';\nimport React, {useCallback, useMemo, useState} from 'react';\nimport {useCellsStore} from '../hooks';\nimport type {Cell, CellContainerProps, VegaCell} from '../types';\nimport {VegaConfigPanel} from './VegaConfigPanel';\n\nexport type VegaCellContentProps = {\n id: string;\n cell: VegaCell;\n renderContainer: (props: CellContainerProps) => React.ReactElement;\n};\n\nexport const VegaCellContent: React.FC<VegaCellContentProps> = ({\n id,\n cell,\n renderContainer,\n}) => {\n const updateCell = useCellsStore((s) => s.cells.updateCell);\n const cellsData = useCellsStore((s) => s.cells.config.data);\n const cellsStatus = useCellsStore((s) => s.cells.status);\n\n // Find available SQL cells in the same sheet\n const currentSheetId = useCellsStore((s) => s.cells.config.currentSheetId);\n const sheetCellIds = useCellsStore((s) =>\n currentSheetId ? s.cells.config.sheets[currentSheetId]?.cellIds : undefined,\n );\n\n const availableSqlCells = useMemo(() => {\n if (!sheetCellIds) return [];\n return sheetCellIds\n .map((cid) => cellsData[cid])\n .filter((c): c is Cell & {type: 'sql'} => c?.type === 'sql');\n }, [sheetCellIds, cellsData]);\n\n const selectedSqlId = cell.data.sqlId;\n const selectedSqlStatus = selectedSqlId\n ? cellsStatus[selectedSqlId]\n : undefined;\n\n const [draftSpec, setDraftSpec] = useState(\n cell.data.vegaSpec ?? {\n data: {name: 'queryResult'},\n mark: 'bar',\n padding: 20,\n },\n );\n const [isEditing, setIsEditing] = useState(false);\n\n const selectedSqlQuery =\n selectedSqlStatus?.type === 'sql' && selectedSqlStatus.resultView\n ? `SELECT * FROM ${selectedSqlStatus.resultView}`\n : selectedSqlId && cellsData[selectedSqlId]?.type === 'sql'\n ? (cellsData[selectedSqlId] as any).data.sql\n : '';\n\n const lastRunTime =\n selectedSqlStatus?.type === 'sql'\n ? selectedSqlStatus.lastRunTime\n : undefined;\n\n const handleSqlIdChange = (value: string) => {\n updateCell(id, (c) =>\n produce(c, (draft) => {\n if (draft.type === 'vega') draft.data.sqlId = value;\n }),\n );\n };\n\n const saveSpec = useCallback(() => {\n updateCell(id, (c) =>\n produce(c, (draft) => {\n if (draft.type === 'vega') draft.data.vegaSpec = draftSpec;\n }),\n );\n setIsEditing(false);\n }, [id, draftSpec, updateCell]);\n\n const header = (\n <div className=\"flex items-center gap-2\">\n <Select value={selectedSqlId} onValueChange={handleSqlIdChange}>\n <SelectTrigger className=\"h-6 w-[150px] text-xs shadow-none\">\n <SelectValue placeholder=\"Select data source\" />\n </SelectTrigger>\n <SelectContent onCloseAutoFocus={(e) => e.preventDefault()}>\n {availableSqlCells.map((sql) => (\n <SelectItem className=\"text-xs\" key={sql.id} value={sql.id}>\n {sql.data.title}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Button\n size=\"xs\"\n variant=\"secondary\"\n className=\"h-6\"\n onClick={() => (isEditing ? saveSpec() : setIsEditing(true))}\n disabled={!selectedSqlId}\n >\n {isEditing ? 'Save' : 'Edit chart'}\n </Button>\n <span className=\"text-[10px] font-bold uppercase text-gray-400\">\n Vega\n </span>\n </div>\n );\n\n const content = (\n <div className=\"flex h-[400px]\">\n {isEditing && (\n <VegaConfigPanel\n sqlQuery={selectedSqlQuery}\n lastRunTime={lastRunTime}\n spec={draftSpec}\n onSpecChange={setDraftSpec}\n />\n )}\n <div\n onDoubleClick={() => selectedSqlId && setIsEditing((prev) => !prev)}\n className=\"flex h-full w-full p-2\"\n >\n {!selectedSqlId ? (\n <div className=\"flex h-full w-full items-center justify-center text-gray-400\">\n Please select a SQL data source first.\n </div>\n ) : !selectedSqlStatus ? (\n <div className=\"flex h-full w-full items-center justify-center text-gray-400\">\n No chart data available, please run the SQL cell first.\n </div>\n ) : (\n <VegaLiteChart\n sqlQuery={selectedSqlQuery}\n // isLoading={\n // selectedSqlStatus?.type === 'sql' &&\n // selectedSqlStatus.status === 'running'\n // }\n // lastRunTime={lastRunTime}\n spec={draftSpec}\n className=\"h-full w-full\"\n aspectRatio={isEditing ? undefined : 3 / 2}\n />\n )}\n </div>\n </div>\n );\n\n return renderContainer({\n header,\n content,\n });\n};\n"]}
1
+ {"version":3,"file":"VegaCellContent.js","sourceRoot":"","sources":["../../src/components/VegaCellContent.tsx"],"names":[],"mappings":"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","sourcesContent":["import {useDebouncedValue} from '@sqlrooms/ui';\nimport {VegaLiteChart} from '@sqlrooms/vega';\nimport {produce} from 'immer';\nimport {useCallback, useState} from 'react';\nimport {useCellsStore} from '../hooks';\nimport {useVegaCellQuery} from '../hooks/useVegaCellQuery';\nimport {useVegaCellDataSource} from '../hooks/useVegaCellDataSource';\nimport type {CellContainerProps, VegaCell} from '../types';\nimport {SelectionListener} from './SelectionListener';\nimport {VegaConfigPanel} from './VegaConfigPanel';\nimport {VegaCellHeader} from './VegaCellHeader';\nimport {toDataSourceCell, toDataSourceTable} from '../helpers';\n\nconst DEFAULT_ASPECT_RATIO = 3 / 2;\nconst DEFAULT_SPEC = {\n data: {name: 'queryResult'},\n mark: 'bar',\n padding: 20,\n} as const;\n\nexport type VegaCellContentProps = {\n id: string;\n cell: VegaCell;\n renderContainer: (props: CellContainerProps) => React.ReactElement;\n};\n\nexport const VegaCellContent: React.FC<VegaCellContentProps> = ({\n id,\n cell,\n renderContainer,\n}) => {\n const updateCell = useCellsStore((s) => s.cells.updateCell);\n\n const crossFilterEnabled = cell.data.crossFilter?.enabled !== false;\n const brushField = cell.data.crossFilter?.brushField;\n const brushFieldType = cell.data.crossFilter?.brushFieldType;\n\n const [isEditing, setIsEditing] = useState(false);\n\n // Local state with debounced persistence\n const [localSpec, handleSpecChange] = useDebouncedValue(\n cell.data.vegaSpec ?? DEFAULT_SPEC,\n (spec) => {\n updateCell(id, (c) =>\n produce(c, (draft) => {\n if (draft.type === 'vega') draft.data.vegaSpec = spec;\n }),\n );\n },\n 300,\n );\n\n const {\n selectedSqlId,\n selectedTableRef,\n selectedSqlStatus,\n baseSqlQuery,\n lastRunTime,\n hasDataSource,\n } = useVegaCellDataSource(cell);\n\n const {selectedSqlQuery, crossFilterPredicate} = useVegaCellQuery({\n cellId: id,\n baseSqlQuery,\n selectedSqlId,\n crossFilterEnabled,\n });\n\n // Encode selection value: \"cell:<id>\" or \"table:<schema.table>\"\n const selectValue = selectedSqlId\n ? toDataSourceCell(selectedSqlId)\n : selectedTableRef\n ? toDataSourceTable(selectedTableRef)\n : undefined;\n\n const handleCrossFilterToggle = useCallback(\n (enabled: boolean) => {\n updateCell(id, (c) =>\n produce(c, (draft) => {\n if (draft.type === 'vega') {\n if (!draft.data.crossFilter) draft.data.crossFilter = {};\n draft.data.crossFilter.enabled = enabled;\n }\n }),\n );\n },\n [id, updateCell],\n );\n\n const handleBrushFieldChange = useCallback(\n (field: string | undefined) => {\n updateCell(id, (c) =>\n produce(c, (draft) => {\n if (draft.type === 'vega') {\n if (!draft.data.crossFilter) draft.data.crossFilter = {};\n draft.data.crossFilter.brushField = field;\n }\n }),\n );\n },\n [id, updateCell],\n );\n\n const handleBrushFieldTypeChange = useCallback(\n (fieldType: string | undefined) => {\n updateCell(id, (c) =>\n produce(c, (draft) => {\n if (draft.type === 'vega') {\n if (!draft.data.crossFilter) draft.data.crossFilter = {};\n draft.data.crossFilter.brushFieldType = fieldType;\n }\n }),\n );\n },\n [id, updateCell],\n );\n\n const header = (\n <VegaCellHeader\n cellId={id}\n selectValue={selectValue}\n onEditToggle={() => setIsEditing((prev) => !prev)}\n hasDataSource={hasDataSource}\n crossFilterEnabled={crossFilterEnabled}\n brushField={brushField}\n crossFilterPredicate={crossFilterPredicate}\n />\n );\n\n const showSelectionListener =\n crossFilterEnabled && brushField && selectedSqlId && selectedSqlStatus;\n\n const content = (\n <div className=\"flex h-[400px]\">\n {isEditing && (\n <VegaConfigPanel\n sqlQuery={baseSqlQuery}\n lastRunTime={lastRunTime}\n spec={localSpec}\n crossFilterEnabled={crossFilterEnabled}\n onSpecChange={handleSpecChange}\n onCrossFilterToggle={handleCrossFilterToggle}\n onBrushFieldChange={handleBrushFieldChange}\n onBrushFieldTypeChange={handleBrushFieldTypeChange}\n />\n )}\n <div\n onDoubleClick={() => hasDataSource && setIsEditing((prev) => !prev)}\n className=\"flex h-full w-full p-2\"\n >\n {!hasDataSource ? (\n <div className=\"flex h-full w-full items-center justify-center text-gray-400\">\n Please select a data source first.\n </div>\n ) : !selectedSqlStatus && !selectedTableRef ? (\n <div className=\"flex h-full w-full items-center justify-center text-gray-400\">\n No chart data available, please run the SQL cell first.\n </div>\n ) : (\n <VegaLiteChart\n sqlQuery={selectedSqlQuery}\n spec={localSpec}\n className=\"h-full w-full\"\n aspectRatio={isEditing ? undefined : DEFAULT_ASPECT_RATIO}\n >\n {showSelectionListener && (\n <SelectionListener\n cellId={id}\n sqlId={selectedSqlId}\n brushField={brushField}\n brushFieldType={brushFieldType}\n />\n )}\n </VegaLiteChart>\n )}\n </div>\n </div>\n );\n\n return renderContainer({\n header,\n content,\n });\n};\n"]}
@@ -0,0 +1,11 @@
1
+ export type VegaCellHeaderProps = {
2
+ cellId: string;
3
+ selectValue: string | undefined;
4
+ onEditToggle: () => void;
5
+ hasDataSource: boolean;
6
+ crossFilterEnabled: boolean;
7
+ brushField: string | undefined;
8
+ crossFilterPredicate: string | null;
9
+ };
10
+ export declare const VegaCellHeader: React.FC<VegaCellHeaderProps>;
11
+ //# sourceMappingURL=VegaCellHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VegaCellHeader.d.ts","sourceRoot":"","sources":["../../src/components/VegaCellHeader.tsx"],"names":[],"mappings":"AAOA,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAmExD,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button, Tooltip, TooltipContent, TooltipTrigger } from '@sqlrooms/ui';
3
+ import { produce } from 'immer';
4
+ import { Filter, Settings } from 'lucide-react';
5
+ import { fromDataSourceCell, fromDataSourceTable } from '../helpers';
6
+ import { CellSourceSelector } from './CellSourceSelector';
7
+ import { useCellsStore } from '../hooks';
8
+ export const VegaCellHeader = ({ cellId, selectValue, onEditToggle, hasDataSource, crossFilterEnabled, brushField, crossFilterPredicate, }) => {
9
+ const updateCell = useCellsStore((s) => s.cells.updateCell);
10
+ const handleDataSourceChange = (value) => {
11
+ updateCell(cellId, (c) => produce(c, (draft) => {
12
+ if (draft.type !== 'vega') {
13
+ return;
14
+ }
15
+ const cellIdFromValue = fromDataSourceCell(value);
16
+ const tableRef = fromDataSourceTable(value);
17
+ if (cellIdFromValue) {
18
+ draft.data.sqlId = cellIdFromValue;
19
+ delete draft.data.tableRef;
20
+ }
21
+ else if (tableRef) {
22
+ draft.data.tableRef = tableRef;
23
+ delete draft.data.sqlId;
24
+ }
25
+ }));
26
+ };
27
+ return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CellSourceSelector, { value: selectValue, onValueChange: handleDataSourceChange }), _jsx(Button, { size: "xs", variant: "secondary", className: "h-6", onClick: onEditToggle, disabled: !hasDataSource, children: _jsx(Settings, { className: "h-4 w-4" }) }), crossFilterEnabled && brushField && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { className: "flex items-center", children: _jsx(Filter, { className: `h-3.5 w-3.5 ${crossFilterPredicate ? 'text-blue-500' : 'text-gray-400'}` }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { className: "text-xs", children: crossFilterPredicate
28
+ ? `Filtered: ${crossFilterPredicate}`
29
+ : `Cross-filter on ${brushField}` }) })] }))] }));
30
+ };
31
+ //# sourceMappingURL=VegaCellHeader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VegaCellHeader.js","sourceRoot":"","sources":["../../src/components/VegaCellHeader.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAC,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAC,OAAO,EAAC,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAC,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAC,kBAAkB,EAAE,mBAAmB,EAAC,MAAM,YAAY,CAAC;AACnE,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAC;AAYvC,MAAM,CAAC,MAAM,cAAc,GAAkC,CAAC,EAC5D,MAAM,EACN,WAAW,EACX,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,UAAU,EACV,oBAAoB,GACrB,EAAE,EAAE;IACH,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAE5D,MAAM,sBAAsB,GAAG,CAAC,KAAa,EAAE,EAAE;QAC/C,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CACvB,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;YACnB,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC1B,OAAO;YACT,CAAC;YAED,MAAM,eAAe,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;YAClD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;YAE5C,IAAI,eAAe,EAAE,CAAC;gBACpB,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,eAAe,CAAC;gBACnC,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC7B,CAAC;iBAAM,IAAI,QAAQ,EAAE,CAAC;gBACpB,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;gBAC/B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;YAC1B,CAAC;QACH,CAAC,CAAC,CACH,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAC,yBAAyB,aACtC,KAAC,kBAAkB,IACjB,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,sBAAsB,GACrC,EACF,KAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,WAAW,EACnB,SAAS,EAAC,KAAK,EACf,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,CAAC,aAAa,YAExB,KAAC,QAAQ,IAAC,SAAS,EAAC,SAAS,GAAG,GACzB,EACR,kBAAkB,IAAI,UAAU,IAAI,CACnC,MAAC,OAAO,eACN,KAAC,cAAc,IAAC,OAAO,kBACrB,cAAK,SAAS,EAAC,mBAAmB,YAChC,KAAC,MAAM,IACL,SAAS,EAAE,eAAe,oBAAoB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe,EAAE,GACpF,GACE,GACS,EACjB,KAAC,cAAc,IAAC,IAAI,EAAC,QAAQ,YAC3B,YAAG,SAAS,EAAC,SAAS,YACnB,oBAAoB;gCACnB,CAAC,CAAC,aAAa,oBAAoB,EAAE;gCACrC,CAAC,CAAC,mBAAmB,UAAU,EAAE,GACjC,GACW,IACT,CACX,IACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {Button, Tooltip, TooltipContent, TooltipTrigger} from '@sqlrooms/ui';\nimport {produce} from 'immer';\nimport {Filter, Settings} from 'lucide-react';\nimport {fromDataSourceCell, fromDataSourceTable} from '../helpers';\nimport {CellSourceSelector} from './CellSourceSelector';\nimport {useCellsStore} from '../hooks';\n\nexport type VegaCellHeaderProps = {\n cellId: string;\n selectValue: string | undefined;\n onEditToggle: () => void;\n hasDataSource: boolean;\n crossFilterEnabled: boolean;\n brushField: string | undefined;\n crossFilterPredicate: string | null;\n};\n\nexport const VegaCellHeader: React.FC<VegaCellHeaderProps> = ({\n cellId,\n selectValue,\n onEditToggle,\n hasDataSource,\n crossFilterEnabled,\n brushField,\n crossFilterPredicate,\n}) => {\n const updateCell = useCellsStore((s) => s.cells.updateCell);\n\n const handleDataSourceChange = (value: string) => {\n updateCell(cellId, (c) =>\n produce(c, (draft) => {\n if (draft.type !== 'vega') {\n return;\n }\n\n const cellIdFromValue = fromDataSourceCell(value);\n const tableRef = fromDataSourceTable(value);\n\n if (cellIdFromValue) {\n draft.data.sqlId = cellIdFromValue;\n delete draft.data.tableRef;\n } else if (tableRef) {\n draft.data.tableRef = tableRef;\n delete draft.data.sqlId;\n }\n }),\n );\n };\n\n return (\n <div className=\"flex items-center gap-2\">\n <CellSourceSelector\n value={selectValue}\n onValueChange={handleDataSourceChange}\n />\n <Button\n size=\"xs\"\n variant=\"secondary\"\n className=\"h-6\"\n onClick={onEditToggle}\n disabled={!hasDataSource}\n >\n <Settings className=\"h-4 w-4\" />\n </Button>\n {crossFilterEnabled && brushField && (\n <Tooltip>\n <TooltipTrigger asChild>\n <div className=\"flex items-center\">\n <Filter\n className={`h-3.5 w-3.5 ${crossFilterPredicate ? 'text-blue-500' : 'text-gray-400'}`}\n />\n </div>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\">\n <p className=\"text-xs\">\n {crossFilterPredicate\n ? `Filtered: ${crossFilterPredicate}`\n : `Cross-filter on ${brushField}`}\n </p>\n </TooltipContent>\n </Tooltip>\n )}\n </div>\n );\n};\n"]}
@@ -1,8 +1,13 @@
1
1
  import React from 'react';
2
+ import type { BrushFieldType } from '../types';
2
3
  export declare const VegaConfigPanel: React.FC<{
3
4
  spec: any;
4
5
  sqlQuery: string;
5
6
  lastRunTime?: number;
7
+ crossFilterEnabled: boolean;
6
8
  onSpecChange: (spec: any) => void;
9
+ onCrossFilterToggle: (enabled: boolean) => void;
10
+ onBrushFieldChange: (field: string | undefined) => void;
11
+ onBrushFieldTypeChange: (fieldType: BrushFieldType | undefined) => void;
7
12
  }>;
8
13
  //# sourceMappingURL=VegaConfigPanel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"VegaConfigPanel.d.ts","sourceRoot":"","sources":["../../src/components/VegaConfigPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAmC1B,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IACrC,IAAI,EAAE,GAAG,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;CACnC,CA6KA,CAAC"}
1
+ {"version":3,"file":"VegaConfigPanel.d.ts","sourceRoot":"","sources":["../../src/components/VegaConfigPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAY1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,UAAU,CAAC;AAW7C,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IACrC,IAAI,EAAE,GAAG,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAClC,mBAAmB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACxD,sBAAsB,EAAE,CAAC,SAAS,EAAE,cAAc,GAAG,SAAS,KAAK,IAAI,CAAC;CACzE,CAyJA,CAAC"}
@@ -1,72 +1,62 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Tabs, TabsContent, TabsList, TabsTrigger, Label, Separator, } from '@sqlrooms/ui';
2
+ import { Tabs, TabsContent, TabsList, TabsTrigger, Label, Separator, Switch, } from '@sqlrooms/ui';
3
3
  import { useSql } from '@sqlrooms/duckdb';
4
- const markOptions = [
5
- { value: 'bar', label: 'Bar' },
6
- { value: 'line', label: 'Line' },
7
- ];
8
- const colorOptions = [
9
- { value: '#077A9D', label: 'Blue' },
10
- { value: '#FFAB00', label: 'Orange' },
11
- { value: '#00A972', label: 'Green' },
12
- { value: '#85b6b2', label: 'Teal' },
13
- { value: '#919191', label: 'Gray' },
14
- ];
15
- const aggregationOptions = [
16
- { value: 'sum', label: 'Sum' },
17
- { value: 'mean', label: 'Mean' },
18
- { value: 'count', label: 'Count' },
19
- ];
20
- export const VegaConfigPanel = ({ sqlQuery, lastRunTime, spec, onSpecChange }) => {
4
+ import { getArrowColumnTypeCategory } from '@sqlrooms/duckdb';
5
+ import { readSpecValues, buildCrossFilterSpec, buildFlatSpec, } from '../vegaSpecBuilder';
6
+ import { FieldSelector } from './FieldSelector';
7
+ import { ColorSelector, colorOptions } from './ColorSelector';
8
+ import { ChartTypeSelector } from './ChartTypeSelector';
9
+ import { AggregationSelector } from './AggregationSelector';
10
+ export const VegaConfigPanel = ({ sqlQuery, lastRunTime, spec, crossFilterEnabled, onSpecChange, onCrossFilterToggle, onBrushFieldChange, onBrushFieldTypeChange, }) => {
21
11
  const result = useSql({ query: sqlQuery, version: lastRunTime });
22
12
  const arrowTable = result.data?.arrowTable;
23
- const fieldNames = arrowTable?.schema?.fields?.map((field) => field.name) || [];
24
- const handleMarkChange = (mark) => {
25
- onSpecChange({
26
- ...spec,
27
- mark,
28
- });
13
+ const fields = arrowTable?.schema?.fields || [];
14
+ const detectBrushFieldType = (fieldName) => {
15
+ const arrowField = arrowTable?.schema?.fields?.find((field) => field.name === fieldName);
16
+ if (!arrowField) {
17
+ return 'numeric';
18
+ }
19
+ const category = getArrowColumnTypeCategory(arrowField.type);
20
+ if (category === 'datetime')
21
+ return 'temporal';
22
+ if (category === 'string')
23
+ return 'string';
24
+ return 'numeric';
29
25
  };
30
- const handleXFieldChange = (field) => {
31
- onSpecChange({
32
- ...spec,
33
- encoding: {
34
- ...spec?.encoding,
35
- x: { ...spec?.encoding?.x, field },
36
- },
37
- });
26
+ const current = readSpecValues(spec);
27
+ const rebuild = (overrides, cfEnabled = crossFilterEnabled) => {
28
+ const merged = { ...current, ...overrides };
29
+ const builder = cfEnabled ? buildCrossFilterSpec : buildFlatSpec;
30
+ onSpecChange(builder({
31
+ mark: merged.mark ?? 'bar',
32
+ xField: merged.xField,
33
+ xFieldType: merged.xField
34
+ ? detectBrushFieldType(merged.xField)
35
+ : undefined,
36
+ yField: merged.yField,
37
+ yAggregate: merged.yAggregate,
38
+ color: merged.color ?? colorOptions[0]?.value,
39
+ }));
40
+ if ('xField' in overrides) {
41
+ onBrushFieldChange(cfEnabled ? overrides.xField : undefined);
42
+ onBrushFieldTypeChange(cfEnabled && overrides.xField
43
+ ? detectBrushFieldType(overrides.xField)
44
+ : undefined);
45
+ }
38
46
  };
39
- const handleYFieldChange = (field) => {
40
- onSpecChange({
41
- ...spec,
42
- encoding: {
43
- ...spec?.encoding,
44
- y: {
45
- ...spec?.encoding?.y,
46
- field,
47
- aggregate: spec?.encoding?.y?.aggregate ?? 'sum',
48
- },
49
- },
50
- });
47
+ const handleMarkChange = (mark) => rebuild({ mark });
48
+ const handleXFieldChange = (field) => rebuild({ xField: field });
49
+ const handleYFieldChange = (field) => rebuild({ yField: field, yAggregate: current.yAggregate ?? 'sum' });
50
+ const handleYAggregationChange = (aggregate) => rebuild({ yAggregate: aggregate });
51
+ const handleColorChange = (color) => rebuild({ color });
52
+ const handleCrossFilterToggle = (enabled) => {
53
+ onCrossFilterToggle(enabled);
54
+ rebuild({}, enabled);
55
+ onBrushFieldChange(enabled ? current.xField : undefined);
56
+ onBrushFieldTypeChange(enabled && current.xField
57
+ ? detectBrushFieldType(current.xField)
58
+ : undefined);
51
59
  };
52
- const handleYAggregationChange = (aggregate) => {
53
- onSpecChange({
54
- ...spec,
55
- encoding: {
56
- ...spec?.encoding,
57
- y: { ...spec?.encoding?.y, aggregate },
58
- },
59
- });
60
- };
61
- const handleColorChange = (color) => {
62
- onSpecChange({
63
- ...spec,
64
- encoding: {
65
- ...spec?.encoding,
66
- color: { value: color },
67
- },
68
- });
69
- };
70
- return (_jsx("div", { className: "w-80 border-r p-4 text-xs", children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Label, { className: "text-xs font-medium text-gray-400", children: "Type" }), _jsxs(Select, { value: spec?.mark, onValueChange: handleMarkChange, children: [_jsx(SelectTrigger, { className: "h-8 text-xs", children: _jsx(SelectValue, { placeholder: "Select chart type" }) }), _jsx(SelectContent, { onCloseAutoFocus: (e) => e.preventDefault(), children: markOptions.map((option) => (_jsx(SelectItem, { value: option.value, children: option.label }, option.value))) })] })] }), _jsx(Separator, {}), _jsxs(Tabs, { defaultValue: "general", className: "w-full", children: [_jsxs(TabsList, { className: "grid w-full grid-cols-2", children: [_jsx(TabsTrigger, { value: "general", className: "text-xs", children: "General" }), _jsx(TabsTrigger, { value: "styles", className: "text-xs", children: "Styles" })] }), _jsxs(TabsContent, { value: "general", className: "mt-4 space-y-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs font-medium text-gray-400", children: "X-Axis" }), _jsxs(Select, { value: spec?.encoding?.x?.field || '', onValueChange: handleXFieldChange, children: [_jsx(SelectTrigger, { className: "h-8 text-xs", children: _jsx(SelectValue, { placeholder: "Select field" }) }), _jsx(SelectContent, { onCloseAutoFocus: (e) => e.preventDefault(), children: fieldNames.map((field) => (_jsx(SelectItem, { value: field, children: field }, field))) })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs font-medium text-gray-400", children: "Y-Axis" }), _jsxs("div", { className: "grid grid-cols-[2fr_1fr] gap-2", children: [_jsxs(Select, { value: spec?.encoding?.y?.field || '', onValueChange: handleYFieldChange, children: [_jsx(SelectTrigger, { className: "h-8 text-xs", children: _jsx(SelectValue, { placeholder: "Select field" }) }), _jsx(SelectContent, { onCloseAutoFocus: (e) => e.preventDefault(), children: fieldNames.map((field) => (_jsx(SelectItem, { value: field, children: field }, field))) })] }), _jsxs(Select, { value: spec?.encoding?.y?.aggregate, onValueChange: handleYAggregationChange, children: [_jsx(SelectTrigger, { className: "h-8 text-xs", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { onCloseAutoFocus: (e) => e.preventDefault(), children: aggregationOptions.map((option) => (_jsx(SelectItem, { value: option.value, children: option.label }, option.value))) })] })] })] })] }), _jsx(TabsContent, { value: "styles", className: "mt-4 space-y-4", children: _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "font-medium", children: "Color" }), _jsxs(Select, { value: spec?.encoding?.color?.value, onValueChange: handleColorChange, children: [_jsx(SelectTrigger, { className: "h-8 text-xs", children: _jsx(SelectValue, { placeholder: "Select color" }) }), _jsx(SelectContent, { onCloseAutoFocus: (e) => e.preventDefault(), children: colorOptions.map((option) => (_jsx(SelectItem, { value: option.value, children: option.label }, option.value))) })] })] }) })] })] }) }));
60
+ return (_jsx("div", { className: "w-80 border-r p-4 text-xs", children: _jsxs("div", { className: "space-y-3", children: [_jsx(ChartTypeSelector, { value: current.mark, onValueChange: handleMarkChange }), _jsx(Separator, {}), _jsxs(Tabs, { defaultValue: "general", className: "w-full", children: [_jsxs(TabsList, { className: "grid w-full grid-cols-2", children: [_jsx(TabsTrigger, { value: "general", className: "text-xs", children: "General" }), _jsx(TabsTrigger, { value: "styles", className: "text-xs", children: "Styles" })] }), _jsxs(TabsContent, { value: "general", className: "mt-4 space-y-4", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs font-medium text-gray-400", children: "X-Axis" }), _jsx(FieldSelector, { value: current.xField, fields: fields, onValueChange: handleXFieldChange })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs font-medium text-gray-400", children: "Y-Axis" }), _jsxs("div", { className: "grid grid-cols-[2fr_1fr] gap-2", children: [_jsx(FieldSelector, { value: current.yField, fields: fields, onValueChange: handleYFieldChange }), _jsx(AggregationSelector, { value: current.yAggregate, onValueChange: handleYAggregationChange })] })] }), _jsx(Separator, {}), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { className: "text-xs font-medium text-gray-400", children: "Cross-filter" }), _jsx(Switch, { checked: crossFilterEnabled, onCheckedChange: handleCrossFilterToggle })] })] }), _jsx(TabsContent, { value: "styles", className: "mt-4 space-y-4", children: _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-xs font-medium text-gray-400", children: "Color" }), _jsx(ColorSelector, { value: current.color, onValueChange: handleColorChange })] }) })] })] }) }));
71
61
  };
72
62
  //# sourceMappingURL=VegaConfigPanel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"VegaConfigPanel.js","sourceRoot":"","sources":["../../src/components/VegaConfigPanel.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,MAAM,EACN,aAAa,EACb,UAAU,EACV,aAAa,EACb,WAAW,EACX,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,WAAW,EACX,KAAK,EACL,SAAS,GACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAC;AAExC,MAAM,WAAW,GAAG;IAClB,EAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAC;IAC5B,EAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAC;CAC/B,CAAC;AAEF,MAAM,YAAY,GAAG;IACnB,EAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAC;IACjC,EAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAC;IACnC,EAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAC;IAClC,EAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAC;IACjC,EAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAC;CAClC,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACzB,EAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAC;IAC5B,EAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAC;IAC9B,EAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAC;CACjC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAKvB,CAAC,EAAC,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAC,EAAE,EAAE;IACnD,MAAM,MAAM,GAAG,MAAM,CAAC,EAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAC,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC;IAC3C,MAAM,UAAU,GACd,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,KAAU,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAEpE,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAE,EAAE;QACxC,YAAY,CAAC;YACX,GAAG,IAAI;YACP,IAAI;SACL,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,kBAAkB,GAAG,CAAC,KAAa,EAAE,EAAE;QAC3C,YAAY,CAAC;YACX,GAAG,IAAI;YACP,QAAQ,EAAE;gBACR,GAAG,IAAI,EAAE,QAAQ;gBACjB,CAAC,EAAE,EAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAC;aACjC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,kBAAkB,GAAG,CAAC,KAAa,EAAE,EAAE;QAC3C,YAAY,CAAC;YACX,GAAG,IAAI;YACP,QAAQ,EAAE;gBACR,GAAG,IAAI,EAAE,QAAQ;gBACjB,CAAC,EAAE;oBACD,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC;oBACpB,KAAK;oBACL,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,IAAI,KAAK;iBACjD;aACF;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,wBAAwB,GAAG,CAAC,SAAiB,EAAE,EAAE;QACrD,YAAY,CAAC;YACX,GAAG,IAAI;YACP,QAAQ,EAAE;gBACR,GAAG,IAAI,EAAE,QAAQ;gBACjB,CAAC,EAAE,EAAC,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAC;aACrC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,iBAAiB,GAAG,CAAC,KAAa,EAAE,EAAE;QAC1C,YAAY,CAAC;YACX,GAAG,IAAI;YACP,QAAQ,EAAE;gBACR,GAAG,IAAI,EAAE,QAAQ;gBACjB,KAAK,EAAE,EAAC,KAAK,EAAE,KAAK,EAAC;aACtB;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,CACL,cAAK,SAAS,EAAC,2BAA2B,YACxC,eAAK,SAAS,EAAC,WAAW,aACxB,eAAK,SAAS,EAAC,yBAAyB,aACtC,KAAC,KAAK,IAAC,SAAS,EAAC,mCAAmC,qBAAa,EACjE,MAAC,MAAM,IAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,gBAAgB,aACxD,KAAC,aAAa,IAAC,SAAS,EAAC,aAAa,YACpC,KAAC,WAAW,IAAC,WAAW,EAAC,mBAAmB,GAAG,GACjC,EAChB,KAAC,aAAa,IAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,YACvD,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAC3B,KAAC,UAAU,IAAoB,KAAK,EAAE,MAAM,CAAC,KAAK,YAC/C,MAAM,CAAC,KAAK,IADE,MAAM,CAAC,KAAK,CAEhB,CACd,CAAC,GACY,IACT,IACL,EAEN,KAAC,SAAS,KAAG,EAEb,MAAC,IAAI,IAAC,YAAY,EAAC,SAAS,EAAC,SAAS,EAAC,QAAQ,aAC7C,MAAC,QAAQ,IAAC,SAAS,EAAC,yBAAyB,aAC3C,KAAC,WAAW,IAAC,KAAK,EAAC,SAAS,EAAC,SAAS,EAAC,SAAS,wBAElC,EACd,KAAC,WAAW,IAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,SAAS,uBAEjC,IACL,EAEX,MAAC,WAAW,IAAC,KAAK,EAAC,SAAS,EAAC,SAAS,EAAC,gBAAgB,aACrD,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,KAAK,IAAC,SAAS,EAAC,mCAAmC,uBAE5C,EACR,MAAC,MAAM,IACL,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,IAAI,EAAE,EACrC,aAAa,EAAE,kBAAkB,aAEjC,KAAC,aAAa,IAAC,SAAS,EAAC,aAAa,YACpC,KAAC,WAAW,IAAC,WAAW,EAAC,cAAc,GAAG,GAC5B,EAChB,KAAC,aAAa,IAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,YACvD,UAAU,CAAC,GAAG,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,CACjC,KAAC,UAAU,IAAa,KAAK,EAAE,KAAK,YACjC,KAAK,IADS,KAAK,CAET,CACd,CAAC,GACY,IACT,IACL,EACN,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,KAAK,IAAC,SAAS,EAAC,mCAAmC,uBAE5C,EACR,eAAK,SAAS,EAAC,gCAAgC,aAC7C,MAAC,MAAM,IACL,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,IAAI,EAAE,EACrC,aAAa,EAAE,kBAAkB,aAEjC,KAAC,aAAa,IAAC,SAAS,EAAC,aAAa,YACpC,KAAC,WAAW,IAAC,WAAW,EAAC,cAAc,GAAG,GAC5B,EAChB,KAAC,aAAa,IAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,YACvD,UAAU,CAAC,GAAG,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,CACjC,KAAC,UAAU,IAAa,KAAK,EAAE,KAAK,YACjC,KAAK,IADS,KAAK,CAET,CACd,CAAC,GACY,IACT,EAET,MAAC,MAAM,IACL,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EACnC,aAAa,EAAE,wBAAwB,aAEvC,KAAC,aAAa,IAAC,SAAS,EAAC,aAAa,YACpC,KAAC,WAAW,KAAG,GACD,EAChB,KAAC,aAAa,IAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,YACvD,kBAAkB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAClC,KAAC,UAAU,IAAoB,KAAK,EAAE,MAAM,CAAC,KAAK,YAC/C,MAAM,CAAC,KAAK,IADE,MAAM,CAAC,KAAK,CAEhB,CACd,CAAC,GACY,IACT,IACL,IACF,IACM,EAEd,KAAC,WAAW,IAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,gBAAgB,YACpD,eAAK,SAAS,EAAC,WAAW,aACxB,KAAC,KAAK,IAAC,SAAS,EAAC,aAAa,sBAAc,EAC5C,MAAC,MAAM,IACL,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EACnC,aAAa,EAAE,iBAAiB,aAEhC,KAAC,aAAa,IAAC,SAAS,EAAC,aAAa,YACpC,KAAC,WAAW,IAAC,WAAW,EAAC,cAAc,GAAG,GAC5B,EAChB,KAAC,aAAa,IAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,EAAE,YACvD,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAC5B,KAAC,UAAU,IAAoB,KAAK,EAAE,MAAM,CAAC,KAAK,YAC/C,MAAM,CAAC,KAAK,IADE,MAAM,CAAC,KAAK,CAEhB,CACd,CAAC,GACY,IACT,IACL,GACM,IACT,IACH,GACF,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import React from 'react';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Label,\n Separator,\n} from '@sqlrooms/ui';\nimport {useSql} from '@sqlrooms/duckdb';\n\nconst markOptions = [\n {value: 'bar', label: 'Bar'},\n {value: 'line', label: 'Line'},\n];\n\nconst colorOptions = [\n {value: '#077A9D', label: 'Blue'},\n {value: '#FFAB00', label: 'Orange'},\n {value: '#00A972', label: 'Green'},\n {value: '#85b6b2', label: 'Teal'},\n {value: '#919191', label: 'Gray'},\n];\n\nconst aggregationOptions = [\n {value: 'sum', label: 'Sum'},\n {value: 'mean', label: 'Mean'},\n {value: 'count', label: 'Count'},\n];\n\nexport const VegaConfigPanel: React.FC<{\n spec: any;\n sqlQuery: string;\n lastRunTime?: number;\n onSpecChange: (spec: any) => void;\n}> = ({sqlQuery, lastRunTime, spec, onSpecChange}) => {\n const result = useSql({query: sqlQuery, version: lastRunTime});\n const arrowTable = result.data?.arrowTable;\n const fieldNames =\n arrowTable?.schema?.fields?.map((field: any) => field.name) || [];\n\n const handleMarkChange = (mark: string) => {\n onSpecChange({\n ...spec,\n mark,\n });\n };\n\n const handleXFieldChange = (field: string) => {\n onSpecChange({\n ...spec,\n encoding: {\n ...spec?.encoding,\n x: {...spec?.encoding?.x, field},\n },\n });\n };\n\n const handleYFieldChange = (field: string) => {\n onSpecChange({\n ...spec,\n encoding: {\n ...spec?.encoding,\n y: {\n ...spec?.encoding?.y,\n field,\n aggregate: spec?.encoding?.y?.aggregate ?? 'sum',\n },\n },\n });\n };\n\n const handleYAggregationChange = (aggregate: string) => {\n onSpecChange({\n ...spec,\n encoding: {\n ...spec?.encoding,\n y: {...spec?.encoding?.y, aggregate},\n },\n });\n };\n\n const handleColorChange = (color: string) => {\n onSpecChange({\n ...spec,\n encoding: {\n ...spec?.encoding,\n color: {value: color},\n },\n });\n };\n\n return (\n <div className=\"w-80 border-r p-4 text-xs\">\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Label className=\"text-xs font-medium text-gray-400\">Type</Label>\n <Select value={spec?.mark} onValueChange={handleMarkChange}>\n <SelectTrigger className=\"h-8 text-xs\">\n <SelectValue placeholder=\"Select chart type\" />\n </SelectTrigger>\n <SelectContent onCloseAutoFocus={(e) => e.preventDefault()}>\n {markOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <Separator />\n\n <Tabs defaultValue=\"general\" className=\"w-full\">\n <TabsList className=\"grid w-full grid-cols-2\">\n <TabsTrigger value=\"general\" className=\"text-xs\">\n General\n </TabsTrigger>\n <TabsTrigger value=\"styles\" className=\"text-xs\">\n Styles\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"general\" className=\"mt-4 space-y-4\">\n <div className=\"space-y-1\">\n <Label className=\"text-xs font-medium text-gray-400\">\n X-Axis\n </Label>\n <Select\n value={spec?.encoding?.x?.field || ''}\n onValueChange={handleXFieldChange}\n >\n <SelectTrigger className=\"h-8 text-xs\">\n <SelectValue placeholder=\"Select field\" />\n </SelectTrigger>\n <SelectContent onCloseAutoFocus={(e) => e.preventDefault()}>\n {fieldNames.map((field: string) => (\n <SelectItem key={field} value={field}>\n {field}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs font-medium text-gray-400\">\n Y-Axis\n </Label>\n <div className=\"grid grid-cols-[2fr_1fr] gap-2\">\n <Select\n value={spec?.encoding?.y?.field || ''}\n onValueChange={handleYFieldChange}\n >\n <SelectTrigger className=\"h-8 text-xs\">\n <SelectValue placeholder=\"Select field\" />\n </SelectTrigger>\n <SelectContent onCloseAutoFocus={(e) => e.preventDefault()}>\n {fieldNames.map((field: string) => (\n <SelectItem key={field} value={field}>\n {field}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select\n value={spec?.encoding?.y?.aggregate}\n onValueChange={handleYAggregationChange}\n >\n <SelectTrigger className=\"h-8 text-xs\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent onCloseAutoFocus={(e) => e.preventDefault()}>\n {aggregationOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n </TabsContent>\n\n <TabsContent value=\"styles\" className=\"mt-4 space-y-4\">\n <div className=\"space-y-1\">\n <Label className=\"font-medium\">Color</Label>\n <Select\n value={spec?.encoding?.color?.value}\n onValueChange={handleColorChange}\n >\n <SelectTrigger className=\"h-8 text-xs\">\n <SelectValue placeholder=\"Select color\" />\n </SelectTrigger>\n <SelectContent onCloseAutoFocus={(e) => e.preventDefault()}>\n {colorOptions.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </TabsContent>\n </Tabs>\n </div>\n </div>\n );\n};\n"]}
1
+ {"version":3,"file":"VegaConfigPanel.js","sourceRoot":"","sources":["../../src/components/VegaConfigPanel.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport {\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Label,\n Separator,\n Switch,\n} from '@sqlrooms/ui';\nimport {useSql} from '@sqlrooms/duckdb';\nimport {getArrowColumnTypeCategory} from '@sqlrooms/duckdb';\nimport type {BrushFieldType} from '../types';\nimport {\n readSpecValues,\n buildCrossFilterSpec,\n buildFlatSpec,\n} from '../vegaSpecBuilder';\nimport {FieldSelector} from './FieldSelector';\nimport {ColorSelector, colorOptions} from './ColorSelector';\nimport {ChartTypeSelector} from './ChartTypeSelector';\nimport {AggregationSelector} from './AggregationSelector';\n\nexport const VegaConfigPanel: React.FC<{\n spec: any;\n sqlQuery: string;\n lastRunTime?: number;\n crossFilterEnabled: boolean;\n onSpecChange: (spec: any) => void;\n onCrossFilterToggle: (enabled: boolean) => void;\n onBrushFieldChange: (field: string | undefined) => void;\n onBrushFieldTypeChange: (fieldType: BrushFieldType | undefined) => void;\n}> = ({\n sqlQuery,\n lastRunTime,\n spec,\n crossFilterEnabled,\n onSpecChange,\n onCrossFilterToggle,\n onBrushFieldChange,\n onBrushFieldTypeChange,\n}) => {\n const result = useSql({query: sqlQuery, version: lastRunTime});\n const arrowTable = result.data?.arrowTable;\n const fields = arrowTable?.schema?.fields || [];\n\n const detectBrushFieldType = (fieldName: string): BrushFieldType => {\n const arrowField = arrowTable?.schema?.fields?.find(\n (field) => field.name === fieldName,\n );\n\n if (!arrowField) {\n return 'numeric';\n }\n\n const category = getArrowColumnTypeCategory(arrowField.type);\n if (category === 'datetime') return 'temporal';\n if (category === 'string') return 'string';\n return 'numeric';\n };\n\n const current = readSpecValues(spec);\n\n const rebuild = (\n overrides: Partial<ReturnType<typeof readSpecValues>>,\n cfEnabled = crossFilterEnabled,\n ) => {\n const merged = {...current, ...overrides};\n const builder = cfEnabled ? buildCrossFilterSpec : buildFlatSpec;\n onSpecChange(\n builder({\n mark: merged.mark ?? 'bar',\n xField: merged.xField,\n xFieldType: merged.xField\n ? detectBrushFieldType(merged.xField)\n : undefined,\n yField: merged.yField,\n yAggregate: merged.yAggregate,\n color: merged.color ?? colorOptions[0]?.value,\n }),\n );\n if ('xField' in overrides) {\n onBrushFieldChange(cfEnabled ? overrides.xField : undefined);\n onBrushFieldTypeChange(\n cfEnabled && overrides.xField\n ? detectBrushFieldType(overrides.xField)\n : undefined,\n );\n }\n };\n\n const handleMarkChange = (mark: string) => rebuild({mark});\n const handleXFieldChange = (field: string) => rebuild({xField: field});\n const handleYFieldChange = (field: string) =>\n rebuild({yField: field, yAggregate: current.yAggregate ?? 'sum'});\n const handleYAggregationChange = (aggregate: string) =>\n rebuild({yAggregate: aggregate});\n const handleColorChange = (color: string) => rebuild({color});\n\n const handleCrossFilterToggle = (enabled: boolean) => {\n onCrossFilterToggle(enabled);\n rebuild({}, enabled);\n onBrushFieldChange(enabled ? current.xField : undefined);\n onBrushFieldTypeChange(\n enabled && current.xField\n ? detectBrushFieldType(current.xField)\n : undefined,\n );\n };\n\n return (\n <div className=\"w-80 border-r p-4 text-xs\">\n <div className=\"space-y-3\">\n <ChartTypeSelector\n value={current.mark}\n onValueChange={handleMarkChange}\n />\n\n <Separator />\n\n <Tabs defaultValue=\"general\" className=\"w-full\">\n <TabsList className=\"grid w-full grid-cols-2\">\n <TabsTrigger value=\"general\" className=\"text-xs\">\n General\n </TabsTrigger>\n <TabsTrigger value=\"styles\" className=\"text-xs\">\n Styles\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"general\" className=\"mt-4 space-y-4\">\n <div className=\"space-y-1\">\n <Label className=\"text-xs font-medium text-gray-400\">\n X-Axis\n </Label>\n <FieldSelector\n value={current.xField}\n fields={fields}\n onValueChange={handleXFieldChange}\n />\n </div>\n <div className=\"space-y-1\">\n <Label className=\"text-xs font-medium text-gray-400\">\n Y-Axis\n </Label>\n <div className=\"grid grid-cols-[2fr_1fr] gap-2\">\n <FieldSelector\n value={current.yField}\n fields={fields}\n onValueChange={handleYFieldChange}\n />\n\n <AggregationSelector\n value={current.yAggregate}\n onValueChange={handleYAggregationChange}\n />\n </div>\n </div>\n\n <Separator />\n\n <div className=\"flex items-center justify-between\">\n <Label className=\"text-xs font-medium text-gray-400\">\n Cross-filter\n </Label>\n <Switch\n checked={crossFilterEnabled}\n onCheckedChange={handleCrossFilterToggle}\n />\n </div>\n </TabsContent>\n\n <TabsContent value=\"styles\" className=\"mt-4 space-y-4\">\n <div className=\"space-y-1\">\n <Label className=\"text-xs font-medium text-gray-400\">Color</Label>\n <ColorSelector\n value={current.color}\n onValueChange={handleColorChange}\n />\n </div>\n </TabsContent>\n </Tabs>\n </div>\n </div>\n );\n};\n"]}
package/dist/crdt.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ import type { CrdtMirror } from '@sqlrooms/crdt';
2
+ import { type CellsSliceState } from './types';
3
+ /**
4
+ * Mirror schema for syncing the `@sqlrooms/cells` slice via `@sqlrooms/crdt`.
5
+ *
6
+ * Syncs: data (cells), sheets, sheetOrder, edges
7
+ * Keeps local: status, currentSheetId, abortControllers
8
+ */
9
+ export declare const cellsMirrorSchema: import("loro-mirror").LoroMapSchema<{
10
+ config: import("loro-mirror").LoroMapSchema<{
11
+ data: /*elided*/ any;
12
+ sheets: /*elided*/ any;
13
+ sheetOrder: /*elided*/ any;
14
+ }> & {
15
+ options: {};
16
+ } & {
17
+ catchall: <C extends import("loro-mirror").SchemaType>(catchallSchema: C) => import("loro-mirror").LoroMapSchemaWithCatchall<{
18
+ data: /*elided*/ any;
19
+ sheets: /*elided*/ any;
20
+ sheetOrder: /*elided*/ any;
21
+ }, C>;
22
+ };
23
+ }> & {
24
+ options: {};
25
+ } & {
26
+ catchall: <C extends import("loro-mirror").SchemaType>(catchallSchema: C) => import("loro-mirror").LoroMapSchemaWithCatchall<{
27
+ config: import("loro-mirror").LoroMapSchema<{
28
+ data: /*elided*/ any;
29
+ sheets: /*elided*/ any;
30
+ sheetOrder: /*elided*/ any;
31
+ }> & {
32
+ options: {};
33
+ } & {
34
+ catchall: <C_1 extends import("loro-mirror").SchemaType>(catchallSchema: C_1) => import("loro-mirror").LoroMapSchemaWithCatchall<{
35
+ data: /*elided*/ any;
36
+ sheets: /*elided*/ any;
37
+ sheetOrder: /*elided*/ any;
38
+ }, C_1>;
39
+ };
40
+ }, C>;
41
+ };
42
+ export type CellsMirrorSchema = typeof cellsMirrorSchema;
43
+ /**
44
+ * Default initial mirror state for the cells CRDT schema.
45
+ */
46
+ export declare const cellsMirrorInitialState: {
47
+ config: {
48
+ data: never[];
49
+ sheets: never[];
50
+ sheetOrder: never[];
51
+ };
52
+ };
53
+ /**
54
+ * Creates a CRDT mirror bundle for the cells slice.
55
+ */
56
+ export declare function createCellsCrdtMirror<S extends CellsSliceState = CellsSliceState>(): CrdtMirror<S, typeof cellsMirrorSchema>;
57
+ /**
58
+ * @deprecated Use `createCellsCrdtMirror` instead.
59
+ */
60
+ export declare const createCellsCrdtModule: typeof createCellsCrdtMirror;
61
+ //# sourceMappingURL=crdt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crdt.d.ts","sourceRoot":"","sources":["../src/crdt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAmB,KAAK,eAAe,EAAC,MAAM,SAAS,CAAC;AAE/D;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6B5B,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;CAMnC,CAAC;AAEF;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,CAAC,SAAS,eAAe,GAAG,eAAe,KACxC,UAAU,CAAC,CAAC,EAAE,OAAO,iBAAiB,CAAC,CAoD3C;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,8BAAwB,CAAC"}
package/dist/crdt.js ADDED
@@ -0,0 +1,82 @@
1
+ import { schema } from 'loro-mirror';
2
+ import { CellsSliceConfig } from './types';
3
+ /**
4
+ * Mirror schema for syncing the `@sqlrooms/cells` slice via `@sqlrooms/crdt`.
5
+ *
6
+ * Syncs: data (cells), sheets, sheetOrder, edges
7
+ * Keeps local: status, currentSheetId, abortControllers
8
+ */
9
+ export const cellsMirrorSchema = schema.LoroMap({
10
+ config: schema.LoroMap({
11
+ data: schema.LoroList(schema.LoroMap({
12
+ id: schema.String(),
13
+ type: schema.String(),
14
+ data: schema.Any(), // Cell-specific data varies by type
15
+ }), (cell) => cell.id),
16
+ sheets: schema.LoroList(schema.LoroMap({
17
+ id: schema.String(),
18
+ type: schema.String(),
19
+ title: schema.String(),
20
+ cellIds: schema.LoroList(schema.String()),
21
+ edges: schema.LoroList(schema.LoroMap({
22
+ id: schema.String(),
23
+ source: schema.String(),
24
+ target: schema.String(),
25
+ }), (edge) => edge.id),
26
+ }), (sheet) => sheet.id),
27
+ sheetOrder: schema.LoroList(schema.String()),
28
+ }),
29
+ });
30
+ /**
31
+ * Default initial mirror state for the cells CRDT schema.
32
+ */
33
+ export const cellsMirrorInitialState = {
34
+ config: {
35
+ data: [],
36
+ sheets: [],
37
+ sheetOrder: [],
38
+ },
39
+ };
40
+ /**
41
+ * Creates a CRDT mirror bundle for the cells slice.
42
+ */
43
+ export function createCellsCrdtMirror() {
44
+ return {
45
+ schema: cellsMirrorSchema,
46
+ initialState: cellsMirrorInitialState,
47
+ select: (state) => ({
48
+ config: {
49
+ data: Object.values(state.cells.config.data),
50
+ sheets: Object.values(state.cells.config.sheets).map((sheet) => ({
51
+ ...sheet,
52
+ edges: sheet.edges || [],
53
+ })),
54
+ sheetOrder: state.cells.config.sheetOrder,
55
+ },
56
+ }),
57
+ apply: (value, set, get) => {
58
+ if (!value?.config)
59
+ return;
60
+ const configValue = value.config;
61
+ const newData = Object.fromEntries((configValue.data || []).map((c) => [c.id, c]));
62
+ const newSheets = Object.fromEntries((configValue.sheets || []).map((s) => [s.id, s]));
63
+ set((state) => ({
64
+ ...state,
65
+ cells: {
66
+ ...state.cells,
67
+ config: CellsSliceConfig.parse({
68
+ ...state.cells.config,
69
+ data: newData,
70
+ sheets: newSheets,
71
+ sheetOrder: configValue.sheetOrder,
72
+ }),
73
+ },
74
+ }));
75
+ },
76
+ };
77
+ }
78
+ /**
79
+ * @deprecated Use `createCellsCrdtMirror` instead.
80
+ */
81
+ export const createCellsCrdtModule = createCellsCrdtMirror;
82
+ //# sourceMappingURL=crdt.js.map