@jsenv/navi 0.30.33 → 0.30.35

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.
@@ -41368,7 +41368,7 @@ const css$16 = /* css */`.navi_route_travel {
41368
41368
  display: flex;
41369
41369
  }
41370
41370
 
41371
- & > [data-route-row-pane][inert] {
41371
+ & > [data-route-row-pane][inert]:not([data-route-row-held]), &:has( > [data-route-row-held]) > [data-route-row-pane]:not([data-route-row-held]) {
41372
41372
  top: var(--navi-route-row-pin, 0);
41373
41373
  contain: paint;
41374
41374
  max-height: 100dvh;
@@ -41418,6 +41418,12 @@ const ROW_READING = {
41418
41418
  // How far a scroll position written can read back from what was written: rounded to
41419
41419
  // the device's pixels.
41420
41420
  const WRITE_ROUNDING = 1;
41421
+ // The most one frame of a travel plays: a frame of a 30fps screen, which is what a
41422
+ // browser saving power gives (an iPhone in Low Power Mode, Chrome's Energy Saver).
41423
+ // A frame later than that is a main thread busy elsewhere (the page arriving builds,
41424
+ // a list fills its budget), and the travel takes longer rather than jump by it. Not a
41425
+ // frame of a 60Hz screen: that would play every travel in slow motion at 30fps.
41426
+ const TRAVEL_FRAME_MAX_MS = 1000 / 30;
41421
41427
 
41422
41428
  /**
41423
41429
  * @type {import("preact").FunctionComponent<{
@@ -41530,9 +41536,23 @@ const RouteTravel = ({
41530
41536
  params
41531
41537
  }) => new URL(route.buildUrl(params), window.location.href).pathname;
41532
41538
 
41539
+ // The pane laid out as the page on screen for the length of a movement, let go of
41540
+ // when the row rests. Not what readers are told the page held is
41541
+ // (route_travel_hold.js): that follows the hand, and goes at its lift.
41542
+ const heldPaneRef = useRef(null);
41543
+ const releasePaneAtRest = () => {
41544
+ const heldPane = heldPaneRef.current;
41545
+ if (heldPane) {
41546
+ heldPaneRef.current = null;
41547
+ heldPane.removeAttribute("data-route-row-held");
41548
+ }
41549
+ };
41550
+
41533
41551
  // One movement, whoever set it off, from the first thing that moves to the row
41534
- // at rest. Null when nothing moves.
41535
- const beginMovement = fields => {
41552
+ // at rest. Null when nothing moves. `paneAtRest` is the pane the row rests on as it
41553
+ // begins: laid out as the page on screen until the row rests again, wherever the
41554
+ // address goes meanwhile (see the CSS).
41555
+ const beginMovement = (fields, paneAtRest) => {
41536
41556
  const row = rowRef.current;
41537
41557
  if (movementRef.current) {
41538
41558
  Object.assign(movementRef.current, fields);
@@ -41547,6 +41567,11 @@ const RouteTravel = ({
41547
41567
  ...fields
41548
41568
  };
41549
41569
  movementRef.current = movement;
41570
+ // In a window the page is a picture, contained with what it holds.
41571
+ if (!windowed && paneAtRest) {
41572
+ heldPaneRef.current = paneAtRest;
41573
+ paneAtRest.setAttribute("data-route-row-held", "");
41574
+ }
41550
41575
  if (movement.scrollsDocument) {
41551
41576
  // Whatever the browser does with the offset meanwhile (clamping it to a page
41552
41577
  // shorter than the trip) is not the reader scrolling, and the address it
@@ -41693,18 +41718,22 @@ const RouteTravel = ({
41693
41718
  // hesitates, then peaks at 1.65 times this one's speed): two rows of tabs in one
41694
41719
  // app would not move alike. The scroll position is written at every frame, so the
41695
41720
  // row stays a scroller all the way: what follows it (a Nav's bar) reads it as it
41696
- // reads a hand, and a finger landing finds it where it is. The clock starts at the
41697
- // first frame, after what the press itself costs, as an animation's does.
41721
+ // reads a hand, and a finger landing finds it where it is. That is played on the
41722
+ // main thread: a transform on the panes would play on the compositor, but it would
41723
+ // capture what the page on screen holds in position: fixed. Hence a clock of its
41724
+ // own, which a busy main thread slows down rather than makes jump.
41698
41725
  const playTravel = (pane, movement) => {
41699
41726
  const row = rowRef.current;
41700
41727
  // Already moving, so there is no ease-in to play: it would stall the row for an
41701
41728
  // instant right where the eye is following it.
41702
41729
  const replaced = movement.playing;
41703
41730
  const inFlight = Boolean(replaced) || Boolean(movement.rowTaken);
41731
+ // Before the row is read: let go of for an instant, it would be snapped to the
41732
+ // nearest page by that read, and the travel would start from there.
41733
+ row.setAttribute("data-route-row-travelling", "");
41704
41734
  if (replaced) {
41705
- // Its frames only: the row stays travelling across the hand-over. Let go of,
41706
- // it snaps to the nearest page at the read below, and the travel starts there.
41707
- replaced.cancelFrame();
41735
+ replaced.cancel();
41736
+ movement.playing = null;
41708
41737
  }
41709
41738
  const from = row.scrollLeft;
41710
41739
  const {
@@ -41716,56 +41745,70 @@ const RouteTravel = ({
41716
41745
  const travelMs = boxes < 1 ? durationMs * boxes : durationMs;
41717
41746
  if (!travelMs) {
41718
41747
  row.scrollLeft = pane.offsetLeft;
41748
+ letRowGo(movement);
41719
41749
  // A write that changes nothing is followed by no scroll event to say the row
41720
41750
  // stopped. After the commit that asked, which goes on to say the row moves.
41721
41751
  Promise.resolve().then(landRow);
41722
41752
  return;
41723
41753
  }
41724
41754
  const curve = inFlight ? CSS_EASE_OUT : CSS_EASE;
41725
- let start = null;
41726
41755
  let frame = null;
41756
+ // How much of the travel has played, frame by frame. A travel setting off plays
41757
+ // nothing at its first frame, which may have begun while the press was still being
41758
+ // handled: that is no travel. One taking over goes on from the last frame of the
41759
+ // one it replaces, or the row would stand still for a frame where the eye follows it.
41760
+ let played = 0;
41727
41761
  const playing = {
41728
41762
  // Where this travel put the row, for a scroll event to tell it from a hand.
41729
41763
  written: from,
41730
- cancelFrame: () => {
41764
+ lastFrame: replaced ? replaced.lastFrame : null,
41765
+ cancel: () => {
41731
41766
  cancelAnimationFrame(frame);
41732
- },
41733
- stop: () => {
41734
- playing.cancelFrame();
41735
- if (movement.playing === playing) {
41736
- movement.playing = null;
41737
- row.removeAttribute("data-route-row-travelling");
41738
- }
41739
41767
  }
41740
41768
  };
41741
41769
  const step = now => {
41742
- if (start === null) {
41743
- // When this frame runs, not when it began: under load it began while the
41744
- // press was still being handled, and that time is no travel.
41745
- start = performance.now();
41770
+ if (playing.lastFrame !== null) {
41771
+ const gap = now - playing.lastFrame;
41772
+ if (gap > TRAVEL_FRAME_MAX_MS) {
41773
+ played += TRAVEL_FRAME_MAX_MS;
41774
+ } else if (gap > 0) {
41775
+ played += gap;
41776
+ }
41746
41777
  }
41747
- // The next frame may have begun while this first one was still being handled.
41748
- const elapsed = now > start ? (now - start) / travelMs : 0;
41778
+ playing.lastFrame = now;
41779
+ const elapsed = played / travelMs;
41749
41780
  // Read at every frame: the layout may move under the travel (a scrollbar
41750
41781
  // coming in narrows the row), and it goes where the page stands.
41751
41782
  const to = pane.offsetLeft;
41752
41783
  const arrived = elapsed >= 1;
41753
41784
  playing.written = arrived ? to : from + (to - from) * easedProgress(elapsed, curve);
41754
41785
  row.scrollLeft = playing.written;
41786
+ // In the frame it is written: the scroll event saying so comes a frame later,
41787
+ // and what follows the row would be drawn a frame behind it.
41788
+ sayRowMoves();
41755
41789
  if (!arrived) {
41756
41790
  frame = requestAnimationFrame(step);
41757
41791
  return;
41758
41792
  }
41759
- playing.stop();
41793
+ letRowGo(movement);
41760
41794
  // Said here rather than waited for: the last frames of a curve can move the row
41761
41795
  // by less than a pixel, and nothing then says it has stopped.
41762
41796
  landRow();
41763
41797
  };
41764
41798
  movement.playing = playing;
41765
- row.setAttribute("data-route-row-travelling", "");
41766
41799
  frame = requestAnimationFrame(step);
41767
41800
  };
41768
41801
 
41802
+ // The row given back to the browser: no travel writes it any more, and it snaps
41803
+ // again. A travel replacing another keeps it (playTravel).
41804
+ const letRowGo = movement => {
41805
+ if (movement.playing) {
41806
+ movement.playing.cancel();
41807
+ movement.playing = null;
41808
+ }
41809
+ rowRef.current.removeAttribute("data-route-row-travelling");
41810
+ };
41811
+
41769
41812
  // Every column back where the map says, and the row on the page of the address, in
41770
41813
  // one go: that page stands at the same place on screen either way.
41771
41814
  const putRowBack = movement => {
@@ -41822,14 +41865,12 @@ const RouteTravel = ({
41822
41865
  return;
41823
41866
  }
41824
41867
  movementRef.current = null;
41825
- if (movement.playing) {
41826
- movement.playing.stop();
41827
- }
41868
+ letRowGo(movement);
41828
41869
  putRowBack(movement);
41829
- row.removeAttribute("data-route-row-travelling");
41830
41870
  row.removeAttribute("data-route-row-taken");
41831
41871
  publishRouteRowMove(null);
41832
41872
  releaseHeldPage();
41873
+ releasePaneAtRest();
41833
41874
  if (liveRef.current) {
41834
41875
  liveRef.current.style.top = "";
41835
41876
  }
@@ -41942,9 +41983,11 @@ const RouteTravel = ({
41942
41983
  return;
41943
41984
  }
41944
41985
  const movementBefore = movementRef.current;
41986
+ // The row still rests on the page answered before. Held in the commit that made
41987
+ // its pane inert, so no frame paints it contained.
41945
41988
  const movement = beginMovement({
41946
41989
  travelling: true
41947
- });
41990
+ }, panesOf(row)[pages.findIndex(each => samePage(each, answered))]);
41948
41991
  if (movementBefore?.byHand && !movementBefore.rowTaken) {
41949
41992
  sayRowMoves();
41950
41993
  takeRowFromHand(movement);
@@ -42003,7 +42046,7 @@ const RouteTravel = ({
42003
42046
  // Not where the travel put it: a hand has taken the row (a trackpad, a wheel),
42004
42047
  // and the travel lets it go, snapping included, as the browser's own scroll does.
42005
42048
  if (moving?.playing && Math.abs(row.scrollLeft - moving.playing.written) > WRITE_ROUNDING) {
42006
- moving.playing.stop();
42049
+ letRowGo(moving);
42007
42050
  }
42008
42051
  const pane = panes[currentIndex];
42009
42052
  if (!moving && pane && restsOn(row, pane, ROW_READING)) {
@@ -42012,7 +42055,7 @@ const RouteTravel = ({
42012
42055
  }
42013
42056
  const movement = moving || beginMovement({
42014
42057
  byHand: true
42015
- });
42058
+ }, pane);
42016
42059
  sayRowMoves();
42017
42060
  if (movement.travelling || !travelByDrag || !pane) {
42018
42061
  return;
@@ -42081,7 +42124,8 @@ const RouteTravel = ({
42081
42124
  askForPageOfRow(index, movement);
42082
42125
  }
42083
42126
  // After the address: a pull coming back to its page must not light the tab the
42084
- // address ran ahead to, even for a render.
42127
+ // address ran ahead to, even for a render. Only what readers are told: the
42128
+ // panes keep their layout until the row rests.
42085
42129
  releaseHeldPage();
42086
42130
  };
42087
42131
  const stopWatching = watchRowScroll(row, {
@@ -42132,12 +42176,14 @@ const RouteTravel = ({
42132
42176
  });
42133
42177
  const movement = movementRef.current;
42134
42178
  if (movement) {
42135
- movement.playing?.stop();
42179
+ movement.playing?.cancel();
42136
42180
  movement.resumeScrollRecording?.();
42137
42181
  }
42182
+ row.removeAttribute("data-route-row-travelling");
42138
42183
  movementRef.current = null;
42139
42184
  publishRouteRowMove(null);
42140
42185
  releaseHeldPage();
42186
+ releasePaneAtRest();
42141
42187
  };
42142
42188
  }, [windowed]);
42143
42189