@jsenv/dom 0.12.0 → 0.12.1

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.
Files changed (2) hide show
  1. package/dist/jsenv_dom.js +26 -6
  2. package/package.json +1 -1
package/dist/jsenv_dom.js CHANGED
@@ -4375,21 +4375,30 @@ const findFocusDelegateTarget = (el) => {
4375
4375
  return null;
4376
4376
  };
4377
4377
 
4378
- const findFocusable = (element) => {
4378
+ const findFocusable = (element, { exclude } = {}) => {
4379
4379
  const associatedElements = getAssociatedElements(element);
4380
4380
  if (associatedElements) {
4381
4381
  for (const associatedElement of associatedElements) {
4382
- const focusable = findFocusable(associatedElement);
4382
+ const focusable = findFocusable(associatedElement, { exclude });
4383
4383
  if (focusable) {
4384
4384
  return focusable;
4385
4385
  }
4386
4386
  }
4387
4387
  return null;
4388
4388
  }
4389
- if (elementIsFocusable(element)) {
4389
+ const isFocusable = (node) => {
4390
+ if (!elementIsFocusable(node)) {
4391
+ return false;
4392
+ }
4393
+ if (exclude && exclude(node)) {
4394
+ return false;
4395
+ }
4396
+ return true;
4397
+ };
4398
+ if (isFocusable(element)) {
4390
4399
  return element;
4391
4400
  }
4392
- const focusableDescendant = findDescendant(element, elementIsFocusable);
4401
+ const focusableDescendant = findDescendant(element, isFocusable);
4393
4402
  if (focusableDescendant) {
4394
4403
  // If the first focusable is an unchecked radio/checkbox, prefer the checked
4395
4404
  // sibling in the same group (mirrors native browser radio focus behavior
@@ -4496,10 +4505,15 @@ const DEFAULT_BEHAVIORS = [
4496
4505
  keys: {
4497
4506
  // Tab moves focus on any element
4498
4507
  tab: "focus_nav",
4499
- // Escape dismisses on any element (dialog, search clear, dropdown close, etc.)
4508
+ },
4509
+ // no fallback: only claims Tab, other keys continue to next entries
4510
+ },
4511
+ {
4512
+ // Escape natively dismisses only <dialog> elements
4513
+ test: (el) => el.tagName === "DIALOG" || Boolean(el.closest("dialog")),
4514
+ keys: {
4500
4515
  escape: "dismiss",
4501
4516
  },
4502
- // no fallback: only claims Tab/Escape, other keys continue to next entries
4503
4517
  },
4504
4518
  {
4505
4519
  test: (el) => el.matches("input[type='radio'], input[type='checkbox']"),
@@ -4518,6 +4532,12 @@ const DEFAULT_BEHAVIORS = [
4518
4532
  "input:not([type]), input[type='text'], input[type='search'], input[type='url'], input[type='email'], input[type='password'], input[type='tel']",
4519
4533
  ),
4520
4534
  keys: {
4535
+ escape: (e) => {
4536
+ if (e.target.type === "search") {
4537
+ return e.target.value ? "clear" : "";
4538
+ }
4539
+ return "";
4540
+ },
4521
4541
  enter: (e) => (e.target.form ? "form_submit" : ""),
4522
4542
  arrowleft: "cursor_move",
4523
4543
  arrowright: "cursor_move",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/dom",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "type": "module",
5
5
  "description": "DOM utilities for writing frontend code",
6
6
  "repository": {