@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.
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { installImportMetaCssBuild, windowHeightSignal, windowWidthSignal, visualViewportHeightSignal, visualViewportWidthSignal, getAppHeight, getAppWidth, coarsePointerSignal, smallTouchScreenSignal } from "./jsenv_navi_side_effects.js";
6
6
  export { disableVirtualKeyboardOverlay } from "./jsenv_navi_side_effects.js";
7
- import { elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, createPubSub, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, scrollIntoViewThroughScrollables, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, keepTouchRefusable, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, getScrollContainer, closestOpenableAncestor, isAncestorOpen, isDisplayedDespiteClosedAncestor, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, releaseWheelGesture, getScrollIntoViewScopedOffsets, wheelGestureIsTakenFrom, claimWheelGesture, scrollIntoViewScoped, initFocusGroup, isTouchDrivenEvent, stringifyStyle as stringifyStyle$1, resolveOklchLightness, contrastColor, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, getVirtualKeyboardOverlayHeight, onAncestorReopen, isPressDisputedByDrag, canScroll, measureWidestChildRow, performTabNavigation, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
7
+ import { elementIsFocusable, createIterableWeakSet, dispatchInternalCustomEvent, dispatchCustomEvent, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, getElementSignature, createPubSub, findEvent, createValueEffect, findFocusDelegateTarget, findFocusable, scrollIntoViewThroughScrollables, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, keepTouchRefusable, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, installPanZoom, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, normalizeStyles, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, getScrollContainer, closestOpenableAncestor, isAncestorOpen, isDisplayedDespiteClosedAncestor, observeAncestorOpenState, getAncestorOpenType, findBefore, findAfter, resolveCSSSize, hasCSSSizeUnit, releaseWheelGesture, getScrollIntoViewScopedOffsets, wheelGestureIsTakenFrom, claimWheelGesture, scrollIntoViewScoped, initFocusGroup, isTouchDrivenEvent, stringifyStyle as stringifyStyle$1, resolveOklchLightness, contrastColor, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, getVirtualKeyboardOverlayHeight, onAncestorReopen, isPressDisputedByDrag, canScroll, measureWidestChildRow, performTabNavigation, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement } from "@jsenv/dom";
8
8
  export { chainEvent, clickIsSuppressed, contrastColor, createDragGestureController, dragAfterIntent, findEvent, markDragSource, startDragTo } from "@jsenv/dom";
9
9
  import { signal, computed, effect, untracked, batch, useComputed, useSignal } from "@preact/signals";
10
10
  import { isValidElement, createContext, render, h, toChildArray, options, cloneElement, Fragment as Fragment$1 } from "preact";
@@ -10334,26 +10334,31 @@ const swipeTypeOf = (axis, pulled) => {
10334
10334
  };
10335
10335
 
10336
10336
  /**
10337
- * `move`, `reorder`, `land`, `toss` — one grab, and what letting go of it means.
10337
+ * `move`, `reorder`, `land`, `toss`, `leave` — one grab, and what letting go of
10338
+ * it means.
10338
10339
  *
10339
- * All four are the same gesture: the element is picked up and carried. What
10340
+ * All five are the same gesture: the element is picked up and carried. What
10340
10341
  * differs is the answer at the release, so one detector reads them all — it is one
10341
10342
  * press, and something has to arbitrate it.
10342
10343
  *
10343
10344
  * interactions={{ reorder: moveBefore, toss: remove }}
10344
10345
  * interactions={{ land: swapPlaces }}
10345
- * interactions={{ move: remember }}
10346
+ * interactions={{ move: remember, leave: remove }}
10346
10347
  *
10347
- * `toss` combines with `reorder` and with `land`: a task dragged onto another
10348
- * changes places, the same task thrown far and fast is gotten rid of. The three
10349
- * others do not combine with each other — an element either goes where it is put,
10350
- * takes a place in a list, or comes down on a place, and no two of those answers
10351
- * can both be true of one release.
10348
+ * `toss` and `leave` each combine with `reorder` and with `land`: a task dragged
10349
+ * onto another changes places, the same task thrown far and fast is gotten rid
10350
+ * of, a marker let go of off its plan is removed. `leave` combines with `move` as
10351
+ * well. `move`, `reorder` and `land` do not combine with each other — an element
10352
+ * either goes where it is put, takes a place in a list, or comes down on a place,
10353
+ * and no two of those answers can both be true of one release.
10352
10354
  *
10353
10355
  * `move` carries the element ITSELF and leaves it where it was put; the others
10354
10356
  * carry a copy and put the original back. That is the same difference said in
10355
10357
  * layout terms: something moved has a new place of its own, something reordered
10356
- * had its place taken by the list.
10358
+ * had its place taken by the list. Where that place is KEPT is the answer's:
10359
+ * an application that redraws the element from state while answering (a new
10360
+ * `left`/`top`) owns the position from then on, and the drag's own translate
10361
+ * goes; one that draws nothing leaves it to the element, where it is baked in.
10357
10362
  *
10358
10363
  * `reorder` VS `land`: both come down on an item, and what separates them is what
10359
10364
  * a place IS. A row of a list is a place BETWEEN two others — free by construction,
@@ -10369,9 +10374,8 @@ const swipeTypeOf = (axis, pulled) => {
10369
10374
  * }}}
10370
10375
  *
10371
10376
  * `toId` is an element and never null: a copy over nothing is a release that meant
10372
- * nothing, and the interaction does not happen at all — unless the element says
10373
- * `data-toss-by="release-outside"` (see below), which gives that release a
10374
- * meaning of its own.
10377
+ * nothing, and the interaction does not happen at all — unless the element
10378
+ * declares `leave` (see below), which gives that release a meaning of its own.
10375
10379
  *
10376
10380
  * WHERE ON IT, in `x`, `y`, `width`, `height`: the box the copy came down in,
10377
10381
  * measured inside the place — its border and its scroll taken out. A place that is
@@ -10408,15 +10412,30 @@ const swipeTypeOf = (axis, pulled) => {
10408
10412
  * <Plan id="plan" data-droppable>…</Plan>
10409
10413
  * </div>
10410
10414
  *
10411
- * HOW A TOSS IS MADE: `data-toss-by`, beside `toss`. A THROW (`"throw"`, the
10412
- * default) is far AND fast — the flick that gets rid of something, a list's
10413
- * gesture, and it is judged before any landing. A RELEASE OUTSIDE
10414
- * (`"release-outside"`) has no place under it — dragging a marker off a plan and
10415
- * letting go, deliberate and never a flick, a surface's gesture. Each is named on
10416
- * its own because each is wrong where the other is right: on a plan a fast drag
10417
- * that ends ON it has not asked for the thing to go, and on a list a row let go of
10418
- * beside it is a row put back. Both at once is
10419
- * `data-toss-by="throw release-outside"`.
10415
+ * LET GO OF AWAY FROM EVERY PLACE: `leave`. A throw is a GESTURE — far and fast,
10416
+ * the flick that gets rid of a row, judged before any landing. A release outside
10417
+ * is a PLACE — the hand let go with nothing under the thing, judged after a
10418
+ * landing was looked for. They share the outcome an application usually attaches
10419
+ * to them and nothing else, so each has its name: `toss` is the throw, `leave` is
10420
+ * the release outside, and neither reads the other's rules — there is no speed to
10421
+ * a release, and on a plan a fast drag that ends ON it has not asked for the
10422
+ * thing to go.
10423
+ *
10424
+ * interactions={{
10425
+ * move: (event) => remember(event.detail.x, event.detail.y),
10426
+ * leave: () => remove(id),
10427
+ * }}
10428
+ *
10429
+ * Beside `land` or `reorder`, "outside" is away from every place. Beside `move`
10430
+ * — the element itself travels, and nothing is a place — it is outside the
10431
+ * surface the element stands in: the nearest `data-droppable` ancestor, or,
10432
+ * without one, what can be seen of the scroll container. Either way it is judged
10433
+ * on the element's box no longer overlapping it, not on the pointer, which is
10434
+ * still well inside the frame when a 30px marker has just left it. The detail is
10435
+ * `toss`'s, `{ id, x, y }`, the distance travelled being what an exit is animated
10436
+ * with. A refused `leave` travels back, as a refused `move` does; picked up and
10437
+ * put straight back down stays a cancel — it has to have gone somewhere to be
10438
+ * away from anything.
10420
10439
  *
10421
10440
  * Nothing of the gesture is decided here. `startDragTo` owns all of it — the
10422
10441
  * copy carried above the page while the original keeps its place in the layout, the
@@ -10464,7 +10483,7 @@ const swipeTypeOf = (axis, pulled) => {
10464
10483
  *
10465
10484
  * interactions={{ toss: remove, grab: () => navigator.vibrate?.(10) }}
10466
10485
  *
10467
- * The four above all answer the RELEASE, and between the press and the release
10486
+ * The five above all answer the RELEASE, and between the press and the release
10468
10487
  * there is one instant that counts for the hand making the gesture: the one where
10469
10488
  * the object stops being pressed and starts being held. `grab` is that instant,
10470
10489
  * and it is the same one whichever way the drag was entered — a finger held still,
@@ -10478,7 +10497,7 @@ const swipeTypeOf = (axis, pulled) => {
10478
10497
  *
10479
10498
  * It is told, not asked: `grab` reports, so what it returns is not waited on and
10480
10499
  * preventing its event does not call the gesture off. And it is not an interaction
10481
- * on its own — declared without one of the four above there is no gesture for it
10500
+ * on its own — declared without one of the five above there is no gesture for it
10482
10501
  * to be the beginning of.
10483
10502
  *
10484
10503
  * A `longpress` needs nothing of this: it already happens at the moment the hold
@@ -10498,6 +10517,8 @@ const REORDER = "reorder";
10498
10517
  // for a file being dropped on the page would get this one and read it as such.
10499
10518
  const LAND = "land";
10500
10519
  const TOSS = "toss";
10520
+ // Let go of away from every place — not a throw, which is judged on its speed.
10521
+ const LEAVE = "leave";
10501
10522
  // The moment the press stops being a press and becomes a hold on the object.
10502
10523
  const GRAB = "grab";
10503
10524
 
@@ -10514,13 +10535,9 @@ const DROP_CONTAINER_ATTRIBUTE = "data-drop-container";
10514
10535
  const AXIS_ATTRIBUTE = "data-drag-axis";
10515
10536
  const DELAY_ATTRIBUTE = "data-drag-delay";
10516
10537
  const SLOP_ATTRIBUTE = "data-drag-slop";
10517
- const THRESHOLD_ATTRIBUTE = "data-drag-threshold";
10538
+ const THRESHOLD_ATTRIBUTE$1 = "data-drag-threshold";
10518
10539
  const TOSS_DISTANCE_ATTRIBUTE = "data-toss-distance";
10519
10540
  const TOSS_SPEED_ATTRIBUTE = "data-toss-speed";
10520
- // How a toss is made here: "throw" (far and fast), "release-outside" (let go of
10521
- // away from every place), or both.
10522
- const TOSS_BY_ATTRIBUTE = "data-toss-by";
10523
- const TOSS_BY_DEFAULT = "throw";
10524
10541
 
10525
10542
  defineInteractionDetector({
10526
10543
  name: "drag",
@@ -10529,6 +10546,7 @@ defineInteractionDetector({
10529
10546
  type === REORDER ||
10530
10547
  type === LAND ||
10531
10548
  type === TOSS ||
10549
+ type === LEAVE ||
10532
10550
  type === GRAB,
10533
10551
  // The press is the beginning of the gesture, not an answer: nothing may read
10534
10552
  // it until it is known whether the hand is dragging or just pressing.
@@ -10538,7 +10556,8 @@ defineInteractionDetector({
10538
10556
  const canReorder = types.includes(REORDER);
10539
10557
  const canLand = types.includes(LAND);
10540
10558
  const canToss = types.includes(TOSS);
10541
- if (!canMove && !canReorder && !canLand && !canToss) {
10559
+ const canLeave = types.includes(LEAVE);
10560
+ if (!canMove && !canReorder && !canLand && !canToss && !canLeave) {
10542
10561
  return undefined;
10543
10562
  }
10544
10563
  const tellsWhenGrabbed = types.includes(GRAB);
@@ -10549,18 +10568,18 @@ defineInteractionDetector({
10549
10568
  axisHolder?.getAttribute(AXIS_ATTRIBUTE) ||
10550
10569
  // A list runs one way, and reordering walks it. Anything else goes wherever
10551
10570
  // the hand takes it: a board has places all around, a thing put somewhere has
10552
- // two axes to be put along, and a throw goes where it was thrown.
10553
- (canReorder && !canLand && !canToss ? "y" : "xy");
10554
- // Where the places are, and what a release away from all of them means —
10555
- // both read at setup for the same reason as the axes: they are what the
10556
- // gesture is about, and it is what holds the two sides of it that says them.
10571
+ // two axes to be put along, a throw goes where it was thrown and a thing
10572
+ // that leaves goes out by whichever edge.
10573
+ (canReorder && !canLand && !canToss && !canLeave ? "y" : "xy");
10574
+ // Where the places are, read at setup for the same reason as the axes: they
10575
+ // are what the gesture is about, and it is what holds the two sides of it
10576
+ // that says them.
10557
10577
  const dropContainer = element.closest(`[${DROP_CONTAINER_ATTRIBUTE}]`);
10558
- const tossByHolder = element.closest(`[${TOSS_BY_ATTRIBUTE}]`);
10559
- const tossBy = (
10560
- tossByHolder?.getAttribute(TOSS_BY_ATTRIBUTE) || TOSS_BY_DEFAULT
10561
- )
10562
- .trim()
10563
- .split(/\s+/);
10578
+ // What a `leave` is outside of when nothing is a place: the surface the
10579
+ // element stands in. Looked for above the parent — the element itself may be
10580
+ // a place of its own (a piece that receives), which is not what it is inside.
10581
+ const outsideOf =
10582
+ element.parentElement.closest(`[${DROPPABLE_ATTRIBUTE}]`) || undefined;
10564
10583
 
10565
10584
  if (canReorder) {
10566
10585
  element.setAttribute(REORDERABLE_ATTRIBUTE, "");
@@ -10588,6 +10607,7 @@ defineInteractionDetector({
10588
10607
  ? `[${REORDERABLE_ATTRIBUTE}]`
10589
10608
  : undefined,
10590
10609
  containerElement: dropContainer || undefined,
10610
+ outsideOf,
10591
10611
  getItemId: (itemElement) => itemElement.id,
10592
10612
  direction: { x: axes.includes("x"), y: axes.includes("y") },
10593
10613
  // Where it may go, said in the DOM. A thing that is put somewhere stays
@@ -10595,21 +10615,20 @@ defineInteractionDetector({
10595
10615
  // the scrollable area can be far larger than the box, and "inside the box"
10596
10616
  // is what a hand expects). `data-drag-free` lifts that, and so does a
10597
10617
  // named container of places: they are somewhere else by construction, and
10598
- // a copy kept in its own scroll area could never reach them. Left alone
10599
- // for a throw, which frees the area on its own — it has to be able to
10600
- // leave.
10618
+ // a copy kept in its own scroll area could never reach them. So does
10619
+ // "leave": a thing let go of outside has to be able to get there. Left
10620
+ // alone for a throw, which frees the area on its own.
10601
10621
  areaConstraint:
10602
- dropContainer || element.closest(`[data-drag-free]`)
10622
+ dropContainer || element.closest(`[data-drag-free]`) || canLeave
10603
10623
  ? "none"
10604
10624
  : canMove
10605
10625
  ? "scrollport"
10606
10626
  : undefined,
10607
- threshold: readConfig(THRESHOLD_ATTRIBUTE, undefined),
10627
+ threshold: readConfig(THRESHOLD_ATTRIBUTE$1, undefined),
10608
10628
  longPressDelay: readConfig(DELAY_ATTRIBUTE, undefined),
10609
10629
  longPressSlop: readConfig(SLOP_ATTRIBUTE, undefined),
10610
10630
  tossDistance: readConfig(TOSS_DISTANCE_ATTRIBUTE, undefined),
10611
10631
  tossSpeed: readConfig(TOSS_SPEED_ATTRIBUTE, undefined),
10612
- tossBy,
10613
10632
  // The one moment the gesture has that is not a release. Said here rather
10614
10633
  // than from the press that led to it, because the press is only one of the
10615
10634
  // two ways in: a finger holds still, a mouse travels a few pixels, and it
@@ -10623,7 +10642,7 @@ defineInteractionDetector({
10623
10642
  });
10624
10643
  }
10625
10644
  : undefined,
10626
- // Handed straight back in all three cases: what `trigger` returns is a
10645
+ // Handed straight back in every case: what `trigger` returns is a
10627
10646
  // promise while the answer is still going, which is what the gesture waits
10628
10647
  // on before it lets go of what it carries.
10629
10648
  onReorder: (fromId, toId, syncCloneWithDropTarget) =>
@@ -10640,6 +10659,8 @@ defineInteractionDetector({
10640
10659
  x: gestureInfo.layout.xDelta,
10641
10660
  y: gestureInfo.layout.yDelta,
10642
10661
  }),
10662
+ onLeave: ({ x, y }) =>
10663
+ trigger(LEAVE, pointerDownEvent, { id: element.id, x, y }),
10643
10664
  onMove: ({ x, y }) => trigger(MOVE, pointerDownEvent, { x, y }),
10644
10665
  });
10645
10666
  };
@@ -10653,6 +10674,71 @@ defineInteractionDetector({
10653
10674
  },
10654
10675
  });
10655
10676
 
10677
+ /**
10678
+ * `pan`, `zoom` — a surface under the hand.
10679
+ *
10680
+ * <Box
10681
+ * interactions={{
10682
+ * pan: (event) => moveCenterBy(event.detail), // { x, y } since the last one
10683
+ * zoom: (event) => zoomBy(event.detail), // { factor, x, y }
10684
+ * }}
10685
+ * />
10686
+ *
10687
+ * Neither is an outcome: both are a stream, reported on every frame while the
10688
+ * hand is still moving, and navi otherwise names only what happens once (see
10689
+ * "A gesture whose product is a value" in docs/interactions.md). They are named
10690
+ * here because of what has to be settled BEFORE the press, which is what
10691
+ * `interactions` exists for: `touch-action` on the surface, said from a
10692
+ * stylesheet since a browser decides what a touch may do when it lands; the pan
10693
+ * stepping back for what is carried ACROSS the surface (a marker declaring
10694
+ * `move`, a handle) and for what answers the pointer on its own; the pinch not
10695
+ * beginning as a pan under its first finger; the wheel and the pinch writing
10696
+ * one `zoom`. A handle turned, a sun dragged around a plan have none of that to
10697
+ * arbitrate — the handle is the whole surface of the gesture — and stay
10698
+ * unnamed.
10699
+ *
10700
+ * `pan`'s detail is the movement since the previous `pan`, in px. `zoom`'s is
10701
+ * the factor (above 1 is in) and the point of the surface it is around,
10702
+ * measured inside its border — the point between the fingers, or under the
10703
+ * wheel. What a pixel of pan means in the application's coordinates, and
10704
+ * whether the zoom is continuous or stepped, is the application's.
10705
+ *
10706
+ * Declared alone, `zoom` leaves one pointer to whatever else reads it and takes
10707
+ * only two fingers and the wheel; `pan` alone leaves the wheel to the page.
10708
+ *
10709
+ * Nothing of the gesture is decided here — `installPanZoom` in @jsenv/dom owns
10710
+ * the pointers, the capture, the wheel burst and the click left behind. This
10711
+ * says which names the element answers with, and hands the numbers over.
10712
+ */
10713
+
10714
+
10715
+ const PAN = "pan";
10716
+ const ZOOM = "zoom";
10717
+ // The same attribute a carried element reads: how far a pointer travels before
10718
+ // it is a gesture rather than a press.
10719
+ const THRESHOLD_ATTRIBUTE = "data-drag-threshold";
10720
+
10721
+ defineInteractionDetector({
10722
+ name: "surface",
10723
+ claims: (type) => type === PAN || type === ZOOM,
10724
+ // A press on the surface may be a tap, a hold or the beginning of a pan, and
10725
+ // nothing may read it as the first until the pointer has said which.
10726
+ disputesPress: true,
10727
+ setup: (element, trigger, { types, readConfig }) => {
10728
+ const canPan = types.includes(PAN);
10729
+ const canZoom = types.includes(ZOOM);
10730
+ return installPanZoom(element, {
10731
+ threshold: readConfig(THRESHOLD_ATTRIBUTE, undefined),
10732
+ onPan: canPan
10733
+ ? ({ event, x, y }) => trigger(PAN, event, { x, y })
10734
+ : undefined,
10735
+ onZoom: canZoom
10736
+ ? ({ event, factor, x, y }) => trigger(ZOOM, event, { factor, x, y })
10737
+ : undefined,
10738
+ });
10739
+ },
10740
+ });
10741
+
10656
10742
  // used by form elements such as <input>, <select>, <textarea> to have their own action bound to a single parameter
10657
10743
  // when inside a <form> the form params are updated when the form element single param is updated
10658
10744
  const useActionBoundToOneParam = (action, paramsSignal) => {
@@ -19921,9 +20007,14 @@ const PSEUDO_STATE_CHILD_PROP_SET = new Set(["tabIndex", "tabindex"]);
19921
20007
  * preventInitialTransition?: boolean,
19922
20008
  * separator?: import("ignore:preact").ComponentChildren | ((index: number) => import("ignore:preact").ComponentChildren),
19923
20009
  * selfInteractions?: string,
20010
+ * interactions?: { [type: string]: "request_action" | "request_ui_action" | ((event: Event) => void) | false | null | undefined },
19924
20011
  * children?: import("ignore:preact").ComponentChildren,
19925
20012
  * [key: string]: any,
19926
20013
  * }>}
20014
+ * @param {object} [interactions] What this box answers, by interaction name
20015
+ * (see docs/interactions.md). A plain box has no wiring of its own: it does
20016
+ * nothing with `action` (that is a control's prop), so a click on it is
20017
+ * declared here like any other interaction: `interactions={{ click: fn }}`.
19927
20018
  */
19928
20019
  const Box = props => {
19929
20020
  const {