@ea-lab/reactive-json 0.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 (66) hide show
  1. package/README.md +83 -0
  2. package/dist/reactive-json.css +5 -0
  3. package/dist/reactive-json.js +56303 -0
  4. package/dist/reactive-json.umd.cjs +382 -0
  5. package/lib/component/action/HashChangeListener.jsx +66 -0
  6. package/lib/component/action/Hide.jsx +14 -0
  7. package/lib/component/action/MessageListener.jsx +62 -0
  8. package/lib/component/action/Popover.jsx +53 -0
  9. package/lib/component/action/ReactOnEvent.jsx +118 -0
  10. package/lib/component/action/Redirect.jsx +26 -0
  11. package/lib/component/action/Tooltip.jsx +27 -0
  12. package/lib/component/action/VisuallyHide.jsx +15 -0
  13. package/lib/component/element/chart/BarChart.jsx +40 -0
  14. package/lib/component/element/chart/DoughnutChart.jsx +32 -0
  15. package/lib/component/element/chart/LineChart.jsx +40 -0
  16. package/lib/component/element/chart/PolarAreaChart.jsx +32 -0
  17. package/lib/component/element/form/CheckBoxField.jsx +215 -0
  18. package/lib/component/element/form/DateField.jsx +42 -0
  19. package/lib/component/element/form/NumberField.jsx +29 -0
  20. package/lib/component/element/form/SelectField.jsx +130 -0
  21. package/lib/component/element/form/TextAreaField.jsx +48 -0
  22. package/lib/component/element/form/TextField.jsx +65 -0
  23. package/lib/component/element/form/formElementsCommon.jsx +54 -0
  24. package/lib/component/element/html/AccordionItem.jsx +42 -0
  25. package/lib/component/element/html/FolderSortableTree.jsx +307 -0
  26. package/lib/component/element/html/FormatNumeral.jsx +118 -0
  27. package/lib/component/element/html/Html.jsx +107 -0
  28. package/lib/component/element/html/LabelFromValue.jsx +89 -0
  29. package/lib/component/element/html/Modal.jsx +77 -0
  30. package/lib/component/element/html/ModalForm.jsx +30 -0
  31. package/lib/component/element/html/Paragraph.jsx +10 -0
  32. package/lib/component/element/html/PreformattedMarkup.jsx +54 -0
  33. package/lib/component/element/html/SortableTreeItemCollapseButton.jsx +20 -0
  34. package/lib/component/element/html/Tabs.jsx +55 -0
  35. package/lib/component/element/special/BootstrapElement.jsx +32 -0
  36. package/lib/component/element/special/Count.jsx +46 -0
  37. package/lib/component/element/special/DataFilter.jsx +156 -0
  38. package/lib/component/element/special/DelayedActions.jsx +119 -0
  39. package/lib/component/element/special/PageControls.jsx +19 -0
  40. package/lib/component/element/special/Phantom.jsx +25 -0
  41. package/lib/component/element/special/Switch.jsx +131 -0
  42. package/lib/component/hook/usePagination.jsx +184 -0
  43. package/lib/component/reaction/addData.jsx +23 -0
  44. package/lib/component/reaction/fetchData.jsx +83 -0
  45. package/lib/component/reaction/moveData.jsx +52 -0
  46. package/lib/component/reaction/postMessage.jsx +43 -0
  47. package/lib/component/reaction/redirectNow.jsx +17 -0
  48. package/lib/component/reaction/removeData.jsx +48 -0
  49. package/lib/component/reaction/setClipboardData.jsx +20 -0
  50. package/lib/component/reaction/setData.jsx +23 -0
  51. package/lib/component/reaction/submitData.jsx +136 -0
  52. package/lib/component/reaction/triggerEvent.jsx +62 -0
  53. package/lib/component/utility/formatString.jsx +59 -0
  54. package/lib/engine/Actions.jsx +392 -0
  55. package/lib/engine/EventDispatcherContext.jsx +16 -0
  56. package/lib/engine/EventDispatcherProvider.jsx +80 -0
  57. package/lib/engine/GlobalDataContext.jsx +13 -0
  58. package/lib/engine/GlobalDataContextProvider.jsx +33 -0
  59. package/lib/engine/PaginationContext.jsx +10 -0
  60. package/lib/engine/PaginationProvider.jsx +61 -0
  61. package/lib/engine/ReactiveJsonRoot.jsx +315 -0
  62. package/lib/engine/TemplateContext.jsx +13 -0
  63. package/lib/engine/TemplateSystem.jsx +302 -0
  64. package/lib/engine/View.jsx +240 -0
  65. package/lib/main.jsx +41 -0
  66. package/package.json +72 -0
@@ -0,0 +1,302 @@
1
+ import {useContext} from "react";
2
+ import GlobalDataContext from "./GlobalDataContext";
3
+ import TemplateContext from "./TemplateContext";
4
+ import {normalizeAttributesForReactJsx} from "../component/element/html/Html";
5
+
6
+ /**
7
+ * Transforms a data location string to a path to be used in the UI components.
8
+ *
9
+ * @param dataLocation
10
+ * @param currentPath
11
+ * @param globalDataContext
12
+ * @param templateContext
13
+ * @returns {string|*}
14
+ * @constructor
15
+ */
16
+ export const dataLocationToPath = ({dataLocation, currentPath, globalDataContext, templateContext}) => {
17
+ if (!(typeof dataLocation === "string") || !(dataLocation.startsWith("~.") || dataLocation.startsWith("~~.") || dataLocation.startsWith("~>"))) {
18
+ if ("~" === dataLocation) {
19
+ // The data location is the template root.
20
+ return templateContext.templatePath;
21
+ }
22
+
23
+ if ("~~" === dataLocation) {
24
+ // The data location is the global template root.
25
+ return globalDataContext.templatePath;
26
+ }
27
+
28
+ // This value is not a data location.
29
+ // Render what is given as is.
30
+ return dataLocation;
31
+ }
32
+
33
+ // This tells if we check in the current template context, the global data, or the current path.
34
+ let pathBase;
35
+
36
+ if (dataLocation.startsWith("~~.")) {
37
+ // Build the path starting from the global data context path (in theory, just "data").
38
+ pathBase = globalDataContext.templatePath;
39
+ } else if (dataLocation.startsWith("~.")) {
40
+ // Build the path starting from the current template path.
41
+ pathBase = templateContext.templatePath;
42
+ } else if (dataLocation.startsWith("~>")) {
43
+ // Build the path starting from an ascendant of the current template path.
44
+ const keyToFind = dataLocation.substring(2, dataLocation.indexOf("."));
45
+
46
+ if (!templateContext.templatePath.includes(keyToFind)) {
47
+ throw new Error(keyToFind + " not found in the current template path.");
48
+ }
49
+
50
+ const keyToFindIndex = templateContext.templatePath.indexOf(keyToFind);
51
+
52
+ pathBase = templateContext.templatePath.substring(0, keyToFindIndex + keyToFind.length);
53
+ } else {
54
+ pathBase = currentPath;
55
+ }
56
+
57
+ const splitLocationArray = dataLocation.split(".");
58
+
59
+ // Remove the template value detection character.
60
+ splitLocationArray.shift();
61
+
62
+ return pathBase + "." + splitLocationArray.join(".");
63
+ };
64
+
65
+ /**
66
+ * Evaluates the given attributes with the given contexts.
67
+ *
68
+ * @param {{}} attrs
69
+ * @param {{}} globalDataContext
70
+ * @param {{}} templateContext
71
+ * @param {{normalizeBeforeEvaluation : boolean}} options normalizeBeforeEvaluation is false if unset.
72
+ *
73
+ * @returns {{}}
74
+ */
75
+ export const evaluateAttributes = ({attrs, globalDataContext, templateContext, options = {}}) => {
76
+ const evaluated = {};
77
+
78
+ if (!attrs) {
79
+ return evaluated;
80
+ }
81
+
82
+ const normalized = options.normalizeBeforeEvaluation ? normalizeAttributesForReactJsx(attrs) : attrs;
83
+
84
+ for (const attrName of Object.keys(normalized)) {
85
+ // This will replace the value by the template value if it's a valid reference.
86
+ // We call directly the TemplateValue component as a function to evaluate the attribute value.
87
+ const evaluatedAttr = evaluateTemplateValue({
88
+ globalDataContext: globalDataContext,
89
+ templateContext: templateContext,
90
+ valueToEvaluate: normalized[attrName]
91
+ });
92
+
93
+ // We only keep the attribute if it can be represented as an attribute value
94
+ // or be interpreted by React such as callbacks.
95
+ if (evaluatedAttr) {
96
+ evaluated[attrName] = evaluatedAttr;
97
+ }
98
+
99
+ // TODO: the next code block has been commented out, because we found that
100
+ // it may be more useful to allow objects as attributes (e.g. the style attribute
101
+ // which is an object). Remove this block after testing.
102
+ // switch (typeof evaluatedAttr) {
103
+ // case "string":
104
+ // case "bigint":
105
+ // case "number":
106
+ // case "boolean":
107
+ // case "function":
108
+ // evaluated[attrName] = evaluatedAttr;
109
+ // break;
110
+ //
111
+ // default:
112
+ // // We only keep the attribute if it can be represented as an attribute value
113
+ // // or be interpreted by React such as callbacks.
114
+ // delete evaluated[attrName];
115
+ // break;
116
+ // }
117
+ }
118
+
119
+ return evaluated;
120
+ }
121
+
122
+ /**
123
+ * Evaluates the template value using the given template and global context.
124
+ *
125
+ * @param valueToEvaluate The value to evaluate.
126
+ * @param globalDataContext The global data context.
127
+ * @param templateContext The template context.
128
+ * @returns {undefined|*}
129
+ */
130
+ export const evaluateTemplateValue = ({valueToEvaluate, globalDataContext, templateContext}) => {
131
+ if (!isTemplateValue(valueToEvaluate)) {
132
+ // This value does not use the template context data.
133
+ // Render what is given as is.
134
+ return valueToEvaluate;
135
+ }
136
+
137
+ /*
138
+ * Experimental zone.
139
+ */
140
+
141
+ if ("~" === valueToEvaluate) {
142
+ // We want the whole template data.
143
+ return templateContext.templateData;
144
+ } else if ("~~" === valueToEvaluate) {
145
+ // We want the whole global template data.
146
+ return globalDataContext.templateData;
147
+ }
148
+
149
+ /*
150
+ * End of experimental zone.
151
+ */
152
+
153
+ let currentNode;
154
+
155
+ if (valueToEvaluate.startsWith("~~.")) {
156
+ // Start from the global data context node.
157
+ currentNode = globalDataContext?.templateData;
158
+ } else if (valueToEvaluate.startsWith("~>")) {
159
+ // Start from the global data context node, but evaluate the "valueToEvaluate"
160
+ // to use one of the ascending nodes of the current template.
161
+ valueToEvaluate = dataLocationToPath({
162
+ dataLocation: valueToEvaluate,
163
+ currentPath: templateContext.templatePath,
164
+ globalDataContext,
165
+ templateContext,
166
+ });
167
+
168
+ currentNode = globalDataContext?.templateData;
169
+ } else {
170
+ // Start from the current template context node.
171
+ currentNode = templateContext?.templateData;
172
+ }
173
+
174
+ if (!currentNode) {
175
+ // No context supplied. This is likely a bug; contexts must be supplied when calling this function.
176
+ return undefined;
177
+ }
178
+
179
+ // Find the value in the template context.
180
+ const splitValueArray = valueToEvaluate.split(".");
181
+
182
+ // Remove the template value detection character.
183
+ splitValueArray.shift();
184
+
185
+ while (splitValueArray.length) {
186
+ if (typeof currentNode !== "object") {
187
+ // Not an object, so there is no need to continue.
188
+ // Return an undefined value.
189
+ return undefined;
190
+ }
191
+
192
+ currentNode = currentNode[splitValueArray.shift()];
193
+
194
+ if (currentNode === undefined) {
195
+ // No need to continue.
196
+ // Return an undefined value.
197
+ return undefined;
198
+ }
199
+ }
200
+
201
+ return currentNode;
202
+ }
203
+
204
+ /**
205
+ * Evaluates an array or object containing values to evaluate.
206
+ *
207
+ * You can also pass a single value to evaluate.
208
+ *
209
+ * @param {Array|object|*} valueToEvaluate The value to evaluate. Usually a string, an array, or an object.
210
+ * @param {{}} globalDataContext The global data context values.
211
+ * @param {{}} templateContext The current template context values.
212
+ *
213
+ * @returns {*} The evaluated value. It tries to keep the same structure (array, object, single) as the given value.
214
+ */
215
+ export const evaluateTemplateValueCollection = ({valueToEvaluate, globalDataContext, templateContext}) => {
216
+ let evaluated;
217
+
218
+ if (typeof valueToEvaluate === "object") {
219
+ // Evaluate any first level values.
220
+ // Deep (recursive) evaluation is technically possible,
221
+ // but we are not doing this yet for performance and usefulness reasons.
222
+ evaluated = Array.isArray(valueToEvaluate) ? [] : {};
223
+
224
+ for (const [key, itemContent] of Object.entries(valueToEvaluate)) {
225
+ evaluated[key] = evaluateTemplateValue({
226
+ globalDataContext,
227
+ templateContext,
228
+ valueToEvaluate: itemContent
229
+ })
230
+ }
231
+ } else {
232
+ // Single value.
233
+ evaluated = evaluateTemplateValue({
234
+ globalDataContext,
235
+ templateContext,
236
+ valueToEvaluate
237
+ });
238
+ }
239
+
240
+ return evaluated;
241
+ }
242
+
243
+ /**
244
+ * Checks if the given value is a value which can be replaced by the template system.
245
+ * @param valueToEvaluate
246
+ * @returns {string|boolean}
247
+ */
248
+ export const isTemplateValue = (valueToEvaluate) => {
249
+ if (!(typeof valueToEvaluate === "string") || !(valueToEvaluate.startsWith("~.") || valueToEvaluate.startsWith("~~.") || valueToEvaluate.startsWith("~>") || "~" === valueToEvaluate || "~~" === valueToEvaluate)) {
250
+ // This value does not use the template context data.
251
+ return false;
252
+ }
253
+
254
+ // Render what is given as is for chaining.
255
+ return valueToEvaluate;
256
+ };
257
+
258
+ /**
259
+ * A template value is a value that is retrieved from the current template data.
260
+ *
261
+ * @param valueToEvaluate
262
+ *
263
+ * @returns {{}}
264
+ *
265
+ * @constructor
266
+ */
267
+ const TemplateValue = ({valueToEvaluate}) => {
268
+ const globalDataContext = useContext(GlobalDataContext);
269
+ const templateContext = useContext(TemplateContext);
270
+
271
+ return evaluateTemplateValue({
272
+ globalDataContext: globalDataContext,
273
+ templateContext: templateContext,
274
+ valueToEvaluate: valueToEvaluate,
275
+ });
276
+ };
277
+
278
+ export default TemplateValue;
279
+
280
+ /**
281
+ * Evaluates the given attributes with the given contexts.
282
+ *
283
+ * @param {{}} attrs
284
+ * @param {{normalizeBeforeEvaluation : boolean}} options normalizeBeforeEvaluation = true if unset.
285
+ *
286
+ * @returns {{}} Evaluated attributes.
287
+ */
288
+ export const useEvaluatedAttributes = (attrs, options = {}) => {
289
+ const globalDataContext = useContext(GlobalDataContext);
290
+ const templateContext = useContext(TemplateContext);
291
+
292
+ return evaluateAttributes(
293
+ {
294
+ attrs,
295
+ globalDataContext,
296
+ templateContext,
297
+ options: options.normalizeBeforeEvaluation === undefined
298
+ ? {...options, normalizeBeforeEvaluation: true}
299
+ : options,
300
+ }
301
+ );
302
+ };
@@ -0,0 +1,240 @@
1
+ import {useContext} from 'react';
2
+ import BarChart from "../component/element/chart/BarChart";
3
+ import DoughnutChart from "../component/element/chart/DoughnutChart";
4
+ import PolarAreaChart from "../component/element/chart/PolarAreaChart";
5
+ import CheckBoxField from "../component/element/form/CheckBoxField";
6
+ import DateField from "../component/element/form/DateField";
7
+ import NumberField from "../component/element/form/NumberField";
8
+ import SelectField from "../component/element/form/SelectField";
9
+ import TextAreaField from "../component/element/form/TextAreaField";
10
+ import TextField from "../component/element/form/TextField";
11
+ import AccordionItem from "../component/element/html/AccordionItem";
12
+ import FolderSortableTree from "../component/element/html/FolderSortableTree";
13
+ import FormatNumeral from "../component/element/html/FormatNumeral";
14
+ import Html from "../component/element/html/Html";
15
+ import LabelFromValue from "../component/element/html/LabelFromValue";
16
+ import Modal from "../component/element/html/Modal";
17
+ import Paragraph from "../component/element/html/Paragraph";
18
+ import PreformattedMarkup from "../component/element/html/PreformattedMarkup";
19
+ import SortableTreeItemCollapseButton from "../component/element/html/SortableTreeItemCollapseButton";
20
+ import Tabs from "../component/element/html/Tabs";
21
+ import BootstrapElement from "../component/element/special/BootstrapElement";
22
+ import Count from "../component/element/special/Count";
23
+ import DataFilter from "../component/element/special/DataFilter";
24
+ import DelayedActions from "../component/element/special/DelayedActions";
25
+ import PageControls from "../component/element/special/PageControls";
26
+ import Phantom from "../component/element/special/Phantom";
27
+ import Switch from "../component/element/special/Switch";
28
+ import GlobalDataContext from "./GlobalDataContext";
29
+ import TemplateContext from "./TemplateContext";
30
+ import TemplateValue, {dataLocationToPath, evaluateTemplateValue} from "./TemplateSystem";
31
+ import {
32
+ Accordion,
33
+ Alert,
34
+ Badge,
35
+ Button,
36
+ } from "react-bootstrap";
37
+ import LineChart from "../component/element/chart/LineChart";
38
+
39
+ const components = {
40
+ AccordionItem,
41
+ BarChart,
42
+ CheckBoxField,
43
+ Count,
44
+ DateField,
45
+ DataFilter,
46
+ DelayedActions,
47
+ DoughnutChart,
48
+ FolderSortableTree,
49
+ FormatNumeral,
50
+ Html,
51
+ LabelFromValue,
52
+ LineChart,
53
+ Modal,
54
+ NumberField,
55
+ PageControls,
56
+ Paragraph,
57
+ Phantom,
58
+ PolarAreaChart,
59
+ PreformattedMarkup,
60
+ SelectField,
61
+ SortableTreeItemCollapseButton,
62
+ Switch,
63
+ Tabs,
64
+ TextAreaField,
65
+ TextField,
66
+ };
67
+
68
+ /**
69
+ * Gives direct access to React Bootstrap components.
70
+ */
71
+ const bootstrapComponents = {
72
+ BsAccordion: Accordion,
73
+ BsAlert: Alert,
74
+ BsBadge: Badge,
75
+ BsButton: Button,
76
+ };
77
+
78
+ function View({props, currentData, datafield, path}) {
79
+ const globalDataContext = useContext(GlobalDataContext);
80
+ const templateContext = useContext(TemplateContext);
81
+
82
+ const {element} = globalDataContext;
83
+
84
+ if (currentData === undefined) {
85
+ currentData = "";
86
+ }
87
+
88
+ if (props?.type) {
89
+ // A type is specified.
90
+ // First, try to find a component matching the given type by name.
91
+ // When not found, we map to a Html component as fallback.
92
+ let componentRegistryId = undefined;
93
+ let ComponentToRender = undefined;
94
+
95
+ const componentRegistries = [
96
+ {"registryId": "module", "components": components},
97
+ {"registryId": "bootstrap", "components": bootstrapComponents},
98
+ ];
99
+
100
+ while (componentRegistries.length) {
101
+ const {registryId, components: registryComponents} = componentRegistries.shift();
102
+
103
+ ComponentToRender = registryComponents[props.type] ?? undefined;
104
+
105
+ if (ComponentToRender !== undefined) {
106
+ componentRegistryId = registryId;
107
+ break;
108
+ }
109
+ }
110
+
111
+ if (ComponentToRender === undefined) {
112
+ // Use the module:Html component as fallback.
113
+ ComponentToRender = Html;
114
+ componentRegistryId = "module";
115
+ }
116
+
117
+ if (componentRegistryId === "bootstrap") {
118
+ return <BootstrapElement
119
+ bsComponent={ComponentToRender}
120
+ path={path}
121
+ props={props}
122
+ currentData={currentData}
123
+ datafield={datafield}/>;
124
+ }
125
+
126
+ if (Html === ComponentToRender) {
127
+ // Either the user has specifically asked for a Html component,
128
+ // or this is a fallback for an unknown type.
129
+ // Make sure the tag is set.
130
+ props.tag = props.tag ?? props.type;
131
+ }
132
+
133
+ return <ComponentToRender
134
+ path={path}
135
+ props={props}
136
+ currentData={currentData}
137
+ datafield={datafield}/>;
138
+ }
139
+
140
+ if (props?.load) {
141
+ // An external render source must be loaded.
142
+ // let load = props.load;
143
+ let loadedRenderArray;
144
+
145
+ const _customDataLocation = props?.customDataLocation ?? undefined;
146
+ if (_customDataLocation) debugger;
147
+
148
+ // Determine which data to use.
149
+ const finalCurrentData = _customDataLocation
150
+ // The data is located somewhere in the current data.
151
+ ? evaluateTemplateValue({
152
+ globalDataContext: globalDataContext,
153
+ templateContext: templateContext,
154
+ valueToEvaluate: _customDataLocation,
155
+ })
156
+ // The data is the current data.
157
+ : currentData;
158
+
159
+ // The data path must be set accordingly.
160
+ const finalDataPath = _customDataLocation
161
+ ? dataLocationToPath({dataLocation: _customDataLocation, currentPath: path, globalDataContext, templateContext})
162
+ : path;
163
+
164
+ // This external source can return a single component to render,
165
+ // or a collection of components.
166
+ if (typeof props.load === "function") {
167
+ // A JS function has been defined. Execute it with the currentData.
168
+ // The function must return a render array.
169
+ loadedRenderArray = props.load(finalCurrentData);
170
+ } else {
171
+ // Load the render array from the registry.
172
+ loadedRenderArray = element[props.load];
173
+ }
174
+
175
+ // Override any values of the registry render array with the current render array,
176
+ // without the properties specific to the "load" method.
177
+ const {load, customDataLocation, ...propsWithoutLoadKey} = props;
178
+ loadedRenderArray = {...loadedRenderArray, ...propsWithoutLoadKey};
179
+
180
+ // Now that we have our render array, recurse on the View component.
181
+ if (props.keepTemplateContext) {
182
+ // Keep the current template context.
183
+ return (
184
+ <View
185
+ currentData={finalCurrentData}
186
+ datafield={datafield}
187
+ path={finalDataPath}
188
+ props={loadedRenderArray}
189
+ />
190
+ );
191
+ }
192
+
193
+ // We open a new template context in the process.
194
+ return (
195
+ <TemplateContext.Provider value={{templateData: finalCurrentData, templatePath: finalDataPath}}>
196
+ <View
197
+ currentData={finalCurrentData}
198
+ datafield={datafield}
199
+ path={finalDataPath}
200
+ props={loadedRenderArray}
201
+ />
202
+ </TemplateContext.Provider>
203
+ );
204
+ }
205
+
206
+ // Try to go deeper to render something.
207
+ if (Array.isArray(props)) {
208
+ return props.map((item, index) =>
209
+ <View
210
+ currentData={currentData[index] ?? undefined}
211
+ datafield={index}
212
+ key={path + "." + index}
213
+ path={path + "." + index}
214
+ props={item ?? undefined}
215
+ />
216
+ );
217
+ }
218
+
219
+ if (typeof props === "object") {
220
+ return Object.entries(props).map(([itemKey, item]) => {
221
+ return <View
222
+ currentData={currentData[itemKey] ?? undefined}
223
+ datafield={itemKey ?? undefined}
224
+ key={path + "." + itemKey}
225
+ path={path + "." + itemKey}
226
+ props={item}
227
+ />
228
+ }
229
+ );
230
+ }
231
+
232
+ // Display the content directly.
233
+ // The content tries to use the currentData in case the data wants to rewrite the output.
234
+ // If not available, we simply use the given props, which is usually a string, which can
235
+ // also be a reference to a template context data.
236
+ // If no props is available, do not render anything.
237
+ return <TemplateValue valueToEvaluate={currentData || (props ?? null)}/>;
238
+ }
239
+
240
+ export default View;
package/lib/main.jsx ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Entry point for the lib.
3
+ */
4
+ import "bootstrap/dist/css/bootstrap.min.css";
5
+
6
+ import ReactiveJsonRoot from "./engine/ReactiveJsonRoot.jsx";
7
+ import {StrictMode} from "react"
8
+ import {createRoot} from "react-dom/client";
9
+
10
+ export {ReactiveJsonRoot};
11
+
12
+ document.querySelectorAll("reactive-json").forEach((element) => {
13
+ // Use this to change the fetch method.
14
+ const maybeMethod = element.dataset?.method;
15
+
16
+ // Get data included in the root element.
17
+ const headersForData_asElements = element.querySelectorAll("data-source-request-header");
18
+ const headersForData = headersForData_asElements.length ? {} : undefined;
19
+
20
+ headersForData_asElements.forEach((headerElement, key, parent) => {
21
+ const headerField = headerElement?.dataset?.headerField;
22
+ const headerValue = headerElement?.dataset?.headerValue;
23
+
24
+ if (!headerField || !headerValue) {
25
+ return;
26
+ }
27
+
28
+ headersForData[headerField] = headerValue;
29
+ });
30
+
31
+ // TODO: Retrieve the reactive-json plugins and inject them in ReactiveJsonRoot.
32
+ createRoot(element).render(
33
+ <StrictMode>
34
+ <ReactiveJsonRoot
35
+ dataFetchMethod={maybeMethod}
36
+ dataUrl={element.dataset.url}
37
+ headersForData={headersForData}
38
+ />
39
+ </StrictMode>,
40
+ );
41
+ });
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@ea-lab/reactive-json",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "files": [
6
+ "dist",
7
+ "lib"
8
+ ],
9
+ "main": "./dist/reactive-json.umd.cjs",
10
+ "module": "./dist/reactive-json.js",
11
+ "exports": {
12
+ ".": {
13
+ "require": "./dist/reactive-json.umd.cjs",
14
+ "import": "./dist/reactive-json.js"
15
+ }
16
+ },
17
+ "scripts": {
18
+ "dev": "vite",
19
+ "build": "vite build",
20
+ "lint": "eslint .",
21
+ "preview": "vite preview"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://bitbucket.org/ea-lab/reactive-json.git"
26
+ },
27
+ "keywords": [
28
+ "build",
29
+ "config",
30
+ "data",
31
+ "dynamic",
32
+ "interactive",
33
+ "json",
34
+ "react",
35
+ "structure",
36
+ "yaml"
37
+ ],
38
+ "author": "Quang-Minh DANG <quang-minh@ea-lab.io> (https://ea-lab.io/)",
39
+ "license": "ISC",
40
+ "bugs": {
41
+ "url": "https://bitbucket.org/ea-lab/reactive-json/issues"
42
+ },
43
+ "homepage": "https://bitbucket.org/ea-lab/reactive-json#readme",
44
+ "devDependencies": {
45
+ "@eslint/js": "^9.21.0",
46
+ "@types/node": "^22.14.1",
47
+ "@types/react": "^18",
48
+ "@types/react-dom": "^18",
49
+ "@vitejs/plugin-react": "^4.3.4",
50
+ "bootstrap": "^5.3.5",
51
+ "eslint": "^9.21.0",
52
+ "eslint-plugin-react-hooks": "^5.1.0",
53
+ "eslint-plugin-react-refresh": "^0.4.19",
54
+ "globals": "^15.15.0",
55
+ "react-router-dom": "^7.5.0",
56
+ "vite": "^6.2.0"
57
+ },
58
+ "peerDependencies": {
59
+ "axios": "^1.8.4",
60
+ "chart.js": "^4.4.8",
61
+ "clsx": "^2.1.1",
62
+ "dnd-kit-sortable-tree": "^0.1.73",
63
+ "html-react-parser": "^5.2.3",
64
+ "js-yaml": "^4.1.0",
65
+ "jsonpath": "^1.1.1",
66
+ "lodash": "^4.17.21",
67
+ "react": ">=18",
68
+ "react-bootstrap": "^2.10.9",
69
+ "react-chartjs-2": "^5.3.0",
70
+ "react-dom": ">=18"
71
+ }
72
+ }