power-focusable 2.1.4 → 2.1.5
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.cjs +45 -49
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +45 -49
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -10,30 +10,31 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
10
10
|
const { composed = false } = options;
|
|
11
11
|
const elements = [];
|
|
12
12
|
if (composed) {
|
|
13
|
-
let
|
|
13
|
+
let traverse2 = function(node) {
|
|
14
14
|
if (node instanceof HTMLElement) {
|
|
15
15
|
if (isFocusable(node)) {
|
|
16
16
|
elements[elements.length] = node;
|
|
17
17
|
}
|
|
18
18
|
const shadow = node.shadowRoot;
|
|
19
19
|
if (shadow && shadow.mode === "open") {
|
|
20
|
-
|
|
20
|
+
traverse2(shadow);
|
|
21
21
|
}
|
|
22
|
-
}
|
|
22
|
+
}
|
|
23
|
+
if (node instanceof HTMLSlotElement) {
|
|
23
24
|
const assigned = node.assignedElements({ flatten: true });
|
|
24
25
|
if (assigned.length) {
|
|
25
26
|
for (let i = 0, l = assigned.length; i < l; i++) {
|
|
26
|
-
|
|
27
|
+
traverse2(assigned[i]);
|
|
27
28
|
}
|
|
28
29
|
return;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
const children = node.childNodes;
|
|
32
33
|
for (let i = 0, l = children.length; i < l; i++) {
|
|
33
|
-
|
|
34
|
+
traverse2(children[i]);
|
|
34
35
|
}
|
|
35
36
|
};
|
|
36
|
-
|
|
37
|
+
traverse2(container);
|
|
37
38
|
} else {
|
|
38
39
|
const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
39
40
|
for (let i = 0, l = candidates.length; i < l; i++) {
|
|
@@ -75,10 +76,8 @@ function isFocusable(element) {
|
|
|
75
76
|
return false;
|
|
76
77
|
}
|
|
77
78
|
const index = element.getAttribute("tabindex");
|
|
78
|
-
if (index) {
|
|
79
|
-
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
79
|
+
if (index && Number(index) < 0) {
|
|
80
|
+
return false;
|
|
82
81
|
}
|
|
83
82
|
if (!element.matches(FOCUSABLE_SELECTOR)) {
|
|
84
83
|
return false;
|
|
@@ -117,29 +116,21 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
117
116
|
return focusables[(offsetIndex + length) % length] ?? null;
|
|
118
117
|
}
|
|
119
118
|
function containsDeep(container, element) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
if (node === container) {
|
|
119
|
+
let current = element;
|
|
120
|
+
while (current) {
|
|
121
|
+
if (current === container) {
|
|
125
122
|
return true;
|
|
126
123
|
}
|
|
127
|
-
|
|
128
|
-
return node.mode === "open" ? walk(node.host) : false;
|
|
129
|
-
}
|
|
130
|
-
return walk(node.parentNode);
|
|
124
|
+
current = current instanceof ShadowRoot ? current.mode === "open" ? current.host : null : current.parentNode;
|
|
131
125
|
}
|
|
132
|
-
return
|
|
126
|
+
return false;
|
|
133
127
|
}
|
|
134
128
|
function getActiveElement() {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
const active = node.shadowRoot?.activeElement;
|
|
140
|
-
return active ? walk(active) : node;
|
|
129
|
+
let current = document.activeElement;
|
|
130
|
+
while (current?.shadowRoot?.activeElement) {
|
|
131
|
+
current = current.shadowRoot.activeElement;
|
|
141
132
|
}
|
|
142
|
-
return
|
|
133
|
+
return current;
|
|
143
134
|
}
|
|
144
135
|
var tabIndexCache = /* @__PURE__ */ new WeakMap();
|
|
145
136
|
function getTabIndex(element) {
|
|
@@ -152,44 +143,49 @@ function getTabIndex(element) {
|
|
|
152
143
|
return index;
|
|
153
144
|
}
|
|
154
145
|
function isDisabled(element) {
|
|
155
|
-
return "disabled" in element && element.disabled;
|
|
146
|
+
return "disabled" in element && !!element.disabled;
|
|
156
147
|
}
|
|
157
148
|
function isDisabledDeep(element) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
149
|
+
let current = element;
|
|
150
|
+
while (current) {
|
|
151
|
+
if (current instanceof ShadowRoot) {
|
|
152
|
+
if (current.mode !== "open") {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
current = current.host;
|
|
156
|
+
continue;
|
|
164
157
|
}
|
|
165
|
-
if (!(
|
|
166
|
-
|
|
158
|
+
if (!(current instanceof Element)) {
|
|
159
|
+
current = current.parentNode;
|
|
160
|
+
continue;
|
|
167
161
|
}
|
|
168
|
-
if (
|
|
162
|
+
if (current === element && isFormControl(current) && isDisabled(current)) {
|
|
169
163
|
return true;
|
|
170
164
|
}
|
|
171
|
-
if (
|
|
165
|
+
if (current.hasAttribute("inert")) {
|
|
172
166
|
return true;
|
|
173
167
|
}
|
|
174
|
-
if (isFormControl(element) &&
|
|
175
|
-
if (
|
|
176
|
-
return
|
|
168
|
+
if (isFormControl(element) && current.tagName === "FIELDSET" && isDisabled(current)) {
|
|
169
|
+
if (!current.querySelector(":scope > legend:first-of-type")?.contains(element)) {
|
|
170
|
+
return true;
|
|
177
171
|
}
|
|
178
|
-
return true;
|
|
179
172
|
}
|
|
180
|
-
|
|
173
|
+
current = current.parentNode;
|
|
181
174
|
}
|
|
182
|
-
return
|
|
175
|
+
return false;
|
|
183
176
|
}
|
|
184
177
|
function isFormControl(element) {
|
|
185
|
-
const
|
|
186
|
-
return
|
|
178
|
+
const name = element.tagName;
|
|
179
|
+
return name === "BUTTON" || name === "INPUT" || name === "SELECT" || name === "TEXTAREA";
|
|
180
|
+
}
|
|
181
|
+
function isUngroupedRadio(element) {
|
|
182
|
+
return element instanceof HTMLInputElement && element.type === "radio" && !!element.name;
|
|
187
183
|
}
|
|
188
184
|
function normalizeRadioGroup(elements) {
|
|
189
185
|
let map = null;
|
|
190
186
|
for (let i = 0, l = elements.length; i < l; i++) {
|
|
191
187
|
const element = elements[i];
|
|
192
|
-
if (!(element
|
|
188
|
+
if (!isUngroupedRadio(element)) {
|
|
193
189
|
continue;
|
|
194
190
|
}
|
|
195
191
|
if (!map) {
|
|
@@ -207,7 +203,7 @@ function normalizeRadioGroup(elements) {
|
|
|
207
203
|
placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
|
|
208
204
|
}
|
|
209
205
|
return elements.filter((element) => {
|
|
210
|
-
if (element
|
|
206
|
+
if (isUngroupedRadio(element)) {
|
|
211
207
|
return placeholder.has(element);
|
|
212
208
|
}
|
|
213
209
|
return true;
|
|
@@ -237,7 +233,7 @@ function sortByTabIndex(elements) {
|
|
|
237
233
|
* High-precision focus management utility with shadow DOM support.
|
|
238
234
|
* Handles complex focus rules including tabindex ordering, radio groups, etc.
|
|
239
235
|
*
|
|
240
|
-
* @version 2.1.
|
|
236
|
+
* @version 2.1.5
|
|
241
237
|
* @author Yusuke Kamiyamane
|
|
242
238
|
* @license MIT
|
|
243
239
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/dist/index.d.cts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* High-precision focus management utility with shadow DOM support.
|
|
4
4
|
* Handles complex focus rules including tabindex ordering, radio groups, etc.
|
|
5
5
|
*
|
|
6
|
-
* @version 2.1.
|
|
6
|
+
* @version 2.1.5
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* High-precision focus management utility with shadow DOM support.
|
|
4
4
|
* Handles complex focus rules including tabindex ordering, radio groups, etc.
|
|
5
5
|
*
|
|
6
|
-
* @version 2.1.
|
|
6
|
+
* @version 2.1.5
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/dist/index.js
CHANGED
|
@@ -8,30 +8,31 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
8
8
|
const { composed = false } = options;
|
|
9
9
|
const elements = [];
|
|
10
10
|
if (composed) {
|
|
11
|
-
let
|
|
11
|
+
let traverse2 = function(node) {
|
|
12
12
|
if (node instanceof HTMLElement) {
|
|
13
13
|
if (isFocusable(node)) {
|
|
14
14
|
elements[elements.length] = node;
|
|
15
15
|
}
|
|
16
16
|
const shadow = node.shadowRoot;
|
|
17
17
|
if (shadow && shadow.mode === "open") {
|
|
18
|
-
|
|
18
|
+
traverse2(shadow);
|
|
19
19
|
}
|
|
20
|
-
}
|
|
20
|
+
}
|
|
21
|
+
if (node instanceof HTMLSlotElement) {
|
|
21
22
|
const assigned = node.assignedElements({ flatten: true });
|
|
22
23
|
if (assigned.length) {
|
|
23
24
|
for (let i = 0, l = assigned.length; i < l; i++) {
|
|
24
|
-
|
|
25
|
+
traverse2(assigned[i]);
|
|
25
26
|
}
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
const children = node.childNodes;
|
|
30
31
|
for (let i = 0, l = children.length; i < l; i++) {
|
|
31
|
-
|
|
32
|
+
traverse2(children[i]);
|
|
32
33
|
}
|
|
33
34
|
};
|
|
34
|
-
|
|
35
|
+
traverse2(container);
|
|
35
36
|
} else {
|
|
36
37
|
const candidates = container.querySelectorAll(FOCUSABLE_SELECTOR);
|
|
37
38
|
for (let i = 0, l = candidates.length; i < l; i++) {
|
|
@@ -73,10 +74,8 @@ function isFocusable(element) {
|
|
|
73
74
|
return false;
|
|
74
75
|
}
|
|
75
76
|
const index = element.getAttribute("tabindex");
|
|
76
|
-
if (index) {
|
|
77
|
-
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
77
|
+
if (index && Number(index) < 0) {
|
|
78
|
+
return false;
|
|
80
79
|
}
|
|
81
80
|
if (!element.matches(FOCUSABLE_SELECTOR)) {
|
|
82
81
|
return false;
|
|
@@ -115,29 +114,21 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
115
114
|
return focusables[(offsetIndex + length) % length] ?? null;
|
|
116
115
|
}
|
|
117
116
|
function containsDeep(container, element) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
if (node === container) {
|
|
117
|
+
let current = element;
|
|
118
|
+
while (current) {
|
|
119
|
+
if (current === container) {
|
|
123
120
|
return true;
|
|
124
121
|
}
|
|
125
|
-
|
|
126
|
-
return node.mode === "open" ? walk(node.host) : false;
|
|
127
|
-
}
|
|
128
|
-
return walk(node.parentNode);
|
|
122
|
+
current = current instanceof ShadowRoot ? current.mode === "open" ? current.host : null : current.parentNode;
|
|
129
123
|
}
|
|
130
|
-
return
|
|
124
|
+
return false;
|
|
131
125
|
}
|
|
132
126
|
function getActiveElement() {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
const active = node.shadowRoot?.activeElement;
|
|
138
|
-
return active ? walk(active) : node;
|
|
127
|
+
let current = document.activeElement;
|
|
128
|
+
while (current?.shadowRoot?.activeElement) {
|
|
129
|
+
current = current.shadowRoot.activeElement;
|
|
139
130
|
}
|
|
140
|
-
return
|
|
131
|
+
return current;
|
|
141
132
|
}
|
|
142
133
|
var tabIndexCache = /* @__PURE__ */ new WeakMap();
|
|
143
134
|
function getTabIndex(element) {
|
|
@@ -150,44 +141,49 @@ function getTabIndex(element) {
|
|
|
150
141
|
return index;
|
|
151
142
|
}
|
|
152
143
|
function isDisabled(element) {
|
|
153
|
-
return "disabled" in element && element.disabled;
|
|
144
|
+
return "disabled" in element && !!element.disabled;
|
|
154
145
|
}
|
|
155
146
|
function isDisabledDeep(element) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
147
|
+
let current = element;
|
|
148
|
+
while (current) {
|
|
149
|
+
if (current instanceof ShadowRoot) {
|
|
150
|
+
if (current.mode !== "open") {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
current = current.host;
|
|
154
|
+
continue;
|
|
162
155
|
}
|
|
163
|
-
if (!(
|
|
164
|
-
|
|
156
|
+
if (!(current instanceof Element)) {
|
|
157
|
+
current = current.parentNode;
|
|
158
|
+
continue;
|
|
165
159
|
}
|
|
166
|
-
if (
|
|
160
|
+
if (current === element && isFormControl(current) && isDisabled(current)) {
|
|
167
161
|
return true;
|
|
168
162
|
}
|
|
169
|
-
if (
|
|
163
|
+
if (current.hasAttribute("inert")) {
|
|
170
164
|
return true;
|
|
171
165
|
}
|
|
172
|
-
if (isFormControl(element) &&
|
|
173
|
-
if (
|
|
174
|
-
return
|
|
166
|
+
if (isFormControl(element) && current.tagName === "FIELDSET" && isDisabled(current)) {
|
|
167
|
+
if (!current.querySelector(":scope > legend:first-of-type")?.contains(element)) {
|
|
168
|
+
return true;
|
|
175
169
|
}
|
|
176
|
-
return true;
|
|
177
170
|
}
|
|
178
|
-
|
|
171
|
+
current = current.parentNode;
|
|
179
172
|
}
|
|
180
|
-
return
|
|
173
|
+
return false;
|
|
181
174
|
}
|
|
182
175
|
function isFormControl(element) {
|
|
183
|
-
const
|
|
184
|
-
return
|
|
176
|
+
const name = element.tagName;
|
|
177
|
+
return name === "BUTTON" || name === "INPUT" || name === "SELECT" || name === "TEXTAREA";
|
|
178
|
+
}
|
|
179
|
+
function isUngroupedRadio(element) {
|
|
180
|
+
return element instanceof HTMLInputElement && element.type === "radio" && !!element.name;
|
|
185
181
|
}
|
|
186
182
|
function normalizeRadioGroup(elements) {
|
|
187
183
|
let map = null;
|
|
188
184
|
for (let i = 0, l = elements.length; i < l; i++) {
|
|
189
185
|
const element = elements[i];
|
|
190
|
-
if (!(element
|
|
186
|
+
if (!isUngroupedRadio(element)) {
|
|
191
187
|
continue;
|
|
192
188
|
}
|
|
193
189
|
if (!map) {
|
|
@@ -205,7 +201,7 @@ function normalizeRadioGroup(elements) {
|
|
|
205
201
|
placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
|
|
206
202
|
}
|
|
207
203
|
return elements.filter((element) => {
|
|
208
|
-
if (element
|
|
204
|
+
if (isUngroupedRadio(element)) {
|
|
209
205
|
return placeholder.has(element);
|
|
210
206
|
}
|
|
211
207
|
return true;
|
|
@@ -235,7 +231,7 @@ function sortByTabIndex(elements) {
|
|
|
235
231
|
* High-precision focus management utility with shadow DOM support.
|
|
236
232
|
* Handles complex focus rules including tabindex ordering, radio groups, etc.
|
|
237
233
|
*
|
|
238
|
-
* @version 2.1.
|
|
234
|
+
* @version 2.1.5
|
|
239
235
|
* @author Yusuke Kamiyamane
|
|
240
236
|
* @license MIT
|
|
241
237
|
* @copyright Copyright (c) Yusuke Kamiyamane
|