@arkcit/engine-render-layer 0.3.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.
package/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # @arkcit/engine-render-layer
2
+
3
+ Renderer-neutral resolved tree layer.
4
+
5
+ ## Position In The Flow
6
+
7
+ ```text
8
+ @arkcit/engine-schema
9
+ @arkcit/engine-runtime
10
+ @arkcit/engine-core
11
+
12
+ @arkcit/engine-render-layer
13
+
14
+ @arkcit/engine
15
+ @arkcit/engine-react
16
+ @arkcit/engine-angular
17
+ @arkcit/engine-react-native
18
+
19
+ target UI libraries
20
+ ```
21
+
22
+ ## What Goes In
23
+
24
+ - schema shape
25
+ - runtime access
26
+ - core resolution rules
27
+
28
+ ## What Comes Out
29
+
30
+ - resolved node descriptors
31
+ - children/content descriptors
32
+ - renderer-neutral shaping helpers
33
+
34
+ ## Responsibilities
35
+
36
+ - define the neutral render output
37
+ - avoid framework-specific rendering
38
+ - provide one resolved tree shape to many adapters
39
+
40
+ ## Do Not Put Here
41
+
42
+ - React elements
43
+ - Angular component instances
44
+ - native widgets
45
+ - docs or studio presentation wrappers
46
+
47
+ ## Used By
48
+
49
+ - `@arkcit/engine`
50
+ - `@arkcit/engine-react`
51
+ - `@arkcit/engine-angular`
52
+ - `@arkcit/engine-react-native`
@@ -0,0 +1,16 @@
1
+ import { UINodeLayout, UINode } from '@arkcit/engine-schema';
2
+ import { UIRuntime } from '@arkcit/engine-runtime';
3
+
4
+ declare const readRenderLayerNodeLayout: (props?: Record<string, unknown>) => UINodeLayout;
5
+
6
+ declare const normalizeRenderLayerTabsProps: (props: Record<string, unknown>) => Record<string, unknown>;
7
+
8
+ type UnknownRecord = Record<string, unknown>;
9
+ declare const RENDER_BINDING_PROP_NAMES: Set<string>;
10
+ declare const getRenderLayerBindingProps: (props: unknown) => UnknownRecord;
11
+
12
+ declare const resolveRenderLayerValue: (value: unknown, runtime: UIRuntime) => unknown;
13
+ declare const resolveRenderLayerNodeProps: (props: Record<string, unknown> | undefined, runtime: UIRuntime) => Record<string, unknown>;
14
+ declare const bindRenderLayerNodeEvents: (events: UINode["events"], runtime: UIRuntime) => Partial<Record<keyof NonNullable<UINode["events"]>, (...args: unknown[]) => void>>;
15
+
16
+ export { RENDER_BINDING_PROP_NAMES, bindRenderLayerNodeEvents, getRenderLayerBindingProps, normalizeRenderLayerTabsProps, readRenderLayerNodeLayout, resolveRenderLayerNodeProps, resolveRenderLayerValue };
@@ -0,0 +1,264 @@
1
+ // src/nodeLayout.ts
2
+ var toPositiveNumber = (value) => {
3
+ const numeric = Number(value);
4
+ if (!Number.isFinite(numeric) || numeric <= 0) return void 0;
5
+ return numeric;
6
+ };
7
+ var toColSpan = (value) => {
8
+ const numeric = Number(value);
9
+ if (!Number.isInteger(numeric) || numeric < 1 || numeric > 12) return void 0;
10
+ return numeric;
11
+ };
12
+ var toCardSection = (value) => {
13
+ if (value === "header" || value === "body" || value === "footer") return value;
14
+ return void 0;
15
+ };
16
+ var readRenderLayerNodeLayout = (props) => {
17
+ var _a, _b, _c, _d, _e, _f, _g;
18
+ if (!props) return {};
19
+ const rawLayout = props.layout && typeof props.layout === "object" && !Array.isArray(props.layout) ? props.layout : {};
20
+ return {
21
+ widthPct: toPositiveNumber((_a = rawLayout.widthPct) != null ? _a : props.__studioWidthPct),
22
+ heightPct: toPositiveNumber((_b = rawLayout.heightPct) != null ? _b : props.__studioHeightPct),
23
+ heightPx: toPositiveNumber((_c = rawLayout.heightPx) != null ? _c : props.__studioHeightPx),
24
+ colSpan: toColSpan((_d = rawLayout.colSpan) != null ? _d : props.__studioColSpan),
25
+ wrapperClassName: String(
26
+ (_f = (_e = rawLayout.wrapperClassName) != null ? _e : props.__studioWrapperClassName) != null ? _f : ""
27
+ ).trim(),
28
+ cardSection: toCardSection((_g = rawLayout.cardSection) != null ? _g : props.__studioCardSection),
29
+ wizardStep: rawLayout.wizardStep === true ? true : void 0,
30
+ wizardStepRole: rawLayout.wizardStepRole === "title" || rawLayout.wizardStepRole === "description" ? rawLayout.wizardStepRole : void 0,
31
+ wizardContent: rawLayout.wizardContent === true ? true : void 0,
32
+ accordionItem: rawLayout.accordionItem === true ? true : void 0,
33
+ formRole: rawLayout.formRole === "field" || rawLayout.formRole === "title" || rawLayout.formRole === "submit" || rawLayout.formRole === "reset" || rawLayout.formRole === "step" || rawLayout.formRole === "step-content" || rawLayout.formRole === "trigger" || rawLayout.formRole === "content" ? rawLayout.formRole : void 0,
34
+ tabId: typeof rawLayout.tabId === "string" || typeof rawLayout.tabId === "number" ? String(rawLayout.tabId) : void 0
35
+ };
36
+ };
37
+
38
+ // src/normalizeTabsProps.ts
39
+ var normalizeRenderLayerTabsProps = (props) => {
40
+ var _a;
41
+ const rawTabs = Array.isArray(props.tabs) ? props.tabs : [];
42
+ const tabs = rawTabs.length ? rawTabs.map((tab, index) => {
43
+ var _a2, _b, _c;
44
+ const rawTab = tab && typeof tab === "object" && !Array.isArray(tab) ? tab : {};
45
+ const id = String((_a2 = rawTab.id) != null ? _a2 : `tab-${index + 1}`);
46
+ const title = (_c = (_b = rawTab.title) != null ? _b : rawTab.label) != null ? _c : `Tab ${index + 1}`;
47
+ return {
48
+ ...rawTab,
49
+ id,
50
+ title,
51
+ label: title
52
+ };
53
+ }) : [
54
+ {
55
+ id: "overview",
56
+ title: "Overview",
57
+ label: "Overview",
58
+ content: "Overview content"
59
+ }
60
+ ];
61
+ const rawDict = props.dict && typeof props.dict === "object" && !Array.isArray(props.dict) ? props.dict : {};
62
+ const rawUrlSync = props.urlSync && typeof props.urlSync === "object" && !Array.isArray(props.urlSync) ? props.urlSync : null;
63
+ const rawNavigation = (rawUrlSync == null ? void 0 : rawUrlSync.navigation) && typeof rawUrlSync.navigation === "object" && !Array.isArray(rawUrlSync.navigation) ? rawUrlSync.navigation : null;
64
+ const normalizedUrlSync = rawNavigation && typeof rawNavigation.getSearch === "function" && typeof rawNavigation.replaceSearch === "function" ? {
65
+ ...rawUrlSync,
66
+ navigation: rawNavigation
67
+ } : void 0;
68
+ return {
69
+ ...props,
70
+ tabs,
71
+ dict: {
72
+ ...rawDict,
73
+ noContentYet: typeof rawDict.noContentYet === "string" && rawDict.noContentYet.trim().length > 0 ? rawDict.noContentYet : "No content yet"
74
+ },
75
+ defaultActive: typeof props.defaultActive === "string" && props.defaultActive.trim().length > 0 ? props.defaultActive : String((_a = tabs[0].id) != null ? _a : "overview"),
76
+ keepMounted: typeof props.keepMounted === "boolean" ? props.keepMounted : true,
77
+ urlSync: normalizedUrlSync
78
+ };
79
+ };
80
+
81
+ // src/renderBindingProps.ts
82
+ var RENDER_BINDING_PROP_NAMES = /* @__PURE__ */ new Set([
83
+ "bindingEnabled",
84
+ "valueBindingKey",
85
+ "optionsBindingKey",
86
+ "useBindingData",
87
+ "entitiesBindingKey",
88
+ "videoBindingKey",
89
+ "mapBindingKey",
90
+ "productBindingKey",
91
+ "useTranslationKeys",
92
+ "tagContentTranslationKey",
93
+ "tagContentTranslationValue",
94
+ "hrefTranslationKey",
95
+ "hrefTranslationValue",
96
+ "mediaSource",
97
+ "mediaSrc",
98
+ "mediaAlt",
99
+ "rowsBindingKey",
100
+ "columnsBindingKey"
101
+ ]);
102
+ var getRenderLayerBindingProps = (props) => {
103
+ if (!props || typeof props !== "object" || Array.isArray(props)) {
104
+ return {};
105
+ }
106
+ const source = props;
107
+ const embedded = source.__studio && typeof source.__studio === "object" && !Array.isArray(source.__studio) ? source.__studio : {};
108
+ const legacy = Array.from(RENDER_BINDING_PROP_NAMES).reduce(
109
+ (accumulator, key) => {
110
+ if (source[key] !== void 0) {
111
+ accumulator[key] = source[key];
112
+ }
113
+ return accumulator;
114
+ },
115
+ {}
116
+ );
117
+ return Array.from(RENDER_BINDING_PROP_NAMES).reduce((accumulator, key) => {
118
+ if (legacy[key] !== void 0) {
119
+ accumulator[key] = legacy[key];
120
+ } else if (embedded[key] !== void 0) {
121
+ accumulator[key] = embedded[key];
122
+ }
123
+ return accumulator;
124
+ }, {});
125
+ };
126
+
127
+ // src/runtimeBindings.ts
128
+ var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
129
+ var MISSING_TRANSLATION_FALLBACK = "Lorem ipsum dolor sit amet. (fallback)";
130
+ var MISSING_REF_FALLBACK = "Lorem ipsum dolor sit amet. (fallback)";
131
+ var isPlainObject = (value) => {
132
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
133
+ const prototype = Object.getPrototypeOf(value);
134
+ return prototype === Object.prototype || prototype === null;
135
+ };
136
+ var isReactElementLike = (value) => isPlainObject(value) && typeof value.$$typeof === "symbol" && "props" in value;
137
+ var isTranslationValue = (value) => isPlainObject(value) && typeof value.$t === "string";
138
+ var isRefValue = (value) => isPlainObject(value) && typeof value.$ref === "string";
139
+ var isExprValue = (value) => isPlainObject(value) && typeof value.$expr === "string";
140
+ var parseStructuredString = (value) => {
141
+ if (typeof value !== "string") return value;
142
+ const trimmed = value.trim();
143
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return value;
144
+ try {
145
+ const parsed = JSON.parse(trimmed);
146
+ if (!isPlainObject(parsed)) return value;
147
+ if ((typeof parsed.$ref === "string" || typeof parsed.$t === "string") && Object.keys(parsed).length >= 1) {
148
+ return parsed;
149
+ }
150
+ return value;
151
+ } catch {
152
+ return value;
153
+ }
154
+ };
155
+ var resolveRenderLayerValue = (value, runtime) => {
156
+ const normalizedValue = parseStructuredString(value);
157
+ if (isTranslationValue(normalizedValue)) {
158
+ const fallback = typeof normalizedValue.defaultValue === "string" && normalizedValue.defaultValue.trim() ? normalizedValue.defaultValue : normalizedValue.$t;
159
+ if (!runtime.t) return fallback || MISSING_TRANSLATION_FALLBACK;
160
+ const translated = runtime.t(normalizedValue.$t, normalizedValue.values);
161
+ if (typeof translated !== "string") return fallback || MISSING_TRANSLATION_FALLBACK;
162
+ if (!translated.trim()) return fallback || MISSING_TRANSLATION_FALLBACK;
163
+ if (translated === normalizedValue.$t) return fallback || MISSING_TRANSLATION_FALLBACK;
164
+ return translated;
165
+ }
166
+ if (isRefValue(normalizedValue)) {
167
+ const resolved = runtime.get(normalizedValue.$ref);
168
+ if (resolved == null) return MISSING_REF_FALLBACK;
169
+ if (typeof resolved === "string" && !resolved.trim()) return MISSING_REF_FALLBACK;
170
+ return resolved;
171
+ }
172
+ if (isExprValue(normalizedValue)) {
173
+ return void 0;
174
+ }
175
+ if (Array.isArray(normalizedValue)) {
176
+ return normalizedValue.map((item) => resolveRenderLayerValue(item, runtime));
177
+ }
178
+ if (isReactElementLike(normalizedValue)) {
179
+ return normalizedValue;
180
+ }
181
+ if (isPlainObject(normalizedValue)) {
182
+ const next = {};
183
+ Object.entries(normalizedValue).forEach(([key, childValue]) => {
184
+ if (DANGEROUS_KEYS.has(key)) return;
185
+ next[key] = resolveRenderLayerValue(childValue, runtime);
186
+ });
187
+ return next;
188
+ }
189
+ return normalizedValue;
190
+ };
191
+ var resolveRenderLayerNodeProps = (props, runtime) => {
192
+ if (!props) return {};
193
+ const resolved = {};
194
+ Object.entries(props).forEach(([key, value]) => {
195
+ if (DANGEROUS_KEYS.has(key)) return;
196
+ resolved[key] = resolveRenderLayerValue(value, runtime);
197
+ });
198
+ return resolved;
199
+ };
200
+ var bindRenderLayerNodeEvents = (events, runtime) => {
201
+ if (!events) return {};
202
+ const handlers = {};
203
+ const extractDynamicPayload = (eventName, args) => {
204
+ var _a;
205
+ if (eventName !== "onChange") return void 0;
206
+ const firstArg = args[0];
207
+ if (!firstArg || typeof firstArg !== "object" || !("target" in firstArg)) {
208
+ return void 0;
209
+ }
210
+ const target = firstArg.target;
211
+ if (!target) return void 0;
212
+ const isCheckboxType = String((_a = target.type) != null ? _a : "").toLowerCase() === "checkbox";
213
+ const query = isCheckboxType ? target.checked : target.value;
214
+ return {
215
+ query,
216
+ value: target.value,
217
+ checked: target.checked
218
+ };
219
+ };
220
+ Object.entries(events).forEach(([eventName, action]) => {
221
+ if (!action) return;
222
+ handlers[eventName] = (...args) => {
223
+ const actionRef = action;
224
+ const dynamicPayload = extractDynamicPayload(eventName, args);
225
+ if (!dynamicPayload) {
226
+ runtime.dispatch(actionRef);
227
+ return;
228
+ }
229
+ if ("id" in actionRef) {
230
+ const basePayload = actionRef.payload && typeof actionRef.payload === "object" ? actionRef.payload : {};
231
+ runtime.dispatch({
232
+ ...actionRef,
233
+ payload: {
234
+ ...basePayload,
235
+ ...dynamicPayload
236
+ }
237
+ });
238
+ return;
239
+ }
240
+ if ("type" in actionRef) {
241
+ const basePayload = actionRef.payload && typeof actionRef.payload === "object" ? actionRef.payload : {};
242
+ runtime.dispatch({
243
+ ...actionRef,
244
+ payload: {
245
+ ...basePayload,
246
+ ...dynamicPayload
247
+ }
248
+ });
249
+ return;
250
+ }
251
+ runtime.dispatch(actionRef);
252
+ };
253
+ });
254
+ return handlers;
255
+ };
256
+ export {
257
+ RENDER_BINDING_PROP_NAMES,
258
+ bindRenderLayerNodeEvents,
259
+ getRenderLayerBindingProps,
260
+ normalizeRenderLayerTabsProps,
261
+ readRenderLayerNodeLayout,
262
+ resolveRenderLayerNodeProps,
263
+ resolveRenderLayerValue
264
+ };
@@ -0,0 +1 @@
1
+ export { ResolvedNode, ResolvedNodeBase, ResolvedNodeChildDescriptor, ResolvedNodeContentDescriptor, createResolvedNode, createResolvedNodeBase } from '@arkcit/engine-core';
@@ -0,0 +1,9 @@
1
+ // src/resolvedNode.ts
2
+ import {
3
+ createResolvedNode,
4
+ createResolvedNodeBase
5
+ } from "@arkcit/engine-core";
6
+ export {
7
+ createResolvedNode,
8
+ createResolvedNodeBase
9
+ };
@@ -0,0 +1,5 @@
1
+ export { ResolvedNode, ResolvedNodeBase, ResolvedNodeChildDescriptor, ResolvedNodeContentDescriptor, createResolvedNode, createResolvedNodeBase } from '@arkcit/engine-core';
2
+ export { RENDER_BINDING_PROP_NAMES, bindRenderLayerNodeEvents, getRenderLayerBindingProps, normalizeRenderLayerTabsProps, readRenderLayerNodeLayout, resolveRenderLayerNodeProps, resolveRenderLayerValue } from './bindings.js';
3
+ export { AccordionItemDescriptor, ContentCompositionPlan, CoverContentDescriptor, ExpandablePanelContentDescriptor, FinalRenderPlan, NavigationCompositionPlan, RenderDirectivePlan, ResolvedEngineNode, TabsContentDescriptor, resolveAccordionItemDescriptors, resolveChildContentDescriptor, resolveChildDescriptors, resolveContentCompositionPlan, resolveCoverContentDescriptor, resolveExpandablePanelContentDescriptor, resolveFinalRenderPlan, resolveNavigationCompositionPlan, resolveNodeBase, resolveRenderDirectivePlan, resolveResolvedNode, resolveTabsContentDescriptors } from './resolution.js';
4
+ import '@arkcit/engine-schema';
5
+ import '@arkcit/engine-runtime';