visualifyjs 2.5.3-2.dev → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/.claude/mem/TIMELINE.md +36 -0
  2. package/.claude/mem/notes/2026-02-11-3d-visualization-docs-fix-external-script-solution.md +24 -0
  3. package/.claude/mem/notes/2026-02-11-3d-visualization-docs-fix-session-summary.md +43 -0
  4. package/.claude/mem/notes/2026-02-11-cli-fix-editor-command-alias.md +26 -0
  5. package/.claude/mem/notes/2026-02-11-phase-3-developer-experience-completed.md +51 -0
  6. package/.claude/mem/notes/2026-02-11-phase-4-web-workers-implementation-complete.md +59 -0
  7. package/.claude/mem/notes/2026-02-11-visualify-phase-2-3d-visualization-complete.md +50 -0
  8. package/.claude/mem/notes/2026-02-11-visualify-phase-2-committed-ready-for-phase-3.md +33 -0
  9. package/.claude/mem/notes/2026-02-11-visualify-phase-3-complete-developer-experience.md +52 -0
  10. package/.claude/mem/notes/2026-02-11-visualify-repository-cleanup-complete.md +28 -0
  11. package/.claude/mem/notes/2026-02-18-codebase-cleanup-docsify-plugin-documentation.md +37 -0
  12. package/.claude/mem/notes/2026-02-19-css-grid-layout-fix-displaycontents-on-vcontroller.md +18 -0
  13. package/.claude/mem/notes/2026-02-19-docsify-plugin-fixes-latex-and-visualify-code-bloc.md +26 -0
  14. package/.claude/mem/notes/2026-02-19-page-mode-docs-update-decisions.md +23 -0
  15. package/.claude/mem/notes/2026-02-19-react-context-infinite-re-render-loop-fix-pattern.md +31 -0
  16. package/.claude/mem/notes/2026-02-19-version-300-bump-and-build-fixes.md +32 -0
  17. package/.claude/mem/notes/2026-02-19-visualify-build-deployment-architecture-bug-fixes.md +25 -0
  18. package/.claude/mem/notes/2026-02-19-visualify-dist-iife-self-contained-build-config.md +30 -0
  19. package/.claude/mem/notes/2026-02-19-visualify-infinite-loop-i18n-fixes.md +31 -0
  20. package/.claude/mem/notes/2026-02-19-visualify-v3-bundle-splitting-docs-restructuring.md +32 -0
  21. package/.claude/mem/notes/2026-02-20-bundle-externalization-final-architecture.md +29 -0
  22. package/.claude/mem/notes/2026-02-20-chromium-page-fix-unstable-keys-and-double-event-b.md +27 -0
  23. package/.claude/mem/notes/2026-02-20-console-cleanup-bundle-optimization-commit.md +20 -0
  24. package/.claude/mem/notes/2026-02-20-dotbio-dot-plot-fix-useeffect-dependency.md +21 -0
  25. package/.claude/mem/notes/2026-02-20-public-folder-cleanup-and-readme-rewrite.md +25 -0
  26. package/.claude/mem/notes/2026-02-20-v300-release-and-beta-channel-strategy.md +29 -0
  27. package/.claude/mem/notes/2026-02-20-visium-background-image-unknown-legend-fix.md +19 -0
  28. package/.claude/mem/notes/2026-02-20-visualify-cdn-loader-bundle-externalization.md +34 -0
  29. package/.claude/mem/sessions/session-2026-02-20-031524.md +54 -0
  30. package/.claude/settings.local.json +21 -0
  31. package/.github/workflows/static.yml.bak +51 -51
  32. package/.sisyphus/boulder.json +65 -0
  33. package/.sisyphus/plans/phase-4-advanced-optimizations.md +217 -0
  34. package/LICENSE +674 -674
  35. package/README.md +94 -59
  36. package/config-overrides.js +31 -31
  37. package/dist/stats.html +4949 -0
  38. package/dist/visualify-3d.esm.js +1 -0
  39. package/dist/visualify-3d.js +1 -0
  40. package/dist/visualify-core.esm.js +1 -0
  41. package/dist/visualify-core.js +1 -0
  42. package/dist/visualify-docs.esm.js +1 -0
  43. package/dist/visualify-docs.js +1 -0
  44. package/dist/visualify-loader.js +1 -0
  45. package/dist/visualify-pages.esm.js +1 -0
  46. package/dist/visualify-pages.js +1 -0
  47. package/dist/visualify-portal.esm.js +1 -0
  48. package/dist/visualify-portal.js +1 -0
  49. package/dist/visualify-shared.js +26571 -0
  50. package/dist/visualify.js +1 -188
  51. package/docs/CHANGELOG.md +148 -0
  52. package/docs/cli/commands.md +513 -0
  53. package/docs/configuration/visualify-json.md +474 -0
  54. package/docs/docs/3d-visualization.md +374 -0
  55. package/docs/docs/CLI.md +303 -34
  56. package/docs/docs/README.md +65 -65
  57. package/docs/docs/Rechart/bar.md +190 -190
  58. package/docs/docs/Rechart/funnel.md +241 -241
  59. package/docs/docs/Rechart/line.md +355 -355
  60. package/docs/docs/Rechart/pie.md +225 -225
  61. package/docs/docs/Rechart/radar.md +253 -253
  62. package/docs/docs/Rechart/scatter.md +298 -298
  63. package/docs/docs/_404.md +51 -51
  64. package/docs/docs/_coverpage.md +11 -11
  65. package/docs/docs/_sidebar.md +54 -44
  66. package/docs/docs/components/dotBio.md +87 -34
  67. package/docs/docs/components/echart.md +171 -82
  68. package/docs/docs/components/html.md +61 -34
  69. package/docs/docs/components/macaron.md +156 -145
  70. package/docs/docs/components/markdown.md +42 -0
  71. package/docs/docs/components/more.md +183 -142
  72. package/docs/docs/components/plotly.md +132 -62
  73. package/docs/docs/components/scatterL.md +171 -70
  74. package/docs/docs/components/visium.md +112 -57
  75. package/docs/docs/configuration.md +121 -121
  76. package/docs/docs/deploy.md +31 -31
  77. package/docs/docs/docsify-plugin.md +655 -0
  78. package/docs/docs/hmr.md +165 -0
  79. package/docs/docs/i18n.md +332 -0
  80. package/docs/docs/log.md +30 -9
  81. package/docs/docs/more-pages.md +23 -23
  82. package/docs/docs/quickstart.md +148 -124
  83. package/docs/docs/rechart-attributes.md +74 -74
  84. package/docs/docs/rechart-basic-usage.md +160 -162
  85. package/docs/docs/theme.md +5 -5
  86. package/docs/docs/typescript.md +306 -0
  87. package/docs/docs/visual-editor.md +359 -0
  88. package/docs/index.html +85 -71
  89. package/docs/manifest.json +23 -23
  90. package/docs/migration/v3-migration.md +392 -0
  91. package/docs/static/css/fluff-stuff.css +169 -169
  92. package/docs/static/css/font-awesome.min.css +4 -4
  93. package/docs/static/css/visualify.css +6 -25
  94. package/docs/static/js/3d-viz-examples.js +181 -0
  95. package/docs/static/js/configuration.js +630 -448
  96. package/docs/static/js/visualify.js +1 -188
  97. package/package.json +106 -84
  98. package/rollup.config.mjs +766 -76
  99. package/src/_css/404.css +115 -115
  100. package/src/_css/App.css +37 -37
  101. package/src/_css/autoSuggestion.css +26 -26
  102. package/src/_css/circular-progress.css +32 -32
  103. package/src/_css/index.css +36 -36
  104. package/src/_css/modern.css +350 -25
  105. package/src/_media/corner.svg +8 -8
  106. package/src/_media/download.svg +3 -3
  107. package/src/_media/logo.svg +14 -14
  108. package/src/_test/App.test.js +15 -15
  109. package/src/_utils/reportWebVitals.js +13 -13
  110. package/src/a11y/README.md +177 -0
  111. package/src/a11y/aria-labels.js +339 -0
  112. package/src/a11y/color-contrast.js +535 -0
  113. package/src/a11y/index.js +197 -0
  114. package/src/a11y/keyboard-nav.js +523 -0
  115. package/src/a11y/styles.css +165 -0
  116. package/src/cli/commands/dev.js +214 -0
  117. package/src/cli/commands/docs.js +521 -0
  118. package/src/cli/commands/edit.js +379 -0
  119. package/src/cli/commands/init.js +213 -0
  120. package/src/cli/commands/portal.js +236 -0
  121. package/src/cli/dev-server.js +530 -0
  122. package/src/cli/hmr.js +456 -0
  123. package/src/cli/index.js +180 -0
  124. package/src/cli/utils/config.js +207 -0
  125. package/src/cli/utils/logger.js +241 -0
  126. package/src/config/defaults.ts +122 -0
  127. package/src/config/index.ts +72 -0
  128. package/src/config/loader.ts +478 -0
  129. package/src/config/schema.ts +227 -0
  130. package/src/config/validator.ts +337 -0
  131. package/src/core/appContext.js +34 -27
  132. package/src/core/components/Bar.js +383 -0
  133. package/src/core/components/Bar3D.js +473 -0
  134. package/src/core/components/LargeDatasetChart.js +296 -0
  135. package/src/core/components/Line3D.js +310 -0
  136. package/src/core/components/Scatter.js +392 -188
  137. package/src/core/components/Scatter3D.js +455 -0
  138. package/src/core/components/ScatterBio.js +601 -572
  139. package/src/core/components/Surface3D.js +326 -0
  140. package/src/core/components/ThreeCustom.js +648 -0
  141. package/src/core/components/ThreeScene.js +459 -0
  142. package/src/core/components/VisiumPlot.js +191 -165
  143. package/src/core/components/browser.js +42 -42
  144. package/src/core/components/dotplot.js +413 -413
  145. package/src/core/components/html.js +29 -29
  146. package/src/core/components/list.js +178 -178
  147. package/src/core/components/macaron.js +206 -201
  148. package/src/core/components/markdown.js +56 -56
  149. package/src/core/components/parser.scatterBio.js +582 -587
  150. package/src/core/components/ratio.js +82 -80
  151. package/src/core/components/scatterL.js +206 -173
  152. package/src/core/components/searchbar.js +156 -131
  153. package/src/core/components/selection.js +310 -193
  154. package/src/core/components/timeline.js +236 -281
  155. package/src/core/components/visium.js +114 -97
  156. package/src/core/data-processor.js +413 -0
  157. package/src/core/fetch/condfetch.js +82 -82
  158. package/src/core/fetch/fetch.js +92 -92
  159. package/src/core/fetch/json.js +29 -29
  160. package/src/core/fetch/vfetch.js +42 -42
  161. package/src/core/hmr-client.js +724 -0
  162. package/src/core/liveEditor.js +44 -44
  163. package/src/core/modules/codeEditorWithPreview.js +104 -104
  164. package/src/core/modules/echarts/common.js +20 -20
  165. package/src/core/modules/echarts/gl.js +228 -0
  166. package/src/core/modules/echarts/presetHandler.js +41 -41
  167. package/src/core/modules/echarts/presets/esodev.chromium.js +172 -172
  168. package/src/core/modules/echarts/presets/esodev.codex.js +130 -130
  169. package/src/core/modules/echarts/presets/esodev.visium.js +123 -123
  170. package/src/core/modules/echarts/presets/mmtrbc.js +186 -186
  171. package/src/core/modules/echarts.js +70 -71
  172. package/src/core/modules/echartsUtils.js +43 -43
  173. package/src/core/modules/echartswitcher.js +227 -152
  174. package/src/core/modules/replotly/presetHandler.js +24 -24
  175. package/src/core/modules/replotly/presets/minimum.js +18 -18
  176. package/src/core/modules/replotly/presets/mmtrbc.dot.js +114 -114
  177. package/src/core/modules/replotly/presets/mmtrbc.violin.js +100 -100
  178. package/src/core/modules/replotly.js +74 -71
  179. package/src/core/modules/threejs/Camera.js +373 -0
  180. package/src/core/modules/threejs/Lighting.js +459 -0
  181. package/src/core/modules/threejs/Renderer.js +364 -0
  182. package/src/core/modules/threejs/Scene.js +266 -0
  183. package/src/core/modules/threejs/index.js +155 -0
  184. package/src/core/pages/404.js +50 -50
  185. package/src/core/pages/error.js +27 -27
  186. package/src/core/pages/jsonPage.js +62 -62
  187. package/src/core/pages/loading.js +44 -44
  188. package/src/core/parser/echart.data.js +204 -183
  189. package/src/core/parser/echart.features.js +125 -125
  190. package/src/core/parser/echart.general.js +147 -147
  191. package/src/core/parser/echart.hilbert.js +57 -57
  192. package/src/core/parser/echart.parser.js +210 -210
  193. package/src/core/parser/echart.series.js +67 -67
  194. package/src/core/parser/echart.types.js +76 -76
  195. package/src/core/parser/plotly.config.js +10 -10
  196. package/src/core/parser/plotly.data.js +132 -132
  197. package/src/core/parser/plotly.layout.js +9 -9
  198. package/src/core/parser/plotly.violin.js +18 -18
  199. package/src/core/recharts.js +361 -62
  200. package/src/core/router/alias.js +49 -49
  201. package/src/core/router/jsonRouter.js +31 -31
  202. package/src/core/themes/modern.js +32 -32
  203. package/src/core/themes/themeSelector.js +33 -33
  204. package/src/core/visualify.js +213 -47
  205. package/src/core/widgets/circularProgress.js +23 -23
  206. package/src/core/widgets/controller.js +116 -83
  207. package/src/core/widgets/errorBoundary.js +36 -36
  208. package/src/core/widgets/footer.js +185 -177
  209. package/src/core/widgets/header.js +238 -234
  210. package/src/core/widgets/layout/Grid.js +31 -31
  211. package/src/core/widgets/layout.js +36 -36
  212. package/src/core/widgets/mapping.js +56 -42
  213. package/src/core/workers/data-worker.js +349 -0
  214. package/src/core/workers/worker-pool.js +396 -0
  215. package/src/docsify/bundle.js +215 -0
  216. package/src/docsify/markdown.js +271 -0
  217. package/src/docsify/plugin.js +268 -0
  218. package/src/editor/README.md +172 -0
  219. package/src/editor/components/ChartBuilder.jsx +341 -0
  220. package/src/editor/components/ChartTypeSidebar.jsx +91 -0
  221. package/src/editor/components/Editor.jsx +367 -0
  222. package/src/editor/components/Preview.jsx +446 -0
  223. package/src/editor/components/PropertyPanel.jsx +468 -0
  224. package/src/editor/components/StatusBar.jsx +85 -0
  225. package/src/editor/context/EditorContext.js +248 -0
  226. package/src/editor/hooks/useDebounce.js +32 -0
  227. package/src/editor/index.js +315 -0
  228. package/src/editor/styles/editor.css +637 -0
  229. package/src/editor/utils/chartValidator.js +263 -0
  230. package/src/entries/charts3d.js +70 -0
  231. package/src/entries/core.js +78 -0
  232. package/src/entries/docs.js +154 -0
  233. package/src/entries/pages.js +93 -0
  234. package/src/entries/portal.js +204 -0
  235. package/src/entries/shared.js +50 -0
  236. package/src/i18n/formatters.js +455 -0
  237. package/src/i18n/index.js +169 -0
  238. package/src/i18n/locales/ar.json +137 -0
  239. package/src/i18n/locales/de.json +137 -0
  240. package/src/i18n/locales/en.json +137 -0
  241. package/src/i18n/locales/es.json +137 -0
  242. package/src/i18n/locales/he.json +137 -0
  243. package/src/i18n/locales/zh.json +137 -0
  244. package/src/i18n/rtl.css +183 -0
  245. package/src/index.js +82 -62
  246. package/src/loader.js +103 -0
  247. package/src/setupTests.js +5 -5
  248. package/tsconfig.json +51 -0
  249. package/types/charts.d.ts +569 -0
  250. package/types/components.d.ts +441 -0
  251. package/types/config.d.ts +199 -0
  252. package/types/index.d.ts +353 -0
@@ -0,0 +1,296 @@
1
+ /**
2
+ * @fileoverview Large Dataset Chart Component
3
+ * @module core/components/LargeDatasetChart
4
+ *
5
+ * Optimized chart component for handling large datasets using Web Workers
6
+ * for data processing and sampling for preview rendering.
7
+ */
8
+
9
+ import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
10
+ import ReCharts from '../modules/echarts';
11
+ import Loading from '../pages/loading';
12
+ import {
13
+ getDataProcessor,
14
+ sampleData,
15
+ calculateStatistics,
16
+ filterData,
17
+ } from '../data-processor';
18
+
19
+ /**
20
+ * Default configuration for large dataset charts
21
+ */
22
+ const DEFAULT_CONFIG = {
23
+ sampleSize: 10000,
24
+ progressiveThreshold: 50000,
25
+ progressiveChunkSize: 5000,
26
+ enableWorkerProcessing: true,
27
+ showDataSummary: true,
28
+ };
29
+
30
+ /**
31
+ * LargeDatasetChart Component
32
+ * Handles datasets with 100k+ rows efficiently using Web Workers
33
+ *
34
+ * @param {Object} props - Component props
35
+ * @param {Object} props.config - Chart configuration
36
+ * @param {Object} props.style - CSS styles
37
+ * @param {React.Ref} ref - Forwarded ref
38
+ */
39
+ const LargeDatasetChart = React.forwardRef(({ config, style }, ref) => {
40
+ const {
41
+ data,
42
+ type = 'line',
43
+ sampleSize = DEFAULT_CONFIG.sampleSize,
44
+ progressiveThreshold = DEFAULT_CONFIG.progressiveThreshold,
45
+ enableWorkerProcessing = DEFAULT_CONFIG.enableWorkerProcessing,
46
+ showDataSummary = DEFAULT_CONFIG.showDataSummary,
47
+ ...chartConfig
48
+ } = config;
49
+
50
+ const [processedData, setProcessedData] = useState(null);
51
+ const [loading, setLoading] = useState({
52
+ active: true,
53
+ message: 'Processing data...',
54
+ progress: 0,
55
+ });
56
+ const [dataSummary, setDataSummary] = useState(null);
57
+ const [error, setError] = useState(null);
58
+
59
+ const processorRef = useRef(null);
60
+ const abortControllerRef = useRef(null);
61
+
62
+ // Initialize data processor
63
+ useEffect(() => {
64
+ if (enableWorkerProcessing) {
65
+ processorRef.current = getDataProcessor();
66
+ }
67
+ return () => {
68
+ if (abortControllerRef.current) {
69
+ abortControllerRef.current.abort();
70
+ }
71
+ };
72
+ }, [enableWorkerProcessing]);
73
+
74
+ // Process data when it changes
75
+ useEffect(() => {
76
+ const processData = async () => {
77
+ if (!data || !Array.isArray(data)) {
78
+ setError('Invalid data provided');
79
+ return;
80
+ }
81
+
82
+ // Reset state
83
+ setError(null);
84
+ setLoading({
85
+ active: true,
86
+ message: `Processing ${data.length.toLocaleString()} data points...`,
87
+ progress: 0,
88
+ });
89
+
90
+ abortControllerRef.current = new AbortController();
91
+
92
+ try {
93
+ let displayData = data;
94
+ let summary = null;
95
+
96
+ // For very large datasets, use sampling
97
+ if (data.length > sampleSize) {
98
+ setLoading((prev) => ({
99
+ ...prev,
100
+ message: `Sampling ${sampleSize.toLocaleString()} points from ${data.length.toLocaleString()}...`,
101
+ }));
102
+
103
+ if (enableWorkerProcessing && processorRef.current) {
104
+ displayData = await sampleData(data, sampleSize);
105
+ } else {
106
+ // Fallback to main thread sampling
107
+ const step = data.length / sampleSize;
108
+ displayData = [];
109
+ for (let i = 0; i < sampleSize; i++) {
110
+ displayData.push(data[Math.floor(i * step)]);
111
+ }
112
+ }
113
+
114
+ // Calculate summary statistics
115
+ if (showDataSummary && enableWorkerProcessing) {
116
+ const numericFields = Object.keys(data[0]).filter(
117
+ (key) => typeof data[0][key] === 'number'
118
+ );
119
+
120
+ if (numericFields.length > 0) {
121
+ summary = await calculateStatistics(data, numericFields[0]);
122
+ }
123
+ }
124
+ }
125
+
126
+ // Process data in chunks for progressive rendering
127
+ if (data.length > progressiveThreshold) {
128
+ setLoading((prev) => ({
129
+ ...prev,
130
+ message: 'Preparing progressive rendering...',
131
+ progress: 50,
132
+ }));
133
+ }
134
+
135
+ setProcessedData(displayData);
136
+ setDataSummary(summary);
137
+ setLoading({ active: false, message: null, progress: 100 });
138
+ } catch (err) {
139
+ if (err.name !== 'AbortError') {
140
+ console.error('[LargeDatasetChart] Processing error:', err);
141
+ setError(err.message);
142
+ setLoading({ active: false, message: null, progress: 0 });
143
+ }
144
+ }
145
+ };
146
+
147
+ processData();
148
+ }, [data, sampleSize, progressiveThreshold, enableWorkerProcessing, showDataSummary]);
149
+
150
+ // Generate chart options
151
+ const chartOptions = useMemo(() => {
152
+ if (!processedData) return null;
153
+
154
+ return {
155
+ title: {
156
+ text: chartConfig.title || 'Large Dataset Chart',
157
+ subtext: data.length > sampleSize
158
+ ? `Showing ${sampleSize.toLocaleString()} of ${data.length.toLocaleString()} points`
159
+ : `${data.length.toLocaleString()} points`,
160
+ left: 'center',
161
+ },
162
+ tooltip: {
163
+ trigger: 'axis',
164
+ ...chartConfig.tooltip,
165
+ },
166
+ xAxis: {
167
+ type: 'category',
168
+ data: processedData.map((_, i) => i),
169
+ ...chartConfig.xAxis,
170
+ },
171
+ yAxis: {
172
+ type: 'value',
173
+ ...chartConfig.yAxis,
174
+ },
175
+ series: [
176
+ {
177
+ type,
178
+ data: processedData,
179
+ large: data.length > progressiveThreshold,
180
+ largeThreshold: progressiveThreshold,
181
+ progressive: 5000,
182
+ progressiveThreshold: progressiveThreshold,
183
+ ...chartConfig.series,
184
+ },
185
+ ],
186
+ dataZoom: data.length > sampleSize ? [
187
+ { type: 'inside', start: 0, end: 100 },
188
+ { type: 'slider', start: 0, end: 100 },
189
+ ] : undefined,
190
+ toolbox: {
191
+ feature: {
192
+ dataZoom: { show: true },
193
+ restore: { show: true },
194
+ saveAsImage: { show: true },
195
+ },
196
+ },
197
+ };
198
+ }, [processedData, data.length, sampleSize, progressiveThreshold, type, chartConfig]);
199
+
200
+ // Handle data filtering
201
+ const handleFilter = useCallback(async (criteria) => {
202
+ if (!enableWorkerProcessing || !processorRef.current) {
203
+ console.warn('[LargeDatasetChart] Worker processing not enabled');
204
+ return;
205
+ }
206
+
207
+ setLoading({
208
+ active: true,
209
+ message: 'Filtering data...',
210
+ progress: 0,
211
+ });
212
+
213
+ try {
214
+ const filtered = await filterData(data, criteria);
215
+ setProcessedData(filtered);
216
+ setLoading({ active: false, message: null, progress: 100 });
217
+ } catch (err) {
218
+ setError(err.message);
219
+ setLoading({ active: false, message: null, progress: 0 });
220
+ }
221
+ }, [data, enableWorkerProcessing]);
222
+
223
+ // Render error state
224
+ if (error) {
225
+ return (
226
+ <div
227
+ style={{
228
+ ...style,
229
+ display: 'flex',
230
+ alignItems: 'center',
231
+ justifyContent: 'center',
232
+ backgroundColor: '#f5f5f5',
233
+ border: '1px solid #ddd',
234
+ borderRadius: '4px',
235
+ padding: '20px',
236
+ textAlign: 'center',
237
+ }}
238
+ >
239
+ <div>
240
+ <h4>Error Processing Data</h4>
241
+ <p style={{ color: '#666', marginTop: '10px' }}>{error}</p>
242
+ </div>
243
+ </div>
244
+ );
245
+ }
246
+
247
+ return (
248
+ <div style={{ ...style, position: 'relative' }}>
249
+ {loading.active && (
250
+ <Loading
251
+ message={loading.message}
252
+ progress={loading.progress}
253
+ style={{ marginTop: '10px' }}
254
+ />
255
+ )}
256
+
257
+ {showDataSummary && dataSummary && (
258
+ <div
259
+ style={{
260
+ position: 'absolute',
261
+ top: '10px',
262
+ right: '10px',
263
+ background: 'rgba(255,255,255,0.9)',
264
+ padding: '10px',
265
+ borderRadius: '4px',
266
+ boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
267
+ fontSize: '12px',
268
+ zIndex: 10,
269
+ }}
270
+ >
271
+ <div><strong>Data Summary</strong></div>
272
+ <div>Count: {dataSummary.count?.toLocaleString()}</div>
273
+ <div>Mean: {dataSummary.mean?.toFixed(2)}</div>
274
+ <div>Min: {dataSummary.min?.toFixed(2)}</div>
275
+ <div>Max: {dataSummary.max?.toFixed(2)}</div>
276
+ </div>
277
+ )}
278
+
279
+ {chartOptions && (
280
+ <ReCharts
281
+ ref={ref}
282
+ options={chartOptions}
283
+ style={{
284
+ width: config.chartWidth || '100%',
285
+ height: config.chartHeight || '400px',
286
+ opacity: loading.active ? 0.5 : 1,
287
+ }}
288
+ />
289
+ )}
290
+ </div>
291
+ );
292
+ });
293
+
294
+ LargeDatasetChart.displayName = 'LargeDatasetChart';
295
+
296
+ export default LargeDatasetChart;
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Line3D Component
3
+ * 3D line chart visualization using ECharts GL
4
+ * Supports trajectory visualization with multiple line series
5
+ * @module components/Line3D
6
+ */
7
+
8
+ import React, { useState, useEffect, forwardRef, useCallback } from 'react';
9
+ import ReCharts from '../modules/echarts';
10
+ import {
11
+ loadEChartsGL,
12
+ isWebGLSupported,
13
+ display3DError,
14
+ cleanupWebGL,
15
+ } from '../modules/echarts/gl';
16
+ import Loading from '../pages/loading';
17
+
18
+ /**
19
+ * Default configuration for 3D line charts
20
+ */
21
+ const DEFAULT_CONFIG = {
22
+ lineStyle: {
23
+ width: 4,
24
+ },
25
+ symbolSize: 8,
26
+ };
27
+
28
+ /**
29
+ * Processes data for 3D line chart
30
+ * Supports multiple series and data formats
31
+ * @param {Array|Object} data - Input data
32
+ * @returns {Array} Processed series data
33
+ */
34
+ const processLineData = (data) => {
35
+ if (!data) return [];
36
+
37
+ // Single series format: [[x, y, z], ...] or {x: [], y: [], z: []}
38
+ if (Array.isArray(data)) {
39
+ // Check if it's already in the correct format
40
+ if (data.length > 0 && Array.isArray(data[0]) && data[0].length === 3) {
41
+ return [{ data, name: 'Series 1' }];
42
+ }
43
+ // Array of series objects
44
+ if (data.length > 0 && data[0].data) {
45
+ return data;
46
+ }
47
+ }
48
+
49
+ // Object format: {x: [], y: [], z: []}
50
+ if (typeof data === 'object' && !Array.isArray(data)) {
51
+ const { x, y, z } = data;
52
+ if (Array.isArray(x) && Array.isArray(y) && Array.isArray(z)) {
53
+ const combined = x.map((xi, i) => [xi, y[i], z[i]]);
54
+ return [{ data: combined, name: 'Series 1' }];
55
+ }
56
+ }
57
+
58
+ return [];
59
+ };
60
+
61
+ /**
62
+ * Generates ECharts option for 3D line chart
63
+ * @param {Object} config - Component configuration
64
+ * @returns {Object} ECharts option object
65
+ */
66
+ const getOptions = (config) => {
67
+ const {
68
+ title,
69
+ data,
70
+ xAxis3D,
71
+ yAxis3D,
72
+ zAxis3D,
73
+ grid3D,
74
+ visualMap,
75
+ series: seriesConfig,
76
+ backgroundColor,
77
+ color,
78
+ tooltip,
79
+ legend,
80
+ toolbox,
81
+ } = config;
82
+
83
+ // Process data into series
84
+ const seriesData = processLineData(data);
85
+
86
+ if (seriesData.length === 0) {
87
+ throw new Error('Line3D requires valid data');
88
+ }
89
+
90
+ // Build series array
91
+ const series = seriesData.map((s, index) => ({
92
+ type: 'line3D',
93
+ name: s.name || `Series ${index + 1}`,
94
+ data: s.data,
95
+ lineStyle: {
96
+ width: seriesConfig?.lineStyle?.width ?? DEFAULT_CONFIG.lineStyle.width,
97
+ color: seriesConfig?.lineStyle?.color || (color?.[index]),
98
+ ...seriesConfig?.lineStyle,
99
+ },
100
+ symbolSize: seriesConfig?.symbolSize ?? DEFAULT_CONFIG.symbolSize,
101
+ symbol: seriesConfig?.symbol || 'circle',
102
+ emphasis: {
103
+ lineStyle: {
104
+ width: (seriesConfig?.lineStyle?.width ?? DEFAULT_CONFIG.lineStyle.width) + 2,
105
+ },
106
+ },
107
+ ...seriesConfig,
108
+ }));
109
+
110
+ const options = {
111
+ title:
112
+ typeof title === 'string'
113
+ ? { text: title }
114
+ : { left: 'center', top: 0, ...title },
115
+ tooltip: {
116
+ trigger: 'item',
117
+ formatter:
118
+ tooltip?.formatter ||
119
+ ((params) => {
120
+ const [x, y, z] = params.value;
121
+ return `${params.seriesName}<br/>X: ${x}<br/>Y: ${y}<br/>Z: ${z}`;
122
+ }),
123
+ ...tooltip,
124
+ },
125
+ legend: legend ?? {
126
+ show: series.length > 1,
127
+ data: series.map((s) => s.name),
128
+ },
129
+ visualMap: visualMap ?? undefined,
130
+ xAxis3D: {
131
+ name: xAxis3D?.name || 'X',
132
+ type: xAxis3D?.type || 'value',
133
+ nameGap: 25,
134
+ ...xAxis3D,
135
+ },
136
+ yAxis3D: {
137
+ name: yAxis3D?.name || 'Y',
138
+ type: yAxis3D?.type || 'value',
139
+ nameGap: 25,
140
+ ...yAxis3D,
141
+ },
142
+ zAxis3D: {
143
+ name: zAxis3D?.name || 'Z',
144
+ type: zAxis3D?.type || 'value',
145
+ nameGap: 25,
146
+ ...zAxis3D,
147
+ },
148
+ grid3D: {
149
+ boxWidth: 100,
150
+ boxDepth: 80,
151
+ boxHeight: 60,
152
+ viewControl: {
153
+ autoRotate: false,
154
+ projection: 'perspective',
155
+ ...grid3D?.viewControl,
156
+ },
157
+ light: {
158
+ main: {
159
+ intensity: 1.2,
160
+ shadow: true,
161
+ },
162
+ ambient: {
163
+ intensity: 0.3,
164
+ },
165
+ },
166
+ ...grid3D,
167
+ },
168
+ series,
169
+ };
170
+
171
+ if (backgroundColor) {
172
+ options.backgroundColor = backgroundColor;
173
+ }
174
+
175
+ if (color) {
176
+ options.color = color;
177
+ }
178
+
179
+ if (toolbox) {
180
+ options.toolbox = {
181
+ feature: {
182
+ ...toolbox,
183
+ saveAsImage: { show: toolbox.saveAsImage?.show ?? false },
184
+ },
185
+ };
186
+ }
187
+
188
+ return options;
189
+ };
190
+
191
+ /**
192
+ * Line3D Component - 3D line chart visualization
193
+ * @param {Object} props - Component props
194
+ * @param {Object} props.props - Configuration object
195
+ * @param {Object} props.style - CSS styles
196
+ * @param {React.Ref} ref - Forwarded ref
197
+ */
198
+ const Line3D = forwardRef(({ props, style }, ref) => {
199
+ const { config = {} } = props;
200
+ const [options, setOptions] = useState(null);
201
+ const [loading, setLoading] = useState({
202
+ active: true,
203
+ message: 'Loading 3D library...',
204
+ });
205
+ const [error, setError] = useState(null);
206
+
207
+ /**
208
+ * Initialize ECharts GL and set up chart options
209
+ */
210
+ const initChart = useCallback(async () => {
211
+ try {
212
+ setLoading({ active: true, message: 'Loading 3D library...' });
213
+ setError(null);
214
+
215
+ // Check WebGL support
216
+ if (!isWebGLSupported()) {
217
+ throw new Error('WebGL not supported');
218
+ }
219
+
220
+ // Lazy load ECharts GL
221
+ await loadEChartsGL();
222
+
223
+ // Generate chart options
224
+ const chartOptions = getOptions(config);
225
+ setOptions(chartOptions);
226
+ setLoading({ active: false, message: null });
227
+ } catch (err) {
228
+ const errorMessage = display3DError(err, { useToast: true });
229
+ setError(errorMessage);
230
+ setLoading({ active: true, message: errorMessage });
231
+ }
232
+ }, [config]);
233
+
234
+ // Initialize chart on mount and config change
235
+ useEffect(() => {
236
+ initChart();
237
+ }, [initChart]);
238
+
239
+ // Cleanup on unmount
240
+ useEffect(() => {
241
+ return () => {
242
+ if (ref?.current) {
243
+ cleanupWebGL(ref.current.getEchartsInstance?.());
244
+ }
245
+ };
246
+ }, [ref]);
247
+
248
+ // Handle resize
249
+ useEffect(() => {
250
+ const handleResize = () => {
251
+ if (ref?.current) {
252
+ const chart = ref.current.getEchartsInstance?.();
253
+ if (chart && !chart.isDisposed()) {
254
+ chart.resize();
255
+ }
256
+ }
257
+ };
258
+
259
+ window.addEventListener('resize', handleResize);
260
+ return () => window.removeEventListener('resize', handleResize);
261
+ }, [ref]);
262
+
263
+ // Render error state
264
+ if (error) {
265
+ return (
266
+ <div
267
+ id={props.id}
268
+ style={{
269
+ ...style,
270
+ display: 'flex',
271
+ alignItems: 'center',
272
+ justifyContent: 'center',
273
+ backgroundColor: '#f5f5f5',
274
+ border: '1px solid #ddd',
275
+ borderRadius: '4px',
276
+ padding: '20px',
277
+ textAlign: 'center',
278
+ }}
279
+ >
280
+ <div>
281
+ <h4>3D Chart Unavailable</h4>
282
+ <p style={{ color: '#666', marginTop: '10px' }}>{error}</p>
283
+ </div>
284
+ </div>
285
+ );
286
+ }
287
+
288
+ return (
289
+ <div id={props.id} style={{ ...style, position: 'relative' }}>
290
+ {loading.active && (
291
+ <Loading message={loading.message} style={{ marginTop: '10px' }} />
292
+ )}
293
+ {options && (
294
+ <ReCharts
295
+ ref={ref}
296
+ options={options}
297
+ style={{
298
+ width: config.chartWidth || '100%',
299
+ height: config.chartHeight || '400px',
300
+ opacity: loading.active ? 0 : 1,
301
+ }}
302
+ />
303
+ )}
304
+ </div>
305
+ );
306
+ });
307
+
308
+ Line3D.displayName = 'Line3D';
309
+
310
+ export default Line3D;