@ea-lab/reactive-json-docs 1.4.0 → 2.0.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.
@@ -0,0 +1,274 @@
1
+ renderView:
2
+ - type: Markdown
3
+ content: |
4
+ # DataSync
5
+
6
+ The `DataSync` component enables automatic synchronization between client-side data and a backend endpoint. It watches a **Syncable Object** in the data tree and pushes changes to a server, either automatically after a period of inactivity, immediately on change, or manually via an explicit trigger.
7
+
8
+ This component does not render any visible UI. It acts as a background observer.
9
+
10
+ ## The Syncable Object
11
+
12
+ `DataSync` watches a data object that follows a specific structure called a **Syncable Object**. This object contains both the data to synchronize and the configuration needed to reach the backend.
13
+
14
+ ### Structure
15
+
16
+ ```yaml
17
+ mySyncableObject:
18
+ submission_url: "https://api.example.com/items/save"
19
+ item_url: "https://api.example.com/items/123"
20
+ delete_url: "https://api.example.com/items/123"
21
+ data:
22
+ entity_id: 123
23
+ name: "John Doe"
24
+ email: "john@example.com"
25
+ status:
26
+ type: "info"
27
+ message: "Ready"
28
+ ```
29
+
30
+ ### Syncable Object Fields
31
+
32
+ - type: DefinitionList
33
+ content:
34
+ - term:
35
+ code: submission_url
36
+ after: " (string, required)"
37
+ details:
38
+ type: Markdown
39
+ content: "URL where the object is sent for create/update (POST). This is the only URL used by the `DataSync` component. If `entity_id` is absent from `data`, the backend should treat it as a creation."
40
+ - term:
41
+ code: item_url
42
+ after: " (string, optional)"
43
+ details:
44
+ type: Markdown
45
+ content: "URL to fetch the latest version of the item (GET). **Not used by `DataSync` itself** — provided as metadata for the backend or other components."
46
+ - term:
47
+ code: delete_url
48
+ after: " (string, optional)"
49
+ details:
50
+ type: Markdown
51
+ content: "URL to delete the item (DELETE). **Not used by `DataSync` itself** — provided as metadata for the backend or other components."
52
+ - term:
53
+ code: data
54
+ after: " (any, required)"
55
+ details:
56
+ type: Markdown
57
+ content: "The actual payload to synchronize. Its structure depends on the backend. **Only changes to this field trigger synchronization.**"
58
+ - term:
59
+ code: status
60
+ after: " (object, optional)"
61
+ details:
62
+ - type: Markdown
63
+ content: "Feedback message from the last operation. Contains:"
64
+ - type: DefinitionList
65
+ content:
66
+ - term:
67
+ code: status.type
68
+ details:
69
+ type: Markdown
70
+ content: "One of: `success`, `error`, `info`, `warning`."
71
+ - term:
72
+ code: status.message
73
+ details: "Human-readable message describing the result."
74
+
75
+ - type: Markdown
76
+ content: |
77
+ ### How `status` is managed
78
+
79
+ 1. **By the component**: Set to `{ type: "info", message: "Synchronisation en cours..." }` while a request is in flight.
80
+ 2. **By the backend**: The server response replaces the entire Syncable Object, including the `status` field. The backend can set it to any type with a relevant message.
81
+ 3. **On HTTP failure**: If the request fails (network error, 5xx), the component generates `{ type: "error", message: "..." }` locally.
82
+
83
+ ### Backend contract
84
+
85
+ When `DataSync` sends a request, it POSTs the **entire Syncable Object** (not just `data`) to `submission_url`. The backend must return the same Syncable Object structure in its response, potentially with:
86
+ - Modified `data` (e.g., server-generated fields like `entity_id` or timestamps).
87
+ - An updated `status` field reflecting the result of the operation.
88
+
89
+ The response completely replaces the local Syncable Object.
90
+
91
+ ## Component Properties
92
+
93
+ - type: TabbedSerializer
94
+ yamlSerializedContent: |
95
+ - type: DataSync
96
+ path: ~~.mySyncableObject
97
+ mode: onIdle
98
+ idleDelay: 2000
99
+ trigger: ~~.saveNow
100
+ maxRetries: 0
101
+ retryDelay: 5000
102
+
103
+ - type: DefinitionList
104
+ content:
105
+ - term:
106
+ code: path
107
+ after: " (string, required)"
108
+ details:
109
+ type: Markdown
110
+ content: "Path to the Syncable Object to watch. Supports template paths (`~~.`, `~.`, `~>`)."
111
+ - term:
112
+ code: mode
113
+ after: " (string, optional)"
114
+ details:
115
+ type: Markdown
116
+ content: "Synchronization mode. Default: `\"onIdle\"`. Options: `\"onIdle\"`, `\"immediate\"`, `\"manual\"`."
117
+ - term:
118
+ code: idleDelay
119
+ after: " (number, optional)"
120
+ details:
121
+ type: Markdown
122
+ content: "Milliseconds of inactivity before syncing. Only used in `onIdle` mode. Default: `1000`."
123
+ - term:
124
+ code: trigger
125
+ after: " (string, optional)"
126
+ details:
127
+ type: Markdown
128
+ content: "Path to a boolean value. When set to `true`, triggers an immediate sync and resets to `false`. Works in **any mode**. In `onIdle` mode, it cancels the pending idle timer."
129
+ - term:
130
+ code: maxRetries
131
+ after: " (number, optional)"
132
+ details:
133
+ type: Markdown
134
+ content: "Number of additional retry attempts after a failed sync. `0` means one attempt with no retry. Default: `0`."
135
+ - term:
136
+ code: retryDelay
137
+ after: " (number, optional)"
138
+ details:
139
+ type: Markdown
140
+ content: "Milliseconds between retry attempts. Default: `5000`."
141
+
142
+ - type: Markdown
143
+ content: |
144
+ ## Synchronization Modes
145
+
146
+ ### `onIdle` (default)
147
+ Waits for the user to stop modifying data. Each change resets the idle timer. The sync fires only after `idleDelay` milliseconds of inactivity.
148
+
149
+ ```yaml
150
+ - type: DataSync
151
+ path: ~~.userProfile
152
+ mode: onIdle
153
+ idleDelay: 2000
154
+ ```
155
+
156
+ ### `immediate`
157
+ Syncs immediately on every data change. Use with caution — this can generate a high volume of requests.
158
+
159
+ ```yaml
160
+ - type: DataSync
161
+ path: ~~.settings
162
+ mode: immediate
163
+ ```
164
+
165
+ ### `manual`
166
+ Does not sync on data changes. Waits for the `trigger` path to become `true`.
167
+
168
+ ```yaml
169
+ - type: DataSync
170
+ path: ~~.formData
171
+ mode: manual
172
+ trigger: ~~.saveNow
173
+ ```
174
+
175
+ ## Manual Trigger
176
+
177
+ The `trigger` property works in **any mode**, not just `manual`. When the trigger path is set to `true`:
178
+
179
+ 1. The trigger is immediately reset to `false`.
180
+ 2. Any pending idle timer is cancelled (in `onIdle` mode).
181
+ 3. A sync is performed immediately.
182
+
183
+ This is useful for adding a "Save Now" button alongside auto-save:
184
+
185
+ ```yaml
186
+ - type: button
187
+ content: "Save Now"
188
+ actions:
189
+ - what: setData
190
+ on: click
191
+ path: ~~.saveNow
192
+ value: true
193
+
194
+ - type: DataSync
195
+ path: ~~.userProfile
196
+ mode: onIdle
197
+ idleDelay: 2000
198
+ trigger: ~~.saveNow
199
+ ```
200
+
201
+ ## Error Handling and Retries
202
+
203
+ ### What triggers retries
204
+ - **Network outages** (browser is offline) — retried.
205
+ - **Server errors** (HTTP 5xx) — retried.
206
+ - **Client errors** (HTTP 4xx) — **not retried**.
207
+ - **CORS failures** — **not retried**.
208
+
209
+ When the user makes a new data change, the retry counter resets and a fresh sync cycle begins.
210
+
211
+ ### Configuration example
212
+ ```yaml
213
+ - type: DataSync
214
+ path: ~~.userProfile
215
+ mode: onIdle
216
+ idleDelay: 1000
217
+ maxRetries: 3
218
+ retryDelay: 10000
219
+ ```
220
+
221
+ ## Change Detection
222
+
223
+ `DataSync` only triggers synchronization when the `data` field of the Syncable Object changes. Changes to `status`, `submission_url`, or other fields are ignored. This prevents feedback loops when the component updates the status or when the server response updates the object.
224
+
225
+ ## Using with Templates
226
+
227
+ `DataSync` supports `TemplateContext`, so it can be used inside templates to sync individual items in a list:
228
+
229
+ ```yaml
230
+ data:
231
+ items:
232
+ - submission_url: "/api/items/1"
233
+ data: { name: "Item 1" }
234
+ - submission_url: "/api/items/2"
235
+ data: { name: "Item 2" }
236
+
237
+ templates:
238
+ itemRow:
239
+ type: div
240
+ content:
241
+ - type: TextField
242
+ dataLocation: ~.data.name
243
+ - type: DataSync
244
+ path: ~
245
+ mode: onIdle
246
+ idleDelay: 1500
247
+
248
+ renderView:
249
+ - type: Switch
250
+ content: ~~.items
251
+ singleOption:
252
+ load: itemRow
253
+ ```
254
+
255
+ ## Displaying Status
256
+
257
+ You can display the status message from the Syncable Object to provide user feedback:
258
+
259
+ ```yaml
260
+ - type: div
261
+ content: ~~.userProfile.status.message
262
+ actions:
263
+ - what: hide
264
+ when: ~~.userProfile.status
265
+ is: undefined
266
+ ```
267
+
268
+ ## Limitations
269
+ - Only one sync can be active at a time per `DataSync` instance. Concurrent changes while a sync is in flight are not queued.
270
+ - The entire Syncable Object is sent in the POST body, not just the `data` field.
271
+ - Only POST requests are supported for submission. Custom HTTP methods are not configurable.
272
+ - `item_url` and `delete_url` are part of the Syncable Object structure but are not used by the component itself. They are available for the backend or for other components to use.
273
+ - No built-in support for optimistic updates. The local data is replaced by the server response.
274
+ - CORS errors are not retried (they are indistinguishable from network errors when the browser is online).
@@ -1,211 +0,0 @@
1
- # DataFilter Example: Filtering Direct Array Items
2
-
3
- This example demonstrates how to use `DataFilter` with arrays where each item is a direct object (not wrapped in a namespace property). This is useful when working with data from APIs that return arrays of objects directly.
4
-
5
- ## Use Case
6
-
7
- When your data structure is a direct array of objects like:
8
- ```yaml
9
- data:
10
- rows:
11
- - id: 1
12
- label: "Operation 1"
13
- done: "done"
14
- operation: "create"
15
- - id: 2
16
- label: "Operation 2"
17
- done: "pending"
18
- operation: "update"
19
- ```
20
-
21
- Instead of:
22
- ```yaml
23
- data:
24
- rows:
25
- - item:
26
- id: 1
27
- label: "Operation 1"
28
- ```
29
-
30
- ## Solution
31
-
32
- Use an existing property that is present in all items (like `id`) as the `subjectsWithProperty` namespace. In `whenFilterableData`, reference properties directly without the namespace prefix.
33
-
34
- **Important**: When using string values for filtering (like status), use a special value like `"all"` to represent "no filter" instead of an empty string, as empty strings can cause issues with select elements.
35
-
36
- ## Complete Example
37
-
38
- ```yaml
39
- renderView:
40
- - type: DataFilter
41
- context: global
42
- filters:
43
- - subjectsWithProperty: id
44
- andConditions:
45
- # Filter by status (string)
46
- - orConditions:
47
- - when: ~~._filters.done
48
- is: "all"
49
- - whenFilterableData: done
50
- is: ~~._filters.done
51
-
52
- # Filter by operation type (text search with contains)
53
- - orConditions:
54
- - when: ~~._filters.operation
55
- is: ""
56
- - andConditions:
57
- - whenFilterableData: operation
58
- isNotEmpty: true
59
- - when: ~~._filters.operation
60
- isNotEmpty: true
61
- - whenFilterableData: operation
62
- contains: ~~._filters.operation
63
-
64
- # Filter by label (text search with contains)
65
- - orConditions:
66
- - when: ~~._filters.label
67
- is: ""
68
- - andConditions:
69
- - whenFilterableData: label
70
- isNotEmpty: true
71
- - when: ~~._filters.label
72
- isNotEmpty: true
73
- - whenFilterableData: label
74
- contains: ~~._filters.label
75
- content:
76
- - type: Switch
77
- content: ~~.rows
78
- singleOption:
79
- load: operationRow
80
-
81
- templates:
82
- operationRow:
83
- - type: tr
84
- content:
85
- - type: td
86
- content: ~.id
87
- - type: td
88
- content: ~.label
89
- - type: td
90
- content: ~.done
91
- - type: td
92
- content: ~.operation
93
-
94
- data:
95
- rows:
96
- - id: 1
97
- label: "Operation 1"
98
- done: "done"
99
- operation: "create"
100
- - id: 2
101
- label: "Operation 2"
102
- done: "pending"
103
- operation: "update"
104
- - id: 3
105
- label: "Operation 3"
106
- done: "done"
107
- operation: "create"
108
- _filters:
109
- done: "all"
110
- label: ""
111
- operation: ""
112
- ```
113
-
114
- ## Key Points
115
-
116
- 1. **Namespace Selection**: Choose a property that exists in all items (e.g., `id`, `name`, `key`). This property acts as the identifier for DataFilter to recognize filterable items.
117
-
118
- 2. **Direct Property Access**: In `whenFilterableData`, reference properties directly:
119
- - ✅ `whenFilterableData: label` (correct)
120
- - ❌ `whenFilterableData: id.label` (incorrect - don't use namespace prefix)
121
-
122
- 3. **String-based Filtering**: When using select elements for filtering, use a special value like `"all"` to represent "no filter" instead of an empty string:
123
- ```yaml
124
- - orConditions:
125
- - when: ~~._filters.done
126
- is: "all" # Shows all items
127
- - whenFilterableData: done
128
- is: ~~._filters.done # Filters by exact match
129
- ```
130
- This avoids issues where select elements might not properly handle empty string values.
131
-
132
- 4. **Text Search**: For text search with `contains`, ensure both the filter value and the data property are not empty:
133
- ```yaml
134
- - orConditions:
135
- - when: ~~._filters.label
136
- is: "" # Shows all when empty
137
- - andConditions:
138
- - whenFilterableData: label
139
- isNotEmpty: true
140
- - when: ~~._filters.label
141
- isNotEmpty: true
142
- - whenFilterableData: label
143
- contains: ~~._filters.label
144
- ```
145
- The `andConditions` wrapper ensures that both the data property and filter value are not empty before attempting the `contains` comparison.
146
-
147
- 5. **Template Access**: In templates, access properties directly without namespace:
148
- - ✅ `~.label`, `~.done`, `~.operation`
149
- - ❌ `~.id.label` (incorrect)
150
-
151
- ## Filter Types Demonstrated
152
-
153
- - **Select Filter with "All" option**: Using `is: "all"` to show all items when a special "all" value is selected
154
- - **Exact String Match**: Using `is` for exact string matching (e.g., status filtering)
155
- - **Text Search**: Using `contains` for substring matching (case-insensitive) with proper empty checks
156
-
157
- ## Additional Template Techniques
158
-
159
- **Conditional Display in Templates**: The example uses `hide` actions to conditionally display badges based on data values. This is a common pattern for showing different UI elements based on data state:
160
-
161
- ```yaml
162
- - type: span
163
- content: "Done"
164
- actions:
165
- - what: hide
166
- when: ~.done
167
- isNot: "done" # Hide if status is not "done"
168
- - type: span
169
- content: "Pending"
170
- actions:
171
- - what: hide
172
- when: ~.done
173
- isNot: "pending" # Hide if status is not "pending"
174
- ```
175
-
176
- This technique is used in the example to display colored status badges, but it's not part of the DataFilter pattern itself - it's just a way to enhance the visual display of filtered data.
177
-
178
- ## Filter Patterns
179
-
180
- ### Pattern 1: Select Filter with "All" Option
181
- ```yaml
182
- - orConditions:
183
- - when: ~~._filters.status
184
- is: "all" # Special value for "show all"
185
- - whenFilterableData: status
186
- is: ~~._filters.status # Exact match filter
187
- ```
188
-
189
- ### Pattern 2: Text Input Filter (Empty String Check)
190
- ```yaml
191
- - orConditions:
192
- - when: ~~._filters.search
193
- is: "" # Empty string means "show all"
194
- - andConditions:
195
- - whenFilterableData: searchField
196
- isNotEmpty: true
197
- - when: ~~._filters.search
198
- isNotEmpty: true
199
- - whenFilterableData: searchField
200
- contains: ~~._filters.search
201
- ```
202
-
203
- ## Notes
204
-
205
- - The `subjectsWithProperty` value (`id` in this example) must exist in every item of the array
206
- - DataFilter filters the data before rendering, so no `hide` actions are needed in templates
207
- - All filter conditions use `orConditions` with an empty/"all" check first, allowing "show all" when filters are empty or set to "all"
208
- - For select elements, prefer using a special value like `"all"` instead of empty strings to avoid UI issues
209
- - For text inputs, empty strings (`""`) work fine for the "show all" condition
210
- - When using `contains` for text search, always wrap the condition in `andConditions` with `isNotEmpty` checks to avoid errors with empty values
211
-