@ea-lab/reactive-json-docs 2.6.0 → 2.8.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.
Files changed (36) hide show
  1. package/package.json +2 -2
  2. package/public/rjbuild/docs/advanced-concepts/forward-update.yaml +1 -1
  3. package/public/rjbuild/docs/advanced-concepts/plugins/component-development.yaml +1 -1
  4. package/public/rjbuild/docs/advanced-concepts/plugins/index.yaml +1 -1
  5. package/public/rjbuild/docs/core/action/CustomEventListener.yaml +1 -1
  6. package/public/rjbuild/docs/core/element/form/AutocompleteField.yaml +1 -1
  7. package/public/rjbuild/docs/core/element/form/CheckBoxField.yaml +1 -1
  8. package/public/rjbuild/docs/core/element/form/DateField.md +79 -0
  9. package/public/rjbuild/docs/core/element/form/DateField.yaml +115 -0
  10. package/public/rjbuild/docs/core/element/form/DateTimeField.md +60 -0
  11. package/public/rjbuild/docs/core/element/form/DateTimeField.yaml +97 -0
  12. package/public/rjbuild/docs/core/element/form/Input.yaml +1 -1
  13. package/public/rjbuild/docs/core/element/form/SelectField.yaml +1 -1
  14. package/public/rjbuild/docs/core/element/form/TextAreaField.yaml +1 -1
  15. package/public/rjbuild/docs/core/element/html/Join.yaml +1 -1
  16. package/public/rjbuild/docs/core/element/special/DataSync.md +20 -0
  17. package/public/rjbuild/docs/core/element/special/DataSync.yaml +67 -0
  18. package/public/rjbuild/docs/core/element/special/PageControls.yaml +1 -1
  19. package/public/rjbuild/docs/core/hook/index.yaml +1 -1
  20. package/public/rjbuild/docs/core/hook/usePagination.yaml +1 -1
  21. package/public/rjbuild/docs/core/hook/useTransformedAttributes.yaml +1 -1
  22. package/public/rjbuild/docs/docs-components/Mermaid.yaml +1 -1
  23. package/public/rjbuild/docs/docs-components/SyntaxHighlighter.yaml +1 -1
  24. package/public/rjbuild/docs/docs-components/index.yaml +1 -1
  25. package/public/rjbuild/docs/getting-started/template-contexts-data-binding.yaml +1 -1
  26. package/public/rjbuild/docs/index.yaml +1 -1
  27. package/public/rjbuild/docs/install.yaml +1 -1
  28. package/public/rjbuild/docs/integration/bootstrap/element/form/CheckBoxField.yaml +1 -1
  29. package/public/rjbuild/docs/integration/bootstrap/element/form/DateField.yaml +1 -1
  30. package/public/rjbuild/docs/integration/bootstrap/element/form/NumberField.yaml +1 -1
  31. package/public/rjbuild/docs/integration/bootstrap/element/form/SelectField.yaml +1 -1
  32. package/public/rjbuild/docs/integration/bootstrap/element/form/TextAreaField.yaml +1 -1
  33. package/public/rjbuild/docs/integration/bootstrap/element/form/TextField.yaml +1 -1
  34. package/public/rjbuild/docs/integration/bootstrap/element/form/index.yaml +1 -1
  35. package/public/rjbuild/docs/integration/bootstrap/example/accordion.yaml +1 -1
  36. package/public/rjbuild/docs/integration/bootstrap/example/dynamic-content.yaml +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ea-lab/reactive-json-docs",
3
- "version": "2.6.0",
3
+ "version": "2.8.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": "^2.5.1",
29
+ "@ea-lab/reactive-json": "^2.8.0",
30
30
  "@ea-lab/reactive-json-chartjs": "^1.1.1",
31
31
  "@npmcli/fs": "^4.0.0",
32
32
  "@reduxjs/toolkit": "^2.6.1",
@@ -176,4 +176,4 @@ renderView:
176
176
 
177
177
  templates:
178
178
 
179
- data: {}
179
+ data:
@@ -225,4 +225,4 @@ renderView:
225
225
 
226
226
  templates:
227
227
 
228
- data: {}
228
+ data:
@@ -26,4 +26,4 @@ renderView:
26
26
 
27
27
  templates:
28
28
 
29
- data: {}
29
+ data:
@@ -155,4 +155,4 @@ renderView:
155
155
 
156
156
  templates:
157
157
 
158
- data: {}
158
+ data:
@@ -222,4 +222,4 @@ renderView:
222
222
  - No built-in keyboard navigation for the dropdown.
223
223
 
224
224
  templates: {}
225
- data: {}
225
+ data:
@@ -218,5 +218,5 @@ renderView:
218
218
  - For radio buttons, `multiple` is automatically set to `false` regardless of the `multiple` property
219
219
 
220
220
  templates: {}
221
- data: {}
221
+ data:
222
222
 
@@ -0,0 +1,79 @@
1
+ # DateField
2
+
3
+ The `DateField` component is a thin convenience wrapper around [`Input`](Input.md) that renders an HTML `date` input (`<input type="date">`). It inherits every `Input` option (label, wrapper, attributes, actions, data binding) and adds an optional `storageFormat` for binding to a fixed-format storage string.
4
+
5
+ ## Basic Syntax
6
+
7
+ ```yaml
8
+ - type: DateField
9
+ dataLocation: ~.startDate
10
+ label: "Start date:"
11
+ ```
12
+
13
+ By default the raw value of the HTML `date` input (`YYYY-MM-DD`) is stored as-is — identical to any other `Input` wrapper.
14
+
15
+ ## Properties
16
+
17
+ - `dataLocation` (string, optional): Path to bind the field value in the data context. Without it, the value is stored in the template context under the component's `datafield`.
18
+ - `defaultFieldValue` (string, optional): Default value when no data is present.
19
+ - `label` (string or View props, optional): Field label text (supports template evaluation and View rendering). A wrapper `<div>` is added automatically when a label is present.
20
+ - `storageFormat` (string, optional): Binds the field to a fixed-format **storage string** instead of the raw input value. Accepts `"date"` or `"datetime"` (see [Storage format](#storage-format)). When omitted, the raw `YYYY-MM-DD` value is stored unchanged (original behavior).
21
+ - `attributes` (object, optional): Attributes applied to the wrapper `<div>` (or to the input itself when there is no wrapper).
22
+ - `inputAttributes` (object, optional): Attributes applied directly to the `<input>` element (e.g. `class`, `id`, `min`, `max`).
23
+ - `labelAttributes` (object, optional): Attributes applied to the label (`htmlFor` is managed automatically).
24
+ - `forceWrapper` (boolean, optional): Forces the presence (`true`) or absence (`false`) of the wrapper `<div>`. If omitted, the wrapper appears only when a label is present.
25
+ - `actions` (array, optional): Actions and conditional reactions to execute based on field state.
26
+
27
+ ## Storage format
28
+
29
+ An HTML `date` input can only read and write `YYYY-MM-DD`. Backends often store dates in a fixed, stricter format — for example Drupal datetime fields validate against `Y-m-d` (date-only field) or `Y-m-d\TH:i:s` (datetime field). Feeding such a value straight to a `date` input leaves it blank (the input rejects a value carrying a time), and writing back `YYYY-MM-DD` is rejected by a datetime field.
30
+
31
+ Set `storageFormat` to bridge the two. It applies **two conversions**:
32
+
33
+ - **On read** — only the date part of the stored value is shown, so a datetime value like `2016-11-29T23:00:00` displays as `2016-11-29` instead of leaving the input empty.
34
+ - **On write** — the value is re-serialized to the target storage format:
35
+ - `storageFormat: date` → `YYYY-MM-DD`
36
+ - `storageFormat: datetime` → `YYYY-MM-DDT00:00:00` (the time is anchored at midnight, since a `date` input carries no time)
37
+
38
+ An empty input stores an empty string (clears the field). When `storageFormat` is omitted, no conversion happens and the raw input value is stored — so existing usages are unaffected.
39
+
40
+ > For editing a value that also carries a **time** the user should see and change, use [`DateTimeField`](DateTimeField.md) instead.
41
+
42
+ ## Examples
43
+
44
+ ### Default behavior (raw `YYYY-MM-DD`)
45
+
46
+ ```yaml
47
+ renderView:
48
+ - type: DateField
49
+ dataLocation: ~.startDate
50
+ label: "Start date:"
51
+ - type: div
52
+ content:
53
+ - type: strong
54
+ content: "Stored value: "
55
+ - ~.startDate
56
+
57
+ data:
58
+ startDate: "2027-03-15"
59
+ ```
60
+
61
+ ### `storageFormat: datetime`
62
+
63
+ The `date` input drives a datetime storage string (time anchored at midnight):
64
+
65
+ ```yaml
66
+ renderView:
67
+ - type: DateField
68
+ dataLocation: ~.closeDate
69
+ label: "Close date:"
70
+ storageFormat: datetime
71
+ - type: div
72
+ content:
73
+ - type: strong
74
+ content: "Stored value: "
75
+ - ~.closeDate
76
+
77
+ data:
78
+ closeDate: "2016-11-29T23:00:00"
79
+ ```
@@ -0,0 +1,115 @@
1
+ renderView:
2
+ - type: Markdown
3
+ content: |
4
+ # DateField
5
+
6
+ The `DateField` component is a thin convenience wrapper around [`Input`](/docs/core/element/form/Input) that renders an HTML `date` input (`<input type="date">`). It inherits every `Input` option (label, wrapper, attributes, actions, data binding) and adds an optional `storageFormat` for binding to a fixed-format storage string.
7
+
8
+ - type: Markdown
9
+ content: |
10
+ ## Basic Syntax
11
+
12
+ ```yaml
13
+ - type: DateField
14
+ dataLocation: ~.startDate
15
+ label: "Start date:"
16
+ ```
17
+
18
+ By default the raw value of the HTML `date` input (`YYYY-MM-DD`) is stored as-is — identical to any other `Input` wrapper.
19
+
20
+ ## Properties
21
+
22
+ - type: DefinitionList
23
+ content:
24
+ - term:
25
+ code: dataLocation
26
+ after: "(string, optional)"
27
+ details: "Path to bind the field value in the data context. Without it, the value is stored in the template context under the component's `datafield`."
28
+ - term:
29
+ code: defaultFieldValue
30
+ after: "(string, optional)"
31
+ details: "Default value when no data is present."
32
+ - term:
33
+ code: label
34
+ after: "(string or View props, optional)"
35
+ details: "Field label text (supports template evaluation and View rendering). A wrapper `<div>` is added automatically when a label is present."
36
+ - term:
37
+ code: storageFormat
38
+ after: "(string, optional)"
39
+ details:
40
+ type: Markdown
41
+ content: |
42
+ Binds the field to a fixed-format **storage string** instead of the raw input value. Accepts `"date"` or `"datetime"`. See [Storage format](#storage-format) below. When omitted, the raw `YYYY-MM-DD` value is stored unchanged (original behavior).
43
+ - term:
44
+ code: attributes
45
+ after: "(object, optional)"
46
+ details: "Attributes applied to the wrapper `<div>` (or to the input itself when there is no wrapper)."
47
+ - term:
48
+ code: inputAttributes
49
+ after: "(object, optional)"
50
+ details: "Attributes applied directly to the `<input>` element (e.g. `class`, `id`, `min`, `max`)."
51
+ - term:
52
+ code: labelAttributes
53
+ after: "(object, optional)"
54
+ details: "Attributes applied to the label (`htmlFor` is managed automatically)."
55
+ - term:
56
+ code: forceWrapper
57
+ after: "(boolean, optional)"
58
+ details: "Forces the presence (`true`) or absence (`false`) of the wrapper `<div>`. If omitted, the wrapper appears only when a label is present."
59
+ - term:
60
+ code: actions
61
+ after: "(array, optional)"
62
+ details: "Actions and conditional reactions to execute based on field state (hide, tooltip, setData, …)."
63
+
64
+ - type: Markdown
65
+ content: |
66
+ ## Storage format
67
+
68
+ An HTML `date` input can only read and write `YYYY-MM-DD`. Backends often store dates in a fixed, stricter format — for example Drupal datetime fields validate against `Y-m-d` (date-only field) or `Y-m-d\TH:i:s` (datetime field). Feeding such a value straight to a `date` input leaves it blank (the input rejects a value carrying a time), and writing back `YYYY-MM-DD` is rejected by a datetime field.
69
+
70
+ Set `storageFormat` to bridge the two. It applies **two conversions**:
71
+
72
+ - **On read** — only the date part of the stored value is shown, so a datetime value like `2016-11-29T23:00:00` displays as `2016-11-29` instead of leaving the input empty.
73
+ - **On write** — the value is re-serialized to the target storage format:
74
+ - `storageFormat: date` → `YYYY-MM-DD`
75
+ - `storageFormat: datetime` → `YYYY-MM-DDT00:00:00` (the time is anchored at midnight, since a `date` input carries no time)
76
+
77
+ An empty input stores an empty string (clears the field). When `storageFormat` is omitted, no conversion happens and the raw input value is stored — so existing usages are unaffected.
78
+
79
+ > For editing a value that also carries a **time** the user should see and change, use [`DateTimeField`](/docs/core/element/form/DateTimeField) instead.
80
+
81
+ - type: RjBuildDescriber
82
+ title: "Default behavior (raw YYYY-MM-DD)"
83
+ description: "Without storageFormat, the raw input value is stored as-is."
84
+ toDescribe:
85
+ renderView:
86
+ - type: DateField
87
+ dataLocation: ~.startDate
88
+ label: "Start date:"
89
+ - type: div
90
+ content:
91
+ - type: strong
92
+ content: "Stored value: "
93
+ - ~.startDate
94
+ data:
95
+ startDate: "2027-03-15"
96
+
97
+ - type: RjBuildDescriber
98
+ title: "storageFormat: datetime"
99
+ description: "The date input drives a datetime storage string (time anchored at midnight)."
100
+ toDescribe:
101
+ renderView:
102
+ - type: DateField
103
+ dataLocation: ~.closeDate
104
+ label: "Close date:"
105
+ storageFormat: datetime
106
+ - type: div
107
+ content:
108
+ - type: strong
109
+ content: "Stored value: "
110
+ - ~.closeDate
111
+ data:
112
+ closeDate: "2016-11-29T23:00:00"
113
+
114
+ templates: {}
115
+ data:
@@ -0,0 +1,60 @@
1
+ # DateTimeField
2
+
3
+ The `DateTimeField` component is a thin convenience wrapper around [`Input`](Input.md) that renders an HTML `datetime-local` input (`<input type="datetime-local">`). It inherits every `Input` option and adds an optional `storageFormat` for binding to a fixed-format storage string.
4
+
5
+ ## Basic Syntax
6
+
7
+ ```yaml
8
+ - type: DateTimeField
9
+ dataLocation: ~.meetingAt
10
+ label: "Meeting date and time:"
11
+ ```
12
+
13
+ By default the raw value of the HTML `datetime-local` input (`YYYY-MM-DDTHH:MM`) is stored as-is.
14
+
15
+ ## Properties
16
+
17
+ - `dataLocation` (string, optional): Path to bind the field value in the data context. Without it, the value is stored in the template context under the component's `datafield`.
18
+ - `defaultFieldValue` (string, optional): Default value when no data is present.
19
+ - `label` (string or View props, optional): Field label text (supports template evaluation and View rendering). A wrapper `<div>` is added automatically when a label is present.
20
+ - `storageFormat` (string, optional): Binds the field to a fixed-format **storage string** instead of the raw input value. Accepts `"date"` or `"datetime"` (see [Storage format](#storage-format)). When omitted, the raw `YYYY-MM-DDTHH:MM` value is stored unchanged (original behavior).
21
+ - `attributes` (object, optional): Attributes applied to the wrapper `<div>` (or to the input itself when there is no wrapper).
22
+ - `inputAttributes` (object, optional): Attributes applied directly to the `<input>` element (e.g. `class`, `id`, `min`, `max`, `step`).
23
+ - `labelAttributes` (object, optional): Attributes applied to the label (`htmlFor` is managed automatically).
24
+ - `forceWrapper` (boolean, optional): Forces the presence (`true`) or absence (`false`) of the wrapper `<div>`. If omitted, the wrapper appears only when a label is present.
25
+ - `actions` (array, optional): Actions and conditional reactions to execute based on field state.
26
+
27
+ ## Storage format
28
+
29
+ An HTML `datetime-local` input reads and writes `YYYY-MM-DDTHH:MM` — **without seconds** (browsers omit the `:00` seconds unless a sub-minute `step` is set, and even then behavior varies). A backend datetime field that validates against a fixed format such as Drupal's `Y-m-d\TH:i:s` therefore rejects the raw value.
30
+
31
+ Set `storageFormat` to bridge the two. It applies **two conversions**:
32
+
33
+ - **On read** — the date + time part of the stored value is shown (a stored `2016-11-29T23:00:00` displays as `2016-11-29T23:00`).
34
+ - **On write** — the value is re-serialized to the target storage format:
35
+ - `storageFormat: datetime` → `YYYY-MM-DDTHH:MM:SS` (seconds appended; `00` when the input has no time)
36
+ - `storageFormat: date` → `YYYY-MM-DD` (time dropped)
37
+
38
+ An empty input stores an empty string (clears the field). When `storageFormat` is omitted, no conversion happens and the raw input value is stored — so existing usages are unaffected.
39
+
40
+ ## Example
41
+
42
+ ### `storageFormat: datetime`
43
+
44
+ The `datetime-local` input drives a datetime storage string, with seconds:
45
+
46
+ ```yaml
47
+ renderView:
48
+ - type: DateTimeField
49
+ dataLocation: ~.closeDate
50
+ label: "Close date and time:"
51
+ storageFormat: datetime
52
+ - type: div
53
+ content:
54
+ - type: strong
55
+ content: "Stored value: "
56
+ - ~.closeDate
57
+
58
+ data:
59
+ closeDate: "2016-11-29T23:00:00"
60
+ ```
@@ -0,0 +1,97 @@
1
+ renderView:
2
+ - type: Markdown
3
+ content: |
4
+ # DateTimeField
5
+
6
+ The `DateTimeField` component is a thin convenience wrapper around [`Input`](/docs/core/element/form/Input) that renders an HTML `datetime-local` input (`<input type="datetime-local">`). It inherits every `Input` option and adds an optional `storageFormat` for binding to a fixed-format storage string.
7
+
8
+ - type: Markdown
9
+ content: |
10
+ ## Basic Syntax
11
+
12
+ ```yaml
13
+ - type: DateTimeField
14
+ dataLocation: ~.meetingAt
15
+ label: "Meeting date and time:"
16
+ ```
17
+
18
+ By default the raw value of the HTML `datetime-local` input (`YYYY-MM-DDTHH:MM`) is stored as-is.
19
+
20
+ ## Properties
21
+
22
+ - type: DefinitionList
23
+ content:
24
+ - term:
25
+ code: dataLocation
26
+ after: "(string, optional)"
27
+ details: "Path to bind the field value in the data context. Without it, the value is stored in the template context under the component's `datafield`."
28
+ - term:
29
+ code: defaultFieldValue
30
+ after: "(string, optional)"
31
+ details: "Default value when no data is present."
32
+ - term:
33
+ code: label
34
+ after: "(string or View props, optional)"
35
+ details: "Field label text (supports template evaluation and View rendering). A wrapper `<div>` is added automatically when a label is present."
36
+ - term:
37
+ code: storageFormat
38
+ after: "(string, optional)"
39
+ details:
40
+ type: Markdown
41
+ content: |
42
+ Binds the field to a fixed-format **storage string** instead of the raw input value. Accepts `"date"` or `"datetime"`. See [Storage format](#storage-format) below. When omitted, the raw `YYYY-MM-DDTHH:MM` value is stored unchanged (original behavior).
43
+ - term:
44
+ code: attributes
45
+ after: "(object, optional)"
46
+ details: "Attributes applied to the wrapper `<div>` (or to the input itself when there is no wrapper)."
47
+ - term:
48
+ code: inputAttributes
49
+ after: "(object, optional)"
50
+ details: "Attributes applied directly to the `<input>` element (e.g. `class`, `id`, `min`, `max`, `step`)."
51
+ - term:
52
+ code: labelAttributes
53
+ after: "(object, optional)"
54
+ details: "Attributes applied to the label (`htmlFor` is managed automatically)."
55
+ - term:
56
+ code: forceWrapper
57
+ after: "(boolean, optional)"
58
+ details: "Forces the presence (`true`) or absence (`false`) of the wrapper `<div>`. If omitted, the wrapper appears only when a label is present."
59
+ - term:
60
+ code: actions
61
+ after: "(array, optional)"
62
+ details: "Actions and conditional reactions to execute based on field state."
63
+
64
+ - type: Markdown
65
+ content: |
66
+ ## Storage format
67
+
68
+ An HTML `datetime-local` input reads and writes `YYYY-MM-DDTHH:MM` — **without seconds** (browsers omit the `:00` seconds unless a sub-minute `step` is set, and even then behavior varies). A backend datetime field that validates against a fixed format such as Drupal's `Y-m-d\TH:i:s` therefore rejects the raw value.
69
+
70
+ Set `storageFormat` to bridge the two. It applies **two conversions**:
71
+
72
+ - **On read** — the date + time part of the stored value is shown (a stored `2016-11-29T23:00:00` displays as `2016-11-29T23:00`).
73
+ - **On write** — the value is re-serialized to the target storage format:
74
+ - `storageFormat: datetime` → `YYYY-MM-DDTHH:MM:SS` (seconds appended; `00` when the input has no time)
75
+ - `storageFormat: date` → `YYYY-MM-DD` (time dropped)
76
+
77
+ An empty input stores an empty string (clears the field). When `storageFormat` is omitted, no conversion happens and the raw input value is stored — so existing usages are unaffected.
78
+
79
+ - type: RjBuildDescriber
80
+ title: "storageFormat: datetime"
81
+ description: "The datetime-local input drives a datetime storage string, with seconds."
82
+ toDescribe:
83
+ renderView:
84
+ - type: DateTimeField
85
+ dataLocation: ~.closeDate
86
+ label: "Close date and time:"
87
+ storageFormat: datetime
88
+ - type: div
89
+ content:
90
+ - type: strong
91
+ content: "Stored value: "
92
+ - ~.closeDate
93
+ data:
94
+ closeDate: "2016-11-29T23:00:00"
95
+
96
+ templates: {}
97
+ data:
@@ -569,4 +569,4 @@ renderView:
569
569
  - Integrated label is limited: for conditional actions, prefer separated label
570
570
 
571
571
  templates: {}
572
- data: {}
572
+ data:
@@ -248,5 +248,5 @@ renderView:
248
248
  - Empty string handling requires explicit `allowEmptyStringAsValue` for semantic distinction
249
249
 
250
250
  templates: {}
251
- data: {}
251
+ data:
252
252
 
@@ -135,5 +135,5 @@ renderView:
135
135
  - Template evaluation for `rows` should return a number
136
136
 
137
137
  templates: {}
138
- data: {}
138
+ data:
139
139
 
@@ -110,4 +110,4 @@ renderView:
110
110
  - No per-item formatting; all elements are joined as plain strings.
111
111
 
112
112
  templates: {}
113
- data: {}
113
+ data:
@@ -242,6 +242,26 @@ Editing either field mirrors live to the other, and the whole group produces a s
242
242
  - Leaving `mergeKey` unset keeps the default behavior — one independent sync per instance.
243
243
  - Known limitation: if the owner unmounts while a sync is still pending, that in-flight edit is not persisted (the new owner only writes on the next edit).
244
244
 
245
+ ### Overriding the grouping (advanced)
246
+
247
+ The group coordination — membership, owner election, and the live broadcast — is not hard-wired into `DataSync`. It is resolved from a **`utility` plugin** named `dataSyncGroups`, falling back to the core singleton registry when none is supplied. This lets an app swap *just* the grouping behavior (e.g. a registry scoped per tab, or one that bridges across iframes) without overriding the whole `DataSync` element.
248
+
249
+ Register your own under `plugins.utility`:
250
+
251
+ ```js
252
+ const myDataSyncGroups = {
253
+ joinSyncGroup(key, member) { /* … returns a leave() function */ },
254
+ broadcastToGroup(key, fromMember, data) { /* … */ },
255
+ getOwner(key) { /* … returns the current writer or null */ },
256
+ };
257
+
258
+ const plugins = mergeComponentCollections([
259
+ { utility: { dataSyncGroups: myDataSyncGroups } },
260
+ ]);
261
+ ```
262
+
263
+ The registry must be a **shared singleton**: every `DataSync` that resolves the same `dataSyncGroups` object joins the same groups. Provide one instance, not one per render.
264
+
245
265
  ## Using with Templates
246
266
 
247
267
  `DataSync` supports `TemplateContext`, so it can be used inside templates to sync individual items in a list:
@@ -138,6 +138,12 @@ renderView:
138
138
  details:
139
139
  type: Markdown
140
140
  content: "Milliseconds between retry attempts. Default: `5000`."
141
+ - term:
142
+ code: mergeKey
143
+ after: " (string, optional)"
144
+ details:
145
+ type: Markdown
146
+ content: "Path to a **stable identity** for the syncable (typically its `submission_url`). All `DataSync` instances resolving to the same value form a shared group: edits are mirrored live across them and persisted by a single owner. See **Shared Syncables** below."
141
147
 
142
148
  - type: Markdown
143
149
  content: |
@@ -268,6 +274,67 @@ renderView:
268
274
 
269
275
  `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.
270
276
 
277
+ ## Shared Syncables (`mergeKey`)
278
+
279
+ When the **same resource** is mounted in several places at once — the same record shown in two views, or one editable item embedded in multiple components — each `DataSync` would normally watch its own copy and POST independently. That causes duplicate writes and lets the copies drift apart.
280
+
281
+ Set `mergeKey` to make those instances cooperate. It is a path resolving to a **stable identity** for the syncable (commonly its `submission_url`). Every `DataSync` that resolves to the same value forms a **group**:
282
+
283
+ - **One writer (the owner).** The first instance to join is the group's owner, re-elected automatically if it unmounts. Only the owner sends requests.
284
+ - **Live mirror.** When any member's `data` changes, that data is applied to every other member's store — with their own change-watcher suppressed, so they neither re-broadcast nor POST. All copies stay in sync on screen.
285
+ - **Single, coalesced write.** Persistence is delegated to the owner, which (re)schedules its one debounced sync and reads the **latest** shared data when it fires. Rapid edits across different members collapse into a single request carrying the final value — no concurrent writers, no stale-data write, no revert.
286
+ - **Server propagation.** On a successful sync, the owner broadcasts the (server-enriched) response `data` back to the group.
287
+
288
+ ```yaml
289
+ data:
290
+ cardA:
291
+ submission_url: "/api/items/123"
292
+ data: { title: "Hello" }
293
+ cardB:
294
+ submission_url: "/api/items/123" # SAME entity, second mount
295
+ data: { title: "Hello" }
296
+
297
+ renderView:
298
+ - type: TextField
299
+ dataLocation: ~~.cardA.data.title
300
+ - type: DataSync
301
+ path: ~~.cardA
302
+ mergeKey: ~~.cardA.submission_url
303
+
304
+ - type: TextField
305
+ dataLocation: ~~.cardB.data.title
306
+ - type: DataSync
307
+ path: ~~.cardB
308
+ mergeKey: ~~.cardB.submission_url
309
+ ```
310
+
311
+ Editing either field mirrors live to the other, and the whole group produces a single POST.
312
+
313
+ **Notes**
314
+ - The library stays agnostic about what makes two syncables "the same": you pick the identity by pointing `mergeKey` at whatever field is stable per resource (usually `submission_url`).
315
+ - Leaving `mergeKey` unset keeps the default behavior — one independent sync per instance.
316
+ - Known limitation: if the owner unmounts while a sync is still pending, that in-flight edit is not persisted (the new owner only writes on the next edit).
317
+
318
+ ### Overriding the grouping (advanced)
319
+
320
+ The group coordination — membership, owner election, and the live broadcast — is not hard-wired into `DataSync`. It is resolved from a **`utility` plugin** named `dataSyncGroups`, falling back to the core singleton registry when none is supplied. This lets an app swap *just* the grouping behavior (e.g. a registry scoped per tab, or one that bridges across iframes) without overriding the whole `DataSync` element.
321
+
322
+ Register your own under `plugins.utility`:
323
+
324
+ ```js
325
+ const myDataSyncGroups = {
326
+ joinSyncGroup(key, member) { /* … returns a leave() function */ },
327
+ broadcastToGroup(key, fromMember, data) { /* … */ },
328
+ getOwner(key) { /* … returns the current writer or null */ },
329
+ };
330
+
331
+ const plugins = mergeComponentCollections([
332
+ { utility: { dataSyncGroups: myDataSyncGroups } },
333
+ ]);
334
+ ```
335
+
336
+ The registry must be a **shared singleton**: every `DataSync` that resolves the same `dataSyncGroups` object joins the same groups. Provide one instance, not one per render.
337
+
271
338
  ## Using with Templates
272
339
 
273
340
  `DataSync` supports `TemplateContext`, so it can be used inside templates to sync individual items in a list:
@@ -131,4 +131,4 @@ renderView:
131
131
 
132
132
  templates:
133
133
 
134
- data: {}
134
+ data:
@@ -41,4 +41,4 @@ renderView:
41
41
  The core Reactive-JSON library uses this same mechanism internally to provide its built-in hooks, ensuring consistency between core functionality and custom extensions.
42
42
 
43
43
  templates: {}
44
- data: {}
44
+ data:
@@ -316,4 +316,4 @@ renderView:
316
316
  - Future version: support for synchronization with external data changes
317
317
 
318
318
  templates: {}
319
- data: {}
319
+ data:
@@ -161,4 +161,4 @@ renderView:
161
161
  For details on individual transformers, see [attributeTransformer documentation](../attributeTransformer/index).
162
162
 
163
163
  templates: {}
164
- data: {}
164
+ data:
@@ -336,4 +336,4 @@ renderView:
336
336
 
337
337
  templates:
338
338
 
339
- data: {}
339
+ data:
@@ -164,4 +164,4 @@ renderView:
164
164
 
165
165
  templates:
166
166
 
167
- data: {}
167
+ data:
@@ -31,4 +31,4 @@ renderView:
31
31
 
32
32
  templates:
33
33
 
34
- data: {}
34
+ data:
@@ -233,4 +233,4 @@ renderView:
233
233
 
234
234
  templates:
235
235
 
236
- data: {}
236
+ data:
@@ -70,4 +70,4 @@ renderView:
70
70
 
71
71
  templates:
72
72
 
73
- data: {}
73
+ data:
@@ -975,4 +975,4 @@ templates:
975
975
  - **Without routing:** "Ready to start!" message
976
976
  - **With routing:** "Welcome to your React + Reactive-JSON app!" message with a navigation bar at the top of the page allowing navigation between pages
977
977
 
978
- data: {}
978
+ data:
@@ -367,4 +367,4 @@ renderView:
367
367
  - Other strings remain as strings
368
368
 
369
369
  templates: {}
370
- data: {}
370
+ data:
@@ -283,4 +283,4 @@ renderView:
283
283
  - Template evaluation for label property
284
284
 
285
285
  templates: {}
286
- data: {}
286
+ data:
@@ -125,4 +125,4 @@ renderView:
125
125
  - No automatic locale-specific number formatting
126
126
 
127
127
  templates: {}
128
- data: {}
128
+ data:
@@ -394,4 +394,4 @@ renderView:
394
394
  ```
395
395
 
396
396
  templates: {}
397
- data: {}
397
+ data:
@@ -171,4 +171,4 @@ renderView:
171
171
  - No built-in spell checking configuration options
172
172
 
173
173
  templates: {}
174
- data: {}
174
+ data:
@@ -174,4 +174,4 @@ renderView:
174
174
  - No support for complex input patterns beyond HTML5 pattern attribute
175
175
 
176
176
  templates: {}
177
- data: {}
177
+ data:
@@ -93,4 +93,4 @@ renderView:
93
93
  formData: ""
94
94
 
95
95
  templates: {}
96
- data: {}
96
+ data:
@@ -61,4 +61,4 @@ renderView:
61
61
 
62
62
  templates:
63
63
 
64
- data: {}
64
+ data:
@@ -234,4 +234,4 @@ renderView:
234
234
 
235
235
  templates:
236
236
 
237
- data: {}
237
+ data: