@pushframe/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +458 -0
- package/dist/index.d.ts +458 -0
- package/dist/index.js +788 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +758 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
import React4, { createContext, forwardRef, useState, useRef, useCallback, useEffect, useImperativeHandle, useContext, useMemo } from 'react';
|
|
2
|
+
import { ScrollView as ScrollView$1, Animated, View as View$1, Text as Text$1, StyleSheet, Dimensions, Modal as Modal$1, Pressable as Pressable$1, Image as Image$1, TextInput as TextInput$1, FlatList as FlatList$1, ActivityIndicator as ActivityIndicator$1, Switch as Switch$1, KeyboardAvoidingView as KeyboardAvoidingView$1, StatusBar as StatusBar$1 } from 'react-native';
|
|
3
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
7
|
+
}) : x)(function(x) {
|
|
8
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
|
+
});
|
|
11
|
+
var PushFrameContext = createContext(null);
|
|
12
|
+
function usePushFrameContext() {
|
|
13
|
+
const ctx = useContext(PushFrameContext);
|
|
14
|
+
if (!ctx) {
|
|
15
|
+
throw new Error("[PushFrame] usePushFrameContext must be called inside <PushFrame.Provider>");
|
|
16
|
+
}
|
|
17
|
+
return ctx;
|
|
18
|
+
}
|
|
19
|
+
function Text({ if: _if, actions: _actions, value, children, ...rest }) {
|
|
20
|
+
return /* @__PURE__ */ jsx(Text$1, { ...rest, children: value !== void 0 ? value : children });
|
|
21
|
+
}
|
|
22
|
+
function View({ if: _if, actions: _actions, ...rest }) {
|
|
23
|
+
return /* @__PURE__ */ jsx(View$1, { ...rest });
|
|
24
|
+
}
|
|
25
|
+
var ScrollView = forwardRef(function ScrollView2({ if: _if, actions: _actions, ...rest }, ref) {
|
|
26
|
+
return /* @__PURE__ */ jsx(ScrollView$1, { ref, ...rest });
|
|
27
|
+
});
|
|
28
|
+
function Image({ if: _if, actions: _actions, src, source, ...rest }) {
|
|
29
|
+
const resolvedSource = src ? { uri: src } : source ?? { uri: "" };
|
|
30
|
+
return /* @__PURE__ */ jsx(Image$1, { source: resolvedSource, ...rest });
|
|
31
|
+
}
|
|
32
|
+
function Pressable({ if: _if, actions: _actions, ...rest }) {
|
|
33
|
+
return /* @__PURE__ */ jsx(Pressable$1, { ...rest });
|
|
34
|
+
}
|
|
35
|
+
function TextInput({
|
|
36
|
+
if: _if,
|
|
37
|
+
actions: _actions,
|
|
38
|
+
onChange,
|
|
39
|
+
onSubmit,
|
|
40
|
+
...rest
|
|
41
|
+
}) {
|
|
42
|
+
return /* @__PURE__ */ jsx(
|
|
43
|
+
TextInput$1,
|
|
44
|
+
{
|
|
45
|
+
onChangeText: onChange,
|
|
46
|
+
onSubmitEditing: onSubmit ? () => onSubmit() : void 0,
|
|
47
|
+
...rest
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
function FlatList({
|
|
52
|
+
if: _if,
|
|
53
|
+
actions: _actions,
|
|
54
|
+
items,
|
|
55
|
+
direction,
|
|
56
|
+
numColumns,
|
|
57
|
+
...rest
|
|
58
|
+
}) {
|
|
59
|
+
return /* @__PURE__ */ jsx(
|
|
60
|
+
FlatList$1,
|
|
61
|
+
{
|
|
62
|
+
data: items,
|
|
63
|
+
horizontal: direction === "horizontal",
|
|
64
|
+
numColumns,
|
|
65
|
+
...rest
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function Modal({ if: _if, actions: _actions, ...rest }) {
|
|
70
|
+
return /* @__PURE__ */ jsx(Modal$1, { ...rest });
|
|
71
|
+
}
|
|
72
|
+
function ActivityIndicator({
|
|
73
|
+
if: _if,
|
|
74
|
+
actions: _actions,
|
|
75
|
+
...rest
|
|
76
|
+
}) {
|
|
77
|
+
return /* @__PURE__ */ jsx(ActivityIndicator$1, { ...rest });
|
|
78
|
+
}
|
|
79
|
+
function Switch({ if: _if, actions: _actions, onChange, ...rest }) {
|
|
80
|
+
return /* @__PURE__ */ jsx(Switch$1, { onValueChange: onChange, ...rest });
|
|
81
|
+
}
|
|
82
|
+
function KeyboardAvoidingView({
|
|
83
|
+
if: _if,
|
|
84
|
+
actions: _actions,
|
|
85
|
+
...rest
|
|
86
|
+
}) {
|
|
87
|
+
return /* @__PURE__ */ jsx(KeyboardAvoidingView$1, { ...rest });
|
|
88
|
+
}
|
|
89
|
+
var NativeSafeAreaView;
|
|
90
|
+
try {
|
|
91
|
+
NativeSafeAreaView = __require("react-native-safe-area-context").SafeAreaView;
|
|
92
|
+
} catch {
|
|
93
|
+
NativeSafeAreaView = __require("react-native").SafeAreaView;
|
|
94
|
+
}
|
|
95
|
+
function SafeAreaView({ if: _if, actions: _actions, ...rest }) {
|
|
96
|
+
return /* @__PURE__ */ jsx(NativeSafeAreaView, { ...rest });
|
|
97
|
+
}
|
|
98
|
+
function StatusBar({ if: _if, actions: _actions, ...rest }) {
|
|
99
|
+
return /* @__PURE__ */ jsx(StatusBar$1, { ...rest });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/registry/ComponentRegistry.ts
|
|
103
|
+
var BUILT_IN_TYPES = /* @__PURE__ */ new Set([
|
|
104
|
+
"text",
|
|
105
|
+
"view",
|
|
106
|
+
"scrollview",
|
|
107
|
+
"image",
|
|
108
|
+
"pressable",
|
|
109
|
+
"textinput",
|
|
110
|
+
"flatlist",
|
|
111
|
+
"modal",
|
|
112
|
+
"activityindicator",
|
|
113
|
+
"switch",
|
|
114
|
+
"keyboardavoidingview",
|
|
115
|
+
"safeareaview",
|
|
116
|
+
"statusbar"
|
|
117
|
+
]);
|
|
118
|
+
var BUILT_IN_COMPONENTS = /* @__PURE__ */ new Map([
|
|
119
|
+
["text", Text],
|
|
120
|
+
["view", View],
|
|
121
|
+
["scrollview", ScrollView],
|
|
122
|
+
["image", Image],
|
|
123
|
+
["pressable", Pressable],
|
|
124
|
+
["textinput", TextInput],
|
|
125
|
+
["flatlist", FlatList],
|
|
126
|
+
["modal", Modal],
|
|
127
|
+
["activityindicator", ActivityIndicator],
|
|
128
|
+
["switch", Switch],
|
|
129
|
+
["keyboardavoidingview", KeyboardAvoidingView],
|
|
130
|
+
["safeareaview", SafeAreaView],
|
|
131
|
+
["statusbar", StatusBar]
|
|
132
|
+
]);
|
|
133
|
+
var ComponentRegistry = class {
|
|
134
|
+
constructor(developerComponents) {
|
|
135
|
+
this.components = new Map(BUILT_IN_COMPONENTS);
|
|
136
|
+
if (developerComponents) {
|
|
137
|
+
for (const [type, component] of Object.entries(developerComponents)) {
|
|
138
|
+
if (BUILT_IN_TYPES.has(type)) {
|
|
139
|
+
console.warn(
|
|
140
|
+
`[PushFrame] ComponentRegistry: "${type}" is a built-in type and cannot be overridden. Registration skipped.`
|
|
141
|
+
);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
this.components.set(type, component);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Resolve a type string to its React component.
|
|
150
|
+
* Returns null if no component is registered for the type.
|
|
151
|
+
*/
|
|
152
|
+
resolve(type) {
|
|
153
|
+
return this.components.get(type) ?? null;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Returns all registered type strings (useful for debugging).
|
|
157
|
+
*/
|
|
158
|
+
types() {
|
|
159
|
+
return Array.from(this.components.keys());
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
var TYPE_COLORS = {
|
|
163
|
+
success: "#22c55e",
|
|
164
|
+
error: "#ef4444",
|
|
165
|
+
info: "#3b82f6",
|
|
166
|
+
warning: "#f59e0b"
|
|
167
|
+
};
|
|
168
|
+
var ToastHost = forwardRef(function ToastHost2(_props, ref) {
|
|
169
|
+
const [toast, setToast] = useState(null);
|
|
170
|
+
const opacity = useRef(new Animated.Value(0)).current;
|
|
171
|
+
const idRef = useRef(0);
|
|
172
|
+
const hide = useCallback(() => {
|
|
173
|
+
Animated.timing(opacity, {
|
|
174
|
+
toValue: 0,
|
|
175
|
+
duration: 200,
|
|
176
|
+
useNativeDriver: true
|
|
177
|
+
}).start(() => {
|
|
178
|
+
setToast(null);
|
|
179
|
+
});
|
|
180
|
+
}, [opacity]);
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
if (!toast) return;
|
|
183
|
+
Animated.timing(opacity, {
|
|
184
|
+
toValue: 1,
|
|
185
|
+
duration: 200,
|
|
186
|
+
useNativeDriver: true
|
|
187
|
+
}).start();
|
|
188
|
+
const timer = setTimeout(() => {
|
|
189
|
+
hide();
|
|
190
|
+
}, toast.duration);
|
|
191
|
+
return () => {
|
|
192
|
+
clearTimeout(timer);
|
|
193
|
+
};
|
|
194
|
+
}, [toast, opacity, hide]);
|
|
195
|
+
useImperativeHandle(
|
|
196
|
+
ref,
|
|
197
|
+
() => ({
|
|
198
|
+
show(payload) {
|
|
199
|
+
idRef.current += 1;
|
|
200
|
+
setToast({
|
|
201
|
+
id: idRef.current,
|
|
202
|
+
message: payload.message,
|
|
203
|
+
duration: payload.duration ?? 3e3,
|
|
204
|
+
type: payload.type ?? "info"
|
|
205
|
+
});
|
|
206
|
+
opacity.setValue(0);
|
|
207
|
+
}
|
|
208
|
+
}),
|
|
209
|
+
[opacity]
|
|
210
|
+
);
|
|
211
|
+
if (!toast) return null;
|
|
212
|
+
const backgroundColor = TYPE_COLORS[toast.type ?? "info"] ?? TYPE_COLORS.info;
|
|
213
|
+
return /* @__PURE__ */ jsx(View$1, { style: styles.container, pointerEvents: "none", children: /* @__PURE__ */ jsx(Animated.View, { style: [styles.toast, { backgroundColor, opacity }], children: /* @__PURE__ */ jsx(Text$1, { style: styles.message, children: toast.message }) }) });
|
|
214
|
+
});
|
|
215
|
+
var styles = StyleSheet.create({
|
|
216
|
+
container: {
|
|
217
|
+
position: "absolute",
|
|
218
|
+
bottom: 60,
|
|
219
|
+
left: 16,
|
|
220
|
+
right: 16,
|
|
221
|
+
alignItems: "center",
|
|
222
|
+
zIndex: 9999
|
|
223
|
+
},
|
|
224
|
+
toast: {
|
|
225
|
+
paddingVertical: 12,
|
|
226
|
+
paddingHorizontal: 20,
|
|
227
|
+
borderRadius: 8,
|
|
228
|
+
maxWidth: 400,
|
|
229
|
+
shadowColor: "#000",
|
|
230
|
+
shadowOffset: { width: 0, height: 2 },
|
|
231
|
+
shadowOpacity: 0.2,
|
|
232
|
+
shadowRadius: 4,
|
|
233
|
+
elevation: 6
|
|
234
|
+
},
|
|
235
|
+
message: {
|
|
236
|
+
color: "#fff",
|
|
237
|
+
fontSize: 14,
|
|
238
|
+
fontWeight: "500",
|
|
239
|
+
textAlign: "center"
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
var SCREEN_HEIGHT = Dimensions.get("window").height;
|
|
243
|
+
var BottomSheetHost = forwardRef(
|
|
244
|
+
function BottomSheetHost2({ renderContent }, ref) {
|
|
245
|
+
const [visible, setVisible] = useState(false);
|
|
246
|
+
const [payload, setPayload] = useState(null);
|
|
247
|
+
const translateY = useRef(new Animated.Value(SCREEN_HEIGHT)).current;
|
|
248
|
+
const animateIn = useCallback(() => {
|
|
249
|
+
Animated.spring(translateY, {
|
|
250
|
+
toValue: 0,
|
|
251
|
+
useNativeDriver: true,
|
|
252
|
+
bounciness: 4
|
|
253
|
+
}).start();
|
|
254
|
+
}, [translateY]);
|
|
255
|
+
const animateOut = useCallback(
|
|
256
|
+
(onDone) => {
|
|
257
|
+
Animated.timing(translateY, {
|
|
258
|
+
toValue: SCREEN_HEIGHT,
|
|
259
|
+
duration: 250,
|
|
260
|
+
useNativeDriver: true
|
|
261
|
+
}).start(() => {
|
|
262
|
+
onDone?.();
|
|
263
|
+
});
|
|
264
|
+
},
|
|
265
|
+
[translateY]
|
|
266
|
+
);
|
|
267
|
+
useEffect(() => {
|
|
268
|
+
if (visible) {
|
|
269
|
+
translateY.setValue(SCREEN_HEIGHT);
|
|
270
|
+
animateIn();
|
|
271
|
+
}
|
|
272
|
+
}, [visible, translateY, animateIn]);
|
|
273
|
+
const dismiss = useCallback(() => {
|
|
274
|
+
animateOut(() => {
|
|
275
|
+
setVisible(false);
|
|
276
|
+
setPayload(null);
|
|
277
|
+
});
|
|
278
|
+
}, [animateOut]);
|
|
279
|
+
useImperativeHandle(
|
|
280
|
+
ref,
|
|
281
|
+
() => ({
|
|
282
|
+
show(p) {
|
|
283
|
+
setPayload(p);
|
|
284
|
+
setVisible(true);
|
|
285
|
+
},
|
|
286
|
+
dismiss() {
|
|
287
|
+
dismiss();
|
|
288
|
+
}
|
|
289
|
+
}),
|
|
290
|
+
[dismiss]
|
|
291
|
+
);
|
|
292
|
+
if (!visible || !payload) return null;
|
|
293
|
+
return /* @__PURE__ */ jsxs(
|
|
294
|
+
Modal$1,
|
|
295
|
+
{
|
|
296
|
+
visible,
|
|
297
|
+
transparent: true,
|
|
298
|
+
animationType: "none",
|
|
299
|
+
onRequestClose: dismiss,
|
|
300
|
+
statusBarTranslucent: true,
|
|
301
|
+
children: [
|
|
302
|
+
/* @__PURE__ */ jsx(Pressable$1, { style: styles2.backdrop, onPress: dismiss }),
|
|
303
|
+
/* @__PURE__ */ jsxs(Animated.View, { style: [styles2.sheet, { transform: [{ translateY }] }], children: [
|
|
304
|
+
/* @__PURE__ */ jsx(View$1, { style: styles2.handle }),
|
|
305
|
+
/* @__PURE__ */ jsx(View$1, { style: styles2.content, children: renderContent(payload.schema, payload.context) })
|
|
306
|
+
] })
|
|
307
|
+
]
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
var styles2 = StyleSheet.create({
|
|
313
|
+
backdrop: {
|
|
314
|
+
...StyleSheet.absoluteFillObject,
|
|
315
|
+
backgroundColor: "rgba(0,0,0,0.5)"
|
|
316
|
+
},
|
|
317
|
+
sheet: {
|
|
318
|
+
position: "absolute",
|
|
319
|
+
bottom: 0,
|
|
320
|
+
left: 0,
|
|
321
|
+
right: 0,
|
|
322
|
+
backgroundColor: "#fff",
|
|
323
|
+
borderTopLeftRadius: 16,
|
|
324
|
+
borderTopRightRadius: 16,
|
|
325
|
+
paddingBottom: 34,
|
|
326
|
+
// safe-area approximation
|
|
327
|
+
minHeight: 200,
|
|
328
|
+
maxHeight: "80%",
|
|
329
|
+
shadowColor: "#000",
|
|
330
|
+
shadowOffset: { width: 0, height: -4 },
|
|
331
|
+
shadowOpacity: 0.15,
|
|
332
|
+
shadowRadius: 12,
|
|
333
|
+
elevation: 20
|
|
334
|
+
},
|
|
335
|
+
handle: {
|
|
336
|
+
alignSelf: "center",
|
|
337
|
+
width: 36,
|
|
338
|
+
height: 4,
|
|
339
|
+
borderRadius: 2,
|
|
340
|
+
backgroundColor: "#d1d5db",
|
|
341
|
+
marginTop: 12,
|
|
342
|
+
marginBottom: 8
|
|
343
|
+
},
|
|
344
|
+
content: {
|
|
345
|
+
flex: 1,
|
|
346
|
+
paddingHorizontal: 16,
|
|
347
|
+
paddingTop: 8
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// src/renderer/bindingResolver.ts
|
|
352
|
+
var FULL_BINDING_RE = /^\{\{(.+)\}\}$/;
|
|
353
|
+
var INLINE_BINDING_RE = /\{\{([^}]+)\}\}/g;
|
|
354
|
+
function evaluateExpression(expr, context) {
|
|
355
|
+
try {
|
|
356
|
+
const keys = Object.keys(context);
|
|
357
|
+
const values = keys.map((k) => context[k]);
|
|
358
|
+
const fn = new Function(...keys, `"use strict"; try { return (${expr}); } catch(e) { return undefined; }`);
|
|
359
|
+
return fn(...values);
|
|
360
|
+
} catch {
|
|
361
|
+
return void 0;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function resolveValue(value, context) {
|
|
365
|
+
if (typeof value !== "string") {
|
|
366
|
+
return value;
|
|
367
|
+
}
|
|
368
|
+
const fullMatch = FULL_BINDING_RE.exec(value);
|
|
369
|
+
if (fullMatch?.[1] !== void 0) {
|
|
370
|
+
return evaluateExpression(fullMatch[1].trim(), context);
|
|
371
|
+
}
|
|
372
|
+
if (INLINE_BINDING_RE.test(value)) {
|
|
373
|
+
INLINE_BINDING_RE.lastIndex = 0;
|
|
374
|
+
return value.replace(INLINE_BINDING_RE, (_, expr) => {
|
|
375
|
+
const resolved = evaluateExpression(expr.trim(), context);
|
|
376
|
+
return resolved !== void 0 && resolved !== null ? String(resolved) : "";
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
return value;
|
|
380
|
+
}
|
|
381
|
+
function resolveProps(props, context) {
|
|
382
|
+
if (!props) return {};
|
|
383
|
+
const resolved = {};
|
|
384
|
+
for (const [key, value] of Object.entries(props)) {
|
|
385
|
+
resolved[key] = resolveDeep(value, context);
|
|
386
|
+
}
|
|
387
|
+
return resolved;
|
|
388
|
+
}
|
|
389
|
+
function resolveDeep(value, context) {
|
|
390
|
+
if (Array.isArray(value)) {
|
|
391
|
+
return value.map((item) => resolveDeep(item, context));
|
|
392
|
+
}
|
|
393
|
+
if (typeof value === "object" && value !== null) {
|
|
394
|
+
const result = {};
|
|
395
|
+
for (const [k, v] of Object.entries(value)) {
|
|
396
|
+
result[k] = resolveDeep(v, context);
|
|
397
|
+
}
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
return resolveValue(value, context);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/renderer/conditionalEvaluator.ts
|
|
404
|
+
function evaluateIf(ifExpr, context) {
|
|
405
|
+
if (ifExpr === void 0) return true;
|
|
406
|
+
const resolved = resolveValue(ifExpr, context);
|
|
407
|
+
return Boolean(resolved);
|
|
408
|
+
}
|
|
409
|
+
function buildActionProps(actions, context, dispatchAction) {
|
|
410
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
411
|
+
for (const action of actions) {
|
|
412
|
+
const existing = grouped.get(action.trigger);
|
|
413
|
+
if (existing) {
|
|
414
|
+
existing.push(action);
|
|
415
|
+
} else {
|
|
416
|
+
grouped.set(action.trigger, [action]);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const result = {};
|
|
420
|
+
for (const [trigger, triggerActions] of grouped) {
|
|
421
|
+
result[trigger] = (..._args) => {
|
|
422
|
+
for (const action of triggerActions) {
|
|
423
|
+
const resolvedPayload = action.payload !== void 0 ? resolveDeep(action.payload, context) : void 0;
|
|
424
|
+
dispatchAction(action.action, resolvedPayload);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
function renderFlatListNode(node, context, registry, dispatchAction, fallbackComponent) {
|
|
431
|
+
const resolvedProps = resolveProps(node.props, context);
|
|
432
|
+
const items = Array.isArray(resolvedProps["items"]) ? resolvedProps["items"] : [];
|
|
433
|
+
const actionProps = node.actions ? buildActionProps(node.actions, context, dispatchAction) : {};
|
|
434
|
+
const { items: _items, keyExtractor: keyExtractorExpr, direction, numColumns, ...restProps } = resolvedProps;
|
|
435
|
+
let keyExtractorFn;
|
|
436
|
+
if (typeof keyExtractorExpr === "string") {
|
|
437
|
+
const extractorExpr = keyExtractorExpr;
|
|
438
|
+
keyExtractorFn = (item, index) => {
|
|
439
|
+
const itemContext = { ...context, item, index };
|
|
440
|
+
const resolved = resolveValue(extractorExpr, itemContext);
|
|
441
|
+
return resolved !== void 0 && resolved !== null ? String(resolved) : String(index);
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
const renderItemFn = ({ item, index }) => {
|
|
445
|
+
const itemContext = { ...context, item, index };
|
|
446
|
+
return /* @__PURE__ */ jsx(
|
|
447
|
+
RecursiveRenderer,
|
|
448
|
+
{
|
|
449
|
+
node: node.renderItem,
|
|
450
|
+
context: itemContext,
|
|
451
|
+
registry,
|
|
452
|
+
dispatchAction,
|
|
453
|
+
fallbackComponent
|
|
454
|
+
},
|
|
455
|
+
keyExtractorFn ? keyExtractorFn(item, index) : index
|
|
456
|
+
);
|
|
457
|
+
};
|
|
458
|
+
const FlatListComponent = registry.resolve("flatlist");
|
|
459
|
+
if (!FlatListComponent) {
|
|
460
|
+
console.warn('[PushFrame] RecursiveRenderer: "flatlist" not found in registry.');
|
|
461
|
+
return fallbackComponent ? fallbackComponent : null;
|
|
462
|
+
}
|
|
463
|
+
return React4.createElement(FlatListComponent, {
|
|
464
|
+
...restProps,
|
|
465
|
+
...actionProps,
|
|
466
|
+
items,
|
|
467
|
+
direction,
|
|
468
|
+
numColumns,
|
|
469
|
+
keyExtractor: keyExtractorFn,
|
|
470
|
+
renderItem: renderItemFn
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
function RecursiveRenderer({
|
|
474
|
+
node,
|
|
475
|
+
context,
|
|
476
|
+
registry,
|
|
477
|
+
dispatchAction,
|
|
478
|
+
fallbackComponent
|
|
479
|
+
}) {
|
|
480
|
+
if (!evaluateIf(node.if, context)) {
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
if (node.type === "flatlist") {
|
|
484
|
+
return renderFlatListNode(
|
|
485
|
+
node,
|
|
486
|
+
context,
|
|
487
|
+
registry,
|
|
488
|
+
dispatchAction,
|
|
489
|
+
fallbackComponent
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
const resolvedProps = resolveProps(node.props, context);
|
|
493
|
+
const actionProps = node.actions ? buildActionProps(node.actions, context, dispatchAction) : {};
|
|
494
|
+
const Component = registry.resolve(node.type);
|
|
495
|
+
if (!Component) {
|
|
496
|
+
console.warn(
|
|
497
|
+
`[PushFrame] RecursiveRenderer: Unknown component type "${node.type}". Registered types: ${registry.types().join(", ")}`
|
|
498
|
+
);
|
|
499
|
+
return fallbackComponent ? fallbackComponent : null;
|
|
500
|
+
}
|
|
501
|
+
const children = node.children && node.children.length > 0 ? node.children.map((child, index) => /* @__PURE__ */ jsx(
|
|
502
|
+
RecursiveRenderer,
|
|
503
|
+
{
|
|
504
|
+
node: child,
|
|
505
|
+
context,
|
|
506
|
+
registry,
|
|
507
|
+
dispatchAction,
|
|
508
|
+
fallbackComponent
|
|
509
|
+
},
|
|
510
|
+
`${child.type}-${index}`
|
|
511
|
+
)) : void 0;
|
|
512
|
+
return React4.createElement(Component, {
|
|
513
|
+
...resolvedProps,
|
|
514
|
+
...actionProps,
|
|
515
|
+
children
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
var DEFAULT_BASE_URL = "https://api.pushframe.io";
|
|
519
|
+
function PushFrameProvider({
|
|
520
|
+
apiKey,
|
|
521
|
+
context = {},
|
|
522
|
+
baseUrl = DEFAULT_BASE_URL,
|
|
523
|
+
appVersion,
|
|
524
|
+
components,
|
|
525
|
+
loadingComponent,
|
|
526
|
+
fallbackComponent,
|
|
527
|
+
onAction,
|
|
528
|
+
onError,
|
|
529
|
+
children
|
|
530
|
+
}) {
|
|
531
|
+
const registry = useMemo(
|
|
532
|
+
() => new ComponentRegistry(components),
|
|
533
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
534
|
+
[]
|
|
535
|
+
);
|
|
536
|
+
const toastRef = useRef(null);
|
|
537
|
+
const bottomSheetRef = useRef(null);
|
|
538
|
+
const showToast = useCallback((payload) => {
|
|
539
|
+
toastRef.current?.show(payload);
|
|
540
|
+
}, []);
|
|
541
|
+
const showBottomSheet = useCallback((payload) => {
|
|
542
|
+
bottomSheetRef.current?.show(payload);
|
|
543
|
+
}, []);
|
|
544
|
+
const dismissBottomSheet = useCallback(() => {
|
|
545
|
+
bottomSheetRef.current?.dismiss();
|
|
546
|
+
}, []);
|
|
547
|
+
const renderBottomSheetContent = useCallback(
|
|
548
|
+
(schema, sheetContext) => {
|
|
549
|
+
const mergedContext = sheetContext ? { ...context, ...sheetContext } : context;
|
|
550
|
+
const dispatchAction = (action, payload) => {
|
|
551
|
+
if (action === "dismiss-bottom-sheet") {
|
|
552
|
+
dismissBottomSheet();
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (action === "show-toast") {
|
|
556
|
+
showToast(payload);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
onAction?.(action, payload);
|
|
560
|
+
};
|
|
561
|
+
return /* @__PURE__ */ jsx(
|
|
562
|
+
RecursiveRenderer,
|
|
563
|
+
{
|
|
564
|
+
node: schema,
|
|
565
|
+
context: mergedContext,
|
|
566
|
+
registry,
|
|
567
|
+
dispatchAction,
|
|
568
|
+
fallbackComponent
|
|
569
|
+
}
|
|
570
|
+
);
|
|
571
|
+
},
|
|
572
|
+
[context, registry, fallbackComponent, onAction, dismissBottomSheet, showToast]
|
|
573
|
+
);
|
|
574
|
+
const contextValue = useMemo(
|
|
575
|
+
() => ({
|
|
576
|
+
apiKey,
|
|
577
|
+
baseUrl,
|
|
578
|
+
appVersion,
|
|
579
|
+
globalContext: context,
|
|
580
|
+
registry,
|
|
581
|
+
loadingComponent,
|
|
582
|
+
fallbackComponent,
|
|
583
|
+
onAction,
|
|
584
|
+
onError,
|
|
585
|
+
showToast,
|
|
586
|
+
showBottomSheet,
|
|
587
|
+
dismissBottomSheet
|
|
588
|
+
}),
|
|
589
|
+
[
|
|
590
|
+
apiKey,
|
|
591
|
+
baseUrl,
|
|
592
|
+
appVersion,
|
|
593
|
+
context,
|
|
594
|
+
registry,
|
|
595
|
+
loadingComponent,
|
|
596
|
+
fallbackComponent,
|
|
597
|
+
onAction,
|
|
598
|
+
onError,
|
|
599
|
+
showToast,
|
|
600
|
+
showBottomSheet,
|
|
601
|
+
dismissBottomSheet
|
|
602
|
+
]
|
|
603
|
+
);
|
|
604
|
+
return /* @__PURE__ */ jsxs(PushFrameContext.Provider, { value: contextValue, children: [
|
|
605
|
+
children,
|
|
606
|
+
/* @__PURE__ */ jsx(ToastHost, { ref: toastRef }),
|
|
607
|
+
/* @__PURE__ */ jsx(BottomSheetHost, { ref: bottomSheetRef, renderContent: renderBottomSheetContent })
|
|
608
|
+
] });
|
|
609
|
+
}
|
|
610
|
+
async function fetchSchema(baseUrl, apiKey, path) {
|
|
611
|
+
const url = new URL(path, baseUrl + "/").toString();
|
|
612
|
+
const headers = {
|
|
613
|
+
Accept: "application/json",
|
|
614
|
+
Authorization: `Bearer ${apiKey}`,
|
|
615
|
+
"x-project-key": apiKey
|
|
616
|
+
};
|
|
617
|
+
const res = await fetch(url, { headers });
|
|
618
|
+
if (!res.ok) {
|
|
619
|
+
throw new Error(`[PushFrame] HTTP ${res.status} fetching "${path}"`);
|
|
620
|
+
}
|
|
621
|
+
const envelope = await res.json();
|
|
622
|
+
return envelope.schema ?? envelope;
|
|
623
|
+
}
|
|
624
|
+
function PushFrameComponentInternal({
|
|
625
|
+
id,
|
|
626
|
+
resourceType,
|
|
627
|
+
context: slotContext,
|
|
628
|
+
isLoading: externalLoading,
|
|
629
|
+
loadingComponent: slotLoading,
|
|
630
|
+
fallbackComponent: slotFallback,
|
|
631
|
+
onAction: slotOnAction,
|
|
632
|
+
fullscreen
|
|
633
|
+
}) {
|
|
634
|
+
const {
|
|
635
|
+
apiKey,
|
|
636
|
+
baseUrl,
|
|
637
|
+
appVersion,
|
|
638
|
+
globalContext,
|
|
639
|
+
registry,
|
|
640
|
+
loadingComponent: providerLoading,
|
|
641
|
+
fallbackComponent: providerFallback,
|
|
642
|
+
onAction: providerOnAction,
|
|
643
|
+
onError,
|
|
644
|
+
showToast,
|
|
645
|
+
showBottomSheet,
|
|
646
|
+
dismissBottomSheet
|
|
647
|
+
} = usePushFrameContext();
|
|
648
|
+
const [schema, setSchema] = useState(null);
|
|
649
|
+
const [fetchError, setFetchError] = useState(null);
|
|
650
|
+
const [isFetching, setIsFetching] = useState(true);
|
|
651
|
+
const fetchPath = `${resourceType}/${encodeURIComponent(id)}/${encodeURIComponent(appVersion ?? "null")}`;
|
|
652
|
+
const mergedContext = slotContext ? { ...globalContext, ...slotContext } : globalContext;
|
|
653
|
+
const loadingUI = slotLoading !== void 0 ? slotLoading : providerLoading;
|
|
654
|
+
const fallbackUI = slotFallback !== void 0 ? slotFallback : providerFallback;
|
|
655
|
+
useEffect(() => {
|
|
656
|
+
let cancelled = false;
|
|
657
|
+
setSchema(null);
|
|
658
|
+
setFetchError(null);
|
|
659
|
+
setIsFetching(true);
|
|
660
|
+
fetchSchema(baseUrl, apiKey, fetchPath).then((s) => {
|
|
661
|
+
if (!cancelled) {
|
|
662
|
+
setSchema(s);
|
|
663
|
+
setIsFetching(false);
|
|
664
|
+
}
|
|
665
|
+
}).catch((err) => {
|
|
666
|
+
if (!cancelled) {
|
|
667
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
668
|
+
setFetchError(error);
|
|
669
|
+
setIsFetching(false);
|
|
670
|
+
onError?.(error);
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
return () => {
|
|
674
|
+
cancelled = true;
|
|
675
|
+
};
|
|
676
|
+
}, [fetchPath, baseUrl, apiKey, onError]);
|
|
677
|
+
const dispatchAction = useCallback(
|
|
678
|
+
(action, payload) => {
|
|
679
|
+
if (action === "show-toast") {
|
|
680
|
+
showToast(payload);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
if (action === "show-bottom-sheet") {
|
|
684
|
+
showBottomSheet(payload);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (action === "dismiss-bottom-sheet") {
|
|
688
|
+
dismissBottomSheet();
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (action === "scroll-to") {
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
const stopped = slotOnAction?.(action, payload);
|
|
695
|
+
if (stopped) return;
|
|
696
|
+
providerOnAction?.(action, payload);
|
|
697
|
+
},
|
|
698
|
+
[showToast, showBottomSheet, dismissBottomSheet, slotOnAction, providerOnAction]
|
|
699
|
+
);
|
|
700
|
+
const isShowingLoader = externalLoading || isFetching;
|
|
701
|
+
if (isShowingLoader) {
|
|
702
|
+
return loadingUI ?? null;
|
|
703
|
+
}
|
|
704
|
+
if (fetchError || !schema) {
|
|
705
|
+
return fallbackUI ?? null;
|
|
706
|
+
}
|
|
707
|
+
const rendered = /* @__PURE__ */ jsx(
|
|
708
|
+
RecursiveRenderer,
|
|
709
|
+
{
|
|
710
|
+
node: schema,
|
|
711
|
+
context: mergedContext,
|
|
712
|
+
registry,
|
|
713
|
+
dispatchAction,
|
|
714
|
+
fallbackComponent: fallbackUI
|
|
715
|
+
}
|
|
716
|
+
);
|
|
717
|
+
if (fullscreen) {
|
|
718
|
+
return /* @__PURE__ */ jsx(Fragment, { children: rendered });
|
|
719
|
+
}
|
|
720
|
+
return rendered;
|
|
721
|
+
}
|
|
722
|
+
function PushFrameComponent(props) {
|
|
723
|
+
return /* @__PURE__ */ jsx(PushFrameComponentInternal, { ...props, resourceType: "components" });
|
|
724
|
+
}
|
|
725
|
+
function PushFrameScreen(props) {
|
|
726
|
+
return /* @__PURE__ */ jsx(View$1, { style: styles3.container, children: /* @__PURE__ */ jsx(PushFrameComponentInternal, { ...props, resourceType: "screens", fullscreen: true }) });
|
|
727
|
+
}
|
|
728
|
+
var styles3 = StyleSheet.create({
|
|
729
|
+
container: {
|
|
730
|
+
flex: 1
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
// src/index.ts
|
|
735
|
+
var PushFrame = {
|
|
736
|
+
// Slot components
|
|
737
|
+
Provider: PushFrameProvider,
|
|
738
|
+
Screen: PushFrameScreen,
|
|
739
|
+
Component: PushFrameComponent,
|
|
740
|
+
// Primitives
|
|
741
|
+
Text,
|
|
742
|
+
View,
|
|
743
|
+
ScrollView,
|
|
744
|
+
Image,
|
|
745
|
+
Pressable,
|
|
746
|
+
TextInput,
|
|
747
|
+
FlatList,
|
|
748
|
+
Modal,
|
|
749
|
+
ActivityIndicator,
|
|
750
|
+
Switch,
|
|
751
|
+
KeyboardAvoidingView,
|
|
752
|
+
SafeAreaView,
|
|
753
|
+
StatusBar
|
|
754
|
+
};
|
|
755
|
+
|
|
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 };
|
|
757
|
+
//# sourceMappingURL=index.mjs.map
|
|
758
|
+
//# sourceMappingURL=index.mjs.map
|