@jsenv/navi 0.29.194 → 0.29.195

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.
@@ -227,7 +227,7 @@ outside it.
227
227
  A drag reads the same press a travel does and holds the pointer from it, so the
228
228
  two share a finger exactly as two travelling boxes do: what is picked up says
229
229
  which axes it walks (`data-drag-source`, written from `data-drag-axis` by
230
- `interactions={{ move, reorder, land, toss }}` — see `docs/interactions.md`), and
230
+ `interactions={{ move, reorder, land, toss, leave }}` — see `docs/interactions.md`), and
231
231
  the box above keeps what is left. A list reordered along its own line inside a
232
232
  row of slides swiped sideways: both gestures live, and neither had to be told
233
233
  about the other.
@@ -565,7 +565,7 @@ outside — and will not find out until they watch a page leave under their
565
565
  finger. The component knows, so the component says it.
566
566
 
567
567
  _Currently marked: the wheel viewport, the table resize handles, the cells of a
568
- table whose columns can be dragged._
568
+ table whose columns can be dragged, a surface declaring `pan` or `zoom`._
569
569
 
570
570
  ## The two consumers that travel between screens
571
571
 
@@ -40,9 +40,13 @@ one — `Box`, `List.Item`, `Button`, `Link`, the field components. Its keys are
40
40
  />
41
41
  ```
42
42
 
43
- `action` is untouched by this and keeps its own wiring — a click on a button, a
44
- change on a field. `interactions` is the other half: everything that is not that
45
- natural one.
43
+ `action` is a control's prop and is untouched by this: a control keeps its own
44
+ wiring — a click on a button, a change on a field — and `interactions` is the
45
+ other half, everything that is not that natural one. A plain `Box` has no such
46
+ wiring: it does nothing with `action` (dev warns), and a click on it is declared
47
+ like any other interaction, `interactions={{ click: onSelect }}`. The click the
48
+ browser fires after a drag is already suppressed, so `click` sits next to `move`
49
+ or `grab` without fighting them.
46
50
 
47
51
  ### The three values
48
52
 
@@ -62,8 +66,9 @@ condition: `{ swipe_right: canArchive && archive }`.
62
66
  | `mousedown` `mouseup` `click` `dblclick` `contextmenu` | the browser's own events |
63
67
  | `swipe_left` `swipe_right` `swipe_up` `swipe_down` | a press that travels |
64
68
  | `longpress` | a press held still |
65
- | `move` `reorder` `land` `toss` | the element carried, and what letting go means |
69
+ | `move` `reorder` `land` `toss` `leave` | the element carried, and what letting go means |
66
70
  | `grab` | the instant a drag takes hold of it |
71
+ | `pan` `zoom` | a surface under the hand, or under a wheel |
67
72
  | `"keyboard:<shortcut>"` | keys, e.g. `"keyboard:ctrl+backspace"` |
68
73
 
69
74
  A name nothing knows how to detect produces a dev warning naming the detectors
@@ -156,17 +161,19 @@ comes back once it settles — a failure leaves the row in place so it can be tr
156
161
  again. What a success does to the element is yours (a list that redemands its
157
162
  rows, a row that leaves): navi does not make it disappear.
158
163
 
159
- ## Carrying something: `move`, `reorder`, `land`, `toss`
164
+ ## Carrying something: `move`, `reorder`, `land`, `toss`, `leave`
160
165
 
161
- All four are the same gesture — the element is picked up and carried — and what
166
+ All five are the same gesture — the element is picked up and carried — and what
162
167
  differs is the release. One detector reads them all, because it is one press.
163
168
 
164
- `toss` **combines** with `reorder` and with `land`: dropped on another item the
165
- element changes places, thrown far and fast it is gotten rid of. It does **not**
166
- combine with `move`, and neither do `reorder` and `land` with each other — one
167
- release cannot mean two of those. Declared together, the copy-carrying one wins
168
- and `move` is never answered — the element itself never travels, and a release
169
- that is not the other outcome means nothing at all (a dev warning says so).
169
+ `toss` and `leave` each **combine** with `reorder` and with `land`: dropped on
170
+ another item the element changes places, thrown far and fast it is gotten rid of,
171
+ let go of away from every place it leaves. `leave` combines with `move` as well.
172
+ `toss` does **not** combine with `move`, and neither do `reorder` and `land` with
173
+ each other — one release cannot mean two of those. Declared together, the
174
+ copy-carrying one wins and `move` is never answered — the element itself never
175
+ travels, and a release that is not the other outcome means nothing at all (a dev
176
+ warning says so).
170
177
 
171
178
  `move` carries the element ITSELF and leaves it where it was put; the others
172
179
  carry a copy and put the original back. That is the same difference said in layout
@@ -188,12 +195,22 @@ be a scroll container at all (`overflow` anything but `visible`), since there is
188
195
  nothing else for "inside" to mean. A `move` whose answer rejects travels back, because a
189
196
  place the application would not accept must not stay on screen as if it had.
190
197
 
198
+ **Where the position is kept is the answer's.** An element drawn from state —
199
+ `left`/`top` computed from a position the application holds — is redrawn by the
200
+ handler above, and from then on its layout owns the position: navi sees the
201
+ element drawn elsewhere while the answer was given and lets go of its own
202
+ translate, so the thing does not land twice as far as the hand went. A handler
203
+ that draws nothing (a token on a free canvas) leaves the position to the element,
204
+ where it is baked in. Nothing to declare either way — but a handler whose draw
205
+ comes later than its answer has to return the promise of that draw.
206
+
191
207
  **A copy that can be thrown frees its own area.** What is dragged is otherwise kept
192
208
  inside its scroll area — right for a reorder, since a row belongs to its list, and
193
209
  fatal for a throw: the copy hits the edge of the list, no distance is ever covered,
194
210
  so no throw can happen and no sideways movement is even visible. So `toss` lifts
195
- that constraint for the copy it carries — it is not a way to free a `move`, which
196
- says `data-drag-free`.
211
+ that constraint for the copy it carries, and `leave` lifts it for whatever it is
212
+ declared on — a thing that can be let go of outside has to be able to get there.
213
+ Neither is a way to free a plain `move`, which says `data-drag-free`.
197
214
 
198
215
  ```jsx
199
216
  <List.Item
@@ -213,7 +230,7 @@ says `data-drag-free`.
213
230
  ```
214
231
 
215
232
  The gesture is `startDragTo`'s, whole: `move` carries the element itself, the
216
- other two carry a copy above the page while the original keeps its place, with a
233
+ others carry a copy above the page while the original keeps its place, with a
217
234
  drop hint, drop targets found by intersection, no-op drops filtered out, and the
218
235
  flight of a thrown copy plus its return when the answer refuses. Only what the
219
236
  declared outcomes need runs — no copy for a move, no hint for something
@@ -246,36 +263,28 @@ name what moves.
246
263
  | `data-drag-delay` `data-drag-slop` `data-drag-threshold` | when the press becomes a grab |
247
264
  | `data-drag-on-contact` | a finger may drag by travelling too |
248
265
  | `data-toss-distance` `data-toss-speed` | how far and how fast counts as a throw |
249
- | `data-toss-by="throw"\|"release-outside"` | how a toss is made here |
250
266
  | `data-drop-container` | where the places are looked for |
251
267
 
252
- ### How a toss is made: `data-toss-by`
253
-
254
- `toss` names the outcome — the thing is gotten rid of — and a hand has two ways of
255
- saying it, each right where the other is wrong:
256
-
257
- - **`throw`** (the default): far **and** fast — that pair is what a flick is, and
258
- it is asked for together because one without the other is moving something while
259
- hesitating. A list's gesture. It is judged before any landing: a row sent across
260
- the screen has not asked to swap places with whatever it flew over.
261
- - **`release-outside`**: let go with no place under it. Dragging a marker off the plan it
262
- sits on and letting go is slow and deliberate — a surface's gesture, and the only
263
- one it wants, because on a plan a fast drag that ends ON it has not asked for the
264
- thing to go.
268
+ ### Let go of away from every place: `leave`
265
269
 
266
- The same `toss` either way, with the same flight off the screen and the same
267
- return if the answer rejects. Both at once is
268
- `data-toss-by="throw release-outside"`.
270
+ A throw is a **gesture**: far and fast, the flick that gets rid of a row, judged
271
+ before any landing. A release outside is a **place**: the hand let go with nothing
272
+ under the thing, judged after a landing was looked for. They share the outcome an
273
+ application usually attaches to them and nothing else — so `toss` is the throw,
274
+ `leave` is the release outside, and neither reads the other's rules. There is no
275
+ speed to a release; a fast drag across a plan that ends ON it has not asked for the
276
+ thing to go, and a row pulled sideways out of its list and let go is a row put
277
+ back, not a row deleted — a list declares `toss`, a surface declares `leave`.
269
278
 
270
279
  ```jsx
271
280
  <div data-drop-container>
272
- <Plan id="plan" data-droppable data-toss-by="release-outside">
281
+ <Plan id="plan" data-droppable>
273
282
  {markers.map((marker) => (
274
283
  <Marker
275
284
  id={marker.id}
276
285
  interactions={{
277
286
  land: (event) => moveTo(marker.id, event.detail),
278
- toss: () => remove(marker.id),
287
+ leave: () => remove(marker.id),
279
288
  }}
280
289
  />
281
290
  ))}
@@ -283,11 +292,36 @@ return if the answer rejects. Both at once is
283
292
  </div>
284
293
  ```
285
294
 
286
- `release-outside` is never the default because the same release means the
287
- opposite on a list: a row pulled sideways out of it and let go is a row put back, not a row
288
- deleted. And it needs places to be outside OF — declared next to `toss` alone,
289
- with nothing that can receive the thing, every release is outside and a dev
290
- warning says so.
295
+ `leave` combines with every other outcome. Beside `land` or `reorder`, "outside" is
296
+ away from every place. Beside `move` — the element itself travels, and nothing is a
297
+ place — it is outside the surface the element stands in: the nearest
298
+ `data-droppable` ancestor, or, without one, what can be seen of the scroll
299
+ container (a dev warning says which). Either way it is judged on the element's
300
+ box no longer overlapping it, not on the pointer, which is still well inside the
301
+ frame when a small marker has just left it.
302
+
303
+ ```jsx
304
+ <Plan data-droppable>
305
+ <Marker
306
+ interactions={{
307
+ move: (event) => remember(event.detail.x, event.detail.y),
308
+ leave: () => remove(marker.id),
309
+ }}
310
+ />
311
+ </Plan>
312
+ ```
313
+
314
+ Its detail is `toss`'s — `{ id, x, y }`, the distance travelled being what an exit
315
+ is animated with. Picked up and put straight back down stays a cancel: it has to
316
+ have gone somewhere to be away from anything.
317
+
318
+ What the thing does while the answer is asked follows what was carried. A copy
319
+ fades where it was let go of; the element itself stays where the hand put it. A
320
+ resolve takes the copy away and lets go of the element's position — the
321
+ application has removed the thing, or drawn it where it goes back to (the address
322
+ point returns to where the geocoder put it) — and a reject brings either back, as
323
+ a refused `move` does. A surface that clips its overflow clips the element on its
324
+ way out; the copy a `land` carries lives in the top layer and does not.
291
325
 
292
326
  ### A finger that does not have to wait: `data-drag-on-contact`
293
327
 
@@ -515,7 +549,7 @@ thing it copies, and dressed by where that thing stands.
515
549
 
516
550
  ### Saying the grab is acquired: `grab`
517
551
 
518
- The three above answer the **release**. Between the press and the release there is
552
+ The interactions above answer the **release**. Between the press and the release there is
519
553
  one instant that counts for the hand: the one where the object stops being pressed
520
554
  and starts being held. `grab` is that instant — the same one whichever way the drag
521
555
  was entered, a finger held still or a mouse travelled a few pixels.
@@ -539,8 +573,8 @@ Nothing here is about vibration: a sound, a class, a measure are the same moment
539
573
 
540
574
  `grab` **reports, it does not ask**: what it returns is not waited on, and
541
575
  preventing its event does not call the gesture off. And it is not an interaction on
542
- its own — declared without `move`, `reorder` or `toss` there is no gesture for it to
543
- be the beginning of, and a dev warning says so. Its detail carries `pointerType` and
576
+ its own — declared without one of the five above there is no gesture for it to be
577
+ the beginning of, and a dev warning says so. Its detail carries `pointerType` and
544
578
  the `gestureInfo`.
545
579
 
546
580
  A `longpress` needs none of this: it already happens at the moment the hold is
@@ -623,7 +657,8 @@ back to it.
623
657
  `data-drag-axis` says which axes the drag walks, and its default is not the same
624
658
  for every outcome: `reorder` alone walks the list (`y`, or `x` for a list that runs
625
659
  sideways), while a `move` goes wherever it is put, a `land` wherever the board has
626
- places and a `toss` wherever it was thrown (`xy`). `data-drag-delay`,
660
+ places, a `toss` wherever it was thrown and a `leave` out by whichever edge (`xy`).
661
+ `data-drag-delay`,
627
662
  `data-drag-slop`, `data-drag-threshold` tune when the press becomes a grab.
628
663
 
629
664
  ### An affordance inside somebody else's box: `selfInteractions`
@@ -876,19 +911,63 @@ container above it does not take the gesture:
876
911
  `docs/drag_to_travel.md`). navi's own swipes do the equivalent with
877
912
  `data-travel-by-drag`.
878
913
 
914
+ ## A surface under the hand: `pan`, `zoom`
915
+
916
+ A plan drawn over an aerial photo, a map, a floor: one box the hand drags to look
917
+ elsewhere on, pinches or rolls a wheel over to look closer at, with things carried
918
+ across it that must not drag the surface along.
919
+
920
+ ```jsx
921
+ <Box
922
+ interactions={{
923
+ pan: (event) => moveCenterBy(event.detail), // { x, y } since the last one
924
+ zoom: (event) => zoomBy(event.detail), // { factor, x, y }
925
+ }}
926
+ >
927
+ {markers.map((marker) => (
928
+ <Marker interactions={{ move: (event) => place(marker, event.detail) }} />
929
+ ))}
930
+ </Box>
931
+ ```
932
+
933
+ Both are a **stream**, reported on every frame: `pan` says how far the hand has
934
+ moved since the previous `pan`, in px; `zoom` by what factor (above 1 is in) and
935
+ around which point of the surface, measured inside its border — the point between
936
+ the fingers, or under the wheel. Two fingers are one gesture, the point between
937
+ them panning and the distance between them zooming, and a finger lifting
938
+ re-anchors on what is left. What a pixel of pan means in your coordinates, and
939
+ whether the zoom is continuous or stepped, is yours: the numbers are the numbers.
940
+
941
+ What navi holds is what has to be settled **before** the press: `touch-action` on
942
+ the surface, said from a stylesheet because a browser decides what a touch may do
943
+ when it lands; the pan stepping back for what is carried across the surface (a
944
+ `move`, a handle, a field, a popover — the same list a travelling box steps back
945
+ for); the pinch not beginning as a pan under its first finger; the wheel and the
946
+ pinch writing one `zoom`; the capture, the pointer the browser drops, the click
947
+ the release leaves behind. A pointer pans only once it has travelled a few px
948
+ (`data-drag-threshold`), so a tap stays a tap and a `longpress` declared beside
949
+ `pan` still gets its hold.
950
+
951
+ Declared alone, `zoom` takes two fingers and the wheel and leaves one pointer to
952
+ whatever else reads it; `pan` alone leaves the wheel to the page.
953
+
879
954
  ## A gesture whose product is a value
880
955
 
881
- `move`, `reorder`, `land` and `toss` answer the same question — where did the
882
- element end up — because all four carry it. A rotation does not: what comes out of
956
+ `move`, `reorder`, `land`, `toss` and `leave` answer the same question — where did
957
+ the element end up — because all five carry it. A rotation does not: what comes out of
883
958
  it is an angle, and the handle that produced it stays exactly where it is. Same for
884
- a scale, a zoom, a plan panned under the finger.
959
+ a scale, or a sun dragged around a plan to set the hour.
885
960
 
886
961
  navi names none of those, and it is not an oversight: an interaction names an
887
962
  **outcome** — something that happened once, that can be told, refused, awaited.
888
963
  There is no outcome here, only a number read while the hand is still moving, and
889
964
  what to do with it (snap it to the neighbouring court, draw it, keep it in a
890
- signal) is knowledge navi does not have. So it hands over the machinery instead
891
- and leaves the paint alone:
965
+ signal) is knowledge navi does not have. `pan` and `zoom` above are the one
966
+ exception, and not for the number: a surface has an arbitration to settle before
967
+ the press — what it yields to, what a touch on it may do — and arbitration is
968
+ what `interactions` is for. A handle has none; it is the whole surface of its own
969
+ gesture. So for those navi hands over the machinery instead and leaves the paint
970
+ alone:
892
971
 
893
972
  ```js
894
973
  import { createDragGestureController, dragAfterIntent } from "@jsenv/navi";
@@ -991,7 +1070,9 @@ travels above it: `data-no-drag-travel` (see `docs/drag_to_travel.md`).
991
1070
  - `src/control/interaction/interaction_press.js` — swipes and holds, and what a
992
1071
  swipe writes on the element.
993
1072
  - `src/control/interaction/interaction_drag.js` — `move`, `reorder`, `land`,
994
- `toss` and the `grab` moment.
1073
+ `toss`, `leave` and the `grab` moment.
1074
+ - `src/control/interaction/interaction_surface.js` — `pan` and `zoom`, on
1075
+ `installPanZoom` from `@jsenv/dom`.
995
1076
  - `src/control/interaction/interaction_keyboard.js`,
996
1077
  `interaction_native.js` — the other two detectors.
997
1078
  - `@jsenv/dom` — `src/interaction/drag/drag_gesture.js` (the loop, its options and
@@ -999,7 +1080,8 @@ travels above it: `data-no-drag-travel` (see `docs/drag_to_travel.md`).
999
1080
  press becomes a drag, and what a source says in the stylesheet).
1000
1081
  - `src/control/demos/38_interactions_demo.html` — every case above, plus a
1001
1082
  mailbox, a board whose places are zones and the same board whose places are the
1002
- pieces, and a custom gesture registered from the page.
1083
+ pieces, a surface panned and zoomed, and a custom gesture registered from the
1084
+ page.
1003
1085
  - `src/control/demos/integration/5_plan_editor_demo.html` — a place that is a
1004
1086
  surface: shapes dragged out of a bank onto a plan, moved on it, and dragged off
1005
- it to go.
1087
+ it to go; and an address point that travels itself (`move` + `leave`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.29.194",
3
+ "version": "0.29.195",
4
4
  "type": "module",
5
5
  "description": "Library of components including navigation to create frontend applications",
6
6
  "repository": {
@@ -34,7 +34,7 @@
34
34
  "preact": "^11.0.0-beta.2"
35
35
  },
36
36
  "dependencies": {
37
- "@jsenv/dom": "0.17.47",
37
+ "@jsenv/dom": "0.17.48",
38
38
  "@jsenv/humanize": "1.8.1",
39
39
  "@jsenv/validity": "0.4.3"
40
40
  },