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,33 +1,33 @@
1
- /*
2
- * @Author : Lihao leolihao@arizona.edu
3
- * @Date : 2023-11-29 22:52:11
4
- * @FilePath : /visualifyjs/src/core/themes/themeSelector.js
5
- * @Description :
6
- * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
- */
8
- import React from 'react';
9
- import Modern from './modern';
10
-
11
- function ThemeSelector({ theme, config, children }) {
12
- switch (theme) {
13
- case 'modern':
14
- return (
15
- <Modern
16
- config={config}
17
- children={children}
18
- />
19
- );
20
- // ... more cases as needed
21
- default:
22
- console.warn('Unsupported theme:', theme, 'using default theme');
23
- // Fallback to default theme
24
- return (
25
- <Modern
26
- config={config}
27
- children={children}
28
- />
29
- );
30
- }
31
- }
32
-
33
- export default ThemeSelector;
1
+ /*
2
+ * @Author : Lihao leolihao@arizona.edu
3
+ * @Date : 2023-11-29 22:52:11
4
+ * @FilePath : /visualifyjs/src/core/themes/themeSelector.js
5
+ * @Description :
6
+ * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
+ */
8
+ import React from 'react';
9
+ import Modern from './modern';
10
+
11
+ function ThemeSelector({ theme, config, children }) {
12
+ switch (theme) {
13
+ case 'modern':
14
+ return (
15
+ <Modern
16
+ config={config}
17
+ children={children}
18
+ />
19
+ );
20
+ // ... more cases as needed
21
+ default:
22
+ console.warn('Unsupported theme:', theme, 'using default theme');
23
+ // Fallback to default theme
24
+ return (
25
+ <Modern
26
+ config={config}
27
+ children={children}
28
+ />
29
+ );
30
+ }
31
+ }
32
+
33
+ export default ThemeSelector;
@@ -1,47 +1,213 @@
1
- /*
2
- * @Author : Lihao leolihao@arizona.edu
3
- * @Date : 2023-11-29 15:35:28
4
- * @FilePath : /visualifyjs/src/core/visualify.js
5
- * @Description :
6
- * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
- */
8
- import React from 'react';
9
- import ReactDOM from 'react-dom/client';
10
- import ThemeSelector from './themes/themeSelector';
11
- import JsonRouter from './router/jsonRouter';
12
- import { VisualifyProvider } from './appContext';
13
-
14
- function Visualify({ config }) {
15
- // global variable
16
- const { theme = 'modern' } = config;
17
-
18
- return (
19
- <VisualifyProvider>
20
- <JsonRouter config={config}>
21
- <ThemeSelector
22
- theme={theme}
23
- config={config}
24
- />
25
- </JsonRouter>
26
- </VisualifyProvider>
27
- );
28
- }
29
-
30
- function CreateApp(config) {
31
- if (!config) throw new Error('Missing configuration.');
32
- const el = document.querySelector(config.el || '#root');
33
- if (!el) throw new Error('el not found. Please check your `el` option.');
34
- const app = ReactDOM.createRoot(el);
35
-
36
- // deletion used configuration
37
- delete config.el;
38
- delete config.mode;
39
-
40
- app.render(
41
- <React.StrictMode>
42
- <Visualify config={{ ...config }} />
43
- </React.StrictMode>,
44
- );
45
- }
46
-
47
- export default CreateApp;
1
+ /*
2
+ * @Author : Lihao leolihao@arizona.edu
3
+ * @Date : 2023-11-29 15:35:28
4
+ * @FilePath : /visualifyjs/src/core/visualify.js
5
+ * @Description :
6
+ * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
+ */
8
+ import React, { useEffect, useRef, useState } from 'react';
9
+ import ReactDOM from 'react-dom/client';
10
+ import ThemeSelector from './themes/themeSelector';
11
+ import JsonRouter from './router/jsonRouter';
12
+ import { VisualifyProvider, useAppContext } from './appContext';
13
+ import { initHMR, useHMR } from './hmr-client';
14
+ import { initializeI18n } from '../i18n';
15
+
16
+ /**
17
+ * Global app state for HMR preservation
18
+ * @type {Object|null}
19
+ */
20
+ let preservedAppState = null;
21
+
22
+ /**
23
+ * Visualify root component with HMR support
24
+ * @param {Object} props - Component props
25
+ * @param {Object} props.config - Visualify configuration
26
+ * @param {Object} [props.initialState] - Preserved state from HMR
27
+ */
28
+ function Visualify({ config, initialState }) {
29
+ // global variable
30
+ const { theme = 'modern' } = config;
31
+ const { setSharedData } = useAppContext();
32
+ const isFirstRender = useRef(true);
33
+
34
+ // Restore preserved state on mount
35
+ useEffect(() => {
36
+ if (initialState?.sharedData && isFirstRender.current) {
37
+ setSharedData(initialState.sharedData);
38
+ }
39
+ isFirstRender.current = false;
40
+ }, [initialState, setSharedData]);
41
+
42
+ // Setup HMR integration
43
+ useHMR({
44
+ configType: 'main',
45
+ onUpdate: (update) => {
46
+ console.log('[Visualify] Received config update:', update.file);
47
+ // Reload the page to apply new config
48
+ // In a more advanced implementation, we could hot-swap the config
49
+ if (update.config) {
50
+ window.location.reload();
51
+ }
52
+ },
53
+ onError: (error) => {
54
+ console.error('[Visualify] HMR error:', error.message);
55
+ },
56
+ preserveState: () => ({
57
+ sharedData: preservedAppState?.sharedData,
58
+ }),
59
+ });
60
+
61
+ return (
62
+ <JsonRouter config={config}>
63
+ <ThemeSelector
64
+ theme={theme}
65
+ config={config}
66
+ />
67
+ </JsonRouter>
68
+ );
69
+ }
70
+
71
+ /**
72
+ * State preservation wrapper component
73
+ * @param {Object} props - Component props
74
+ * @param {Object} props.config - Visualify configuration
75
+ */
76
+ function VisualifyWithStatePreservation({ config }) {
77
+ const [currentConfig, setCurrentConfig] = useState(config);
78
+ const [initialState, setInitialState] = useState(null);
79
+ const { sharedData } = useAppContext();
80
+
81
+ // Preserve state before unloading
82
+ useEffect(() => {
83
+ const preserveState = () => {
84
+ preservedAppState = {
85
+ sharedData,
86
+ config: currentConfig,
87
+ timestamp: Date.now(),
88
+ };
89
+ };
90
+
91
+ window.addEventListener('beforeunload', preserveState);
92
+ return () => window.removeEventListener('beforeunload', preserveState);
93
+ }, [sharedData, currentConfig]);
94
+
95
+ // Handle HMR updates
96
+ useEffect(() => {
97
+ if (typeof window !== 'undefined' && window.__VISUALIFY_HMR__?.enabled) {
98
+ const hmrClient = initHMR();
99
+
100
+ if (hmrClient) {
101
+ // Register update handler for main config
102
+ const unsubscribe = hmrClient.onUpdate('main', (update) => {
103
+ console.log('[Visualify] Config update received:', update);
104
+
105
+ if (update.config) {
106
+ // Preserve current state
107
+ preservedAppState = { sharedData };
108
+
109
+ // Update config
110
+ setCurrentConfig(update.config);
111
+ setInitialState(preservedAppState);
112
+ }
113
+ });
114
+
115
+ return () => unsubscribe();
116
+ }
117
+ }
118
+ }, [sharedData]);
119
+
120
+ return (
121
+ <Visualify
122
+ config={currentConfig}
123
+ initialState={initialState}
124
+ />
125
+ );
126
+ }
127
+
128
+ /**
129
+ * Create and render the Visualify application
130
+ * @param {Object} config - Application configuration
131
+ */
132
+ function CreateApp(config) {
133
+ if (!config) throw new Error('Missing configuration.');
134
+ const el = document.querySelector(config.el || '#root');
135
+ if (!el) throw new Error('el not found. Please check your `el` option.');
136
+
137
+ // Initialize i18n before rendering any components that use useTranslation
138
+ initializeI18n(config.i18n || {});
139
+
140
+ // Store original config for HMR reloads
141
+ const originalConfig = { ...config };
142
+
143
+ // deletion used configuration
144
+ delete config.el;
145
+ delete config.mode;
146
+
147
+ const app = ReactDOM.createRoot(el);
148
+
149
+ // Initialize HMR client if enabled
150
+ if (typeof window !== 'undefined' && window.__VISUALIFY_HMR__?.enabled) {
151
+ initHMR();
152
+ }
153
+
154
+ // Render the app — VisualifyProvider must wrap everything that uses useAppContext
155
+ app.render(
156
+ <React.StrictMode>
157
+ <VisualifyProvider>
158
+ <VisualifyWithStatePreservation config={originalConfig} />
159
+ </VisualifyProvider>
160
+ </React.StrictMode>,
161
+ );
162
+
163
+ // Expose reload function for HMR
164
+ if (typeof window !== 'undefined') {
165
+ window.__VISUALIFY_RELOAD__ = (newConfig) => {
166
+ app.render(
167
+ <React.StrictMode>
168
+ <VisualifyProvider>
169
+ <VisualifyWithStatePreservation
170
+ config={newConfig || originalConfig}
171
+ />
172
+ </VisualifyProvider>
173
+ </React.StrictMode>,
174
+ );
175
+ };
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Reload the application with new configuration (for HMR)
181
+ * @param {Object} newConfig - New configuration object
182
+ * @param {Object} [preservedState] - State to preserve during reload
183
+ */
184
+ export function reloadApp(newConfig, preservedState) {
185
+ if (typeof window !== 'undefined' && window.__VISUALIFY_RELOAD__) {
186
+ // Store preserved state globally
187
+ if (preservedState) {
188
+ preservedAppState = preservedState;
189
+ }
190
+
191
+ // Trigger reload
192
+ window.__VISUALIFY_RELOAD__(newConfig);
193
+ } else {
194
+ console.warn('[Visualify] Reload function not available');
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Get the current preserved application state
200
+ * @returns {Object|null} The preserved state
201
+ */
202
+ export function getPreservedState() {
203
+ return preservedAppState;
204
+ }
205
+
206
+ /**
207
+ * Clear the preserved application state
208
+ */
209
+ export function clearPreservedState() {
210
+ preservedAppState = null;
211
+ }
212
+
213
+ export default CreateApp;
@@ -1,24 +1,24 @@
1
- /*
2
- * @Author : Lihao leolihao@arizona.edu
3
- * @Date : 2023-12-23 13:24:11
4
- * @FilePath : /visualifyjs/src/core/widgets/CircularProgress.js
5
- * @Description :
6
- * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
- */
8
- import React from 'react';
9
- import PropTypes from 'prop-types';
10
- import '../../_css/circular-progress.css';
11
-
12
- function CircularProgress({ color }) {
13
- return (
14
- <div className='circular-progress'>
15
- <div className={`spinner ${color}`} />
16
- </div>
17
- );
18
- }
19
-
20
- CircularProgress.propTypes = {
21
- color: PropTypes.oneOf(['primary', 'secondary']),
22
- };
23
-
1
+ /*
2
+ * @Author : Lihao leolihao@arizona.edu
3
+ * @Date : 2023-12-23 13:24:11
4
+ * @FilePath : /visualifyjs/src/core/widgets/CircularProgress.js
5
+ * @Description :
6
+ * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
+ */
8
+ import React from 'react';
9
+ import PropTypes from 'prop-types';
10
+ import '../../_css/circular-progress.css';
11
+
12
+ function CircularProgress({ color }) {
13
+ return (
14
+ <div className='circular-progress'>
15
+ <div className={`spinner ${color}`} />
16
+ </div>
17
+ );
18
+ }
19
+
20
+ CircularProgress.propTypes = {
21
+ color: PropTypes.oneOf(['primary', 'secondary']),
22
+ };
23
+
24
24
  export default CircularProgress;
@@ -1,83 +1,116 @@
1
- /*
2
- * @Author : Lihao leolihao@arizona.edu
3
- * @Date : 2023-12-21 13:07:59
4
- * @FilePath : /visualifyjs/src/core/widgets/controller.js
5
- * @Description :
6
- * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
- */
8
- import React from 'react';
9
- import widgetMapping from './mapping';
10
-
11
- function Vcontroller({ components = [], layout }) {
12
- // ----------------------------------------------------------------------
13
- const UUIDs = new Set();
14
- function generateUniqueId() {
15
- let uniqueId;
16
- do {
17
- uniqueId = 'xxxx-xxxx-4xxx-yxxx-xxxx-xxxx'.replace(
18
- /[xy]/g,
19
- function (c) {
20
- const r = (Math.random() * 16) | 0;
21
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
22
- return v.toString(16);
23
- },
24
- );
25
- } while (UUIDs.has(uniqueId));
26
- UUIDs.add(uniqueId);
27
- return uniqueId;
28
- }
29
-
30
- const _components = {};
31
- // ----------------------------------------------------------------------
32
- // Iterate over the config array and render the components based on their type and position
33
- const Allcomponents = components.map((componentConfig, index) => {
34
- const {
35
- row = 1,
36
- col = 1,
37
- colspan = 1,
38
- rowspan = 1,
39
- debug = false,
40
- } = componentConfig;
41
-
42
- const componentStyle =
43
- layout === 'grid'
44
- ? {
45
- gridColumn: `${col} / span ${colspan}`,
46
- gridRow: `${row} / span ${rowspan}`,
47
- border: debug ? '1px solid red' : 'none',
48
- }
49
- : {};
50
-
51
- const Component = widgetMapping[componentConfig.type];
52
-
53
- // Generate a unique ID for the component using both ID and index
54
- const uniqueId = generateUniqueId();
55
-
56
- //console.log(uniqueId);
57
-
58
- if (!Component) {
59
- return (
60
- <span
61
- key={uniqueId}
62
- style={componentStyle}>
63
- Error: Component{' '}
64
- <b style={{ color: 'red' }}>{componentConfig.type}</b> not
65
- found.
66
- </span>
67
- );
68
- }
69
-
70
- const _Component = React.cloneElement(<Component key={uniqueId} />, {
71
- style: componentStyle,
72
- props: componentConfig,
73
- });
74
-
75
- _components[uniqueId] = _Component;
76
- // Clone the Component element with the necessary styles and props
77
- return _Component;
78
- });
79
-
80
- return <>{Allcomponents}</>;
81
- }
82
-
83
- export default Vcontroller;
1
+ /*
2
+ * @Author : Lihao leolihao@arizona.edu
3
+ * @Date : 2023-12-21 13:07:59
4
+ * @FilePath : /visualifyjs/src/core/widgets/controller.js
5
+ * @Description :
6
+ * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
+ */
8
+ import React, { useRef, useEffect } from 'react';
9
+ import widgetMapping from './mapping';
10
+ import { applyA11yStyles } from '../../a11y';
11
+
12
+ function Vcontroller({ components = [], layout, ariaLabel = 'Dashboard' }) {
13
+ const containerRef = useRef(null);
14
+
15
+ // Apply accessibility styles on mount
16
+ useEffect(() => {
17
+ applyA11yStyles();
18
+ }, []);
19
+
20
+ // ----------------------------------------------------------------------
21
+ // Iterate over the config array and render the components based on their type and position
22
+ // Use stable keys derived from component id/index to prevent unmount/remount on re-render
23
+ const Allcomponents = components.map((componentConfig, index) => {
24
+ const {
25
+ row = 1,
26
+ col = 1,
27
+ colspan = 1,
28
+ rowspan = 1,
29
+ debug = false,
30
+ } = componentConfig;
31
+
32
+ // Stable key: prefer component id, fall back to type+index
33
+ const stableKey = componentConfig.id || `${componentConfig.type}-${index}`;
34
+
35
+ const componentStyle =
36
+ layout === 'grid'
37
+ ? {
38
+ gridColumn: `${col} / span ${colspan}`,
39
+ gridRow: `${row} / span ${rowspan}`,
40
+ border: debug ? '1px solid red' : 'none',
41
+ }
42
+ : {};
43
+
44
+ const Component = widgetMapping[componentConfig.type];
45
+
46
+ const componentAriaLabel = componentConfig.ariaLabel ||
47
+ `${componentConfig.type} widget ${index + 1} of ${components.length}`;
48
+
49
+ if (!Component) {
50
+ return (
51
+ <span
52
+ key={stableKey}
53
+ style={componentStyle}
54
+ role="alert"
55
+ aria-live="assertive">
56
+ Error: Component{' '}
57
+ <b style={{ color: 'red' }}>{componentConfig.type}</b> not
58
+ found.
59
+ </span>
60
+ );
61
+ }
62
+
63
+ return React.cloneElement(<Component key={stableKey} />, {
64
+ style: componentStyle,
65
+ props: {
66
+ ...componentConfig,
67
+ id: componentConfig.id || stableKey,
68
+ },
69
+ 'aria-label': componentAriaLabel,
70
+ role: 'region',
71
+ });
72
+ });
73
+
74
+ return (
75
+ <div
76
+ ref={containerRef}
77
+ className="visualify-controller"
78
+ role="main"
79
+ aria-label={ariaLabel}
80
+ tabIndex={-1}
81
+ style={{ display: 'contents' }}
82
+ >
83
+ {/* Skip link for keyboard navigation */}
84
+ <a
85
+ href="#visualify-main-content"
86
+ className="sr-only sr-only-focusable"
87
+ style={{
88
+ position: 'absolute',
89
+ top: '-40px',
90
+ left: '0',
91
+ background: '#000',
92
+ color: '#fff',
93
+ padding: '8px 16px',
94
+ zIndex: 9999,
95
+ textDecoration: 'none',
96
+ transition: 'top 0.2s',
97
+ }}
98
+ onFocus={(e) => {
99
+ e.target.style.top = '0';
100
+ }}
101
+ onBlur={(e) => {
102
+ e.target.style.top = '-40px';
103
+ }}
104
+ >
105
+ Skip to main content
106
+ </a>
107
+
108
+ {/* Main content — display:contents makes grid items direct participants */}
109
+ <div id="visualify-main-content" className="visualify-components-container" style={{ display: 'contents' }}>
110
+ {Allcomponents}
111
+ </div>
112
+ </div>
113
+ );
114
+ }
115
+
116
+ export default Vcontroller;
@@ -1,36 +1,36 @@
1
- /*
2
- * @Author : Lihao leolihao@arizona.edu
3
- * @Date : 2023-12-02 16:34:41
4
- * @FilePath : /visualifyjs/src/core/widgets/errorBoundary.js
5
- * @Description :
6
- * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
- */
8
- import React from 'react';
9
-
10
- class ErrorBoundary extends React.Component {
11
- constructor(props) {
12
- super(props);
13
- this.state = { hasError: false, error: null };
14
- }
15
-
16
- static getDerivedStateFromError(error) {
17
- // Update state so the next render will show the fallback UI.
18
- return { hasError: true, error };
19
- }
20
-
21
- componentDidCatch(error, errorInfo) {
22
- // You can also log the error to an error reporting service
23
- console.error('Caught an error:', error, errorInfo);
24
- }
25
-
26
- render() {
27
- if (this.state.hasError) {
28
- // You can render any custom fallback UI
29
- return <h1>Something went wrong: {this.state.error.message}</h1>;
30
- }
31
-
32
- return this.props.children;
33
- }
34
- }
35
-
36
- export default ErrorBoundary;
1
+ /*
2
+ * @Author : Lihao leolihao@arizona.edu
3
+ * @Date : 2023-12-02 16:34:41
4
+ * @FilePath : /visualifyjs/src/core/widgets/errorBoundary.js
5
+ * @Description :
6
+ * Copyright (c) 2023 by Lihao (leolihao@arizona.edu), All Rights Reserved.
7
+ */
8
+ import React from 'react';
9
+
10
+ class ErrorBoundary extends React.Component {
11
+ constructor(props) {
12
+ super(props);
13
+ this.state = { hasError: false, error: null };
14
+ }
15
+
16
+ static getDerivedStateFromError(error) {
17
+ // Update state so the next render will show the fallback UI.
18
+ return { hasError: true, error };
19
+ }
20
+
21
+ componentDidCatch(error, errorInfo) {
22
+ // You can also log the error to an error reporting service
23
+ console.error('Caught an error:', error, errorInfo);
24
+ }
25
+
26
+ render() {
27
+ if (this.state.hasError) {
28
+ // You can render any custom fallback UI
29
+ return <h1>Something went wrong: {this.state.error.message}</h1>;
30
+ }
31
+
32
+ return this.props.children;
33
+ }
34
+ }
35
+
36
+ export default ErrorBoundary;