@jsenv/navi 0.30.37 → 0.30.39

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.
@@ -42632,7 +42632,17 @@ const watchRowScroll = (
42632
42632
  };
42633
42633
 
42634
42634
  installImportMetaCssBuild(import.meta);
42635
- const css$16 = /* css */`.navi_route_travel {
42635
+ const css$16 = /* css */`@property --navi-route-row-pin {
42636
+ syntax: "*";
42637
+ inherits: false
42638
+ }
42639
+
42640
+ @property --navi-route-row-stand {
42641
+ syntax: "*";
42642
+ inherits: false
42643
+ }
42644
+
42645
+ .navi_route_travel {
42636
42646
  flex-direction: column;
42637
42647
  display: flex;
42638
42648
  position: relative;
@@ -42688,7 +42698,7 @@ const css$16 = /* css */`.navi_route_travel {
42688
42698
  display: flex;
42689
42699
  }
42690
42700
 
42691
- & > [data-route-row-pane][inert]:not([data-route-row-held]), &:has( > [data-route-row-held]) > [data-route-row-pane]:not([data-route-row-held]) {
42701
+ & > [data-route-row-pane][inert]:not([data-route-row-held]), &[data-route-row-holding] > [data-route-row-pane]:not([data-route-row-held]) {
42692
42702
  top: var(--navi-route-row-pin, 0);
42693
42703
  contain: paint;
42694
42704
  max-height: 100dvh;
@@ -42860,11 +42870,25 @@ const RouteTravel = ({
42860
42870
  // when the row rests. Not what readers are told the page held is
42861
42871
  // (route_travel_hold.js): that follows the hand, and goes at its lift.
42862
42872
  const heldPaneRef = useRef(null);
42863
- const releasePaneAtRest = () => {
42873
+ const releasePaneAtRest = row => {
42864
42874
  const heldPane = heldPaneRef.current;
42865
42875
  if (heldPane) {
42866
42876
  heldPaneRef.current = null;
42867
42877
  heldPane.removeAttribute("data-route-row-held");
42878
+ row.removeAttribute("data-route-row-holding");
42879
+ }
42880
+ };
42881
+
42882
+ // How far down the reader is looking, on every pane, for those the CSS pins.
42883
+ const pinPanes = row => {
42884
+ const pin = `${pinOf(row)}px`;
42885
+ for (const pane of panesOf(row)) {
42886
+ pane.style.setProperty("--navi-route-row-pin", pin);
42887
+ }
42888
+ };
42889
+ const unpinPanes = row => {
42890
+ for (const pane of panesOf(row)) {
42891
+ pane.style.removeProperty("--navi-route-row-pin");
42868
42892
  }
42869
42893
  };
42870
42894
 
@@ -42891,13 +42915,14 @@ const RouteTravel = ({
42891
42915
  if (!windowed && paneAtRest) {
42892
42916
  heldPaneRef.current = paneAtRest;
42893
42917
  paneAtRest.setAttribute("data-route-row-held", "");
42918
+ row.setAttribute("data-route-row-holding", "");
42894
42919
  }
42895
42920
  if (movement.scrollsDocument) {
42896
42921
  // Whatever the browser does with the offset meanwhile (clamping it to a page
42897
42922
  // shorter than the trip) is not the reader scrolling, and the address it
42898
42923
  // would be written against may already be another page's.
42899
42924
  movement.resumeScrollRecording = suspendScrollRecording();
42900
- row.style.setProperty("--navi-route-row-pin", `${pinOf(row)}px`);
42925
+ pinPanes(row);
42901
42926
  }
42902
42927
  return movement;
42903
42928
  };
@@ -43158,16 +43183,14 @@ const RouteTravel = ({
43158
43183
  movement.rowTaken = true;
43159
43184
  // A travel no hand set off holds nothing: it goes where it goes.
43160
43185
  releaseHeldPage();
43161
- row.style.setProperty("--navi-route-row-stand", `${-position}px`);
43162
- row.setAttribute("data-route-row-taken", "");
43186
+ holdRowAt(row, position);
43163
43187
  afterFrames(2, () => {
43164
43188
  if (movementRef.current !== movement) {
43165
43189
  return;
43166
43190
  }
43167
43191
  // A scroller again, standing where no page does: not snapped meanwhile.
43168
43192
  row.setAttribute("data-route-row-travelling", "");
43169
- row.removeAttribute("data-route-row-taken");
43170
- row.style.removeProperty("--navi-route-row-stand");
43193
+ releaseTakenRow(row);
43171
43194
  afterFrames(1, () => {
43172
43195
  if (movementRef.current === movement) {
43173
43196
  travelRowTo(latestRef.current.currentIndex, movement);
@@ -43187,10 +43210,10 @@ const RouteTravel = ({
43187
43210
  movementRef.current = null;
43188
43211
  letRowGo(movement);
43189
43212
  putRowBack(movement);
43190
- row.removeAttribute("data-route-row-taken");
43213
+ releaseTakenRow(row);
43191
43214
  publishRouteRowMove(null);
43192
43215
  releaseHeldPage();
43193
- releasePaneAtRest();
43216
+ releasePaneAtRest(row);
43194
43217
  if (liveRef.current) {
43195
43218
  liveRef.current.style.top = "";
43196
43219
  }
@@ -43198,7 +43221,7 @@ const RouteTravel = ({
43198
43221
  picture.remove();
43199
43222
  }
43200
43223
  if (movement.scrollsDocument) {
43201
- row.style.removeProperty("--navi-route-row-pin");
43224
+ unpinPanes(row);
43202
43225
  const {
43203
43226
  pages,
43204
43227
  currentIndex
@@ -43503,7 +43526,7 @@ const RouteTravel = ({
43503
43526
  movementRef.current = null;
43504
43527
  publishRouteRowMove(null);
43505
43528
  releaseHeldPage();
43506
- releasePaneAtRest();
43529
+ releasePaneAtRest(row);
43507
43530
  };
43508
43531
  }, [windowed]);
43509
43532
 
@@ -43567,7 +43590,7 @@ const RouteTravel = ({
43567
43590
  const row = rowRef.current;
43568
43591
  const onTouchStart = () => {
43569
43592
  if (!movementRef.current && rowFollowsDocument(row)) {
43570
- row.style.setProperty("--navi-route-row-pin", `${pinOf(row)}px`);
43593
+ pinPanes(row);
43571
43594
  }
43572
43595
  };
43573
43596
  row.addEventListener("touchstart", onTouchStart, {
@@ -43665,6 +43688,22 @@ const copyScrollPositions = (from, to) => {
43665
43688
  // A tab pressed over a row a hand has left moving holds it still (takeRowFromHand):
43666
43689
  // what the row says meanwhile is not a hand moving it.
43667
43690
  const isRowTaken = row => row.hasAttribute("data-route-row-taken");
43691
+
43692
+ // What the CSS offsets while the row is taken. A picture left meanwhile is a clone
43693
+ // of the live page, and takes its offset along.
43694
+ const STANDING_IN_ROW = ":scope > [data-route-row-pane], :scope > [data-route-row-live], :scope > [data-route-row-pictures] > *";
43695
+ const holdRowAt = (row, position) => {
43696
+ for (const element of row.querySelectorAll(STANDING_IN_ROW)) {
43697
+ element.style.setProperty("--navi-route-row-stand", `${-position}px`);
43698
+ }
43699
+ row.setAttribute("data-route-row-taken", "");
43700
+ };
43701
+ const releaseTakenRow = row => {
43702
+ row.removeAttribute("data-route-row-taken");
43703
+ for (const element of row.querySelectorAll(STANDING_IN_ROW)) {
43704
+ element.style.removeProperty("--navi-route-row-stand");
43705
+ }
43706
+ };
43668
43707
  const afterFrames = (count, callback) => {
43669
43708
  if (count === 0) {
43670
43709
  callback();
@@ -84371,9 +84410,15 @@ const PickerNaviMinute = props => {
84371
84410
  * element crossing the cursor — a dozen per wheel tick. Those hovers are noise:
84372
84411
  * the user asked to scroll, not to hover. Anything doing real work on hover (a
84373
84412
  * map highlight, a preview, a prefetch) then pays for that noise on the main
84374
- * thread, exactly while a scroll animation is running. A rule matching
84375
- * `[navi-scrolling] *` takes the rows out of hit-testing and the browser
84376
- * suppresses it all, at no cost per element.
84413
+ * thread, exactly while a scroll animation is running. A rule like
84414
+ * `[navi-scrolling] .my_row { pointer-events: none }` takes the rows out of
84415
+ * hit-testing and the browser suppresses it all.
84416
+ *
84417
+ * Such a rule must end on a class (or an id, a tag, an attribute) of what it
84418
+ * styles: Chrome then restyles only those elements when the attribute comes and
84419
+ * goes. One ending on `*`, including `.my_row:is([navi-scrolling] *)`, gives it
84420
+ * nothing to narrow to, so it restyles everything under the scroller (the whole
84421
+ * document when the page scrolls) at the start and at the end of every scroll.
84377
84422
  *
84378
84423
  * One capturing listener on the document sees them all: "scroll" does not
84379
84424
  * bubble, but it does propagate in the capture phase.
@@ -86758,7 +86803,7 @@ const css$w = /* css */`@property --list-group-label-height {
86758
86803
  }
86759
86804
  }
86760
86805
 
86761
- &:not([navi-hover-while-scrolling]) > .navi_list_scroll_container > .navi_list:is([navi-scrolling] *) {
86806
+ [navi-scrolling] &:not([navi-hover-while-scrolling]) > .navi_list_scroll_container > .navi_list, &[navi-scrolling]:not([navi-hover-while-scrolling]) > .navi_list_scroll_container > .navi_list, &:not([navi-hover-while-scrolling]) > .navi_list_scroll_container[navi-scrolling] > .navi_list {
86762
86807
  pointer-events: none;
86763
86808
  }
86764
86809
 
@@ -88477,7 +88522,7 @@ const useListScrollSync = ({
88477
88522
  // where it lands.
88478
88523
  if (listItems.pagesSignal.peek() === 0) {
88479
88524
  if (typeof scrolledWanted.index === "number") {
88480
- placeItem(findItemElementAt(getListEl(), scrolledWanted.index), scrolledWanted.offset || 0);
88525
+ placeItem(findItemElementAt(getListEl(), scrolledWanted.index), scrolledWanted.offset || 0, scrolledWanted.align);
88481
88526
  }
88482
88527
  return;
88483
88528
  }
@@ -88541,11 +88586,13 @@ const useListScrollSync = ({
88541
88586
  const itemRect = itemEl.getBoundingClientRect();
88542
88587
  const viewportSize = (horizontal ? viewportRect.width : viewportRect.height) / scale;
88543
88588
  const itemSize = (horizontal ? itemRect.width : itemRect.height) / scale;
88544
- const scrollInset = getItemScrollInset(scrollerEl, itemEl, horizontal);
88545
- // Centred in what the room at the start leaves of the view (a fixed bar,
88546
- // a sticky header), not in the whole of it.
88547
- const alignedOffset = align === "center" ? scrollInset + (viewportSize - scrollInset - itemSize) / 2 : align === "end" ? viewportSize - itemSize : scrollInset;
88548
- const offsetWanted = resolveOpenOffset(alignedOffset + offset, viewportSize, itemSize);
88589
+ const offsetWanted = resolveHeldOffset({
88590
+ align,
88591
+ offset,
88592
+ scrollInset: getItemScrollInset(scrollerEl, itemEl, horizontal),
88593
+ viewportSize,
88594
+ itemSize
88595
+ });
88549
88596
  const offsetNow = (horizontal ? itemRect.left - viewportRect.left : itemRect.top - viewportRect.top) / scale;
88550
88597
  const scrollNow = horizontal ? scrollerEl.scrollLeft : scrollerEl.scrollTop;
88551
88598
  const delta = offsetNow - offsetWanted;
@@ -89284,6 +89331,7 @@ const useListScrollSync = ({
89284
89331
  }
89285
89332
  let index = null;
89286
89333
  let offset = 0;
89334
+ let align;
89287
89335
  if (typeof wanted === "number") {
89288
89336
  index = wanted;
89289
89337
  } else if (wanted && wanted.id !== undefined) {
@@ -89292,6 +89340,7 @@ const useListScrollSync = ({
89292
89340
  index = wanted.index;
89293
89341
  }
89294
89342
  offset = wanted.offset || 0;
89343
+ align = wanted.align;
89295
89344
  }
89296
89345
  if (index === null || index < 0 || index >= total) {
89297
89346
  return null;
@@ -89302,7 +89351,15 @@ const useListScrollSync = ({
89302
89351
  const rect = itemEl.getBoundingClientRect();
89303
89352
  itemSize = horizontal ? rect.width : rect.height;
89304
89353
  }
89305
- const above = resolveOpenOffset(getItemScrollInset(scrollerEl, itemEl, horizontal) + offset, screenSize, itemSize);
89354
+ // Not drawn yet, the item weighs nothing and has no inset: centred, it
89355
+ // stands half the screen down, which is where it will be put.
89356
+ const above = resolveHeldOffset({
89357
+ align,
89358
+ offset,
89359
+ scrollInset: getItemScrollInset(scrollerEl, itemEl, horizontal),
89360
+ viewportSize: screenSize,
89361
+ itemSize
89362
+ });
89306
89363
  return {
89307
89364
  index,
89308
89365
  above
@@ -89974,6 +90031,21 @@ const resolveOpenOffset = (offset, viewportSize, itemSize) => {
89974
90031
  }
89975
90032
  return offset;
89976
90033
  };
90034
+ // How far from the start of the view an item held at `{ offset, align }`
90035
+ // stands: read the same way where the list is placed (placeItem) and where the
90036
+ // window is sized around it (readHold), or the window frames a screen the
90037
+ // item is not on. Centred in what the room at the start leaves of the view (a
90038
+ // fixed bar, a sticky header), not in the whole of it.
90039
+ const resolveHeldOffset = ({
90040
+ align,
90041
+ offset,
90042
+ scrollInset,
90043
+ viewportSize,
90044
+ itemSize
90045
+ }) => {
90046
+ const alignedOffset = align === "center" ? scrollInset + (viewportSize - scrollInset - itemSize) / 2 : align === "end" ? viewportSize - itemSize : scrollInset;
90047
+ return resolveOpenOffset(alignedOffset + offset, viewportSize, itemSize);
90048
+ };
89977
90049
 
89978
90050
  // The room an item must be given at the top (or left) of the view: the
89979
90051
  // scroller's own scroll-padding — where a fixed bar publishes the space it