@jsenv/navi 0.29.4 → 0.29.5

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.
@@ -6422,6 +6422,16 @@ const applyOnTwoProps = (propA, propB) => {
6422
6422
  };
6423
6423
  };
6424
6424
 
6425
+ // How much of the free space an expanding item claims next to its siblings:
6426
+ // expand={30} between four expand={18} is the wide one in the middle. A bare
6427
+ // `expand` claims one share, like everyone else asking for their part.
6428
+ const expandWeight = (value) => {
6429
+ if (value === true || value === "") {
6430
+ return 1;
6431
+ }
6432
+ return value;
6433
+ };
6434
+
6425
6435
  const LAYOUT_PROPS = {
6426
6436
  // all are handled by navi-attributes
6427
6437
  inline: () => {},
@@ -6529,7 +6539,7 @@ const DIMENSION_PROPS = {
6529
6539
  return { flexGrow: 1 };
6530
6540
  }
6531
6541
  // Parent is flex-x: grow as flex item
6532
- return { flexGrow: 1, flexBasis: "0%" };
6542
+ return { flexGrow: expandWeight(value), flexBasis: "0%" };
6533
6543
  }
6534
6544
  if (parentBoxFlow === "flex-y" || parentBoxFlow === "inline-flex-y") {
6535
6545
  return {
@@ -6553,7 +6563,7 @@ const DIMENSION_PROPS = {
6553
6563
  return { flexGrow: 1 };
6554
6564
  }
6555
6565
  // Parent is flex-y: grow as flex item
6556
- return { flexGrow: 1, flexBasis: "0%" };
6566
+ return { flexGrow: expandWeight(value), flexBasis: "0%" };
6557
6567
  }
6558
6568
  if (parentBoxFlow === "flex-x" || parentBoxFlow === "inline-flex-x") {
6559
6569
  return {
@@ -7834,6 +7844,11 @@ Object.assign(PSEUDO_CLASSES, {
7834
7844
  ":visited": {
7835
7845
  attribute: "data-visited",
7836
7846
  },
7847
+ // Written by whoever knows the current url — a Link from its href, a Button
7848
+ // from its route — so it lives here rather than with one of them.
7849
+ ":-navi-href-current": {
7850
+ attribute: "data-href-current",
7851
+ },
7837
7852
  });
7838
7853
  const definePseudoClass = (pseudoClass, definition) => {
7839
7854
  PSEUDO_CLASSES[pseudoClass] = definition;
@@ -30354,9 +30369,6 @@ Object.assign(PSEUDO_CLASSES, {
30354
30369
  },
30355
30370
  ":-navi-href-anchor": {
30356
30371
  attribute: "data-href-anchor"
30357
- },
30358
- ":-navi-href-current": {
30359
- attribute: "data-href-current"
30360
30372
  }
30361
30373
  });
30362
30374
 
@@ -31958,15 +31970,16 @@ installImportMetaCssBuild(import.meta);/**
31958
31970
  * 2. **It gives its space back.** Being fixed it covers the content: without a
31959
31971
  * reserve the end of a long page stays under it, unreachable. It publishes
31960
31972
  * what it takes on <html> — see fixed_bar_space.js.
31961
- * 3. **It reaches under the notch.** `env(safe-area-inset-*)` pushes the
31962
- * content in while the background keeps running to the edge of the screen.
31963
- * Note that every `env(safe-area-inset-*)` is 0 unless the page asks for
31964
- * it: `<meta name="viewport" content="…, viewport-fit=cover">`.
31965
- * 4. **Its hairline is a box-shadow, not a border.** `width`/`height` is the
31966
- * bar's content box, so the safe-area padding adds outside it: the size you
31967
- * asked for is the size the content gets, whatever the device does. A real
31968
- * border would eat into it; a box-shadow draws the identical line and stays
31969
- * out of layout.
31973
+ * 3. **It reaches under the notch.** `env(safe-area-inset-*)` is padding, so
31974
+ * the content is pushed in while the background keeps running to the edge of
31975
+ * the screen. Across the bar that inset is added to `width`/`height` too, so
31976
+ * the size asked for is the size the content gets whatever the device does;
31977
+ * along the bar it adds to the padding asked for. Note that every
31978
+ * `env(safe-area-inset-*)` is 0 unless the page asks for it:
31979
+ * `<meta name="viewport" content="…, viewport-fit=cover">`.
31980
+ * 4. **Its hairline is a box-shadow, not a border.** A real border would eat
31981
+ * into the size; a box-shadow draws the identical line and stays out of
31982
+ * layout.
31970
31983
  */
31971
31984
  const css$M = /* css */`
31972
31985
  @layer navi {
@@ -31977,11 +31990,11 @@ const css$M = /* css */`
31977
31990
  --navi-fixed-bar-background: var(--navi-surface-color);
31978
31991
  --navi-fixed-bar-border-width: 1px;
31979
31992
  --navi-fixed-bar-border-color: var(--navi-separator-color-default);
31980
- /* Along the bar only: room so its content never touches the edge of the
31981
- screen. Across it there is nothing to add that direction is what the
31982
- width/height prop names, and padding there would only make the bar
31983
- thicker than the size asked for. */
31984
- --navi-fixed-bar-padding: var(--navi-s);
31993
+ /* Along the bar only across it there is nothing to add, that direction
31994
+ is what the width/height prop names. None by default: a row of items
31995
+ sharing the whole strip is as common as a toolbar wanting air at its
31996
+ ends, and only the second can ask. */
31997
+ --navi-fixed-bar-padding: 0px;
31985
31998
  }
31986
31999
  }
31987
32000
 
@@ -31991,58 +32004,69 @@ const css$M = /* css */`
31991
32004
  position: fixed;
31992
32005
  z-index: 1;
31993
32006
  display: flex;
31994
- /* content-box against an app that is border-box everywhere else: the size
31995
- stays bare and the safe-area padding adds OUTSIDE it, so a bar asked for
31996
- 56px still shows 56px of bar under the notch instead of 56px minus it. */
31997
- box-sizing: content-box;
32007
+ box-sizing: border-box;
31998
32008
  margin: auto;
31999
32009
  align-items: center;
32000
32010
  background: var(--navi-fixed-bar-background);
32001
32011
 
32012
+ /* Along the bar, the two insets that can bite into it there are added to
32013
+ the padding asked for: in landscape the notch is on a side, and a top bar
32014
+ whose padding ignored it would put its first item under it. */
32002
32015
  &[data-area="top"],
32003
32016
  &[data-area="bottom"] {
32004
32017
  right: 0;
32005
32018
  left: 0;
32006
32019
  /* No width of its own: pinned to both edges, the used width absorbs the
32007
- padding instead of being inflated by it which a content-box
32008
- "width: 100%" would do. max-width then narrows it and the auto
32009
- margins re-center it. */
32020
+ padding instead of being inflated by it. max-width then narrows it and
32021
+ the auto margins re-center it. */
32010
32022
  max-width: var(--navi-fixed-bar-max-width);
32011
- height: var(--navi-fixed-bar-height);
32012
- padding-right: var(--navi-fixed-bar-padding);
32013
- padding-left: var(--navi-fixed-bar-padding);
32023
+ padding-right: calc(
32024
+ var(--navi-fixed-bar-padding) + env(safe-area-inset-right)
32025
+ );
32026
+ padding-left: calc(
32027
+ var(--navi-fixed-bar-padding) + env(safe-area-inset-left)
32028
+ );
32014
32029
  }
32015
32030
  &[data-area="left"],
32016
32031
  &[data-area="right"] {
32017
32032
  top: 0;
32018
32033
  bottom: 0;
32019
- width: var(--navi-fixed-bar-width);
32020
- padding-top: var(--navi-fixed-bar-padding);
32021
- padding-bottom: var(--navi-fixed-bar-padding);
32034
+ padding-top: calc(
32035
+ var(--navi-fixed-bar-padding) + env(safe-area-inset-top)
32036
+ );
32037
+ padding-bottom: calc(
32038
+ var(--navi-fixed-bar-padding) + env(safe-area-inset-bottom)
32039
+ );
32022
32040
  flex-direction: column;
32023
32041
  }
32024
32042
 
32025
- /* The inset on this edge alone: the one the bar is pinned to. */
32043
+ /* Across the bar, the inset of the edge it is pinned to is padding AND is
32044
+ added to the size: the background then runs under the notch while the
32045
+ content keeps the whole width/height asked for. */
32026
32046
  &[data-area="top"] {
32027
32047
  top: 0;
32048
+ height: calc(var(--navi-fixed-bar-height) + env(safe-area-inset-top));
32028
32049
  padding-top: env(safe-area-inset-top);
32029
32050
  box-shadow: 0 var(--navi-fixed-bar-border-width) 0
32030
32051
  var(--navi-fixed-bar-border-color);
32031
32052
  }
32032
32053
  &[data-area="bottom"] {
32033
32054
  bottom: 0;
32055
+ height: calc(var(--navi-fixed-bar-height) + env(safe-area-inset-bottom));
32034
32056
  padding-bottom: env(safe-area-inset-bottom);
32035
32057
  box-shadow: 0 calc(-1 * var(--navi-fixed-bar-border-width)) 0
32036
32058
  var(--navi-fixed-bar-border-color);
32037
32059
  }
32038
32060
  &[data-area="left"] {
32039
32061
  left: 0;
32062
+ width: calc(var(--navi-fixed-bar-width) + env(safe-area-inset-left));
32040
32063
  padding-left: env(safe-area-inset-left);
32041
32064
  box-shadow: var(--navi-fixed-bar-border-width) 0 0
32042
32065
  var(--navi-fixed-bar-border-color);
32043
32066
  }
32044
32067
  &[data-area="right"] {
32045
32068
  right: 0;
32069
+ width: calc(var(--navi-fixed-bar-width) + env(safe-area-inset-right));
32046
32070
  padding-right: env(safe-area-inset-right);
32047
32071
  box-shadow: calc(-1 * var(--navi-fixed-bar-border-width)) 0 0
32048
32072
  var(--navi-fixed-bar-border-color);
@@ -32053,6 +32077,11 @@ const css$M = /* css */`
32053
32077
  }
32054
32078
  `;
32055
32079
  const FixedBarStyleCSSVars = {
32080
+ // width/height go through the vars the size calc reads: written as plain
32081
+ // inline styles they would land on the border box and the safe-area inset
32082
+ // would eat into the size asked for.
32083
+ width: "--navi-fixed-bar-width",
32084
+ height: "--navi-fixed-bar-height",
32056
32085
  maxWidth: "--navi-fixed-bar-max-width",
32057
32086
  padding: "--navi-fixed-bar-padding",
32058
32087
  background: "--navi-fixed-bar-background",
@@ -32076,8 +32105,8 @@ const FixedBarStyleCSSVars = {
32076
32105
  * the window it is pinned to.
32077
32106
  * @param {string|number} [props.height] - For a bar on the top or bottom
32078
32107
  * @param {string|number} [props.width] - …and for one on a side. The safe-area
32079
- * inset is NOT included in it: the inset is added outside, and the content
32080
- * keeps the size asked for.
32108
+ * inset is NOT part of it: it is added on top, so the content keeps the size
32109
+ * asked for.
32081
32110
  * @param {boolean} [props.border=true] - The hairline on the content side.
32082
32111
  * Drawn with a box-shadow so it never eats into the size; give it a
32083
32112
  * `borderWidth`/`borderColor`, or `border={false}` for none.
@@ -32095,10 +32124,10 @@ const FixedBar = ({
32095
32124
  props.ref = props.ref || defaultRef;
32096
32125
  // Whichever of width/height crosses the edge the bar sits on is what the
32097
32126
  // content has to be given back — and the bar's border box already IS that:
32098
- // the size it was given, plus the safe-area padding added outside it.
32099
- // Measured rather than rebuilt as a calc() expression, so a size coming from
32100
- // anywhere — a prop, a theme variable, the content itself — is reserved just
32101
- // the same, and each `env()` inset stays the browser's business alone.
32127
+ // the size it was given plus the inset of that edge. Measured rather than
32128
+ // rebuilt as a calc() expression, so a size coming from anywhere — a prop, a
32129
+ // theme variable, the content itself — is reserved just the same, and each
32130
+ // `env()` inset stays the browser's business alone.
32102
32131
  const vertical = area === "left" || area === "right";
32103
32132
  const {
32104
32133
  ref
@@ -35220,6 +35249,7 @@ const ButtonWithRoute = props => {
35220
35249
  route,
35221
35250
  routeParams,
35222
35251
  children,
35252
+ pseudoState,
35223
35253
  ...rest
35224
35254
  } = props;
35225
35255
  const url = route.buildUrl(routeParams);
@@ -35228,9 +35258,15 @@ const ButtonWithRoute = props => {
35228
35258
  } = useRouteStatus(route);
35229
35259
  const paramsAreMatching = route.matchesParams(routeParams);
35230
35260
  const linkMatching = matching && paramsAreMatching;
35261
+
35262
+ // Merged into whatever the caller already holds: a button can be forced into
35263
+ // a state for a demo and still learn its own current-ness from its route.
35231
35264
  return jsx(Next, {
35232
35265
  href: url,
35233
- "data-href-current": linkMatching ? "" : undefined,
35266
+ pseudoState: {
35267
+ ...pseudoState,
35268
+ ":-navi-href-current": linkMatching
35269
+ },
35234
35270
  ...rest,
35235
35271
  children: children || route.buildRelativeUrl(routeParams)
35236
35272
  });
@@ -35272,6 +35308,11 @@ installImportMetaCssBuild(import.meta);const css$E = /* css */`
35272
35308
  black
35273
35309
  );
35274
35310
  --button-color-hover: var(--button-color);
35311
+ /* Current: the button points at the page one is already on. Nothing by
35312
+ default — it is a nav item that wants to say so, not every button. */
35313
+ --button-border-color-current: var(--button-border-color);
35314
+ --button-background-color-current: var(--button-background-color);
35315
+ --button-color-current: var(--button-color);
35275
35316
  /* Pressed */
35276
35317
  --button-border-color-pressed: color-mix(
35277
35318
  in srgb,
@@ -35315,6 +35356,13 @@ installImportMetaCssBuild(import.meta);const css$E = /* css */`
35315
35356
  .navi_button {
35316
35357
  --x-button-outline-offset: var(--button-outline-offset);
35317
35358
  --x-button-border-color: var(--button-border-color);
35359
+ /* The shorthand wins over the parts when it is given, which is what makes
35360
+ border="none" remove the line: the parts alone can only ever describe
35361
+ a border, never the absence of one. */
35362
+ --x-button-border: var(
35363
+ --button-border,
35364
+ var(--button-border-width) solid var(--x-button-border-color)
35365
+ );
35318
35366
  --x-button-background: var(--button-background);
35319
35367
  --x-button-background-color: var(--button-background-color);
35320
35368
  --x-button-color: var(--button-color);
@@ -35378,9 +35426,7 @@ installImportMetaCssBuild(import.meta);const css$E = /* css */`
35378
35426
  --x-button-background-color,
35379
35427
  var(--x-button-background)
35380
35428
  );
35381
- border-width: var(--button-border-width);
35382
- border-style: solid;
35383
- border-color: var(--x-button-border-color);
35429
+ border: var(--x-button-border);
35384
35430
  border-radius: inherit;
35385
35431
  outline-width: var(--button-outline-width);
35386
35432
  outline-color: var(--button-outline-color);
@@ -35401,6 +35447,12 @@ installImportMetaCssBuild(import.meta);const css$E = /* css */`
35401
35447
  }
35402
35448
  }
35403
35449
 
35450
+ /* Current */
35451
+ &[data-href-current] {
35452
+ --x-button-border-color: var(--button-border-color-current);
35453
+ --x-button-background-color: var(--button-background-color-current);
35454
+ --x-button-color: var(--button-color-current);
35455
+ }
35404
35456
  /* Hover */
35405
35457
  &[data-hover] {
35406
35458
  --x-button-border-color: var(--button-border-color-hover);
@@ -35547,6 +35599,21 @@ installImportMetaCssBuild(import.meta);const css$E = /* css */`
35547
35599
  --x-button-background-color: transparent;
35548
35600
  }
35549
35601
  }
35602
+ /* Last word on the shrink, over whatever the variant decided: the variant
35603
+ guesses from how the button is drawn, and that guess is wrong as soon as
35604
+ the content is a box of its own — a discrete button holding a filled
35605
+ pill reads as a button being pressed, not as its content flinching. */
35606
+ &[data-press-effect="scale"][data-pressed] {
35607
+ .navi_button_content {
35608
+ transform: scale(0.9);
35609
+ }
35610
+ }
35611
+ &[data-press-effect="none"][data-pressed] {
35612
+ .navi_button_content {
35613
+ transform: none;
35614
+ }
35615
+ }
35616
+
35550
35617
  &[data-icon] {
35551
35618
  --button-padding: 0;
35552
35619
  display: inline-flex;
@@ -35607,6 +35674,7 @@ const ButtonUI = props => {
35607
35674
  rel,
35608
35675
  // visual
35609
35676
  variant,
35677
+ pressEffect,
35610
35678
  icon,
35611
35679
  cta,
35612
35680
  spacing
@@ -35620,18 +35688,31 @@ const ButtonUI = props => {
35620
35688
  children
35621
35689
  } = buttonControlHostProps;
35622
35690
  const loading = basePseudoState[":-navi-loading"];
35691
+
35692
+ // subscribe to document url to re-render and re-compute getHrefTargetInfo
35693
+ useDocumentUrl();
35623
35694
  const isLink = href !== undefined;
35624
35695
  let as = "button";
35625
35696
  let innerTarget;
35626
35697
  let innerRel;
35698
+ let innerCurrent;
35627
35699
  if (isLink) {
35628
35700
  as = "a";
35629
35701
  const {
35630
- isSameSite
35702
+ isSameSite,
35703
+ isCurrent
35631
35704
  } = getHrefTargetInfo(href);
35632
35705
  innerTarget = target === undefined ? isSameSite ? undefined : "_blank" : target;
35633
35706
  innerRel = rel === undefined ? isSameSite ? undefined : "noopener noreferrer" : rel;
35707
+ innerCurrent = isCurrent;
35634
35708
  }
35709
+ // For a button that has only an href: nothing else knows it points at the
35710
+ // page one is on. A route says so through `pseudoState`, which Box lays over
35711
+ // this one.
35712
+ buttonControlHostProps.basePseudoState = {
35713
+ ...basePseudoState,
35714
+ ":-navi-href-current": innerCurrent
35715
+ };
35635
35716
  const visualSelector = ".navi_button_content";
35636
35717
  useAccentColorAttributes(ref, null, {
35637
35718
  elementSelector: visualSelector
@@ -35651,6 +35732,7 @@ const ButtonUI = props => {
35651
35732
  type: "button",
35652
35733
  spacing: undefined,
35653
35734
  cta: undefined,
35735
+ pressEffect: undefined,
35654
35736
  ref: ref,
35655
35737
  as: as,
35656
35738
  href: href,
@@ -35680,6 +35762,7 @@ const ButtonUI = props => {
35680
35762
  e.preventDefault();
35681
35763
  },
35682
35764
  "data-variant": variant,
35765
+ "data-press-effect": pressEffect,
35683
35766
  "data-icon": icon ? "" : undefined,
35684
35767
  "data-cta": cta ? "" : undefined,
35685
35768
  "data-callout-arrow-x": "center"
@@ -35741,6 +35824,11 @@ const ButtonStyleCSSVars = {
35741
35824
  ":-navi-pressed": {
35742
35825
  borderColor: "--button-border-color-pressed"
35743
35826
  },
35827
+ ":-navi-href-current": {
35828
+ backgroundColor: "--button-background-color-current",
35829
+ borderColor: "--button-border-color-current",
35830
+ color: "--button-color-current"
35831
+ },
35744
35832
  ":read-only": {
35745
35833
  backgroundColor: "--button-background-color-readonly",
35746
35834
  borderColor: "--button-border-color-readonly",
@@ -35752,7 +35840,7 @@ const ButtonStyleCSSVars = {
35752
35840
  color: "--button-color-disabled"
35753
35841
  }
35754
35842
  };
35755
- const ButtonPseudoClasses = [":hover", ":active", ":-navi-pressed", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading"];
35843
+ const ButtonPseudoClasses = [":-navi-href-current", ":hover", ":active", ":-navi-pressed", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading"];
35756
35844
  const ButtonPseudoElements = ["::-navi-loader"];
35757
35845
  const ButtonShadow = () => {
35758
35846
  return jsx("span", {