drizzle-cube 0.2.24 → 0.2.25

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 (52) hide show
  1. package/dist/adapters/compiler-C6JQHGrF.cjs +22 -0
  2. package/dist/adapters/{compiler-DkzdLcxq.js → compiler-Ddwn99Ja.js} +1419 -1287
  3. package/dist/adapters/express/index.cjs +1 -1
  4. package/dist/adapters/express/index.d.ts +6 -1
  5. package/dist/adapters/express/index.js +22 -20
  6. package/dist/adapters/fastify/index.cjs +1 -1
  7. package/dist/adapters/fastify/index.d.ts +6 -1
  8. package/dist/adapters/fastify/index.js +18 -16
  9. package/dist/adapters/hono/index.cjs +1 -1
  10. package/dist/adapters/hono/index.d.ts +6 -1
  11. package/dist/adapters/hono/index.js +87 -85
  12. package/dist/adapters/nextjs/index.cjs +1 -1
  13. package/dist/adapters/nextjs/index.d.ts +6 -1
  14. package/dist/adapters/nextjs/index.js +64 -63
  15. package/dist/adapters/utils.d.ts +18 -0
  16. package/dist/client/charts.js +2 -2
  17. package/dist/client/chunks/{charts-DboFPJFN.js → charts-BsXrHSCm.js} +32 -32
  18. package/dist/client/chunks/{charts-DboFPJFN.js.map → charts-BsXrHSCm.js.map} +1 -1
  19. package/dist/client/chunks/{charts--hFH-bsu.js → charts-BvLb1eub.js} +710 -703
  20. package/dist/client/chunks/charts-BvLb1eub.js.map +1 -0
  21. package/dist/client/chunks/{components-D7wTB56l.js → components-DhA8GIUY.js} +6019 -5299
  22. package/dist/client/chunks/components-DhA8GIUY.js.map +1 -0
  23. package/dist/client/chunks/core-DcwgBEzv.js +6 -0
  24. package/dist/client/chunks/core-DcwgBEzv.js.map +1 -0
  25. package/dist/client/chunks/{index-yc7cf-yE.js → index-9x0R-Fme.js} +2 -2
  26. package/dist/client/chunks/{index-yc7cf-yE.js.map → index-9x0R-Fme.js.map} +1 -1
  27. package/dist/client/components/AnalysisBuilder/AnalysisQueryPanel.d.ts +3 -2
  28. package/dist/client/components/AnalysisBuilder/types.d.ts +63 -15
  29. package/dist/client/components/DashboardPortletCard.d.ts +12 -0
  30. package/dist/client/components/DebugModal.d.ts +7 -1
  31. package/dist/client/components.js +2 -2
  32. package/dist/client/hooks/useMultiCubeQuery.d.ts +36 -0
  33. package/dist/client/hooks.js +2 -2
  34. package/dist/client/icons.js +1 -1
  35. package/dist/client/index.d.ts +7 -1
  36. package/dist/client/index.js +70 -52
  37. package/dist/client/providers.js +1 -1
  38. package/dist/client/styles.css +1 -1
  39. package/dist/client/types.d.ts +32 -0
  40. package/dist/client/utils/multiQueryUtils.d.ts +86 -0
  41. package/dist/client/utils/multiQueryValidation.d.ts +69 -0
  42. package/dist/client/utils/shareUtils.d.ts +3 -3
  43. package/dist/client-bundle-stats.html +1 -1
  44. package/dist/server/index.cjs +17 -17
  45. package/dist/server/index.d.ts +326 -1
  46. package/dist/server/index.js +2429 -2148
  47. package/package.json +1 -1
  48. package/dist/adapters/compiler-BFqMax9M.cjs +0 -22
  49. package/dist/client/chunks/charts--hFH-bsu.js.map +0 -1
  50. package/dist/client/chunks/components-D7wTB56l.js.map +0 -1
  51. package/dist/client/chunks/core-PxWXpBbW.js +0 -6
  52. package/dist/client/chunks/core-PxWXpBbW.js.map +0 -1
@@ -0,0 +1,6 @@
1
+ import { H as a } from "./components-DhA8GIUY.js";
2
+ export {
3
+ a as HighlightJS,
4
+ a as default
5
+ };
6
+ //# sourceMappingURL=core-DcwgBEzv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-DcwgBEzv.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,7 +1,7 @@
1
1
  import { jsxs as o, jsx as t, Fragment as J } from "react/jsx-runtime";
2
2
  import { useMemo as j, useState as H, useEffect as T, useCallback as S } from "react";
3
3
  import Z, { Handle as P, Position as g, getBezierPath as K, BaseEdge as Q, EdgeLabelRenderer as U, useNodesState as _, useEdgesState as ee, addEdge as te, ConnectionMode as se, Controls as ne, MiniMap as re, Background as oe, BackgroundVariant as ae } from "reactflow";
4
- import { u as ie } from "./charts--hFH-bsu.js";
4
+ import { u as ie } from "./charts-BvLb1eub.js";
5
5
  import O from "dagre";
6
6
  function ce({ data: i }) {
7
7
  const { cube: s, onFieldClick: f, isHighlighted: n, highlightedFields: w, searchTerm: m } = i, d = () => {
@@ -548,4 +548,4 @@ export {
548
548
  Ce as CubeRelationshipDiagram,
549
549
  Ce as default
550
550
  };
551
- //# sourceMappingURL=index-yc7cf-yE.js.map
551
+ //# sourceMappingURL=index-9x0R-Fme.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-yc7cf-yE.js","sources":["../../../src/client/components/CubeRelationshipDiagram/CubeNode.tsx","../../../src/client/components/CubeRelationshipDiagram/RelationshipEdge.tsx","../../../src/client/components/CubeRelationshipDiagram/useERDLayout.ts","../../../src/client/components/CubeRelationshipDiagram/index.tsx"],"sourcesContent":["import { Handle, Position } from 'reactflow'\nimport type { CubeMetaCube } from '../../hooks/useCubeMeta'\n\ninterface CubeNodeProps {\n data: {\n cube: CubeMetaCube\n onCubeClick?: (cubeName: string) => void\n onFieldClick?: (cubeName: string, fieldName: string, fieldType: 'measure' | 'dimension') => void\n isHighlighted: boolean\n highlightedFields: string[]\n searchTerm?: string\n }\n}\n\nexport function CubeNode({ data }: CubeNodeProps) {\n const { cube, onFieldClick, isHighlighted, highlightedFields, searchTerm } = data\n\n const handleCubeHeaderClick = () => {\n // Do nothing - disable cube header clicks in ERD\n }\n\n const handleFieldClick = (fieldName: string, fieldType: 'measure' | 'dimension') => {\n if (onFieldClick) {\n onFieldClick(cube.name, fieldName, fieldType)\n }\n }\n\n const isFieldHighlighted = (fullFieldName: string) => {\n return highlightedFields.includes(fullFieldName)\n }\n\n const isFieldSearchMatch = (field: { name: string; title?: string }) => {\n if (!searchTerm?.trim()) return true // No search term, show all fields\n \n const term = searchTerm.toLowerCase()\n return (\n field.name.toLowerCase().includes(term) ||\n (field.title && field.title.toLowerCase().includes(term))\n )\n }\n\n // Check if the cube has any matching fields\n const cubeHasMatches = () => {\n if (!searchTerm?.trim()) return true // No search term, show all cubes normally\n \n // Check if any measure matches\n const measureMatches = cube.measures.some(measure => isFieldSearchMatch(measure))\n \n // Check if any dimension matches\n const dimensionMatches = cube.dimensions.some(dimension => isFieldSearchMatch(dimension))\n \n return measureMatches || dimensionMatches\n }\n\n const hasCubeMatches = cubeHasMatches()\n\n const getFieldVisibilityClasses = (field: { name: string; title?: string; type?: string }, isHighlighted: boolean, fieldType: 'measure' | 'dimension') => {\n const isSearchMatch = isFieldSearchMatch(field)\n const baseClasses = 'px-4 py-2 text-xs cursor-pointer transition-all border-b border-dc-border last:border-b-0'\n\n // If the whole cube has no matches, rely on cube-level fading\n if (!hasCubeMatches && searchTerm?.trim()) {\n // Still show selected field highlighting even in faded cubes\n if (isHighlighted) {\n if (fieldType === 'measure') {\n return `${baseClasses} bg-dc-warning-bg text-dc-warning`\n } else if (fieldType === 'dimension') {\n if (field.type === 'time') {\n return `${baseClasses} bg-dc-accent-bg text-dc-accent`\n } else {\n return `${baseClasses} bg-dc-success-bg text-dc-success`\n }\n }\n }\n return `${baseClasses} hover:bg-dc-surface-hover text-dc-text-secondary`\n }\n\n // If searching and this specific field doesn't match, make it faded\n if (searchTerm?.trim() && !isSearchMatch) {\n return `${baseClasses} opacity-40 hover:opacity-60 text-dc-text-muted`\n }\n\n // If searching and this field matches, make it prominent with bold purple text\n if (searchTerm?.trim() && isSearchMatch && !isHighlighted) {\n return `${baseClasses} font-bold hover:bg-dc-accent-bg`\n }\n\n // Normal highlighting behavior for selected fields (takes priority over search match styling)\n if (isHighlighted) {\n if (fieldType === 'measure') {\n return `${baseClasses} bg-dc-warning-bg text-dc-warning font-semibold`\n } else if (fieldType === 'dimension') {\n // Check if this is a time dimension\n if (field.type === 'time') {\n return `${baseClasses} bg-dc-accent-bg text-dc-accent font-semibold` // time dimensions\n } else {\n return `${baseClasses} bg-dc-success-bg text-dc-success font-semibold` // regular dimensions\n }\n }\n }\n\n return `${baseClasses} hover:bg-dc-surface-hover text-dc-text-secondary`\n }\n\n return (\n <div\n className={`\n border-2 rounded-lg shadow-lg min-w-[280px] overflow-hidden transition-all\n ${!hasCubeMatches && searchTerm?.trim() ? 'opacity-30 grayscale' : ''}\n ${isHighlighted ? 'border-dc-accent ring-2 ring-dc-accent' : 'border-dc-border'}\n `}\n style={{\n backgroundColor: 'var(--dc-surface)'\n }}\n >\n {/* Cube Header */}\n <div\n className={`\n px-4 py-3 cursor-pointer transition-colors\n ${isHighlighted ? 'bg-dc-accent-bg hover:bg-dc-accent-bg' : 'bg-dc-surface-secondary hover:bg-dc-surface-hover'}\n `}\n onClick={handleCubeHeaderClick}\n >\n <div className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"font-semibold text-dc-text text-sm\">\n {cube.title || cube.name}\n </h3>\n {cube.description && (\n <p className=\"text-xs text-dc-text-muted mt-1 line-clamp-2\">\n {cube.description}\n </p>\n )}\n </div>\n <div className=\"text-xs text-dc-text-muted ml-2\">\n <div>{cube.measures.length}M</div>\n <div>{cube.dimensions.length}D</div>\n </div>\n </div>\n </div>\n\n {/* Measures Section */}\n {cube.measures.length > 0 && (\n <div className=\"border-t border-dc-border\">\n <div className=\"px-4 py-2 bg-dc-warning-bg border-b border-dc-border\">\n <h4 className=\"text-xs font-medium text-dc-warning flex items-center\">\n <span className=\"w-2 h-2 bg-dc-warning-bg0 rounded-full mr-2\"></span>\n Measures ({cube.measures.length})\n </h4>\n </div>\n <div className=\"max-h-32 overflow-y-auto\">\n {cube.measures.map((measure) => {\n const fieldName = measure.name.split('.')[1] || measure.name\n const highlighted = isFieldHighlighted(measure.name)\n return (\n <div\n key={measure.name}\n className={getFieldVisibilityClasses(measure, highlighted, 'measure')}\n onClick={() => handleFieldClick(fieldName, 'measure')}\n title={measure.title}\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"font-mono truncate\">\n {measure.shortTitle || measure.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted ml-2 text-[10px] uppercase\">\n {measure.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Time Dimensions Section */}\n {cube.dimensions.filter(d => d.type === 'time').length > 0 && (\n <div className=\"border-t border-dc-border\">\n <div className=\"px-4 py-2 bg-dc-accent-bg border-b border-dc-border\">\n <h4 className=\"text-xs font-medium text-dc-accent flex items-center\">\n <span className=\"w-2 h-2 bg-dc-accent-bg0 rounded-full mr-2\"></span>\n Time Dimensions ({cube.dimensions.filter(d => d.type === 'time').length})\n </h4>\n </div>\n <div className=\"max-h-32 overflow-y-auto\">\n {cube.dimensions.filter(d => d.type === 'time').map((dimension) => {\n const fieldName = dimension.name.split('.')[1] || dimension.name\n const highlighted = isFieldHighlighted(dimension.name)\n return (\n <div\n key={dimension.name}\n className={getFieldVisibilityClasses(dimension, highlighted, 'dimension')}\n onClick={() => handleFieldClick(fieldName, 'dimension')}\n title={dimension.title}\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"font-mono truncate\">\n {dimension.shortTitle || dimension.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted ml-2 text-[10px] uppercase\">\n {dimension.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Dimensions Section (non-time) */}\n {cube.dimensions.filter(d => d.type !== 'time').length > 0 && (\n <div className=\"border-t border-dc-border\">\n <div className=\"px-4 py-2 bg-dc-success-bg border-b border-dc-border\">\n <h4 className=\"text-xs font-medium text-dc-success flex items-center\">\n <span className=\"w-2 h-2 bg-dc-success-bg0 rounded-full mr-2\"></span>\n Dimensions ({cube.dimensions.filter(d => d.type !== 'time').length})\n </h4>\n </div>\n <div className=\"max-h-32 overflow-y-auto\">\n {cube.dimensions.filter(d => d.type !== 'time').map((dimension) => {\n const fieldName = dimension.name.split('.')[1] || dimension.name\n const highlighted = isFieldHighlighted(dimension.name)\n return (\n <div\n key={dimension.name}\n className={getFieldVisibilityClasses(dimension, highlighted, 'dimension')}\n onClick={() => handleFieldClick(fieldName, 'dimension')}\n title={dimension.title}\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"font-mono truncate\">\n {dimension.shortTitle || dimension.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted ml-2 text-[10px] uppercase\">\n {dimension.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Connection handles for relationships - hidden */}\n <Handle\n type=\"source\"\n position={Position.Right}\n id=\"right\"\n className=\"opacity-0\"\n isConnectable={false}\n />\n <Handle\n type=\"target\"\n position={Position.Left}\n id=\"left\"\n className=\"opacity-0\"\n isConnectable={false}\n />\n <Handle\n type=\"source\"\n position={Position.Bottom}\n id=\"bottom\"\n className=\"opacity-0\"\n isConnectable={false}\n />\n <Handle\n type=\"target\"\n position={Position.Top}\n id=\"top\"\n className=\"opacity-0\"\n isConnectable={false}\n />\n </div>\n )\n}\n\nexport default CubeNode","import { \n EdgeProps, \n getBezierPath, \n EdgeLabelRenderer,\n BaseEdge,\n} from 'reactflow'\nimport type { CubeMetaRelationship } from '../../hooks/useCubeMeta'\n\ninterface RelationshipEdgeData {\n relationship: CubeMetaRelationship\n joinFields: Array<{\n sourceField: string\n targetField: string\n }>\n}\n\nexport function RelationshipEdge({\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n style = {},\n data,\n markerEnd,\n}: EdgeProps<RelationshipEdgeData>) {\n const [edgePath, labelX, labelY] = getBezierPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n })\n\n if (!data) return null\n\n const { relationship, joinFields } = data\n\n // Get relationship symbols and colors\n const getRelationshipSymbol = (rel: string) => {\n switch (rel) {\n case 'belongsTo':\n return '∈' // belongs to symbol\n case 'hasOne':\n return '1:1'\n case 'hasMany':\n return '1:M'\n default:\n return '?'\n }\n }\n\n const getRelationshipColor = (rel: string) => {\n switch (rel) {\n case 'belongsTo':\n return '#10b981' // green\n case 'hasOne':\n return '#3b82f6' // blue\n case 'hasMany':\n return '#f59e0b' // amber\n default:\n return '#6b7280' // gray\n }\n }\n\n const color = getRelationshipColor(relationship.relationship)\n const symbol = getRelationshipSymbol(relationship.relationship)\n\n return (\n <>\n <BaseEdge path={edgePath} markerEnd={markerEnd} style={{ ...style, stroke: color }} />\n <EdgeLabelRenderer>\n <div\n style={{\n position: 'absolute',\n transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n fontSize: 10,\n pointerEvents: 'all',\n }}\n className=\"nodrag nopan\"\n >\n <div\n className=\"border-2 rounded-md px-2 py-1 shadow-xs\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n borderColor: color\n }}\n >\n <div className=\"text-center\">\n <div\n className=\"font-bold text-xs mb-1\"\n style={{ color }}\n >\n {symbol}\n </div>\n <div className=\"text-[9px] text-dc-text-muted leading-tight\">\n {joinFields.map((field, index) => (\n <div key={index} className=\"font-mono\">\n {field.sourceField} → {field.targetField}\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n </EdgeLabelRenderer>\n </>\n )\n}\n\nexport default RelationshipEdge","import { useMemo } from 'react'\nimport { Node, Edge, Position } from 'reactflow'\nimport dagre from 'dagre'\n\nexport interface LayoutOptions {\n direction: 'TB' | 'BT' | 'LR' | 'RL'\n nodeWidth: number\n nodeHeight: number\n nodeSep: number\n rankSep: number\n ranker: 'network-simplex' | 'tight-tree' | 'longest-path'\n}\n\nconst defaultOptions: LayoutOptions = {\n direction: 'TB',\n nodeWidth: 320,\n nodeHeight: 220,\n nodeSep: 80,\n rankSep: 150,\n ranker: 'network-simplex',\n}\n\nexport function useERDLayout(\n nodes: Node[],\n edges: Edge[],\n options: Partial<LayoutOptions> = {}\n): { nodes: Node[], edges: Edge[] } {\n const layoutOptions = useMemo(() => ({ ...defaultOptions, ...options }), [options])\n\n const { layoutedNodes, layoutedEdges } = useMemo(() => {\n if (nodes.length === 0) {\n return { layoutedNodes: [], layoutedEdges: [] }\n }\n\n // Create a new dagre graph\n const dagreGraph = new dagre.graphlib.Graph()\n dagreGraph.setDefaultEdgeLabel(() => ({}))\n dagreGraph.setGraph({ \n rankdir: layoutOptions.direction,\n nodesep: layoutOptions.nodeSep,\n ranksep: layoutOptions.rankSep,\n ranker: layoutOptions.ranker,\n })\n\n // Add nodes to the graph\n nodes.forEach((node) => {\n dagreGraph.setNode(node.id, { \n width: layoutOptions.nodeWidth, \n height: layoutOptions.nodeHeight \n })\n })\n\n // Add edges to the graph\n edges.forEach((edge) => {\n dagreGraph.setEdge(edge.source, edge.target)\n })\n\n // Run the layout algorithm\n dagre.layout(dagreGraph)\n\n // Apply the calculated positions to nodes\n const layoutedNodes = nodes.map((node) => {\n const nodeWithPosition = dagreGraph.node(node.id)\n \n const newNode = {\n ...node,\n targetPosition: getTargetPosition(layoutOptions.direction),\n sourcePosition: getSourcePosition(layoutOptions.direction),\n position: {\n x: nodeWithPosition.x - layoutOptions.nodeWidth / 2,\n y: nodeWithPosition.y - layoutOptions.nodeHeight / 2,\n },\n }\n\n return newNode\n })\n\n return { \n layoutedNodes, \n layoutedEdges: edges \n }\n }, [nodes, edges, layoutOptions.direction, layoutOptions.nodeSep, layoutOptions.rankSep, layoutOptions.ranker, layoutOptions.nodeWidth, layoutOptions.nodeHeight])\n\n return { nodes: layoutedNodes, edges: layoutedEdges }\n}\n\n// Helper functions to determine handle positions based on layout direction\nfunction getTargetPosition(direction: string): Position {\n switch (direction) {\n case 'TB':\n return Position.Top\n case 'BT':\n return Position.Bottom\n case 'LR':\n return Position.Left\n case 'RL':\n return Position.Right\n default:\n return Position.Top\n }\n}\n\nfunction getSourcePosition(direction: string): Position {\n switch (direction) {\n case 'TB':\n return Position.Bottom\n case 'BT':\n return Position.Top\n case 'LR':\n return Position.Right\n case 'RL':\n return Position.Left\n default:\n return Position.Bottom\n }\n}\n\n// Custom layout function for manual positioning with smart defaults\nexport function useManualLayout(\n nodes: Node[],\n edges: Edge[],\n spacing = { x: 400, y: 300 }\n): { nodes: Node[], edges: Edge[] } {\n return useMemo(() => {\n if (nodes.length === 0) {\n return { nodes: [], edges }\n }\n\n // Simple grid layout as fallback\n const layoutedNodes = nodes.map((node, index) => ({\n ...node,\n position: {\n x: (index % 3) * spacing.x,\n y: Math.floor(index / 3) * spacing.y,\n },\n sourcePosition: Position.Right,\n targetPosition: Position.Left,\n }))\n\n return { nodes: layoutedNodes, edges }\n }, [nodes, edges, spacing])\n}\n\nexport default useERDLayout","import { useCallback, useMemo, useEffect, useState, MouseEvent } from 'react'\nimport ReactFlow, {\n Node,\n Edge,\n addEdge,\n Connection,\n useNodesState,\n useEdgesState,\n Controls,\n MiniMap,\n Background,\n BackgroundVariant,\n ConnectionMode,\n} from 'reactflow'\nimport 'reactflow/dist/style.css'\n\nimport { useCubeContext } from '../../providers/CubeProvider'\nimport { CubeNode } from './CubeNode'\nimport { RelationshipEdge } from './RelationshipEdge'\nimport { useERDLayout } from './useERDLayout'\n\n// Define the custom node and edge types OUTSIDE the component to prevent re-creation\nconst nodeTypes = {\n cubeNode: CubeNode,\n}\n\nconst edgeTypes = {\n relationshipEdge: RelationshipEdge,\n}\n\ninterface CubeRelationshipDiagramProps {\n className?: string\n onCubeClick?: (cubeName: string) => void\n onFieldClick?: (cubeName: string, fieldName: string, fieldType: 'measure' | 'dimension') => void\n highlightedCubes?: string[]\n highlightedFields?: string[]\n searchTerm?: string\n}\n\nexport function CubeRelationshipDiagram({\n className = '',\n onCubeClick,\n onFieldClick,\n highlightedCubes = [],\n highlightedFields = [],\n searchTerm,\n}: CubeRelationshipDiagramProps) {\n const { meta, metaLoading, metaError } = useCubeContext()\n\n const [savedPositions, setSavedPositions] = useState<Record<string, {x: number, y: number}>>({})\n const [autoLayoutRequested, setAutoLayoutRequested] = useState(false)\n const [contextMenu, setContextMenu] = useState<{x: number, y: number} | null>(null)\n\n // Load saved positions from localStorage on mount\n useEffect(() => {\n try {\n const savedPos = localStorage.getItem('drizzle-cube-erd-node-positions')\n if (savedPos) {\n setSavedPositions(JSON.parse(savedPos))\n }\n } catch {\n // Ignore localStorage errors\n }\n }, [])\n\n // Create base nodes and edges structure (without selection data)\n const { nodes: baseNodes, edges: baseEdges } = useMemo(() => {\n if (!meta) return { nodes: [], edges: [] }\n\n const nodes: Node[] = []\n const edges: Edge[] = []\n\n // Create nodes for each cube\n meta.cubes.forEach((cube, index) => {\n nodes.push({\n id: cube.name,\n type: 'cubeNode',\n position: savedPositions[cube.name] || { x: (index % 3) * 400, y: Math.floor(index / 3) * 300 },\n data: {\n cube,\n onCubeClick,\n onFieldClick,\n isHighlighted: false, // Will be updated separately\n highlightedFields: [], // Will be updated separately\n },\n })\n })\n\n // Create edges for relationships (excluding belongsTo)\n meta.cubes.forEach(cube => {\n if (cube.relationships) {\n cube.relationships.forEach((relationship, index) => {\n // Skip belongsTo relationships\n if (relationship.relationship === 'belongsTo') {\n return\n }\n \n const edgeId = `${cube.name}-${relationship.targetCube}-${index}`\n edges.push({\n id: edgeId,\n source: cube.name,\n target: relationship.targetCube,\n type: 'relationshipEdge',\n data: {\n relationship,\n joinFields: relationship.joinFields,\n },\n animated: false,\n style: {\n stroke: getRelationshipColor(relationship.relationship),\n strokeWidth: 2,\n },\n })\n })\n }\n })\n\n return { nodes, edges }\n }, [meta, onCubeClick, onFieldClick, savedPositions])\n\n // Apply auto-layout when explicitly requested or if no saved positions\n const needsAutoLayout = autoLayoutRequested || Object.keys(savedPositions).length === 0\n \n // Get auto-layout result\n const { nodes: autoLayoutedNodes, edges: autoLayoutedEdges } = useERDLayout(baseNodes, baseEdges, {\n direction: 'LR',\n nodeWidth: 320,\n nodeHeight: 220,\n nodeSep: 100,\n rankSep: 200\n })\n \n // Use auto-layout or base nodes based on needsAutoLayout, and update selection data\n const layoutedNodes = useMemo(() => {\n const nodes = needsAutoLayout ? autoLayoutedNodes : baseNodes\n return nodes.map((node) => ({\n ...node,\n data: {\n ...node.data,\n isHighlighted: highlightedCubes.includes(node.id),\n highlightedFields: highlightedFields,\n searchTerm: searchTerm,\n }\n }))\n }, [needsAutoLayout, autoLayoutedNodes, baseNodes, highlightedCubes, highlightedFields, searchTerm])\n \n const layoutedEdges = needsAutoLayout ? autoLayoutedEdges : baseEdges\n \n // Reset auto-layout request and clear saved positions when auto-layout is applied\n useEffect(() => {\n if (autoLayoutRequested && layoutedNodes.length > 0) {\n // Clear saved positions so we use the new auto-layout positions\n setSavedPositions({})\n try {\n localStorage.removeItem('drizzle-cube-erd-node-positions')\n } catch {\n // Ignore localStorage errors\n }\n setAutoLayoutRequested(false)\n }\n }, [autoLayoutRequested, layoutedNodes])\n\n const [nodes, setNodes, onNodesChange] = useNodesState(layoutedNodes)\n const [edges, setEdges, onEdgesChange] = useEdgesState(layoutedEdges)\n\n\n // Sync React Flow nodes with layout changes\n useEffect(() => {\n setNodes(layoutedNodes)\n }, [layoutedNodes, setNodes])\n\n // Sync React Flow edges with changes\n useEffect(() => {\n setEdges(layoutedEdges)\n }, [layoutedEdges, setEdges])\n\n // Save node positions to localStorage when manually moved\n const handleNodesChange = useCallback((changes: any[]) => {\n onNodesChange(changes)\n \n // Check if any nodes were dragged and save positions\n const dragChanges = changes.filter(change => change.type === 'position' && change.dragging === false)\n if (dragChanges.length > 0) {\n setNodes((currentNodes) => {\n const newPositions: Record<string, {x: number, y: number}> = {}\n currentNodes.forEach(node => {\n if (node.position) {\n newPositions[node.id] = node.position\n }\n })\n \n // Save to localStorage\n try {\n localStorage.setItem('drizzle-cube-erd-node-positions', JSON.stringify(newPositions))\n } catch {\n // Ignore localStorage errors\n }\n \n setSavedPositions(newPositions)\n return currentNodes\n })\n }\n }, [onNodesChange, setNodes])\n\n const onConnect = useCallback(\n (params: Connection) => setEdges((eds) => addEdge(params, eds)),\n [setEdges]\n )\n\n // Handle right-click context menu\n const handleContextMenu = useCallback((event: MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n console.log('Context menu triggered at:', event.clientX, event.clientY) // Debug\n setContextMenu({\n x: event.clientX,\n y: event.clientY,\n })\n }, [])\n\n // Close context menu when clicking elsewhere\n const handleClick = useCallback(() => {\n if (contextMenu) {\n setContextMenu(null)\n }\n }, [contextMenu])\n\n // Handle auto layout from context menu\n const handleAutoLayout = useCallback(() => {\n setAutoLayoutRequested(true)\n setContextMenu(null)\n }, [])\n\n\n // Loading state\n if (metaLoading) {\n return (\n <div className={`flex items-center justify-center h-96 ${className}`}>\n <div className=\"text-center\">\n <div className=\"animate-spin rounded-full h-8 w-8 border-b-2 border-dc-accent mx-auto mb-2\"></div>\n <p className=\"text-dc-text-muted\">Loading cube schema...</p>\n </div>\n </div>\n )\n }\n\n // Error state\n if (metaError) {\n return (\n <div className={`flex items-center justify-center h-96 ${className}`}>\n <div className=\"text-center text-dc-error\">\n <p className=\"font-medium\">Failed to load cube schema</p>\n <p className=\"text-sm mt-1\">{metaError}</p>\n </div>\n </div>\n )\n }\n\n // Empty state\n if (!meta || meta.cubes.length === 0) {\n return (\n <div className={`flex items-center justify-center h-96 ${className}`}>\n <div className=\"text-center text-dc-text-muted\">\n <p className=\"font-medium\">No cubes found</p>\n <p className=\"text-sm mt-1\">Register some cubes to see the relationship diagram</p>\n </div>\n </div>\n )\n }\n\n return (\n <div className={`h-full ${className}`} style={{ height: '600px' }}>\n <div className=\"h-full\">\n <ReactFlow\n nodes={nodes}\n edges={edges}\n onNodesChange={handleNodesChange}\n onEdgesChange={onEdgesChange}\n onConnect={onConnect}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n connectionMode={ConnectionMode.Loose}\n fitView // Always fit view to show entire ERD\n minZoom={0.1}\n maxZoom={2}\n defaultViewport={{ x: 0, y: 0, zoom: 0.8 }}\n proOptions={{ hideAttribution: true }}\n onPaneContextMenu={handleContextMenu}\n onPaneClick={handleClick}\n >\n <Controls />\n <MiniMap \n nodeColor={(node) => {\n const isHighlighted = highlightedCubes.includes(node.id)\n return isHighlighted ? '#8b5cf6' : '#e5e7eb'\n }}\n maskColor=\"rgb(240, 242, 246, 0.7)\"\n />\n <Background variant={BackgroundVariant.Dots} gap={12} size={1} />\n </ReactFlow>\n </div>\n\n {/* Context Menu */}\n {contextMenu && (\n <div\n className=\"fixed z-50 bg-dc-surface rounded-md shadow-lg border border-dc-border py-1 min-w-[120px]\"\n style={{\n left: contextMenu.x,\n top: contextMenu.y,\n }}\n >\n <button\n onClick={handleAutoLayout}\n className=\"w-full px-3 py-2 text-sm text-dc-text-secondary hover:bg-dc-surface-hover text-left\"\n >\n Auto Layout\n </button>\n </div>\n )}\n </div>\n )\n}\n\n// Helper function to get relationship colors\nfunction getRelationshipColor(relationship: 'belongsTo' | 'hasOne' | 'hasMany'): string {\n switch (relationship) {\n case 'belongsTo':\n return '#10b981' // green\n case 'hasOne':\n return '#3b82f6' // blue\n case 'hasMany':\n return '#f59e0b' // amber\n default:\n return '#6b7280' // gray\n }\n}\n\nexport default CubeRelationshipDiagram"],"names":["CubeNode","data","cube","onFieldClick","isHighlighted","highlightedFields","searchTerm","handleCubeHeaderClick","handleFieldClick","fieldName","fieldType","isFieldHighlighted","fullFieldName","isFieldSearchMatch","field","term","hasCubeMatches","measureMatches","measure","dimensionMatches","dimension","getFieldVisibilityClasses","isSearchMatch","baseClasses","jsxs","jsx","highlighted","d","Handle","Position","RelationshipEdge","sourceX","sourceY","targetX","targetY","sourcePosition","targetPosition","style","markerEnd","edgePath","labelX","labelY","getBezierPath","relationship","joinFields","getRelationshipSymbol","rel","color","symbol","Fragment","BaseEdge","EdgeLabelRenderer","index","defaultOptions","useERDLayout","nodes","edges","options","layoutOptions","useMemo","layoutedNodes","layoutedEdges","dagreGraph","dagre","node","edge","nodeWithPosition","getTargetPosition","getSourcePosition","direction","nodeTypes","edgeTypes","CubeRelationshipDiagram","className","onCubeClick","highlightedCubes","meta","metaLoading","metaError","useCubeContext","savedPositions","setSavedPositions","useState","autoLayoutRequested","setAutoLayoutRequested","contextMenu","setContextMenu","useEffect","savedPos","baseNodes","baseEdges","edgeId","getRelationshipColor","needsAutoLayout","autoLayoutedNodes","autoLayoutedEdges","setNodes","onNodesChange","useNodesState","setEdges","onEdgesChange","useEdgesState","handleNodesChange","useCallback","changes","change","currentNodes","newPositions","onConnect","params","eds","addEdge","handleContextMenu","event","handleClick","handleAutoLayout","ReactFlow","ConnectionMode","Controls","MiniMap","Background","BackgroundVariant"],"mappings":"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"}
1
+ {"version":3,"file":"index-9x0R-Fme.js","sources":["../../../src/client/components/CubeRelationshipDiagram/CubeNode.tsx","../../../src/client/components/CubeRelationshipDiagram/RelationshipEdge.tsx","../../../src/client/components/CubeRelationshipDiagram/useERDLayout.ts","../../../src/client/components/CubeRelationshipDiagram/index.tsx"],"sourcesContent":["import { Handle, Position } from 'reactflow'\nimport type { CubeMetaCube } from '../../hooks/useCubeMeta'\n\ninterface CubeNodeProps {\n data: {\n cube: CubeMetaCube\n onCubeClick?: (cubeName: string) => void\n onFieldClick?: (cubeName: string, fieldName: string, fieldType: 'measure' | 'dimension') => void\n isHighlighted: boolean\n highlightedFields: string[]\n searchTerm?: string\n }\n}\n\nexport function CubeNode({ data }: CubeNodeProps) {\n const { cube, onFieldClick, isHighlighted, highlightedFields, searchTerm } = data\n\n const handleCubeHeaderClick = () => {\n // Do nothing - disable cube header clicks in ERD\n }\n\n const handleFieldClick = (fieldName: string, fieldType: 'measure' | 'dimension') => {\n if (onFieldClick) {\n onFieldClick(cube.name, fieldName, fieldType)\n }\n }\n\n const isFieldHighlighted = (fullFieldName: string) => {\n return highlightedFields.includes(fullFieldName)\n }\n\n const isFieldSearchMatch = (field: { name: string; title?: string }) => {\n if (!searchTerm?.trim()) return true // No search term, show all fields\n \n const term = searchTerm.toLowerCase()\n return (\n field.name.toLowerCase().includes(term) ||\n (field.title && field.title.toLowerCase().includes(term))\n )\n }\n\n // Check if the cube has any matching fields\n const cubeHasMatches = () => {\n if (!searchTerm?.trim()) return true // No search term, show all cubes normally\n \n // Check if any measure matches\n const measureMatches = cube.measures.some(measure => isFieldSearchMatch(measure))\n \n // Check if any dimension matches\n const dimensionMatches = cube.dimensions.some(dimension => isFieldSearchMatch(dimension))\n \n return measureMatches || dimensionMatches\n }\n\n const hasCubeMatches = cubeHasMatches()\n\n const getFieldVisibilityClasses = (field: { name: string; title?: string; type?: string }, isHighlighted: boolean, fieldType: 'measure' | 'dimension') => {\n const isSearchMatch = isFieldSearchMatch(field)\n const baseClasses = 'px-4 py-2 text-xs cursor-pointer transition-all border-b border-dc-border last:border-b-0'\n\n // If the whole cube has no matches, rely on cube-level fading\n if (!hasCubeMatches && searchTerm?.trim()) {\n // Still show selected field highlighting even in faded cubes\n if (isHighlighted) {\n if (fieldType === 'measure') {\n return `${baseClasses} bg-dc-warning-bg text-dc-warning`\n } else if (fieldType === 'dimension') {\n if (field.type === 'time') {\n return `${baseClasses} bg-dc-accent-bg text-dc-accent`\n } else {\n return `${baseClasses} bg-dc-success-bg text-dc-success`\n }\n }\n }\n return `${baseClasses} hover:bg-dc-surface-hover text-dc-text-secondary`\n }\n\n // If searching and this specific field doesn't match, make it faded\n if (searchTerm?.trim() && !isSearchMatch) {\n return `${baseClasses} opacity-40 hover:opacity-60 text-dc-text-muted`\n }\n\n // If searching and this field matches, make it prominent with bold purple text\n if (searchTerm?.trim() && isSearchMatch && !isHighlighted) {\n return `${baseClasses} font-bold hover:bg-dc-accent-bg`\n }\n\n // Normal highlighting behavior for selected fields (takes priority over search match styling)\n if (isHighlighted) {\n if (fieldType === 'measure') {\n return `${baseClasses} bg-dc-warning-bg text-dc-warning font-semibold`\n } else if (fieldType === 'dimension') {\n // Check if this is a time dimension\n if (field.type === 'time') {\n return `${baseClasses} bg-dc-accent-bg text-dc-accent font-semibold` // time dimensions\n } else {\n return `${baseClasses} bg-dc-success-bg text-dc-success font-semibold` // regular dimensions\n }\n }\n }\n\n return `${baseClasses} hover:bg-dc-surface-hover text-dc-text-secondary`\n }\n\n return (\n <div\n className={`\n border-2 rounded-lg shadow-lg min-w-[280px] overflow-hidden transition-all\n ${!hasCubeMatches && searchTerm?.trim() ? 'opacity-30 grayscale' : ''}\n ${isHighlighted ? 'border-dc-accent ring-2 ring-dc-accent' : 'border-dc-border'}\n `}\n style={{\n backgroundColor: 'var(--dc-surface)'\n }}\n >\n {/* Cube Header */}\n <div\n className={`\n px-4 py-3 cursor-pointer transition-colors\n ${isHighlighted ? 'bg-dc-accent-bg hover:bg-dc-accent-bg' : 'bg-dc-surface-secondary hover:bg-dc-surface-hover'}\n `}\n onClick={handleCubeHeaderClick}\n >\n <div className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"font-semibold text-dc-text text-sm\">\n {cube.title || cube.name}\n </h3>\n {cube.description && (\n <p className=\"text-xs text-dc-text-muted mt-1 line-clamp-2\">\n {cube.description}\n </p>\n )}\n </div>\n <div className=\"text-xs text-dc-text-muted ml-2\">\n <div>{cube.measures.length}M</div>\n <div>{cube.dimensions.length}D</div>\n </div>\n </div>\n </div>\n\n {/* Measures Section */}\n {cube.measures.length > 0 && (\n <div className=\"border-t border-dc-border\">\n <div className=\"px-4 py-2 bg-dc-warning-bg border-b border-dc-border\">\n <h4 className=\"text-xs font-medium text-dc-warning flex items-center\">\n <span className=\"w-2 h-2 bg-dc-warning-bg0 rounded-full mr-2\"></span>\n Measures ({cube.measures.length})\n </h4>\n </div>\n <div className=\"max-h-32 overflow-y-auto\">\n {cube.measures.map((measure) => {\n const fieldName = measure.name.split('.')[1] || measure.name\n const highlighted = isFieldHighlighted(measure.name)\n return (\n <div\n key={measure.name}\n className={getFieldVisibilityClasses(measure, highlighted, 'measure')}\n onClick={() => handleFieldClick(fieldName, 'measure')}\n title={measure.title}\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"font-mono truncate\">\n {measure.shortTitle || measure.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted ml-2 text-[10px] uppercase\">\n {measure.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Time Dimensions Section */}\n {cube.dimensions.filter(d => d.type === 'time').length > 0 && (\n <div className=\"border-t border-dc-border\">\n <div className=\"px-4 py-2 bg-dc-accent-bg border-b border-dc-border\">\n <h4 className=\"text-xs font-medium text-dc-accent flex items-center\">\n <span className=\"w-2 h-2 bg-dc-accent-bg0 rounded-full mr-2\"></span>\n Time Dimensions ({cube.dimensions.filter(d => d.type === 'time').length})\n </h4>\n </div>\n <div className=\"max-h-32 overflow-y-auto\">\n {cube.dimensions.filter(d => d.type === 'time').map((dimension) => {\n const fieldName = dimension.name.split('.')[1] || dimension.name\n const highlighted = isFieldHighlighted(dimension.name)\n return (\n <div\n key={dimension.name}\n className={getFieldVisibilityClasses(dimension, highlighted, 'dimension')}\n onClick={() => handleFieldClick(fieldName, 'dimension')}\n title={dimension.title}\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"font-mono truncate\">\n {dimension.shortTitle || dimension.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted ml-2 text-[10px] uppercase\">\n {dimension.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Dimensions Section (non-time) */}\n {cube.dimensions.filter(d => d.type !== 'time').length > 0 && (\n <div className=\"border-t border-dc-border\">\n <div className=\"px-4 py-2 bg-dc-success-bg border-b border-dc-border\">\n <h4 className=\"text-xs font-medium text-dc-success flex items-center\">\n <span className=\"w-2 h-2 bg-dc-success-bg0 rounded-full mr-2\"></span>\n Dimensions ({cube.dimensions.filter(d => d.type !== 'time').length})\n </h4>\n </div>\n <div className=\"max-h-32 overflow-y-auto\">\n {cube.dimensions.filter(d => d.type !== 'time').map((dimension) => {\n const fieldName = dimension.name.split('.')[1] || dimension.name\n const highlighted = isFieldHighlighted(dimension.name)\n return (\n <div\n key={dimension.name}\n className={getFieldVisibilityClasses(dimension, highlighted, 'dimension')}\n onClick={() => handleFieldClick(fieldName, 'dimension')}\n title={dimension.title}\n >\n <div className=\"flex items-center justify-between\">\n <span className=\"font-mono truncate\">\n {dimension.shortTitle || dimension.title || fieldName}\n </span>\n <span className=\"text-dc-text-muted ml-2 text-[10px] uppercase\">\n {dimension.type}\n </span>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Connection handles for relationships - hidden */}\n <Handle\n type=\"source\"\n position={Position.Right}\n id=\"right\"\n className=\"opacity-0\"\n isConnectable={false}\n />\n <Handle\n type=\"target\"\n position={Position.Left}\n id=\"left\"\n className=\"opacity-0\"\n isConnectable={false}\n />\n <Handle\n type=\"source\"\n position={Position.Bottom}\n id=\"bottom\"\n className=\"opacity-0\"\n isConnectable={false}\n />\n <Handle\n type=\"target\"\n position={Position.Top}\n id=\"top\"\n className=\"opacity-0\"\n isConnectable={false}\n />\n </div>\n )\n}\n\nexport default CubeNode","import { \n EdgeProps, \n getBezierPath, \n EdgeLabelRenderer,\n BaseEdge,\n} from 'reactflow'\nimport type { CubeMetaRelationship } from '../../hooks/useCubeMeta'\n\ninterface RelationshipEdgeData {\n relationship: CubeMetaRelationship\n joinFields: Array<{\n sourceField: string\n targetField: string\n }>\n}\n\nexport function RelationshipEdge({\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n style = {},\n data,\n markerEnd,\n}: EdgeProps<RelationshipEdgeData>) {\n const [edgePath, labelX, labelY] = getBezierPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n })\n\n if (!data) return null\n\n const { relationship, joinFields } = data\n\n // Get relationship symbols and colors\n const getRelationshipSymbol = (rel: string) => {\n switch (rel) {\n case 'belongsTo':\n return '∈' // belongs to symbol\n case 'hasOne':\n return '1:1'\n case 'hasMany':\n return '1:M'\n default:\n return '?'\n }\n }\n\n const getRelationshipColor = (rel: string) => {\n switch (rel) {\n case 'belongsTo':\n return '#10b981' // green\n case 'hasOne':\n return '#3b82f6' // blue\n case 'hasMany':\n return '#f59e0b' // amber\n default:\n return '#6b7280' // gray\n }\n }\n\n const color = getRelationshipColor(relationship.relationship)\n const symbol = getRelationshipSymbol(relationship.relationship)\n\n return (\n <>\n <BaseEdge path={edgePath} markerEnd={markerEnd} style={{ ...style, stroke: color }} />\n <EdgeLabelRenderer>\n <div\n style={{\n position: 'absolute',\n transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n fontSize: 10,\n pointerEvents: 'all',\n }}\n className=\"nodrag nopan\"\n >\n <div\n className=\"border-2 rounded-md px-2 py-1 shadow-xs\"\n style={{\n backgroundColor: 'var(--dc-surface)',\n borderColor: color\n }}\n >\n <div className=\"text-center\">\n <div\n className=\"font-bold text-xs mb-1\"\n style={{ color }}\n >\n {symbol}\n </div>\n <div className=\"text-[9px] text-dc-text-muted leading-tight\">\n {joinFields.map((field, index) => (\n <div key={index} className=\"font-mono\">\n {field.sourceField} → {field.targetField}\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n </EdgeLabelRenderer>\n </>\n )\n}\n\nexport default RelationshipEdge","import { useMemo } from 'react'\nimport { Node, Edge, Position } from 'reactflow'\nimport dagre from 'dagre'\n\nexport interface LayoutOptions {\n direction: 'TB' | 'BT' | 'LR' | 'RL'\n nodeWidth: number\n nodeHeight: number\n nodeSep: number\n rankSep: number\n ranker: 'network-simplex' | 'tight-tree' | 'longest-path'\n}\n\nconst defaultOptions: LayoutOptions = {\n direction: 'TB',\n nodeWidth: 320,\n nodeHeight: 220,\n nodeSep: 80,\n rankSep: 150,\n ranker: 'network-simplex',\n}\n\nexport function useERDLayout(\n nodes: Node[],\n edges: Edge[],\n options: Partial<LayoutOptions> = {}\n): { nodes: Node[], edges: Edge[] } {\n const layoutOptions = useMemo(() => ({ ...defaultOptions, ...options }), [options])\n\n const { layoutedNodes, layoutedEdges } = useMemo(() => {\n if (nodes.length === 0) {\n return { layoutedNodes: [], layoutedEdges: [] }\n }\n\n // Create a new dagre graph\n const dagreGraph = new dagre.graphlib.Graph()\n dagreGraph.setDefaultEdgeLabel(() => ({}))\n dagreGraph.setGraph({ \n rankdir: layoutOptions.direction,\n nodesep: layoutOptions.nodeSep,\n ranksep: layoutOptions.rankSep,\n ranker: layoutOptions.ranker,\n })\n\n // Add nodes to the graph\n nodes.forEach((node) => {\n dagreGraph.setNode(node.id, { \n width: layoutOptions.nodeWidth, \n height: layoutOptions.nodeHeight \n })\n })\n\n // Add edges to the graph\n edges.forEach((edge) => {\n dagreGraph.setEdge(edge.source, edge.target)\n })\n\n // Run the layout algorithm\n dagre.layout(dagreGraph)\n\n // Apply the calculated positions to nodes\n const layoutedNodes = nodes.map((node) => {\n const nodeWithPosition = dagreGraph.node(node.id)\n \n const newNode = {\n ...node,\n targetPosition: getTargetPosition(layoutOptions.direction),\n sourcePosition: getSourcePosition(layoutOptions.direction),\n position: {\n x: nodeWithPosition.x - layoutOptions.nodeWidth / 2,\n y: nodeWithPosition.y - layoutOptions.nodeHeight / 2,\n },\n }\n\n return newNode\n })\n\n return { \n layoutedNodes, \n layoutedEdges: edges \n }\n }, [nodes, edges, layoutOptions.direction, layoutOptions.nodeSep, layoutOptions.rankSep, layoutOptions.ranker, layoutOptions.nodeWidth, layoutOptions.nodeHeight])\n\n return { nodes: layoutedNodes, edges: layoutedEdges }\n}\n\n// Helper functions to determine handle positions based on layout direction\nfunction getTargetPosition(direction: string): Position {\n switch (direction) {\n case 'TB':\n return Position.Top\n case 'BT':\n return Position.Bottom\n case 'LR':\n return Position.Left\n case 'RL':\n return Position.Right\n default:\n return Position.Top\n }\n}\n\nfunction getSourcePosition(direction: string): Position {\n switch (direction) {\n case 'TB':\n return Position.Bottom\n case 'BT':\n return Position.Top\n case 'LR':\n return Position.Right\n case 'RL':\n return Position.Left\n default:\n return Position.Bottom\n }\n}\n\n// Custom layout function for manual positioning with smart defaults\nexport function useManualLayout(\n nodes: Node[],\n edges: Edge[],\n spacing = { x: 400, y: 300 }\n): { nodes: Node[], edges: Edge[] } {\n return useMemo(() => {\n if (nodes.length === 0) {\n return { nodes: [], edges }\n }\n\n // Simple grid layout as fallback\n const layoutedNodes = nodes.map((node, index) => ({\n ...node,\n position: {\n x: (index % 3) * spacing.x,\n y: Math.floor(index / 3) * spacing.y,\n },\n sourcePosition: Position.Right,\n targetPosition: Position.Left,\n }))\n\n return { nodes: layoutedNodes, edges }\n }, [nodes, edges, spacing])\n}\n\nexport default useERDLayout","import { useCallback, useMemo, useEffect, useState, MouseEvent } from 'react'\nimport ReactFlow, {\n Node,\n Edge,\n addEdge,\n Connection,\n useNodesState,\n useEdgesState,\n Controls,\n MiniMap,\n Background,\n BackgroundVariant,\n ConnectionMode,\n} from 'reactflow'\nimport 'reactflow/dist/style.css'\n\nimport { useCubeContext } from '../../providers/CubeProvider'\nimport { CubeNode } from './CubeNode'\nimport { RelationshipEdge } from './RelationshipEdge'\nimport { useERDLayout } from './useERDLayout'\n\n// Define the custom node and edge types OUTSIDE the component to prevent re-creation\nconst nodeTypes = {\n cubeNode: CubeNode,\n}\n\nconst edgeTypes = {\n relationshipEdge: RelationshipEdge,\n}\n\ninterface CubeRelationshipDiagramProps {\n className?: string\n onCubeClick?: (cubeName: string) => void\n onFieldClick?: (cubeName: string, fieldName: string, fieldType: 'measure' | 'dimension') => void\n highlightedCubes?: string[]\n highlightedFields?: string[]\n searchTerm?: string\n}\n\nexport function CubeRelationshipDiagram({\n className = '',\n onCubeClick,\n onFieldClick,\n highlightedCubes = [],\n highlightedFields = [],\n searchTerm,\n}: CubeRelationshipDiagramProps) {\n const { meta, metaLoading, metaError } = useCubeContext()\n\n const [savedPositions, setSavedPositions] = useState<Record<string, {x: number, y: number}>>({})\n const [autoLayoutRequested, setAutoLayoutRequested] = useState(false)\n const [contextMenu, setContextMenu] = useState<{x: number, y: number} | null>(null)\n\n // Load saved positions from localStorage on mount\n useEffect(() => {\n try {\n const savedPos = localStorage.getItem('drizzle-cube-erd-node-positions')\n if (savedPos) {\n setSavedPositions(JSON.parse(savedPos))\n }\n } catch {\n // Ignore localStorage errors\n }\n }, [])\n\n // Create base nodes and edges structure (without selection data)\n const { nodes: baseNodes, edges: baseEdges } = useMemo(() => {\n if (!meta) return { nodes: [], edges: [] }\n\n const nodes: Node[] = []\n const edges: Edge[] = []\n\n // Create nodes for each cube\n meta.cubes.forEach((cube, index) => {\n nodes.push({\n id: cube.name,\n type: 'cubeNode',\n position: savedPositions[cube.name] || { x: (index % 3) * 400, y: Math.floor(index / 3) * 300 },\n data: {\n cube,\n onCubeClick,\n onFieldClick,\n isHighlighted: false, // Will be updated separately\n highlightedFields: [], // Will be updated separately\n },\n })\n })\n\n // Create edges for relationships (excluding belongsTo)\n meta.cubes.forEach(cube => {\n if (cube.relationships) {\n cube.relationships.forEach((relationship, index) => {\n // Skip belongsTo relationships\n if (relationship.relationship === 'belongsTo') {\n return\n }\n \n const edgeId = `${cube.name}-${relationship.targetCube}-${index}`\n edges.push({\n id: edgeId,\n source: cube.name,\n target: relationship.targetCube,\n type: 'relationshipEdge',\n data: {\n relationship,\n joinFields: relationship.joinFields,\n },\n animated: false,\n style: {\n stroke: getRelationshipColor(relationship.relationship),\n strokeWidth: 2,\n },\n })\n })\n }\n })\n\n return { nodes, edges }\n }, [meta, onCubeClick, onFieldClick, savedPositions])\n\n // Apply auto-layout when explicitly requested or if no saved positions\n const needsAutoLayout = autoLayoutRequested || Object.keys(savedPositions).length === 0\n \n // Get auto-layout result\n const { nodes: autoLayoutedNodes, edges: autoLayoutedEdges } = useERDLayout(baseNodes, baseEdges, {\n direction: 'LR',\n nodeWidth: 320,\n nodeHeight: 220,\n nodeSep: 100,\n rankSep: 200\n })\n \n // Use auto-layout or base nodes based on needsAutoLayout, and update selection data\n const layoutedNodes = useMemo(() => {\n const nodes = needsAutoLayout ? autoLayoutedNodes : baseNodes\n return nodes.map((node) => ({\n ...node,\n data: {\n ...node.data,\n isHighlighted: highlightedCubes.includes(node.id),\n highlightedFields: highlightedFields,\n searchTerm: searchTerm,\n }\n }))\n }, [needsAutoLayout, autoLayoutedNodes, baseNodes, highlightedCubes, highlightedFields, searchTerm])\n \n const layoutedEdges = needsAutoLayout ? autoLayoutedEdges : baseEdges\n \n // Reset auto-layout request and clear saved positions when auto-layout is applied\n useEffect(() => {\n if (autoLayoutRequested && layoutedNodes.length > 0) {\n // Clear saved positions so we use the new auto-layout positions\n setSavedPositions({})\n try {\n localStorage.removeItem('drizzle-cube-erd-node-positions')\n } catch {\n // Ignore localStorage errors\n }\n setAutoLayoutRequested(false)\n }\n }, [autoLayoutRequested, layoutedNodes])\n\n const [nodes, setNodes, onNodesChange] = useNodesState(layoutedNodes)\n const [edges, setEdges, onEdgesChange] = useEdgesState(layoutedEdges)\n\n\n // Sync React Flow nodes with layout changes\n useEffect(() => {\n setNodes(layoutedNodes)\n }, [layoutedNodes, setNodes])\n\n // Sync React Flow edges with changes\n useEffect(() => {\n setEdges(layoutedEdges)\n }, [layoutedEdges, setEdges])\n\n // Save node positions to localStorage when manually moved\n const handleNodesChange = useCallback((changes: any[]) => {\n onNodesChange(changes)\n \n // Check if any nodes were dragged and save positions\n const dragChanges = changes.filter(change => change.type === 'position' && change.dragging === false)\n if (dragChanges.length > 0) {\n setNodes((currentNodes) => {\n const newPositions: Record<string, {x: number, y: number}> = {}\n currentNodes.forEach(node => {\n if (node.position) {\n newPositions[node.id] = node.position\n }\n })\n \n // Save to localStorage\n try {\n localStorage.setItem('drizzle-cube-erd-node-positions', JSON.stringify(newPositions))\n } catch {\n // Ignore localStorage errors\n }\n \n setSavedPositions(newPositions)\n return currentNodes\n })\n }\n }, [onNodesChange, setNodes])\n\n const onConnect = useCallback(\n (params: Connection) => setEdges((eds) => addEdge(params, eds)),\n [setEdges]\n )\n\n // Handle right-click context menu\n const handleContextMenu = useCallback((event: MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n console.log('Context menu triggered at:', event.clientX, event.clientY) // Debug\n setContextMenu({\n x: event.clientX,\n y: event.clientY,\n })\n }, [])\n\n // Close context menu when clicking elsewhere\n const handleClick = useCallback(() => {\n if (contextMenu) {\n setContextMenu(null)\n }\n }, [contextMenu])\n\n // Handle auto layout from context menu\n const handleAutoLayout = useCallback(() => {\n setAutoLayoutRequested(true)\n setContextMenu(null)\n }, [])\n\n\n // Loading state\n if (metaLoading) {\n return (\n <div className={`flex items-center justify-center h-96 ${className}`}>\n <div className=\"text-center\">\n <div className=\"animate-spin rounded-full h-8 w-8 border-b-2 border-dc-accent mx-auto mb-2\"></div>\n <p className=\"text-dc-text-muted\">Loading cube schema...</p>\n </div>\n </div>\n )\n }\n\n // Error state\n if (metaError) {\n return (\n <div className={`flex items-center justify-center h-96 ${className}`}>\n <div className=\"text-center text-dc-error\">\n <p className=\"font-medium\">Failed to load cube schema</p>\n <p className=\"text-sm mt-1\">{metaError}</p>\n </div>\n </div>\n )\n }\n\n // Empty state\n if (!meta || meta.cubes.length === 0) {\n return (\n <div className={`flex items-center justify-center h-96 ${className}`}>\n <div className=\"text-center text-dc-text-muted\">\n <p className=\"font-medium\">No cubes found</p>\n <p className=\"text-sm mt-1\">Register some cubes to see the relationship diagram</p>\n </div>\n </div>\n )\n }\n\n return (\n <div className={`h-full ${className}`} style={{ height: '600px' }}>\n <div className=\"h-full\">\n <ReactFlow\n nodes={nodes}\n edges={edges}\n onNodesChange={handleNodesChange}\n onEdgesChange={onEdgesChange}\n onConnect={onConnect}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n connectionMode={ConnectionMode.Loose}\n fitView // Always fit view to show entire ERD\n minZoom={0.1}\n maxZoom={2}\n defaultViewport={{ x: 0, y: 0, zoom: 0.8 }}\n proOptions={{ hideAttribution: true }}\n onPaneContextMenu={handleContextMenu}\n onPaneClick={handleClick}\n >\n <Controls />\n <MiniMap \n nodeColor={(node) => {\n const isHighlighted = highlightedCubes.includes(node.id)\n return isHighlighted ? '#8b5cf6' : '#e5e7eb'\n }}\n maskColor=\"rgb(240, 242, 246, 0.7)\"\n />\n <Background variant={BackgroundVariant.Dots} gap={12} size={1} />\n </ReactFlow>\n </div>\n\n {/* Context Menu */}\n {contextMenu && (\n <div\n className=\"fixed z-50 bg-dc-surface rounded-md shadow-lg border border-dc-border py-1 min-w-[120px]\"\n style={{\n left: contextMenu.x,\n top: contextMenu.y,\n }}\n >\n <button\n onClick={handleAutoLayout}\n className=\"w-full px-3 py-2 text-sm text-dc-text-secondary hover:bg-dc-surface-hover text-left\"\n >\n Auto Layout\n </button>\n </div>\n )}\n </div>\n )\n}\n\n// Helper function to get relationship colors\nfunction getRelationshipColor(relationship: 'belongsTo' | 'hasOne' | 'hasMany'): string {\n switch (relationship) {\n case 'belongsTo':\n return '#10b981' // green\n case 'hasOne':\n return '#3b82f6' // blue\n case 'hasMany':\n return '#f59e0b' // amber\n default:\n return '#6b7280' // gray\n }\n}\n\nexport default CubeRelationshipDiagram"],"names":["CubeNode","data","cube","onFieldClick","isHighlighted","highlightedFields","searchTerm","handleCubeHeaderClick","handleFieldClick","fieldName","fieldType","isFieldHighlighted","fullFieldName","isFieldSearchMatch","field","term","hasCubeMatches","measureMatches","measure","dimensionMatches","dimension","getFieldVisibilityClasses","isSearchMatch","baseClasses","jsxs","jsx","highlighted","d","Handle","Position","RelationshipEdge","sourceX","sourceY","targetX","targetY","sourcePosition","targetPosition","style","markerEnd","edgePath","labelX","labelY","getBezierPath","relationship","joinFields","getRelationshipSymbol","rel","color","symbol","Fragment","BaseEdge","EdgeLabelRenderer","index","defaultOptions","useERDLayout","nodes","edges","options","layoutOptions","useMemo","layoutedNodes","layoutedEdges","dagreGraph","dagre","node","edge","nodeWithPosition","getTargetPosition","getSourcePosition","direction","nodeTypes","edgeTypes","CubeRelationshipDiagram","className","onCubeClick","highlightedCubes","meta","metaLoading","metaError","useCubeContext","savedPositions","setSavedPositions","useState","autoLayoutRequested","setAutoLayoutRequested","contextMenu","setContextMenu","useEffect","savedPos","baseNodes","baseEdges","edgeId","getRelationshipColor","needsAutoLayout","autoLayoutedNodes","autoLayoutedEdges","setNodes","onNodesChange","useNodesState","setEdges","onEdgesChange","useEdgesState","handleNodesChange","useCallback","changes","change","currentNodes","newPositions","onConnect","params","eds","addEdge","handleContextMenu","event","handleClick","handleAutoLayout","ReactFlow","ConnectionMode","Controls","MiniMap","Background","BackgroundVariant"],"mappings":"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"}
@@ -1,11 +1,12 @@
1
+ import { default as React } from 'react';
1
2
  import { AnalysisQueryPanelProps } from './types';
2
3
  /**
3
4
  * AnalysisQueryPanel displays the right-side query builder with:
4
- * - Query/Chart tab switcher
5
+ * - Query/Chart tab switcher (with multi-query support)
5
6
  * - Metrics section (measures)
6
7
  * - Filter section
7
8
  * - Breakdown section (dimensions)
8
9
  * - Chart configuration (in Chart tab)
9
10
  */
10
- declare const AnalysisQueryPanel: import('react').NamedExoticComponent<AnalysisQueryPanelProps>;
11
+ declare const AnalysisQueryPanel: React.NamedExoticComponent<AnalysisQueryPanelProps>;
11
12
  export default AnalysisQueryPanel;
@@ -1,8 +1,9 @@
1
1
  import { MouseEvent, DragEvent } from 'react';
2
- import { CubeQuery, Filter, ChartType, ChartAxisConfig, ChartDisplayConfig } from '../../types';
2
+ import { CubeQuery, Filter, ChartType, ChartAxisConfig, ChartDisplayConfig, MultiQueryConfig } from '../../types';
3
3
  import { ColorPalette } from '../../utils/colorPalettes';
4
4
  import { MetaResponse, MetaField, MetaCube, QueryAnalysis } from '../../shared/types';
5
5
  import { ChartAvailabilityMap } from '../../shared/chartDefaults';
6
+ import { MultiQueryValidationResult } from '../../utils/multiQueryValidation';
6
7
  export type { MetaResponse, MetaField, MetaCube, QueryAnalysis };
7
8
  /**
8
9
  * A selected metric (measure) with a letter label (A, B, C, ...)
@@ -44,6 +45,8 @@ export interface AnalysisBuilderState {
44
45
  breakdowns: BreakdownItem[];
45
46
  /** Applied filters */
46
47
  filters: Filter[];
48
+ /** Sort order for this query (field name -> 'asc' | 'desc') */
49
+ order?: Record<string, 'asc' | 'desc'>;
47
50
  validationStatus: ValidationStatus;
48
51
  validationError: string | null;
49
52
  executionStatus: ExecutionStatus;
@@ -190,6 +193,28 @@ export interface AnalysisQueryPanelProps {
190
193
  onDisplayConfigChange: (config: ChartDisplayConfig) => void;
191
194
  validationStatus: ValidationStatus;
192
195
  validationError: string | null;
196
+ /** Number of queries (determines single vs multi-query display) */
197
+ queryCount?: number;
198
+ /** Index of the currently active query tab */
199
+ activeQueryIndex?: number;
200
+ /** Strategy for merging results from multiple queries */
201
+ mergeStrategy?: 'concat' | 'merge';
202
+ /** Callback when active query tab changes */
203
+ onActiveQueryChange?: (index: number) => void;
204
+ /** Callback to add a new query */
205
+ onAddQuery?: () => void;
206
+ /** Callback to remove a query at specified index */
207
+ onRemoveQuery?: (index: number) => void;
208
+ /** Callback when merge strategy changes */
209
+ onMergeStrategyChange?: (strategy: 'concat' | 'merge') => void;
210
+ /** Whether breakdowns are locked (synced from Q1 in merge mode) */
211
+ breakdownsLocked?: boolean;
212
+ /** Combined metrics from all queries (for chart config in multi-query mode) */
213
+ combinedMetrics?: MetricItem[];
214
+ /** Combined breakdowns from all queries (for chart config in multi-query mode) */
215
+ combinedBreakdowns?: BreakdownItem[];
216
+ /** Validation result for multi-query mode (errors and warnings) */
217
+ multiQueryValidation?: MultiQueryValidationResult | null;
193
218
  }
194
219
  /**
195
220
  * Props for the AnalysisResultsPanel component
@@ -217,8 +242,8 @@ export interface AnalysisResultsPanelProps {
217
242
  currentPaletteName?: string;
218
243
  /** Callback when color palette changes (shows selector when provided) */
219
244
  onColorPaletteChange?: (paletteName: string) => void;
220
- /** Current query object (for annotation/metadata) */
221
- query: CubeQuery;
245
+ /** All queries for multi-query mode (used for table column headers per-query) */
246
+ allQueries?: CubeQuery[];
222
247
  /** Schema metadata */
223
248
  schema: MetaResponse | null;
224
249
  /** Active view (table or chart) */
@@ -231,14 +256,16 @@ export interface AnalysisResultsPanelProps {
231
256
  onDisplayLimitChange: (limit: number) => void;
232
257
  /** Whether the query has metrics (measures) - needed to enable/disable chart view */
233
258
  hasMetrics: boolean;
234
- debugQuery?: CubeQuery | null;
235
- debugSql?: {
236
- sql: string;
237
- params: any[];
238
- } | null;
239
- debugAnalysis?: QueryAnalysis | null;
240
- debugLoading?: boolean;
241
- debugError?: string | null;
259
+ /** Debug data for each query (SQL, analysis, loading/error state) */
260
+ debugDataPerQuery?: Array<{
261
+ sql: {
262
+ sql: string;
263
+ params: any[];
264
+ } | null;
265
+ analysis: QueryAnalysis | null;
266
+ loading: boolean;
267
+ error: string | null;
268
+ }>;
242
269
  onShareClick?: () => void;
243
270
  canShare?: boolean;
244
271
  shareButtonState?: 'idle' | 'copied' | 'copied-no-chart';
@@ -247,6 +274,14 @@ export interface AnalysisResultsPanelProps {
247
274
  enableAI?: boolean;
248
275
  isAIOpen?: boolean;
249
276
  onAIToggle?: () => void;
277
+ /** Number of queries (for showing Table 1, Table 2 tabs) */
278
+ queryCount?: number;
279
+ /** Per-query results (for table view in multi-query mode) */
280
+ perQueryResults?: (any[] | null)[];
281
+ /** Active table index in multi-query mode */
282
+ activeTableIndex?: number;
283
+ /** Callback when active table changes */
284
+ onActiveTableChange?: (index: number) => void;
250
285
  }
251
286
  /**
252
287
  * Props for the MetricsSection component
@@ -378,8 +413,12 @@ export interface AnalysisBuilderProps {
378
413
  className?: string;
379
414
  /** Maximum height for the component (e.g., '800px', '100vh', 'calc(100vh - 64px)') */
380
415
  maxHeight?: string;
381
- /** Initial query to load */
382
- initialQuery?: CubeQuery;
416
+ /**
417
+ * Initial query configuration to load.
418
+ * Accepts either a single CubeQuery or a MultiQueryConfig - the component handles both internally.
419
+ * This keeps multi-query complexity contained within AnalysisBuilder.
420
+ */
421
+ initialQuery?: CubeQuery | MultiQueryConfig;
383
422
  /** Initial chart configuration (for editing existing portlets) */
384
423
  initialChartConfig?: {
385
424
  chartType?: ChartType;
@@ -407,8 +446,12 @@ export interface AnalysisBuilderProps {
407
446
  * Ref interface for AnalysisBuilder (for external access)
408
447
  */
409
448
  export interface AnalysisBuilderRef {
410
- /** Get the current query object */
411
- getCurrentQuery: () => CubeQuery;
449
+ /**
450
+ * Get the current query configuration.
451
+ * Returns either a CubeQuery (single query) or MultiQueryConfig (multiple queries).
452
+ * Consumers should just JSON.stringify the result - no need to check the type.
453
+ */
454
+ getQueryConfig: () => CubeQuery | MultiQueryConfig;
412
455
  /** Get current chart configuration */
413
456
  getChartConfig: () => {
414
457
  chartType: ChartType;
@@ -432,6 +475,11 @@ export interface AnalysisBuilderStorageState {
432
475
  chartConfig: ChartAxisConfig;
433
476
  displayConfig: ChartDisplayConfig;
434
477
  activeView: 'table' | 'chart';
478
+ queryStates?: AnalysisBuilderState[];
479
+ activeQueryIndex?: number;
480
+ mergeStrategy?: 'concat' | 'merge';
481
+ /** Dimension keys used for merging in 'merge' strategy */
482
+ mergeKeys?: string[];
435
483
  }
436
484
  /**
437
485
  * Recent fields storage shape
@@ -12,6 +12,12 @@ interface DashboardPortletCardProps {
12
12
  queryObject: any;
13
13
  data: any[];
14
14
  chartType: string;
15
+ cacheInfo?: {
16
+ hit: true;
17
+ cachedAt: string;
18
+ ttlMs: number;
19
+ ttlRemainingMs: number;
20
+ };
15
21
  };
16
22
  dashboardFilters?: DashboardFilter[];
17
23
  configEagerLoad?: boolean;
@@ -31,6 +37,12 @@ interface DashboardPortletCardProps {
31
37
  queryObject: any;
32
38
  data: any[];
33
39
  chartType: string;
40
+ cacheInfo?: {
41
+ hit: true;
42
+ cachedAt: string;
43
+ ttlMs: number;
44
+ ttlRemainingMs: number;
45
+ };
34
46
  }) => void;
35
47
  setPortletRef: (portletId: string, element: HTMLDivElement | null) => void;
36
48
  setPortletComponentRef: (portletId: string, element: {
@@ -4,6 +4,12 @@ interface DebugModalProps {
4
4
  queryObject: any;
5
5
  data: any[];
6
6
  chartType: string;
7
+ cacheInfo?: {
8
+ hit: true;
9
+ cachedAt: string;
10
+ ttlMs: number;
11
+ ttlRemainingMs: number;
12
+ };
7
13
  }
8
- export default function DebugModal({ chartConfig, displayConfig, queryObject, data, chartType }: DebugModalProps): import("react/jsx-runtime").JSX.Element;
14
+ export default function DebugModal({ chartConfig, displayConfig, queryObject, data, chartType, cacheInfo }: DebugModalProps): import("react/jsx-runtime").JSX.Element;
9
15
  export {};
@@ -1,5 +1,5 @@
1
- import { a as t, A as s, d as o, D as r, M as d, P as l, b as i, Q as n } from "./chunks/components-D7wTB56l.js";
2
- import { c as b, j as u, h as y, e as c, d as h, v as D } from "./chunks/charts-DboFPJFN.js";
1
+ import { a as t, A as s, d as o, D as r, M as d, P as l, b as i, Q as n } from "./chunks/components-DhA8GIUY.js";
2
+ import { c as b, j as u, h as y, e as c, d as h, v as D } from "./chunks/charts-BsXrHSCm.js";
3
3
  export {
4
4
  t as AnalyticsDashboard,
5
5
  s as AnalyticsPortlet,
@@ -0,0 +1,36 @@
1
+ import { CubeQueryOptions, CubeResultSet, MultiQueryConfig } from '../types';
2
+ export interface UseMultiCubeQueryResult {
3
+ /** Merged data from all queries (null while loading) */
4
+ data: unknown[] | null;
5
+ /** Individual result sets from each query */
6
+ resultSets: CubeResultSet[] | null;
7
+ /** Whether any query is still loading */
8
+ isLoading: boolean;
9
+ /** First error encountered (null if all succeeded) */
10
+ error: Error | null;
11
+ /** Per-query errors (null for successful queries) */
12
+ errors: (Error | null)[];
13
+ /** Unique identifier for this query execution */
14
+ queryId: string | null;
15
+ }
16
+ /**
17
+ * Hook for executing multiple Cube queries with merged results
18
+ *
19
+ * @param config - MultiQueryConfig containing queries and merge settings
20
+ * @param options - Query options (skip, resetResultSetOnChange)
21
+ * @returns Query results with merged data and per-query error tracking
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * const { data, isLoading, error } = useMultiCubeQuery({
26
+ * queries: [
27
+ * { measures: ['Sales.revenue'] },
28
+ * { measures: ['Costs.total'] }
29
+ * ],
30
+ * mergeStrategy: 'merge',
31
+ * mergeKey: 'Sales.date',
32
+ * queryLabels: ['Revenue', 'Costs']
33
+ * })
34
+ * ```
35
+ */
36
+ export declare function useMultiCubeQuery(config: MultiQueryConfig | null, options?: CubeQueryOptions): UseMultiCubeQueryResult;
@@ -1,6 +1,6 @@
1
1
  import { useState as b, useRef as w, useEffect as y, useCallback as C, useMemo as m } from "react";
2
- import { u as L } from "./chunks/charts--hFH-bsu.js";
3
- import { K as F } from "./chunks/charts--hFH-bsu.js";
2
+ import { u as L } from "./chunks/charts-BvLb1eub.js";
3
+ import { K as F } from "./chunks/charts-BvLb1eub.js";
4
4
  function W(e, r = {}) {
5
5
  const { cubeApi: s, batchCoordinator: u, enableBatching: a } = L(), [l, d] = b({
6
6
  resultSet: null,
@@ -1,4 +1,4 @@
1
- import { D as a, x as t, y as o, l as n, m as I, q as c, t as g, v as r, o as y, p, n as T } from "./chunks/charts--hFH-bsu.js";
1
+ import { D as a, x as t, y as o, l as n, m as I, q as c, t as g, v as r, o as y, p, n as T } from "./chunks/charts-BvLb1eub.js";
2
2
  export {
3
3
  a as DEFAULT_ICONS,
4
4
  t as getChartTypeIcon,
@@ -17,14 +17,20 @@ useCubeMeta, // Only re-renders on metadata changes
17
17
  useCubeFeatures } from './providers/CubeProvider';
18
18
  export { ScrollContainerProvider, useScrollContainer } from './providers/ScrollContainerContext';
19
19
  export { useCubeQuery } from './hooks/useCubeQuery';
20
+ export { useMultiCubeQuery } from './hooks/useMultiCubeQuery';
21
+ export type { UseMultiCubeQueryResult } from './hooks/useMultiCubeQuery';
20
22
  export { useCubeFieldLabel } from './hooks/useCubeFieldLabel';
21
23
  export { createCubeClient } from './client/CubeClient';
24
+ export { validateMultiQueryConfig, validateTimeDimensionAlignment, validateMergeKeys, detectMeasureCollisions, detectAsymmetricDateRanges, isMultiQueryValid, getValidationSummary } from './utils/multiQueryValidation';
25
+ export type { MultiQueryValidationError, MultiQueryValidationWarning, MultiQueryValidationResult } from './utils/multiQueryValidation';
22
26
  export { useTheme } from './hooks/useTheme';
23
27
  export { useScrollDetection } from './hooks/useScrollDetection';
24
28
  export { useElementVisibility } from './hooks/useElementVisibility';
25
- export type { PortletConfig, ChartType, ChartAxisConfig, ChartDisplayConfig, CubeQuery, CubeQueryOptions, CubeApiOptions, DashboardConfig } from './types';
29
+ export type { PortletConfig, ChartType, ChartAxisConfig, ChartDisplayConfig, CubeQuery, CubeQueryOptions, CubeApiOptions, DashboardConfig, MultiQueryConfig, QueryMergeStrategy } from './types';
30
+ export { isMultiQueryConfig } from './types';
26
31
  export type { ChartTypeConfig, DisplayOptionConfig, AxisDropZoneConfig } from './charts/chartConfigs';
27
32
  export { createDashboardLayout, formatChartData, highlightCodeBlocks } from './utils/index';
33
+ export { isMultiQueryData, mergeQueryResults, mergeResultsConcat, mergeResultsByKey, getCombinedFields, getQueryLabels, getQueryIndices, generateQueryLabel, validateMergeKey } from './utils/multiQueryUtils';
28
34
  export { getThemeVariable, setThemeVariable, applyTheme, resetTheme, getTheme, setTheme, isDarkMode, watchThemeChanges, THEME_PRESETS } from './theme';
29
35
  export type { ThemeColorTokens, ThemeConfig, Theme } from './theme';
30
36
  export { getIcon, getIconData, setIcon, registerIcons, resetIcons, getIconRegistry, getIconsByCategory, getMeasureTypeIcon, getChartTypeIcon, getFieldTypeIcon, DEFAULT_ICONS } from './icons';