@jsenv/navi 0.29.89 → 0.29.91
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/dist/jsenv_navi.js +964 -84
- package/dist/jsenv_navi.js.map +92 -19
- package/docs/AI_INSTRUCTIONS.md +19 -0
- package/docs/actions.md +10 -0
- package/docs/control_object.md +13 -1
- package/docs/control_value.md +23 -0
- package/package.json +1 -1
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -149,6 +149,25 @@ consistency across the app, not from any single call site.
|
|
|
149
149
|
and what a nameless one merges, and what a `<Picker type="object">` needs in
|
|
150
150
|
its popup (one group, not two controls). Read it before making one value out
|
|
151
151
|
of several controls, and before putting anything in a picker popup.
|
|
152
|
+
- A time of day, and a span between two of them, come as a pair of components
|
|
153
|
+
and the choice between them is about the GESTURE:
|
|
154
|
+
`TimeSpin`/`TimeRangeSpin` (`src/control/picker/preset/spin_time.jsx`) are
|
|
155
|
+
fields one types in, and `TimeWheel`/`TimeRangeWheel`
|
|
156
|
+
(`src/control/wheel/wheel_time.jsx`) are wheels one turns. Both carry a single
|
|
157
|
+
`"HH:MM"` (or `{ start, end }` for a span), so a form holds one field either
|
|
158
|
+
way. Prefer the wheels whenever a half-written value would be nonsense — a
|
|
159
|
+
time typed digit by digit goes through states that are not times ("1" on its
|
|
160
|
+
way to "18"), each of them bounded and corrected under the fingers, while a
|
|
161
|
+
wheel only ever shows values that exist. Two things only the wheels have:
|
|
162
|
+
the bounds of a span PUSH each other while they turn (`minDuration`) instead
|
|
163
|
+
of being refused at send, and `placeholder` is a position shown without being
|
|
164
|
+
an answer — for a span that is optional ("any time of day") on wheels that
|
|
165
|
+
have no blank row to land on. A clear (or a value written as `undefined`) puts
|
|
166
|
+
such a pair back on its placeholder and back to answering nothing, so a row's
|
|
167
|
+
cross means what it says. `hours` bounds what the wheels offer
|
|
168
|
+
(`{ min: 7, max: 21 }`, or the list itself) — rows nobody can land on are rows
|
|
169
|
+
in the way. Read this before writing a time input, and
|
|
170
|
+
before making a pair of wheels say "nothing set" by hand.
|
|
152
171
|
- `docs/control_group.md` — `<Group>`: several controls reading as one framed
|
|
153
172
|
object (one border per seam, radius on the outer corners only). Read it
|
|
154
173
|
before placing bordered controls against each other, and before writing
|
package/docs/actions.md
CHANGED
|
@@ -72,6 +72,16 @@ const userAction = getUser.bindParams({ id: userIdSignal });
|
|
|
72
72
|
| `reset()` | Aborts what is running and puts the action back to idle, data and all. |
|
|
73
73
|
| `abort()` | Calls off the run in flight, keeping the data it had. |
|
|
74
74
|
|
|
75
|
+
### Aborting saves resources, it does not undo
|
|
76
|
+
|
|
77
|
+
`abort()` cancels what can still be cancelled — a `fetch` wired to the
|
|
78
|
+
callback's `signal` — and nothing more. The server may have done the work
|
|
79
|
+
before the cancellation reached it, or may not honor cancellations at all:
|
|
80
|
+
whether the work happened is known from the run's settlement alone. For that
|
|
81
|
+
reason a run's promise settles only when its callback settles, even after an
|
|
82
|
+
abort, and anything sequenced behind a run — an optimistic control's queued
|
|
83
|
+
request, for instance — waits for that settlement, never for the abort.
|
|
84
|
+
|
|
75
85
|
## Reading an action
|
|
76
86
|
|
|
77
87
|
```jsx
|
package/docs/control_object.md
CHANGED
|
@@ -146,7 +146,19 @@ are not all there yet:
|
|
|
146
146
|
or once a child really acts;
|
|
147
147
|
- a child arriving **after** the value did is placed from what the group holds,
|
|
148
148
|
so it shows its part of it. A child arriving with something of its own to show
|
|
149
|
-
is answering, and keeps it.
|
|
149
|
+
is answering, and keeps it. A child bound to a `signal` is placed like any
|
|
150
|
+
other — bound is not frozen — and the placement writes the signal, so an
|
|
151
|
+
`<Input type="hidden" signal>` carrying a piece of the answer shows what the
|
|
152
|
+
group holds. Only a child controlled by a `value`/`checked` prop is left
|
|
153
|
+
alone: its owner decides.
|
|
154
|
+
|
|
155
|
+
A group with an `aggregateChildStates` of its own is outside all of this: what
|
|
156
|
+
it returns is taken as the truth, `undefined` included. That is the one way such
|
|
157
|
+
a group says **"I have nothing to say yet"** — return `undefined` while the
|
|
158
|
+
children it needs are missing, and navi leaves the value alone instead of
|
|
159
|
+
publishing an empty shape upward. A group written to always return its keys
|
|
160
|
+
(`{ mode: undefined, levels: [] }`) is claiming an answer nobody gave, and that
|
|
161
|
+
claim overwrites the row above it the moment the popup opens.
|
|
150
162
|
|
|
151
163
|
Together they are what makes the two-hop case work — the form fills the row, the
|
|
152
164
|
row fills the control in its popup — whatever order the pieces turn up in. Get
|
package/docs/control_value.md
CHANGED
|
@@ -10,6 +10,7 @@ somewhere else in the app.
|
|
|
10
10
|
- [What a signal holds, control by control](#what-a-signal-holds-control-by-control)
|
|
11
11
|
- [Empty keeps the shape of the question](#empty-keeps-the-shape-of-the-question)
|
|
12
12
|
- [Which controls take a `signal`](#which-controls-take-a-signal)
|
|
13
|
+
- [The PROP is what controls, not its value](#the-prop-is-what-controls-not-its-value)
|
|
13
14
|
- [`value` and `signal` exclude each other](#value-and-signal-exclude-each-other)
|
|
14
15
|
- [A `stateSignal` brings more than a value](#a-statesignal-brings-more-than-a-value)
|
|
15
16
|
|
|
@@ -160,6 +161,28 @@ own `selected` — but not expect the two to arbitrate. An item that declares
|
|
|
160
161
|
it. On screen that reads as a list where clicking does nothing, so navi says it
|
|
161
162
|
in dev the moment the two claims meet: bind one end or the other, not both.
|
|
162
163
|
|
|
164
|
+
## The PROP is what controls, not its value
|
|
165
|
+
|
|
166
|
+
`value` (and `checked`, and a row's `selected`) makes a control controlled by
|
|
167
|
+
being **there**. Its value is a separate question: `value={undefined}` says "I
|
|
168
|
+
hold this one, and right now it holds nothing" — the control shows nothing, and
|
|
169
|
+
nothing else can fill it. That is a real state and it is the one asked for.
|
|
170
|
+
|
|
171
|
+
The trap is the shorthand for an optional prop:
|
|
172
|
+
|
|
173
|
+
```jsx
|
|
174
|
+
// WRONG — the key is always there, so the control is always controlled,
|
|
175
|
+
// and the group above it can never fill it
|
|
176
|
+
<Input checked={bound ? undefined : checked} />
|
|
177
|
+
|
|
178
|
+
// RIGHT — the prop is there only when you are the one answering
|
|
179
|
+
<Input {...(bound ? {} : { checked })} />
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Passing a prop the code knows will always be `undefined` is the same mistake
|
|
183
|
+
written once instead of twice: leave it out. navi says so in dev when a group
|
|
184
|
+
tries to place a child that has claimed itself this way.
|
|
185
|
+
|
|
163
186
|
## `value` and `signal` exclude each other
|
|
164
187
|
|
|
165
188
|
`value` (or `checked`) says "you hold it", `signal` says "the signal holds it".
|