drizzle-cube 0.2.10 → 0.2.12
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-CMwSRhTS.cjs +22 -0
- package/dist/adapters/{compiler-gcKytLwd.js → compiler-DrkCCzf0.js} +16 -11
- package/dist/adapters/express/index.cjs +1 -1
- package/dist/adapters/express/index.js +8 -8
- package/dist/adapters/fastify/index.cjs +1 -1
- package/dist/adapters/fastify/index.js +9 -9
- package/dist/adapters/hono/index.cjs +1 -1
- package/dist/adapters/hono/index.js +11 -11
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/nextjs/index.js +59 -59
- package/dist/client/charts/chartConfigs.d.ts +3 -1
- package/dist/client/charts.js +11 -11
- package/dist/client/chunks/{chart-activitygridchart-C1ts13cL.js → chart-activitygridchart-CUGN9Xq9.js} +848 -738
- package/dist/client/chunks/chart-activitygridchart-CUGN9Xq9.js.map +1 -0
- package/dist/client/chunks/chart-areachart-B4tknnsY.js +239 -0
- package/dist/client/chunks/chart-areachart-B4tknnsY.js.map +1 -0
- package/dist/client/chunks/{chart-areachart-config-0ICuIaRP.js → chart-areachart-config-D9pPUKHZ.js} +15 -2
- package/dist/client/chunks/chart-areachart-config-D9pPUKHZ.js.map +1 -0
- package/dist/client/chunks/chart-axisformatcontrols-Ch_IYF94.js +196 -0
- package/dist/client/chunks/chart-axisformatcontrols-Ch_IYF94.js.map +1 -0
- package/dist/client/chunks/chart-barchart-D_op06r-.js +216 -0
- package/dist/client/chunks/chart-barchart-D_op06r-.js.map +1 -0
- package/dist/client/chunks/{chart-barchart-config-DalFHdmP.js → chart-barchart-config-iniz62ni.js} +15 -2
- package/dist/client/chunks/chart-barchart-config-iniz62ni.js.map +1 -0
- package/dist/client/chunks/chart-bubblechart-BsaIXUbS.js +214 -0
- package/dist/client/chunks/chart-bubblechart-BsaIXUbS.js.map +1 -0
- package/dist/client/chunks/{chart-bubblechart-config-WjiDlqnP.js → chart-bubblechart-config-yLq2oyR3.js} +18 -4
- package/dist/client/chunks/{chart-bubblechart-config-WjiDlqnP.js.map → chart-bubblechart-config-yLq2oyR3.js.map} +1 -1
- package/dist/client/chunks/{chart-charttooltip-5tzcFRt0.js → chart-charttooltip-Bx3I8jQv.js} +4 -4
- package/dist/client/chunks/{chart-charttooltip-5tzcFRt0.js.map → chart-charttooltip-Bx3I8jQv.js.map} +1 -1
- package/dist/client/chunks/chart-datatable-C7MS9q4Y.js +283 -0
- package/dist/client/chunks/chart-datatable-C7MS9q4Y.js.map +1 -0
- package/dist/client/chunks/{chart-datatable-config-C7TgMD3u.js → chart-datatable-config-DmEA3A-7.js} +10 -2
- package/dist/client/chunks/{chart-datatable-config-C7TgMD3u.js.map → chart-datatable-config-DmEA3A-7.js.map} +1 -1
- package/dist/client/chunks/{chart-kpidelta-D_VuZaVL.js → chart-kpidelta-7-KOmb3w.js} +62 -60
- package/dist/client/chunks/chart-kpidelta-7-KOmb3w.js.map +1 -0
- package/dist/client/chunks/{chart-kpinumber-DeCkVi2e.js → chart-kpinumber-HOPfcK2N.js} +4 -4
- package/dist/client/chunks/{chart-kpinumber-DeCkVi2e.js.map → chart-kpinumber-HOPfcK2N.js.map} +1 -1
- package/dist/client/chunks/{chart-kpitext-D8OpA0CY.js → chart-kpitext-BZkC9u3A.js} +24 -24
- package/dist/client/chunks/{chart-kpitext-D8OpA0CY.js.map → chart-kpitext-BZkC9u3A.js.map} +1 -1
- package/dist/client/chunks/chart-linechart-DqFmLbRe.js +198 -0
- package/dist/client/chunks/chart-linechart-DqFmLbRe.js.map +1 -0
- package/dist/client/chunks/{chart-linechart-config-C5tpU_2u.js → chart-linechart-config-DLVS2Zxc.js} +17 -4
- package/dist/client/chunks/chart-linechart-config-DLVS2Zxc.js.map +1 -0
- package/dist/client/chunks/chart-markdownchart-9n_TemoB.js +256 -0
- package/dist/client/chunks/chart-markdownchart-9n_TemoB.js.map +1 -0
- package/dist/client/chunks/{chart-piechart-CiFJQtC8.js → chart-piechart-CrXFd9pE.js} +61 -55
- package/dist/client/chunks/chart-piechart-CrXFd9pE.js.map +1 -0
- package/dist/client/chunks/{chart-piechart-config-CtP_JkGm.js → chart-piechart-config-DgwOeKHr.js} +12 -4
- package/dist/client/chunks/{chart-piechart-config-CtP_JkGm.js.map → chart-piechart-config-DgwOeKHr.js.map} +1 -1
- package/dist/client/chunks/{chart-radarchart-config-BkD_6Khq.js → chart-radarchart-config-BAV8D5PR.js} +12 -4
- package/dist/client/chunks/{chart-radarchart-config-BkD_6Khq.js.map → chart-radarchart-config-BAV8D5PR.js.map} +1 -1
- package/dist/client/chunks/chart-radarchart-tar2GBkO.js +131 -0
- package/dist/client/chunks/chart-radarchart-tar2GBkO.js.map +1 -0
- package/dist/client/chunks/chart-radialbarchart-ab8Swtal.js +120 -0
- package/dist/client/chunks/chart-radialbarchart-ab8Swtal.js.map +1 -0
- package/dist/client/chunks/{chart-radialbarchart-config-CBZDlB2k.js → chart-radialbarchart-config-CKozBBEC.js} +10 -2
- package/dist/client/chunks/{chart-radialbarchart-config-CBZDlB2k.js.map → chart-radialbarchart-config-CKozBBEC.js.map} +1 -1
- package/dist/client/chunks/{chart-scatterchart-DeGqPbYm.js → chart-scatterchart-BP06BeU5.js} +89 -87
- package/dist/client/chunks/chart-scatterchart-BP06BeU5.js.map +1 -0
- package/dist/client/chunks/{chart-scatterchart-config-DsdmlpHr.js → chart-scatterchart-config-UdHmbZ3s.js} +18 -4
- package/dist/client/chunks/{chart-scatterchart-config-DsdmlpHr.js.map → chart-scatterchart-config-UdHmbZ3s.js.map} +1 -1
- package/dist/client/chunks/chart-treemapchart-DAiixITm.js +265 -0
- package/dist/client/chunks/chart-treemapchart-DAiixITm.js.map +1 -0
- package/dist/client/chunks/{chart-treemapchart-config-CdF9NvPP.js → chart-treemapchart-config-D17VOwTM.js} +12 -4
- package/dist/client/chunks/{chart-treemapchart-config-CdF9NvPP.js.map → chart-treemapchart-config-D17VOwTM.js.map} +1 -1
- package/dist/client/chunks/{charts-DY7pF_-4.js → charts-CHzWeaY1.js} +36 -36
- package/dist/client/chunks/charts-CHzWeaY1.js.map +1 -0
- package/dist/client/chunks/components-DnM9CCUS.js +13931 -0
- package/dist/client/chunks/components-DnM9CCUS.js.map +1 -0
- package/dist/client/chunks/{index-DH5abYWH.js → index-DlsvcKXf.js} +135 -135
- package/dist/client/chunks/index-DlsvcKXf.js.map +1 -0
- package/dist/client/components/AnalysisBuilder/AnalysisAIPanel.d.ts +25 -0
- package/dist/client/components/AnalysisBuilder/AnalysisAxisDropZone.d.ts +8 -6
- package/dist/client/components/AnalysisBuilder/AnalysisChartConfigPanel.d.ts +2 -5
- package/dist/client/components/AnalysisBuilder/AnalysisDisplayConfigPanel.d.ts +9 -0
- package/dist/client/components/AnalysisBuilder/SectionHeading.d.ts +12 -0
- package/dist/client/components/AnalysisBuilder/types.d.ts +38 -9
- package/dist/client/components/charts/AxisFormatControls.d.ts +35 -0
- package/dist/client/components.js +2 -2
- package/dist/client/hooks/useResponsiveDashboard.d.ts +2 -8
- package/dist/client/hooks.js +10 -10
- package/dist/client/hooks.js.map +1 -1
- package/dist/client/icons/types.d.ts +2 -2
- package/dist/client/index.js +4 -4
- package/dist/client/providers.js +1 -1
- package/dist/client/styles.css +1 -1
- package/dist/client/types.d.ts +12 -0
- package/dist/client/utils/chartUtils.d.ts +20 -0
- package/dist/client/utils/pivotUtils.d.ts +11 -1
- package/dist/client-bundle-stats.html +1 -1
- package/dist/server/index.cjs +2 -2
- package/dist/server/index.js +16 -11
- package/package.json +4 -3
- package/dist/adapters/compiler-DP1pPIcg.cjs +0 -22
- package/dist/client/chunks/chart-activitygridchart-C1ts13cL.js.map +0 -1
- package/dist/client/chunks/chart-areachart-config-0ICuIaRP.js.map +0 -1
- package/dist/client/chunks/chart-areachart-fOi6lwUq.js +0 -204
- package/dist/client/chunks/chart-areachart-fOi6lwUq.js.map +0 -1
- package/dist/client/chunks/chart-barchart-CJfPZQin.js +0 -171
- package/dist/client/chunks/chart-barchart-CJfPZQin.js.map +0 -1
- package/dist/client/chunks/chart-barchart-config-DalFHdmP.js.map +0 -1
- package/dist/client/chunks/chart-bubblechart-BVk_qFly.js +0 -210
- package/dist/client/chunks/chart-bubblechart-BVk_qFly.js.map +0 -1
- package/dist/client/chunks/chart-datatable-BW_dWRbb.js +0 -249
- package/dist/client/chunks/chart-datatable-BW_dWRbb.js.map +0 -1
- package/dist/client/chunks/chart-kpidelta-D_VuZaVL.js.map +0 -1
- package/dist/client/chunks/chart-linechart-BdfBVl3m.js +0 -155
- package/dist/client/chunks/chart-linechart-BdfBVl3m.js.map +0 -1
- package/dist/client/chunks/chart-linechart-config-C5tpU_2u.js.map +0 -1
- package/dist/client/chunks/chart-markdownchart-C3FAQFuO.js +0 -254
- package/dist/client/chunks/chart-markdownchart-C3FAQFuO.js.map +0 -1
- package/dist/client/chunks/chart-piechart-CiFJQtC8.js.map +0 -1
- package/dist/client/chunks/chart-radarchart-CJ4kpk5J.js +0 -124
- package/dist/client/chunks/chart-radarchart-CJ4kpk5J.js.map +0 -1
- package/dist/client/chunks/chart-radialbarchart-DLQqN_1Z.js +0 -109
- package/dist/client/chunks/chart-radialbarchart-DLQqN_1Z.js.map +0 -1
- package/dist/client/chunks/chart-scatterchart-DeGqPbYm.js.map +0 -1
- package/dist/client/chunks/chart-treemapchart-ClXsAOjr.js +0 -253
- package/dist/client/chunks/chart-treemapchart-ClXsAOjr.js.map +0 -1
- package/dist/client/chunks/charts-DY7pF_-4.js.map +0 -1
- package/dist/client/chunks/components-DAOVOiM_.js +0 -13429
- package/dist/client/chunks/components-DAOVOiM_.js.map +0 -1
- package/dist/client/chunks/index-DH5abYWH.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-DlsvcKXf.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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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AnalysisAIPanel Component
|
|
3
|
+
*
|
|
4
|
+
* A collapsible panel for AI-powered query generation.
|
|
5
|
+
* Appears above the results panel when activated.
|
|
6
|
+
*/
|
|
7
|
+
export interface AnalysisAIPanelProps {
|
|
8
|
+
/** User's natural language prompt */
|
|
9
|
+
userPrompt: string;
|
|
10
|
+
/** Callback when prompt changes */
|
|
11
|
+
onPromptChange: (prompt: string) => void;
|
|
12
|
+
/** Whether a query is being generated */
|
|
13
|
+
isGenerating: boolean;
|
|
14
|
+
/** Error message from generation */
|
|
15
|
+
error: string | null;
|
|
16
|
+
/** Whether the AI has generated a query */
|
|
17
|
+
hasGeneratedQuery: boolean;
|
|
18
|
+
/** Callback to generate query */
|
|
19
|
+
onGenerate: () => void;
|
|
20
|
+
/** Callback to accept the generated query */
|
|
21
|
+
onAccept: () => void;
|
|
22
|
+
/** Callback to cancel and restore previous state */
|
|
23
|
+
onCancel: () => void;
|
|
24
|
+
}
|
|
25
|
+
export default function AnalysisAIPanel({ userPrompt, onPromptChange, isGenerating, error, hasGeneratedQuery, onGenerate, onAccept, onCancel }: AnalysisAIPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DragEvent } from 'react';
|
|
2
2
|
import { AxisDropZoneConfig } from '../../charts/chartConfigs';
|
|
3
3
|
interface FieldMeta {
|
|
4
4
|
title?: string;
|
|
@@ -10,11 +10,11 @@ interface FieldMeta {
|
|
|
10
10
|
interface AnalysisAxisDropZoneProps {
|
|
11
11
|
config: AxisDropZoneConfig;
|
|
12
12
|
fields: string[];
|
|
13
|
-
onDrop: (e:
|
|
13
|
+
onDrop: (e: DragEvent<HTMLDivElement>, toKey: string) => void;
|
|
14
14
|
onRemove: (field: string, fromKey: string) => void;
|
|
15
|
-
onDragStart: (e:
|
|
16
|
-
onDragEnd?: (e:
|
|
17
|
-
onDragOver: (e:
|
|
15
|
+
onDragStart: (e: DragEvent<HTMLDivElement>, field: string, fromKey: string, fromIndex?: number) => void;
|
|
16
|
+
onDragEnd?: (e: DragEvent<HTMLDivElement>) => void;
|
|
17
|
+
onDragOver: (e: DragEvent<HTMLDivElement>) => void;
|
|
18
18
|
onReorder?: (fromIndex: number, toIndex: number, axisKey: string) => void;
|
|
19
19
|
draggedItem?: {
|
|
20
20
|
field: string;
|
|
@@ -22,6 +22,8 @@ interface AnalysisAxisDropZoneProps {
|
|
|
22
22
|
fromIndex?: number;
|
|
23
23
|
} | null;
|
|
24
24
|
getFieldMeta?: (field: string) => FieldMeta;
|
|
25
|
+
yAxisAssignment?: Record<string, 'left' | 'right'>;
|
|
26
|
+
onYAxisAssignmentChange?: (field: string, axis: 'left' | 'right') => void;
|
|
25
27
|
}
|
|
26
|
-
export default function AnalysisAxisDropZone({ config, fields, onDrop, onRemove, onDragStart, onDragEnd, onDragOver, onReorder, draggedItem, getFieldMeta }: AnalysisAxisDropZoneProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
export default function AnalysisAxisDropZone({ config, fields, onDrop, onRemove, onDragStart, onDragEnd, onDragOver, onReorder, draggedItem, getFieldMeta, yAxisAssignment, onYAxisAssignmentChange }: AnalysisAxisDropZoneProps): import("react/jsx-runtime").JSX.Element;
|
|
27
29
|
export {};
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import { ChartType, ChartAxisConfig
|
|
1
|
+
import { ChartType, ChartAxisConfig } from '../../types';
|
|
2
2
|
import { MetricItem, BreakdownItem } from './types';
|
|
3
3
|
import { ChartAvailabilityMap } from '../../shared/chartDefaults';
|
|
4
4
|
import { MetaResponse } from '../../shared/types';
|
|
5
5
|
interface AnalysisChartConfigPanelProps {
|
|
6
6
|
chartType: ChartType;
|
|
7
7
|
chartConfig: ChartAxisConfig;
|
|
8
|
-
displayConfig: ChartDisplayConfig;
|
|
9
8
|
metrics: MetricItem[];
|
|
10
9
|
breakdowns: BreakdownItem[];
|
|
11
|
-
colorPalette?: ColorPalette;
|
|
12
10
|
/** Schema metadata for resolving field titles */
|
|
13
11
|
schema?: MetaResponse | null;
|
|
14
12
|
/** Map of chart type availability for disabling unavailable chart types */
|
|
15
13
|
chartAvailability?: ChartAvailabilityMap;
|
|
16
14
|
onChartTypeChange: (type: ChartType) => void;
|
|
17
15
|
onChartConfigChange: (config: ChartAxisConfig) => void;
|
|
18
|
-
onDisplayConfigChange: (config: ChartDisplayConfig) => void;
|
|
19
16
|
}
|
|
20
|
-
export default function AnalysisChartConfigPanel({ chartType, chartConfig,
|
|
17
|
+
export default function AnalysisChartConfigPanel({ chartType, chartConfig, metrics, breakdowns, schema, chartAvailability, onChartTypeChange, onChartConfigChange }: AnalysisChartConfigPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
21
18
|
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ChartType, ChartDisplayConfig, ColorPalette } from '../../types';
|
|
2
|
+
interface AnalysisDisplayConfigPanelProps {
|
|
3
|
+
chartType: ChartType;
|
|
4
|
+
displayConfig: ChartDisplayConfig;
|
|
5
|
+
colorPalette?: ColorPalette;
|
|
6
|
+
onDisplayConfigChange: (config: ChartDisplayConfig) => void;
|
|
7
|
+
}
|
|
8
|
+
export default function AnalysisDisplayConfigPanel({ chartType, displayConfig, colorPalette, onDisplayConfigChange }: AnalysisDisplayConfigPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
interface SectionHeadingProps {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
/** Optional className to add additional styles */
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Consistent section heading style for Analysis Builder panels.
|
|
9
|
+
* Change the styles here to update all section headings at once.
|
|
10
|
+
*/
|
|
11
|
+
export default function SectionHeading({ children, className }: SectionHeadingProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MouseEvent, DragEvent } from 'react';
|
|
2
2
|
import { CubeQuery, Filter, ChartType, ChartAxisConfig, ChartDisplayConfig } from '../../types';
|
|
3
3
|
import { ColorPalette } from '../../utils/colorPalettes';
|
|
4
4
|
import { MetaResponse, MetaField, MetaCube, QueryAnalysis } from '../../shared/types';
|
|
@@ -50,6 +50,30 @@ export interface AnalysisBuilderState {
|
|
|
50
50
|
totalRowCount: number | null;
|
|
51
51
|
resultsStale: boolean;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* State for the AI query generation panel
|
|
55
|
+
*/
|
|
56
|
+
export interface AIState {
|
|
57
|
+
/** Whether the AI panel is open */
|
|
58
|
+
isOpen: boolean;
|
|
59
|
+
/** User's natural language prompt */
|
|
60
|
+
userPrompt: string;
|
|
61
|
+
/** Whether a query is being generated */
|
|
62
|
+
isGenerating: boolean;
|
|
63
|
+
/** Error message from generation */
|
|
64
|
+
error: string | null;
|
|
65
|
+
/** Whether the AI has generated a query that's been loaded */
|
|
66
|
+
hasGeneratedQuery: boolean;
|
|
67
|
+
/** Snapshot of state before AI was opened (for undo) */
|
|
68
|
+
previousState: {
|
|
69
|
+
metrics: MetricItem[];
|
|
70
|
+
breakdowns: BreakdownItem[];
|
|
71
|
+
filters: Filter[];
|
|
72
|
+
chartType: ChartType;
|
|
73
|
+
chartConfig: ChartAxisConfig;
|
|
74
|
+
displayConfig: ChartDisplayConfig;
|
|
75
|
+
} | null;
|
|
76
|
+
}
|
|
53
77
|
/**
|
|
54
78
|
* Mode for the field search modal - determines which field types are shown
|
|
55
79
|
*/
|
|
@@ -107,7 +131,7 @@ export interface FieldSearchItemProps {
|
|
|
107
131
|
/** Whether this field is focused/highlighted */
|
|
108
132
|
isFocused: boolean;
|
|
109
133
|
/** Click handler - receives mouse event for shift-click multi-select */
|
|
110
|
-
onClick: (e:
|
|
134
|
+
onClick: (e: MouseEvent) => void;
|
|
111
135
|
/** Mouse enter handler (for detail panel) */
|
|
112
136
|
onMouseEnter: () => void;
|
|
113
137
|
}
|
|
@@ -121,7 +145,7 @@ export interface FieldDetailPanelProps {
|
|
|
121
145
|
/**
|
|
122
146
|
* Tab options for the query panel
|
|
123
147
|
*/
|
|
124
|
-
export type QueryPanelTab = 'query' | 'chart';
|
|
148
|
+
export type QueryPanelTab = 'query' | 'chart' | 'display';
|
|
125
149
|
/**
|
|
126
150
|
* Props for the AnalysisQueryPanel component
|
|
127
151
|
*/
|
|
@@ -215,6 +239,11 @@ export interface AnalysisResultsPanelProps {
|
|
|
215
239
|
onShareClick?: () => void;
|
|
216
240
|
canShare?: boolean;
|
|
217
241
|
shareButtonState?: 'idle' | 'copied' | 'copied-no-chart';
|
|
242
|
+
onClearClick?: () => void;
|
|
243
|
+
canClear?: boolean;
|
|
244
|
+
enableAI?: boolean;
|
|
245
|
+
isAIOpen?: boolean;
|
|
246
|
+
onAIToggle?: () => void;
|
|
218
247
|
}
|
|
219
248
|
/**
|
|
220
249
|
* Props for the MetricsSection component
|
|
@@ -291,11 +320,11 @@ export interface MetricItemCardProps {
|
|
|
291
320
|
/** Whether dragging over this item */
|
|
292
321
|
isDragOver?: boolean;
|
|
293
322
|
/** Drag start handler */
|
|
294
|
-
onDragStart?: (e:
|
|
323
|
+
onDragStart?: (e: DragEvent, index: number) => void;
|
|
295
324
|
/** Drag over handler */
|
|
296
|
-
onDragOver?: (e:
|
|
325
|
+
onDragOver?: (e: DragEvent, index: number) => void;
|
|
297
326
|
/** Drop handler */
|
|
298
|
-
onDrop?: (e:
|
|
327
|
+
onDrop?: (e: DragEvent, index: number) => void;
|
|
299
328
|
/** Drag end handler */
|
|
300
329
|
onDragEnd?: () => void;
|
|
301
330
|
}
|
|
@@ -324,11 +353,11 @@ export interface BreakdownItemCardProps {
|
|
|
324
353
|
/** Whether dragging over this item */
|
|
325
354
|
isDragOver?: boolean;
|
|
326
355
|
/** Drag start handler */
|
|
327
|
-
onDragStart?: (e:
|
|
356
|
+
onDragStart?: (e: DragEvent, index: number) => void;
|
|
328
357
|
/** Drag over handler */
|
|
329
|
-
onDragOver?: (e:
|
|
358
|
+
onDragOver?: (e: DragEvent, index: number) => void;
|
|
330
359
|
/** Drop handler */
|
|
331
|
-
onDrop?: (e:
|
|
360
|
+
onDrop?: (e: DragEvent, index: number) => void;
|
|
332
361
|
/** Drag end handler */
|
|
333
362
|
onDragEnd?: () => void;
|
|
334
363
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AxisFormatConfig } from '../../types';
|
|
2
|
+
interface AxisFormatControlsProps {
|
|
3
|
+
value: AxisFormatConfig;
|
|
4
|
+
onChange: (config: AxisFormatConfig) => void;
|
|
5
|
+
axisLabel: string;
|
|
6
|
+
/** Sample value for preview (default: 1250000) */
|
|
7
|
+
previewValue?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Single axis format control section
|
|
11
|
+
*/
|
|
12
|
+
export declare function AxisFormatControls({ value, onChange, axisLabel, previewValue }: AxisFormatControlsProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
interface MultiAxisFormatControlsProps {
|
|
14
|
+
displayConfig: {
|
|
15
|
+
xAxisFormat?: AxisFormatConfig;
|
|
16
|
+
leftYAxisFormat?: AxisFormatConfig;
|
|
17
|
+
rightYAxisFormat?: AxisFormatConfig;
|
|
18
|
+
};
|
|
19
|
+
onChange: (updates: {
|
|
20
|
+
xAxisFormat?: AxisFormatConfig;
|
|
21
|
+
leftYAxisFormat?: AxisFormatConfig;
|
|
22
|
+
rightYAxisFormat?: AxisFormatConfig;
|
|
23
|
+
}) => void;
|
|
24
|
+
/** Which axes to show controls for */
|
|
25
|
+
showAxes?: {
|
|
26
|
+
xAxis?: boolean;
|
|
27
|
+
leftYAxis?: boolean;
|
|
28
|
+
rightYAxis?: boolean;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Container component for multiple axis format controls
|
|
33
|
+
*/
|
|
34
|
+
export declare function MultiAxisFormatControls({ displayConfig, onChange, showAxes }: MultiAxisFormatControlsProps): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
export default AxisFormatControls;
|
|
@@ -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-DnM9CCUS.js";
|
|
2
|
+
import { c as b, j as u, h as y, e as c, d as h, v as D } from "./chunks/charts-CHzWeaY1.js";
|
|
3
3
|
export {
|
|
4
4
|
t as AnalyticsDashboard,
|
|
5
5
|
s as AnalyticsPortlet,
|
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
* Custom hook for responsive dashboard layout management
|
|
3
|
-
* Implements a three-tier responsive strategy:
|
|
4
|
-
* - Desktop (1200px+): Normal grid layout with full editing
|
|
5
|
-
* - Scaled (768-1199px): CSS transform scaling, read-only
|
|
6
|
-
* - Mobile (<768px): Single-column stacked layout, read-only
|
|
7
|
-
*/
|
|
1
|
+
import { RefCallback } from 'react';
|
|
8
2
|
export type DashboardDisplayMode = 'desktop' | 'scaled' | 'mobile';
|
|
9
3
|
export interface UseResponsiveDashboardResult {
|
|
10
|
-
containerRef:
|
|
4
|
+
containerRef: RefCallback<HTMLDivElement>;
|
|
11
5
|
containerWidth: number;
|
|
12
6
|
displayMode: DashboardDisplayMode;
|
|
13
7
|
scaleFactor: number;
|
package/dist/client/hooks.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { useState as b, useRef as
|
|
2
|
-
import { u as L } from "./chunks/chart-activitygridchart-
|
|
3
|
-
import {
|
|
1
|
+
import { useState as b, useRef as E, useEffect as y, useCallback as C, useMemo as m } from "react";
|
|
2
|
+
import { u as L } from "./chunks/chart-activitygridchart-CUGN9Xq9.js";
|
|
3
|
+
import { E as F } from "./chunks/chart-activitygridchart-CUGN9Xq9.js";
|
|
4
4
|
function W(e, r = {}) {
|
|
5
5
|
const { cubeApi: s, batchCoordinator: u, enableBatching: a } = L(), [l, d] = b({
|
|
6
6
|
resultSet: null,
|
|
7
7
|
isLoading: !1,
|
|
8
8
|
error: null,
|
|
9
9
|
queryId: null
|
|
10
|
-
}), f =
|
|
10
|
+
}), f = E("");
|
|
11
11
|
return y(() => {
|
|
12
12
|
if (!e || r.skip)
|
|
13
13
|
return;
|
|
@@ -38,8 +38,8 @@ function W(e, r = {}) {
|
|
|
38
38
|
});
|
|
39
39
|
}, [e, s, u, a, r.skip, r.resetResultSetOnChange]), l;
|
|
40
40
|
}
|
|
41
|
-
function
|
|
42
|
-
const [s, u] = b([]), [a, l] = b(null), d =
|
|
41
|
+
function D(e, r = !0) {
|
|
42
|
+
const [s, u] = b([]), [a, l] = b(null), d = E(null), f = E(""), {
|
|
43
43
|
resultSet: n,
|
|
44
44
|
isLoading: o,
|
|
45
45
|
error: i,
|
|
@@ -54,8 +54,8 @@ function O(e, r = !0) {
|
|
|
54
54
|
try {
|
|
55
55
|
const S = c.tablePivot(), g = /* @__PURE__ */ new Set();
|
|
56
56
|
return S.forEach((v) => {
|
|
57
|
-
const
|
|
58
|
-
|
|
57
|
+
const w = v[e];
|
|
58
|
+
w != null && w !== "" && g.add(w);
|
|
59
59
|
}), Array.from(g);
|
|
60
60
|
} catch (S) {
|
|
61
61
|
return console.error("Error extracting values from result set:", S), [];
|
|
@@ -129,7 +129,7 @@ const R = 1200, p = 768;
|
|
|
129
129
|
function k() {
|
|
130
130
|
const [e, r] = b(
|
|
131
131
|
() => typeof window < "u" ? window.innerWidth : R
|
|
132
|
-
), s =
|
|
132
|
+
), s = E(null), u = E(null), a = C((n) => {
|
|
133
133
|
if (s.current && (s.current.disconnect(), s.current = null), u.current = n, n) {
|
|
134
134
|
const o = n.offsetWidth;
|
|
135
135
|
o > 0 && r(o), s.current = new ResizeObserver((i) => {
|
|
@@ -167,7 +167,7 @@ export {
|
|
|
167
167
|
F as useCubeMeta,
|
|
168
168
|
W as useCubeQuery,
|
|
169
169
|
Q as useDebounce,
|
|
170
|
-
|
|
170
|
+
D as useFilterValues,
|
|
171
171
|
k as useResponsiveDashboard
|
|
172
172
|
};
|
|
173
173
|
//# sourceMappingURL=hooks.js.map
|
package/dist/client/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sources":["../../src/client/hooks/useCubeQuery.ts","../../src/client/hooks/useFilterValues.ts","../../src/client/hooks/useDebounce.ts","../../src/client/hooks/useResponsiveDashboard.ts"],"sourcesContent":["/**\n * React hook for executing Cube queries\n * Replaces @cubejs-client/react useCubeQuery\n */\n\nimport { useState, useEffect, useRef } from 'react'\nimport { useCubeContext } from '../providers/CubeProvider'\nimport type { CubeQuery, CubeQueryOptions, CubeResultSet } from '../types'\n\ninterface UseCubeQueryResult {\n resultSet: CubeResultSet | null\n isLoading: boolean\n error: Error | null\n queryId: string | null\n}\n\nexport function useCubeQuery(\n query: CubeQuery | null,\n options: CubeQueryOptions = {}\n): UseCubeQueryResult {\n const { cubeApi, batchCoordinator, enableBatching } = useCubeContext()\n \n // Use a single state object to ensure atomic updates\n const [state, setState] = useState<UseCubeQueryResult>({\n resultSet: null,\n isLoading: false,\n error: null,\n queryId: null\n })\n \n // Track the last query to avoid unnecessary re-fetches\n const lastQueryRef = useRef<string>('')\n \n useEffect(() => {\n // Skip if query is null or skip option is true\n if (!query || options.skip) {\n return\n }\n\n // Create a stable query string for comparison\n const queryString = JSON.stringify(query)\n \n // Skip if query hasn't changed (unless resetResultSetOnChange is true)\n if (queryString === lastQueryRef.current && !options.resetResultSetOnChange) {\n return\n }\n\n lastQueryRef.current = queryString\n \n // Create a unique ID for this query execution\n const queryId = `${Date.now()}_${Math.random().toString(36).substring(7)}`\n\n // Update state atomically with new query ID and loading state\n setState(prevState => ({\n resultSet: options.resetResultSetOnChange ? null : prevState.resultSet,\n isLoading: true,\n error: null,\n queryId\n }))\n\n // Use batch coordinator if enabled, otherwise use direct API call\n const executeQuery = enableBatching && batchCoordinator\n ? batchCoordinator.register(query)\n : cubeApi.load(query)\n\n executeQuery\n .then((result) => {\n setState(prevState => {\n // Only update if this is still the current query\n if (prevState.queryId === queryId) {\n return {\n resultSet: result,\n isLoading: false,\n error: null,\n queryId\n }\n }\n return prevState\n })\n })\n .catch((err) => {\n setState(prevState => {\n // Only update if this is still the current query\n if (prevState.queryId === queryId) {\n return {\n resultSet: null,\n isLoading: false,\n error: err instanceof Error ? err : new Error(String(err)),\n queryId\n }\n }\n return prevState\n })\n })\n }, [query, cubeApi, batchCoordinator, enableBatching, options.skip, options.resetResultSetOnChange])\n\n return state\n}","/**\n * Hook for fetching distinct field values for filter dropdowns\n * Uses the /load API to get actual data values\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { useCubeQuery } from '../hooks/useCubeQuery'\nimport type { CubeQuery } from '../types'\n\ninterface UseFilterValuesResult {\n values: any[]\n loading: boolean\n error: string | null\n refetch: () => void\n searchValues: (searchTerm: string, force?: boolean) => void\n}\n\n/**\n * Custom hook to fetch distinct values for a field\n */\nexport function useFilterValues(\n fieldName: string | null, \n enabled: boolean = true\n): UseFilterValuesResult {\n const [values, setValues] = useState<any[]>([])\n const [currentQuery, setCurrentQuery] = useState<CubeQuery | null>(null)\n const lastProcessedQueryId = useRef<string | null>(null)\n const lastSearchTerm = useRef<string>('')\n \n // Use cube query hook for actual data fetching\n const { \n resultSet, \n isLoading,\n error: queryError,\n queryId\n } = useCubeQuery(currentQuery, {\n skip: !currentQuery || !enabled,\n resetResultSetOnChange: true // Clear old results when query changes\n })\n \n // Extract unique values from result set\n const extractValuesFromResultSet = useCallback((rs: any): any[] => {\n if (!rs || !fieldName) {\n return []\n }\n \n try {\n const data = rs.tablePivot()\n \n const uniqueValues = new Set<any>()\n \n data.forEach((row: any) => {\n const value = row[fieldName]\n if (value !== null && value !== undefined && value !== '') {\n uniqueValues.add(value)\n }\n })\n \n // Convert to array - already sorted by query\n const sortedValues = Array.from(uniqueValues)\n \n return sortedValues\n } catch (err) {\n console.error('Error extracting values from result set:', err)\n return []\n }\n }, [fieldName])\n \n // Process results only when we have a new matching query result\n useEffect(() => {\n // Skip if no query ID\n if (!queryId) {\n return\n }\n \n // Skip if we've already processed this query\n if (queryId === lastProcessedQueryId.current) {\n return\n }\n \n // Skip if still loading\n if (isLoading) {\n return\n }\n \n // Mark as processed\n lastProcessedQueryId.current = queryId\n \n if (queryError) {\n setValues([])\n } else if (resultSet) {\n const extractedValues = extractValuesFromResultSet(resultSet)\n setValues(extractedValues)\n } else {\n setValues([])\n }\n }, [resultSet, isLoading, queryError, queryId, extractValuesFromResultSet])\n \n // Reset values when fieldName becomes null or enabled changes\n useEffect(() => {\n if (!fieldName || !enabled) {\n setValues([])\n setCurrentQuery(null)\n lastProcessedQueryId.current = null\n lastSearchTerm.current = ''\n }\n }, [fieldName, enabled])\n \n // Refetch function\n const refetch = useCallback(() => {\n if (!fieldName) return\n \n lastSearchTerm.current = ''\n \n try {\n const query: CubeQuery = {\n dimensions: [fieldName], \n limit: 25,\n order: { [fieldName]: 'asc' }\n }\n setCurrentQuery(query)\n } catch (err) {\n console.error('Error creating query:', err)\n }\n }, [fieldName])\n\n // Search function for server-side filtering\n const searchValues = useCallback((searchTerm: string, force: boolean = false) => {\n if (!fieldName) {\n return\n }\n \n // Don't create a new query if the search term hasn't changed (unless forced)\n if (!force && searchTerm === lastSearchTerm.current) {\n return\n }\n \n lastSearchTerm.current = searchTerm\n \n try {\n // Create query inline to avoid dependency issues\n const query: CubeQuery = {\n dimensions: [fieldName], \n limit: 25,\n order: { [fieldName]: 'asc' }\n }\n \n if (searchTerm && searchTerm.trim()) {\n query.filters = [{\n member: fieldName,\n operator: 'contains',\n values: [searchTerm.trim()]\n }]\n }\n \n setCurrentQuery(query)\n } catch (err) {\n console.error('Error creating search query:', err)\n }\n }, [fieldName])\n \n return {\n values,\n loading: isLoading,\n error: queryError ? (queryError instanceof Error ? queryError.message : String(queryError)) : null,\n refetch,\n searchValues\n }\n}","/**\n * Custom hook for debouncing values\n * Delays updating the value until after the specified delay has passed\n * since the last change\n */\n\nimport { useState, useEffect } from 'react'\n\n/**\n * Debounces a value by the specified delay\n * @param value The value to debounce\n * @param delay The delay in milliseconds\n * @returns The debounced value\n */\nexport function useDebounce<T>(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState(value)\n\n useEffect(() => {\n // Set up a timer to update the debounced value after the delay\n const handler = setTimeout(() => {\n setDebouncedValue(value)\n }, delay)\n\n // Clean up the timer if the value changes before the delay\n return () => {\n clearTimeout(handler)\n }\n }, [value, delay])\n\n return debouncedValue\n}","/**\n * Custom hook for responsive dashboard layout management\n * Implements a three-tier responsive strategy:\n * - Desktop (1200px+): Normal grid layout with full editing\n * - Scaled (768-1199px): CSS transform scaling, read-only\n * - Mobile (<768px): Single-column stacked layout, read-only\n */\n\nimport { useState, useEffect, useRef, useMemo, useCallback } from 'react'\n\nexport type DashboardDisplayMode = 'desktop' | 'scaled' | 'mobile'\n\nconst DESIGN_WIDTH = 1200\nconst MOBILE_THRESHOLD = 768\n\nexport interface UseResponsiveDashboardResult {\n containerRef: React.RefCallback<HTMLDivElement>\n containerWidth: number\n displayMode: DashboardDisplayMode\n scaleFactor: number\n isEditable: boolean\n designWidth: number\n}\n\n/**\n * Hook for managing responsive dashboard layouts\n * Uses ResizeObserver for accurate width detection and calculates\n * the appropriate display mode and scale factor\n */\nexport function useResponsiveDashboard(): UseResponsiveDashboardResult {\n // Start with window width as initial estimate\n const [containerWidth, setContainerWidth] = useState(() =>\n typeof window !== 'undefined' ? window.innerWidth : DESIGN_WIDTH\n )\n const observerRef = useRef<ResizeObserver | null>(null)\n const elementRef = useRef<HTMLDivElement | null>(null)\n\n // Ref callback - called when element is attached/detached\n // This is key: unlike useEffect, this fires immediately when the DOM element exists\n const containerRef = useCallback((node: HTMLDivElement | null) => {\n // Cleanup previous observer\n if (observerRef.current) {\n observerRef.current.disconnect()\n observerRef.current = null\n }\n\n elementRef.current = node\n\n if (node) {\n // Get initial width immediately (synchronously when element attaches)\n const initialWidth = node.offsetWidth\n if (initialWidth > 0) {\n setContainerWidth(initialWidth)\n }\n\n // Set up ResizeObserver for ongoing changes\n observerRef.current = new ResizeObserver((entries) => {\n const width = entries[0]?.contentRect.width\n if (width && width > 0) {\n setContainerWidth(width)\n }\n })\n observerRef.current.observe(node)\n }\n }, [])\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (observerRef.current) {\n observerRef.current.disconnect()\n }\n }\n }, [])\n\n // Fallback: window resize listener to catch resize events that ResizeObserver might miss\n // This is particularly important for containers in flex/grid layouts or deeply nested elements\n useEffect(() => {\n const handleWindowResize = () => {\n if (elementRef.current) {\n const width = elementRef.current.offsetWidth\n if (width > 0) {\n setContainerWidth(width)\n }\n }\n }\n\n window.addEventListener('resize', handleWindowResize)\n\n // Also measure after a short delay to catch late layout calculations\n const timeoutId = setTimeout(handleWindowResize, 100)\n\n return () => {\n window.removeEventListener('resize', handleWindowResize)\n clearTimeout(timeoutId)\n }\n }, [])\n\n const displayMode = useMemo<DashboardDisplayMode>(() => {\n if (containerWidth >= DESIGN_WIDTH) return 'desktop'\n if (containerWidth >= MOBILE_THRESHOLD) return 'scaled'\n return 'mobile'\n }, [containerWidth])\n\n const scaleFactor = useMemo(() => {\n if (displayMode !== 'scaled') return 1\n return containerWidth / DESIGN_WIDTH\n }, [containerWidth, displayMode])\n\n const isEditable = displayMode === 'desktop'\n\n return {\n containerRef,\n containerWidth,\n displayMode,\n scaleFactor,\n isEditable,\n designWidth: DESIGN_WIDTH\n }\n}\n"],"names":["useCubeQuery","query","options","cubeApi","batchCoordinator","enableBatching","useCubeContext","state","setState","useState","lastQueryRef","useRef","useEffect","queryString","queryId","prevState","result","err","useFilterValues","fieldName","enabled","values","setValues","currentQuery","setCurrentQuery","lastProcessedQueryId","lastSearchTerm","resultSet","isLoading","queryError","extractValuesFromResultSet","useCallback","rs","data","uniqueValues","row","value","extractedValues","refetch","searchValues","searchTerm","force","useDebounce","delay","debouncedValue","setDebouncedValue","handler","DESIGN_WIDTH","MOBILE_THRESHOLD","useResponsiveDashboard","containerWidth","setContainerWidth","observerRef","elementRef","containerRef","node","initialWidth","entries","width","handleWindowResize","timeoutId","displayMode","useMemo","scaleFactor"],"mappings":";;;AAgBO,SAASA,EACdC,GACAC,IAA4B,IACR;AACpB,QAAM,EAAE,SAAAC,GAAS,kBAAAC,GAAkB,gBAAAC,EAAA,IAAmBC,EAAA,GAGhD,CAACC,GAAOC,CAAQ,IAAIC,EAA6B;AAAA,IACrD,WAAW;AAAA,IACX,WAAW;AAAA,IACX,OAAO;AAAA,IACP,SAAS;AAAA,EAAA,CACV,GAGKC,IAAeC,EAAe,EAAE;AAEtC,SAAAC,EAAU,MAAM;AAEd,QAAI,CAACX,KAASC,EAAQ;AACpB;AAIF,UAAMW,IAAc,KAAK,UAAUZ,CAAK;AAGxC,QAAIY,MAAgBH,EAAa,WAAW,CAACR,EAAQ;AACnD;AAGF,IAAAQ,EAAa,UAAUG;AAGvB,UAAMC,IAAU,GAAG,KAAK,IAAA,CAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;AAGxE,IAAAN,EAAS,CAAAO,OAAc;AAAA,MACrB,WAAWb,EAAQ,yBAAyB,OAAOa,EAAU;AAAA,MAC7D,WAAW;AAAA,MACX,OAAO;AAAA,MACP,SAAAD;AAAA,IAAA,EACA,IAGmBT,KAAkBD,IACnCA,EAAiB,SAASH,CAAK,IAC/BE,EAAQ,KAAKF,CAAK,GAGnB,KAAK,CAACe,MAAW;AAChB,MAAAR,EAAS,CAAAO,MAEHA,EAAU,YAAYD,IACjB;AAAA,QACL,WAAWE;AAAA,QACX,WAAW;AAAA,QACX,OAAO;AAAA,QACP,SAAAF;AAAA,MAAA,IAGGC,CACR;AAAA,IACH,CAAC,EACA,MAAM,CAACE,MAAQ;AACd,MAAAT,EAAS,CAAAO,MAEHA,EAAU,YAAYD,IACjB;AAAA,QACL,WAAW;AAAA,QACX,WAAW;AAAA,QACX,OAAOG,aAAe,QAAQA,IAAM,IAAI,MAAM,OAAOA,CAAG,CAAC;AAAA,QACzD,SAAAH;AAAA,MAAA,IAGGC,CACR;AAAA,IACH,CAAC;AAAA,EACL,GAAG,CAACd,GAAOE,GAASC,GAAkBC,GAAgBH,EAAQ,MAAMA,EAAQ,sBAAsB,CAAC,GAE5FK;AACT;AC7EO,SAASW,EACdC,GACAC,IAAmB,IACI;AACvB,QAAM,CAACC,GAAQC,CAAS,IAAIb,EAAgB,CAAA,CAAE,GACxC,CAACc,GAAcC,CAAe,IAAIf,EAA2B,IAAI,GACjEgB,IAAuBd,EAAsB,IAAI,GACjDe,IAAiBf,EAAe,EAAE,GAGlC;AAAA,IACJ,WAAAgB;AAAA,IACA,WAAAC;AAAA,IACA,OAAOC;AAAA,IACP,SAAAf;AAAA,EAAA,IACEd,EAAauB,GAAc;AAAA,IAC7B,MAAM,CAACA,KAAgB,CAACH;AAAA,IACxB,wBAAwB;AAAA;AAAA,EAAA,CACzB,GAGKU,IAA6BC,EAAY,CAACC,MAAmB;AACjE,QAAI,CAACA,KAAM,CAACb;AACV,aAAO,CAAA;AAGT,QAAI;AACF,YAAMc,IAAOD,EAAG,WAAA,GAEVE,wBAAmB,IAAA;AAEzB,aAAAD,EAAK,QAAQ,CAACE,MAAa;AACzB,cAAMC,IAAQD,EAAIhB,CAAS;AAC3B,QAAIiB,KAAU,QAA+BA,MAAU,MACrDF,EAAa,IAAIE,CAAK;AAAA,MAE1B,CAAC,GAGoB,MAAM,KAAKF,CAAY;AAAA,IAG9C,SAASjB,GAAK;AACZ,qBAAQ,MAAM,4CAA4CA,CAAG,GACtD,CAAA;AAAA,IACT;AAAA,EACF,GAAG,CAACE,CAAS,CAAC;AAGd,EAAAP,EAAU,MAAM;AAEd,QAAKE,KAKDA,MAAYW,EAAqB,WAKjC,CAAAG;AAOJ,UAFAH,EAAqB,UAAUX,GAE3Be;AACF,QAAAP,EAAU,CAAA,CAAE;AAAA,eACHK,GAAW;AACpB,cAAMU,IAAkBP,EAA2BH,CAAS;AAC5D,QAAAL,EAAUe,CAAe;AAAA,MAC3B;AACE,QAAAf,EAAU,CAAA,CAAE;AAAA,EAEhB,GAAG,CAACK,GAAWC,GAAWC,GAAYf,GAASgB,CAA0B,CAAC,GAG1ElB,EAAU,MAAM;AACd,KAAI,CAACO,KAAa,CAACC,OACjBE,EAAU,CAAA,CAAE,GACZE,EAAgB,IAAI,GACpBC,EAAqB,UAAU,MAC/BC,EAAe,UAAU;AAAA,EAE7B,GAAG,CAACP,GAAWC,CAAO,CAAC;AAGvB,QAAMkB,IAAUP,EAAY,MAAM;AAChC,QAAKZ,GAEL;AAAA,MAAAO,EAAe,UAAU;AAEzB,UAAI;AACF,cAAMzB,IAAmB;AAAA,UACvB,YAAY,CAACkB,CAAS;AAAA,UACtB,OAAO;AAAA,UACP,OAAO,EAAE,CAACA,CAAS,GAAG,MAAA;AAAA,QAAM;AAE9B,QAAAK,EAAgBvB,CAAK;AAAA,MACvB,SAASgB,GAAK;AACZ,gBAAQ,MAAM,yBAAyBA,CAAG;AAAA,MAC5C;AAAA;AAAA,EACF,GAAG,CAACE,CAAS,CAAC,GAGRoB,IAAeR,EAAY,CAACS,GAAoBC,IAAiB,OAAU;AAC/E,QAAKtB,KAKD,GAACsB,KAASD,MAAed,EAAe,UAI5C;AAAA,MAAAA,EAAe,UAAUc;AAEzB,UAAI;AAEF,cAAMvC,IAAmB;AAAA,UACvB,YAAY,CAACkB,CAAS;AAAA,UACtB,OAAO;AAAA,UACP,OAAO,EAAE,CAACA,CAAS,GAAG,MAAA;AAAA,QAAM;AAG9B,QAAIqB,KAAcA,EAAW,WAC3BvC,EAAM,UAAU,CAAC;AAAA,UACf,QAAQkB;AAAA,UACR,UAAU;AAAA,UACV,QAAQ,CAACqB,EAAW,KAAA,CAAM;AAAA,QAAA,CAC3B,IAGHhB,EAAgBvB,CAAK;AAAA,MACvB,SAASgB,GAAK;AACZ,gBAAQ,MAAM,gCAAgCA,CAAG;AAAA,MACnD;AAAA;AAAA,EACF,GAAG,CAACE,CAAS,CAAC;AAEd,SAAO;AAAA,IACL,QAAAE;AAAA,IACA,SAASO;AAAA,IACT,OAAOC,IAAcA,aAAsB,QAAQA,EAAW,UAAU,OAAOA,CAAU,IAAK;AAAA,IAC9F,SAAAS;AAAA,IACA,cAAAC;AAAA,EAAA;AAEJ;AC1JO,SAASG,EAAeN,GAAUO,GAAkB;AACzD,QAAM,CAACC,GAAgBC,CAAiB,IAAIpC,EAAS2B,CAAK;AAE1D,SAAAxB,EAAU,MAAM;AAEd,UAAMkC,IAAU,WAAW,MAAM;AAC/B,MAAAD,EAAkBT,CAAK;AAAA,IACzB,GAAGO,CAAK;AAGR,WAAO,MAAM;AACX,mBAAaG,CAAO;AAAA,IACtB;AAAA,EACF,GAAG,CAACV,GAAOO,CAAK,CAAC,GAEVC;AACT;AClBA,MAAMG,IAAe,MACfC,IAAmB;AAgBlB,SAASC,IAAuD;AAErE,QAAM,CAACC,GAAgBC,CAAiB,IAAI1C;AAAA,IAAS,MACnD,OAAO,SAAW,MAAc,OAAO,aAAasC;AAAA,EAAA,GAEhDK,IAAczC,EAA8B,IAAI,GAChD0C,IAAa1C,EAA8B,IAAI,GAI/C2C,IAAevB,EAAY,CAACwB,MAAgC;AAShE,QAPIH,EAAY,YACdA,EAAY,QAAQ,WAAA,GACpBA,EAAY,UAAU,OAGxBC,EAAW,UAAUE,GAEjBA,GAAM;AAER,YAAMC,IAAeD,EAAK;AAC1B,MAAIC,IAAe,KACjBL,EAAkBK,CAAY,GAIhCJ,EAAY,UAAU,IAAI,eAAe,CAACK,MAAY;AACpD,cAAMC,IAAQD,EAAQ,CAAC,GAAG,YAAY;AACtC,QAAIC,KAASA,IAAQ,KACnBP,EAAkBO,CAAK;AAAA,MAE3B,CAAC,GACDN,EAAY,QAAQ,QAAQG,CAAI;AAAA,IAClC;AAAA,EACF,GAAG,CAAA,CAAE;AAGL,EAAA3C,EAAU,MACD,MAAM;AACX,IAAIwC,EAAY,WACdA,EAAY,QAAQ,WAAA;AAAA,EAExB,GACC,CAAA,CAAE,GAILxC,EAAU,MAAM;AACd,UAAM+C,IAAqB,MAAM;AAC/B,UAAIN,EAAW,SAAS;AACtB,cAAMK,IAAQL,EAAW,QAAQ;AACjC,QAAIK,IAAQ,KACVP,EAAkBO,CAAK;AAAA,MAE3B;AAAA,IACF;AAEA,WAAO,iBAAiB,UAAUC,CAAkB;AAGpD,UAAMC,IAAY,WAAWD,GAAoB,GAAG;AAEpD,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAUA,CAAkB,GACvD,aAAaC,CAAS;AAAA,IACxB;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,QAAMC,IAAcC,EAA8B,MAC5CZ,KAAkBH,IAAqB,YACvCG,KAAkBF,IAAyB,WACxC,UACN,CAACE,CAAc,CAAC,GAEba,IAAcD,EAAQ,MACtBD,MAAgB,WAAiB,IAC9BX,IAAiBH,GACvB,CAACG,GAAgBW,CAAW,CAAC;AAIhC,SAAO;AAAA,IACL,cAAAP;AAAA,IACA,gBAAAJ;AAAA,IACA,aAAAW;AAAA,IACA,aAAAE;AAAA,IACA,YAPiBF,MAAgB;AAAA,IAQjC,aAAad;AAAA,EAAA;AAEjB;"}
|
|
1
|
+
{"version":3,"file":"hooks.js","sources":["../../src/client/hooks/useCubeQuery.ts","../../src/client/hooks/useFilterValues.ts","../../src/client/hooks/useDebounce.ts","../../src/client/hooks/useResponsiveDashboard.ts"],"sourcesContent":["/**\n * React hook for executing Cube queries\n * Replaces @cubejs-client/react useCubeQuery\n */\n\nimport { useState, useEffect, useRef } from 'react'\nimport { useCubeContext } from '../providers/CubeProvider'\nimport type { CubeQuery, CubeQueryOptions, CubeResultSet } from '../types'\n\ninterface UseCubeQueryResult {\n resultSet: CubeResultSet | null\n isLoading: boolean\n error: Error | null\n queryId: string | null\n}\n\nexport function useCubeQuery(\n query: CubeQuery | null,\n options: CubeQueryOptions = {}\n): UseCubeQueryResult {\n const { cubeApi, batchCoordinator, enableBatching } = useCubeContext()\n \n // Use a single state object to ensure atomic updates\n const [state, setState] = useState<UseCubeQueryResult>({\n resultSet: null,\n isLoading: false,\n error: null,\n queryId: null\n })\n \n // Track the last query to avoid unnecessary re-fetches\n const lastQueryRef = useRef<string>('')\n \n useEffect(() => {\n // Skip if query is null or skip option is true\n if (!query || options.skip) {\n return\n }\n\n // Create a stable query string for comparison\n const queryString = JSON.stringify(query)\n \n // Skip if query hasn't changed (unless resetResultSetOnChange is true)\n if (queryString === lastQueryRef.current && !options.resetResultSetOnChange) {\n return\n }\n\n lastQueryRef.current = queryString\n \n // Create a unique ID for this query execution\n const queryId = `${Date.now()}_${Math.random().toString(36).substring(7)}`\n\n // Update state atomically with new query ID and loading state\n setState(prevState => ({\n resultSet: options.resetResultSetOnChange ? null : prevState.resultSet,\n isLoading: true,\n error: null,\n queryId\n }))\n\n // Use batch coordinator if enabled, otherwise use direct API call\n const executeQuery = enableBatching && batchCoordinator\n ? batchCoordinator.register(query)\n : cubeApi.load(query)\n\n executeQuery\n .then((result) => {\n setState(prevState => {\n // Only update if this is still the current query\n if (prevState.queryId === queryId) {\n return {\n resultSet: result,\n isLoading: false,\n error: null,\n queryId\n }\n }\n return prevState\n })\n })\n .catch((err) => {\n setState(prevState => {\n // Only update if this is still the current query\n if (prevState.queryId === queryId) {\n return {\n resultSet: null,\n isLoading: false,\n error: err instanceof Error ? err : new Error(String(err)),\n queryId\n }\n }\n return prevState\n })\n })\n }, [query, cubeApi, batchCoordinator, enableBatching, options.skip, options.resetResultSetOnChange])\n\n return state\n}","/**\n * Hook for fetching distinct field values for filter dropdowns\n * Uses the /load API to get actual data values\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport { useCubeQuery } from '../hooks/useCubeQuery'\nimport type { CubeQuery } from '../types'\n\ninterface UseFilterValuesResult {\n values: any[]\n loading: boolean\n error: string | null\n refetch: () => void\n searchValues: (searchTerm: string, force?: boolean) => void\n}\n\n/**\n * Custom hook to fetch distinct values for a field\n */\nexport function useFilterValues(\n fieldName: string | null, \n enabled: boolean = true\n): UseFilterValuesResult {\n const [values, setValues] = useState<any[]>([])\n const [currentQuery, setCurrentQuery] = useState<CubeQuery | null>(null)\n const lastProcessedQueryId = useRef<string | null>(null)\n const lastSearchTerm = useRef<string>('')\n \n // Use cube query hook for actual data fetching\n const { \n resultSet, \n isLoading,\n error: queryError,\n queryId\n } = useCubeQuery(currentQuery, {\n skip: !currentQuery || !enabled,\n resetResultSetOnChange: true // Clear old results when query changes\n })\n \n // Extract unique values from result set\n const extractValuesFromResultSet = useCallback((rs: any): any[] => {\n if (!rs || !fieldName) {\n return []\n }\n \n try {\n const data = rs.tablePivot()\n \n const uniqueValues = new Set<any>()\n \n data.forEach((row: any) => {\n const value = row[fieldName]\n if (value !== null && value !== undefined && value !== '') {\n uniqueValues.add(value)\n }\n })\n \n // Convert to array - already sorted by query\n const sortedValues = Array.from(uniqueValues)\n \n return sortedValues\n } catch (err) {\n console.error('Error extracting values from result set:', err)\n return []\n }\n }, [fieldName])\n \n // Process results only when we have a new matching query result\n useEffect(() => {\n // Skip if no query ID\n if (!queryId) {\n return\n }\n \n // Skip if we've already processed this query\n if (queryId === lastProcessedQueryId.current) {\n return\n }\n \n // Skip if still loading\n if (isLoading) {\n return\n }\n \n // Mark as processed\n lastProcessedQueryId.current = queryId\n \n if (queryError) {\n setValues([])\n } else if (resultSet) {\n const extractedValues = extractValuesFromResultSet(resultSet)\n setValues(extractedValues)\n } else {\n setValues([])\n }\n }, [resultSet, isLoading, queryError, queryId, extractValuesFromResultSet])\n \n // Reset values when fieldName becomes null or enabled changes\n useEffect(() => {\n if (!fieldName || !enabled) {\n setValues([])\n setCurrentQuery(null)\n lastProcessedQueryId.current = null\n lastSearchTerm.current = ''\n }\n }, [fieldName, enabled])\n \n // Refetch function\n const refetch = useCallback(() => {\n if (!fieldName) return\n \n lastSearchTerm.current = ''\n \n try {\n const query: CubeQuery = {\n dimensions: [fieldName], \n limit: 25,\n order: { [fieldName]: 'asc' }\n }\n setCurrentQuery(query)\n } catch (err) {\n console.error('Error creating query:', err)\n }\n }, [fieldName])\n\n // Search function for server-side filtering\n const searchValues = useCallback((searchTerm: string, force: boolean = false) => {\n if (!fieldName) {\n return\n }\n \n // Don't create a new query if the search term hasn't changed (unless forced)\n if (!force && searchTerm === lastSearchTerm.current) {\n return\n }\n \n lastSearchTerm.current = searchTerm\n \n try {\n // Create query inline to avoid dependency issues\n const query: CubeQuery = {\n dimensions: [fieldName], \n limit: 25,\n order: { [fieldName]: 'asc' }\n }\n \n if (searchTerm && searchTerm.trim()) {\n query.filters = [{\n member: fieldName,\n operator: 'contains',\n values: [searchTerm.trim()]\n }]\n }\n \n setCurrentQuery(query)\n } catch (err) {\n console.error('Error creating search query:', err)\n }\n }, [fieldName])\n \n return {\n values,\n loading: isLoading,\n error: queryError ? (queryError instanceof Error ? queryError.message : String(queryError)) : null,\n refetch,\n searchValues\n }\n}","/**\n * Custom hook for debouncing values\n * Delays updating the value until after the specified delay has passed\n * since the last change\n */\n\nimport { useState, useEffect } from 'react'\n\n/**\n * Debounces a value by the specified delay\n * @param value The value to debounce\n * @param delay The delay in milliseconds\n * @returns The debounced value\n */\nexport function useDebounce<T>(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState(value)\n\n useEffect(() => {\n // Set up a timer to update the debounced value after the delay\n const handler = setTimeout(() => {\n setDebouncedValue(value)\n }, delay)\n\n // Clean up the timer if the value changes before the delay\n return () => {\n clearTimeout(handler)\n }\n }, [value, delay])\n\n return debouncedValue\n}","/**\n * Custom hook for responsive dashboard layout management\n * Implements a three-tier responsive strategy:\n * - Desktop (1200px+): Normal grid layout with full editing\n * - Scaled (768-1199px): CSS transform scaling, read-only\n * - Mobile (<768px): Single-column stacked layout, read-only\n */\n\nimport { useState, useEffect, useRef, useMemo, useCallback, RefCallback } from 'react'\n\nexport type DashboardDisplayMode = 'desktop' | 'scaled' | 'mobile'\n\nconst DESIGN_WIDTH = 1200\nconst MOBILE_THRESHOLD = 768\n\nexport interface UseResponsiveDashboardResult {\n containerRef: RefCallback<HTMLDivElement>\n containerWidth: number\n displayMode: DashboardDisplayMode\n scaleFactor: number\n isEditable: boolean\n designWidth: number\n}\n\n/**\n * Hook for managing responsive dashboard layouts\n * Uses ResizeObserver for accurate width detection and calculates\n * the appropriate display mode and scale factor\n */\nexport function useResponsiveDashboard(): UseResponsiveDashboardResult {\n // Start with window width as initial estimate\n const [containerWidth, setContainerWidth] = useState(() =>\n typeof window !== 'undefined' ? window.innerWidth : DESIGN_WIDTH\n )\n const observerRef = useRef<ResizeObserver | null>(null)\n const elementRef = useRef<HTMLDivElement | null>(null)\n\n // Ref callback - called when element is attached/detached\n // This is key: unlike useEffect, this fires immediately when the DOM element exists\n const containerRef = useCallback((node: HTMLDivElement | null) => {\n // Cleanup previous observer\n if (observerRef.current) {\n observerRef.current.disconnect()\n observerRef.current = null\n }\n\n elementRef.current = node\n\n if (node) {\n // Get initial width immediately (synchronously when element attaches)\n const initialWidth = node.offsetWidth\n if (initialWidth > 0) {\n setContainerWidth(initialWidth)\n }\n\n // Set up ResizeObserver for ongoing changes\n observerRef.current = new ResizeObserver((entries) => {\n const width = entries[0]?.contentRect.width\n if (width && width > 0) {\n setContainerWidth(width)\n }\n })\n observerRef.current.observe(node)\n }\n }, [])\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (observerRef.current) {\n observerRef.current.disconnect()\n }\n }\n }, [])\n\n // Fallback: window resize listener to catch resize events that ResizeObserver might miss\n // This is particularly important for containers in flex/grid layouts or deeply nested elements\n useEffect(() => {\n const handleWindowResize = () => {\n if (elementRef.current) {\n const width = elementRef.current.offsetWidth\n if (width > 0) {\n setContainerWidth(width)\n }\n }\n }\n\n window.addEventListener('resize', handleWindowResize)\n\n // Also measure after a short delay to catch late layout calculations\n const timeoutId = setTimeout(handleWindowResize, 100)\n\n return () => {\n window.removeEventListener('resize', handleWindowResize)\n clearTimeout(timeoutId)\n }\n }, [])\n\n const displayMode = useMemo<DashboardDisplayMode>(() => {\n if (containerWidth >= DESIGN_WIDTH) return 'desktop'\n if (containerWidth >= MOBILE_THRESHOLD) return 'scaled'\n return 'mobile'\n }, [containerWidth])\n\n const scaleFactor = useMemo(() => {\n if (displayMode !== 'scaled') return 1\n return containerWidth / DESIGN_WIDTH\n }, [containerWidth, displayMode])\n\n const isEditable = displayMode === 'desktop'\n\n return {\n containerRef,\n containerWidth,\n displayMode,\n scaleFactor,\n isEditable,\n designWidth: DESIGN_WIDTH\n }\n}\n"],"names":["useCubeQuery","query","options","cubeApi","batchCoordinator","enableBatching","useCubeContext","state","setState","useState","lastQueryRef","useRef","useEffect","queryString","queryId","prevState","result","err","useFilterValues","fieldName","enabled","values","setValues","currentQuery","setCurrentQuery","lastProcessedQueryId","lastSearchTerm","resultSet","isLoading","queryError","extractValuesFromResultSet","useCallback","rs","data","uniqueValues","row","value","extractedValues","refetch","searchValues","searchTerm","force","useDebounce","delay","debouncedValue","setDebouncedValue","handler","DESIGN_WIDTH","MOBILE_THRESHOLD","useResponsiveDashboard","containerWidth","setContainerWidth","observerRef","elementRef","containerRef","node","initialWidth","entries","width","handleWindowResize","timeoutId","displayMode","useMemo","scaleFactor"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IconifyIcon } from '@iconify/types';
|
|
2
|
-
import { ComponentType } from 'react';
|
|
2
|
+
import { ComponentType, CSSProperties } from 'react';
|
|
3
3
|
/**
|
|
4
4
|
* Icon categories for organization and filtering
|
|
5
5
|
*/
|
|
@@ -10,7 +10,7 @@ export type IconCategory = 'action' | 'field' | 'chart' | 'measure' | 'state' |
|
|
|
10
10
|
export interface IconProps {
|
|
11
11
|
className?: string;
|
|
12
12
|
'aria-hidden'?: boolean;
|
|
13
|
-
style?:
|
|
13
|
+
style?: CSSProperties;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Icon component type that can be used in JSX
|
package/dist/client/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { e as s, a as r, A as t, d as o, D as l, M as i, c as n, P as d, b as c, Q as h } from "./chunks/components-
|
|
1
|
+
import { e as s, a as r, A as t, d as o, D as l, M as i, c as n, P as d, b as c, Q as h } from "./chunks/components-DnM9CCUS.js";
|
|
2
2
|
import { L as g } from "./chunks/chart-chartcontainer-CdwzIKP1.js";
|
|
3
|
-
import { L as p, c as T, f as m, g as u, i as b, p as I, a as
|
|
4
|
-
import {
|
|
3
|
+
import { L as p, c as T, f as m, g as u, i as b, p as I, a as D } from "./chunks/charts-CHzWeaY1.js";
|
|
4
|
+
import { r as x, T as A, z as M, v as P, g as E, x as L, D as S, A as v, B, y as Q, u as V, w } from "./chunks/chart-activitygridchart-CUGN9Xq9.js";
|
|
5
5
|
import { ScrollContainerProvider as F, useScrollContainer as R } from "./providers.js";
|
|
6
6
|
import { useCubeQuery as k } from "./hooks.js";
|
|
7
7
|
import { D as H, g as N, h as O, b as U, c as j, e as q, f as J, a as K, r as W, d as X, s as Y } from "./chunks/chart-activitygridchart-config-AVBoHdRn.js";
|
|
@@ -39,7 +39,7 @@ export {
|
|
|
39
39
|
S as isDarkMode,
|
|
40
40
|
b as isValidChartType,
|
|
41
41
|
I as preloadChart,
|
|
42
|
-
|
|
42
|
+
D as preloadCharts,
|
|
43
43
|
W as registerIcons,
|
|
44
44
|
X as resetIcons,
|
|
45
45
|
v as resetTheme,
|
package/dist/client/providers.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as a, v as c, u as i } from "./chunks/chart-activitygridchart-CUGN9Xq9.js";
|
|
2
2
|
import { createContext as o, useContext as r } from "react";
|
|
3
3
|
const e = o(null), n = e.Provider, C = () => r(e);
|
|
4
4
|
export {
|