@jsenv/navi 0.29.31 → 0.29.33
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 +2 -0
- package/dist/jsenv_navi.js +1136 -279
- package/dist/jsenv_navi.js.map +78 -36
- package/dist/jsenv_navi_side_effects.js +1 -0
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/docs/AI_INSTRUCTIONS.md +41 -6
- package/docs/control_group.md +4 -1
- package/docs/interactions.md +165 -14
- package/package.json +1 -1
package/docs/interactions.md
CHANGED
|
@@ -62,7 +62,8 @@ condition: `{ swipe_right: canArchive && archive }`.
|
|
|
62
62
|
| `mousedown` `mouseup` `click` `dblclick` `contextmenu` | the browser's own events |
|
|
63
63
|
| `swipe_left` `swipe_right` `swipe_up` `swipe_down` | a press that travels |
|
|
64
64
|
| `longpress` | a press held still |
|
|
65
|
-
| `move` `reorder` `toss`
|
|
65
|
+
| `move` `reorder` `land` `toss` | the element carried, and what letting go means |
|
|
66
|
+
| `grab` | the instant a drag takes hold of it |
|
|
66
67
|
| `"keyboard:<shortcut>"` | keys, e.g. `"keyboard:ctrl+backspace"` |
|
|
67
68
|
|
|
68
69
|
A name nothing knows how to detect produces a dev warning naming the detectors
|
|
@@ -141,17 +142,18 @@ comes back once it settles — a failure leaves the row in place so it can be tr
|
|
|
141
142
|
again. What a success does to the element is yours (a list that redemands its
|
|
142
143
|
rows, a row that leaves): navi does not make it disappear.
|
|
143
144
|
|
|
144
|
-
## Carrying something: `move`, `reorder`, `toss`
|
|
145
|
+
## Carrying something: `move`, `reorder`, `land`, `toss`
|
|
145
146
|
|
|
146
|
-
All
|
|
147
|
+
All four are the same gesture — the element is picked up and carried — and what
|
|
147
148
|
differs is the release. One detector reads them all, because it is one press.
|
|
148
149
|
|
|
149
|
-
`
|
|
150
|
-
places, thrown far and fast it is gotten rid of. `move
|
|
151
|
-
`reorder`
|
|
152
|
-
|
|
150
|
+
`toss` **combines** with either of the two others: dropped on another item the
|
|
151
|
+
element changes places, thrown far and fast it is gotten rid of. `move`,
|
|
152
|
+
`reorder` and `land` do **not** combine with each other — an element either goes
|
|
153
|
+
where it is put, takes a place in a list, or comes down on a place, and one
|
|
154
|
+
release cannot mean two of those (a dev warning says so).
|
|
153
155
|
|
|
154
|
-
`move` carries the element ITSELF and leaves it where it was put; the
|
|
156
|
+
`move` carries the element ITSELF and leaves it where it was put; the others
|
|
155
157
|
carry a copy and put the original back. That is the same difference said in layout
|
|
156
158
|
terms: something moved has a new place of its own, something reordered had its
|
|
157
159
|
place taken by the list.
|
|
@@ -228,6 +230,109 @@ name what moves.
|
|
|
228
230
|
| `data-drag-delay` `data-drag-slop` `data-drag-threshold` | when the press becomes a grab |
|
|
229
231
|
| `data-toss-distance` `data-toss-speed` | how far and how fast counts as a throw |
|
|
230
232
|
|
|
233
|
+
### Landing on a place: `land`
|
|
234
|
+
|
|
235
|
+
`reorder` and `land` both come down on an item, and what separates them is **what
|
|
236
|
+
a place is**. A row of a list is a place BETWEEN two others — free by
|
|
237
|
+
construction, so the answer is an insertion and putting a row back where it
|
|
238
|
+
already was is a no-op. A place of a board is a place of its own, which may
|
|
239
|
+
already be taken — so nothing is inserted, nothing is a no-op, and the answer is
|
|
240
|
+
simply "this one came down on that one". What that means is yours: take the
|
|
241
|
+
place, swap the two, refuse.
|
|
242
|
+
|
|
243
|
+
```jsx
|
|
244
|
+
<Box
|
|
245
|
+
id={playerId}
|
|
246
|
+
interactions={{
|
|
247
|
+
land: (event) => {
|
|
248
|
+
const { fromId, toId, syncCloneWithDropTarget } = event.detail;
|
|
249
|
+
return document.startViewTransition(() => {
|
|
250
|
+
syncCloneWithDropTarget();
|
|
251
|
+
setLineup((previous) => swapPlaces(previous, fromId, toId));
|
|
252
|
+
}).finished;
|
|
253
|
+
},
|
|
254
|
+
}}
|
|
255
|
+
/>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`toId` is an element and **never null**: a copy over nothing is a release that
|
|
259
|
+
meant nothing, and the interaction does not happen at all.
|
|
260
|
+
|
|
261
|
+
**Which elements are places: those marked `data-droppable`, and only those.**
|
|
262
|
+
Declaring `land` says an element can be CARRIED, which on a board is a different
|
|
263
|
+
thing from being somewhere one can be put — a zone receives without ever being
|
|
264
|
+
carried, a piece is carried without ever receiving, and both at once is a third
|
|
265
|
+
case (dropped on a piece, the two swap, so it says `data-droppable` as well). A
|
|
266
|
+
list has no such distinction, every row being both, which is why `reorder` needs
|
|
267
|
+
no marker in the markup.
|
|
268
|
+
|
|
269
|
+
Places are looked for among the carried element's **siblings**, so a piece must
|
|
270
|
+
not be nested inside its place: nested, it would see that one place and have
|
|
271
|
+
nowhere else to go. Draw the pieces beside the places, positioned over them.
|
|
272
|
+
|
|
273
|
+
**When the place is bigger than what stands on it** — a zone holding a smaller
|
|
274
|
+
card, a square holding a piece — the copy must not take the place's box, or it
|
|
275
|
+
resizes on landing and resizes back when the real element appears.
|
|
276
|
+
`syncCloneWithDropTarget` takes an element for that: the copy comes down on THAT
|
|
277
|
+
box instead of the target's. Pass whatever occupies the destination — the piece
|
|
278
|
+
already standing there, the empty slot waiting.
|
|
279
|
+
|
|
280
|
+
```jsx
|
|
281
|
+
land: (event) => {
|
|
282
|
+
const { fromId, toId, syncCloneWithDropTarget } = event.detail;
|
|
283
|
+
const landingElement = pieceAt(toId) || slotOf(toId);
|
|
284
|
+
return document.startViewTransition(() => {
|
|
285
|
+
syncCloneWithDropTarget(landingElement);
|
|
286
|
+
…
|
|
287
|
+
}).finished;
|
|
288
|
+
};
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
The hint follows what a place is: a line drawn in the gap for `reorder`, the
|
|
292
|
+
place itself lit up for `land`. Both are drawn inside the carried element's
|
|
293
|
+
parent, so the variables dressing them are read from the list or the board and
|
|
294
|
+
reach them by plain inheritance.
|
|
295
|
+
|
|
296
|
+
| Variable | Dresses |
|
|
297
|
+
| ------------------------------------------------------------------------------------------ | ----------------------- |
|
|
298
|
+
| `--drop-hint-size` `--drop-hint-background-color` `--drop-hint-border-radius` | the line of a reorder |
|
|
299
|
+
| `--drop-hint-margin-x` `--drop-hint-margin-y` `--drop-hint-arrow-size` | where it sits, its caps |
|
|
300
|
+
| `--drop-surface-border-width` `--drop-surface-border-color` `--drop-surface-border-radius` | the lit place of a land |
|
|
301
|
+
| `--drop-surface-background-color` | and its fill |
|
|
302
|
+
|
|
303
|
+
### Saying the grab is acquired: `grab`
|
|
304
|
+
|
|
305
|
+
The three above answer the **release**. Between the press and the release there is
|
|
306
|
+
one instant that counts for the hand: the one where the object stops being pressed
|
|
307
|
+
and starts being held. `grab` is that instant — the same one whichever way the drag
|
|
308
|
+
was entered, a finger held still or a mouse travelled a few pixels.
|
|
309
|
+
|
|
310
|
+
```jsx
|
|
311
|
+
<Box
|
|
312
|
+
interactions={{
|
|
313
|
+
toss: (event) => remove(event.detail.id),
|
|
314
|
+
grab: () => navigator.vibrate?.(10),
|
|
315
|
+
}}
|
|
316
|
+
/>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
It matters most where it is least visible. On a screen the held object is under the
|
|
320
|
+
thumb that hides it, so the only feedback available is the one that is felt; without
|
|
321
|
+
it the hand waits, doubts the press was heard, and lets go too early — the whole
|
|
322
|
+
gesture fails, not its decoration. With a mouse the grab is acquired after a few
|
|
323
|
+
pixels and the object has visibly moved, so the answer is already there.
|
|
324
|
+
|
|
325
|
+
Nothing here is about vibration: a sound, a class, a measure are the same moment.
|
|
326
|
+
|
|
327
|
+
`grab` **reports, it does not ask**: what it returns is not waited on, and
|
|
328
|
+
preventing its event does not call the gesture off. And it is not an interaction on
|
|
329
|
+
its own — declared without `move`, `reorder` or `toss` there is no gesture for it to
|
|
330
|
+
be the beginning of, and a dev warning says so. Its detail carries `pointerType` and
|
|
331
|
+
the `gestureInfo`.
|
|
332
|
+
|
|
333
|
+
A `longpress` needs none of this: it already happens at the moment the hold is
|
|
334
|
+
acquired, not at the release.
|
|
335
|
+
|
|
231
336
|
### Dressing the clone
|
|
232
337
|
|
|
233
338
|
What the pointer carries is a copy, and a copy of a transparent element is
|
|
@@ -250,7 +355,27 @@ it:
|
|
|
250
355
|
```
|
|
251
356
|
|
|
252
357
|
Reusing the item's own class is the point: the copy is that item, so it is styled
|
|
253
|
-
as that item plus whatever being carried changes.
|
|
358
|
+
as that item plus whatever being carried changes.
|
|
359
|
+
|
|
360
|
+
**Which is also the trap, for anything positioned on a board.** The copy is the
|
|
361
|
+
same element re-parented into a carrier box, so a geometry written in the style
|
|
362
|
+
attribute follows it there — `width: calc(50% - 2 * var(--gap))` then means half
|
|
363
|
+
of the copy instead of half of the board, and the piece is carried at the wrong
|
|
364
|
+
size. Put what a piece LOOKS like in a class and leave only which place it is
|
|
365
|
+
inline (two custom properties will do), then let it fill its carrier:
|
|
366
|
+
|
|
367
|
+
```css
|
|
368
|
+
[navi-drag-clone-wrapper] .piece {
|
|
369
|
+
position: static;
|
|
370
|
+
width: 100%;
|
|
371
|
+
height: 100%;
|
|
372
|
+
translate: none;
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
Said of what is inside the wrapper rather than of `[navi-drag-clone]` itself,
|
|
377
|
+
because the copy loses that mark as it lands — that is how it drops its lift for
|
|
378
|
+
the transition — and it has to keep its size all the way down. The copy is a real element in the
|
|
254
379
|
page, in the top layer, and everything about its look is reachable from CSS —
|
|
255
380
|
including these, read off the dragged element so a whole list or a single item can
|
|
256
381
|
answer:
|
|
@@ -258,7 +383,7 @@ answer:
|
|
|
258
383
|
| Variable | What it changes |
|
|
259
384
|
| --------------------- | ------------------------------------------------------------- |
|
|
260
385
|
| `--drag-clone-shadow` | what being lifted casts; `none` for something that flies flat |
|
|
261
|
-
| `--drag-clone-scale` | how much bigger it gets once picked up
|
|
386
|
+
| `--drag-clone-scale` | how much bigger it gets once picked up; `1` to keep its size |
|
|
262
387
|
|
|
263
388
|
**What stays behind is the source, not a hole.** The original is never taken out of
|
|
264
389
|
the page — it keeps its place in the layout and wears `navi-drag-clone-source`,
|
|
@@ -284,9 +409,32 @@ back to it.
|
|
|
284
409
|
|
|
285
410
|
`data-drag-axis` says which axes the drag walks, and its default is not the same
|
|
286
411
|
for every outcome: `reorder` alone walks the list (`y`, or `x` for a list that runs
|
|
287
|
-
sideways), while a `move` goes wherever it is put
|
|
288
|
-
thrown (`xy`). `data-drag-delay`,
|
|
289
|
-
the press becomes a grab.
|
|
412
|
+
sideways), while a `move` goes wherever it is put, a `land` wherever the board has
|
|
413
|
+
places and a `toss` wherever it was thrown (`xy`). `data-drag-delay`,
|
|
414
|
+
`data-drag-slop`, `data-drag-threshold` tune when the press becomes a grab.
|
|
415
|
+
|
|
416
|
+
### A control inside something draggable
|
|
417
|
+
|
|
418
|
+
`data-drag-ignore` says the press there is none of the gesture's business: the
|
|
419
|
+
element under it never starts a drag, and keeps both its cursor and its text
|
|
420
|
+
selection. That is what a button living inside a carried piece needs — a cross
|
|
421
|
+
that removes it, a menu — otherwise the piece is picked up from the button like
|
|
422
|
+
from anywhere else. A click of its own usually has to stop there too, or the
|
|
423
|
+
piece reads it as its own click.
|
|
424
|
+
|
|
425
|
+
### What says a thing can be picked up
|
|
426
|
+
|
|
427
|
+
Almost nothing, on purpose. A **handle** (`data-drag-handle`) exists only to drag,
|
|
428
|
+
so it shows the hand; a **source** does not — it drags only once the intent shows,
|
|
429
|
+
a plain click on it stays a click, and it is usually something else FIRST (a link,
|
|
430
|
+
a card one opens). The cursor says what an element IS, and the gesture is not the
|
|
431
|
+
one who knows, so it leaves it alone (`default`, not an I-beam: the text inside
|
|
432
|
+
cannot be selected either).
|
|
433
|
+
|
|
434
|
+
So on a board where a piece is also clickable, the cursor is already spoken for and
|
|
435
|
+
the affordance has to be said in the piece itself — a grip mark in a corner, a
|
|
436
|
+
shadow appearing under the pointer, a handle. It is worth deciding, not defaulting:
|
|
437
|
+
a board one may drag on is worth nothing if nobody tries.
|
|
290
438
|
|
|
291
439
|
## Tuning
|
|
292
440
|
|
|
@@ -400,7 +548,10 @@ container above it does not take the gesture:
|
|
|
400
548
|
the registry.
|
|
401
549
|
- `src/control/interaction/interaction_press.js` — swipes and holds, and what a
|
|
402
550
|
swipe writes on the element.
|
|
551
|
+
- `src/control/interaction/interaction_drag.js` — `move`, `reorder`, `land`,
|
|
552
|
+
`toss` and the `grab` moment.
|
|
403
553
|
- `src/control/interaction/interaction_keyboard.js`,
|
|
404
554
|
`interaction_native.js` — the other two detectors.
|
|
405
555
|
- `src/control/demos/38_interactions_demo.html` — every case above, plus a
|
|
406
|
-
mailbox and
|
|
556
|
+
mailbox, a board whose places are zones and the same board whose places are the
|
|
557
|
+
pieces, and a custom gesture registered from the page.
|