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