@jsenv/navi 0.29.193 → 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.
- package/dist/dev/jsenv_navi.js +210 -68
- package/dist/dev/jsenv_navi.js.map +6 -3
- package/dist/jsenv_navi.js +139 -48
- package/dist/jsenv_navi.js.map +6 -3
- package/docs/drag_to_travel.md +2 -2
- package/docs/interactions.md +137 -52
- package/package.json +2 -2
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -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";
|
|
@@ -10500,26 +10500,31 @@ const swipeTypeOf = (axis, pulled) => {
|
|
|
10500
10500
|
};
|
|
10501
10501
|
|
|
10502
10502
|
/**
|
|
10503
|
-
* `move`, `reorder`, `land`, `toss` — one grab, and what letting go of
|
|
10503
|
+
* `move`, `reorder`, `land`, `toss`, `leave` — one grab, and what letting go of
|
|
10504
|
+
* it means.
|
|
10504
10505
|
*
|
|
10505
|
-
* All
|
|
10506
|
+
* All five are the same gesture: the element is picked up and carried. What
|
|
10506
10507
|
* differs is the answer at the release, so one detector reads them all — it is one
|
|
10507
10508
|
* press, and something has to arbitrate it.
|
|
10508
10509
|
*
|
|
10509
10510
|
* interactions={{ reorder: moveBefore, toss: remove }}
|
|
10510
10511
|
* interactions={{ land: swapPlaces }}
|
|
10511
|
-
* interactions={{ move: remember }}
|
|
10512
|
+
* interactions={{ move: remember, leave: remove }}
|
|
10512
10513
|
*
|
|
10513
|
-
* `toss`
|
|
10514
|
-
* changes places, the same task thrown far and fast is gotten rid
|
|
10515
|
-
*
|
|
10516
|
-
*
|
|
10517
|
-
*
|
|
10514
|
+
* `toss` and `leave` each combine with `reorder` and with `land`: a task dragged
|
|
10515
|
+
* onto another changes places, the same task thrown far and fast is gotten rid
|
|
10516
|
+
* of, a marker let go of off its plan is removed. `leave` combines with `move` as
|
|
10517
|
+
* well. `move`, `reorder` and `land` do not combine with each other — an element
|
|
10518
|
+
* either goes where it is put, takes a place in a list, or comes down on a place,
|
|
10519
|
+
* and no two of those answers can both be true of one release.
|
|
10518
10520
|
*
|
|
10519
10521
|
* `move` carries the element ITSELF and leaves it where it was put; the others
|
|
10520
10522
|
* carry a copy and put the original back. That is the same difference said in
|
|
10521
10523
|
* layout terms: something moved has a new place of its own, something reordered
|
|
10522
|
-
* had its place taken by the list.
|
|
10524
|
+
* had its place taken by the list. Where that place is KEPT is the answer's:
|
|
10525
|
+
* an application that redraws the element from state while answering (a new
|
|
10526
|
+
* `left`/`top`) owns the position from then on, and the drag's own translate
|
|
10527
|
+
* goes; one that draws nothing leaves it to the element, where it is baked in.
|
|
10523
10528
|
*
|
|
10524
10529
|
* `reorder` VS `land`: both come down on an item, and what separates them is what
|
|
10525
10530
|
* a place IS. A row of a list is a place BETWEEN two others — free by construction,
|
|
@@ -10535,9 +10540,8 @@ const swipeTypeOf = (axis, pulled) => {
|
|
|
10535
10540
|
* }}}
|
|
10536
10541
|
*
|
|
10537
10542
|
* `toId` is an element and never null: a copy over nothing is a release that meant
|
|
10538
|
-
* nothing, and the interaction does not happen at all — unless the element
|
|
10539
|
-
* `
|
|
10540
|
-
* meaning of its own.
|
|
10543
|
+
* nothing, and the interaction does not happen at all — unless the element
|
|
10544
|
+
* declares `leave` (see below), which gives that release a meaning of its own.
|
|
10541
10545
|
*
|
|
10542
10546
|
* WHERE ON IT, in `x`, `y`, `width`, `height`: the box the copy came down in,
|
|
10543
10547
|
* measured inside the place — its border and its scroll taken out. A place that is
|
|
@@ -10574,15 +10578,30 @@ const swipeTypeOf = (axis, pulled) => {
|
|
|
10574
10578
|
* <Plan id="plan" data-droppable>…</Plan>
|
|
10575
10579
|
* </div>
|
|
10576
10580
|
*
|
|
10577
|
-
*
|
|
10578
|
-
*
|
|
10579
|
-
*
|
|
10580
|
-
*
|
|
10581
|
-
*
|
|
10582
|
-
*
|
|
10583
|
-
* that ends ON it has not asked for the
|
|
10584
|
-
*
|
|
10585
|
-
*
|
|
10581
|
+
* LET GO OF AWAY FROM EVERY PLACE: `leave`. A throw is a GESTURE — far and fast,
|
|
10582
|
+
* the flick that gets rid of a row, judged before any landing. A release outside
|
|
10583
|
+
* is a PLACE — the hand let go with nothing under the thing, judged after a
|
|
10584
|
+
* landing was looked for. They share the outcome an application usually attaches
|
|
10585
|
+
* to them and nothing else, so each has its name: `toss` is the throw, `leave` is
|
|
10586
|
+
* the release outside, and neither reads the other's rules — there is no speed to
|
|
10587
|
+
* a release, and on a plan a fast drag that ends ON it has not asked for the
|
|
10588
|
+
* thing to go.
|
|
10589
|
+
*
|
|
10590
|
+
* interactions={{
|
|
10591
|
+
* move: (event) => remember(event.detail.x, event.detail.y),
|
|
10592
|
+
* leave: () => remove(id),
|
|
10593
|
+
* }}
|
|
10594
|
+
*
|
|
10595
|
+
* Beside `land` or `reorder`, "outside" is away from every place. Beside `move`
|
|
10596
|
+
* — the element itself travels, and nothing is a place — it is outside the
|
|
10597
|
+
* surface the element stands in: the nearest `data-droppable` ancestor, or,
|
|
10598
|
+
* without one, what can be seen of the scroll container. Either way it is judged
|
|
10599
|
+
* on the element's box no longer overlapping it, not on the pointer, which is
|
|
10600
|
+
* still well inside the frame when a 30px marker has just left it. The detail is
|
|
10601
|
+
* `toss`'s, `{ id, x, y }`, the distance travelled being what an exit is animated
|
|
10602
|
+
* with. A refused `leave` travels back, as a refused `move` does; picked up and
|
|
10603
|
+
* put straight back down stays a cancel — it has to have gone somewhere to be
|
|
10604
|
+
* away from anything.
|
|
10586
10605
|
*
|
|
10587
10606
|
* Nothing of the gesture is decided here. `startDragTo` owns all of it — the
|
|
10588
10607
|
* copy carried above the page while the original keeps its place in the layout, the
|
|
@@ -10630,7 +10649,7 @@ const swipeTypeOf = (axis, pulled) => {
|
|
|
10630
10649
|
*
|
|
10631
10650
|
* interactions={{ toss: remove, grab: () => navigator.vibrate?.(10) }}
|
|
10632
10651
|
*
|
|
10633
|
-
* The
|
|
10652
|
+
* The five above all answer the RELEASE, and between the press and the release
|
|
10634
10653
|
* there is one instant that counts for the hand making the gesture: the one where
|
|
10635
10654
|
* the object stops being pressed and starts being held. `grab` is that instant,
|
|
10636
10655
|
* and it is the same one whichever way the drag was entered — a finger held still,
|
|
@@ -10644,7 +10663,7 @@ const swipeTypeOf = (axis, pulled) => {
|
|
|
10644
10663
|
*
|
|
10645
10664
|
* It is told, not asked: `grab` reports, so what it returns is not waited on and
|
|
10646
10665
|
* preventing its event does not call the gesture off. And it is not an interaction
|
|
10647
|
-
* on its own — declared without one of the
|
|
10666
|
+
* on its own — declared without one of the five above there is no gesture for it
|
|
10648
10667
|
* to be the beginning of.
|
|
10649
10668
|
*
|
|
10650
10669
|
* A `longpress` needs nothing of this: it already happens at the moment the hold
|
|
@@ -10664,6 +10683,8 @@ const REORDER = "reorder";
|
|
|
10664
10683
|
// for a file being dropped on the page would get this one and read it as such.
|
|
10665
10684
|
const LAND = "land";
|
|
10666
10685
|
const TOSS = "toss";
|
|
10686
|
+
// Let go of away from every place — not a throw, which is judged on its speed.
|
|
10687
|
+
const LEAVE = "leave";
|
|
10667
10688
|
// The moment the press stops being a press and becomes a hold on the object.
|
|
10668
10689
|
const GRAB = "grab";
|
|
10669
10690
|
|
|
@@ -10680,14 +10701,9 @@ const DROP_CONTAINER_ATTRIBUTE = "data-drop-container";
|
|
|
10680
10701
|
const AXIS_ATTRIBUTE = "data-drag-axis";
|
|
10681
10702
|
const DELAY_ATTRIBUTE = "data-drag-delay";
|
|
10682
10703
|
const SLOP_ATTRIBUTE = "data-drag-slop";
|
|
10683
|
-
const THRESHOLD_ATTRIBUTE = "data-drag-threshold";
|
|
10704
|
+
const THRESHOLD_ATTRIBUTE$1 = "data-drag-threshold";
|
|
10684
10705
|
const TOSS_DISTANCE_ATTRIBUTE = "data-toss-distance";
|
|
10685
10706
|
const TOSS_SPEED_ATTRIBUTE = "data-toss-speed";
|
|
10686
|
-
// How a toss is made here: "throw" (far and fast), "release-outside" (let go of
|
|
10687
|
-
// away from every place), or both.
|
|
10688
|
-
const TOSS_BY_ATTRIBUTE = "data-toss-by";
|
|
10689
|
-
const TOSS_BY_DEFAULT = "throw";
|
|
10690
|
-
const TOSS_WAYS = ["throw", "release-outside"];
|
|
10691
10707
|
|
|
10692
10708
|
defineInteractionDetector({
|
|
10693
10709
|
name: "drag",
|
|
@@ -10696,6 +10712,7 @@ defineInteractionDetector({
|
|
|
10696
10712
|
type === REORDER ||
|
|
10697
10713
|
type === LAND ||
|
|
10698
10714
|
type === TOSS ||
|
|
10715
|
+
type === LEAVE ||
|
|
10699
10716
|
type === GRAB,
|
|
10700
10717
|
// The press is the beginning of the gesture, not an answer: nothing may read
|
|
10701
10718
|
// it until it is known whether the hand is dragging or just pressing.
|
|
@@ -10705,20 +10722,32 @@ defineInteractionDetector({
|
|
|
10705
10722
|
const canReorder = types.includes(REORDER);
|
|
10706
10723
|
const canLand = types.includes(LAND);
|
|
10707
10724
|
const canToss = types.includes(TOSS);
|
|
10708
|
-
|
|
10725
|
+
const canLeave = types.includes(LEAVE);
|
|
10726
|
+
if (!canMove && !canReorder && !canLand && !canToss && !canLeave) {
|
|
10709
10727
|
// Only "grab": there is no gesture to be taken by, so there is no moment to
|
|
10710
10728
|
// be told about either.
|
|
10711
10729
|
{
|
|
10712
10730
|
console.warn(
|
|
10713
|
-
`interactions: "${GRAB}" says when a drag takes hold, so it needs a drag to say it about. Declare it beside "${MOVE}", "${REORDER}", "${LAND}" or "${
|
|
10731
|
+
`interactions: "${GRAB}" says when a drag takes hold, so it needs a drag to say it about. Declare it beside "${MOVE}", "${REORDER}", "${LAND}", "${TOSS}" or "${LEAVE}".`,
|
|
10714
10732
|
);
|
|
10715
10733
|
}
|
|
10716
10734
|
return undefined;
|
|
10717
10735
|
}
|
|
10718
10736
|
const tellsWhenGrabbed = types.includes(GRAB);
|
|
10719
|
-
|
|
10737
|
+
// `startDragTo` arbitrates the same conflicts and says so too, and that voice
|
|
10738
|
+
// is not heard from here: @jsenv/dom publishes one build with `import.meta.dev`
|
|
10739
|
+
// false, so nothing of its dev code exists for whoever consumes navi. What a
|
|
10740
|
+
// caller wrote is `interactions`, so this is also where the warning can name it.
|
|
10741
|
+
if (canMove && (canReorder || canLand || canToss)) {
|
|
10742
|
+
// The other three all carry a copy and put the original back — there is no
|
|
10743
|
+
// move anywhere in that gesture, so the element itself never travels.
|
|
10744
|
+
const other = canLand ? LAND : canReorder ? REORDER : TOSS;
|
|
10745
|
+
const instead =
|
|
10746
|
+
other === TOSS
|
|
10747
|
+
? ` Something that moves in place AND goes away when dragged off its surface declares "${LEAVE}" beside "${MOVE}": a release outside the surface is what "${LEAVE}" answers, and it is not a throw.`
|
|
10748
|
+
: "";
|
|
10720
10749
|
console.warn(
|
|
10721
|
-
`interactions: "
|
|
10750
|
+
`interactions: "${MOVE}" and "${other}" cannot both answer one release — "${MOVE}" leaves the element where the hand put it, "${other}" carries a copy and puts the original back. "${other}" wins here, so the element never travels.${instead}`,
|
|
10722
10751
|
);
|
|
10723
10752
|
}
|
|
10724
10753
|
if (canReorder && canLand) {
|
|
@@ -10733,34 +10762,23 @@ defineInteractionDetector({
|
|
|
10733
10762
|
axisHolder?.getAttribute(AXIS_ATTRIBUTE) ||
|
|
10734
10763
|
// A list runs one way, and reordering walks it. Anything else goes wherever
|
|
10735
10764
|
// the hand takes it: a board has places all around, a thing put somewhere has
|
|
10736
|
-
// two axes to be put along,
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
//
|
|
10740
|
-
// gesture is about, and it is what holds the two sides of it
|
|
10765
|
+
// two axes to be put along, a throw goes where it was thrown and a thing
|
|
10766
|
+
// that leaves goes out by whichever edge.
|
|
10767
|
+
(canReorder && !canLand && !canToss && !canLeave ? "y" : "xy");
|
|
10768
|
+
// Where the places are, read at setup for the same reason as the axes: they
|
|
10769
|
+
// are what the gesture is about, and it is what holds the two sides of it
|
|
10770
|
+
// that says them.
|
|
10741
10771
|
const dropContainer = element.closest(`[${DROP_CONTAINER_ATTRIBUTE}]`);
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
.
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
`interactions: "${way}" is not a way a toss is made. ${TOSS_BY_ATTRIBUTE} takes "${TOSS_WAYS.join('", "')}", or both.`,
|
|
10753
|
-
);
|
|
10754
|
-
}
|
|
10755
|
-
}
|
|
10756
|
-
if (
|
|
10757
|
-
tossBy.includes("release-outside") &&
|
|
10758
|
-
(!canToss || (!canReorder && !canLand))
|
|
10759
|
-
) {
|
|
10760
|
-
console.warn(
|
|
10761
|
-
`interactions: ${TOSS_BY_ATTRIBUTE}="release-outside" makes a release away from every place mean the thing is gotten rid of, so it needs both "${TOSS}" and places to be away from. Declare "${TOSS}" beside "${REORDER}" or "${LAND}".`,
|
|
10762
|
-
);
|
|
10763
|
-
}
|
|
10772
|
+
// What a `leave` is outside of when nothing is a place: the surface the
|
|
10773
|
+
// element stands in. Looked for above the parent — the element itself may be
|
|
10774
|
+
// a place of its own (a piece that receives), which is not what it is inside.
|
|
10775
|
+
const outsideOf =
|
|
10776
|
+
element.parentElement.closest(`[${DROPPABLE_ATTRIBUTE}]`) || undefined;
|
|
10777
|
+
if (canLeave && !canLand && !canReorder && !outsideOf) {
|
|
10778
|
+
console.warn(
|
|
10779
|
+
`interactions: "${LEAVE}" is judged against the surface this element stands in, and nothing above it is marked ${DROPPABLE_ATTRIBUTE} — so it is judged against what can be seen of the scroll container, which on a page is the window. Mark the surface it leaves.`,
|
|
10780
|
+
element,
|
|
10781
|
+
);
|
|
10764
10782
|
}
|
|
10765
10783
|
|
|
10766
10784
|
if (canReorder) {
|
|
@@ -10776,7 +10794,16 @@ defineInteractionDetector({
|
|
|
10776
10794
|
// moment, not an outcome, and the gesture must not read it as one.
|
|
10777
10795
|
const effects = types.filter((type) => type !== GRAB);
|
|
10778
10796
|
|
|
10797
|
+
// Whether there is anywhere to land, asked at the first press rather than
|
|
10798
|
+
// here: the places are drawn by whatever renders them, which at setup may
|
|
10799
|
+
// not have happened yet.
|
|
10800
|
+
let placesLookedFor = false;
|
|
10801
|
+
|
|
10779
10802
|
const onPointerDown = (pointerDownEvent) => {
|
|
10803
|
+
if (canLand && !placesLookedFor) {
|
|
10804
|
+
placesLookedFor = true;
|
|
10805
|
+
warnWhenNothingToLandOn(element, dropContainer, canLeave);
|
|
10806
|
+
}
|
|
10780
10807
|
// What this element says a release can mean. The gesture then runs only what
|
|
10781
10808
|
// those need — no copy for a move, no drop hint for something that can only
|
|
10782
10809
|
// be thrown away.
|
|
@@ -10789,6 +10816,7 @@ defineInteractionDetector({
|
|
|
10789
10816
|
? `[${REORDERABLE_ATTRIBUTE}]`
|
|
10790
10817
|
: undefined,
|
|
10791
10818
|
containerElement: dropContainer || undefined,
|
|
10819
|
+
outsideOf,
|
|
10792
10820
|
getItemId: (itemElement) => itemElement.id,
|
|
10793
10821
|
direction: { x: axes.includes("x"), y: axes.includes("y") },
|
|
10794
10822
|
// Where it may go, said in the DOM. A thing that is put somewhere stays
|
|
@@ -10796,21 +10824,20 @@ defineInteractionDetector({
|
|
|
10796
10824
|
// the scrollable area can be far larger than the box, and "inside the box"
|
|
10797
10825
|
// is what a hand expects). `data-drag-free` lifts that, and so does a
|
|
10798
10826
|
// named container of places: they are somewhere else by construction, and
|
|
10799
|
-
// a copy kept in its own scroll area could never reach them.
|
|
10800
|
-
//
|
|
10801
|
-
//
|
|
10827
|
+
// a copy kept in its own scroll area could never reach them. So does
|
|
10828
|
+
// "leave": a thing let go of outside has to be able to get there. Left
|
|
10829
|
+
// alone for a throw, which frees the area on its own.
|
|
10802
10830
|
areaConstraint:
|
|
10803
|
-
dropContainer || element.closest(`[data-drag-free]`)
|
|
10831
|
+
dropContainer || element.closest(`[data-drag-free]`) || canLeave
|
|
10804
10832
|
? "none"
|
|
10805
10833
|
: canMove
|
|
10806
10834
|
? "scrollport"
|
|
10807
10835
|
: undefined,
|
|
10808
|
-
threshold: readConfig(THRESHOLD_ATTRIBUTE, undefined),
|
|
10836
|
+
threshold: readConfig(THRESHOLD_ATTRIBUTE$1, undefined),
|
|
10809
10837
|
longPressDelay: readConfig(DELAY_ATTRIBUTE, undefined),
|
|
10810
10838
|
longPressSlop: readConfig(SLOP_ATTRIBUTE, undefined),
|
|
10811
10839
|
tossDistance: readConfig(TOSS_DISTANCE_ATTRIBUTE, undefined),
|
|
10812
10840
|
tossSpeed: readConfig(TOSS_SPEED_ATTRIBUTE, undefined),
|
|
10813
|
-
tossBy,
|
|
10814
10841
|
// The one moment the gesture has that is not a release. Said here rather
|
|
10815
10842
|
// than from the press that led to it, because the press is only one of the
|
|
10816
10843
|
// two ways in: a finger holds still, a mouse travels a few pixels, and it
|
|
@@ -10824,7 +10851,7 @@ defineInteractionDetector({
|
|
|
10824
10851
|
});
|
|
10825
10852
|
}
|
|
10826
10853
|
: undefined,
|
|
10827
|
-
// Handed straight back in
|
|
10854
|
+
// Handed straight back in every case: what `trigger` returns is a
|
|
10828
10855
|
// promise while the answer is still going, which is what the gesture waits
|
|
10829
10856
|
// on before it lets go of what it carries.
|
|
10830
10857
|
onReorder: (fromId, toId, syncCloneWithDropTarget) =>
|
|
@@ -10841,6 +10868,8 @@ defineInteractionDetector({
|
|
|
10841
10868
|
x: gestureInfo.layout.xDelta,
|
|
10842
10869
|
y: gestureInfo.layout.yDelta,
|
|
10843
10870
|
}),
|
|
10871
|
+
onLeave: ({ x, y }) =>
|
|
10872
|
+
trigger(LEAVE, pointerDownEvent, { id: element.id, x, y }),
|
|
10844
10873
|
onMove: ({ x, y }) => trigger(MOVE, pointerDownEvent, { x, y }),
|
|
10845
10874
|
});
|
|
10846
10875
|
};
|
|
@@ -10854,6 +10883,106 @@ defineInteractionDetector({
|
|
|
10854
10883
|
},
|
|
10855
10884
|
});
|
|
10856
10885
|
|
|
10886
|
+
// A place is looked for INSIDE the container and nowhere else, so the surface a
|
|
10887
|
+
// piece already stands on is not one of them — a search never finds what it starts
|
|
10888
|
+
// from. Nothing then happens at all, and beside `leave` it is worse than nothing:
|
|
10889
|
+
// every release is away from every place, so every release is a leave.
|
|
10890
|
+
const warnWhenNothingToLandOn = (element, dropContainer, canLeave) => {
|
|
10891
|
+
const container = dropContainer || element.parentElement;
|
|
10892
|
+
if (!container) {
|
|
10893
|
+
return;
|
|
10894
|
+
}
|
|
10895
|
+
for (const droppable of container.querySelectorAll(
|
|
10896
|
+
`[${DROPPABLE_ATTRIBUTE}]`,
|
|
10897
|
+
)) {
|
|
10898
|
+
if (droppable !== element) {
|
|
10899
|
+
return;
|
|
10900
|
+
}
|
|
10901
|
+
}
|
|
10902
|
+
const consequence = canLeave
|
|
10903
|
+
? ` Every release is then away from every place, which "${LEAVE}" answers.`
|
|
10904
|
+
: " Nothing answers a release.";
|
|
10905
|
+
const droppableAround = element.parentElement?.closest(
|
|
10906
|
+
`[${DROPPABLE_ATTRIBUTE}]`,
|
|
10907
|
+
);
|
|
10908
|
+
if (droppableAround) {
|
|
10909
|
+
console.warn(
|
|
10910
|
+
`interactions: "${LAND}" has nowhere to land — this element sits INSIDE the only place there is, and places are looked for inside ${dropContainer ? `the [${DROP_CONTAINER_ATTRIBUTE}]` : `the element's parent`}, never above it.${consequence} Put ${DROP_CONTAINER_ATTRIBUTE} on an ancestor of both, so the surface is among the places rather than around them.`,
|
|
10911
|
+
droppableAround,
|
|
10912
|
+
);
|
|
10913
|
+
return;
|
|
10914
|
+
}
|
|
10915
|
+
console.warn(
|
|
10916
|
+
`interactions: "${LAND}" has nowhere to land — nothing marked ${DROPPABLE_ATTRIBUTE} is inside ${dropContainer ? `the [${DROP_CONTAINER_ATTRIBUTE}]` : `the element's parent`}, which is where the places are looked for.${consequence} Mark them, and when they are not siblings of this element put ${DROP_CONTAINER_ATTRIBUTE} on an ancestor of both.`,
|
|
10917
|
+
container,
|
|
10918
|
+
);
|
|
10919
|
+
};
|
|
10920
|
+
|
|
10921
|
+
/**
|
|
10922
|
+
* `pan`, `zoom` — a surface under the hand.
|
|
10923
|
+
*
|
|
10924
|
+
* <Box
|
|
10925
|
+
* interactions={{
|
|
10926
|
+
* pan: (event) => moveCenterBy(event.detail), // { x, y } since the last one
|
|
10927
|
+
* zoom: (event) => zoomBy(event.detail), // { factor, x, y }
|
|
10928
|
+
* }}
|
|
10929
|
+
* />
|
|
10930
|
+
*
|
|
10931
|
+
* Neither is an outcome: both are a stream, reported on every frame while the
|
|
10932
|
+
* hand is still moving, and navi otherwise names only what happens once (see
|
|
10933
|
+
* "A gesture whose product is a value" in docs/interactions.md). They are named
|
|
10934
|
+
* here because of what has to be settled BEFORE the press, which is what
|
|
10935
|
+
* `interactions` exists for: `touch-action` on the surface, said from a
|
|
10936
|
+
* stylesheet since a browser decides what a touch may do when it lands; the pan
|
|
10937
|
+
* stepping back for what is carried ACROSS the surface (a marker declaring
|
|
10938
|
+
* `move`, a handle) and for what answers the pointer on its own; the pinch not
|
|
10939
|
+
* beginning as a pan under its first finger; the wheel and the pinch writing
|
|
10940
|
+
* one `zoom`. A handle turned, a sun dragged around a plan have none of that to
|
|
10941
|
+
* arbitrate — the handle is the whole surface of the gesture — and stay
|
|
10942
|
+
* unnamed.
|
|
10943
|
+
*
|
|
10944
|
+
* `pan`'s detail is the movement since the previous `pan`, in px. `zoom`'s is
|
|
10945
|
+
* the factor (above 1 is in) and the point of the surface it is around,
|
|
10946
|
+
* measured inside its border — the point between the fingers, or under the
|
|
10947
|
+
* wheel. What a pixel of pan means in the application's coordinates, and
|
|
10948
|
+
* whether the zoom is continuous or stepped, is the application's.
|
|
10949
|
+
*
|
|
10950
|
+
* Declared alone, `zoom` leaves one pointer to whatever else reads it and takes
|
|
10951
|
+
* only two fingers and the wheel; `pan` alone leaves the wheel to the page.
|
|
10952
|
+
*
|
|
10953
|
+
* Nothing of the gesture is decided here — `installPanZoom` in @jsenv/dom owns
|
|
10954
|
+
* the pointers, the capture, the wheel burst and the click left behind. This
|
|
10955
|
+
* says which names the element answers with, and hands the numbers over.
|
|
10956
|
+
*/
|
|
10957
|
+
|
|
10958
|
+
|
|
10959
|
+
const PAN = "pan";
|
|
10960
|
+
const ZOOM = "zoom";
|
|
10961
|
+
// The same attribute a carried element reads: how far a pointer travels before
|
|
10962
|
+
// it is a gesture rather than a press.
|
|
10963
|
+
const THRESHOLD_ATTRIBUTE = "data-drag-threshold";
|
|
10964
|
+
|
|
10965
|
+
defineInteractionDetector({
|
|
10966
|
+
name: "surface",
|
|
10967
|
+
claims: (type) => type === PAN || type === ZOOM,
|
|
10968
|
+
// A press on the surface may be a tap, a hold or the beginning of a pan, and
|
|
10969
|
+
// nothing may read it as the first until the pointer has said which.
|
|
10970
|
+
disputesPress: true,
|
|
10971
|
+
setup: (element, trigger, { types, readConfig }) => {
|
|
10972
|
+
const canPan = types.includes(PAN);
|
|
10973
|
+
const canZoom = types.includes(ZOOM);
|
|
10974
|
+
return installPanZoom(element, {
|
|
10975
|
+
threshold: readConfig(THRESHOLD_ATTRIBUTE, undefined),
|
|
10976
|
+
onPan: canPan
|
|
10977
|
+
? ({ event, x, y }) => trigger(PAN, event, { x, y })
|
|
10978
|
+
: undefined,
|
|
10979
|
+
onZoom: canZoom
|
|
10980
|
+
? ({ event, factor, x, y }) => trigger(ZOOM, event, { factor, x, y })
|
|
10981
|
+
: undefined,
|
|
10982
|
+
});
|
|
10983
|
+
},
|
|
10984
|
+
});
|
|
10985
|
+
|
|
10857
10986
|
// used by form elements such as <input>, <select>, <textarea> to have their own action bound to a single parameter
|
|
10858
10987
|
// when inside a <form> the form params are updated when the form element single param is updated
|
|
10859
10988
|
const useActionBoundToOneParam = (action, paramsSignal) => {
|
|
@@ -20150,9 +20279,14 @@ const PSEUDO_STATE_CHILD_PROP_SET = new Set(["tabIndex", "tabindex"]);
|
|
|
20150
20279
|
* preventInitialTransition?: boolean,
|
|
20151
20280
|
* separator?: import("ignore:preact").ComponentChildren | ((index: number) => import("ignore:preact").ComponentChildren),
|
|
20152
20281
|
* selfInteractions?: string,
|
|
20282
|
+
* interactions?: { [type: string]: "request_action" | "request_ui_action" | ((event: Event) => void) | false | null | undefined },
|
|
20153
20283
|
* children?: import("ignore:preact").ComponentChildren,
|
|
20154
20284
|
* [key: string]: any,
|
|
20155
20285
|
* }>}
|
|
20286
|
+
* @param {object} [interactions] What this box answers, by interaction name
|
|
20287
|
+
* (see docs/interactions.md). A plain box has no wiring of its own: it does
|
|
20288
|
+
* nothing with `action` (that is a control's prop), so a click on it is
|
|
20289
|
+
* declared here like any other interaction: `interactions={{ click: fn }}`.
|
|
20156
20290
|
*/
|
|
20157
20291
|
const Box = props => {
|
|
20158
20292
|
const {
|
|
@@ -20292,6 +20426,14 @@ const computeBox = (props, parentBoxFlow) => {
|
|
|
20292
20426
|
rest[SELF_INTERACTIONS_ATTRIBUTE] = selfInteractionsAttributeValue(selfInteractions);
|
|
20293
20427
|
}
|
|
20294
20428
|
let as = asProp;
|
|
20429
|
+
if (Object.hasOwn(rest, "action")) {
|
|
20430
|
+
// A control (Button, Link, a field) takes `action` off its props before
|
|
20431
|
+
// reaching here, so an `action` still present is one nothing will run. A
|
|
20432
|
+
// string is a form's own attribute and stays.
|
|
20433
|
+
if (typeof rest.action !== "string") {
|
|
20434
|
+
console.warn(`Box: "action" is a control's prop and a plain box does nothing with it. To answer a click, declare it as an interaction: interactions={{ click: fn }}.`);
|
|
20435
|
+
}
|
|
20436
|
+
}
|
|
20295
20437
|
|
|
20296
20438
|
// A box that scrolls is what gives header/footer/body their meaning, and
|
|
20297
20439
|
// saying overflow="auto" is already saying it — no second prop for the same
|