@jsenv/navi 0.29.416 → 0.29.418
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/dev/jsenv_navi.js +1595 -1103
- package/dist/dev/jsenv_navi.js.map +19 -17
- package/dist/jsenv_navi.js +1566 -1088
- package/dist/jsenv_navi.js.map +19 -17
- package/docs/AI_INSTRUCTIONS.md +13 -6
- package/docs/drag_interactions.md +297 -401
- package/docs/drag_to_travel.md +157 -754
- package/docs/interactions.md +170 -328
- package/docs/mobile_touch.md +35 -0
- package/docs/resource.md +13 -0
- package/docs/route_transitions.md +35 -7
- package/docs/scroll.md +23 -5
- package/docs/state_binding.md +39 -84
- package/package.json +2 -2
|
@@ -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, and `move` is told once, at the end. An
|
|
74
|
+
element drawn from state — `left`/`top` computed from a position the application
|
|
75
|
+
holds — is redrawn by the handler, and from then on its layout owns the
|
|
76
|
+
position: navi sees it drawn elsewhere and lets go of its translate (both kept,
|
|
77
|
+
it would land twice as far as the hand went). A handler that draws nothing (a
|
|
78
|
+
token on a free canvas) leaves the position to the element, baked in. A handler
|
|
79
|
+
whose draw comes later than its answer returns the promise of that draw. A thing
|
|
80
|
+
that has to change something WHILE it is dragged, or must never be translated,
|
|
81
|
+
declares [`moving`](#while-it-is-being-moved-moving).
|
|
82
|
+
|
|
83
|
+
**Where it may go.** A `move` stays inside what one can SEE of its scroll
|
|
84
|
+
container — the nearest ancestor whose `overflow` is `auto` or `scroll`, the
|
|
85
|
+
viewport when there is none — so a board that only cuts its content (`hidden`,
|
|
86
|
+
`clip`) does not hold it; a copy stays inside its scroll area, right for a row
|
|
87
|
+
that belongs to its list. `data-drag-free` on the element or a container lets it
|
|
88
|
+
out, and a `toss`, a `leave` or a `data-drop-container` free the copy on their
|
|
89
|
+
own: a throw kept inside its list covers no distance, and a place outside can
|
|
90
|
+
only be 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
111
|
simply does not declare it. An element declaring only `toss` marks nothing: it is
|
|
111
|
-
not a place anything lands.
|
|
112
|
-
|
|
113
|
-
`toId` is null for a drop at the end. `syncCloneWithDropTarget` must be called
|
|
114
|
-
synchronously inside the transition callback, next to the state change, so the copy
|
|
115
|
-
is captured where it lands rather than where it was let go of.
|
|
112
|
+
not a place anything lands. `toId` is null for a drop at the end.
|
|
116
113
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
`syncCloneWithDropTarget` must be called synchronously inside the transition
|
|
115
|
+
callback, next to the state change, so the copy is captured where it lands rather
|
|
116
|
+
than where it was let go of; returning the transition is what makes a landing
|
|
117
|
+
continuous. Starting that transition is the application's call, not navi's: a
|
|
118
|
+
`view-transition-name` must be unique per document, so only the application can
|
|
119
|
+
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,32 @@ all along, and needs to be the one drawing.
|
|
|
152
152
|
/>
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
Declaring it says both
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
frame
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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).
|
|
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 (which is what `pan` gives, a surface having no grab to count from): a
|
|
159
|
+
caller adding up steps drifts, and has nothing to re-read after a frame it
|
|
160
|
+
missed.
|
|
161
|
+
|
|
162
|
+
Told, not asked — a draw that has to be awaited before the next frame is a draw
|
|
163
|
+
one frame late — yet a gesture of its own: `moving` alone is a complete
|
|
164
|
+
declaration, keeping everything the drag knows (the axes, the threshold, the
|
|
165
|
+
hold a finger owes, `data-drag-free`, `grab`/`release`, `leave`, `"refuse"` to
|
|
166
|
+
lock it). Write `move` beside it only when the release settles something the
|
|
167
|
+
frames did not. And nothing travels back: a `move` or a `leave` beside it that
|
|
168
|
+
is refused has no home to send the element to, so the state the frames wrote is
|
|
169
|
+
the caller's to put back.
|
|
180
170
|
|
|
181
171
|
### Let go of away from every place: `leave`
|
|
182
172
|
|
|
183
|
-
A throw is a **gesture**: far and fast,
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
173
|
+
A throw is a **gesture**: far and fast, judged before any landing. A release
|
|
174
|
+
outside is a **place**: the hand let go with nothing under the thing, judged
|
|
175
|
+
after a landing was looked for. They share the outcome an application usually
|
|
176
|
+
attaches to them and nothing else, so neither reads the other's rules — there is
|
|
177
|
+
no speed to a release, and a fast drag across a plan that ends ON it has not
|
|
178
|
+
asked for the thing to go. A row pulled sideways out of its list and let go is a
|
|
179
|
+
row put back, not a row deleted: a list declares `toss`, a surface declares
|
|
180
|
+
`leave`.
|
|
191
181
|
|
|
192
182
|
```jsx
|
|
193
183
|
<div data-drop-container>
|
|
@@ -205,37 +195,21 @@ back, not a row deleted — a list declares `toss`, a surface declares `leave`.
|
|
|
205
195
|
</div>
|
|
206
196
|
```
|
|
207
197
|
|
|
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
|
|
198
|
+
`leave` combines with every other outcome. Beside `land` or `reorder`, "outside"
|
|
199
|
+
is away from every place. Where nothing is a place — beside `move`, beside
|
|
200
|
+
`toss`, or alone — it is outside the surface the element stands in: the nearest
|
|
212
201
|
`data-droppable` ancestor, or, without one, what can be seen of the scroll
|
|
213
202
|
container (a dev warning says which). Either way it is judged on the element's
|
|
214
203
|
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.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
<Plan data-droppable>
|
|
219
|
-
<Marker
|
|
220
|
-
interactions={{
|
|
221
|
-
move: (event) => remember(event.detail.x, event.detail.y),
|
|
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.
|
|
204
|
+
frame when a small marker has just left it. Picked up and put straight back down
|
|
205
|
+
stays a cancel: it has to have gone somewhere to be away from anything. Its
|
|
206
|
+
`x`/`y` are what an exit is animated with.
|
|
231
207
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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.
|
|
208
|
+
While the answer is asked, a copy fades where it was let go of and the element
|
|
209
|
+
itself stays where the hand put it; a resolve takes the copy away and lets go of
|
|
210
|
+
the element's position (the application removed the thing, or drew it where it
|
|
211
|
+
goes back to). A surface that clips its overflow clips the element on its way
|
|
212
|
+
out; the copy lives in the top layer and does not.
|
|
239
213
|
|
|
240
214
|
### A finger that does not have to wait: `data-drag-on-contact`
|
|
241
215
|
|
|
@@ -296,17 +270,15 @@ Declaring `land` says an element can be CARRIED, which on a board is a different
|
|
|
296
270
|
thing from being somewhere one can be put — a zone receives without ever being
|
|
297
271
|
carried, a piece is carried without ever receiving, and both at once is a third
|
|
298
272
|
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.
|
|
273
|
+
list has no such distinction, which is why `reorder` needs no marker.
|
|
301
274
|
|
|
302
275
|
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.
|
|
276
|
+
board is. When what is carried does not stand among the places — a palette
|
|
277
|
+
BESIDE the surface it fills, a marker drawn INSIDE the surface it can be put back
|
|
278
|
+
on — say what holds both with `data-drop-container`, on an ancestor of the
|
|
279
|
+
places: a surface that is itself the place is never found from inside itself.
|
|
280
|
+
Missed, nothing lands, so the first press on a `land` with no place in reach
|
|
281
|
+
warns, and names the surface it is standing on.
|
|
310
282
|
|
|
311
283
|
```jsx
|
|
312
284
|
<div data-drop-container>
|
|
@@ -319,36 +291,20 @@ itself. Missed, nothing lands and nothing says why — so the first press on a
|
|
|
319
291
|
</div>
|
|
320
292
|
```
|
|
321
293
|
|
|
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
294
|
**When the place is bigger than what stands on it** — a zone holding a smaller
|
|
326
295
|
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
|
-
```
|
|
296
|
+
resizes on landing and resizes back when the real element appears. Pass
|
|
297
|
+
`syncCloneWithDropTarget` whatever occupies the destination — the piece already
|
|
298
|
+
standing there, the empty slot waiting — and the copy comes down on THAT box:
|
|
299
|
+
`syncCloneWithDropTarget(pieceAt(toId) || slotOf(toId))`.
|
|
342
300
|
|
|
343
301
|
#### A place that is a surface
|
|
344
302
|
|
|
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.
|
|
303
|
+
A **surface** — a plan drawn over an aerial photo, a map, a floor — is one box
|
|
304
|
+
where every point is a place, and "which element did it come down on" says
|
|
305
|
+
nothing there. So the detail says **where**: `x`, `y`, `width`, `height`, the box
|
|
306
|
+
the copy came down in, measured inside the place with its border and its scroll
|
|
307
|
+
taken out. `toId` names the surface.
|
|
352
308
|
|
|
353
309
|
```jsx
|
|
354
310
|
<Shape
|
|
@@ -363,62 +319,35 @@ the original stays where it was.
|
|
|
363
319
|
|
|
364
320
|
The size comes along because the anchor is yours: a chip dragged out of a palette
|
|
365
321
|
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.
|
|
322
|
+
point that was aimed at. Leave `syncCloneWithDropTarget` alone here — called with
|
|
323
|
+
nothing it takes the whole surface's box, and the thing is created by the answer
|
|
324
|
+
itself — so the copy stays where the hand put it, over the thing appearing there.
|
|
372
325
|
|
|
373
326
|
#### Naming what travels
|
|
374
327
|
|
|
375
|
-
**The copy is already named, and it is the copy that does the visible travel
|
|
376
|
-
`syncCloneWithDropTarget` moves it onto the destination inside the callback
|
|
377
|
-
the
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
Both follow from one rule: **a name rides the element that MOVES, and that
|
|
399
|
-
element has to be visible at both ends of the transition.** Where the places are
|
|
400
|
-
fixed and the pieces are drawn into them, nothing moves — two boxes change
|
|
401
|
-
content, and hand-writing a name on each so that it "follows the player" names a
|
|
402
|
-
journey whose start or end is the hidden source. Key the piece by WHO it is and
|
|
403
|
-
let it be re-parented:
|
|
404
|
-
|
|
405
|
-
```jsx
|
|
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.
|
|
328
|
+
**The copy is already named, and it is the copy that does the visible travel**:
|
|
329
|
+
`syncCloneWithDropTarget` moves it onto the destination inside the callback,
|
|
330
|
+
whether the application names anything or not. Each copy has a name of its own,
|
|
331
|
+
two being on screen at once while a `toss` is answered; what they share is the
|
|
332
|
+
class `navi-drag-clone`, so a stylesheet reaches the landing with
|
|
333
|
+
`::view-transition-group(.navi-drag-clone)` (read from Chrome 125 on; before
|
|
334
|
+
that only a rule written against the class goes unread).
|
|
335
|
+
|
|
336
|
+
What is left to name is the OTHER piece, the one that was standing there and
|
|
337
|
+
goes the other way. **A name rides the element that MOVES, and that element has
|
|
338
|
+
to be visible at both ends of the transition.** Never the source: it stays
|
|
339
|
+
hidden (`navi-drag-clone-source`) until the promise `land` returned settles, and
|
|
340
|
+
a hidden element is still captured, so a name on it is a group fading in from
|
|
341
|
+
nothing, or out into nothing, over the copy doing the real travel. Where the
|
|
342
|
+
places are fixed and the pieces drawn into them, key each piece by WHO it is
|
|
343
|
+
(`key={playerId}`), so the same node walks from one place to the other instead
|
|
344
|
+
of two boxes changing content.
|
|
345
|
+
|
|
346
|
+
The name is written twice, and neither write is redundant: by hand on the DOM,
|
|
347
|
+
because the old state is captured the moment `startViewTransition` is called;
|
|
348
|
+
and from state, because the render inside the callback would put the plain name
|
|
349
|
+
straight back. Clear that state when `finished` resolves — a name left behind is
|
|
350
|
+
claimed twice by the next transition, and that one is dropped for it.
|
|
422
351
|
|
|
423
352
|
```jsx
|
|
424
353
|
land: (event) => {
|
|
@@ -438,29 +367,17 @@ land: (event) => {
|
|
|
438
367
|
};
|
|
439
368
|
```
|
|
440
369
|
|
|
441
|
-
The roles are names because a `::view-transition` pseudo
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
```
|
|
370
|
+
The roles are names because a `::view-transition` pseudo is selected by name and
|
|
371
|
+
by nothing else — which is how one piece is told to pass over the other, and the
|
|
372
|
+
travel given a length worth a card crossing a board. Address both
|
|
373
|
+
`::view-transition-group(…)` and `::view-transition-image-pair(…)`: the morph
|
|
374
|
+
lives in one, anything a style adds rides in the other. The whole board is in
|
|
375
|
+
`38_interactions_demo.html`.
|
|
457
376
|
|
|
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
|
|
460
|
-
|
|
461
|
-
|
|
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.
|
|
377
|
+
The drop hint follows what a place is: a line drawn in the gap for `reorder`, the
|
|
378
|
+
place itself lit up for `land`. Both are drawn among the places — the parent, or
|
|
379
|
+
the `data-drop-container` — so the variables dressing them are set on the list,
|
|
380
|
+
the board or the surface and reach them by inheritance:
|
|
464
381
|
|
|
465
382
|
| Variable | Dresses |
|
|
466
383
|
| ------------------------------------------------------------------------------------------ | ----------------------- |
|
|
@@ -471,10 +388,10 @@ thing it copies, and dressed by where that thing stands.
|
|
|
471
388
|
|
|
472
389
|
### Saying the grab is acquired: `grab`
|
|
473
390
|
|
|
474
|
-
The
|
|
475
|
-
one instant that counts for the hand: the one where the object stops being
|
|
476
|
-
and starts being held
|
|
477
|
-
|
|
391
|
+
The outcomes answer the **release**. Between the press and the release there is
|
|
392
|
+
one instant that counts for the hand: the one where the object stops being
|
|
393
|
+
pressed and starts being held — the same one whichever way the drag was entered,
|
|
394
|
+
a finger held still or a mouse travelled a few pixels. `grab` is that instant.
|
|
478
395
|
|
|
479
396
|
```jsx
|
|
480
397
|
<Box
|
|
@@ -485,36 +402,26 @@ was entered, a finger held still or a mouse travelled a few pixels.
|
|
|
485
402
|
/>
|
|
486
403
|
```
|
|
487
404
|
|
|
488
|
-
It matters most where it is least visible. On a screen the held object is under
|
|
489
|
-
thumb that hides it, so the only feedback available is the one that is felt;
|
|
490
|
-
it the hand waits, doubts the press was heard, and lets go too early —
|
|
491
|
-
gesture fails, not its decoration. With a mouse the
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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`.
|
|
405
|
+
It matters most where it is least visible. On a screen the held object is under
|
|
406
|
+
the thumb that hides it, so the only feedback available is the one that is felt;
|
|
407
|
+
without it the hand waits, doubts the press was heard, and lets go too early —
|
|
408
|
+
the whole gesture fails, not its decoration. (With a mouse the object has visibly
|
|
409
|
+
moved by then.) If the moment is for **paint**, no listener is needed: navi puts
|
|
410
|
+
`data-grabbed` on the element for as long as the gesture holds it. `grab` is for
|
|
411
|
+
what a stylesheet cannot do — a vibration, a state kept elsewhere, a counter.
|
|
508
412
|
|
|
509
|
-
|
|
510
|
-
|
|
413
|
+
It is not an interaction on its own: declared without an outcome or `moving`
|
|
414
|
+
there is no gesture for it to begin, and a dev warning says so — unless the
|
|
415
|
+
element declares `pan` or `zoom`, whose moment it then is
|
|
416
|
+
([pan_zoom.md](./pan_zoom.md)). A `longpress` needs none of this: it already
|
|
417
|
+
happens at the moment the hold is acquired.
|
|
511
418
|
|
|
512
419
|
### Saying the hold is let go: `release`
|
|
513
420
|
|
|
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.
|
|
421
|
+
The mirror of `grab`, and the only interaction of the family always told. Each
|
|
422
|
+
outcome answers ONE meaning, so a release that means none of them — let go of
|
|
423
|
+
over nothing, taken away by the system — reaches nobody, and whatever `grab` set
|
|
424
|
+
up (a counter of what is in the hand) stays set up with nothing to take it down.
|
|
518
425
|
|
|
519
426
|
```jsx
|
|
520
427
|
<Chip
|
|
@@ -526,28 +433,20 @@ up stays set up with nothing to take it down.
|
|
|
526
433
|
/>
|
|
527
434
|
```
|
|
528
435
|
|
|
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.
|
|
436
|
+
`outcome` is which one is about to answer — `"move"`, `"reorder"`, `"land"`,
|
|
437
|
+
`"toss"`, `"leave"` — or `null` when the release means none of them. It is told
|
|
438
|
+
**before** that answer runs, so what the grab put up comes down the moment the
|
|
439
|
+
hand lets go, while still knowing whether something is on its way. Like `grab`,
|
|
440
|
+
it needs a drag to happen in, and a dev warning says so.
|
|
542
441
|
|
|
543
442
|
### The hand pulls and nothing follows: `refuse`
|
|
544
443
|
|
|
545
444
|
Something that can be carried is not always free to be: a court whose place on the
|
|
546
445
|
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
|
-
|
|
446
|
+
`move: false` — makes the element **deaf** rather than locked: the press is
|
|
447
|
+
nobody's, so whatever it stands on answers it (the surface pans under an object
|
|
448
|
+
the hand was aiming at), and the pull is told nothing — a thing that does not move
|
|
449
|
+
and says nothing reads as a broken screen, so the hand insists.
|
|
551
450
|
|
|
552
451
|
```jsx
|
|
553
452
|
<Court
|
|
@@ -563,44 +462,50 @@ move and says nothing reads as a screen that is broken, so the hand insists.
|
|
|
563
462
|
/>
|
|
564
463
|
```
|
|
565
464
|
|
|
566
|
-
So the interaction stays declared and says `"refuse"` in place of what it does.
|
|
567
|
-
threshold is the same one
|
|
568
|
-
|
|
569
|
-
acquired there is none: nothing translates, no copy is made, no
|
|
570
|
-
answered. `refuse` is that instant,
|
|
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
|
-
|
|
465
|
+
So the interaction stays declared and says `"refuse"` in place of what it does.
|
|
466
|
+
The threshold is the same one — a mouse travelling, a finger holding still, a
|
|
467
|
+
mouse's few pixels inside a `data-drag-on-contact` — and at the instant the grab
|
|
468
|
+
would have been acquired there is none: nothing translates, no copy is made, no
|
|
469
|
+
release is answered. `refuse` is told at that instant, where feedback is
|
|
470
|
+
expected; it has no `gestureInfo`, there being no gesture, and `pointerType`
|
|
471
|
+
says which hand asked — a vibration for a finger, nothing extra for a mouse whose
|
|
472
|
+
cursor has already said it. One outcome refusing refuses the whole carry.
|
|
473
|
+
|
|
474
|
+
**It walks no axis, so it takes none.** Whatever reads what a drag source walks
|
|
475
|
+
to know what is left for itself — a box that travels, a surface that pans — steps
|
|
476
|
+
over an element that is refusing: a swipe starting on a locked row is the row of
|
|
477
|
+
slides' swipe, a drag starting on a pinned court pans the plan. A thing that
|
|
478
|
+
cannot be taken hold of is exactly the one a hand rests on without thinking, and
|
|
479
|
+
a surface with a dead zone the size of an object in the middle of it is wrong
|
|
480
|
+
every time. For the axes, `move: "refuse"` is `move: false`. For the press it is
|
|
481
|
+
not:
|
|
482
|
+
|
|
483
|
+
**The press itself stays the element's**, settled there like any gesture settles
|
|
484
|
+
one: the pointer is taken, so a `longpress` declared beside the drag does not
|
|
485
|
+
answer afterwards, and the click the release leaves behind is swallowed. What is
|
|
486
|
+
locked behaves like what is not, up to the moment it says no. The exception is a
|
|
487
|
+
box declaring `pan`/`zoom`, the one thing that takes a press whole and in every
|
|
488
|
+
direction: there the surface keeps it, and the refusal is told without taking
|
|
489
|
+
anything — no pointer, no click, nothing prevented.
|
|
490
|
+
|
|
491
|
+
### A press made elsewhere: `--navi-grab`
|
|
492
|
+
|
|
493
|
+
A hold opens a popup, and something in it is what the hand meant to take:
|
|
494
|
+
`triggerNaviCommand(element, "--navi-grab", openEvent)` hands the press still
|
|
495
|
+
down to that element (or the drag source around it), so the finger carries it
|
|
496
|
+
without lifting and pressing again. It brings **a copy only**, to the finger,
|
|
497
|
+
once the popup has settled: `move` and `moving` carry nothing there (a dev
|
|
498
|
+
warning says so), and let go of before it has travelled — the hold only meant to
|
|
499
|
+
open the popup — the copy goes back and nothing is answered. Its interactions
|
|
500
|
+
are chained to that request rather than to a press on the element, so
|
|
501
|
+
`event.detail.event` is the request: reach the `pointerdown` with
|
|
502
|
+
`findEvent(event, "pointerdown")`.
|
|
596
503
|
|
|
597
504
|
### Dressing the clone
|
|
598
505
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
clone's look is the page's to declare, through the attributes the gesture puts on
|
|
603
|
-
it:
|
|
506
|
+
A copy of a transparent element is invisible — a row usually gets its background
|
|
507
|
+
from the list around it, which the copy has left. So the clone's look is the
|
|
508
|
+
page's to declare, through the attributes the gesture puts on it:
|
|
604
509
|
|
|
605
510
|
| Attribute | On |
|
|
606
511
|
| ------------------------- | ---------------------------------------------------- |
|
|
@@ -615,17 +520,19 @@ it:
|
|
|
615
520
|
}
|
|
616
521
|
```
|
|
617
522
|
|
|
618
|
-
Reusing the item's own class is the point: the copy is that item,
|
|
619
|
-
|
|
523
|
+
Reusing the item's own class is the point: the copy is that item, styled as that
|
|
524
|
+
item plus whatever being carried changes.
|
|
620
525
|
|
|
621
526
|
**Which is also the trap, for anything positioned on a board.** The copy is a
|
|
622
|
-
deep clone
|
|
623
|
-
|
|
624
|
-
`
|
|
625
|
-
`width: calc(50% - 2 * var(--gap))` then means half of the carrier
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
527
|
+
deep clone put in a carrier box, so a geometry written in the style attribute
|
|
528
|
+
comes with it. navi takes back where it stands (`position`, `inset`, `margin` and
|
|
529
|
+
`translate` are reset on the copy) but not its size —
|
|
530
|
+
`width: calc(50% - 2 * var(--gap))` then means half of the carrier, and the piece
|
|
531
|
+
is carried at the wrong size. Put what a piece LOOKS like in a class, leave only
|
|
532
|
+
which place it is inline (two custom properties will do), and let it fill its
|
|
533
|
+
carrier — said of what is inside the wrapper rather than of `[navi-drag-clone]`,
|
|
534
|
+
because the copy loses that mark as it lands (that is how it drops its lift for
|
|
535
|
+
the transition) and has to keep its size all the way down:
|
|
629
536
|
|
|
630
537
|
```css
|
|
631
538
|
[navi-drag-clone-wrapper] .piece {
|
|
@@ -634,24 +541,18 @@ properties will do), then let it fill its carrier:
|
|
|
634
541
|
}
|
|
635
542
|
```
|
|
636
543
|
|
|
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:
|
|
544
|
+
Read off the dragged element, so a whole list or a single item can answer:
|
|
643
545
|
|
|
644
546
|
| Variable | What it changes |
|
|
645
547
|
| --------------------- | ------------------------------------------------------------- |
|
|
646
548
|
| `--drag-clone-shadow` | what being lifted casts; `none` for something that flies flat |
|
|
647
549
|
| `--drag-clone-scale` | how much bigger it gets once picked up; `1` to keep its size |
|
|
648
550
|
|
|
649
|
-
**What stays behind is the source, not a hole.** The original
|
|
650
|
-
the
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
back from the hidden source:
|
|
551
|
+
**What stays behind is the source, not a hole.** The original keeps its place in
|
|
552
|
+
the layout, only `visibility: hidden`, until the answer settles — so it says
|
|
553
|
+
where the thing left from for as long as the question is open. A mark left where
|
|
554
|
+
the thing was — an imprint, a dashed outline — is drawn ON that element, and its
|
|
555
|
+
parts say `visibility: visible` to come back from the hidden source:
|
|
655
556
|
|
|
656
557
|
```css
|
|
657
558
|
.paper[navi-drag-clone-source]::after {
|
|
@@ -664,17 +565,6 @@ back from the hidden source:
|
|
|
664
565
|
}
|
|
665
566
|
```
|
|
666
567
|
|
|
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
568
|
### What says a thing can be picked up
|
|
679
569
|
|
|
680
570
|
Almost nothing, on purpose. A **handle** (`data-drag-handle`) exists only to drag,
|
|
@@ -686,8 +576,8 @@ text inside cannot be selected either — a finger is another matter, see below)
|
|
|
686
576
|
|
|
687
577
|
So on a board where a piece is also clickable, the cursor is already spoken for and
|
|
688
578
|
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
|
-
|
|
579
|
+
shadow appearing under the pointer, a handle. Decide it rather than default it: a
|
|
580
|
+
board one may drag on is worth nothing if nobody tries.
|
|
691
581
|
|
|
692
582
|
### The text inside: `user-select: none`
|
|
693
583
|
|
|
@@ -711,9 +601,9 @@ a stylesheet, and it has to be true before the press:
|
|
|
711
601
|
|
|
712
602
|
**Most of the time that is what you want.** What can be picked up carries a
|
|
713
603
|
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
|
-
|
|
604
|
+
press is there for the drag. Keep the selection where the text IS the point (a
|
|
605
|
+
note one copies from) and give that one a `data-drag-handle`: the grip drags, the
|
|
606
|
+
text stays text.
|
|
717
607
|
|
|
718
608
|
navi says it on its own in one place only, inside `[data-drag-on-contact]`, where
|
|
719
609
|
there is no wait left to answer the finger with.
|
|
@@ -721,42 +611,49 @@ there is no wait left to answer the finger with.
|
|
|
721
611
|
## A gesture whose product is a value
|
|
722
612
|
|
|
723
613
|
`move`, `reorder`, `land`, `toss` and `leave` answer the same question — where did
|
|
724
|
-
the element end up
|
|
725
|
-
|
|
726
|
-
|
|
614
|
+
the element end up. A rotation does not: what comes out of it is an angle, and
|
|
615
|
+
the handle that produced it stays exactly where it is. Same for a scale, or a sun
|
|
616
|
+
dragged around a plan to set the hour.
|
|
727
617
|
|
|
728
|
-
What navi names is never the value
|
|
618
|
+
What navi names is never the value — it does not know that these pixels are an
|
|
729
619
|
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
|
-
|
|
620
|
+
kept — but the **arbitration**: a press that could have been a tap, a scroll, a
|
|
621
|
+
page travelling, the surface underneath. Once that is settled it hands the
|
|
622
|
+
numbers over. **A value computed from how far a handle was taken is `moving`**:
|
|
623
|
+
the deltas from the grab, every frame, nothing translated, and `move` beside it
|
|
624
|
+
when the release commits something. A value read off a surface is `pan` and
|
|
625
|
+
`zoom` ([pan_zoom.md](./pan_zoom.md)). Both are `interactions` work.
|
|
626
|
+
|
|
627
|
+
What is left is a gesture whose SHAPE is none of navi's: it does not begin on an
|
|
628
|
+
element that could be carried, or on a surface — it begins wherever the
|
|
629
|
+
application says, on its own rules. Then navi hands over the machinery and leaves
|
|
630
|
+
the paint alone; the options are in the JSDoc of `dragAfterIntent`,
|
|
631
|
+
`markDragSource` and `createDragGestureController`:
|
|
741
632
|
|
|
742
633
|
```js
|
|
743
|
-
import {
|
|
634
|
+
import {
|
|
635
|
+
createDragGestureController,
|
|
636
|
+
dragAfterIntent,
|
|
637
|
+
markDragSource,
|
|
638
|
+
} from "@jsenv/navi";
|
|
639
|
+
|
|
640
|
+
// At mount, not in the pointerdown.
|
|
641
|
+
const unmarkDragSource = markDragSource(element, "xy");
|
|
744
642
|
|
|
745
643
|
const onPointerDown = (pointerDownEvent) => {
|
|
746
|
-
//
|
|
747
|
-
// when `currentTarget` is null.
|
|
748
|
-
const element = pointerDownEvent.currentTarget;
|
|
644
|
+
// `element`, not `pointerDownEvent.currentTarget`: the callback runs once the
|
|
645
|
+
// intent shows, after dispatch, when `currentTarget` is null.
|
|
749
646
|
dragAfterIntent(pointerDownEvent, () => {
|
|
750
647
|
const controller = createDragGestureController({
|
|
751
648
|
onDrag: (gestureInfo) => {
|
|
752
649
|
const { xDelta, yDelta } = gestureInfo.layout;
|
|
753
|
-
|
|
650
|
+
valueSignal.value = valueFrom(xDelta, yDelta);
|
|
754
651
|
},
|
|
755
652
|
onRelease: (gestureInfo) => {
|
|
756
653
|
if (gestureInfo.cancelled) {
|
|
757
654
|
return;
|
|
758
655
|
}
|
|
759
|
-
|
|
656
|
+
commit(valueSignal.peek());
|
|
760
657
|
},
|
|
761
658
|
});
|
|
762
659
|
return controller.grabViaPointer(pointerDownEvent, { element });
|
|
@@ -764,40 +661,39 @@ const onPointerDown = (pointerDownEvent) => {
|
|
|
764
661
|
};
|
|
765
662
|
```
|
|
766
663
|
|
|
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
|
-
stylesheet, on the source itself — which is how what surrounds it keeps
|
|
783
|
-
scrolling. A blanket `touch-action: none` on the container buys the same drag by
|
|
784
|
-
taking the pan away, and the pan then has to be written by hand too.
|
|
664
|
+
An element that ends up somewhere is NOT this: that one is `move` — or `land`
|
|
665
|
+
when what it ends up on is a surface — and doing it by hand gives up the
|
|
666
|
+
constraint, the commit and the way back when the answer refuses. What the
|
|
667
|
+
machinery brings is the reason not to write it again:
|
|
668
|
+
|
|
669
|
+
- **When the press becomes a drag** is `dragAfterIntent`: a `data-drag-handle`
|
|
670
|
+
on contact, a mouse by distance, a finger by TIME, because travel is exactly
|
|
671
|
+
what a scroll looks like — one timer for every pointer makes the mouse wait for
|
|
672
|
+
something it never had to prove.
|
|
673
|
+
- **A touch has to be refusable before it is refused**: `markDragSource` goes
|
|
674
|
+
down at mount, on the source itself, so what surrounds it keeps scrolling (the
|
|
675
|
+
two conditions are in
|
|
676
|
+
[mobile_touch.md](./mobile_touch.md#the-browser-decides-about-8px-in-and-does-not-wait)).
|
|
677
|
+
A blanket `touch-action: none` on the container buys the same drag by taking
|
|
678
|
+
the pan away, and the pan then has to be written by hand too.
|
|
785
679
|
- **A capture that goes was not necessarily given back.** `lostpointercapture`
|
|
786
|
-
reads the same whether the gesture handed the pointer over or the browser
|
|
787
|
-
it mid-drag, which it does more often than the specification suggests.
|
|
788
|
-
tells the two apart, and `gestureInfo.cancelled` is where it comes
|
|
789
|
-
nobody asked for must commit nothing.
|
|
680
|
+
reads the same whether the gesture handed the pointer over or the browser
|
|
681
|
+
dropped it mid-drag, which it does more often than the specification suggests.
|
|
682
|
+
The loop tells the two apart, and `gestureInfo.cancelled` is where it comes
|
|
683
|
+
out: a release nobody asked for must commit nothing.
|
|
790
684
|
|
|
791
|
-
A gesture driven this way is outside the registry, so it says so itself to
|
|
792
|
-
travels above it: `data-no-drag-travel` (see
|
|
685
|
+
A gesture driven this way is outside the registry, so it says so itself to
|
|
686
|
+
whatever travels above it: `data-no-drag-travel` (see
|
|
687
|
+
[drag_to_travel.md](./drag_to_travel.md#a-navi-component-that-reads-the-pointer-marks-itself)).
|
|
793
688
|
|
|
794
689
|
## Reference
|
|
795
690
|
|
|
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
|
|
691
|
+
- `src/control/interaction/interaction_drag.js` — the outcomes, `moving`, the
|
|
692
|
+
`grab`/`release`/`refuse` moments and `--navi-grab`.
|
|
693
|
+
- `@jsenv/dom` — `src/interaction/drag/drag_to.js` (`startDragTo`,
|
|
694
|
+
`refuseDragTo`), `drag_gesture.js` (the loop and what `gestureInfo` holds),
|
|
695
|
+
`drag_after_intent.js` (when a press becomes a drag, and what a source says in
|
|
696
|
+
the stylesheet).
|
|
801
697
|
- `src/control/demos/38_interactions_demo.html` — a board whose places are
|
|
802
698
|
zones and the same board whose places are the pieces.
|
|
803
699
|
- `src/control/demos/integration/5_plan_editor_demo.html` — a place that is a
|