@zag-js/interact-outside 1.24.2 → 1.26.0

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.
package/dist/index.d.mts CHANGED
@@ -15,6 +15,14 @@ interface InteractOutsideHandlers {
15
15
  interface InteractOutsideOptions extends InteractOutsideHandlers {
16
16
  exclude?: ((target: HTMLElement) => boolean) | undefined;
17
17
  defer?: boolean | undefined;
18
+ /**
19
+ * Default: `true`. If `true`, the interact outside detection will recursively include elements
20
+ * that are controlled by elements within the trap via `aria-controls`. Only elements
21
+ * with `aria-expanded="true"` and interactive container roles (menu, listbox, dialog, etc.)
22
+ * will be included. This allows nested menus, select dropdowns, and other portalled
23
+ * content to remain within the interaction boundary.
24
+ */
25
+ followControlledElements?: boolean | undefined;
18
26
  }
19
27
  interface EventDetails<T> {
20
28
  originalEvent: T;
package/dist/index.d.ts CHANGED
@@ -15,6 +15,14 @@ interface InteractOutsideHandlers {
15
15
  interface InteractOutsideOptions extends InteractOutsideHandlers {
16
16
  exclude?: ((target: HTMLElement) => boolean) | undefined;
17
17
  defer?: boolean | undefined;
18
+ /**
19
+ * Default: `true`. If `true`, the interact outside detection will recursively include elements
20
+ * that are controlled by elements within the trap via `aria-controls`. Only elements
21
+ * with `aria-expanded="true"` and interactive container roles (menu, listbox, dialog, etc.)
22
+ * will be included. This allows nested menus, select dropdowns, and other portalled
23
+ * content to remain within the interaction boundary.
24
+ */
25
+ followControlledElements?: boolean | undefined;
18
26
  }
19
27
  interface EventDetails<T> {
20
28
  originalEvent: T;
package/dist/index.js CHANGED
@@ -102,7 +102,14 @@ function isEventWithinScrollbar(event, ancestor) {
102
102
  return onScrollbarY || onScrollbarX;
103
103
  }
104
104
  function trackInteractOutsideImpl(node, options) {
105
- const { exclude, onFocusOutside, onPointerDownOutside, onInteractOutside, defer } = options;
105
+ const {
106
+ exclude,
107
+ onFocusOutside,
108
+ onPointerDownOutside,
109
+ onInteractOutside,
110
+ defer,
111
+ followControlledElements = true
112
+ } = options;
106
113
  if (!node) return;
107
114
  const doc = domQuery.getDocument(node);
108
115
  const win = domQuery.getWindow(node);
@@ -113,6 +120,7 @@ function trackInteractOutsideImpl(node, options) {
113
120
  if (!target.isConnected) return false;
114
121
  if (domQuery.contains(node, target)) return false;
115
122
  if (isEventPointWithin(node, event)) return false;
123
+ if (followControlledElements && domQuery.isControlledElement(node, target)) return false;
116
124
  const triggerEl = doc.querySelector(`[aria-controls="${node.id}"]`);
117
125
  if (triggerEl) {
118
126
  const triggerAncestor = domQuery.getNearestOverflowAncestor(triggerEl);
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { raf, getDocument, getWindow, isShadowRoot, addDomEvent, isTouchDevice, getEventTarget, isFocusable, isContextMenuEvent, isHTMLElement, contains, getNearestOverflowAncestor } from '@zag-js/dom-query';
1
+ import { raf, getDocument, getWindow, isShadowRoot, addDomEvent, isTouchDevice, getEventTarget, isFocusable, isContextMenuEvent, isHTMLElement, contains, isControlledElement, getNearestOverflowAncestor } from '@zag-js/dom-query';
2
2
  import { callAll } from '@zag-js/utils';
3
3
 
4
4
  // src/index.ts
@@ -100,7 +100,14 @@ function isEventWithinScrollbar(event, ancestor) {
100
100
  return onScrollbarY || onScrollbarX;
101
101
  }
102
102
  function trackInteractOutsideImpl(node, options) {
103
- const { exclude, onFocusOutside, onPointerDownOutside, onInteractOutside, defer } = options;
103
+ const {
104
+ exclude,
105
+ onFocusOutside,
106
+ onPointerDownOutside,
107
+ onInteractOutside,
108
+ defer,
109
+ followControlledElements = true
110
+ } = options;
104
111
  if (!node) return;
105
112
  const doc = getDocument(node);
106
113
  const win = getWindow(node);
@@ -111,6 +118,7 @@ function trackInteractOutsideImpl(node, options) {
111
118
  if (!target.isConnected) return false;
112
119
  if (contains(node, target)) return false;
113
120
  if (isEventPointWithin(node, event)) return false;
121
+ if (followControlledElements && isControlledElement(node, target)) return false;
114
122
  const triggerEl = doc.querySelector(`[aria-controls="${node.id}"]`);
115
123
  if (triggerEl) {
116
124
  const triggerAncestor = getNearestOverflowAncestor(triggerEl);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/interact-outside",
3
- "version": "1.24.2",
3
+ "version": "1.26.0",
4
4
  "description": "Track interactions or focus outside an element",
5
5
  "keywords": [
6
6
  "js",
@@ -16,8 +16,8 @@
16
16
  "dist"
17
17
  ],
18
18
  "dependencies": {
19
- "@zag-js/dom-query": "1.24.2",
20
- "@zag-js/utils": "1.24.2"
19
+ "@zag-js/dom-query": "1.26.0",
20
+ "@zag-js/utils": "1.26.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "clean-package": "2.2.0"