@jsenv/navi 0.29.78 → 0.29.80
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 +490 -347
- package/dist/jsenv_navi.js.map +10 -9
- package/docs/AI_INSTRUCTIONS.md +8 -0
- package/docs/autofocus.md +115 -0
- package/docs/popup_open.md +3 -0
- package/package.json +1 -1
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -110,6 +110,14 @@ consistency across the app, not from any single call site.
|
|
|
110
110
|
`smallTouchScreenSignal` in an app to change a dialog's size, before passing
|
|
111
111
|
`expandX={false}` to stop a dialog sprawling, and before writing CSS to make a
|
|
112
112
|
dialog fit the screen.
|
|
113
|
+
- `docs/autofocus.md` — who gets the keyboard when a popup opens or a slide
|
|
114
|
+
arrives: the ladder navi walks, what `autoFocus` means on a surface (`true`
|
|
115
|
+
= the surface takes it, for a popup that is READ before it is filled) versus
|
|
116
|
+
on a field, why a docked dialog keeps the keyboard down on a phone, and what
|
|
117
|
+
happens when the opening finds nothing to focus. Read it before wondering
|
|
118
|
+
where the focus went when a popup opened, and before removing `autoFocus`
|
|
119
|
+
from fields to stop a virtual keyboard rising — the surface is what decides,
|
|
120
|
+
not the absence of a field asking.
|
|
113
121
|
- `docs/scroll.md` — where scrolling happens: what turns `Box`
|
|
114
122
|
`header`/`body`/`footer` on, `FixedBar` space, `List`'s `scroller`, scroll
|
|
115
123
|
inside a `Dialog`/`Popover`, and what a scroll does to hover
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Where the keyboard goes when something opens
|
|
2
|
+
|
|
3
|
+
A dialog, a popover, a slide arriving: one of them opens and something inside
|
|
4
|
+
has to hold the keyboard. `autoFocus` is how each element takes part in that
|
|
5
|
+
decision.
|
|
6
|
+
|
|
7
|
+
- [What we want](#what-we-want)
|
|
8
|
+
- [The ladder](#the-ladder)
|
|
9
|
+
- [A popup that is read before it is filled](#a-popup-that-is-read-before-it-is-filled)
|
|
10
|
+
- [The most precise wins](#the-most-precise-wins)
|
|
11
|
+
- [Docked on a small touch screen: fields are withdrawn](#docked-on-a-small-touch-screen-fields-are-withdrawn)
|
|
12
|
+
- [What a field says about itself](#what-a-field-says-about-itself)
|
|
13
|
+
- [When the opening places nothing](#when-the-opening-places-nothing)
|
|
14
|
+
|
|
15
|
+
## What we want
|
|
16
|
+
|
|
17
|
+
The focus is where the user is. So an opening has to answer one question — what
|
|
18
|
+
did the user come here to do? — and the answer is rarely "type": a picker opens
|
|
19
|
+
on its search box, but a popup that explains something opens on the explanation.
|
|
20
|
+
|
|
21
|
+
On a phone the difference is not a nuance. Focusing a field raises the virtual
|
|
22
|
+
keyboard, the keyboard takes a third of the height, and the popup scrolls the
|
|
23
|
+
focused field into what is left. Everything above it — the title, the sentence
|
|
24
|
+
saying why the field is asked for — is already past the top edge when the user
|
|
25
|
+
first looks at the popup. Nobody scrolls back up to read what they were never
|
|
26
|
+
shown, so a popup that opens on its field is a popup whose text does not exist.
|
|
27
|
+
|
|
28
|
+
Hence the rule: **the surface is read, then touched.** The keyboard rises when
|
|
29
|
+
the user asks for it, or when a field says it is what the user came for.
|
|
30
|
+
|
|
31
|
+
## The ladder
|
|
32
|
+
|
|
33
|
+
Whoever hands out the focus — a popup opening, a slide arriving — tries these
|
|
34
|
+
in order, and stops at the first that leads somewhere focusable:
|
|
35
|
+
|
|
36
|
+
1. the element that held the focus when this container was last closed;
|
|
37
|
+
2. the first `autoFocus` — "put it here";
|
|
38
|
+
3. the first focusable element — what one came to do;
|
|
39
|
+
4. the deepest `autoFocus="last-resort"`, the container itself included;
|
|
40
|
+
5. nothing, and the caller decides what that means.
|
|
41
|
+
|
|
42
|
+
Step 1 is why reopening a popup comes back to where the user was, rather than to
|
|
43
|
+
what the content asks for on a fresh open.
|
|
44
|
+
|
|
45
|
+
## A popup that is read before it is filled
|
|
46
|
+
|
|
47
|
+
`autoFocus` (the plain boolean `true`) on the `Dialog`/`Popover` itself:
|
|
48
|
+
|
|
49
|
+
```jsx
|
|
50
|
+
<Dialog autoFocus>
|
|
51
|
+
<Heading>Almost there</Heading>
|
|
52
|
+
<Text>We need a first name so the others know who joined.</Text>
|
|
53
|
+
<Input name="first_name" />
|
|
54
|
+
</Dialog>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The focus lands on the surface, which is focusable for exactly this
|
|
58
|
+
(`tabIndex={-1}`). No keyboard rises, nothing is scrolled, and Tab starts from
|
|
59
|
+
the beginning of the reading order — the user reads, then reaches the field by
|
|
60
|
+
the route the content lays out.
|
|
61
|
+
|
|
62
|
+
This is the value to reach for whenever the popup's first job is to say
|
|
63
|
+
something. It is not the same as `"last-resort"`, which is the default and means
|
|
64
|
+
the opposite: "anything in here before me".
|
|
65
|
+
|
|
66
|
+
## The most precise wins
|
|
67
|
+
|
|
68
|
+
`autoFocus` on a field beats `autoFocus` on the surface around it (step 2 above
|
|
69
|
+
comes before step 4). The two can be stated together without a conflict to
|
|
70
|
+
resolve: the surface says where the focus goes by default, a field that really
|
|
71
|
+
is what the user came for says so itself.
|
|
72
|
+
|
|
73
|
+
```jsx
|
|
74
|
+
<Dialog autoFocus>
|
|
75
|
+
<Text>Search the catalog</Text>
|
|
76
|
+
<Input name="query" autoFocus /> {/* … except here */}
|
|
77
|
+
</Dialog>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Docked on a small touch screen: fields are withdrawn
|
|
81
|
+
|
|
82
|
+
A `Dialog` with `dockedOnSmallTouchScreen` becomes a bottom sheet on a phone —
|
|
83
|
+
the one shape the keyboard hurts most, since the sheet starts at the very edge
|
|
84
|
+
the keyboard covers. There, step 3 of the ladder does not consider fields at
|
|
85
|
+
all: rather than the first text input it finds, the opening falls through to
|
|
86
|
+
what remains, usually the surface itself.
|
|
87
|
+
|
|
88
|
+
Nothing to pass, and nothing to remember per call site. A field that wants the
|
|
89
|
+
keyboard on a phone still says so with its own `autoFocus`, which is where that
|
|
90
|
+
decision belongs.
|
|
91
|
+
|
|
92
|
+
## What a field says about itself
|
|
93
|
+
|
|
94
|
+
- `autoFocus` — "I am what the user came for". A picker's search box on a
|
|
95
|
+
desktop, the one field of a one-field popup.
|
|
96
|
+
- `autoFocus="restore"` — "never on a fresh open, but bring me back". A field
|
|
97
|
+
the user was typing in when a popup over it closed: reopening returns to it,
|
|
98
|
+
opening for the first time does not raise a keyboard on it.
|
|
99
|
+
- `autoFocus="last-resort"` — "anything else in here before me". Said by a poor
|
|
100
|
+
place to arrive that is still better than nowhere: a close button, a chevron.
|
|
101
|
+
A container says it about its own contents, which is the default for
|
|
102
|
+
`Dialog`/`Popover`.
|
|
103
|
+
|
|
104
|
+
## When the opening places nothing
|
|
105
|
+
|
|
106
|
+
A popup can open on content that holds nothing focusable yet — content still
|
|
107
|
+
being built, a screen not yet interactive. The ladder then comes back empty and
|
|
108
|
+
the opening places no focus at all.
|
|
109
|
+
|
|
110
|
+
What arrives a moment later is allowed to take it: an opening that placed
|
|
111
|
+
nothing owes the focus to whatever appears next, and an `autoFocus` in that
|
|
112
|
+
content is honored rather than deferring to a transfer that never happened.
|
|
113
|
+
Without this, the same popup would land the focus in a different place — or
|
|
114
|
+
nowhere — depending on whether it was opened by a click or by the page loading,
|
|
115
|
+
which is the same popup behaving differently for no reason the user can see.
|
package/docs/popup_open.md
CHANGED
|
@@ -13,6 +13,9 @@ What opens a `Dialog` or a `Popover`, and who owns the fact that it is open.
|
|
|
13
13
|
- [When `open` is the right answer, and what it costs](#when-open-is-the-right-answer-and-what-it-costs)
|
|
14
14
|
- [What the popup holds while it is closed](#what-the-popup-holds-while-it-is-closed)
|
|
15
15
|
|
|
16
|
+
Where the focus goes once it is open is its own subject — see
|
|
17
|
+
[autofocus.md](./autofocus.md).
|
|
18
|
+
|
|
16
19
|
## The popup owns its open state
|
|
17
20
|
|
|
18
21
|
A `Dialog`/`Popover` with no `open` prop keeps its own open state and listens
|