@jsenv/navi 0.29.117 → 0.29.119

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.
@@ -26618,6 +26618,49 @@ const RouteUI = ({
26618
26618
  return element;
26619
26619
  };
26620
26620
 
26621
+ /**
26622
+ * Where a route movement is heading, said before its first picture is taken.
26623
+ *
26624
+ * A view transition pairs, across its two pictures, whatever wears the same
26625
+ * name on both — and a name is read off the DOM as it stands when the
26626
+ * transition starts. So whoever names an element for a movement between two
26627
+ * pages (the bar under the current tab of a <Nav>, which glides from one tab
26628
+ * to the next) has to decide whether this movement is one it takes part in
26629
+ * before that reading: on a page the row is not on, its bar must leave with the
26630
+ * page, unnamed.
26631
+ *
26632
+ * Everything that decision needs is on the page itself, except one thing:
26633
+ * where the movement goes. That is what is published here, by whoever starts
26634
+ * the movement — route_transition.jsx for a relation between two pages,
26635
+ * route_travel.jsx for a row a finger pushes — before their transition starts.
26636
+ * The navigation's own announcement (before_routing.js) cannot stand in for
26637
+ * it: a travel a finger sets off starts its transition first and navigates
26638
+ * from inside it.
26639
+ *
26640
+ * Only one movement plays at a time (one view transition per document), which
26641
+ * is what lets both say it in one place. Taken down by the one that put it up,
26642
+ * and by it alone: a movement ending after another has replaced it must not
26643
+ * wipe what the new one stands on.
26644
+ */
26645
+
26646
+
26647
+ const [publishDestination, observeTransitionDestination] = createPubSub();
26648
+
26649
+ let destinationOwner = null;
26650
+
26651
+ const holdTransitionDestination = (owner, url) => {
26652
+ destinationOwner = owner;
26653
+ publishDestination(new URL(url, window.location.href).href);
26654
+ };
26655
+
26656
+ const releaseTransitionDestination = (owner) => {
26657
+ if (owner !== destinationOwner) {
26658
+ return;
26659
+ }
26660
+ destinationOwner = null;
26661
+ publishDestination(null);
26662
+ };
26663
+
26621
26664
  /**
26622
26665
  * The window two pages are seen through while one replaces the other, measured
26623
26666
  * once and published for the length of the movement.
@@ -27498,6 +27541,7 @@ const rebuildWatcher = () => {
27498
27541
  }
27499
27542
  beginTransition({
27500
27543
  page: pages[index],
27544
+ url: navigationUrl,
27501
27545
  // Which way it plays: what the navigation itself said first — the link
27502
27546
  // being pressed is where the way the app is being walked is known — then
27503
27547
  // the relation, and forward for a navigation that asked for a movement
@@ -27518,11 +27562,12 @@ const rebuildWatcher = () => {
27518
27562
 
27519
27563
  // What plays when no relation matched (see defineRouteDefaultTransition), what
27520
27564
  // the navigation now landing asked for on its own (see readNavigationRequest),
27521
- // and whether it found an answer already — a relation's transition, a "none",
27522
- // a RouteTravel travel. The last two are read at the start of every
27523
- // navigation, so they are always about the latest one.
27565
+ // where it goes, and whether it found an answer already — a relation's
27566
+ // transition, a "none", a RouteTravel travel. The last three are read at the
27567
+ // start of every navigation, so they are always about the latest one.
27524
27568
  let defaultTransition = null;
27525
27569
  let navigationRequest = null;
27570
+ let navigationUrl = null;
27526
27571
  let navigationAnimated = false;
27527
27572
 
27528
27573
  // The two ends of every navigation, watched from here on. The picture of the
@@ -27534,6 +27579,7 @@ let navigationAnimated = false;
27534
27579
  observeBeforeRouting(details => {
27535
27580
  navigationAnimated = false;
27536
27581
  navigationRequest = readNavigationRequest(details);
27582
+ navigationUrl = details.url;
27537
27583
  if (relations.length === 0 && !defaultTransition && !navigationRequest) {
27538
27584
  return;
27539
27585
  }
@@ -27541,9 +27587,11 @@ observeBeforeRouting(details => {
27541
27587
  });
27542
27588
  observeAfterRouting(() => {
27543
27589
  const request = navigationRequest;
27590
+ const url = navigationUrl;
27544
27591
  // Read here and dropped here: a request answers for the navigation it was
27545
27592
  // made on, and the next one is back to the relations.
27546
27593
  navigationRequest = null;
27594
+ navigationUrl = null;
27547
27595
  if (!navigationAnimated && (request || defaultTransition)) {
27548
27596
  const {
27549
27597
  type,
@@ -27552,6 +27600,7 @@ observeAfterRouting(() => {
27552
27600
  if (type !== "none") {
27553
27601
  beginTransition({
27554
27602
  page: null,
27603
+ url,
27555
27604
  // A default has no direction: nothing says which of two arbitrary
27556
27605
  // pages is before the other, and the attribute is then worn empty —
27557
27606
  // present for whoever keys on "one of ours is playing", silent on the
@@ -27599,6 +27648,7 @@ const findRelation = (fromPage, toPage) => {
27599
27648
  let currentTransition = null;
27600
27649
  const beginTransition = ({
27601
27650
  page,
27651
+ url,
27602
27652
  direction,
27603
27653
  type,
27604
27654
  duration
@@ -27615,6 +27665,9 @@ const beginTransition = ({
27615
27665
  }
27616
27666
  const transition = {};
27617
27667
  currentTransition = transition;
27668
+ // Said before the picture is taken: whoever names something for a movement
27669
+ // between two pages decides on it now (see transition_destination.js).
27670
+ holdTransitionDestination(transition, url);
27618
27671
  documentElement.setAttribute(TRANSITION_ATTRIBUTE, direction);
27619
27672
  if (type) {
27620
27673
  documentElement.setAttribute(TRANSITION_TYPE_ATTRIBUTE, type);
@@ -27719,6 +27772,7 @@ const beginTransition = ({
27719
27772
  documentElement.removeAttribute(TRANSITION_TYPE_ATTRIBUTE);
27720
27773
  documentElement.removeAttribute(TRANSITION_TARGET_ATTRIBUTE);
27721
27774
  releaseTransitionWindow(transition);
27775
+ releaseTransitionDestination(transition);
27722
27776
  if (restoreDuration) {
27723
27777
  restoreDuration();
27724
27778
  }
@@ -28380,8 +28434,12 @@ const RouteTravel = ({
28380
28434
  travelRef.current = travel;
28381
28435
  // Taken before the picture is: the browser reads the name off the DOM as it
28382
28436
  // stands when the transition starts, and this box is only a picture of its
28383
- // own for as long as it is the one travelling.
28437
+ // own for as long as it is the one travelling. Where it travels to is said
28438
+ // at the same moment and for the same reason: whoever names something for
28439
+ // a movement between two pages decides on it now (see
28440
+ // transition_destination.js).
28384
28441
  nameForTravel(elementRef.current);
28442
+ holdTransitionDestination(travel, page.route.buildUrl(page.params));
28385
28443
  document.documentElement.setAttribute(TRAVEL_ATTRIBUTE, direction);
28386
28444
  document.documentElement.setAttribute(TRAVEL_AXIS_ATTRIBUTE, axis);
28387
28445
  if (scrub) {
@@ -28737,6 +28795,7 @@ const RouteTravel = ({
28737
28795
  document.documentElement.removeAttribute(DRAGGED_ATTRIBUTE);
28738
28796
  document.documentElement.removeAttribute(TURNED_ATTRIBUTE);
28739
28797
  releaseTransitionWindow(travel);
28798
+ releaseTransitionDestination(travel);
28740
28799
  }
28741
28800
  };
28742
28801
 
@@ -38724,8 +38783,7 @@ const BinderItemContext = createContext(null);
38724
38783
 
38725
38784
  /**
38726
38785
  * What a <Link> learns from the <Nav> around it: where to draw the bar that
38727
- * says "you are here", the name under which the browser is to recognise that
38728
- * bar from one page to the next, and — for a row of tabs that are slides — which
38786
+ * says "you are here", and for a row of tabs that are slides — which
38729
38787
  * <SlideContainer> they are about and which of its slides is on screen (see
38730
38788
  * nav.jsx).
38731
38789
  */
@@ -39421,14 +39479,7 @@ const LinkPlain = props => {
39421
39479
  // around this link.
39422
39480
  const currentIndicatorAsked = currentIndicator ?? nav?.currentIndicator;
39423
39481
  const currentIndicatorPosition = currentIndicatorAsked === true ? "bottom" : currentIndicatorAsked;
39424
- const currentIndicatorEl = currentIndicatorPosition === "left" || currentIndicatorPosition === "right" || currentIndicatorPosition === "top" || currentIndicatorPosition === "bottom" ? jsx(LinkCurrentIndicator
39425
- // Only the bar one can actually see carries the row's name, because a
39426
- // name belongs to one element at a time and every tab holds a bar. The
39427
- // browser then has the same thing in two places from one page to the
39428
- // next, and moves it — which is the whole of "the bar slides".
39429
- , {
39430
- viewTransitionName: innerCurrent ? nav?.indicatorName : null
39431
- }) : null;
39482
+ const currentIndicatorEl = currentIndicatorPosition === "left" || currentIndicatorPosition === "right" || currentIndicatorPosition === "top" || currentIndicatorPosition === "bottom" ? jsx(LinkCurrentIndicator, {}) : null;
39432
39483
  const {
39433
39484
  onClick,
39434
39485
  preventDefault
@@ -39547,14 +39598,12 @@ const LinkPlain = props => {
39547
39598
  children: [startIconEl, innerChildren, endIconEl]
39548
39599
  });
39549
39600
  };
39550
- const LinkCurrentIndicator = ({
39551
- viewTransitionName
39552
- }) => {
39601
+
39602
+ // Named by the <Nav> around the link when the link is current, from its CSS:
39603
+ // that is what makes the bar glide from one tab to the next (see nav.jsx).
39604
+ const LinkCurrentIndicator = () => {
39553
39605
  return jsx("span", {
39554
- className: "navi_current_indicator",
39555
- style: viewTransitionName ? {
39556
- viewTransitionName
39557
- } : undefined
39606
+ className: "navi_current_indicator"
39558
39607
  });
39559
39608
  };
39560
39609
  markAsOutsideTextFlow(LinkCurrentIndicator);
@@ -39564,6 +39613,9 @@ installImportMetaCssBuild(import.meta);/**
39564
39613
  * https://dribbble.com/search/tabs
39565
39614
  */
39566
39615
  let navCount = 0;
39616
+ // Worn by a nav of routes while a route movement between two of its tabs is
39617
+ // pictured (see markIndicatorTakesPart, and the CSS below for what it decides).
39618
+ const BETWEEN_TABS_ATTRIBUTE = "data-nav-between-tabs";
39567
39619
  const css$V = /* css */`
39568
39620
  @layer navi {
39569
39621
  .navi_nav {
@@ -39643,6 +39695,30 @@ const css$V = /* css */`
39643
39695
  }
39644
39696
  }
39645
39697
 
39698
+ /* The bar under the current tab of a nav made of routes is NAMED: a change
39699
+ played as a view transition then finds it on both pictures and moves it
39700
+ from the tab it was under to the tab it is under, which is all "the bar
39701
+ slides" is. The name is the row's (--nav-indicator-name, absent when the
39702
+ row asked for no slide) and only the bar one can see wears it: every tab
39703
+ holds a bar, and a name belongs to one element at a time. */
39704
+ .navi_nav .navi_link[data-href-current] .navi_current_indicator {
39705
+ view-transition-name: var(--nav-indicator-name);
39706
+ }
39707
+ /* Named for a movement it takes part in, and for that alone. While the PAGES
39708
+ are the ones moving — a route transition, a route travel — a name lifts the
39709
+ bar out of its page's picture into a picture of its own, and a picture of
39710
+ its own is precisely what does not travel: it stands where it was captured,
39711
+ fading, while the row slides away under it. Right when the row is on both
39712
+ sides and the bar has a tab to glide to; wrong anywhere else, where the bar
39713
+ must leave or arrive with its row. So the name is dropped unless the
39714
+ movement goes from a tab of this row to another one of its tabs (see
39715
+ markIndicatorTakesPart). */
39716
+ :root:is([data-navi-route-transition], [data-navi-route-travel])
39717
+ .navi_nav:not([${BETWEEN_TABS_ATTRIBUTE}])
39718
+ .navi_current_indicator {
39719
+ view-transition-name: none;
39720
+ }
39721
+
39646
39722
  .navi_nav {
39647
39723
  display: flex;
39648
39724
  width: fit-content;
@@ -39824,9 +39900,12 @@ const positionOfCurrentIndicator = (currentIndicator, vertical) => {
39824
39900
  * does so by being NAMED, which is all the browser needs: any change played as
39825
39901
  * a view transition animates it on the same clock as everything else in that
39826
39902
  * transition. Inside a `RouteTravel` that means it follows the pages, and the
39827
- * thumb dragging them, without either of them being told about the other. For
39828
- * a nav made of slides (`slideContainer`) the bar is one element for the whole
39829
- * row, and it reads the travel the container publishes.
39903
+ * thumb dragging them, without either of them being told about the other. It
39904
+ * is named for a movement between two tabs of the row and for nothing else:
39905
+ * when the pages move and the row is on one side only, the bar leaves or
39906
+ * arrives with its row. For a nav made of slides (`slideContainer`) the bar
39907
+ * is one element for the whole row, and it reads the travel the container
39908
+ * publishes.
39830
39909
  * @param {string} [props.slideContainer] - the id of a `<SlideContainer>` these
39831
39910
  * tabs are about: each one says which slide it is (`<Link slide="…">`), the
39832
39911
  * container says which one is on screen, and pressing a tab travels there.
@@ -39959,16 +40038,47 @@ const Nav = ({
39959
40038
  useLayoutEffect(() => {
39960
40039
  paintIndicatorGeometry();
39961
40040
  });
40041
+
40042
+ // Whether the bar keeps its name for the route movement about to be pictured
40043
+ // (see the CSS above). Written on the DOM as the movement is announced rather
40044
+ // than rendered: rendering is held from a navigation's first word until the
40045
+ // movement's first picture is taken (see rendering_hold.js), so a render
40046
+ // would land on the second picture with the first already taken.
40047
+ useLayoutEffect(() => {
40048
+ const navElement = navRef.current;
40049
+ const markIndicatorTakesPart = destinationUrl => {
40050
+ if (destinationUrl === null) {
40051
+ navElement.removeAttribute(BETWEEN_TABS_ATTRIBUTE);
40052
+ return;
40053
+ }
40054
+ // From a tab of this row: the row still shows the page being left, and
40055
+ // the tab it is on. To a tab of this row: one of its links aims at the
40056
+ // page the movement goes to. A row mounted while the movement plays hears
40057
+ // nothing here and stays unnamed, rightly: its bar is on the second
40058
+ // picture alone, with nothing on the first to glide from.
40059
+ const fromTab = navElement.querySelector("[data-href-current]");
40060
+ let toTab = null;
40061
+ for (const linkElement of navElement.querySelectorAll("a[href]")) {
40062
+ if (linkElement.href === destinationUrl) {
40063
+ toTab = linkElement;
40064
+ break;
40065
+ }
40066
+ }
40067
+ if (fromTab && toTab) {
40068
+ navElement.setAttribute(BETWEEN_TABS_ATTRIBUTE, "");
40069
+ } else {
40070
+ navElement.removeAttribute(BETWEEN_TABS_ATTRIBUTE);
40071
+ }
40072
+ };
40073
+ return observeTransitionDestination(markIndicatorTakesPart);
40074
+ }, []);
39962
40075
  const navContextValue = useMemo(() => ({
39963
40076
  // The bar belongs to the row itself when the tabs are slides, so the
39964
40077
  // links draw none of their own.
39965
40078
  currentIndicator: slideContainer ? undefined : currentIndicator,
39966
- // Read by the link that is current, and by it alone: a name belongs to
39967
- // one element at a time, and the bar exists in every tab.
39968
- indicatorName: currentIndicatorSlides ? indicatorNameRef.current : null,
39969
40079
  slideContainer,
39970
40080
  currentSlideArea
39971
- }), [currentIndicator, currentIndicatorSlides, slideContainer, currentSlideArea]);
40081
+ }), [currentIndicator, slideContainer, currentSlideArea]);
39972
40082
  children = toChildArray(children);
39973
40083
  return jsxs(Box, {
39974
40084
  as: "nav",
@@ -39998,6 +40108,13 @@ const Nav = ({
39998
40108
  expandY: expandY,
39999
40109
  spacing: spacing,
40000
40110
  ...props,
40111
+ // The name the bar of the current tab wears, handed to the CSS above
40112
+ // rather than to the links: which bar wears it, and when, is decided
40113
+ // there.
40114
+ style: currentIndicatorSlides && !slideContainer ? {
40115
+ ...props.style,
40116
+ "--nav-indicator-name": indicatorNameRef.current
40117
+ } : props.style,
40001
40118
  styleCSSVars: NavStyleCSSVars,
40002
40119
  children: [indicatorPosition && jsx("span", {
40003
40120
  className: "navi_nav_indicator"