@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.
@@ -11751,7 +11751,7 @@ const getMessageString = (message) => {
11751
11751
  if (typeof message === "string") {
11752
11752
  return message;
11753
11753
  }
11754
- if (Error.isError(message)) {
11754
+ if (message instanceof Error) {
11755
11755
  return message.message;
11756
11756
  }
11757
11757
  return undefined;
@@ -20281,7 +20281,7 @@ const openCallout = (
20281
20281
  element: calloutElement,
20282
20282
  });
20283
20283
  } else {
20284
- if (Error.isError(newMessage)) {
20284
+ if (newMessage instanceof Error) {
20285
20285
  const error = newMessage;
20286
20286
  newMessage = error.message;
20287
20287
  if (showErrorStack && error.stack) {
@@ -23931,7 +23931,7 @@ const sayRefusal = (element, error) => {
23931
23931
  let anchorElement = element;
23932
23932
  if (
23933
23933
  typeof error === "object" &&
23934
- !Error.isError(error) &&
23934
+ !(error instanceof Error) &&
23935
23935
  !isValidElement(error)
23936
23936
  ) {
23937
23937
  message = error.message;
@@ -26739,7 +26739,7 @@ const useExecuteAction = (
26739
26739
  // and it is then shown exactly as if there were no mapping at all.
26740
26740
  if (typeof errorMappingResult === "string") {
26741
26741
  message = errorMappingResult;
26742
- } else if (Error.isError(errorMappingResult)) {
26742
+ } else if (errorMappingResult instanceof Error) {
26743
26743
  message = errorMappingResult;
26744
26744
  } else if (isValidElement(errorMappingResult)) {
26745
26745
  message = errorMappingResult;
@@ -37756,9 +37756,23 @@ const collectRoutePages = children => {
37756
37756
  * Whether the next container down is a row a hand scrolls: every page has a
37757
37757
  * place on it from the start, and the browser moves between them (see
37758
37758
  * RouteTravel's `nativeScroll`). Told to one container, not to the pages it holds.
37759
+ * False, or `{ renderNeighbour }`.
37759
37760
  */
37760
37761
  const RouteRowContext = createContext(false);
37761
37762
 
37763
+ /**
37764
+ * What stands in the pane of a neighbour until its page does. A component of its
37765
+ * own: what the application calls in there (a hook, a signal) answers to it, however
37766
+ * many neighbours the row has at that moment.
37767
+ */
37768
+ const RouteRowNeighbour = ({
37769
+ renderNeighbour,
37770
+ page
37771
+ }) => jsx("div", {
37772
+ "data-route-row-neighbour": "",
37773
+ children: renderNeighbour(page)
37774
+ });
37775
+
37762
37776
  // A container as a row: one pane per leaf, in the order they are written. A page
37763
37777
  // still builds when its route first matches, since a route's actions only ask
37764
37778
  // while it does: before that its pane is empty. Once built it stays, with what
@@ -37771,6 +37785,9 @@ const RouteRowContext = createContext(false);
37771
37785
  const RouteRowContainer = ({
37772
37786
  children
37773
37787
  }) => {
37788
+ const {
37789
+ renderNeighbour
37790
+ } = useContext(RouteRowContext);
37774
37791
  const {
37775
37792
  activeBranch,
37776
37793
  leaves
@@ -37819,16 +37836,19 @@ const RouteRowContainer = ({
37819
37836
  shown: true
37820
37837
  }, "fallback");
37821
37838
  }
37822
- return leaves.map(leaf => jsx(RouteRowPane, {
37839
+ const shownIndex = activeBranch ? leaves.findIndex(leaf => leaf.id === activeBranch.id) : -1;
37840
+ return leaves.map((leaf, index) => jsx(RouteRowPane, {
37823
37841
  branch: leaf,
37824
37842
  page: pageOf(leaf),
37825
- shown: activeBranch !== null && leaf.id === activeBranch.id
37843
+ shown: index === shownIndex,
37844
+ renderNeighbour: shownIndex !== -1 && (index === shownIndex - 1 || index === shownIndex + 1) ? renderNeighbour : undefined
37826
37845
  }, leaf.id));
37827
37846
  };
37828
37847
  const RouteRowPane = ({
37829
37848
  branch,
37830
37849
  page,
37831
- shown
37850
+ shown,
37851
+ renderNeighbour
37832
37852
  }) => {
37833
37853
  // The very vnode rendered last, while not on screen: Preact does not look into
37834
37854
  // it, and a page nobody can see costs nothing when the address moves.
@@ -37847,7 +37867,10 @@ const RouteRowPane = ({
37847
37867
  "data-route-row-pane": "",
37848
37868
  "data-built": contentRef.current ? "" : undefined,
37849
37869
  inert: !shown,
37850
- children: contentRef.current
37870
+ children: contentRef.current || renderNeighbour && jsx(RouteRowNeighbour, {
37871
+ renderNeighbour: renderNeighbour,
37872
+ page: branchRoutePage(branch)
37873
+ })
37851
37874
  });
37852
37875
  };
37853
37876
 
@@ -41703,7 +41726,9 @@ const readRowStretch = (scroller, pages, page) => {
41703
41726
  /**
41704
41727
  * @param {Element} scroller
41705
41728
  * @param {object} options
41706
- * @param {() => void} options.onScroll - the row moved.
41729
+ * @param {(move: {finger: boolean}) => void} options.onScroll - the row moved.
41730
+ * `finger`: a touch has come since the row last stopped, so a lift will say where
41731
+ * it goes (`onLeftToSnap`). Without one (a trackpad, a wheel) nothing will.
41707
41732
  * @param {(event: Event|null) => void} options.onLand - the row may have stopped.
41708
41733
  * Read `pageAtRest` to know whether it stopped on a page. Heard more than once
41709
41734
  * for one landing, so what it does must find nothing to do the second time.
@@ -41719,11 +41744,13 @@ const watchRowScroll = (
41719
41744
  ) => {
41720
41745
  let stoppedTimeout = null;
41721
41746
  let touching = false;
41747
+ let touchedSinceStop = false;
41722
41748
  let liftedAt = null;
41723
41749
  const landOnSilence = () => {
41724
41750
  liftedAt = null;
41725
41751
  // A finger holding the row still: nothing has stopped.
41726
41752
  if (!touching) {
41753
+ touchedSinceStop = false;
41727
41754
  onLand(null);
41728
41755
  }
41729
41756
  };
@@ -41732,7 +41759,7 @@ const watchRowScroll = (
41732
41759
  stoppedTimeout = setTimeout(landOnSilence, STOPPED_SILENCE);
41733
41760
  };
41734
41761
  const onScrollEvent = () => {
41735
- onScroll();
41762
+ onScroll({ finger: touchedSinceStop });
41736
41763
  armSilence();
41737
41764
  if (!liftedAt) {
41738
41765
  return;
@@ -41748,6 +41775,7 @@ const watchRowScroll = (
41748
41775
  };
41749
41776
  const onTouchStart = () => {
41750
41777
  touching = true;
41778
+ touchedSinceStop = true;
41751
41779
  liftedAt = null;
41752
41780
  clearTimeout(stoppedTimeout);
41753
41781
  };
@@ -41775,6 +41803,8 @@ const watchRowScroll = (
41775
41803
 
41776
41804
  installImportMetaCssBuild(import.meta);
41777
41805
  const css$16 = /* css */`.navi_route_travel {
41806
+ flex-direction: column;
41807
+ display: flex;
41778
41808
  position: relative;
41779
41809
  }
41780
41810
 
@@ -41826,6 +41856,15 @@ const css$16 = /* css */`.navi_route_travel {
41826
41856
  overflow: clip;
41827
41857
  }
41828
41858
 
41859
+ & > [data-route-row-pane] > [data-route-row-neighbour] {
41860
+ contain: layout paint;
41861
+ visibility: visible;
41862
+ flex-direction: column;
41863
+ display: flex;
41864
+ position: absolute;
41865
+ inset: 0;
41866
+ }
41867
+
41829
41868
  & > [data-route-row-live] {
41830
41869
  flex-direction: column;
41831
41870
  grid-row: 1;
@@ -41858,6 +41897,7 @@ const SET_OFF_PULL = 16;
41858
41897
  * routes?: Array<object|{route: object, params?: object}>,
41859
41898
  * travelByDrag?: boolean,
41860
41899
  * onTravel?: (detail: {route: object, params: object|undefined, cause: string}) => void|Promise<void>,
41900
+ * renderNeighbour?: (page: {route: object, params: object|undefined}) => import("preact").ComponentChildren,
41861
41901
  * }>}
41862
41902
  * @param {Array<object|{route: object, params?: object}>} [props.routes] - a window
41863
41903
  * over something longer, in order. Read from the <Route> children by default,
@@ -41874,6 +41914,13 @@ const SET_OFF_PULL = 16;
41874
41914
  * of the page under six entries. A tab pressed goes the way its <Link> says —
41875
41915
  * `<Link replace>` for a row of tabs, so a press and a swipe towards the same
41876
41916
  * neighbour write the same history.
41917
+ * @param {(page: {route: object, params: object|undefined}) => import("preact").ComponentChildren} [props.renderNeighbour]
41918
+ * - what stands in the pane of a page next to the one on screen, until the hand
41919
+ * has pulled far enough to bring that page: its background, its skeleton, what the
41920
+ * list already knows. A `<Route>` page already built shows itself. Receives
41921
+ * `{ route, params }`, or the entry of `routes` as it was given, and is a column
41922
+ * the height of the row, as the page is. Its route does not match while it stands
41923
+ * there: what it reads is not asked for.
41877
41924
  *
41878
41925
  * Every other prop lands on the box itself (`id`, `data-testid`, `className`, …).
41879
41926
  * The box has to outlive every page of its row: around those <Route>s, or around
@@ -41889,6 +41936,7 @@ const RouteTravel = ({
41889
41936
  replace: true,
41890
41937
  routeTransition: "none"
41891
41938
  }),
41939
+ renderNeighbour,
41892
41940
  className,
41893
41941
  children,
41894
41942
  ...rest
@@ -41949,9 +41997,7 @@ const RouteTravel = ({
41949
41997
  travelling: false,
41950
41998
  pictures: new Map(),
41951
41999
  resumeScrollRecording: null,
41952
- scrollsDocument: getScrollContainer(row.parentElement, {
41953
- includeHidden: true
41954
- }) === document.documentElement,
42000
+ scrollsDocument: rowFollowsDocument(row),
41955
42001
  ...fields
41956
42002
  };
41957
42003
  movementRef.current = movement;
@@ -42287,6 +42333,30 @@ const RouteTravel = ({
42287
42333
  Promise.resolve().then(endMovement);
42288
42334
  }
42289
42335
  }, [currentIndex]);
42336
+
42337
+ // A neighbour shows from the first frame the compositor moves the row, before
42338
+ // any scroll event: a finger landing is the last word before that.
42339
+ const paintsNeighbours = Boolean(renderNeighbour);
42340
+ useLayoutEffect(() => {
42341
+ if (!paintsNeighbours) {
42342
+ return undefined;
42343
+ }
42344
+ const row = rowRef.current;
42345
+ const onTouchStart = () => {
42346
+ if (!movementRef.current && rowFollowsDocument(row)) {
42347
+ row.style.setProperty("--navi-route-row-pin", `${pinOf(row)}px`);
42348
+ }
42349
+ };
42350
+ row.addEventListener("touchstart", onTouchStart, {
42351
+ passive: true
42352
+ });
42353
+ return () => {
42354
+ row.removeEventListener("touchstart", onTouchStart);
42355
+ };
42356
+ }, [paintsNeighbours]);
42357
+ const rowContext = useMemo(() => ({
42358
+ renderNeighbour
42359
+ }), [renderNeighbour]);
42290
42360
  const rowKind = pages.length < 2 && windowed ? "still" : travelByDrag ? "" : "code";
42291
42361
  return jsx("div", {
42292
42362
  ...rest,
@@ -42299,12 +42369,20 @@ const RouteTravel = ({
42299
42369
  ,
42300
42370
  "data-travel-by-drag": travelByDrag && rowKind !== "still" ? "x" : undefined,
42301
42371
  children: windowed ? jsxs(Fragment, {
42302
- children: [pages.map((page, index) => jsx("div", {
42303
- "data-route-row-pane": "",
42304
- style: {
42305
- gridColumn: index + 1
42306
- }
42307
- }, index)), jsx("div", {
42372
+ children: [pages.map((page, index) => {
42373
+ const holdsNeighbour = paintsNeighbours && isNeighbourPane(index, liveIndex, movementRef.current);
42374
+ return jsx("div", {
42375
+ "data-route-row-pane": "",
42376
+ inert: holdsNeighbour,
42377
+ style: {
42378
+ gridColumn: index + 1
42379
+ },
42380
+ children: holdsNeighbour && jsx(RouteRowNeighbour, {
42381
+ renderNeighbour: renderNeighbour,
42382
+ page: page
42383
+ })
42384
+ }, index);
42385
+ }), jsx("div", {
42308
42386
  ref: liveRef,
42309
42387
  "data-route-row-live": "",
42310
42388
  style: {
@@ -42316,13 +42394,25 @@ const RouteTravel = ({
42316
42394
  "data-route-row-pictures": ""
42317
42395
  })]
42318
42396
  }) : jsx(RouteRowContext.Provider, {
42319
- value: true,
42397
+ value: rowContext,
42320
42398
  children: children
42321
42399
  })
42322
42400
  })
42323
42401
  });
42324
42402
  };
42325
42403
 
42404
+ // The panes on each side of the live page. Not one holding a picture of the page
42405
+ // left there: a page with no background of its own would show it through.
42406
+ const isNeighbourPane = (index, liveIndex, movement) => {
42407
+ if (index !== liveIndex - 1 && index !== liveIndex + 1) {
42408
+ return false;
42409
+ }
42410
+ return !movement || !movement.pictures.has(index);
42411
+ };
42412
+ const rowFollowsDocument = row => getScrollContainer(row.parentElement, {
42413
+ includeHidden: true
42414
+ }) === document.documentElement;
42415
+
42326
42416
  // How far down the row the reader is looking, for a page brought in: a page on
42327
42417
  // screen scrolls with the document, and one arriving has to start from there.
42328
42418
  const pinOf = row => {
@@ -58059,6 +58149,10 @@ const SLIDE_TOWARD_ATTRIBUTE = "data-slide-travel-toward";
58059
58149
  // one: a hand on a row the browser scrolls can carry it past several slides, and
58060
58150
  // what follows it goes on between the two it is standing between.
58061
58151
  const SLIDE_BASE_ATTRIBUTE = "data-slide-travel-base";
58152
+ // The slide the row is on its way to while the current one has not changed yet,
58153
+ // because that only happens at rest: nothing is rendered and no address is written
58154
+ // under a snap, and what says where one is should not wait for it.
58155
+ const SLIDE_HEADING_ATTRIBUTE = "data-slide-heading";
58062
58156
  // Every way out that would DO something right now, in the words the commands
58063
58157
  // use: a direction ("left", "right", "up", "down") when there is a slide that
58064
58158
  // way, plus "first" and "last" when one is not already standing at that end of
@@ -58093,9 +58187,16 @@ const wordsOf = (element, attribute) => {
58093
58187
  return value ? value.split(" ") : NO_WAYS;
58094
58188
  };
58095
58189
 
58190
+ // The slide to say one is on: for whatever tells the reader where they are, as
58191
+ // opposed to data-slide-current, which is what a travel departs from.
58192
+ const readSlideOnScreen = (element) =>
58193
+ element.getAttribute(SLIDE_HEADING_ATTRIBUTE) ??
58194
+ element.getAttribute(SLIDE_CURRENT_ATTRIBUTE) ??
58195
+ undefined;
58196
+
58096
58197
  const readSlideContainerState = (element) => ({
58097
58198
  known: true,
58098
- current: element.getAttribute(SLIDE_CURRENT_ATTRIBUTE) ?? undefined,
58199
+ current: readSlideOnScreen(element),
58099
58200
  toward: element.getAttribute(SLIDE_TOWARD_ATTRIBUTE) ?? undefined,
58100
58201
  // In DOM order — which is the order of the WALK for a line, and not
58101
58202
  // necessarily for a map: there the order is the one the areas are written in
@@ -58133,11 +58234,14 @@ const sameSlideContainerState = (a, b) =>
58133
58234
  * can: (wayOut: "left"|"right"|"up"|"down"|"first"|"last") => boolean,
58134
58235
  * held: (wayOut: "left"|"right"|"up"|"down"|"first"|"last") => boolean,
58135
58236
  * }} where the box stands. `current` is the slide on screen — the one being
58136
- * travelled TO while a travel plays, because that is what one is looking at;
58237
+ * travelled TO while a travel plays, and the one the row is going to once a hand
58238
+ * has let go, because that is what one is looking at. It can come before the
58239
+ * slide's own signal and `onCurrentChange`, which wait for the row to rest.
58137
58240
  * `toward` is the other slide in the frame while the picture is between two,
58138
- * and nothing at rest. `can` is "asking for it would do something", `held` is
58139
- * "it is there and this slide says no" — a way out is dead in both cases, and
58140
- * only the second is worth a word to the reader.
58241
+ * and nothing at rest. `can` is "asking for it would do something", from the
58242
+ * slide a travel would depart from, `held` is "it is there and this slide says
58243
+ * no" — a way out is dead in both cases, and only the second is worth a word to
58244
+ * the reader.
58141
58245
  * `known` is false until the box has been read: no box was named and none is
58142
58246
  * above, the id names nothing, or — for one commit — this mounted before the
58143
58247
  * box had painted. Until then `can` answers YES, because it is the answer that
@@ -58991,7 +59095,7 @@ const Nav = ({
58991
59095
  }
58992
59096
  slideContainerElementRef.current = containerElement;
58993
59097
  const readContainer = () => {
58994
- setCurrentSlideArea(containerElement.getAttribute(SLIDE_CURRENT_ATTRIBUTE) ?? undefined);
59098
+ setCurrentSlideArea(readSlideOnScreen(containerElement));
58995
59099
  paintIndicatorGeometryRef.current();
58996
59100
  scheduleTabScrollSyncRef.current();
58997
59101
  };
@@ -70051,6 +70155,20 @@ const SlideContainer = ({
70051
70155
  const paintCurrentArea = area => {
70052
70156
  containerRef.current?.setAttribute(SLIDE_CURRENT_ATTRIBUTE, area);
70053
70157
  };
70158
+
70159
+ // Where the row is going, for as long as the current slide has not changed:
70160
+ // anything saying where one is reads this first (see readSlideOnScreen). Left to
70161
+ // the caller to announce, a landing having more to say in the same breath.
70162
+ const writeHeading = headingElement => {
70163
+ const containerEl = containerRef.current;
70164
+ const area = headingElement ? readArea(headingElement) : null;
70165
+ // A window does not change slide, it rolls: it has nowhere to be heading.
70166
+ if (loop || area === null || area === containerEl.getAttribute(SLIDE_CURRENT_ATTRIBUTE)) {
70167
+ containerEl.removeAttribute(SLIDE_HEADING_ATTRIBUTE);
70168
+ return;
70169
+ }
70170
+ containerEl.setAttribute(SLIDE_HEADING_ATTRIBUTE, area);
70171
+ };
70054
70172
  const markAnswered = area => {
70055
70173
  const order = readMap().slideElements.map(readArea);
70056
70174
  const rank = order.indexOf(area);
@@ -70256,6 +70374,10 @@ const SlideContainer = ({
70256
70374
  slideElements[0];
70257
70375
  const currentArea = readArea(currentElement);
70258
70376
  paintCurrentArea(currentArea);
70377
+ // In the commit the slide changes in, so nothing reads the slide left in between.
70378
+ if (!handMovingRef.current) {
70379
+ writeHeading(null);
70380
+ }
70259
70381
  const realPlaceOf = area => placeOf.get(area) || {
70260
70382
  x: 0,
70261
70383
  y: 0
@@ -70288,6 +70410,8 @@ const SlideContainer = ({
70288
70410
  const drawnArea = stage ? stage.area : drawnAreaRef.current;
70289
70411
  const travelStarts = !still && durationMs > 0 && drawnArea !== undefined && drawnArea !== currentArea && slideElements.some(slideElement => readArea(slideElement) === drawnArea);
70290
70412
  if (travelStarts) {
70413
+ // Asked for over a row still snapping: the travel is what one will see.
70414
+ writeHeading(null);
70291
70415
  // Where the slide being left IS — the place the stage gave it if a travel
70292
70416
  // was already playing (a press landing mid-travel departs from where the
70293
70417
  // eye is, not from the map), its own place otherwise.
@@ -71054,7 +71178,9 @@ const SlideContainer = ({
71054
71178
  // Where a hand has the row, in the terms of --slide-travel-progress: from the
71055
71179
  // current slide while it stands next to it, and past that between the two it
71056
71180
  // stands between, as far as the hand carries it.
71057
- const paintNativeRow = () => {
71181
+ const paintNativeRow = ({
71182
+ finger
71183
+ }) => {
71058
71184
  if (trackAnimationRef.current?.playState === "running") {
71059
71185
  return;
71060
71186
  }
@@ -71066,6 +71192,9 @@ const SlideContainer = ({
71066
71192
  }
71067
71193
  if (restsOn(containerEl, currentElement)) {
71068
71194
  if (handMovingRef.current) {
71195
+ if (!finger) {
71196
+ writeHeading(null);
71197
+ }
71069
71198
  paintTravelProgress();
71070
71199
  }
71071
71200
  return;
@@ -71086,6 +71215,12 @@ const SlideContainer = ({
71086
71215
  }
71087
71216
  element.style.setProperty("--slide-travel-progress", forward ? -fraction : fraction);
71088
71217
  }
71218
+ // Nothing will say when a trackpad's fingers leave, and a long stroke would
71219
+ // keep the first tab lit for the whole trip: the nearest slide answers. A finger
71220
+ // has a lift to wait for (headNativeRow), and until then the slide held stands.
71221
+ if (!finger) {
71222
+ writeHeading(fraction >= 0.5 && toward ? toward : base);
71223
+ }
71089
71224
  paintTravelToward(toward ? readArea(toward) : null);
71090
71225
  // What the hand reaches ahead of the neighbours built at rest is asked for on
71091
71226
  // the way: an empty slide goes by otherwise.
@@ -71093,6 +71228,28 @@ const SlideContainer = ({
71093
71228
  alongside: true
71094
71229
  });
71095
71230
  };
71231
+
71232
+ // A finger has lifted and the row moves: on past the slide it stands after, or
71233
+ // back to it. Already standing on one, it has arrived, whichever way it came.
71234
+ const headNativeRow = movedForward => {
71235
+ if (!handMovingRef.current || trackAnimationRef.current?.playState === "running") {
71236
+ return;
71237
+ }
71238
+ const slideElements = Array.from(trackRef.current.children);
71239
+ const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current"));
71240
+ if (!currentElement) {
71241
+ return;
71242
+ }
71243
+ const {
71244
+ base,
71245
+ toward,
71246
+ fraction,
71247
+ forward
71248
+ } = readRowStretch(containerRef.current, slideElements, currentElement);
71249
+ const goesOn = fraction > 0 && movedForward === forward;
71250
+ writeHeading(goesOn && toward ? toward : base);
71251
+ announceState();
71252
+ };
71096
71253
  const landNativeRow = scrollEndEvent => {
71097
71254
  if (!handMovingRef.current) {
71098
71255
  return;
@@ -71107,13 +71264,15 @@ const SlideContainer = ({
71107
71264
  return;
71108
71265
  }
71109
71266
  handMovingRef.current = false;
71110
- paintTravelProgress();
71111
71267
  const area = readArea(landedElement);
71112
71268
  const currentArea = containerEl.getAttribute(SLIDE_CURRENT_ATTRIBUTE);
71113
71269
  if (area === currentArea) {
71270
+ writeHeading(null);
71271
+ paintTravelProgress();
71114
71272
  settleTravel();
71115
71273
  return;
71116
71274
  }
71275
+ paintTravelProgress();
71117
71276
  const event = scrollEndEvent ?? new Event("scrollend");
71118
71277
  if (loop) {
71119
71278
  // A window does not change slide, it rolls: back at rest in the render the
@@ -71144,6 +71303,8 @@ const SlideContainer = ({
71144
71303
  }
71145
71304
  // Refused: the row is put back in view of the slide that stays.
71146
71305
  handLandedRef.current = null;
71306
+ writeHeading(null);
71307
+ announceState();
71147
71308
  const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current"));
71148
71309
  containerEl.scrollTo({
71149
71310
  left: currentElement.offsetLeft,
@@ -71157,11 +71318,13 @@ const SlideContainer = ({
71157
71318
  const nativeRowHandlersRef = useRef(null);
71158
71319
  nativeRowHandlersRef.current = {
71159
71320
  paintNativeRow,
71321
+ headNativeRow,
71160
71322
  landNativeRow
71161
71323
  };
71162
71324
  useLayoutEffect(() => {
71163
71325
  return watchRowScroll(containerRef.current, {
71164
- onScroll: () => nativeRowHandlersRef.current.paintNativeRow(),
71326
+ onScroll: move => nativeRowHandlersRef.current.paintNativeRow(move),
71327
+ onLeftToSnap: forward => nativeRowHandlersRef.current.headNativeRow(forward),
71165
71328
  onLand: event => nativeRowHandlersRef.current.landNativeRow(event)
71166
71329
  });
71167
71330
  }, []);
@@ -84981,7 +85144,7 @@ const css$w = /* css */`@property --list-group-label-height {
84981
85144
  overflow: clip;
84982
85145
  }
84983
85146
 
84984
- .navi_list_container[data-horizontal]:not([data-scroller], [data-overflow-visible]) > .navi_list_scroll_container > .navi_list:not([navi-box-flow="grid"]) {
85147
+ .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"]) {
84985
85148
  width: min-content;
84986
85149
  min-width: 100%;
84987
85150
  }
@@ -85542,7 +85705,7 @@ const ListUI = props => {
85542
85705
  // rule (min-content, never narrower than its box — see the css), which
85543
85706
  // already gives it what expandX asks. An inline width from expandX would beat
85544
85707
  // that rule and lay the end room among the items again.
85545
- const trackSizedByItsRule = horizontal && !scrollerAttribute && !overflowVisibleAttribute && !(columns || itemColumns);
85708
+ const trackSizedByItsRule = horizontal && !scrollerAttribute && !overflowVisibleAttribute && !rest.flexWrap && !(columns || itemColumns);
85546
85709
  return jsx(Box, {
85547
85710
  ...rest,
85548
85711
  ref: ref,
@@ -85553,6 +85716,7 @@ const ListUI = props => {
85553
85716
  "data-borderless": isBorderless(rest) ? "" : undefined,
85554
85717
  "data-scroller": scrollerAttribute,
85555
85718
  "data-overflow-visible": overflowVisibleAttribute,
85719
+ "data-flex-wrap": rest.flexWrap ? "" : undefined,
85556
85720
  "navi-hover-while-scrolling": hoverWhileScrolling ? "" : undefined,
85557
85721
  "data-expand-x": expandX || expand ? "" : undefined,
85558
85722
  "data-expand-y": expandY || expand ? "" : undefined,
@@ -86425,6 +86589,12 @@ const useListScrollSync = ({
86425
86589
  // (pour l'instant pas grave car on travaille pour le mode select qui fermera le dialog au select)
86426
86590
  const topMatchScoresKeyRef = useRef("");
86427
86591
  const restoreScrollRafRef = useRef(null);
86592
+ // The frame reads the list's ref, which an unmount empties.
86593
+ useLayoutEffect(() => {
86594
+ return () => {
86595
+ cancelAnimationFrame(restoreScrollRafRef.current);
86596
+ };
86597
+ }, []);
86428
86598
  useLayoutEffect(() => {
86429
86599
  const listScrollContainerEl = ref.current ? getScroller() : null;
86430
86600
  if (!listScrollContainerEl) {
@@ -86463,10 +86633,9 @@ const useListScrollSync = ({
86463
86633
  // based on virtual item height which can wrongly restore the scroll.
86464
86634
  // However we have a contract with outside to inside which item is scrolled
86465
86635
  // (used by keyboard nav to enable anchoring the item for list item nav with arrow keys)
86466
- // so we do our best to give that item back
86467
- const {
86468
- item
86469
- } = getScrollInfo({
86636
+ // so we do our best to give that item back: there is none to give when
86637
+ // the list has no visible part, and the restore happens all the same.
86638
+ const scrollInfo = getScrollInfo({
86470
86639
  scrollValues: savedScroll,
86471
86640
  scrollerEl: listScrollContainerEl,
86472
86641
  listEl: getListEl(),
@@ -86482,7 +86651,7 @@ const useListScrollSync = ({
86482
86651
  });
86483
86652
  const listEl = getListEl();
86484
86653
  dispatchPublicCustomEvent(listEl, "navi_scroll", {
86485
- item,
86654
+ item: scrollInfo?.item,
86486
86655
  event: new CustomEvent("navi_scroll_restore")
86487
86656
  });
86488
86657
  });