@ea-lab/reactive-json-docs 1.4.1 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ea-lab/reactive-json-docs",
3
- "version": "1.4.1",
3
+ "version": "2.0.0",
4
4
  "description": "Complete documentation for Reactive-JSON - Components, examples and LLM-parsable guides",
5
5
  "main": "public/rjbuild/docs/index.yaml",
6
6
  "files": [
@@ -26,7 +26,7 @@
26
26
  "private": false,
27
27
  "devDependencies": {
28
28
  "@craco/craco": "^7.1.0",
29
- "@ea-lab/reactive-json": "^1.3.0",
29
+ "@ea-lab/reactive-json": "^1.2.0",
30
30
  "@ea-lab/reactive-json-chartjs": "^1.0.0",
31
31
  "@npmcli/fs": "^4.0.0",
32
32
  "@reduxjs/toolkit": "^2.6.1",
@@ -8,73 +8,42 @@ Dynamically sets or modifies the value of an HTML attribute before rendering. Th
8
8
 
9
9
  ```yaml
10
10
  attributeTransforms:
11
- # Add CSS class (string mode)
11
+ # Add CSS class
12
12
  - what: setAttributeValue
13
13
  name: "class"
14
14
  value: "active"
15
15
 
16
- # Replace attribute value (string mode)
16
+ # Replace attribute value
17
17
  - what: setAttributeValue
18
18
  name: "data-status"
19
19
  mode: "replace"
20
20
  value: ~.currentStatus
21
-
22
- # Merge style object (object mode)
23
- - what: setAttributeValue
24
- name: "style"
25
- value:
26
- borderColor: "#3b82f6"
27
- backgroundColor: "var(--bs-secondary-bg-subtle, rgba(0, 0, 0, 0.05))"
28
21
  ```
29
22
 
30
23
  ## Properties
31
24
 
32
25
  - **name** *(string, required)*: The name of the attribute to modify.
33
26
  - **mode** *(string, optional)*: The modification mode. Default: `"append"`.
34
- - `"append"`: Adds the value to the existing attribute value (space-separated for strings, merges properties for objects).
27
+ - `"append"`: Adds the value to the existing attribute value (space-separated).
35
28
  - `"replace"`: Completely replaces the existing attribute value.
36
- - **value** *(string | object, required)*: The value to set or append. Supports template evaluation (e.g., `~.dynamicValue`, `~~.globalValue`).
37
- - **String mode**: When `value` is a string, operates in string mode for simple attributes like `class`, `data-*`, etc.
38
- - **Object mode**: When `value` is an object, operates in object mode for complex attributes like `style`. In append mode, merges properties property by property.
39
- - **preventDuplicateValues** *(boolean, optional)*: When `true` (default), prevents duplicate values when using append mode (applies to string mode and string properties in object mode).
40
- - **separator** *(string, optional)*: The separator used between values in string mode. Default: `" "` (space).
29
+ - **value** *(string, required)*: The value to set or append. Supports template evaluation (e.g., `~.dynamicValue`, `~~.globalValue`). Automatically converted to string if not already. Special characters are handled safely.
30
+ - **preventDuplicateValues** *(boolean, optional)*: When `true` (default), prevents duplicate values when using append mode.
31
+ - **separator** *(string, optional)*: The separator used between values. Default: `" "` (space).
41
32
 
42
33
  ## Behavior
43
34
 
44
- The transformer operates in two distinct modes based on the `value` type:
45
-
46
- ### String Mode (when `value` is a string)
47
-
48
35
  - **Append mode**: Adds the new value to the existing attribute, separated by the specified separator.
49
36
  - **Replace mode**: Completely overwrites the existing attribute value.
50
37
  - **Duplicate prevention**: In append mode, prevents adding duplicate values when enabled.
51
38
 
52
- ### Object Mode (when `value` is an object)
53
-
54
- - **Replace mode**: Completely replaces the entire object attribute.
55
- - **Append mode**: Merges properties property by property:
56
- - Starts from the current object attribute
57
- - For each property in `value`, applies the same logic as string mode:
58
- - If both current and new values are strings: appends them (respecting `preventDuplicateValues`)
59
- - Otherwise: replaces the property with the new value (new value has precedence)
60
- - Handles nested objects recursively
61
- - Works on a copy to avoid mutation issues
62
-
63
- ### Undefined Values
64
-
65
- - **Append mode**: `undefined` values are ignored, keeping the current attribute unchanged.
66
- - **Replace mode**: `undefined` is assigned to the attribute.
67
-
68
39
  ## Common Use Cases
69
40
 
70
- - **Dynamic CSS classes**: Adding/removing CSS classes based on state (string mode).
71
- - **Data attributes**: Setting data-* attributes for JavaScript integration (string mode).
72
- - **ARIA attributes**: Dynamically updating accessibility attributes (string mode).
73
- - **Style attributes**: Modifying inline styles conditionally (object mode). Merge style properties without losing existing ones.
74
-
75
- ## Examples
41
+ - **Dynamic CSS classes**: Adding/removing CSS classes based on state.
42
+ - **Data attributes**: Setting data-* attributes for JavaScript integration.
43
+ - **ARIA attributes**: Dynamically updating accessibility attributes.
44
+ - **Style attributes**: Modifying inline styles conditionally.
76
45
 
77
- ### String Mode Example
46
+ ## Example
78
47
 
79
48
  ```yaml
80
49
  renderView:
@@ -92,8 +61,6 @@ renderView:
92
61
  margin: "10px 0"
93
62
  width: "300px"
94
63
  display: "block"
95
- backgroundColor: "var(--bs-secondary-bg-subtle, rgba(0, 0, 0, 0.05))"
96
- color: "#212529"
97
64
  attributeTransforms:
98
65
  - what: setAttributeValue
99
66
  name: "class"
@@ -124,81 +91,11 @@ data:
124
91
  input_data: ""
125
92
  ```
126
93
 
127
- ### Object Mode Example (Style Merging)
128
-
129
- ```yaml
130
- renderView:
131
- - type: div
132
- attributes:
133
- style:
134
- padding: "20px"
135
- borderWidth: "2px"
136
- borderStyle: "solid"
137
- borderColor: "#007bff #007bff"
138
- borderRadius: "4px"
139
- backgroundColor: "var(--bs-secondary-bg-subtle, rgba(0, 0, 0, 0.05))"
140
- color: "#212529"
141
- attributeTransforms:
142
- # Merge additional style properties without losing existing ones
143
- - what: setAttributeValue
144
- name: "style"
145
- value:
146
- borderColor: "#007bff var(--bs-primary, #3b82f6)"
147
- when: ~.isHighlighted
148
- is: true
149
- # Result when isHighlighted is true:
150
- # The modification changes the highlight color for vertical borders (left and right)
151
- # borderColor becomes: "#007bff var(--bs-primary, #3b82f6)"
152
- # (horizontal borders stay #007bff, vertical borders change to primary color)
153
-
154
- data:
155
- isHighlighted: false
156
- ```
157
-
158
- ### Object Mode Replace Example
159
-
160
- ```yaml
161
- renderView:
162
- - type: div
163
- attributes:
164
- style:
165
- padding: "10px"
166
- border: "2px solid #007bff"
167
- backgroundColor: "var(--bs-secondary-bg-subtle, rgba(0, 0, 0, 0.05))"
168
- color: "#212529"
169
- attributeTransforms:
170
- # Completely replace the style object
171
- - what: setAttributeValue
172
- name: "style"
173
- mode: "replace"
174
- value:
175
- backgroundColor: "var(--bs-primary-bg-subtle, #d4f0ec)"
176
- color: "var(--bs-primary-text-emphasis, #2b6b5a)"
177
- padding: "15px"
178
- borderRadius: "8px"
179
- border: "2px solid var(--bs-primary, #44a08d)"
180
- # Result:
181
- # style: {
182
- # backgroundColor: "var(--bs-primary-bg-subtle, #d4f0ec)",
183
- # color: "var(--bs-primary-text-emphasis, #2b6b5a)",
184
- # padding: "15px",
185
- # borderRadius: "8px",
186
- # border: "2px solid var(--bs-primary, #44a08d)"
187
- # }
188
- # (original styles are completely replaced)
189
- ```
190
-
191
94
  ## Notes
192
95
 
193
96
  - **Pre-render execution**: This transformer modifies attributes before the component renders, ensuring child components receive the transformed attributes.
194
- - **Mode detection**: The transformer automatically detects whether to use string mode or object mode based on the `value` type after template evaluation.
195
- - **Append mode behavior**:
196
- - **String mode**: Respects existing attribute values, adding new values separated by the specified separator.
197
- - **Object mode**: Merges properties property by property, applying string append logic where both values are strings, otherwise replacing with the new value.
198
- - **Replace mode**: Use when you need complete control over the attribute value. In object mode, completely replaces the entire object.
199
- - **Duplicate prevention**: Only applies to append mode, and works for string values in both string mode and object mode.
200
- - **Object merging**: In object mode append, nested objects are merged recursively. Properties that don't match the expected format for append are replaced (new value has precedence).
201
- - **Extended properties vs shorthand**: When transformations are required, prefer using extended CSS properties (e.g., `borderWidth`, `borderStyle`, `borderColor`) instead of shorthand properties (e.g., `border`). This ensures the browser handles property modifications correctly. When you modify a single property like `borderColor` on an element that uses the shorthand `border`, the browser may decompose the shorthand into individual properties, which can lead to unexpected behavior. Using extended properties from the start avoids this issue.
97
+ - **Append mode behavior**: Respects existing attribute values when using append mode.
98
+ - **Replace mode**: Use when you need complete control over the attribute value.
99
+ - **Duplicate prevention**: Only applies to append mode.
202
100
  - **Template evaluation**: The value property supports full template evaluation including `~.localData`, `~~.globalData`, `~>nearestKey`, and `~~>globalKey` patterns.
203
101
  - **Conditional execution**: Supports the same condition system as actions (`when`, `is`, `isEmpty`, `isNotEmpty`, etc.).
204
- - **Undefined handling**: `undefined` values are ignored in append mode but assigned in replace mode.
@@ -12,23 +12,16 @@ renderView:
12
12
  - type: TabbedSerializer
13
13
  yamlSerializedContent: |
14
14
  attributeTransforms:
15
- # Add CSS class (string mode)
15
+ # Add CSS class
16
16
  - what: setAttributeValue
17
17
  name: "class"
18
18
  value: "active"
19
19
 
20
- # Replace attribute value (string mode)
20
+ # Replace attribute value
21
21
  - what: setAttributeValue
22
22
  name: "data-status"
23
23
  mode: "replace"
24
24
  value: ~.currentStatus
25
-
26
- # Merge style object (object mode)
27
- - what: setAttributeValue
28
- name: "style"
29
- value:
30
- borderColor: "#3b82f6"
31
- backgroundColor: "var(--bs-secondary-bg-subtle, rgba(0, 0, 0, 0.05))"
32
25
 
33
26
  - type: Markdown
34
27
  content: |
@@ -51,14 +44,8 @@ renderView:
51
44
  - `"replace"`: Completely replaces the existing attribute value
52
45
  - term:
53
46
  code: value
54
- after: "(string | object, required)"
55
- details:
56
- type: Markdown
57
- content: |
58
- The value to set or append. Supports template evaluation (e.g., `~.dynamicValue`, `~~.globalValue`).
59
-
60
- - **String mode**: When `value` is a string, operates in string mode for simple attributes like `class`, `data-*`, etc.
61
- - **Object mode**: When `value` is an object, operates in object mode for complex attributes like `style`. In append mode, merges properties property by property.
47
+ after: "(string, required)"
48
+ details: The value to set or append. Supports template evaluation (e.g., `~.dynamicValue`, `~~.globalValue`). Automatically converted to string if not already. Special characters are handled safely.
62
49
  - term:
63
50
  code: preventDuplicateValues
64
51
  after: "(boolean, optional)"
@@ -78,36 +65,16 @@ renderView:
78
65
 
79
66
  ## Behavior
80
67
 
81
- The transformer operates in two distinct modes based on the `value` type:
82
-
83
- ### String Mode (when `value` is a string)
84
-
85
68
  - **Append mode**: Adds the new value to the existing attribute, separated by the specified separator.
86
69
  - **Replace mode**: Completely overwrites the existing attribute value.
87
70
  - **Duplicate prevention**: In append mode, prevents adding duplicate values when enabled.
88
71
 
89
- ### Object Mode (when `value` is an object)
90
-
91
- - **Replace mode**: Completely replaces the entire object attribute.
92
- - **Append mode**: Merges properties property by property:
93
- - Starts from the current object attribute
94
- - For each property in `value`, applies the same logic as string mode:
95
- - If both current and new values are strings: appends them (respecting `preventDuplicateValues`)
96
- - Otherwise: replaces the property with the new value (new value has precedence)
97
- - Handles nested objects recursively
98
- - Works on a copy to avoid mutation issues
99
-
100
- ### Undefined Values
101
-
102
- - **Append mode**: `undefined` values are ignored, keeping the current attribute unchanged.
103
- - **Replace mode**: `undefined` is assigned to the attribute.
104
-
105
72
  ## Common Use Cases
106
73
 
107
- - **Dynamic CSS classes**: Adding/removing CSS classes based on state (string mode).
108
- - **Data attributes**: Setting data-* attributes for JavaScript integration (string mode).
109
- - **ARIA attributes**: Dynamically updating accessibility attributes (string mode).
110
- - **Style attributes**: Modifying inline styles conditionally (object mode). Merge style properties without losing existing ones.
74
+ - **Dynamic CSS classes**: Adding/removing CSS classes based on state.
75
+ - **Data attributes**: Setting data-* attributes for JavaScript integration.
76
+ - **ARIA attributes**: Dynamically updating accessibility attributes.
77
+ - **Style attributes**: Modifying inline styles conditionally.
111
78
 
112
79
  - type: RjBuildDescriber
113
80
  title: "SetAttributeValue Action Examples"
@@ -141,8 +108,6 @@ renderView:
141
108
  margin: "10px 0"
142
109
  width: "300px"
143
110
  display: "block"
144
- backgroundColor: "var(--bs-secondary-bg-subtle, rgba(0, 0, 0, 0.05))"
145
- color: "#212529"
146
111
  actions:
147
112
  - what: setData
148
113
  on: change
@@ -169,108 +134,11 @@ renderView:
169
134
  data:
170
135
  input_data: ""
171
136
 
172
- - type: RjBuildDescriber
173
- title: "Object Mode Example - Style Merging"
174
- description:
175
- - type: Markdown
176
- content: |
177
- This example demonstrates how to use `setAttributeValue` in object mode to merge style properties without losing existing ones.
178
-
179
- **Expected behavior:**
180
- - The div has initial styles (padding, border, borderRadius)
181
- - When `isHighlighted` is true, additional style properties are merged
182
- - The modification changes the highlight color for vertical borders (left and right), as `borderColor` contains two colors
183
- - The original styles are preserved, and new properties are added
184
- - Try toggling the highlight to see the style merge in action
185
-
186
- toDescribe:
187
- renderView:
188
- - type: div
189
- attributes:
190
- style:
191
- padding: "10px"
192
- borderWidth: "5px"
193
- borderStyle: "solid"
194
- borderColor: "black"
195
- borderRadius: "4px"
196
- backgroundColor: "var(--bs-secondary-bg-subtle, rgba(0, 0, 0, 0.05))"
197
- color: "#212529"
198
- attributeTransforms:
199
- # Merge additional style properties without losing existing ones
200
- - what: setAttributeValue
201
- name: "style"
202
- value:
203
- borderColor: "var(--bs-primary, #3b82f6)"
204
- when: ~.isHighlighted
205
- is: true
206
- content: "Click to toggle highlight"
207
-
208
- - type: button
209
- content: "Toggle Highlight"
210
- actions:
211
- - what: setData
212
- on: click
213
- path: ~.isHighlighted
214
- value: true
215
- when: ~.isHighlighted
216
- is: false
217
- - what: setData
218
- on: click
219
- path: ~.isHighlighted
220
- value: false
221
- when: ~.isHighlighted
222
- is: true
223
-
224
- data:
225
- isHighlighted: false
226
-
227
- - type: RjBuildDescriber
228
- title: "Object Mode Replace Example"
229
- description:
230
- - type: Markdown
231
- content: |
232
- This example demonstrates how to completely replace a style object using `mode: "replace"`.
233
-
234
- **Expected behavior:**
235
- - The div has initial styles (padding, border)
236
- - When replace mode is used, the entire style object is replaced
237
- - Original styles are lost, only the new styles remain
238
-
239
- toDescribe:
240
- renderView:
241
- - type: div
242
- attributes:
243
- style:
244
- padding: "10px"
245
- border: "2px solid #007bff"
246
- backgroundColor: "var(--bs-secondary-bg-subtle, rgba(0, 0, 0, 0.05))"
247
- color: "#212529"
248
- attributeTransforms:
249
- # Completely replace the style object
250
- - what: setAttributeValue
251
- name: "style"
252
- mode: "replace"
253
- value:
254
- backgroundColor: "var(--bs-primary-bg-subtle, #d4f0ec)"
255
- color: "var(--bs-primary-text-emphasis, #2b6b5a)"
256
- padding: "15px"
257
- borderRadius: "8px"
258
- border: "2px solid var(--bs-primary, #44a08d)"
259
- content: "This div's style was completely replaced"
260
-
261
137
  - type: Markdown
262
138
  content: |
263
139
  ## Notes
264
140
 
265
- - **Pre-render execution**: This transformer modifies attributes before the component renders, ensuring child components receive the transformed attributes.
266
- - **Mode detection**: The transformer automatically detects whether to use string mode or object mode based on the `value` type after template evaluation.
267
- - **Append mode behavior**:
268
- - **String mode**: Respects existing attribute values, adding new values separated by the specified separator.
269
- - **Object mode**: Merges properties property by property, applying string append logic where both values are strings, otherwise replacing with the new value.
270
- - **Replace mode**: Use when you need complete control over the attribute value. In object mode, completely replaces the entire object.
271
- - **Duplicate prevention**: Only applies to append mode, and works for string values in both string mode and object mode.
272
- - **Object merging**: In object mode append, nested objects are merged recursively. Properties that don't match the expected format for append are replaced (new value has precedence).
273
- - **Extended properties vs shorthand**: When transformations are required, prefer using extended CSS properties (e.g., `borderWidth`, `borderStyle`, `borderColor`) instead of shorthand properties (e.g., `border`). This ensures the browser handles property modifications correctly. When you modify a single property like `borderColor` on an element that uses the shorthand `border`, the browser may decompose the shorthand into individual properties, which can lead to unexpected behavior. Using extended properties from the start avoids this issue.
274
- - **Template evaluation**: The value property supports full template evaluation including `~.localData`, `~~.globalData`, `~>nearestKey`, and `~~>globalKey` patterns.
275
- - **Conditional execution**: Supports the same condition system as actions (`when`, `is`, `isEmpty`, `isNotEmpty`, etc.).
276
- - **Undefined handling**: `undefined` values are ignored in append mode but assigned in replace mode.
141
+ - The action respects existing attribute values when using append mode.
142
+ - Use replace mode when you need complete control over the attribute value.
143
+ - Duplicate prevention only applies to append mode.
144
+ - The value property supports full template evaluation including `~.localData`, `~~.globalData`, `~>nearestKey`, and `~~>globalKey` patterns.
@@ -0,0 +1,252 @@
1
+ # DataSync
2
+
3
+ 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 when triggered, either automatically after a period of inactivity, immediately on change, or manually via an explicit trigger.
4
+
5
+ This component does not render any visible UI. It acts as a background observer.
6
+
7
+ ## The Syncable Object
8
+
9
+ `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.
10
+
11
+ ### Structure
12
+
13
+ ```yaml
14
+ mySyncableObject:
15
+ submission_url: "https://api.example.com/items/save"
16
+ item_url: "https://api.example.com/items/123"
17
+ delete_url: "https://api.example.com/items/123"
18
+ data:
19
+ entity_id: 123
20
+ name: "John Doe"
21
+ email: "john@example.com"
22
+ status:
23
+ type: "info"
24
+ message: "Ready"
25
+ ```
26
+
27
+ ### Fields
28
+
29
+ | Field | Type | Required | Description |
30
+ |---|---|---|---|
31
+ | `submission_url` | string | Yes | 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. |
32
+ | `item_url` | string | No | URL to fetch the latest version of the item (GET). **Not used by `DataSync` itself** — provided as metadata for the backend or other components. |
33
+ | `delete_url` | string | No | URL to delete the item (DELETE). **Not used by `DataSync` itself** — provided as metadata for the backend or other components. |
34
+ | `data` | any | Yes | The actual payload to synchronize. Its structure depends on the backend. Only changes to this field trigger synchronization. |
35
+ | `status` | object | No | Feedback message from the last operation. Contains `type` and `message`. |
36
+
37
+ ### The `status` field
38
+
39
+ The `status` field follows a standard structure with two properties:
40
+
41
+ | Property | Type | Description |
42
+ |---|---|---|
43
+ | `type` | string | One of: `success`, `error`, `info`, `warning`. |
44
+ | `message` | string | Human-readable message describing the result. |
45
+
46
+ The `status` field is managed in three ways:
47
+ 1. **By the component**: Set to `{ type: "info", message: "Synchronisation en cours..." }` while a request is in flight.
48
+ 2. **By the backend**: The server response replaces the entire Syncable Object, including the `status` field. The backend can set it to `success`, `error`, or any type with a relevant message.
49
+ 3. **On HTTP failure**: If the request fails (network error, 5xx), the component generates `{ type: "error", message: "..." }` locally.
50
+
51
+ ### Backend contract
52
+
53
+ 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:
54
+ - Modified `data` (e.g., server-generated fields like `entity_id` or timestamps).
55
+ - An updated `status` field reflecting the result of the operation.
56
+
57
+ The response completely replaces the local Syncable Object.
58
+
59
+ ## Properties
60
+
61
+ | Property | Type | Default | Description |
62
+ |---|---|---|---|
63
+ | `path` | string | (required) | Path to the Syncable Object to watch. Supports template paths (`~~.`, `~.`, `~>`). |
64
+ | `mode` | string | `"onIdle"` | Synchronization mode: `"onIdle"`, `"immediate"`, or `"manual"`. |
65
+ | `idleDelay` | number | `1000` | Milliseconds of inactivity before syncing (only used in `onIdle` mode). |
66
+ | `trigger` | string | - | 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. |
67
+ | `maxRetries` | number | `0` | Number of additional retry attempts after a failed sync. `0` means one attempt with no retry. |
68
+ | `retryDelay` | number | `5000` | Milliseconds between retry attempts. |
69
+
70
+ ## Synchronization Modes
71
+
72
+ ### `onIdle` (default)
73
+ Waits for the user to stop modifying data. Each change resets the idle timer. The sync fires only after `idleDelay` milliseconds of inactivity.
74
+
75
+ ```yaml
76
+ - type: DataSync
77
+ path: ~~.userProfile
78
+ mode: onIdle
79
+ idleDelay: 2000
80
+ ```
81
+
82
+ ### `immediate`
83
+ Syncs immediately on every data change. Use with caution -- this can generate a high volume of requests.
84
+
85
+ ```yaml
86
+ - type: DataSync
87
+ path: ~~.settings
88
+ mode: immediate
89
+ ```
90
+
91
+ ### `manual`
92
+ Does not sync on data changes. Waits for the `trigger` path to become `true`.
93
+
94
+ ```yaml
95
+ - type: DataSync
96
+ path: ~~.formData
97
+ mode: manual
98
+ trigger: ~~.saveNow
99
+ ```
100
+
101
+ ## Manual Trigger
102
+
103
+ The `trigger` property works in **any mode**, not just `manual`. When the trigger path is set to `true`:
104
+
105
+ 1. The trigger is immediately reset to `false`.
106
+ 2. Any pending idle timer is cancelled (in `onIdle` mode).
107
+ 3. A sync is performed immediately.
108
+
109
+ This is useful for adding a "Save Now" button alongside auto-save:
110
+
111
+ ```yaml
112
+ - type: button
113
+ content: "Save Now"
114
+ actions:
115
+ - what: setData
116
+ on: click
117
+ path: ~~.saveNow
118
+ value: true
119
+
120
+ - type: DataSync
121
+ path: ~~.userProfile
122
+ mode: onIdle
123
+ idleDelay: 2000
124
+ trigger: ~~.saveNow
125
+ ```
126
+
127
+ ## Error Handling and Retries
128
+
129
+ ### Retry behavior
130
+ - Only **network outages** (browser is offline) and **server errors** (HTTP 5xx) trigger retries.
131
+ - Client errors (4xx) and CORS failures do **not** trigger retries.
132
+ - When the user makes a new data change, the retry counter resets and a fresh sync cycle begins.
133
+
134
+ ### Configuration
135
+ ```yaml
136
+ - type: DataSync
137
+ path: ~~.userProfile
138
+ mode: onIdle
139
+ idleDelay: 1000
140
+ maxRetries: 3
141
+ retryDelay: 10000
142
+ ```
143
+
144
+ ## Change Detection
145
+
146
+ `DataSync` only triggers synchronization when the `data` field of the Syncable Object changes. Changes to `status`, `submission_url`, or other fields are ignored.
147
+
148
+ This prevents feedback loops: when the component updates `status` to "syncing" or when the server response updates `status` to "success", these changes do not trigger a new sync cycle.
149
+
150
+ ## Using with Templates
151
+
152
+ `DataSync` supports `TemplateContext`, so it can be used inside templates to sync individual items in a list:
153
+
154
+ ```yaml
155
+ data:
156
+ items:
157
+ - submission_url: "/api/items/1"
158
+ data: { name: "Item 1" }
159
+ - submission_url: "/api/items/2"
160
+ data: { name: "Item 2" }
161
+
162
+ templates:
163
+ itemRow:
164
+ type: div
165
+ content:
166
+ - type: TextField
167
+ dataLocation: ~.data.name
168
+ - type: DataSync
169
+ path: ~
170
+ mode: onIdle
171
+ idleDelay: 1500
172
+
173
+ renderView:
174
+ - type: Switch
175
+ content: ~~.items
176
+ singleOption:
177
+ load: itemRow
178
+ ```
179
+
180
+ ## Displaying Status
181
+
182
+ You can display the status message from the Syncable Object to provide feedback to the user:
183
+
184
+ ```yaml
185
+ - type: div
186
+ content: ~~.userProfile.status.message
187
+ actions:
188
+ - what: hide
189
+ when: ~~.userProfile.status
190
+ is: undefined
191
+ ```
192
+
193
+ ## Complete Example
194
+
195
+ ```yaml
196
+ data:
197
+ userProfile:
198
+ submission_url: "/api/user/save"
199
+ item_url: "/api/user/123"
200
+ delete_url: "/api/user/123"
201
+ data:
202
+ name: "John Doe"
203
+ email: "john@example.com"
204
+ status:
205
+ type: "info"
206
+ message: "Ready"
207
+ saveNow: false
208
+
209
+ renderView:
210
+ - type: div
211
+ content:
212
+ # Status display
213
+ - type: div
214
+ content: ~~.userProfile.status.message
215
+ actions:
216
+ - what: hide
217
+ when: ~~.userProfile.status
218
+ is: undefined
219
+
220
+ # Form fields
221
+ - type: TextField
222
+ label: "Name"
223
+ dataLocation: ~~.userProfile.data.name
224
+
225
+ - type: TextField
226
+ label: "Email"
227
+ dataLocation: ~~.userProfile.data.email
228
+
229
+ # Manual save button
230
+ - type: button
231
+ content: "Save Now"
232
+ actions:
233
+ - what: setData
234
+ on: click
235
+ path: ~~.saveNow
236
+ value: true
237
+
238
+ # DataSync: auto-saves after 2s of idle, manual trigger available
239
+ - type: DataSync
240
+ path: ~~.userProfile
241
+ mode: onIdle
242
+ idleDelay: 2000
243
+ trigger: ~~.saveNow
244
+ ```
245
+
246
+ ## Limitations
247
+ - Only one sync can be active at a time per `DataSync` instance. Concurrent changes while a sync is in flight are not queued.
248
+ - The entire Syncable Object is sent in the POST body, not just the `data` field.
249
+ - Only POST requests are supported for submission. Custom HTTP methods are not configurable.
250
+ - `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.
251
+ - No built-in support for optimistic updates. The local data is replaced by the server response.
252
+ - CORS errors are not retried (they are indistinguishable from network errors when the browser is online).