@jsenv/navi 0.30.36 → 0.30.38

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.
@@ -30518,8 +30518,9 @@ const SCOPED_MANY_OPTIONS = new Set([
30518
30518
  * policy as that verb, and is a write of its own for a dependent:
30519
30519
  * `{ resource: ORDER, on: ["cancel"] }`. Its `rerunOn` says which reads of
30520
30520
  * this resource it reruns (`{ GET_RANGE: true }`); a read it leaves out
30521
- * follows `rerunOn` for its verb. A name a verb takes, or one the resource
30522
- * already exposes (`withParams`, `store`…), throws. A `.scopedOne()` or
30521
+ * follows `rerunOn` for its verb. A verb's name in any case (`post`,
30522
+ * `get_many`…), or one the resource already exposes (`withParams`, `store`…),
30523
+ * throws. A `.scopedOne()` or
30523
30524
  * `.scopedMany()` takes `writes` too, `.scopedMany()` with the plural verbs.
30524
30525
  * @param {{ GET?: { ms?: number, max?: number }, GET_MANY?: { ms?: number, max?: number } }} [restCallbacks.keep] -
30525
30526
  * how long an answer of a read is trusted, coming back to the same params
@@ -32084,12 +32085,15 @@ ${name}.many("${propertyName}") source location: ${declarationSite}`,
32084
32085
 
32085
32086
  // expose one action per write named in `writes`
32086
32087
  if (writes) {
32088
+ const membersExposed = Object.keys(stateFacade).filter(
32089
+ (key) => !VERB_KEYS.includes(key),
32090
+ );
32087
32091
  for (const [writeName, write] of Object.entries(writes)) {
32088
32092
  assertWriteCanBeNamed(
32089
32093
  name,
32090
32094
  writeName,
32091
32095
  write,
32092
- stateFacade,
32096
+ membersExposed,
32093
32097
  namedWriteVerbs,
32094
32098
  );
32095
32099
  const { verb: writeVerb, callback, rerunOn: writeRerunOn } = write;
@@ -32402,7 +32406,7 @@ const assertWriteCanBeNamed = (
32402
32406
  name,
32403
32407
  writeName,
32404
32408
  write,
32405
- stateFacade,
32409
+ membersExposed,
32406
32410
  namedWriteVerbs,
32407
32411
  ) => {
32408
32412
  const where = `resource(${name}) writes.${writeName}`;
@@ -32411,9 +32415,9 @@ const assertWriteCanBeNamed = (
32411
32415
  `${where}: "${writeName}" is a verb's name — a write named in "writes" takes a name of its own (cancel, archive…)`,
32412
32416
  );
32413
32417
  }
32414
- if (Object.hasOwn(stateFacade, writeName)) {
32418
+ if (membersExposed.includes(writeName)) {
32415
32419
  throw new Error(
32416
- `${where}: the resource already exposes "${writeName}" — pick another name`,
32420
+ `${where}: the resource already exposes "${writeName}" — a write takes a name none of its members has: ${membersExposed.join(", ")}`,
32417
32421
  );
32418
32422
  }
32419
32423
  if (!write || typeof write !== "object") {
@@ -42628,7 +42632,12 @@ const watchRowScroll = (
42628
42632
  };
42629
42633
 
42630
42634
  installImportMetaCssBuild(import.meta);
42631
- 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
+ .navi_route_travel {
42632
42641
  flex-direction: column;
42633
42642
  display: flex;
42634
42643
  position: relative;
@@ -42684,7 +42693,7 @@ const css$16 = /* css */`.navi_route_travel {
42684
42693
  display: flex;
42685
42694
  }
42686
42695
 
42687
- & > [data-route-row-pane][inert]:not([data-route-row-held]), &:has( > [data-route-row-held]) > [data-route-row-pane]:not([data-route-row-held]) {
42696
+ & > [data-route-row-pane][inert]:not([data-route-row-held]), &[data-route-row-holding] > [data-route-row-pane]:not([data-route-row-held]) {
42688
42697
  top: var(--navi-route-row-pin, 0);
42689
42698
  contain: paint;
42690
42699
  max-height: 100dvh;
@@ -42856,11 +42865,25 @@ const RouteTravel = ({
42856
42865
  // when the row rests. Not what readers are told the page held is
42857
42866
  // (route_travel_hold.js): that follows the hand, and goes at its lift.
42858
42867
  const heldPaneRef = useRef(null);
42859
- const releasePaneAtRest = () => {
42868
+ const releasePaneAtRest = row => {
42860
42869
  const heldPane = heldPaneRef.current;
42861
42870
  if (heldPane) {
42862
42871
  heldPaneRef.current = null;
42863
42872
  heldPane.removeAttribute("data-route-row-held");
42873
+ row.removeAttribute("data-route-row-holding");
42874
+ }
42875
+ };
42876
+
42877
+ // How far down the reader is looking, on every pane, for those the CSS pins.
42878
+ const pinPanes = row => {
42879
+ const pin = `${pinOf(row)}px`;
42880
+ for (const pane of panesOf(row)) {
42881
+ pane.style.setProperty("--navi-route-row-pin", pin);
42882
+ }
42883
+ };
42884
+ const unpinPanes = row => {
42885
+ for (const pane of panesOf(row)) {
42886
+ pane.style.removeProperty("--navi-route-row-pin");
42864
42887
  }
42865
42888
  };
42866
42889
 
@@ -42887,13 +42910,14 @@ const RouteTravel = ({
42887
42910
  if (!windowed && paneAtRest) {
42888
42911
  heldPaneRef.current = paneAtRest;
42889
42912
  paneAtRest.setAttribute("data-route-row-held", "");
42913
+ row.setAttribute("data-route-row-holding", "");
42890
42914
  }
42891
42915
  if (movement.scrollsDocument) {
42892
42916
  // Whatever the browser does with the offset meanwhile (clamping it to a page
42893
42917
  // shorter than the trip) is not the reader scrolling, and the address it
42894
42918
  // would be written against may already be another page's.
42895
42919
  movement.resumeScrollRecording = suspendScrollRecording();
42896
- row.style.setProperty("--navi-route-row-pin", `${pinOf(row)}px`);
42920
+ pinPanes(row);
42897
42921
  }
42898
42922
  return movement;
42899
42923
  };
@@ -43186,7 +43210,7 @@ const RouteTravel = ({
43186
43210
  row.removeAttribute("data-route-row-taken");
43187
43211
  publishRouteRowMove(null);
43188
43212
  releaseHeldPage();
43189
- releasePaneAtRest();
43213
+ releasePaneAtRest(row);
43190
43214
  if (liveRef.current) {
43191
43215
  liveRef.current.style.top = "";
43192
43216
  }
@@ -43194,7 +43218,7 @@ const RouteTravel = ({
43194
43218
  picture.remove();
43195
43219
  }
43196
43220
  if (movement.scrollsDocument) {
43197
- row.style.removeProperty("--navi-route-row-pin");
43221
+ unpinPanes(row);
43198
43222
  const {
43199
43223
  pages,
43200
43224
  currentIndex
@@ -43499,7 +43523,7 @@ const RouteTravel = ({
43499
43523
  movementRef.current = null;
43500
43524
  publishRouteRowMove(null);
43501
43525
  releaseHeldPage();
43502
- releasePaneAtRest();
43526
+ releasePaneAtRest(row);
43503
43527
  };
43504
43528
  }, [windowed]);
43505
43529
 
@@ -43563,7 +43587,7 @@ const RouteTravel = ({
43563
43587
  const row = rowRef.current;
43564
43588
  const onTouchStart = () => {
43565
43589
  if (!movementRef.current && rowFollowsDocument(row)) {
43566
- row.style.setProperty("--navi-route-row-pin", `${pinOf(row)}px`);
43590
+ pinPanes(row);
43567
43591
  }
43568
43592
  };
43569
43593
  row.addEventListener("touchstart", onTouchStart, {
@@ -84367,9 +84391,15 @@ const PickerNaviMinute = props => {
84367
84391
  * element crossing the cursor — a dozen per wheel tick. Those hovers are noise:
84368
84392
  * the user asked to scroll, not to hover. Anything doing real work on hover (a
84369
84393
  * map highlight, a preview, a prefetch) then pays for that noise on the main
84370
- * thread, exactly while a scroll animation is running. A rule matching
84371
- * `[navi-scrolling] *` takes the rows out of hit-testing and the browser
84372
- * suppresses it all, at no cost per element.
84394
+ * thread, exactly while a scroll animation is running. A rule like
84395
+ * `[navi-scrolling] .my_row { pointer-events: none }` takes the rows out of
84396
+ * hit-testing and the browser suppresses it all.
84397
+ *
84398
+ * Such a rule must end on a class (or an id, a tag, an attribute) of what it
84399
+ * styles: Chrome then restyles only those elements when the attribute comes and
84400
+ * goes. One ending on `*`, including `.my_row:is([navi-scrolling] *)`, gives it
84401
+ * nothing to narrow to, so it restyles everything under the scroller (the whole
84402
+ * document when the page scrolls) at the start and at the end of every scroll.
84373
84403
  *
84374
84404
  * One capturing listener on the document sees them all: "scroll" does not
84375
84405
  * bubble, but it does propagate in the capture phase.
@@ -86754,7 +86784,7 @@ const css$w = /* css */`@property --list-group-label-height {
86754
86784
  }
86755
86785
  }
86756
86786
 
86757
- &:not([navi-hover-while-scrolling]) > .navi_list_scroll_container > .navi_list:is([navi-scrolling] *) {
86787
+ [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 {
86758
86788
  pointer-events: none;
86759
86789
  }
86760
86790
 
@@ -92780,8 +92810,19 @@ const useItemStore = ({
92780
92810
  // was asked for, and the window keeps growing meanwhile: dropping what the
92781
92811
  // page did not cover empties items on screen.
92782
92812
  const staleRanksRef = useRef(null);
92813
+ // A run standing for no item has no rank to mark: its count is what is from
92814
+ // before, and no window frames anything that would ask for it again.
92815
+ const countStaleRef = useRef(false);
92816
+ const markHeldStale = () => {
92817
+ staleRanksRef.current = new Set(pages.byIndex.keys());
92818
+ countStaleRef.current = pages.count === 0;
92819
+ };
92783
92820
  if (staleRanksRef.current === null) {
92784
- staleRanksRef.current = restored ? new Set(pages.byIndex.keys()) : new Set();
92821
+ if (restored) {
92822
+ markHeldStale();
92823
+ } else {
92824
+ staleRanksRef.current = new Set();
92825
+ }
92785
92826
  }
92786
92827
  const takeInPage = ({
92787
92828
  pageItems,
@@ -92820,6 +92861,7 @@ const useItemStore = ({
92820
92861
  i++;
92821
92862
  }
92822
92863
  pages.count = pageCount;
92864
+ countStaleRef.current = false;
92823
92865
  // Which rank holds which id, kept by the source so a list drawing
92824
92866
  // this collection again finds it drawn (see readComposition above).
92825
92867
  if (itemsAction.writeComposition) {
@@ -92855,7 +92897,7 @@ const useItemStore = ({
92855
92897
  const invalidationRef = useRef(invalidation);
92856
92898
  if (invalidationRef.current !== invalidation) {
92857
92899
  invalidationRef.current = invalidation;
92858
- staleRanksRef.current = new Set(pages.byIndex.keys());
92900
+ markHeldStale();
92859
92901
  }
92860
92902
  // The one request in flight, with the means to call it off: a page asked for
92861
92903
  // a window the list has left is work the server and the browser are doing for
@@ -93053,7 +93095,8 @@ const useItemStore = ({
93053
93095
  rank++;
93054
93096
  }
93055
93097
  }
93056
- const revalidating = staleFrom !== -1;
93098
+ const countStale = countStaleRef.current;
93099
+ const revalidating = staleFrom !== -1 || countStale;
93057
93100
  // The list is held on an item nothing on screen leads to: the items it holds
93058
93101
  // do not contain it, so no window it could draw will ever bring it. Only
93059
93102
  // asking for it by name does.
@@ -93069,6 +93112,10 @@ const useItemStore = ({
93069
93112
  const from = typeof wanted.index === "number" ? rankOfIndex(wanted.index) - Math.floor(budget / 2) : 0;
93070
93113
  start = from < 0 ? 0 : from;
93071
93114
  end = start + budget - 1;
93115
+ } else if (countStale) {
93116
+ // Nothing drawn to anchor on: the first page, like the very first ask.
93117
+ start = 0;
93118
+ end = budget - 1;
93072
93119
  } else if (revalidating) {
93073
93120
  // The items from before that the window draws, and the missing ones
93074
93121
  // with them: one ask.
@@ -93270,6 +93317,7 @@ const useItemStore = ({
93270
93317
  staleRanksRef.current.delete(rank);
93271
93318
  rank++;
93272
93319
  }
93320
+ countStaleRef.current = false;
93273
93321
  setRefreshing(false);
93274
93322
  return;
93275
93323
  }