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,193 +1,310 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { useAppContext } from '../appContext';
3
- import simplefetch from '../fetch/fetch';
4
- import Select from 'react-select';
5
-
6
- function Selection({ props, style }) {
7
- const { debug } = props;
8
- const { style: _style } = props;
9
-
10
- // Render title if it exists
11
- const { title } = props;
12
- const renderTitle = () => {
13
- return title && <h3>{title}</h3>;
14
- };
15
-
16
- // If val is exist, store it to SharedData
17
- const { sharedData, setSharedData } = useAppContext();
18
-
19
- // store attr in state so that we can update it when it changes
20
- const [selected, setSelected] = useState([]);
21
-
22
- // store options in state so that we can update it when it changes
23
- const [selectionOptions, setSelectionOptions] = useState([]);
24
-
25
- const [menuIsOpen, setMenuIsOpen] = useState();
26
-
27
- useEffect(() => {
28
- const { selection, urlval, rm_suffix, nested = false, entry } = props;
29
-
30
- const fetchData = async () => {
31
- if (nested) {
32
- if (entry) {
33
- const entryValue = sharedData[entry];
34
- if (entryValue && entryValue.length > 0) {
35
- if (debug)
36
- console.log('Selection: entryValue:', entryValue);
37
- if (debug)
38
- console.log(
39
- 'Selection: nested url:',
40
- selection + entryValue[0],
41
- urlval,
42
- );
43
- try {
44
- const response = await simplefetch(
45
- selection + entryValue[0],
46
- {
47
- key: urlval,
48
- debug: debug,
49
- },
50
- );
51
- // remove the suffix "_metadata" from the options
52
- try {
53
- const removed_suffix = response.map((item) =>
54
- item.replace(rm_suffix, ''),
55
- );
56
- // insert the "None" into removed_Suffix
57
- removed_suffix.unshift('None');
58
- if (debug)
59
- console.log(
60
- 'Removed suffix:',
61
- removed_suffix,
62
- );
63
- setSelectionOptions(removed_suffix);
64
- } catch (error) {
65
- setSelectionOptions([]);
66
- }
67
- } catch (error) {
68
- console.error('Error fetching options:', error);
69
- }
70
- }
71
- } else
72
- console.error(
73
- 'Error: nested is true but entry is not defined.',
74
- );
75
- } else {
76
- try {
77
- const response = await simplefetch(selection, {
78
- key: urlval,
79
- debug: debug,
80
- });
81
- // remove the suffix "_metadata" from the options
82
- try {
83
- const removed_suffix = response.map((item) =>
84
- item.replace(rm_suffix, ''),
85
- );
86
- //console.log("Removed suffix:", removed_suffix);
87
- setSelectionOptions(removed_suffix);
88
- } catch (error) {
89
- setSelectionOptions(response);
90
- }
91
- } catch (error) {
92
- console.error('Error fetching options:', error);
93
- }
94
- }
95
- };
96
-
97
- fetchData();
98
- }, [props, setSelectionOptions, sharedData, debug]);
99
-
100
- const handleChange = (selectedOptions) => {
101
- if (single) {
102
- // If single selection, selectedOptions will be an object
103
- setSelected(selectedOptions ? [selectedOptions] : []);
104
- } else {
105
- // If multi-selection, selectedOptions will be an array
106
- setSelected(selectedOptions || []);
107
- }
108
- };
109
-
110
- const onInputChange = (inputValue, { action, prevInputValue }) => {
111
- if (action === 'input-change') return inputValue;
112
- if (action === 'menu-close') {
113
- if (prevInputValue) setMenuIsOpen(true);
114
- else setMenuIsOpen(undefined);
115
- }
116
- return prevInputValue;
117
- };
118
-
119
- // ----------------- Selection bar ------------------------------------------------
120
- const { config = {} } = props;
121
- const {
122
- bar_width = '300px',
123
- bar_margin = '0 auto',
124
- bar_maxHeight = '300px',
125
- menu_maxHeight = '300px',
126
- single = false,
127
- } = config;
128
-
129
- const renderSelection = () => {
130
- const options = selectionOptions.map((item) => ({
131
- value: item,
132
- label: item,
133
- }));
134
-
135
- return (
136
- <div className='select-wrapper'>
137
- <Select
138
- isMulti={!single}
139
- options={options}
140
- value={selected}
141
- onChange={handleChange}
142
- onInputChange={onInputChange}
143
- menuIsOpen={menuIsOpen}
144
- styles={{
145
- container: (provided) => ({
146
- ...provided,
147
- width: bar_width,
148
- margin: bar_margin,
149
- maxHeight: bar_maxHeight,
150
- }),
151
- valueContainer: (provided) => ({
152
- ...provided,
153
- maxHeight: bar_maxHeight ?? 'auto',
154
- overflowY: 'auto',
155
- }),
156
- menu: (provided) => ({
157
- ...provided,
158
- maxHeight: menu_maxHeight ?? 'auto', // Set the max height as required
159
- overflowY: 'auto', // Enable scroll if exceeds max height
160
- }),
161
- }}
162
- />
163
- </div>
164
- );
165
- };
166
-
167
- // Store value to SharedData
168
- const { id, val } = props;
169
-
170
- useEffect(() => {
171
- if (val) {
172
- setSharedData((prevSharedData) => {
173
- const selectedValues = selected
174
- ? selected.map((option) => option.value)
175
- : [];
176
- //console.log("Selection: selected:", selectedValues);
177
- return { ...prevSharedData, [val]: selectedValues };
178
- });
179
- }
180
- }, [selected, val, setSharedData]);
181
-
182
- return (
183
- <div
184
- key={id}
185
- style={{ ...style, ..._style }}
186
- className='selection-box-container'>
187
- {renderTitle()}
188
- {renderSelection()}
189
- </div>
190
- );
191
- }
192
-
193
- export default Selection;
1
+ import React, { useEffect, useState } from 'react';
2
+ import { useAppContext } from '../appContext';
3
+ import simplefetch from '../fetch/fetch';
4
+ import Select from 'react-select';
5
+
6
+ const selectTheme = (theme) => ({
7
+ ...theme,
8
+ borderRadius: 6,
9
+ spacing: { ...theme.spacing, controlHeight: 36, baseUnit: 3 },
10
+ colors: {
11
+ ...theme.colors,
12
+ primary: '#4a90d9',
13
+ primary75: '#6ba3e0',
14
+ primary50: '#e8f0fe',
15
+ primary25: '#f5f8fd',
16
+ neutral5: '#fafbfc',
17
+ neutral10: '#f0f2f5',
18
+ neutral20: '#e0e0e0',
19
+ neutral30: '#ccc',
20
+ danger: '#e25c5c',
21
+ dangerLight: '#fce8e8',
22
+ },
23
+ });
24
+
25
+ function Selection({ props, style }) {
26
+ const { debug } = props;
27
+ const { style: _style } = props;
28
+
29
+ const { title } = props;
30
+ const renderTitle = () => {
31
+ return title && <h3 className='v-control-title'>{title}</h3>;
32
+ };
33
+
34
+ // If val is exist, store it to SharedData
35
+ const { sharedData, setSharedData } = useAppContext();
36
+
37
+ // store attr in state so that we can update it when it changes
38
+ const [selected, setSelected] = useState([]);
39
+
40
+ // store options in state so that we can update it when it changes
41
+ const [selectionOptions, setSelectionOptions] = useState([]);
42
+
43
+ const [menuIsOpen, setMenuIsOpen] = useState();
44
+
45
+ // Extract stable primitives from props to avoid object reference dependency
46
+ const { selection, urlval, rm_suffix, nested = false, entry } = props;
47
+
48
+ // For nested selections, track the entry value from sharedData
49
+ const entryValue = entry ? sharedData[entry] : undefined;
50
+ const entryFirst = Array.isArray(entryValue) && entryValue.length > 0 ? entryValue[0] : null;
51
+
52
+ useEffect(() => {
53
+ const fetchData = async () => {
54
+ if (nested) {
55
+ if (entry) {
56
+ if (entryFirst) {
57
+ if (debug)
58
+ console.log('Selection: entryValue:', entryFirst);
59
+ if (debug)
60
+ console.log(
61
+ 'Selection: nested url:',
62
+ selection + entryFirst,
63
+ urlval,
64
+ );
65
+ try {
66
+ const response = await simplefetch(
67
+ selection + entryFirst,
68
+ {
69
+ key: urlval,
70
+ debug: debug,
71
+ },
72
+ );
73
+ try {
74
+ const removed_suffix = response.map((item) =>
75
+ item.replace(rm_suffix, ''),
76
+ );
77
+ removed_suffix.unshift('None');
78
+ if (debug)
79
+ console.log(
80
+ 'Removed suffix:',
81
+ removed_suffix,
82
+ );
83
+ setSelectionOptions(removed_suffix);
84
+ } catch (error) {
85
+ setSelectionOptions([]);
86
+ }
87
+ } catch (error) {
88
+ console.error('Error fetching options:', error);
89
+ }
90
+ }
91
+ } else
92
+ console.error(
93
+ 'Error: nested is true but entry is not defined.',
94
+ );
95
+ } else {
96
+ try {
97
+ const response = await simplefetch(selection, {
98
+ key: urlval,
99
+ debug: debug,
100
+ });
101
+ try {
102
+ const removed_suffix = response.map((item) =>
103
+ item.replace(rm_suffix, ''),
104
+ );
105
+ setSelectionOptions(removed_suffix);
106
+ } catch (error) {
107
+ setSelectionOptions(response);
108
+ }
109
+ } catch (error) {
110
+ console.error('Error fetching options:', error);
111
+ }
112
+ }
113
+ };
114
+
115
+ fetchData();
116
+ // Depend on stable primitives, not entire props/sharedData objects
117
+ }, [selection, urlval, rm_suffix, nested, entry, entryFirst, debug]);
118
+
119
+ const handleChange = (selectedOptions) => {
120
+ if (single) {
121
+ setSelected(selectedOptions ? [selectedOptions] : []);
122
+ } else {
123
+ setSelected(selectedOptions || []);
124
+ }
125
+ };
126
+
127
+ const onInputChange = (inputValue, { action, prevInputValue }) => {
128
+ if (action === 'input-change') return inputValue;
129
+ if (action === 'menu-close') {
130
+ if (prevInputValue) setMenuIsOpen(true);
131
+ else setMenuIsOpen(undefined);
132
+ }
133
+ return prevInputValue;
134
+ };
135
+
136
+ // ----------------- Selection bar ------------------------------------------------
137
+ const { config = {} } = props;
138
+ const {
139
+ bar_width = '300px',
140
+ bar_margin = '0 auto',
141
+ bar_maxHeight = '300px',
142
+ menu_maxHeight = '300px',
143
+ single = false,
144
+ } = config;
145
+
146
+ const renderSelection = () => {
147
+ const options = selectionOptions.map((item) => ({
148
+ value: item,
149
+ label: item,
150
+ }));
151
+
152
+ return (
153
+ <div className='select-wrapper'>
154
+ <Select
155
+ isMulti={!single}
156
+ options={options}
157
+ value={selected}
158
+ onChange={handleChange}
159
+ onInputChange={onInputChange}
160
+ menuIsOpen={menuIsOpen}
161
+ placeholder={single ? 'Select...' : 'Select items...'}
162
+ theme={selectTheme}
163
+ styles={{
164
+ container: (provided) => ({
165
+ ...provided,
166
+ width: bar_width,
167
+ margin: bar_margin,
168
+ }),
169
+ control: (provided, state) => ({
170
+ ...provided,
171
+ minHeight: 36,
172
+ borderColor: state.isFocused ? '#4a90d9' : 'transparent',
173
+ background: state.isFocused ? '#fff' : '#f5f7fa',
174
+ boxShadow: state.isFocused
175
+ ? '0 0 0 3px rgba(74, 144, 217, 0.12)'
176
+ : 'none',
177
+ transition: 'all 0.2s ease',
178
+ '&:hover': {
179
+ borderColor: '#4a90d9',
180
+ background: '#fff',
181
+ },
182
+ }),
183
+ valueContainer: (provided) => ({
184
+ ...provided,
185
+ maxHeight: bar_maxHeight ?? '200px',
186
+ overflowY: 'auto',
187
+ padding: '2px 8px',
188
+ gap: '3px',
189
+ }),
190
+ multiValue: (provided) => ({
191
+ ...provided,
192
+ borderRadius: 12,
193
+ background: '#e8f0fe',
194
+ border: '1px solid #c5d9f2',
195
+ }),
196
+ multiValueLabel: (provided) => ({
197
+ ...provided,
198
+ fontSize: '0.8rem',
199
+ fontWeight: 500,
200
+ color: '#2c5282',
201
+ padding: '2px 6px',
202
+ }),
203
+ multiValueRemove: (provided) => ({
204
+ ...provided,
205
+ borderRadius: '0 12px 12px 0',
206
+ color: '#4a90d9',
207
+ '&:hover': {
208
+ background: '#c5d9f2',
209
+ color: '#2c5282',
210
+ },
211
+ }),
212
+ menu: (provided) => ({
213
+ ...provided,
214
+ maxHeight: menu_maxHeight ?? '250px',
215
+ borderRadius: 8,
216
+ border: '1px solid #e0e0e0',
217
+ boxShadow: '0 4px 16px rgba(0, 0, 0, 0.1)',
218
+ overflow: 'hidden',
219
+ zIndex: 10,
220
+ }),
221
+ menuList: (provided) => ({
222
+ ...provided,
223
+ maxHeight: menu_maxHeight ?? '250px',
224
+ padding: '4px',
225
+ }),
226
+ option: (provided, state) => ({
227
+ ...provided,
228
+ borderRadius: 6,
229
+ fontSize: '0.85rem',
230
+ padding: '8px 12px',
231
+ margin: '1px 0',
232
+ cursor: 'pointer',
233
+ background: state.isSelected
234
+ ? '#4a90d9'
235
+ : state.isFocused
236
+ ? '#f0f5ff'
237
+ : 'transparent',
238
+ color: state.isSelected ? '#fff' : '#333',
239
+ '&:active': {
240
+ background: '#e8f0fe',
241
+ },
242
+ }),
243
+ placeholder: (provided) => ({
244
+ ...provided,
245
+ fontSize: '0.85rem',
246
+ color: '#aaa',
247
+ }),
248
+ indicatorSeparator: () => ({
249
+ display: 'none',
250
+ }),
251
+ dropdownIndicator: (provided, state) => ({
252
+ ...provided,
253
+ color: state.isFocused ? '#4a90d9' : '#bbb',
254
+ padding: '6px',
255
+ transition: 'transform 0.2s ease, color 0.2s ease',
256
+ transform: state.selectProps.menuIsOpen
257
+ ? 'rotate(180deg)'
258
+ : 'rotate(0deg)',
259
+ '&:hover': {
260
+ color: '#4a90d9',
261
+ },
262
+ }),
263
+ clearIndicator: (provided) => ({
264
+ ...provided,
265
+ color: '#bbb',
266
+ padding: '6px',
267
+ '&:hover': {
268
+ color: '#e25c5c',
269
+ },
270
+ }),
271
+ }}
272
+ />
273
+ </div>
274
+ );
275
+ };
276
+
277
+ // Store value to SharedData
278
+ const { id, val } = props;
279
+
280
+ // Serialize selected values for stable comparison
281
+ const selectedValuesKey = JSON.stringify(
282
+ selected ? selected.map((option) => option.value) : [],
283
+ );
284
+ useEffect(() => {
285
+ if (val) {
286
+ setSharedData((prevSharedData) => {
287
+ const selectedValues = selected
288
+ ? selected.map((option) => option.value)
289
+ : [];
290
+ // Skip update if the value is already the same
291
+ if (JSON.stringify(prevSharedData[val]) === selectedValuesKey)
292
+ return prevSharedData;
293
+ return { ...prevSharedData, [val]: selectedValues };
294
+ });
295
+ }
296
+ // eslint-disable-next-line react-hooks/exhaustive-deps
297
+ }, [selectedValuesKey, val, setSharedData]);
298
+
299
+ return (
300
+ <div
301
+ key={id}
302
+ style={{ ...style, ..._style }}
303
+ className='selection-box-container'>
304
+ {renderTitle()}
305
+ {renderSelection()}
306
+ </div>
307
+ );
308
+ }
309
+
310
+ export default Selection;