@pushframe/sdk 0.1.5 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/lib/commonjs/PushframeProvider.js +69 -0
  2. package/lib/commonjs/PushframeProvider.js.map +1 -0
  3. package/lib/commonjs/PushframeScreen.js +69 -0
  4. package/lib/commonjs/PushframeScreen.js.map +1 -0
  5. package/lib/commonjs/bindings.js +73 -0
  6. package/lib/commonjs/bindings.js.map +1 -0
  7. package/lib/commonjs/components/ButtonComponent.js +72 -0
  8. package/lib/commonjs/components/ButtonComponent.js.map +1 -0
  9. package/lib/commonjs/components/FlatListComponent.js +73 -0
  10. package/lib/commonjs/components/FlatListComponent.js.map +1 -0
  11. package/lib/commonjs/components/ImageComponent.js +67 -0
  12. package/lib/commonjs/components/ImageComponent.js.map +1 -0
  13. package/lib/commonjs/components/PushFrameComponent.js +179 -0
  14. package/lib/commonjs/components/PushFrameComponent.js.map +1 -0
  15. package/lib/commonjs/components/PushFrameProvider.js +115 -0
  16. package/lib/commonjs/components/PushFrameProvider.js.map +1 -0
  17. package/lib/commonjs/components/PushFrameScreen.js +39 -0
  18. package/lib/commonjs/components/PushFrameScreen.js.map +1 -0
  19. package/lib/commonjs/components/ScrollViewComponent.js +64 -0
  20. package/lib/commonjs/components/ScrollViewComponent.js.map +1 -0
  21. package/lib/commonjs/components/StackComponent.js +61 -0
  22. package/lib/commonjs/components/StackComponent.js.map +1 -0
  23. package/lib/commonjs/components/TextComponent.js +62 -0
  24. package/lib/commonjs/components/TextComponent.js.map +1 -0
  25. package/lib/commonjs/conditions.js +44 -0
  26. package/lib/commonjs/conditions.js.map +1 -0
  27. package/lib/commonjs/context/PushFrameContext.js +33 -0
  28. package/lib/commonjs/context/PushFrameContext.js.map +1 -0
  29. package/lib/commonjs/index.js +200 -0
  30. package/lib/commonjs/index.js.map +1 -0
  31. package/lib/commonjs/overlays/BottomSheetHost.js +144 -0
  32. package/lib/commonjs/overlays/BottomSheetHost.js.map +1 -0
  33. package/lib/commonjs/overlays/ToastHost.js +135 -0
  34. package/lib/commonjs/overlays/ToastHost.js.map +1 -0
  35. package/lib/commonjs/package.json +1 -0
  36. package/lib/commonjs/primitives/ActivityIndicator.js +24 -0
  37. package/lib/commonjs/primitives/ActivityIndicator.js.map +1 -0
  38. package/lib/commonjs/primitives/FlatList.js +34 -0
  39. package/lib/commonjs/primitives/FlatList.js.map +1 -0
  40. package/lib/commonjs/primitives/Image.js +33 -0
  41. package/lib/commonjs/primitives/Image.js.map +1 -0
  42. package/lib/commonjs/primitives/KeyboardAvoidingView.js +24 -0
  43. package/lib/commonjs/primitives/KeyboardAvoidingView.js.map +1 -0
  44. package/lib/commonjs/primitives/Modal.js +24 -0
  45. package/lib/commonjs/primitives/Modal.js.map +1 -0
  46. package/lib/commonjs/primitives/Pressable.js +26 -0
  47. package/lib/commonjs/primitives/Pressable.js.map +1 -0
  48. package/lib/commonjs/primitives/SafeAreaView.js +38 -0
  49. package/lib/commonjs/primitives/SafeAreaView.js.map +1 -0
  50. package/lib/commonjs/primitives/ScrollView.js +26 -0
  51. package/lib/commonjs/primitives/ScrollView.js.map +1 -0
  52. package/lib/commonjs/primitives/StatusBar.js +24 -0
  53. package/lib/commonjs/primitives/StatusBar.js.map +1 -0
  54. package/lib/commonjs/primitives/Switch.js +28 -0
  55. package/lib/commonjs/primitives/Switch.js.map +1 -0
  56. package/lib/commonjs/primitives/Text.js +37 -0
  57. package/lib/commonjs/primitives/Text.js.map +1 -0
  58. package/lib/commonjs/primitives/TextInput.js +31 -0
  59. package/lib/commonjs/primitives/TextInput.js.map +1 -0
  60. package/lib/commonjs/primitives/View.js +24 -0
  61. package/lib/commonjs/primitives/View.js.map +1 -0
  62. package/lib/commonjs/primitives/index.js +97 -0
  63. package/lib/commonjs/primitives/index.js.map +1 -0
  64. package/lib/commonjs/registry/ComponentRegistry.js +70 -0
  65. package/lib/commonjs/registry/ComponentRegistry.js.map +1 -0
  66. package/lib/commonjs/registry.js +94 -0
  67. package/lib/commonjs/registry.js.map +1 -0
  68. package/lib/commonjs/renderer/RecursiveRenderer.js +202 -0
  69. package/lib/commonjs/renderer/RecursiveRenderer.js.map +1 -0
  70. package/lib/commonjs/renderer/bindingResolver.js +98 -0
  71. package/lib/commonjs/renderer/bindingResolver.js.map +1 -0
  72. package/lib/commonjs/renderer/conditionalEvaluator.js +31 -0
  73. package/lib/commonjs/renderer/conditionalEvaluator.js.map +1 -0
  74. package/lib/commonjs/renderer.js +107 -0
  75. package/lib/commonjs/renderer.js.map +1 -0
  76. package/lib/commonjs/schema.js +79 -0
  77. package/lib/commonjs/schema.js.map +1 -0
  78. package/lib/commonjs/transformer/index.js +1055 -0
  79. package/lib/commonjs/transformer/index.js.map +1 -0
  80. package/lib/commonjs/transport.js +86 -0
  81. package/lib/commonjs/transport.js.map +1 -0
  82. package/lib/module/PushframeProvider.js +62 -0
  83. package/lib/module/PushframeProvider.js.map +1 -0
  84. package/lib/module/PushframeScreen.js +65 -0
  85. package/lib/module/PushframeScreen.js.map +1 -0
  86. package/lib/module/bindings.js +68 -0
  87. package/lib/module/bindings.js.map +1 -0
  88. package/lib/module/components/ButtonComponent.js +67 -0
  89. package/lib/module/components/ButtonComponent.js.map +1 -0
  90. package/lib/module/components/FlatListComponent.js +68 -0
  91. package/lib/module/components/FlatListComponent.js.map +1 -0
  92. package/lib/module/components/ImageComponent.js +62 -0
  93. package/lib/module/components/ImageComponent.js.map +1 -0
  94. package/lib/module/components/PushFrameComponent.js +174 -0
  95. package/lib/module/components/PushFrameComponent.js.map +1 -0
  96. package/lib/module/components/PushFrameProvider.js +110 -0
  97. package/lib/module/components/PushFrameProvider.js.map +1 -0
  98. package/lib/module/components/PushFrameScreen.js +34 -0
  99. package/lib/module/components/PushFrameScreen.js.map +1 -0
  100. package/lib/module/components/ScrollViewComponent.js +59 -0
  101. package/lib/module/components/ScrollViewComponent.js.map +1 -0
  102. package/lib/module/components/StackComponent.js +56 -0
  103. package/lib/module/components/StackComponent.js.map +1 -0
  104. package/lib/module/components/TextComponent.js +57 -0
  105. package/lib/module/components/TextComponent.js.map +1 -0
  106. package/lib/module/conditions.js +40 -0
  107. package/lib/module/conditions.js.map +1 -0
  108. package/lib/module/context/PushFrameContext.js +29 -0
  109. package/lib/module/context/PushFrameContext.js.map +1 -0
  110. package/lib/module/index.js +99 -0
  111. package/lib/module/index.js.map +1 -0
  112. package/lib/module/overlays/BottomSheetHost.js +139 -0
  113. package/lib/module/overlays/BottomSheetHost.js.map +1 -0
  114. package/lib/module/overlays/ToastHost.js +130 -0
  115. package/lib/module/overlays/ToastHost.js.map +1 -0
  116. package/lib/module/primitives/ActivityIndicator.js +19 -0
  117. package/lib/module/primitives/ActivityIndicator.js.map +1 -0
  118. package/lib/module/primitives/FlatList.js +29 -0
  119. package/lib/module/primitives/FlatList.js.map +1 -0
  120. package/lib/module/primitives/Image.js +28 -0
  121. package/lib/module/primitives/Image.js.map +1 -0
  122. package/lib/module/primitives/KeyboardAvoidingView.js +19 -0
  123. package/lib/module/primitives/KeyboardAvoidingView.js.map +1 -0
  124. package/lib/module/primitives/Modal.js +19 -0
  125. package/lib/module/primitives/Modal.js.map +1 -0
  126. package/lib/module/primitives/Pressable.js +21 -0
  127. package/lib/module/primitives/Pressable.js.map +1 -0
  128. package/lib/module/primitives/SafeAreaView.js +33 -0
  129. package/lib/module/primitives/SafeAreaView.js.map +1 -0
  130. package/lib/module/primitives/ScrollView.js +21 -0
  131. package/lib/module/primitives/ScrollView.js.map +1 -0
  132. package/lib/module/primitives/StatusBar.js +19 -0
  133. package/lib/module/primitives/StatusBar.js.map +1 -0
  134. package/lib/module/primitives/Switch.js +23 -0
  135. package/lib/module/primitives/Switch.js.map +1 -0
  136. package/lib/module/primitives/Text.js +32 -0
  137. package/lib/module/primitives/Text.js.map +1 -0
  138. package/lib/module/primitives/TextInput.js +26 -0
  139. package/lib/module/primitives/TextInput.js.map +1 -0
  140. package/lib/module/primitives/View.js +19 -0
  141. package/lib/module/primitives/View.js.map +1 -0
  142. package/lib/module/primitives/index.js +16 -0
  143. package/lib/module/primitives/index.js.map +1 -0
  144. package/lib/module/registry/ComponentRegistry.js +66 -0
  145. package/lib/module/registry/ComponentRegistry.js.map +1 -0
  146. package/lib/module/registry.js +88 -0
  147. package/lib/module/registry.js.map +1 -0
  148. package/lib/module/renderer/RecursiveRenderer.js +197 -0
  149. package/lib/module/renderer/RecursiveRenderer.js.map +1 -0
  150. package/lib/module/renderer/bindingResolver.js +92 -0
  151. package/lib/module/renderer/bindingResolver.js.map +1 -0
  152. package/lib/module/renderer/conditionalEvaluator.js +28 -0
  153. package/lib/module/renderer/conditionalEvaluator.js.map +1 -0
  154. package/lib/module/renderer.js +103 -0
  155. package/lib/module/renderer.js.map +1 -0
  156. package/lib/module/schema.js +74 -0
  157. package/lib/module/schema.js.map +1 -0
  158. package/lib/module/transformer/index.js +1051 -0
  159. package/lib/module/transformer/index.js.map +1 -0
  160. package/lib/module/transport.js +82 -0
  161. package/lib/module/transport.js.map +1 -0
  162. package/lib/typescript/PushframeProvider.d.ts +58 -0
  163. package/lib/typescript/PushframeProvider.d.ts.map +1 -0
  164. package/lib/typescript/PushframeScreen.d.ts +36 -0
  165. package/lib/typescript/PushframeScreen.d.ts.map +1 -0
  166. package/lib/typescript/bindings.d.ts +29 -0
  167. package/lib/typescript/bindings.d.ts.map +1 -0
  168. package/lib/typescript/components/ButtonComponent.d.ts +11 -0
  169. package/lib/typescript/components/ButtonComponent.d.ts.map +1 -0
  170. package/lib/typescript/components/FlatListComponent.d.ts +28 -0
  171. package/lib/typescript/components/FlatListComponent.d.ts.map +1 -0
  172. package/lib/typescript/components/ImageComponent.d.ts +12 -0
  173. package/lib/typescript/components/ImageComponent.d.ts.map +1 -0
  174. package/lib/typescript/components/PushFrameComponent.d.ts +48 -0
  175. package/lib/typescript/components/PushFrameComponent.d.ts.map +1 -0
  176. package/lib/typescript/components/PushFrameProvider.d.ts +51 -0
  177. package/lib/typescript/components/PushFrameProvider.d.ts.map +1 -0
  178. package/lib/typescript/components/PushFrameScreen.d.ts +15 -0
  179. package/lib/typescript/components/PushFrameScreen.d.ts.map +1 -0
  180. package/lib/typescript/components/ScrollViewComponent.d.ts +19 -0
  181. package/lib/typescript/components/ScrollViewComponent.d.ts.map +1 -0
  182. package/lib/typescript/components/StackComponent.d.ts +16 -0
  183. package/lib/typescript/components/StackComponent.d.ts.map +1 -0
  184. package/lib/typescript/components/TextComponent.d.ts +13 -0
  185. package/lib/typescript/components/TextComponent.d.ts.map +1 -0
  186. package/lib/typescript/conditions.d.ts +12 -0
  187. package/lib/typescript/conditions.d.ts.map +1 -0
  188. package/lib/typescript/context/PushFrameContext.d.ts +57 -0
  189. package/lib/typescript/context/PushFrameContext.d.ts.map +1 -0
  190. package/lib/typescript/index.d.ts +74 -0
  191. package/lib/typescript/index.d.ts.map +1 -0
  192. package/lib/typescript/overlays/BottomSheetHost.d.ts +21 -0
  193. package/lib/typescript/overlays/BottomSheetHost.d.ts.map +1 -0
  194. package/lib/typescript/overlays/ToastHost.d.ts +12 -0
  195. package/lib/typescript/overlays/ToastHost.d.ts.map +1 -0
  196. package/lib/typescript/primitives/ActivityIndicator.d.ts +12 -0
  197. package/lib/typescript/primitives/ActivityIndicator.d.ts.map +1 -0
  198. package/lib/typescript/primitives/FlatList.d.ts +29 -0
  199. package/lib/typescript/primitives/FlatList.d.ts.map +1 -0
  200. package/lib/typescript/primitives/Image.d.ts +20 -0
  201. package/lib/typescript/primitives/Image.d.ts.map +1 -0
  202. package/lib/typescript/primitives/KeyboardAvoidingView.d.ts +12 -0
  203. package/lib/typescript/primitives/KeyboardAvoidingView.d.ts.map +1 -0
  204. package/lib/typescript/primitives/Modal.d.ts +12 -0
  205. package/lib/typescript/primitives/Modal.d.ts.map +1 -0
  206. package/lib/typescript/primitives/Pressable.d.ts +14 -0
  207. package/lib/typescript/primitives/Pressable.d.ts.map +1 -0
  208. package/lib/typescript/primitives/SafeAreaView.d.ts +20 -0
  209. package/lib/typescript/primitives/SafeAreaView.d.ts.map +1 -0
  210. package/lib/typescript/primitives/ScrollView.d.ts +15 -0
  211. package/lib/typescript/primitives/ScrollView.d.ts.map +1 -0
  212. package/lib/typescript/primitives/StatusBar.d.ts +12 -0
  213. package/lib/typescript/primitives/StatusBar.d.ts.map +1 -0
  214. package/lib/typescript/primitives/Switch.d.ts +19 -0
  215. package/lib/typescript/primitives/Switch.d.ts.map +1 -0
  216. package/lib/typescript/primitives/Text.d.ts +25 -0
  217. package/lib/typescript/primitives/Text.d.ts.map +1 -0
  218. package/lib/typescript/primitives/TextInput.d.ts +25 -0
  219. package/lib/typescript/primitives/TextInput.d.ts.map +1 -0
  220. package/lib/typescript/primitives/View.d.ts +12 -0
  221. package/lib/typescript/primitives/View.d.ts.map +1 -0
  222. package/lib/typescript/primitives/index.d.ts +27 -0
  223. package/lib/typescript/primitives/index.d.ts.map +1 -0
  224. package/lib/typescript/registry/ComponentRegistry.d.ts +21 -0
  225. package/lib/typescript/registry/ComponentRegistry.d.ts.map +1 -0
  226. package/lib/typescript/registry.d.ts +57 -0
  227. package/lib/typescript/registry.d.ts.map +1 -0
  228. package/lib/typescript/renderer/RecursiveRenderer.d.ts +32 -0
  229. package/lib/typescript/renderer/RecursiveRenderer.d.ts.map +1 -0
  230. package/lib/typescript/renderer/bindingResolver.d.ts +26 -0
  231. package/lib/typescript/renderer/bindingResolver.d.ts.map +1 -0
  232. package/lib/typescript/renderer/conditionalEvaluator.d.ts +15 -0
  233. package/lib/typescript/renderer/conditionalEvaluator.d.ts.map +1 -0
  234. package/lib/typescript/renderer.d.ts +29 -0
  235. package/lib/typescript/renderer.d.ts.map +1 -0
  236. package/lib/typescript/schema.d.ts +84 -0
  237. package/lib/typescript/schema.d.ts.map +1 -0
  238. package/lib/typescript/transformer/index.d.ts +49 -0
  239. package/lib/typescript/transformer/index.d.ts.map +1 -0
  240. package/lib/typescript/transport.d.ts +19 -0
  241. package/lib/typescript/transport.d.ts.map +1 -0
  242. package/package.json +20 -18
  243. package/src/PushframeProvider.tsx +119 -0
  244. package/src/PushframeScreen.tsx +107 -0
  245. package/src/bindings.ts +72 -0
  246. package/src/components/ButtonComponent.tsx +87 -0
  247. package/src/components/FlatListComponent.tsx +86 -0
  248. package/src/components/ImageComponent.tsx +70 -0
  249. package/src/components/PushFrameComponent.tsx +221 -0
  250. package/src/components/PushFrameProvider.tsx +177 -0
  251. package/src/components/PushFrameScreen.tsx +30 -0
  252. package/src/components/ScrollViewComponent.tsx +65 -0
  253. package/src/components/StackComponent.tsx +69 -0
  254. package/src/components/TextComponent.tsx +60 -0
  255. package/src/conditions.ts +46 -0
  256. package/src/context/PushFrameContext.ts +89 -0
  257. package/src/index.ts +119 -0
  258. package/src/overlays/BottomSheetHost.tsx +175 -0
  259. package/src/overlays/ToastHost.tsx +147 -0
  260. package/src/primitives/ActivityIndicator.tsx +21 -0
  261. package/src/primitives/FlatList.tsx +49 -0
  262. package/src/primitives/Image.tsx +26 -0
  263. package/src/primitives/KeyboardAvoidingView.tsx +21 -0
  264. package/src/primitives/Modal.tsx +17 -0
  265. package/src/primitives/Pressable.tsx +19 -0
  266. package/src/primitives/SafeAreaView.tsx +42 -0
  267. package/src/primitives/ScrollView.tsx +21 -0
  268. package/src/primitives/StatusBar.tsx +17 -0
  269. package/src/primitives/Switch.tsx +24 -0
  270. package/src/primitives/Text.tsx +43 -0
  271. package/src/primitives/TextInput.tsx +42 -0
  272. package/src/primitives/View.tsx +17 -0
  273. package/src/primitives/index.ts +38 -0
  274. package/src/registry/ComponentRegistry.ts +99 -0
  275. package/src/registry.ts +99 -0
  276. package/src/renderer/RecursiveRenderer.tsx +242 -0
  277. package/src/renderer/bindingResolver.ts +94 -0
  278. package/src/renderer/conditionalEvaluator.ts +29 -0
  279. package/src/renderer.tsx +124 -0
  280. package/src/schema.ts +132 -0
  281. package/src/transformer/index.ts +1016 -0
  282. package/src/transport.ts +104 -0
  283. package/dist/index.d.mts +0 -534
  284. package/dist/index.d.ts +0 -534
  285. package/dist/index.js +0 -1572
  286. package/dist/index.js.map +0 -1
  287. package/dist/index.mjs +0 -1541
  288. package/dist/index.mjs.map +0 -1
@@ -0,0 +1,87 @@
1
+ import React from 'react';
2
+ import { TouchableOpacity, Text, StyleSheet, ActivityIndicator } from 'react-native';
3
+ import type { PushframeComponentProps } from '../registry';
4
+
5
+ export type ButtonVariant = 'primary' | 'secondary' | 'ghost';
6
+
7
+ export interface ButtonProps {
8
+ label?: string;
9
+ variant?: ButtonVariant;
10
+ disabled?: boolean;
11
+ loading?: boolean;
12
+ onPress?: () => void;
13
+ }
14
+
15
+ export function ButtonComponent({ props }: PushframeComponentProps<ButtonProps>) {
16
+ const { label = 'Button', variant = 'primary', disabled = false, loading = false, onPress } = props;
17
+
18
+ return (
19
+ <TouchableOpacity
20
+ style={[
21
+ styles.base,
22
+ variant === 'secondary' && styles.secondary,
23
+ variant === 'ghost' && styles.ghost,
24
+ (disabled || loading) && styles.disabled,
25
+ ]}
26
+ onPress={onPress}
27
+ disabled={disabled || loading}
28
+ activeOpacity={0.8}
29
+ >
30
+ {loading ? (
31
+ <ActivityIndicator
32
+ size="small"
33
+ color={variant === 'primary' ? '#ffffff' : '#2563eb'}
34
+ />
35
+ ) : (
36
+ <Text
37
+ style={[
38
+ styles.label,
39
+ variant === 'secondary' && styles.labelSecondary,
40
+ variant === 'ghost' && styles.labelGhost,
41
+ (disabled || loading) && styles.labelDisabled,
42
+ ]}
43
+ >
44
+ {label}
45
+ </Text>
46
+ )}
47
+ </TouchableOpacity>
48
+ );
49
+ }
50
+
51
+ const styles = StyleSheet.create({
52
+ base: {
53
+ backgroundColor: '#2563eb',
54
+ paddingVertical: 12,
55
+ paddingHorizontal: 24,
56
+ borderRadius: 8,
57
+ alignItems: 'center',
58
+ justifyContent: 'center',
59
+ minHeight: 44,
60
+ },
61
+ secondary: {
62
+ backgroundColor: 'transparent',
63
+ borderWidth: 1.5,
64
+ borderColor: '#2563eb',
65
+ },
66
+ ghost: {
67
+ backgroundColor: 'transparent',
68
+ borderWidth: 0,
69
+ },
70
+ disabled: {
71
+ opacity: 0.4,
72
+ },
73
+ label: {
74
+ color: '#ffffff',
75
+ fontSize: 16,
76
+ fontWeight: '600',
77
+ },
78
+ labelSecondary: {
79
+ color: '#2563eb',
80
+ },
81
+ labelGhost: {
82
+ color: '#2563eb',
83
+ },
84
+ labelDisabled: {
85
+ opacity: 0.6,
86
+ },
87
+ });
@@ -0,0 +1,86 @@
1
+ import React from 'react';
2
+ import { FlatList, View } from 'react-native';
3
+ import type { ReactElement } from 'react';
4
+ import type { PushframeComponentProps } from '../registry';
5
+
6
+ export interface FlatListProps {
7
+ /**
8
+ * Array of data items to render. Typically bound from context via
9
+ * { "$bind": "path.to.array" }. Each item is available as `item` and
10
+ * the index as `index` inside the item template (first child node).
11
+ */
12
+ data?: Record<string, unknown>[];
13
+ /**
14
+ * Name of the field on each item to use as the React key.
15
+ * @default "id"
16
+ */
17
+ keyExtractor?: string;
18
+ horizontal?: boolean;
19
+ numColumns?: number;
20
+ padding?: number;
21
+ paddingTop?: number;
22
+ paddingBottom?: number;
23
+ paddingLeft?: number;
24
+ paddingRight?: number;
25
+ backgroundColor?: string;
26
+ flex?: number;
27
+ showsScrollIndicator?: boolean;
28
+ /** Height of the separator between items (vertical lists only). */
29
+ itemSeparatorHeight?: number;
30
+ }
31
+
32
+ export function FlatListComponent({
33
+ props,
34
+ rawChildren,
35
+ renderChild,
36
+ }: PushframeComponentProps<FlatListProps>) {
37
+ const {
38
+ data = [],
39
+ keyExtractor = 'id',
40
+ horizontal = false,
41
+ numColumns = 1,
42
+ padding,
43
+ paddingTop,
44
+ paddingBottom,
45
+ paddingLeft,
46
+ paddingRight,
47
+ backgroundColor,
48
+ flex,
49
+ showsScrollIndicator = true,
50
+ itemSeparatorHeight = 0,
51
+ } = props;
52
+
53
+ // The first child node is used as the item template.
54
+ const itemTemplate = rawChildren?.[0];
55
+
56
+ return (
57
+ <FlatList
58
+ data={data}
59
+ horizontal={horizontal}
60
+ numColumns={horizontal ? 1 : numColumns}
61
+ showsHorizontalScrollIndicator={horizontal ? showsScrollIndicator : false}
62
+ showsVerticalScrollIndicator={!horizontal ? showsScrollIndicator : false}
63
+ keyExtractor={(item, index) =>
64
+ String((item as Record<string, unknown>)[keyExtractor] ?? index)
65
+ }
66
+ renderItem={({ item, index }) => {
67
+ if (!itemTemplate || !renderChild) return null;
68
+ return renderChild(itemTemplate, { item, index }) as ReactElement | null;
69
+ }}
70
+ ItemSeparatorComponent={
71
+ itemSeparatorHeight > 0
72
+ ? () => <View style={{ height: itemSeparatorHeight }} />
73
+ : undefined
74
+ }
75
+ style={[
76
+ padding !== undefined && { padding },
77
+ paddingTop !== undefined && { paddingTop },
78
+ paddingBottom !== undefined && { paddingBottom },
79
+ paddingLeft !== undefined && { paddingLeft },
80
+ paddingRight !== undefined && { paddingRight },
81
+ backgroundColor !== undefined && { backgroundColor },
82
+ flex !== undefined && { flex },
83
+ ]}
84
+ />
85
+ );
86
+ }
@@ -0,0 +1,70 @@
1
+ import React from 'react';
2
+ import { Image, StyleSheet, View, Text } from 'react-native';
3
+ import type { PushframeComponentProps } from '../registry';
4
+
5
+ export type ImageResizeMode = 'cover' | 'contain' | 'stretch' | 'center';
6
+
7
+ export interface ImageProps {
8
+ src?: string;
9
+ alt?: string;
10
+ width?: number;
11
+ height?: number;
12
+ resizeMode?: ImageResizeMode;
13
+ borderRadius?: number;
14
+ }
15
+
16
+ export function ImageComponent({ props }: PushframeComponentProps<ImageProps>) {
17
+ const {
18
+ src,
19
+ alt = '',
20
+ width,
21
+ height = 200,
22
+ resizeMode = 'cover',
23
+ borderRadius,
24
+ } = props;
25
+
26
+ if (!src) {
27
+ return (
28
+ <View
29
+ style={[
30
+ styles.placeholder,
31
+ width !== undefined && { width },
32
+ { height },
33
+ borderRadius !== undefined && { borderRadius },
34
+ ]}
35
+ >
36
+ <Text style={styles.placeholderText}>{alt || 'No image'}</Text>
37
+ </View>
38
+ );
39
+ }
40
+
41
+ return (
42
+ <Image
43
+ source={{ uri: src }}
44
+ accessibilityLabel={alt}
45
+ resizeMode={resizeMode}
46
+ style={[
47
+ styles.image,
48
+ width !== undefined && { width },
49
+ { height },
50
+ borderRadius !== undefined && { borderRadius },
51
+ ]}
52
+ />
53
+ );
54
+ }
55
+
56
+ const styles = StyleSheet.create({
57
+ image: {
58
+ width: '100%',
59
+ },
60
+ placeholder: {
61
+ width: '100%',
62
+ backgroundColor: '#e5e7eb',
63
+ alignItems: 'center',
64
+ justifyContent: 'center',
65
+ },
66
+ placeholderText: {
67
+ color: '#9ca3af',
68
+ fontSize: 14,
69
+ },
70
+ });
@@ -0,0 +1,221 @@
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
+ import type { ReactNode, ReactElement } from 'react';
3
+ import { usePushFrameContext } from '../context/PushFrameContext';
4
+ import type { SchemaNode, ToastPayload, BottomSheetPayload } from '../context/PushFrameContext';
5
+ import { RecursiveRenderer } from '../renderer/RecursiveRenderer';
6
+
7
+ // ---------------------------------------------------------------------------
8
+ // Shared slot props (used by both Screen and Component)
9
+ // ---------------------------------------------------------------------------
10
+
11
+ export interface PushFrameSlotProps {
12
+ /** ID of the screen/component to fetch from the delivery service. */
13
+ id: string;
14
+ /**
15
+ * Data context local to this slot. Merged with the Provider context;
16
+ * local keys win on conflict.
17
+ */
18
+ context?: Record<string, unknown>;
19
+ /**
20
+ * External loading signal from the host app. When true, loading UI is shown
21
+ * even if the schema has already been fetched.
22
+ */
23
+ isLoading?: boolean;
24
+ /**
25
+ * Loading UI shown while isLoading=true OR schema is being fetched.
26
+ * Falls back to Provider-level loadingComponent.
27
+ */
28
+ loadingComponent?: ReactNode;
29
+ /**
30
+ * Fallback UI shown on fetch error or unresolvable component type.
31
+ * Falls back to Provider-level fallbackComponent.
32
+ */
33
+ fallbackComponent?: ReactNode;
34
+ /**
35
+ * Slot-level action handler. Return true to stop the action from bubbling
36
+ * to the Provider-level onAction.
37
+ */
38
+ onAction?: (action: string, payload?: Record<string, unknown>) => boolean | void;
39
+ }
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Schema fetch
43
+ // ---------------------------------------------------------------------------
44
+
45
+ async function fetchSchema(
46
+ baseUrl: string,
47
+ apiKey: string,
48
+ path: string,
49
+ ): Promise<SchemaNode> {
50
+ const url = new URL(path, baseUrl + '/').toString();
51
+ const headers: Record<string, string> = {
52
+ Accept: 'application/json',
53
+ Authorization: `Bearer ${apiKey}`,
54
+ 'x-project-key': apiKey,
55
+ };
56
+
57
+ const res = await fetch(url, { headers });
58
+ if (!res.ok) {
59
+ throw new Error(`[PushFrame] HTTP ${res.status} fetching "${path}"`);
60
+ }
61
+
62
+ const envelope = (await res.json()) as { schema?: SchemaNode } & SchemaNode;
63
+ return envelope.schema ?? envelope;
64
+ }
65
+
66
+ // ---------------------------------------------------------------------------
67
+ // PushFrameComponent
68
+ // ---------------------------------------------------------------------------
69
+
70
+ interface PushFrameComponentInternalProps extends PushFrameSlotProps {
71
+ /** Resource type — determines which delivery endpoint to hit. */
72
+ resourceType: 'screens' | 'components';
73
+ /** When true, wraps the rendered output with flex:1 (used by Screen). */
74
+ fullscreen?: boolean;
75
+ }
76
+
77
+ function PushFrameComponentInternal({
78
+ id,
79
+ resourceType,
80
+ context: slotContext,
81
+ isLoading: externalLoading,
82
+ loadingComponent: slotLoading,
83
+ fallbackComponent: slotFallback,
84
+ onAction: slotOnAction,
85
+ fullscreen,
86
+ }: PushFrameComponentInternalProps) {
87
+ const {
88
+ apiKey,
89
+ baseUrl,
90
+ appVersion,
91
+ globalContext,
92
+ registry,
93
+ loadingComponent: providerLoading,
94
+ fallbackComponent: providerFallback,
95
+ onAction: providerOnAction,
96
+ onError,
97
+ showToast,
98
+ showBottomSheet,
99
+ dismissBottomSheet,
100
+ } = usePushFrameContext();
101
+
102
+ const [schema, setSchema] = useState<SchemaNode | null>(null);
103
+ const [fetchError, setFetchError] = useState<Error | null>(null);
104
+ const [isFetching, setIsFetching] = useState(true);
105
+
106
+ // Build fetch path: {resourceType}/{id}/{appVersion} — "null" when not set
107
+ const fetchPath = `${resourceType}/${encodeURIComponent(id)}/${encodeURIComponent(appVersion ?? 'null')}`;
108
+
109
+ // Merged data context: provider → slot (local wins)
110
+ const mergedContext = slotContext ? { ...globalContext, ...slotContext } : globalContext;
111
+
112
+ // Resolved UI components with fallback precedence
113
+ const loadingUI = slotLoading !== undefined ? slotLoading : providerLoading;
114
+ const fallbackUI = slotFallback !== undefined ? slotFallback : providerFallback;
115
+
116
+ // Fetch schema
117
+ useEffect(() => {
118
+ let cancelled = false;
119
+ setSchema(null);
120
+ setFetchError(null);
121
+ setIsFetching(true);
122
+
123
+ fetchSchema(baseUrl, apiKey, fetchPath)
124
+ .then((s) => {
125
+ if (!cancelled) {
126
+ setSchema(s);
127
+ setIsFetching(false);
128
+ }
129
+ })
130
+ .catch((err: unknown) => {
131
+ if (!cancelled) {
132
+ const error = err instanceof Error ? err : new Error(String(err));
133
+ setFetchError(error);
134
+ setIsFetching(false);
135
+ onError?.(error);
136
+ }
137
+ });
138
+
139
+ return () => {
140
+ cancelled = true;
141
+ };
142
+ }, [fetchPath, baseUrl, apiKey, onError]);
143
+
144
+ // Dispatch action — handles built-ins, then bubbles via slot → provider
145
+ const dispatchAction = useCallback(
146
+ (action: string, payload?: Record<string, unknown>) => {
147
+ // Built-in actions — never bubble to host app
148
+ if (action === 'show-toast') {
149
+ showToast(payload as unknown as ToastPayload);
150
+ return;
151
+ }
152
+ if (action === 'show-bottom-sheet') {
153
+ showBottomSheet(payload as unknown as BottomSheetPayload);
154
+ return;
155
+ }
156
+ if (action === 'dismiss-bottom-sheet') {
157
+ dismissBottomSheet();
158
+ return;
159
+ }
160
+ if (action === 'scroll-to') {
161
+ // scroll-to is handled by the ScrollView primitive via ref; no bubbling needed
162
+ return;
163
+ }
164
+
165
+ // Bubble: slot onAction first; if it returns true, stop
166
+ const stopped = slotOnAction?.(action, payload);
167
+ if (stopped) return;
168
+
169
+ // Provider onAction
170
+ providerOnAction?.(action, payload);
171
+ },
172
+ [showToast, showBottomSheet, dismissBottomSheet, slotOnAction, providerOnAction],
173
+ );
174
+
175
+ // Loading state: external flag OR schema not yet fetched
176
+ const isShowingLoader = externalLoading || isFetching;
177
+
178
+ if (isShowingLoader) {
179
+ return (loadingUI as ReactElement | null) ?? null;
180
+ }
181
+
182
+ if (fetchError || !schema) {
183
+ return (fallbackUI as ReactElement | null) ?? null;
184
+ }
185
+
186
+ const rendered = (
187
+ <RecursiveRenderer
188
+ node={schema}
189
+ context={mergedContext}
190
+ registry={registry}
191
+ dispatchAction={dispatchAction}
192
+ fallbackComponent={fallbackUI}
193
+ />
194
+ );
195
+
196
+ if (fullscreen) {
197
+ // PushFrame.Screen wraps with flex:1
198
+ return <>{rendered}</>;
199
+ }
200
+
201
+ return rendered;
202
+ }
203
+
204
+ // ---------------------------------------------------------------------------
205
+ // Public PushFrameComponent
206
+ // ---------------------------------------------------------------------------
207
+
208
+ /**
209
+ * Fetches a component schema from GET /components/:id[/:appVersion] on the
210
+ * delivery service and renders it inline using RecursiveRenderer.
211
+ *
212
+ * Context is merged: Provider context → slot context (local wins).
213
+ * Actions bubble: slot onAction → Provider onAction.
214
+ * Built-in actions (show-toast, show-bottom-sheet, etc.) are handled internally.
215
+ */
216
+ export function PushFrameComponent(props: PushFrameSlotProps) {
217
+ return <PushFrameComponentInternal {...props} resourceType="components" />;
218
+ }
219
+
220
+ // Re-export internal for Screen to use
221
+ export { PushFrameComponentInternal };
@@ -0,0 +1,177 @@
1
+ import React, { useCallback, useMemo, useRef } from 'react';
2
+ import type { ReactNode } from 'react';
3
+ import { PushFrameContext } from '../context/PushFrameContext';
4
+ import type {
5
+ PushFrameContextValue,
6
+ ToastPayload,
7
+ BottomSheetPayload,
8
+ SchemaNode,
9
+ } from '../context/PushFrameContext';
10
+ import { ComponentRegistry } from '../registry/ComponentRegistry';
11
+ import { ToastHost } from '../overlays/ToastHost';
12
+ import type { ToastHostHandle } from '../overlays/ToastHost';
13
+ import { BottomSheetHost } from '../overlays/BottomSheetHost';
14
+ import type { BottomSheetHostHandle } from '../overlays/BottomSheetHost';
15
+ import { RecursiveRenderer } from '../renderer/RecursiveRenderer';
16
+
17
+ const DEFAULT_BASE_URL = 'https://api.pushframe.io';
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Props
21
+ // ---------------------------------------------------------------------------
22
+
23
+ export interface PushFrameProviderProps {
24
+ /** Pushframe API key — sent as Authorization header on all fetches. */
25
+ apiKey: string;
26
+ /**
27
+ * Runtime data context. Screens/Components bind to this via {{expressions}}.
28
+ * Merged at the Provider level; slot-level context wins on conflict.
29
+ */
30
+ context?: Record<string, unknown>;
31
+ /**
32
+ * Base URL of the Pushframe delivery service.
33
+ * Defaults to the production endpoint: https://api.pushframe.io
34
+ */
35
+ baseUrl?: string;
36
+ /**
37
+ * Semver string appended to fetch paths for schema version targeting.
38
+ * e.g. "1.2.3" → GET /screens/home/1.2.3
39
+ * When not provided, "null" is sent as the appVersion in the URL.
40
+ */
41
+ appVersion?: string;
42
+ /**
43
+ * Developer-registered native components merged into the ComponentRegistry.
44
+ * Built-in types cannot be overridden — conflicting keys are warned and skipped.
45
+ */
46
+ components?: Record<string, React.ComponentType<unknown>>;
47
+ /** Default loading UI shown while schema is fetching or isLoading=true. */
48
+ loadingComponent?: ReactNode;
49
+ /** Default fallback UI shown on fetch error or unknown component type. */
50
+ fallbackComponent?: ReactNode;
51
+ /**
52
+ * Global action handler. Receives actions that are not handled as built-ins
53
+ * and were not stopped by a slot-level onAction.
54
+ */
55
+ onAction?: (action: string, payload?: Record<string, unknown>) => void;
56
+ /** Called whenever a fetch or render error occurs. */
57
+ onError?: (error: Error) => void;
58
+ children: ReactNode;
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // PushFrameProvider
63
+ // ---------------------------------------------------------------------------
64
+
65
+ /**
66
+ * Root provider for the PushFrame SDK.
67
+ *
68
+ * Wrap your app (or a subtree) in this provider. All PushFrame.Screen and
69
+ * PushFrame.Component slots inside will fetch their schemas from the delivery
70
+ * service and render them using the shared ComponentRegistry and context.
71
+ *
72
+ * Also renders the ToastHost and BottomSheetHost overlay hosts at root level
73
+ * so any component in the tree can trigger them via built-in actions.
74
+ */
75
+ export function PushFrameProvider({
76
+ apiKey,
77
+ context = {},
78
+ baseUrl = DEFAULT_BASE_URL,
79
+ appVersion,
80
+ components,
81
+ loadingComponent,
82
+ fallbackComponent,
83
+ onAction,
84
+ onError,
85
+ children,
86
+ }: PushFrameProviderProps) {
87
+ // Registry — created once (components prop should be stable)
88
+ const registry = useMemo(
89
+ () => new ComponentRegistry(components),
90
+ // eslint-disable-next-line react-hooks/exhaustive-deps
91
+ [],
92
+ );
93
+
94
+ // Overlay refs
95
+ const toastRef = useRef<ToastHostHandle>(null);
96
+ const bottomSheetRef = useRef<BottomSheetHostHandle>(null);
97
+
98
+ const showToast = useCallback((payload: ToastPayload) => {
99
+ toastRef.current?.show(payload);
100
+ }, []);
101
+
102
+ const showBottomSheet = useCallback((payload: BottomSheetPayload) => {
103
+ bottomSheetRef.current?.show(payload);
104
+ }, []);
105
+
106
+ const dismissBottomSheet = useCallback(() => {
107
+ bottomSheetRef.current?.dismiss();
108
+ }, []);
109
+
110
+ // renderContent for BottomSheetHost — renders a schema node with the Provider's registry
111
+ const renderBottomSheetContent = useCallback(
112
+ (schema: SchemaNode, sheetContext?: Record<string, unknown>) => {
113
+ const mergedContext = sheetContext ? { ...context, ...sheetContext } : context;
114
+ // No-op dispatchAction for bottom sheet content (actions bubble via onAction)
115
+ const dispatchAction = (action: string, payload?: Record<string, unknown>) => {
116
+ if (action === 'dismiss-bottom-sheet') {
117
+ dismissBottomSheet();
118
+ return;
119
+ }
120
+ if (action === 'show-toast') {
121
+ showToast(payload as unknown as ToastPayload);
122
+ return;
123
+ }
124
+ onAction?.(action, payload);
125
+ };
126
+ return (
127
+ <RecursiveRenderer
128
+ node={schema}
129
+ context={mergedContext}
130
+ registry={registry}
131
+ dispatchAction={dispatchAction}
132
+ fallbackComponent={fallbackComponent}
133
+ />
134
+ );
135
+ },
136
+ [context, registry, fallbackComponent, onAction, dismissBottomSheet, showToast],
137
+ );
138
+
139
+ const contextValue = useMemo<PushFrameContextValue>(
140
+ () => ({
141
+ apiKey,
142
+ baseUrl,
143
+ appVersion,
144
+ globalContext: context,
145
+ registry,
146
+ loadingComponent,
147
+ fallbackComponent,
148
+ onAction,
149
+ onError,
150
+ showToast,
151
+ showBottomSheet,
152
+ dismissBottomSheet,
153
+ }),
154
+ [
155
+ apiKey,
156
+ baseUrl,
157
+ appVersion,
158
+ context,
159
+ registry,
160
+ loadingComponent,
161
+ fallbackComponent,
162
+ onAction,
163
+ onError,
164
+ showToast,
165
+ showBottomSheet,
166
+ dismissBottomSheet,
167
+ ],
168
+ );
169
+
170
+ return (
171
+ <PushFrameContext.Provider value={contextValue}>
172
+ {children}
173
+ <ToastHost ref={toastRef} />
174
+ <BottomSheetHost ref={bottomSheetRef} renderContent={renderBottomSheetContent} />
175
+ </PushFrameContext.Provider>
176
+ );
177
+ }
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import { PushFrameComponentInternal } from './PushFrameComponent';
4
+ import type { PushFrameSlotProps } from './PushFrameComponent';
5
+
6
+ /**
7
+ * Fetches a screen schema from GET /screens/:id[/:appVersion] on the delivery
8
+ * service and renders it with flex:1 sizing (full-page layout).
9
+ *
10
+ * Identical to PushFrame.Component in behaviour, but:
11
+ * - Fetches from /screens/:id instead of /components/:id
12
+ * - Applies flex:1 to its container so it fills the available space
13
+ *
14
+ * Context is merged: Provider context → slot context (local wins).
15
+ * Actions bubble: slot onAction → Provider onAction.
16
+ * Built-in actions (show-toast, show-bottom-sheet, etc.) are handled internally.
17
+ */
18
+ export function PushFrameScreen(props: PushFrameSlotProps) {
19
+ return (
20
+ <View style={styles.container}>
21
+ <PushFrameComponentInternal {...props} resourceType="screens" fullscreen />
22
+ </View>
23
+ );
24
+ }
25
+
26
+ const styles = StyleSheet.create({
27
+ container: {
28
+ flex: 1,
29
+ },
30
+ });