@arkcit/engine-react 0.3.8 → 0.3.10

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 CHANGED
@@ -1,51 +1,23 @@
1
- # @arkcit/engine-react
1
+ # arkcit-engine-react
2
2
 
3
- Concrete React renderer package for the Arkcit platform.
3
+ Adapter React du moteur Arkcit.
4
4
 
5
- ## Position In The Flow
5
+ ## Comprendre le projet
6
6
 
7
- ```text
8
- @arkcit/engine-schema
9
- @arkcit/engine-runtime
10
- @arkcit/engine-core
11
- @arkcit/engine-render-layer
12
-
13
- @arkcit/engine-react
14
-
15
- @arkcit/engine
16
-
17
- @arkcit/react-ui
18
- @arkcit/studio preview adapters
19
- @arkcit/docs-shell
20
- ```
7
+ - [SUMMARY.md](docs/SUMMARY.md) - explication courte du projet et liens architecture.
8
+ - [ARCHITECTURE.md](docs/ARCHITECTURE.md) - carte visuelle, couches et flux.
9
+ - [DECOUPLING.md](docs/DECOUPLING.md) - refactor, decouplage, couverture et risques.
10
+ - [ROADMAP.md](docs/ROADMAP.md) - reste-a-faire uniquement.
11
+ - [NOTES.md](docs/NOTES.md) - log nettoyable des acquis et decisions.
12
+ - [ENVIRONNEMENT.md](docs/ENVIRONNEMENT.md) - variables d'environnement detectees.
13
+ - [GOUVERNANCE.md](docs/GOUVERNANCE.md) - maintenance, validation et production.
14
+ - [AGENTS.md](AGENTS.md) - consignes de travail pour Codex et agents.
21
15
 
22
- ## What Goes In
16
+ ## Commandes utiles
23
17
 
24
- - neutral resolved-node contracts
25
- - shared engine primitives
26
- - React-specific rendering needs
27
-
28
- ## What Comes Out
29
-
30
- - React rendering helpers
31
- - React engine root pieces
32
- - React-specific composition/materialization utilities
33
-
34
- ## Responsibilities
35
-
36
- - own React-only rendering behavior
37
- - keep React-specific utilities out of neutral engine layers
38
- - expose reusable React renderer seams to `@arkcit/engine`
39
-
40
- ## Do Not Put Here
41
-
42
- - canonical schema rules
43
- - runtime contract ownership
44
- - Angular or React Native behavior
45
-
46
- ## Main Consumers
47
-
48
- - `@arkcit/engine`
49
- - `@arkcit/react-ui`
50
- - `@arkcit/studio`
51
- - `@arkcit/docs-shell`
18
+ - `npm run typecheck`
19
+ - `npm run test`
20
+ - `npm run test:smoke`
21
+ - `npm run test:unit`
22
+ - `npm run test:coverage`
23
+ - `npm run build`
@@ -219,6 +219,7 @@ var materializeTabsContent = ({
219
219
  renderSafeNode,
220
220
  normalizeRenderableChild
221
221
  }) => tabs.map((tab) => ({
222
+ disabled: Boolean(tab.disabled),
222
223
  id: tab.id,
223
224
  title: tab.title,
224
225
  label: tab.label,
package/dist/engine.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
1
  import React__default from 'react';
3
2
  import { UIEngineProps, UINodeSize, UINodeWrapperProps } from '@arkcit/engine-core';
4
3
  import { UINode } from '@arkcit/engine-schema';
@@ -10,7 +9,7 @@ import '@arkcit/engine-render-layer';
10
9
  type EngineWarningFallbackProps = {
11
10
  message: string;
12
11
  };
13
- declare const EngineWarningFallback: ({ message }: EngineWarningFallbackProps) => react_jsx_runtime.JSX.Element;
12
+ declare const EngineWarningFallback: ({ message }: EngineWarningFallbackProps) => React__default.JSX.Element;
14
13
 
15
14
  type NodeErrorBoundaryProps = {
16
15
  nodeId: string;
@@ -27,7 +26,7 @@ declare class NodeErrorBoundary extends React__default.Component<NodeErrorBounda
27
26
  componentDidUpdate(prevProps: Readonly<{
28
27
  resetToken: string;
29
28
  }>): void;
30
- render(): string | number | bigint | boolean | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
29
+ render(): string | number | bigint | boolean | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | React__default.JSX.Element | null | undefined;
31
30
  }
32
31
 
33
32
  type ReactWebUseUIEngineEffectsArgs = {
@@ -92,6 +91,6 @@ type ReactWebEngineRootDependencies<TInlineEditing = unknown> = {
92
91
  type ReactWebEngineRootArgs<TInlineEditing = unknown> = UIEngineProps & {
93
92
  dependencies: ReactWebEngineRootDependencies<TInlineEditing>;
94
93
  };
95
- declare const ReactWebEngineRoot: <TInlineEditing = unknown>({ schema, registry, store, onNodeClick, onInlineTextEdit, onNodeResize, onNodeResizeStart, onNodeResizeEnd, onNodeDragStart, onNodeDragOverTarget, onNodeDropTarget, selectedNodeId, nodeWrapper, dependencies, }: ReactWebEngineRootArgs<TInlineEditing>) => react_jsx_runtime.JSX.Element;
94
+ declare const ReactWebEngineRoot: <TInlineEditing = unknown>({ schema, registry, store, onNodeClick, onInlineTextEdit, onNodeResize, onNodeResizeStart, onNodeResizeEnd, onNodeDragStart, onNodeDragOverTarget, onNodeDropTarget, selectedNodeId, nodeWrapper, dependencies, }: ReactWebEngineRootArgs<TInlineEditing>) => React__default.JSX.Element;
96
95
 
97
96
  export { EngineWarningFallback, NodeErrorBoundary, ReactWebEngineRoot, type ReactWebEngineRootArgs, type ReactWebEngineRootDependencies, type ReactWebRenderSafeNodeArgs, type ReactWebUseUIEngineEffectsArgs, type ReactWebUseUIEngineEffectsResult };
package/dist/engine.js CHANGED
@@ -58,7 +58,7 @@ var NodeErrorBoundary = class extends React.Component {
58
58
  };
59
59
 
60
60
  // src/engine/ReactWebEngineRoot.tsx
61
- import React4, { useRef, useState as useState2 } from "react";
61
+ import React4, { useEffect, useRef, useState as useState2 } from "react";
62
62
 
63
63
  // src/rendering/resolveResolvedReactNode.ts
64
64
  import {
@@ -387,6 +387,111 @@ var useUIEngineState = () => {
387
387
  };
388
388
  };
389
389
 
390
+ // src/engine/tablistController.ts
391
+ var TAB_ACTION_SELECTOR = '[role="tab"],[data-saas-preview-tab-panel-id]';
392
+ var asHTMLElement = (value) => value instanceof HTMLElement ? value : null;
393
+ var closestHTMLElement = (target, selector) => {
394
+ const element = target;
395
+ return typeof (element == null ? void 0 : element.closest) === "function" ? asHTMLElement(element.closest(selector)) : null;
396
+ };
397
+ var queryByIdOrNodeId = (root, id) => {
398
+ var _a, _b;
399
+ if (!id) return null;
400
+ if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
401
+ const escapedId = CSS.escape(id);
402
+ return asHTMLElement(root.querySelector(`#${escapedId},[data-node-id="${escapedId}"]`));
403
+ }
404
+ return (_b = (_a = Array.from(root.querySelectorAll("[id]")).find((element) => element.id === id)) != null ? _a : Array.from(root.querySelectorAll("[data-node-id]")).find(
405
+ (element) => element.getAttribute("data-node-id") === id
406
+ )) != null ? _b : null;
407
+ };
408
+ var readHashTargetId = (element) => {
409
+ var _a, _b;
410
+ const href = (_b = (_a = element.getAttribute("href")) == null ? void 0 : _a.trim()) != null ? _b : "";
411
+ const hashIndex = href.indexOf("#");
412
+ return hashIndex >= 0 ? href.slice(hashIndex + 1).trim() : "";
413
+ };
414
+ var readTabPanelId = (element) => {
415
+ var _a, _b;
416
+ return ((_a = element.getAttribute("data-saas-preview-tab-panel-id")) == null ? void 0 : _a.trim()) || ((_b = element.getAttribute("aria-controls")) == null ? void 0 : _b.trim()) || readHashTargetId(element);
417
+ };
418
+ var readTabSectionId = (element) => {
419
+ var _a, _b, _c;
420
+ return ((_a = element.getAttribute("data-saas-preview-tab-section-id")) == null ? void 0 : _a.trim()) || ((_c = (_b = element.closest("[data-saas-preview-tab-section-id]")) == null ? void 0 : _b.getAttribute("data-saas-preview-tab-section-id")) == null ? void 0 : _c.trim()) || "";
421
+ };
422
+ var isDisabledTabAction = (element) => element.getAttribute("aria-disabled") === "true" || element instanceof HTMLButtonElement && element.disabled;
423
+ var getTabListTabs = (tabList) => Array.from(tabList.querySelectorAll('[role="tab"]')).filter(
424
+ (tab) => tab.closest('[role="tablist"]') === tabList
425
+ );
426
+ var findMatchingTab = (root, action) => {
427
+ var _a;
428
+ const panelId = readTabPanelId(action);
429
+ if (!panelId) return null;
430
+ const sectionId = readTabSectionId(action);
431
+ return (_a = Array.from(root.querySelectorAll('[role="tab"]')).find((tab) => {
432
+ if (readTabPanelId(tab) !== panelId) return false;
433
+ return !sectionId || readTabSectionId(tab) === sectionId;
434
+ })) != null ? _a : null;
435
+ };
436
+ var applyTablistSelection = (root, activeTab) => {
437
+ const tabList = activeTab.closest('[role="tablist"]');
438
+ if (!tabList || !root.contains(tabList)) return false;
439
+ const tabs = getTabListTabs(tabList);
440
+ if (!tabs.includes(activeTab) || isDisabledTabAction(activeTab)) return true;
441
+ const panelByTab = /* @__PURE__ */ new Map();
442
+ for (const tab of tabs) {
443
+ const panel = queryByIdOrNodeId(root, readTabPanelId(tab));
444
+ if (panel) {
445
+ panelByTab.set(tab, panel);
446
+ }
447
+ }
448
+ const activePanel = panelByTab.get(activeTab);
449
+ if (!activePanel || panelByTab.size === 0) return false;
450
+ for (const tab of tabs) {
451
+ const selected = tab === activeTab;
452
+ tab.setAttribute("aria-selected", selected ? "true" : "false");
453
+ tab.tabIndex = selected ? 0 : -1;
454
+ }
455
+ for (const panel of panelByTab.values()) {
456
+ const selected = panel === activePanel;
457
+ panel.hidden = !selected;
458
+ panel.setAttribute("aria-hidden", selected ? "false" : "true");
459
+ if (selected) {
460
+ panel.style.removeProperty("display");
461
+ } else {
462
+ panel.style.display = "none";
463
+ }
464
+ }
465
+ return true;
466
+ };
467
+ var initializeTablistPanels = (root) => {
468
+ var _a, _b;
469
+ const tabLists = Array.from(root.querySelectorAll('[role="tablist"]'));
470
+ for (const tabList of tabLists) {
471
+ const tabs = getTabListTabs(tabList);
472
+ const activeTab = (_b = (_a = tabs.find(
473
+ (tab) => tab.getAttribute("aria-selected") === "true" && !isDisabledTabAction(tab)
474
+ )) != null ? _a : tabs.find((tab) => !isDisabledTabAction(tab))) != null ? _b : null;
475
+ if (activeTab) {
476
+ applyTablistSelection(root, activeTab);
477
+ }
478
+ }
479
+ };
480
+ var bindTablistController = (root) => {
481
+ initializeTablistPanels(root);
482
+ const handleClick = (event) => {
483
+ const action = closestHTMLElement(event.target, TAB_ACTION_SELECTOR);
484
+ if (!action || !root.contains(action) || isDisabledTabAction(action)) return;
485
+ const tab = action.getAttribute("role") === "tab" ? action : findMatchingTab(root, action);
486
+ if (!tab || !applyTablistSelection(root, tab)) return;
487
+ event.preventDefault();
488
+ };
489
+ root.addEventListener("click", handleClick, true);
490
+ return () => {
491
+ root.removeEventListener("click", handleClick, true);
492
+ };
493
+ };
494
+
390
495
  // src/engine/ReactWebEngineRoot.tsx
391
496
  import { jsx as jsx6 } from "react/jsx-runtime";
392
497
  var INTERNAL_STUDIO_NODE_TYPES = /* @__PURE__ */ new Set();
@@ -494,6 +599,7 @@ var ReactWebEngineRoot = ({
494
599
  const resizeStateRef = useRef(null);
495
600
  const pinchStateRef = useRef(null);
496
601
  const pendingFieldFocusRef = useRef(null);
602
+ const rootRef = useRef(null);
497
603
  const { ancestorTypeMembership, overlaysByNodeId, captureFieldFocus } = dependencies.useUIEngineEffects({
498
604
  schema,
499
605
  runtime,
@@ -558,7 +664,19 @@ var ReactWebEngineRoot = ({
558
664
  InlineTextEditorComponent: InlineTextEditor,
559
665
  isFormInteractiveTarget
560
666
  });
561
- return /* @__PURE__ */ jsx6("div", { className: "flex h-full min-w-0 w-full flex-wrap content-start items-start gap-3", children: schema.nodes.map((node) => /* @__PURE__ */ jsx6(React4.Fragment, { children: renderSafeEngineNode(node) }, node.id)) });
667
+ useEffect(() => {
668
+ const root = rootRef.current;
669
+ return root ? bindTablistController(root) : void 0;
670
+ }, [schema]);
671
+ return /* @__PURE__ */ jsx6(
672
+ "div",
673
+ {
674
+ ref: rootRef,
675
+ "data-arkcit-engine-root": true,
676
+ className: "flex min-w-0 w-full flex-wrap content-start items-start gap-3",
677
+ children: schema.nodes.map((node) => /* @__PURE__ */ jsx6(React4.Fragment, { children: renderSafeEngineNode(node) }, node.id))
678
+ }
679
+ );
562
680
  };
563
681
 
564
682
  // src/engine/StaticReactWebEngineRoot.tsx
@@ -653,7 +771,14 @@ var StaticReactWebEngineRoot = ({
653
771
  NodeWrapper,
654
772
  isFormInteractiveTarget: isFormInteractiveTarget2
655
773
  });
656
- return /* @__PURE__ */ jsx7("div", { className: "flex h-full min-w-0 w-full flex-wrap content-start items-start gap-3", children: schema.nodes.map((node) => /* @__PURE__ */ jsx7(React5.Fragment, { children: renderSafeEngineNode(node) }, node.id)) });
774
+ return /* @__PURE__ */ jsx7(
775
+ "div",
776
+ {
777
+ "data-arkcit-engine-root": true,
778
+ className: "flex min-w-0 w-full flex-wrap content-start items-start gap-3",
779
+ children: schema.nodes.map((node) => /* @__PURE__ */ jsx7(React5.Fragment, { children: renderSafeEngineNode(node) }, node.id))
780
+ }
781
+ );
657
782
  };
658
783
  export {
659
784
  EngineWarningFallback_default as EngineWarningFallback,
package/dist/index.d.ts CHANGED
@@ -11,4 +11,3 @@ export { ComponentRegistry, ComponentRegistryEntry } from '@arkcit/engine-core';
11
11
  import 'react';
12
12
  import '@arkcit/engine-schema';
13
13
  import '@arkcit/engine-runtime';
14
- import 'react/jsx-runtime';
package/dist/index.js CHANGED
@@ -219,6 +219,7 @@ var materializeTabsContent = ({
219
219
  renderSafeNode,
220
220
  normalizeRenderableChild: normalizeRenderableChild2
221
221
  }) => tabs.map((tab) => ({
222
+ disabled: Boolean(tab.disabled),
222
223
  id: tab.id,
223
224
  title: tab.title,
224
225
  label: tab.label,
@@ -435,7 +436,7 @@ var NodeErrorBoundary = class extends React4.Component {
435
436
  };
436
437
 
437
438
  // src/engine/ReactWebEngineRoot.tsx
438
- import React7, { useRef, useState as useState2 } from "react";
439
+ import React7, { useEffect, useRef, useState as useState2 } from "react";
439
440
 
440
441
  // src/rendering/resolveResolvedReactNode.ts
441
442
  import {
@@ -764,6 +765,111 @@ var useUIEngineState = () => {
764
765
  };
765
766
  };
766
767
 
768
+ // src/engine/tablistController.ts
769
+ var TAB_ACTION_SELECTOR = '[role="tab"],[data-saas-preview-tab-panel-id]';
770
+ var asHTMLElement = (value) => value instanceof HTMLElement ? value : null;
771
+ var closestHTMLElement = (target, selector) => {
772
+ const element = target;
773
+ return typeof (element == null ? void 0 : element.closest) === "function" ? asHTMLElement(element.closest(selector)) : null;
774
+ };
775
+ var queryByIdOrNodeId = (root, id) => {
776
+ var _a, _b;
777
+ if (!id) return null;
778
+ if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
779
+ const escapedId = CSS.escape(id);
780
+ return asHTMLElement(root.querySelector(`#${escapedId},[data-node-id="${escapedId}"]`));
781
+ }
782
+ return (_b = (_a = Array.from(root.querySelectorAll("[id]")).find((element) => element.id === id)) != null ? _a : Array.from(root.querySelectorAll("[data-node-id]")).find(
783
+ (element) => element.getAttribute("data-node-id") === id
784
+ )) != null ? _b : null;
785
+ };
786
+ var readHashTargetId = (element) => {
787
+ var _a, _b;
788
+ const href = (_b = (_a = element.getAttribute("href")) == null ? void 0 : _a.trim()) != null ? _b : "";
789
+ const hashIndex = href.indexOf("#");
790
+ return hashIndex >= 0 ? href.slice(hashIndex + 1).trim() : "";
791
+ };
792
+ var readTabPanelId = (element) => {
793
+ var _a, _b;
794
+ return ((_a = element.getAttribute("data-saas-preview-tab-panel-id")) == null ? void 0 : _a.trim()) || ((_b = element.getAttribute("aria-controls")) == null ? void 0 : _b.trim()) || readHashTargetId(element);
795
+ };
796
+ var readTabSectionId = (element) => {
797
+ var _a, _b, _c;
798
+ return ((_a = element.getAttribute("data-saas-preview-tab-section-id")) == null ? void 0 : _a.trim()) || ((_c = (_b = element.closest("[data-saas-preview-tab-section-id]")) == null ? void 0 : _b.getAttribute("data-saas-preview-tab-section-id")) == null ? void 0 : _c.trim()) || "";
799
+ };
800
+ var isDisabledTabAction = (element) => element.getAttribute("aria-disabled") === "true" || element instanceof HTMLButtonElement && element.disabled;
801
+ var getTabListTabs = (tabList) => Array.from(tabList.querySelectorAll('[role="tab"]')).filter(
802
+ (tab) => tab.closest('[role="tablist"]') === tabList
803
+ );
804
+ var findMatchingTab = (root, action) => {
805
+ var _a;
806
+ const panelId = readTabPanelId(action);
807
+ if (!panelId) return null;
808
+ const sectionId = readTabSectionId(action);
809
+ return (_a = Array.from(root.querySelectorAll('[role="tab"]')).find((tab) => {
810
+ if (readTabPanelId(tab) !== panelId) return false;
811
+ return !sectionId || readTabSectionId(tab) === sectionId;
812
+ })) != null ? _a : null;
813
+ };
814
+ var applyTablistSelection = (root, activeTab) => {
815
+ const tabList = activeTab.closest('[role="tablist"]');
816
+ if (!tabList || !root.contains(tabList)) return false;
817
+ const tabs = getTabListTabs(tabList);
818
+ if (!tabs.includes(activeTab) || isDisabledTabAction(activeTab)) return true;
819
+ const panelByTab = /* @__PURE__ */ new Map();
820
+ for (const tab of tabs) {
821
+ const panel = queryByIdOrNodeId(root, readTabPanelId(tab));
822
+ if (panel) {
823
+ panelByTab.set(tab, panel);
824
+ }
825
+ }
826
+ const activePanel = panelByTab.get(activeTab);
827
+ if (!activePanel || panelByTab.size === 0) return false;
828
+ for (const tab of tabs) {
829
+ const selected = tab === activeTab;
830
+ tab.setAttribute("aria-selected", selected ? "true" : "false");
831
+ tab.tabIndex = selected ? 0 : -1;
832
+ }
833
+ for (const panel of panelByTab.values()) {
834
+ const selected = panel === activePanel;
835
+ panel.hidden = !selected;
836
+ panel.setAttribute("aria-hidden", selected ? "false" : "true");
837
+ if (selected) {
838
+ panel.style.removeProperty("display");
839
+ } else {
840
+ panel.style.display = "none";
841
+ }
842
+ }
843
+ return true;
844
+ };
845
+ var initializeTablistPanels = (root) => {
846
+ var _a, _b;
847
+ const tabLists = Array.from(root.querySelectorAll('[role="tablist"]'));
848
+ for (const tabList of tabLists) {
849
+ const tabs = getTabListTabs(tabList);
850
+ const activeTab = (_b = (_a = tabs.find(
851
+ (tab) => tab.getAttribute("aria-selected") === "true" && !isDisabledTabAction(tab)
852
+ )) != null ? _a : tabs.find((tab) => !isDisabledTabAction(tab))) != null ? _b : null;
853
+ if (activeTab) {
854
+ applyTablistSelection(root, activeTab);
855
+ }
856
+ }
857
+ };
858
+ var bindTablistController = (root) => {
859
+ initializeTablistPanels(root);
860
+ const handleClick = (event) => {
861
+ const action = closestHTMLElement(event.target, TAB_ACTION_SELECTOR);
862
+ if (!action || !root.contains(action) || isDisabledTabAction(action)) return;
863
+ const tab = action.getAttribute("role") === "tab" ? action : findMatchingTab(root, action);
864
+ if (!tab || !applyTablistSelection(root, tab)) return;
865
+ event.preventDefault();
866
+ };
867
+ root.addEventListener("click", handleClick, true);
868
+ return () => {
869
+ root.removeEventListener("click", handleClick, true);
870
+ };
871
+ };
872
+
767
873
  // src/engine/ReactWebEngineRoot.tsx
768
874
  import { jsx as jsx9 } from "react/jsx-runtime";
769
875
  var INTERNAL_STUDIO_NODE_TYPES = /* @__PURE__ */ new Set();
@@ -871,6 +977,7 @@ var ReactWebEngineRoot = ({
871
977
  const resizeStateRef = useRef(null);
872
978
  const pinchStateRef = useRef(null);
873
979
  const pendingFieldFocusRef = useRef(null);
980
+ const rootRef = useRef(null);
874
981
  const { ancestorTypeMembership, overlaysByNodeId, captureFieldFocus } = dependencies.useUIEngineEffects({
875
982
  schema,
876
983
  runtime,
@@ -935,7 +1042,19 @@ var ReactWebEngineRoot = ({
935
1042
  InlineTextEditorComponent: InlineTextEditor,
936
1043
  isFormInteractiveTarget
937
1044
  });
938
- return /* @__PURE__ */ jsx9("div", { className: "flex h-full min-w-0 w-full flex-wrap content-start items-start gap-3", children: schema.nodes.map((node) => /* @__PURE__ */ jsx9(React7.Fragment, { children: renderSafeEngineNode(node) }, node.id)) });
1045
+ useEffect(() => {
1046
+ const root = rootRef.current;
1047
+ return root ? bindTablistController(root) : void 0;
1048
+ }, [schema]);
1049
+ return /* @__PURE__ */ jsx9(
1050
+ "div",
1051
+ {
1052
+ ref: rootRef,
1053
+ "data-arkcit-engine-root": true,
1054
+ className: "flex min-w-0 w-full flex-wrap content-start items-start gap-3",
1055
+ children: schema.nodes.map((node) => /* @__PURE__ */ jsx9(React7.Fragment, { children: renderSafeEngineNode(node) }, node.id))
1056
+ }
1057
+ );
939
1058
  };
940
1059
 
941
1060
  // src/engine/StaticReactWebEngineRoot.tsx
@@ -1030,11 +1149,18 @@ var StaticReactWebEngineRoot = ({
1030
1149
  NodeWrapper,
1031
1150
  isFormInteractiveTarget: isFormInteractiveTarget2
1032
1151
  });
1033
- return /* @__PURE__ */ jsx10("div", { className: "flex h-full min-w-0 w-full flex-wrap content-start items-start gap-3", children: schema.nodes.map((node) => /* @__PURE__ */ jsx10(React8.Fragment, { children: renderSafeEngineNode(node) }, node.id)) });
1152
+ return /* @__PURE__ */ jsx10(
1153
+ "div",
1154
+ {
1155
+ "data-arkcit-engine-root": true,
1156
+ className: "flex min-w-0 w-full flex-wrap content-start items-start gap-3",
1157
+ children: schema.nodes.map((node) => /* @__PURE__ */ jsx10(React8.Fragment, { children: renderSafeEngineNode(node) }, node.id))
1158
+ }
1159
+ );
1034
1160
  };
1035
1161
 
1036
1162
  // src/hooks/useUIEngineEffects.ts
1037
- import { useEffect, useLayoutEffect, useMemo } from "react";
1163
+ import { useEffect as useEffect2, useLayoutEffect, useMemo } from "react";
1038
1164
  var useUIEngineEffects = ({
1039
1165
  schema,
1040
1166
  runtime,
@@ -1062,13 +1188,13 @@ var useUIEngineEffects = ({
1062
1188
  pendingFieldFocusRef.current = null;
1063
1189
  dependencies.restorePendingFieldFocus(pending);
1064
1190
  }, [dependencies, schema, pendingFieldFocusRef]);
1065
- useEffect(() => {
1191
+ useEffect2(() => {
1066
1192
  if (!runtime.subscribe) return;
1067
1193
  return runtime.subscribe(() => {
1068
1194
  setVersion((previous) => previous + 1);
1069
1195
  });
1070
1196
  }, [runtime, setVersion]);
1071
- useEffect(() => {
1197
+ useEffect2(() => {
1072
1198
  if (!onNodeResize) return;
1073
1199
  const handleMouseMove = (event) => {
1074
1200
  const state = resizeStateRef.current;
@@ -1344,7 +1470,10 @@ import React12 from "react";
1344
1470
  var configurePreviewLinkBehavior = (componentProps) => {
1345
1471
  const existingOnClick = componentProps.onClick;
1346
1472
  componentProps.onClick = (event) => {
1347
- event.preventDefault();
1473
+ const href = componentProps.href;
1474
+ if (!(typeof href === "string" && href.startsWith("#"))) {
1475
+ event.preventDefault();
1476
+ }
1348
1477
  if (typeof existingOnClick === "function") {
1349
1478
  existingOnClick(event);
1350
1479
  }
@@ -1352,6 +1481,7 @@ var configurePreviewLinkBehavior = (componentProps) => {
1352
1481
  };
1353
1482
 
1354
1483
  // src/rendering/finalizeRenderedNode.tsx
1484
+ import { jsx as jsx13 } from "react/jsx-runtime";
1355
1485
  var RESPONSIVE_MEDIA_NODE_TYPES = /* @__PURE__ */ new Set(["Image", "Video", "EmbeddedVideo", "Cover", "Document"]);
1356
1486
  var RESPONSIVE_MEDIA_CLASS_NAME = "max-md:!w-full max-md:![flex-basis:100%!important] max-md:!h-auto";
1357
1487
  var RESPONSIVE_TEXT_NODE_TYPES = /* @__PURE__ */ new Set([
@@ -1394,6 +1524,45 @@ var RESPONSIVE_TEXT_CLASS_NAME = "max-md:!h-auto";
1394
1524
  var hasExplicitStudioSizing = (studioSizing) => Boolean(
1395
1525
  studioSizing && (studioSizing.widthPct !== null || studioSizing.heightPct !== null || studioSizing.heightPx !== null)
1396
1526
  );
1527
+ var buildExplicitSizingWrapperStyle = (studioSizing) => {
1528
+ if (!hasExplicitStudioSizing(studioSizing)) return null;
1529
+ return __spreadValues(__spreadValues(__spreadValues({}, (studioSizing == null ? void 0 : studioSizing.widthPct) !== null && (studioSizing == null ? void 0 : studioSizing.widthPct) !== void 0 ? {
1530
+ width: `${studioSizing.widthPct}%`,
1531
+ flexBasis: `${studioSizing.widthPct}%`,
1532
+ maxWidth: "100%"
1533
+ } : {}), (studioSizing == null ? void 0 : studioSizing.heightPx) !== null && (studioSizing == null ? void 0 : studioSizing.heightPx) !== void 0 ? { height: `${studioSizing.heightPx}px` } : {}), (studioSizing == null ? void 0 : studioSizing.heightPx) === null && (studioSizing == null ? void 0 : studioSizing.heightPct) !== null && (studioSizing == null ? void 0 : studioSizing.heightPct) !== void 0 ? { height: `${studioSizing.heightPct}%` } : {});
1534
+ };
1535
+ var removeExplicitSizingFromComponentStyle = (componentProps, shouldWrapExplicitSizing) => {
1536
+ if (!shouldWrapExplicitSizing) return;
1537
+ const currentStyle = componentProps.style && typeof componentProps.style === "object" ? __spreadValues({}, componentProps.style) : null;
1538
+ if (!currentStyle) return;
1539
+ delete currentStyle.width;
1540
+ delete currentStyle.flexBasis;
1541
+ delete currentStyle.maxWidth;
1542
+ delete currentStyle.height;
1543
+ componentProps.style = currentStyle;
1544
+ };
1545
+ var removeInternalLayoutProps = (componentProps) => {
1546
+ delete componentProps.fullBleed;
1547
+ };
1548
+ var wrapExplicitlySizedNode = ({
1549
+ nodeId,
1550
+ nodeType,
1551
+ element,
1552
+ wrapperStyle
1553
+ }) => {
1554
+ if (!wrapperStyle) return element;
1555
+ return /* @__PURE__ */ jsx13(
1556
+ "div",
1557
+ {
1558
+ "data-node-id": nodeId,
1559
+ "data-node-type": nodeType,
1560
+ className: "relative min-w-0 max-w-full flex-none",
1561
+ style: wrapperStyle,
1562
+ children: /* @__PURE__ */ jsx13("div", { className: "min-w-0 max-w-full", style: { width: "100%", height: "100%" }, children: element })
1563
+ }
1564
+ );
1565
+ };
1397
1566
  var finalizeRenderedNode = ({
1398
1567
  node,
1399
1568
  children,
@@ -1436,6 +1605,9 @@ var finalizeRenderedNode = ({
1436
1605
  delete componentProps.layout;
1437
1606
  }
1438
1607
  }
1608
+ const explicitSizingWrapperStyle = !isStudioRendererContext ? buildExplicitSizingWrapperStyle(studioSizing) : null;
1609
+ removeExplicitSizingFromComponentStyle(componentProps, explicitSizingWrapperStyle !== null);
1610
+ removeInternalLayoutProps(componentProps);
1439
1611
  if (plans.some((plan) => plan.kind === "table-fallback")) {
1440
1612
  const tableNode = materializeBoundTable({
1441
1613
  nodeId: node.id,
@@ -1444,17 +1616,31 @@ var finalizeRenderedNode = ({
1444
1616
  TableComponent: registryComponent
1445
1617
  });
1446
1618
  if (tableNode) {
1447
- return tableNode;
1619
+ return wrapExplicitlySizedNode({
1620
+ nodeId: node.id,
1621
+ nodeType: node.type,
1622
+ element: tableNode,
1623
+ wrapperStyle: explicitSizingWrapperStyle
1624
+ });
1448
1625
  }
1449
1626
  }
1450
1627
  const finalComponentProps = dependencies.omitStudioProps(componentProps);
1628
+ let renderedElement;
1451
1629
  if (children && children.length > 0) {
1452
1630
  if (node.type === "Cover" || node.type === "Tabs" || node.type === "Accordion") {
1453
- return React12.createElement(registryComponent, finalComponentProps);
1631
+ renderedElement = React12.createElement(registryComponent, finalComponentProps);
1632
+ } else {
1633
+ renderedElement = React12.createElement(registryComponent, finalComponentProps, children);
1454
1634
  }
1455
- return React12.createElement(registryComponent, finalComponentProps, children);
1635
+ } else {
1636
+ renderedElement = React12.createElement(registryComponent, finalComponentProps);
1456
1637
  }
1457
- return React12.createElement(registryComponent, finalComponentProps);
1638
+ return wrapExplicitlySizedNode({
1639
+ nodeId: node.id,
1640
+ nodeType: node.type,
1641
+ element: renderedElement,
1642
+ wrapperStyle: explicitSizingWrapperStyle
1643
+ });
1458
1644
  };
1459
1645
 
1460
1646
  // src/rendering/nodeResetToken.ts
@@ -1487,7 +1673,7 @@ var buildNodeResetToken = (node) => {
1487
1673
 
1488
1674
  // src/rendering/renderChildren.tsx
1489
1675
  import React13, { isValidElement as isValidElement2 } from "react";
1490
- import { jsx as jsx13 } from "react/jsx-runtime";
1676
+ import { jsx as jsx14 } from "react/jsx-runtime";
1491
1677
  var toBoolean = (value) => {
1492
1678
  if (typeof value === "boolean") return value;
1493
1679
  if (typeof value === "string") return value.toLowerCase() === "true";
@@ -1498,7 +1684,7 @@ var normalizeRenderableChild = (value) => {
1498
1684
  if (typeof value === "string" || typeof value === "number") return value;
1499
1685
  if (isValidElement2(value)) return value;
1500
1686
  if (Array.isArray(value)) {
1501
- return value.map((item, index) => /* @__PURE__ */ jsx13(React13.Fragment, { children: normalizeRenderableChild(item) }, `normalized-child-${index}`));
1687
+ return value.map((item, index) => /* @__PURE__ */ jsx14(React13.Fragment, { children: normalizeRenderableChild(item) }, `normalized-child-${index}`));
1502
1688
  }
1503
1689
  if (value && typeof value === "object") {
1504
1690
  const candidate = value;
@@ -1,4 +1,3 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
1
  import React__default from 'react';
3
2
  import { UINode } from '@arkcit/engine-schema';
4
3
  import { UIRuntimeAdapter } from '@arkcit/engine-runtime';
@@ -13,15 +12,15 @@ declare const buildCoverContent: ({ rawChildren, renderSafeNode, }: {
13
12
  rawChildren: UINode[];
14
13
  renderSafeNode: (node: UINode) => React__default.ReactNode;
15
14
  }) => {
16
- actions: react_jsx_runtime.JSX.Element | undefined;
17
- children: react_jsx_runtime.JSX.Element | null;
15
+ actions: React__default.JSX.Element | undefined;
16
+ children: React__default.JSX.Element | null;
18
17
  };
19
18
  declare const buildScrollRevealChildren: ({ rawChildren, renderSafeNode, normalizeRenderableChild, fallbackChildren, }: {
20
19
  rawChildren: UINode[];
21
20
  renderSafeNode: (node: UINode) => React__default.ReactNode;
22
21
  normalizeRenderableChild: (value: unknown) => React__default.ReactNode;
23
22
  fallbackChildren: unknown;
24
- }) => string | number | bigint | boolean | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
23
+ }) => string | number | bigint | boolean | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | React__default.JSX.Element | null | undefined;
25
24
 
26
25
  type TableComponentShape = {
27
26
  Header: React__default.ComponentType<Record<string, unknown>>;
@@ -46,8 +45,8 @@ declare const materializeCoverContent: ({ descriptor, renderSafeNode, }: {
46
45
  descriptor: CoverContentDescriptor;
47
46
  renderSafeNode: (node: UINode) => React__default.ReactNode;
48
47
  }) => {
49
- actions: react_jsx_runtime.JSX.Element | undefined;
50
- children: react_jsx_runtime.JSX.Element | null;
48
+ actions: React__default.JSX.Element | undefined;
49
+ children: React__default.JSX.Element | null;
51
50
  };
52
51
 
53
52
  declare const materializeAccordionItems: ({ items, renderSafeNode, }: {
@@ -57,30 +56,31 @@ declare const materializeAccordionItems: ({ items, renderSafeNode, }: {
57
56
  id: string;
58
57
  title: string;
59
58
  disabled: boolean;
60
- content: string | react_jsx_runtime.JSX.Element;
59
+ content: string | React__default.JSX.Element;
61
60
  }[];
62
61
  declare const materializeExpandablePanelContent: ({ descriptor, renderSafeNode, }: {
63
62
  descriptor: ExpandablePanelContentDescriptor;
64
63
  renderSafeNode: (node: UINode) => React__default.ReactNode;
65
- }) => react_jsx_runtime.JSX.Element | undefined;
64
+ }) => React__default.JSX.Element | undefined;
66
65
 
67
66
  declare const materializeTabsContent: ({ tabs, renderSafeNode, normalizeRenderableChild, }: {
68
67
  tabs: TabsContentDescriptor[];
69
68
  renderSafeNode: (node: UINode) => React__default.ReactNode;
70
69
  normalizeRenderableChild: (value: unknown) => React__default.ReactNode;
71
70
  }) => {
71
+ disabled: boolean;
72
72
  id: string;
73
73
  title: unknown;
74
74
  label: unknown;
75
- content: string | number | bigint | boolean | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element[] | null | undefined;
75
+ content: string | number | bigint | boolean | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | React__default.JSX.Element[] | null | undefined;
76
76
  }[];
77
77
 
78
78
  declare const buildAccordionItems: (accordionChildren: UINode[], renderSafeNode: (node: UINode) => React__default.ReactNode) => {
79
79
  id: string;
80
80
  title: string;
81
- content: string | react_jsx_runtime.JSX.Element;
81
+ content: string | React__default.JSX.Element;
82
82
  disabled: boolean;
83
83
  }[];
84
- declare const buildExpandablePanelChildren: (panelChildren: UINode[], renderSafeNode: (node: UINode) => React__default.ReactNode) => react_jsx_runtime.JSX.Element | undefined;
84
+ declare const buildExpandablePanelChildren: (panelChildren: UINode[], renderSafeNode: (node: UINode) => React__default.ReactNode) => React__default.JSX.Element | undefined;
85
85
 
86
86
  export { buildAccordionItems, buildCoverContent, buildCoverMedia, buildExpandablePanelChildren, buildScrollRevealChildren, materializeAccordionItems, materializeBoundTable, materializeChildContent, materializeCoverContent, materializeExpandablePanelContent, materializeTabsContent };
@@ -342,6 +342,7 @@ var materializeTabsContent = ({
342
342
  renderSafeNode,
343
343
  normalizeRenderableChild
344
344
  }) => tabs.map((tab) => ({
345
+ disabled: Boolean(tab.disabled),
345
346
  id: tab.id,
346
347
  title: tab.title,
347
348
  label: tab.label,
@@ -2,7 +2,6 @@ import React__default from 'react';
2
2
  import { FinalRenderPlan, ResolvedNodeChildDescriptor, ResolvedNodeContentDescriptor, ResolvedNodeBase, ResolvedEngineNode } from '@arkcit/engine-render-layer';
3
3
  import { UINode } from '@arkcit/engine-schema';
4
4
  import { UIRuntimeAdapter } from '@arkcit/engine-runtime';
5
- import * as react_jsx_runtime from 'react/jsx-runtime';
6
5
  export { R as RenderReactNodeArgs, a as RenderReactNodeDependencies, r as renderReactNode } from './renderReactNode-xwA81IXf.js';
7
6
  import { ComponentRegistry } from '@arkcit/engine-core';
8
7
 
@@ -56,7 +55,7 @@ type PrepareRenderableChildrenArgs = {
56
55
  childContentDescriptor?: ResolvedNodeContentDescriptor;
57
56
  };
58
57
  declare const prepareRenderableChildren: ({ node, internalStudioNodeTypes, renderSafeNode, childDescriptors, childContentDescriptor, }: PrepareRenderableChildrenArgs) => {
59
- children: react_jsx_runtime.JSX.Element[];
58
+ children: React__default.JSX.Element[];
60
59
  resolvedChildContent: React__default.ReactNode;
61
60
  childDescriptors: ResolvedNodeChildDescriptor[];
62
61
  childContentDescriptor: ResolvedNodeContentDescriptor;
package/dist/rendering.js CHANGED
@@ -25,7 +25,10 @@ import React2 from "react";
25
25
  var configurePreviewLinkBehavior = (componentProps) => {
26
26
  const existingOnClick = componentProps.onClick;
27
27
  componentProps.onClick = (event) => {
28
- event.preventDefault();
28
+ const href = componentProps.href;
29
+ if (!(typeof href === "string" && href.startsWith("#"))) {
30
+ event.preventDefault();
31
+ }
29
32
  if (typeof existingOnClick === "function") {
30
33
  existingOnClick(event);
31
34
  }
@@ -174,6 +177,7 @@ var materializeBoundTable = ({
174
177
  };
175
178
 
176
179
  // src/rendering/finalizeRenderedNode.tsx
180
+ import { jsx } from "react/jsx-runtime";
177
181
  var RESPONSIVE_MEDIA_NODE_TYPES = /* @__PURE__ */ new Set(["Image", "Video", "EmbeddedVideo", "Cover", "Document"]);
178
182
  var RESPONSIVE_MEDIA_CLASS_NAME = "max-md:!w-full max-md:![flex-basis:100%!important] max-md:!h-auto";
179
183
  var RESPONSIVE_TEXT_NODE_TYPES = /* @__PURE__ */ new Set([
@@ -216,6 +220,45 @@ var RESPONSIVE_TEXT_CLASS_NAME = "max-md:!h-auto";
216
220
  var hasExplicitStudioSizing = (studioSizing) => Boolean(
217
221
  studioSizing && (studioSizing.widthPct !== null || studioSizing.heightPct !== null || studioSizing.heightPx !== null)
218
222
  );
223
+ var buildExplicitSizingWrapperStyle = (studioSizing) => {
224
+ if (!hasExplicitStudioSizing(studioSizing)) return null;
225
+ return __spreadValues(__spreadValues(__spreadValues({}, (studioSizing == null ? void 0 : studioSizing.widthPct) !== null && (studioSizing == null ? void 0 : studioSizing.widthPct) !== void 0 ? {
226
+ width: `${studioSizing.widthPct}%`,
227
+ flexBasis: `${studioSizing.widthPct}%`,
228
+ maxWidth: "100%"
229
+ } : {}), (studioSizing == null ? void 0 : studioSizing.heightPx) !== null && (studioSizing == null ? void 0 : studioSizing.heightPx) !== void 0 ? { height: `${studioSizing.heightPx}px` } : {}), (studioSizing == null ? void 0 : studioSizing.heightPx) === null && (studioSizing == null ? void 0 : studioSizing.heightPct) !== null && (studioSizing == null ? void 0 : studioSizing.heightPct) !== void 0 ? { height: `${studioSizing.heightPct}%` } : {});
230
+ };
231
+ var removeExplicitSizingFromComponentStyle = (componentProps, shouldWrapExplicitSizing) => {
232
+ if (!shouldWrapExplicitSizing) return;
233
+ const currentStyle = componentProps.style && typeof componentProps.style === "object" ? __spreadValues({}, componentProps.style) : null;
234
+ if (!currentStyle) return;
235
+ delete currentStyle.width;
236
+ delete currentStyle.flexBasis;
237
+ delete currentStyle.maxWidth;
238
+ delete currentStyle.height;
239
+ componentProps.style = currentStyle;
240
+ };
241
+ var removeInternalLayoutProps = (componentProps) => {
242
+ delete componentProps.fullBleed;
243
+ };
244
+ var wrapExplicitlySizedNode = ({
245
+ nodeId,
246
+ nodeType,
247
+ element,
248
+ wrapperStyle
249
+ }) => {
250
+ if (!wrapperStyle) return element;
251
+ return /* @__PURE__ */ jsx(
252
+ "div",
253
+ {
254
+ "data-node-id": nodeId,
255
+ "data-node-type": nodeType,
256
+ className: "relative min-w-0 max-w-full flex-none",
257
+ style: wrapperStyle,
258
+ children: /* @__PURE__ */ jsx("div", { className: "min-w-0 max-w-full", style: { width: "100%", height: "100%" }, children: element })
259
+ }
260
+ );
261
+ };
219
262
  var finalizeRenderedNode = ({
220
263
  node,
221
264
  children,
@@ -258,6 +301,9 @@ var finalizeRenderedNode = ({
258
301
  delete componentProps.layout;
259
302
  }
260
303
  }
304
+ const explicitSizingWrapperStyle = !isStudioRendererContext ? buildExplicitSizingWrapperStyle(studioSizing) : null;
305
+ removeExplicitSizingFromComponentStyle(componentProps, explicitSizingWrapperStyle !== null);
306
+ removeInternalLayoutProps(componentProps);
261
307
  if (plans.some((plan) => plan.kind === "table-fallback")) {
262
308
  const tableNode = materializeBoundTable({
263
309
  nodeId: node.id,
@@ -266,17 +312,31 @@ var finalizeRenderedNode = ({
266
312
  TableComponent: registryComponent
267
313
  });
268
314
  if (tableNode) {
269
- return tableNode;
315
+ return wrapExplicitlySizedNode({
316
+ nodeId: node.id,
317
+ nodeType: node.type,
318
+ element: tableNode,
319
+ wrapperStyle: explicitSizingWrapperStyle
320
+ });
270
321
  }
271
322
  }
272
323
  const finalComponentProps = dependencies.omitStudioProps(componentProps);
324
+ let renderedElement;
273
325
  if (children && children.length > 0) {
274
326
  if (node.type === "Cover" || node.type === "Tabs" || node.type === "Accordion") {
275
- return React2.createElement(registryComponent, finalComponentProps);
327
+ renderedElement = React2.createElement(registryComponent, finalComponentProps);
328
+ } else {
329
+ renderedElement = React2.createElement(registryComponent, finalComponentProps, children);
276
330
  }
277
- return React2.createElement(registryComponent, finalComponentProps, children);
331
+ } else {
332
+ renderedElement = React2.createElement(registryComponent, finalComponentProps);
278
333
  }
279
- return React2.createElement(registryComponent, finalComponentProps);
334
+ return wrapExplicitlySizedNode({
335
+ nodeId: node.id,
336
+ nodeType: node.type,
337
+ element: renderedElement,
338
+ wrapperStyle: explicitSizingWrapperStyle
339
+ });
280
340
  };
281
341
 
282
342
  // src/rendering/gridItemWrapperProps.ts
@@ -353,7 +413,7 @@ import {
353
413
 
354
414
  // src/materialization/materializeChildContent.tsx
355
415
  import React3 from "react";
356
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
416
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
357
417
  var materializeChildContent = ({
358
418
  descriptor,
359
419
  renderSafeNode
@@ -363,7 +423,7 @@ var materializeChildContent = ({
363
423
  }
364
424
  const renderDescriptor = (childDescriptor) => {
365
425
  if (childDescriptor.kind === "grid-item") {
366
- return /* @__PURE__ */ jsx(
426
+ return /* @__PURE__ */ jsx2(
367
427
  "div",
368
428
  __spreadProps(__spreadValues({}, getGridItemWrapperProps(
369
429
  childDescriptor.colSpan,
@@ -376,22 +436,22 @@ var materializeChildContent = ({
376
436
  childDescriptor.child.id
377
437
  );
378
438
  }
379
- return /* @__PURE__ */ jsx(React3.Fragment, { children: renderSafeNode(childDescriptor.child) }, childDescriptor.child.id);
439
+ return /* @__PURE__ */ jsx2(React3.Fragment, { children: renderSafeNode(childDescriptor.child) }, childDescriptor.child.id);
380
440
  };
381
441
  if (descriptor.kind === "single") {
382
442
  return /* @__PURE__ */ jsxs(Fragment, { children: [
383
- descriptor.child.kind === "grid-item" ? /* @__PURE__ */ jsx("style", { children: GRID_ITEM_RESPONSIVE_STYLE }) : null,
443
+ descriptor.child.kind === "grid-item" ? /* @__PURE__ */ jsx2("style", { children: GRID_ITEM_RESPONSIVE_STYLE }) : null,
384
444
  renderDescriptor(descriptor.child)
385
445
  ] });
386
446
  }
387
447
  return /* @__PURE__ */ jsxs(Fragment, { children: [
388
- descriptor.children.some((child) => child.kind === "grid-item") ? /* @__PURE__ */ jsx("style", { children: GRID_ITEM_RESPONSIVE_STYLE }) : null,
448
+ descriptor.children.some((child) => child.kind === "grid-item") ? /* @__PURE__ */ jsx2("style", { children: GRID_ITEM_RESPONSIVE_STYLE }) : null,
389
449
  descriptor.children.map(renderDescriptor)
390
450
  ] });
391
451
  };
392
452
 
393
453
  // src/rendering/prepareRenderableChildren.tsx
394
- import { jsx as jsx2 } from "react/jsx-runtime";
454
+ import { jsx as jsx3 } from "react/jsx-runtime";
395
455
  var prepareRenderableChildren = ({
396
456
  node,
397
457
  internalStudioNodeTypes,
@@ -405,7 +465,7 @@ var prepareRenderableChildren = ({
405
465
  });
406
466
  const children = resolvedDescriptors.map((descriptor) => {
407
467
  if (descriptor.kind === "grid-item") {
408
- return /* @__PURE__ */ jsx2(
468
+ return /* @__PURE__ */ jsx3(
409
469
  "div",
410
470
  __spreadProps(__spreadValues({}, getGridItemWrapperProps(
411
471
  descriptor.colSpan,
@@ -418,10 +478,10 @@ var prepareRenderableChildren = ({
418
478
  descriptor.child.id
419
479
  );
420
480
  }
421
- return /* @__PURE__ */ jsx2(React4.Fragment, { children: renderSafeNode(descriptor.child) }, descriptor.child.id);
481
+ return /* @__PURE__ */ jsx3(React4.Fragment, { children: renderSafeNode(descriptor.child) }, descriptor.child.id);
422
482
  });
423
483
  const hasGridItems = resolvedDescriptors.some((descriptor) => descriptor.kind === "grid-item");
424
- const wrappedChildren = hasGridItems ? [/* @__PURE__ */ jsx2("style", { children: GRID_ITEM_RESPONSIVE_STYLE }, "arkcit-grid-item-responsive-style"), ...children] : children;
484
+ const wrappedChildren = hasGridItems ? [/* @__PURE__ */ jsx3("style", { children: GRID_ITEM_RESPONSIVE_STYLE }, "arkcit-grid-item-responsive-style"), ...children] : children;
425
485
  const resolvedChildContentDescriptor = childContentDescriptor != null ? childContentDescriptor : resolveChildContentDescriptor({
426
486
  childDescriptors: resolvedDescriptors
427
487
  });
@@ -439,7 +499,7 @@ var prepareRenderableChildren = ({
439
499
 
440
500
  // src/rendering/renderChildren.tsx
441
501
  import React5, { isValidElement as isValidElement2 } from "react";
442
- import { jsx as jsx3 } from "react/jsx-runtime";
502
+ import { jsx as jsx4 } from "react/jsx-runtime";
443
503
  var toBoolean = (value) => {
444
504
  if (typeof value === "boolean") return value;
445
505
  if (typeof value === "string") return value.toLowerCase() === "true";
@@ -450,7 +510,7 @@ var normalizeRenderableChild = (value) => {
450
510
  if (typeof value === "string" || typeof value === "number") return value;
451
511
  if (isValidElement2(value)) return value;
452
512
  if (Array.isArray(value)) {
453
- return value.map((item, index) => /* @__PURE__ */ jsx3(React5.Fragment, { children: normalizeRenderableChild(item) }, `normalized-child-${index}`));
513
+ return value.map((item, index) => /* @__PURE__ */ jsx4(React5.Fragment, { children: normalizeRenderableChild(item) }, `normalized-child-${index}`));
454
514
  }
455
515
  if (value && typeof value === "object") {
456
516
  const candidate = value;
@@ -468,8 +528,8 @@ var normalizeRenderableChild = (value) => {
468
528
  };
469
529
 
470
530
  // src/engine/EngineWarningFallback.tsx
471
- import { jsx as jsx4 } from "react/jsx-runtime";
472
- var EngineWarningFallback = ({ message }) => /* @__PURE__ */ jsx4(
531
+ import { jsx as jsx5 } from "react/jsx-runtime";
532
+ var EngineWarningFallback = ({ message }) => /* @__PURE__ */ jsx5(
473
533
  "div",
474
534
  {
475
535
  role: "alert",
@@ -518,7 +578,7 @@ var resolveResolvedReactNode = ({
518
578
  };
519
579
 
520
580
  // src/rendering/renderReactNode.tsx
521
- import { jsx as jsx5 } from "react/jsx-runtime";
581
+ import { jsx as jsx6 } from "react/jsx-runtime";
522
582
  var renderReactNode = ({
523
583
  node,
524
584
  runtime,
@@ -555,7 +615,7 @@ var renderReactNode = ({
555
615
  studioSizing
556
616
  });
557
617
  if (!resolvedNode) {
558
- return /* @__PURE__ */ jsx5(
618
+ return /* @__PURE__ */ jsx6(
559
619
  EngineWarningFallback_default,
560
620
  {
561
621
  message: `Unknown component type "${node.type}" for node "${node.id}".`
@@ -641,7 +701,7 @@ var renderReactNode = ({
641
701
  plans: finalRenderPlans
642
702
  });
643
703
  } catch (e) {
644
- return /* @__PURE__ */ jsx5(
704
+ return /* @__PURE__ */ jsx6(
645
705
  EngineWarningFallback_default,
646
706
  {
647
707
  message: `Renderer fallback: node "${node.type}" (${node.id}) could not be resolved.`
@@ -1,4 +1,3 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
1
  import React__default from 'react';
3
2
  import { UIEngineProps, UINodeWrapperProps } from '@arkcit/engine-core';
4
3
  import { UINode } from '@arkcit/engine-schema';
@@ -44,6 +43,6 @@ type StaticReactWebEngineRootDependencies<TInlineEditing = unknown> = {
44
43
  type StaticReactWebEngineRootArgs<TInlineEditing = unknown> = UIEngineProps & {
45
44
  dependencies: StaticReactWebEngineRootDependencies<TInlineEditing>;
46
45
  };
47
- declare const StaticReactWebEngineRoot: <TInlineEditing = unknown>({ schema, registry, store, nodeWrapper, dependencies, }: StaticReactWebEngineRootArgs<TInlineEditing>) => react_jsx_runtime.JSX.Element;
46
+ declare const StaticReactWebEngineRoot: <TInlineEditing = unknown>({ schema, registry, store, nodeWrapper, dependencies, }: StaticReactWebEngineRootArgs<TInlineEditing>) => React__default.JSX.Element;
48
47
 
49
48
  export { StaticReactWebEngineRoot, type StaticReactWebEngineRootArgs, type StaticReactWebEngineRootDependencies, type StaticRenderSafeNodeArgs };
@@ -427,7 +427,14 @@ var StaticReactWebEngineRoot = ({
427
427
  NodeWrapper,
428
428
  isFormInteractiveTarget
429
429
  });
430
- return /* @__PURE__ */ jsx5("div", { className: "flex h-full min-w-0 w-full flex-wrap content-start items-start gap-3", children: schema.nodes.map((node) => /* @__PURE__ */ jsx5(React3.Fragment, { children: renderSafeEngineNode(node) }, node.id)) });
430
+ return /* @__PURE__ */ jsx5(
431
+ "div",
432
+ {
433
+ "data-arkcit-engine-root": true,
434
+ className: "flex min-w-0 w-full flex-wrap content-start items-start gap-3",
435
+ children: schema.nodes.map((node) => /* @__PURE__ */ jsx5(React3.Fragment, { children: renderSafeEngineNode(node) }, node.id))
436
+ }
437
+ );
431
438
  };
432
439
  export {
433
440
  StaticReactWebEngineRoot
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arkcit/engine-react",
3
3
  "private": false,
4
- "version": "0.3.8",
4
+ "version": "0.3.10",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "React-specific renderer package for the Arkcit engine platform.",