drizzle-cube 0.2.19 → 0.2.22
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/adapters/{compiler-7cp4XzHx.js → compiler-DDXMrb9d.js} +2470 -2181
- package/dist/adapters/compiler-S2NEGW7Q.cjs +22 -0
- package/dist/adapters/express/index.cjs +1 -1
- package/dist/adapters/express/index.js +1 -1
- package/dist/adapters/fastify/index.cjs +1 -1
- package/dist/adapters/fastify/index.js +1 -1
- package/dist/adapters/hono/index.cjs +1 -1
- package/dist/adapters/hono/index.js +1 -1
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/client/charts.js +2 -2
- package/dist/client/chunks/{charts-B8YMw1mi.js → charts-BympPNAT.js} +2249 -2062
- package/dist/client/chunks/charts-BympPNAT.js.map +1 -0
- package/dist/client/chunks/{charts-amt7VOyw.js → charts-CEz9wcfI.js} +32 -32
- package/dist/client/chunks/{charts-amt7VOyw.js.map → charts-CEz9wcfI.js.map} +1 -1
- package/dist/client/chunks/{components-K3xrnHN-.js → components-__XqYVbP.js} +4694 -4502
- package/dist/client/chunks/components-__XqYVbP.js.map +1 -0
- package/dist/client/chunks/core-DWO_N91H.js +6 -0
- package/dist/client/chunks/core-DWO_N91H.js.map +1 -0
- package/dist/client/chunks/{index-B7NSVb33.js → index-mA36fTTV.js} +2 -2
- package/dist/client/chunks/{index-B7NSVb33.js.map → index-mA36fTTV.js.map} +1 -1
- package/dist/client/components/AnalysisBuilder/types.d.ts +9 -0
- package/dist/client/components/charts/ChartTooltip.d.ts +1 -1
- package/dist/client/components.js +2 -2
- package/dist/client/hooks.js +2 -2
- package/dist/client/icons.js +1 -1
- package/dist/client/index.js +3 -3
- package/dist/client/providers.js +1 -1
- package/dist/client/types.d.ts +18 -0
- package/dist/client/utils/comparisonUtils.d.ts +96 -0
- package/dist/client/utils/index.d.ts +1 -0
- package/dist/client-bundle-stats.html +1 -1
- package/dist/server/index.cjs +16 -16
- package/dist/server/index.d.ts +44 -0
- package/dist/server/index.js +3164 -2875
- package/package.json +1 -1
- package/dist/adapters/compiler-CITtZq3r.cjs +0 -22
- package/dist/client/chunks/charts-B8YMw1mi.js.map +0 -1
- package/dist/client/chunks/components-K3xrnHN-.js.map +0 -1
- package/dist/client/chunks/core-Dkym7d1O.js +0 -6
- package/dist/client/chunks/core-Dkym7d1O.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-DWO_N91H.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-
|
|
4
|
+
import { u as ie } from "./charts-BympPNAT.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-
|
|
551
|
+
//# sourceMappingURL=index-mA36fTTV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-B7NSVb33.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-mA36fTTV.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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"}
|
|
@@ -27,6 +27,8 @@ export interface BreakdownItem {
|
|
|
27
27
|
granularity?: string;
|
|
28
28
|
/** Whether this is a time dimension */
|
|
29
29
|
isTimeDimension: boolean;
|
|
30
|
+
/** Enable period comparison for time dimensions (compares current filter period vs prior period) */
|
|
31
|
+
enableComparison?: boolean;
|
|
30
32
|
}
|
|
31
33
|
/** Validation status for query building */
|
|
32
34
|
export type ValidationStatus = 'idle' | 'validating' | 'valid' | 'invalid';
|
|
@@ -168,6 +170,7 @@ export interface AnalysisQueryPanelProps {
|
|
|
168
170
|
onAddBreakdown: () => void;
|
|
169
171
|
onRemoveBreakdown: (id: string) => void;
|
|
170
172
|
onBreakdownGranularityChange: (id: string, granularity: string) => void;
|
|
173
|
+
onBreakdownComparisonToggle?: (id: string) => void;
|
|
171
174
|
onReorderBreakdowns?: (fromIndex: number, toIndex: number) => void;
|
|
172
175
|
onFiltersChange: (filters: Filter[]) => void;
|
|
173
176
|
onDropFieldToFilter?: (field: string) => void;
|
|
@@ -284,6 +287,8 @@ export interface BreakdownSectionProps {
|
|
|
284
287
|
onRemove: (id: string) => void;
|
|
285
288
|
/** Change granularity for time dimension */
|
|
286
289
|
onGranularityChange: (id: string, granularity: string) => void;
|
|
290
|
+
/** Toggle comparison for time dimension */
|
|
291
|
+
onComparisonToggle?: (id: string) => void;
|
|
287
292
|
/** Whether the section is expanded */
|
|
288
293
|
isExpanded?: boolean;
|
|
289
294
|
/** Toggle expansion */
|
|
@@ -340,6 +345,10 @@ export interface BreakdownItemCardProps {
|
|
|
340
345
|
onRemove: () => void;
|
|
341
346
|
/** Granularity change handler (for time dimensions) */
|
|
342
347
|
onGranularityChange?: (granularity: string) => void;
|
|
348
|
+
/** Toggle comparison for time dimensions */
|
|
349
|
+
onComparisonToggle?: () => void;
|
|
350
|
+
/** Whether another time dimension already has comparison enabled */
|
|
351
|
+
comparisonDisabled?: boolean;
|
|
343
352
|
/** Current sort direction for this field */
|
|
344
353
|
sortDirection?: 'asc' | 'desc' | null;
|
|
345
354
|
/** Sort priority (1, 2, 3...) if sorted */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
interface ChartTooltipProps {
|
|
3
3
|
formatter?: (value: any, name: any, props: any) => [React.ReactText, React.ReactText];
|
|
4
|
-
labelFormatter?: (label: any) => React.ReactText;
|
|
4
|
+
labelFormatter?: (label: any, payload?: readonly any[]) => React.ReactText;
|
|
5
5
|
}
|
|
6
6
|
export default function ChartTooltip({ formatter, labelFormatter }: ChartTooltipProps): import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
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-
|
|
2
|
-
import { c as b, j as u, h as y, e as c, d as h, v as D } from "./chunks/charts-
|
|
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-__XqYVbP.js";
|
|
2
|
+
import { c as b, j as u, h as y, e as c, d as h, v as D } from "./chunks/charts-CEz9wcfI.js";
|
|
3
3
|
export {
|
|
4
4
|
t as AnalyticsDashboard,
|
|
5
5
|
s as AnalyticsPortlet,
|
package/dist/client/hooks.js
CHANGED
|
@@ -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-
|
|
3
|
-
import { K as F } from "./chunks/charts-
|
|
2
|
+
import { u as L } from "./chunks/charts-BympPNAT.js";
|
|
3
|
+
import { K as F } from "./chunks/charts-BympPNAT.js";
|
|
4
4
|
function W(e, r = {}) {
|
|
5
5
|
const { cubeApi: s, batchCoordinator: u, enableBatching: a } = L(), [l, d] = b({
|
|
6
6
|
resultSet: null,
|
package/dist/client/icons.js
CHANGED
|
@@ -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-
|
|
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-BympPNAT.js";
|
|
2
2
|
export {
|
|
3
3
|
a as DEFAULT_ICONS,
|
|
4
4
|
t as getChartTypeIcon,
|
package/dist/client/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { e as s, a as t, A as r, d as o, D as l, M as i, c as n, P as d, b as h, Q as u, h as C, u as c } from "./chunks/components-
|
|
2
|
-
import { C as b, D as y, L as p, T, h as m, e as I, x as D, y as M, l as f, m as A, q as P, t as x, v as E, i as L, g as S, k as v, o as B, p as F, r as k, n as Q, j as V, s as w, a as R, u as _, c as j, d as q, b as z, f as G, w as H } from "./chunks/charts-
|
|
3
|
-
import { L as O, c as U, f as J, g as K, i as W, p as X, a as Y } from "./chunks/charts-
|
|
1
|
+
import { e as s, a as t, A as r, d as o, D as l, M as i, c as n, P as d, b as h, Q as u, h as C, u as c } from "./chunks/components-__XqYVbP.js";
|
|
2
|
+
import { C as b, D as y, L as p, T, h as m, e as I, x as D, y as M, l as f, m as A, q as P, t as x, v as E, i as L, g as S, k as v, o as B, p as F, r as k, n as Q, j as V, s as w, a as R, u as _, c as j, d as q, b as z, f as G, w as H } from "./chunks/charts-BympPNAT.js";
|
|
3
|
+
import { L as O, c as U, f as J, g as K, i as W, p as X, a as Y } from "./chunks/charts-CEz9wcfI.js";
|
|
4
4
|
import { ScrollContainerProvider as $, useScrollContainer as aa } from "./providers.js";
|
|
5
5
|
import { useCubeQuery as sa } from "./hooks.js";
|
|
6
6
|
export {
|
package/dist/client/providers.js
CHANGED
package/dist/client/types.d.ts
CHANGED
|
@@ -59,6 +59,18 @@ export interface ChartDisplayConfig {
|
|
|
59
59
|
xAxisFormat?: AxisFormatConfig;
|
|
60
60
|
leftYAxisFormat?: AxisFormatConfig;
|
|
61
61
|
rightYAxisFormat?: AxisFormatConfig;
|
|
62
|
+
/**
|
|
63
|
+
* How to display compared periods:
|
|
64
|
+
* - 'separate': Each period as distinct series with different colors (default)
|
|
65
|
+
* - 'overlay': Periods aligned by day-of-period index with ghost styling for prior periods
|
|
66
|
+
*/
|
|
67
|
+
comparisonMode?: 'separate' | 'overlay';
|
|
68
|
+
/** Line style for prior periods in overlay mode */
|
|
69
|
+
priorPeriodStyle?: 'solid' | 'dashed' | 'dotted';
|
|
70
|
+
/** Opacity for prior period lines (0-1), default: 0.5 */
|
|
71
|
+
priorPeriodOpacity?: number;
|
|
72
|
+
/** Include period labels in legend */
|
|
73
|
+
showPeriodLabels?: boolean;
|
|
62
74
|
}
|
|
63
75
|
export interface PortletConfig {
|
|
64
76
|
id: string;
|
|
@@ -127,6 +139,12 @@ export interface CubeQuery {
|
|
|
127
139
|
dimension: string;
|
|
128
140
|
granularity?: string;
|
|
129
141
|
dateRange?: string[] | string;
|
|
142
|
+
fillMissingDates?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Array of date ranges for period-over-period comparison.
|
|
145
|
+
* When specified, queries are executed for each period and results are merged.
|
|
146
|
+
*/
|
|
147
|
+
compareDateRange?: (string | [string, string])[];
|
|
130
148
|
}>;
|
|
131
149
|
filters?: Filter[];
|
|
132
150
|
order?: {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comparison Data Utilities
|
|
3
|
+
* Handles period-over-period comparison data transformation for charts
|
|
4
|
+
*
|
|
5
|
+
* These utilities detect comparison query results (with __periodIndex metadata)
|
|
6
|
+
* and transform them for visualization in either 'separate' or 'overlay' mode.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Check if data contains comparison period metadata
|
|
10
|
+
*/
|
|
11
|
+
export declare function isComparisonData(data: any[]): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Get unique period labels from comparison data
|
|
14
|
+
*/
|
|
15
|
+
export declare function getPeriodLabels(data: any[]): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Get period indices from comparison data
|
|
18
|
+
*/
|
|
19
|
+
export declare function getPeriodIndices(data: any[]): number[];
|
|
20
|
+
/**
|
|
21
|
+
* Generate a short label for a period (used in legends)
|
|
22
|
+
* Uses simple "Current" / "Prior" labels for clarity
|
|
23
|
+
*/
|
|
24
|
+
export declare function generatePeriodShortLabel(_periodLabel: string, index: number): string;
|
|
25
|
+
/**
|
|
26
|
+
* Transform comparison data for 'separate' mode
|
|
27
|
+
* Returns data with each row having its original measure values
|
|
28
|
+
* Series are created per period (e.g., "totalLinesOfCode (Current)", "totalLinesOfCode (Prior)")
|
|
29
|
+
*/
|
|
30
|
+
export declare function transformForSeparateMode(data: any[], measures: string[], _timeDimensionKey: string): {
|
|
31
|
+
data: any[];
|
|
32
|
+
seriesKeys: string[];
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Transform comparison data for 'overlay' mode
|
|
36
|
+
* Pivots data so each row represents a day-of-period index,
|
|
37
|
+
* with separate columns for each period's values.
|
|
38
|
+
*
|
|
39
|
+
* When dimensions are present, creates series keys that include
|
|
40
|
+
* dimension values: "Engineering - Total Lines of Code (Current)"
|
|
41
|
+
*
|
|
42
|
+
* Input (without dimensions):
|
|
43
|
+
* [
|
|
44
|
+
* { date: '2024-01-01', value: 100, __periodIndex: 0, __periodDayIndex: 0 },
|
|
45
|
+
* { date: '2024-01-02', value: 110, __periodIndex: 0, __periodDayIndex: 1 },
|
|
46
|
+
* { date: '2023-01-01', value: 80, __periodIndex: 1, __periodDayIndex: 0 },
|
|
47
|
+
* { date: '2023-01-02', value: 85, __periodIndex: 1, __periodDayIndex: 1 },
|
|
48
|
+
* ]
|
|
49
|
+
*
|
|
50
|
+
* Output (without dimensions):
|
|
51
|
+
* [
|
|
52
|
+
* { __periodDayIndex: 0, 'value (Current)': 100, 'value (Prior)': 80 },
|
|
53
|
+
* { __periodDayIndex: 1, 'value (Current)': 110, 'value (Prior)': 85 },
|
|
54
|
+
* ]
|
|
55
|
+
*
|
|
56
|
+
* Input (with dimensions):
|
|
57
|
+
* [
|
|
58
|
+
* { date: '2024-01-01', 'Dept.name': 'Engineering', value: 100, __periodIndex: 0, __periodDayIndex: 0 },
|
|
59
|
+
* { date: '2024-01-01', 'Dept.name': 'Sales', value: 50, __periodIndex: 0, __periodDayIndex: 0 },
|
|
60
|
+
* { date: '2023-01-01', 'Dept.name': 'Engineering', value: 80, __periodIndex: 1, __periodDayIndex: 0 },
|
|
61
|
+
* { date: '2023-01-01', 'Dept.name': 'Sales', value: 40, __periodIndex: 1, __periodDayIndex: 0 },
|
|
62
|
+
* ]
|
|
63
|
+
*
|
|
64
|
+
* Output (with dimensions):
|
|
65
|
+
* [
|
|
66
|
+
* {
|
|
67
|
+
* __periodDayIndex: 0,
|
|
68
|
+
* 'Engineering - value (Current)': 100,
|
|
69
|
+
* 'Engineering - value (Prior)': 80,
|
|
70
|
+
* 'Sales - value (Current)': 50,
|
|
71
|
+
* 'Sales - value (Prior)': 40,
|
|
72
|
+
* },
|
|
73
|
+
* ]
|
|
74
|
+
*/
|
|
75
|
+
export declare function transformForOverlayMode(data: any[], measures: string[], timeDimensionKey: string, getFieldLabel?: (fieldName: string) => string): {
|
|
76
|
+
data: any[];
|
|
77
|
+
seriesKeys: string[];
|
|
78
|
+
xAxisKey: string;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Format the period day index for display on X-axis
|
|
82
|
+
* Can show as "Day 1", "Day 2", etc., or as the original date
|
|
83
|
+
*/
|
|
84
|
+
export declare function formatPeriodDayIndex(dayIndex: number, displayDate?: string | Date, options?: {
|
|
85
|
+
showDayNumber?: boolean;
|
|
86
|
+
dateFormat?: 'short' | 'long';
|
|
87
|
+
}): string;
|
|
88
|
+
/**
|
|
89
|
+
* Check if a series key represents a prior period (not the first period)
|
|
90
|
+
* Used for applying different styling to prior period lines
|
|
91
|
+
*/
|
|
92
|
+
export declare function isPriorPeriodSeries(seriesKey: string, periodLabels: string[]): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Get the stroke dash array for prior period styling
|
|
95
|
+
*/
|
|
96
|
+
export declare function getPriorPeriodStrokeDashArray(style?: 'solid' | 'dashed' | 'dotted'): string | undefined;
|