@jsenv/navi 0.29.417 → 0.29.419
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/README.md +1 -1
- package/dist/dev/jsenv_navi.js +347 -83
- package/dist/dev/jsenv_navi.js.map +12 -12
- package/dist/jsenv_navi.js +345 -81
- package/dist/jsenv_navi.js.map +12 -12
- package/docs/AI_INSTRUCTIONS.md +177 -258
- package/docs/actions.md +164 -251
- package/docs/autofocus.md +66 -83
- package/docs/control_object.md +117 -214
- package/docs/control_value.md +121 -185
- package/docs/create_and_edit.md +159 -254
- package/docs/css_architecture.md +196 -260
- package/docs/data_states.md +41 -44
- package/docs/dialog_shape.md +91 -134
- package/docs/drag_interactions.md +291 -454
- package/docs/drag_to_travel.md +152 -748
- package/docs/dynamic_import.md +119 -136
- package/docs/error_handling.md +75 -127
- package/docs/field_validation.md +49 -89
- package/docs/form_changed.md +61 -78
- package/docs/group.md +141 -0
- package/docs/i18n.md +31 -76
- package/docs/interactions.md +170 -326
- package/docs/list_action.md +36 -51
- package/docs/list_refresh.md +73 -115
- package/docs/mobile_touch.md +56 -62
- package/docs/navigation.md +377 -723
- package/docs/network_policy.md +61 -75
- package/docs/pan_zoom.md +62 -103
- package/docs/popup_backdrop.md +128 -168
- package/docs/popup_lift.md +179 -268
- package/docs/popup_open.md +380 -651
- package/docs/resource.md +228 -408
- package/docs/route_transitions.md +333 -473
- package/docs/safe_area.md +102 -89
- package/docs/scroll.md +175 -317
- package/docs/state_binding.md +40 -84
- package/docs/testid.md +18 -23
- package/docs/typography.md +111 -141
- package/docs/view_transitions.md +106 -104
- package/docs/z_index.md +61 -109
- package/package.json +2 -2
- package/docs/badge_list.md +0 -83
- package/docs/control_group.md +0 -168
- package/docs/mobile_layout_pitfalls.md +0 -145
|
@@ -6,8 +6,6 @@ An element picked up and carried, and what letting go means. It is one gesture
|
|
|
6
6
|
the gate they go through and how a control learns which of them asked are in
|
|
7
7
|
[interactions.md](./interactions.md); who owns a press when the carried thing
|
|
8
8
|
stands in a box that travels is in [drag_to_travel.md](./drag_to_travel.md).
|
|
9
|
-
Whatever the moment, `event.detail.event` is the `pointerdown` the carry began
|
|
10
|
-
with — for the release as well, unlike a swipe's.
|
|
11
9
|
|
|
12
10
|
- [Carrying something: `move`, `reorder`, `land`, `toss`, `leave`](#carrying-something-move-reorder-land-toss-leave)
|
|
13
11
|
- [While it is being moved: `moving`](#while-it-is-being-moved-moving)
|
|
@@ -17,6 +15,7 @@ with — for the release as well, unlike a swipe's.
|
|
|
17
15
|
- [Saying the grab is acquired: `grab`](#saying-the-grab-is-acquired-grab)
|
|
18
16
|
- [Saying the hold is let go: `release`](#saying-the-hold-is-let-go-release)
|
|
19
17
|
- [The hand pulls and nothing follows: `refuse`](#the-hand-pulls-and-nothing-follows-refuse)
|
|
18
|
+
- [A press made elsewhere: `--navi-grab`](#a-press-made-elsewhere---navi-grab)
|
|
20
19
|
- [Dressing the clone](#dressing-the-clone)
|
|
21
20
|
- [What says a thing can be picked up](#what-says-a-thing-can-be-picked-up)
|
|
22
21
|
- [The text inside: `user-select: none`](#the-text-inside-user-select-none)
|
|
@@ -25,23 +24,19 @@ with — for the release as well, unlike a swipe's.
|
|
|
25
24
|
|
|
26
25
|
## Carrying something: `move`, `reorder`, `land`, `toss`, `leave`
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
`move` carries the element ITSELF and leaves it where it was put; `reorder`,
|
|
28
|
+
`land` and `toss` carry a copy and put the original back; `leave` carries the
|
|
29
|
+
element beside `move` or `moving`, a copy otherwise. Something moved has a new
|
|
30
|
+
place of its own; something reordered had its place taken by the list.
|
|
30
31
|
|
|
31
32
|
`toss` and `leave` each **combine** with `reorder` and with `land`: dropped on
|
|
32
33
|
another item the element changes places, thrown far and fast it is gotten rid of,
|
|
33
34
|
let go of away from every place it leaves. `leave` combines with `move` as well.
|
|
34
|
-
`move` does **not** combine with
|
|
35
|
-
`
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
`land` wins. A dev warning says so in each case.
|
|
40
|
-
|
|
41
|
-
`move` carries the element ITSELF and leaves it where it was put; the others
|
|
42
|
-
carry a copy and put the original back. That is the same difference said in layout
|
|
43
|
-
terms: something moved has a new place of its own, something reordered had its
|
|
44
|
-
place taken by the list.
|
|
35
|
+
`move` does **not** combine with the three that carry a copy, and neither do
|
|
36
|
+
`reorder` and `land` with each other — one release cannot mean two of those.
|
|
37
|
+
Declared together, the copy-carrying one wins and the element itself never
|
|
38
|
+
travels; of `reorder` and `land`, `land` wins. `moving` goes with `move` and
|
|
39
|
+
`leave`, never with a copy. A dev warning says so in each case.
|
|
45
40
|
|
|
46
41
|
```jsx
|
|
47
42
|
<Box
|
|
@@ -52,34 +47,47 @@ place taken by the list.
|
|
|
52
47
|
/>
|
|
53
48
|
```
|
|
54
49
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
50
|
+
What each one is told, in `event.detail` beside `event` — the `pointerdown` the
|
|
51
|
+
carry began with, for the release as well (unlike a swipe's), except for
|
|
52
|
+
[a press handed over](#a-press-made-elsewhere---navi-grab):
|
|
53
|
+
|
|
54
|
+
| interaction | `event.detail` |
|
|
55
|
+
| ---------------- | ---------------------------------------------------------------- |
|
|
56
|
+
| `move`, `moving` | `{ x, y }`, how far from the grab: once, or on every frame |
|
|
57
|
+
| `reorder` | `{ fromId, toId, syncCloneWithDropTarget }` |
|
|
58
|
+
| `land` | `{ fromId, toId, x, y, width, height, syncCloneWithDropTarget }` |
|
|
59
|
+
| `toss` | `{ id, velocity, x, y }` |
|
|
60
|
+
| `leave` | `{ id, x, y }` |
|
|
61
|
+
| `grab` | `{ pointerType, gestureInfo }` |
|
|
62
|
+
| `release` | `{ id, x, y, outcome }` |
|
|
63
|
+
| `refuse` | `{ pointerType }` |
|
|
64
|
+
|
|
65
|
+
Items and places are named by their `id`. **What an outcome returns is waited
|
|
66
|
+
on**: the gesture holds what it carries until that promise settles, and a
|
|
67
|
+
rejection sends it home — a place the application would not accept must not stay
|
|
68
|
+
on screen as if it had. `moving`, `grab`, `release` and `refuse` **report, they
|
|
69
|
+
do not ask**: what they return is not waited on, and preventing their event
|
|
70
|
+
calls nothing off.
|
|
71
|
+
|
|
72
|
+
**Where the position is kept is the answer's.** Until the release navi carries
|
|
73
|
+
the element with a translate of its own. An element drawn from state
|
|
74
|
+
(`left`/`top` computed from a position the application holds) is redrawn by the
|
|
75
|
+
`move` handler, and its layout owns the position from then on: navi sees it drawn
|
|
76
|
+
elsewhere and drops its translate, or it would land twice as far as the hand
|
|
77
|
+
went. A handler that draws nothing (a token on a free canvas) leaves the position
|
|
78
|
+
baked into the element; one whose draw comes later than its answer returns the
|
|
79
|
+
promise of that draw. Something that changes WHILE it is dragged, or must never
|
|
80
|
+
be translated, is [`moving`](#while-it-is-being-moved-moving).
|
|
81
|
+
|
|
82
|
+
**Where it may go.** A `move` stays inside what one can SEE of its scroll
|
|
83
|
+
container — the nearest ancestor whose `overflow` is `auto` or `scroll`, the
|
|
84
|
+
viewport when there is none — so a board that only cuts its content (`hidden`,
|
|
85
|
+
`clip`) does not hold it; a copy stays inside its scroll area, as a row belongs
|
|
86
|
+
to its list. `data-drag-free` on the element or a container lets it out, and
|
|
87
|
+
three things do on their own, each needing to get somewhere: a `toss` for the
|
|
88
|
+
copy it throws (kept inside its list, it covers no distance), a `leave` for
|
|
89
|
+
whatever it is declared on, and a `data-drop-container` (a place outside is only
|
|
90
|
+
reached from outside).
|
|
83
91
|
|
|
84
92
|
```jsx
|
|
85
93
|
<List.Item
|
|
@@ -98,42 +106,34 @@ Neither is a way to free a plain `move`, which says `data-drag-free`.
|
|
|
98
106
|
/>
|
|
99
107
|
```
|
|
100
108
|
|
|
101
|
-
The gesture is `startDragTo`'s, whole: `move` carries the element itself, the
|
|
102
|
-
others carry a copy above the page while the original keeps its place, with a
|
|
103
|
-
drop hint, drop targets found by intersection, no-op drops filtered out, and the
|
|
104
|
-
flight of a thrown copy plus its return when the answer refuses. Only what the
|
|
105
|
-
declared outcomes need runs — no copy for a move, no hint for something
|
|
106
|
-
that can only be thrown away.
|
|
107
|
-
|
|
108
109
|
Every element declaring `reorder` marks itself, so the set of items IS the set of
|
|
109
110
|
elements that declared it — no selector to pass, and an item that must not move
|
|
110
|
-
simply does not declare it
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
is
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
rejects brings the copy back, because the thing still exists and the screen has to
|
|
120
|
-
say so.
|
|
111
|
+
simply does not declare it; one declaring only `toss` is not a place anything
|
|
112
|
+
lands. `toId` is null for a drop at the end. `syncCloneWithDropTarget` must be
|
|
113
|
+
called synchronously inside the transition callback, next to the state change,
|
|
114
|
+
so the copy is captured where it lands rather than where it was let go of;
|
|
115
|
+
returning the transition is what makes the landing continuous. Starting that
|
|
116
|
+
transition is the application's call, not navi's: a `view-transition-name` must
|
|
117
|
+
be unique per document (see
|
|
118
|
+
[view_transitions.md](./view_transitions.md#a-name-is-unique-per-document)), so
|
|
119
|
+
only the application can name what moves.
|
|
121
120
|
|
|
122
121
|
A throw is asked about before a landing: a hand that sent something across the
|
|
123
122
|
screen has not asked for it to swap places with whatever it flew over.
|
|
124
123
|
|
|
125
|
-
Starting a document transition is the application's call, not navi's: a
|
|
126
|
-
`view-transition-name` must be unique per document, so only the application can
|
|
127
|
-
name what moves.
|
|
128
|
-
|
|
129
124
|
| Attribute | Meaning |
|
|
130
125
|
| -------------------------------------------------------- | -------------------------------------- |
|
|
131
126
|
| `data-drag-axis="x"\|"y"\|"xy"` | which axes the drag walks |
|
|
132
127
|
| `data-drag-delay` `data-drag-slop` `data-drag-threshold` | when the press becomes a grab |
|
|
133
128
|
| `data-drag-on-contact` | a finger may drag by travelling too |
|
|
129
|
+
| `data-drag-free` | it may leave its scroll area |
|
|
134
130
|
| `data-toss-distance` `data-toss-speed` | how far and how fast counts as a throw |
|
|
135
131
|
| `data-drop-container` | where the places are looked for |
|
|
136
132
|
|
|
133
|
+
`data-drag-axis` is `y` for a `reorder` with no `land`, `toss` or `leave` beside
|
|
134
|
+
it — it walks the list; a list that runs sideways says `x` — and `xy` for
|
|
135
|
+
everything else, which goes wherever the hand takes it.
|
|
136
|
+
|
|
137
137
|
### While it is being moved: `moving`
|
|
138
138
|
|
|
139
139
|
`move` says where the element ended up, once. That is the answer for something
|
|
@@ -152,42 +152,28 @@ all along, and needs to be the one drawing.
|
|
|
152
152
|
/>
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
Declaring it says both
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
it
|
|
155
|
+
Declaring it says both at once: the element is told where the hand has taken it
|
|
156
|
+
on every frame, and **navi moves nothing** — no translate, nothing to hand back
|
|
157
|
+
at the release. The numbers are counted from the grab, not steps since the last
|
|
158
|
+
frame (what `pan` gives, a surface having no grab to count from): a caller adding
|
|
159
|
+
up steps drifts, and has nothing to re-read after a frame it missed.
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
the
|
|
168
|
-
it is a gesture of its own: `moving` alone is a complete declaration, and it keeps
|
|
169
|
-
everything the drag knows — the axes, the threshold, the hold a finger owes,
|
|
170
|
-
`data-drag-free`, `grab`/`release`, `leave`, and `"refuse"` to lock it. Write
|
|
171
|
-
`move` beside it only when the release settles something the frames did not.
|
|
172
|
-
|
|
173
|
-
Nothing travels back, either: a `move` or a `leave` beside it whose answer rejects
|
|
174
|
-
normally sends the element home, and there is no home to send it to when navi
|
|
175
|
-
never moved it. The state the frames wrote is the caller's to put back.
|
|
176
|
-
|
|
177
|
-
It carries the element itself, so it goes with `move` and `leave` and not with the
|
|
178
|
-
three that carry a copy — for those, the original never goes anywhere, and there
|
|
179
|
-
is nothing being moved to tell about (a dev warning says so).
|
|
161
|
+
Told, not asked — a draw awaited before the next frame is a draw one frame late —
|
|
162
|
+
yet a complete declaration on its own, keeping everything the drag knows: the
|
|
163
|
+
axes, the threshold, the hold a finger owes, `data-drag-free`,
|
|
164
|
+
`grab`/`release`, `leave`, `"refuse"` to lock it. Write `move` beside it only
|
|
165
|
+
when the release settles something the frames did not. Nothing travels back: a
|
|
166
|
+
refused `move` or `leave` beside it has no home to send the element to, so what
|
|
167
|
+
the frames wrote is the caller's to put back.
|
|
180
168
|
|
|
181
169
|
### Let go of away from every place: `leave`
|
|
182
170
|
|
|
183
|
-
A throw is a **gesture
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
thing to go, and a row pulled sideways out of its list and let go is a row put
|
|
190
|
-
back, not a row deleted — a list declares `toss`, a surface declares `leave`.
|
|
171
|
+
A throw is a **gesture** — far and fast, judged before any landing. A release
|
|
172
|
+
outside is a **place** — nothing under the thing, judged after a landing was
|
|
173
|
+
looked for. Neither reads the other's rules: there is no speed to a release, a
|
|
174
|
+
fast drag that ends ON a plan has not asked for the thing to go, and a row pulled
|
|
175
|
+
sideways out of its list and let go is a row put back, not a row deleted — a list
|
|
176
|
+
declares `toss`, a surface declares `leave`.
|
|
191
177
|
|
|
192
178
|
```jsx
|
|
193
179
|
<div data-drop-container>
|
|
@@ -205,37 +191,21 @@ back, not a row deleted — a list declares `toss`, a surface declares `leave`.
|
|
|
205
191
|
</div>
|
|
206
192
|
```
|
|
207
193
|
|
|
208
|
-
`leave` combines with every other outcome. Beside `land` or `reorder`, "outside"
|
|
209
|
-
away from every place. Where nothing is a place — beside `move`,
|
|
210
|
-
|
|
211
|
-
stands in: the nearest
|
|
194
|
+
`leave` combines with every other outcome. Beside `land` or `reorder`, "outside"
|
|
195
|
+
is away from every place. Where nothing is a place — beside `move`, beside
|
|
196
|
+
`toss`, or alone — it is outside the surface the element stands in: the nearest
|
|
212
197
|
`data-droppable` ancestor, or, without one, what can be seen of the scroll
|
|
213
198
|
container (a dev warning says which). Either way it is judged on the element's
|
|
214
199
|
box no longer overlapping it, not on the pointer, which is still well inside the
|
|
215
|
-
frame when a small marker has just left it.
|
|
200
|
+
frame when a small marker has just left it. Picked up and put straight back down
|
|
201
|
+
stays a cancel: it has to have gone somewhere to be away from anything. Its
|
|
202
|
+
`x`/`y` are what an exit is animated with.
|
|
216
203
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
leave: () => remove(marker.id),
|
|
223
|
-
}}
|
|
224
|
-
/>
|
|
225
|
-
</Plan>
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
Its detail is `toss`'s without the speed — `{ id, x, y }`, the distance travelled
|
|
229
|
-
being what an exit is animated with. Picked up and put straight back down stays a cancel: it has to
|
|
230
|
-
have gone somewhere to be away from anything.
|
|
231
|
-
|
|
232
|
-
What the thing does while the answer is asked follows what was carried. A copy
|
|
233
|
-
fades where it was let go of; the element itself stays where the hand put it. A
|
|
234
|
-
resolve takes the copy away and lets go of the element's position — the
|
|
235
|
-
application has removed the thing, or drawn it where it goes back to (the address
|
|
236
|
-
point returns to where the geocoder put it) — and a reject brings either back, as
|
|
237
|
-
a refused `move` does. A surface that clips its overflow clips the element on its
|
|
238
|
-
way out; the copy a `land` carries lives in the top layer and does not.
|
|
204
|
+
While the answer is asked, a copy fades where it was let go of and the element
|
|
205
|
+
itself stays where the hand put it; a resolve takes the copy away and lets go of
|
|
206
|
+
the element's position (the application removed the thing, or drew it where it
|
|
207
|
+
goes back to). A surface that clips its overflow clips the element on its way
|
|
208
|
+
out; the copy lives in the top layer and does not.
|
|
239
209
|
|
|
240
210
|
### A finger that does not have to wait: `data-drag-on-contact`
|
|
241
211
|
|
|
@@ -271,42 +241,25 @@ construction, so the answer is an insertion and putting a row back where it
|
|
|
271
241
|
already was is a no-op. A place of a board is a place of its own, which may
|
|
272
242
|
already be taken — so nothing is inserted, nothing is a no-op, and the answer is
|
|
273
243
|
simply "this one came down on that one". What that means is yours: take the
|
|
274
|
-
place, swap the two, refuse.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
id={playerId}
|
|
279
|
-
interactions={{
|
|
280
|
-
land: (event) => {
|
|
281
|
-
const { fromId, toId, syncCloneWithDropTarget } = event.detail;
|
|
282
|
-
return document.startViewTransition(() => {
|
|
283
|
-
syncCloneWithDropTarget();
|
|
284
|
-
setLineup((previous) => swapPlaces(previous, fromId, toId));
|
|
285
|
-
}).finished;
|
|
286
|
-
},
|
|
287
|
-
}}
|
|
288
|
-
/>
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
`toId` always names an element and is **never null**: a copy over nothing is a
|
|
292
|
-
release that meant nothing, and the interaction does not happen at all.
|
|
244
|
+
place, swap the two, refuse. It is answered in the shape of a `reorder` (the
|
|
245
|
+
transition returned, `syncCloneWithDropTarget` inside it), and `toId` always names
|
|
246
|
+
an element — **never null**: a copy over nothing is a release that meant nothing,
|
|
247
|
+
and the interaction does not happen at all.
|
|
293
248
|
|
|
294
249
|
**Which elements are places: those marked `data-droppable`, and only those.**
|
|
295
250
|
Declaring `land` says an element can be CARRIED, which on a board is a different
|
|
296
251
|
thing from being somewhere one can be put — a zone receives without ever being
|
|
297
252
|
carried, a piece is carried without ever receiving, and both at once is a third
|
|
298
253
|
case (dropped on a piece, the two swap, so it says `data-droppable` as well). A
|
|
299
|
-
list has no such distinction,
|
|
300
|
-
no marker in the markup.
|
|
254
|
+
list has no such distinction, which is why `reorder` needs no marker.
|
|
301
255
|
|
|
302
256
|
Places are looked for among the carried element's **siblings**, which is what a
|
|
303
|
-
board is
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
`land` with no place in reach warns, and names the surface it is standing on.
|
|
257
|
+
board is. When what is carried does not stand among the places — a palette
|
|
258
|
+
BESIDE the surface it fills, a marker drawn INSIDE the surface it can be put back
|
|
259
|
+
on — say what holds both with `data-drop-container`, on an ancestor of the
|
|
260
|
+
places: a surface that is itself the place is never found from inside itself.
|
|
261
|
+
Missed, nothing lands, so the first press on a `land` with no place in reach
|
|
262
|
+
warns, and names the surface it is standing on.
|
|
310
263
|
|
|
311
264
|
```jsx
|
|
312
265
|
<div data-drop-container>
|
|
@@ -319,36 +272,20 @@ itself. Missed, nothing lands and nothing says why — so the first press on a
|
|
|
319
272
|
</div>
|
|
320
273
|
```
|
|
321
274
|
|
|
322
|
-
It also frees the copy's travel: the places being elsewhere by construction, a
|
|
323
|
-
copy kept inside its own scroll area could never reach them.
|
|
324
|
-
|
|
325
275
|
**When the place is bigger than what stands on it** — a zone holding a smaller
|
|
326
276
|
card, a square holding a piece — the copy must not take the place's box, or it
|
|
327
|
-
resizes on landing and resizes back when the real element appears.
|
|
328
|
-
`syncCloneWithDropTarget`
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
```jsx
|
|
333
|
-
land: (event) => {
|
|
334
|
-
const { fromId, toId, syncCloneWithDropTarget } = event.detail;
|
|
335
|
-
const landingElement = pieceAt(toId) || slotOf(toId);
|
|
336
|
-
return document.startViewTransition(() => {
|
|
337
|
-
syncCloneWithDropTarget(landingElement);
|
|
338
|
-
…
|
|
339
|
-
}).finished;
|
|
340
|
-
};
|
|
341
|
-
```
|
|
277
|
+
resizes on landing and resizes back when the real element appears. Pass
|
|
278
|
+
`syncCloneWithDropTarget` whatever occupies the destination — the piece already
|
|
279
|
+
standing there, the empty slot waiting — and the copy comes down on THAT box:
|
|
280
|
+
`syncCloneWithDropTarget(pieceAt(toId) || slotOf(toId))`.
|
|
342
281
|
|
|
343
282
|
#### A place that is a surface
|
|
344
283
|
|
|
345
|
-
A
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
surface it came down on, and nothing changes about the rest — the copy travels,
|
|
351
|
-
the original stays where it was.
|
|
284
|
+
A **surface** — a plan drawn over an aerial photo, a map, a floor — is one box
|
|
285
|
+
where every point is a place, and "which element did it come down on" says
|
|
286
|
+
nothing there. So the detail says **where**: `x`, `y`, `width`, `height`, the box
|
|
287
|
+
the copy came down in, measured inside the place with its border and its scroll
|
|
288
|
+
taken out. `toId` names the surface.
|
|
352
289
|
|
|
353
290
|
```jsx
|
|
354
291
|
<Shape
|
|
@@ -363,104 +300,43 @@ the original stays where it was.
|
|
|
363
300
|
|
|
364
301
|
The size comes along because the anchor is yours: a chip dragged out of a palette
|
|
365
302
|
is not the shape it becomes, so the middle of what the hand carried is usually the
|
|
366
|
-
point that was aimed at.
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
nothing it takes the whole surface's box, and there is rarely an element to pass
|
|
370
|
-
it either, the thing being created by the answer itself. Not calling it leaves the
|
|
371
|
-
copy where the hand put it, over the thing appearing there.
|
|
303
|
+
point that was aimed at. Leave `syncCloneWithDropTarget` alone here — called with
|
|
304
|
+
nothing it takes the whole surface's box, and the thing is created by the answer
|
|
305
|
+
itself — so the copy stays where the hand put it, over the thing appearing there.
|
|
372
306
|
|
|
373
307
|
#### Naming what travels
|
|
374
308
|
|
|
375
|
-
**The copy is already named, and it
|
|
376
|
-
`syncCloneWithDropTarget` moves it onto the destination inside the callback
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
{places.map((place) => {
|
|
407
|
-
const playerId = lineupAt(place.id);
|
|
408
|
-
// Keyed by the player: the same DOM node walks from one place to the other,
|
|
409
|
-
// so the browser has something to morph — and the displaced one was visible
|
|
410
|
-
// before and is visible after.
|
|
411
|
-
return <Piece key={playerId} id={playerId} style={boxOf(place)} … />;
|
|
412
|
-
})}
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
**The name is written twice, and neither write is redundant.** The old state is
|
|
416
|
-
captured the moment `startViewTransition` is called, before any render, so the
|
|
417
|
-
name must be on the DOM by then — written by hand, on the element. The render
|
|
418
|
-
happening inside the callback would then put the plain name straight back, so
|
|
419
|
-
the same name must also come from state. Clear that state when `finished`
|
|
420
|
-
resolves: a name left behind is claimed twice by the next transition, and that
|
|
421
|
-
one is dropped for it.
|
|
422
|
-
|
|
423
|
-
```jsx
|
|
424
|
-
land: (event) => {
|
|
425
|
-
const { fromId, toId, syncCloneWithDropTarget } = event.detail;
|
|
426
|
-
const displacedId = playerAt(toId);
|
|
427
|
-
const roles = { [fromId]: OVER, [displacedId]: UNDER };
|
|
428
|
-
for (const id of Object.keys(roles)) {
|
|
429
|
-
document.getElementById(id).style.viewTransitionName = roles[id];
|
|
430
|
-
}
|
|
431
|
-
const transition = document.startViewTransition(() => {
|
|
432
|
-
syncCloneWithDropTarget(document.getElementById(displacedId));
|
|
433
|
-
setSwapRoles(roles); // the render inside the callback keeps the names
|
|
434
|
-
setLineup((previous) => swapPlaces(previous, fromId, toId));
|
|
435
|
-
});
|
|
436
|
-
transition.finished.then(() => setSwapRoles(null));
|
|
437
|
-
return transition.finished;
|
|
438
|
-
};
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
The roles are names because a `::view-transition` pseudo can be selected by name
|
|
442
|
-
and by nothing else — which is also how one of the two is told to pass over the
|
|
443
|
-
other, and how the travel is given a length worth a card crossing a board rather
|
|
444
|
-
than a menu opening. Both the group and the image pair are addressed: the morph
|
|
445
|
-
lives in one, anything a style adds rides in the other.
|
|
446
|
-
|
|
447
|
-
```css
|
|
448
|
-
::view-transition-group(swap_over) {
|
|
449
|
-
z-index: 20;
|
|
450
|
-
}
|
|
451
|
-
::view-transition-group(swap_over),
|
|
452
|
-
::view-transition-image-pair(swap_over) {
|
|
453
|
-
animation-duration: 420ms;
|
|
454
|
-
animation-timing-function: ease-in-out;
|
|
455
|
-
}
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
The hint follows what a place is: a line drawn in the gap for `reorder`, the
|
|
459
|
-
place itself lit up for `land`. Both are drawn among the places — the carried
|
|
460
|
-
element's parent, or the `data-drop-container` when there is one — so the
|
|
461
|
-
variables dressing them are read from the list, the board or the surface and reach
|
|
462
|
-
them by plain inheritance. The copy goes the other way: it is drawn beside the
|
|
463
|
-
thing it copies, and dressed by where that thing stands.
|
|
309
|
+
**The copy is already named, and it does the visible travel**:
|
|
310
|
+
`syncCloneWithDropTarget` moves it onto the destination inside the callback. Each
|
|
311
|
+
copy has a name of its own, two being on screen at once while a `toss` is
|
|
312
|
+
answered, and all share the class `navi-drag-clone`, so
|
|
313
|
+
`::view-transition-group(.navi-drag-clone)` styles the landing (read from Chrome
|
|
314
|
+
125 on; before that only a rule written against the class goes unread).
|
|
315
|
+
|
|
316
|
+
What is left to name is the OTHER piece, the one standing there that goes the
|
|
317
|
+
other way. **A name rides the element that MOVES, and that element has to be
|
|
318
|
+
visible at both ends of the transition.** Never the source: it is
|
|
319
|
+
`visibility: hidden` until the promise `land` returned settles, which still gets
|
|
320
|
+
it captured, as an empty image — a name on it is a group fading in from nothing,
|
|
321
|
+
or out into nothing, over the copy doing the real travel. Where the places are
|
|
322
|
+
fixed and the pieces drawn into them, key each piece by WHO it is, so the same
|
|
323
|
+
node walks from one place to the other.
|
|
324
|
+
|
|
325
|
+
The name is written twice, and neither write is redundant: by hand on the DOM
|
|
326
|
+
before `startViewTransition` is called, because the old state is captured right
|
|
327
|
+
then, and from state, because the render inside the callback would put the plain
|
|
328
|
+
name straight back. Clear that state when `finished` resolves — a name left
|
|
329
|
+
behind is claimed twice by the next transition, and that one is dropped for it.
|
|
330
|
+
The names are roles (the piece passing over, the one passing under) because a
|
|
331
|
+
`::view-transition` pseudo is selected by name and by nothing else; address both
|
|
332
|
+
its group and its image pair — the morph lives in one, anything a style adds
|
|
333
|
+
rides in the other. The padel board in `38_interactions_demo.html` does all of
|
|
334
|
+
it.
|
|
335
|
+
|
|
336
|
+
The drop hint follows what a place is: a line drawn in the gap for `reorder`, the
|
|
337
|
+
place itself lit up for `land`. Both are drawn among the places — the parent, or
|
|
338
|
+
the `data-drop-container` — so the variables dressing them are set on the list,
|
|
339
|
+
the board or the surface and reach them by inheritance:
|
|
464
340
|
|
|
465
341
|
| Variable | Dresses |
|
|
466
342
|
| ------------------------------------------------------------------------------------------ | ----------------------- |
|
|
@@ -471,50 +347,29 @@ thing it copies, and dressed by where that thing stands.
|
|
|
471
347
|
|
|
472
348
|
### Saying the grab is acquired: `grab`
|
|
473
349
|
|
|
474
|
-
The
|
|
475
|
-
one instant that counts for the hand
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
it the hand waits, doubts the press was heard, and lets go too early — the whole
|
|
491
|
-
gesture fails, not its decoration. With a mouse the grab is acquired after a few
|
|
492
|
-
pixels and the object has visibly moved, so the answer is already there.
|
|
493
|
-
|
|
494
|
-
Nothing here is about vibration: a sound, a class, a measure are the same moment.
|
|
495
|
-
|
|
496
|
-
If what the moment is for is **paint**, no listener is needed at all: navi puts
|
|
497
|
-
`data-grabbed` on the element for as long as the gesture holds it, and a
|
|
498
|
-
stylesheet draws from that. `grab` is for what a stylesheet cannot do — the
|
|
499
|
-
vibration above, a state kept elsewhere, a counter.
|
|
500
|
-
|
|
501
|
-
`grab` **reports, it does not ask**: what it returns is not waited on, and
|
|
502
|
-
preventing its event does not call the gesture off. And it is not an interaction on
|
|
503
|
-
its own — declared without one of the five above (or `moving`) there is no
|
|
504
|
-
gesture for it to be the beginning of, and a dev warning says so, unless the
|
|
505
|
-
element declares `pan` or `zoom`, whose moments they then are
|
|
506
|
-
([pan_zoom.md](./pan_zoom.md)). Its detail carries `pointerType` and the
|
|
507
|
-
`gestureInfo`.
|
|
508
|
-
|
|
509
|
-
A `longpress` needs none of this: it already happens at the moment the hold is
|
|
510
|
-
acquired, not at the release.
|
|
350
|
+
The outcomes answer the **release**. Between the press and the release there is
|
|
351
|
+
one instant that counts for the hand — the object stops being pressed and starts
|
|
352
|
+
being held, whichever way the drag was entered — and `grab` is that instant:
|
|
353
|
+
`grab: () => navigator.vibrate?.(10)` beside the outcome. On a screen the held
|
|
354
|
+
object is under the thumb that hides it, so the only feedback available is the
|
|
355
|
+
one that is felt; without it the hand doubts the press was heard and lets go too
|
|
356
|
+
early — the whole gesture fails, not its decoration. For **paint** no listener
|
|
357
|
+
is needed: navi puts `data-grabbed` on the element for as long as the gesture
|
|
358
|
+
holds it. `grab` is for what a stylesheet cannot do — a vibration, a state kept
|
|
359
|
+
elsewhere, a counter.
|
|
360
|
+
|
|
361
|
+
It is not an interaction on its own: declared without an outcome or `moving`
|
|
362
|
+
there is no gesture for it to begin, and a dev warning says so — unless the
|
|
363
|
+
element declares `pan` or `zoom`, whose moment it then is
|
|
364
|
+
([pan_zoom.md](./pan_zoom.md)). A `longpress` needs none of this: it already
|
|
365
|
+
happens at the moment the hold is acquired.
|
|
511
366
|
|
|
512
367
|
### Saying the hold is let go: `release`
|
|
513
368
|
|
|
514
|
-
The mirror of `grab`, and the only interaction of the family always told. Each
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
up stays set up with nothing to take it down.
|
|
369
|
+
The mirror of `grab`, and the only interaction of the family always told. Each
|
|
370
|
+
outcome answers ONE meaning, so a release that means none of them — let go of
|
|
371
|
+
over nothing, taken away by the system — reaches nobody, and whatever `grab` set
|
|
372
|
+
up (a counter of what is in the hand) stays set up with nothing to take it down.
|
|
518
373
|
|
|
519
374
|
```jsx
|
|
520
375
|
<Chip
|
|
@@ -526,28 +381,20 @@ up stays set up with nothing to take it down.
|
|
|
526
381
|
/>
|
|
527
382
|
```
|
|
528
383
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
Its detail is `{ id, x, y, outcome }`. `outcome` is which of the five is about to
|
|
535
|
-
answer — `"move"`, `"reorder"`, `"land"`, `"toss"`, `"leave"` — or `null` when the
|
|
536
|
-
release means none of them. It is told **before** that answer runs, so what the grab
|
|
537
|
-
put up comes down at the moment the hand lets go, while still knowing whether
|
|
538
|
-
something is on its way.
|
|
539
|
-
|
|
540
|
-
Like `grab`, it **reports, it does not ask**, and it is not an interaction on its
|
|
541
|
-
own: a moment of a drag needs a drag to happen in, and a dev warning says so.
|
|
384
|
+
`outcome` is which one is about to answer — `"move"`, `"reorder"`, `"land"`,
|
|
385
|
+
`"toss"`, `"leave"` — or `null` when the release means none of them. It is told
|
|
386
|
+
**before** that answer runs, so what the grab put up comes down the moment the
|
|
387
|
+
hand lets go, while still knowing whether something is on its way. Like `grab`,
|
|
388
|
+
it needs a drag to happen in, and a dev warning says so.
|
|
542
389
|
|
|
543
390
|
### The hand pulls and nothing follows: `refuse`
|
|
544
391
|
|
|
545
392
|
Something that can be carried is not always free to be: a court whose place on the
|
|
546
393
|
plan is settled, a marker pinned by whoever owns it. Taking the interaction away —
|
|
547
|
-
`move: false` — makes the element **deaf** rather than locked
|
|
548
|
-
nobody's, so whatever it stands on answers it (the surface pans under an object
|
|
549
|
-
hand was aiming at), and the pull is told nothing
|
|
550
|
-
|
|
394
|
+
`move: false` — makes the element **deaf** rather than locked: the press is
|
|
395
|
+
nobody's, so whatever it stands on answers it (the surface pans under an object
|
|
396
|
+
the hand was aiming at), and the pull is told nothing — a thing that does not move
|
|
397
|
+
and says nothing reads as a broken screen, so the hand insists.
|
|
551
398
|
|
|
552
399
|
```jsx
|
|
553
400
|
<Court
|
|
@@ -563,44 +410,49 @@ move and says nothing reads as a screen that is broken, so the hand insists.
|
|
|
563
410
|
/>
|
|
564
411
|
```
|
|
565
412
|
|
|
566
|
-
So the interaction stays declared and says `"refuse"` in place of what it does.
|
|
567
|
-
threshold is the same
|
|
568
|
-
|
|
569
|
-
acquired there is none: nothing translates, no copy is made, no release
|
|
570
|
-
answered. `refuse` is
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
413
|
+
So the interaction stays declared and says `"refuse"` in place of what it does.
|
|
414
|
+
The threshold is the same — a mouse travelling, a finger holding still, a mouse's
|
|
415
|
+
few pixels inside a `data-drag-on-contact` — and at the instant the grab would
|
|
416
|
+
have been acquired there is none: nothing translates, no copy is made, no release
|
|
417
|
+
is answered. `refuse` is told then, where feedback is expected, with no
|
|
418
|
+
`gestureInfo` (there was no gesture) and the `pointerType` that says which hand
|
|
419
|
+
asked — a vibration for a finger, nothing extra for a mouse whose cursor has
|
|
420
|
+
already said it. One outcome refusing refuses the whole carry.
|
|
421
|
+
|
|
422
|
+
**It walks no axis, so it takes none.** Whatever reads what a drag source walks
|
|
423
|
+
to know what is left for itself — a box that travels, a surface that pans — steps
|
|
424
|
+
over an element that is refusing: a swipe starting on a locked row is the row of
|
|
425
|
+
slides' swipe, a drag starting on a pinned court pans the plan. A thing that
|
|
426
|
+
cannot be taken hold of is exactly the one a hand rests on without thinking, and
|
|
427
|
+
a surface with a dead zone the size of an object in the middle of it is wrong
|
|
428
|
+
every time. For the axes, `move: "refuse"` is `move: false`; for the press it is
|
|
429
|
+
not.
|
|
430
|
+
|
|
431
|
+
**The press itself stays the element's**, settled there like any gesture settles
|
|
432
|
+
one: the pointer is taken, so a `longpress` declared beside the drag does not
|
|
433
|
+
answer afterwards, and the click the release leaves behind is swallowed. The
|
|
434
|
+
exception is a box declaring `pan`/`zoom`, the one thing that takes a press whole
|
|
435
|
+
and in every direction: there the surface keeps it, and the refusal is told
|
|
436
|
+
without taking anything — no pointer, no click, nothing prevented.
|
|
437
|
+
|
|
438
|
+
### A press made elsewhere: `--navi-grab`
|
|
439
|
+
|
|
440
|
+
A hold opens a popup, and something in it is what the hand meant to take:
|
|
441
|
+
`triggerNaviCommand(element, "--navi-grab", openEvent)` hands the press still
|
|
442
|
+
down to that element (or the drag source around it), so the finger carries it
|
|
443
|
+
without lifting and pressing again. It brings **a copy only**, to the finger,
|
|
444
|
+
once the popup has settled: `move` and `moving` carry nothing there (a dev
|
|
445
|
+
warning says so), and let go of before it has travelled — the hold only meant to
|
|
446
|
+
open the popup — the copy goes back and nothing is answered. Its interactions
|
|
447
|
+
are chained to that request rather than to a press on the element, so
|
|
448
|
+
`event.detail.event` is the request: reach the `pointerdown` with
|
|
449
|
+
`findEvent(event, "pointerdown")`.
|
|
596
450
|
|
|
597
451
|
### Dressing the clone
|
|
598
452
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
clone's look is the page's to declare, through the attributes the gesture puts on
|
|
603
|
-
it:
|
|
453
|
+
A copy of a transparent element is invisible — a row usually gets its background
|
|
454
|
+
from the list around it, which the copy has left. So the clone's look is the
|
|
455
|
+
page's to declare, through the attributes the gesture puts on it:
|
|
604
456
|
|
|
605
457
|
| Attribute | On |
|
|
606
458
|
| ------------------------- | ---------------------------------------------------- |
|
|
@@ -615,17 +467,18 @@ it:
|
|
|
615
467
|
}
|
|
616
468
|
```
|
|
617
469
|
|
|
618
|
-
Reusing the item's own class is the point: the copy is that item,
|
|
619
|
-
|
|
470
|
+
Reusing the item's own class is the point: the copy is that item, styled as that
|
|
471
|
+
item plus whatever being carried changes.
|
|
620
472
|
|
|
621
473
|
**Which is also the trap, for anything positioned on a board.** The copy is a
|
|
622
|
-
deep clone
|
|
623
|
-
|
|
624
|
-
`
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
474
|
+
deep clone put in a carrier box, so a geometry written in the style attribute
|
|
475
|
+
comes with it. navi resets where it stands (`position`, `inset`, `margin`,
|
|
476
|
+
`translate`) but not its size: `width: calc(50% - 2 * var(--gap))` then means
|
|
477
|
+
half of the carrier, and the piece is carried at the wrong size. Put what a piece
|
|
478
|
+
LOOKS like in a class, leave only which place it is inline (two custom properties
|
|
479
|
+
will do), and let it fill its carrier — said of what is inside the wrapper, not
|
|
480
|
+
of `[navi-drag-clone]`, which the copy loses as it lands (that is how it drops
|
|
481
|
+
its lift for the transition) while it has to keep its size all the way down:
|
|
629
482
|
|
|
630
483
|
```css
|
|
631
484
|
[navi-drag-clone-wrapper] .piece {
|
|
@@ -634,24 +487,18 @@ properties will do), then let it fill its carrier:
|
|
|
634
487
|
}
|
|
635
488
|
```
|
|
636
489
|
|
|
637
|
-
|
|
638
|
-
because the copy loses that mark as it lands — that is how it drops its lift for
|
|
639
|
-
the transition — and it has to keep its size all the way down. The copy is a real element in the
|
|
640
|
-
page, in the top layer, and everything about its look is reachable from CSS —
|
|
641
|
-
including these, read off the dragged element so a whole list or a single item can
|
|
642
|
-
answer:
|
|
490
|
+
Read off the dragged element, so a whole list or a single item can answer:
|
|
643
491
|
|
|
644
492
|
| Variable | What it changes |
|
|
645
493
|
| --------------------- | ------------------------------------------------------------- |
|
|
646
494
|
| `--drag-clone-shadow` | what being lifted casts; `none` for something that flies flat |
|
|
647
495
|
| `--drag-clone-scale` | how much bigger it gets once picked up; `1` to keep its size |
|
|
648
496
|
|
|
649
|
-
**What stays behind is the source, not a hole.** The original
|
|
650
|
-
the
|
|
651
|
-
|
|
652
|
-
imprint, a dashed outline
|
|
653
|
-
|
|
654
|
-
back from the hidden source:
|
|
497
|
+
**What stays behind is the source, not a hole.** The original keeps its place in
|
|
498
|
+
the layout, only `visibility: hidden`, until the answer settles — it says where
|
|
499
|
+
the thing left from for as long as the question is open. A mark left there (an
|
|
500
|
+
imprint, a dashed outline) is drawn ON that element, and its parts say
|
|
501
|
+
`visibility: visible` to come back from the hidden source:
|
|
655
502
|
|
|
656
503
|
```css
|
|
657
504
|
.paper[navi-drag-clone-source]::after {
|
|
@@ -664,17 +511,6 @@ back from the hidden source:
|
|
|
664
511
|
}
|
|
665
512
|
```
|
|
666
513
|
|
|
667
|
-
It stays until the answer settles, which is what makes it say where the thing left
|
|
668
|
-
from for as long as the question is open — and if the answer refuses, the copy comes
|
|
669
|
-
back to it.
|
|
670
|
-
|
|
671
|
-
`data-drag-axis` says which axes the drag walks, and its default is not the same
|
|
672
|
-
for every outcome: `reorder` alone walks the list (`y`; a list that runs sideways
|
|
673
|
-
says `data-drag-axis="x"`), while a `move` goes wherever it is put, a `land` wherever the board has
|
|
674
|
-
places, a `toss` wherever it was thrown and a `leave` out by whichever edge (`xy`).
|
|
675
|
-
`data-drag-delay`,
|
|
676
|
-
`data-drag-slop`, `data-drag-threshold` tune when the press becomes a grab.
|
|
677
|
-
|
|
678
514
|
### What says a thing can be picked up
|
|
679
515
|
|
|
680
516
|
Almost nothing, on purpose. A **handle** (`data-drag-handle`) exists only to drag,
|
|
@@ -686,8 +522,8 @@ text inside cannot be selected either — a finger is another matter, see below)
|
|
|
686
522
|
|
|
687
523
|
So on a board where a piece is also clickable, the cursor is already spoken for and
|
|
688
524
|
the affordance has to be said in the piece itself — a grip mark in a corner, a
|
|
689
|
-
shadow appearing under the pointer, a handle.
|
|
690
|
-
|
|
525
|
+
shadow appearing under the pointer, a handle. Decide it rather than default it: a
|
|
526
|
+
board one may drag on is worth nothing if nobody tries.
|
|
691
527
|
|
|
692
528
|
### The text inside: `user-select: none`
|
|
693
529
|
|
|
@@ -711,9 +547,9 @@ a stylesheet, and it has to be true before the press:
|
|
|
711
547
|
|
|
712
548
|
**Most of the time that is what you want.** What can be picked up carries a
|
|
713
549
|
label rather than a text to read — a token, a row, a card one moves — and the
|
|
714
|
-
press is there for the drag
|
|
715
|
-
|
|
716
|
-
|
|
550
|
+
press is there for the drag. Keep the selection where the text IS the point (a
|
|
551
|
+
note one copies from) and give that one a `data-drag-handle`: the grip drags, the
|
|
552
|
+
text stays text.
|
|
717
553
|
|
|
718
554
|
navi says it on its own in one place only, inside `[data-drag-on-contact]`, where
|
|
719
555
|
there is no wait left to answer the finger with.
|
|
@@ -721,42 +557,49 @@ there is no wait left to answer the finger with.
|
|
|
721
557
|
## A gesture whose product is a value
|
|
722
558
|
|
|
723
559
|
`move`, `reorder`, `land`, `toss` and `leave` answer the same question — where did
|
|
724
|
-
the element end up
|
|
725
|
-
|
|
726
|
-
|
|
560
|
+
the element end up. A rotation does not: what comes out of it is an angle, and
|
|
561
|
+
the handle that produced it stays exactly where it is. Same for a scale, or a sun
|
|
562
|
+
dragged around a plan to set the hour.
|
|
727
563
|
|
|
728
|
-
What navi names is never the value
|
|
564
|
+
What navi names is never the value — it does not know that these pixels are an
|
|
729
565
|
angle, that the angle snaps to the neighbouring court, or where the result is
|
|
730
|
-
kept
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
grab, every frame, nothing translated
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
What is left
|
|
738
|
-
|
|
739
|
-
application says, on its own rules. Then navi hands over the machinery
|
|
740
|
-
|
|
566
|
+
kept — but the **arbitration**: a press that could have been a tap, a scroll, a
|
|
567
|
+
page travelling, the surface underneath. Once that is settled it hands the
|
|
568
|
+
numbers over. **A value computed from how far a handle was taken is `moving`**:
|
|
569
|
+
the deltas from the grab, every frame, nothing translated, and `move` beside it
|
|
570
|
+
when the release commits something. A value read off a surface is `pan` and
|
|
571
|
+
`zoom` ([pan_zoom.md](./pan_zoom.md)). Both are `interactions` work.
|
|
572
|
+
|
|
573
|
+
What is left is a gesture whose SHAPE is none of navi's: it does not begin on an
|
|
574
|
+
element that could be carried, or on a surface — it begins wherever the
|
|
575
|
+
application says, on its own rules. Then navi hands over the machinery and leaves
|
|
576
|
+
the paint alone; the options are in the JSDoc of `dragAfterIntent`,
|
|
577
|
+
`markDragSource` and `createDragGestureController`:
|
|
741
578
|
|
|
742
579
|
```js
|
|
743
|
-
import {
|
|
580
|
+
import {
|
|
581
|
+
createDragGestureController,
|
|
582
|
+
dragAfterIntent,
|
|
583
|
+
markDragSource,
|
|
584
|
+
} from "@jsenv/navi";
|
|
585
|
+
|
|
586
|
+
// At mount, not in the pointerdown.
|
|
587
|
+
const unmarkDragSource = markDragSource(element, "xy");
|
|
744
588
|
|
|
745
589
|
const onPointerDown = (pointerDownEvent) => {
|
|
746
|
-
//
|
|
747
|
-
// when `currentTarget` is null.
|
|
748
|
-
const element = pointerDownEvent.currentTarget;
|
|
590
|
+
// `element`, not `pointerDownEvent.currentTarget`: the callback runs once the
|
|
591
|
+
// intent shows, after dispatch, when `currentTarget` is null.
|
|
749
592
|
dragAfterIntent(pointerDownEvent, () => {
|
|
750
593
|
const controller = createDragGestureController({
|
|
751
594
|
onDrag: (gestureInfo) => {
|
|
752
595
|
const { xDelta, yDelta } = gestureInfo.layout;
|
|
753
|
-
|
|
596
|
+
valueSignal.value = valueFrom(xDelta, yDelta);
|
|
754
597
|
},
|
|
755
598
|
onRelease: (gestureInfo) => {
|
|
756
599
|
if (gestureInfo.cancelled) {
|
|
757
600
|
return;
|
|
758
601
|
}
|
|
759
|
-
|
|
602
|
+
commit(valueSignal.peek());
|
|
760
603
|
},
|
|
761
604
|
});
|
|
762
605
|
return controller.grabViaPointer(pointerDownEvent, { element });
|
|
@@ -764,40 +607,34 @@ const onPointerDown = (pointerDownEvent) => {
|
|
|
764
607
|
};
|
|
765
608
|
```
|
|
766
609
|
|
|
767
|
-
An element that ends up somewhere is NOT this: that one is `move` — or `land`
|
|
768
|
-
what it ends up on is a surface
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
-
|
|
786
|
-
reads the same whether the gesture handed the pointer over or the browser dropped
|
|
787
|
-
it mid-drag, which it does more often than the specification suggests. The loop
|
|
788
|
-
tells the two apart, and `gestureInfo.cancelled` is where it comes out: a release
|
|
789
|
-
nobody asked for must commit nothing.
|
|
790
|
-
|
|
791
|
-
A gesture driven this way is outside the registry, so it says so itself to whatever
|
|
792
|
-
travels above it: `data-no-drag-travel` (see `docs/drag_to_travel.md`).
|
|
610
|
+
An element that ends up somewhere is NOT this: that one is `move` — or `land`
|
|
611
|
+
when what it ends up on is a surface — and doing it by hand gives up the
|
|
612
|
+
constraint, the commit and the way back when the answer refuses.
|
|
613
|
+
|
|
614
|
+
**A touch has to be refusable before it is refused**, so `markDragSource` goes
|
|
615
|
+
down at mount, on the source itself, and what surrounds it keeps scrolling (the
|
|
616
|
+
two conditions are in
|
|
617
|
+
[mobile_touch.md](./mobile_touch.md#the-browser-decides-about-8px-in-and-does-not-wait)).
|
|
618
|
+
A blanket `touch-action: none` on the container buys the same drag by taking the
|
|
619
|
+
pan away, and the pan then has to be written by hand too. **A capture that goes
|
|
620
|
+
was not necessarily given back**: `lostpointercapture` reads the same whether the
|
|
621
|
+
gesture handed the pointer over or the browser dropped it mid-drag, which it does
|
|
622
|
+
more often than the specification suggests. The loop tells the two apart, and
|
|
623
|
+
`gestureInfo.cancelled` is where it comes out — a release nobody asked for must
|
|
624
|
+
commit nothing.
|
|
625
|
+
|
|
626
|
+
A gesture driven this way is outside the registry, so it says so itself to
|
|
627
|
+
whatever travels above it: `data-no-drag-travel` (see
|
|
628
|
+
[drag_to_travel.md](./drag_to_travel.md#a-navi-component-that-reads-the-pointer-marks-itself)).
|
|
793
629
|
|
|
794
630
|
## Reference
|
|
795
631
|
|
|
796
|
-
- `src/control/interaction/interaction_drag.js` —
|
|
797
|
-
`
|
|
798
|
-
- `@jsenv/dom` — `src/interaction/drag/
|
|
799
|
-
and what `gestureInfo` holds)
|
|
800
|
-
(when a press becomes a drag, and what a source says in
|
|
632
|
+
- `src/control/interaction/interaction_drag.js` — the outcomes, `moving`, the
|
|
633
|
+
`grab`/`release`/`refuse` moments and `--navi-grab`.
|
|
634
|
+
- `@jsenv/dom` — `src/interaction/drag/drag_to.js` (`startDragTo`,
|
|
635
|
+
`refuseDragTo`), `drag_gesture.js` (the loop and what `gestureInfo` holds),
|
|
636
|
+
`drag_after_intent.js` (when a press becomes a drag, and what a source says in
|
|
637
|
+
the stylesheet).
|
|
801
638
|
- `src/control/demos/38_interactions_demo.html` — a board whose places are
|
|
802
639
|
zones and the same board whose places are the pieces.
|
|
803
640
|
- `src/control/demos/integration/5_plan_editor_demo.html` — a place that is a
|