@jsenv/navi 0.30.3 → 0.30.5

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.
@@ -11346,7 +11346,7 @@ const getMessageString = (message) => {
11346
11346
  if (typeof message === "string") {
11347
11347
  return message;
11348
11348
  }
11349
- if (Error.isError(message)) {
11349
+ if (message instanceof Error) {
11350
11350
  return message.message;
11351
11351
  }
11352
11352
  return undefined;
@@ -19492,7 +19492,7 @@ const openCallout = (
19492
19492
  element: calloutElement,
19493
19493
  });
19494
19494
  } else {
19495
- if (Error.isError(newMessage)) {
19495
+ if (newMessage instanceof Error) {
19496
19496
  const error = newMessage;
19497
19497
  newMessage = error.message;
19498
19498
  if (showErrorStack && error.stack) {
@@ -23051,7 +23051,7 @@ const sayRefusal = (element, error) => {
23051
23051
  let anchorElement = element;
23052
23052
  if (
23053
23053
  typeof error === "object" &&
23054
- !Error.isError(error) &&
23054
+ !(error instanceof Error) &&
23055
23055
  !isValidElement(error)
23056
23056
  ) {
23057
23057
  message = error.message;
@@ -25680,7 +25680,7 @@ const useExecuteAction = (
25680
25680
  // and it is then shown exactly as if there were no mapping at all.
25681
25681
  if (typeof errorMappingResult === "string") {
25682
25682
  message = errorMappingResult;
25683
- } else if (Error.isError(errorMappingResult)) {
25683
+ } else if (errorMappingResult instanceof Error) {
25684
25684
  message = errorMappingResult;
25685
25685
  } else if (isValidElement(errorMappingResult)) {
25686
25686
  message = errorMappingResult;
@@ -36505,9 +36505,23 @@ const collectRoutePages = children => {
36505
36505
  * Whether the next container down is a row a hand scrolls: every page has a
36506
36506
  * place on it from the start, and the browser moves between them (see
36507
36507
  * RouteTravel's `nativeScroll`). Told to one container, not to the pages it holds.
36508
+ * False, or `{ renderNeighbour }`.
36508
36509
  */
36509
36510
  const RouteRowContext = createContext(false);
36510
36511
 
36512
+ /**
36513
+ * What stands in the pane of a neighbour until its page does. A component of its
36514
+ * own: what the application calls in there (a hook, a signal) answers to it, however
36515
+ * many neighbours the row has at that moment.
36516
+ */
36517
+ const RouteRowNeighbour = ({
36518
+ renderNeighbour,
36519
+ page
36520
+ }) => jsx("div", {
36521
+ "data-route-row-neighbour": "",
36522
+ children: renderNeighbour(page)
36523
+ });
36524
+
36511
36525
  // A container as a row: one pane per leaf, in the order they are written. A page
36512
36526
  // still builds when its route first matches, since a route's actions only ask
36513
36527
  // while it does: before that its pane is empty. Once built it stays, with what
@@ -36520,6 +36534,9 @@ const RouteRowContext = createContext(false);
36520
36534
  const RouteRowContainer = ({
36521
36535
  children
36522
36536
  }) => {
36537
+ const {
36538
+ renderNeighbour
36539
+ } = useContext(RouteRowContext);
36523
36540
  const {
36524
36541
  activeBranch,
36525
36542
  leaves
@@ -36568,16 +36585,19 @@ const RouteRowContainer = ({
36568
36585
  shown: true
36569
36586
  }, "fallback");
36570
36587
  }
36571
- return leaves.map(leaf => jsx(RouteRowPane, {
36588
+ const shownIndex = activeBranch ? leaves.findIndex(leaf => leaf.id === activeBranch.id) : -1;
36589
+ return leaves.map((leaf, index) => jsx(RouteRowPane, {
36572
36590
  branch: leaf,
36573
36591
  page: pageOf(leaf),
36574
- shown: activeBranch !== null && leaf.id === activeBranch.id
36592
+ shown: index === shownIndex,
36593
+ renderNeighbour: shownIndex !== -1 && (index === shownIndex - 1 || index === shownIndex + 1) ? renderNeighbour : undefined
36575
36594
  }, leaf.id));
36576
36595
  };
36577
36596
  const RouteRowPane = ({
36578
36597
  branch,
36579
36598
  page,
36580
- shown
36599
+ shown,
36600
+ renderNeighbour
36581
36601
  }) => {
36582
36602
  // The very vnode rendered last, while not on screen: Preact does not look into
36583
36603
  // it, and a page nobody can see costs nothing when the address moves.
@@ -36596,7 +36616,10 @@ const RouteRowPane = ({
36596
36616
  "data-route-row-pane": "",
36597
36617
  "data-built": contentRef.current ? "" : undefined,
36598
36618
  inert: !shown,
36599
- children: contentRef.current
36619
+ children: contentRef.current || renderNeighbour && jsx(RouteRowNeighbour, {
36620
+ renderNeighbour: renderNeighbour,
36621
+ page: branchRoutePage(branch)
36622
+ })
36600
36623
  });
36601
36624
  };
36602
36625
 
@@ -40412,7 +40435,9 @@ const readRowStretch = (scroller, pages, page) => {
40412
40435
  /**
40413
40436
  * @param {Element} scroller
40414
40437
  * @param {object} options
40415
- * @param {() => void} options.onScroll - the row moved.
40438
+ * @param {(move: {finger: boolean}) => void} options.onScroll - the row moved.
40439
+ * `finger`: a touch has come since the row last stopped, so a lift will say where
40440
+ * it goes (`onLeftToSnap`). Without one (a trackpad, a wheel) nothing will.
40416
40441
  * @param {(event: Event|null) => void} options.onLand - the row may have stopped.
40417
40442
  * Read `pageAtRest` to know whether it stopped on a page. Heard more than once
40418
40443
  * for one landing, so what it does must find nothing to do the second time.
@@ -40428,11 +40453,13 @@ const watchRowScroll = (
40428
40453
  ) => {
40429
40454
  let stoppedTimeout = null;
40430
40455
  let touching = false;
40456
+ let touchedSinceStop = false;
40431
40457
  let liftedAt = null;
40432
40458
  const landOnSilence = () => {
40433
40459
  liftedAt = null;
40434
40460
  // A finger holding the row still: nothing has stopped.
40435
40461
  if (!touching) {
40462
+ touchedSinceStop = false;
40436
40463
  onLand(null);
40437
40464
  }
40438
40465
  };
@@ -40441,7 +40468,7 @@ const watchRowScroll = (
40441
40468
  stoppedTimeout = setTimeout(landOnSilence, STOPPED_SILENCE);
40442
40469
  };
40443
40470
  const onScrollEvent = () => {
40444
- onScroll();
40471
+ onScroll({ finger: touchedSinceStop });
40445
40472
  armSilence();
40446
40473
  if (!liftedAt) {
40447
40474
  return;
@@ -40457,6 +40484,7 @@ const watchRowScroll = (
40457
40484
  };
40458
40485
  const onTouchStart = () => {
40459
40486
  touching = true;
40487
+ touchedSinceStop = true;
40460
40488
  liftedAt = null;
40461
40489
  clearTimeout(stoppedTimeout);
40462
40490
  };
@@ -40484,6 +40512,8 @@ const watchRowScroll = (
40484
40512
 
40485
40513
  installImportMetaCssBuild(import.meta);
40486
40514
  const css$16 = /* css */`.navi_route_travel {
40515
+ flex-direction: column;
40516
+ display: flex;
40487
40517
  position: relative;
40488
40518
  }
40489
40519
 
@@ -40535,6 +40565,15 @@ const css$16 = /* css */`.navi_route_travel {
40535
40565
  overflow: clip;
40536
40566
  }
40537
40567
 
40568
+ & > [data-route-row-pane] > [data-route-row-neighbour] {
40569
+ contain: layout paint;
40570
+ visibility: visible;
40571
+ flex-direction: column;
40572
+ display: flex;
40573
+ position: absolute;
40574
+ inset: 0;
40575
+ }
40576
+
40538
40577
  & > [data-route-row-live] {
40539
40578
  flex-direction: column;
40540
40579
  grid-row: 1;
@@ -40567,6 +40606,7 @@ const SET_OFF_PULL = 16;
40567
40606
  * routes?: Array<object|{route: object, params?: object}>,
40568
40607
  * travelByDrag?: boolean,
40569
40608
  * onTravel?: (detail: {route: object, params: object|undefined, cause: string}) => void|Promise<void>,
40609
+ * renderNeighbour?: (page: {route: object, params: object|undefined}) => import("preact").ComponentChildren,
40570
40610
  * }>}
40571
40611
  * @param {Array<object|{route: object, params?: object}>} [props.routes] - a window
40572
40612
  * over something longer, in order. Read from the <Route> children by default,
@@ -40583,6 +40623,13 @@ const SET_OFF_PULL = 16;
40583
40623
  * of the page under six entries. A tab pressed goes the way its <Link> says —
40584
40624
  * `<Link replace>` for a row of tabs, so a press and a swipe towards the same
40585
40625
  * neighbour write the same history.
40626
+ * @param {(page: {route: object, params: object|undefined}) => import("preact").ComponentChildren} [props.renderNeighbour]
40627
+ * - what stands in the pane of a page next to the one on screen, until the hand
40628
+ * has pulled far enough to bring that page: its background, its skeleton, what the
40629
+ * list already knows. A `<Route>` page already built shows itself. Receives
40630
+ * `{ route, params }`, or the entry of `routes` as it was given, and is a column
40631
+ * the height of the row, as the page is. Its route does not match while it stands
40632
+ * there: what it reads is not asked for.
40586
40633
  *
40587
40634
  * Every other prop lands on the box itself (`id`, `data-testid`, `className`, …).
40588
40635
  * The box has to outlive every page of its row: around those <Route>s, or around
@@ -40598,6 +40645,7 @@ const RouteTravel = ({
40598
40645
  replace: true,
40599
40646
  routeTransition: "none"
40600
40647
  }),
40648
+ renderNeighbour,
40601
40649
  className,
40602
40650
  children,
40603
40651
  ...rest
@@ -40658,9 +40706,7 @@ const RouteTravel = ({
40658
40706
  travelling: false,
40659
40707
  pictures: new Map(),
40660
40708
  resumeScrollRecording: null,
40661
- scrollsDocument: getScrollContainer(row.parentElement, {
40662
- includeHidden: true
40663
- }) === document.documentElement,
40709
+ scrollsDocument: rowFollowsDocument(row),
40664
40710
  ...fields
40665
40711
  };
40666
40712
  movementRef.current = movement;
@@ -40996,6 +41042,30 @@ const RouteTravel = ({
40996
41042
  Promise.resolve().then(endMovement);
40997
41043
  }
40998
41044
  }, [currentIndex]);
41045
+
41046
+ // A neighbour shows from the first frame the compositor moves the row, before
41047
+ // any scroll event: a finger landing is the last word before that.
41048
+ const paintsNeighbours = Boolean(renderNeighbour);
41049
+ useLayoutEffect(() => {
41050
+ if (!paintsNeighbours) {
41051
+ return undefined;
41052
+ }
41053
+ const row = rowRef.current;
41054
+ const onTouchStart = () => {
41055
+ if (!movementRef.current && rowFollowsDocument(row)) {
41056
+ row.style.setProperty("--navi-route-row-pin", `${pinOf(row)}px`);
41057
+ }
41058
+ };
41059
+ row.addEventListener("touchstart", onTouchStart, {
41060
+ passive: true
41061
+ });
41062
+ return () => {
41063
+ row.removeEventListener("touchstart", onTouchStart);
41064
+ };
41065
+ }, [paintsNeighbours]);
41066
+ const rowContext = useMemo(() => ({
41067
+ renderNeighbour
41068
+ }), [renderNeighbour]);
40999
41069
  const rowKind = pages.length < 2 && windowed ? "still" : travelByDrag ? "" : "code";
41000
41070
  return jsx("div", {
41001
41071
  ...rest,
@@ -41008,12 +41078,20 @@ const RouteTravel = ({
41008
41078
  ,
41009
41079
  "data-travel-by-drag": travelByDrag && rowKind !== "still" ? "x" : undefined,
41010
41080
  children: windowed ? jsxs(Fragment, {
41011
- children: [pages.map((page, index) => jsx("div", {
41012
- "data-route-row-pane": "",
41013
- style: {
41014
- gridColumn: index + 1
41015
- }
41016
- }, index)), jsx("div", {
41081
+ children: [pages.map((page, index) => {
41082
+ const holdsNeighbour = paintsNeighbours && isNeighbourPane(index, liveIndex, movementRef.current);
41083
+ return jsx("div", {
41084
+ "data-route-row-pane": "",
41085
+ inert: holdsNeighbour,
41086
+ style: {
41087
+ gridColumn: index + 1
41088
+ },
41089
+ children: holdsNeighbour && jsx(RouteRowNeighbour, {
41090
+ renderNeighbour: renderNeighbour,
41091
+ page: page
41092
+ })
41093
+ }, index);
41094
+ }), jsx("div", {
41017
41095
  ref: liveRef,
41018
41096
  "data-route-row-live": "",
41019
41097
  style: {
@@ -41025,13 +41103,25 @@ const RouteTravel = ({
41025
41103
  "data-route-row-pictures": ""
41026
41104
  })]
41027
41105
  }) : jsx(RouteRowContext.Provider, {
41028
- value: true,
41106
+ value: rowContext,
41029
41107
  children: children
41030
41108
  })
41031
41109
  })
41032
41110
  });
41033
41111
  };
41034
41112
 
41113
+ // The panes on each side of the live page. Not one holding a picture of the page
41114
+ // left there: a page with no background of its own would show it through.
41115
+ const isNeighbourPane = (index, liveIndex, movement) => {
41116
+ if (index !== liveIndex - 1 && index !== liveIndex + 1) {
41117
+ return false;
41118
+ }
41119
+ return !movement || !movement.pictures.has(index);
41120
+ };
41121
+ const rowFollowsDocument = row => getScrollContainer(row.parentElement, {
41122
+ includeHidden: true
41123
+ }) === document.documentElement;
41124
+
41035
41125
  // How far down the row the reader is looking, for a page brought in: a page on
41036
41126
  // screen scrolls with the document, and one arriving has to start from there.
41037
41127
  const pinOf = row => {
@@ -56513,6 +56603,10 @@ const SLIDE_TOWARD_ATTRIBUTE = "data-slide-travel-toward";
56513
56603
  // one: a hand on a row the browser scrolls can carry it past several slides, and
56514
56604
  // what follows it goes on between the two it is standing between.
56515
56605
  const SLIDE_BASE_ATTRIBUTE = "data-slide-travel-base";
56606
+ // The slide the row is on its way to while the current one has not changed yet,
56607
+ // because that only happens at rest: nothing is rendered and no address is written
56608
+ // under a snap, and what says where one is should not wait for it.
56609
+ const SLIDE_HEADING_ATTRIBUTE = "data-slide-heading";
56516
56610
  // Every way out that would DO something right now, in the words the commands
56517
56611
  // use: a direction ("left", "right", "up", "down") when there is a slide that
56518
56612
  // way, plus "first" and "last" when one is not already standing at that end of
@@ -56547,9 +56641,16 @@ const wordsOf = (element, attribute) => {
56547
56641
  return value ? value.split(" ") : NO_WAYS;
56548
56642
  };
56549
56643
 
56644
+ // The slide to say one is on: for whatever tells the reader where they are, as
56645
+ // opposed to data-slide-current, which is what a travel departs from.
56646
+ const readSlideOnScreen = (element) =>
56647
+ element.getAttribute(SLIDE_HEADING_ATTRIBUTE) ??
56648
+ element.getAttribute(SLIDE_CURRENT_ATTRIBUTE) ??
56649
+ undefined;
56650
+
56550
56651
  const readSlideContainerState = (element) => ({
56551
56652
  known: true,
56552
- current: element.getAttribute(SLIDE_CURRENT_ATTRIBUTE) ?? undefined,
56653
+ current: readSlideOnScreen(element),
56553
56654
  toward: element.getAttribute(SLIDE_TOWARD_ATTRIBUTE) ?? undefined,
56554
56655
  // In DOM order — which is the order of the WALK for a line, and not
56555
56656
  // necessarily for a map: there the order is the one the areas are written in
@@ -56587,11 +56688,14 @@ const sameSlideContainerState = (a, b) =>
56587
56688
  * can: (wayOut: "left"|"right"|"up"|"down"|"first"|"last") => boolean,
56588
56689
  * held: (wayOut: "left"|"right"|"up"|"down"|"first"|"last") => boolean,
56589
56690
  * }} where the box stands. `current` is the slide on screen — the one being
56590
- * travelled TO while a travel plays, because that is what one is looking at;
56691
+ * travelled TO while a travel plays, and the one the row is going to once a hand
56692
+ * has let go, because that is what one is looking at. It can come before the
56693
+ * slide's own signal and `onCurrentChange`, which wait for the row to rest.
56591
56694
  * `toward` is the other slide in the frame while the picture is between two,
56592
- * and nothing at rest. `can` is "asking for it would do something", `held` is
56593
- * "it is there and this slide says no" — a way out is dead in both cases, and
56594
- * only the second is worth a word to the reader.
56695
+ * and nothing at rest. `can` is "asking for it would do something", from the
56696
+ * slide a travel would depart from, `held` is "it is there and this slide says
56697
+ * no" — a way out is dead in both cases, and only the second is worth a word to
56698
+ * the reader.
56595
56699
  * `known` is false until the box has been read: no box was named and none is
56596
56700
  * above, the id names nothing, or — for one commit — this mounted before the
56597
56701
  * box had painted. Until then `can` answers YES, because it is the answer that
@@ -57445,7 +57549,7 @@ const Nav = ({
57445
57549
  }
57446
57550
  slideContainerElementRef.current = containerElement;
57447
57551
  const readContainer = () => {
57448
- setCurrentSlideArea(containerElement.getAttribute(SLIDE_CURRENT_ATTRIBUTE) ?? undefined);
57552
+ setCurrentSlideArea(readSlideOnScreen(containerElement));
57449
57553
  paintIndicatorGeometryRef.current();
57450
57554
  scheduleTabScrollSyncRef.current();
57451
57555
  };
@@ -68444,6 +68548,20 @@ const SlideContainer = ({
68444
68548
  const paintCurrentArea = area => {
68445
68549
  containerRef.current?.setAttribute(SLIDE_CURRENT_ATTRIBUTE, area);
68446
68550
  };
68551
+
68552
+ // Where the row is going, for as long as the current slide has not changed:
68553
+ // anything saying where one is reads this first (see readSlideOnScreen). Left to
68554
+ // the caller to announce, a landing having more to say in the same breath.
68555
+ const writeHeading = headingElement => {
68556
+ const containerEl = containerRef.current;
68557
+ const area = headingElement ? readArea(headingElement) : null;
68558
+ // A window does not change slide, it rolls: it has nowhere to be heading.
68559
+ if (loop || area === null || area === containerEl.getAttribute(SLIDE_CURRENT_ATTRIBUTE)) {
68560
+ containerEl.removeAttribute(SLIDE_HEADING_ATTRIBUTE);
68561
+ return;
68562
+ }
68563
+ containerEl.setAttribute(SLIDE_HEADING_ATTRIBUTE, area);
68564
+ };
68447
68565
  const markAnswered = area => {
68448
68566
  const order = readMap().slideElements.map(readArea);
68449
68567
  const rank = order.indexOf(area);
@@ -68649,6 +68767,10 @@ const SlideContainer = ({
68649
68767
  slideElements[0];
68650
68768
  const currentArea = readArea(currentElement);
68651
68769
  paintCurrentArea(currentArea);
68770
+ // In the commit the slide changes in, so nothing reads the slide left in between.
68771
+ if (!handMovingRef.current) {
68772
+ writeHeading(null);
68773
+ }
68652
68774
  const realPlaceOf = area => placeOf.get(area) || {
68653
68775
  x: 0,
68654
68776
  y: 0
@@ -68681,6 +68803,8 @@ const SlideContainer = ({
68681
68803
  const drawnArea = stage ? stage.area : drawnAreaRef.current;
68682
68804
  const travelStarts = !still && durationMs > 0 && drawnArea !== undefined && drawnArea !== currentArea && slideElements.some(slideElement => readArea(slideElement) === drawnArea);
68683
68805
  if (travelStarts) {
68806
+ // Asked for over a row still snapping: the travel is what one will see.
68807
+ writeHeading(null);
68684
68808
  // Where the slide being left IS — the place the stage gave it if a travel
68685
68809
  // was already playing (a press landing mid-travel departs from where the
68686
68810
  // eye is, not from the map), its own place otherwise.
@@ -69447,7 +69571,9 @@ const SlideContainer = ({
69447
69571
  // Where a hand has the row, in the terms of --slide-travel-progress: from the
69448
69572
  // current slide while it stands next to it, and past that between the two it
69449
69573
  // stands between, as far as the hand carries it.
69450
- const paintNativeRow = () => {
69574
+ const paintNativeRow = ({
69575
+ finger
69576
+ }) => {
69451
69577
  if (trackAnimationRef.current?.playState === "running") {
69452
69578
  return;
69453
69579
  }
@@ -69459,6 +69585,9 @@ const SlideContainer = ({
69459
69585
  }
69460
69586
  if (restsOn(containerEl, currentElement)) {
69461
69587
  if (handMovingRef.current) {
69588
+ if (!finger) {
69589
+ writeHeading(null);
69590
+ }
69462
69591
  paintTravelProgress();
69463
69592
  }
69464
69593
  return;
@@ -69479,6 +69608,12 @@ const SlideContainer = ({
69479
69608
  }
69480
69609
  element.style.setProperty("--slide-travel-progress", forward ? -fraction : fraction);
69481
69610
  }
69611
+ // Nothing will say when a trackpad's fingers leave, and a long stroke would
69612
+ // keep the first tab lit for the whole trip: the nearest slide answers. A finger
69613
+ // has a lift to wait for (headNativeRow), and until then the slide held stands.
69614
+ if (!finger) {
69615
+ writeHeading(fraction >= 0.5 && toward ? toward : base);
69616
+ }
69482
69617
  paintTravelToward(toward ? readArea(toward) : null);
69483
69618
  // What the hand reaches ahead of the neighbours built at rest is asked for on
69484
69619
  // the way: an empty slide goes by otherwise.
@@ -69486,6 +69621,28 @@ const SlideContainer = ({
69486
69621
  alongside: true
69487
69622
  });
69488
69623
  };
69624
+
69625
+ // A finger has lifted and the row moves: on past the slide it stands after, or
69626
+ // back to it. Already standing on one, it has arrived, whichever way it came.
69627
+ const headNativeRow = movedForward => {
69628
+ if (!handMovingRef.current || trackAnimationRef.current?.playState === "running") {
69629
+ return;
69630
+ }
69631
+ const slideElements = Array.from(trackRef.current.children);
69632
+ const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current"));
69633
+ if (!currentElement) {
69634
+ return;
69635
+ }
69636
+ const {
69637
+ base,
69638
+ toward,
69639
+ fraction,
69640
+ forward
69641
+ } = readRowStretch(containerRef.current, slideElements, currentElement);
69642
+ const goesOn = fraction > 0 && movedForward === forward;
69643
+ writeHeading(goesOn && toward ? toward : base);
69644
+ announceState();
69645
+ };
69489
69646
  const landNativeRow = scrollEndEvent => {
69490
69647
  if (!handMovingRef.current) {
69491
69648
  return;
@@ -69500,13 +69657,15 @@ const SlideContainer = ({
69500
69657
  return;
69501
69658
  }
69502
69659
  handMovingRef.current = false;
69503
- paintTravelProgress();
69504
69660
  const area = readArea(landedElement);
69505
69661
  const currentArea = containerEl.getAttribute(SLIDE_CURRENT_ATTRIBUTE);
69506
69662
  if (area === currentArea) {
69663
+ writeHeading(null);
69664
+ paintTravelProgress();
69507
69665
  settleTravel();
69508
69666
  return;
69509
69667
  }
69668
+ paintTravelProgress();
69510
69669
  const event = scrollEndEvent ?? new Event("scrollend");
69511
69670
  if (loop) {
69512
69671
  // A window does not change slide, it rolls: back at rest in the render the
@@ -69537,6 +69696,8 @@ const SlideContainer = ({
69537
69696
  }
69538
69697
  // Refused: the row is put back in view of the slide that stays.
69539
69698
  handLandedRef.current = null;
69699
+ writeHeading(null);
69700
+ announceState();
69540
69701
  const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current"));
69541
69702
  containerEl.scrollTo({
69542
69703
  left: currentElement.offsetLeft,
@@ -69550,11 +69711,13 @@ const SlideContainer = ({
69550
69711
  const nativeRowHandlersRef = useRef(null);
69551
69712
  nativeRowHandlersRef.current = {
69552
69713
  paintNativeRow,
69714
+ headNativeRow,
69553
69715
  landNativeRow
69554
69716
  };
69555
69717
  useLayoutEffect(() => {
69556
69718
  return watchRowScroll(containerRef.current, {
69557
- onScroll: () => nativeRowHandlersRef.current.paintNativeRow(),
69719
+ onScroll: move => nativeRowHandlersRef.current.paintNativeRow(move),
69720
+ onLeftToSnap: forward => nativeRowHandlersRef.current.headNativeRow(forward),
69558
69721
  onLand: event => nativeRowHandlersRef.current.landNativeRow(event)
69559
69722
  });
69560
69723
  }, []);
@@ -83190,7 +83353,7 @@ const css$w = /* css */`@property --list-group-label-height {
83190
83353
  overflow: clip;
83191
83354
  }
83192
83355
 
83193
- .navi_list_container[data-horizontal]:not([data-scroller], [data-overflow-visible]) > .navi_list_scroll_container > .navi_list:not([navi-box-flow="grid"]) {
83356
+ .navi_list_container[data-horizontal]:not([data-scroller], [data-overflow-visible], [data-flex-wrap]) > .navi_list_scroll_container > .navi_list:not([navi-box-flow="grid"]) {
83194
83357
  width: min-content;
83195
83358
  min-width: 100%;
83196
83359
  }
@@ -83751,7 +83914,7 @@ const ListUI = props => {
83751
83914
  // rule (min-content, never narrower than its box — see the css), which
83752
83915
  // already gives it what expandX asks. An inline width from expandX would beat
83753
83916
  // that rule and lay the end room among the items again.
83754
- const trackSizedByItsRule = horizontal && !scrollerAttribute && !overflowVisibleAttribute && !(columns || itemColumns);
83917
+ const trackSizedByItsRule = horizontal && !scrollerAttribute && !overflowVisibleAttribute && !rest.flexWrap && !(columns || itemColumns);
83755
83918
  return jsx(Box, {
83756
83919
  ...rest,
83757
83920
  ref: ref,
@@ -83762,6 +83925,7 @@ const ListUI = props => {
83762
83925
  "data-borderless": isBorderless(rest) ? "" : undefined,
83763
83926
  "data-scroller": scrollerAttribute,
83764
83927
  "data-overflow-visible": overflowVisibleAttribute,
83928
+ "data-flex-wrap": rest.flexWrap ? "" : undefined,
83765
83929
  "navi-hover-while-scrolling": hoverWhileScrolling ? "" : undefined,
83766
83930
  "data-expand-x": expandX || expand ? "" : undefined,
83767
83931
  "data-expand-y": expandY || expand ? "" : undefined,
@@ -84583,6 +84747,12 @@ const useListScrollSync = ({
84583
84747
  // (pour l'instant pas grave car on travaille pour le mode select qui fermera le dialog au select)
84584
84748
  const topMatchScoresKeyRef = useRef("");
84585
84749
  const restoreScrollRafRef = useRef(null);
84750
+ // The frame reads the list's ref, which an unmount empties.
84751
+ useLayoutEffect(() => {
84752
+ return () => {
84753
+ cancelAnimationFrame(restoreScrollRafRef.current);
84754
+ };
84755
+ }, []);
84586
84756
  useLayoutEffect(() => {
84587
84757
  const listScrollContainerEl = ref.current ? getScroller() : null;
84588
84758
  if (!listScrollContainerEl) {
@@ -84620,10 +84790,9 @@ const useListScrollSync = ({
84620
84790
  // based on virtual item height which can wrongly restore the scroll.
84621
84791
  // However we have a contract with outside to inside which item is scrolled
84622
84792
  // (used by keyboard nav to enable anchoring the item for list item nav with arrow keys)
84623
- // so we do our best to give that item back
84624
- const {
84625
- item
84626
- } = getScrollInfo({
84793
+ // so we do our best to give that item back: there is none to give when
84794
+ // the list has no visible part, and the restore happens all the same.
84795
+ const scrollInfo = getScrollInfo({
84627
84796
  scrollValues: savedScroll,
84628
84797
  scrollerEl: listScrollContainerEl,
84629
84798
  listEl: getListEl(),
@@ -84639,7 +84808,7 @@ const useListScrollSync = ({
84639
84808
  });
84640
84809
  const listEl = getListEl();
84641
84810
  dispatchPublicCustomEvent(listEl, "navi_scroll", {
84642
- item,
84811
+ item: scrollInfo?.item,
84643
84812
  event: new CustomEvent("navi_scroll_restore")
84644
84813
  });
84645
84814
  });