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