@zag-js/focus-visible 2.0.0-next.1 → 2.0.0-next.3

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.js CHANGED
@@ -34,6 +34,20 @@ function isValidKey(e) {
34
34
  return !(e.metaKey || !(0, import_dom_query.isMac)() && e.altKey || e.ctrlKey || e.key === "Control" || e.key === "Shift" || e.key === "Meta");
35
35
  }
36
36
  var nonTextInputTypes = /* @__PURE__ */ new Set(["checkbox", "radio", "range", "color", "file", "image", "button", "submit", "reset"]);
37
+ var interactiveContentSelector = [
38
+ "a[href]",
39
+ "audio[controls]",
40
+ "button",
41
+ "details",
42
+ "embed",
43
+ "iframe",
44
+ "img[controls]",
45
+ "img[usemap]",
46
+ "input",
47
+ "select",
48
+ "textarea",
49
+ "video[controls]"
50
+ ].join(",");
37
51
  function isKeyboardFocusEvent(isTextInput, modality, e) {
38
52
  const eventTarget = e ? (0, import_dom_query.getEventTarget)(e) : null;
39
53
  const doc = (0, import_dom_query.getDocument)(eventTarget);
@@ -46,7 +60,9 @@ var currentModality = null;
46
60
  var changeHandlers = /* @__PURE__ */ new Set();
47
61
  var listenerMap = /* @__PURE__ */ new Map();
48
62
  var hasEventBeforeFocus = false;
63
+ var pendingLabelControl = null;
49
64
  var hasBlurredWindowRecently = false;
65
+ var lastPointerPosition = null;
50
66
  var ignoreFocusEvent = false;
51
67
  var FOCUS_VISIBLE_INPUT_KEYS = {
52
68
  Tab: true,
@@ -58,6 +74,7 @@ function triggerChangeHandlers(modality, e) {
58
74
  }
59
75
  }
60
76
  function handleKeyboardEvent(e) {
77
+ pendingLabelControl = null;
61
78
  hasEventBeforeFocus = true;
62
79
  if (isValidKey(e)) {
63
80
  currentModality = "keyboard";
@@ -65,33 +82,55 @@ function handleKeyboardEvent(e) {
65
82
  }
66
83
  }
67
84
  function handlePointerEvent(e) {
85
+ const isMove = e.type === "pointermove" || e.type === "mousemove";
86
+ if (isMove && e.isTrusted && lastPointerPosition?.x === e.clientX && lastPointerPosition?.y === e.clientY) return;
87
+ lastPointerPosition = { x: e.clientX, y: e.clientY };
68
88
  currentModality = "pointer";
69
89
  if (e.type === "mousedown" || e.type === "pointerdown") {
90
+ pendingLabelControl = null;
70
91
  hasEventBeforeFocus = true;
71
92
  triggerChangeHandlers("pointer", e);
72
93
  }
73
94
  }
74
95
  function handleClickEvent(e) {
75
96
  if ((0, import_dom_query.isVirtualClick)(e)) {
97
+ pendingLabelControl = null;
76
98
  hasEventBeforeFocus = true;
77
99
  currentModality = "virtual";
100
+ return;
78
101
  }
102
+ pendingLabelControl = null;
103
+ const target = (0, import_dom_query.getEventTarget)(e);
104
+ const label = target?.closest("label");
105
+ const control = label?.control;
106
+ if (!label || !control || control.matches(":disabled")) return;
107
+ const interactive = target?.closest(interactiveContentSelector);
108
+ if (interactive && label.contains(interactive) && interactive !== control) return;
109
+ if (control !== (0, import_dom_query.getActiveElement)(label.ownerDocument)) pendingLabelControl = control;
79
110
  }
80
111
  function handleFocusEvent(e) {
81
112
  const target = (0, import_dom_query.getEventTarget)(e);
82
- if (target === (0, import_dom_query.getWindow)(target) || target === (0, import_dom_query.getDocument)(target) || ignoreFocusEvent || !e.isTrusted) {
113
+ const isLabelActivationFocus = target === pendingLabelControl;
114
+ if (ignoreFocusEvent) return;
115
+ if ((0, import_dom_query.isWindow)(target)) {
116
+ hasBlurredWindowRecently = true;
117
+ return;
118
+ }
119
+ if ((0, import_dom_query.isDocument)(target) || !e.isTrusted) {
83
120
  return;
84
121
  }
85
- if (!hasEventBeforeFocus && !hasBlurredWindowRecently) {
122
+ if (!hasEventBeforeFocus && !isLabelActivationFocus && !hasBlurredWindowRecently) {
86
123
  currentModality = "virtual";
87
124
  triggerChangeHandlers("virtual", e);
88
125
  }
89
126
  hasEventBeforeFocus = false;
127
+ pendingLabelControl = null;
90
128
  hasBlurredWindowRecently = false;
91
129
  }
92
130
  function handleWindowBlur() {
93
131
  if (ignoreFocusEvent) return;
94
132
  hasEventBeforeFocus = false;
133
+ pendingLabelControl = null;
95
134
  hasBlurredWindowRecently = true;
96
135
  }
97
136
  function setupGlobalFocusEvents(root) {
package/dist/index.mjs CHANGED
@@ -1,9 +1,32 @@
1
1
  // src/index.ts
2
- import { getActiveElement, getDocument, getEventTarget, getWindow, isMac, isVirtualClick } from "@zag-js/dom-query";
2
+ import {
3
+ getActiveElement,
4
+ getDocument,
5
+ getEventTarget,
6
+ getWindow,
7
+ isDocument,
8
+ isMac,
9
+ isVirtualClick,
10
+ isWindow
11
+ } from "@zag-js/dom-query";
3
12
  function isValidKey(e) {
4
13
  return !(e.metaKey || !isMac() && e.altKey || e.ctrlKey || e.key === "Control" || e.key === "Shift" || e.key === "Meta");
5
14
  }
6
15
  var nonTextInputTypes = /* @__PURE__ */ new Set(["checkbox", "radio", "range", "color", "file", "image", "button", "submit", "reset"]);
16
+ var interactiveContentSelector = [
17
+ "a[href]",
18
+ "audio[controls]",
19
+ "button",
20
+ "details",
21
+ "embed",
22
+ "iframe",
23
+ "img[controls]",
24
+ "img[usemap]",
25
+ "input",
26
+ "select",
27
+ "textarea",
28
+ "video[controls]"
29
+ ].join(",");
7
30
  function isKeyboardFocusEvent(isTextInput, modality, e) {
8
31
  const eventTarget = e ? getEventTarget(e) : null;
9
32
  const doc = getDocument(eventTarget);
@@ -16,7 +39,9 @@ var currentModality = null;
16
39
  var changeHandlers = /* @__PURE__ */ new Set();
17
40
  var listenerMap = /* @__PURE__ */ new Map();
18
41
  var hasEventBeforeFocus = false;
42
+ var pendingLabelControl = null;
19
43
  var hasBlurredWindowRecently = false;
44
+ var lastPointerPosition = null;
20
45
  var ignoreFocusEvent = false;
21
46
  var FOCUS_VISIBLE_INPUT_KEYS = {
22
47
  Tab: true,
@@ -28,6 +53,7 @@ function triggerChangeHandlers(modality, e) {
28
53
  }
29
54
  }
30
55
  function handleKeyboardEvent(e) {
56
+ pendingLabelControl = null;
31
57
  hasEventBeforeFocus = true;
32
58
  if (isValidKey(e)) {
33
59
  currentModality = "keyboard";
@@ -35,33 +61,55 @@ function handleKeyboardEvent(e) {
35
61
  }
36
62
  }
37
63
  function handlePointerEvent(e) {
64
+ const isMove = e.type === "pointermove" || e.type === "mousemove";
65
+ if (isMove && e.isTrusted && lastPointerPosition?.x === e.clientX && lastPointerPosition?.y === e.clientY) return;
66
+ lastPointerPosition = { x: e.clientX, y: e.clientY };
38
67
  currentModality = "pointer";
39
68
  if (e.type === "mousedown" || e.type === "pointerdown") {
69
+ pendingLabelControl = null;
40
70
  hasEventBeforeFocus = true;
41
71
  triggerChangeHandlers("pointer", e);
42
72
  }
43
73
  }
44
74
  function handleClickEvent(e) {
45
75
  if (isVirtualClick(e)) {
76
+ pendingLabelControl = null;
46
77
  hasEventBeforeFocus = true;
47
78
  currentModality = "virtual";
79
+ return;
48
80
  }
81
+ pendingLabelControl = null;
82
+ const target = getEventTarget(e);
83
+ const label = target?.closest("label");
84
+ const control = label?.control;
85
+ if (!label || !control || control.matches(":disabled")) return;
86
+ const interactive = target?.closest(interactiveContentSelector);
87
+ if (interactive && label.contains(interactive) && interactive !== control) return;
88
+ if (control !== getActiveElement(label.ownerDocument)) pendingLabelControl = control;
49
89
  }
50
90
  function handleFocusEvent(e) {
51
91
  const target = getEventTarget(e);
52
- if (target === getWindow(target) || target === getDocument(target) || ignoreFocusEvent || !e.isTrusted) {
92
+ const isLabelActivationFocus = target === pendingLabelControl;
93
+ if (ignoreFocusEvent) return;
94
+ if (isWindow(target)) {
95
+ hasBlurredWindowRecently = true;
96
+ return;
97
+ }
98
+ if (isDocument(target) || !e.isTrusted) {
53
99
  return;
54
100
  }
55
- if (!hasEventBeforeFocus && !hasBlurredWindowRecently) {
101
+ if (!hasEventBeforeFocus && !isLabelActivationFocus && !hasBlurredWindowRecently) {
56
102
  currentModality = "virtual";
57
103
  triggerChangeHandlers("virtual", e);
58
104
  }
59
105
  hasEventBeforeFocus = false;
106
+ pendingLabelControl = null;
60
107
  hasBlurredWindowRecently = false;
61
108
  }
62
109
  function handleWindowBlur() {
63
110
  if (ignoreFocusEvent) return;
64
111
  hasEventBeforeFocus = false;
112
+ pendingLabelControl = null;
65
113
  hasBlurredWindowRecently = true;
66
114
  }
67
115
  function setupGlobalFocusEvents(root) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/focus-visible",
3
- "version": "2.0.0-next.1",
3
+ "version": "2.0.0-next.3",
4
4
  "description": "Focus visible polyfill utility based on WICG",
5
5
  "keywords": [
6
6
  "js",
@@ -28,7 +28,7 @@
28
28
  "clean-package": "../../../clean-package.config.json",
29
29
  "main": "dist/index.js",
30
30
  "dependencies": {
31
- "@zag-js/dom-query": "2.0.0-next.1"
31
+ "@zag-js/dom-query": "2.0.0-next.3"
32
32
  },
33
33
  "devDependencies": {
34
34
  "clean-package": "2.2.0"
@@ -51,7 +51,7 @@
51
51
  "scripts": {
52
52
  "build": "tsup",
53
53
  "test": "vitest",
54
- "lint": "eslint src",
54
+ "lint": "oxlint src",
55
55
  "test-ci": "pnpm test --ci --runInBand -u",
56
56
  "test-watch": "pnpm test --watchAll"
57
57
  }