@pushframe/sdk 0.1.4 → 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.
- package/lib/commonjs/PushframeProvider.js +69 -0
- package/lib/commonjs/PushframeProvider.js.map +1 -0
- package/lib/commonjs/PushframeScreen.js +69 -0
- package/lib/commonjs/PushframeScreen.js.map +1 -0
- package/lib/commonjs/bindings.js +73 -0
- package/lib/commonjs/bindings.js.map +1 -0
- package/lib/commonjs/components/ButtonComponent.js +72 -0
- package/lib/commonjs/components/ButtonComponent.js.map +1 -0
- package/lib/commonjs/components/FlatListComponent.js +73 -0
- package/lib/commonjs/components/FlatListComponent.js.map +1 -0
- package/lib/commonjs/components/ImageComponent.js +67 -0
- package/lib/commonjs/components/ImageComponent.js.map +1 -0
- package/lib/commonjs/components/PushFrameComponent.js +179 -0
- package/lib/commonjs/components/PushFrameComponent.js.map +1 -0
- package/lib/commonjs/components/PushFrameProvider.js +115 -0
- package/lib/commonjs/components/PushFrameProvider.js.map +1 -0
- package/lib/commonjs/components/PushFrameScreen.js +39 -0
- package/lib/commonjs/components/PushFrameScreen.js.map +1 -0
- package/lib/commonjs/components/ScrollViewComponent.js +64 -0
- package/lib/commonjs/components/ScrollViewComponent.js.map +1 -0
- package/lib/commonjs/components/StackComponent.js +61 -0
- package/lib/commonjs/components/StackComponent.js.map +1 -0
- package/lib/commonjs/components/TextComponent.js +62 -0
- package/lib/commonjs/components/TextComponent.js.map +1 -0
- package/lib/commonjs/conditions.js +44 -0
- package/lib/commonjs/conditions.js.map +1 -0
- package/lib/commonjs/context/PushFrameContext.js +33 -0
- package/lib/commonjs/context/PushFrameContext.js.map +1 -0
- package/lib/commonjs/index.js +200 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/overlays/BottomSheetHost.js +144 -0
- package/lib/commonjs/overlays/BottomSheetHost.js.map +1 -0
- package/lib/commonjs/overlays/ToastHost.js +135 -0
- package/lib/commonjs/overlays/ToastHost.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/primitives/ActivityIndicator.js +24 -0
- package/lib/commonjs/primitives/ActivityIndicator.js.map +1 -0
- package/lib/commonjs/primitives/FlatList.js +34 -0
- package/lib/commonjs/primitives/FlatList.js.map +1 -0
- package/lib/commonjs/primitives/Image.js +33 -0
- package/lib/commonjs/primitives/Image.js.map +1 -0
- package/lib/commonjs/primitives/KeyboardAvoidingView.js +24 -0
- package/lib/commonjs/primitives/KeyboardAvoidingView.js.map +1 -0
- package/lib/commonjs/primitives/Modal.js +24 -0
- package/lib/commonjs/primitives/Modal.js.map +1 -0
- package/lib/commonjs/primitives/Pressable.js +26 -0
- package/lib/commonjs/primitives/Pressable.js.map +1 -0
- package/lib/commonjs/primitives/SafeAreaView.js +38 -0
- package/lib/commonjs/primitives/SafeAreaView.js.map +1 -0
- package/lib/commonjs/primitives/ScrollView.js +26 -0
- package/lib/commonjs/primitives/ScrollView.js.map +1 -0
- package/lib/commonjs/primitives/StatusBar.js +24 -0
- package/lib/commonjs/primitives/StatusBar.js.map +1 -0
- package/lib/commonjs/primitives/Switch.js +28 -0
- package/lib/commonjs/primitives/Switch.js.map +1 -0
- package/lib/commonjs/primitives/Text.js +37 -0
- package/lib/commonjs/primitives/Text.js.map +1 -0
- package/lib/commonjs/primitives/TextInput.js +31 -0
- package/lib/commonjs/primitives/TextInput.js.map +1 -0
- package/lib/commonjs/primitives/View.js +24 -0
- package/lib/commonjs/primitives/View.js.map +1 -0
- package/lib/commonjs/primitives/index.js +97 -0
- package/lib/commonjs/primitives/index.js.map +1 -0
- package/lib/commonjs/registry/ComponentRegistry.js +70 -0
- package/lib/commonjs/registry/ComponentRegistry.js.map +1 -0
- package/lib/commonjs/registry.js +94 -0
- package/lib/commonjs/registry.js.map +1 -0
- package/lib/commonjs/renderer/RecursiveRenderer.js +202 -0
- package/lib/commonjs/renderer/RecursiveRenderer.js.map +1 -0
- package/lib/commonjs/renderer/bindingResolver.js +98 -0
- package/lib/commonjs/renderer/bindingResolver.js.map +1 -0
- package/lib/commonjs/renderer/conditionalEvaluator.js +31 -0
- package/lib/commonjs/renderer/conditionalEvaluator.js.map +1 -0
- package/lib/commonjs/renderer.js +107 -0
- package/lib/commonjs/renderer.js.map +1 -0
- package/lib/commonjs/schema.js +79 -0
- package/lib/commonjs/schema.js.map +1 -0
- package/lib/commonjs/transformer/index.js +1055 -0
- package/lib/commonjs/transformer/index.js.map +1 -0
- package/lib/commonjs/transport.js +86 -0
- package/lib/commonjs/transport.js.map +1 -0
- package/lib/module/PushframeProvider.js +62 -0
- package/lib/module/PushframeProvider.js.map +1 -0
- package/lib/module/PushframeScreen.js +65 -0
- package/lib/module/PushframeScreen.js.map +1 -0
- package/lib/module/bindings.js +68 -0
- package/lib/module/bindings.js.map +1 -0
- package/lib/module/components/ButtonComponent.js +67 -0
- package/lib/module/components/ButtonComponent.js.map +1 -0
- package/lib/module/components/FlatListComponent.js +68 -0
- package/lib/module/components/FlatListComponent.js.map +1 -0
- package/lib/module/components/ImageComponent.js +62 -0
- package/lib/module/components/ImageComponent.js.map +1 -0
- package/lib/module/components/PushFrameComponent.js +174 -0
- package/lib/module/components/PushFrameComponent.js.map +1 -0
- package/lib/module/components/PushFrameProvider.js +110 -0
- package/lib/module/components/PushFrameProvider.js.map +1 -0
- package/lib/module/components/PushFrameScreen.js +34 -0
- package/lib/module/components/PushFrameScreen.js.map +1 -0
- package/lib/module/components/ScrollViewComponent.js +59 -0
- package/lib/module/components/ScrollViewComponent.js.map +1 -0
- package/lib/module/components/StackComponent.js +56 -0
- package/lib/module/components/StackComponent.js.map +1 -0
- package/lib/module/components/TextComponent.js +57 -0
- package/lib/module/components/TextComponent.js.map +1 -0
- package/lib/module/conditions.js +40 -0
- package/lib/module/conditions.js.map +1 -0
- package/lib/module/context/PushFrameContext.js +29 -0
- package/lib/module/context/PushFrameContext.js.map +1 -0
- package/lib/module/index.js +99 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/overlays/BottomSheetHost.js +139 -0
- package/lib/module/overlays/BottomSheetHost.js.map +1 -0
- package/lib/module/overlays/ToastHost.js +130 -0
- package/lib/module/overlays/ToastHost.js.map +1 -0
- package/lib/module/primitives/ActivityIndicator.js +19 -0
- package/lib/module/primitives/ActivityIndicator.js.map +1 -0
- package/lib/module/primitives/FlatList.js +29 -0
- package/lib/module/primitives/FlatList.js.map +1 -0
- package/lib/module/primitives/Image.js +28 -0
- package/lib/module/primitives/Image.js.map +1 -0
- package/lib/module/primitives/KeyboardAvoidingView.js +19 -0
- package/lib/module/primitives/KeyboardAvoidingView.js.map +1 -0
- package/lib/module/primitives/Modal.js +19 -0
- package/lib/module/primitives/Modal.js.map +1 -0
- package/lib/module/primitives/Pressable.js +21 -0
- package/lib/module/primitives/Pressable.js.map +1 -0
- package/lib/module/primitives/SafeAreaView.js +33 -0
- package/lib/module/primitives/SafeAreaView.js.map +1 -0
- package/lib/module/primitives/ScrollView.js +21 -0
- package/lib/module/primitives/ScrollView.js.map +1 -0
- package/lib/module/primitives/StatusBar.js +19 -0
- package/lib/module/primitives/StatusBar.js.map +1 -0
- package/lib/module/primitives/Switch.js +23 -0
- package/lib/module/primitives/Switch.js.map +1 -0
- package/lib/module/primitives/Text.js +32 -0
- package/lib/module/primitives/Text.js.map +1 -0
- package/lib/module/primitives/TextInput.js +26 -0
- package/lib/module/primitives/TextInput.js.map +1 -0
- package/lib/module/primitives/View.js +19 -0
- package/lib/module/primitives/View.js.map +1 -0
- package/lib/module/primitives/index.js +16 -0
- package/lib/module/primitives/index.js.map +1 -0
- package/lib/module/registry/ComponentRegistry.js +66 -0
- package/lib/module/registry/ComponentRegistry.js.map +1 -0
- package/lib/module/registry.js +88 -0
- package/lib/module/registry.js.map +1 -0
- package/lib/module/renderer/RecursiveRenderer.js +197 -0
- package/lib/module/renderer/RecursiveRenderer.js.map +1 -0
- package/lib/module/renderer/bindingResolver.js +92 -0
- package/lib/module/renderer/bindingResolver.js.map +1 -0
- package/lib/module/renderer/conditionalEvaluator.js +28 -0
- package/lib/module/renderer/conditionalEvaluator.js.map +1 -0
- package/lib/module/renderer.js +103 -0
- package/lib/module/renderer.js.map +1 -0
- package/lib/module/schema.js +74 -0
- package/lib/module/schema.js.map +1 -0
- package/lib/module/transformer/index.js +1051 -0
- package/lib/module/transformer/index.js.map +1 -0
- package/lib/module/transport.js +82 -0
- package/lib/module/transport.js.map +1 -0
- package/lib/typescript/PushframeProvider.d.ts +58 -0
- package/lib/typescript/PushframeProvider.d.ts.map +1 -0
- package/lib/typescript/PushframeScreen.d.ts +36 -0
- package/lib/typescript/PushframeScreen.d.ts.map +1 -0
- package/lib/typescript/bindings.d.ts +29 -0
- package/lib/typescript/bindings.d.ts.map +1 -0
- package/lib/typescript/components/ButtonComponent.d.ts +11 -0
- package/lib/typescript/components/ButtonComponent.d.ts.map +1 -0
- package/lib/typescript/components/FlatListComponent.d.ts +28 -0
- package/lib/typescript/components/FlatListComponent.d.ts.map +1 -0
- package/lib/typescript/components/ImageComponent.d.ts +12 -0
- package/lib/typescript/components/ImageComponent.d.ts.map +1 -0
- package/lib/typescript/components/PushFrameComponent.d.ts +48 -0
- package/lib/typescript/components/PushFrameComponent.d.ts.map +1 -0
- package/lib/typescript/components/PushFrameProvider.d.ts +51 -0
- package/lib/typescript/components/PushFrameProvider.d.ts.map +1 -0
- package/lib/typescript/components/PushFrameScreen.d.ts +15 -0
- package/lib/typescript/components/PushFrameScreen.d.ts.map +1 -0
- package/lib/typescript/components/ScrollViewComponent.d.ts +19 -0
- package/lib/typescript/components/ScrollViewComponent.d.ts.map +1 -0
- package/lib/typescript/components/StackComponent.d.ts +16 -0
- package/lib/typescript/components/StackComponent.d.ts.map +1 -0
- package/lib/typescript/components/TextComponent.d.ts +13 -0
- package/lib/typescript/components/TextComponent.d.ts.map +1 -0
- package/lib/typescript/conditions.d.ts +12 -0
- package/lib/typescript/conditions.d.ts.map +1 -0
- package/lib/typescript/context/PushFrameContext.d.ts +57 -0
- package/lib/typescript/context/PushFrameContext.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +74 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/overlays/BottomSheetHost.d.ts +21 -0
- package/lib/typescript/overlays/BottomSheetHost.d.ts.map +1 -0
- package/lib/typescript/overlays/ToastHost.d.ts +12 -0
- package/lib/typescript/overlays/ToastHost.d.ts.map +1 -0
- package/lib/typescript/primitives/ActivityIndicator.d.ts +12 -0
- package/lib/typescript/primitives/ActivityIndicator.d.ts.map +1 -0
- package/lib/typescript/primitives/FlatList.d.ts +29 -0
- package/lib/typescript/primitives/FlatList.d.ts.map +1 -0
- package/lib/typescript/primitives/Image.d.ts +20 -0
- package/lib/typescript/primitives/Image.d.ts.map +1 -0
- package/lib/typescript/primitives/KeyboardAvoidingView.d.ts +12 -0
- package/lib/typescript/primitives/KeyboardAvoidingView.d.ts.map +1 -0
- package/lib/typescript/primitives/Modal.d.ts +12 -0
- package/lib/typescript/primitives/Modal.d.ts.map +1 -0
- package/lib/typescript/primitives/Pressable.d.ts +14 -0
- package/lib/typescript/primitives/Pressable.d.ts.map +1 -0
- package/lib/typescript/primitives/SafeAreaView.d.ts +20 -0
- package/lib/typescript/primitives/SafeAreaView.d.ts.map +1 -0
- package/lib/typescript/primitives/ScrollView.d.ts +15 -0
- package/lib/typescript/primitives/ScrollView.d.ts.map +1 -0
- package/lib/typescript/primitives/StatusBar.d.ts +12 -0
- package/lib/typescript/primitives/StatusBar.d.ts.map +1 -0
- package/lib/typescript/primitives/Switch.d.ts +19 -0
- package/lib/typescript/primitives/Switch.d.ts.map +1 -0
- package/lib/typescript/primitives/Text.d.ts +25 -0
- package/lib/typescript/primitives/Text.d.ts.map +1 -0
- package/lib/typescript/primitives/TextInput.d.ts +25 -0
- package/lib/typescript/primitives/TextInput.d.ts.map +1 -0
- package/lib/typescript/primitives/View.d.ts +12 -0
- package/lib/typescript/primitives/View.d.ts.map +1 -0
- package/lib/typescript/primitives/index.d.ts +27 -0
- package/lib/typescript/primitives/index.d.ts.map +1 -0
- package/lib/typescript/registry/ComponentRegistry.d.ts +21 -0
- package/lib/typescript/registry/ComponentRegistry.d.ts.map +1 -0
- package/lib/typescript/registry.d.ts +57 -0
- package/lib/typescript/registry.d.ts.map +1 -0
- package/lib/typescript/renderer/RecursiveRenderer.d.ts +32 -0
- package/lib/typescript/renderer/RecursiveRenderer.d.ts.map +1 -0
- package/lib/typescript/renderer/bindingResolver.d.ts +26 -0
- package/lib/typescript/renderer/bindingResolver.d.ts.map +1 -0
- package/lib/typescript/renderer/conditionalEvaluator.d.ts +15 -0
- package/lib/typescript/renderer/conditionalEvaluator.d.ts.map +1 -0
- package/lib/typescript/renderer.d.ts +29 -0
- package/lib/typescript/renderer.d.ts.map +1 -0
- package/lib/typescript/schema.d.ts +84 -0
- package/lib/typescript/schema.d.ts.map +1 -0
- package/lib/typescript/transformer/index.d.ts +49 -0
- package/lib/typescript/transformer/index.d.ts.map +1 -0
- package/lib/typescript/transport.d.ts +19 -0
- package/lib/typescript/transport.d.ts.map +1 -0
- package/package.json +20 -18
- package/src/PushframeProvider.tsx +119 -0
- package/src/PushframeScreen.tsx +107 -0
- package/src/bindings.ts +72 -0
- package/src/components/ButtonComponent.tsx +87 -0
- package/src/components/FlatListComponent.tsx +86 -0
- package/src/components/ImageComponent.tsx +70 -0
- package/src/components/PushFrameComponent.tsx +221 -0
- package/src/components/PushFrameProvider.tsx +177 -0
- package/src/components/PushFrameScreen.tsx +30 -0
- package/src/components/ScrollViewComponent.tsx +65 -0
- package/src/components/StackComponent.tsx +69 -0
- package/src/components/TextComponent.tsx +60 -0
- package/src/conditions.ts +46 -0
- package/src/context/PushFrameContext.ts +89 -0
- package/src/index.ts +119 -0
- package/src/overlays/BottomSheetHost.tsx +175 -0
- package/src/overlays/ToastHost.tsx +147 -0
- package/src/primitives/ActivityIndicator.tsx +21 -0
- package/src/primitives/FlatList.tsx +49 -0
- package/src/primitives/Image.tsx +26 -0
- package/src/primitives/KeyboardAvoidingView.tsx +21 -0
- package/src/primitives/Modal.tsx +17 -0
- package/src/primitives/Pressable.tsx +19 -0
- package/src/primitives/SafeAreaView.tsx +42 -0
- package/src/primitives/ScrollView.tsx +21 -0
- package/src/primitives/StatusBar.tsx +17 -0
- package/src/primitives/Switch.tsx +24 -0
- package/src/primitives/Text.tsx +43 -0
- package/src/primitives/TextInput.tsx +42 -0
- package/src/primitives/View.tsx +17 -0
- package/src/primitives/index.ts +38 -0
- package/src/registry/ComponentRegistry.ts +99 -0
- package/src/registry.ts +99 -0
- package/src/renderer/RecursiveRenderer.tsx +242 -0
- package/src/renderer/bindingResolver.ts +94 -0
- package/src/renderer/conditionalEvaluator.ts +29 -0
- package/src/renderer.tsx +124 -0
- package/src/schema.ts +132 -0
- package/src/transformer/index.ts +1016 -0
- package/src/transport.ts +104 -0
- package/dist/index.d.mts +0 -534
- package/dist/index.d.ts +0 -534
- package/dist/index.js +0 -1572
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -1541
- package/dist/index.mjs.map +0 -1
package/src/registry.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ComponentType, ReactElement, ReactNode } from 'react';
|
|
2
|
+
import type { PushframeNode } from './schema';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Props passed to every Pushframe component.
|
|
6
|
+
*
|
|
7
|
+
* `props` — fully-resolved (all $bind references substituted) props map.
|
|
8
|
+
* `children` — pre-rendered child nodes (if any).
|
|
9
|
+
* `onAction` — callback for emitting an action event back to the host app.
|
|
10
|
+
* `rawChildren` — raw schema child nodes, available for data-driven components
|
|
11
|
+
* (e.g. FlatList) that need to render the same template
|
|
12
|
+
* multiple times with different context.
|
|
13
|
+
* `renderChild` — render a schema node with an optional context override that
|
|
14
|
+
* gets merged into the parent context. Use this together with
|
|
15
|
+
* `rawChildren` to implement per-item rendering.
|
|
16
|
+
*/
|
|
17
|
+
export interface PushframeComponentProps<TProps = Record<string, unknown>> {
|
|
18
|
+
props: TProps;
|
|
19
|
+
children?: ReactNode;
|
|
20
|
+
onAction?: (eventName: string, payload: unknown) => void;
|
|
21
|
+
rawChildren?: readonly PushframeNode[];
|
|
22
|
+
renderChild?: (
|
|
23
|
+
node: PushframeNode,
|
|
24
|
+
contextOverride?: Record<string, unknown>,
|
|
25
|
+
) => ReactElement | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type PushframeComponent<TProps = Record<string, unknown>> =
|
|
29
|
+
ComponentType<PushframeComponentProps<TProps>>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* ComponentRegistry maps a component type string (e.g. "stack") to its
|
|
33
|
+
* React component implementation.
|
|
34
|
+
*
|
|
35
|
+
* Use `createRegistry()` to construct one, then call `register()` to add
|
|
36
|
+
* custom components on top.
|
|
37
|
+
*/
|
|
38
|
+
export class ComponentRegistry {
|
|
39
|
+
private readonly components = new Map<string, PushframeComponent>();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Register a component under the given type string.
|
|
43
|
+
* Calling register() with an existing type overwrites the previous entry,
|
|
44
|
+
* allowing host apps to override built-ins.
|
|
45
|
+
*/
|
|
46
|
+
register<TProps>(
|
|
47
|
+
type: string,
|
|
48
|
+
component: PushframeComponent<TProps>,
|
|
49
|
+
): this {
|
|
50
|
+
this.components.set(type, component as PushframeComponent);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Look up a component by type string.
|
|
56
|
+
* Returns undefined if no component is registered for that type.
|
|
57
|
+
*/
|
|
58
|
+
get(type: string): PushframeComponent | undefined {
|
|
59
|
+
return this.components.get(type);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns true if the registry contains an entry for the given type.
|
|
64
|
+
*/
|
|
65
|
+
has(type: string): boolean {
|
|
66
|
+
return this.components.has(type);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns all registered type strings (useful for debugging).
|
|
71
|
+
*/
|
|
72
|
+
types(): string[] {
|
|
73
|
+
return Array.from(this.components.keys());
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Build the default registry pre-loaded with all built-in components.
|
|
79
|
+
*/
|
|
80
|
+
export function createDefaultRegistry(): ComponentRegistry {
|
|
81
|
+
// Lazy-import to keep tree-shaking effective when consumers only need
|
|
82
|
+
// a subset of built-ins.
|
|
83
|
+
const { StackComponent } = require('./components/StackComponent');
|
|
84
|
+
const { TextComponent } = require('./components/TextComponent');
|
|
85
|
+
const { ButtonComponent } = require('./components/ButtonComponent');
|
|
86
|
+
const { ImageComponent } = require('./components/ImageComponent');
|
|
87
|
+
const { ScrollViewComponent } = require('./components/ScrollViewComponent');
|
|
88
|
+
const { FlatListComponent } = require('./components/FlatListComponent');
|
|
89
|
+
|
|
90
|
+
return new ComponentRegistry()
|
|
91
|
+
.register('stack', StackComponent)
|
|
92
|
+
// 'screen' is a common server-side root container alias for stack
|
|
93
|
+
.register('screen', StackComponent)
|
|
94
|
+
.register('text', TextComponent)
|
|
95
|
+
.register('button', ButtonComponent)
|
|
96
|
+
.register('image', ImageComponent)
|
|
97
|
+
.register('scrollview', ScrollViewComponent)
|
|
98
|
+
.register('flatlist', FlatListComponent);
|
|
99
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ReactElement } from 'react';
|
|
3
|
+
import type { SchemaNode, FlatListSchemaNode, Action } from '../context/PushFrameContext';
|
|
4
|
+
import type { ComponentRegistry } from '../registry/ComponentRegistry';
|
|
5
|
+
import { resolveProps, resolveDeep, resolveValue } from './bindingResolver';
|
|
6
|
+
import { evaluateIf } from './conditionalEvaluator';
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Types
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
export interface DispatchAction {
|
|
13
|
+
(actionName: string, payload?: Record<string, unknown>): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface RecursiveRendererProps {
|
|
17
|
+
node: SchemaNode;
|
|
18
|
+
/** Current merged data context for binding resolution. */
|
|
19
|
+
context: Record<string, unknown>;
|
|
20
|
+
registry: ComponentRegistry;
|
|
21
|
+
/** Handles all actions — built-ins and host-app bubbling. Provided by the slot component. */
|
|
22
|
+
dispatchAction: DispatchAction;
|
|
23
|
+
/** Shown when a component type is not found in the registry. */
|
|
24
|
+
fallbackComponent?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Action wiring helpers
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Group an actions array by trigger name and produce a prop map where each
|
|
33
|
+
* trigger key maps to a callback that fires all actions for that trigger.
|
|
34
|
+
*/
|
|
35
|
+
function buildActionProps(
|
|
36
|
+
actions: Action[],
|
|
37
|
+
context: Record<string, unknown>,
|
|
38
|
+
dispatchAction: DispatchAction,
|
|
39
|
+
): Record<string, (...args: unknown[]) => void> {
|
|
40
|
+
// Group by trigger
|
|
41
|
+
const grouped = new Map<string, Action[]>();
|
|
42
|
+
for (const action of actions) {
|
|
43
|
+
const existing = grouped.get(action.trigger);
|
|
44
|
+
if (existing) {
|
|
45
|
+
existing.push(action);
|
|
46
|
+
} else {
|
|
47
|
+
grouped.set(action.trigger, [action]);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const result: Record<string, (...args: unknown[]) => void> = {};
|
|
52
|
+
for (const [trigger, triggerActions] of grouped) {
|
|
53
|
+
result[trigger] = (..._args: unknown[]) => {
|
|
54
|
+
for (const action of triggerActions) {
|
|
55
|
+
const resolvedPayload =
|
|
56
|
+
action.payload !== undefined
|
|
57
|
+
? (resolveDeep(action.payload, context) as Record<string, unknown>)
|
|
58
|
+
: undefined;
|
|
59
|
+
dispatchAction(action.action, resolvedPayload);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// FlatList special handling
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
function renderFlatListNode(
|
|
71
|
+
node: FlatListSchemaNode,
|
|
72
|
+
context: Record<string, unknown>,
|
|
73
|
+
registry: ComponentRegistry,
|
|
74
|
+
dispatchAction: DispatchAction,
|
|
75
|
+
fallbackComponent: React.ReactNode,
|
|
76
|
+
): ReactElement | null {
|
|
77
|
+
// Resolve all props including the data/items binding
|
|
78
|
+
const resolvedProps = resolveProps(node.props, context);
|
|
79
|
+
|
|
80
|
+
// Build action props for the FlatList container itself
|
|
81
|
+
const actionProps = node.actions ? buildActionProps(node.actions, context, dispatchAction) : {};
|
|
82
|
+
|
|
83
|
+
// Support both `data` (transformer output) and `items` (legacy) for the array source.
|
|
84
|
+
// Extract and resolve; the primitive receives it as `items`.
|
|
85
|
+
const {
|
|
86
|
+
data: resolvedData,
|
|
87
|
+
items: resolvedItems,
|
|
88
|
+
keyExtractor: keyExtractorExpr,
|
|
89
|
+
direction,
|
|
90
|
+
numColumns,
|
|
91
|
+
visible: _visible,
|
|
92
|
+
_propValues: _pv,
|
|
93
|
+
...restProps
|
|
94
|
+
} = resolvedProps;
|
|
95
|
+
|
|
96
|
+
const items = Array.isArray(resolvedData)
|
|
97
|
+
? (resolvedData as unknown[])
|
|
98
|
+
: Array.isArray(resolvedItems)
|
|
99
|
+
? (resolvedItems as unknown[])
|
|
100
|
+
: [];
|
|
101
|
+
|
|
102
|
+
// Build a keyExtractor function if specified in schema
|
|
103
|
+
let keyExtractorFn: ((item: unknown, index: number) => string) | undefined;
|
|
104
|
+
if (typeof keyExtractorExpr === 'string') {
|
|
105
|
+
const extractorExpr = keyExtractorExpr;
|
|
106
|
+
keyExtractorFn = (item: unknown, index: number) => {
|
|
107
|
+
const itemContext = { ...context, item, index };
|
|
108
|
+
const resolved = resolveValue(extractorExpr, itemContext);
|
|
109
|
+
return resolved !== undefined && resolved !== null ? String(resolved) : String(index);
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// itemTemplate: per-item context merges { item, index } into current context
|
|
114
|
+
const renderItemFn = ({ item, index }: { item: unknown; index: number }): ReactElement | null => {
|
|
115
|
+
const itemContext = { ...context, item, index };
|
|
116
|
+
return (
|
|
117
|
+
<RecursiveRenderer
|
|
118
|
+
node={node.itemTemplate}
|
|
119
|
+
context={itemContext}
|
|
120
|
+
registry={registry}
|
|
121
|
+
dispatchAction={dispatchAction}
|
|
122
|
+
fallbackComponent={fallbackComponent}
|
|
123
|
+
key={keyExtractorFn ? keyExtractorFn(item, index) : index}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const FlatListComponent = registry.resolve('flatlist');
|
|
129
|
+
if (!FlatListComponent) {
|
|
130
|
+
console.warn('[PushFrame] RecursiveRenderer: "flatlist" not found in registry.');
|
|
131
|
+
return fallbackComponent ? (fallbackComponent as ReactElement) : null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return React.createElement(FlatListComponent, {
|
|
135
|
+
...restProps,
|
|
136
|
+
...actionProps,
|
|
137
|
+
items,
|
|
138
|
+
direction,
|
|
139
|
+
numColumns,
|
|
140
|
+
keyExtractor: keyExtractorFn,
|
|
141
|
+
renderItem: renderItemFn,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// RecursiveRenderer
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Stateless recursive renderer. Evaluates schema nodes and produces React elements.
|
|
151
|
+
*
|
|
152
|
+
* - Evaluates `if` expression (string or non-string); returns null when falsy
|
|
153
|
+
* - Handles `visible` prop: returns null when visible resolves to false
|
|
154
|
+
* - Handles FlatList nodes with per-item context injection via `itemTemplate`
|
|
155
|
+
* - Supports `data` (transformer) and `items` (legacy) as FlatList data source
|
|
156
|
+
* - Resolves all prop bindings against current context
|
|
157
|
+
* - Merges `_propValues` into child context (custom component prop injection)
|
|
158
|
+
* - Wires actions to component event handler props
|
|
159
|
+
* - Looks up component in registry; renders fallback or null when not found
|
|
160
|
+
* - Recurses into children
|
|
161
|
+
*/
|
|
162
|
+
export function RecursiveRenderer({
|
|
163
|
+
node,
|
|
164
|
+
context,
|
|
165
|
+
registry,
|
|
166
|
+
dispatchAction,
|
|
167
|
+
fallbackComponent,
|
|
168
|
+
}: RecursiveRendererProps): ReactElement | null {
|
|
169
|
+
// 1. Evaluate conditional — skip entire subtree if falsy
|
|
170
|
+
if (!evaluateIf(node.if, context)) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 2. FlatList special case
|
|
175
|
+
if (node.type === 'flatlist') {
|
|
176
|
+
return renderFlatListNode(
|
|
177
|
+
node as FlatListSchemaNode,
|
|
178
|
+
context,
|
|
179
|
+
registry,
|
|
180
|
+
dispatchAction,
|
|
181
|
+
fallbackComponent,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 3. Resolve props
|
|
186
|
+
const resolvedProps = resolveProps(node.props, context);
|
|
187
|
+
|
|
188
|
+
// 4. Handle `visible` prop — transformer sets visible:false when a node is hidden.
|
|
189
|
+
// A missing or true value means visible; anything falsy hides the node.
|
|
190
|
+
if (resolvedProps.visible !== undefined && !resolvedProps.visible) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 5. Extract SDK-internal props that must not be forwarded to RN components.
|
|
195
|
+
// `_propValues` carries custom-component instance overrides → merge into
|
|
196
|
+
// child context so nested bindings resolve against them.
|
|
197
|
+
const {
|
|
198
|
+
visible: _visible,
|
|
199
|
+
_propValues: propValuesRaw,
|
|
200
|
+
...propsToForward
|
|
201
|
+
} = resolvedProps;
|
|
202
|
+
|
|
203
|
+
const propValues = propValuesRaw as Record<string, unknown> | undefined;
|
|
204
|
+
const childContext =
|
|
205
|
+
propValues && Object.keys(propValues).length > 0
|
|
206
|
+
? { ...context, ...propValues }
|
|
207
|
+
: context;
|
|
208
|
+
|
|
209
|
+
// 6. Wire actions → inject as trigger-named props
|
|
210
|
+
const actionProps = node.actions ? buildActionProps(node.actions, context, dispatchAction) : {};
|
|
211
|
+
|
|
212
|
+
// 7. Resolve component from registry
|
|
213
|
+
const Component = registry.resolve(node.type);
|
|
214
|
+
if (!Component) {
|
|
215
|
+
console.warn(
|
|
216
|
+
`[PushFrame] RecursiveRenderer: Unknown component type "${node.type}". ` +
|
|
217
|
+
`Registered types: ${registry.types().join(', ')}`,
|
|
218
|
+
);
|
|
219
|
+
return fallbackComponent ? (fallbackComponent as ReactElement) : null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 8. Render children recursively (using childContext for _propValues injection)
|
|
223
|
+
const children =
|
|
224
|
+
node.children && node.children.length > 0
|
|
225
|
+
? node.children.map((child, index) => (
|
|
226
|
+
<RecursiveRenderer
|
|
227
|
+
key={`${child.type}-${index}`}
|
|
228
|
+
node={child}
|
|
229
|
+
context={childContext}
|
|
230
|
+
registry={registry}
|
|
231
|
+
dispatchAction={dispatchAction}
|
|
232
|
+
fallbackComponent={fallbackComponent}
|
|
233
|
+
/>
|
|
234
|
+
))
|
|
235
|
+
: undefined;
|
|
236
|
+
|
|
237
|
+
return React.createElement(Component, {
|
|
238
|
+
...propsToForward,
|
|
239
|
+
...actionProps,
|
|
240
|
+
children,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binding resolver for PushFrame {{expression}} syntax.
|
|
3
|
+
*
|
|
4
|
+
* Rules:
|
|
5
|
+
* - A value that is entirely "{{expr}}" resolves to the bound value (any type)
|
|
6
|
+
* - A value with inline bindings "Hello {{user.name}}" resolves to a string
|
|
7
|
+
* - Unresolved paths return undefined — never throws
|
|
8
|
+
* - Non-string values are returned as-is
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const FULL_BINDING_RE = /^\{\{(.+)\}\}$/;
|
|
12
|
+
const INLINE_BINDING_RE = /\{\{([^}]+)\}\}/g;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Evaluate a binding expression string against the provided context.
|
|
16
|
+
* Uses Function constructor for safe, sandboxed expression evaluation.
|
|
17
|
+
* Returns undefined on any error.
|
|
18
|
+
*/
|
|
19
|
+
function evaluateExpression(expr: string, context: Record<string, unknown>): unknown {
|
|
20
|
+
try {
|
|
21
|
+
const keys = Object.keys(context);
|
|
22
|
+
const values = keys.map((k) => context[k]);
|
|
23
|
+
// eslint-disable-next-line no-new-func
|
|
24
|
+
const fn = new Function(...keys, `"use strict"; try { return (${expr}); } catch(e) { return undefined; }`) as (
|
|
25
|
+
...args: unknown[]
|
|
26
|
+
) => unknown;
|
|
27
|
+
return fn(...values);
|
|
28
|
+
} catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolve a single prop value against context.
|
|
35
|
+
*
|
|
36
|
+
* - String values: check for full binding "{{expr}}" or inline bindings "Hello {{name}}"
|
|
37
|
+
* - Non-string values: returned as-is
|
|
38
|
+
*/
|
|
39
|
+
export function resolveValue(value: unknown, context: Record<string, unknown>): unknown {
|
|
40
|
+
if (typeof value !== 'string') {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Full binding: entire string is "{{expr}}" — return the actual resolved value
|
|
45
|
+
const fullMatch = FULL_BINDING_RE.exec(value);
|
|
46
|
+
if (fullMatch?.[1] !== undefined) {
|
|
47
|
+
return evaluateExpression(fullMatch[1].trim(), context);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Inline binding: replace each "{{expr}}" occurrence within the string
|
|
51
|
+
if (INLINE_BINDING_RE.test(value)) {
|
|
52
|
+
INLINE_BINDING_RE.lastIndex = 0;
|
|
53
|
+
return value.replace(INLINE_BINDING_RE, (_, expr: string) => {
|
|
54
|
+
const resolved = evaluateExpression(expr.trim(), context);
|
|
55
|
+
return resolved !== undefined && resolved !== null ? String(resolved) : '';
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// No bindings — return as-is
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Resolve all values in a props map against the context.
|
|
65
|
+
* Recursively resolves nested objects and arrays.
|
|
66
|
+
*/
|
|
67
|
+
export function resolveProps(
|
|
68
|
+
props: Record<string, unknown> | undefined,
|
|
69
|
+
context: Record<string, unknown>,
|
|
70
|
+
): Record<string, unknown> {
|
|
71
|
+
if (!props) return {};
|
|
72
|
+
const resolved: Record<string, unknown> = {};
|
|
73
|
+
for (const [key, value] of Object.entries(props)) {
|
|
74
|
+
resolved[key] = resolveDeep(value, context);
|
|
75
|
+
}
|
|
76
|
+
return resolved;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Recursively resolve binding expressions in a value (including nested objects/arrays).
|
|
81
|
+
*/
|
|
82
|
+
export function resolveDeep(value: unknown, context: Record<string, unknown>): unknown {
|
|
83
|
+
if (Array.isArray(value)) {
|
|
84
|
+
return value.map((item) => resolveDeep(item, context));
|
|
85
|
+
}
|
|
86
|
+
if (typeof value === 'object' && value !== null) {
|
|
87
|
+
const result: Record<string, unknown> = {};
|
|
88
|
+
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
89
|
+
result[k] = resolveDeep(v, context);
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
return resolveValue(value, context);
|
|
94
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { resolveValue } from './bindingResolver';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Evaluate the `if` expression of a SchemaNode against the current context.
|
|
5
|
+
*
|
|
6
|
+
* Returns true → node should render
|
|
7
|
+
* Returns false → node (and its entire subtree) should be skipped
|
|
8
|
+
*
|
|
9
|
+
* Falsy values: false, null, undefined, 0, "" all hide the node.
|
|
10
|
+
* When `ifExpr` is undefined/null the node always renders.
|
|
11
|
+
*
|
|
12
|
+
* Accepts `unknown` to match the transformer's `if?: unknown` output:
|
|
13
|
+
* - String values are resolved as {{}} binding expressions
|
|
14
|
+
* - Non-string values (boolean, number, null) are coerced directly
|
|
15
|
+
*/
|
|
16
|
+
export function evaluateIf(
|
|
17
|
+
ifExpr: unknown,
|
|
18
|
+
context: Record<string, unknown>,
|
|
19
|
+
): boolean {
|
|
20
|
+
if (ifExpr === undefined || ifExpr === null) return true;
|
|
21
|
+
|
|
22
|
+
// Non-string: treat the value itself as the condition (e.g. `if: false`)
|
|
23
|
+
if (typeof ifExpr !== 'string') return Boolean(ifExpr);
|
|
24
|
+
|
|
25
|
+
const resolved = resolveValue(ifExpr, context);
|
|
26
|
+
|
|
27
|
+
// Standard falsy check — any falsy value hides the node
|
|
28
|
+
return Boolean(resolved);
|
|
29
|
+
}
|
package/src/renderer.tsx
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ReactElement } from 'react';
|
|
3
|
+
import type { PushframeNode, ActionsMap } from './schema';
|
|
4
|
+
import type { ComponentRegistry } from './registry';
|
|
5
|
+
import type { PushframeContext } from './bindings';
|
|
6
|
+
import { resolveProps } from './bindings';
|
|
7
|
+
import { evaluateCondition } from './conditions';
|
|
8
|
+
import type { PushframeAction } from './schema';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Called by the host app whenever an action is triggered.
|
|
12
|
+
* `eventName` is the prop key (e.g. "onPress").
|
|
13
|
+
* `action` is the resolved action descriptor from the schema.
|
|
14
|
+
*/
|
|
15
|
+
export type ActionHandler = (eventName: string, action: PushframeAction) => void;
|
|
16
|
+
|
|
17
|
+
interface RenderNodeOptions {
|
|
18
|
+
node: PushframeNode;
|
|
19
|
+
context: PushframeContext;
|
|
20
|
+
registry: ComponentRegistry;
|
|
21
|
+
onAction?: ActionHandler;
|
|
22
|
+
/** Key to help React reconcile siblings. */
|
|
23
|
+
key?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Recursively render a single PushframeNode.
|
|
28
|
+
*
|
|
29
|
+
* Returns null when:
|
|
30
|
+
* - The node's condition evaluates to false
|
|
31
|
+
* - The node's type is not found in the registry
|
|
32
|
+
*/
|
|
33
|
+
export function renderNode({
|
|
34
|
+
node,
|
|
35
|
+
context,
|
|
36
|
+
registry,
|
|
37
|
+
onAction,
|
|
38
|
+
key,
|
|
39
|
+
}: RenderNodeOptions): ReactElement | null {
|
|
40
|
+
// 1. Evaluate conditions — skip if false.
|
|
41
|
+
if (!evaluateCondition(node.conditions, context)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 2. Look up the component.
|
|
46
|
+
const Component = registry.get(node.type);
|
|
47
|
+
if (!Component) {
|
|
48
|
+
console.warn(
|
|
49
|
+
`[Pushframe] No component registered for type "${node.type}". ` +
|
|
50
|
+
`Registered types: ${registry.types().join(', ')}`,
|
|
51
|
+
);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 3. Resolve props.
|
|
56
|
+
const resolvedProps = resolveProps(node.props, context);
|
|
57
|
+
|
|
58
|
+
// 4. Build action callbacks and merge into props.
|
|
59
|
+
if (node.actions) {
|
|
60
|
+
mergeActionCallbacks(resolvedProps, node.actions, onAction);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 5. Render children recursively.
|
|
64
|
+
const childOptions = onAction
|
|
65
|
+
? { context, registry, onAction }
|
|
66
|
+
: { context, registry };
|
|
67
|
+
|
|
68
|
+
const children =
|
|
69
|
+
node.children && node.children.length > 0
|
|
70
|
+
? node.children.map((child, index) =>
|
|
71
|
+
renderNode({
|
|
72
|
+
node: child,
|
|
73
|
+
key: `${child.type}-${index}`,
|
|
74
|
+
...childOptions,
|
|
75
|
+
}),
|
|
76
|
+
)
|
|
77
|
+
: undefined;
|
|
78
|
+
|
|
79
|
+
// Helper for data-driven components (e.g. FlatList) to render a template
|
|
80
|
+
// node multiple times with different context (e.g. one render per list item).
|
|
81
|
+
const renderChild = (
|
|
82
|
+
childNode: PushframeNode,
|
|
83
|
+
contextOverride?: Record<string, unknown>,
|
|
84
|
+
) =>
|
|
85
|
+
renderNode({
|
|
86
|
+
node: childNode,
|
|
87
|
+
context: contextOverride ? { ...context, ...contextOverride } : context,
|
|
88
|
+
registry,
|
|
89
|
+
onAction,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// 6. Build component props — only include onAction when defined
|
|
93
|
+
// (required by exactOptionalPropertyTypes).
|
|
94
|
+
const componentProps = {
|
|
95
|
+
props: resolvedProps as Record<string, unknown>,
|
|
96
|
+
children,
|
|
97
|
+
rawChildren: node.children,
|
|
98
|
+
renderChild,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
return React.createElement(Component, { key, ...componentProps });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* For each entry in the actions map, inject a callback function into resolvedProps
|
|
106
|
+
* so the component can call e.g. `props.onPress()` without coupling to the schema.
|
|
107
|
+
*/
|
|
108
|
+
function mergeActionCallbacks(
|
|
109
|
+
resolvedProps: Record<string, unknown>,
|
|
110
|
+
actions: ActionsMap,
|
|
111
|
+
onAction?: ActionHandler,
|
|
112
|
+
): void {
|
|
113
|
+
for (const [eventName, action] of Object.entries(actions)) {
|
|
114
|
+
resolvedProps[eventName] = () => {
|
|
115
|
+
if (onAction) {
|
|
116
|
+
onAction(eventName, action);
|
|
117
|
+
} else {
|
|
118
|
+
console.warn(
|
|
119
|
+
`[Pushframe] Action "${eventName}" fired but no onAction handler was provided.`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|