{"$schema":"https:\/\/ui.particle.academy\/schema\/registry-item.json","name":"echart-3d","type":"registry:ui","title":"EChart3D","description":"3D chart wrapper (globe \/ surface \/ scatter3D).","package":"fancy-echarts","dependencies":["echarts","echarts-gl"],"registryDependencies":["types"],"files":[{"path":"components\/fancy\/echart-3d\/EChart3D.tsx","content":"import React, { forwardRef, useEffect, useMemo, useState } from \"react\";\r\nimport type { EChartsOption } from \"echarts\";\r\nimport { useECharts } from \"..\/hooks\/use-echarts\";\r\nimport type {\r\n  EChartComponentProps,\r\n  SeriesComponentProps,\r\n  ChartDivProps,\r\n} from \"..\/types\";\r\n\r\nconst DEFAULT_STYLE: React.CSSProperties = { width: \"100%\", height: 500 };\r\n\r\nlet glLoaded = false;\r\nlet glPromise: Promise<void> | null = null;\r\n\r\nfunction loadEChartsGL(): Promise<void> {\r\n  if (glLoaded) return Promise.resolve();\r\n  if (!glPromise) {\r\n    glPromise = import(\"echarts-gl\")\r\n      .then(() => {\r\n        glLoaded = true;\r\n      })\r\n      .catch((err: unknown) => {\r\n        console.warn(\"Failed to load echarts-gl for 3D charts:\", err);\r\n      });\r\n  }\r\n  return glPromise;\r\n}\r\n\r\n\/** Wrapper that delays mount until echarts-gl is loaded *\/\r\nfunction WithGL({ children, style, className }: { children: React.ReactNode; style?: React.CSSProperties; className?: string }) {\r\n  const [ready, setReady] = useState(glLoaded);\r\n\r\n  useEffect(() => {\r\n    if (glLoaded) {\r\n      setReady(true);\r\n      return;\r\n    }\r\n    loadEChartsGL().then(() => setReady(true));\r\n  }, []);\r\n\r\n  if (!ready) {\r\n    return (\r\n      <div style={{ ...DEFAULT_STYLE, ...style, display: \"flex\", alignItems: \"center\", justifyContent: \"center\" }} className={className}>\r\n        <span style={{ color: \"#9ca3af\", fontSize: 14 }}>Loading 3D engine...<\/span>\r\n      <\/div>\r\n    );\r\n  }\r\n\r\n  return <>{children}<\/>;\r\n}\r\n\r\n\/**\r\n * EChart3D \u2014 3D chart component that lazy-loads the bundled echarts-gl\r\n * engine on first mount (split into its own chunk to keep the main bundle small).\r\n *\/\r\nconst EChart3DInner = forwardRef<HTMLDivElement, EChartComponentProps>(\r\n  (\r\n    {\r\n      option,\r\n      theme,\r\n      renderer,\r\n      notMerge,\r\n      lazyUpdate,\r\n      showLoading,\r\n      loadingOption,\r\n      onEvents,\r\n      autoResize = true,\r\n      style,\r\n      className,\r\n      ...rest\r\n    },\r\n    _ref\r\n  ) => {\r\n    const { chartRef } = useECharts({\r\n      option,\r\n      theme,\r\n      renderer,\r\n      notMerge,\r\n      lazyUpdate,\r\n      showLoading,\r\n      loadingOption,\r\n      onEvents,\r\n      autoResize,\r\n    });\r\n\r\n    return (\r\n      <div\r\n        ref={chartRef}\r\n        style={{ ...DEFAULT_STYLE, ...style }}\r\n        className={className}\r\n        {...rest}\r\n      \/>\r\n    );\r\n  }\r\n);\r\n\r\nEChart3DInner.displayName = \"EChart3DInner\";\r\n\r\nconst EChart3DBase = forwardRef<HTMLDivElement, EChartComponentProps>(\r\n  (props, ref) => {\r\n    const { style, className, ...rest } = props;\r\n    return (\r\n      <WithGL style={style} className={className}>\r\n        <EChart3DInner ref={ref} style={style} className={className} {...rest} \/>\r\n      <\/WithGL>\r\n    );\r\n  }\r\n);\r\n\r\nEChart3DBase.displayName = \"EChart3D\";\r\n\r\nfunction create3DSeriesComponent(\r\n  seriesType: string,\r\n  displayName: string\r\n): React.FC<SeriesComponentProps & ChartDivProps> {\r\n  const Component: React.FC<SeriesComponentProps & ChartDivProps> = ({\r\n    data,\r\n    title,\r\n    tooltip = true,\r\n    seriesOptions = {},\r\n    option: extraOption = {},\r\n    style,\r\n    className,\r\n    ...rest\r\n  }) => {\r\n    const option = useMemo((): EChartsOption => {\r\n      const base: any = {};\r\n\r\n      if (title) {\r\n        base.title = { text: title };\r\n      }\r\n\r\n      if (tooltip === true) {\r\n        base.tooltip = {};\r\n      } else if (tooltip && typeof tooltip === \"object\") {\r\n        base.tooltip = tooltip;\r\n      }\r\n\r\n      if ([\"bar3D\", \"scatter3D\", \"line3D\", \"surface\"].includes(seriesType)) {\r\n        base.xAxis3D = { type: \"value\" };\r\n        base.yAxis3D = { type: \"value\" };\r\n        base.zAxis3D = { type: \"value\" };\r\n        base.grid3D = {};\r\n      }\r\n\r\n      base.series = [{ type: seriesType, data, ...seriesOptions }];\r\n\r\n      return { ...base, ...extraOption } as EChartsOption;\r\n    }, [data, title, tooltip, seriesOptions, extraOption]);\r\n\r\n    return (\r\n      <EChart3DBase\r\n        option={option}\r\n        style={style}\r\n        className={className}\r\n        {...rest}\r\n      \/>\r\n    );\r\n  };\r\n\r\n  Component.displayName = displayName;\r\n  return Component;\r\n}\r\n\r\nfunction createGlobeComponent(): React.FC<\r\n  Omit<SeriesComponentProps, \"data\"> &\r\n    ChartDivProps & { globe?: any; series?: any[] }\r\n> {\r\n  const Component: React.FC<\r\n    Omit<SeriesComponentProps, \"data\"> &\r\n      ChartDivProps & { globe?: any; series?: any[] }\r\n  > = ({\r\n    title,\r\n    globe,\r\n    series = [],\r\n    option: extraOption = {},\r\n    style,\r\n    className,\r\n    ...rest\r\n  }) => {\r\n    const option = useMemo((): EChartsOption => {\r\n      const base: any = {};\r\n\r\n      if (title) {\r\n        base.title = { text: title };\r\n      }\r\n\r\n      base.globe = globe ?? {\r\n        baseTexture: \"\",\r\n        shading: \"lambert\",\r\n        atmosphere: { show: true },\r\n      };\r\n\r\n      if (series.length > 0) {\r\n        base.series = series;\r\n      }\r\n\r\n      return { ...base, ...extraOption } as EChartsOption;\r\n    }, [title, globe, series, extraOption]);\r\n\r\n    return (\r\n      <EChart3DBase\r\n        option={option}\r\n        style={style}\r\n        className={className}\r\n        {...rest}\r\n      \/>\r\n    );\r\n  };\r\n\r\n  Component.displayName = \"EChart3D.Globe\";\r\n  return Component;\r\n}\r\n\r\nexport const EChart3D = Object.assign(EChart3DBase, {\r\n  Bar: create3DSeriesComponent(\"bar3D\", \"EChart3D.Bar\"),\r\n  Scatter: create3DSeriesComponent(\"scatter3D\", \"EChart3D.Scatter\"),\r\n  Line: create3DSeriesComponent(\"line3D\", \"EChart3D.Line\"),\r\n  Surface: create3DSeriesComponent(\"surface\", \"EChart3D.Surface\"),\r\n  Globe: createGlobeComponent(),\r\n});\r\n","type":"registry:ui","target":"components\/fancy\/echart-3d\/EChart3D.tsx"}]}