@pushframe/sdk 0.1.1 → 0.1.2

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/dist/index.js CHANGED
@@ -22,8 +22,18 @@ function usePushFrameContext() {
22
22
  }
23
23
  return ctx;
24
24
  }
25
- function Text({ if: _if, actions: _actions, value, children, ...rest }) {
26
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { ...rest, children: value !== void 0 ? value : children });
25
+ function Text({
26
+ if: _if,
27
+ actions: _actions,
28
+ value,
29
+ content,
30
+ ...rest
31
+ }) {
32
+ const textContent = value !== void 0 ? value : content;
33
+ if (textContent !== void 0) {
34
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { ...rest, children: textContent });
35
+ }
36
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { ...rest });
27
37
  }
28
38
  function View({ if: _if, actions: _actions, ...rest }) {
29
39
  return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ...rest });
@@ -408,7 +418,8 @@ function resolveDeep(value, context) {
408
418
 
409
419
  // src/renderer/conditionalEvaluator.ts
410
420
  function evaluateIf(ifExpr, context) {
411
- if (ifExpr === void 0) return true;
421
+ if (ifExpr === void 0 || ifExpr === null) return true;
422
+ if (typeof ifExpr !== "string") return Boolean(ifExpr);
412
423
  const resolved = resolveValue(ifExpr, context);
413
424
  return Boolean(resolved);
414
425
  }
@@ -435,9 +446,18 @@ function buildActionProps(actions, context, dispatchAction) {
435
446
  }
436
447
  function renderFlatListNode(node, context, registry, dispatchAction, fallbackComponent) {
437
448
  const resolvedProps = resolveProps(node.props, context);
438
- const items = Array.isArray(resolvedProps["items"]) ? resolvedProps["items"] : [];
439
449
  const actionProps = node.actions ? buildActionProps(node.actions, context, dispatchAction) : {};
440
- const { items: _items, keyExtractor: keyExtractorExpr, direction, numColumns, ...restProps } = resolvedProps;
450
+ const {
451
+ data: resolvedData,
452
+ items: resolvedItems,
453
+ keyExtractor: keyExtractorExpr,
454
+ direction,
455
+ numColumns,
456
+ visible: _visible,
457
+ _propValues: _pv,
458
+ ...restProps
459
+ } = resolvedProps;
460
+ const items = Array.isArray(resolvedData) ? resolvedData : Array.isArray(resolvedItems) ? resolvedItems : [];
441
461
  let keyExtractorFn;
442
462
  if (typeof keyExtractorExpr === "string") {
443
463
  const extractorExpr = keyExtractorExpr;
@@ -452,7 +472,7 @@ function renderFlatListNode(node, context, registry, dispatchAction, fallbackCom
452
472
  return /* @__PURE__ */ jsxRuntime.jsx(
453
473
  RecursiveRenderer,
454
474
  {
455
- node: node.renderItem,
475
+ node: node.itemTemplate,
456
476
  context: itemContext,
457
477
  registry,
458
478
  dispatchAction,
@@ -496,6 +516,16 @@ function RecursiveRenderer({
496
516
  );
497
517
  }
498
518
  const resolvedProps = resolveProps(node.props, context);
519
+ if (resolvedProps.visible !== void 0 && !resolvedProps.visible) {
520
+ return null;
521
+ }
522
+ const {
523
+ visible: _visible,
524
+ _propValues: propValuesRaw,
525
+ ...propsToForward
526
+ } = resolvedProps;
527
+ const propValues = propValuesRaw;
528
+ const childContext = propValues && Object.keys(propValues).length > 0 ? { ...context, ...propValues } : context;
499
529
  const actionProps = node.actions ? buildActionProps(node.actions, context, dispatchAction) : {};
500
530
  const Component = registry.resolve(node.type);
501
531
  if (!Component) {
@@ -508,7 +538,7 @@ function RecursiveRenderer({
508
538
  RecursiveRenderer,
509
539
  {
510
540
  node: child,
511
- context,
541
+ context: childContext,
512
542
  registry,
513
543
  dispatchAction,
514
544
  fallbackComponent
@@ -516,7 +546,7 @@ function RecursiveRenderer({
516
546
  `${child.type}-${index}`
517
547
  )) : void 0;
518
548
  return React4__default.default.createElement(Component, {
519
- ...resolvedProps,
549
+ ...propsToForward,
520
550
  ...actionProps,
521
551
  children
522
552
  });
@@ -737,6 +767,744 @@ var styles3 = reactNative.StyleSheet.create({
737
767
  }
738
768
  });
739
769
 
770
+ // src/transformer/index.ts
771
+ var BRIDGE_KEYS = /* @__PURE__ */ new Set([
772
+ "_schemaId",
773
+ "_schemaType",
774
+ "_schemaActions",
775
+ "_schemaIf",
776
+ "_schemaRenderItem"
777
+ ]);
778
+ var ALIGN_MAP = {
779
+ start: "flex-start",
780
+ center: "center",
781
+ end: "flex-end",
782
+ stretch: "stretch",
783
+ baseline: "baseline"
784
+ };
785
+ var DISTRIBUTE_MAP = {
786
+ start: "flex-start",
787
+ center: "center",
788
+ end: "flex-end",
789
+ "space-between": "space-between",
790
+ "space-around": "space-around",
791
+ "space-evenly": "space-evenly"
792
+ };
793
+ var FIT_TO_RESIZE_MODE = {
794
+ cover: "cover",
795
+ contain: "contain",
796
+ fill: "stretch",
797
+ // RN uses 'stretch', not 'fill'
798
+ center: "center"
799
+ };
800
+ var FONT_WEIGHT_MAP = {
801
+ regular: "400",
802
+ medium: "500",
803
+ semibold: "600",
804
+ bold: "700"
805
+ };
806
+ var TEXT_STYLE_PRESETS = {
807
+ "heading-1": { fontSize: 28, fontWeight: "700" },
808
+ "heading-2": { fontSize: 22, fontWeight: "700" },
809
+ "heading-3": { fontSize: 18, fontWeight: "600" },
810
+ "body": { fontSize: 16, fontWeight: "400" },
811
+ "body-sm": { fontSize: 14, fontWeight: "400" },
812
+ "caption": { fontSize: 12, fontWeight: "400" },
813
+ "label": { fontSize: 13, fontWeight: "500" },
814
+ "overline": { fontSize: 11, fontWeight: "500", textTransform: "uppercase", letterSpacing: 0.8 }
815
+ };
816
+ var INPUT_TYPE_MAP = {
817
+ text: { keyboardType: "default", secureTextEntry: false, autoCapitalize: "sentences", autoCorrect: true },
818
+ email: { keyboardType: "email-address", secureTextEntry: false, autoCapitalize: "none", autoCorrect: false },
819
+ password: { keyboardType: "default", secureTextEntry: true, autoCapitalize: "none", autoCorrect: false },
820
+ number: { keyboardType: "numeric", secureTextEntry: false, autoCapitalize: "none", autoCorrect: false },
821
+ decimal: { keyboardType: "decimal-pad", secureTextEntry: false, autoCapitalize: "none", autoCorrect: false },
822
+ phone: { keyboardType: "phone-pad", secureTextEntry: false, autoCapitalize: "none", autoCorrect: false },
823
+ url: { keyboardType: "url", secureTextEntry: false, autoCapitalize: "none", autoCorrect: false },
824
+ search: { keyboardType: "default", secureTextEntry: false, autoCapitalize: "none", autoCorrect: false }
825
+ };
826
+ function stripBridgeKeys(props) {
827
+ const out = {};
828
+ for (const k in props) {
829
+ if (!BRIDGE_KEYS.has(k)) out[k] = props[k];
830
+ }
831
+ return out;
832
+ }
833
+ function extractMeta(raw) {
834
+ return {
835
+ actions: raw._schemaActions,
836
+ condition: raw._schemaIf
837
+ };
838
+ }
839
+ function resolveSizeModeRN(value, axis, out, parentDirection) {
840
+ if (value === void 0) return;
841
+ if (value === "fill") {
842
+ const isMainAxis = parentDirection === "vertical" && axis === "height" || parentDirection === "horizontal" && axis === "width";
843
+ if (isMainAxis) {
844
+ out.flex = 1;
845
+ } else {
846
+ out.alignSelf = "stretch";
847
+ }
848
+ } else if (value === "hug") {
849
+ out.alignSelf = "flex-start";
850
+ } else {
851
+ out[axis] = value;
852
+ }
853
+ }
854
+ function resolvePaddingRN(padding) {
855
+ if (padding === void 0) return {};
856
+ if (typeof padding === "number") return { padding };
857
+ const { top, right, bottom, left } = padding;
858
+ if (top === bottom && left === right && top !== void 0 && left !== void 0) {
859
+ return { paddingVertical: top, paddingHorizontal: left };
860
+ }
861
+ return {
862
+ ...top !== void 0 ? { paddingTop: top } : {},
863
+ ...right !== void 0 ? { paddingRight: right } : {},
864
+ ...bottom !== void 0 ? { paddingBottom: bottom } : {},
865
+ ...left !== void 0 ? { paddingLeft: left } : {}
866
+ };
867
+ }
868
+ function resolveRadiusRN(radius) {
869
+ if (radius === void 0) return {};
870
+ if (typeof radius === "number") return { borderRadius: radius };
871
+ return {
872
+ ...radius.tl !== void 0 ? { borderTopLeftRadius: radius.tl } : {},
873
+ ...radius.tr !== void 0 ? { borderTopRightRadius: radius.tr } : {},
874
+ ...radius.br !== void 0 ? { borderBottomRightRadius: radius.br } : {},
875
+ ...radius.bl !== void 0 ? { borderBottomLeftRadius: radius.bl } : {}
876
+ };
877
+ }
878
+ function resolveBorderRN(border) {
879
+ if (!border) return {};
880
+ const out = {};
881
+ if (border.style) out.borderStyle = border.style;
882
+ if (border.color) out.borderColor = border.color;
883
+ if (border.widths) {
884
+ const w = border.widths;
885
+ if (w.top !== void 0) out.borderTopWidth = w.top;
886
+ if (w.right !== void 0) out.borderRightWidth = w.right;
887
+ if (w.bottom !== void 0) out.borderBottomWidth = w.bottom;
888
+ if (w.left !== void 0) out.borderLeftWidth = w.left;
889
+ } else if (border.width !== void 0) {
890
+ out.borderWidth = border.width;
891
+ }
892
+ return out;
893
+ }
894
+ function resolveShadowRN(shadow) {
895
+ if (!shadow) return {};
896
+ return {
897
+ shadowColor: shadow.color,
898
+ shadowOffset: { width: shadow.offsetX, height: shadow.offsetY },
899
+ shadowRadius: shadow.blur,
900
+ shadowOpacity: shadow.opacity,
901
+ elevation: Math.round(shadow.blur / 2)
902
+ };
903
+ }
904
+ function resolveTransformRN(props) {
905
+ const transforms = [];
906
+ if (props.rotate !== void 0 && props.rotate !== 0) transforms.push({ rotate: `${props.rotate}deg` });
907
+ if (props.scale !== void 0 && props.scale !== 1) transforms.push({ scale: props.scale });
908
+ if (props.translateX !== void 0 && props.translateX !== 0) transforms.push({ translateX: props.translateX });
909
+ if (props.translateY !== void 0 && props.translateY !== 0) transforms.push({ translateY: props.translateY });
910
+ return transforms.length ? { transform: transforms } : {};
911
+ }
912
+ function resolvePositionRN(props) {
913
+ const out = {};
914
+ if (props.position) out.position = props.position;
915
+ if (props.position === "absolute") {
916
+ if (props.top !== void 0) out.top = props.top;
917
+ if (props.right !== void 0) out.right = props.right;
918
+ if (props.bottom !== void 0) out.bottom = props.bottom;
919
+ if (props.left !== void 0) out.left = props.left;
920
+ }
921
+ if (props.zIndex !== void 0) out.zIndex = props.zIndex;
922
+ return out;
923
+ }
924
+ function resolveAccessibilityProps(props) {
925
+ const out = {};
926
+ if (props.accessible !== void 0) out.accessible = props.accessible;
927
+ if (props.accessibilityLabel) out.accessibilityLabel = props.accessibilityLabel;
928
+ if (props.accessibilityRole) out.accessibilityRole = props.accessibilityRole;
929
+ if (props.testID) out.testID = props.testID;
930
+ return out;
931
+ }
932
+ function buildFrameStyle(props, parentDirection) {
933
+ const style = {};
934
+ const frameType = props.type ?? "stack";
935
+ if (frameType === "grid") {
936
+ if (props.align) style.alignItems = ALIGN_MAP[props.align] ?? props.align;
937
+ } else {
938
+ style.flexDirection = props.direction === "horizontal" ? "row" : "column";
939
+ if (props.align) style.alignItems = ALIGN_MAP[props.align] ?? props.align;
940
+ if (props.distribute) style.justifyContent = DISTRIBUTE_MAP[props.distribute] ?? props.distribute;
941
+ if (props.gap !== void 0 && props.gap !== 0) style.gap = props.gap;
942
+ if (props.wrap === "yes") style.flexWrap = "wrap";
943
+ }
944
+ resolveSizeModeRN(props.width, "width", style, parentDirection);
945
+ resolveSizeModeRN(props.height, "height", style, parentDirection);
946
+ if (props.minWidth !== void 0) style.minWidth = props.minWidth;
947
+ if (props.maxWidth !== void 0) style.maxWidth = props.maxWidth;
948
+ if (props.minHeight !== void 0) style.minHeight = props.minHeight;
949
+ if (props.maxHeight !== void 0) style.maxHeight = props.maxHeight;
950
+ if (props.aspectRatio !== void 0) style.aspectRatio = props.aspectRatio;
951
+ Object.assign(style, resolvePaddingRN(props.padding));
952
+ if (props.fill && props.fill !== "transparent") style.backgroundColor = props.fill;
953
+ Object.assign(style, resolveRadiusRN(props.radius));
954
+ Object.assign(style, resolveBorderRN(props.border));
955
+ Object.assign(style, resolveShadowRN(props.shadow));
956
+ if (props.opacity !== void 0) style.opacity = props.opacity / 100;
957
+ if (props.overflow === true) style.overflow = "hidden";
958
+ Object.assign(style, resolvePositionRN(props));
959
+ Object.assign(style, resolveTransformRN(props));
960
+ return style;
961
+ }
962
+ function splitFrameStyle(style) {
963
+ const OUTER_KEYS = /* @__PURE__ */ new Set([
964
+ "flex",
965
+ "alignSelf",
966
+ "width",
967
+ "height",
968
+ "minWidth",
969
+ "maxWidth",
970
+ "minHeight",
971
+ "maxHeight",
972
+ "aspectRatio",
973
+ "position",
974
+ "top",
975
+ "right",
976
+ "bottom",
977
+ "left",
978
+ "zIndex",
979
+ "transform"
980
+ ]);
981
+ const outer = {};
982
+ const inner = {};
983
+ for (const [k, v] of Object.entries(style)) {
984
+ if (OUTER_KEYS.has(k)) outer[k] = v;
985
+ else inner[k] = v;
986
+ }
987
+ return { outer, inner };
988
+ }
989
+ function splitScrollStyle(style) {
990
+ const CONTENT_KEYS = /* @__PURE__ */ new Set([
991
+ "flexDirection",
992
+ "justifyContent",
993
+ "alignItems",
994
+ "flexWrap",
995
+ "gap",
996
+ "padding",
997
+ "paddingTop",
998
+ "paddingRight",
999
+ "paddingBottom",
1000
+ "paddingLeft",
1001
+ "paddingVertical",
1002
+ "paddingHorizontal"
1003
+ ]);
1004
+ const containerStyle = {};
1005
+ const contentStyle = {};
1006
+ for (const [k, v] of Object.entries(style)) {
1007
+ if (CONTENT_KEYS.has(k)) contentStyle[k] = v;
1008
+ else containerStyle[k] = v;
1009
+ }
1010
+ return { containerStyle, contentStyle };
1011
+ }
1012
+ function safeAreaEdges(safeArea) {
1013
+ if (!safeArea) return [];
1014
+ return Object.entries(safeArea).filter(([, v]) => v === true).map(([edge]) => edge);
1015
+ }
1016
+ function transformScreen(id, raw, children) {
1017
+ const { actions, condition } = extractMeta(raw);
1018
+ const props = stripBridgeKeys(raw);
1019
+ const edges = safeAreaEdges(props.safeArea);
1020
+ const hasSafeArea = edges.length > 0;
1021
+ const vis = props.visible !== void 0 && props.visible !== true ? { visible: props.visible } : {};
1022
+ const style = { flex: 1 };
1023
+ if (props.backgroundColor) style.backgroundColor = props.backgroundColor;
1024
+ let content;
1025
+ if (props.scrollable) {
1026
+ const paddingStyle = resolvePaddingRN(props.padding);
1027
+ const scrollNode = {
1028
+ id: `${id}__scroll`,
1029
+ type: "scrollview",
1030
+ props: {
1031
+ style: { flex: 1 },
1032
+ ...Object.keys(paddingStyle).length ? { contentContainerStyle: paddingStyle } : {},
1033
+ showsVerticalScrollIndicator: false,
1034
+ showsHorizontalScrollIndicator: false
1035
+ },
1036
+ children
1037
+ };
1038
+ content = [scrollNode];
1039
+ } else {
1040
+ Object.assign(style, resolvePaddingRN(props.padding));
1041
+ content = children;
1042
+ }
1043
+ const statusBar = props.statusBarStyle && props.statusBarStyle !== "dark" ? { statusBarStyle: props.statusBarStyle } : {};
1044
+ const node = {
1045
+ id,
1046
+ type: hasSafeArea ? "safeareaview" : "view",
1047
+ props: {
1048
+ style,
1049
+ ...hasSafeArea ? { edges } : {},
1050
+ ...statusBar,
1051
+ ...vis
1052
+ },
1053
+ children: content
1054
+ };
1055
+ if (condition !== void 0) node.if = condition;
1056
+ if (actions) node.actions = actions;
1057
+ return node;
1058
+ }
1059
+ function transformFrame(id, raw, children, parentDirection) {
1060
+ const { actions, condition } = extractMeta(raw);
1061
+ const p = stripBridgeKeys(raw);
1062
+ const isGrid = (p.type ?? "stack") === "grid";
1063
+ const isScrollable = p.scrollable === true;
1064
+ const isPressable = p.pressable === true;
1065
+ const direction = p.direction === "horizontal" ? "horizontal" : "vertical";
1066
+ const edges = safeAreaEdges(p.safeArea);
1067
+ const hasSafeArea = edges.length > 0;
1068
+ const access = resolveAccessibilityProps(p);
1069
+ const vis = p.visible !== void 0 && p.visible !== true ? { visible: p.visible } : {};
1070
+ const fullStyle = buildFrameStyle(p, parentDirection);
1071
+ const { outer: sizingStyle, inner: appearanceStyle } = splitFrameStyle(fullStyle);
1072
+ const { containerStyle: scrollOuter, contentStyle: scrollContent } = splitScrollStyle(fullStyle);
1073
+ function finalize(node) {
1074
+ if (condition !== void 0) node.if = condition;
1075
+ if (actions && node.type === "pressable") node.actions = actions;
1076
+ else if (actions && !isPressable) node.actions = actions;
1077
+ return node;
1078
+ }
1079
+ if (isGrid) {
1080
+ const gridStyle = { ...fullStyle, flexDirection: "row", flexWrap: "wrap" };
1081
+ delete gridStyle.gap;
1082
+ if (p.columnGap !== void 0) gridStyle.columnGap = p.columnGap;
1083
+ if (p.rowGap !== void 0) gridStyle.rowGap = p.rowGap;
1084
+ const gridView = {
1085
+ id: hasSafeArea || isPressable ? `${id}__grid` : id,
1086
+ type: "view",
1087
+ props: {
1088
+ ...Object.keys(gridStyle).length ? { style: gridStyle } : {},
1089
+ numColumns: p.columns ?? 2,
1090
+ ...access,
1091
+ ...hasSafeArea || isPressable ? {} : vis
1092
+ },
1093
+ children
1094
+ };
1095
+ if (!hasSafeArea && !isPressable) return finalize(gridView);
1096
+ let inner = gridView;
1097
+ if (hasSafeArea) {
1098
+ inner = {
1099
+ id: isPressable ? `${id}__sav` : id,
1100
+ type: "safeareaview",
1101
+ props: { edges, ...isPressable ? {} : vis },
1102
+ children: [gridView]
1103
+ };
1104
+ }
1105
+ if (!isPressable) return finalize(inner);
1106
+ return finalize({
1107
+ id,
1108
+ type: "pressable",
1109
+ props: {
1110
+ ...Object.keys(sizingStyle).length ? { style: sizingStyle } : {},
1111
+ ...actions ? { actions } : {},
1112
+ ...vis
1113
+ },
1114
+ children: [inner]
1115
+ });
1116
+ }
1117
+ if (isScrollable) {
1118
+ const scrollNode = {
1119
+ id: hasSafeArea || isPressable ? `${id}__scroll` : id,
1120
+ type: "scrollview",
1121
+ props: {
1122
+ ...Object.keys(scrollOuter).length ? { style: scrollOuter } : {},
1123
+ ...Object.keys(scrollContent).length ? { contentContainerStyle: scrollContent } : {},
1124
+ horizontal: direction === "horizontal",
1125
+ showsHorizontalScrollIndicator: p.showScrollIndicator === true,
1126
+ showsVerticalScrollIndicator: p.showScrollIndicator === true,
1127
+ ...access,
1128
+ ...hasSafeArea || isPressable ? {} : vis
1129
+ },
1130
+ children
1131
+ };
1132
+ if (!hasSafeArea && !isPressable) return finalize(scrollNode);
1133
+ let inner = scrollNode;
1134
+ if (hasSafeArea) {
1135
+ inner = {
1136
+ id: isPressable ? `${id}__sav` : id,
1137
+ type: "safeareaview",
1138
+ props: {
1139
+ edges,
1140
+ ...Object.keys(sizingStyle).length ? { style: sizingStyle } : {},
1141
+ ...isPressable ? {} : vis
1142
+ },
1143
+ children: [scrollNode]
1144
+ };
1145
+ }
1146
+ if (!isPressable) return finalize(inner);
1147
+ return finalize({
1148
+ id,
1149
+ type: "pressable",
1150
+ props: {
1151
+ ...Object.keys(sizingStyle).length ? { style: sizingStyle } : {},
1152
+ ...actions ? { actions } : {},
1153
+ ...vis
1154
+ },
1155
+ children: [inner]
1156
+ });
1157
+ }
1158
+ if (!isPressable && !hasSafeArea) {
1159
+ return finalize({
1160
+ id,
1161
+ type: "view",
1162
+ props: {
1163
+ ...Object.keys(fullStyle).length ? { style: fullStyle } : {},
1164
+ ...access,
1165
+ ...vis
1166
+ },
1167
+ children
1168
+ });
1169
+ }
1170
+ if (!isPressable && hasSafeArea) {
1171
+ return finalize({
1172
+ id,
1173
+ type: "safeareaview",
1174
+ props: {
1175
+ edges,
1176
+ ...Object.keys(fullStyle).length ? { style: fullStyle } : {},
1177
+ ...access,
1178
+ ...vis
1179
+ },
1180
+ children
1181
+ });
1182
+ }
1183
+ const innerNode = hasSafeArea ? {
1184
+ id: `${id}__sav`,
1185
+ type: "safeareaview",
1186
+ props: {
1187
+ edges,
1188
+ ...Object.keys(appearanceStyle).length ? { style: appearanceStyle } : {},
1189
+ ...access
1190
+ },
1191
+ children
1192
+ } : {
1193
+ id: `${id}__content`,
1194
+ type: "view",
1195
+ props: {
1196
+ ...Object.keys(appearanceStyle).length ? { style: appearanceStyle } : {},
1197
+ ...access
1198
+ },
1199
+ children
1200
+ };
1201
+ return finalize({
1202
+ id,
1203
+ type: "pressable",
1204
+ props: {
1205
+ ...Object.keys(sizingStyle).length ? { style: sizingStyle } : {},
1206
+ ...actions ? { actions } : {},
1207
+ ...vis
1208
+ },
1209
+ children: [innerNode]
1210
+ });
1211
+ }
1212
+ function transformText(id, raw, parentDirection) {
1213
+ const { actions, condition } = extractMeta(raw);
1214
+ const props = stripBridgeKeys(raw);
1215
+ const style = {};
1216
+ if (props.textStyle) {
1217
+ const preset = TEXT_STYLE_PRESETS[props.textStyle];
1218
+ if (preset) {
1219
+ style.fontSize = preset.fontSize;
1220
+ style.fontWeight = preset.fontWeight;
1221
+ if (preset.lineHeight !== void 0) style.lineHeight = preset.lineHeight;
1222
+ if (preset.textTransform) style.textTransform = preset.textTransform;
1223
+ if (preset.letterSpacing !== void 0) style.letterSpacing = preset.letterSpacing;
1224
+ }
1225
+ }
1226
+ if (props.color) style.color = props.color;
1227
+ if (props.fontSize !== void 0) style.fontSize = props.fontSize;
1228
+ if (props.fontWeight) style.fontWeight = FONT_WEIGHT_MAP[props.fontWeight] ?? props.fontWeight;
1229
+ if (props.fontFamily) style.fontFamily = props.fontFamily;
1230
+ if (props.fontStyle) style.fontStyle = props.fontStyle;
1231
+ if (props.lineHeight !== void 0) style.lineHeight = props.lineHeight;
1232
+ if (props.letterSpacing !== void 0) style.letterSpacing = props.letterSpacing;
1233
+ if (props.align) style.textAlign = props.align;
1234
+ if (props.transform && props.transform !== "none") style.textTransform = props.transform;
1235
+ if (props.decoration && props.decoration !== "none") style.textDecorationLine = props.decoration;
1236
+ if (props.opacity !== void 0) style.opacity = props.opacity / 100;
1237
+ Object.assign(style, resolvePositionRN(props));
1238
+ resolveSizeModeRN(props.width, "width", style, parentDirection);
1239
+ const textProps = {};
1240
+ if (typeof props.truncate === "number" && props.truncate > 0) {
1241
+ textProps.numberOfLines = props.truncate;
1242
+ textProps.ellipsizeMode = "tail";
1243
+ }
1244
+ if (props.adjustFontSize === true) {
1245
+ textProps.adjustsFontSizeToFit = true;
1246
+ if (props.minFontSize !== void 0) textProps.minimumFontScale = props.minFontSize;
1247
+ }
1248
+ if (props.selectable !== void 0) textProps.selectable = props.selectable;
1249
+ const node = {
1250
+ id,
1251
+ type: "text",
1252
+ props: {
1253
+ // SDK contract: text content lives in the `value` prop.
1254
+ // The Craft editor stores text in `content`; map it to `value` here.
1255
+ value: props.content ?? "",
1256
+ ...Object.keys(style).length ? { style } : {},
1257
+ ...textProps,
1258
+ ...resolveAccessibilityProps(props),
1259
+ ...props.visible !== void 0 && props.visible !== true ? { visible: props.visible } : {}
1260
+ }
1261
+ };
1262
+ if (condition !== void 0) node.if = condition;
1263
+ if (actions) node.actions = actions;
1264
+ return node;
1265
+ }
1266
+ function transformImage(id, raw, children, parentDirection) {
1267
+ const { actions, condition } = extractMeta(raw);
1268
+ const props = stripBridgeKeys(raw);
1269
+ const style = {};
1270
+ resolveSizeModeRN(props.width, "width", style, parentDirection);
1271
+ resolveSizeModeRN(props.height, "height", style, parentDirection);
1272
+ if (props.aspectRatio !== void 0) style.aspectRatio = props.aspectRatio;
1273
+ Object.assign(style, resolveRadiusRN(props.radius));
1274
+ if (props.opacity !== void 0) style.opacity = props.opacity / 100;
1275
+ Object.assign(style, resolvePositionRN(props));
1276
+ const imageProps = {};
1277
+ if (props.src) imageProps.source = { uri: props.src };
1278
+ if (props.placeholder) imageProps.defaultSource = { uri: props.placeholder };
1279
+ if (props.fallback) imageProps.fallback = { uri: props.fallback };
1280
+ imageProps.resizeMode = FIT_TO_RESIZE_MODE[props.fit ?? "cover"] ?? "cover";
1281
+ if (props.background === true) imageProps.background = true;
1282
+ if (props.decorative) {
1283
+ imageProps.accessible = false;
1284
+ imageProps.accessibilityElementsHidden = true;
1285
+ } else if (props.alt) {
1286
+ imageProps.accessible = true;
1287
+ imageProps.accessibilityLabel = props.alt;
1288
+ }
1289
+ if (props.testID) imageProps.testID = props.testID;
1290
+ const node = {
1291
+ id,
1292
+ type: "image",
1293
+ props: {
1294
+ ...Object.keys(style).length ? { style } : {},
1295
+ ...imageProps,
1296
+ ...props.visible !== void 0 && props.visible !== true ? { visible: props.visible } : {}
1297
+ },
1298
+ ...props.background === true && children.length ? { children } : {}
1299
+ };
1300
+ if (condition !== void 0) node.if = condition;
1301
+ if (actions) node.actions = actions;
1302
+ return node;
1303
+ }
1304
+ function transformInput(id, raw, parentDirection) {
1305
+ const { actions, condition } = extractMeta(raw);
1306
+ const props = stripBridgeKeys(raw);
1307
+ const style = {};
1308
+ resolveSizeModeRN(props.width ?? "fill", "width", style, parentDirection);
1309
+ Object.assign(style, resolvePaddingRN(props.padding));
1310
+ if (props.opacity !== void 0) style.opacity = props.opacity / 100;
1311
+ Object.assign(style, resolvePositionRN(props));
1312
+ const inputTypeDefaults = INPUT_TYPE_MAP[props.inputType ?? "text"] ?? INPUT_TYPE_MAP.text;
1313
+ const node = {
1314
+ id,
1315
+ type: "textinput",
1316
+ props: {
1317
+ ...Object.keys(style).length ? { style } : {},
1318
+ ...props.label !== void 0 ? { label: props.label } : {},
1319
+ ...props.placeholder !== void 0 ? { placeholder: props.placeholder } : {},
1320
+ ...props.value !== void 0 ? { value: props.value } : {},
1321
+ ...props.helperText !== void 0 ? { helperText: props.helperText } : {},
1322
+ ...props.errorText !== void 0 ? { errorText: props.errorText } : {},
1323
+ ...inputTypeDefaults,
1324
+ ...props.autoCapitalize ? { autoCapitalize: props.autoCapitalize } : {},
1325
+ returnKeyType: props.returnKey ?? "done",
1326
+ multiline: props.multiline ?? false,
1327
+ ...props.multiline && props.minLines ? { numberOfLines: props.minLines } : {},
1328
+ ...props.multiline && props.maxLines ? { maxNumberOfLines: props.maxLines } : {},
1329
+ variant: props.variant ?? "outline",
1330
+ size: props.size ?? "md",
1331
+ ...props.leadingIcon ? { leadingIcon: props.leadingIcon } : {},
1332
+ ...props.trailingIcon ? { trailingIcon: props.trailingIcon } : {},
1333
+ ...resolveAccessibilityProps(props),
1334
+ ...props.visible !== void 0 && props.visible !== true ? { visible: props.visible } : {}
1335
+ }
1336
+ };
1337
+ if (condition !== void 0) node.if = condition;
1338
+ if (actions) node.actions = actions;
1339
+ return node;
1340
+ }
1341
+ function transformList(id, raw, children, parentDirection) {
1342
+ const { actions, condition } = extractMeta(raw);
1343
+ const props = stripBridgeKeys(raw);
1344
+ const listType = props.type ?? "stack";
1345
+ const direction = props.direction === "horizontal" ? "horizontal" : "vertical";
1346
+ const edges = safeAreaEdges(props.safeArea);
1347
+ const hasSafeArea = edges.length > 0;
1348
+ const vis = props.visible !== void 0 && props.visible !== true ? { visible: props.visible } : {};
1349
+ const fullStyle = buildFrameStyle(props, parentDirection);
1350
+ const { containerStyle, contentStyle } = splitScrollStyle(fullStyle);
1351
+ let itemTemplate;
1352
+ if (children.length === 1) {
1353
+ itemTemplate = children[0];
1354
+ } else if (children.length > 1) {
1355
+ itemTemplate = { id: `${id}__template`, type: "view", props: {}, children };
1356
+ }
1357
+ const flatlistNode = {
1358
+ id: hasSafeArea ? `${id}__list` : id,
1359
+ type: "flatlist",
1360
+ props: {
1361
+ ...Object.keys(containerStyle).length ? { style: containerStyle } : {},
1362
+ ...Object.keys(contentStyle).length ? { contentContainerStyle: contentStyle } : {},
1363
+ // `data` holds the binding expression for the list items array.
1364
+ // RecursiveRenderer resolves it and passes items to the FlatList primitive.
1365
+ data: props.data,
1366
+ keyExtractor: props.keyExtractor ?? "id",
1367
+ ...listType === "grid" ? { numColumns: props.columns ?? 2 } : {},
1368
+ ...listType === "grid" && props.columnGap ? { columnGap: props.columnGap } : {},
1369
+ ...listType === "grid" && props.rowGap ? { rowGap: props.rowGap } : {},
1370
+ horizontal: direction === "horizontal",
1371
+ showsHorizontalScrollIndicator: props.showScrollIndicator === true,
1372
+ showsVerticalScrollIndicator: props.showScrollIndicator === true,
1373
+ ...resolveAccessibilityProps(props),
1374
+ ...hasSafeArea ? {} : vis
1375
+ },
1376
+ ...itemTemplate ? { itemTemplate } : {}
1377
+ };
1378
+ if (!hasSafeArea) {
1379
+ if (condition !== void 0) flatlistNode.if = condition;
1380
+ if (actions) flatlistNode.actions = actions;
1381
+ return flatlistNode;
1382
+ }
1383
+ const { outer: sizingStyle } = splitFrameStyle(fullStyle);
1384
+ const savNode = {
1385
+ id,
1386
+ type: "safeareaview",
1387
+ props: {
1388
+ edges,
1389
+ ...Object.keys(sizingStyle).length ? { style: sizingStyle } : {},
1390
+ ...vis
1391
+ },
1392
+ children: [flatlistNode]
1393
+ };
1394
+ if (condition !== void 0) savNode.if = condition;
1395
+ if (actions) savNode.actions = actions;
1396
+ return savNode;
1397
+ }
1398
+ function transformIcon(id, raw) {
1399
+ const { actions, condition } = extractMeta(raw);
1400
+ const props = stripBridgeKeys(raw);
1401
+ const style = {};
1402
+ if (props.opacity !== void 0) style.opacity = props.opacity / 100;
1403
+ Object.assign(style, resolvePositionRN(props));
1404
+ const node = {
1405
+ id,
1406
+ type: "icon",
1407
+ props: {
1408
+ name: props.name,
1409
+ set: props.set ?? "default",
1410
+ size: props.size ?? 24,
1411
+ ...props.color ? { color: props.color } : {},
1412
+ ...Object.keys(style).length ? { style } : {},
1413
+ ...resolveAccessibilityProps(props),
1414
+ ...props.visible !== void 0 && props.visible !== true ? { visible: props.visible } : {}
1415
+ }
1416
+ };
1417
+ if (condition !== void 0) node.if = condition;
1418
+ if (actions) node.actions = actions;
1419
+ return node;
1420
+ }
1421
+ function buildNodeFromSchema(nodeId, schema, customComponents, visited, parentId) {
1422
+ if (visited.has(nodeId)) {
1423
+ throw new Error(`Circular reference detected at node ${nodeId}`);
1424
+ }
1425
+ visited.add(nodeId);
1426
+ const node = schema[nodeId];
1427
+ if (!node) throw new Error(`Node not found: ${nodeId}`);
1428
+ const resolvedName = node.type?.resolvedName;
1429
+ if (!resolvedName) throw new Error(`Invalid node type at ${nodeId}`);
1430
+ const raw = node.props ?? {};
1431
+ const parentDirection = parentId ? schema[parentId]?.props?.["direction"] === "horizontal" ? "horizontal" : "vertical" : "vertical";
1432
+ const outputId = nodeId === "ROOT" ? "root" : nodeId;
1433
+ const childNodes = (node.nodes ?? []).map(
1434
+ (childId) => buildNodeFromSchema(childId, schema, customComponents, visited, nodeId)
1435
+ );
1436
+ switch (resolvedName) {
1437
+ case "PF.Screen":
1438
+ return transformScreen(outputId, raw, childNodes);
1439
+ case "PF.Frame":
1440
+ return transformFrame(outputId, raw, childNodes, parentDirection);
1441
+ case "PF.Text":
1442
+ return transformText(outputId, raw, parentDirection);
1443
+ case "PF.Image":
1444
+ return transformImage(outputId, raw, childNodes, parentDirection);
1445
+ case "PF.Input":
1446
+ return transformInput(outputId, raw, parentDirection);
1447
+ case "PF.List":
1448
+ return transformList(outputId, raw, childNodes, parentDirection);
1449
+ case "PF.Icon":
1450
+ return transformIcon(outputId, raw);
1451
+ case "Component":
1452
+ return resolveCustomComponent(outputId, raw, parentDirection, customComponents);
1453
+ default:
1454
+ return { id: outputId, type: "view", props: {}, children: childNodes };
1455
+ }
1456
+ }
1457
+ function resolveCustomComponent(id, raw, parentDirection, customComponents) {
1458
+ const { actions, condition } = extractMeta(raw);
1459
+ const props = stripBridgeKeys(raw);
1460
+ const { componentId, componentKey, propValues, ...frameProps } = props;
1461
+ const key = componentKey ?? componentId;
1462
+ const def = key ? customComponents[key] : void 0;
1463
+ const wrapperStyle = buildFrameStyle(frameProps, parentDirection);
1464
+ const vis = props.visible !== void 0 && props.visible !== true ? { visible: props.visible } : {};
1465
+ if (!def) {
1466
+ return {
1467
+ id,
1468
+ type: "view",
1469
+ props: { ...Object.keys(wrapperStyle).length ? { style: wrapperStyle } : {}, ...vis },
1470
+ children: []
1471
+ };
1472
+ }
1473
+ const componentVisited = /* @__PURE__ */ new Set();
1474
+ const componentRoot = buildNodeFromSchema(
1475
+ def.rootNodeId,
1476
+ def.nodes,
1477
+ customComponents,
1478
+ componentVisited
1479
+ );
1480
+ if (propValues && Object.keys(propValues).length) {
1481
+ componentRoot.props = { ...componentRoot.props, _propValues: propValues };
1482
+ }
1483
+ if (Object.keys(wrapperStyle).length > 0) {
1484
+ const wrapper = {
1485
+ id,
1486
+ type: "view",
1487
+ props: { style: wrapperStyle, ...vis },
1488
+ children: [componentRoot]
1489
+ };
1490
+ if (condition !== void 0) wrapper.if = condition;
1491
+ if (actions) wrapper.actions = actions;
1492
+ return wrapper;
1493
+ }
1494
+ componentRoot.id = id;
1495
+ componentRoot.props = { ...componentRoot.props, ...vis };
1496
+ if (condition !== void 0) componentRoot.if = condition;
1497
+ if (actions) componentRoot.actions = actions;
1498
+ return componentRoot;
1499
+ }
1500
+ function transformCraftToSDUI(craftJson, customComponents = {}) {
1501
+ if (!craftJson || typeof craftJson !== "object") {
1502
+ throw new Error("Invalid craft schema");
1503
+ }
1504
+ const visited = /* @__PURE__ */ new Set();
1505
+ return buildNodeFromSchema("ROOT", craftJson, customComponents, visited);
1506
+ }
1507
+
740
1508
  // src/index.ts
741
1509
  var PushFrame = {
742
1510
  // Slot components
@@ -783,6 +1551,7 @@ exports.evaluateIf = evaluateIf;
783
1551
  exports.resolveDeep = resolveDeep;
784
1552
  exports.resolveProps = resolveProps;
785
1553
  exports.resolveValue = resolveValue;
1554
+ exports.transformCraftToSDUI = transformCraftToSDUI;
786
1555
  exports.usePushFrameContext = usePushFrameContext;
787
1556
  //# sourceMappingURL=index.js.map
788
1557
  //# sourceMappingURL=index.js.map