@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.
- package/dist/cellsSlice.d.ts.map +1 -1
- package/dist/cellsSlice.js +191 -73
- package/dist/cellsSlice.js.map +1 -1
- package/dist/components/AggregationSelector.d.ts +8 -0
- package/dist/components/AggregationSelector.d.ts.map +1 -0
- package/dist/components/AggregationSelector.js +13 -0
- package/dist/components/AggregationSelector.js.map +1 -0
- package/dist/components/CellSourceSelector.d.ts +11 -0
- package/dist/components/CellSourceSelector.d.ts.map +1 -0
- package/dist/components/CellSourceSelector.js +35 -0
- package/dist/components/CellSourceSelector.js.map +1 -0
- package/dist/components/ChartTypeSelector.d.ts +8 -0
- package/dist/components/ChartTypeSelector.d.ts.map +1 -0
- package/dist/components/ChartTypeSelector.js +13 -0
- package/dist/components/ChartTypeSelector.js.map +1 -0
- package/dist/components/CollapsibleSectionButton.d.ts +10 -0
- package/dist/components/CollapsibleSectionButton.d.ts.map +1 -0
- package/dist/components/CollapsibleSectionButton.js +10 -0
- package/dist/components/CollapsibleSectionButton.js.map +1 -0
- package/dist/components/ColorSelector.d.ts +24 -0
- package/dist/components/ColorSelector.d.ts.map +1 -0
- package/dist/components/ColorSelector.js +17 -0
- package/dist/components/ColorSelector.js.map +1 -0
- package/dist/components/FieldSelector.d.ts +11 -0
- package/dist/components/FieldSelector.d.ts.map +1 -0
- package/dist/components/FieldSelector.js +11 -0
- package/dist/components/FieldSelector.js.map +1 -0
- package/dist/components/FieldTypeIcon.d.ts +8 -0
- package/dist/components/FieldTypeIcon.d.ts.map +1 -0
- package/dist/components/FieldTypeIcon.js +27 -0
- package/dist/components/FieldTypeIcon.js.map +1 -0
- package/dist/components/InputCellContent.d.ts.map +1 -1
- package/dist/components/InputCellContent.js +3 -3
- package/dist/components/InputCellContent.js.map +1 -1
- package/dist/components/SelectionListener.d.ts +15 -0
- package/dist/components/SelectionListener.d.ts.map +1 -0
- package/dist/components/SelectionListener.js +74 -0
- package/dist/components/SelectionListener.js.map +1 -0
- package/dist/components/SheetsTabBar.d.ts.map +1 -1
- package/dist/components/SheetsTabBar.js +2 -1
- package/dist/components/SheetsTabBar.js.map +1 -1
- package/dist/components/SqlCellBody.d.ts +6 -0
- package/dist/components/SqlCellBody.d.ts.map +1 -0
- package/dist/components/SqlCellBody.js +12 -0
- package/dist/components/SqlCellBody.js.map +1 -0
- package/dist/components/SqlCellConnectionSelector.d.ts +9 -0
- package/dist/components/SqlCellConnectionSelector.d.ts.map +1 -0
- package/dist/components/SqlCellConnectionSelector.js +21 -0
- package/dist/components/SqlCellConnectionSelector.js.map +1 -0
- package/dist/components/SqlCellContent.d.ts.map +1 -1
- package/dist/components/SqlCellContent.js +29 -154
- package/dist/components/SqlCellContent.js.map +1 -1
- package/dist/components/SqlCellDependentsMenu.d.ts +11 -0
- package/dist/components/SqlCellDependentsMenu.d.ts.map +1 -0
- package/dist/components/SqlCellDependentsMenu.js +62 -0
- package/dist/components/SqlCellDependentsMenu.js.map +1 -0
- package/dist/components/SqlCellEditor.d.ts +14 -0
- package/dist/components/SqlCellEditor.d.ts.map +1 -0
- package/dist/components/SqlCellEditor.js +17 -0
- package/dist/components/SqlCellEditor.js.map +1 -0
- package/dist/components/SqlCellResultNameEditor.d.ts +8 -0
- package/dist/components/SqlCellResultNameEditor.d.ts.map +1 -0
- package/dist/components/SqlCellResultNameEditor.js +38 -0
- package/dist/components/SqlCellResultNameEditor.js.map +1 -0
- package/dist/components/SqlCellResults.d.ts +17 -0
- package/dist/components/SqlCellResults.d.ts.map +1 -0
- package/dist/components/SqlCellResults.js +45 -0
- package/dist/components/SqlCellResults.js.map +1 -0
- package/dist/components/SqlCellRunButton.d.ts +5 -6
- package/dist/components/SqlCellRunButton.d.ts.map +1 -1
- package/dist/components/SqlCellRunButton.js +11 -7
- package/dist/components/SqlCellRunButton.js.map +1 -1
- package/dist/components/SqlQueryPreview.d.ts +9 -0
- package/dist/components/SqlQueryPreview.d.ts.map +1 -0
- package/dist/components/SqlQueryPreview.js +14 -0
- package/dist/components/SqlQueryPreview.js.map +1 -0
- package/dist/components/TextCellContent.d.ts.map +1 -1
- package/dist/components/TextCellContent.js +24 -7
- package/dist/components/TextCellContent.js.map +1 -1
- package/dist/components/VegaCellContent.d.ts +0 -1
- package/dist/components/VegaCellContent.d.ts.map +1 -1
- package/dist/components/VegaCellContent.js +61 -47
- package/dist/components/VegaCellContent.js.map +1 -1
- package/dist/components/VegaCellHeader.d.ts +11 -0
- package/dist/components/VegaCellHeader.d.ts.map +1 -0
- package/dist/components/VegaCellHeader.js +31 -0
- package/dist/components/VegaCellHeader.js.map +1 -0
- package/dist/components/VegaConfigPanel.d.ts +5 -0
- package/dist/components/VegaConfigPanel.d.ts.map +1 -1
- package/dist/components/VegaConfigPanel.js +53 -63
- package/dist/components/VegaConfigPanel.js.map +1 -1
- package/dist/crdt.d.ts +61 -0
- package/dist/crdt.d.ts.map +1 -0
- package/dist/crdt.js +82 -0
- package/dist/crdt.js.map +1 -0
- package/dist/dagSlice.d.ts +3 -0
- package/dist/dagSlice.d.ts.map +1 -0
- package/dist/dagSlice.js +51 -0
- package/dist/dagSlice.js.map +1 -0
- package/dist/dagUtils.d.ts +2 -1
- package/dist/dagUtils.d.ts.map +1 -1
- package/dist/dagUtils.js +35 -11
- package/dist/dagUtils.js.map +1 -1
- package/dist/defaultCellRegistry.d.ts.map +1 -1
- package/dist/defaultCellRegistry.js +63 -24
- package/dist/defaultCellRegistry.js.map +1 -1
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +2 -1
- package/dist/execution.js.map +1 -1
- package/dist/helpers.d.ts +14 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +56 -2
- package/dist/helpers.js.map +1 -1
- package/dist/hooks/useVegaCellDataSource.d.ts +18 -0
- package/dist/hooks/useVegaCellDataSource.d.ts.map +1 -0
- package/dist/hooks/useVegaCellDataSource.js +40 -0
- package/dist/hooks/useVegaCellDataSource.js.map +1 -0
- package/dist/hooks/useVegaCellQuery.d.ts +16 -0
- package/dist/hooks/useVegaCellQuery.d.ts.map +1 -0
- package/dist/hooks/useVegaCellQuery.js +25 -0
- package/dist/hooks/useVegaCellQuery.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/sqlHelpers.d.ts +3 -3
- package/dist/sqlHelpers.d.ts.map +1 -1
- package/dist/sqlHelpers.js +9 -5
- package/dist/sqlHelpers.js.map +1 -1
- package/dist/typeGuards.d.ts +2 -0
- package/dist/typeGuards.d.ts.map +1 -0
- package/dist/typeGuards.js +4 -0
- package/dist/typeGuards.js.map +1 -0
- package/dist/types.d.ts +97 -26
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +17 -2
- package/dist/types.js.map +1 -1
- package/dist/useValidateResultName.d.ts +34 -0
- package/dist/useValidateResultName.d.ts.map +1 -0
- package/dist/useValidateResultName.js +111 -0
- package/dist/useValidateResultName.js.map +1 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +16 -0
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +16 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +40 -0
- package/dist/validation.js.map +1 -0
- package/dist/vegaSelectionUtils.d.ts +14 -0
- package/dist/vegaSelectionUtils.d.ts.map +1 -0
- package/dist/vegaSelectionUtils.js +97 -0
- package/dist/vegaSelectionUtils.js.map +1 -0
- package/dist/vegaSpecBuilder.d.ts +34 -0
- package/dist/vegaSpecBuilder.d.ts.map +1 -0
- package/dist/vegaSpecBuilder.js +90 -0
- package/dist/vegaSpecBuilder.js.map +1 -0
- package/package.json +12 -13
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useValidateResultName.js","sourceRoot":"","sources":["../src/useValidateResultName.ts"],"names":[],"mappings":"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","sourcesContent":["import {convertToValidColumnOrTableName} from '@sqlrooms/utils';\nimport {useMemo} from 'react';\nimport {useCellsStore} from './hooks';\nimport type {Cell, SqlCellData} from './types';\nimport {getEffectiveResultName, isValidSqlIdentifier} from './utils';\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Strips single-quoted SQL string literals from text so that identifier\n * checks don't produce false positives on quoted string content.\n * Handles escaped single-quotes inside literals (e.g. 'it''s fine').\n */\nfunction stripStringLiterals(sql: string): string {\n return sql.replace(/'(?:[^']|'')*'/g, \"''\");\n}\n\nfunction containsIdentifier(text: string, name: string): boolean {\n // The lookbehind includes '.' so that qualified names like `schema.my_result`\n // are NOT matched — we only want to detect unqualified references.\n // The lookahead intentionally omits '.' so that column-access expressions like\n // `my_result.column` ARE matched (they reference the `my_result` table).\n const pattern = new RegExp(\n `(?<![a-zA-Z0-9_.])${escapeRegExp(name)}(?![a-zA-Z0-9_])`,\n 'i',\n );\n return pattern.test(stripStringLiterals(text));\n}\n\n/**\n * Validates whether a proposed result name is available for a SQL cell.\n *\n * Returns a human-readable error message if the name is taken or would cause\n * problems, or `null` if the name is acceptable.\n *\n * Checks performed (in order):\n * 1. Basic validation (empty, valid SQL identifier)\n * 2. Not already used by another SQL cell in the same sheet (name collision).\n * 3. Not conflicting with a main-schema table name (would shadow real data).\n * 4. Not creating a self-reference cycle (SQL references the proposed name).\n */\nexport function validateResultName(opts: {\n proposedName: string;\n currentCellId: string;\n currentCellSql: string;\n sheetCellIds: string[];\n cells: Record<string, Cell>;\n mainSchemaTableNames: ReadonlyArray<string>;\n convertToValidName: (name: string) => string;\n}): string | null {\n const {\n proposedName,\n currentCellId,\n currentCellSql,\n sheetCellIds,\n cells,\n mainSchemaTableNames,\n convertToValidName,\n } = opts;\n\n const trimmedName = proposedName.trim();\n\n // 1. Basic validation first\n if (!trimmedName) {\n return 'Cannot be empty';\n }\n\n if (!isValidSqlIdentifier(trimmedName)) {\n return 'Must be a valid SQL identifier';\n }\n\n const nameLower = trimmedName.toLowerCase();\n\n // 2. Check for name collision with other SQL cells in the same sheet\n for (const cellId of sheetCellIds) {\n if (cellId === currentCellId) {\n continue;\n }\n\n const cell = cells[cellId];\n if (!cell || cell.type !== 'sql') {\n continue;\n }\n\n const otherName = getEffectiveResultName(\n cell.data as SqlCellData,\n convertToValidName,\n );\n\n if (otherName.toLowerCase() === nameLower) {\n return 'Already used by another cell';\n }\n }\n\n // 3. Check for conflict with main-schema table names\n if (mainSchemaTableNames.some((t) => t.toLowerCase() === nameLower)) {\n return 'Conflicts with existing table';\n }\n\n // 4. Check for self-reference cycle: the cell's SQL references the proposed name\n if (containsIdentifier(currentCellSql, trimmedName)) {\n return 'Name creates a dependency cycle';\n }\n\n return null;\n}\n\n/**\n * Hook to validate SQL cell result names.\n * Returns an error message if invalid, or null if valid.\n *\n * Performs all validation checks via validateResultName:\n * 1. Basic validation (empty, valid SQL identifier)\n * 2. Name collision with other cells\n * 3. Conflict with main-schema tables\n * 4. Self-reference cycle detection\n */\nexport function useValidateResultName(\n cellId: string,\n name: string,\n): string | null {\n const currentSheetId = useCellsStore((s) => s.cells.config.currentSheetId);\n const cellsData = useCellsStore((s) => s.cells.config.data);\n const sheets = useCellsStore((s) => s.cells.config.sheets);\n const tableSchemas = useCellsStore((s) => s.db.tables);\n\n const currentCell = cellsData[cellId];\n const currentCellSql =\n currentCell?.type === 'sql' ? currentCell.data.sql : '';\n\n const sheetCellIds = useMemo(\n () => (currentSheetId ? (sheets[currentSheetId]?.cellIds ?? []) : []),\n [currentSheetId, sheets],\n );\n\n const mainSchemaTableNames = useMemo(\n () =>\n tableSchemas\n .filter((t) => (t.table.schema ?? t.schema) === 'main')\n .map((t) => t.table.table ?? t.tableName),\n [tableSchemas],\n );\n\n return useMemo(() => {\n return validateResultName({\n proposedName: name,\n currentCellId: cellId,\n currentCellSql,\n sheetCellIds,\n cells: cellsData,\n mainSchemaTableNames,\n convertToValidName: convertToValidColumnOrTableName,\n });\n }, [\n name,\n cellId,\n currentCellSql,\n sheetCellIds,\n cellsData,\n mainSchemaTableNames,\n ]);\n}\n"]}
|
package/dist/utils.d.ts
CHANGED
|
@@ -14,4 +14,12 @@ export declare function getEffectiveResultName(data: SqlCellData, convertToValid
|
|
|
14
14
|
* Generates a stable SQL schema name for a sheet id.
|
|
15
15
|
*/
|
|
16
16
|
export declare function getSheetSchemaName(sheetId: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Extracts a preview from SQL query - first non-empty line and whether there are more lines.
|
|
19
|
+
* Returns empty firstLine if the SQL is empty or contains only whitespace.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getSqlQueryPreview(sql: string): {
|
|
22
|
+
firstLine: string;
|
|
23
|
+
hasMoreLines: boolean;
|
|
24
|
+
};
|
|
17
25
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AAEzC,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,GAAG,KAAK,IAAI,CAAC,CAE7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAC3C,MAAM,CAeR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG1D"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AAEzC,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,GAAG,KAAK,IAAI,CAAC,CAE7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,WAAW,EACjB,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAC3C,MAAM,CAeR;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB,CAaA"}
|
package/dist/utils.js
CHANGED
|
@@ -32,4 +32,20 @@ export function getSheetSchemaName(sheetId) {
|
|
|
32
32
|
const normalized = sheetId.replace(/[^a-zA-Z0-9_]/g, '_').toLowerCase();
|
|
33
33
|
return `sheet_${normalized}`;
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Extracts a preview from SQL query - first non-empty line and whether there are more lines.
|
|
37
|
+
* Returns empty firstLine if the SQL is empty or contains only whitespace.
|
|
38
|
+
*/
|
|
39
|
+
export function getSqlQueryPreview(sql) {
|
|
40
|
+
const [firstLine, ...rest] = sql
|
|
41
|
+
.split('\n')
|
|
42
|
+
.filter((line) => line.trim() !== '');
|
|
43
|
+
if (!firstLine) {
|
|
44
|
+
return { firstLine: '', hasMoreLines: false };
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
firstLine,
|
|
48
|
+
hasMoreLines: rest.length > 0,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
35
51
|
//# sourceMappingURL=utils.js.map
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAI,KAAoB;IAC/C,OAAO,KAAK,KAAK,SAAS,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAiB,EACjB,kBAA4C;IAE5C,IAAI,IAAI,CAAC,UAAU,IAAI,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,MAAM,eAAe,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAErE,6DAA6D;IAC7D,sDAAsD;IACtD,MAAM,eAAe,GAAG,eAAe,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAC7E,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;IACxE,CAAC;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IACxE,OAAO,SAAS,UAAU,EAAE,CAAC;AAC/B,CAAC","sourcesContent":["import type {SqlCellData} from './types';\n\nexport function isDefined<T>(value: T | undefined): value is T {\n return value !== undefined;\n}\n\n/**\n * Validates that a string is a valid SQL identifier.\n * Must start with a letter or underscore, followed by letters, digits, or underscores.\n */\nexport function isValidSqlIdentifier(name: string): boolean {\n return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name);\n}\n\n/**\n * Gets the effective result name for a SQL cell.\n * Returns explicit resultName if valid, otherwise auto-generates from title.\n */\nexport function getEffectiveResultName(\n data: SqlCellData,\n convertToValidName: (name: string) => string,\n): string {\n if (data.resultName && isValidSqlIdentifier(data.resultName)) {\n return data.resultName;\n }\n\n const normalizedTitle = convertToValidName(data.title).toLowerCase();\n\n // Keep SQL default titles mapped to a neutral result_* name:\n // \"SQL 1\" -> \"result_1\", \"SQL Query 2\" -> \"result_2\".\n const sqlDefaultMatch = normalizedTitle.match(/^sql(?:_query)?(?:_(\\d+))?$/);\n if (sqlDefaultMatch) {\n return sqlDefaultMatch[1] ? `result_${sqlDefaultMatch[1]}` : 'result';\n }\n\n return normalizedTitle;\n}\n\n/**\n * Generates a stable SQL schema name for a sheet id.\n */\nexport function getSheetSchemaName(sheetId: string): string {\n const normalized = sheetId.replace(/[^a-zA-Z0-9_]/g, '_').toLowerCase();\n return `sheet_${normalized}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAI,KAAoB;IAC/C,OAAO,KAAK,KAAK,SAAS,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,IAAiB,EACjB,kBAA4C;IAE5C,IAAI,IAAI,CAAC,UAAU,IAAI,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,MAAM,eAAe,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAErE,6DAA6D;IAC7D,sDAAsD;IACtD,MAAM,eAAe,GAAG,eAAe,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IAC7E,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;IACxE,CAAC;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IACxE,OAAO,SAAS,UAAU,EAAE,CAAC;AAC/B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAI5C,MAAM,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG;SAC7B,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAExC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,EAAC,SAAS,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAC,CAAC;IAC9C,CAAC;IAED,OAAO;QACL,SAAS;QACT,YAAY,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC;KAC9B,CAAC;AACJ,CAAC","sourcesContent":["import type {SqlCellData} from './types';\n\nexport function isDefined<T>(value: T | undefined): value is T {\n return value !== undefined;\n}\n\n/**\n * Validates that a string is a valid SQL identifier.\n * Must start with a letter or underscore, followed by letters, digits, or underscores.\n */\nexport function isValidSqlIdentifier(name: string): boolean {\n return /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name);\n}\n\n/**\n * Gets the effective result name for a SQL cell.\n * Returns explicit resultName if valid, otherwise auto-generates from title.\n */\nexport function getEffectiveResultName(\n data: SqlCellData,\n convertToValidName: (name: string) => string,\n): string {\n if (data.resultName && isValidSqlIdentifier(data.resultName)) {\n return data.resultName;\n }\n\n const normalizedTitle = convertToValidName(data.title).toLowerCase();\n\n // Keep SQL default titles mapped to a neutral result_* name:\n // \"SQL 1\" -> \"result_1\", \"SQL Query 2\" -> \"result_2\".\n const sqlDefaultMatch = normalizedTitle.match(/^sql(?:_query)?(?:_(\\d+))?$/);\n if (sqlDefaultMatch) {\n return sqlDefaultMatch[1] ? `result_${sqlDefaultMatch[1]}` : 'result';\n }\n\n return normalizedTitle;\n}\n\n/**\n * Generates a stable SQL schema name for a sheet id.\n */\nexport function getSheetSchemaName(sheetId: string): string {\n const normalized = sheetId.replace(/[^a-zA-Z0-9_]/g, '_').toLowerCase();\n return `sheet_${normalized}`;\n}\n\n/**\n * Extracts a preview from SQL query - first non-empty line and whether there are more lines.\n * Returns empty firstLine if the SQL is empty or contains only whitespace.\n */\nexport function getSqlQueryPreview(sql: string): {\n firstLine: string;\n hasMoreLines: boolean;\n} {\n const [firstLine, ...rest] = sql\n .split('\\n')\n .filter((line) => line.trim() !== '');\n\n if (!firstLine) {\n return {firstLine: '', hasMoreLines: false};\n }\n\n return {\n firstLine,\n hasMoreLines: rest.length > 0,\n };\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Cell, Sheet } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Validates that a cell title is unique within its sheet.
|
|
4
|
+
*/
|
|
5
|
+
export declare function validateCellTitle(title: string, cellIds: string[], cells: Record<string, Cell>, excludeCellId?: string): {
|
|
6
|
+
valid: boolean;
|
|
7
|
+
error?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Validates that a sheet title is unique.
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateSheetTitle(title: string, sheets: Record<string, Sheet>, excludeSheetId?: string): {
|
|
13
|
+
valid: boolean;
|
|
14
|
+
error?: string;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAE,KAAK,EAAC,MAAM,SAAS,CAAC;AAEpC;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,EAC3B,aAAa,CAAC,EAAE,MAAM,GACrB;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,CAqBlC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAC7B,cAAc,CAAC,EAAE,MAAM,GACtB;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC,CAclC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that a cell title is unique within its sheet.
|
|
3
|
+
*/
|
|
4
|
+
export function validateCellTitle(title, cellIds, cells, excludeCellId) {
|
|
5
|
+
const normalizedTitle = title.trim().toLowerCase();
|
|
6
|
+
if (!normalizedTitle) {
|
|
7
|
+
return { valid: false, error: 'Title cannot be empty' };
|
|
8
|
+
}
|
|
9
|
+
for (const id of cellIds) {
|
|
10
|
+
if (id === excludeCellId)
|
|
11
|
+
continue;
|
|
12
|
+
const cell = cells[id];
|
|
13
|
+
if (cell &&
|
|
14
|
+
cell.data.title?.trim().toLowerCase() === normalizedTitle) {
|
|
15
|
+
return {
|
|
16
|
+
valid: false,
|
|
17
|
+
error: `A cell named "${title}" already exists in this sheet`,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return { valid: true };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Validates that a sheet title is unique.
|
|
25
|
+
*/
|
|
26
|
+
export function validateSheetTitle(title, sheets, excludeSheetId) {
|
|
27
|
+
const normalizedTitle = title.trim().toLowerCase();
|
|
28
|
+
if (!normalizedTitle) {
|
|
29
|
+
return { valid: false, error: 'Title cannot be empty' };
|
|
30
|
+
}
|
|
31
|
+
for (const sheet of Object.values(sheets)) {
|
|
32
|
+
if (sheet.id === excludeSheetId)
|
|
33
|
+
continue;
|
|
34
|
+
if (sheet.title.trim().toLowerCase() === normalizedTitle) {
|
|
35
|
+
return { valid: false, error: `A sheet named "${title}" already exists` };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return { valid: true };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAa,EACb,OAAiB,EACjB,KAA2B,EAC3B,aAAsB;IAEtB,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACnD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,EAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAC,CAAC;IACxD,CAAC;IAED,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QACzB,IAAI,EAAE,KAAK,aAAa;YAAE,SAAS;QACnC,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;QACvB,IACE,IAAI;YACH,IAAI,CAAC,IAAY,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,eAAe,EAClE,CAAC;YACD,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,iBAAiB,KAAK,gCAAgC;aAC9D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAa,EACb,MAA6B,EAC7B,cAAuB;IAEvB,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACnD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,EAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAC,CAAC;IACxD,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,IAAI,KAAK,CAAC,EAAE,KAAK,cAAc;YAAE,SAAS;QAC1C,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,eAAe,EAAE,CAAC;YACzD,OAAO,EAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,KAAK,kBAAkB,EAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAED,OAAO,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC;AACvB,CAAC","sourcesContent":["import {Cell, Sheet} from './types';\n\n/**\n * Validates that a cell title is unique within its sheet.\n */\nexport function validateCellTitle(\n title: string,\n cellIds: string[],\n cells: Record<string, Cell>,\n excludeCellId?: string,\n): {valid: boolean; error?: string} {\n const normalizedTitle = title.trim().toLowerCase();\n if (!normalizedTitle) {\n return {valid: false, error: 'Title cannot be empty'};\n }\n\n for (const id of cellIds) {\n if (id === excludeCellId) continue;\n const cell = cells[id];\n if (\n cell &&\n (cell.data as any).title?.trim().toLowerCase() === normalizedTitle\n ) {\n return {\n valid: false,\n error: `A cell named \"${title}\" already exists in this sheet`,\n };\n }\n }\n\n return {valid: true};\n}\n\n/**\n * Validates that a sheet title is unique.\n */\nexport function validateSheetTitle(\n title: string,\n sheets: Record<string, Sheet>,\n excludeSheetId?: string,\n): {valid: boolean; error?: string} {\n const normalizedTitle = title.trim().toLowerCase();\n if (!normalizedTitle) {\n return {valid: false, error: 'Title cannot be empty'};\n }\n\n for (const sheet of Object.values(sheets)) {\n if (sheet.id === excludeSheetId) continue;\n if (sheet.title.trim().toLowerCase() === normalizedTitle) {\n return {valid: false, error: `A sheet named \"${title}\" already exists`};\n }\n }\n\n return {valid: true};\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CrossFilterSelection } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* The standard Vega param name used for brush selections
|
|
4
|
+
* in cross-filter-enabled charts.
|
|
5
|
+
*/
|
|
6
|
+
export declare const BRUSH_PARAM_NAME = "brush";
|
|
7
|
+
/**
|
|
8
|
+
* Build a SQL WHERE clause fragment from an array of sibling cross-filter
|
|
9
|
+
* selections. Returns `null` when there are no active selections.
|
|
10
|
+
*
|
|
11
|
+
* Supports interval selections (BETWEEN) and point selections (IN).
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildCrossFilterPredicate(selections: Array<CrossFilterSelection | null | undefined>): string | null;
|
|
14
|
+
//# sourceMappingURL=vegaSelectionUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vegaSelectionUtils.d.ts","sourceRoot":"","sources":["../src/vegaSelectionUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,SAAS,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,UAAU,CAAC;AA8FxC;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,KAAK,CAAC,oBAAoB,GAAG,IAAI,GAAG,SAAS,CAAC,GACzD,MAAM,GAAG,IAAI,CAiBf"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The standard Vega param name used for brush selections
|
|
3
|
+
* in cross-filter-enabled charts.
|
|
4
|
+
*/
|
|
5
|
+
export const BRUSH_PARAM_NAME = 'brush';
|
|
6
|
+
/**
|
|
7
|
+
* Escapes a field name for use in SQL by wrapping in double quotes
|
|
8
|
+
* and escaping embedded double quotes.
|
|
9
|
+
*/
|
|
10
|
+
function escapeFieldName(field) {
|
|
11
|
+
return `"${field.replace(/"/g, '""')}"`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Escapes a string value for use in SQL by escaping single quotes.
|
|
15
|
+
*/
|
|
16
|
+
function escapeSqlString(str) {
|
|
17
|
+
return str.replace(/'/g, "''");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Formats a value for use in SQL (numbers as-is, strings quoted and escaped).
|
|
21
|
+
*/
|
|
22
|
+
function formatSqlValue(value) {
|
|
23
|
+
if (typeof value === 'number') {
|
|
24
|
+
return String(value);
|
|
25
|
+
}
|
|
26
|
+
return `'${escapeSqlString(String(value))}'`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Builds a SQL BETWEEN clause for interval selections.
|
|
30
|
+
* Returns null if the range is invalid.
|
|
31
|
+
*/
|
|
32
|
+
function buildIntervalClause(quotedField, selection) {
|
|
33
|
+
const range = selection.value;
|
|
34
|
+
if (!Array.isArray(range) || range.length !== 2) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const [lo, hi] = range;
|
|
38
|
+
if (typeof lo === 'number' && typeof hi === 'number') {
|
|
39
|
+
if (selection.fieldType === 'temporal') {
|
|
40
|
+
return `${quotedField} BETWEEN epoch_ms(${Math.round(lo)}) AND epoch_ms(${Math.round(hi)})`;
|
|
41
|
+
}
|
|
42
|
+
return `${quotedField} BETWEEN ${lo} AND ${hi}`;
|
|
43
|
+
}
|
|
44
|
+
if (typeof lo === 'string' && typeof hi === 'string') {
|
|
45
|
+
const loEsc = escapeSqlString(lo);
|
|
46
|
+
const hiEsc = escapeSqlString(hi);
|
|
47
|
+
return `${quotedField} BETWEEN '${loEsc}' AND '${hiEsc}'`;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Builds a SQL IN clause for point selections.
|
|
53
|
+
* Returns null if the values array is empty or invalid.
|
|
54
|
+
*/
|
|
55
|
+
function buildPointClause(quotedField, selection) {
|
|
56
|
+
const values = selection.value;
|
|
57
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const commaSeparated = values.map(formatSqlValue).join(', ');
|
|
61
|
+
return `${quotedField} IN (${commaSeparated})`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Builds a SQL clause for a single selection based on its type.
|
|
65
|
+
* Returns null if the selection type is unknown or the clause cannot be built.
|
|
66
|
+
*/
|
|
67
|
+
function buildClause(quotedField, selection) {
|
|
68
|
+
switch (selection.type) {
|
|
69
|
+
case 'interval':
|
|
70
|
+
return buildIntervalClause(quotedField, selection);
|
|
71
|
+
case 'point':
|
|
72
|
+
return buildPointClause(quotedField, selection);
|
|
73
|
+
default:
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Build a SQL WHERE clause fragment from an array of sibling cross-filter
|
|
79
|
+
* selections. Returns `null` when there are no active selections.
|
|
80
|
+
*
|
|
81
|
+
* Supports interval selections (BETWEEN) and point selections (IN).
|
|
82
|
+
*/
|
|
83
|
+
export function buildCrossFilterPredicate(selections) {
|
|
84
|
+
const clauses = [];
|
|
85
|
+
for (const selection of selections) {
|
|
86
|
+
if (!selection || selection.value == null) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const quotedField = escapeFieldName(selection.field);
|
|
90
|
+
const clause = buildClause(quotedField, selection);
|
|
91
|
+
if (clause) {
|
|
92
|
+
clauses.push(clause);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return clauses.length > 0 ? clauses.join(' AND ') : null;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=vegaSelectionUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vegaSelectionUtils.js","sourceRoot":"","sources":["../src/vegaSelectionUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import type {CrossFilterSelection} from './types';\n\n/**\n * The standard Vega param name used for brush selections\n * in cross-filter-enabled charts.\n */\nexport const BRUSH_PARAM_NAME = 'brush';\n\n/**\n * Escapes a field name for use in SQL by wrapping in double quotes\n * and escaping embedded double quotes.\n */\nfunction escapeFieldName(field: string): string {\n return `\"${field.replace(/\"/g, '\"\"')}\"`;\n}\n\n/**\n * Escapes a string value for use in SQL by escaping single quotes.\n */\nfunction escapeSqlString(str: string): string {\n return str.replace(/'/g, \"''\");\n}\n\n/**\n * Formats a value for use in SQL (numbers as-is, strings quoted and escaped).\n */\nfunction formatSqlValue(value: unknown): string {\n if (typeof value === 'number') {\n return String(value);\n }\n return `'${escapeSqlString(String(value))}'`;\n}\n\n/**\n * Builds a SQL BETWEEN clause for interval selections.\n * Returns null if the range is invalid.\n */\nfunction buildIntervalClause(\n quotedField: string,\n selection: CrossFilterSelection,\n): string | null {\n const range = selection.value;\n if (!Array.isArray(range) || range.length !== 2) {\n return null;\n }\n\n const [lo, hi] = range;\n\n if (typeof lo === 'number' && typeof hi === 'number') {\n if (selection.fieldType === 'temporal') {\n return `${quotedField} BETWEEN epoch_ms(${Math.round(lo)}) AND epoch_ms(${Math.round(hi)})`;\n }\n return `${quotedField} BETWEEN ${lo} AND ${hi}`;\n }\n\n if (typeof lo === 'string' && typeof hi === 'string') {\n const loEsc = escapeSqlString(lo);\n const hiEsc = escapeSqlString(hi);\n return `${quotedField} BETWEEN '${loEsc}' AND '${hiEsc}'`;\n }\n\n return null;\n}\n\n/**\n * Builds a SQL IN clause for point selections.\n * Returns null if the values array is empty or invalid.\n */\nfunction buildPointClause(\n quotedField: string,\n selection: CrossFilterSelection,\n): string | null {\n const values = selection.value;\n if (!Array.isArray(values) || values.length === 0) {\n return null;\n }\n\n const commaSeparated = values.map(formatSqlValue).join(', ');\n\n return `${quotedField} IN (${commaSeparated})`;\n}\n\n/**\n * Builds a SQL clause for a single selection based on its type.\n * Returns null if the selection type is unknown or the clause cannot be built.\n */\nfunction buildClause(\n quotedField: string,\n selection: CrossFilterSelection,\n): string | null {\n switch (selection.type) {\n case 'interval':\n return buildIntervalClause(quotedField, selection);\n case 'point':\n return buildPointClause(quotedField, selection);\n default:\n return null;\n }\n}\n\n/**\n * Build a SQL WHERE clause fragment from an array of sibling cross-filter\n * selections. Returns `null` when there are no active selections.\n *\n * Supports interval selections (BETWEEN) and point selections (IN).\n */\nexport function buildCrossFilterPredicate(\n selections: Array<CrossFilterSelection | null | undefined>,\n): string | null {\n const clauses: string[] = [];\n\n for (const selection of selections) {\n if (!selection || selection.value == null) {\n continue;\n }\n\n const quotedField = escapeFieldName(selection.field);\n const clause = buildClause(quotedField, selection);\n\n if (clause) {\n clauses.push(clause);\n }\n }\n\n return clauses.length > 0 ? clauses.join(' AND ') : null;\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { BrushFieldType } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Read the flat encoding/mark values from either a flat spec or a
|
|
4
|
+
* dual-layer cross-filter spec so the config panel dropdowns reflect
|
|
5
|
+
* the current state regardless of spec shape.
|
|
6
|
+
*/
|
|
7
|
+
export declare function readSpecValues(spec: any): {
|
|
8
|
+
mark: string | undefined;
|
|
9
|
+
xField: string | undefined;
|
|
10
|
+
yField: string | undefined;
|
|
11
|
+
yAggregate: string | undefined;
|
|
12
|
+
color: string | undefined;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Build a dual-layer Vega-Lite spec with a brush selection param on the
|
|
16
|
+
* background layer and a filter transform on the foreground layer.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildCrossFilterSpec(opts: {
|
|
19
|
+
mark: string;
|
|
20
|
+
xField?: string;
|
|
21
|
+
xFieldType?: BrushFieldType;
|
|
22
|
+
yField?: string;
|
|
23
|
+
yAggregate?: string;
|
|
24
|
+
color?: string;
|
|
25
|
+
}): any;
|
|
26
|
+
/** Build a simple flat spec (no cross-filtering). */
|
|
27
|
+
export declare function buildFlatSpec(opts: {
|
|
28
|
+
mark: string;
|
|
29
|
+
xField?: string;
|
|
30
|
+
yField?: string;
|
|
31
|
+
yAggregate?: string;
|
|
32
|
+
color?: string;
|
|
33
|
+
}): any;
|
|
34
|
+
//# sourceMappingURL=vegaSpecBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vegaSpecBuilder.d.ts","sourceRoot":"","sources":["../src/vegaSpecBuilder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,SAAS,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,GAAG,GAAG;IACzC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAmBA;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,GAAG,CA4CN;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,GAAG,CAQN"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { BRUSH_PARAM_NAME } from './vegaSelectionUtils';
|
|
2
|
+
/**
|
|
3
|
+
* Read the flat encoding/mark values from either a flat spec or a
|
|
4
|
+
* dual-layer cross-filter spec so the config panel dropdowns reflect
|
|
5
|
+
* the current state regardless of spec shape.
|
|
6
|
+
*/
|
|
7
|
+
export function readSpecValues(spec) {
|
|
8
|
+
if (spec?.layer && Array.isArray(spec.layer)) {
|
|
9
|
+
const fg = spec.layer[1] ?? spec.layer[0];
|
|
10
|
+
const bg = spec.layer[0];
|
|
11
|
+
return {
|
|
12
|
+
mark: fg?.mark ?? bg?.mark,
|
|
13
|
+
xField: fg?.encoding?.x?.field ?? bg?.encoding?.x?.field,
|
|
14
|
+
yField: fg?.encoding?.y?.field ?? bg?.encoding?.y?.field,
|
|
15
|
+
yAggregate: fg?.encoding?.y?.aggregate ?? bg?.encoding?.y?.aggregate,
|
|
16
|
+
color: fg?.encoding?.color?.value,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
mark: spec?.mark,
|
|
21
|
+
xField: spec?.encoding?.x?.field,
|
|
22
|
+
yField: spec?.encoding?.y?.field,
|
|
23
|
+
yAggregate: spec?.encoding?.y?.aggregate,
|
|
24
|
+
color: spec?.encoding?.color?.value,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Build a dual-layer Vega-Lite spec with a brush selection param on the
|
|
29
|
+
* background layer and a filter transform on the foreground layer.
|
|
30
|
+
*/
|
|
31
|
+
export function buildCrossFilterSpec(opts) {
|
|
32
|
+
const { mark, xField, xFieldType, yField, yAggregate, color } = opts;
|
|
33
|
+
const xEnc = xField
|
|
34
|
+
? {
|
|
35
|
+
field: xField,
|
|
36
|
+
...(xFieldType === 'numeric' ? { bin: { maxbins: 20 } } : {}),
|
|
37
|
+
}
|
|
38
|
+
: undefined;
|
|
39
|
+
const yEnc = yField
|
|
40
|
+
? { field: yField, aggregate: yAggregate ?? 'sum' }
|
|
41
|
+
: yAggregate === 'count'
|
|
42
|
+
? { aggregate: 'count' }
|
|
43
|
+
: undefined;
|
|
44
|
+
const encoding = {};
|
|
45
|
+
if (xEnc)
|
|
46
|
+
encoding.x = xEnc;
|
|
47
|
+
if (yEnc)
|
|
48
|
+
encoding.y = yEnc;
|
|
49
|
+
return {
|
|
50
|
+
layer: [
|
|
51
|
+
{
|
|
52
|
+
params: [
|
|
53
|
+
{
|
|
54
|
+
name: BRUSH_PARAM_NAME,
|
|
55
|
+
select: { type: 'interval', encodings: ['x'] },
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
mark,
|
|
59
|
+
encoding: {
|
|
60
|
+
...encoding,
|
|
61
|
+
color: { value: '#ddd' },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
transform: [{ filter: { param: BRUSH_PARAM_NAME } }],
|
|
66
|
+
mark,
|
|
67
|
+
encoding: {
|
|
68
|
+
...encoding,
|
|
69
|
+
...(color ? { color: { value: color } } : {}),
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
padding: 20,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Build a simple flat spec (no cross-filtering). */
|
|
77
|
+
export function buildFlatSpec(opts) {
|
|
78
|
+
const { mark, xField, yField, yAggregate, color } = opts;
|
|
79
|
+
const encoding = {};
|
|
80
|
+
if (xField)
|
|
81
|
+
encoding.x = { field: xField };
|
|
82
|
+
if (yField)
|
|
83
|
+
encoding.y = { field: yField, aggregate: yAggregate ?? 'sum' };
|
|
84
|
+
else if (yAggregate === 'count')
|
|
85
|
+
encoding.y = { aggregate: 'count' };
|
|
86
|
+
if (color)
|
|
87
|
+
encoding.color = { value: color };
|
|
88
|
+
return { mark, encoding, padding: 20 };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=vegaSpecBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vegaSpecBuilder.js","sourceRoot":"","sources":["../src/vegaSpecBuilder.ts"],"names":[],"mappings":"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","sourcesContent":["import {BRUSH_PARAM_NAME} from './vegaSelectionUtils';\nimport type {BrushFieldType} from './types';\n\n/**\n * Read the flat encoding/mark values from either a flat spec or a\n * dual-layer cross-filter spec so the config panel dropdowns reflect\n * the current state regardless of spec shape.\n */\nexport function readSpecValues(spec: any): {\n mark: string | undefined;\n xField: string | undefined;\n yField: string | undefined;\n yAggregate: string | undefined;\n color: string | undefined;\n} {\n if (spec?.layer && Array.isArray(spec.layer)) {\n const fg = spec.layer[1] ?? spec.layer[0];\n const bg = spec.layer[0];\n return {\n mark: fg?.mark ?? bg?.mark,\n xField: fg?.encoding?.x?.field ?? bg?.encoding?.x?.field,\n yField: fg?.encoding?.y?.field ?? bg?.encoding?.y?.field,\n yAggregate: fg?.encoding?.y?.aggregate ?? bg?.encoding?.y?.aggregate,\n color: fg?.encoding?.color?.value,\n };\n }\n return {\n mark: spec?.mark,\n xField: spec?.encoding?.x?.field,\n yField: spec?.encoding?.y?.field,\n yAggregate: spec?.encoding?.y?.aggregate,\n color: spec?.encoding?.color?.value,\n };\n}\n\n/**\n * Build a dual-layer Vega-Lite spec with a brush selection param on the\n * background layer and a filter transform on the foreground layer.\n */\nexport function buildCrossFilterSpec(opts: {\n mark: string;\n xField?: string;\n xFieldType?: BrushFieldType;\n yField?: string;\n yAggregate?: string;\n color?: string;\n}): any {\n const {mark, xField, xFieldType, yField, yAggregate, color} = opts;\n const xEnc: any = xField\n ? {\n field: xField,\n ...(xFieldType === 'numeric' ? {bin: {maxbins: 20}} : {}),\n }\n : undefined;\n const yEnc: any = yField\n ? {field: yField, aggregate: yAggregate ?? 'sum'}\n : yAggregate === 'count'\n ? {aggregate: 'count'}\n : undefined;\n\n const encoding: any = {};\n if (xEnc) encoding.x = xEnc;\n if (yEnc) encoding.y = yEnc;\n\n return {\n layer: [\n {\n params: [\n {\n name: BRUSH_PARAM_NAME,\n select: {type: 'interval', encodings: ['x']},\n },\n ],\n mark,\n encoding: {\n ...encoding,\n color: {value: '#ddd'},\n },\n },\n {\n transform: [{filter: {param: BRUSH_PARAM_NAME}}],\n mark,\n encoding: {\n ...encoding,\n ...(color ? {color: {value: color}} : {}),\n },\n },\n ],\n padding: 20,\n };\n}\n\n/** Build a simple flat spec (no cross-filtering). */\nexport function buildFlatSpec(opts: {\n mark: string;\n xField?: string;\n yField?: string;\n yAggregate?: string;\n color?: string;\n}): any {\n const {mark, xField, yField, yAggregate, color} = opts;\n const encoding: any = {};\n if (xField) encoding.x = {field: xField};\n if (yField) encoding.y = {field: yField, aggregate: yAggregate ?? 'sum'};\n else if (yAggregate === 'count') encoding.y = {aggregate: 'count'};\n if (color) encoding.color = {value: color};\n return {mark, encoding, padding: 20};\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqlrooms/cells",
|
|
3
|
-
"version": "0.29.0-rc.
|
|
4
|
-
"private": false,
|
|
3
|
+
"version": "0.29.0-rc.2",
|
|
5
4
|
"type": "module",
|
|
6
5
|
"main": "dist/index.js",
|
|
7
6
|
"module": "dist/index.js",
|
|
@@ -13,7 +12,7 @@
|
|
|
13
12
|
"license": "MIT",
|
|
14
13
|
"repository": {
|
|
15
14
|
"type": "git",
|
|
16
|
-
"url": "https://github.com/sqlrooms/sqlrooms.git"
|
|
15
|
+
"url": "git+https://github.com/sqlrooms/sqlrooms.git"
|
|
17
16
|
},
|
|
18
17
|
"sideEffects": false,
|
|
19
18
|
"scripts": {
|
|
@@ -27,21 +26,21 @@
|
|
|
27
26
|
},
|
|
28
27
|
"dependencies": {
|
|
29
28
|
"@paralleldrive/cuid2": "^2.2.2",
|
|
30
|
-
"@sqlrooms/data-table": "0.29.0-rc.
|
|
31
|
-
"@sqlrooms/db": "0.29.0-rc.
|
|
32
|
-
"@sqlrooms/duckdb": "0.29.0-rc.
|
|
33
|
-
"@sqlrooms/room-store": "0.29.0-rc.
|
|
34
|
-
"@sqlrooms/sql-editor": "0.29.0-rc.
|
|
35
|
-
"@sqlrooms/ui": "0.29.0-rc.
|
|
36
|
-
"@sqlrooms/utils": "0.29.0-rc.
|
|
37
|
-
"@sqlrooms/vega": "0.29.0-rc.
|
|
29
|
+
"@sqlrooms/data-table": "0.29.0-rc.2",
|
|
30
|
+
"@sqlrooms/db": "0.29.0-rc.2",
|
|
31
|
+
"@sqlrooms/duckdb": "0.29.0-rc.2",
|
|
32
|
+
"@sqlrooms/room-store": "0.29.0-rc.2",
|
|
33
|
+
"@sqlrooms/sql-editor": "0.29.0-rc.2",
|
|
34
|
+
"@sqlrooms/ui": "0.29.0-rc.2",
|
|
35
|
+
"@sqlrooms/utils": "0.29.0-rc.2",
|
|
36
|
+
"@sqlrooms/vega": "0.29.0-rc.2",
|
|
38
37
|
"@tanstack/react-table": "^8.21.3",
|
|
39
38
|
"apache-arrow": "17.0.0",
|
|
40
39
|
"immer": "^11.0.1",
|
|
41
40
|
"lucide-react": "^0.555.0",
|
|
42
|
-
"monaco-editor": "^0.55.1",
|
|
43
41
|
"react-hook-form": "^7.68.0",
|
|
44
42
|
"react-markdown": "^10.1.0",
|
|
43
|
+
"remark-breaks": "^4.0.0",
|
|
45
44
|
"remark-gfm": "^4.0.1",
|
|
46
45
|
"zod": "^4.1.8"
|
|
47
46
|
},
|
|
@@ -53,5 +52,5 @@
|
|
|
53
52
|
"react": "^18 || ^19",
|
|
54
53
|
"react-dom": "^18 || ^19"
|
|
55
54
|
},
|
|
56
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "5d511631992c1af8852ea79ced488867aad4a555"
|
|
57
56
|
}
|