@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 +2 -2
- package/public/rjbuild/docs/core/attributeTransformer/setAttributeValue.md +14 -117
- package/public/rjbuild/docs/core/attributeTransformer/setAttributeValue.yaml +12 -144
- package/public/rjbuild/docs/core/element/special/DataSync.md +252 -0
- package/public/rjbuild/docs/core/element/special/DataSync.yaml +274 -0
- package/public/rjbuild/docs/core/example/DataFilter-example-direct-array.md +0 -211
- package/public/rjbuild/docs/core/example/DataFilter-example-direct-array.yaml +0 -323
- package/public/rjbuild/docs/core/example/button-wrapper-pattern.md +0 -99
- package/public/rjbuild/docs/core/example/button-wrapper-pattern.yaml +0 -132
- package/public/rjbuild/docs/core/example/conditional-field-with-dual-update.md +0 -209
- package/public/rjbuild/docs/core/example/conditional-field-with-dual-update.yaml +0 -74
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ea-lab/reactive-json-docs",
|
|
3
|
-
"version": "
|
|
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.
|
|
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
|
|
11
|
+
# Add CSS class
|
|
12
12
|
- what: setAttributeValue
|
|
13
13
|
name: "class"
|
|
14
14
|
value: "active"
|
|
15
15
|
|
|
16
|
-
# Replace attribute value
|
|
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
|
|
27
|
+
- `"append"`: Adds the value to the existing attribute value (space-separated).
|
|
35
28
|
- `"replace"`: Completely replaces the existing attribute value.
|
|
36
|
-
- **value** *(string
|
|
37
|
-
|
|
38
|
-
|
|
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
|
|
71
|
-
- **Data attributes**: Setting data-* attributes for JavaScript integration
|
|
72
|
-
- **ARIA attributes**: Dynamically updating accessibility attributes
|
|
73
|
-
- **Style attributes**: Modifying inline styles conditionally
|
|
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
|
-
|
|
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
|
-
- **
|
|
195
|
-
- **
|
|
196
|
-
|
|
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
|
|
15
|
+
# Add CSS class
|
|
16
16
|
- what: setAttributeValue
|
|
17
17
|
name: "class"
|
|
18
18
|
value: "active"
|
|
19
19
|
|
|
20
|
-
# Replace attribute value
|
|
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
|
|
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
|
|
108
|
-
- **Data attributes**: Setting data-* attributes for JavaScript integration
|
|
109
|
-
- **ARIA attributes**: Dynamically updating accessibility attributes
|
|
110
|
-
- **Style attributes**: Modifying inline styles conditionally
|
|
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
|
-
-
|
|
266
|
-
-
|
|
267
|
-
-
|
|
268
|
-
|
|
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).
|