@jsenv/navi 0.29.422 → 0.29.424

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.
@@ -59590,6 +59590,20 @@ const readArea = slideElement => slideElement.getAttribute("data-slide-area") ||
59590
59590
  * `sizing={loading ? "largest" : "frozen"}` and is measured once the real
59591
59591
  * content is there. The freeze holds against what happens INSIDE the box, not
59592
59592
  * against the room it is given: a window resize measures again.
59593
+ * @param {"always"|"near"} [props.mount="always"] - when a slide's CONTENT is
59594
+ * rendered. The <Slide> element always is, so areas, tabs following the box
59595
+ * and `aria-labelledby` are the same either way. "always": every slide's,
59596
+ * from the start. "near": once a travel can show it — the slide on screen,
59597
+ * the one a travel is going to, and at rest the ones a hand dragging the box
59598
+ * would bring alongside (neighbours on the map, along the axes
59599
+ * `travelByDrag` opens, the other end of a `loop` included) — and kept from
59600
+ * then on. For slides that are costly to build (a grid of a few hundred
59601
+ * controls each): opening builds the slide shown and its neighbours rather
59602
+ * than all of them, and a jump to a far slide builds that one and none of
59603
+ * those in between, which a travel never shows. The box is then as large as
59604
+ * the largest slide BUILT so far (see `sizing`), so it grows as a larger one
59605
+ * is reached: give it a size of its own when the slides differ.
59606
+ * `<Slide mount>` says it for one slide — a heavy one among light ones.
59593
59607
  */
59594
59608
  const SlideContainer = ({
59595
59609
  layout = "row",
@@ -59605,6 +59619,7 @@ const SlideContainer = ({
59605
59619
  travelByScroll = "x",
59606
59620
  duration = "300ms",
59607
59621
  sizing = "largest",
59622
+ mount = "always",
59608
59623
  children,
59609
59624
  ...rest
59610
59625
  }) => {
@@ -59717,6 +59732,15 @@ const SlideContainer = ({
59717
59732
  // Undefined at mount on purpose: the first area held outside is news like any
59718
59733
  // other, and it is walked to rather than opened on.
59719
59734
  const areaAskedSeenRef = useRef(undefined);
59735
+ // An area held outside that this container has not answered yet: the effect
59736
+ // reading currentFromCaller walks to it, after the render that brings it.
59737
+ const requestIsNews = () => currentFromCaller !== undefined && currentFromCaller !== areaAskedSeenRef.current;
59738
+ // The slide about to be on screen, as far as a render can tell: the area
59739
+ // asked from outside while it is news — the picture is walked there before
59740
+ // it is painted, the first request included — and the container's own answer
59741
+ // otherwise. What a slide waiting for a travel reads to have its content in
59742
+ // the very render that shows it (see Slide's `mount`).
59743
+ const areaShownNext = requestIsNews() ? currentFromCaller : current;
59720
59744
  // Where the container stands, said where the caller holds it.
59721
59745
  //
59722
59746
  // Whether a slide is a place one came from is the STATE's own business (see
@@ -59771,6 +59795,22 @@ const SlideContainer = ({
59771
59795
  // state: nothing on screen depends on it, and a travel must read what the
59772
59796
  // one before it wrote, not what the last render saw.
59773
59797
  const cameFromRef = useRef({});
59798
+ // The slides a travel has come near while their content was not rendered
59799
+ // (mount="near", see renderNear): only ever grows, and only for a slide that
59800
+ // was waiting — a container whose slides all render never re-renders for it.
59801
+ const [nearAreas, setNearAreas] = useState([]);
59802
+ // Nothing names the slide on screen (no area held outside, no default): it
59803
+ // is the first one in the DOM, which the first render cannot read yet. The
59804
+ // first Slide to render is that one, so it claims it — the slide on screen
59805
+ // then has its content from the first commit (a size frozen at mount, a
59806
+ // focus landing in it) rather than one render late.
59807
+ const openingClaimRef = useRef(null);
59808
+ const claimOpening = area => {
59809
+ if (openingClaimRef.current === null) {
59810
+ openingClaimRef.current = area;
59811
+ }
59812
+ return openingClaimRef.current === area;
59813
+ };
59774
59814
 
59775
59815
  // The controller has caught up with the slide the container went to on its
59776
59816
  // own (commit="rest"): there are no longer two answers to give, so the
@@ -59911,6 +59951,44 @@ const SlideContainer = ({
59911
59951
  }
59912
59952
  };
59913
59953
 
59954
+ // The slides a hand dragging the box from this area would bring alongside:
59955
+ // the map's neighbours on the axes it drags, read the way the drag reads them
59956
+ // (areaTowards knows the holes and a loop's two ends).
59957
+ const areasAlongside = area => {
59958
+ const alongside = [];
59959
+ if (dragAxes?.includes("x")) {
59960
+ alongside.push(areaTowards(-1, 0, area), areaTowards(1, 0, area));
59961
+ }
59962
+ if (dragAxes?.includes("y")) {
59963
+ alongside.push(areaTowards(0, -1, area), areaTowards(0, 1, area));
59964
+ }
59965
+ return alongside;
59966
+ };
59967
+
59968
+ // mount="near": the content of a slide is asked for before a travel shows
59969
+ // it. The neighbours are asked for AT REST, never when a travel sets off: the
59970
+ // travel shows the slide it goes to and nothing past it, and a slide built in
59971
+ // the render that starts it is a frame the travel waits for. At rest nothing
59972
+ // moves, and a hand landing next is still answered after the build.
59973
+ const renderNear = (area, {
59974
+ alongside
59975
+ }) => {
59976
+ // Asked on every render: a container with nothing waiting stops here.
59977
+ if (!trackRef.current?.querySelector(":scope > [data-slide-unrendered]")) {
59978
+ return;
59979
+ }
59980
+ const unrendered = readMap().slideElements.filter(slideElement => slideElement.hasAttribute("data-slide-unrendered")).map(readArea);
59981
+ const near = alongside ? [area, ...areasAlongside(area)] : [area];
59982
+ const toRender = near.filter(nearArea => unrendered.includes(nearArea));
59983
+ if (!toRender.length) {
59984
+ return;
59985
+ }
59986
+ setNearAreas(previous => {
59987
+ const added = toRender.filter(nearArea => !previous.includes(nearArea));
59988
+ return added.length ? [...previous, ...added] : previous;
59989
+ });
59990
+ };
59991
+
59914
59992
  // The travel is over: the stage is struck and every slide goes back where the
59915
59993
  // map says it is. Nothing is seen moving for it — the slide on screen sits at
59916
59994
  // the same place whatever the arrangement (its own offset and the track's are
@@ -59959,6 +60037,9 @@ const SlideContainer = ({
59959
60037
  const offset = `${-x * 100}% ${-y * 100}%`;
59960
60038
  offsetRef.current = offset;
59961
60039
  track.style.setProperty("--slide-container-offset", offset);
60040
+ renderNear(currentArea, {
60041
+ alongside: true
60042
+ });
59962
60043
  // Arrived, so the change can be told (commit="rest"): the picture is at
59963
60044
  // rest and whatever the caller does with it — write the URL, ask a server —
59964
60045
  // costs the gesture nothing anymore.
@@ -60236,6 +60317,15 @@ const SlideContainer = ({
60236
60317
  if (!stageRef.current) {
60237
60318
  drawnAreaRef.current = currentArea;
60238
60319
  }
60320
+ // What a travel can show next (mount="near"), around where the picture
60321
+ // SETTLES: a request from outside is walked to by an effect running after
60322
+ // this one, in the same commit, and the slides around a picture about to be
60323
+ // walked away from would be built for nothing. The neighbours only at
60324
+ // rest, see renderNear.
60325
+ const settlingArea = requestIsNews() && reachableTowards(currentArea, currentFromCaller) || currentArea;
60326
+ renderNear(settlingArea, {
60327
+ alongside: !stageRef.current
60328
+ });
60239
60329
  // Said last, and after data-current has been written: what a travel would do
60240
60330
  // is read off the map from the slide that is now current, and off the locks
60241
60331
  // that slide is wearing in this very commit.
@@ -60473,12 +60563,9 @@ const SlideContainer = ({
60473
60563
  // request later), and what nothing has answered yet must still be answered
60474
60564
  // once it can be.
60475
60565
  useLayoutEffect(() => {
60476
- if (currentFromCaller === undefined) {
60477
- return;
60478
- }
60479
- if (currentFromCaller === areaAskedSeenRef.current) {
60480
- // Not news: either nothing moved, or this container is reading back what
60481
- // it wrote itself.
60566
+ if (!requestIsNews()) {
60567
+ // Not news: nothing held outside, nothing moved, or this container
60568
+ // reading back what it wrote itself.
60482
60569
  return;
60483
60570
  }
60484
60571
  // The slide asked for is not rendered yet — no slides at all, or one whose
@@ -61308,6 +61395,11 @@ const SlideContainer = ({
61308
61395
  if (!past) {
61309
61396
  return false;
61310
61397
  }
61398
+ // Two slides from the one at rest, so possibly never rendered: asked
61399
+ // for now, and built before the frame that brings it in is painted.
61400
+ renderNear(past, {
61401
+ alongside: false
61402
+ });
61311
61403
  const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current")) || slideElements[0];
61312
61404
  const basePlace = stageRef.current?.placeByArea.get(reached) || placeOf.get(reached) || {
61313
61405
  x: 0,
@@ -61766,6 +61858,12 @@ const SlideContainer = ({
61766
61858
  done,
61767
61859
  valueByArea,
61768
61860
  settleFocus,
61861
+ // What a slide reads to know whether its content is rendered yet
61862
+ // (see Slide's `mount`).
61863
+ mount,
61864
+ areaShownNext,
61865
+ nearAreas,
61866
+ claimOpening: areaShownNext === undefined ? claimOpening : null,
61769
61867
  // The box itself, for what is written INSIDE it to read the same
61770
61868
  // facts what is written outside reads by id (useSlideContainer):
61771
61869
  // a way out is a way out on either side of the box.
@@ -61805,9 +61903,12 @@ const SlideContainer = ({
61805
61903
  * try to leave.
61806
61904
  * @param {boolean} [props.preventNavNext] - hold them from going right or down.
61807
61905
  * @param {boolean} [props.preventNavPrevious] - …left or up.
61906
+ * @param {"always"|"near"} [props.mount] - when this slide's content is
61907
+ * rendered; the container's `mount` when not said (see there).
61808
61908
  */
61809
61909
  const Slide = ({
61810
61910
  area,
61911
+ mount,
61811
61912
  required,
61812
61913
  preventNav,
61813
61914
  preventNavNext = preventNav,
@@ -61817,6 +61918,17 @@ const Slide = ({
61817
61918
  }) => {
61818
61919
  const container = useContext(SlideContainerContext);
61819
61920
  const slideArea = area ?? rest.id;
61921
+ // Asked by every slide, whatever its own mode: the claim goes to the first
61922
+ // slide to RENDER, and a slide that skipped asking would hand it to the one
61923
+ // after it.
61924
+ const opening = container?.claimOpening?.(slideArea);
61925
+ // Kept once rendered (mount="near"): going back builds nothing again, and a
61926
+ // slide being left keeps its content for as long as it is still in the frame.
61927
+ const renderedRef = useRef(false);
61928
+ if (!renderedRef.current && (!container || (mount ?? container.mount) !== "near" || opening || slideArea === container.areaShownNext || container.nearAreas.includes(slideArea))) {
61929
+ renderedRef.current = true;
61930
+ }
61931
+ const rendered = renderedRef.current;
61820
61932
  const answered = Boolean(container?.answeredAreas.includes(slideArea));
61821
61933
  const holdsUntilAnswered = Boolean(required) && !answered;
61822
61934
  const nextIsLocked = Boolean(preventNavNext) || holdsUntilAnswered;
@@ -61866,13 +61978,17 @@ const Slide = ({
61866
61978
  "data-slide-area": slideArea,
61867
61979
  "data-prevent-nav-next": nextIsLocked ? "" : undefined,
61868
61980
  "data-prevent-nav-previous": preventNavPrevious ? "" : undefined
61981
+ // What the container reads to know a slide is waiting for a travel
61982
+ // to come near (see renderNear).
61983
+ ,
61984
+ "data-slide-unrendered": rendered ? undefined : ""
61869
61985
  // A surface one interacts with says what state it is in, the way every
61870
61986
  // other surface does — a dialog carries the very same set.
61871
61987
  // :focus-within is the one a slide is really about: it is what tells
61872
61988
  // the slide holding the keyboard from the ones waiting.
61873
61989
  ,
61874
61990
  pseudoClasses: SLIDE_PSEUDO_CLASSES,
61875
- children: children
61991
+ children: rendered ? children : null
61876
61992
  })
61877
61993
  })
61878
61994
  });
@@ -74162,9 +74278,12 @@ const readRoomAround = ({
74162
74278
  // too — and an item not drawn is taken to weigh what the drawn items at that
74163
74279
  // edge of the window weigh. Items of one kind come together (the one-line
74164
74280
  // items of the past, the cards ahead), and an item misjudged is weighed again
74165
- // once it is drawn (see "after slide"): weighing it at the average of all
74166
- // items would draw the cards next to one-line items at the one-line size, and
74167
- // a few dozen of them.
74281
+ // once it is drawn (see "after slide"): a window that came up short is
74282
+ // extended on the next frame, from what it measured. Weighing an item not drawn
74283
+ // at the average of all items, or at the smallest an item can be so that the
74284
+ // window never comes up short, walks into the cards at the one-line size,
74285
+ // several at a time — the latter measured in wematch: no blank spared, 50–65 ms
74286
+ // tasks while reading down through the cards.
74168
74287
  const EDGE_SAMPLE_ITEM_COUNT = 8;
74169
74288
  const createItemSizeReader = (items, start, end, itemSize) => {
74170
74289
  const sizeByIndex = new Map();
@@ -76637,11 +76756,18 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
76637
76756
  * source for is a page (see `<List.Items pageSize>`), whatever either says,
76638
76757
  * so the first picture costs no second request.
76639
76758
  * @param {number} [props.virtualItemSize]
76640
- * The size of one item along the scroll axis, in px, when every item has the
76641
- * same: what the fillers are sized with and what a scroll position is
76642
- * estimated from. Left out, the list measures its items — once when it
76643
- * mounts, again when a popup around it opens, and after each commit while
76644
- * items are held off screen. Given, it never measures.
76759
+ * The room an item not drawn is held at, in px along the scroll axis: what
76760
+ * the fillers hold for each item outside the render window, what a scroll
76761
+ * position inside them is read with, and the least an item on its way takes
76762
+ * (see `renderSkeleton`). Left out, it is the average of the items measured
76763
+ * so far — once when the list mounts, again when a popup around it opens,
76764
+ * and after each commit while items are held off screen. Given, it is the
76765
+ * worst case: the size every item has, or, in a list whose items differ, the
76766
+ * smallest an item can be (a thread of one-line items and cards: the
76767
+ * one-line size) — an item guessed too small builds one more, an item
76768
+ * guessed too big leaves a blank (see docs/scroll.md, "What the list knows,
76769
+ * and what it guesses"). The items drawn are measured either way: the render
76770
+ * window is sized on them.
76645
76771
  * @param {"self"|"parent"|"document"|Element|{current: Element}} [props.scroller="self"]
76646
76772
  * Which box scrolls — and with it, which box the render window follows and
76647
76773
  * which box a scroll position is read from (`onScrolledChange`). `"self"`