@jsenv/navi 0.30.35 → 0.30.36
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 +200 -60
- package/dist/dev/jsenv_navi.js.map +6 -6
- package/dist/jsenv_navi.js +175 -60
- package/dist/jsenv_navi.js.map +6 -6
- package/docs/AI_INSTRUCTIONS.md +3 -2
- package/docs/drag_to_travel.md +5 -3
- package/docs/interactions.md +19 -0
- package/docs/popup_open.md +7 -6
- package/docs/safe_area.md +27 -0
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -75472,18 +75472,19 @@ const readRoom = () =>
|
|
|
75472
75472
|
visualViewportHeightSignal.peek() - getVirtualKeyboardOverlayHeight();
|
|
75473
75473
|
|
|
75474
75474
|
/**
|
|
75475
|
-
* Pushing a popup docked to an edge (a side panel, a bottom sheet
|
|
75476
|
-
* it came in closes it: it follows the finger while it travels,
|
|
75477
|
-
* either finishes the departure or brings it back to rest —
|
|
75478
|
-
* travel was already heading to.
|
|
75475
|
+
* Pushing a popup docked to an edge (a side panel, a bottom sheet, a toast)
|
|
75476
|
+
* back the way it came in closes it: it follows the finger while it travels,
|
|
75477
|
+
* and letting go either finishes the departure or brings it back to rest —
|
|
75478
|
+
* whichever the travel was already heading to. A popup may be given other ways
|
|
75479
|
+
* out than that one: a toast is also thrown sideways.
|
|
75479
75480
|
*
|
|
75480
75481
|
* Reading the pointer is not done here: when a press becomes a gesture, which
|
|
75481
75482
|
* axis it leans on, how fast it was going when it was let go, who else has a
|
|
75482
75483
|
* claim on it (a field, a scroller with room left, a box inside the panel that
|
|
75483
75484
|
* travels the same way) is one gesture system for the whole codebase
|
|
75484
75485
|
* (@jsenv/dom's startDragToTravel). A panel closing is a travel of exactly one
|
|
75485
|
-
* box,
|
|
75486
|
-
* file has to say.
|
|
75486
|
+
* box, out by one of the edges it may leave by and nowhere else — which is all
|
|
75487
|
+
* this file has to say.
|
|
75487
75488
|
*
|
|
75488
75489
|
* The release travel is driven here (Web Animations) rather than handed to the
|
|
75489
75490
|
* exit transition the `animation` prop may install: its pace is what is left to
|
|
@@ -75498,7 +75499,7 @@ const readRoom = () =>
|
|
|
75498
75499
|
// less, so the panel keeps the same speed whenever the finger let go.
|
|
75499
75500
|
const TRAVEL_DURATION = 220;
|
|
75500
75501
|
|
|
75501
|
-
// Which way a popup
|
|
75502
|
+
// Which way a popup leaving by each side travels — read by the panel itself to
|
|
75502
75503
|
// say so in the DOM, which is how a box travelling INSIDE it (a row of slides,
|
|
75503
75504
|
// a route travel) knows that axis is already walked.
|
|
75504
75505
|
const SWIPE_AXIS_BY_SIDE = {
|
|
@@ -75507,12 +75508,27 @@ const SWIPE_AXIS_BY_SIDE = {
|
|
|
75507
75508
|
top: "y",
|
|
75508
75509
|
bottom: "y",
|
|
75509
75510
|
};
|
|
75510
|
-
// Which way the finger pushes to send the panel
|
|
75511
|
-
//
|
|
75511
|
+
// Which way the finger pushes to send the panel out by each side, in screen
|
|
75512
|
+
// coordinates: positive is right/down, the same sign the gesture reports.
|
|
75512
75513
|
const CLOSE_DIRECTION_BY_SIDE = { left: -1, right: 1, top: -1, bottom: 1 };
|
|
75513
75514
|
|
|
75514
75515
|
/**
|
|
75515
|
-
*
|
|
75516
|
+
* The axes a popup leaving by `sides` travels on, as the gesture and the DOM
|
|
75517
|
+
* say them: "x", "y" or "xy".
|
|
75518
|
+
*/
|
|
75519
|
+
const swipeAxesOf = (sides) => {
|
|
75520
|
+
const hasX = sides.some((side) => SWIPE_AXIS_BY_SIDE[side] === "x");
|
|
75521
|
+
const hasY = sides.some((side) => SWIPE_AXIS_BY_SIDE[side] === "y");
|
|
75522
|
+
if (hasX && hasY) {
|
|
75523
|
+
return "xy";
|
|
75524
|
+
}
|
|
75525
|
+
return hasX ? "x" : "y";
|
|
75526
|
+
};
|
|
75527
|
+
|
|
75528
|
+
/**
|
|
75529
|
+
* Builds the `pointerdown` handler of a popup that leaves by any of `sides`:
|
|
75530
|
+
* usually the one edge it is docked to, the way it came in; a toast is also
|
|
75531
|
+
* thrown sideways, and leaves the way it was thrown.
|
|
75516
75532
|
*
|
|
75517
75533
|
* `grip` says where the gesture may start, as a selector the pressed element is
|
|
75518
75534
|
* matched against with `closest`. A popup that names one is held THERE and
|
|
@@ -75520,11 +75536,17 @@ const CLOSE_DIRECTION_BY_SIDE = { left: -1, right: 1, top: -1, bottom: 1 };
|
|
|
75520
75536
|
* and a press on it is that content's — including gestures of its own, which
|
|
75521
75537
|
* this file has no way of knowing about. A popup that names no grip is pushed
|
|
75522
75538
|
* from its whole surface, which only suits one made of nothing else (a side
|
|
75523
|
-
* panel showing a page).
|
|
75524
|
-
*/
|
|
75525
|
-
const createSwipeToClose = (
|
|
75526
|
-
const
|
|
75527
|
-
|
|
75539
|
+
* panel showing a page, a toast).
|
|
75540
|
+
*/
|
|
75541
|
+
const createSwipeToClose = (sides, { grip } = {}) => {
|
|
75542
|
+
const axes = swipeAxesOf(sides);
|
|
75543
|
+
// The side a push along `axis` towards `sign` leaves by, if it leaves at all.
|
|
75544
|
+
const sideTowards = (axis, sign) =>
|
|
75545
|
+
sides.find(
|
|
75546
|
+
(side) =>
|
|
75547
|
+
SWIPE_AXIS_BY_SIDE[side] === axis &&
|
|
75548
|
+
CLOSE_DIRECTION_BY_SIDE[side] === sign,
|
|
75549
|
+
);
|
|
75528
75550
|
|
|
75529
75551
|
return (pointerDownEvent) => {
|
|
75530
75552
|
const panelEl = pointerDownEvent.currentTarget;
|
|
@@ -75541,6 +75563,11 @@ const createSwipeToClose = (side, { grip } = {}) => {
|
|
|
75541
75563
|
}
|
|
75542
75564
|
}
|
|
75543
75565
|
|
|
75566
|
+
// Decided when the finger picks its axis (onStart), once for the gesture.
|
|
75567
|
+
let axis;
|
|
75568
|
+
// How far the panel travels to be out, for each way it can go on that axis.
|
|
75569
|
+
const distanceOutBySign = new Map();
|
|
75570
|
+
|
|
75544
75571
|
// How far the panel has been pulled, as the style showing it — written on
|
|
75545
75572
|
// the panel while the finger drives it, handed to the release travel as
|
|
75546
75573
|
// its two keyframes. The gesture reports a distance in screen coordinates,
|
|
@@ -75550,7 +75577,7 @@ const createSwipeToClose = (side, { grip } = {}) => {
|
|
|
75550
75577
|
// composes under the placement instead of replacing it.
|
|
75551
75578
|
const styleAt = (distance) => ({
|
|
75552
75579
|
transform: translateOf(axis, distance),
|
|
75553
|
-
clipPath: clipOf(
|
|
75580
|
+
clipPath: clipOf(sides, axis, distance),
|
|
75554
75581
|
});
|
|
75555
75582
|
const paint = (distance) => {
|
|
75556
75583
|
Object.assign(panelEl.style, styleAt(distance));
|
|
@@ -75562,21 +75589,22 @@ const createSwipeToClose = (side, { grip } = {}) => {
|
|
|
75562
75589
|
};
|
|
75563
75590
|
// The resting value is written first and the animation only covers the way
|
|
75564
75591
|
// to it: both end on the same number, so nothing is seen changing when the
|
|
75565
|
-
// animation hands the panel back to its own style.
|
|
75566
|
-
|
|
75592
|
+
// animation hands the panel back to its own style. `span` is the full travel
|
|
75593
|
+
// on that side, which the duration is a fraction of.
|
|
75594
|
+
const travelTo = (from, to, span, onArrival) => {
|
|
75567
75595
|
const covered = from > to ? from - to : to - from;
|
|
75568
75596
|
paint(to);
|
|
75569
75597
|
const animation = panelEl.animate([styleAt(from), styleAt(to)], {
|
|
75570
|
-
duration: (covered /
|
|
75598
|
+
duration: (covered / span) * TRAVEL_DURATION,
|
|
75571
75599
|
easing: "ease-out",
|
|
75572
75600
|
});
|
|
75573
75601
|
animation.finished.then(onArrival, () => {});
|
|
75574
75602
|
};
|
|
75575
|
-
const close = (event) => {
|
|
75603
|
+
const close = (event, out, span) => {
|
|
75576
75604
|
triggerNaviCommand(panelEl, "--navi-close", event);
|
|
75577
75605
|
if (panelEl.getAttribute("aria-expanded") === "true") {
|
|
75578
75606
|
// Refused: the panel is staying, so it goes back where it was.
|
|
75579
|
-
travelTo(
|
|
75607
|
+
travelTo(out, 0, span, restore);
|
|
75580
75608
|
return;
|
|
75581
75609
|
}
|
|
75582
75610
|
restore();
|
|
@@ -75584,79 +75612,94 @@ const createSwipeToClose = (side, { grip } = {}) => {
|
|
|
75584
75612
|
|
|
75585
75613
|
startDragToTravel(pointerDownEvent, {
|
|
75586
75614
|
element: panelEl,
|
|
75587
|
-
axes
|
|
75588
|
-
onStart: ({ sign, target }) => {
|
|
75589
|
-
|
|
75615
|
+
axes,
|
|
75616
|
+
onStart: ({ axis: gestureAxis, sign, target }) => {
|
|
75617
|
+
const side = sideTowards(gestureAxis, sign);
|
|
75618
|
+
if (!side) {
|
|
75590
75619
|
// Pushing the panel further in is not a travel: there is nowhere that
|
|
75591
75620
|
// way, and reading it as one would make the panel lean at every
|
|
75592
75621
|
// gesture that starts by going the wrong way.
|
|
75593
75622
|
return false;
|
|
75594
75623
|
}
|
|
75595
|
-
const size =
|
|
75624
|
+
const size = distanceOut(panelEl, side);
|
|
75596
75625
|
if (!size) {
|
|
75597
75626
|
return false;
|
|
75598
75627
|
}
|
|
75599
75628
|
// Something else with a better claim on the gesture: a scroller between
|
|
75600
75629
|
// the finger and the panel, with room left that way.
|
|
75601
|
-
if (scrollRoomTowards(target, panelEl,
|
|
75630
|
+
if (scrollRoomTowards(target, panelEl, gestureAxis, sign)) {
|
|
75602
75631
|
return false;
|
|
75603
75632
|
}
|
|
75633
|
+
axis = gestureAxis;
|
|
75634
|
+
distanceOutBySign.set(sign, size);
|
|
75635
|
+
const otherSide = sideTowards(gestureAxis, -sign);
|
|
75636
|
+
if (otherSide) {
|
|
75637
|
+
distanceOutBySign.set(-sign, distanceOut(panelEl, otherSide));
|
|
75638
|
+
}
|
|
75604
75639
|
panelEl.style.transitionProperty = "none";
|
|
75605
|
-
//
|
|
75606
|
-
// leans against a wall and comes back.
|
|
75640
|
+
// Only the ways out lead anywhere: pulling towards a side the panel
|
|
75641
|
+
// does not leave by leans against a wall and comes back.
|
|
75607
75642
|
return {
|
|
75608
75643
|
size,
|
|
75609
|
-
travelBack:
|
|
75610
|
-
travelOn:
|
|
75644
|
+
travelBack: Boolean(sideTowards(gestureAxis, 1)),
|
|
75645
|
+
travelOn: Boolean(sideTowards(gestureAxis, -1)),
|
|
75611
75646
|
};
|
|
75612
75647
|
},
|
|
75613
75648
|
onPull: ({ pulled }) => {
|
|
75614
75649
|
paint(pulled);
|
|
75615
75650
|
},
|
|
75616
|
-
onEnd: ({ pulled, size, travels, event }) => {
|
|
75651
|
+
onEnd: ({ pulled, size, sign, travels, event }) => {
|
|
75617
75652
|
if (travels) {
|
|
75618
|
-
|
|
75653
|
+
const span = distanceOutBySign.get(sign);
|
|
75654
|
+
travelTo(pulled, span * sign, span, () =>
|
|
75655
|
+
close(event, span * sign, span),
|
|
75656
|
+
);
|
|
75619
75657
|
return;
|
|
75620
75658
|
}
|
|
75621
|
-
travelTo(pulled, 0, restore);
|
|
75659
|
+
travelTo(pulled, 0, size, restore);
|
|
75622
75660
|
},
|
|
75623
75661
|
});
|
|
75624
75662
|
};
|
|
75625
75663
|
};
|
|
75626
75664
|
|
|
75627
|
-
|
|
75665
|
+
// What takes the popup out of sight: its own size, plus the gap a margin keeps
|
|
75666
|
+
// between it and the edge it is pushed through — the room applyNewPosition
|
|
75667
|
+
// publishes on that side. Travelling its size alone, a popup kept off its edge
|
|
75668
|
+
// (a toast 12px above a bar) would stop with that much of itself still showing.
|
|
75669
|
+
const distanceOut = (element, side) => {
|
|
75628
75670
|
const rect = element.getBoundingClientRect();
|
|
75629
|
-
|
|
75671
|
+
const size = SWIPE_AXIS_BY_SIDE[side] === "x" ? rect.width : rect.height;
|
|
75672
|
+
const room = parseFloat(
|
|
75673
|
+
element.style.getPropertyValue(`--container-position-room-${side}`),
|
|
75674
|
+
);
|
|
75675
|
+
return Number.isNaN(room) ? size : size + room;
|
|
75630
75676
|
};
|
|
75631
75677
|
const translateOf = (axis, distance) =>
|
|
75632
75678
|
axis === "x"
|
|
75633
75679
|
? `translate(${distance}px, 0px)`
|
|
75634
75680
|
: `translate(0px, ${distance}px)`;
|
|
75635
75681
|
// The same cut the entry/exit animation makes (popup_css.js), at a distance the
|
|
75636
|
-
// finger decides instead of a transition:
|
|
75637
|
-
//
|
|
75638
|
-
// edge where a band of glass lies past it, 100vmax out where the
|
|
75639
|
-
// edge does the cutting),
|
|
75640
|
-
//
|
|
75641
|
-
// the box — the pull never takes the popup
|
|
75642
|
-
// only shave what it legitimately paints
|
|
75643
|
-
// policy from popup_css.js rather than the
|
|
75644
|
-
// (a Web Animation on transform AND clip-path,
|
|
75645
|
-
// cut the entrance would not.
|
|
75682
|
+
// finger decides instead of a transition: each edge the popup may leave by on
|
|
75683
|
+
// the axis it travels, as popup_css.js placed it (--x-popup-cut-*-at-area — on
|
|
75684
|
+
// the area's edge where a band of glass lies past it, 100vmax out where the
|
|
75685
|
+
// window's own edge does the cutting), moved by what has been pulled towards
|
|
75686
|
+
// it, so the cut holds that line while the box travels under it, whichever way.
|
|
75687
|
+
// Every other side is left far outside the box — the pull never takes the popup
|
|
75688
|
+
// past them, and cutting there would only shave what it legitimately paints
|
|
75689
|
+
// outside its own box. Reading the policy from popup_css.js rather than the
|
|
75690
|
+
// rooms keeps the release travel (a Web Animation on transform AND clip-path,
|
|
75691
|
+
// two clocks) from animating a cut the entrance would not.
|
|
75646
75692
|
const UNCUT = "-100vmax";
|
|
75647
|
-
const
|
|
75648
|
-
|
|
75649
|
-
const
|
|
75650
|
-
|
|
75651
|
-
|
|
75652
|
-
|
|
75653
|
-
|
|
75654
|
-
return `
|
|
75655
|
-
}
|
|
75656
|
-
|
|
75657
|
-
return `inset(${UNCUT} ${UNCUT} ${cut} ${UNCUT})`;
|
|
75658
|
-
}
|
|
75659
|
-
return `inset(${UNCUT} ${UNCUT} ${UNCUT} ${cut})`;
|
|
75693
|
+
const INSET_ORDER = ["top", "right", "bottom", "left"];
|
|
75694
|
+
const clipOf = (sides, axis, distance) => {
|
|
75695
|
+
const insets = INSET_ORDER.map((side) => {
|
|
75696
|
+
if (!sides.includes(side) || SWIPE_AXIS_BY_SIDE[side] !== axis) {
|
|
75697
|
+
return UNCUT;
|
|
75698
|
+
}
|
|
75699
|
+
const towards = distance * CLOSE_DIRECTION_BY_SIDE[side];
|
|
75700
|
+
return `calc(var(--x-popup-cut-${side}-at-area, ${UNCUT}) + ${towards}px)`;
|
|
75701
|
+
});
|
|
75702
|
+
return `inset(${insets.join(" ")})`;
|
|
75660
75703
|
};
|
|
75661
75704
|
|
|
75662
75705
|
/**
|
|
@@ -77438,7 +77481,7 @@ const useDialogProps = props => {
|
|
|
77438
77481
|
// actually resting on an edge: anywhere else the gesture would send the
|
|
77439
77482
|
// dialog somewhere it never came from.
|
|
77440
77483
|
const swipeToCloseSide = isDocked && (parsedPositionArea.y === "top" || parsedPositionArea.y === "bottom") ? parsedPositionArea.y : null;
|
|
77441
|
-
const onSwipePointerDown = swipeToCloseSide ? createSwipeToClose(swipeToCloseSide, {
|
|
77484
|
+
const onSwipePointerDown = swipeToCloseSide ? createSwipeToClose([swipeToCloseSide], {
|
|
77442
77485
|
grip: DOCKED_SWIPE_GRIP
|
|
77443
77486
|
}) : null;
|
|
77444
77487
|
// A corner sitting exactly on the container's own corner must not be
|
|
@@ -78531,6 +78574,16 @@ const css$C = /* css */`
|
|
|
78531
78574
|
to filter). An entrance animation moves the popover through scale and
|
|
78532
78575
|
transform instead, which compose under it: see popup_css.js. */
|
|
78533
78576
|
|
|
78577
|
+
/* What a touch may do on a popover pushed sideways to close it (see
|
|
78578
|
+
swipe_to_close.js): the push takes the sideways axis, the page keeps the
|
|
78579
|
+
vertical scroll. A push up or down travels the way the page scrolls and
|
|
78580
|
+
cannot give that axis away: the browser keeps it, and the gesture is
|
|
78581
|
+
settled by keepTouchRefusable. */
|
|
78582
|
+
&[data-swipe-to-close~="left"],
|
|
78583
|
+
&[data-swipe-to-close~="right"] {
|
|
78584
|
+
touch-action: pan-y;
|
|
78585
|
+
}
|
|
78586
|
+
|
|
78534
78587
|
/* The clamped max, not --popover-maxmax-*: that one ignores what the
|
|
78535
78588
|
placement left available, which reaches here through
|
|
78536
78589
|
--container-position-remaining-* (see applyNewPosition) — the min()
|
|
@@ -78761,6 +78814,20 @@ const css$C = /* css */`
|
|
|
78761
78814
|
* overlap with the anchor; wrap it in `inset(...)` (e.g. `inset(top)`,
|
|
78762
78815
|
* `inset(top-left)`) to overlap the anchor instead (edges touching or
|
|
78763
78816
|
* inside it).
|
|
78817
|
+
* @param {boolean|Array<"top"|"right"|"bottom"|"left">} [props.closeByDrag=false] -
|
|
78818
|
+
* Pushing the popover out of its container closes it: it follows the pointer
|
|
78819
|
+
* and, when released, finishes leaving or comes back to rest. `true` pushes
|
|
78820
|
+
* it back through the edge it is docked to, the word `positionArea` leads
|
|
78821
|
+
* with (`"bottom"`, `"bottom-start"`: pushed down; `"left-end"`: pushed
|
|
78822
|
+
* left); a centred popover rests on no edge and is not pushed (warned in
|
|
78823
|
+
* dev). A list names the sides it may leave by, and it leaves the way it was
|
|
78824
|
+
* pushed: `["bottom", "left", "right"]` for a toast pushed back down behind
|
|
78825
|
+
* the bar or thrown sideways. A popover placed against an anchor at its
|
|
78826
|
+
* opening is not pushed either way (warned in dev). It is pushed from its
|
|
78827
|
+
* whole surface, so it suits a popover holding little else: a button inside
|
|
78828
|
+
* keeps its tap, and a press that turns into a push swallows the click it
|
|
78829
|
+
* would have left. The close keeps, as a cross's does. See docs/interactions.md
|
|
78830
|
+
* for a swipe on the popover's own element, which does not move it.
|
|
78764
78831
|
* @param {string} [props.positionAreaFixed] - Overrides `positionArea` once
|
|
78765
78832
|
* the popover has actually been positioned once, so a live reposition
|
|
78766
78833
|
* (e.g. anchor moved) doesn't jump to a different side.
|
|
@@ -79212,6 +79279,7 @@ const usePopoverProps = props => {
|
|
|
79212
79279
|
positionAreaWhenAnchorIsInvalid,
|
|
79213
79280
|
marginWithContainer = 0,
|
|
79214
79281
|
container = "app",
|
|
79282
|
+
closeByDrag = false,
|
|
79215
79283
|
// "close" → pointer press outside closes (a plain close = commit)
|
|
79216
79284
|
// "cancel" → pointer press outside closes AS A CANCEL (revert)
|
|
79217
79285
|
// "capture"→ absorb the press, stay open
|
|
@@ -79339,6 +79407,7 @@ const usePopoverProps = props => {
|
|
|
79339
79407
|
return popupGroup.register(openController);
|
|
79340
79408
|
}, [popupGroup, openController]);
|
|
79341
79409
|
const hasBackdrop = pressOutside !== "ignore" && backdrop && !popupGroup;
|
|
79410
|
+
const swipeToCloseSides = resolveSwipeToCloseSides(closeByDrag, positionArea);
|
|
79342
79411
|
if (!popupGroup && !backdrop && pressOutside === "capture") {
|
|
79343
79412
|
console.warn(`Popover: pressOutside="capture" needs a backdrop. Absorbing a press is what a wall does, and backdrop={false} takes it away — every press outside reaches the page, exactly as with "ignore".`);
|
|
79344
79413
|
}
|
|
@@ -80172,6 +80241,7 @@ const usePopoverProps = props => {
|
|
|
80172
80241
|
}
|
|
80173
80242
|
}
|
|
80174
80243
|
});
|
|
80244
|
+
const swipeToClose = swipeToCloseSides ? createSwipeToClose(swipeToCloseSides) : null;
|
|
80175
80245
|
Object.assign(contentProps, {
|
|
80176
80246
|
id,
|
|
80177
80247
|
tabIndex,
|
|
@@ -80250,6 +80320,26 @@ const usePopoverProps = props => {
|
|
|
80250
80320
|
onKeyDown?.(e);
|
|
80251
80321
|
onKeyDownShortcuts(e);
|
|
80252
80322
|
},
|
|
80323
|
+
...(swipeToClose ? {
|
|
80324
|
+
"data-swipe-to-close": swipeToCloseSides.join(" "),
|
|
80325
|
+
// The axes it travels on when pushed out, said to the shared gesture
|
|
80326
|
+
// layer before any finger lands (see side_panel.jsx, which says the
|
|
80327
|
+
// same for the same reasons).
|
|
80328
|
+
"data-drag-travel": swipeAxesOf(swipeToCloseSides),
|
|
80329
|
+
"data-travel-by-drag": swipeAxesOf(swipeToCloseSides),
|
|
80330
|
+
"onTouchMove": e => {
|
|
80331
|
+
rest.onTouchMove?.(e);
|
|
80332
|
+
keepTouchRefusable(e);
|
|
80333
|
+
},
|
|
80334
|
+
"onPointerDown": e => {
|
|
80335
|
+
rest.onPointerDown?.(e);
|
|
80336
|
+
// Placed against an anchor at this opening: it is not in its
|
|
80337
|
+
// container to be pushed out of (warned at the opening).
|
|
80338
|
+
if (!anchorElementRef.current) {
|
|
80339
|
+
swipeToClose(e);
|
|
80340
|
+
}
|
|
80341
|
+
}
|
|
80342
|
+
} : null),
|
|
80253
80343
|
// A popup opened from inside this one is in front of it, not a peer of
|
|
80254
80344
|
// it: the group stops here.
|
|
80255
80345
|
"children": popupGroup ? jsx(PopupGroupContext.Provider, {
|
|
@@ -80323,6 +80413,31 @@ const resolvePositionAreaAndAnimationKind = ({
|
|
|
80323
80413
|
Popover.Close = PopupClose;
|
|
80324
80414
|
Popover.Title = PopupTitle;
|
|
80325
80415
|
|
|
80416
|
+
// The sides a popover pushed away leaves by, or null when it is not pushed.
|
|
80417
|
+
const resolveSwipeToCloseSides = (closeByDrag, positionArea) => {
|
|
80418
|
+
if (!closeByDrag) {
|
|
80419
|
+
return null;
|
|
80420
|
+
}
|
|
80421
|
+
if (Array.isArray(closeByDrag)) {
|
|
80422
|
+
const sides = closeByDrag.filter(side => side in SWIPE_AXIS_BY_SIDE);
|
|
80423
|
+
return sides.length ? sides : null;
|
|
80424
|
+
}
|
|
80425
|
+
const side = dockedSideOf(positionArea);
|
|
80426
|
+
if (!side) {
|
|
80427
|
+
return null;
|
|
80428
|
+
}
|
|
80429
|
+
return [side];
|
|
80430
|
+
};
|
|
80431
|
+
const INSET_TOKEN_RE = /^inset\(\s*([a-z-]+)\s*\)$/;
|
|
80432
|
+
// The edge a popover docked to its container rests on: the word its
|
|
80433
|
+
// positionArea leads with, "bottom-start" resting on the bottom and "left-end"
|
|
80434
|
+
// on the left. "center" rests on none.
|
|
80435
|
+
const dockedSideOf = positionArea => {
|
|
80436
|
+
const token = INSET_TOKEN_RE.exec(positionArea)?.[1] ?? positionArea;
|
|
80437
|
+
const side = token.split("-")[0];
|
|
80438
|
+
return side in SWIPE_AXIS_BY_SIDE ? side : null;
|
|
80439
|
+
};
|
|
80440
|
+
|
|
80326
80441
|
// The placement needs pixels: a value only CSS could evaluate (a percentage, a
|
|
80327
80442
|
// calc()…) would put the popover at NaN, which is the top of the page.
|
|
80328
80443
|
const resolveMarginInPixels = (margin, name) => {
|
|
@@ -108030,7 +108145,7 @@ const SidePanel = ({
|
|
|
108030
108145
|
}) => {
|
|
108031
108146
|
import.meta.css = [css, "@jsenv/navi/src/layout/side_panel.jsx"];
|
|
108032
108147
|
const rest = dropPickerPopupProps(restGiven);
|
|
108033
|
-
const onSwipePointerDown = closeByDrag ? createSwipeToClose(side) : null;
|
|
108148
|
+
const onSwipePointerDown = closeByDrag ? createSwipeToClose([side]) : null;
|
|
108034
108149
|
return jsx(Popup, {
|
|
108035
108150
|
mode: mode
|
|
108036
108151
|
// Spread rather than written: the collision warning between `signal` and
|