@ea-lab/reactive-json-docs 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,323 @@
1
+ renderView:
2
+ - type: Markdown
3
+ content: |
4
+ # DataFilter Example: Filtering Direct Array Items
5
+
6
+ This example demonstrates how to use `DataFilter` with arrays where each item is a direct object (not wrapped in a namespace property). This is useful when working with data from APIs that return arrays of objects directly.
7
+
8
+ ## Use Case
9
+
10
+ When your data structure is a direct array of objects like:
11
+ ```yaml
12
+ data:
13
+ rows:
14
+ - id: 1
15
+ label: "Operation 1"
16
+ done: "done"
17
+ operation: "create"
18
+ - id: 2
19
+ label: "Operation 2"
20
+ done: "pending"
21
+ operation: "update"
22
+ ```
23
+
24
+ Instead of:
25
+ ```yaml
26
+ data:
27
+ rows:
28
+ - item:
29
+ id: 1
30
+ label: "Operation 1"
31
+ ```
32
+
33
+ ## Solution
34
+
35
+ Use an existing property that is present in all items (like `id`) as the `subjectsWithProperty` namespace. In `whenFilterableData`, reference properties directly without the namespace prefix.
36
+
37
+ **Important**: When using string values for filtering (like status), use a special value like `"all"` to represent "no filter" instead of an empty string, as empty strings can cause issues with select elements.
38
+
39
+ - type: RjBuildDescriber
40
+ title: Complete Example
41
+ description: |
42
+ Example showing how to filter a direct array using DataFilter with string-based select filters and text search filters.
43
+ toDescribe:
44
+ renderView:
45
+ - type: div
46
+ attributes:
47
+ class: "mb-3"
48
+ content:
49
+ - type: div
50
+ attributes:
51
+ class: "mb-2"
52
+ content:
53
+ - type: label
54
+ attributes:
55
+ style:
56
+ display: "block"
57
+ marginBottom: "4px"
58
+ content: "Filter by Status:"
59
+ - type: select
60
+ attributes:
61
+ value: ~~._filters.done
62
+ style:
63
+ padding: "4px 8px"
64
+ border: "1px solid #ccc"
65
+ borderRadius: "4px"
66
+ content:
67
+ - type: option
68
+ attributes:
69
+ value: "all"
70
+ content: "All"
71
+ - type: option
72
+ attributes:
73
+ value: "done"
74
+ content: "Done"
75
+ - type: option
76
+ attributes:
77
+ value: "pending"
78
+ content: "Pending"
79
+ actions:
80
+ - what: setData
81
+ on: change
82
+ path: ~~._filters.done
83
+ value: <reactive-json:event-new-value>
84
+ - type: div
85
+ attributes:
86
+ class: "mb-2"
87
+ content:
88
+ - type: label
89
+ attributes:
90
+ style:
91
+ display: "block"
92
+ marginBottom: "4px"
93
+ content: "Filter by Operation:"
94
+ - type: input
95
+ attributes:
96
+ type: "text"
97
+ value: ~~._filters.operation
98
+ placeholder: "Enter operation type"
99
+ style:
100
+ padding: "4px 8px"
101
+ border: "1px solid #ccc"
102
+ borderRadius: "4px"
103
+ width: "100%"
104
+ actions:
105
+ - what: setData
106
+ on: input
107
+ path: ~~._filters.operation
108
+ value: <reactive-json:event-new-value>
109
+ - type: div
110
+ attributes:
111
+ class: "mb-2"
112
+ content:
113
+ - type: label
114
+ attributes:
115
+ style:
116
+ display: "block"
117
+ marginBottom: "4px"
118
+ content: "Search Label:"
119
+ - type: input
120
+ attributes:
121
+ type: "text"
122
+ value: ~~._filters.label
123
+ placeholder: "Search in labels"
124
+ style:
125
+ padding: "4px 8px"
126
+ border: "1px solid #ccc"
127
+ borderRadius: "4px"
128
+ width: "100%"
129
+ actions:
130
+ - what: setData
131
+ on: input
132
+ path: ~~._filters.label
133
+ value: <reactive-json:event-new-value>
134
+ - type: table
135
+ attributes:
136
+ class: "table table-striped"
137
+ content:
138
+ - type: thead
139
+ content:
140
+ - type: tr
141
+ content:
142
+ - type: th
143
+ content: "ID"
144
+ - type: th
145
+ content: "Label"
146
+ - type: th
147
+ content: "Status"
148
+ - type: th
149
+ content: "Operation"
150
+ - type: tbody
151
+ content:
152
+ - type: DataFilter
153
+ context: global
154
+ filters:
155
+ - subjectsWithProperty: id
156
+ andConditions:
157
+ # Filter by status (string)
158
+ - orConditions:
159
+ - when: ~~._filters.done
160
+ is: "all"
161
+ - whenFilterableData: done
162
+ is: ~~._filters.done
163
+
164
+ # Filter by operation type (text search with contains)
165
+ - orConditions:
166
+ - when: ~~._filters.operation
167
+ is: ""
168
+ - andConditions:
169
+ - whenFilterableData: operation
170
+ isNotEmpty: true
171
+ - when: ~~._filters.operation
172
+ isNotEmpty: true
173
+ - whenFilterableData: operation
174
+ contains: ~~._filters.operation
175
+
176
+ # Filter by label (text search with contains)
177
+ - orConditions:
178
+ - when: ~~._filters.label
179
+ is: ""
180
+ - andConditions:
181
+ - whenFilterableData: label
182
+ isNotEmpty: true
183
+ - when: ~~._filters.label
184
+ isNotEmpty: true
185
+ - whenFilterableData: label
186
+ contains: ~~._filters.label
187
+ content:
188
+ - type: Switch
189
+ content: ~~.rows
190
+ singleOption:
191
+ load: operationRow
192
+
193
+ templates:
194
+ operationRow:
195
+ - type: tr
196
+ content:
197
+ - type: td
198
+ content: ~.id
199
+ - type: td
200
+ content: ~.label
201
+ - type: td
202
+ content:
203
+ - type: span
204
+ attributes:
205
+ class: "badge"
206
+ style:
207
+ backgroundColor: "#28a745"
208
+ color: "white"
209
+ padding: "4px 8px"
210
+ borderRadius: "4px"
211
+ content: "Done"
212
+ actions:
213
+ - what: hide
214
+ when: ~.done
215
+ isNot: "done"
216
+ - type: span
217
+ attributes:
218
+ class: "badge"
219
+ style:
220
+ backgroundColor: "#ffc107"
221
+ color: "white"
222
+ padding: "4px 8px"
223
+ borderRadius: "4px"
224
+ content: "Pending"
225
+ actions:
226
+ - what: hide
227
+ when: ~.done
228
+ isNot: "pending"
229
+ - type: td
230
+ content: ~.operation
231
+
232
+ data:
233
+ rows:
234
+ - id: 1
235
+ label: "Operation 1"
236
+ done: "done"
237
+ operation: "create"
238
+ - id: 2
239
+ label: "Operation 2"
240
+ done: "pending"
241
+ operation: "update"
242
+ - id: 3
243
+ label: "Operation 3"
244
+ done: "done"
245
+ operation: "create"
246
+ _filters:
247
+ done: "all"
248
+ label: ""
249
+ operation: ""
250
+
251
+ - type: Markdown
252
+ content: |
253
+ ## Key Points
254
+
255
+ 1. **Namespace Selection**: Choose a property that exists in all items (e.g., `id`, `name`, `key`). This property acts as the identifier for DataFilter to recognize filterable items.
256
+
257
+ 2. **Direct Property Access**: In `whenFilterableData`, reference properties directly:
258
+ - ✅ `whenFilterableData: label` (correct)
259
+ - ❌ `whenFilterableData: id.label` (incorrect - don't use namespace prefix)
260
+
261
+ 3. **String-based Filtering**: When using select elements for filtering, use a special value like `"all"` to represent "no filter" instead of an empty string:
262
+ ```yaml
263
+ - orConditions:
264
+ - when: ~~._filters.done
265
+ is: "all" # Shows all items
266
+ - whenFilterableData: done
267
+ is: ~~._filters.done # Filters by exact match
268
+ ```
269
+ This avoids issues where select elements might not properly handle empty string values.
270
+
271
+ 4. **Text Search**: For text search with `contains`, ensure both the filter value and the data property are not empty:
272
+ ```yaml
273
+ - orConditions:
274
+ - when: ~~._filters.label
275
+ is: "" # Shows all when empty
276
+ - andConditions:
277
+ - whenFilterableData: label
278
+ isNotEmpty: true
279
+ - when: ~~._filters.label
280
+ isNotEmpty: true
281
+ - whenFilterableData: label
282
+ contains: ~~._filters.label
283
+ ```
284
+ The `andConditions` wrapper ensures that both the data property and filter value are not empty before attempting the `contains` comparison.
285
+
286
+ 5. **Template Access**: In templates, access properties directly without namespace:
287
+ - ✅ `~.label`, `~.done`, `~.operation`
288
+ - ❌ `~.id.label` (incorrect)
289
+
290
+ ## Filter Types Demonstrated
291
+
292
+ - **Select Filter with "All" option**: Using `is: "all"` to show all items when a special "all" value is selected
293
+ - **Exact String Match**: Using `is` for exact string matching (e.g., status filtering)
294
+ - **Text Search**: Using `contains` for substring matching (case-insensitive) with proper empty checks
295
+
296
+ ## Additional Template Techniques
297
+
298
+ **Conditional Display in Templates**: The example uses `hide` actions to conditionally display badges based on data values. This is a common pattern for showing different UI elements based on data state, but it's not part of the DataFilter pattern itself - it's just a way to enhance the visual display of filtered data:
299
+
300
+ ```yaml
301
+ - type: span
302
+ content: "Done"
303
+ actions:
304
+ - what: hide
305
+ when: ~.done
306
+ isNot: "done" # Hide if status is not "done"
307
+ - type: span
308
+ content: "Pending"
309
+ actions:
310
+ - what: hide
311
+ when: ~.done
312
+ isNot: "pending" # Hide if status is not "pending"
313
+ ```
314
+
315
+ ## Notes
316
+
317
+ - The `subjectsWithProperty` value (`id` in this example) must exist in every item of the array
318
+ - DataFilter filters the data before rendering, so no `hide` actions are needed in templates for filtering (but they can be used for conditional display within templates)
319
+ - All filter conditions use `orConditions` with an empty/"all" check first, allowing "show all" when filters are empty or set to "all"
320
+ - For select elements, prefer using a special value like `"all"` instead of empty strings to avoid UI issues
321
+ - For text inputs, empty strings (`""`) work fine for the "show all" condition
322
+ - When using `contains` for text search, always wrap the condition in `andConditions` with `isNotEmpty` checks to avoid errors with empty values
323
+
@@ -0,0 +1,99 @@
1
+ # Adding Behaviors to Reusable Components via Wrapper Pattern
2
+
3
+ This pattern demonstrates how to add behaviors (actions, event handlers, conditional logic) to reusable components loaded via `ReactiveJsonSubroot` without modifying the component itself. This is particularly useful when you need to attach context-specific behaviors to shared components.
4
+
5
+ ## Why Use This Pattern?
6
+
7
+ When working with reusable components loaded via `ReactiveJsonSubroot`, you might encounter situations where:
8
+
9
+ - **The component is shared**: You can't modify it directly because it's used in multiple places
10
+ - **Context-specific behavior needed**: Different instances need different behaviors (click handlers, data updates, conditional logic)
11
+ - **Separation of concerns**: You want to keep the component generic and add behavior at the usage site
12
+
13
+ The wrapper pattern solves this by wrapping the component in an element that can handle events and execute actions, while the component itself remains unchanged.
14
+
15
+ ## How It Works
16
+
17
+ The pattern works by leveraging **event propagation** in the DOM:
18
+
19
+ 1. **Wrapper element**: A DOM element (like `div`) wraps the component and has actions attached
20
+ 2. **Component inside**: The reusable component is loaded via `ReactiveJsonSubroot` inside the wrapper
21
+ 3. **Event bubbling**: When a user interacts with the component (e.g., clicks a button), the event bubbles up to the wrapper
22
+ 4. **Action execution**: The wrapper's actions execute, allowing you to add behavior without modifying the component
23
+
24
+ **Important**: This works because DOM events naturally bubble up from child elements to parent elements. The wrapper captures these events and executes its actions.
25
+
26
+ ## Example: Adding Click Behavior to a Button Component
27
+
28
+ In this example, we have a reusable button component that we want to use in multiple places. Instead of modifying the button component itself, we wrap it and add the behavior we need:
29
+
30
+ ```yaml
31
+ renderView:
32
+ - type: div
33
+ actions:
34
+ - what: setData
35
+ on: click
36
+ path: ~~.lastAction
37
+ value: "Button was clicked!"
38
+ content:
39
+ - type: ReactiveJsonSubroot
40
+ rjOptions:
41
+ maybeRawAppRjBuild:
42
+ renderView:
43
+ - type: button
44
+ attributes:
45
+ style: ~~.buttonStyle
46
+ content: ~~.content
47
+ dataOverride:
48
+ content: "Click me"
49
+ buttonStyle:
50
+ color: "white"
51
+ backgroundColor: "#2563eb"
52
+ padding: "8px 16px"
53
+ border: "none"
54
+ borderRadius: "4px"
55
+ cursor: "pointer"
56
+
57
+ data:
58
+ lastAction: "No action yet"
59
+ ```
60
+
61
+ ## Key Concepts
62
+
63
+ ### Event Propagation
64
+
65
+ When a user interacts with the button (clicks it), the click event bubbles up through the DOM hierarchy. The wrapper `div` captures this event and executes its actions. This is why the wrapper pattern works - it leverages the natural behavior of DOM events.
66
+
67
+ ### Component Independence
68
+
69
+ The button component inside the wrapper doesn't need to know about the wrapper's actions. It remains a generic, reusable component. The behavior is added at the usage site, not in the component definition.
70
+
71
+ ### DataOverride for Customization
72
+
73
+ Use `dataOverride` to pass different props to the component for different instances. This allows the same component to be customized without modification.
74
+
75
+ ## When to Use This Pattern
76
+
77
+ This pattern is ideal when:
78
+
79
+ - ✅ You have reusable components that need different behaviors in different contexts
80
+ - ✅ You want to add event handlers without modifying the component
81
+ - ✅ You need to attach conditional logic or data updates to shared components
82
+ - ✅ You want to keep components generic and add behavior at the usage site
83
+
84
+ **Not suitable when**:
85
+ - ❌ The component itself needs to handle the event (attach actions directly to the component)
86
+ - ❌ You need to prevent event propagation (use `stopPropagation` in actions if needed)
87
+ - ❌ You're using `Phantom` wrapper for DOM events (it doesn't render DOM, so events can't bubble to it)
88
+
89
+ **Note about Phantom**: While `Phantom` cannot handle DOM events, it can still be useful for other purposes like passing values via `dataOverride`, applying non-event actions (tooltips, conditional logic), or when you only need to customize component appearance without event handling.
90
+
91
+ ## Limitations and Considerations
92
+
93
+ - **DOM element required for events**: The wrapper must be a DOM element (like `div`) for DOM events to bubble. `Phantom` won't work for DOM events since it doesn't render anything in the DOM. However, `Phantom` can still be useful in other scenarios:
94
+ - **Passing values**: When you only need to pass data via `dataOverride` without event handling
95
+ - **Styling/esthetics**: When you want to apply actions that don't require DOM events (like tooltips, conditional logic, or data manipulations)
96
+ - **Non-event actions**: For actions that don't rely on DOM event listeners
97
+ - **Event propagation**: Events bubble up naturally. If you need to stop propagation, you can use `stopPropagation: true` in your actions.
98
+ - **No sharedUpdates needed**: If the component doesn't need to update parent data, `sharedUpdates` is optional. Only use it if the component needs to modify data that the parent is watching.
99
+
@@ -0,0 +1,132 @@
1
+ renderView:
2
+ - type: Markdown
3
+ content: |
4
+ # Adding Behaviors to Reusable Components via Wrapper Pattern
5
+
6
+ This pattern demonstrates how to add behaviors (actions, event handlers, conditional logic) to reusable components loaded via `ReactiveJsonSubroot` without modifying the component itself. This is particularly useful when you need to attach context-specific behaviors to shared components.
7
+
8
+ - type: Markdown
9
+ content: |
10
+ ## Why Use This Pattern?
11
+
12
+ When working with reusable components loaded via `ReactiveJsonSubroot`, you might encounter situations where:
13
+
14
+ - **The component is shared**: You can't modify it directly because it's used in multiple places
15
+ - **Context-specific behavior needed**: Different instances need different behaviors (click handlers, data updates, conditional logic)
16
+ - **Separation of concerns**: You want to keep the component generic and add behavior at the usage site
17
+
18
+ The wrapper pattern solves this by wrapping the component in an element that can handle events and execute actions, while the component itself remains unchanged.
19
+
20
+ - type: Markdown
21
+ content: |
22
+ ## How It Works
23
+
24
+ The pattern works by leveraging **event propagation** in the DOM:
25
+
26
+ 1. **Wrapper element**: A DOM element (like `div`) wraps the component and has actions attached
27
+ 2. **Component inside**: The reusable component is loaded via `ReactiveJsonSubroot` inside the wrapper
28
+ 3. **Event bubbling**: When a user interacts with the component (e.g., clicks a button), the event bubbles up to the wrapper
29
+ 4. **Action execution**: The wrapper's actions execute, allowing you to add behavior without modifying the component
30
+
31
+ **Important**: This works because DOM events naturally bubble up from child elements to parent elements. The wrapper captures these events and executes its actions.
32
+
33
+ - type: Markdown
34
+ content: |
35
+ ## Example: Adding Click Behavior to a Button Component
36
+
37
+ In this example, we have a reusable button component that we want to use in multiple places. Instead of modifying the button component itself, we wrap it and add the behavior we need.
38
+
39
+ - type: RjBuildDescriber
40
+ title: "Example: Button with wrapper actions"
41
+ description: |
42
+ A reusable button component is wrapped in a div that adds click behavior. When the button is clicked, the wrapper's action executes, updating data without modifying the button component.
43
+ toDescribe:
44
+ renderView:
45
+ - type: div
46
+ attributes:
47
+ style:
48
+ display: "inline-block"
49
+ actions:
50
+ - what: setData
51
+ on: click
52
+ path: ~~.lastAction
53
+ value: "Button was clicked!"
54
+ content:
55
+ - type: ReactiveJsonSubroot
56
+ rjOptions:
57
+ maybeRawAppRjBuild:
58
+ renderView:
59
+ - type: button
60
+ attributes:
61
+ style: ~~.buttonStyle
62
+ content: ~~.content
63
+ dataOverride:
64
+ content: "Click me"
65
+ buttonStyle:
66
+ color: "white"
67
+ backgroundColor: "#2563eb"
68
+ padding: "8px 16px"
69
+ border: "none"
70
+ borderRadius: "4px"
71
+ cursor: "pointer"
72
+ - type: div
73
+ attributes:
74
+ style:
75
+ marginTop: "16px"
76
+ padding: "8px"
77
+ borderRadius: "4px"
78
+ content:
79
+ - type: span
80
+ content: ~~.lastAction
81
+ data:
82
+ lastAction: "No action yet"
83
+
84
+ - type: Markdown
85
+ content: |
86
+ ## Key Concepts
87
+
88
+ ### Event Propagation
89
+
90
+ When a user interacts with the button (clicks it), the click event bubbles up through the DOM hierarchy. The wrapper `div` captures this event and executes its actions. This is why the wrapper pattern works - it leverages the natural behavior of DOM events.
91
+
92
+ ### Component Independence
93
+
94
+ The button component inside the wrapper doesn't need to know about the wrapper's actions. It remains a generic, reusable component. The behavior is added at the usage site, not in the component definition.
95
+
96
+ ### DataOverride for Customization
97
+
98
+ Use `dataOverride` to pass different props to the component for different instances. This allows the same component to be customized without modification.
99
+
100
+ - type: Markdown
101
+ content: |
102
+ ## When to Use This Pattern
103
+
104
+ This pattern is ideal when:
105
+
106
+ - ✅ You have reusable components that need different behaviors in different contexts
107
+ - ✅ You want to add event handlers without modifying the component
108
+ - ✅ You need to attach conditional logic or data updates to shared components
109
+ - ✅ You want to keep components generic and add behavior at the usage site
110
+
111
+ **Not suitable when**:
112
+ - ❌ The component itself needs to handle the event (attach actions directly to the component)
113
+ - ❌ You need to prevent event propagation (use `stopPropagation` in actions if needed)
114
+ - ❌ You're using `Phantom` wrapper for DOM events (it doesn't render DOM, so events can't bubble to it)
115
+
116
+ **Note about Phantom**: While `Phantom` cannot handle DOM events, it can still be useful for other purposes like passing values via `dataOverride`, applying non-event actions (tooltips, conditional logic), or when you only need to customize component appearance without event handling.
117
+
118
+ - type: Markdown
119
+ content: |
120
+ ## Limitations and Considerations
121
+
122
+ - **DOM element required for events**: The wrapper must be a DOM element (like `div`) for DOM events to bubble. `Phantom` won't work for DOM events since it doesn't render anything in the DOM. However, `Phantom` can still be useful in other scenarios:
123
+ - **Passing values**: When you only need to pass data via `dataOverride` without event handling
124
+ - **Styling/esthetics**: When you want to apply actions that don't require DOM events (like tooltips, conditional logic, or data manipulations)
125
+ - **Non-event actions**: For actions that don't rely on DOM event listeners
126
+ - **Event propagation**: Events bubble up naturally. If you need to stop propagation, you can use `stopPropagation: true` in your actions.
127
+ - **No sharedUpdates needed**: If the component doesn't need to update parent data, `sharedUpdates` is optional. Only use it if the component needs to modify data that the parent is watching.
128
+
129
+ templates:
130
+
131
+ data:
132
+