@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.
@@ -60814,6 +60814,20 @@ const readArea = slideElement => slideElement.getAttribute("data-slide-area") ||
60814
60814
  * `sizing={loading ? "largest" : "frozen"}` and is measured once the real
60815
60815
  * content is there. The freeze holds against what happens INSIDE the box, not
60816
60816
  * against the room it is given: a window resize measures again.
60817
+ * @param {"always"|"near"} [props.mount="always"] - when a slide's CONTENT is
60818
+ * rendered. The <Slide> element always is, so areas, tabs following the box
60819
+ * and `aria-labelledby` are the same either way. "always": every slide's,
60820
+ * from the start. "near": once a travel can show it — the slide on screen,
60821
+ * the one a travel is going to, and at rest the ones a hand dragging the box
60822
+ * would bring alongside (neighbours on the map, along the axes
60823
+ * `travelByDrag` opens, the other end of a `loop` included) — and kept from
60824
+ * then on. For slides that are costly to build (a grid of a few hundred
60825
+ * controls each): opening builds the slide shown and its neighbours rather
60826
+ * than all of them, and a jump to a far slide builds that one and none of
60827
+ * those in between, which a travel never shows. The box is then as large as
60828
+ * the largest slide BUILT so far (see `sizing`), so it grows as a larger one
60829
+ * is reached: give it a size of its own when the slides differ.
60830
+ * `<Slide mount>` says it for one slide — a heavy one among light ones.
60817
60831
  */
60818
60832
  const SlideContainer = ({
60819
60833
  layout = "row",
@@ -60829,6 +60843,7 @@ const SlideContainer = ({
60829
60843
  travelByScroll = "x",
60830
60844
  duration = "300ms",
60831
60845
  sizing = "largest",
60846
+ mount = "always",
60832
60847
  children,
60833
60848
  ...rest
60834
60849
  }) => {
@@ -60948,6 +60963,15 @@ const SlideContainer = ({
60948
60963
  // Undefined at mount on purpose: the first area held outside is news like any
60949
60964
  // other, and it is walked to rather than opened on.
60950
60965
  const areaAskedSeenRef = useRef(undefined);
60966
+ // An area held outside that this container has not answered yet: the effect
60967
+ // reading currentFromCaller walks to it, after the render that brings it.
60968
+ const requestIsNews = () => currentFromCaller !== undefined && currentFromCaller !== areaAskedSeenRef.current;
60969
+ // The slide about to be on screen, as far as a render can tell: the area
60970
+ // asked from outside while it is news — the picture is walked there before
60971
+ // it is painted, the first request included — and the container's own answer
60972
+ // otherwise. What a slide waiting for a travel reads to have its content in
60973
+ // the very render that shows it (see Slide's `mount`).
60974
+ const areaShownNext = requestIsNews() ? currentFromCaller : current;
60951
60975
  // Where the container stands, said where the caller holds it.
60952
60976
  //
60953
60977
  // Whether a slide is a place one came from is the STATE's own business (see
@@ -61002,6 +61026,22 @@ const SlideContainer = ({
61002
61026
  // state: nothing on screen depends on it, and a travel must read what the
61003
61027
  // one before it wrote, not what the last render saw.
61004
61028
  const cameFromRef = useRef({});
61029
+ // The slides a travel has come near while their content was not rendered
61030
+ // (mount="near", see renderNear): only ever grows, and only for a slide that
61031
+ // was waiting — a container whose slides all render never re-renders for it.
61032
+ const [nearAreas, setNearAreas] = useState([]);
61033
+ // Nothing names the slide on screen (no area held outside, no default): it
61034
+ // is the first one in the DOM, which the first render cannot read yet. The
61035
+ // first Slide to render is that one, so it claims it — the slide on screen
61036
+ // then has its content from the first commit (a size frozen at mount, a
61037
+ // focus landing in it) rather than one render late.
61038
+ const openingClaimRef = useRef(null);
61039
+ const claimOpening = area => {
61040
+ if (openingClaimRef.current === null) {
61041
+ openingClaimRef.current = area;
61042
+ }
61043
+ return openingClaimRef.current === area;
61044
+ };
61005
61045
 
61006
61046
  // The controller has caught up with the slide the container went to on its
61007
61047
  // own (commit="rest"): there are no longer two answers to give, so the
@@ -61142,6 +61182,44 @@ const SlideContainer = ({
61142
61182
  }
61143
61183
  };
61144
61184
 
61185
+ // The slides a hand dragging the box from this area would bring alongside:
61186
+ // the map's neighbours on the axes it drags, read the way the drag reads them
61187
+ // (areaTowards knows the holes and a loop's two ends).
61188
+ const areasAlongside = area => {
61189
+ const alongside = [];
61190
+ if (dragAxes?.includes("x")) {
61191
+ alongside.push(areaTowards(-1, 0, area), areaTowards(1, 0, area));
61192
+ }
61193
+ if (dragAxes?.includes("y")) {
61194
+ alongside.push(areaTowards(0, -1, area), areaTowards(0, 1, area));
61195
+ }
61196
+ return alongside;
61197
+ };
61198
+
61199
+ // mount="near": the content of a slide is asked for before a travel shows
61200
+ // it. The neighbours are asked for AT REST, never when a travel sets off: the
61201
+ // travel shows the slide it goes to and nothing past it, and a slide built in
61202
+ // the render that starts it is a frame the travel waits for. At rest nothing
61203
+ // moves, and a hand landing next is still answered after the build.
61204
+ const renderNear = (area, {
61205
+ alongside
61206
+ }) => {
61207
+ // Asked on every render: a container with nothing waiting stops here.
61208
+ if (!trackRef.current?.querySelector(":scope > [data-slide-unrendered]")) {
61209
+ return;
61210
+ }
61211
+ const unrendered = readMap().slideElements.filter(slideElement => slideElement.hasAttribute("data-slide-unrendered")).map(readArea);
61212
+ const near = alongside ? [area, ...areasAlongside(area)] : [area];
61213
+ const toRender = near.filter(nearArea => unrendered.includes(nearArea));
61214
+ if (!toRender.length) {
61215
+ return;
61216
+ }
61217
+ setNearAreas(previous => {
61218
+ const added = toRender.filter(nearArea => !previous.includes(nearArea));
61219
+ return added.length ? [...previous, ...added] : previous;
61220
+ });
61221
+ };
61222
+
61145
61223
  // The travel is over: the stage is struck and every slide goes back where the
61146
61224
  // map says it is. Nothing is seen moving for it — the slide on screen sits at
61147
61225
  // the same place whatever the arrangement (its own offset and the track's are
@@ -61190,6 +61268,9 @@ const SlideContainer = ({
61190
61268
  const offset = `${-x * 100}% ${-y * 100}%`;
61191
61269
  offsetRef.current = offset;
61192
61270
  track.style.setProperty("--slide-container-offset", offset);
61271
+ renderNear(currentArea, {
61272
+ alongside: true
61273
+ });
61193
61274
  // Arrived, so the change can be told (commit="rest"): the picture is at
61194
61275
  // rest and whatever the caller does with it — write the URL, ask a server —
61195
61276
  // costs the gesture nothing anymore.
@@ -61467,6 +61548,15 @@ const SlideContainer = ({
61467
61548
  if (!stageRef.current) {
61468
61549
  drawnAreaRef.current = currentArea;
61469
61550
  }
61551
+ // What a travel can show next (mount="near"), around where the picture
61552
+ // SETTLES: a request from outside is walked to by an effect running after
61553
+ // this one, in the same commit, and the slides around a picture about to be
61554
+ // walked away from would be built for nothing. The neighbours only at
61555
+ // rest, see renderNear.
61556
+ const settlingArea = requestIsNews() && reachableTowards(currentArea, currentFromCaller) || currentArea;
61557
+ renderNear(settlingArea, {
61558
+ alongside: !stageRef.current
61559
+ });
61470
61560
  // Said last, and after data-current has been written: what a travel would do
61471
61561
  // is read off the map from the slide that is now current, and off the locks
61472
61562
  // that slide is wearing in this very commit.
@@ -61704,12 +61794,9 @@ const SlideContainer = ({
61704
61794
  // request later), and what nothing has answered yet must still be answered
61705
61795
  // once it can be.
61706
61796
  useLayoutEffect(() => {
61707
- if (currentFromCaller === undefined) {
61708
- return;
61709
- }
61710
- if (currentFromCaller === areaAskedSeenRef.current) {
61711
- // Not news: either nothing moved, or this container is reading back what
61712
- // it wrote itself.
61797
+ if (!requestIsNews()) {
61798
+ // Not news: nothing held outside, nothing moved, or this container
61799
+ // reading back what it wrote itself.
61713
61800
  return;
61714
61801
  }
61715
61802
  // The slide asked for is not rendered yet — no slides at all, or one whose
@@ -62539,6 +62626,11 @@ const SlideContainer = ({
62539
62626
  if (!past) {
62540
62627
  return false;
62541
62628
  }
62629
+ // Two slides from the one at rest, so possibly never rendered: asked
62630
+ // for now, and built before the frame that brings it in is painted.
62631
+ renderNear(past, {
62632
+ alongside: false
62633
+ });
62542
62634
  const currentElement = slideElements.find(slideElement => slideElement.hasAttribute("data-current")) || slideElements[0];
62543
62635
  const basePlace = stageRef.current?.placeByArea.get(reached) || placeOf.get(reached) || {
62544
62636
  x: 0,
@@ -62997,6 +63089,12 @@ const SlideContainer = ({
62997
63089
  done,
62998
63090
  valueByArea,
62999
63091
  settleFocus,
63092
+ // What a slide reads to know whether its content is rendered yet
63093
+ // (see Slide's `mount`).
63094
+ mount,
63095
+ areaShownNext,
63096
+ nearAreas,
63097
+ claimOpening: areaShownNext === undefined ? claimOpening : null,
63000
63098
  // The box itself, for what is written INSIDE it to read the same
63001
63099
  // facts what is written outside reads by id (useSlideContainer):
63002
63100
  // a way out is a way out on either side of the box.
@@ -63053,9 +63151,12 @@ const warnOnNonStringArea = area => {
63053
63151
  * try to leave.
63054
63152
  * @param {boolean} [props.preventNavNext] - hold them from going right or down.
63055
63153
  * @param {boolean} [props.preventNavPrevious] - …left or up.
63154
+ * @param {"always"|"near"} [props.mount] - when this slide's content is
63155
+ * rendered; the container's `mount` when not said (see there).
63056
63156
  */
63057
63157
  const Slide = ({
63058
63158
  area,
63159
+ mount,
63059
63160
  required,
63060
63161
  preventNav,
63061
63162
  preventNavNext = preventNav,
@@ -63068,6 +63169,17 @@ const Slide = ({
63068
63169
  {
63069
63170
  warnOnNonStringArea(slideArea);
63070
63171
  }
63172
+ // Asked by every slide, whatever its own mode: the claim goes to the first
63173
+ // slide to RENDER, and a slide that skipped asking would hand it to the one
63174
+ // after it.
63175
+ const opening = container?.claimOpening?.(slideArea);
63176
+ // Kept once rendered (mount="near"): going back builds nothing again, and a
63177
+ // slide being left keeps its content for as long as it is still in the frame.
63178
+ const renderedRef = useRef(false);
63179
+ if (!renderedRef.current && (!container || (mount ?? container.mount) !== "near" || opening || slideArea === container.areaShownNext || container.nearAreas.includes(slideArea))) {
63180
+ renderedRef.current = true;
63181
+ }
63182
+ const rendered = renderedRef.current;
63071
63183
  const answered = Boolean(container?.answeredAreas.includes(slideArea));
63072
63184
  const holdsUntilAnswered = Boolean(required) && !answered;
63073
63185
  const nextIsLocked = Boolean(preventNavNext) || holdsUntilAnswered;
@@ -63117,13 +63229,17 @@ const Slide = ({
63117
63229
  "data-slide-area": slideArea,
63118
63230
  "data-prevent-nav-next": nextIsLocked ? "" : undefined,
63119
63231
  "data-prevent-nav-previous": preventNavPrevious ? "" : undefined
63232
+ // What the container reads to know a slide is waiting for a travel
63233
+ // to come near (see renderNear).
63234
+ ,
63235
+ "data-slide-unrendered": rendered ? undefined : ""
63120
63236
  // A surface one interacts with says what state it is in, the way every
63121
63237
  // other surface does — a dialog carries the very same set.
63122
63238
  // :focus-within is the one a slide is really about: it is what tells
63123
63239
  // the slide holding the keyboard from the ones waiting.
63124
63240
  ,
63125
63241
  pseudoClasses: SLIDE_PSEUDO_CLASSES,
63126
- children: children
63242
+ children: rendered ? children : null
63127
63243
  })
63128
63244
  })
63129
63245
  });
@@ -75640,9 +75756,12 @@ const readRoomAround = ({
75640
75756
  // too — and an item not drawn is taken to weigh what the drawn items at that
75641
75757
  // edge of the window weigh. Items of one kind come together (the one-line
75642
75758
  // items of the past, the cards ahead), and an item misjudged is weighed again
75643
- // once it is drawn (see "after slide"): weighing it at the average of all
75644
- // items would draw the cards next to one-line items at the one-line size, and
75645
- // a few dozen of them.
75759
+ // once it is drawn (see "after slide"): a window that came up short is
75760
+ // extended on the next frame, from what it measured. Weighing an item not drawn
75761
+ // at the average of all items, or at the smallest an item can be so that the
75762
+ // window never comes up short, walks into the cards at the one-line size,
75763
+ // several at a time — the latter measured in wematch: no blank spared, 50–65 ms
75764
+ // tasks while reading down through the cards.
75646
75765
  const EDGE_SAMPLE_ITEM_COUNT = 8;
75647
75766
  const createItemSizeReader = (items, start, end, itemSize) => {
75648
75767
  const sizeByIndex = new Map();
@@ -78127,11 +78246,18 @@ const ListResolved = /*#__PURE__*/createComponentResolver([ListFirstResolver, Li
78127
78246
  * source for is a page (see `<List.Items pageSize>`), whatever either says,
78128
78247
  * so the first picture costs no second request.
78129
78248
  * @param {number} [props.virtualItemSize]
78130
- * The size of one item along the scroll axis, in px, when every item has the
78131
- * same: what the fillers are sized with and what a scroll position is
78132
- * estimated from. Left out, the list measures its items — once when it
78133
- * mounts, again when a popup around it opens, and after each commit while
78134
- * items are held off screen. Given, it never measures.
78249
+ * The room an item not drawn is held at, in px along the scroll axis: what
78250
+ * the fillers hold for each item outside the render window, what a scroll
78251
+ * position inside them is read with, and the least an item on its way takes
78252
+ * (see `renderSkeleton`). Left out, it is the average of the items measured
78253
+ * so far — once when the list mounts, again when a popup around it opens,
78254
+ * and after each commit while items are held off screen. Given, it is the
78255
+ * worst case: the size every item has, or, in a list whose items differ, the
78256
+ * smallest an item can be (a thread of one-line items and cards: the
78257
+ * one-line size) — an item guessed too small builds one more, an item
78258
+ * guessed too big leaves a blank (see docs/scroll.md, "What the list knows,
78259
+ * and what it guesses"). The items drawn are measured either way: the render
78260
+ * window is sized on them.
78135
78261
  * @param {"self"|"parent"|"document"|Element|{current: Element}} [props.scroller="self"]
78136
78262
  * Which box scrolls — and with it, which box the render window follows and
78137
78263
  * which box a scroll position is read from (`onScrolledChange`). `"self"`