@jsenv/navi 0.29.456 → 0.29.458

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);
@@ -39456,6 +39484,14 @@ const activeRouteReadsSignal = computed(() => {
39456
39484
  * params read: that address is the current one with the state set to the
39457
39485
  * popup's value (see `prefetch` on Button).
39458
39486
  * @param {object} [options]
39487
+ * @param {() => any} [options.when] - a condition on app state the action
39488
+ * asks under, read off signals like the params: falsy, it runs nothing.
39489
+ * Unlike a params function it leaves the action asking nothing of the
39490
+ * address, so a link to the route still prefetches an action without
39491
+ * params when the condition holds at that moment — a page's code only some
39492
+ * readers may download (`() => meIsAdminSignal.value`). A condition reading
39493
+ * the address belongs in the params: a link reads `when` as it is, not as
39494
+ * the address it leads to will make it.
39459
39495
  * @param {boolean} [options.prefetch=true] - `false` keeps the action from
39460
39496
  * being prerun ahead of what asks for it, when the read is not worth a
39461
39497
  * hover or a press that may not end in an opening.
@@ -39464,7 +39500,7 @@ const routeAction = (
39464
39500
  routeOrRoutes,
39465
39501
  action,
39466
39502
  paramsEffect,
39467
- { prefetch = true, ...options } = {},
39503
+ { when, prefetch = true, ...options } = {},
39468
39504
  ) => {
39469
39505
  if (typeof action === "function") {
39470
39506
  action = createAction(action);
@@ -39474,7 +39510,12 @@ const routeAction = (
39474
39510
  routes.length === 1
39475
39511
  ? routes[0].matchingSignal
39476
39512
  : anyMatchingRouteSignal(routes);
39477
- const readParams = paramsEffect || (() => true);
39513
+ const readParams = () => {
39514
+ if (when && !when()) {
39515
+ return null;
39516
+ }
39517
+ return paramsEffect ? paramsEffect() : true;
39518
+ };
39478
39519
  const readParamsAsked = () => {
39479
39520
  const matching = routeMatchingSignal.value;
39480
39521
  const params = readParams();
@@ -39510,10 +39551,13 @@ const routeAction = (
39510
39551
  for (const route of routes) {
39511
39552
  if (paramsEffect) {
39512
39553
  registerRouteStatePreload(route, (values) => {
39513
- prefetchAsIf(action, paramsEffect, values);
39554
+ prefetchAsIf(action, readParams, values);
39514
39555
  });
39515
39556
  } else {
39516
39557
  registerRoutePreload(route, () => {
39558
+ if (!untracked(readParams)) {
39559
+ return;
39560
+ }
39517
39561
  prerunAhead(action.bindParams(true));
39518
39562
  });
39519
39563
  }
@@ -39526,12 +39570,12 @@ const routeAction = (
39526
39570
  // (see readStateAsIf), prerun only when that changes them: params the state
39527
39571
  // leaves alone are the ones the action already runs on — or is meant not to,
39528
39572
  // and a prerun would retry a FAILED one behind the screen's back.
39529
- const prefetchAsIf = (action, paramsEffect, values) => {
39530
- const params = readStateAsIf(values, paramsEffect);
39573
+ const prefetchAsIf = (action, readParams, values) => {
39574
+ const params = readStateAsIf(values, readParams);
39531
39575
  if (!params) {
39532
39576
  return;
39533
39577
  }
39534
- if (compareTwoJsValues(params, untracked(paramsEffect))) {
39578
+ if (compareTwoJsValues(params, untracked(readParams))) {
39535
39579
  return;
39536
39580
  }
39537
39581
  prerunAhead(action.bindParams(params));