@jsenv/navi 0.29.39 → 0.29.41
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 +102 -1
- package/dist/jsenv_navi.js.map +5 -2
- package/docs/AI_INSTRUCTIONS.md +3 -2
- package/docs/actions.md +37 -0
- package/docs/control_value.md +2 -2
- package/docs/scroll.md +53 -0
- package/package.json +2 -2
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -75,8 +75,9 @@ consistency across the app, not from any single call site.
|
|
|
75
75
|
supported ways to override component styles (props > CSS variables > direct
|
|
76
76
|
rule overrides, in that preference order).
|
|
77
77
|
- `docs/scroll.md` — where scrolling happens: what turns `Box`
|
|
78
|
-
`header`/`body`/`footer` on, `FixedBar` space, `List`'s `scroller`,
|
|
79
|
-
|
|
78
|
+
`header`/`body`/`footer` on, `FixedBar` space, `List`'s `scroller`, scroll
|
|
79
|
+
inside a `Dialog`/`Popover`, and what a scroll does to hover
|
|
80
|
+
(`hoverWhileScrolling`, `isScrolling()`). Read it before writing CSS to make
|
|
80
81
|
something scroll — navi almost certainly already has the prop.
|
|
81
82
|
- `docs/control_value.md` — who holds a control's value: nobody, a bound
|
|
82
83
|
`signal` (two-way, in both directions), or you (`value`/`checked`). What
|
package/docs/actions.md
CHANGED
|
@@ -97,6 +97,43 @@ callback:
|
|
|
97
97
|
</Button>
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
+
## `action` or `uiAction`
|
|
101
|
+
|
|
102
|
+
Both fire when a control's value changes, and they are not two ways of writing
|
|
103
|
+
the same thing:
|
|
104
|
+
|
|
105
|
+
| | `uiAction(value, event)` | `action` |
|
|
106
|
+
| ----------------- | ------------------------------------------ | -------------------------------------------------------------------------- |
|
|
107
|
+
| what it is | a plain callback | an action bound to the control's value |
|
|
108
|
+
| while it runs | nothing | the control is busy (`aria-busy`, its loading state) |
|
|
109
|
+
| if it fails | an unhandled rejection — nothing on screen | an error callout on the control, and the control goes back to what it held |
|
|
110
|
+
| a popup around it | closes | refuses to close until it is done |
|
|
111
|
+
|
|
112
|
+
`uiAction` is a notification: the value has changed, here it is. Use it for what
|
|
113
|
+
cannot fail — logging, moving something else on screen, keeping a local
|
|
114
|
+
variable.
|
|
115
|
+
|
|
116
|
+
Anything that can fail or take time is an `action`, and it does not have to be
|
|
117
|
+
an action instance: a plain function is wrapped into one, bound to the control's
|
|
118
|
+
value, so the callback receives what the control now holds.
|
|
119
|
+
|
|
120
|
+
```jsx
|
|
121
|
+
// ✓ the box shows it is saving, says so if the save fails, and goes back to
|
|
122
|
+
// where it was — nothing to write for any of it
|
|
123
|
+
<Input type="checkbox" action={(visibility) => saveMe({ visibility })} />
|
|
124
|
+
|
|
125
|
+
// ✗ same save, and the user learns nothing: no pending state, and a failure
|
|
126
|
+
// leaves the box showing something the server never accepted
|
|
127
|
+
<Input type="checkbox" uiAction={(visibility) => saveMe({ visibility })} />
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The give-away is an `async` `uiAction`, or one that calls something that writes:
|
|
131
|
+
`uiAction` never waits for what it starts, so nobody is left holding the result.
|
|
132
|
+
|
|
133
|
+
To merely REMEMBER the value rather than send it, neither is the answer: bind a
|
|
134
|
+
signal and drop the callback entirely — see
|
|
135
|
+
[control_value.md](./control_value.md).
|
|
136
|
+
|
|
100
137
|
## Reruns
|
|
101
138
|
|
|
102
139
|
Actions do not stay stale on their own: a resource's `POST` reruns the
|
package/docs/control_value.md
CHANGED
|
@@ -126,7 +126,7 @@ directions; it just has nothing extra to say.
|
|
|
126
126
|
|
|
127
127
|
- [control_group.md](./control_group.md) — several controls reading as one
|
|
128
128
|
framed object
|
|
129
|
-
- [actions.md](./actions.md) — `action`
|
|
130
|
-
error
|
|
129
|
+
- [actions.md](./actions.md#action-or-uiaction) — `action` or `uiAction`: which
|
|
130
|
+
one carries loading and error, and which one carries nothing
|
|
131
131
|
- [popup_open.md](./popup_open.md#escape-cancels-the-other-gestures-keep) — what
|
|
132
132
|
a cancelled popup does to the value inside it
|
package/docs/scroll.md
CHANGED
|
@@ -7,6 +7,7 @@ scrolling area (`Box header/body/footer`, `List`, a popup) are told about it.
|
|
|
7
7
|
- [1. The document scrolls](#1-the-document-scrolls)
|
|
8
8
|
- [2. A part of the document scrolls](#2-a-part-of-the-document-scrolls)
|
|
9
9
|
- [3. A popup scrolls](#3-a-popup-scrolls)
|
|
10
|
+
- [Hover while scrolling](#hover-while-scrolling)
|
|
10
11
|
- [The list border](#the-list-border)
|
|
11
12
|
|
|
12
13
|
## What makes header/body/footer work: the overflow
|
|
@@ -236,6 +237,58 @@ changes screen mid-reading.
|
|
|
236
237
|
Reference: `src/layout/dialog.jsx`, `src/layout/popover.jsx`,
|
|
237
238
|
`src/layout/slide_container.jsx`.
|
|
238
239
|
|
|
240
|
+
## Hover while scrolling
|
|
241
|
+
|
|
242
|
+
A scroll moves the content under a pointer that does not move. The browser
|
|
243
|
+
reports that as hover: it fires `mouseleave` + `mouseenter` for **every element
|
|
244
|
+
crossing the cursor** — a dozen per wheel tick. None of it was asked for; the
|
|
245
|
+
user asked to scroll.
|
|
246
|
+
|
|
247
|
+
It is free as long as hover only paints a background. It stops being free the
|
|
248
|
+
moment hover triggers real work — a highlight somewhere else in the tree, a
|
|
249
|
+
prefetch, a map redrawing a layer — because that work then lands on the main
|
|
250
|
+
thread exactly while a scroll animation is running, and the scroll stutters.
|
|
251
|
+
|
|
252
|
+
### The fact is in the DOM: `navi-scrolling`
|
|
253
|
+
|
|
254
|
+
While an element scrolls it carries `navi-scrolling`, written by one capturing
|
|
255
|
+
listener on the document (`scroll` does not bubble, but it does propagate in
|
|
256
|
+
the capture phase) and removed once it has been quiet for a moment — scroll
|
|
257
|
+
events stop before the movement does. Nothing subscribes to anything: whoever
|
|
258
|
+
is concerned says so in CSS.
|
|
259
|
+
|
|
260
|
+
```css
|
|
261
|
+
/* my rows answer the pointer only when nothing is moving them */
|
|
262
|
+
[navi-scrolling] .my_row {
|
|
263
|
+
pointer-events: none;
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`pointer-events` is what does the work, and it does the whole of it: enter,
|
|
268
|
+
move and leave at once, in the browser, at no cost per element. Hand-written in
|
|
269
|
+
JS the same suppression takes three handlers — once `mouseenter` has been
|
|
270
|
+
swallowed the pointer is already inside the element, so only `mousemove` can
|
|
271
|
+
ever bring the hover back.
|
|
272
|
+
|
|
273
|
+
The page scroll carries the attribute on `document.scrollingElement`, so an
|
|
274
|
+
ancestor rule covers it too. In JS the same fact reads as `isScrolling()` /
|
|
275
|
+
`isScrolling(element)`, or `scrollActivitySignal` to react to it.
|
|
276
|
+
|
|
277
|
+
### In a `List`: nothing to do
|
|
278
|
+
|
|
279
|
+
`List` rows leave hit-testing while anything scrolling them moves — its own
|
|
280
|
+
scroll box, the panel around it, the page.
|
|
281
|
+
|
|
282
|
+
```jsx
|
|
283
|
+
<List hoverWhileScrolling> {/* opt back in */}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The default costs one thing, and it is the honest half of the same trade: right
|
|
287
|
+
after a scroll, the row under the pointer lights up only once the pointer moves
|
|
288
|
+
by a pixel.
|
|
289
|
+
|
|
290
|
+
Reference: `src/utils/scroll_activity.js`.
|
|
291
|
+
|
|
239
292
|
## The list border
|
|
240
293
|
|
|
241
294
|
Not scroll, but the same family of problem — a reasonable default nobody knows
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/navi",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.41",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Library of components including navigation to create frontend applications",
|
|
6
6
|
"repository": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"prepublishOnly": "npm run build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@jsenv/dom": "0.17.
|
|
32
|
+
"@jsenv/dom": "0.17.14",
|
|
33
33
|
"@jsenv/humanize": "1.7.8",
|
|
34
34
|
"@jsenv/validity": "0.4.2"
|
|
35
35
|
},
|