@jsenv/navi 0.29.85 → 0.29.87

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/autofocus.md CHANGED
@@ -82,17 +82,24 @@ itself.
82
82
 
83
83
  ## On a touch device: the surface is what one arrives on
84
84
 
85
- Where the keyboard is a virtual one — anything answering `pointer: coarse` — a
86
- popup opening drops step 3 of the ladder entirely: the focus goes where
87
- something ASKED for it, and otherwise to the surface itself.
88
-
89
- The condition is the device, not the popup's shape and not the gesture that
90
- opened it. A virtual keyboard is a fact about the screen: it costs a third of
91
- the height whichever popup raised it, and a popup opened by the page loading —
92
- no pointer in it at all is exactly the one that must not be answered "no
93
- keyboard here". Docking only makes the cost more visible (a bottom sheet is
94
- short, so there is less room to lose before the title goes), it is not what
95
- creates it.
85
+ Where the keyboard is a virtual one — anything answering `pointer: coarse` — an
86
+ arrival drops step 3 of the ladder entirely: the focus goes where something
87
+ ASKED for it, and otherwise to the surface itself.
88
+
89
+ Every arrival, not just a popup opening. A slide travelling in (so a
90
+ `RouteTravel` screen too) hands out the focus the same way and loses the same
91
+ thing by landing on the first focusable more of it, even, a screen having more
92
+ above the fold than a popup. Its surface is the `SlideContainer` box, which is
93
+ what takes the keyboard when the slide holds nothing that can, so the arrows
94
+ keep working from there.
95
+
96
+ The condition is the device, not the shape of what arrives and not the gesture
97
+ that brought it. A virtual keyboard is a fact about the screen: it costs a third
98
+ of the height whatever raised it, and an arrival with no pointer in it at all —
99
+ a popup opened by the page loading, a travel asked for by code — is exactly the
100
+ one that must not be answered "no keyboard here". Docking only makes the cost
101
+ more visible (a bottom sheet is short, so there is less room to lose before the
102
+ title goes), it is not what creates it.
96
103
 
97
104
  Withdrawing only the FIELDS would not be enough either. The first focusable is
98
105
  wherever the content happens to put it — and in a popup that explains before it
@@ -105,7 +112,8 @@ Nothing to pass, and nothing to remember per call site.
105
112
 
106
113
  ### Opting a field back in
107
114
 
108
- Some popups really are opened to type in: one comment box, one rename field.
115
+ Some popups — and some screens — really are opened to type in: one comment box,
116
+ one rename field.
109
117
  There, the field says so itself, and that beats the device — step 2 of the
110
118
  ladder comes before step 3 was ever skipped.
111
119
 
@@ -11,6 +11,7 @@ whose value is an object needs in its popup.
11
11
  - [`<Form>`: the shape, plus a send](#form-the-shape-plus-a-send)
12
12
  - [Naming, and what a nameless group does](#naming-and-what-a-nameless-group-does)
13
13
  - [A picker whose value is an object](#a-picker-whose-value-is-an-object)
14
+ - [One line, one key](#one-line-one-key)
14
15
  - [A settings sheet](#a-settings-sheet)
15
16
  - [`Group` is not `ControlGroup`](#group-is-not-controlgroup)
16
17
 
@@ -113,6 +114,39 @@ Two things to get right:
113
114
  kind a scalar type and there is no group to distribute anything — the whole
114
115
  object lands on one control, which is how `"[object Object]"` ends up in a
115
116
  url.
117
+ - **A control that helps FIND the answer is not the answer.** A search box above
118
+ a long list, a "select all" beside it: they are tools, and a tool says so with
119
+ `allowNameless`. The picker then walks past it and talks to the list, which is
120
+ what a popup made of one choice and the means to reach it needs.
121
+
122
+ ```jsx
123
+ <Picker name="place_ids" type="array">
124
+ <Input allowNameless placeholder="chercher" navi-list="places" />
125
+ <List id="places" selectable multiple>
126
+
127
+ </List>
128
+ </Picker>
129
+ ```
130
+
131
+ ## One line, one key
132
+
133
+ A row that opens a popup is one control, so what it answers arrives under its
134
+ one name. A row answering two questions at once — where the level comes from
135
+ AND which levels, when it starts AND when it ends — hands back a sub-object:
136
+
137
+ ```jsx
138
+ <Picker name="level" type="object"> // { level: { level_mode, levels } }
139
+ <Picker name="hours" type="object"> // { hours: { from_minute, to_minute } }
140
+ ```
141
+
142
+ Flattening those into the sent object is the caller's business
143
+ (`{ ...value.level, ...value.hours }`), and it is the only reasonable place for
144
+ it: a picker with no name of its own could not be collected, and one merging its
145
+ keys into the object around it would take the row's own identity away — nothing
146
+ would say which row a key came from, nor which row to put an incoming value
147
+ back on. A `<ControlGroup>` with no name IS that merge, and it exists for the
148
+ case where there is no row: several controls in one screen, no door between
149
+ them.
116
150
 
117
151
  ## A settings sheet
118
152
 
@@ -8,6 +8,7 @@ somewhere else in the app.
8
8
  - [A bound signal works in both directions](#a-bound-signal-works-in-both-directions)
9
9
  - [`signal` + `defaultValue`: the answer and where it starts](#signal--defaultvalue-the-answer-and-where-it-starts)
10
10
  - [What a signal holds, control by control](#what-a-signal-holds-control-by-control)
11
+ - [Empty keeps the shape of the question](#empty-keeps-the-shape-of-the-question)
11
12
  - [Which controls take a `signal`](#which-controls-take-a-signal)
12
13
  - [`value` and `signal` exclude each other](#value-and-signal-exclude-each-other)
13
14
  - [A `stateSignal` brings more than a value](#a-statesignal-brings-more-than-a-value)
@@ -117,6 +118,31 @@ writes it, and what they change is written back into it. One signal for a screen
117
118
  whose values arrive together — see
118
119
  [create_and_edit.md](./create_and_edit.md#two-screens-two-states).
119
120
 
121
+ ## Empty keeps the shape of the question
122
+
123
+ What a control is worth when it holds nothing has the type of what it holds when
124
+ it holds something. A list of days nobody picked is `[]`, not `""`:
125
+
126
+ | control | empty is |
127
+ | ------------------------------------------------- | -------- |
128
+ | `Picker type="array"`, `List selectable multiple` | `[]` |
129
+ | `Picker type="object"` | `{}` |
130
+ | text/number/date `Input`, `Select` | `""` |
131
+ | `Input type="checkbox" value={true}` | `false` |
132
+ | radio, checkbox holding a value of its own | absent |
133
+
134
+ This is what a clear (`--navi-clear`, a row's cross) leaves behind and what the
135
+ object around it carries, so the conversion nobody writes — `value.days || []` —
136
+ is not needed, and not needed only after having watched the wrong shape reach
137
+ the server.
138
+
139
+ A checkbox is a member of a set, the way HTML has it: checked it carries its
140
+ `value` (`"on"` when it was given none), unchecked it carries nothing at all —
141
+ which is what lets several checkboxes sharing a name aggregate into an array. A
142
+ checkbox that is a yes/no rather than a member says so with `value={true}`, and
143
+ is then `true` or `false`. Its bound `signal` holds the boolean either way (see
144
+ the table above): what a signal on a checkbox is about is whether it is checked.
145
+
120
146
  ## Which controls take a `signal`
121
147
 
122
148
  All of them: `Input` (every type), `Picker`, `Select`, `Wheel`, `Spin`,
@@ -131,7 +157,8 @@ something else read where the slides are — or move them by writing it.
131
157
  Inside a `List selectable` you can bind the list, or give each `List.Item` its
132
158
  own `selected` — but not expect the two to arbitrate. An item that declares
133
159
  `selected` is answering for itself, and the list's signal does not reposition
134
- it.
160
+ it. On screen that reads as a list where clicking does nothing, so navi says it
161
+ in dev the moment the two claims meet: bind one end or the other, not both.
135
162
 
136
163
  ## `value` and `signal` exclude each other
137
164
 
@@ -168,7 +168,10 @@ row whose value is carried by a hidden input beside it) says so with
168
168
  <Picker allowNameless ui={…}>
169
169
  ```
170
170
 
171
- It is then neither collected nor complained about.
171
+ It is then neither collected nor complained about — and inside a picker's popup
172
+ it is not the control the picker talks to either, which is what lets a search
173
+ box sit above the list that IS the answer (see
174
+ [control_object.md](./control_object.md#a-picker-whose-value-is-an-object)).
172
175
 
173
176
  ## See also
174
177
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.29.85",
3
+ "version": "0.29.87",
4
4
  "type": "module",
5
5
  "description": "Library of components including navigation to create frontend applications",
6
6
  "repository": {