visualifyjs 2.5.3 → 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 -193
  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 -0
  63. package/docs/docs/_404.md +51 -51
  64. package/docs/docs/_coverpage.md +11 -11
  65. package/docs/docs/_sidebar.md +54 -43
  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 -123
  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 -1
  81. package/docs/docs/more-pages.md +23 -23
  82. package/docs/docs/quickstart.md +148 -119
  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 -579
  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 -143
  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
@@ -1,71 +1,70 @@
1
- /*
2
- * @Author : Lihao leolihao@arizona.edu
3
- * @Date : 2023-12-01 14:18:43
4
- * @FilePath : /visualifyjs/src/core/libs/echarts.js
5
- * @Description :
6
- * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
- */
8
- import React, { useEffect, useRef, forwardRef } from 'react';
9
- import ReactEcharts from 'echarts-for-react';
10
-
11
- const ReCharts = forwardRef(({ options, style, onEvents = {} }, ref) => {
12
- const internalRef = useRef(null);
13
-
14
- // Use the provided ref or the internalRef
15
- const chartRef = ref || internalRef;
16
-
17
- useEffect(() => {
18
- // Ensure the chart instance is available
19
- if (chartRef.current) {
20
- const chart = chartRef.current.getEchartsInstance();
21
-
22
- // Set the 'silent' option to suppress warnings
23
- chart.setOption({
24
- ...options,
25
- silent: true,
26
- });
27
-
28
- // Efficiently update options
29
- if (JSON.stringify(options) !== JSON.stringify(chart.getOption())) {
30
- chart.clear();
31
- chart.setOption(options);
32
- }
33
-
34
- // Handle events
35
- Object.keys(onEvents).forEach((eventName) => {
36
- chart.off(eventName); // Remove old listener
37
- chart.on(eventName, onEvents[eventName]); // Attach new listener
38
- });
39
-
40
- // Resize observer for responsive charts
41
- const resizeObserver = new ResizeObserver(() => {
42
- chart.resize();
43
- });
44
- const parent = chart.getDom().parentElement;
45
- if (parent) {
46
- resizeObserver.observe(parent);
47
- }
48
-
49
- // Cleanup
50
- return () => {
51
- resizeObserver.disconnect();
52
-
53
- if (!chart.isDisposed()) {
54
- chart.off();
55
- chart.dispose();
56
- }
57
- };
58
- }
59
- }, [options, chartRef, onEvents]); // Include onEvents in the dependency array
60
-
61
- return (
62
- <ReactEcharts
63
- ref={chartRef}
64
- option={options}
65
- style={style}
66
- onEvents={onEvents}
67
- />
68
- );
69
- });
70
-
71
- export default ReCharts;
1
+ /*
2
+ * @Author : Lihao leolihao@arizona.edu
3
+ * @Date : 2023-12-01 14:18:43
4
+ * @FilePath : /visualify.js/src/core/modules/echarts.js
5
+ * @Description :
6
+ * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
+ */
8
+ import React, { useEffect, useRef, forwardRef } from 'react';
9
+ import ReactEcharts from 'echarts-for-react';
10
+
11
+ const ReCharts = forwardRef(({ options, style, onEvents = {} }, ref) => {
12
+ const internalRef = useRef(null);
13
+
14
+ // Use the provided ref or the internalRef
15
+ const chartRef = ref || internalRef;
16
+
17
+ useEffect(() => {
18
+ // Ensure the chart instance is available
19
+ if (chartRef.current) {
20
+ const chart = chartRef.current.getEchartsInstance();
21
+
22
+ // Set the 'silent' option to suppress warnings
23
+ chart.setOption({
24
+ ...options,
25
+ silent: true,
26
+ });
27
+
28
+ // Efficiently update options
29
+ if (JSON.stringify(options) !== JSON.stringify(chart.getOption())) {
30
+ chart.clear();
31
+ chart.setOption(options);
32
+ }
33
+
34
+ // Handle events
35
+ Object.keys(onEvents).forEach((eventName) => {
36
+ chart.off(eventName); // Remove old listener
37
+ chart.on(eventName, onEvents[eventName]); // Attach new listener
38
+ });
39
+
40
+ // Resize observer for responsive charts
41
+ const resizeObserver = new ResizeObserver(() => {
42
+ chart.resize();
43
+ });
44
+ const parent = chart.getDom().parentElement;
45
+ if (parent) {
46
+ resizeObserver.observe(parent);
47
+ }
48
+
49
+ // Cleanup
50
+ return () => {
51
+ resizeObserver.disconnect();
52
+
53
+ if (!chart.isDisposed()) {
54
+ chart.off();
55
+ chart.dispose();
56
+ }
57
+ };
58
+ }
59
+ }, [options, chartRef, onEvents]); // Include onEvents in the dependency array
60
+
61
+ return (
62
+ <ReactEcharts
63
+ ref={chartRef}
64
+ option={options}
65
+ style={style}
66
+ />
67
+ );
68
+ });
69
+
70
+ export default ReCharts;
@@ -1,43 +1,43 @@
1
- /*
2
- * @Author : Lihao leolihao@arizona.edu
3
- * @Date : 2023-11-11 23:09:44
4
- * @FilePath : /visualifyjs/src/core/lib/echartsUtils
5
- * @Description :
6
- * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
- */
8
- // echartsUtils.js
9
- import * as echarts from 'echarts';
10
-
11
- export const initChart = (chartRef, chartOption) => {
12
- if (!chartRef.current) {
13
- throw new Error('Chart container DOM element not available.');
14
- }
15
-
16
- let chartInstance = echarts.getInstanceByDom(chartRef.current);
17
- if (!chartInstance) {
18
- chartInstance = echarts.init(chartRef.current);
19
- }
20
- chartInstance.setOption(chartOption);
21
- return chartInstance;
22
- };
23
-
24
- export const disposeChart = (chartInstance) => {
25
- chartInstance && chartInstance.dispose();
26
- };
27
-
28
- export const handleChartForSharedDataChange = (
29
- chartRef,
30
- sharedData,
31
- previousSharedDataRef,
32
- extra_Excute_Func = null,
33
- ) => {
34
- if (sharedData !== previousSharedDataRef.current) {
35
- const chartInstance = echarts.getInstanceByDom(chartRef.current);
36
- disposeChart(chartInstance);
37
- previousSharedDataRef.current = sharedData;
38
- if (extra_Excute_Func) {
39
- //console.log('excute func: ', extra_Excute_Func);
40
- extra_Excute_Func();
41
- }
42
- }
43
- };
1
+ /*
2
+ * @Author : Lihao leolihao@arizona.edu
3
+ * @Date : 2023-11-11 23:09:44
4
+ * @FilePath : /visualifyjs/src/core/lib/echartsUtils
5
+ * @Description :
6
+ * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
+ */
8
+ // echartsUtils.js
9
+ import * as echarts from 'echarts';
10
+
11
+ export const initChart = (chartRef, chartOption) => {
12
+ if (!chartRef.current) {
13
+ throw new Error('Chart container DOM element not available.');
14
+ }
15
+
16
+ let chartInstance = echarts.getInstanceByDom(chartRef.current);
17
+ if (!chartInstance) {
18
+ chartInstance = echarts.init(chartRef.current);
19
+ }
20
+ chartInstance.setOption(chartOption);
21
+ return chartInstance;
22
+ };
23
+
24
+ export const disposeChart = (chartInstance) => {
25
+ chartInstance && chartInstance.dispose();
26
+ };
27
+
28
+ export const handleChartForSharedDataChange = (
29
+ chartRef,
30
+ sharedData,
31
+ previousSharedDataRef,
32
+ extra_Excute_Func = null,
33
+ ) => {
34
+ if (sharedData !== previousSharedDataRef.current) {
35
+ const chartInstance = echarts.getInstanceByDom(chartRef.current);
36
+ disposeChart(chartInstance);
37
+ previousSharedDataRef.current = sharedData;
38
+ if (extra_Excute_Func) {
39
+ //console.log('excute func: ', extra_Excute_Func);
40
+ extra_Excute_Func();
41
+ }
42
+ }
43
+ };
@@ -1,152 +1,227 @@
1
- /*
2
- * @Author : Lihao leolihao@arizona.edu
3
- * @Date : 2023-12-01 14:21:40
4
- * @FilePath : /visualifyjs/src/core/modules/echartswitcher.js
5
- * @Description :
6
- * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
- */
8
- import React, { useState, useEffect, useRef } from 'react';
9
- import ReCharts from './echarts';
10
- import Loading from '../pages/loading';
11
- import _ from 'lodash';
12
- import _parser_gereral from '../parser/echart.general';
13
- import _parser_data from '../parser/echart.data';
14
- import _fetch_data, { _process_fetched_data } from '../parser/echart.parser';
15
-
16
- import {
17
- fetchPresetFromURL,
18
- getEmbeddedPreset,
19
- } from '../modules/echarts/presetHandler';
20
- import minimumPreset from './echarts/common';
21
-
22
- import ErrorBoundary from '../widgets/errorBoundary';
23
- import { useAppContext } from '../appContext';
24
-
25
- import downsampleSeries from '../parser/echart.hilbert';
26
-
27
- const EChartSwitcher = ({ props, style }) => {
28
- const { config = {}, parser, advanced, style: _style } = props;
29
-
30
- const [loading, setLoading] = useState({
31
- active: true,
32
- message: null,
33
- style: advanced?.loadingStlye ?? {},
34
- });
35
- // Store the previous sharedData value using a ref
36
- const previousSharedDataRef = useRef(null);
37
- const chartRef = useRef(null);
38
- const [presetData, setPresetData] = useState(null);
39
- const [Options, setOptions] = useState({});
40
- const [onEvents] = useState({});
41
- const { width, height = '400px' } = config;
42
- const { sharedData } = useAppContext();
43
-
44
- useEffect(() => {
45
- let fetched_data, combinedOptions;
46
- const parsedGeneral = _parser_gereral(config, presetData);
47
- const parsedData = _parser_data(config, parsedGeneral);
48
-
49
- const loadPreset = async () => {
50
- try {
51
- if (typeof config.preset === 'string') {
52
- if (
53
- config.preset.startsWith('http://') ||
54
- config.preset.startsWith('https://')
55
- ) {
56
- const presetData = await fetchPresetFromURL(
57
- config.preset,
58
- );
59
- setPresetData(presetData);
60
- } else {
61
- const embeddedPreset = getEmbeddedPreset(config.preset);
62
- setPresetData(embeddedPreset);
63
- }
64
- } else {
65
- setPresetData(minimumPreset);
66
- }
67
- } catch (error) {
68
- console.error('Error loading preset:', error);
69
- // Handle error or set to a default value
70
- setPresetData(minimumPreset);
71
- }
72
- };
73
-
74
- const updatePlot = async () => {
75
- try {
76
- let parsedAPI = parsedData;
77
-
78
- if (parser) {
79
- // fetch data from api
80
- fetched_data = await _fetch_data(
81
- parser,
82
- parsedData,
83
- sharedData,
84
- );
85
- parsedAPI = _process_fetched_data(
86
- fetched_data,
87
- parser,
88
- parsedData,
89
- );
90
- }
91
-
92
- combinedOptions = _.merge(parsedAPI, config.overrides);
93
-
94
- if (advanced?.hilbert)
95
- combinedOptions.series = downsampleSeries(
96
- combinedOptions.series,
97
- advanced.hilbert,
98
- );
99
-
100
- console.log('Final Options:', combinedOptions);
101
- setOptions(combinedOptions);
102
-
103
- setLoading((prev) => ({
104
- ...prev,
105
- active: false,
106
- message: null,
107
- }));
108
- } catch (error) {
109
- setLoading((prev) => ({
110
- ...prev,
111
- active: true,
112
- message: error.message,
113
- }));
114
- }
115
- };
116
-
117
- loadPreset();
118
-
119
- updatePlot();
120
-
121
- if (!_.isEqual(sharedData, previousSharedDataRef.current))
122
- previousSharedDataRef.current = sharedData;
123
- }, [config, parser, advanced, presetData, sharedData]);
124
-
125
- return (
126
- <div
127
- id={props.id}
128
- style={{ ...style, position: 'relative' }}>
129
- {loading.active && (
130
- <Loading
131
- message={loading.message}
132
- style={loading.style}
133
- />
134
- )}
135
- <ErrorBoundary>
136
- <ReCharts
137
- options={Options}
138
- style={{
139
- opacity: loading.active ? 0 : 1,
140
- width,
141
- height,
142
- ..._style,
143
- }}
144
- onEvents={onEvents}
145
- ref={chartRef}
146
- />
147
- </ErrorBoundary>
148
- </div>
149
- );
150
- };
151
-
152
- export default EChartSwitcher;
1
+ /*
2
+ * @Author : Lihao leolihao@arizona.edu
3
+ * @Date : 2023-12-01 14:21:40
4
+ * @FilePath : /visualify.js/src/core/modules/echartswitcher.js
5
+ * @Description :
6
+ * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
+ */
8
+ import React, { useState, useEffect, useRef } from 'react';
9
+ import ReCharts from './echarts';
10
+ import Loading from '../pages/loading';
11
+ import _ from 'lodash';
12
+ import _parser_gereral from '../parser/echart.general';
13
+ import _parser_data from '../parser/echart.data';
14
+ import _fetch_data, { _process_fetched_data } from '../parser/echart.parser';
15
+
16
+ import {
17
+ fetchPresetFromURL,
18
+ getEmbeddedPreset,
19
+ } from '../modules/echarts/presetHandler';
20
+ import minimumPreset from './echarts/common';
21
+
22
+ import ErrorBoundary from '../widgets/errorBoundary';
23
+ import { useAppContext } from '../appContext';
24
+
25
+ import downsampleSeries from '../parser/echart.hilbert';
26
+ import {
27
+ generateChartAriaAttributes,
28
+ formatDataForScreenReader,
29
+ announceToScreenReader,
30
+ } from '../../a11y/aria-labels';
31
+ import { createEChartsSwitcherKeyboardHandlers } from '../../a11y/keyboard-nav';
32
+ import { validateChartColors, applyAccessibleColors } from '../../a11y/color-contrast';
33
+
34
+ const EChartSwitcher = ({ props, style }) => {
35
+ const { config = {}, parser, advanced, style: _style } = props;
36
+ const chartId = props.id || `echart-switcher-${Math.random().toString(36).substr(2, 9)}`;
37
+
38
+ // Accessibility: Validate and fix colors
39
+ const accessibleConfig = React.useMemo(() => {
40
+ const validation = validateChartColors(config);
41
+ if (!validation.valid && config.a11y?.autoFix !== false) {
42
+ return applyAccessibleColors(config);
43
+ }
44
+ return config;
45
+ }, [config]);
46
+
47
+ const [loading, setLoading] = useState({
48
+ active: true,
49
+ message: null,
50
+ style: advanced?.loadingStlye ?? {},
51
+ });
52
+ // Store the previous sharedData value using a ref
53
+ const previousSharedDataRef = useRef(null);
54
+ const chartRef = useRef(null);
55
+ const containerRef = useRef(null);
56
+ const [presetData, setPresetData] = useState(null);
57
+ const [Options, setOptions] = useState({});
58
+ const [onEvents] = useState({});
59
+ const { width, height = '400px' } = accessibleConfig;
60
+ const { sharedData } = useAppContext();
61
+
62
+ // Use ref for sharedData to access latest value without depending on it
63
+ const sharedDataRef = useRef(sharedData);
64
+ sharedDataRef.current = sharedData;
65
+
66
+ // Build a serialized snapshot of the specific sharedData keys this component reads
67
+ // via the parser config (parser.api attributes)
68
+ const parserApiKeys = React.useMemo(() => {
69
+ if (!parser?.api) return [];
70
+ return Object.values(parser.api)
71
+ .map((attr) => attr.val)
72
+ .filter(Boolean)
73
+ .sort();
74
+ }, [parser]);
75
+ const sharedDataSnapshot = JSON.stringify(
76
+ parserApiKeys.reduce((acc, key) => {
77
+ acc[key] = sharedData[key];
78
+ return acc;
79
+ }, {}),
80
+ );
81
+
82
+ // Generate ARIA attributes
83
+ const ariaAttributes = React.useMemo(() =>
84
+ generateChartAriaAttributes(accessibleConfig, 'chart', chartId),
85
+ [accessibleConfig, chartId]
86
+ );
87
+
88
+ // Keyboard handlers
89
+ const { handleKeyDown } = React.useMemo(() =>
90
+ createEChartsSwitcherKeyboardHandlers(chartRef, accessibleConfig),
91
+ [accessibleConfig]
92
+ );
93
+
94
+ useEffect(() => {
95
+ let fetched_data, combinedOptions;
96
+ const parsedGeneral = _parser_gereral(config, presetData);
97
+ const parsedData = _parser_data(config, parsedGeneral);
98
+
99
+ const loadPreset = async () => {
100
+ try {
101
+ if (typeof accessibleConfig.preset === 'string') {
102
+ if (
103
+ accessibleConfig.preset.startsWith('http://') ||
104
+ accessibleConfig.preset.startsWith('https://')
105
+ ) {
106
+ const presetData = await fetchPresetFromURL(
107
+ accessibleConfig.preset,
108
+ );
109
+ setPresetData(presetData);
110
+ } else {
111
+ const embeddedPreset = getEmbeddedPreset(accessibleConfig.preset);
112
+ setPresetData(embeddedPreset);
113
+ }
114
+ } else {
115
+ setPresetData(minimumPreset);
116
+ }
117
+ } catch (error) {
118
+ console.error('Error loading preset:', error);
119
+ // Handle error or set to a default value
120
+ setPresetData(minimumPreset);
121
+ }
122
+ };
123
+
124
+ const updatePlot = async () => {
125
+ try {
126
+ let parsedAPI = parsedData;
127
+ const currentSharedData = sharedDataRef.current;
128
+
129
+ if (parser) {
130
+ // fetch data from api
131
+ fetched_data = await _fetch_data(
132
+ parser,
133
+ parsedData,
134
+ currentSharedData,
135
+ );
136
+ parsedAPI = _process_fetched_data(
137
+ fetched_data,
138
+ parser,
139
+ parsedData,
140
+ );
141
+ }
142
+
143
+ combinedOptions = _.merge(parsedAPI, accessibleConfig.overrides);
144
+
145
+ if (advanced?.hilbert)
146
+ combinedOptions.series = downsampleSeries(
147
+ combinedOptions.series,
148
+ advanced.hilbert,
149
+ );
150
+
151
+ setOptions(combinedOptions);
152
+
153
+ setLoading((prev) => ({
154
+ ...prev,
155
+ active: false,
156
+ message: null,
157
+ }));
158
+
159
+ // Announce chart load to screen readers
160
+ if (accessibleConfig.a11y?.announceLoad !== false) {
161
+ announceToScreenReader(
162
+ `${ariaAttributes['aria-label']}. Chart loaded with ${combinedOptions.series?.[0]?.data?.length || 0} data points.`,
163
+ 'polite'
164
+ );
165
+ }
166
+ } catch (error) {
167
+ setLoading((prev) => ({
168
+ ...prev,
169
+ active: true,
170
+ message: error.message,
171
+ }));
172
+
173
+ // Announce error to screen readers
174
+ announceToScreenReader(`Error loading chart: ${error.message}`, 'assertive');
175
+ }
176
+ };
177
+
178
+ loadPreset();
179
+
180
+ updatePlot();
181
+
182
+ const currentSharedDataFull = sharedDataRef.current;
183
+ if (!_.isEqual(currentSharedDataFull, previousSharedDataRef.current))
184
+ previousSharedDataRef.current = currentSharedDataFull;
185
+ // eslint-disable-next-line react-hooks/exhaustive-deps
186
+ }, [config, parser, advanced, presetData, sharedDataSnapshot]);
187
+
188
+ return (
189
+ <div
190
+ id={chartId}
191
+ ref={containerRef}
192
+ style={{ ...style, position: 'relative' }}
193
+ className="visualify-chart visualify-echart-switcher"
194
+ {...ariaAttributes}
195
+ onKeyDown={handleKeyDown}
196
+ data-testid="echart-switcher"
197
+ >
198
+ {/* Screen reader description */}
199
+ <div id={`${chartId}-description`} className="sr-only">
200
+ {ariaAttributes['aria-label']}
201
+ </div>
202
+
203
+ {loading.active && (
204
+ <Loading
205
+ message={loading.message}
206
+ style={loading.style}
207
+ />
208
+ )}
209
+ <ErrorBoundary>
210
+ <ReCharts
211
+ options={Options}
212
+ style={{
213
+ opacity: loading.active ? 0 : 1,
214
+ width,
215
+ height,
216
+ ..._style,
217
+ }}
218
+ onEvents={onEvents}
219
+ ref={chartRef}
220
+ aria-hidden="true"
221
+ />
222
+ </ErrorBoundary>
223
+ </div>
224
+ );
225
+ };
226
+
227
+ export default EChartSwitcher;
@@ -1,24 +1,24 @@
1
- /*
2
- * @Author : Lihao leolihao@arizona.edu
3
- * @Date : 2024-01-14 14:54:27
4
- * @FilePath : /visualifyjs/src/core/modules/replotly/presetHandler.js
5
- * @Description :
6
- * Copyright (c) 2024 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
- */
8
-
9
- import minimumPreset from './presets/minimum';
10
- import mmtrbc_violin from './presets/mmtrbc.violin';
11
- import mmtrbc_dot from './presets/mmtrbc.dot';
12
-
13
- // Example embedded presets
14
- const embeddedPresets = {
15
- mmtrbc_violin,
16
- mmtrbc_dot,
17
- // 'presetName': { ...preset data... }
18
- // Define embedded presets here
19
- };
20
-
21
- export const getPreset = (presetKey) => {
22
- // Return minimumPreset if preset not found
23
- return embeddedPresets[presetKey] || minimumPreset;
24
- };
1
+ /*
2
+ * @Author : Lihao leolihao@arizona.edu
3
+ * @Date : 2024-01-14 14:54:27
4
+ * @FilePath : /visualifyjs/src/core/modules/replotly/presetHandler.js
5
+ * @Description :
6
+ * Copyright (c) 2024 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
+ */
8
+
9
+ import minimumPreset from './presets/minimum';
10
+ import mmtrbc_violin from './presets/mmtrbc.violin';
11
+ import mmtrbc_dot from './presets/mmtrbc.dot';
12
+
13
+ // Example embedded presets
14
+ const embeddedPresets = {
15
+ mmtrbc_violin,
16
+ mmtrbc_dot,
17
+ // 'presetName': { ...preset data... }
18
+ // Define embedded presets here
19
+ };
20
+
21
+ export const getPreset = (presetKey) => {
22
+ // Return minimumPreset if preset not found
23
+ return embeddedPresets[presetKey] || minimumPreset;
24
+ };