@jsenv/navi 0.29.513 → 0.29.515

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.
@@ -10973,6 +10973,19 @@ const recallScrollerPosition = (name) => {
10973
10973
  return positionByName.get(name);
10974
10974
  };
10975
10975
 
10976
+ // Said by a scroller sent back to its start while it stays mounted: where it
10977
+ // was is a place in what it no longer shows, so nothing is carried and nothing
10978
+ // is come back to under this address until it says where it is again. What it
10979
+ // said under another address stays, for the way back to that address.
10980
+ const forgetScrollerPosition = (name) => {
10981
+ loadStore();
10982
+ saidByScrollerName.delete(name);
10983
+ const positionByName = scrollerPositionsByUrl.get(readShownUrl());
10984
+ if (positionByName) {
10985
+ positionByName.delete(name);
10986
+ }
10987
+ };
10988
+
10976
10989
  // Said by a scroller as it unmounts. Its page is being left when the url is
10977
10990
  // already another one — the history is written before the page it names is
10978
10991
  // taken down — and then its position is kept for the way back. The url still
@@ -38356,9 +38369,11 @@ const releaseTransitionDestination = (owner) => {
38356
38369
  * inside its rectangle — which is where the hand thinks it pressed. The same
38357
38370
  * move route_travel.jsx makes for the box a finger reaches for mid-flight.
38358
38371
  *
38359
- * A `RouteTravel` holds this too. It leaves the bars live, so a control there
38360
- * gets its click on its own; what this serves is a control in the part of a bar
38361
- * photographed for the travel (`data-navi-fixed-bar-overhang`, fixed_bar.jsx).
38372
+ * A `RouteTravel` holds this too, for the bars it leaves live. A control there
38373
+ * gets its click on its own except in the first frames of the travel, when a
38374
+ * press falls to the document root like any other; the part of a bar photographed
38375
+ * for the travel (`data-navi-fixed-bar-overhang`, fixed_bar.jsx) is deaf
38376
+ * throughout.
38362
38377
  *
38363
38378
  * ASKED FOR, never derived, and that is the whole safety of it: only the
38364
38379
  * application knows the control does not move. One that travels with the pages
@@ -39362,6 +39377,21 @@ const placeTransitionWindow = (owner, groupName) => {
39362
39377
  document.documentElement.setAttribute(WINDOW_PLACED_ATTRIBUTE, "");
39363
39378
  };
39364
39379
 
39380
+ /**
39381
+ * Whether a point of the window lies in a band the furniture keeps: the pictures
39382
+ * are cut at those bands, so what is seen there is never a page, however far the
39383
+ * box runs under it.
39384
+ */
39385
+ const isPointInTransitionBand = (clientX, clientY) => {
39386
+ const band = readBand();
39387
+ return (
39388
+ clientX < band.left ||
39389
+ clientX > window.innerWidth - band.right ||
39390
+ clientY < band.top ||
39391
+ clientY > window.innerHeight - band.bottom
39392
+ );
39393
+ };
39394
+
39365
39395
  // The live layout takes the box back. A discontinuity by construction — the
39366
39396
  // window stands at the held height, the box is at its own — and an invisible
39367
39397
  // one: the page arriving is fully in place, and the strip below it that the
@@ -42549,11 +42579,16 @@ const RouteTravel = ({
42549
42579
  gestureRef.current = gesture;
42550
42580
  };
42551
42581
 
42552
- // A press that lands ON the box while a travel is playing does not reach it:
42553
- // the browser's transition covers the page and the press is delivered to the
42554
- // document root instead, whatever the pictures are told about pointer events.
42555
- // So while a travel plays, the press is caught at the document and handed to
42556
- // the box when it fell inside it — which is where the hand thinks it pressed.
42582
+ // A press aimed at a page while a travel is playing does not always reach the
42583
+ // box: the browser's transition covers the page and the press can be delivered
42584
+ // to the document root instead, whatever the pictures are told about pointer
42585
+ // events. So while a travel plays, the press is caught at the document and
42586
+ // handed to the box when it fell where a page is SEEN, which is where the hand
42587
+ // thinks it pressed.
42588
+ //
42589
+ // Decided by the position and never by the target: in the first frames of a
42590
+ // travel a press on a live bar falls to the root like any other, and what lies
42591
+ // under a picture is not what the hand is aiming at.
42557
42592
  pointerDownRef.current = onPointerDown;
42558
42593
  useLayoutEffect(() => {
42559
42594
  const onDocumentPointerDown = pointerDownEvent => {
@@ -42578,6 +42613,11 @@ const RouteTravel = ({
42578
42613
  if (clientX < left || clientX > right || clientY < top || clientY > bottom) {
42579
42614
  return;
42580
42615
  }
42616
+ // A box scrolled under a bar still has the press in its rectangle, but
42617
+ // the pictures are cut at that bar: the press is aimed at the bar.
42618
+ if (isPointInTransitionBand(clientX, clientY)) {
42619
+ return;
42620
+ }
42581
42621
  pointerDownRef.current(pointerDownEvent);
42582
42622
  };
42583
42623
  document.addEventListener("pointerdown", onDocumentPointerDown, true);
@@ -58011,8 +58051,9 @@ const LinkPseudoElements = ["::-navi-loader", "::-navi-indicator"];
58011
58051
  * press on it is aimed at what the reader can see. Say it there, and only
58012
58052
  * there: a control that TRAVELS with the pages has a rectangle where it will
58013
58053
  * be, not where it is seen, and a press handed to it fires something nobody
58014
- * aimed at. A `RouteTravel` honours it as well, for a control in the part of
58015
- * a `FixedBar` it photographs (`data-navi-fixed-bar-overhang`).
58054
+ * aimed at. A `RouteTravel` honours it as well, for a control in a `FixedBar`:
58055
+ * the part it photographs (`data-navi-fixed-bar-overhang`) is deaf for the
58056
+ * whole travel, and the rest for its first frames.
58016
58057
  * @param {boolean} [props.replace] - Go to the destination by TAKING THE PLACE
58017
58058
  * of the current history entry instead of stacking onto it: the link stays a
58018
58059
  * link (an address, a middle click, the keyboard, `aria-current`), only the
@@ -64006,7 +64047,8 @@ const COMMAND_DEFAULT_PROPS_FACTORIES = {
64006
64047
  * drawn — must still close it. What `<Link pressableDuringRouteTransition>`
64007
64048
  * says, for the other half of a toggle. Only for a control that does not
64008
64049
  * travel with the pages. A `RouteTravel` honours it as well, for a control in
64009
- * the part of a `FixedBar` it photographs (`data-navi-fixed-bar-overhang`).
64050
+ * a `FixedBar`: the part it photographs (`data-navi-fixed-bar-overhang`) is
64051
+ * deaf for the whole travel, and the rest for its first frames.
64010
64052
  * @param {Function} [action] What the press runs: a button holds no value, so
64011
64053
  * every press is one call. Inside a `<Form>` or a group it carries the value
64012
64054
  * around it, merged over the action's bound params — a value that is not an
@@ -85831,6 +85873,7 @@ const ListUI = props => {
85831
85873
  scroller = "self",
85832
85874
  hoverWhileScrolling = false,
85833
85875
  scrollResetOnNavigation = false,
85876
+ scrollResetKey,
85834
85877
  lockSize,
85835
85878
  columns,
85836
85879
  itemColumns,
@@ -85949,6 +85992,7 @@ const ListUI = props => {
85949
85992
  scrolled,
85950
85993
  defaultScrolled,
85951
85994
  onScrolledChange,
85995
+ scrollResetKey,
85952
85996
  rememberScroll,
85953
85997
  listId: rest.id,
85954
85998
  scroller,
@@ -86319,6 +86363,7 @@ const useListScrollSync = ({
86319
86363
  scrolled,
86320
86364
  defaultScrolled,
86321
86365
  onScrolledChange,
86366
+ scrollResetKey,
86322
86367
  rememberScroll,
86323
86368
  listId,
86324
86369
  scroller,
@@ -86327,6 +86372,8 @@ const useListScrollSync = ({
86327
86372
  columns
86328
86373
  }) => {
86329
86374
  const debugScroll = useDebugScroll();
86375
+ // The offset a search gives back once emptied (see below).
86376
+ const savedScrollRef = useRef(null);
86330
86377
  // Where the list must be: what the caller holds it at (`scrolled`), or where
86331
86378
  // it opens and then lets go (`defaultScrolled`). A `scrolled` that changes is
86332
86379
  // the caller moving the list, so the hold is armed again — that is what makes
@@ -86335,7 +86382,9 @@ const useListScrollSync = ({
86335
86382
  // one kept from the previous visit): followed for as long as nobody has
86336
86383
  // moved the list, and compared by value, since it is said at every render.
86337
86384
  // Nothing to hold it at (a position not saved yet) is not a position: the
86338
- // list opens where it opens.
86385
+ // list opens where it opens. A `scrollResetKey` that changes sends it back to
86386
+ // its start ("back_to_start", see placeBackAtStart), unless a `scrolled`
86387
+ // changing in the same render says where it goes.
86339
86388
  const startPlaceRef = useRef(null);
86340
86389
  if (startPlaceRef.current === null) {
86341
86390
  startPlaceRef.current = {
@@ -86343,6 +86392,7 @@ const useListScrollSync = ({
86343
86392
  wanted: scrolled ?? defaultScrolled,
86344
86393
  scrolled,
86345
86394
  defaultScrolled,
86395
+ scrollResetKey,
86346
86396
  way: null,
86347
86397
  arrived: false,
86348
86398
  // Whether the hold's writes glide (--navi-scroll:smooth).
@@ -86350,6 +86400,20 @@ const useListScrollSync = ({
86350
86400
  };
86351
86401
  } else {
86352
86402
  const place = startPlaceRef.current;
86403
+ if (!compareTwoJsValues(place.scrollResetKey, scrollResetKey)) {
86404
+ place.scrollResetKey = scrollResetKey;
86405
+ place.wanted = "back_to_start";
86406
+ place.userTookOver = false;
86407
+ place.way = null;
86408
+ place.arrived = false;
86409
+ place.smooth = false;
86410
+ // What the list said it was reading, and what a search would give back,
86411
+ // are places in what it no longer shows.
86412
+ savedScrollRef.current = null;
86413
+ if (rememberScroll) {
86414
+ forgetScrollerPosition(listId);
86415
+ }
86416
+ }
86353
86417
  if (place.scrolled !== scrolled) {
86354
86418
  place.scrolled = scrolled;
86355
86419
  place.defaultScrolled = defaultScrolled;
@@ -86358,7 +86422,7 @@ const useListScrollSync = ({
86358
86422
  place.smooth = false;
86359
86423
  } else if ((scrolled === undefined || scrolled === null) && !compareTwoJsValues(place.defaultScrolled, defaultScrolled)) {
86360
86424
  place.defaultScrolled = defaultScrolled;
86361
- if (!place.userTookOver) {
86425
+ if (!place.userTookOver && place.wanted !== "back_to_start") {
86362
86426
  place.wanted = defaultScrolled;
86363
86427
  // Placed again from where it stands: the way to the previous place
86364
86428
  // says nothing about whose the next scrolls are.
@@ -86702,6 +86766,8 @@ const useListScrollSync = ({
86702
86766
  wantedStart = total - windowSize;
86703
86767
  } else if (typeof scrolledWanted === "number") {
86704
86768
  heldItem = scrolledWanted;
86769
+ } else if (scrolledWanted === "back_to_start") {
86770
+ heldItem = 0;
86705
86771
  } else if (scrolledWanted && scrolledWanted.id !== undefined) {
86706
86772
  const itemIndex = listItems.locateItem(scrolledWanted.id);
86707
86773
  if (itemIndex !== null) {
@@ -86937,7 +87003,6 @@ const useListScrollSync = ({
86937
87003
  // Lorsqu'on selectionne quelque chose pendant une recherche, alors ensuite meme si on clear
86938
87004
  // on veut pas revenir a la position scroll précédente car on veut garde l'item qu'on a selectionné visible
86939
87005
  // (pour l'instant pas grave car on travaille pour le mode select qui fermera le dialog au select)
86940
- const savedScrollRef = useRef(null);
86941
87006
  const topMatchScoresKeyRef = useRef("");
86942
87007
  const restoreScrollRafRef = useRef(null);
86943
87008
  useLayoutEffect(() => {
@@ -87047,6 +87112,10 @@ const useListScrollSync = ({
87047
87112
  if (scrolledWanted === "start" || scrolledWanted === undefined || startPlaceRef.current.userTookOver || !ref.current) {
87048
87113
  return;
87049
87114
  }
87115
+ if (scrolledWanted === "back_to_start") {
87116
+ placeBackAtStart();
87117
+ return;
87118
+ }
87050
87119
  if (listItems.totalSignal.peek() === 0 || virtualItemSizeSignal.peek() === 0) {
87051
87120
  return;
87052
87121
  }
@@ -87145,6 +87214,24 @@ const useListScrollSync = ({
87145
87214
  }
87146
87215
  sendHeldScroll(scrollNow + delta);
87147
87216
  };
87217
+ // The start of the list, what comes before its first item included, not an
87218
+ // item: a box of its own goes to 0. A box the list is further down of also
87219
+ // shows what stands above it, the control that asked for this among others, so
87220
+ // the list is brought back to the box's start and never sent forward to it.
87221
+ const placeBackAtStart = () => {
87222
+ const scrollerEl = getScroller();
87223
+ if (ref.current.contains(scrollerEl)) {
87224
+ sendHeldScroll(0);
87225
+ return;
87226
+ }
87227
+ const scale = readScrollerScale(scrollerEl, horizontal);
87228
+ const viewportRect = getScrollerViewportRect(scrollerEl);
87229
+ const listRect = ref.current.getBoundingClientRect();
87230
+ const offsetNow = (horizontal ? listRect.left - viewportRect.left : listRect.top - viewportRect.top) / scale;
87231
+ const delta = offsetNow - getItemScrollInset(scrollerEl, ref.current, horizontal);
87232
+ const scrollNow = horizontal ? scrollerEl.scrollLeft : scrollerEl.scrollTop;
87233
+ sendHeldScroll(delta < 0 ? scrollNow + delta : scrollNow);
87234
+ };
87148
87235
  // Every placement goes through here, the ones that find the list in place
87149
87236
  // too: it keeps the way from where the scroller was to where it is sent,
87150
87237
  // which is how a scroll event tells the hold's own scrolls from anyone
@@ -92015,6 +92102,7 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
92015
92102
  * scroller?: "self" | "parent" | "document" | Element | {current: Element},
92016
92103
  * hoverWhileScrolling?: boolean,
92017
92104
  * scrollResetOnNavigation?: boolean,
92105
+ * scrollResetKey?: any,
92018
92106
  * fallback?: import("preact").ComponentChildren,
92019
92107
  * searchFallback?: import("preact").ComponentChildren,
92020
92108
  * searchText?: string,
@@ -92259,6 +92347,15 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
92259
92347
  * overwrite each other (see docs/scroll.md). A fresh arrival at the page
92260
92348
  * opens at `defaultScrolled` either way, and so does a list the caller holds
92261
92349
  * through `scrolled`.
92350
+ * @param {any} [props.scrollResetKey]
92351
+ * What the list shows, said as a value: another order, another filter. When
92352
+ * it changes after mount (compared by value: `[order, filter]` can be written
92353
+ * at every render) the list goes back to its start before the paint, in
92354
+ * whichever box `scroller` names, and is held there until the user reaches
92355
+ * for it. A list whose start is in view in a box that also shows what stands
92356
+ * above it does not move. A `scrolled` changing in the same render wins. The
92357
+ * position remembered under the `id` for the address shown is dropped; see
92358
+ * docs/scroll.md.
92262
92359
  * @param {boolean} [props.deselectable]
92263
92360
  * A single-select list allowed to hold nothing: the selected item, pressed
92264
92361
  * again, lets go. Without it the list is a radio group — a choice, once