@jsenv/navi 0.29.30 → 0.29.32

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.
@@ -46,12 +46,15 @@ Live examples: `src/control/demos/15_group_demo.html`.
46
46
  - **Corners**: the first member loses the radius on the side that joins, the
47
47
  last one loses it on the other side, and any member in between loses all
48
48
  four. A single member keeps its own radius — a group of one looks like the
49
- control alone.
50
- - **Overlap order**: the member under the pointer, and the member holding
51
- focus, paints above its neighbours (`position: relative; z-index: 1`).
52
- Without it the border color change and the focus ring of the active member
53
- would be sliced by whichever neighbour is painted after it. The value is `1`
54
- and there is deliberately no `isolation: isolate` see
49
+ control alone. The ask is made twice, in two forms — see below.
50
+ - **Overlap order**: the member under the pointer, and the member showing a
51
+ focus ring, paint above their neighbours (`position: relative` plus
52
+ `--navi-z-index-control-hovered` / `-focused`). Without it the border color
53
+ change and the focus ring of the active member would be sliced by whichever
54
+ neighbour is painted after it. The focused member is matched whether it wears
55
+ `data-focus-visible` itself or merely contains it — a control that wraps a
56
+ real input (`Picker`, `Spin`) draws the ring on its own frame while the
57
+ keyboard is held inside. There is deliberately no `isolation: isolate` — see
55
58
  [z_index.md](./z_index.md).
56
59
 
57
60
  Nothing else: a group does not restyle its members, does not impose a size,
@@ -59,14 +62,21 @@ and takes any `Box` prop for its own layout.
59
62
 
60
63
  ## Writing a control that belongs in a group
61
64
 
62
- A group squares the corners of its **direct children**it addresses the
63
- control root, never the elements inside it. So a control declares the radius
64
- of its frame on its own root element, and whatever inner element actually
65
- paints the frame takes `border-radius: inherit`:
65
+ A group never writes a selector that reaches inside a member a member's
66
+ subtree holds more than the member (a `Picker` renders its popup inside
67
+ itself, not in a portal; a control carries buttons of its own, like the clear
68
+ cross in a slot), and a rule matching "some descendant" finds all of them. It
69
+ asks for a square corner in two forms instead, and a control answers with
70
+ whichever fits.
71
+
72
+ **The property, on the member itself.** A control declares the radius of its
73
+ frame on its own root, and whatever inner element paints that frame takes
74
+ `border-radius: inherit`. The group sets the property on the root; the frame
75
+ follows.
66
76
 
67
77
  ```css
68
78
  .navi_thing {
69
- /* The radius is declared here even though the frame below draws it */
79
+ /* Declared here even though the box below is what draws it */
70
80
  border-radius: var(--thing-border-radius);
71
81
 
72
82
  .navi_thing_box {
@@ -76,6 +86,36 @@ paints the frame takes `border-radius: inherit`:
76
86
  }
77
87
  ```
78
88
 
89
+ **The custom property, which travels.** A member is not always the control
90
+ that carries the frame: a button can arrive wrapped in a tooltip or a link, at
91
+ any depth. So the group also sets `--x-corner-top-left-radius` and its three
92
+ siblings on the member, and a control that can arrive wrapped reads them as an
93
+ override of its own radius. The `--x-` prefix says what they are: navi's
94
+ internal wiring between a group and its members, not a surface an app writes
95
+ to (an app changes a radius with the `borderRadius` prop, which lands in the
96
+ fallback below and is what the corner keeps everywhere the group has no claim):
97
+
98
+ ```css
99
+ .navi_thing {
100
+ border-top-left-radius: var(
101
+ --x-corner-top-left-radius,
102
+ var(--thing-border-radius)
103
+ );
104
+ /* …and the three others */
105
+ }
106
+ ```
107
+
108
+ **Whoever answers the ask also stops it.** Custom properties inherit all the
109
+ way down, so the control that consumed a corner sets the four back to
110
+ `initial` on the first element inside it — otherwise a button in a slot, or the
111
+ Save button of a form in an open popup, reads a corner meant for the row that
112
+ opened it. `Popover` and `Dialog` stop it too, at their own root: nothing a
113
+ popup holds is at a seam.
114
+
115
+ Reference: `.navi_button_content` in `button_ui.jsx` (a button reads then
116
+ stops), `.navi_picker_right_slot`, `.navi_input_slot`, `.navi_popover`,
117
+ `.navi_dialog`.
118
+
79
119
  A control that declares its radius on an inner element instead is invisible to
80
120
  `Group`: it keeps round corners in the middle of the row, and no rule written
81
121
  in `group.jsx` can reach it without naming that private class — which is how a
@@ -63,6 +63,7 @@ condition: `{ swipe_right: canArchive && archive }`.
63
63
  | `swipe_left` `swipe_right` `swipe_up` `swipe_down` | a press that travels |
64
64
  | `longpress` | a press held still |
65
65
  | `move` `reorder` `toss` | the element carried, and what letting go means |
66
+ | `grab` | the instant a drag takes hold of it |
66
67
  | `"keyboard:<shortcut>"` | keys, e.g. `"keyboard:ctrl+backspace"` |
67
68
 
68
69
  A name nothing knows how to detect produces a dev warning naming the detectors
@@ -109,17 +110,18 @@ in dev.
109
110
  navi makes the element follow the finger — there is nothing to decide about
110
111
  that — and says where the gesture is up to:
111
112
 
112
- | Written on the element | Meaning |
113
- | ---------------------------------------- | ----------------------------------------- |
114
- | `--swipe-pulled` | how far it has come, signed, in px |
115
- | `--swipe-progress` | the same as a fraction, signed, inherited |
116
- | `[data-swiping="left\|right\|up\|down"]` | which way, while a finger holds it |
117
- | `[data-swipe-past-threshold]` | letting go now would go through with it |
113
+ | Written on the element | Meaning |
114
+ | ---------------------------------------- | --------------------------------------- |
115
+ | `--swipe-pulled` | how far it has come, signed, in px |
116
+ | `--swipe-progress` | the same as a fraction, signed |
117
+ | `[data-swiping="left\|right\|up\|down"]` | which way, while a finger holds it |
118
+ | `[data-swipe-past-threshold]` | letting go now would go through with it |
118
119
 
119
120
  WHAT is revealed behind is yours: navi does not know what putting a row away
120
121
  looks like. A trail is usually a child of the swiped element sized off
121
- `--swipe-pulled` which is what makes those values reachable from CSS at all, a
122
- sibling could not read them.
122
+ `--swipe-pulled`. Both values inherit, so a child reads them; a sibling cannot,
123
+ which is why the trail goes inside — and it travels with the row, since what
124
+ navi translates is the element that declares the gesture.
123
125
 
124
126
  ```css
125
127
  .trail {
@@ -227,6 +229,39 @@ name what moves.
227
229
  | `data-drag-delay` `data-drag-slop` `data-drag-threshold` | when the press becomes a grab |
228
230
  | `data-toss-distance` `data-toss-speed` | how far and how fast counts as a throw |
229
231
 
232
+ ### Saying the grab is acquired: `grab`
233
+
234
+ The three above answer the **release**. Between the press and the release there is
235
+ one instant that counts for the hand: the one where the object stops being pressed
236
+ and starts being held. `grab` is that instant — the same one whichever way the drag
237
+ was entered, a finger held still or a mouse travelled a few pixels.
238
+
239
+ ```jsx
240
+ <Box
241
+ interactions={{
242
+ toss: (event) => remove(event.detail.id),
243
+ grab: () => navigator.vibrate?.(10),
244
+ }}
245
+ />
246
+ ```
247
+
248
+ It matters most where it is least visible. On a screen the held object is under the
249
+ thumb that hides it, so the only feedback available is the one that is felt; without
250
+ it the hand waits, doubts the press was heard, and lets go too early — the whole
251
+ gesture fails, not its decoration. With a mouse the grab is acquired after a few
252
+ pixels and the object has visibly moved, so the answer is already there.
253
+
254
+ Nothing here is about vibration: a sound, a class, a measure are the same moment.
255
+
256
+ `grab` **reports, it does not ask**: what it returns is not waited on, and
257
+ preventing its event does not call the gesture off. And it is not an interaction on
258
+ its own — declared without `move`, `reorder` or `toss` there is no gesture for it to
259
+ be the beginning of, and a dev warning says so. Its detail carries `pointerType` and
260
+ the `gestureInfo`.
261
+
262
+ A `longpress` needs none of this: it already happens at the moment the hold is
263
+ acquired, not at the release.
264
+
230
265
  ### Dressing the clone
231
266
 
232
267
  What the pointer carries is a copy, and a copy of a transparent element is
@@ -399,6 +434,8 @@ container above it does not take the gesture:
399
434
  the registry.
400
435
  - `src/control/interaction/interaction_press.js` — swipes and holds, and what a
401
436
  swipe writes on the element.
437
+ - `src/control/interaction/interaction_drag.js` — `move`, `reorder`, `toss` and
438
+ the `grab` moment.
402
439
  - `src/control/interaction/interaction_keyboard.js`,
403
440
  `interaction_native.js` — the other two detectors.
404
441
  - `src/control/demos/38_interactions_demo.html` — every case above, plus a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.29.30",
3
+ "version": "0.29.32",
4
4
  "type": "module",
5
5
  "description": "Library of components including navigation to create frontend applications",
6
6
  "repository": {