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

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 (144) hide show
  1. package/LICENSE.md +2 -1
  2. package/README.md +8 -8
  3. package/dist/cellsSlice.d.ts.map +1 -1
  4. package/dist/cellsSlice.js +305 -264
  5. package/dist/cellsSlice.js.map +1 -1
  6. package/dist/components/AggregationSelector.d.ts +8 -0
  7. package/dist/components/AggregationSelector.d.ts.map +1 -0
  8. package/dist/components/AggregationSelector.js +13 -0
  9. package/dist/components/AggregationSelector.js.map +1 -0
  10. package/dist/components/CellSourceSelector.d.ts +12 -0
  11. package/dist/components/CellSourceSelector.d.ts.map +1 -0
  12. package/dist/components/CellSourceSelector.js +34 -0
  13. package/dist/components/CellSourceSelector.js.map +1 -0
  14. package/dist/components/ChartTypeSelector.d.ts +8 -0
  15. package/dist/components/ChartTypeSelector.d.ts.map +1 -0
  16. package/dist/components/ChartTypeSelector.js +13 -0
  17. package/dist/components/ChartTypeSelector.js.map +1 -0
  18. package/dist/components/CollapsibleSectionButton.d.ts +10 -0
  19. package/dist/components/CollapsibleSectionButton.d.ts.map +1 -0
  20. package/dist/components/CollapsibleSectionButton.js +10 -0
  21. package/dist/components/CollapsibleSectionButton.js.map +1 -0
  22. package/dist/components/ColorSelector.d.ts +24 -0
  23. package/dist/components/ColorSelector.d.ts.map +1 -0
  24. package/dist/components/ColorSelector.js +17 -0
  25. package/dist/components/ColorSelector.js.map +1 -0
  26. package/dist/components/FieldSelector.d.ts +11 -0
  27. package/dist/components/FieldSelector.d.ts.map +1 -0
  28. package/dist/components/FieldSelector.js +11 -0
  29. package/dist/components/FieldSelector.js.map +1 -0
  30. package/dist/components/FieldTypeIcon.d.ts +8 -0
  31. package/dist/components/FieldTypeIcon.d.ts.map +1 -0
  32. package/dist/components/FieldTypeIcon.js +27 -0
  33. package/dist/components/FieldTypeIcon.js.map +1 -0
  34. package/dist/components/InputCellContent.d.ts.map +1 -1
  35. package/dist/components/InputCellContent.js +3 -3
  36. package/dist/components/InputCellContent.js.map +1 -1
  37. package/dist/components/SqlCellConnectionSelector.d.ts +9 -0
  38. package/dist/components/SqlCellConnectionSelector.d.ts.map +1 -0
  39. package/dist/components/SqlCellConnectionSelector.js +21 -0
  40. package/dist/components/SqlCellConnectionSelector.js.map +1 -0
  41. package/dist/components/SqlCellContent.d.ts.map +1 -1
  42. package/dist/components/SqlCellContent.js +31 -160
  43. package/dist/components/SqlCellContent.js.map +1 -1
  44. package/dist/components/SqlCellDependentsMenu.d.ts +11 -0
  45. package/dist/components/SqlCellDependentsMenu.d.ts.map +1 -0
  46. package/dist/components/SqlCellDependentsMenu.js +62 -0
  47. package/dist/components/SqlCellDependentsMenu.js.map +1 -0
  48. package/dist/components/SqlCellEditor.d.ts +14 -0
  49. package/dist/components/SqlCellEditor.d.ts.map +1 -0
  50. package/dist/components/SqlCellEditor.js +17 -0
  51. package/dist/components/SqlCellEditor.js.map +1 -0
  52. package/dist/components/SqlCellResultNameEditor.d.ts +8 -0
  53. package/dist/components/SqlCellResultNameEditor.d.ts.map +1 -0
  54. package/dist/components/SqlCellResultNameEditor.js +38 -0
  55. package/dist/components/SqlCellResultNameEditor.js.map +1 -0
  56. package/dist/components/SqlCellResults.d.ts +17 -0
  57. package/dist/components/SqlCellResults.d.ts.map +1 -0
  58. package/dist/components/SqlCellResults.js +44 -0
  59. package/dist/components/SqlCellResults.js.map +1 -0
  60. package/dist/components/SqlCellRunButton.d.ts +5 -6
  61. package/dist/components/SqlCellRunButton.d.ts.map +1 -1
  62. package/dist/components/SqlCellRunButton.js +11 -7
  63. package/dist/components/SqlCellRunButton.js.map +1 -1
  64. package/dist/components/SqlQueryPreview.d.ts +9 -0
  65. package/dist/components/SqlQueryPreview.d.ts.map +1 -0
  66. package/dist/components/SqlQueryPreview.js +14 -0
  67. package/dist/components/SqlQueryPreview.js.map +1 -0
  68. package/dist/components/TextCellContent.d.ts.map +1 -1
  69. package/dist/components/TextCellContent.js +24 -7
  70. package/dist/components/TextCellContent.js.map +1 -1
  71. package/dist/components/VegaCellContent.d.ts +0 -1
  72. package/dist/components/VegaCellContent.d.ts.map +1 -1
  73. package/dist/components/VegaCellContent.js +28 -49
  74. package/dist/components/VegaCellContent.js.map +1 -1
  75. package/dist/components/VegaCellHeader.d.ts +8 -0
  76. package/dist/components/VegaCellHeader.d.ts.map +1 -0
  77. package/dist/components/VegaCellHeader.js +30 -0
  78. package/dist/components/VegaCellHeader.js.map +1 -0
  79. package/dist/components/VegaConfigPanel.d.ts.map +1 -1
  80. package/dist/components/VegaConfigPanel.js +23 -64
  81. package/dist/components/VegaConfigPanel.js.map +1 -1
  82. package/dist/dagUtils.d.ts +10 -9
  83. package/dist/dagUtils.d.ts.map +1 -1
  84. package/dist/dagUtils.js +76 -50
  85. package/dist/dagUtils.js.map +1 -1
  86. package/dist/defaultCellRegistry.d.ts.map +1 -1
  87. package/dist/defaultCellRegistry.js +82 -39
  88. package/dist/defaultCellRegistry.js.map +1 -1
  89. package/dist/execution.d.ts.map +1 -1
  90. package/dist/execution.js +27 -20
  91. package/dist/execution.js.map +1 -1
  92. package/dist/helpers.d.ts +21 -28
  93. package/dist/helpers.d.ts.map +1 -1
  94. package/dist/helpers.js +77 -90
  95. package/dist/helpers.js.map +1 -1
  96. package/dist/hooks/useVegaCellDataSource.d.ts +18 -0
  97. package/dist/hooks/useVegaCellDataSource.d.ts.map +1 -0
  98. package/dist/hooks/useVegaCellDataSource.js +40 -0
  99. package/dist/hooks/useVegaCellDataSource.js.map +1 -0
  100. package/dist/hooks/useVegaCellQuery.d.ts +12 -0
  101. package/dist/hooks/useVegaCellQuery.d.ts.map +1 -0
  102. package/dist/hooks/useVegaCellQuery.js +12 -0
  103. package/dist/hooks/useVegaCellQuery.js.map +1 -0
  104. package/dist/index.d.ts +2 -3
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +2 -3
  107. package/dist/index.js.map +1 -1
  108. package/dist/resultRelationPolicy.d.ts +2 -2
  109. package/dist/resultRelationPolicy.d.ts.map +1 -1
  110. package/dist/resultRelationPolicy.js +9 -1
  111. package/dist/resultRelationPolicy.js.map +1 -1
  112. package/dist/sqlHelpers.d.ts +8 -8
  113. package/dist/sqlHelpers.d.ts.map +1 -1
  114. package/dist/sqlHelpers.js +16 -12
  115. package/dist/sqlHelpers.js.map +1 -1
  116. package/dist/types.d.ts +113 -70
  117. package/dist/types.d.ts.map +1 -1
  118. package/dist/types.js +22 -13
  119. package/dist/types.js.map +1 -1
  120. package/dist/useValidateResultName.d.ts +34 -0
  121. package/dist/useValidateResultName.d.ts.map +1 -0
  122. package/dist/useValidateResultName.js +111 -0
  123. package/dist/useValidateResultName.js.map +1 -0
  124. package/dist/utils.d.ts +10 -2
  125. package/dist/utils.d.ts.map +1 -1
  126. package/dist/utils.js +20 -4
  127. package/dist/utils.js.map +1 -1
  128. package/dist/vegaSelectionUtils.d.ts +14 -0
  129. package/dist/vegaSelectionUtils.d.ts.map +1 -0
  130. package/dist/vegaSelectionUtils.js +97 -0
  131. package/dist/vegaSelectionUtils.js.map +1 -0
  132. package/dist/vegaSpecBuilder.d.ts +34 -0
  133. package/dist/vegaSpecBuilder.d.ts.map +1 -0
  134. package/dist/vegaSpecBuilder.js +90 -0
  135. package/dist/vegaSpecBuilder.js.map +1 -0
  136. package/package.json +20 -21
  137. package/dist/components/DeleteSheetModal.d.ts +0 -9
  138. package/dist/components/DeleteSheetModal.d.ts.map +0 -1
  139. package/dist/components/DeleteSheetModal.js +0 -6
  140. package/dist/components/DeleteSheetModal.js.map +0 -1
  141. package/dist/components/SheetsTabBar.d.ts +0 -6
  142. package/dist/components/SheetsTabBar.d.ts.map +0 -1
  143. package/dist/components/SheetsTabBar.js +0 -57
  144. package/dist/components/SheetsTabBar.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlCellEditor.d.ts","sourceRoot":"","sources":["../../src/components/SqlCellEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAE,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEjE,OAAO,KAAiB,MAAM,OAAO,CAAC;AAItC,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAC9C,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAsCtD,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SqlCodeMirrorEditor } from '@sqlrooms/sql-editor';
3
+ import { useState } from 'react';
4
+ import { CollapsibleSectionButton } from './CollapsibleSectionButton';
5
+ import { SqlQueryPreview } from './SqlQueryPreview';
6
+ /**
7
+ * SQL query editor with collapse/expand functionality
8
+ */
9
+ export const SqlCellEditor = ({ sql, connector, tableSchemas, onChange, onRunQuery, }) => {
10
+ const [isCollapsed, setIsCollapsed] = useState(false);
11
+ return (_jsxs("div", { className: "group relative w-full py-1", children: [_jsx(CollapsibleSectionButton, { isCollapsed: isCollapsed, onToggle: () => setIsCollapsed(!isCollapsed) }), isCollapsed ? (_jsx("div", { className: "text-muted-foreground ml-5 px-2 py-1 text-xs", children: sql && _jsx(SqlQueryPreview, { sql: sql }) })) : (_jsx("div", { className: "w-full px-1", children: _jsx(SqlCodeMirrorEditor, { className: "h-auto min-h-[48px] w-full", connector: connector, tableSchemas: tableSchemas, value: sql, onChange: onChange, onRunQuery: onRunQuery, options: {
12
+ lineNumbers: true,
13
+ lineWrapping: false,
14
+ highlightActiveLine: true,
15
+ } }) }))] }));
16
+ };
17
+ //# sourceMappingURL=SqlCellEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlCellEditor.js","sourceRoot":"","sources":["../../src/components/SqlCellEditor.tsx"],"names":[],"mappings":";AACA,OAAO,EAAC,mBAAmB,EAAC,MAAM,sBAAsB,CAAC;AACzD,OAAc,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AACtC,OAAO,EAAC,wBAAwB,EAAC,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAUlD;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAiC,CAAC,EAC1D,GAAG,EACH,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,UAAU,GACX,EAAE,EAAE;IACH,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEtD,OAAO,CACL,eAAK,SAAS,EAAC,4BAA4B,aACzC,KAAC,wBAAwB,IACvB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,WAAW,CAAC,GAC5C,EACD,WAAW,CAAC,CAAC,CAAC,CACb,cAAK,SAAS,EAAC,8CAA8C,YAC1D,GAAG,IAAI,KAAC,eAAe,IAAC,GAAG,EAAE,GAAG,GAAI,GACjC,CACP,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,aAAa,YAC1B,KAAC,mBAAmB,IAClB,SAAS,EAAC,4BAA4B,EACtC,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,YAAY,EAC1B,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE;wBACP,WAAW,EAAE,IAAI;wBACjB,YAAY,EAAE,KAAK;wBACnB,mBAAmB,EAAE,IAAI;qBAC1B,GACD,GACE,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import type {DataTable, DuckDbConnector} from '@sqlrooms/duckdb';\nimport {SqlCodeMirrorEditor} from '@sqlrooms/sql-editor';\nimport React, {useState} from 'react';\nimport {CollapsibleSectionButton} from './CollapsibleSectionButton';\nimport {SqlQueryPreview} from './SqlQueryPreview';\n\nexport interface SqlCellEditorProps {\n sql: string;\n connector?: DuckDbConnector;\n tableSchemas?: DataTable[];\n onChange: (value: string | undefined) => void;\n onRunQuery: (query: string) => void;\n}\n\n/**\n * SQL query editor with collapse/expand functionality\n */\nexport const SqlCellEditor: React.FC<SqlCellEditorProps> = ({\n sql,\n connector,\n tableSchemas,\n onChange,\n onRunQuery,\n}) => {\n const [isCollapsed, setIsCollapsed] = useState(false);\n\n return (\n <div className=\"group relative w-full py-1\">\n <CollapsibleSectionButton\n isCollapsed={isCollapsed}\n onToggle={() => setIsCollapsed(!isCollapsed)}\n />\n {isCollapsed ? (\n <div className=\"text-muted-foreground ml-5 px-2 py-1 text-xs\">\n {sql && <SqlQueryPreview sql={sql} />}\n </div>\n ) : (\n <div className=\"w-full px-1\">\n <SqlCodeMirrorEditor\n className=\"h-auto min-h-[48px] w-full\"\n connector={connector}\n tableSchemas={tableSchemas}\n value={sql}\n onChange={onChange}\n onRunQuery={onRunQuery}\n options={{\n lineNumbers: true,\n lineWrapping: false,\n highlightActiveLine: true,\n }}\n />\n </div>\n )}\n </div>\n );\n};\n"]}
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export type SqlCellResultNameEditorProps = {
3
+ cellId: string;
4
+ value: string;
5
+ onChange: (value: string) => void;
6
+ };
7
+ export declare const SqlCellResultNameEditor: React.FC<SqlCellResultNameEditorProps>;
8
+ //# sourceMappingURL=SqlCellResultNameEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlCellResultNameEditor.d.ts","sourceRoot":"","sources":["../../src/components/SqlCellResultNameEditor.tsx"],"names":[],"mappings":"AAOA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,MAAM,MAAM,4BAA4B,GAAG;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,KAAK,CAAC,EAAE,CAC5C,4BAA4B,CAiE7B,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn, EditableText, Tooltip, TooltipContent, TooltipTrigger, } from '@sqlrooms/ui';
3
+ import { useCallback, useState } from 'react';
4
+ import { useValidateResultName } from '../useValidateResultName';
5
+ export const SqlCellResultNameEditor = ({ cellId, value, onChange }) => {
6
+ const [currentInput, setCurrentInput] = useState('');
7
+ const [isEditing, setIsEditing] = useState(false);
8
+ const currentValue = isEditing ? currentInput.trim() : value;
9
+ // Get validation function from hook
10
+ const errorMessage = useValidateResultName(cellId, currentValue);
11
+ const handleInputChange = useCallback((newValue) => {
12
+ setCurrentInput(newValue);
13
+ }, []);
14
+ // Called on Enter or Blur - only commit if valid
15
+ const handleChange = useCallback((newValue) => {
16
+ if (!errorMessage && newValue !== value) {
17
+ // Valid and changed - commit to store
18
+ onChange(newValue);
19
+ }
20
+ // If invalid or unchanged - don't commit, EditableText will reset to value prop
21
+ }, [errorMessage, value, onChange]);
22
+ // Called when editing starts/ends (including Escape)
23
+ const handleEditingChange = useCallback((editing) => {
24
+ setIsEditing(editing);
25
+ if (editing) {
26
+ // Track what user is typing for validation
27
+ setCurrentInput(value);
28
+ }
29
+ else {
30
+ // Clear tracked input
31
+ setCurrentInput('');
32
+ }
33
+ }, [value]);
34
+ return (_jsxs(Tooltip, { open: Boolean(errorMessage), children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("div", { children: _jsx(EditableText, { className: cn('h-6 w-40 font-mono text-xs text-green-500 shadow-none', {
35
+ 'border-red-500 ring-1 ring-red-500 focus:border-red-500 focus:ring-red-500': Boolean(errorMessage),
36
+ }), value: value, onInputChange: handleInputChange, onChange: handleChange, onEditingChange: handleEditingChange }) }) }), _jsx(TooltipContent, { side: "top", children: errorMessage && _jsx("span", { className: "text-xs", children: errorMessage }) })] }));
37
+ };
38
+ //# sourceMappingURL=SqlCellResultNameEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlCellResultNameEditor.js","sourceRoot":"","sources":["../../src/components/SqlCellResultNameEditor.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,EAAE,EACF,YAAY,EACZ,OAAO,EACP,cAAc,EACd,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAc,EAAC,WAAW,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAC;AACnD,OAAO,EAAC,qBAAqB,EAAC,MAAM,0BAA0B,CAAC;AAQ/D,MAAM,CAAC,MAAM,uBAAuB,GAEhC,CAAC,EAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAC,EAAE,EAAE;IAChC,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAS,EAAE,CAAC,CAAC;IAC7D,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAE3D,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IAE7D,oCAAoC;IACpC,MAAM,YAAY,GAAG,qBAAqB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAEjE,MAAM,iBAAiB,GAAG,WAAW,CAAC,CAAC,QAAgB,EAAE,EAAE;QACzD,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,iDAAiD;IACjD,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,QAAgB,EAAE,EAAE;QACnB,IAAI,CAAC,YAAY,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACxC,sCAAsC;YACtC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC;QACD,gFAAgF;IAClF,CAAC,EACD,CAAC,YAAY,EAAE,KAAK,EAAE,QAAQ,CAAC,CAChC,CAAC;IAEF,qDAAqD;IACrD,MAAM,mBAAmB,GAAG,WAAW,CACrC,CAAC,OAAgB,EAAE,EAAE;QACnB,YAAY,CAAC,OAAO,CAAC,CAAC;QACtB,IAAI,OAAO,EAAE,CAAC;YACZ,2CAA2C;YAC3C,eAAe,CAAC,KAAK,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,sBAAsB;YACtB,eAAe,CAAC,EAAE,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EACD,CAAC,KAAK,CAAC,CACR,CAAC;IAEF,OAAO,CACL,MAAC,OAAO,IAAC,IAAI,EAAE,OAAO,CAAC,YAAY,CAAC,aAClC,KAAC,cAAc,IAAC,OAAO,kBACrB,wBACE,KAAC,YAAY,IACX,SAAS,EAAE,EAAE,CACX,uDAAuD,EACvD;4BACE,4EAA4E,EAC1E,OAAO,CAAC,YAAY,CAAC;yBACxB,CACF,EACD,KAAK,EAAE,KAAK,EACZ,aAAa,EAAE,iBAAiB,EAChC,QAAQ,EAAE,YAAY,EACtB,eAAe,EAAE,mBAAmB,GACpC,GACE,GACS,EACjB,KAAC,cAAc,IAAC,IAAI,EAAC,KAAK,YACvB,YAAY,IAAI,eAAM,SAAS,EAAC,SAAS,YAAE,YAAY,GAAQ,GACjD,IACT,CACX,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {\n cn,\n EditableText,\n Tooltip,\n TooltipContent,\n TooltipTrigger,\n} from '@sqlrooms/ui';\nimport React, {useCallback, useState} from 'react';\nimport {useValidateResultName} from '../useValidateResultName';\n\nexport type SqlCellResultNameEditorProps = {\n cellId: string;\n value: string;\n onChange: (value: string) => void;\n};\n\nexport const SqlCellResultNameEditor: React.FC<\n SqlCellResultNameEditorProps\n> = ({cellId, value, onChange}) => {\n const [currentInput, setCurrentInput] = useState<string>('');\n const [isEditing, setIsEditing] = useState<boolean>(false);\n\n const currentValue = isEditing ? currentInput.trim() : value;\n\n // Get validation function from hook\n const errorMessage = useValidateResultName(cellId, currentValue);\n\n const handleInputChange = useCallback((newValue: string) => {\n setCurrentInput(newValue);\n }, []);\n\n // Called on Enter or Blur - only commit if valid\n const handleChange = useCallback(\n (newValue: string) => {\n if (!errorMessage && newValue !== value) {\n // Valid and changed - commit to store\n onChange(newValue);\n }\n // If invalid or unchanged - don't commit, EditableText will reset to value prop\n },\n [errorMessage, value, onChange],\n );\n\n // Called when editing starts/ends (including Escape)\n const handleEditingChange = useCallback(\n (editing: boolean) => {\n setIsEditing(editing);\n if (editing) {\n // Track what user is typing for validation\n setCurrentInput(value);\n } else {\n // Clear tracked input\n setCurrentInput('');\n }\n },\n [value],\n );\n\n return (\n <Tooltip open={Boolean(errorMessage)}>\n <TooltipTrigger asChild>\n <div>\n <EditableText\n className={cn(\n 'h-6 w-40 font-mono text-xs text-green-500 shadow-none',\n {\n 'border-red-500 ring-1 ring-red-500 focus:border-red-500 focus:ring-red-500':\n Boolean(errorMessage),\n },\n )}\n value={value}\n onInputChange={handleInputChange}\n onChange={handleChange}\n onEditingChange={handleEditingChange}\n />\n </div>\n </TooltipTrigger>\n <TooltipContent side=\"top\">\n {errorMessage && <span className=\"text-xs\">{errorMessage}</span>}\n </TooltipContent>\n </Tooltip>\n );\n};\n"]}
@@ -0,0 +1,17 @@
1
+ import type { PaginationState, SortingState } from '@tanstack/react-table';
2
+ import React from 'react';
3
+ import { CellResultData } from '../types';
4
+ export interface SqlCellResultsProps {
5
+ cellId: string;
6
+ cellResult?: CellResultData | undefined;
7
+ resultName?: string;
8
+ isRunning?: boolean;
9
+ errorMessage?: string;
10
+ resultVersion: number;
11
+ fetchCellResultPage: (cellId: string, pagination: PaginationState, sorting: SortingState) => void;
12
+ }
13
+ /**
14
+ * SQL query results display with collapse/expand functionality
15
+ */
16
+ export declare const SqlCellResults: React.FC<SqlCellResultsProps>;
17
+ //# sourceMappingURL=SqlCellResults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlCellResults.d.ts","sourceRoot":"","sources":["../../src/components/SqlCellResults.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,eAAe,EAAE,YAAY,EAAC,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAoC,MAAM,OAAO,CAAC;AAGzD,OAAO,EAAC,cAAc,EAAC,MAAM,UAAU,CAAC;AAExC,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,CACnB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,YAAY,KAClB,IAAI,CAAC;CACX;AAED;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAyGxD,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { DataTablePaginated, QueryDataTableActionsMenu, useArrowDataTable, } from '@sqlrooms/data-table';
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { cn } from '@sqlrooms/ui';
5
+ import { CollapsibleSectionButton } from './CollapsibleSectionButton';
6
+ /**
7
+ * SQL query results display with collapse/expand functionality
8
+ */
9
+ export const SqlCellResults = ({ cellId, cellResult, resultName, isRunning, errorMessage, resultVersion, fetchCellResultPage, }) => {
10
+ const [isCollapsed, setIsCollapsed] = useState(false);
11
+ const arrowTableData = useArrowDataTable(cellResult?.arrowTable);
12
+ // Pagination and sorting state for the result table
13
+ const [sorting, setSorting] = useState([]);
14
+ const [pagination, setPagination] = useState({
15
+ pageIndex: 0,
16
+ pageSize: 10,
17
+ });
18
+ // Fetch new page when pagination or sorting changes (not on initial render
19
+ // since executeSqlCell already fetches the first page)
20
+ const isFirstRender = useRef(true);
21
+ useEffect(() => {
22
+ if (isFirstRender.current) {
23
+ isFirstRender.current = false;
24
+ return;
25
+ }
26
+ if (resultName) {
27
+ fetchCellResultPage(cellId, pagination, sorting);
28
+ }
29
+ }, [pagination, sorting, cellId, fetchCellResultPage, resultName]);
30
+ // Reset pagination when a new result arrives (new run)
31
+ const prevResultVersion = useRef(resultVersion);
32
+ useEffect(() => {
33
+ if (resultVersion !== prevResultVersion.current) {
34
+ prevResultVersion.current = resultVersion;
35
+ setPagination((prev) => prev.pageIndex === 0 ? prev : { ...prev, pageIndex: 0 });
36
+ setSorting((prev) => (prev.length === 0 ? prev : []));
37
+ }
38
+ }, [resultVersion]);
39
+ if (!errorMessage && (!resultName || !arrowTableData)) {
40
+ return null;
41
+ }
42
+ return (_jsxs("div", { className: "group relative w-full", children: [_jsx(CollapsibleSectionButton, { isCollapsed: isCollapsed, onToggle: () => setIsCollapsed(!isCollapsed) }), isCollapsed ? (_jsx("div", { className: "ml-5 px-2 py-1 text-xs", children: errorMessage ? (_jsx("span", { className: "font-normal text-red-600", children: errorMessage })) : cellResult?.totalRows !== undefined ? (_jsxs("span", { className: "text-muted-foreground font-normal", children: [cellResult.totalRows.toLocaleString(), " rows"] })) : null })) : (_jsx(_Fragment, { children: errorMessage ? (_jsx("div", { className: "relative max-h-[400px] overflow-auto p-4", children: _jsx("span", { className: "font-mono text-xs whitespace-pre-wrap text-red-600", children: errorMessage }) })) : resultName && arrowTableData ? (_jsxs("div", { className: "relative min-h-[200px] overflow-hidden", children: [_jsx(DataTablePaginated, { className: "absolute inset-0 h-full w-full", fontSize: "text-xs", data: arrowTableData.data, columns: arrowTableData.columns, numRows: cellResult?.totalRows, pagination: pagination, onPaginationChange: setPagination, sorting: sorting, onSortingChange: setSorting, isFetching: isRunning, footerActions: _jsx(QueryDataTableActionsMenu, { query: `SELECT * FROM ${resultName}` }) }), _jsx("div", { "aria-hidden": !isRunning, className: cn('bg-background/45 pointer-events-none absolute inset-0 z-10 transition-opacity duration-200', isRunning ? 'animate-pulse opacity-100' : 'opacity-0') })] })) : null }))] }));
43
+ };
44
+ //# sourceMappingURL=SqlCellResults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlCellResults.js","sourceRoot":"","sources":["../../src/components/SqlCellResults.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n DataTablePaginated,\n QueryDataTableActionsMenu,\n useArrowDataTable,\n} from '@sqlrooms/data-table';\nimport type {PaginationState, SortingState} from '@tanstack/react-table';\nimport React, {useEffect, useRef, useState} from 'react';\nimport {cn} from '@sqlrooms/ui';\nimport {CollapsibleSectionButton} from './CollapsibleSectionButton';\nimport {CellResultData} from '../types';\n\nexport interface SqlCellResultsProps {\n cellId: string;\n cellResult?: CellResultData | undefined;\n resultName?: string;\n isRunning?: boolean;\n errorMessage?: string;\n resultVersion: number;\n fetchCellResultPage: (\n cellId: string,\n pagination: PaginationState,\n sorting: SortingState,\n ) => void;\n}\n\n/**\n * SQL query results display with collapse/expand functionality\n */\nexport const SqlCellResults: React.FC<SqlCellResultsProps> = ({\n cellId,\n cellResult,\n resultName,\n isRunning,\n errorMessage,\n resultVersion,\n fetchCellResultPage,\n}) => {\n const [isCollapsed, setIsCollapsed] = useState(false);\n const arrowTableData = useArrowDataTable(cellResult?.arrowTable);\n\n // Pagination and sorting state for the result table\n const [sorting, setSorting] = useState<SortingState>([]);\n const [pagination, setPagination] = useState<PaginationState>({\n pageIndex: 0,\n pageSize: 10,\n });\n\n // Fetch new page when pagination or sorting changes (not on initial render\n // since executeSqlCell already fetches the first page)\n const isFirstRender = useRef(true);\n\n useEffect(() => {\n if (isFirstRender.current) {\n isFirstRender.current = false;\n return;\n }\n if (resultName) {\n fetchCellResultPage(cellId, pagination, sorting);\n }\n }, [pagination, sorting, cellId, fetchCellResultPage, resultName]);\n\n // Reset pagination when a new result arrives (new run)\n const prevResultVersion = useRef(resultVersion);\n useEffect(() => {\n if (resultVersion !== prevResultVersion.current) {\n prevResultVersion.current = resultVersion;\n setPagination((prev) =>\n prev.pageIndex === 0 ? prev : {...prev, pageIndex: 0},\n );\n setSorting((prev) => (prev.length === 0 ? prev : []));\n }\n }, [resultVersion]);\n\n if (!errorMessage && (!resultName || !arrowTableData)) {\n return null;\n }\n\n return (\n <div className=\"group relative w-full\">\n <CollapsibleSectionButton\n isCollapsed={isCollapsed}\n onToggle={() => setIsCollapsed(!isCollapsed)}\n />\n {isCollapsed ? (\n <div className=\"ml-5 px-2 py-1 text-xs\">\n {errorMessage ? (\n <span className=\"font-normal text-red-600\">{errorMessage}</span>\n ) : cellResult?.totalRows !== undefined ? (\n <span className=\"text-muted-foreground font-normal\">\n {cellResult.totalRows.toLocaleString()} rows\n </span>\n ) : null}\n </div>\n ) : (\n <>\n {errorMessage ? (\n <div className=\"relative max-h-[400px] overflow-auto p-4\">\n <span className=\"font-mono text-xs whitespace-pre-wrap text-red-600\">\n {errorMessage}\n </span>\n </div>\n ) : resultName && arrowTableData ? (\n <div className=\"relative min-h-[200px] overflow-hidden\">\n <DataTablePaginated\n className=\"absolute inset-0 h-full w-full\"\n fontSize=\"text-xs\"\n data={arrowTableData.data}\n columns={arrowTableData.columns}\n numRows={cellResult?.totalRows}\n pagination={pagination}\n onPaginationChange={setPagination}\n sorting={sorting}\n onSortingChange={setSorting}\n isFetching={isRunning}\n footerActions={\n <QueryDataTableActionsMenu\n query={`SELECT * FROM ${resultName}`}\n />\n }\n />\n <div\n aria-hidden={!isRunning}\n className={cn(\n 'bg-background/45 pointer-events-none absolute inset-0 z-10 transition-opacity duration-200',\n isRunning ? 'animate-pulse opacity-100' : 'opacity-0',\n )}\n />\n </div>\n ) : null}\n </>\n )}\n </div>\n );\n};\n"]}
@@ -1,17 +1,16 @@
1
+ import { type RunButtonProps } from '@sqlrooms/ui';
1
2
  import { SqlCellRunStatus } from '../types';
2
- /**
3
- * Props for the standalone run/cancel control used by SQL cells.
4
- */
5
3
  type SqlCellRunButtonProps = {
6
4
  onRun: () => void;
7
5
  onCancel?: () => void;
8
6
  status?: SqlCellRunStatus;
9
7
  runLabel?: string;
10
8
  disabled?: boolean;
11
- };
9
+ } & Pick<RunButtonProps, 'variant' | 'cancelVariant' | 'className'>;
12
10
  /**
13
- * Renders the run/cancel control for a SQL cell and shows error feedback.
11
+ * Renders the run/cancel control for a SQL cell.
12
+ * Thin wrapper that maps {@link SqlCellRunStatus} to {@link RunQueryButton} state.
14
13
  */
15
- export declare function SqlCellRunButton({ onRun, onCancel, status, runLabel, disabled, }: SqlCellRunButtonProps): import("react/jsx-runtime").JSX.Element;
14
+ export declare function SqlCellRunButton({ onRun, onCancel, status, runLabel, disabled, ...rest }: SqlCellRunButtonProps): import("react/jsx-runtime").JSX.Element;
16
15
  export {};
17
16
  //# sourceMappingURL=SqlCellRunButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SqlCellRunButton.d.ts","sourceRoot":"","sources":["../../src/components/SqlCellRunButton.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAC,gBAAgB,EAAC,MAAM,UAAU,CAAC;AAE1C;;GAEG;AACH,KAAK,qBAAqB,GAAG;IAC3B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AACF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAgB,EAChB,QAAQ,GACT,EAAE,qBAAqB,2CA6BvB"}
1
+ {"version":3,"file":"SqlCellRunButton.d.ts","sourceRoot":"","sources":["../../src/components/SqlCellRunButton.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,cAAc,EAAC,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAC,gBAAgB,EAAC,MAAM,UAAU,CAAC;AAE1C,KAAK,qBAAqB,GAAG;IAC3B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GAAG,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,eAAe,GAAG,WAAW,CAAC,CAAC;AAEpE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,qBAAqB,2CAkBvB"}
@@ -1,11 +1,15 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Spinner } from '@sqlrooms/ui';
3
- import { Play } from 'lucide-react';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { RunButton } from '@sqlrooms/ui';
4
3
  /**
5
- * Renders the run/cancel control for a SQL cell and shows error feedback.
4
+ * Renders the run/cancel control for a SQL cell.
5
+ * Thin wrapper that maps {@link SqlCellRunStatus} to {@link RunQueryButton} state.
6
6
  */
7
- export function SqlCellRunButton({ onRun, onCancel, status, runLabel = 'Run', disabled, }) {
8
- const showCancel = status?.state === 'running' && Boolean(onCancel);
9
- return (_jsx("div", { className: "flex items-center gap-2", children: showCancel ? (_jsxs(Button, { size: "xs", variant: "destructive", className: "flex h-7 w-20 gap-1", onClick: onCancel, children: [_jsx(Spinner, {}), "Cancel"] })) : (_jsxs(Button, { size: "xs", variant: "secondary", className: "flex h-7 w-20 gap-1", onClick: onRun, disabled: disabled, children: [_jsx(Play, { className: "h-3 w-3" }), runLabel] })) }));
7
+ export function SqlCellRunButton({ onRun, onCancel, status, runLabel, disabled, ...rest }) {
8
+ const state = status?.state === 'running'
9
+ ? 'running'
10
+ : status?.state === 'cancel'
11
+ ? 'cancelling'
12
+ : 'idle';
13
+ return (_jsx(RunButton, { state: state, onRun: onRun, onCancel: onCancel, runLabel: runLabel, disabled: disabled, ...rest }));
10
14
  }
11
15
  //# sourceMappingURL=SqlCellRunButton.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SqlCellRunButton.js","sourceRoot":"","sources":["../../src/components/SqlCellRunButton.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,MAAM,EAAE,OAAO,EAAC,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAC,IAAI,EAAC,MAAM,cAAc,CAAC;AAalC;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,GAAG,KAAK,EAChB,QAAQ,GACc;IACtB,MAAM,UAAU,GAAG,MAAM,EAAE,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEpE,OAAO,CACL,cAAK,SAAS,EAAC,yBAAyB,YACrC,UAAU,CAAC,CAAC,CAAC,CACZ,MAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,aAAa,EACrB,SAAS,EAAC,qBAAqB,EAC/B,OAAO,EAAE,QAAQ,aAEjB,KAAC,OAAO,KAAG,cAEJ,CACV,CAAC,CAAC,CAAC,CACF,MAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,WAAW,EACnB,SAAS,EAAC,qBAAqB,EAC/B,OAAO,EAAE,KAAK,EACd,QAAQ,EAAE,QAAQ,aAElB,KAAC,IAAI,IAAC,SAAS,EAAC,SAAS,GAAG,EAC3B,QAAQ,IACF,CACV,GACG,CACP,CAAC;AACJ,CAAC","sourcesContent":["import {Button, Spinner} from '@sqlrooms/ui';\nimport {Play} from 'lucide-react';\nimport {SqlCellRunStatus} from '../types';\n\n/**\n * Props for the standalone run/cancel control used by SQL cells.\n */\ntype SqlCellRunButtonProps = {\n onRun: () => void;\n onCancel?: () => void;\n status?: SqlCellRunStatus;\n runLabel?: string;\n disabled?: boolean;\n};\n/**\n * Renders the run/cancel control for a SQL cell and shows error feedback.\n */\nexport function SqlCellRunButton({\n onRun,\n onCancel,\n status,\n runLabel = 'Run',\n disabled,\n}: SqlCellRunButtonProps) {\n const showCancel = status?.state === 'running' && Boolean(onCancel);\n\n return (\n <div className=\"flex items-center gap-2\">\n {showCancel ? (\n <Button\n size=\"xs\"\n variant=\"destructive\"\n className=\"flex h-7 w-20 gap-1\"\n onClick={onCancel}\n >\n <Spinner />\n Cancel\n </Button>\n ) : (\n <Button\n size=\"xs\"\n variant=\"secondary\"\n className=\"flex h-7 w-20 gap-1\"\n onClick={onRun}\n disabled={disabled}\n >\n <Play className=\"h-3 w-3\" />\n {runLabel}\n </Button>\n )}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"SqlCellRunButton.js","sourceRoot":"","sources":["../../src/components/SqlCellRunButton.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,SAAS,EAAsB,MAAM,cAAc,CAAC;AAW5D;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,GAAG,IAAI,EACe;IACtB,MAAM,KAAK,GACT,MAAM,EAAE,KAAK,KAAK,SAAS;QACzB,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,MAAM,EAAE,KAAK,KAAK,QAAQ;YAC1B,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,MAAM,CAAC;IAEf,OAAO,CACL,KAAC,SAAS,IACR,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,KACd,IAAI,GACR,CACH,CAAC;AACJ,CAAC","sourcesContent":["import {RunButton, type RunButtonProps} from '@sqlrooms/ui';\nimport {SqlCellRunStatus} from '../types';\n\ntype SqlCellRunButtonProps = {\n onRun: () => void;\n onCancel?: () => void;\n status?: SqlCellRunStatus;\n runLabel?: string;\n disabled?: boolean;\n} & Pick<RunButtonProps, 'variant' | 'cancelVariant' | 'className'>;\n\n/**\n * Renders the run/cancel control for a SQL cell.\n * Thin wrapper that maps {@link SqlCellRunStatus} to {@link RunQueryButton} state.\n */\nexport function SqlCellRunButton({\n onRun,\n onCancel,\n status,\n runLabel,\n disabled,\n ...rest\n}: SqlCellRunButtonProps) {\n const state =\n status?.state === 'running'\n ? 'running'\n : status?.state === 'cancel'\n ? 'cancelling'\n : 'idle';\n\n return (\n <RunButton\n state={state}\n onRun={onRun}\n onCancel={onCancel}\n runLabel={runLabel}\n disabled={disabled}\n {...rest}\n />\n );\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ export interface SqlQueryPreviewProps {
3
+ sql: string;
4
+ }
5
+ /**
6
+ * Shows a preview of SQL code - displays first non-empty line with "..." if more lines exist
7
+ */
8
+ export declare const SqlQueryPreview: React.FC<SqlQueryPreviewProps>;
9
+ //# sourceMappingURL=SqlQueryPreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlQueryPreview.d.ts","sourceRoot":"","sources":["../../src/components/SqlQueryPreview.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgB,MAAM,OAAO,CAAC;AAGrC,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAgB1D,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { getSqlQueryPreview } from '../utils';
4
+ /**
5
+ * Shows a preview of SQL code - displays first non-empty line with "..." if more lines exist
6
+ */
7
+ export const SqlQueryPreview = ({ sql }) => {
8
+ const { firstLine, hasMoreLines } = useMemo(() => getSqlQueryPreview(sql), [sql]);
9
+ if (!firstLine) {
10
+ return null;
11
+ }
12
+ return (_jsxs(_Fragment, { children: [_jsx("span", { className: "truncate font-mono", children: firstLine }), hasMoreLines && _jsx("span", { className: "ml-1", children: "..." })] }));
13
+ };
14
+ //# sourceMappingURL=SqlQueryPreview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SqlQueryPreview.js","sourceRoot":"","sources":["../../src/components/SqlQueryPreview.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAC,OAAO,EAAC,MAAM,OAAO,CAAC;AACrC,OAAO,EAAC,kBAAkB,EAAC,MAAM,UAAU,CAAC;AAM5C;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAmC,CAAC,EAAC,GAAG,EAAC,EAAE,EAAE;IACvE,MAAM,EAAC,SAAS,EAAE,YAAY,EAAC,GAAG,OAAO,CACvC,GAAG,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,EAC7B,CAAC,GAAG,CAAC,CACN,CAAC;IAEF,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CACL,8BACE,eAAM,SAAS,EAAC,oBAAoB,YAAE,SAAS,GAAQ,EACtD,YAAY,IAAI,eAAM,SAAS,EAAC,MAAM,oBAAW,IACjD,CACJ,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import React, {useMemo} from 'react';\nimport {getSqlQueryPreview} from '../utils';\n\nexport interface SqlQueryPreviewProps {\n sql: string;\n}\n\n/**\n * Shows a preview of SQL code - displays first non-empty line with \"...\" if more lines exist\n */\nexport const SqlQueryPreview: React.FC<SqlQueryPreviewProps> = ({sql}) => {\n const {firstLine, hasMoreLines} = useMemo(\n () => getSqlQueryPreview(sql),\n [sql],\n );\n\n if (!firstLine) {\n return null;\n }\n\n return (\n <>\n <span className=\"truncate font-mono\">{firstLine}</span>\n {hasMoreLines && <span className=\"ml-1\">...</span>}\n </>\n );\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"TextCellContent.d.ts","sourceRoot":"","sources":["../../src/components/TextCellContent.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAKnD,OAAO,KAAK,EAAC,kBAAkB,EAAE,QAAQ,EAAC,MAAM,UAAU,CAAC;AAG3D,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,CAgE1D,CAAC"}
1
+ {"version":3,"file":"TextCellContent.d.ts","sourceRoot":"","sources":["../../src/components/TextCellContent.tsx"],"names":[],"mappings":"AACA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAKtE,OAAO,KAAK,EAAC,kBAAkB,EAAE,QAAQ,EAAC,MAAM,UAAU,CAAC;AAE3D,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,CAwE1D,CAAC"}
@@ -1,26 +1,43 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useCallback } from 'react';
2
+ import { produce } from 'immer';
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
4
  import Markdown from 'react-markdown';
5
+ import remarkBreaks from 'remark-breaks';
4
6
  import remarkGfm from 'remark-gfm';
5
- import { Button } from '@sqlrooms/ui';
6
7
  import { useCellsStore } from '../hooks';
7
- import { produce } from 'immer';
8
8
  export const TextCellContent = ({ id, cell, renderContainer, }) => {
9
9
  const updateCell = useCellsStore((s) => s.cells.updateCell);
10
10
  const [isEditing, setIsEditing] = useState(false);
11
11
  const [draftText, setDraftText] = useState(cell.data.text);
12
+ const textareaRef = useRef(null);
12
13
  const saveText = useCallback(() => {
13
- updateCell(id, (c) => produce(c, (draft) => {
14
+ updateCell(id, (cell) => produce(cell, (draft) => {
14
15
  if (draft.type === 'text') {
15
16
  draft.data.text = draftText;
16
17
  }
17
18
  }));
18
19
  setIsEditing(false);
19
20
  }, [id, draftText, updateCell]);
20
- const header = (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { size: "xs", variant: "secondary", className: "h-6", onClick: () => (isEditing ? saveText() : setIsEditing(true)), children: isEditing ? 'Save' : 'Edit text' }), _jsx("span", { className: "text-[10px] font-bold uppercase text-gray-400", children: "Text" })] }));
21
- const content = (_jsxs("div", { className: "flex min-h-[100px] flex-col divide-y dark:divide-gray-800", onDoubleClick: () => setIsEditing((prev) => !prev), children: [isEditing && (_jsx("textarea", { className: "bg-accent min-h-[100px] w-full p-3 text-xs outline-none", value: draftText, onChange: (e) => setDraftText(e.target.value), placeholder: "Write text... (Markdown supported)", autoFocus: true })), !isEditing && (_jsx("div", { className: "prose dark:prose-invert max-w-none flex-1 p-3 text-sm", children: _jsx(Markdown, { remarkPlugins: [remarkGfm], children: cell.data.text || '_No content yet. Double click to edit._' }) }))] }));
21
+ const cancelEdit = useCallback(() => {
22
+ setDraftText(cell.data.text);
23
+ setIsEditing(false);
24
+ }, [cell.data.text]);
25
+ // Auto-resize textarea
26
+ useEffect(() => {
27
+ const textarea = textareaRef.current;
28
+ if (textarea && isEditing) {
29
+ textarea.style.height = 'auto';
30
+ textarea.style.height = `${textarea.scrollHeight}px`;
31
+ }
32
+ }, [draftText, isEditing]);
33
+ const content = (_jsxs("div", { className: "flex flex-col divide-y dark:divide-gray-800", onDoubleClick: () => !isEditing && setIsEditing(true), children: [isEditing && (_jsx("textarea", { ref: textareaRef, className: "bg-accent w-full resize-none overflow-hidden p-3 text-sm leading-relaxed outline-none", value: draftText, onChange: (e) => setDraftText(e.target.value), placeholder: "Write text... (Markdown supported)", autoFocus: true, onBlur: saveText, rows: 1, onKeyDown: (e) => {
34
+ if (e.key === 'Escape') {
35
+ e.preventDefault();
36
+ cancelEdit();
37
+ }
38
+ } })), !isEditing && (_jsx("div", { className: "prose dark:prose-invert max-w-none flex-1 p-3 text-sm", children: _jsx(Markdown, { remarkPlugins: [remarkGfm, remarkBreaks], children: cell.data.text || '_No content yet. Double click to edit._' }) }))] }));
22
39
  return renderContainer({
23
- header,
40
+ showHeader: false,
24
41
  content,
25
42
  });
26
43
  };
@@ -1 +1 @@
1
- {"version":3,"file":"TextCellContent.js","sourceRoot":"","sources":["../../src/components/TextCellContent.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAC,QAAQ,EAAE,WAAW,EAAC,MAAM,OAAO,CAAC;AACnD,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,EAAC,MAAM,EAAK,MAAM,cAAc,CAAC;AACxC,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAC;AAEvC,OAAO,EAAC,OAAO,EAAC,MAAM,OAAO,CAAC;AAQ9B,MAAM,CAAC,MAAM,eAAe,GAAmC,CAAC,EAC9D,EAAE,EACF,IAAI,EACJ,eAAe,GAChB,EAAE,EAAE;IACH,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5D,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE3D,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;QAChC,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CACnB,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;YACnB,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC1B,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;YAC9B,CAAC;QACH,CAAC,CAAC,CACH,CAAC;QACF,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IAEhC,MAAM,MAAM,GAAG,CACb,eAAK,SAAS,EAAC,yBAAyB,aACtC,KAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,WAAW,EACnB,SAAS,EAAC,KAAK,EACf,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,YAE3D,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,GAC1B,EACT,eAAM,SAAS,EAAC,+CAA+C,qBAExD,IACH,CACP,CAAC;IAEF,MAAM,OAAO,GAAG,CACd,eACE,SAAS,EAAC,2DAA2D,EACrE,aAAa,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,aAEjD,SAAS,IAAI,CACZ,mBACE,SAAS,EAAC,yDAAyD,EACnE,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAC7C,WAAW,EAAC,oCAAoC,EAChD,SAAS,SACT,CACH,EACA,CAAC,SAAS,IAAI,CACb,cAAK,SAAS,EAAC,uDAAuD,YACpE,KAAC,QAAQ,IAAC,aAAa,EAAE,CAAC,SAAS,CAAC,YACjC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,yCAAyC,GACnD,GACP,CACP,IACG,CACP,CAAC;IAEF,OAAO,eAAe,CAAC;QACrB,MAAM;QACN,OAAO;KACR,CAAC,CAAC;AACL,CAAC,CAAC","sourcesContent":["import React, {useState, useCallback} from 'react';\nimport Markdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport {Button, cn} from '@sqlrooms/ui';\nimport {useCellsStore} from '../hooks';\nimport type {CellContainerProps, TextCell} from '../types';\nimport {produce} from 'immer';\n\nexport type TextCellContentProps = {\n id: string;\n cell: TextCell;\n renderContainer: (props: CellContainerProps) => React.ReactElement;\n};\n\nexport const TextCellContent: React.FC<TextCellContentProps> = ({\n id,\n cell,\n renderContainer,\n}) => {\n const updateCell = useCellsStore((s) => s.cells.updateCell);\n const [isEditing, setIsEditing] = useState(false);\n const [draftText, setDraftText] = useState(cell.data.text);\n\n const saveText = useCallback(() => {\n updateCell(id, (c) =>\n produce(c, (draft) => {\n if (draft.type === 'text') {\n draft.data.text = draftText;\n }\n }),\n );\n setIsEditing(false);\n }, [id, draftText, updateCell]);\n\n const header = (\n <div className=\"flex items-center gap-2\">\n <Button\n size=\"xs\"\n variant=\"secondary\"\n className=\"h-6\"\n onClick={() => (isEditing ? saveText() : setIsEditing(true))}\n >\n {isEditing ? 'Save' : 'Edit text'}\n </Button>\n <span className=\"text-[10px] font-bold uppercase text-gray-400\">\n Text\n </span>\n </div>\n );\n\n const content = (\n <div\n className=\"flex min-h-[100px] flex-col divide-y dark:divide-gray-800\"\n onDoubleClick={() => setIsEditing((prev) => !prev)}\n >\n {isEditing && (\n <textarea\n className=\"bg-accent min-h-[100px] w-full p-3 text-xs outline-none\"\n value={draftText}\n onChange={(e) => setDraftText(e.target.value)}\n placeholder=\"Write text... (Markdown supported)\"\n autoFocus\n />\n )}\n {!isEditing && (\n <div className=\"prose dark:prose-invert max-w-none flex-1 p-3 text-sm\">\n <Markdown remarkPlugins={[remarkGfm]}>\n {cell.data.text || '_No content yet. Double click to edit._'}\n </Markdown>\n </div>\n )}\n </div>\n );\n\n return renderContainer({\n header,\n content,\n });\n};\n"]}
1
+ {"version":3,"file":"TextCellContent.js","sourceRoot":"","sources":["../../src/components/TextCellContent.tsx"],"names":[],"mappings":"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","sourcesContent":["import {produce} from 'immer';\nimport React, {useCallback, useEffect, useRef, useState} from 'react';\nimport Markdown from 'react-markdown';\nimport remarkBreaks from 'remark-breaks';\nimport remarkGfm from 'remark-gfm';\nimport {useCellsStore} from '../hooks';\nimport type {CellContainerProps, TextCell} from '../types';\n\nexport type TextCellContentProps = {\n id: string;\n cell: TextCell;\n renderContainer: (props: CellContainerProps) => React.ReactElement;\n};\n\nexport const TextCellContent: React.FC<TextCellContentProps> = ({\n id,\n cell,\n renderContainer,\n}) => {\n const updateCell = useCellsStore((s) => s.cells.updateCell);\n const [isEditing, setIsEditing] = useState(false);\n const [draftText, setDraftText] = useState(cell.data.text);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const saveText = useCallback(() => {\n updateCell(id, (cell) =>\n produce(cell, (draft) => {\n if (draft.type === 'text') {\n draft.data.text = draftText;\n }\n }),\n );\n setIsEditing(false);\n }, [id, draftText, updateCell]);\n\n const cancelEdit = useCallback(() => {\n setDraftText(cell.data.text);\n setIsEditing(false);\n }, [cell.data.text]);\n\n // Auto-resize textarea\n useEffect(() => {\n const textarea = textareaRef.current;\n if (textarea && isEditing) {\n textarea.style.height = 'auto';\n textarea.style.height = `${textarea.scrollHeight}px`;\n }\n }, [draftText, isEditing]);\n\n const content = (\n <div\n className=\"flex flex-col divide-y dark:divide-gray-800\"\n onDoubleClick={() => !isEditing && setIsEditing(true)}\n >\n {isEditing && (\n <textarea\n ref={textareaRef}\n className=\"bg-accent w-full resize-none overflow-hidden p-3 text-sm leading-relaxed outline-none\"\n value={draftText}\n onChange={(e) => setDraftText(e.target.value)}\n placeholder=\"Write text... (Markdown supported)\"\n autoFocus\n onBlur={saveText}\n rows={1}\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n cancelEdit();\n }\n }}\n />\n )}\n {!isEditing && (\n <div className=\"prose dark:prose-invert max-w-none flex-1 p-3 text-sm\">\n <Markdown remarkPlugins={[remarkGfm, remarkBreaks]}>\n {cell.data.text || '_No content yet. Double click to edit._'}\n </Markdown>\n </div>\n )}\n </div>\n );\n\n return renderContainer({\n showHeader: false,\n content,\n });\n};\n"]}
@@ -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;AAY3D,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,CAyF1D,CAAC"}
@@ -1,63 +1,42 @@
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 { useState } from 'react';
6
6
  import { useCellsStore } from '../hooks';
7
+ import { useVegaCellQuery } from '../hooks/useVegaCellQuery';
8
+ import { useVegaCellDataSource } from '../hooks/useVegaCellDataSource';
7
9
  import { VegaConfigPanel } from './VegaConfigPanel';
10
+ import { VegaCellHeader } from './VegaCellHeader';
11
+ import { toDataSourceCell, toDataSourceTable } from '../helpers';
12
+ const DEFAULT_ASPECT_RATIO = 3 / 2;
13
+ const DEFAULT_SPEC = {
14
+ data: { name: 'queryResult' },
15
+ mark: 'bar',
16
+ padding: 20,
17
+ };
8
18
  export const VegaCellContent = ({ id, cell, renderContainer, }) => {
9
19
  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
- });
31
20
  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) => {
41
- updateCell(id, (c) => produce(c, (draft) => {
42
- if (draft.type === 'vega')
43
- draft.data.sqlId = value;
44
- }));
45
- };
46
- const saveSpec = useCallback(() => {
21
+ // Local state with debounced persistence
22
+ const [localSpec, handleSpecChange] = useDebouncedValue(cell.data.vegaSpec ?? DEFAULT_SPEC, (spec) => {
47
23
  updateCell(id, (c) => produce(c, (draft) => {
48
24
  if (draft.type === 'vega')
49
- draft.data.vegaSpec = draftSpec;
25
+ draft.data.vegaSpec = spec;
50
26
  }));
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 })) })] }));
27
+ }, 300);
28
+ const { selectedSqlId, selectedTableRef, selectedSqlStatus, baseSqlQuery, lastRunTime, hasDataSource, } = useVegaCellDataSource(cell);
29
+ const { selectedSqlQuery } = useVegaCellQuery({
30
+ baseSqlQuery,
31
+ });
32
+ // Encode selection value: "cell:<id>" or "table:<schema.table>"
33
+ const selectValue = selectedSqlId
34
+ ? toDataSourceCell(selectedSqlId)
35
+ : selectedTableRef
36
+ ? toDataSourceTable(selectedTableRef)
37
+ : undefined;
38
+ const header = (_jsx(VegaCellHeader, { cellId: id, selectValue: selectValue, onEditToggle: () => setIsEditing((prev) => !prev), hasDataSource: hasDataSource }));
39
+ const content = (_jsxs("div", { className: "flex h-[400px]", children: [isEditing && (_jsx(VegaConfigPanel, { sqlQuery: baseSqlQuery, lastRunTime: lastRunTime, spec: localSpec, onSpecChange: handleSpecChange })), _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 })) })] }));
61
40
  return renderContainer({
62
41
  header,
63
42
  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 {useState} from 'react';\nimport {useCellsStore} from '../hooks';\nimport {useVegaCellQuery} from '../hooks/useVegaCellQuery';\nimport {useVegaCellDataSource} from '../hooks/useVegaCellDataSource';\nimport type {CellContainerProps, VegaCell} from '../types';\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 [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} = useVegaCellQuery({\n baseSqlQuery,\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 header = (\n <VegaCellHeader\n cellId={id}\n selectValue={selectValue}\n onEditToggle={() => setIsEditing((prev) => !prev)}\n hasDataSource={hasDataSource}\n />\n );\n\n const content = (\n <div className=\"flex h-[400px]\">\n {isEditing && (\n <VegaConfigPanel\n sqlQuery={baseSqlQuery}\n lastRunTime={lastRunTime}\n spec={localSpec}\n onSpecChange={handleSpecChange}\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 )}\n </div>\n </div>\n );\n\n return renderContainer({\n header,\n content,\n });\n};\n"]}
@@ -0,0 +1,8 @@
1
+ export type VegaCellHeaderProps = {
2
+ cellId: string;
3
+ selectValue: string | undefined;
4
+ onEditToggle: () => void;
5
+ hasDataSource: boolean;
6
+ };
7
+ export declare const VegaCellHeader: React.FC<VegaCellHeaderProps>;
8
+ //# 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;CACxB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAgDxD,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '@sqlrooms/ui';
3
+ import { produce } from 'immer';
4
+ import { 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, }) => {
9
+ const updateCell = useCellsStore((s) => s.cells.updateCell);
10
+ const artifactId = useCellsStore((s) => s.cells.getArtifactIdForCell(cellId));
11
+ const handleDataSourceChange = (value) => {
12
+ updateCell(cellId, (c) => produce(c, (draft) => {
13
+ if (draft.type !== 'vega') {
14
+ return;
15
+ }
16
+ const cellIdFromValue = fromDataSourceCell(value);
17
+ const tableRef = fromDataSourceTable(value);
18
+ if (cellIdFromValue) {
19
+ draft.data.sqlId = cellIdFromValue;
20
+ delete draft.data.tableRef;
21
+ }
22
+ else if (tableRef) {
23
+ draft.data.tableRef = tableRef;
24
+ delete draft.data.sqlId;
25
+ }
26
+ }));
27
+ };
28
+ return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CellSourceSelector, { artifactId: artifactId ?? '', 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" }) })] }));
29
+ };
30
+ //# sourceMappingURL=VegaCellHeader.js.map