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
@@ -0,0 +1,248 @@
1
+ /**
2
+ * @fileoverview Editor Context - State management for the visual editor
3
+ * @module editor/context/EditorContext
4
+ */
5
+
6
+ import React, { createContext, useContext, useReducer, useCallback } from 'react';
7
+
8
+ // Action types
9
+ const ACTIONS = {
10
+ SET_CONFIG: 'SET_CONFIG',
11
+ UPDATE_CHART: 'UPDATE_CHART',
12
+ ADD_CHART: 'ADD_CHART',
13
+ REMOVE_CHART: 'REMOVE_CHART',
14
+ REORDER_CHARTS: 'REORDER_CHARTS',
15
+ SET_SELECTED_CHART: 'SET_SELECTED_CHART',
16
+ UPDATE_LAYOUT: 'UPDATE_LAYOUT',
17
+ SET_HISTORY: 'SET_HISTORY',
18
+ UNDO: 'UNDO',
19
+ REDO: 'REDO',
20
+ SET_LOADING: 'SET_LOADING',
21
+ SET_ERROR: 'SET_ERROR',
22
+ };
23
+
24
+ // Initial state
25
+ const initialState = {
26
+ config: {
27
+ version: '3.0.0',
28
+ charts: [],
29
+ layout: {
30
+ type: 'grid',
31
+ rows: 1,
32
+ cols: 1,
33
+ gap: '10px',
34
+ },
35
+ theme: 'modern',
36
+ },
37
+ selectedChartId: null,
38
+ history: {
39
+ past: [],
40
+ present: null,
41
+ future: [],
42
+ },
43
+ loading: false,
44
+ error: null,
45
+ };
46
+
47
+ // Reducer
48
+ function editorReducer(state, action) {
49
+ switch (action.type) {
50
+ case ACTIONS.SET_CONFIG:
51
+ return {
52
+ ...state,
53
+ config: action.payload,
54
+ history: {
55
+ past: [...state.history.past, state.config],
56
+ present: action.payload,
57
+ future: [],
58
+ },
59
+ };
60
+
61
+ case ACTIONS.UPDATE_CHART:
62
+ return {
63
+ ...state,
64
+ config: {
65
+ ...state.config,
66
+ charts: state.config.charts.map((chart) =>
67
+ chart.id === action.payload.id
68
+ ? { ...chart, ...action.payload.updates }
69
+ : chart,
70
+ ),
71
+ },
72
+ };
73
+
74
+ case ACTIONS.ADD_CHART:
75
+ return {
76
+ ...state,
77
+ config: {
78
+ ...state.config,
79
+ charts: [...state.config.charts, action.payload],
80
+ },
81
+ selectedChartId: action.payload.id,
82
+ };
83
+
84
+ case ACTIONS.REMOVE_CHART:
85
+ return {
86
+ ...state,
87
+ config: {
88
+ ...state.config,
89
+ charts: state.config.charts.filter(
90
+ (c) => c.id !== action.payload,
91
+ ),
92
+ },
93
+ selectedChartId:
94
+ state.selectedChartId === action.payload
95
+ ? null
96
+ : state.selectedChartId,
97
+ };
98
+
99
+ case ACTIONS.REORDER_CHARTS:
100
+ return {
101
+ ...state,
102
+ config: {
103
+ ...state.config,
104
+ charts: action.payload,
105
+ },
106
+ };
107
+
108
+ case ACTIONS.SET_SELECTED_CHART:
109
+ return {
110
+ ...state,
111
+ selectedChartId: action.payload,
112
+ };
113
+
114
+ case ACTIONS.UPDATE_LAYOUT:
115
+ return {
116
+ ...state,
117
+ config: {
118
+ ...state.config,
119
+ layout: { ...state.config.layout, ...action.payload },
120
+ },
121
+ };
122
+
123
+ case ACTIONS.UNDO:
124
+ if (state.history.past.length === 0) return state;
125
+ const previous = state.history.past[state.history.past.length - 1];
126
+ const newPast = state.history.past.slice(0, -1);
127
+ return {
128
+ ...state,
129
+ config: previous,
130
+ history: {
131
+ past: newPast,
132
+ present: previous,
133
+ future: [state.config, ...state.history.future],
134
+ },
135
+ };
136
+
137
+ case ACTIONS.REDO:
138
+ if (state.history.future.length === 0) return state;
139
+ const next = state.history.future[0];
140
+ const newFuture = state.history.future.slice(1);
141
+ return {
142
+ ...state,
143
+ config: next,
144
+ history: {
145
+ past: [...state.history.past, state.config],
146
+ present: next,
147
+ future: newFuture,
148
+ },
149
+ };
150
+
151
+ case ACTIONS.SET_LOADING:
152
+ return { ...state, loading: action.payload };
153
+
154
+ case ACTIONS.SET_ERROR:
155
+ return { ...state, error: action.payload };
156
+
157
+ default:
158
+ return state;
159
+ }
160
+ }
161
+
162
+ // Context
163
+ const EditorContext = createContext(null);
164
+
165
+ // Provider component
166
+ export function EditorProvider({ children, initialState: customInitialState }) {
167
+ const [state, dispatch] = useReducer(
168
+ editorReducer,
169
+ customInitialState || initialState,
170
+ );
171
+
172
+ // Actions
173
+ const setConfig = useCallback((config) => {
174
+ dispatch({ type: ACTIONS.SET_CONFIG, payload: config });
175
+ }, []);
176
+
177
+ const updateChart = useCallback((id, updates) => {
178
+ dispatch({ type: ACTIONS.UPDATE_CHART, payload: { id, updates } });
179
+ }, []);
180
+
181
+ const addChart = useCallback((chart) => {
182
+ dispatch({ type: ACTIONS.ADD_CHART, payload: chart });
183
+ }, []);
184
+
185
+ const removeChart = useCallback((id) => {
186
+ dispatch({ type: ACTIONS.REMOVE_CHART, payload: id });
187
+ }, []);
188
+
189
+ const reorderCharts = useCallback((charts) => {
190
+ dispatch({ type: ACTIONS.REORDER_CHARTS, payload: charts });
191
+ }, []);
192
+
193
+ const setSelectedChart = useCallback((id) => {
194
+ dispatch({ type: ACTIONS.SET_SELECTED_CHART, payload: id });
195
+ }, []);
196
+
197
+ const updateLayout = useCallback((layout) => {
198
+ dispatch({ type: ACTIONS.UPDATE_LAYOUT, payload: layout });
199
+ }, []);
200
+
201
+ const undo = useCallback(() => {
202
+ dispatch({ type: ACTIONS.UNDO });
203
+ }, []);
204
+
205
+ const redo = useCallback(() => {
206
+ dispatch({ type: ACTIONS.REDO });
207
+ }, []);
208
+
209
+ const canUndo = state.history.past.length > 0;
210
+ const canRedo = state.history.future.length > 0;
211
+
212
+ const value = {
213
+ state,
214
+ dispatch,
215
+ // Actions
216
+ setConfig,
217
+ updateChart,
218
+ addChart,
219
+ removeChart,
220
+ reorderCharts,
221
+ setSelectedChart,
222
+ updateLayout,
223
+ undo,
224
+ redo,
225
+ // Computed
226
+ canUndo,
227
+ canRedo,
228
+ selectedChart: state.config.charts.find(
229
+ (c) => c.id === state.selectedChartId,
230
+ ),
231
+ };
232
+
233
+ return (
234
+ <EditorContext.Provider value={value}>{children}</EditorContext.Provider>
235
+ );
236
+ }
237
+
238
+ // Hook
239
+ export function useEditor() {
240
+ const context = useContext(EditorContext);
241
+ if (!context) {
242
+ throw new Error('useEditor must be used within an EditorProvider');
243
+ }
244
+ return context;
245
+ }
246
+
247
+ export { ACTIONS };
248
+ export default EditorContext;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @fileoverview Debounce Hook
3
+ * @module editor/hooks/useDebounce
4
+ *
5
+ * Hook for delaying value updates to improve performance.
6
+ */
7
+
8
+ import { useState, useEffect } from 'react';
9
+
10
+ /**
11
+ * Debounce hook
12
+ * @param {any} value - Value to debounce
13
+ * @param {number} delay - Delay in milliseconds
14
+ * @returns {any} Debounced value
15
+ */
16
+ export function useDebounce(value, delay) {
17
+ const [debouncedValue, setDebouncedValue] = useState(value);
18
+
19
+ useEffect(() => {
20
+ const timer = setTimeout(() => {
21
+ setDebouncedValue(value);
22
+ }, delay);
23
+
24
+ return () => {
25
+ clearTimeout(timer);
26
+ };
27
+ }, [value, delay]);
28
+
29
+ return debouncedValue;
30
+ }
31
+
32
+ export default useDebounce;
@@ -0,0 +1,315 @@
1
+ /**
2
+ * @fileoverview Visualify Visual Editor - Main Entry Point
3
+ * @module editor/index
4
+ *
5
+ * Visual configuration editor for creating and editing Visualify configurations
6
+ * without writing JSON. Provides a drag-and-drop interface for building charts.
7
+ */
8
+
9
+ import React, { useState, useCallback, useEffect, useRef } from 'react';
10
+ import { createRoot } from 'react-dom/client';
11
+ import { Container, Row, Col, Toast, ToastContainer } from 'react-bootstrap';
12
+ import Editor from './components/Editor';
13
+ import { EditorProvider } from './context/EditorContext';
14
+ import './styles/editor.css';
15
+
16
+ /**
17
+ * Default empty configuration
18
+ * @readonly
19
+ */
20
+ const DEFAULT_CONFIG = {
21
+ version: '3.0.0',
22
+ charts: [],
23
+ layout: {
24
+ type: 'grid',
25
+ rows: 1,
26
+ cols: 1,
27
+ gap: '10px',
28
+ },
29
+ theme: 'modern',
30
+ };
31
+
32
+ /**
33
+ * Load configuration from file or localStorage
34
+ * @param {string|null} filePath - Path to config file
35
+ * @returns {Object} Loaded configuration
36
+ */
37
+ function loadConfiguration(filePath) {
38
+ // Try to load from localStorage first (auto-save)
39
+ const savedConfig = localStorage.getItem('visualify_editor_autosave');
40
+ if (savedConfig) {
41
+ try {
42
+ return JSON.parse(savedConfig);
43
+ } catch (e) {
44
+ console.warn('Failed to parse auto-saved config:', e);
45
+ }
46
+ }
47
+
48
+ // Return default config
49
+ return { ...DEFAULT_CONFIG };
50
+ }
51
+
52
+ /**
53
+ * Main Editor Application Component
54
+ */
55
+ function EditorApp({ initialConfig = null, filePath = null }) {
56
+ const [config, setConfig] = useState(() =>
57
+ initialConfig || loadConfiguration(filePath),
58
+ );
59
+ const [toasts, setToasts] = useState([]);
60
+ const [isReady, setIsReady] = useState(false);
61
+ const autoSaveTimeoutRef = useRef(null);
62
+
63
+ // Show toast notification
64
+ const showToast = useCallback((message, type = 'info') => {
65
+ const id = Date.now();
66
+ setToasts((prev) => [...prev, { id, message, type }]);
67
+ setTimeout(() => {
68
+ setToasts((prev) => prev.filter((t) => t.id !== id));
69
+ }, 3000);
70
+ }, []);
71
+
72
+ // Auto-save to localStorage with debounce
73
+ useEffect(() => {
74
+ if (autoSaveTimeoutRef.current) {
75
+ clearTimeout(autoSaveTimeoutRef.current);
76
+ }
77
+
78
+ autoSaveTimeoutRef.current = setTimeout(() => {
79
+ try {
80
+ localStorage.setItem(
81
+ 'visualify_editor_autosave',
82
+ JSON.stringify(config),
83
+ );
84
+ } catch (e) {
85
+ console.warn('Auto-save failed:', e);
86
+ }
87
+ }, 1000);
88
+
89
+ return () => {
90
+ if (autoSaveTimeoutRef.current) {
91
+ clearTimeout(autoSaveTimeoutRef.current);
92
+ }
93
+ };
94
+ }, [config]);
95
+
96
+ // Handle configuration updates
97
+ const updateConfig = useCallback((updates) => {
98
+ setConfig((prev) => ({
99
+ ...prev,
100
+ ...updates,
101
+ }));
102
+ }, []);
103
+
104
+ // Handle chart updates
105
+ const updateChart = useCallback((chartId, updates) => {
106
+ setConfig((prev) => ({
107
+ ...prev,
108
+ charts: prev.charts.map((chart) =>
109
+ chart.id === chartId ? { ...chart, ...updates } : chart,
110
+ ),
111
+ }));
112
+ }, []);
113
+
114
+ // Add new chart
115
+ const addChart = useCallback((chartType) => {
116
+ const newChart = {
117
+ id: `chart_${Date.now()}`,
118
+ type: chartType,
119
+ title: `${chartType} Chart`,
120
+ data: [],
121
+ options: {},
122
+ position: { x: 0, y: 0, w: 1, h: 1 },
123
+ };
124
+
125
+ setConfig((prev) => ({
126
+ ...prev,
127
+ charts: [...prev.charts, newChart],
128
+ }));
129
+
130
+ showToast(`Added ${chartType} chart`, 'success');
131
+ return newChart.id;
132
+ }, [showToast]);
133
+
134
+ // Remove chart
135
+ const removeChart = useCallback((chartId) => {
136
+ setConfig((prev) => ({
137
+ ...prev,
138
+ charts: prev.charts.filter((c) => c.id !== chartId),
139
+ }));
140
+ showToast('Chart removed', 'info');
141
+ }, [showToast]);
142
+
143
+ // Export configuration
144
+ const exportConfig = useCallback(() => {
145
+ try {
146
+ // Validate configuration
147
+ if (config.charts.length === 0) {
148
+ showToast('No charts to export', 'warning');
149
+ return null;
150
+ }
151
+
152
+ // Clean up config for export
153
+ const exportData = {
154
+ ...config,
155
+ charts: config.charts.map((chart) => ({
156
+ ...chart,
157
+ // Remove internal properties
158
+ id: undefined,
159
+ })),
160
+ };
161
+
162
+ const json = JSON.stringify(exportData, null, 2);
163
+ const blob = new Blob([json], { type: 'application/json' });
164
+ const url = URL.createObjectURL(blob);
165
+
166
+ const link = document.createElement('a');
167
+ link.href = url;
168
+ link.download = 'visualify.json';
169
+ document.body.appendChild(link);
170
+ link.click();
171
+ document.body.removeChild(link);
172
+ URL.revokeObjectURL(url);
173
+
174
+ showToast('Configuration exported successfully', 'success');
175
+ return json;
176
+ } catch (error) {
177
+ showToast(`Export failed: ${error.message}`, 'danger');
178
+ return null;
179
+ }
180
+ }, [config, showToast]);
181
+
182
+ // Import configuration
183
+ const importConfig = useCallback((jsonString) => {
184
+ try {
185
+ const imported = JSON.parse(jsonString);
186
+
187
+ // Validate required fields
188
+ if (!imported.charts || !Array.isArray(imported.charts)) {
189
+ throw new Error('Invalid configuration: missing charts array');
190
+ }
191
+
192
+ // Add IDs to charts
193
+ const chartsWithIds = imported.charts.map((chart, index) => ({
194
+ ...chart,
195
+ id: chart.id || `chart_${Date.now()}_${index}`,
196
+ }));
197
+
198
+ setConfig({
199
+ ...DEFAULT_CONFIG,
200
+ ...imported,
201
+ charts: chartsWithIds,
202
+ });
203
+
204
+ showToast('Configuration imported successfully', 'success');
205
+ return true;
206
+ } catch (error) {
207
+ showToast(`Import failed: ${error.message}`, 'danger');
208
+ return false;
209
+ }
210
+ }, [showToast]);
211
+
212
+ // Clear all charts
213
+ const clearAll = useCallback(() => {
214
+ if (window.confirm('Are you sure you want to clear all charts?')) {
215
+ setConfig((prev) => ({
216
+ ...prev,
217
+ charts: [],
218
+ }));
219
+ showToast('All charts cleared', 'info');
220
+ }
221
+ }, [showToast]);
222
+
223
+ // Initialize
224
+ useEffect(() => {
225
+ setIsReady(true);
226
+ }, []);
227
+
228
+ if (!isReady) {
229
+ return (
230
+ <Container className='editor-loading'>
231
+ <div className='text-center py-5'>
232
+ <div className='spinner-border text-primary' role='status'>
233
+ <span className='visually-hidden'>Loading...</span>
234
+ </div>
235
+ <p className='mt-3'>Loading Visualify Editor...</p>
236
+ </div>
237
+ </Container>
238
+ );
239
+ }
240
+
241
+ return (
242
+ <EditorProvider initialState={{ config }}>
243
+ <div className='visualify-editor'>
244
+ <Editor />
245
+
246
+ {/* Toast Notifications */}
247
+ <ToastContainer
248
+ position='bottom-end'
249
+ className='p-3'>
250
+ {toasts.map((toast) => (
251
+ <Toast
252
+ key={toast.id}
253
+ bg={toast.type}
254
+ onClose={() =>
255
+ setToasts((prev) =>
256
+ prev.filter((t) => t.id !== toast.id),
257
+ )
258
+ }>
259
+ <Toast.Body className='text-white'>
260
+ {toast.message}
261
+ </Toast.Body>
262
+ </Toast>
263
+ ))}
264
+ </ToastContainer>
265
+ </div>
266
+ </EditorProvider>
267
+ );
268
+ }
269
+
270
+ /**
271
+ * Mount the editor application
272
+ * @param {HTMLElement|string} element - DOM element or selector
273
+ * @param {Object} options - Editor options
274
+ * @param {Object} options.config - Initial configuration
275
+ * @param {string} options.filePath - Path to config file
276
+ */
277
+ function mountEditor(element, options = {}) {
278
+ const el =
279
+ typeof element === 'string' ? document.querySelector(element) : element;
280
+
281
+ if (!el) {
282
+ throw new Error(`Element not found: ${element}`);
283
+ }
284
+
285
+ const root = createRoot(el);
286
+ root.render(
287
+ <EditorApp
288
+ initialConfig={options.config}
289
+ filePath={options.filePath}
290
+ />,
291
+ );
292
+
293
+ return root;
294
+ }
295
+
296
+ // Export for module usage
297
+ export { EditorApp, mountEditor, DEFAULT_CONFIG };
298
+ export default mountEditor;
299
+
300
+ // Auto-mount if DOM is ready and editor container exists
301
+ if (typeof window !== 'undefined') {
302
+ if (document.readyState === 'loading') {
303
+ document.addEventListener('DOMContentLoaded', () => {
304
+ const container = document.getElementById('visualify-editor');
305
+ if (container) {
306
+ mountEditor(container);
307
+ }
308
+ });
309
+ } else {
310
+ const container = document.getElementById('visualify-editor');
311
+ if (container) {
312
+ mountEditor(container);
313
+ }
314
+ }
315
+ }