@sonata-innovations/fiber-fbre 3.2.1 → 3.4.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/AGENTS.md +58 -0
- package/CHANGELOG.md +129 -0
- package/README.md +61 -365
- package/dist/fiber-fbre.cjs +1 -1
- package/dist/fiber-fbre.css +1 -1
- package/dist/fiber-fbre.js +1555 -1496
- package/dist/index.d.ts +27 -3
- package/docs/features/confirmation-screen.md +125 -0
- package/docs/features/fbre-theming.md +264 -0
- package/docs/fiber-concepts.md +461 -0
- package/docs/integration/fbre.md +387 -0
- package/package.json +19 -10
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# @sonata-innovations/fiber-fbre
|
|
1
|
+
# @sonata-innovations/fiber-fbre
|
|
2
2
|
|
|
3
|
-
Fiber Render Engine — consumes Flow JSON and renders data collection forms. Handles conditional logic, validation, screen transitions, and outputs collected FlowData back to the parent application.
|
|
3
|
+
Fiber Render Engine — consumes Flow JSON and renders data collection forms. Handles conditional logic, validation, calculations, screen transitions, theming, and outputs collected FlowData back to the parent application. Renders 30 component types across standard and conversational modes; zero UI-framework dependency (custom CSS only).
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
@@ -68,7 +68,7 @@ function App() {
|
|
|
68
68
|
npm install @sonata-innovations/fiber-fbre
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
Then import the CSS in your app entry point:
|
|
71
|
+
Then import the CSS **once** in your app entry point — without it the form renders unstyled:
|
|
72
72
|
|
|
73
73
|
```ts
|
|
74
74
|
import "@sonata-innovations/fiber-fbre/styles";
|
|
@@ -78,6 +78,22 @@ import "@sonata-innovations/fiber-fbre/styles";
|
|
|
78
78
|
|
|
79
79
|
**Runtime dependencies:** `zustand`, `@sonata-innovations/fiber-types`, `@sonata-innovations/fiber-shared`
|
|
80
80
|
|
|
81
|
+
## Documentation
|
|
82
|
+
|
|
83
|
+
This README covers evaluation and day-to-day API lookup. Deeper guides ship **inside the package** under `docs/` (and an `AGENTS.md` routing guide at the package root), and are browsable in the public docs mirror at [github.com/sonata-innovations/fiber-docs](https://github.com/sonata-innovations/fiber-docs).
|
|
84
|
+
|
|
85
|
+
| Topic | In package | Mirror |
|
|
86
|
+
|-------|-----------|--------|
|
|
87
|
+
| Concepts overview | `docs/fiber-concepts.md` | [fiber-concepts.md](https://github.com/sonata-innovations/fiber-docs/blob/main/fiber-concepts.md) |
|
|
88
|
+
| FBRE integration guide | `docs/integration/fbre.md` | [integration/fbre.md](https://github.com/sonata-innovations/fiber-docs/blob/main/integration/fbre.md) |
|
|
89
|
+
| Theming reference | `docs/features/fbre-theming.md` | [features/fbre-theming.md](https://github.com/sonata-innovations/fiber-docs/blob/main/features/fbre-theming.md) |
|
|
90
|
+
| Confirmation screen | `docs/features/confirmation-screen.md` | [features/confirmation-screen.md](https://github.com/sonata-innovations/fiber-docs/blob/main/features/confirmation-screen.md) |
|
|
91
|
+
|
|
92
|
+
Schema references (Flow input and FlowData output) ship in the **`@sonata-innovations/fiber-types`** package under `docs/schema/`, and are mirrored at:
|
|
93
|
+
|
|
94
|
+
- [schema/flow-schema.md](https://github.com/sonata-innovations/fiber-docs/blob/main/schema/flow-schema.md) · [schema/flow-quick-reference.md](https://github.com/sonata-innovations/fiber-docs/blob/main/schema/flow-quick-reference.md)
|
|
95
|
+
- [schema/flow-data-schema.md](https://github.com/sonata-innovations/fiber-docs/blob/main/schema/flow-data-schema.md) · [schema/flow-schema.json](https://github.com/sonata-innovations/fiber-docs/blob/main/schema/flow-schema.json)
|
|
96
|
+
|
|
81
97
|
## API
|
|
82
98
|
|
|
83
99
|
### `<FBRE />` Props
|
|
@@ -91,7 +107,7 @@ Render a Flow object you already have in memory.
|
|
|
91
107
|
| Prop | Type | Required | Description |
|
|
92
108
|
|------|------|----------|-------------|
|
|
93
109
|
| `flow` | `Flow` | Yes | Flow JSON object |
|
|
94
|
-
| `data` | `FlowData` | No | Pre-populated form data |
|
|
110
|
+
| `data` | `FlowData` | No | Pre-populated form data (not reactive — read once at mount) |
|
|
95
111
|
| `mode` | `FlowModeType` | No | Override form mode (`"standard"` \| `"conversational"`) |
|
|
96
112
|
| `screenIndex` | `number` | No | Initial screen index (default `0`) |
|
|
97
113
|
| `theme` | `ThemeConfig` | No | Override theme settings (merged over `flow.config.theme`) |
|
|
@@ -99,9 +115,8 @@ Render a Flow object you already have in memory.
|
|
|
99
115
|
| `controls` | `ControlsConfig` | No | Override controls settings (merged over `flow.config.controls`) |
|
|
100
116
|
| `context` | `Record<string, string \| boolean \| number>` | No | External context values for condition evaluation and calculations |
|
|
101
117
|
| `storeRef` | `MutableRefObject<StoreApi<FBREStoreState> \| null>` | No | Ref to access the Zustand store |
|
|
102
|
-
| `onFlowComplete` | `(data: FlowData) => void
|
|
118
|
+
| `onFlowComplete` | `(data: FlowData) => void \| ConfirmationResult \| Promise<void \| ConfirmationResult>` | Yes | Called when the user completes the flow. Return/resolve a `ConfirmationResult` to override the configured confirmation message; a rejected Promise surfaces a completion error |
|
|
103
119
|
| `onScreenChange` | `(index: number, data: FlowData) => void` | No | Called on screen navigation |
|
|
104
|
-
| `onScreenValidationChange` | `(index: number, data: any) => void` | No | Called when screen validity changes |
|
|
105
120
|
|
|
106
121
|
#### Remote Mode
|
|
107
122
|
|
|
@@ -112,7 +127,7 @@ Fetch a published flow from a Fiber API by ID.
|
|
|
112
127
|
| `flowId` | `string` | Yes | ID of the published flow |
|
|
113
128
|
| `apiEndpoint` | `string` | Yes | Base URL of the Fiber API |
|
|
114
129
|
| `apiKey` | `string` | No | API key for authentication |
|
|
115
|
-
| `data` | `FlowData` | No | Pre-populated form data |
|
|
130
|
+
| `data` | `FlowData` | No | Pre-populated form data (not reactive) |
|
|
116
131
|
| `mode` | `FlowModeType` | No | Override form mode (`"standard"` \| `"conversational"`) |
|
|
117
132
|
| `screenIndex` | `number` | No | Initial screen index (default `0`) |
|
|
118
133
|
| `theme` | `ThemeConfig` | No | Override theme settings |
|
|
@@ -120,13 +135,12 @@ Fetch a published flow from a Fiber API by ID.
|
|
|
120
135
|
| `controls` | `ControlsConfig` | No | Override controls settings |
|
|
121
136
|
| `context` | `Record<string, string \| boolean \| number>` | No | External context values for condition evaluation and calculations |
|
|
122
137
|
| `storeRef` | `MutableRefObject<StoreApi<FBREStoreState> \| null>` | No | Ref to access the Zustand store |
|
|
123
|
-
| `onFlowComplete` | `(data: FlowData) => void
|
|
138
|
+
| `onFlowComplete` | `(data: FlowData) => void \| ConfirmationResult \| Promise<void \| ConfirmationResult>` | Yes | Called when the user completes the flow (see Local Mode) |
|
|
124
139
|
| `onScreenChange` | `(index: number, data: FlowData) => void` | No | Called on screen navigation |
|
|
125
|
-
| `onScreenValidationChange` | `(index: number, data: any) => void` | No | Called when screen validity changes |
|
|
126
140
|
|
|
127
141
|
#### Server-Driven Mode
|
|
128
142
|
|
|
129
|
-
Session-based rendering. The server evaluates conditions and validation; the client renders one screen at a time.
|
|
143
|
+
Session-based rendering. The server evaluates conditions and validation; the client renders one screen at a time. On completion it resets the submit button and shows a terminal confirmation (from `config.confirmation`, else a generic "Thank you"); `onFlowComplete` still fires with the server result if you want to drive your own post-completion UI.
|
|
130
144
|
|
|
131
145
|
| Prop | Type | Required | Description |
|
|
132
146
|
|------|------|----------|-------------|
|
|
@@ -137,7 +151,7 @@ Session-based rendering. The server evaluates conditions and validation; the cli
|
|
|
137
151
|
| `theme` | `ThemeConfig` | No | Override theme settings |
|
|
138
152
|
| `context` | `Record<string, string \| boolean \| number>` | No | External context values for condition evaluation and calculations |
|
|
139
153
|
| `onFlowComplete` | `(data: FlowData) => void` | Yes | Called when the user completes the flow |
|
|
140
|
-
| `onScreenChange` | `(screenNumber: number) => void` | No | Called on screen navigation (
|
|
154
|
+
| `onScreenChange` | `(screenNumber: number) => void` | No | Called on screen navigation (receives screen number, not index + data) |
|
|
141
155
|
|
|
142
156
|
### Imperative Access
|
|
143
157
|
|
|
@@ -155,6 +169,8 @@ const data = storeApi.getState().getFlowData();
|
|
|
155
169
|
const apiContext = useFBREApi();
|
|
156
170
|
```
|
|
157
171
|
|
|
172
|
+
`getMaxScreenIndex()` on the store returns the maximum screen **index** (screen count − 1). (Renamed from `getMaxScreenCount`, whose name implied a count.)
|
|
173
|
+
|
|
158
174
|
### Events
|
|
159
175
|
|
|
160
176
|
```tsx
|
|
@@ -165,10 +181,12 @@ addFBREEventListener("file-upload", handler);
|
|
|
165
181
|
removeFBREEventListener("file-upload", handler);
|
|
166
182
|
```
|
|
167
183
|
|
|
184
|
+
Note: the `file-upload` event bus is **module-global** — a listener fires for uploads from every `<FBRE />` instance on the page, not just one. Filter by the emitted component id if you run multiple instances.
|
|
185
|
+
|
|
168
186
|
### Exports
|
|
169
187
|
|
|
170
188
|
```ts
|
|
171
|
-
//
|
|
189
|
+
// Component
|
|
172
190
|
import { FBRE } from "@sonata-innovations/fiber-fbre";
|
|
173
191
|
|
|
174
192
|
// Store hooks
|
|
@@ -180,7 +198,7 @@ import { addFBREEventListener, removeFBREEventListener } from "@sonata-innovatio
|
|
|
180
198
|
// Error classes
|
|
181
199
|
import { ApiError, TimeoutError } from "@sonata-innovations/fiber-fbre";
|
|
182
200
|
|
|
183
|
-
// Types
|
|
201
|
+
// Types — props, store, and the full Flow/FlowData/Component type surface
|
|
184
202
|
import type {
|
|
185
203
|
FBREProps,
|
|
186
204
|
FBRELocalProps,
|
|
@@ -194,335 +212,64 @@ import type {
|
|
|
194
212
|
ThemeConfig,
|
|
195
213
|
NavigationConfig,
|
|
196
214
|
ControlsConfig,
|
|
197
|
-
|
|
215
|
+
ConfirmationConfig,
|
|
216
|
+
ConfirmationResult,
|
|
198
217
|
Component,
|
|
199
218
|
ComponentProperties,
|
|
200
|
-
ComponentDisplayProperties,
|
|
201
|
-
ComponentDividerProperties,
|
|
202
|
-
ComponentInputProperties,
|
|
203
|
-
ComponentOptionProperties,
|
|
204
|
-
ComponentSliderProperties,
|
|
205
|
-
ComponentSwitchProperties,
|
|
206
|
-
ComponentRatingProperties,
|
|
207
|
-
ComponentFileUploadProperties,
|
|
208
|
-
ComponentGroupProperties,
|
|
209
219
|
FlowData,
|
|
210
220
|
ScreenData,
|
|
211
221
|
ComponentData,
|
|
212
222
|
FileUploadData,
|
|
213
|
-
FileUploadBase64Data,
|
|
214
|
-
FileUploadS3Data,
|
|
215
223
|
FlowConditionConfig,
|
|
216
224
|
ConditionOperator,
|
|
217
|
-
|
|
218
|
-
ConditionGroup,
|
|
219
|
-
ConditionDependency,
|
|
225
|
+
// …plus per-component property types and condition/rule types
|
|
220
226
|
} from "@sonata-innovations/fiber-fbre";
|
|
221
227
|
```
|
|
222
228
|
|
|
223
|
-
## Flow
|
|
224
|
-
|
|
225
|
-
```
|
|
226
|
-
Flow
|
|
227
|
-
├── uuid: string
|
|
228
|
-
├── metadata: { name?, description?, ...}
|
|
229
|
-
├── config?: { mode?, theme?: { color?, colorScheme?, style?, background?, surface?, text?, border?, radius?, fontFamily?, error?, success?, warning? }, navigation?: { transition?, allowInvalidTransition? }, controls?: { show?, layout?, showStepper? }, summary? }
|
|
230
|
-
└── screens: FlowScreen[]
|
|
231
|
-
├── uuid: string
|
|
232
|
-
├── label?: string
|
|
233
|
-
├── conditions?: FlowConditionConfig
|
|
234
|
-
└── components: Component[]
|
|
235
|
-
├── uuid: string
|
|
236
|
-
├── type: string
|
|
237
|
-
├── properties: { ... }
|
|
238
|
-
├── conditions?: FlowConditionConfig
|
|
239
|
-
└── components?: Component[] (groups and repeaters)
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
### Component Types
|
|
243
|
-
|
|
244
|
-
| Type | Category | Description |
|
|
245
|
-
|------|----------|-------------|
|
|
246
|
-
| `header` | Display | Heading text |
|
|
247
|
-
| `text` | Display | Rich text with markup support |
|
|
248
|
-
| `divider` | Display | Horizontal rule with optional label |
|
|
249
|
-
| `callout` | Display | Styled alert/info box with variant coloring (info, success, warning, neutral) |
|
|
250
|
-
| `table` | Display | Static comparison/data table with optional column highlighting |
|
|
251
|
-
| `inputText` | Input | Single-line text field |
|
|
252
|
-
| `inputTextArea` | Input | Multi-line text field |
|
|
253
|
-
| `inputNumber` | Input | Numeric input (supports decimal restriction and start adornment) |
|
|
254
|
-
| `dropDown` | Selection | Native select dropdown |
|
|
255
|
-
| `dropDownMulti` | Selection | Multi-select with tag chips |
|
|
256
|
-
| `checkbox` | Selection | Checkbox group |
|
|
257
|
-
| `radio` | Selection | Radio button group |
|
|
258
|
-
| `toggleSwitch` | Selection | On/off toggle |
|
|
259
|
-
| `yesNo` | Selection | Two large tappable buttons for binary yes/no |
|
|
260
|
-
| `cardSelect` | Selection | Card-based visual selection with image/icon support |
|
|
261
|
-
| `date` | Date & Time | Calendar popup date picker |
|
|
262
|
-
| `time` | Date & Time | Hour/minute time selector |
|
|
263
|
-
| `dateTime` | Date & Time | Combined date + time picker |
|
|
264
|
-
| `dateRange` | Date & Time | Two date pickers for start/end |
|
|
265
|
-
| `timeRange` | Date & Time | Two time pickers for start/end |
|
|
266
|
-
| `dateTimeRange` | Date & Time | Two datetime pickers for start/end |
|
|
267
|
-
| `fileUpload` | Interactive | File picker with size validation |
|
|
268
|
-
| `rating` | Interactive | Star rating with half-star precision |
|
|
269
|
-
| `slider` | Interactive | Range slider with value label |
|
|
270
|
-
| `colorPicker` | Interactive | Saturation/hue picker with hex input |
|
|
271
|
-
| `computed` | Display | Calculated field with formula-based computed values |
|
|
272
|
-
| `signature` | Interactive | Signature pad — draw on canvas, type name, or both |
|
|
273
|
-
| `group` | Container | Component container with layout grid, collapsible |
|
|
274
|
-
| `repeater` | Container | Iterable component container (add/remove iterations, pre-populated rows) |
|
|
275
|
-
|
|
276
|
-
### Conditions
|
|
277
|
-
|
|
278
|
-
Components and screens can be conditionally shown/hidden using `FlowConditionConfig`:
|
|
279
|
-
|
|
280
|
-
```json
|
|
281
|
-
{
|
|
282
|
-
"action": "show",
|
|
283
|
-
"when": {
|
|
284
|
-
"logic": "and",
|
|
285
|
-
"rules": [
|
|
286
|
-
{
|
|
287
|
-
"source": "other-component-uuid",
|
|
288
|
-
"operator": "equals",
|
|
289
|
-
"value": "yes"
|
|
290
|
-
}
|
|
291
|
-
]
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
**Logic:** `"and"` (all rules must match) or `"or"` (any rule matches).
|
|
297
|
-
|
|
298
|
-
**Context rules:** Set `sourceType: "context"` on a rule to reference an external context value instead of a component. In this case, `source` is a context key (not a component UUID).
|
|
299
|
-
|
|
300
|
-
**Operators:**
|
|
301
|
-
|
|
302
|
-
| Category | Operators |
|
|
303
|
-
|----------|-----------|
|
|
304
|
-
| Equality | `equals`, `notEquals` |
|
|
305
|
-
| String | `contains`, `notContains`, `startsWith`, `endsWith` |
|
|
306
|
-
| Presence | `isEmpty`, `isNotEmpty` |
|
|
307
|
-
| Numeric | `greaterThan`, `greaterThanOrEqual`, `lessThan`, `lessThanOrEqual` |
|
|
308
|
-
| Set (single) | `isOneOf`, `isNotOneOf` |
|
|
309
|
-
| Set (multi) | `includesAny`, `includesAll`, `includesNone` |
|
|
310
|
-
| Boolean | `isTrue`, `isFalse` |
|
|
311
|
-
|
|
312
|
-
Condition results are stored separately from the Flow JSON in a `conditionResults` map (keyed by target UUID). Hidden components are excluded from FlowData output.
|
|
313
|
-
|
|
314
|
-
### Validation
|
|
315
|
-
|
|
316
|
-
Components support a rules-based validation system via `properties.validation`:
|
|
317
|
-
|
|
318
|
-
```json
|
|
319
|
-
{
|
|
320
|
-
"validation": {
|
|
321
|
-
"rules": [
|
|
322
|
-
{ "type": "required" },
|
|
323
|
-
{ "type": "email" },
|
|
324
|
-
{ "type": "minLength", "params": { "min": 5 } }
|
|
325
|
-
]
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
**17 built-in validators:** `required`, `email`, `phone`, `url`, `minLength`, `maxLength`, `exactLength`, `minValue`, `maxValue`, `pattern`, `minSelected`, `maxSelected`, `fileType`, `fileSize`, `contains`, `excludes`, `matchesField`
|
|
229
|
+
## Flow schema, conditions, validation, calculations
|
|
331
230
|
|
|
332
|
-
|
|
231
|
+
The Flow JSON hierarchy (Flow → Screen → Component), all **30 component types**, the **19 condition operators**, the **17 validators**, calculation formulas, and inline markup are documented in the schema references — `docs/schema/` in `@sonata-innovations/fiber-types`, mirrored at [schema/flow-schema.md](https://github.com/sonata-innovations/fiber-docs/blob/main/schema/flow-schema.md) (with a [quick reference](https://github.com/sonata-innovations/fiber-docs/blob/main/schema/flow-quick-reference.md)).
|
|
333
232
|
|
|
334
|
-
|
|
233
|
+
A few runtime facts worth knowing up front:
|
|
335
234
|
|
|
336
|
-
**
|
|
235
|
+
- **No auto-migration.** Legacy flat `required` / `regex` properties are **not** converted at load. Pre-migrate flows to `validation.rules` (and `conditions`) before passing them to FBRE.
|
|
236
|
+
- **Calculation formulas use single braces** to reference component values: `{uuid}`. They support arithmetic, comparisons, `IF()`, aggregation (`SUM`/`COUNT`/`AVG`/`MIN`/`MAX` over repeaters), and update reactively.
|
|
237
|
+
- **Condition and validation results live on the store** (`conditionResults`, `validationErrors`), not in the Flow JSON. Components hidden by a condition are excluded from FlowData and don't block screen validity.
|
|
337
238
|
|
|
338
|
-
|
|
239
|
+
## Confirmation Screen
|
|
339
240
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
### Calculations
|
|
343
|
-
|
|
344
|
-
Flows can include formula-based computed values via `FlowCalculation`:
|
|
345
|
-
|
|
346
|
-
```json
|
|
347
|
-
{
|
|
348
|
-
"calculations": [
|
|
349
|
-
{
|
|
350
|
-
"uuid": "calc-uuid",
|
|
351
|
-
"formula": "{{comp-a-uuid}} + {{comp-b-uuid}} * 0.1",
|
|
352
|
-
"label": "Total"
|
|
353
|
-
}
|
|
354
|
-
]
|
|
355
|
-
}
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
- Formulas reference component values via `{uuid}` tokens and support standard arithmetic (`+`, `-`, `*`, `/`)
|
|
359
|
-
- Aggregation functions: `SUM()`, `COUNT()`, `AVG()`, `MIN()`, `MAX()` over repeater iterations
|
|
360
|
-
- Scalar `MIN(expr, ...)` / `MAX(expr, ...)` for capping values (e.g. `MIN({discount}, 25)`)
|
|
361
|
-
- `IF(condition, then, else)` for conditional formulas (e.g. tiered pricing)
|
|
362
|
-
- Comparison operators: `>`, `<`, `>=`, `<=`, `==`, `!=` (return 1/0)
|
|
363
|
-
- Option metadata is accessible for option-based components
|
|
364
|
-
- Results update reactively when source values change
|
|
365
|
-
- Access results via `getCalculationResult(uuid)` on the store
|
|
366
|
-
|
|
367
|
-
### Markup
|
|
368
|
-
|
|
369
|
-
Text components and `detail` fields support inline markup:
|
|
370
|
-
|
|
371
|
-
- `[b]bold[/b]` → **bold**
|
|
372
|
-
- `[i]italic[/i]` → *italic*
|
|
373
|
-
- `[l href="url"]link[/l]` → [link](url)
|
|
241
|
+
A terminal "thank you" screen shown after submission, configured on the flow (`config.confirmation`) rather than as a `Screen`. It replaces the final screen and hides the controls/stepper; `title`/`body` support reference markup (collected fields, calculations, and `context` values by name). `onFlowComplete` decides *when* it shows — return `void` for immediate, a `Promise` to keep the submit button in-flight until it settles, or a `ConfirmationResult` (`{ title?, body? }`) to override the configured message (e.g. with a server reference number). Full guide: [features/confirmation-screen.md](https://github.com/sonata-innovations/fiber-docs/blob/main/features/confirmation-screen.md).
|
|
374
242
|
|
|
375
243
|
## Conversational Mode
|
|
376
244
|
|
|
377
|
-
Set `mode: "conversational"` to
|
|
245
|
+
Set `mode: "conversational"` (via the JSX prop or `flow.config.mode`) to turn FBRE into a one-question-per-screen experience: content is centered, single-select components auto-advance ~500ms after selection, and Enter advances text/number fields. Auto-advance only fires when a screen renders **exactly one** interactive component (display-only and condition-hidden components don't count); multi-input screens behave like a normal form. Components fade/scale/stagger in on transitions (respecting `prefers-reduced-motion`), and controls are enlarged for tapping.
|
|
378
246
|
|
|
379
|
-
|
|
380
|
-
// Via JSX prop
|
|
381
|
-
<FBRE flow={myFlow} mode="conversational" onFlowComplete={handleComplete} />
|
|
382
|
-
|
|
383
|
-
// Via flow config
|
|
384
|
-
const flow = {
|
|
385
|
-
...myFlow,
|
|
386
|
-
config: { ...myFlow.config, mode: "conversational" }
|
|
387
|
-
};
|
|
388
|
-
<FBRE flow={flow} onFlowComplete={handleComplete} />
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
**What changes:**
|
|
392
|
-
|
|
393
|
-
- Content is vertically and horizontally centered
|
|
394
|
-
- Single-select components (`radio`, `yesNo`, `cardSelect`, `dropDown`) auto-advance ~500ms after selection
|
|
395
|
-
- Pressing Enter on `inputText` / `inputNumber` advances to the next screen
|
|
396
|
-
- Auto-advance (both Enter and on-change) only fires when the screen renders **exactly one interactive component**. Multi-input screens — including those produced by FBTL's per-card page-break toggle — behave like a normal form: the user fills every field and clicks Continue. Display-only components (text, header, callout, divider, table, computed) and components hidden by conditions don't count toward the input total.
|
|
397
|
-
- Components animate in with fade + scale + stagger on screen transitions (respects `prefers-reduced-motion`)
|
|
398
|
-
- Option items, Yes/No buttons, card-select cards, and input fields are enlarged for easier tapping
|
|
399
|
-
- Each of the 6 existing styles has a tailored conversational presentation (e.g. clean = bordered cards, soft-outlined = pill-shaped options, defined-outlined = inverted selection)
|
|
400
|
-
|
|
401
|
-
**What doesn't change:**
|
|
402
|
-
|
|
403
|
-
- FlowData output is identical
|
|
404
|
-
- Conditions, validation, calculations all work the same
|
|
405
|
-
- Navigation buttons still work alongside auto-advance
|
|
406
|
-
- Works with all 6 transition types and dark mode
|
|
247
|
+
FlowData output, conditions, validation, calculations, transitions, and dark mode all behave identically to standard mode. Styles partition by mode: **6 standard** (`clean`, `outlined`, `refined-clean`, `airy-clean`, `soft-outlined`, `defined-outlined`) and **4 conversational** (`centered-minimal`, `stacked-cards`, `soft-float`, `bold-statement`).
|
|
407
248
|
|
|
408
249
|
## Theming
|
|
409
250
|
|
|
410
|
-
FBRE's appearance is driven entirely by CSS custom properties
|
|
411
|
-
|
|
412
|
-
Palette values resolve through four layers, each overriding the one above, so you only specify what you want to change:
|
|
251
|
+
FBRE's appearance is driven entirely by `--fbre-*` CSS custom properties. Two independent axes: **`theme.style`** controls shape (borders vs. underlines, fills, spacing), and the **palette** controls colors. There are three ways to set the palette, and they **layer** — each overrides the one before, so you specify only what you want to change:
|
|
413
252
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
3. Knobs: theme.{surface,text…} inline custom properties
|
|
418
|
-
4. Raw CSS: your own --fbre-* rules your stylesheet (highest)
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
### Color scheme (presets)
|
|
422
|
-
|
|
423
|
-
`theme.colorScheme` (`"light"` default | `"dark"`) seeds the **entire** token set with a coherent light or dark palette and sets `data-mode` on the container. Set it on the `theme` prop or in the flow config (the prop takes precedence):
|
|
424
|
-
|
|
425
|
-
```tsx
|
|
426
|
-
<FBRE flow={myFlow} theme={{ colorScheme: "dark" }} onFlowComplete={handleComplete} />
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
> Replaces the former `darkMode: boolean`: `darkMode: true` → `colorScheme: "dark"`, `darkMode: false`/absent → `colorScheme: "light"`. There is no runtime fallback — migrate stored flows before upgrading.
|
|
430
|
-
|
|
431
|
-
### Theme knobs
|
|
432
|
-
|
|
433
|
-
Layer brand colors on top of the preset with the `theme` palette knobs. Each knob sets its primary `--fbre-*` token **plus the tokens derived from it** (e.g. `surface` also drives the input fills and hover/alt surfaces, via mode-aware `color-mix`). Any subset may be set; unset knobs fall through to the preset.
|
|
253
|
+
1. **`colorScheme` preset** — `theme.colorScheme` (`"light"` default | `"dark"`) seeds the entire token set with a coherent palette and sets `data-mode` on the container. (Replaces the former `darkMode` boolean; there is no runtime fallback — migrate stored flows.)
|
|
254
|
+
2. **Palette knobs** — `theme.{color, background, surface, text, border, radius, fontFamily, error, success, warning}` set their primary token plus the tokens derived from it (e.g. `surface` also drives input fills and hover/alt surfaces). Any subset; unset knobs fall through to the preset.
|
|
255
|
+
3. **Raw `--fbre-*` CSS** — override any token directly (including ones with no knob, like `--fbre-star-filled`) by scoping rules to your wrapper.
|
|
434
256
|
|
|
435
257
|
```tsx
|
|
436
258
|
<FBRE
|
|
437
259
|
flow={myFlow}
|
|
438
|
-
theme={{
|
|
439
|
-
colorScheme: "dark",
|
|
440
|
-
color: "#bcd3cd", // accent
|
|
441
|
-
background: "#1a2a3a", // form ground
|
|
442
|
-
surface: "#243244", // input fills / cards
|
|
443
|
-
text: "#e8ede9",
|
|
444
|
-
border: "#2e4158",
|
|
445
|
-
radius: "3px",
|
|
446
|
-
fontFamily: '"Inter", system-ui, sans-serif',
|
|
447
|
-
}}
|
|
260
|
+
theme={{ colorScheme: "dark", color: "#bcd3cd", surface: "#243244", radius: "3px" }}
|
|
448
261
|
onFlowComplete={handleComplete}
|
|
449
262
|
/>
|
|
450
263
|
```
|
|
451
264
|
|
|
452
|
-
|
|
453
|
-
| --- | --- | --- |
|
|
454
|
-
| `color` | `--fbre-theme-color` | light/dark accent tints |
|
|
455
|
-
| `background` | `--fbre-bg` | — |
|
|
456
|
-
| `surface` | `--fbre-surface` | input fills, hover/alt/subtle surfaces, control surface |
|
|
457
|
-
| `text` | `--fbre-text` | secondary/placeholder/disabled, label |
|
|
458
|
-
| `border` | `--fbre-border` | hover/light/subtle |
|
|
459
|
-
| `radius` | `--fbre-radius` | — |
|
|
460
|
-
| `fontFamily` | `--fbre-font` | — |
|
|
461
|
-
| `error` / `success` / `warning` | `--fbre-{error,success,warning}` | their `-bg` / `-border` (and `error-light`) |
|
|
462
|
-
|
|
463
|
-
### Raw CSS (escape hatch)
|
|
464
|
-
|
|
465
|
-
For tokens not exposed as a knob (e.g. `--fbre-star-filled`, `--fbre-shadow-color`) — or for surgical control — override the `--fbre-*` variables directly. Scope to your wrapper so you out-specify the engine defaults regardless of load order:
|
|
466
|
-
|
|
467
|
-
```css
|
|
468
|
-
.my-form-wrapper .fbre-container {
|
|
469
|
-
--fbre-surface: #243244;
|
|
470
|
-
--fbre-star-filled: #bcd3cd;
|
|
471
|
-
}
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
Knobs are inline (highest specificity among engine-supplied values), so a knob and a raw rule on the *same* token: the knob wins unless your selector is also high-specificity. Use knobs for the common palette, raw CSS for the long tail.
|
|
475
|
-
|
|
476
|
-
### Token catalog
|
|
477
|
-
|
|
478
|
-
Every themeable value, with light/dark preset defaults. Tokens marked **(derived)** are produced from a knob when it's set; you can also set any of them raw.
|
|
479
|
-
|
|
480
|
-
**Accent** — `--fbre-theme-color` (`#1976d2` / `#42a5f5`, knob `color`), `--fbre-theme-light` (`#e3f2fd` / `#1a2332`, derived), `--fbre-theme-dark` (`#1565c0` / `#1e88e5`, derived), `--fbre-on-primary` (`#fff` / `#fff`).
|
|
481
|
-
|
|
482
|
-
**Ground & surfaces**
|
|
483
|
-
|
|
484
|
-
| Token | Light | Dark | Knob |
|
|
485
|
-
| --- | --- | --- | --- |
|
|
486
|
-
| `--fbre-bg` | `#fff` | `#121212` | `background` |
|
|
487
|
-
| `--fbre-surface` | `#fff` | `#1e1e1e` | `surface` |
|
|
488
|
-
| `--fbre-surface-hover` | `#f5f5f5` | `#2a2a2a` | (derived) |
|
|
489
|
-
| `--fbre-surface-alt` | `#e0e0e0` | `#333` | (derived) |
|
|
490
|
-
| `--fbre-surface-subtle` | `#e8e8e8` | `#2a2a2a` | (derived) |
|
|
491
|
-
| `--fbre-control-surface` | `#fff` | `#e0e0e0` | (derived) |
|
|
492
|
-
| `--fbre-input-bg` | `var(--fbre-surface)` | `var(--fbre-surface)` | (derived) |
|
|
493
|
-
| `--fbre-input-bg-focus` | `var(--fbre-bg)` | `var(--fbre-bg)` | (derived) |
|
|
494
|
-
|
|
495
|
-
**Text** — `--fbre-text` (`#212121` / `#e0e0e0`, knob `text`), `--fbre-text-secondary` (`#666` / `#9e9e9e`, derived), `--fbre-text-placeholder` (`#aaa` / `#666`, derived), `--fbre-text-disabled` (`#bbb` / `#555`, derived), `--fbre-label` (`var(--fbre-text-secondary)`, derived).
|
|
496
|
-
|
|
497
|
-
**Borders** — `--fbre-border` (`#ccc` / `#444`, knob `border`), `--fbre-border-hover` (`#999` / `#666`, derived), `--fbre-border-light` (`#ddd` / `#444`, derived), `--fbre-border-subtle` (`#eee` / `#333`, derived).
|
|
498
|
-
|
|
499
|
-
**State colors**
|
|
500
|
-
|
|
501
|
-
| Token | Light | Dark | Knob |
|
|
502
|
-
| --- | --- | --- | --- |
|
|
503
|
-
| `--fbre-error` | `#d32f2f` | `#ef5350` | `error` |
|
|
504
|
-
| `--fbre-error-light` | `#fdecea` | `#2c1b1b` | (derived) |
|
|
505
|
-
| `--fbre-success` | `#2d6a28` | `#a8e6a0` | `success` |
|
|
506
|
-
| `--fbre-success-bg` | `#ddffda` | `#1a3318` | (derived) |
|
|
507
|
-
| `--fbre-success-border` | `#b8e6b4` | `#2d5a28` | (derived) |
|
|
508
|
-
| `--fbre-warning` | `#7a5900` | `#ffe082` | `warning` |
|
|
509
|
-
| `--fbre-warning-bg` | `#fff8e1` | `#332b00` | (derived) |
|
|
510
|
-
| `--fbre-warning-border` | `#ffe082` | `#665500` | (derived) |
|
|
511
|
-
|
|
512
|
-
**Controls (no knob — set raw)** — `--fbre-toggle-track` (`#ccc` / `#555`), `--fbre-star-empty` (`#ddd` / `#444`), `--fbre-star-filled` (`#ffc107` / `#ffca28`), `--fbre-slider-track` (`#ddd` / `#444`), `--fbre-shadow-color` (`rgba(0,0,0,.12)` / `rgba(0,0,0,.4)`).
|
|
265
|
+
Defaults worth noting: `--fbre-radius` is `4px` and `--fbre-font` is `"Segoe UI", system-ui, -apple-system, sans-serif`. The complete token catalog with light/dark defaults is in [features/fbre-theming.md](https://github.com/sonata-innovations/fiber-docs/blob/main/features/fbre-theming.md).
|
|
513
266
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
### Screen Transitions
|
|
267
|
+
## Screen Transitions
|
|
517
268
|
|
|
518
269
|
Animate screen changes by setting `navigation.transition` in the flow config:
|
|
519
270
|
|
|
520
271
|
```json
|
|
521
|
-
{
|
|
522
|
-
"config": {
|
|
523
|
-
"navigation": { "transition": "slideFade" }
|
|
524
|
-
}
|
|
525
|
-
}
|
|
272
|
+
{ "config": { "navigation": { "transition": "slideFade" } } }
|
|
526
273
|
```
|
|
527
274
|
|
|
528
275
|
| Type | Effect | Direction-aware? |
|
|
@@ -534,76 +281,25 @@ Animate screen changes by setting `navigation.transition` in the flow config:
|
|
|
534
281
|
| `"rise"` | Vertical rise/sink | Yes |
|
|
535
282
|
| `"scaleFade"` | Scale + opacity | No |
|
|
536
283
|
|
|
537
|
-
Direction-aware transitions reverse when navigating backward
|
|
538
|
-
|
|
539
|
-
Customize timing via CSS custom properties:
|
|
540
|
-
|
|
541
|
-
```css
|
|
542
|
-
.fbre-container {
|
|
543
|
-
--fbre-transition-duration: 250ms;
|
|
544
|
-
--fbre-transition-easing: cubic-bezier(0.4, 0, 0.2, 1);
|
|
545
|
-
}
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
`@media (prefers-reduced-motion: reduce)` sets duration to `0ms` automatically.
|
|
549
|
-
|
|
550
|
-
## Architecture
|
|
551
|
-
|
|
552
|
-
| Concern | Implementation |
|
|
553
|
-
|---------|---------------|
|
|
554
|
-
| UI framework | Zero-dependency custom CSS |
|
|
555
|
-
| Styling | CSS custom properties + BEM (`fbre-` prefix) |
|
|
556
|
-
| Display detection | `isDisplayComponent()` checks type against constant set (`header`, `text`, `divider`, `callout`, `table`) |
|
|
557
|
-
| State | Zustand store factory, one store per `<FBRE />` instance via React Context |
|
|
558
|
-
| Re-renders | Zustand selectors — granular, automatic |
|
|
559
|
-
| Build | Vite library mode |
|
|
560
|
-
| Components | Registry pattern (`Map<string, FC>`), one file per type |
|
|
561
|
-
| Dependencies | react, zustand, @sonata-innovations/fiber-types, @sonata-innovations/fiber-shared |
|
|
284
|
+
Direction-aware transitions reverse when navigating backward; buttons are disabled mid-animation to prevent overlap. Customize timing via `--fbre-transition-duration` / `--fbre-transition-easing`. `@media (prefers-reduced-motion: reduce)` sets the duration to `0ms` automatically.
|
|
562
285
|
|
|
563
286
|
## FlowData Output
|
|
564
287
|
|
|
565
|
-
`getFlowData()` returns collected form data. Display-only components
|
|
566
|
-
|
|
567
|
-
```
|
|
568
|
-
FlowData
|
|
569
|
-
├── uuid: string
|
|
570
|
-
├── metadata: Record<string, string>
|
|
571
|
-
└── screens: ScreenData[]
|
|
572
|
-
├── uuid: string
|
|
573
|
-
├── label?: string
|
|
574
|
-
└── components: ComponentData[]
|
|
575
|
-
├── uuid: string
|
|
576
|
-
├── type: string
|
|
577
|
-
├── label?: string (omitted when empty)
|
|
578
|
-
├── value: string | string[] | number | number[] | boolean | null | FileUploadData
|
|
579
|
-
└── components?: ComponentData[][] (groups and repeaters)
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
## Build
|
|
583
|
-
|
|
584
|
-
```bash
|
|
585
|
-
npm run build
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
Output:
|
|
589
|
-
|
|
590
|
-
```
|
|
591
|
-
dist/
|
|
592
|
-
├── index.d.ts # Bundled type declarations
|
|
593
|
-
├── fiber-fbre.js # ES module
|
|
594
|
-
├── fiber-fbre.cjs # CommonJS
|
|
595
|
-
└── fiber-fbre.css # All styles
|
|
596
|
-
```
|
|
288
|
+
`getFlowData()` returns the collected form data, mirroring the Flow structure (Flow → ScreenData → ComponentData, with `components?: ComponentData[][]` for groups and repeaters). Display-only components — `header`, `text`, `divider`, `callout`, `table` — are excluded automatically; `computed` fields **are** included. Full shape and field semantics: [schema/flow-data-schema.md](https://github.com/sonata-innovations/fiber-docs/blob/main/schema/flow-data-schema.md).
|
|
597
289
|
|
|
598
290
|
## Responsive Sizing
|
|
599
291
|
|
|
600
|
-
FBRE uses CSS container queries to automatically scale controls in narrow containers (under 400px). Buttons, stepper dots, and toggle switches shrink proportionally
|
|
292
|
+
FBRE uses CSS container queries to automatically scale controls in narrow containers (under 400px). Buttons, stepper dots, and toggle switches shrink proportionally — no configuration needed; just embed in a smaller container and the compact sizing kicks in.
|
|
601
293
|
|
|
602
294
|
## Multiple Instances
|
|
603
295
|
|
|
604
|
-
Each `<FBRE />` creates its own isolated Zustand store
|
|
296
|
+
Each `<FBRE />` creates its own isolated Zustand store, so multiple instances run simultaneously on the same page without state conflicts. (The one exception is the module-global `file-upload` event bus noted under Events.)
|
|
605
297
|
|
|
606
298
|
```tsx
|
|
607
299
|
<FBRE flow={flowA} onFlowComplete={handleA} />
|
|
608
300
|
<FBRE flow={flowB} onFlowComplete={handleB} />
|
|
609
|
-
```
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## License
|
|
304
|
+
|
|
305
|
+
MIT
|