@jsenv/navi 0.29.49 → 0.29.50

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, smallTouchScreenSignal } from "./jsenv_navi_side_effects.js";
6
6
  export { coarsePointerSignal } from "./jsenv_navi_side_effects.js";
7
- import { elementIsFocusable, createPubSub, dispatchInternalCustomEvent, dispatchCustomEvent, getElementSignature, findEvent, createValueEffect, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, createIterableWeakSet, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, mergeTwoStyles, normalizeStyles, resolveCSSSize, hasCSSSizeUnit, resolveOklchLightness, contrastColor, closestOpenableAncestor, isAncestorOpen, observeAncestorOpenState, getAncestorOpenType, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, onAncestorReopen, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, scrollRoomTowards, findBefore, findAfter, initFocusGroup, scrollIntoViewScoped, getScrollContainer, canScroll, measureWidestChildRow, performTabNavigation, wheelGestureIsTakenFrom, releaseWheelGesture, claimWheelGesture, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement, stringifyStyle as stringifyStyle$1 } from "@jsenv/dom";
7
+ import { elementIsFocusable, createPubSub, dispatchInternalCustomEvent, dispatchCustomEvent, getElementSignature, findEvent, createValueEffect, getVisuallyVisibleInfo, getFirstVisuallyVisibleAncestor, findFocusDelegateTarget, findFocusable, allowWheelThrough, dispatchPublicCustomEvent, resolveCSSColor, ELEMENT_SIZE_CHANGE, findSelfOrAncestorFixedPosition, visibleRectEffect, pickPositionRelativeTo, getBorderSizes, getPaddingSizes, applyNewPosition, measureLongestVisualLineWidth, chainEvent, waitForPressHeld, suppressClickAfterGesture, startDragToTravel, markDragSource, startDragTo, createIterableWeakSet, createEventGroupLogger, getKeyboardEventDefaultAction, activeElementSignal, normalizeStyle, mergeOneStyle, getPositionedParent, mergeTwoStyles, normalizeStyles, resolveCSSSize, hasCSSSizeUnit, resolveOklchLightness, contrastColor, closestOpenableAncestor, isAncestorOpen, observeAncestorOpenState, getAncestorOpenType, scrollRoomTowards, parsePositionArea, snapToPixel, trapFocusInside, trapScrollInside, onAncestorReopen, createGroupTransitionController, getBorderRadius, preventIntermediateScrollbar, createOpacityTransition, watchWheelTravel, findBefore, findAfter, initFocusGroup, scrollIntoViewScoped, getScrollContainer, canScroll, measureWidestChildRow, performTabNavigation, wheelGestureIsTakenFrom, releaseWheelGesture, claimWheelGesture, dragAfterIntent, stickyAsRelativeCoords, createDragToMoveGestureController, getDropTargetInfo, setStyles, useActiveElement, stringifyStyle as stringifyStyle$1 } from "@jsenv/dom";
8
8
  export { contrastColor, findEvent, startDragTo } from "@jsenv/dom";
9
9
  import { signal, computed, effect, batch, useSignal } from "@preact/signals";
10
10
  import { createContext, isValidElement, h, Fragment, render, toChildArray, options, cloneElement } from "preact";
@@ -19034,6 +19034,43 @@ import.meta.css = [/* css */`
19034
19034
  --loading-outline-min-inset: 0px;
19035
19035
  }
19036
19036
 
19037
+ /* A scroller inside a box that TRAVELS keeps its leftovers to itself: what a
19038
+ list has left of a gesture once it has reached its end must not reach the
19039
+ page, or the page moves behind a travel that is already answering the same
19040
+ finger. The rule belongs to @jsenv/dom's drag_to_travel — this is the part
19041
+ of it only navi can write.
19042
+
19043
+ What a browser is asked, and where, is not the same everywhere: Blink asks
19044
+ every scroll container between the pointer and the page, so containing the
19045
+ travelling box answers for everything inside it; Gecko and WebKit ask only
19046
+ the ones that actually scroll, so the box — which travels, it does not
19047
+ scroll — is walked past and the SCROLLER itself has to be told. drag_to_
19048
+ travel says it to everything inside the box for those two, and to the box
19049
+ alone on Blink, where saying it to everything turns anything that clips
19050
+ (an ellipsis, a rounded card, the invisible checkbox covering a selectable
19051
+ row) into a dead zone under the wheel — a scroll container with nothing to
19052
+ scroll, and Blink stops at it.
19053
+
19054
+ Which leaves, on Blink, a travelling box that does NOT clip: nothing is
19055
+ asked, and the leftovers reach the page. RouteTravel is that box, and
19056
+ cannot be made to clip — a scroll container there would become the nearest
19057
+ one for every "position: sticky" inside the pages it holds.
19058
+
19059
+ So navi contains what it KNOWS scrolls. [data-scrollable] is worn by a Box
19060
+ that ASKED for overflow auto/scroll (see below), never by one that merely
19061
+ clips: containing it costs nothing on the engines where the rule above
19062
+ already covers it, and on Blink it is what keeps a list inside a travelling
19063
+ page from taking the page with it. What is left over is a scroller nobody
19064
+ declared — a bare div with an overflow of its own, a textarea, a widget
19065
+ from elsewhere: those still hand their leftovers up, on Blink, under a box
19066
+ that does not clip. */
19067
+ [data-drag-travel*="x"] [data-scrollable] {
19068
+ overscroll-behavior-x: contain !important;
19069
+ }
19070
+ [data-drag-travel*="y"] [data-scrollable] {
19071
+ overscroll-behavior-y: contain !important;
19072
+ }
19073
+
19037
19074
  [data-scrollable] {
19038
19075
  overflow: var(--x-scrollable-overflow, auto);
19039
19076
  --box-header-z-index: var(--navi-z-index-sticky);
@@ -28818,6 +28855,164 @@ const unfreezeSize = (el) => {
28818
28855
  el.style.height = "";
28819
28856
  };
28820
28857
 
28858
+ /**
28859
+ * Pushing a popup docked to an edge (a side panel, a bottom sheet) back the way
28860
+ * it came in closes it: it follows the finger while it travels, and letting go
28861
+ * either finishes the departure or brings it back to rest — whichever the
28862
+ * travel was already heading to.
28863
+ *
28864
+ * Reading the pointer is not done here: when a press becomes a gesture, which
28865
+ * axis it leans on, how fast it was going when it was let go, who else has a
28866
+ * claim on it (a field, a scroller with room left, a box inside the panel that
28867
+ * travels the same way) is one gesture system for the whole codebase
28868
+ * (@jsenv/dom's startDragToTravel). A panel closing is a travel of exactly one
28869
+ * box, towards the edge it is docked to and nowhere else — which is all this
28870
+ * file has to say.
28871
+ *
28872
+ * The release travel is driven here (Web Animations) rather than handed to the
28873
+ * exit transition the `animation` prop may install: its pace is what is left to
28874
+ * cover, which a CSS transition written for a full-size travel cannot know, and
28875
+ * two movements on the same property would contradict each other. So CSS
28876
+ * transitions are suppressed on the panel for the whole gesture, release
28877
+ * included, and restored once the panel has either left or come back.
28878
+ */
28879
+
28880
+
28881
+ // What a full-size release travel takes; a shorter one takes proportionally
28882
+ // less, so the panel keeps the same speed whenever the finger let go.
28883
+ const TRAVEL_DURATION = 220;
28884
+
28885
+ // Which way a popup docked to each side travels — read by the panel itself to
28886
+ // say so in the DOM, which is how a box travelling INSIDE it (a row of slides,
28887
+ // a route travel) knows that axis is already walked.
28888
+ const SWIPE_AXIS_BY_SIDE = {
28889
+ left: "x",
28890
+ right: "x",
28891
+ top: "y",
28892
+ bottom: "y",
28893
+ };
28894
+ // Which way the finger pushes to send the panel back where it came from, in
28895
+ // screen coordinates: positive is right/down, the same sign the gesture reports.
28896
+ const CLOSE_DIRECTION_BY_SIDE = { left: -1, right: 1, top: -1, bottom: 1 };
28897
+
28898
+ /**
28899
+ * Builds the `pointerdown` handler a popup docked to `side` answers with.
28900
+ *
28901
+ * `grip` narrows where the gesture may start to one part of the popup, given as
28902
+ * a selector matched inside it: a popup whose whole surface is a place to push
28903
+ * from has nothing else to do with the press, while one made of content the
28904
+ * finger operates (a bottom sheet full of controls) only offers the strip that
28905
+ * is there to be held. A popup that has no such part is pushed from anywhere,
28906
+ * so naming a grip never leaves it undismissable.
28907
+ */
28908
+ const createSwipeToClose = (side, { grip } = {}) => {
28909
+ const axis = SWIPE_AXIS_BY_SIDE[side];
28910
+ const closeDirection = CLOSE_DIRECTION_BY_SIDE[side];
28911
+
28912
+ return (pointerDownEvent) => {
28913
+ const panelEl = pointerDownEvent.currentTarget;
28914
+ if (panelEl.getAttribute("aria-expanded") !== "true") {
28915
+ return;
28916
+ }
28917
+ if (grip) {
28918
+ const gripEl = panelEl.querySelector(grip);
28919
+ if (gripEl && !gripEl.contains(pointerDownEvent.target)) {
28920
+ return;
28921
+ }
28922
+ }
28923
+
28924
+ // Where the panel stands, written on it directly: the gesture reports a
28925
+ // distance in screen coordinates, which is exactly what a translate takes.
28926
+ const paint = (distance) => {
28927
+ panelEl.style.translate =
28928
+ axis === "x" ? `${distance}px 0px` : `0px ${distance}px`;
28929
+ };
28930
+ const restore = () => {
28931
+ panelEl.style.translate = "";
28932
+ panelEl.style.transitionProperty = "";
28933
+ panelEl.style.userSelect = "";
28934
+ };
28935
+ // The resting value is written first and the animation only covers the way
28936
+ // to it: both end on the same number, so nothing is seen changing when the
28937
+ // animation hands the panel back to its own style.
28938
+ const travelTo = (from, to, onArrival) => {
28939
+ const covered = from > to ? from - to : to - from;
28940
+ paint(to);
28941
+ const animation = panelEl.animate(
28942
+ [
28943
+ { translate: translateOf(axis, from) },
28944
+ { translate: translateOf(axis, to) },
28945
+ ],
28946
+ {
28947
+ duration: (covered / sizeOf(panelEl, axis)) * TRAVEL_DURATION,
28948
+ easing: "ease-out",
28949
+ },
28950
+ );
28951
+ animation.finished.then(onArrival, () => {});
28952
+ };
28953
+ const close = (event) => {
28954
+ triggerNaviCommand(panelEl, "--navi-close", event);
28955
+ if (panelEl.getAttribute("aria-expanded") === "true") {
28956
+ // Refused: the panel is staying, so it goes back where it was.
28957
+ travelTo(sizeOf(panelEl, axis) * closeDirection, 0, restore);
28958
+ return;
28959
+ }
28960
+ restore();
28961
+ };
28962
+
28963
+ startDragToTravel(pointerDownEvent, {
28964
+ element: panelEl,
28965
+ axes: axis,
28966
+ onStart: ({ sign, target }) => {
28967
+ if (sign !== closeDirection) {
28968
+ // Pushing the panel further in is not a travel: there is nowhere that
28969
+ // way, and reading it as one would make the panel lean at every
28970
+ // gesture that starts by going the wrong way.
28971
+ return false;
28972
+ }
28973
+ const size = sizeOf(panelEl, axis);
28974
+ if (!size) {
28975
+ return false;
28976
+ }
28977
+ // Something else with a better claim on the gesture: a scroller between
28978
+ // the finger and the panel, with room left that way.
28979
+ if (scrollRoomTowards(target, panelEl, axis, sign)) {
28980
+ return false;
28981
+ }
28982
+ panelEl.style.transitionProperty = "none";
28983
+ panelEl.style.userSelect = "none";
28984
+ // What the first pixels of the gesture may have started selecting is
28985
+ // not a selection, it is the beginning of this travel.
28986
+ window.getSelection().removeAllRanges();
28987
+ // The way out is the only way there is anything: pulling the other way
28988
+ // leans against a wall and comes back.
28989
+ return {
28990
+ size,
28991
+ travelBack: closeDirection > 0,
28992
+ travelOn: closeDirection < 0,
28993
+ };
28994
+ },
28995
+ onPull: ({ pulled }) => {
28996
+ paint(pulled);
28997
+ },
28998
+ onEnd: ({ pulled, size, travels, event }) => {
28999
+ if (travels) {
29000
+ travelTo(pulled, size * closeDirection, () => close(event));
29001
+ return;
29002
+ }
29003
+ travelTo(pulled, 0, restore);
29004
+ },
29005
+ });
29006
+ };
29007
+ };
29008
+
29009
+ const sizeOf = (element, axis) => {
29010
+ const rect = element.getBoundingClientRect();
29011
+ return axis === "x" ? rect.width : rect.height;
29012
+ };
29013
+ const translateOf = (axis, distance) =>
29014
+ axis === "x" ? `${distance}px 0px` : `0px ${distance}px`;
29015
+
28821
29016
  installImportMetaCssBuild(import.meta);/**
28822
29017
  * A dialog is a surface, not a control: it holds no value, has no action and
28823
29018
  * aggregates nothing. Fields and a submit go in a `<Form>` inside it, exactly
@@ -29109,6 +29304,16 @@ const css$V = /* css */`
29109
29304
  &:has(> [data-focus-outline-delegate][data-focus-visible]) {
29110
29305
  outline-style: solid;
29111
29306
  }
29307
+ /* The header of a sheet that closes by being pushed back down is a handle,
29308
+ not a piece of the scroll: a finger on it moves the sheet, and letting
29309
+ the browser scroll the sheet under the same gesture would show two
29310
+ movements answering one drag. Direct children only — the sheet's own
29311
+ header, not one belonging to something it contains (the same part
29312
+ swipe_to_close.js takes hold of). */
29313
+ &[data-swipe-to-close] > [data-header] {
29314
+ touch-action: none;
29315
+ }
29316
+
29112
29317
  /* …and the delegate stands down. */
29113
29318
  > [data-focus-outline-delegate] {
29114
29319
  --navi-focus-outline-style: none;
@@ -29250,7 +29455,9 @@ const css$V = /* css */`
29250
29455
  * desktop window, which is still a mouse. It supplies defaults for
29251
29456
  * `positionArea`, `marginWithContainer`, `expandX` and `scrollCapture`, so
29252
29457
  * any of them can still be pinned explicitly. Re-resolves live as the pointer
29253
- * type or the window size changes.
29458
+ * type or the window size changes. A sheet resting on the bottom edge is also
29459
+ * pushed back down to close it, held by its header (a direct child `Box` with
29460
+ * the `header` prop) — or from anywhere when it has none. See `swipe_to_close.js`.
29254
29461
  * @param {string} [props.positionArea="center"] - Where to dock the dialog
29255
29462
  * within its container (the viewport for `layer="top"`, the positioned
29256
29463
  * ancestor for `layer="local"`) — Dialog is never anchored to a real
@@ -29504,6 +29711,13 @@ const DOCKED = {
29504
29711
  scrollCapture: true
29505
29712
  };
29506
29713
 
29714
+ // Where a bottom sheet is held to push it back down: the strip a direct-child
29715
+ // Box declares with `header` — the rest of the sheet is content the finger
29716
+ // operates, and a drag started there would fight whatever it landed on. A sheet
29717
+ // with no header of its own is pushed from anywhere (see createSwipeToClose's
29718
+ // own `grip`).
29719
+ const DOCKED_SWIPE_GRIP = ":scope > [data-header]";
29720
+
29507
29721
  // The first control inside `dialogEl` that is mid-action, if any. Walks the
29508
29722
  // controls rather than reading an attribute off the dialog: a dialog carries no
29509
29723
  // state of its own (see this file's top comment), and `aria-busy` on the
@@ -29654,6 +29868,14 @@ const useDialogProps = props => {
29654
29868
  y: "center",
29655
29869
  x: "center"
29656
29870
  };
29871
+ // Pushing the sheet back down closes it — a bottom sheet is reached with a
29872
+ // thumb, and the thumb is already on the edge it would push. Only a sheet
29873
+ // actually resting on the bottom edge: anywhere else the gesture would send
29874
+ // the dialog somewhere it never came from.
29875
+ const swipeToCloseDown = isDocked && parsedPositionArea.y === "bottom";
29876
+ const onSwipePointerDown = swipeToCloseDown ? createSwipeToClose("bottom", {
29877
+ grip: DOCKED_SWIPE_GRIP
29878
+ }) : null;
29657
29879
  // A corner sitting exactly on the container's own corner must not be
29658
29880
  // rounded: the gap a radius carves out would show the container through it,
29659
29881
  // reading as a rendering glitch rather than as a rounded box. A corner is on
@@ -30151,6 +30373,16 @@ const useDialogProps = props => {
30151
30373
  "data-flush-right": flushEdges.right ? "" : undefined,
30152
30374
  "data-flush-bottom": flushEdges.bottom ? "" : undefined,
30153
30375
  "data-flush-left": flushEdges.left ? "" : undefined,
30376
+ "data-swipe-to-close": swipeToCloseDown ? "" : undefined,
30377
+ // The axis the sheet travels on when it is pushed back, said to the shared
30378
+ // gesture layer: it is what a box travelling inside the sheet reads to know
30379
+ // this axis is already walked (see @jsenv/dom's drag_to_travel).
30380
+ "data-drag-travel": swipeToCloseDown ? SWIPE_AXIS_BY_SIDE.bottom : undefined,
30381
+ "data-travel-by-drag": swipeToCloseDown ? SWIPE_AXIS_BY_SIDE.bottom : undefined,
30382
+ "onPointerDown": e => {
30383
+ rest.onPointerDown?.(e);
30384
+ onSwipePointerDown?.(e);
30385
+ },
30154
30386
  "onKeyDown": e => {
30155
30387
  onKeyDown?.(e);
30156
30388
  onKeyDownShortcuts(e);
@@ -70769,150 +71001,6 @@ const ViewportLayout = props => {
70769
71001
  });
70770
71002
  };
70771
71003
 
70772
- /**
70773
- * Pushing a side panel back the way it came in closes it: the panel follows
70774
- * the finger while it travels, and letting go either finishes the departure or
70775
- * brings it back to rest — whichever the travel was already heading to.
70776
- *
70777
- * Reading the pointer is not done here: when a press becomes a gesture, which
70778
- * axis it leans on, how fast it was going when it was let go, who else has a
70779
- * claim on it (a field, a scroller with room left, a box inside the panel that
70780
- * travels the same way) is one gesture system for the whole codebase
70781
- * (@jsenv/dom's startDragToTravel). A panel closing is a travel of exactly one
70782
- * box, towards the edge it is docked to and nowhere else — which is all this
70783
- * file has to say.
70784
- *
70785
- * The release travel is driven here (Web Animations) rather than handed to the
70786
- * exit transition the `animation` prop may install: its pace is what is left to
70787
- * cover, which a CSS transition written for a full-size travel cannot know, and
70788
- * two movements on the same property would contradict each other. So CSS
70789
- * transitions are suppressed on the panel for the whole gesture, release
70790
- * included, and restored once the panel has either left or come back.
70791
- */
70792
-
70793
-
70794
- // What a full-size release travel takes; a shorter one takes proportionally
70795
- // less, so the panel keeps the same speed whenever the finger let go.
70796
- const TRAVEL_DURATION = 220;
70797
-
70798
- // Which way a panel docked to each side travels — read by the panel itself to
70799
- // say so in the DOM, which is how a box travelling INSIDE it (a row of slides,
70800
- // a route travel) knows that axis is already walked.
70801
- const SWIPE_AXIS_BY_SIDE = {
70802
- left: "x",
70803
- right: "x",
70804
- top: "y",
70805
- bottom: "y",
70806
- };
70807
- // Which way the finger pushes to send the panel back where it came from, in
70808
- // screen coordinates: positive is right/down, the same sign the gesture reports.
70809
- const CLOSE_DIRECTION_BY_SIDE = { left: -1, right: 1, top: -1, bottom: 1 };
70810
-
70811
- /**
70812
- * Builds the `pointerdown` handler a side panel docked to `side` answers with.
70813
- */
70814
- const createSwipeToClose = (side) => {
70815
- const axis = SWIPE_AXIS_BY_SIDE[side];
70816
- const closeDirection = CLOSE_DIRECTION_BY_SIDE[side];
70817
-
70818
- return (pointerDownEvent) => {
70819
- const panelEl = pointerDownEvent.currentTarget;
70820
- if (panelEl.getAttribute("aria-expanded") !== "true") {
70821
- return;
70822
- }
70823
-
70824
- // Where the panel stands, written on it directly: the gesture reports a
70825
- // distance in screen coordinates, which is exactly what a translate takes.
70826
- const paint = (distance) => {
70827
- panelEl.style.translate =
70828
- axis === "x" ? `${distance}px 0px` : `0px ${distance}px`;
70829
- };
70830
- const restore = () => {
70831
- panelEl.style.translate = "";
70832
- panelEl.style.transitionProperty = "";
70833
- panelEl.style.userSelect = "";
70834
- };
70835
- // The resting value is written first and the animation only covers the way
70836
- // to it: both end on the same number, so nothing is seen changing when the
70837
- // animation hands the panel back to its own style.
70838
- const travelTo = (from, to, onArrival) => {
70839
- const covered = from > to ? from - to : to - from;
70840
- paint(to);
70841
- const animation = panelEl.animate(
70842
- [
70843
- { translate: translateOf(axis, from) },
70844
- { translate: translateOf(axis, to) },
70845
- ],
70846
- {
70847
- duration: (covered / sizeOf(panelEl, axis)) * TRAVEL_DURATION,
70848
- easing: "ease-out",
70849
- },
70850
- );
70851
- animation.finished.then(onArrival, () => {});
70852
- };
70853
- const close = (event) => {
70854
- triggerNaviCommand(panelEl, "--navi-close", event);
70855
- if (panelEl.getAttribute("aria-expanded") === "true") {
70856
- // Refused: the panel is staying, so it goes back where it was.
70857
- travelTo(sizeOf(panelEl, axis) * closeDirection, 0, restore);
70858
- return;
70859
- }
70860
- restore();
70861
- };
70862
-
70863
- startDragToTravel(pointerDownEvent, {
70864
- element: panelEl,
70865
- axes: axis,
70866
- onStart: ({ sign, target }) => {
70867
- if (sign !== closeDirection) {
70868
- // Pushing the panel further in is not a travel: there is nowhere that
70869
- // way, and reading it as one would make the panel lean at every
70870
- // gesture that starts by going the wrong way.
70871
- return false;
70872
- }
70873
- const size = sizeOf(panelEl, axis);
70874
- if (!size) {
70875
- return false;
70876
- }
70877
- // Something else with a better claim on the gesture: a scroller between
70878
- // the finger and the panel, with room left that way.
70879
- if (scrollRoomTowards(target, panelEl, axis, sign)) {
70880
- return false;
70881
- }
70882
- panelEl.style.transitionProperty = "none";
70883
- panelEl.style.userSelect = "none";
70884
- // What the first pixels of the gesture may have started selecting is
70885
- // not a selection, it is the beginning of this travel.
70886
- window.getSelection().removeAllRanges();
70887
- // The way out is the only way there is anything: pulling the other way
70888
- // leans against a wall and comes back.
70889
- return {
70890
- size,
70891
- travelBack: closeDirection > 0,
70892
- travelOn: closeDirection < 0,
70893
- };
70894
- },
70895
- onPull: ({ pulled }) => {
70896
- paint(pulled);
70897
- },
70898
- onEnd: ({ pulled, size, travels, event }) => {
70899
- if (travels) {
70900
- travelTo(pulled, size * closeDirection, () => close(event));
70901
- return;
70902
- }
70903
- travelTo(pulled, 0, restore);
70904
- },
70905
- });
70906
- };
70907
- };
70908
-
70909
- const sizeOf = (element, axis) => {
70910
- const rect = element.getBoundingClientRect();
70911
- return axis === "x" ? rect.width : rect.height;
70912
- };
70913
- const translateOf = (axis, distance) =>
70914
- axis === "x" ? `${distance}px 0px` : `0px ${distance}px`;
70915
-
70916
71004
  installImportMetaCssBuild(import.meta);/**
70917
71005
  * A drawer docked flush to a viewport (or container) edge, built on top of
70918
71006
  * `Popup`. Sizing, the perpendicular-axis fill, and the flush-edge