@jsenv/navi 0.29.455 → 0.29.457

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.
@@ -25189,10 +25189,9 @@ const useKeyboardShortcuts = (
25189
25189
  );
25190
25190
  shortcut.action = useAction(shortcut.action);
25191
25191
  }
25192
- const onKeyDown = createOnKeyDownForShortcutArray(
25193
- shortcuts,
25194
- shortcutActionIsBusyRef,
25195
- );
25192
+ const onKeyDown = createOnKeyDownForShortcutArray(shortcuts, {
25193
+ busyRef: shortcutActionIsBusyRef,
25194
+ });
25196
25195
  useEffect(() => {
25197
25196
  const element = elementRef.current;
25198
25197
  if (!element) {
@@ -25210,7 +25209,10 @@ const useKeyboardShortcuts = (
25210
25209
  }, [shortcutDeps]);
25211
25210
  };
25212
25211
 
25213
- const createOnKeyDownForShortcutArray = (shortcuts, busyRef) => {
25212
+ const createOnKeyDownForShortcutArray = (
25213
+ shortcuts,
25214
+ { busyRef, ownsTarget } = {},
25215
+ ) => {
25214
25216
  const shortcutsCopy = [];
25215
25217
  for (const shortcutCandidate of shortcuts) {
25216
25218
  shortcutsCopy.push({
@@ -25256,10 +25258,20 @@ const createOnKeyDownForShortcutArray = (shortcuts, busyRef) => {
25256
25258
  }
25257
25259
 
25258
25260
  return (keyboardEvent) => {
25259
- return applyKeyboardShortcuts(shortcutsCopy, keyboardEvent);
25261
+ return applyKeyboardShortcuts(shortcutsCopy, keyboardEvent, {
25262
+ ownsTarget,
25263
+ });
25260
25264
  };
25261
25265
  };
25262
- const createOnKeyDownForShortcuts = (shortcuts) => {
25266
+ /**
25267
+ * @param {object} shortcuts
25268
+ * @param {object} [options]
25269
+ * @param {(target: Element) => boolean} [options.ownsTarget] Whether a
25270
+ * descendant the key was pressed in is one the shortcuts speak for, so its
25271
+ * native behavior (the caret, typing) is theirs to take. Without it only the
25272
+ * listening element itself is; a descendant keeps every key it has a use for.
25273
+ */
25274
+ const createOnKeyDownForShortcuts = (shortcuts, { ownsTarget } = {}) => {
25263
25275
  const shortcutsArray = [];
25264
25276
  for (const key of Object.keys(shortcuts)) {
25265
25277
  const value = shortcuts[key];
@@ -25271,16 +25283,18 @@ const createOnKeyDownForShortcuts = (shortcuts) => {
25271
25283
  }
25272
25284
  shortcutsArray.push(shortcut);
25273
25285
  }
25274
- return createOnKeyDownForShortcutArray(shortcutsArray);
25286
+ return createOnKeyDownForShortcutArray(shortcutsArray, { ownsTarget });
25275
25287
  };
25276
25288
 
25277
- const applyKeyboardShortcuts = (shortcuts, keyboardEvent) => {
25289
+ const applyKeyboardShortcuts = (shortcuts, keyboardEvent, { ownsTarget }) => {
25278
25290
  const currentTarget = keyboardEvent.currentTarget;
25279
25291
  const target = keyboardEvent.target;
25280
25292
  let canIntercept;
25281
25293
  if (target === currentTarget) {
25282
25294
  // the event occurs on the element itself so we are sure we won't interfere with native behaviors of child elements
25283
25295
  canIntercept = true;
25296
+ } else if (ownsTarget && ownsTarget(target)) {
25297
+ canIntercept = true;
25284
25298
  } else {
25285
25299
  // we need to check if the default action is something that we can allow to be intercepted
25286
25300
  const defaultAction = getKeyboardEventDefaultAction(keyboardEvent);
@@ -25498,6 +25512,17 @@ const isSameKey = (browserEventKey, key) => {
25498
25512
  * knows what "ctrl+backspace" means, which keys a control may take without
25499
25513
  * swallowing what the browser owed them, and which it may not.
25500
25514
  *
25515
+ * The control's own element is the exception to that last part. `interactions`
25516
+ * are set up on a field's outer box while the key goes down in its `<input>`,
25517
+ * and that field is the control the declaration speaks for: an ArrowUp declared
25518
+ * on a text field is the field saying what ArrowUp does there, not a container
25519
+ * taking the caret from a field inside it. A field inside a box that declares
25520
+ * keys for something else (a row, a list) keeps every key it has a use for.
25521
+ *
25522
+ * A declared key that matches takes the key: its default does not happen as
25523
+ * well, whether the gate lets the interaction through or refuses it and says
25524
+ * why — the same as a number field's arrows.
25525
+ *
25501
25526
  * The interaction is dispatched under its full name, colon included, so
25502
25527
  * `findEvent(event, "keyboard:ctrl+backspace")` gets back to it — the shortcut
25503
25528
  * that asked is what the action wants to know, not that a key went down.
@@ -25514,9 +25539,12 @@ defineInteractionDetector({
25514
25539
  for (const type of types) {
25515
25540
  shortcuts[type.slice(KEYBOARD_PREFIX.length)] = (keyboardEvent) => {
25516
25541
  trigger(type, keyboardEvent);
25542
+ keyboardEvent.preventDefault();
25517
25543
  };
25518
25544
  }
25519
- const onKeyDown = createOnKeyDownForShortcuts(shortcuts);
25545
+ const onKeyDown = createOnKeyDownForShortcuts(shortcuts, {
25546
+ ownsTarget: (target) => target === findNearestControlHost(element),
25547
+ });
25520
25548
  element.addEventListener("keydown", onKeyDown);
25521
25549
  return () => {
25522
25550
  element.removeEventListener("keydown", onKeyDown);
@@ -33755,9 +33783,33 @@ const pushHeadThemeColors = (themeColors) => {
33755
33783
  };
33756
33784
  };
33757
33785
 
33786
+ /**
33787
+ * What a popup asks of the colour while it is open: its `themeColor` prop,
33788
+ * else the `--navi-popup-theme-color` it inherits — where the choice goes
33789
+ * when it is the app's, or a part of the app's, rather than one popup's.
33790
+ *
33791
+ * @param {Element} element - The popup element.
33792
+ * @param {string|false} [themeColor] - The popup's prop.
33793
+ * @returns {string} `"auto"` paints what the popup lays over the top edge,
33794
+ * `"none"` leaves the colour as it is, anything else is a colour forcing it.
33795
+ */
33796
+ const readPopupThemeColor = (element, themeColor) => {
33797
+ if (themeColor === false) {
33798
+ return "none";
33799
+ }
33800
+ if (themeColor) {
33801
+ return themeColor;
33802
+ }
33803
+ const token = getComputedStyle(element)
33804
+ .getPropertyValue("--navi-popup-theme-color")
33805
+ .trim();
33806
+ return token || "auto";
33807
+ };
33808
+
33758
33809
  /**
33759
33810
  * @param {object} paint
33760
- * @param {string} [paint.themeColor] - Forces the colour, nothing is painted.
33811
+ * @param {string} paint.themeColor - `"auto"` paints the two colours below,
33812
+ * a colour forces it and nothing is painted.
33761
33813
  * @param {string|null} [paint.backdropColor] - Laid over the page's colour.
33762
33814
  * @param {string|null} [paint.surfaceColor] - Laid over that, for a popup
33763
33815
  * flush with the top edge.
@@ -33769,7 +33821,7 @@ const pushPopupThemeColor = ({
33769
33821
  surfaceColor,
33770
33822
  }) => {
33771
33823
  let layer;
33772
- if (themeColor) {
33824
+ if (themeColor !== "auto") {
33773
33825
  layer = { color: themeColor };
33774
33826
  } else {
33775
33827
  const paints = [];
@@ -67908,16 +67960,17 @@ const css$E = /* css */`
67908
67960
  * `saturate()`/`grayscale()` work too. How far what is behind withdraws is
67909
67961
  * a question of paint, not of what an outside click does — a backdrop that
67910
67962
  * closes can blur.
67911
- * @param {string|false} [props.themeColor] - The colour the browser paints
67912
- * above the page (`<meta name="theme-color">`: Chrome's address bar on
67913
- * Android, an installed app's status bar) while this dialog is open.
67914
- * Left out, it is what the dialog lays over the top edge of the screen: its
67963
+ * @param {"auto"|string|false} [props.themeColor] - The colour the browser
67964
+ * paints above the page (`<meta name="theme-color">`: Chrome's address bar
67965
+ * on Android, an installed app's status bar) while this dialog is open.
67966
+ * `"auto"` is what the dialog lays over the top edge of the screen: its
67915
67967
  * backdrop over the colour the page asked for (`<Head>`), and its own
67916
67968
  * background over that when it is flush with the top edge
67917
67969
  * (`marginWithContainer={0}` with `expand`/`expandY` or a top
67918
67970
  * `positionArea`). A colour forces it, for a surface whose colour cannot be
67919
67971
  * read (an image, a gradient); `false` leaves the page's colour alone.
67920
- * `layer="top"` only.
67972
+ * Left out, the `--navi-popup-theme-color` token decides, `"auto"` unless
67973
+ * the app says otherwise. `layer="top"` only.
67921
67974
  * @param {boolean} [props.scrollCapture] - Traps scroll gestures inside the
67922
67975
  * dialog so the page/container behind it can't scroll while it's open.
67923
67976
  * A `layer="local"` dialog always locks its own positioned ancestor's
@@ -69087,12 +69140,15 @@ const useDialogProps = props => {
69087
69140
  // What the browser paints above the page goes with what is laid over its
69088
69141
  // top edge, from the start of the opening to the start of the closing: a
69089
69142
  // local dialog lays nothing there.
69090
- if (isTopLayer && themeColor !== false) {
69091
- addCleanup(pushPopupThemeColor({
69092
- themeColor,
69093
- backdropColor: backdrop ? readBackdropColor(dialogEl) : null,
69094
- surfaceColor: flushEdges.top ? getComputedStyle(dialogEl).backgroundColor : null
69095
- }));
69143
+ if (isTopLayer) {
69144
+ const popupThemeColor = readPopupThemeColor(dialogEl, themeColor);
69145
+ if (popupThemeColor !== "none") {
69146
+ addCleanup(pushPopupThemeColor({
69147
+ themeColor: popupThemeColor,
69148
+ backdropColor: backdrop ? readBackdropColor(dialogEl) : null,
69149
+ surfaceColor: flushEdges.top ? getComputedStyle(dialogEl).backgroundColor : null
69150
+ }));
69151
+ }
69096
69152
  }
69097
69153
  const cancelOpenInteractionSuppression = !silent && hasCssTransitionAnimation ? suppressPointerEventsDuringTransition(dialogEl) : null;
69098
69154
  // Handing the focus to a field is what raises the on-screen keyboard, and
@@ -69958,12 +70014,14 @@ const css$D = /* css */`
69958
70014
  * `saturate()`/`grayscale()` work too. How far what is behind withdraws is
69959
70015
  * a question of paint, not of what an outside click does — a backdrop that
69960
70016
  * closes can blur.
69961
- * @param {string|false} [props.themeColor] - The colour the browser paints
69962
- * above the page (`<meta name="theme-color">`: Chrome's address bar on
69963
- * Android, an installed app's status bar) while this popover is open.
69964
- * Left out, it is the backdrop laid over the colour the page asked for
70017
+ * @param {"auto"|string|false} [props.themeColor] - The colour the browser
70018
+ * paints above the page (`<meta name="theme-color">`: Chrome's address bar
70019
+ * on Android, an installed app's status bar) while this popover is open.
70020
+ * `"auto"` is the backdrop laid over the colour the page asked for
69965
70021
  * (`<Head>`), and the page's colour when there is no backdrop. A colour
69966
- * forces it; `false` leaves the page's colour alone. `layer="top"` only.
70022
+ * forces it; `false` leaves the page's colour alone. Left out, the
70023
+ * `--navi-popup-theme-color` token decides, `"auto"` unless the app says
70024
+ * otherwise. `layer="top"` only.
69967
70025
  * @param {boolean} [props.scrollCapture] - Traps scroll gestures inside the
69968
70026
  * popover so the page/container behind it can't scroll while it's open.
69969
70027
  * @param {boolean} [props.focusCapture] - Traps Tab navigation inside the
@@ -70924,11 +70982,14 @@ const usePopoverProps = props => {
70924
70982
  // What the browser paints above the page goes with what is laid over its
70925
70983
  // top edge, from the start of the opening to the start of the closing: a
70926
70984
  // local popover's backdrop stops at its container.
70927
- if (isTopLayer && themeColor !== false) {
70928
- addCleanup(pushPopupThemeColor({
70929
- themeColor,
70930
- backdropColor: backdropEl ? readBackdropColor(backdropEl) : null
70931
- }));
70985
+ if (isTopLayer) {
70986
+ const popupThemeColor = readPopupThemeColor(popoverEl, themeColor);
70987
+ if (popupThemeColor !== "none") {
70988
+ addCleanup(pushPopupThemeColor({
70989
+ themeColor: popupThemeColor,
70990
+ backdropColor: backdropEl ? readBackdropColor(backdropEl) : null
70991
+ }));
70992
+ }
70932
70993
  }
70933
70994
  const cancelOpenInteractionSuppression = !silent && hasCssTransitionAnimation ? suppressPointerEventsDuringTransition(popoverEl) : null;
70934
70995
  // autoFocus={false}: opening moves no focus at all — whoever has the
@@ -71346,10 +71407,11 @@ const css$C = /* css */`@layer navi {
71346
71407
  * identically): the wash the backdrop paints over what is behind.
71347
71408
  * @param {string} [props.backdropFilter] - Forwarded as-is: what that wash
71348
71409
  * does to the picture underneath, `"blur(4px)"` and the like.
71349
- * @param {string|false} [props.themeColor] - Forwarded as-is: the colour the
71350
- * browser paints above the page while the popup is open, worked out from
71351
- * what it lays over the top edge when left out. A colour forces it, `false`
71352
- * leaves the page's colour alone.
71410
+ * @param {"auto"|string|false} [props.themeColor] - Forwarded as-is: the
71411
+ * colour the browser paints above the page while the popup is open.
71412
+ * `"auto"` works it out from what the popup lays over the top edge, a
71413
+ * colour forces it, `false` leaves the page's colour alone. Left out, the
71414
+ * `--navi-popup-theme-color` token decides.
71353
71415
  * @param {boolean|"auto"|"fading"|"scaling"|"sliding"|`slide-from-${string}`|"expanding"|`expand-${string}`|"lifting"|{open: boolean|"auto"|"fading"|"scaling"|"sliding"|`slide-from-${string}`, close: "lifting"}} [props.animation]
71354
71416
  * - Forwarded to whichever renders — see either component's own doc.
71355
71417
  * `"expanding"`/`"expand-*"` are `Popover`'s own; `"lifting"` and