pict-section-form 1.0.195 → 1.0.197
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/docs/Comprehensions.md +181 -0
- package/docs/Comprehensions_Advanced.md +295 -0
- package/docs/Pict_Section_Form_Architecture.md +2 -2
- package/docs/Solvers.md +32 -8
- package/docs/_brand.json +18 -0
- package/docs/_cover.md +1 -1
- package/docs/_sidebar.md +2 -0
- package/docs/_version.json +7 -0
- package/docs/examples/README.md +14 -14
- package/docs/index.html +6 -7
- package/docs/input_providers/005-precise-number.md +5 -5
- package/docs/input_providers/009-chart.md +1 -1
- package/docs/input_providers/011-autofill-trigger-group.md +4 -4
- package/docs/input_providers/013-tab-section-selector.md +1 -1
- package/docs/input_providers/README.md +2 -2
- package/docs/retold-catalog.json +71 -1
- package/docs/retold-keyword-index.json +1998 -1984
- package/example_applications/authortopia/html/index.html +5 -5
- package/example_applications/change_tracking/Change-Tracking_Manifest.json +8 -56
- package/example_applications/change_tracking/html/index.html +4 -4
- package/example_applications/complex_table/.claude/launch.json +11 -0
- package/example_applications/complex_table/Complex-Tabular-Application.js +31 -0
- package/example_applications/complex_table/html/index.html +5 -5
- package/example_applications/complex_tuigrid/html/index.html +4 -4
- package/example_applications/dynamic_analysis/html/index.html +7 -7
- package/example_applications/ndt_field_test/html/index.html +9 -9
- package/example_applications/postcard_example/css/postcard.css +12 -12
- package/example_applications/postcard_example/css/pure.min.css +1 -1
- package/example_applications/scope_mathematics/Scope-Mathematics_Manifest.json +1 -1
- package/example_applications/scope_mathematics/html/index.html +4 -4
- package/example_applications/simple_distill/html/index.html +4 -4
- package/example_applications/simple_form/html/index.html +4 -4
- package/example_applications/simple_table/html/index.html +4 -4
- package/package.json +9 -8
- package/source/providers/Pict-Provider-DynamicFormSolverBehaviors.js +126 -0
- package/source/views/Pict-View-Form-Metacontroller.js +8 -0
- package/source/views/support/Pict-Provider-PSF-Support.js +12 -12
- package/test/PictSectionForm-Basic_tests.js +138 -0
- package/docs/css/docuserve.css +0 -73
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Comprehensions
|
|
2
|
+
|
|
3
|
+
The `addComprehensionEntity` solver function builds **multi-context, multi-entity
|
|
4
|
+
comprehensions** from form data — a JSON shape that can be inspected, diffed,
|
|
5
|
+
and pushed to a Meadow REST API via
|
|
6
|
+
[`meadow-integration load_comprehension`](https://github.com/stevenvelozo/meadow-integration).
|
|
7
|
+
|
|
8
|
+
Think of it as the "save side" of a form: a single function call lays down one
|
|
9
|
+
property of one record under one workflow context, and many calls compose into a
|
|
10
|
+
single nested tree that a downstream pipeline can read in one go.
|
|
11
|
+
|
|
12
|
+
## Signature
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
addComprehensionEntity(Context, Entity, GUID, Property, Value)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
| Parameter | Type | Meaning |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `Context` | string (manyfest address) | Workflow bucket — `"OnSave"`, `"OnApprovalAction.Approve"`, etc. Dots create nested branches. |
|
|
21
|
+
| `Entity` | string | The entity name — `"Book"`, `"Recipe"`, `"Fruit"`. Opaque key (not parsed). |
|
|
22
|
+
| `GUID` | string | External GUID for the record. Opaque key (dots are NOT interpreted). |
|
|
23
|
+
| `Property` | string | The field to set on the record. Opaque key. |
|
|
24
|
+
| `Value` | any | The value to write. Strings, numbers, booleans, objects, arrays. |
|
|
25
|
+
|
|
26
|
+
Successive calls to the same `(Context, Entity, GUID)` **accumulate properties**
|
|
27
|
+
on the same record. Successive calls to the same
|
|
28
|
+
`(Context, Entity, GUID, Property)` **overwrite**.
|
|
29
|
+
|
|
30
|
+
## The shape it builds
|
|
31
|
+
|
|
32
|
+
Given these solvers on a Book form:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
"Solvers":
|
|
36
|
+
[
|
|
37
|
+
`addComprehensionEntity("OnSave", "Book", BookGUID, "Title", BookTitle)`,
|
|
38
|
+
`addComprehensionEntity("OnSave", "Book", BookGUID, "Author", BookAuthor)`,
|
|
39
|
+
`addComprehensionEntity("OnSave", "Book", BookGUID, "ISBN", BookISBN)`,
|
|
40
|
+
`addComprehensionEntity("OnApprovalAction.Submit", "Book", BookGUID, "Status", "Submitted")`,
|
|
41
|
+
`addComprehensionEntity("OnApprovalAction.Approve", "Book", BookGUID, "Status", "Approved")`
|
|
42
|
+
]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
…the destination ends up looking like:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"OnSave": {
|
|
50
|
+
"Book": {
|
|
51
|
+
"0x73278432987": {
|
|
52
|
+
"Title": "The Giving Tree",
|
|
53
|
+
"Author": "Shel Silverstein",
|
|
54
|
+
"ISBN": "8675309"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"OnApprovalAction": {
|
|
59
|
+
"Submit": {
|
|
60
|
+
"Book": {
|
|
61
|
+
"0x73278432987": { "Status": "Submitted" }
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"Approve": {
|
|
65
|
+
"Book": {
|
|
66
|
+
"0x73278432987": { "Status": "Approved" }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Every leaf in this tree is a Meadow-shaped record keyed by external GUID — the
|
|
74
|
+
exact format [`load_comprehension`](https://github.com/stevenvelozo/meadow-integration)
|
|
75
|
+
expects.
|
|
76
|
+
|
|
77
|
+
## Where the result lands
|
|
78
|
+
|
|
79
|
+
By default the tree is written to `AppData.FormEntityComprehensions`.
|
|
80
|
+
|
|
81
|
+
The destination is **a manyfest address** resolved against the pict instance, so
|
|
82
|
+
addresses like `AppData.X.Y`, `Bundle.X`, etc. all work. Change it on the
|
|
83
|
+
metacontroller:
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
// At any point after the metacontroller is registered (i.e. inside the
|
|
87
|
+
// application's constructor, after super() has run):
|
|
88
|
+
this.pict.views.PictFormMetacontroller.comprehensionDestinationAddress = 'AppData.MyWorkflowComprehensions';
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
…or pass it in the metacontroller view options if you're constructing the
|
|
92
|
+
metacontroller manually:
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
pict.addView('PictFormMetacontroller', { ComprehensionDestinationAddress: 'AppData.MyWorkflowComprehensions' },
|
|
96
|
+
libPictSectionForm.PictFormMetacontroller);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
If the address resolves to nothing, the function materializes an object there on
|
|
100
|
+
the first write. If it resolves to a non-object scalar, the call logs a warning
|
|
101
|
+
and bails (it won't overwrite a number with an object).
|
|
102
|
+
|
|
103
|
+
## Basic example — flat OnSave context
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
"Sections":
|
|
107
|
+
[
|
|
108
|
+
{
|
|
109
|
+
"Hash": "BookEditor",
|
|
110
|
+
"Solvers":
|
|
111
|
+
[
|
|
112
|
+
`addComprehensionEntity("OnSave", "Book", BookGUID, "Title", BookTitle)`,
|
|
113
|
+
`addComprehensionEntity("OnSave", "Book", BookGUID, "Author", BookAuthor)`,
|
|
114
|
+
`addComprehensionEntity("OnSave", "Book", BookGUID, "Status", "New")`
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
After a solve, `AppData.FormEntityComprehensions.OnSave.Book[<BookGUID>]` holds
|
|
121
|
+
the three properties.
|
|
122
|
+
|
|
123
|
+
## Quick gotchas
|
|
124
|
+
|
|
125
|
+
1. **Empty GUIDs bail.** If any of `Context`, `Entity`, `GUID`, or `Property`
|
|
126
|
+
resolves to `null`, `undefined`, or the empty string, the call logs a warning
|
|
127
|
+
and returns `undefined`. Recipes with an empty `RecipeName` will not silently
|
|
128
|
+
create a `""` bucket — they just no-op until the user fills the name in.
|
|
129
|
+
2. **Solver ordinals.** Solvers run in ascending ordinal order. Put your
|
|
130
|
+
`addComprehensionEntity` calls *after* any solvers they depend on (e.g. after
|
|
131
|
+
the `TotalCalories = SUM(...)` aggregate they read from). The complex_table
|
|
132
|
+
example uses ordinals 200–220 to keep them after the default-ordinal compute
|
|
133
|
+
solvers.
|
|
134
|
+
3. **Re-solves overwrite.** Each solve re-runs every solver, so each
|
|
135
|
+
`addComprehensionEntity` call overwrites the property it wrote last time
|
|
136
|
+
with the current value. This is what you want — the comprehension always
|
|
137
|
+
reflects the current form state.
|
|
138
|
+
4. **The destination is *not* cleared between solves.** If your form removes a
|
|
139
|
+
record (e.g. deletes a row from a grid), the previous comprehension for that
|
|
140
|
+
record stays behind. If that matters for your workflow, reset the destination
|
|
141
|
+
at the start of the solve cycle (`AppData.FormEntityComprehensions = {}`) or
|
|
142
|
+
in `marshalFromView` before the comprehension solvers fire.
|
|
143
|
+
|
|
144
|
+
## Pushing the result
|
|
145
|
+
|
|
146
|
+
Once the comprehension is built, push it via meadow-integration:
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
// Browser-side -- POST the AppData blob directly to the Comprehension/Push REST endpoint.
|
|
150
|
+
fetch('/1.0/Comprehension/Push',
|
|
151
|
+
{
|
|
152
|
+
method: 'POST',
|
|
153
|
+
headers: { 'Content-Type': 'application/json' },
|
|
154
|
+
body: JSON.stringify(
|
|
155
|
+
{
|
|
156
|
+
Comprehension: pict.AppData.FormEntityComprehensions.OnSave,
|
|
157
|
+
GUIDPrefix: 'MYAPP'
|
|
158
|
+
})
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Or write to a file and push from a CLI:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
npx meadow-integration load_comprehension out.json --prefix MYAPP
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
See [meadow-integration: Comprehensions](https://github.com/stevenvelozo/meadow-integration/blob/main/docs/comprehensions.md)
|
|
169
|
+
for the full push semantics — GUID marshaling, foreign-key resolution, batch
|
|
170
|
+
upserts, idempotency.
|
|
171
|
+
|
|
172
|
+
## See also
|
|
173
|
+
|
|
174
|
+
- [Advanced patterns](Comprehensions_Advanced.md) — mixing hashes and direct
|
|
175
|
+
addresses, computed contexts, per-row `MAP VAR` generation, customized
|
|
176
|
+
destinations.
|
|
177
|
+
- [Solvers](Solvers.md) — full solver function reference.
|
|
178
|
+
- The [Complex Table example](../example_applications/complex_table/Complex-Tabular-Application.js)
|
|
179
|
+
builds a complete `RecipeWorkflowComprehensions` tree with `OnSave` and
|
|
180
|
+
`OnApprovalAction.{Submit,Approve}` contexts off the Recipe section and the
|
|
181
|
+
FruitGrid recordset.
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Comprehensions — Advanced patterns
|
|
2
|
+
|
|
3
|
+
This document goes deeper than [Comprehensions](Comprehensions.md):
|
|
4
|
+
|
|
5
|
+
- **Hash vs. address arguments** — when to write `BookGUID` (bare symbol),
|
|
6
|
+
`"AppData.Bundle.BookGUID"` (string address), `getvalue("...")` (explicit
|
|
7
|
+
lookup), and when to mix them.
|
|
8
|
+
- **Computed contexts** — `IF`/ternary results as the `Context` argument so a
|
|
9
|
+
single solver routes between `OnApprovalAction.Submit` and
|
|
10
|
+
`OnApprovalAction.Approve`.
|
|
11
|
+
- **Per-row generation** with `MAP VAR` over a recordset.
|
|
12
|
+
- **Customized destinations** at the metacontroller level.
|
|
13
|
+
- **Resetting between solves**.
|
|
14
|
+
|
|
15
|
+
The complete worked example for everything here lives at
|
|
16
|
+
[`example_applications/complex_table/Complex-Tabular-Application.js`](../example_applications/complex_table/Complex-Tabular-Application.js)
|
|
17
|
+
— if you only read one thing, read that file. This page explains the *why* behind
|
|
18
|
+
the patterns it uses.
|
|
19
|
+
|
|
20
|
+
## Argument resolution — hashes, addresses, and quoted strings
|
|
21
|
+
|
|
22
|
+
The solver expression parser treats each argument to `addComprehensionEntity`
|
|
23
|
+
the same way it would treat any other function argument:
|
|
24
|
+
|
|
25
|
+
| Argument form | What happens |
|
|
26
|
+
|---|---|
|
|
27
|
+
| `BookGUID` | **Bare symbol** — resolved from the form's manifest. Looked up first by descriptor hash, then by address against the marshal destination. |
|
|
28
|
+
| `Record.GUID` / `AppData.Bundle.X` | **Dotted symbol** — resolved as an address (the parser does NOT need quotes around addresses). |
|
|
29
|
+
| `"OnSave"` / `"Book"` | **Quoted string** — taken literally, no resolution. |
|
|
30
|
+
| `getvalue("AppData.X.Y")` | **Explicit lookup** — useful when you want to force address-resolution semantics on a value built up from other solvers. |
|
|
31
|
+
| `IF(...)` / `CONCAT(...)` | **Nested function call** — the inner function's return value becomes the argument. |
|
|
32
|
+
|
|
33
|
+
In practice you mix freely:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
"Solvers":
|
|
37
|
+
[
|
|
38
|
+
// Context and Property are literals; Entity is a literal; GUID and Value
|
|
39
|
+
// are resolved from form data.
|
|
40
|
+
`addComprehensionEntity("OnSave", "Book", BookGUID, "Title", BookTitle)`,
|
|
41
|
+
|
|
42
|
+
// Same shape, but the GUID and Value come from absolute addresses rather
|
|
43
|
+
// than descriptor hashes. Useful if the descriptor hashes haven't been
|
|
44
|
+
// wired up or the data lives outside the form.
|
|
45
|
+
`addComprehensionEntity("OnSave", "Book", AppData.SelectedBook.IDBook, "Status", AppData.SelectedBook.Status)`,
|
|
46
|
+
|
|
47
|
+
// Pull a value through a getvalue() call -- equivalent to the line above
|
|
48
|
+
// but with explicit resolution syntax. Use this form when an inner
|
|
49
|
+
// expression already produces an address string and you want to evaluate it.
|
|
50
|
+
`addComprehensionEntity("OnSave", "Book", getvalue("AppData.SelectedBook.IDBook"), "Status", getvalue("AppData.SelectedBook.Status"))`,
|
|
51
|
+
|
|
52
|
+
// The property name itself is computed. CONCAT returns a string, which
|
|
53
|
+
// becomes the Property argument.
|
|
54
|
+
`addComprehensionEntity("OnSave", "Book", BookGUID, CONCAT("Field_", FieldType), FieldValue)`
|
|
55
|
+
]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Rule of thumb:** quote when you want a literal, leave unquoted when you want
|
|
59
|
+
the parser to look the symbol up. The first three arguments are almost always
|
|
60
|
+
literal strings (Context, Entity name) plus one resolved value (GUID); the
|
|
61
|
+
fourth is almost always a literal Property; the fifth is almost always resolved.
|
|
62
|
+
|
|
63
|
+
## Computed contexts — routing with `IF`
|
|
64
|
+
|
|
65
|
+
The `Context` argument is a manyfest address. It's also just a string that the
|
|
66
|
+
function uses to walk a nested object — which means a *computed* string works
|
|
67
|
+
fine. The complex_table example routes between `OnApprovalAction.Submit` and
|
|
68
|
+
`OnApprovalAction.Approve` based on a `Proprietary` boolean:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
{ Ordinal: 220, Expression:
|
|
72
|
+
`addComprehensionEntity(
|
|
73
|
+
IF(Proprietary, "==", 1, "OnApprovalAction.Submit", "OnApprovalAction.Approve"),
|
|
74
|
+
"Recipe",
|
|
75
|
+
RecipeName,
|
|
76
|
+
"Status",
|
|
77
|
+
IF(Proprietary, "==", 1, "Submitted", "Approved")
|
|
78
|
+
)`
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Both `Submit` and `Approve` branches sit under `OnApprovalAction`, which lets
|
|
83
|
+
downstream code key off `Object.keys(comprehension.OnApprovalAction)` to discover
|
|
84
|
+
which actions fired this solve.
|
|
85
|
+
|
|
86
|
+
The same trick scales to richer routing — e.g. context-per-environment:
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
`addComprehensionEntity(
|
|
90
|
+
CONCAT("OnSave.", EnvironmentName),
|
|
91
|
+
"Recipe", RecipeName, "Status", "Saved"
|
|
92
|
+
)`
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This produces `OnSave.Production.Recipe.<name>.Status` or
|
|
96
|
+
`OnSave.Staging.Recipe.<name>.Status` depending on the value of
|
|
97
|
+
`EnvironmentName`.
|
|
98
|
+
|
|
99
|
+
## Per-row generation with `MAP VAR`
|
|
100
|
+
|
|
101
|
+
`MAP VAR` iterates a recordset and fires the body expression once per row, with
|
|
102
|
+
the row bound to a name you choose (`row` is the convention). Combined with
|
|
103
|
+
`addComprehensionEntity`, this fans one solver across an entire grid:
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
// From complex_table -- the Recipe section's solvers reach into the FruitGrid
|
|
107
|
+
// recordset and emit one OnSave.Fruit.<name>.<property> entry per (fruit, property)
|
|
108
|
+
// pair. Three MAP VARs produce 3 * N comprehension writes in a single solve.
|
|
109
|
+
{ Ordinal: 210, Expression: `MAP VAR row FROM FruitData.FruityVice : addComprehensionEntity("OnSave", "Fruit", row.name, "Family", row.family)` },
|
|
110
|
+
{ Ordinal: 210, Expression: `MAP VAR row FROM FruitData.FruityVice : addComprehensionEntity("OnSave", "Fruit", row.name, "Order", row.order)` },
|
|
111
|
+
{ Ordinal: 210, Expression: `MAP VAR row FROM FruitData.FruityVice : addComprehensionEntity("OnSave", "Fruit", row.name, "Calories", row.nutritions.calories)` }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Inside the body, `row.X.Y` resolves against each row in turn — so you get
|
|
115
|
+
deep-property access without writing per-row solvers.
|
|
116
|
+
|
|
117
|
+
After the solve runs against the bundled FruityVice data, the comprehension at
|
|
118
|
+
`AppData.RecipeWorkflowComprehensions.OnSave.Fruit` looks like:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"Apple": { "Family": "Rosaceae", "Order": "Rosales", "Calories": "52" },
|
|
123
|
+
"Banana": { "Family": "Musaceae", "Order": "Zingiberales", "Calories": "96" },
|
|
124
|
+
"Mango": { "Family": "Anacardiaceae", "Order": "Sapindales", "Calories": "60" },
|
|
125
|
+
...
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
(49 fruits total in the complex_table dataset.)
|
|
130
|
+
|
|
131
|
+
If you need one comprehension write per row that touches *every* property of the
|
|
132
|
+
row, you have two reasonable options:
|
|
133
|
+
|
|
134
|
+
1. Multiple `MAP VAR` solvers, one per property (as above). Clear and easy to
|
|
135
|
+
audit.
|
|
136
|
+
2. A single helper solver function registered on
|
|
137
|
+
`DynamicFormSolverBehaviors` that takes a row + property list and calls
|
|
138
|
+
`addComprehensionEntity` internally. Worth the indirection only if you have
|
|
139
|
+
many entities with many properties; otherwise just write the explicit
|
|
140
|
+
`MAP VAR`s.
|
|
141
|
+
|
|
142
|
+
## Customizing the destination
|
|
143
|
+
|
|
144
|
+
Each metacontroller has a `comprehensionDestinationAddress` property —
|
|
145
|
+
mirroring the existing `viewMarshalDestination` knob — that controls where
|
|
146
|
+
`addComprehensionEntity` writes. The default is `AppData.FormEntityComprehensions`.
|
|
147
|
+
|
|
148
|
+
### Option 1: in the application constructor
|
|
149
|
+
|
|
150
|
+
This is what the [complex_table example](../example_applications/complex_table/Complex-Tabular-Application.js)
|
|
151
|
+
does. After `super()` (which registers the metacontroller view via
|
|
152
|
+
`PictFormApplication`), set the destination directly:
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
class MyWorkflowApplication extends libPictSectionForm.PictFormApplication
|
|
156
|
+
{
|
|
157
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
158
|
+
{
|
|
159
|
+
super(pFable, pOptions, pServiceHash);
|
|
160
|
+
this.pict.views.PictFormMetacontroller.comprehensionDestinationAddress = 'AppData.WorkflowComprehensions';
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Option 2: via metacontroller options
|
|
166
|
+
|
|
167
|
+
If you're registering the metacontroller view yourself (no `PictFormApplication`
|
|
168
|
+
parent), pass `ComprehensionDestinationAddress` in the options:
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
pict.addView(
|
|
172
|
+
'PictFormMetacontroller',
|
|
173
|
+
{ ComprehensionDestinationAddress: 'Bundle.PendingWrites' },
|
|
174
|
+
libPictSectionForm.PictFormMetacontroller
|
|
175
|
+
);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Option 3: change it mid-flight
|
|
179
|
+
|
|
180
|
+
The property is a plain string -- reassign whenever you need different
|
|
181
|
+
destinations for different phases:
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
// Before fanning the form's solvers, redirect to a transient staging slot.
|
|
185
|
+
this.pict.views.PictFormMetacontroller.comprehensionDestinationAddress = 'TempData.PendingComprehension';
|
|
186
|
+
this.pict.PictApplication.solve();
|
|
187
|
+
const tmpPending = this.pict.TempData.PendingComprehension;
|
|
188
|
+
|
|
189
|
+
// Promote / discard / inspect tmpPending however you like.
|
|
190
|
+
|
|
191
|
+
// Switch back to the canonical destination for the next solve.
|
|
192
|
+
this.pict.views.PictFormMetacontroller.comprehensionDestinationAddress = 'AppData.WorkflowComprehensions';
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The destination address is resolved against the pict instance, so any subtree
|
|
196
|
+
works (`AppData.*`, `Bundle.*`, `TempData.*`, ...).
|
|
197
|
+
|
|
198
|
+
## Resetting the tree between solves
|
|
199
|
+
|
|
200
|
+
`addComprehensionEntity` never deletes keys -- it only writes / overwrites. If
|
|
201
|
+
your form workflow expects "only emit comprehensions for *currently visible*
|
|
202
|
+
records," you need to clear the destination at the start of the solve cycle.
|
|
203
|
+
Two patterns:
|
|
204
|
+
|
|
205
|
+
### Pattern A: low-ordinal reset solver
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
"Solvers":
|
|
209
|
+
[
|
|
210
|
+
// Runs before any addComprehensionEntity calls because of the explicit
|
|
211
|
+
// low ordinal. `getvalue` returns a reference to the live AppData branch,
|
|
212
|
+
// but assigning to `AppData.X` rebinds the address. In manyfest assignments
|
|
213
|
+
// we want to nuke the previous tree, so explicitly empty it.
|
|
214
|
+
{ Ordinal: 1, Expression: `AppData.RecipeWorkflowComprehensions = "{}"` },
|
|
215
|
+
// ...then the addComprehensionEntity calls at higher ordinals
|
|
216
|
+
]
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The string `"{}"` becomes `{}` after JSON round-trip on the assignment. If your
|
|
220
|
+
solver dialect doesn't coerce strings to JSON for you, do the reset in
|
|
221
|
+
JavaScript instead:
|
|
222
|
+
|
|
223
|
+
### Pattern B: JS-side reset
|
|
224
|
+
|
|
225
|
+
Override `marshalFromView` or `onBeforeSolve` to zero the destination:
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
class MyWorkflowApplication extends libPictSectionForm.PictFormApplication
|
|
229
|
+
{
|
|
230
|
+
onBeforeSolve()
|
|
231
|
+
{
|
|
232
|
+
this.pict.AppData.RecipeWorkflowComprehensions = {};
|
|
233
|
+
return super.onBeforeSolve();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
This is the simplest version. The `addComprehensionEntity` resolver handles a
|
|
239
|
+
missing-or-emptied destination by re-materializing it on the next write.
|
|
240
|
+
|
|
241
|
+
## Full reference: the complex_table sample config
|
|
242
|
+
|
|
243
|
+
The [complex_table example](../example_applications/complex_table/Complex-Tabular-Application.js)
|
|
244
|
+
exercises every pattern on this page in one application. The relevant pieces:
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
// Constructor sets a customized destination.
|
|
248
|
+
this.pict.views.PictFormMetacontroller.comprehensionDestinationAddress = 'AppData.RecipeWorkflowComprehensions';
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
// Recipe section solvers -- mix of bare-symbol GUID (RecipeName) and address
|
|
253
|
+
// arguments, multiple OnSave properties, MAP VAR fanning over a recordset,
|
|
254
|
+
// and IF-routed OnApprovalAction.
|
|
255
|
+
Solvers:
|
|
256
|
+
[
|
|
257
|
+
// ...prior compute solvers...
|
|
258
|
+
|
|
259
|
+
// OnSave.Recipe.<RecipeName>.<Property> for the recipe-level facts.
|
|
260
|
+
{ Ordinal: 200, Expression: `addComprehensionEntity("OnSave", "Recipe", RecipeName, "Name", RecipeName)` },
|
|
261
|
+
{ Ordinal: 200, Expression: `addComprehensionEntity("OnSave", "Recipe", RecipeName, "Type", RecipeType)` },
|
|
262
|
+
{ Ordinal: 200, Expression: `addComprehensionEntity("OnSave", "Recipe", RecipeName, "Description", RecipeDescription)` },
|
|
263
|
+
{ Ordinal: 200, Expression: `addComprehensionEntity("OnSave", "Recipe", RecipeName, "Inventor", Inventor)` },
|
|
264
|
+
{ Ordinal: 200, Expression: `addComprehensionEntity("OnSave", "Recipe", RecipeName, "TotalCalories", TotalFruitCalories)` },
|
|
265
|
+
{ Ordinal: 200, Expression: `addComprehensionEntity("OnSave", "Recipe", RecipeName, "AverageFatPercent", AverageFatPercent)` },
|
|
266
|
+
|
|
267
|
+
// OnSave.Fruit.<fruit>.<Property> -- per-row via MAP VAR over the FruitGrid recordset.
|
|
268
|
+
{ Ordinal: 210, Expression: `MAP VAR row FROM FruitData.FruityVice : addComprehensionEntity("OnSave", "Fruit", row.name, "Family", row.family)` },
|
|
269
|
+
{ Ordinal: 210, Expression: `MAP VAR row FROM FruitData.FruityVice : addComprehensionEntity("OnSave", "Fruit", row.name, "Order", row.order)` },
|
|
270
|
+
{ Ordinal: 210, Expression: `MAP VAR row FROM FruitData.FruityVice : addComprehensionEntity("OnSave", "Fruit", row.name, "Calories", row.nutritions.calories)` },
|
|
271
|
+
|
|
272
|
+
// OnApprovalAction.{Submit,Approve}.Recipe.<RecipeName> -- computed context.
|
|
273
|
+
{ Ordinal: 220, Expression: `addComprehensionEntity(IF(Proprietary, "==", 1, "OnApprovalAction.Submit", "OnApprovalAction.Approve"), "Recipe", RecipeName, "Status", IF(Proprietary, "==", 1, "Submitted", "Approved"))` },
|
|
274
|
+
{ Ordinal: 220, Expression: `addComprehensionEntity(IF(Proprietary, "==", 1, "OnApprovalAction.Submit", "OnApprovalAction.Approve"), "Recipe", RecipeName, "Reviewer", Inventor)` }
|
|
275
|
+
]
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
After loading the example, fill in the Recipe section and toggle the
|
|
279
|
+
Proprietary checkbox; inspect `_Pict.AppData.RecipeWorkflowComprehensions` in
|
|
280
|
+
the browser console to see the OnSave / OnApprovalAction subtrees update.
|
|
281
|
+
|
|
282
|
+
## Where this stops being the right tool
|
|
283
|
+
|
|
284
|
+
`addComprehensionEntity` is for shaping comprehension trees inside the
|
|
285
|
+
solver. If you're operating outside the solver loop -- e.g. building a
|
|
286
|
+
comprehension from an HTTP response, transforming CSV, or merging two pre-built
|
|
287
|
+
comprehensions -- reach for the meadow-integration toolchain directly:
|
|
288
|
+
|
|
289
|
+
- `meadow-integration csvtransform` to map columns into entity records.
|
|
290
|
+
- `meadow-integration comprehensionintersect` to merge two comprehensions.
|
|
291
|
+
- The `Comprehension` object's `Object.assign` semantics for in-code merges.
|
|
292
|
+
|
|
293
|
+
See [meadow-integration: Comprehensions](https://github.com/stevenvelozo/meadow-integration/blob/main/docs/comprehensions.md)
|
|
294
|
+
for those tools. The solver helper is purpose-built for "as I edit this form,
|
|
295
|
+
build the comprehension that will save it."
|
|
@@ -121,8 +121,8 @@ for export and editing.
|
|
|
121
121
|
|
|
122
122
|
### Marshal Operations
|
|
123
123
|
|
|
124
|
-
- **toView** (`onMarshalToView`): AppData
|
|
125
|
-
- **fromView** (`onMarshalFromView`): Form inputs
|
|
124
|
+
- **toView** (`onMarshalToView`): AppData -> Form inputs
|
|
125
|
+
- **fromView** (`onMarshalFromView`): Form inputs -> AppData
|
|
126
126
|
|
|
127
127
|
The Informary provider handles the actual DOM operations, using data attributes
|
|
128
128
|
to map form elements to their corresponding data addresses.
|
package/docs/Solvers.md
CHANGED
|
@@ -96,13 +96,13 @@ Standard mathematical operators are supported:
|
|
|
96
96
|
|
|
97
97
|
| Function | Description | Example |
|
|
98
98
|
|----------|-------------|---------|
|
|
99
|
-
| `abs(x)` | Absolute value | `abs(-5)`
|
|
100
|
-
| `round(x)` | Round to nearest integer | `round(3.7)`
|
|
101
|
-
| `floor(x)` | Round down | `floor(3.7)`
|
|
102
|
-
| `ceil(x)` | Round up | `ceil(3.2)`
|
|
103
|
-
| `min(a, b)` | Minimum value | `min(5, 3)`
|
|
104
|
-
| `max(a, b)` | Maximum value | `max(5, 3)`
|
|
105
|
-
| `sqrt(x)` | Square root | `sqrt(16)`
|
|
99
|
+
| `abs(x)` | Absolute value | `abs(-5)` -> 5 |
|
|
100
|
+
| `round(x)` | Round to nearest integer | `round(3.7)` -> 4 |
|
|
101
|
+
| `floor(x)` | Round down | `floor(3.7)` -> 3 |
|
|
102
|
+
| `ceil(x)` | Round up | `ceil(3.2)` -> 4 |
|
|
103
|
+
| `min(a, b)` | Minimum value | `min(5, 3)` -> 3 |
|
|
104
|
+
| `max(a, b)` | Maximum value | `max(5, 3)` -> 5 |
|
|
105
|
+
| `sqrt(x)` | Square root | `sqrt(16)` -> 4 |
|
|
106
106
|
|
|
107
107
|
### String Functions
|
|
108
108
|
|
|
@@ -111,7 +111,7 @@ Standard mathematical operators are supported:
|
|
|
111
111
|
| `concat(a, b, ...)` | Concatenate values into a string | `concat('Hello', ' World')` |
|
|
112
112
|
| `join(separator, a, b, ...)` | Join values with a separator | `join(', ', 'a', 'b', 'c')` |
|
|
113
113
|
| `stringgetsegments(s, delimiter)` | Split string into segments | `stringgetsegments('a,b,c', ',')` |
|
|
114
|
-
| `stringcountsegments(s, delimiter)` | Count segments in string | `stringcountsegments('a,b,c', ',')`
|
|
114
|
+
| `stringcountsegments(s, delimiter)` | Count segments in string | `stringcountsegments('a,b,c', ',')` -> 3 |
|
|
115
115
|
| `resolvehtmlentities(s)` | Resolve HTML entities in string | `resolvehtmlentities('&')` |
|
|
116
116
|
|
|
117
117
|
### Array Aggregation
|
|
@@ -222,6 +222,28 @@ aggregate values from arrays:
|
|
|
222
222
|
| `SetTabularRowLength(hash, length)` | Set the row count for a tabular data set |
|
|
223
223
|
| `RefreshTabularSection(hash)` | Refresh a tabular section display |
|
|
224
224
|
|
|
225
|
+
### Comprehension Generation
|
|
226
|
+
|
|
227
|
+
| Function | Description |
|
|
228
|
+
|----------|-------------|
|
|
229
|
+
| `addComprehensionEntity(Context, Entity, GUID, Property, Value)` | Writes a single Property/Value into a nested comprehension tree at `Context -> Entity -> GUID -> Property` in the configured destination (default `AppData.FormEntityComprehensions`). |
|
|
230
|
+
|
|
231
|
+
The `Context` argument is treated as a manyfest address, so dotted contexts
|
|
232
|
+
like `"OnApprovalAction.Approve"` produce nested context branches. Successive
|
|
233
|
+
calls accumulate properties on the same record. See [Comprehensions](Comprehensions.md)
|
|
234
|
+
for the basic walkthrough and [Comprehensions — Advanced](Comprehensions_Advanced.md)
|
|
235
|
+
for computed contexts, `MAP VAR` patterns, and customized destinations.
|
|
236
|
+
|
|
237
|
+
#### Comprehension Example
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
"Solvers": [
|
|
241
|
+
"addComprehensionEntity(\"OnSave\", \"Book\", BookGUID, \"Title\", BookTitle)",
|
|
242
|
+
"addComprehensionEntity(\"OnSave\", \"Book\", BookGUID, \"Author\", BookAuthor)",
|
|
243
|
+
"addComprehensionEntity(\"OnApprovalAction.Approve\", \"Book\", BookGUID, \"Status\", \"Approved\")"
|
|
244
|
+
]
|
|
245
|
+
```
|
|
246
|
+
|
|
225
247
|
### Logging
|
|
226
248
|
|
|
227
249
|
| Function | Description |
|
|
@@ -365,3 +387,5 @@ Use in solvers:
|
|
|
365
387
|
- [Configuration](Configuration.md) - Solver configuration options
|
|
366
388
|
- [Architecture](Pict_Section_Form_Architecture.md) - Solver system internals
|
|
367
389
|
- [Input Types](Input_Types.md) - Input visibility control
|
|
390
|
+
- [Comprehensions](Comprehensions.md) - `addComprehensionEntity` basics
|
|
391
|
+
- [Comprehensions — Advanced](Comprehensions_Advanced.md) - Computed contexts, `MAP VAR`, customized destinations
|
package/docs/_brand.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Hash": "pict-section-form",
|
|
3
|
+
"Name": "Pict Section Form",
|
|
4
|
+
"Tagline": "Configuration-driven dynamic forms with 13+ input types, data marshaling, and mathematical solving",
|
|
5
|
+
"Palette": "mix",
|
|
6
|
+
"Icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 96 96\" width=\"96\" height=\"96\">\n\t\t<defs>\n\t\t\t<clipPath id=\"frame-pict-section-form-filled-light\">\n\t\t\t\t<path d=\"M 2 48\n\t\t\tC 2 18.0222416, 18.0222416 2, 48 2\n\t\t\tC 77.9777584 2, 94 18.0222416, 94 48\n\t\t\tC 94 77.9777584, 77.9777584 94, 48 94\n\t\t\tC 18.0222416 94, 2 77.9777584, 2 48 Z\"/>\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<path d=\"M 2 48\n\t\t\tC 2 18.0222416, 18.0222416 2, 48 2\n\t\t\tC 77.9777584 2, 94 18.0222416, 94 48\n\t\t\tC 94 77.9777584, 77.9777584 94, 48 94\n\t\t\tC 18.0222416 94, 2 77.9777584, 2 48 Z\" fill=\"#ccb526\"/>\n\t\t<g clip-path=\"url(#frame-pict-section-form-filled-light)\"><defs><mask id=\"_cr_34122\"><rect x=\"0\" y=\"0\" width=\"96\" height=\"96\" fill=\"#fff\"/>\n\t\t\t\t\t\t<circle cx=\"64\" cy=\"48\" r=\"32\" fill=\"#000\"/></mask></defs>\n\t\t\t\t\t<circle cx=\"48\" cy=\"48\" r=\"32\" fill=\"rgba(255,255,255,0.18)\" mask=\"url(#_cr_34122)\"/>\n\t\t\t\t\t<circle cx=\"64\" cy=\"48\" r=\"26\" fill=\"#4bacbc\" opacity=\"0.7\"/></g>\n\t\t<text x=\"48\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\"\n\t\t\tfont-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"\n\t\t\tfont-size=\"28\" font-weight=\"700\"\n\t\t\tfill=\"#ffffff\" letter-spacing=\"-1\">PSF</text>\n\t</svg>",
|
|
7
|
+
"IconType": "svg",
|
|
8
|
+
"Favicon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 96 96\" width=\"96\" height=\"96\">\n\t\t<defs>\n\t\t\t<clipPath id=\"fav-pict-section-form-light\">\n\t\t\t\t<path d=\"M 2 48\n\t\t\tC 2 18.0222416, 18.0222416 2, 48 2\n\t\t\tC 77.9777584 2, 94 18.0222416, 94 48\n\t\t\tC 94 77.9777584, 77.9777584 94, 48 94\n\t\t\tC 18.0222416 94, 2 77.9777584, 2 48 Z\"/>\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<path d=\"M 2 48\n\t\t\tC 2 18.0222416, 18.0222416 2, 48 2\n\t\t\tC 77.9777584 2, 94 18.0222416, 94 48\n\t\t\tC 94 77.9777584, 77.9777584 94, 48 94\n\t\t\tC 18.0222416 94, 2 77.9777584, 2 48 Z\" fill=\"#ccb526\"/>\n\t\t<g clip-path=\"url(#fav-pict-section-form-light)\"><circle cx=\"48\" cy=\"48\" r=\"36\" fill=\"rgba(255,255,255,0.22)\"/></g>\n\t\t<text x=\"48\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\"\n\t\t\tfont-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"\n\t\t\tfont-size=\"60\" font-weight=\"800\"\n\t\t\tfill=\"#ffffff\" letter-spacing=\"-1\">P</text>\n\t</svg>",
|
|
9
|
+
"FaviconDark": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 96 96\" width=\"96\" height=\"96\">\n\t\t<defs>\n\t\t\t<clipPath id=\"fav-pict-section-form-dark\">\n\t\t\t\t<path d=\"M 2 48\n\t\t\tC 2 18.0222416, 18.0222416 2, 48 2\n\t\t\tC 77.9777584 2, 94 18.0222416, 94 48\n\t\t\tC 94 77.9777584, 77.9777584 94, 48 94\n\t\t\tC 18.0222416 94, 2 77.9777584, 2 48 Z\"/>\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<path d=\"M 2 48\n\t\t\tC 2 18.0222416, 18.0222416 2, 48 2\n\t\t\tC 77.9777584 2, 94 18.0222416, 94 48\n\t\t\tC 94 77.9777584, 77.9777584 94, 48 94\n\t\t\tC 18.0222416 94, 2 77.9777584, 2 48 Z\" fill=\"#dece70\"/>\n\t\t<g clip-path=\"url(#fav-pict-section-form-dark)\"><circle cx=\"48\" cy=\"48\" r=\"36\" fill=\"rgba(255,255,255,0.22)\"/></g>\n\t\t<text x=\"48\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\"\n\t\t\tfont-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"\n\t\t\tfont-size=\"60\" font-weight=\"800\"\n\t\t\tfill=\"#101418\" letter-spacing=\"-1\">P</text>\n\t</svg>",
|
|
10
|
+
"Colors": {
|
|
11
|
+
"Primary": "#ccb526",
|
|
12
|
+
"Secondary": "#4bacbc",
|
|
13
|
+
"PrimaryLight": "#ccb526",
|
|
14
|
+
"PrimaryDark": "#dece70",
|
|
15
|
+
"SecondaryLight": "#4bacbc",
|
|
16
|
+
"SecondaryDark": "#92c8d0"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/docs/_cover.md
CHANGED
package/docs/_sidebar.md
CHANGED
package/docs/examples/README.md
CHANGED
|
@@ -94,17 +94,17 @@ node ServeExamples.js
|
|
|
94
94
|
|
|
95
95
|
| Feature | simple_form | simple_table | simple_distill | gradebook | postcard | complex_table | complex_tuigrid | manyfest_editor |
|
|
96
96
|
|---------|:-----------:|:------------:|:--------------:|:---------:|:--------:|:-------------:|:---------------:|:---------------:|
|
|
97
|
-
| Basic Inputs |
|
|
98
|
-
| Tabular Layout | |
|
|
99
|
-
| TuiGrid | | | | | | |
|
|
100
|
-
| Solvers |
|
|
101
|
-
| Row Solvers | | | | | |
|
|
102
|
-
| Pick Lists | | |
|
|
103
|
-
| Entity Bundles | | |
|
|
104
|
-
| Trigger Groups | | |
|
|
105
|
-
| localStorage | | | |
|
|
106
|
-
| Custom Themes | | | | |
|
|
107
|
-
| Navigation | | | | |
|
|
108
|
-
| Charts | | | | | |
|
|
109
|
-
| DateTime | | | | | |
|
|
110
|
-
| JSON Editor | | | | | | | |
|
|
97
|
+
| Basic Inputs | [x] | [x] | [x] | [x] | [x] | [x] | [x] | [x] |
|
|
98
|
+
| Tabular Layout | | [x] | | [x] | | [x] | | [x] |
|
|
99
|
+
| TuiGrid | | | | | | | [x] | [x] |
|
|
100
|
+
| Solvers | [x] | | [x] | | | [x] | [x] | |
|
|
101
|
+
| Row Solvers | | | | | | [x] | [x] | |
|
|
102
|
+
| Pick Lists | | | [x] | | | [x] | [x] | |
|
|
103
|
+
| Entity Bundles | | | [x] | | | [x] | | |
|
|
104
|
+
| Trigger Groups | | | [x] | | | [x] | | |
|
|
105
|
+
| localStorage | | | | [x] | | | | [x] |
|
|
106
|
+
| Custom Themes | | | | | [x] | | | |
|
|
107
|
+
| Navigation | | | | | [x] | | | [x] |
|
|
108
|
+
| Charts | | | | | | [x] | | |
|
|
109
|
+
| DateTime | | | | | | [x] | [x] | |
|
|
110
|
+
| JSON Editor | | | | | | | | [x] |
|