@pushframe/sdk 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +405 -0
- package/dist/index.d.mts +89 -13
- package/dist/index.d.ts +89 -13
- package/dist/index.js +777 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +777 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/README.md
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# @pushframe/sdk
|
|
2
|
+
|
|
3
|
+
React Native SDK for [PushFrame](https://pushframe.io) — server-driven UI rendering engine that fetches and renders UI schemas at runtime without app updates.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @pushframe/sdk
|
|
9
|
+
# or
|
|
10
|
+
yarn add @pushframe/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Peer dependencies** (must be installed in your project):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install react react-native
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Optionally, if you use `react-native-safe-area-context`, the `safeareaview` component will automatically prefer it over the React Native core fallback.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
Wrap your app with `PushFrame.Provider`, then drop `PushFrame.Screen` or `PushFrame.Component` wherever you want server-driven UI to appear.
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { PushFrame } from '@pushframe/sdk';
|
|
29
|
+
|
|
30
|
+
export default function App() {
|
|
31
|
+
return (
|
|
32
|
+
<PushFrame.Provider apiKey="your-api-key" appVersion="1.0.0">
|
|
33
|
+
<PushFrame.Screen id="home" />
|
|
34
|
+
</PushFrame.Provider>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Provider
|
|
42
|
+
|
|
43
|
+
`PushFrame.Provider` is the root wrapper. Place it at the top of your component tree.
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<PushFrame.Provider
|
|
47
|
+
apiKey="your-api-key"
|
|
48
|
+
appVersion="1.0.0"
|
|
49
|
+
baseUrl="https://api.pushframe.io"
|
|
50
|
+
context={{ user: { name: 'Alice', isAdmin: true } }}
|
|
51
|
+
components={{ MyCustomCard }}
|
|
52
|
+
loadingComponent={<MySpinner />}
|
|
53
|
+
fallbackComponent={<MyErrorView />}
|
|
54
|
+
onAction={(action, payload) => {
|
|
55
|
+
if (action === 'navigate') {
|
|
56
|
+
navigation.navigate(payload.screen);
|
|
57
|
+
}
|
|
58
|
+
}}
|
|
59
|
+
onError={(error) => console.error(error)}
|
|
60
|
+
>
|
|
61
|
+
{/* your app */}
|
|
62
|
+
</PushFrame.Provider>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Provider Props
|
|
66
|
+
|
|
67
|
+
| Prop | Type | Required | Default | Description |
|
|
68
|
+
|------|------|----------|---------|-------------|
|
|
69
|
+
| `apiKey` | `string` | Yes | — | API key used for authentication (`Authorization` and `x-project-key` headers). |
|
|
70
|
+
| `appVersion` | `string` | No | `undefined` | Semver version string sent in fetch URL path. When omitted, the literal `"null"` is sent. Pass this explicitly — auto-detection is not supported. |
|
|
71
|
+
| `baseUrl` | `string` | No | `https://api.pushframe.io` | Override the API base URL. |
|
|
72
|
+
| `context` | `Record<string, unknown>` | No | `{}` | Global runtime data available to all binding expressions. |
|
|
73
|
+
| `components` | `Record<string, React.ComponentType>` | No | `{}` | Register custom components by type name, available to the renderer. |
|
|
74
|
+
| `loadingComponent` | `ReactNode` | No | `null` | Shown while a schema is being fetched. |
|
|
75
|
+
| `fallbackComponent` | `ReactNode` | No | `null` | Shown when a schema fetch fails. |
|
|
76
|
+
| `onAction` | `(action: string, payload?) => void` | No | — | Called for any action that is not handled internally (see [Actions](#actions)). |
|
|
77
|
+
| `onError` | `(error: Error) => void` | No | — | Called when a schema fetch or render error occurs. |
|
|
78
|
+
| `children` | `ReactNode` | Yes | — | Your application content. |
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Slot Components
|
|
83
|
+
|
|
84
|
+
### `PushFrame.Screen`
|
|
85
|
+
|
|
86
|
+
Fetches and renders a full-page screen schema. Uses `flex: 1` layout.
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
<PushFrame.Screen
|
|
90
|
+
id="home"
|
|
91
|
+
context={{ currentTab: 'feed' }}
|
|
92
|
+
onAction={(action, payload) => {
|
|
93
|
+
if (action === 'navigate') {
|
|
94
|
+
navigation.navigate(payload.screen);
|
|
95
|
+
return true; // stop bubbling to Provider
|
|
96
|
+
}
|
|
97
|
+
}}
|
|
98
|
+
/>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Fetches from: `GET {baseUrl}/screens/{id}/{appVersion}`
|
|
102
|
+
|
|
103
|
+
### `PushFrame.Component`
|
|
104
|
+
|
|
105
|
+
Fetches and renders an inline component schema. Renders inline without forced flex.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<PushFrame.Component
|
|
109
|
+
id="product-card"
|
|
110
|
+
context={{ productId: '123' }}
|
|
111
|
+
/>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Fetches from: `GET {baseUrl}/components/{id}/{appVersion}`
|
|
115
|
+
|
|
116
|
+
### Slot Props
|
|
117
|
+
|
|
118
|
+
Both components share these props:
|
|
119
|
+
|
|
120
|
+
| Prop | Type | Required | Description |
|
|
121
|
+
|------|------|----------|-------------|
|
|
122
|
+
| `id` | `string` | Yes | Screen or component ID. |
|
|
123
|
+
| `context` | `Record<string, unknown>` | No | Local context merged with (and overriding) the Provider context. |
|
|
124
|
+
| `isLoading` | `boolean` | No | Externally controlled loading state. |
|
|
125
|
+
| `loadingComponent` | `ReactNode` | No | Overrides the Provider-level loading UI. |
|
|
126
|
+
| `fallbackComponent` | `ReactNode` | No | Overrides the Provider-level fallback UI. |
|
|
127
|
+
| `onAction` | `(action, payload?) => boolean \| void` | No | Local action handler. Return `true` to stop the action from bubbling to the Provider. |
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Schema Format
|
|
132
|
+
|
|
133
|
+
The API server must return schemas in this envelope format:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"schema": {
|
|
138
|
+
"type": "view",
|
|
139
|
+
"props": { "style": { "flex": 1, "padding": 16 } },
|
|
140
|
+
"children": [
|
|
141
|
+
{
|
|
142
|
+
"type": "text",
|
|
143
|
+
"props": { "value": "Hello {{user.name}}" }
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### SchemaNode
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
interface SchemaNode {
|
|
154
|
+
id?: string;
|
|
155
|
+
type: string; // Component type (see built-in types below)
|
|
156
|
+
props?: Record<string, unknown>; // Props passed to the component
|
|
157
|
+
children?: SchemaNode[]; // Nested nodes
|
|
158
|
+
actions?: Action[]; // Event → action mappings
|
|
159
|
+
if?: string; // Binding expression; falsy = node is hidden
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface Action {
|
|
163
|
+
trigger: string; // e.g. "onPress", "onChange", "onLongPress"
|
|
164
|
+
action: string; // Action name (built-in or custom)
|
|
165
|
+
payload?: Record<string, unknown>;
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Built-in Component Types
|
|
170
|
+
|
|
171
|
+
| Type | React Native Equivalent | Notes |
|
|
172
|
+
|------|------------------------|-------|
|
|
173
|
+
| `view` | `View` | Layout container |
|
|
174
|
+
| `scrollview` | `ScrollView` | Scrollable container; supports `scroll-to` action |
|
|
175
|
+
| `text` | `Text` | Use `value` prop for text content |
|
|
176
|
+
| `image` | `Image` | Use `src` prop for URI strings |
|
|
177
|
+
| `pressable` | `Pressable` | Triggers `onPress`, `onLongPress` |
|
|
178
|
+
| `textinput` | `TextInput` | `onChange` → `onChangeText`, `onSubmit` → `onSubmitEditing` |
|
|
179
|
+
| `flatlist` | `FlatList` | Requires `items` array and `renderItem` node (see below) |
|
|
180
|
+
| `modal` | `Modal` | |
|
|
181
|
+
| `activityindicator` | `ActivityIndicator` | Spinner |
|
|
182
|
+
| `switch` | `Switch` | `onChange` → `onValueChange` |
|
|
183
|
+
| `keyboardavoidingview` | `KeyboardAvoidingView` | |
|
|
184
|
+
| `safeareaview` | `SafeAreaView` | Prefers `react-native-safe-area-context` when available |
|
|
185
|
+
| `statusbar` | `StatusBar` | |
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Data Binding
|
|
190
|
+
|
|
191
|
+
Props support `{{expression}}` syntax resolved against the runtime context.
|
|
192
|
+
|
|
193
|
+
**Full binding** — resolves to the actual value (any type):
|
|
194
|
+
```json
|
|
195
|
+
{ "type": "text", "props": { "value": "{{user.name}}" } }
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Inline binding** — string interpolation:
|
|
199
|
+
```json
|
|
200
|
+
{ "type": "text", "props": { "value": "Hello, {{user.name}}!" } }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Nested paths:**
|
|
204
|
+
```json
|
|
205
|
+
{ "type": "image", "props": { "src": "{{product.imageUrl}}" } }
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Unresolved paths silently return `undefined` — they never cause a crash.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Conditional Rendering
|
|
213
|
+
|
|
214
|
+
Use the `if` field with a binding expression to show or hide a node:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"type": "text",
|
|
219
|
+
"props": { "value": "Admin Panel" },
|
|
220
|
+
"if": "{{user.isAdmin}}"
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Falsy values (`false`, `null`, `undefined`, `0`, `""`) hide the node and its entire subtree.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Actions
|
|
229
|
+
|
|
230
|
+
Actions link UI events to behaviour.
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"type": "pressable",
|
|
235
|
+
"actions": [
|
|
236
|
+
{
|
|
237
|
+
"trigger": "onPress",
|
|
238
|
+
"action": "show-toast",
|
|
239
|
+
"payload": { "message": "Saved!", "type": "success" }
|
|
240
|
+
}
|
|
241
|
+
],
|
|
242
|
+
"children": [
|
|
243
|
+
{ "type": "text", "props": { "value": "Save" } }
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Built-in Actions
|
|
249
|
+
|
|
250
|
+
These are handled internally and do **not** reach your `onAction` callback.
|
|
251
|
+
|
|
252
|
+
| Action | Payload |
|
|
253
|
+
|--------|---------|
|
|
254
|
+
| `show-toast` | `message: string`, `duration?: number` (ms), `type?: "success" \| "error" \| "info" \| "warning"` |
|
|
255
|
+
| `show-bottom-sheet` | `schema: SchemaNode`, `context?: Record<string, unknown>` |
|
|
256
|
+
| `dismiss-bottom-sheet` | *(none)* |
|
|
257
|
+
| `scroll-to` | `x?: number`, `y?: number`, `animated?: boolean` |
|
|
258
|
+
|
|
259
|
+
All other action names bubble to the slot's `onAction`, then to the Provider's `onAction` if the slot handler does not return `true`.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## FlatList
|
|
264
|
+
|
|
265
|
+
`flatlist` nodes require an `items` array in props and a `renderItem` node as a sibling field (not inside `children`). The renderer injects `{ item, index }` into the context for each rendered item.
|
|
266
|
+
|
|
267
|
+
```json
|
|
268
|
+
{
|
|
269
|
+
"type": "flatlist",
|
|
270
|
+
"props": {
|
|
271
|
+
"items": "{{products}}",
|
|
272
|
+
"direction": "vertical"
|
|
273
|
+
},
|
|
274
|
+
"renderItem": {
|
|
275
|
+
"type": "view",
|
|
276
|
+
"props": { "style": { "padding": 8 } },
|
|
277
|
+
"children": [
|
|
278
|
+
{ "type": "text", "props": { "value": "{{item.name}}" } },
|
|
279
|
+
{ "type": "text", "props": { "value": "{{item.price}}" } }
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
| Prop | Type | Description |
|
|
286
|
+
|------|------|-------------|
|
|
287
|
+
| `items` | `array \| string` | Array of data items (or binding expression resolving to one). |
|
|
288
|
+
| `direction` | `"vertical" \| "horizontal"` | Maps to the `horizontal` prop. Default: `"vertical"`. |
|
|
289
|
+
| `numColumns` | `number` | Passed through to `FlatList`. |
|
|
290
|
+
| `keyExtractor` | `string` | Binding expression evaluated per item (e.g. `"{{item.id}}"`). |
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Custom Components
|
|
295
|
+
|
|
296
|
+
Register your own React Native components and reference them by type name in schemas.
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
import { PushFrame } from '@pushframe/sdk';
|
|
300
|
+
|
|
301
|
+
function ProductCard({ title, price }: { title: string; price: string }) {
|
|
302
|
+
return (
|
|
303
|
+
<View>
|
|
304
|
+
<Text>{title}</Text>
|
|
305
|
+
<Text>{price}</Text>
|
|
306
|
+
</View>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
<PushFrame.Provider
|
|
311
|
+
apiKey="..."
|
|
312
|
+
components={{ 'product-card': ProductCard }}
|
|
313
|
+
>
|
|
314
|
+
{/* Schema can now use "type": "product-card" */}
|
|
315
|
+
</PushFrame.Provider>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Built-in type names are reserved and cannot be overridden.
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Context Merging
|
|
323
|
+
|
|
324
|
+
Context flows from Provider → Slot, with the Slot's context taking precedence on key conflicts.
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
// Provider sets global context
|
|
328
|
+
<PushFrame.Provider context={{ user: { name: 'Alice' }, theme: 'dark' }}>
|
|
329
|
+
{/* Slot adds/overrides local context */}
|
|
330
|
+
<PushFrame.Screen
|
|
331
|
+
id="profile"
|
|
332
|
+
context={{ pageTitle: 'My Profile', theme: 'light' }} // overrides theme
|
|
333
|
+
/>
|
|
334
|
+
</PushFrame.Provider>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## TypeScript
|
|
340
|
+
|
|
341
|
+
The SDK is written in TypeScript. Key types are exported for use in your own code:
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import type {
|
|
345
|
+
SchemaNode,
|
|
346
|
+
Action,
|
|
347
|
+
PushFrameProviderProps,
|
|
348
|
+
PushFrameSlotProps,
|
|
349
|
+
ToastPayload,
|
|
350
|
+
BottomSheetPayload,
|
|
351
|
+
} from '@pushframe/sdk';
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Advanced: Accessing Context
|
|
357
|
+
|
|
358
|
+
Use the `usePushFrameContext` hook inside any component rendered within `PushFrame.Provider` to access SDK internals.
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
import { usePushFrameContext } from '@pushframe/sdk';
|
|
362
|
+
|
|
363
|
+
function MyButton() {
|
|
364
|
+
const { showToast } = usePushFrameContext();
|
|
365
|
+
return (
|
|
366
|
+
<Button onPress={() => showToast({ message: 'Hello!', type: 'info' })} />
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## API Reference
|
|
374
|
+
|
|
375
|
+
### Request Format
|
|
376
|
+
|
|
377
|
+
Every schema fetch includes these headers:
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
Authorization: Bearer {apiKey}
|
|
381
|
+
x-project-key: {apiKey}
|
|
382
|
+
Accept: application/json
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
IDs and `appVersion` are `encodeURIComponent`-encoded in the URL path. When `appVersion` is not set on the Provider, the literal string `"null"` is sent (e.g. `GET /screens/home/null`).
|
|
386
|
+
|
|
387
|
+
### Response Format
|
|
388
|
+
|
|
389
|
+
```json
|
|
390
|
+
{
|
|
391
|
+
"schema": { },
|
|
392
|
+
"version": 28,
|
|
393
|
+
"status": "published",
|
|
394
|
+
"publishedAt": "2024-01-01T00:00:00Z",
|
|
395
|
+
"targetMinVersion": "0.0.0"
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
The SDK extracts `response.schema` automatically. A bare `SchemaNode` (without the envelope) is also accepted as a fallback.
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## License
|
|
404
|
+
|
|
405
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -127,15 +127,23 @@ declare class ComponentRegistry {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
interface SchemaNode {
|
|
130
|
+
id?: string;
|
|
130
131
|
type: string;
|
|
131
132
|
props?: Record<string, unknown>;
|
|
132
133
|
children?: SchemaNode[];
|
|
134
|
+
/** Per-item render template for flatlist nodes. */
|
|
135
|
+
itemTemplate?: SchemaNode;
|
|
133
136
|
actions?: Action[];
|
|
134
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Conditional render expression. String values are resolved against the
|
|
139
|
+
* context via {{}} binding syntax. Non-string values (boolean, number, null)
|
|
140
|
+
* are coerced directly. Falsy → node is hidden.
|
|
141
|
+
*/
|
|
142
|
+
if?: unknown;
|
|
135
143
|
}
|
|
136
144
|
interface FlatListSchemaNode extends SchemaNode {
|
|
137
145
|
type: 'flatlist';
|
|
138
|
-
|
|
146
|
+
itemTemplate: SchemaNode;
|
|
139
147
|
}
|
|
140
148
|
interface Action {
|
|
141
149
|
trigger: string;
|
|
@@ -179,17 +187,22 @@ interface PushFrameTextProps extends TextProps {
|
|
|
179
187
|
/** Pushframe: converted to handlers by RecursiveRenderer before component renders. */
|
|
180
188
|
actions?: Action[];
|
|
181
189
|
/**
|
|
182
|
-
* Text content
|
|
183
|
-
* Pushframe
|
|
190
|
+
* Text content. Takes precedence over `children`.
|
|
191
|
+
* Pushframe schema convention: use `value`.
|
|
184
192
|
*/
|
|
185
193
|
value?: string;
|
|
194
|
+
/**
|
|
195
|
+
* Alias for `value`. The transformer outputs the Craft editor's `content`
|
|
196
|
+
* field here. `value` takes precedence when both are present.
|
|
197
|
+
*/
|
|
198
|
+
content?: string;
|
|
186
199
|
}
|
|
187
200
|
/**
|
|
188
201
|
* Thin wrapper over React Native Text.
|
|
189
|
-
* Strips `if`, `actions` before forwarding to RN.
|
|
190
|
-
*
|
|
202
|
+
* Strips `if`, `actions`, `content` before forwarding to RN.
|
|
203
|
+
* Display priority: value > content > children (children remain in rest).
|
|
191
204
|
*/
|
|
192
|
-
declare function Text({ if: _if, actions: _actions, value,
|
|
205
|
+
declare function Text({ if: _if, actions: _actions, value, content, ...rest }: PushFrameTextProps): react_jsx_runtime.JSX.Element;
|
|
193
206
|
|
|
194
207
|
interface PushFrameViewProps extends ViewProps {
|
|
195
208
|
if?: string;
|
|
@@ -331,11 +344,18 @@ declare function KeyboardAvoidingView({ if: _if, actions: _actions, ...rest }: P
|
|
|
331
344
|
interface PushFrameSafeAreaViewProps extends ViewProps {
|
|
332
345
|
if?: string;
|
|
333
346
|
actions?: Action[];
|
|
347
|
+
/**
|
|
348
|
+
* Edges to apply safe-area insets on.
|
|
349
|
+
* Forwarded to react-native-safe-area-context's SafeAreaView when available.
|
|
350
|
+
* Example: ['top', 'bottom']
|
|
351
|
+
*/
|
|
352
|
+
edges?: string[];
|
|
334
353
|
}
|
|
335
354
|
/**
|
|
336
355
|
* Thin wrapper over SafeAreaView.
|
|
337
356
|
* Uses react-native-safe-area-context when available, RN core otherwise.
|
|
338
|
-
* Strips `if`, `actions` before forwarding
|
|
357
|
+
* Strips `if`, `actions` before forwarding; passes `edges` through to
|
|
358
|
+
* react-native-safe-area-context (ignored gracefully in the RN core fallback).
|
|
339
359
|
*/
|
|
340
360
|
declare function SafeAreaView({ if: _if, actions: _actions, ...rest }: PushFrameSafeAreaViewProps): react_jsx_runtime.JSX.Element;
|
|
341
361
|
|
|
@@ -365,9 +385,12 @@ interface RecursiveRendererProps {
|
|
|
365
385
|
/**
|
|
366
386
|
* Stateless recursive renderer. Evaluates schema nodes and produces React elements.
|
|
367
387
|
*
|
|
368
|
-
* - Evaluates `if` expression; returns null when falsy
|
|
369
|
-
* - Handles
|
|
388
|
+
* - Evaluates `if` expression (string or non-string); returns null when falsy
|
|
389
|
+
* - Handles `visible` prop: returns null when visible resolves to false
|
|
390
|
+
* - Handles FlatList nodes with per-item context injection via `itemTemplate`
|
|
391
|
+
* - Supports `data` (transformer) and `items` (legacy) as FlatList data source
|
|
370
392
|
* - Resolves all prop bindings against current context
|
|
393
|
+
* - Merges `_propValues` into child context (custom component prop injection)
|
|
371
394
|
* - Wires actions to component event handler props
|
|
372
395
|
* - Looks up component in registry; renders fallback or null when not found
|
|
373
396
|
* - Recurses into children
|
|
@@ -407,9 +430,13 @@ declare function resolveDeep(value: unknown, context: Record<string, unknown>):
|
|
|
407
430
|
* Returns false → node (and its entire subtree) should be skipped
|
|
408
431
|
*
|
|
409
432
|
* Falsy values: false, null, undefined, 0, "" all hide the node.
|
|
410
|
-
* When `ifExpr` is undefined the node always renders.
|
|
433
|
+
* When `ifExpr` is undefined/null the node always renders.
|
|
434
|
+
*
|
|
435
|
+
* Accepts `unknown` to match the transformer's `if?: unknown` output:
|
|
436
|
+
* - String values are resolved as {{}} binding expressions
|
|
437
|
+
* - Non-string values (boolean, number, null) are coerced directly
|
|
411
438
|
*/
|
|
412
|
-
declare function evaluateIf(ifExpr:
|
|
439
|
+
declare function evaluateIf(ifExpr: unknown, context: Record<string, unknown>): boolean;
|
|
413
440
|
|
|
414
441
|
interface PushFrameScrollViewProps extends ScrollViewProps {
|
|
415
442
|
if?: string;
|
|
@@ -422,6 +449,55 @@ interface PushFrameScrollViewProps extends ScrollViewProps {
|
|
|
422
449
|
*/
|
|
423
450
|
declare const ScrollView: React__default.ForwardRefExoticComponent<PushFrameScrollViewProps & React__default.RefAttributes<ScrollView$1>>;
|
|
424
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Craft.js → PushFrame SDUI transformer
|
|
454
|
+
*
|
|
455
|
+
* Converts a Craft.js editor schema (produced by the dashboard) into the
|
|
456
|
+
* SDUINode tree consumed by the SDK's RecursiveRenderer at runtime.
|
|
457
|
+
*
|
|
458
|
+
* This module is intended for use by the backend/service layer or the
|
|
459
|
+
* dashboard — it is NOT executed inside the React Native app at runtime.
|
|
460
|
+
*/
|
|
461
|
+
|
|
462
|
+
type CraftNode = {
|
|
463
|
+
type: {
|
|
464
|
+
resolvedName: string;
|
|
465
|
+
};
|
|
466
|
+
props?: Record<string, unknown>;
|
|
467
|
+
nodes?: string[];
|
|
468
|
+
parent?: string;
|
|
469
|
+
};
|
|
470
|
+
type CraftSchema = Record<string, CraftNode>;
|
|
471
|
+
/**
|
|
472
|
+
* The SDUI node shape produced by this transformer and consumed by the SDK's
|
|
473
|
+
* RecursiveRenderer. Aligned with `SchemaNode` from the SDK context.
|
|
474
|
+
*/
|
|
475
|
+
interface SDUINode extends SchemaNode {
|
|
476
|
+
id: string;
|
|
477
|
+
type: string;
|
|
478
|
+
props: Record<string, unknown>;
|
|
479
|
+
children?: SDUINode[];
|
|
480
|
+
/** FlatList per-item render template. */
|
|
481
|
+
itemTemplate?: SDUINode;
|
|
482
|
+
/** Conditional render expression. Falsy → node is hidden. */
|
|
483
|
+
if?: unknown;
|
|
484
|
+
actions?: Action[];
|
|
485
|
+
}
|
|
486
|
+
interface CustomComponentDef {
|
|
487
|
+
rootNodeId: string;
|
|
488
|
+
nodes: CraftSchema;
|
|
489
|
+
}
|
|
490
|
+
type CustomComponentsMap = Record<string, CustomComponentDef>;
|
|
491
|
+
/**
|
|
492
|
+
* Convert a Craft.js editor schema into an SDUINode tree ready for the SDK's
|
|
493
|
+
* RecursiveRenderer.
|
|
494
|
+
*
|
|
495
|
+
* @param craftJson - The full Craft.js node map (keyed by node ID, must include "ROOT").
|
|
496
|
+
* @param customComponents - Optional map of reusable component definitions.
|
|
497
|
+
* @returns The root SDUINode.
|
|
498
|
+
*/
|
|
499
|
+
declare function transformCraftToSDUI(craftJson: CraftSchema, customComponents?: CustomComponentsMap): SDUINode;
|
|
500
|
+
|
|
425
501
|
/**
|
|
426
502
|
* The PushFrame namespace. Import this as the single entry-point for the SDK.
|
|
427
503
|
*
|
|
@@ -455,4 +531,4 @@ declare const PushFrame: {
|
|
|
455
531
|
readonly StatusBar: typeof StatusBar;
|
|
456
532
|
};
|
|
457
533
|
|
|
458
|
-
export { type Action, ActivityIndicator, type BottomSheetPayload, ComponentRegistry, type DispatchAction, FlatList, type FlatListSchemaNode, Image, KeyboardAvoidingView, Modal, Pressable, PushFrame, type PushFrameActivityIndicatorProps, PushFrameComponent, PushFrameContext, type PushFrameContextValue, type PushFrameFlatListProps, type PushFrameImageProps, type PushFrameKeyboardAvoidingViewProps, type PushFrameModalProps, type PushFramePressableProps, PushFrameProvider, type PushFrameProviderProps, type PushFrameSafeAreaViewProps, PushFrameScreen, type PushFrameScrollViewProps, type PushFrameSlotProps, type PushFrameStatusBarProps, type PushFrameSwitchProps, type PushFrameTextInputProps, type PushFrameTextProps, type PushFrameViewProps, RecursiveRenderer, type RecursiveRendererProps, SafeAreaView, type SchemaNode, type ScrollToPayload, ScrollView, StatusBar, Switch, Text, TextInput, type ToastPayload, View, evaluateIf, resolveDeep, resolveProps, resolveValue, usePushFrameContext };
|
|
534
|
+
export { type Action, ActivityIndicator, type BottomSheetPayload, ComponentRegistry, type CraftNode, type CraftSchema, type CustomComponentDef, type CustomComponentsMap, type DispatchAction, FlatList, type FlatListSchemaNode, Image, KeyboardAvoidingView, Modal, Pressable, PushFrame, type PushFrameActivityIndicatorProps, PushFrameComponent, PushFrameContext, type PushFrameContextValue, type PushFrameFlatListProps, type PushFrameImageProps, type PushFrameKeyboardAvoidingViewProps, type PushFrameModalProps, type PushFramePressableProps, PushFrameProvider, type PushFrameProviderProps, type PushFrameSafeAreaViewProps, PushFrameScreen, type PushFrameScrollViewProps, type PushFrameSlotProps, type PushFrameStatusBarProps, type PushFrameSwitchProps, type PushFrameTextInputProps, type PushFrameTextProps, type PushFrameViewProps, RecursiveRenderer, type RecursiveRendererProps, type SDUINode, SafeAreaView, type SchemaNode, type ScrollToPayload, ScrollView, StatusBar, Switch, Text, TextInput, type ToastPayload, View, evaluateIf, resolveDeep, resolveProps, resolveValue, transformCraftToSDUI, usePushFrameContext };
|