xmlui 0.7.20 → 0.7.21

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 (58) hide show
  1. package/dist/{apiInterceptorWorker-D3L7dpSV.mjs → apiInterceptorWorker-LRHkKnha.mjs} +1 -1
  2. package/dist/{grammar.tmLanguage-69iP6c5d.mjs → grammar.tmLanguage-DNepe_jP.mjs} +1 -1
  3. package/dist/index-5NLXyjX0.mjs +31055 -0
  4. package/dist/index.css +1 -1
  5. package/dist/scripts/bin/build-lib.js +1 -1
  6. package/dist/scripts/bin/index.js +2 -2
  7. package/dist/scripts/bin/start.js +23 -6
  8. package/dist/scripts/bin/viteConfig.js +1 -0
  9. package/dist/scripts/package.json +2 -1
  10. package/dist/scripts/src/abstractions/ExtensionDefs.js +2 -0
  11. package/dist/scripts/src/components/ComponentProvider.js +171 -148
  12. package/dist/scripts/src/components/FormItem/FormItemNative.js +1 -1
  13. package/dist/scripts/src/components/HtmlTags/HtmlTags.js +33 -0
  14. package/dist/scripts/src/components/RawHtml/RawHtml.js +39 -0
  15. package/dist/scripts/src/components/RawHtml/RawHtmlNative.js +13 -0
  16. package/dist/scripts/src/components/Theme/ThemeNative.js +1 -1
  17. package/dist/scripts/src/components-core/LoaderComponent.js +1 -1
  18. package/dist/scripts/src/components-core/RestApiProxy.js +1 -1
  19. package/dist/scripts/src/components-core/StandaloneApp.js +19 -19
  20. package/dist/scripts/src/components-core/{StandaloneComponentManager.js → StandaloneExtensionManager.js} +6 -13
  21. package/dist/scripts/src/components-core/action/APICall.js +1 -1
  22. package/dist/scripts/src/components-core/loader/PageableLoader.js +4 -4
  23. package/dist/scripts/src/components-core/{AppRoot.js → rendering/AppContent.js} +60 -145
  24. package/dist/scripts/src/components-core/rendering/AppRoot.js +55 -0
  25. package/dist/scripts/src/components-core/rendering/AppWrapper.js +44 -0
  26. package/dist/scripts/src/components-core/{ComponentBed.js → rendering/ComponentAdapter.js} +8 -8
  27. package/dist/scripts/src/components-core/rendering/ComponentWrapper.js +147 -0
  28. package/dist/scripts/src/components-core/rendering/Container.js +576 -0
  29. package/dist/scripts/src/components-core/rendering/ContainerWrapper.js +82 -0
  30. package/dist/scripts/src/components-core/{ErrorBoundary.js → rendering/ErrorBoundary.js} +9 -3
  31. package/dist/scripts/src/components-core/rendering/StateContainer.js +331 -0
  32. package/dist/scripts/src/components-core/{container → rendering}/buildProxy.js +11 -7
  33. package/dist/scripts/src/components-core/{container → rendering}/collectFnVarDeps.js +2 -2
  34. package/dist/scripts/src/components-core/{container → rendering}/reducer.js +3 -0
  35. package/dist/scripts/src/components-core/rendering/renderChild.js +81 -0
  36. package/dist/scripts/src/index.js +3 -6
  37. package/dist/scripts/src/parsers/xmlui-parser/transform.js +193 -164
  38. package/dist/scripts/src/syntax/grammar.tmLanguage.json +1 -1
  39. package/dist/style.css +1 -1
  40. package/dist/xmlui-metadata.mjs +4216 -4232
  41. package/dist/xmlui-metadata.umd.js +16 -16
  42. package/dist/xmlui-standalone.umd.js +262 -290
  43. package/dist/xmlui.d.ts +36 -61
  44. package/dist/xmlui.mjs +1 -1
  45. package/package.json +2 -1
  46. package/dist/index-BYjaMGrD.mjs +0 -76804
  47. package/dist/scripts/src/components/BarChart/BarChart.js +0 -49
  48. package/dist/scripts/src/components/BarChart/BarChartNative.js +0 -176
  49. package/dist/scripts/src/components/Map/Map.js +0 -75
  50. package/dist/scripts/src/components/Map/world_countries.json +0 -45307
  51. package/dist/scripts/src/components/PieChart/PieChart.js +0 -45
  52. package/dist/scripts/src/components/PieChart/PieChartNative.js +0 -165
  53. package/dist/scripts/src/components/chart-color-schemes.js +0 -43
  54. package/dist/scripts/src/components-core/container/Container.js +0 -1186
  55. package/dist/scripts/src/components-core/container/ContainerComponentDef.js +0 -15
  56. /package/dist/scripts/src/components-core/{InvalidComponent.js → rendering/InvalidComponent.js} +0 -0
  57. /package/dist/scripts/src/components-core/{UnknownComponent.js → rendering/UnknownComponent.js} +0 -0
  58. /package/dist/scripts/src/components-core/{container → rendering}/valueExtractor.js +0 -0
@@ -0,0 +1,576 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.Container = void 0;
49
+ const jsx_runtime_1 = require("react/jsx-runtime");
50
+ const containers_1 = require("@components-core/abstractions/containers");
51
+ const AppContext_1 = require("@components-core/AppContext");
52
+ const buildProxy_1 = require("@components-core/rendering/buildProxy");
53
+ const DebugViewProvider_1 = require("@components-core/DebugViewProvider");
54
+ const process_statement_async_1 = require("@components-core/script-runner/process-statement-async");
55
+ const process_statement_sync_1 = require("@components-core/script-runner/process-statement-sync");
56
+ const extractParam_1 = require("@components-core/utils/extractParam");
57
+ const hooks_1 = require("@components-core/utils/hooks");
58
+ const misc_1 = require("@components-core/utils/misc");
59
+ const statementUtils_1 = require("@components-core/utils/statementUtils");
60
+ const StateViewerNative_1 = require("@components/StateViewer/StateViewerNative");
61
+ const lodash_es_1 = require("lodash-es");
62
+ const react_compose_refs_1 = require("@radix-ui/react-compose-refs");
63
+ const memoize_one_1 = __importDefault(require("memoize-one"));
64
+ const react_1 = __importStar(require("react"));
65
+ const react_router_dom_1 = require("react-router-dom");
66
+ const renderChild_1 = require("./renderChild");
67
+ const ThemeContext_1 = require("@components-core/theming/ThemeContext");
68
+ const LoaderComponent_1 = require("@components-core/LoaderComponent");
69
+ const constants_1 = require("@components-core/constants");
70
+ // React component to display a view container and implement its behavior
71
+ exports.Container = (0, react_1.memo)((0, react_1.forwardRef)(function Container({ node, componentState, dispatch: containerDispatch, parentDispatch, resolvedKey, version, setVersion, statePartChanged, registerComponentApi: containerRegisterComponentApi, parentRegisterComponentApi, layoutContextRef, parentRenderContext, memoedVarsRef, isImplicit, uidInfoRef: parentUidInfoRef, }, ref) {
72
+ var _a;
73
+ const { apiBoundContainer } = node;
74
+ const dispatch = isImplicit ? parentDispatch : containerDispatch;
75
+ const registerComponentApi = isImplicit
76
+ ? parentRegisterComponentApi
77
+ : containerRegisterComponentApi;
78
+ const appContext = (0, AppContext_1.useAppContext)();
79
+ const { getThemeVar } = (0, ThemeContext_1.useTheme)();
80
+ const navigate = (0, react_router_dom_1.useNavigate)();
81
+ const location = (0, react_router_dom_1.useLocation)();
82
+ const fnsRef = (0, react_1.useRef)({});
83
+ const stateRef = (0, react_1.useRef)(componentState);
84
+ //generally bad practise to write ref in render (https://react.dev/learn/referencing-values-with-refs#best-practices-for-refs), but:
85
+ // this stateRef is only used in runCodeSync/async functions, which are memoized, so it's safe to use it here (as I know: illesg)
86
+ // In case we sync up the stateRef with the componentState in the useEffect/useInsertionEffect/useLayoutEffect, the stateRef would lag behind the componentState
87
+ stateRef.current = componentState;
88
+ const parsedStatementsRef = (0, react_1.useRef)({});
89
+ const [_, startTransition] = (0, react_1.useTransition)();
90
+ const mountedRef = (0, react_1.useRef)(true);
91
+ // --- This ref holds a map of promises for each statement execution that cause any state change.
92
+ const statementPromises = (0, react_1.useRef)(new Map());
93
+ // --- Ensure that re-rendering because of version change resolves all pending statement promises.
94
+ (0, hooks_1.useIsomorphicLayoutEffect)(() => {
95
+ for (const resolve of statementPromises.current.values()) {
96
+ resolve();
97
+ }
98
+ }, [version]);
99
+ // --- Ensure that component disposal resolves all pending statement promises.
100
+ (0, react_1.useEffect)(() => {
101
+ mountedRef.current = true;
102
+ const leftPromises = statementPromises.current;
103
+ return () => {
104
+ mountedRef.current = false;
105
+ for (const resolve of leftPromises.values()) {
106
+ resolve();
107
+ }
108
+ };
109
+ }, []);
110
+ const runCodeAsync = (0, misc_1.useEvent)((source, componentUid, options, ...eventArgs) => __awaiter(this, void 0, void 0, function* () {
111
+ var _a, _b, _c;
112
+ // --- Check if the event handler can sign its lifecycle state
113
+ const canSignEventLifecycle = () => componentUid.description !== undefined && (options === null || options === void 0 ? void 0 : options.eventName) !== undefined;
114
+ let changes = [];
115
+ const getComponentStateClone = () => {
116
+ changes.length = 0;
117
+ const poj = (0, lodash_es_1.cloneDeep)(Object.assign({}, stateRef.current));
118
+ poj["$this"] = stateRef.current[componentUid];
119
+ return (0, buildProxy_1.buildProxy)(poj, (changeInfo) => {
120
+ var _a;
121
+ const idRoot = (_a = changeInfo.pathArray) === null || _a === void 0 ? void 0 : _a[0];
122
+ if (idRoot === null || idRoot === void 0 ? void 0 : idRoot.startsWith("$")) {
123
+ throw new Error("Cannot update a read-only variable");
124
+ }
125
+ changes.push(changeInfo);
126
+ });
127
+ };
128
+ const evalAppContext = Object.assign(Object.assign({}, appContext), { getThemeVar });
129
+ const evalContext = {
130
+ appContext: evalAppContext,
131
+ eventArgs,
132
+ localContext: getComponentStateClone(),
133
+ implicitContextGetter: () => {
134
+ return {
135
+ uid: componentUid,
136
+ state: stateRef.current,
137
+ dispatch,
138
+ appContext: evalAppContext,
139
+ navigate,
140
+ location,
141
+ lookupAction: (action, uid, actionOptions = {}) => {
142
+ return lookupAction(action, uid, Object.assign(Object.assign({}, actionOptions), { ephemeral: true }));
143
+ },
144
+ };
145
+ },
146
+ options: {
147
+ defaultToOptionalMemberAccess: typeof ((_a = appContext.appGlobals) === null || _a === void 0 ? void 0 : _a.defaultToOptionalMemberAccess) === "boolean"
148
+ ? appContext.appGlobals.defaultToOptionalMemberAccess
149
+ : true,
150
+ },
151
+ };
152
+ try {
153
+ // --- Prepare the event handler to an arrow expression statement
154
+ let statements;
155
+ if (typeof source === "string") {
156
+ if (!parsedStatementsRef.current[source]) {
157
+ parsedStatementsRef.current[source] = (0, statementUtils_1.prepareHandlerStatements)((0, statementUtils_1.parseHandlerCode)(source), evalContext);
158
+ }
159
+ statements = parsedStatementsRef.current[source];
160
+ }
161
+ else {
162
+ statements = [
163
+ {
164
+ type: "ArrowS",
165
+ expression: (0, lodash_es_1.cloneDeep)(source), //TODO illesg (talk it through why we need to deep clone, it it's omitted, it gets slower every time we run it)
166
+ },
167
+ ];
168
+ }
169
+ if (!(statements === null || statements === void 0 ? void 0 : statements.length)) {
170
+ return;
171
+ }
172
+ if (canSignEventLifecycle()) {
173
+ // --- Sign the event handler has been started
174
+ dispatch({
175
+ type: containers_1.ContainerActionKind.EVENT_HANDLER_STARTED,
176
+ payload: {
177
+ uid: componentUid.description,
178
+ eventName: options.eventName,
179
+ },
180
+ });
181
+ }
182
+ let mainThreadBlockingRuns = 0;
183
+ yield (0, process_statement_async_1.processStatementQueueAsync)(statements, evalContext, undefined, (evalContext) => __awaiter(this, void 0, void 0, function* () {
184
+ if (changes.length) {
185
+ mainThreadBlockingRuns = 0;
186
+ changes.forEach((change) => {
187
+ statePartChanged(change.pathArray, (0, lodash_es_1.cloneDeep)(change.newValue), change.target, change.action);
188
+ });
189
+ let resolve = null;
190
+ const stateUpdatedPromise = new Promise((res) => {
191
+ resolve = () => {
192
+ res(null);
193
+ };
194
+ });
195
+ const key = (0, misc_1.generatedId)();
196
+ statementPromises.current.set(key, resolve);
197
+ // We use this to tell react that this update is not high-priority.
198
+ // If we don't put it to a transition, the whole app would be blocked if we run a long,
199
+ // update intensive queue (e.g. an infinite loop which
200
+ // increments a counter, see playground example learning/01_Experiments/01_Event_Framework/app ).
201
+ // Before this solution, we used a setTimeout(..., 0); hack, but some browsers (chrome especially)
202
+ // do some funky stuff with the background tabs (e.g. all the setTimeouts are
203
+ // maximized to run in 1 time / minute, doesn't matter if it's timeout is 0)
204
+ // As of 2023. June 20, this solution works with backgrounded tabs, too.
205
+ startTransition(() => {
206
+ setVersion((prev) => prev + 1);
207
+ });
208
+ //TODO this could be a problem - if this container gets unmounted, we still have to wait for the update,
209
+ // but in that case this update probably happened in the parent (e.g. a button's event handler removes the whole container
210
+ // where the button lives, but it still has some statements to run).
211
+ // with this solution the statement execution doesn't stop, and we fallback waiting with a setTimeout(0)
212
+ if (mountedRef.current) {
213
+ yield stateUpdatedPromise;
214
+ }
215
+ else {
216
+ yield (0, misc_1.delay)(0);
217
+ }
218
+ statementPromises.current.delete(key);
219
+ changes = [];
220
+ }
221
+ else {
222
+ //in this else branch normally we block the main thread (we don't wait for any state promise to be resolved),
223
+ // so in a long-running (typically infinite loop) situation, where there aren't any changes in the state
224
+ // we block the main thread indefinitely... this 'mainThreadBlockingRuns' var solution makes sure that
225
+ // we pause in every 100 runs, and let the main thread breath a bit, so it's not frozen for the whole time
226
+ // (we clear that counter above, too, where we use a startTransition call to de-prioritize this work)
227
+ mainThreadBlockingRuns++;
228
+ if (mainThreadBlockingRuns > 100) {
229
+ mainThreadBlockingRuns = 0;
230
+ yield (0, misc_1.delay)(0);
231
+ }
232
+ }
233
+ evalContext.localContext = getComponentStateClone();
234
+ }));
235
+ if (canSignEventLifecycle()) {
236
+ // --- Sign the event handler has successfully completed
237
+ dispatch({
238
+ type: containers_1.ContainerActionKind.EVENT_HANDLER_COMPLETED,
239
+ payload: {
240
+ uid: componentUid.description,
241
+ eventName: options.eventName,
242
+ },
243
+ });
244
+ }
245
+ if ((_c = (_b = evalContext.mainThread) === null || _b === void 0 ? void 0 : _b.blocks) === null || _c === void 0 ? void 0 : _c.length) {
246
+ return evalContext.mainThread.blocks[evalContext.mainThread.blocks.length - 1]
247
+ .returnValue;
248
+ }
249
+ }
250
+ catch (e) {
251
+ //if we pass down an event handler to a component, we should sign the error once, not in every step of the component chain
252
+ // (we use it in the compoundComponent, resolving it's event handlers)
253
+ if ((options === null || options === void 0 ? void 0 : options.signError) !== false) {
254
+ appContext.signError(e);
255
+ }
256
+ if (canSignEventLifecycle()) {
257
+ dispatch({
258
+ type: containers_1.ContainerActionKind.EVENT_HANDLER_ERROR,
259
+ payload: {
260
+ uid: componentUid.description,
261
+ eventName: options.eventName,
262
+ error: e,
263
+ },
264
+ });
265
+ }
266
+ throw e;
267
+ }
268
+ }));
269
+ const runCodeSync = (0, react_1.useCallback)((arrowExpression, ...eventArgs) => {
270
+ var _a, _b;
271
+ const evalContext = {
272
+ localContext: (0, lodash_es_1.cloneDeep)(stateRef.current),
273
+ appContext,
274
+ eventArgs,
275
+ };
276
+ try {
277
+ const arrowStmt = {
278
+ type: "ArrowS",
279
+ expression: arrowExpression,
280
+ };
281
+ (0, process_statement_sync_1.processStatementQueue)([arrowStmt], evalContext);
282
+ if ((_b = (_a = evalContext.mainThread) === null || _a === void 0 ? void 0 : _a.blocks) === null || _b === void 0 ? void 0 : _b.length) {
283
+ return evalContext.mainThread.blocks[evalContext.mainThread.blocks.length - 1]
284
+ .returnValue;
285
+ }
286
+ }
287
+ catch (e) {
288
+ console.error(e);
289
+ throw e;
290
+ }
291
+ }, [appContext]);
292
+ const getOrCreateEventHandlerFn = (0, misc_1.useEvent)((src, uid, options) => {
293
+ var _a;
294
+ const stringSrc = typeof src === "string" ? src : src.statement.source;
295
+ const fnCacheKey = `${options === null || options === void 0 ? void 0 : options.eventName};${stringSrc}`;
296
+ const handler = (...eventArgs) => {
297
+ return runCodeAsync(src, uid, options, ...(0, lodash_es_1.cloneDeep)(eventArgs));
298
+ };
299
+ if (options === null || options === void 0 ? void 0 : options.ephemeral) {
300
+ return handler;
301
+ }
302
+ if (!((_a = fnsRef.current[uid]) === null || _a === void 0 ? void 0 : _a[fnCacheKey])) {
303
+ fnsRef.current[uid] = fnsRef.current[uid] || {};
304
+ fnsRef.current[uid][fnCacheKey] = handler;
305
+ }
306
+ return fnsRef.current[uid][fnCacheKey];
307
+ });
308
+ const getOrCreateSyncCallbackFn = (0, react_1.useCallback)((arrowExpression, uid) => {
309
+ var _a;
310
+ const fnCacheKey = `sync-callback-${arrowExpression.source}`;
311
+ if (!((_a = fnsRef.current[uid]) === null || _a === void 0 ? void 0 : _a[fnCacheKey])) {
312
+ fnsRef.current[uid] = fnsRef.current[uid] || {};
313
+ fnsRef.current[uid][fnCacheKey] = (0, memoize_one_1.default)((arrowExpression) => {
314
+ // console.log('busting sync callback cache', arrowExpression);
315
+ return (...eventArgs) => {
316
+ // console.log("calling sync callback", arrowExpression);
317
+ return runCodeSync(arrowExpression, ...eventArgs);
318
+ };
319
+ });
320
+ }
321
+ return fnsRef.current[uid][fnCacheKey](arrowExpression);
322
+ }, [runCodeSync]);
323
+ const lookupSyncCallback = (0, react_1.useCallback)((action, uid) => {
324
+ if (!action) {
325
+ return undefined;
326
+ }
327
+ if (typeof action === "function") {
328
+ return action;
329
+ }
330
+ // const resolvedAction = extractParam(componentState, action, appContext, true);
331
+ if (!action) {
332
+ return undefined;
333
+ }
334
+ if (typeof action === "function") {
335
+ return action;
336
+ }
337
+ if (!action._ARROW_EXPR_) {
338
+ throw new Error("Only arrow expression allowed in sync callback");
339
+ }
340
+ return getOrCreateSyncCallbackFn(action, uid);
341
+ }, [getOrCreateSyncCallbackFn]);
342
+ const lookupAction = (0, react_1.useCallback)((action, uid, options) => {
343
+ let safeAction = action;
344
+ if (!action && uid.description && (options === null || options === void 0 ? void 0 : options.eventName)) {
345
+ const handlerFnName = `${uid.description}_on${(0, misc_1.capitalizeFirstLetter)(options === null || options === void 0 ? void 0 : options.eventName)}`;
346
+ if (componentState[handlerFnName] && componentState[handlerFnName]._ARROW_EXPR_) {
347
+ safeAction = componentState[handlerFnName];
348
+ }
349
+ }
350
+ if (!safeAction) {
351
+ return undefined;
352
+ }
353
+ if (typeof safeAction === "function") {
354
+ return safeAction;
355
+ }
356
+ return getOrCreateEventHandlerFn(safeAction, uid, options);
357
+ }, [componentState, getOrCreateEventHandlerFn]);
358
+ const isApiRegisteredInnerRef = (0, react_1.useRef)(false);
359
+ (0, react_1.useEffect)(() => {
360
+ if (!node.api) {
361
+ return;
362
+ }
363
+ if (!node.containerUid) {
364
+ return;
365
+ }
366
+ if (isApiRegisteredInnerRef.current) {
367
+ return;
368
+ }
369
+ isApiRegisteredInnerRef.current = true;
370
+ const api = {};
371
+ const self = Symbol("$self");
372
+ Object.entries(node.api).forEach(([key, value]) => {
373
+ api[key] = lookupAction(value, self);
374
+ });
375
+ if (!isImplicit) {
376
+ registerComponentApi(self, api); //we register the api as $self for the compound components,
377
+ }
378
+ parentRegisterComponentApi(node.containerUid, api); // and register it for the parent component instance
379
+ }, [
380
+ lookupAction,
381
+ node.api,
382
+ node.containerUid,
383
+ node.uid,
384
+ isImplicit,
385
+ parentRegisterComponentApi,
386
+ registerComponentApi,
387
+ ]);
388
+ const cleanup = (0, misc_1.useEvent)((uid) => {
389
+ // console.log("CLEANUP CALLED FOR", node);
390
+ //TODO cleanup registered component api for that uid
391
+ //TODO cleanup state for that uid
392
+ delete fnsRef.current[uid];
393
+ });
394
+ // --- The container wraps the `renderChild` function to provide that to the child components
395
+ const stableRenderChild = (0, react_1.useCallback)((childNode, lc, pRenderContext, uidInfoRef) => {
396
+ // TODO: Check if this is a valid use case
397
+ if (typeof childNode === "string") {
398
+ throw Error("should be resolved for now");
399
+ }
400
+ // --- Make children an array if it's not
401
+ const children = (0, lodash_es_1.isArray)(childNode) ? childNode : [childNode];
402
+ if (!children || !children.length) {
403
+ // --- No child, nothing to render
404
+ return null;
405
+ }
406
+ // --- If there are multiple children, we need to add a `key` to each of them
407
+ const wrapWithFragment = children.length > 1;
408
+ // --- Render each child
409
+ const renderedChildren = children.map((child, childIndex) => {
410
+ if (!child) {
411
+ // --- No child, nothing to render: Should not happen
412
+ return undefined;
413
+ }
414
+ // --- Invoke the jolly-joker `renderChild` function to render the child. Note that
415
+ // --- in ithe context, we pass the `stableRenderChild` function, so the child can
416
+ // --- render its children recursively.
417
+ const renderedChild = (0, renderChild_1.renderChild)({
418
+ node: child,
419
+ state: componentState,
420
+ dispatch,
421
+ appContext,
422
+ lookupAction,
423
+ lookupSyncCallback,
424
+ registerComponentApi,
425
+ renderChild: stableRenderChild,
426
+ statePartChanged: statePartChanged,
427
+ layoutContext: lc,
428
+ parentRenderContext: pRenderContext,
429
+ memoedVarsRef,
430
+ cleanup,
431
+ uidInfoRef,
432
+ });
433
+ if (renderedChild === undefined) {
434
+ // --- No displayable child, nothing to render
435
+ return undefined;
436
+ }
437
+ // --- Let's process the rendered child
438
+ let rendered = renderedChild;
439
+ const key = `${childIndex}_${child.uid}`;
440
+ if (wrapWithFragment) {
441
+ // --- Add the `key` attribute to the child
442
+ if (react_1.default.isValidElement(renderedChild)) {
443
+ // --- React can display this element
444
+ rendered = react_1.default.cloneElement(renderedChild, { key });
445
+ }
446
+ else {
447
+ // --- A simple text node (or alike). We need to wrap it in a `Fragment`
448
+ rendered = (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: renderedChild }, key);
449
+ }
450
+ }
451
+ // --- Done.
452
+ return rendered;
453
+ });
454
+ // --- At this point we have a React node for each child
455
+ if (renderedChildren.length === 1) {
456
+ // --- If we have a single (and valid React element) child, we compose its
457
+ // --- `ref` with the parent's `ref`. This allows the parent to access the child's
458
+ // --- DOM node. Otherwise, we use the child as is.
459
+ return ref && renderedChildren[0] && (0, react_1.isValidElement)(renderedChildren[0])
460
+ ? react_1.default.cloneElement(renderedChildren[0], {
461
+ ref: (0, react_compose_refs_1.composeRefs)(ref, renderedChildren[0].ref),
462
+ })
463
+ : renderedChildren[0];
464
+ }
465
+ // --- Done.
466
+ return renderedChildren;
467
+ }, [
468
+ componentState,
469
+ dispatch,
470
+ appContext,
471
+ lookupAction,
472
+ lookupSyncCallback,
473
+ registerComponentApi,
474
+ statePartChanged,
475
+ memoedVarsRef,
476
+ cleanup,
477
+ ref,
478
+ ]);
479
+ // --- Log the component state if you need it for debugging
480
+ if ((_a = node.props) === null || _a === void 0 ? void 0 : _a.debug) {
481
+ console.log(`Container: ${resolvedKey}`, {
482
+ componentState,
483
+ node,
484
+ });
485
+ }
486
+ // --- Use this object to store information about already rendered UIDs.
487
+ // --- We do not allow any action, loader, or transform to use the same UID; however (as of now) children
488
+ // --- may use the same UID.
489
+ const uidInfo = {};
490
+ const thisUidInfoRef = (0, react_1.useRef)({});
491
+ const uidInfoRef = node.uses === undefined ? parentUidInfoRef : thisUidInfoRef;
492
+ const debugContext = (0, DebugViewProvider_1.useDebugView)();
493
+ const stateViewProps = debugContext === null || debugContext === void 0 ? void 0 : debugContext.stateViewOptions;
494
+ const showContainer = stateViewProps && debugContext.displayStateView;
495
+ return ((0, jsx_runtime_1.jsxs)(react_1.Fragment, { children: [showContainer && ((0, jsx_runtime_1.jsxs)(StateViewerNative_1.StateViewer, { state: componentState, showBoundary: stateViewProps === null || stateViewProps === void 0 ? void 0 : stateViewProps.showBoundary, blink: stateViewProps === null || stateViewProps === void 0 ? void 0 : stateViewProps.blink, children: [renderLoaders({
496
+ uidInfo,
497
+ uidInfoRef,
498
+ loaders: node.loaders,
499
+ componentState,
500
+ memoedVarsRef,
501
+ //if it's an api bound container, we always use this container, otherwise use the parent if it's an implicit one
502
+ dispatch: apiBoundContainer ? containerDispatch : dispatch,
503
+ registerComponentApi: apiBoundContainer
504
+ ? containerRegisterComponentApi
505
+ : registerComponentApi,
506
+ appContext,
507
+ lookupAction,
508
+ lookupSyncCallback,
509
+ cleanup,
510
+ }), stableRenderChild(node.children, layoutContextRef === null || layoutContextRef === void 0 ? void 0 : layoutContextRef.current, parentRenderContext, uidInfoRef)] })), !showContainer && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [renderLoaders({
511
+ uidInfo,
512
+ uidInfoRef,
513
+ loaders: node.loaders,
514
+ componentState,
515
+ memoedVarsRef,
516
+ //if it's an api bound container, we always use this container, otherwise use the parent if it's an implicit one
517
+ dispatch: apiBoundContainer ? containerDispatch : dispatch,
518
+ registerComponentApi: apiBoundContainer
519
+ ? containerRegisterComponentApi
520
+ : registerComponentApi,
521
+ appContext,
522
+ lookupAction,
523
+ lookupSyncCallback,
524
+ cleanup,
525
+ }), stableRenderChild(node.children, layoutContextRef === null || layoutContextRef === void 0 ? void 0 : layoutContextRef.current, parentRenderContext, uidInfoRef)] }))] }, node.uid
526
+ ? `${resolvedKey}>${(0, extractParam_1.extractParam)(componentState, node.uid, appContext, true)}`
527
+ : undefined));
528
+ }));
529
+ function renderLoaders({ uidInfo, uidInfoRef, loaders = constants_1.EMPTY_ARRAY, componentState, dispatch, appContext, registerComponentApi, lookupAction, lookupSyncCallback, cleanup, memoedVarsRef, }) {
530
+ return loaders.map((loader) => {
531
+ // --- Check for the uniqueness of UIDs
532
+ if (loader === null || loader === void 0 ? void 0 : loader.uid) {
533
+ if (uidInfo[loader.uid]) {
534
+ // --- We have a duplicated ID (another loader)
535
+ throw new Error(`Another ${uidInfo[loader.uid]} definition in this container already uses the uid '${loader.uid}'`);
536
+ }
537
+ uidInfo[loader.uid] = "loader";
538
+ uidInfoRef.current[loader.uid] = {
539
+ type: "loader",
540
+ value: "loaderValue",
541
+ uid: loader.uid,
542
+ };
543
+ }
544
+ // --- Render the current loader
545
+ const renderedLoader = renderLoader({
546
+ loader,
547
+ componentState,
548
+ dispatch,
549
+ appContext,
550
+ registerComponentApi,
551
+ lookupAction,
552
+ lookupSyncCallback,
553
+ memoedVarsRef,
554
+ cleanup,
555
+ });
556
+ // --- Skip loaders with rendering errors
557
+ if (renderedLoader === undefined) {
558
+ return undefined;
559
+ }
560
+ // --- Take care to use a key property for the loader
561
+ return (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: renderedLoader }, loader.uid);
562
+ });
563
+ function renderLoader({ loader, componentState, dispatch, appContext, registerComponentApi, lookupAction, lookupSyncCallback, cleanup, memoedVarsRef, }) {
564
+ // --- For the sake of avoiding further issues
565
+ if (!loader) {
566
+ return null;
567
+ }
568
+ // --- Evaluate "when" to decide if the loader should be rendered
569
+ // --- Render only visible components
570
+ if (!(0, extractParam_1.shouldKeep)(loader.when, componentState, appContext)) {
571
+ return null;
572
+ }
573
+ // --- Use the loader type's renderer function
574
+ return ((0, jsx_runtime_1.jsx)(LoaderComponent_1.LoaderComponent, { onUnmount: cleanup, node: loader, state: componentState, dispatch: dispatch, registerComponentApi: registerComponentApi, lookupAction: lookupAction, lookupSyncCallback: lookupSyncCallback, memoedVarsRef: memoedVarsRef, appContext: appContext }));
575
+ }
576
+ }
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContainerWrapper = void 0;
4
+ exports.isContainerLike = isContainerLike;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const ErrorBoundary_1 = require("@components-core/rendering/ErrorBoundary");
7
+ const react_1 = require("react");
8
+ const StateContainer_1 = require("./StateContainer");
9
+ /**
10
+ * This function checks if a particular component needs a wrapping container to
11
+ * manage its internal state, which is closed from its external context but
12
+ * available to its children.
13
+ * @param node The component definition node to check
14
+ * @returns Tru, if the component needs a wrapping container
15
+ */
16
+ function isContainerLike(node) {
17
+ if (node.type === "Container") {
18
+ return true;
19
+ }
20
+ // --- If any of the following properties have a value, we need a container
21
+ return !!(node.loaders ||
22
+ node.vars ||
23
+ node.uses ||
24
+ node.contextVars ||
25
+ node.functions ||
26
+ node.scriptCollected);
27
+ }
28
+ /**
29
+ * This component is a container that manages the state of its children. It
30
+ * provides a context for the children to access the state and the API of the
31
+ * parent component.
32
+ */
33
+ exports.ContainerWrapper = (0, react_1.memo)((0, react_1.forwardRef)(function ContainerWrapper({ node, resolvedKey, parentState, parentStatePartChanged, parentRegisterComponentApi, parentDispatch, parentRenderContext, layoutContextRef, uidInfoRef, }, ref) {
34
+ // --- Make sure the component node is wrapped with a container
35
+ const containerizedNode = (0, react_1.useMemo)(() => getWrappedWithContainer(node), [node]);
36
+ return ((0, jsx_runtime_1.jsx)(ErrorBoundary_1.ErrorBoundary, { node: node, location: "container", children: (0, jsx_runtime_1.jsx)(StateContainer_1.StateContainer, { node: containerizedNode, resolvedKey: resolvedKey, parentState: parentState, parentStatePartChanged: parentStatePartChanged, parentRegisterComponentApi: parentRegisterComponentApi, parentDispatch: parentDispatch, parentRenderContext: parentRenderContext, layoutContextRef: layoutContextRef, uidInfoRef: uidInfoRef, isImplicit: node.type !== "Container" && containerizedNode.uses === undefined, ref: ref }) }));
37
+ }));
38
+ /**
39
+ * Wraps the specified component node with a container
40
+ * @param node The component node to wrap
41
+ * @returns A "Container" node
42
+ */
43
+ const getWrappedWithContainer = (node) => {
44
+ var _a, _b;
45
+ if (node.type === "Container") {
46
+ // --- Already wrapped
47
+ return node;
48
+ }
49
+ // --- Clone the node and remove the properties that will be moved to the container
50
+ // --- Note: we need the "when" property in the ModalDialog component, so we don't remove it
51
+ const wrappedNode = Object.assign(Object.assign({}, node), { props: Object.assign({}, node.props) });
52
+ delete wrappedNode.loaders;
53
+ delete wrappedNode.vars;
54
+ delete wrappedNode.functions;
55
+ delete wrappedNode.script;
56
+ delete wrappedNode.scriptCollected;
57
+ delete wrappedNode.scriptError;
58
+ delete wrappedNode.uses;
59
+ (_a = wrappedNode.props) === null || _a === void 0 ? true : delete _a.uses;
60
+ delete wrappedNode.api;
61
+ delete wrappedNode.contextVars;
62
+ // --- Do the wrapping
63
+ return {
64
+ type: "Container",
65
+ uid: node.uid,
66
+ when: node.when,
67
+ loaders: node.loaders,
68
+ vars: node.vars,
69
+ functions: node.functions,
70
+ scriptCollected: node.scriptCollected,
71
+ scriptError: node.scriptError,
72
+ uses: node.uses,
73
+ api: node.api,
74
+ containerUid: node === null || node === void 0 ? void 0 : node.containerUid,
75
+ apiBoundContainer: node === null || node === void 0 ? void 0 : node.apiBoundContainer,
76
+ contextVars: node.contextVars,
77
+ props: {
78
+ debug: (_b = node.props) === null || _b === void 0 ? void 0 : _b.debug,
79
+ },
80
+ children: [wrappedNode],
81
+ };
82
+ };