power-focusable 2.1.2 → 2.1.4
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 +24 -28
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +24 -28
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -21,7 +21,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
21
21
|
}
|
|
22
22
|
} else if (node instanceof HTMLSlotElement) {
|
|
23
23
|
const assigned = node.assignedElements({ flatten: true });
|
|
24
|
-
if (assigned.length
|
|
24
|
+
if (assigned.length) {
|
|
25
25
|
for (let i = 0, l = assigned.length; i < l; i++) {
|
|
26
26
|
walk2(assigned[i]);
|
|
27
27
|
}
|
|
@@ -64,7 +64,7 @@ function hasFocusable(container = document.body, options = {}) {
|
|
|
64
64
|
console.warn("Invalid container element. Fallback: <body> element.");
|
|
65
65
|
container = document.body;
|
|
66
66
|
}
|
|
67
|
-
return getFocusables(container, options).length
|
|
67
|
+
return !!getFocusables(container, options).length;
|
|
68
68
|
}
|
|
69
69
|
function isFocusable(element) {
|
|
70
70
|
if (!(element instanceof HTMLElement)) {
|
|
@@ -74,9 +74,9 @@ function isFocusable(element) {
|
|
|
74
74
|
if (element.hasAttribute("hidden") || element.hasAttribute("inert")) {
|
|
75
75
|
return false;
|
|
76
76
|
}
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
79
|
-
if (Number(
|
|
77
|
+
const index = element.getAttribute("tabindex");
|
|
78
|
+
if (index) {
|
|
79
|
+
if (Number(index) < 0) {
|
|
80
80
|
return false;
|
|
81
81
|
}
|
|
82
82
|
}
|
|
@@ -95,15 +95,15 @@ function isFocusable(element) {
|
|
|
95
95
|
}
|
|
96
96
|
return true;
|
|
97
97
|
}
|
|
98
|
-
function getRelativeFocusable(container, offset
|
|
98
|
+
function getRelativeFocusable(container, offset, options) {
|
|
99
99
|
const { active: a = null, composed = false, wrap = false } = options;
|
|
100
100
|
const focusables = getFocusables(container, { composed });
|
|
101
101
|
const { length } = focusables;
|
|
102
|
-
if (length
|
|
102
|
+
if (!length) {
|
|
103
103
|
return null;
|
|
104
104
|
}
|
|
105
105
|
const active = a ?? getActiveElement();
|
|
106
|
-
if (active
|
|
106
|
+
if (!active || !containsDeep(container, active)) {
|
|
107
107
|
return null;
|
|
108
108
|
}
|
|
109
109
|
const currentIndex = focusables.indexOf(active);
|
|
@@ -118,7 +118,7 @@ function getRelativeFocusable(container, offset = 0, options) {
|
|
|
118
118
|
}
|
|
119
119
|
function containsDeep(container, element) {
|
|
120
120
|
function walk(node) {
|
|
121
|
-
if (node
|
|
121
|
+
if (!node) {
|
|
122
122
|
return false;
|
|
123
123
|
}
|
|
124
124
|
if (node === container) {
|
|
@@ -133,7 +133,7 @@ function containsDeep(container, element) {
|
|
|
133
133
|
}
|
|
134
134
|
function getActiveElement() {
|
|
135
135
|
function walk(node) {
|
|
136
|
-
if (node
|
|
136
|
+
if (!node) {
|
|
137
137
|
return null;
|
|
138
138
|
}
|
|
139
139
|
const active = node.shadowRoot?.activeElement;
|
|
@@ -147,16 +147,16 @@ function getTabIndex(element) {
|
|
|
147
147
|
if (cached !== void 0) {
|
|
148
148
|
return cached;
|
|
149
149
|
}
|
|
150
|
-
const
|
|
151
|
-
tabIndexCache.set(element,
|
|
152
|
-
return
|
|
150
|
+
const index = Number(element.getAttribute("tabindex"));
|
|
151
|
+
tabIndexCache.set(element, index);
|
|
152
|
+
return index;
|
|
153
153
|
}
|
|
154
154
|
function isDisabled(element) {
|
|
155
155
|
return "disabled" in element && element.disabled;
|
|
156
156
|
}
|
|
157
157
|
function isDisabledDeep(element) {
|
|
158
158
|
function walk(node) {
|
|
159
|
-
if (node
|
|
159
|
+
if (!node) {
|
|
160
160
|
return false;
|
|
161
161
|
}
|
|
162
162
|
if (node instanceof ShadowRoot) {
|
|
@@ -189,26 +189,22 @@ function normalizeRadioGroup(elements) {
|
|
|
189
189
|
let map = null;
|
|
190
190
|
for (let i = 0, l = elements.length; i < l; i++) {
|
|
191
191
|
const element = elements[i];
|
|
192
|
-
if (element instanceof HTMLInputElement
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const group = map.get(key) ?? map.set(key, []).get(key);
|
|
198
|
-
group[group.length] = element;
|
|
192
|
+
if (!(element instanceof HTMLInputElement) || element.type !== "radio" || !element.name) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (!map) {
|
|
196
|
+
map = /* @__PURE__ */ new Map();
|
|
199
197
|
}
|
|
198
|
+
const key = `${element.form?.id ?? "no-form"}::${element.name}`;
|
|
199
|
+
const group = map.get(key) ?? map.set(key, []).get(key);
|
|
200
|
+
group[group.length] = element;
|
|
200
201
|
}
|
|
201
202
|
if (!map) {
|
|
202
203
|
return elements;
|
|
203
204
|
}
|
|
204
205
|
const placeholder = /* @__PURE__ */ new Set();
|
|
205
206
|
for (const group of map.values()) {
|
|
206
|
-
|
|
207
|
-
const enabled = group.filter(isFocusable);
|
|
208
|
-
if (enabled.length > 0) {
|
|
209
|
-
placeholder.add(enabled.find((radio) => radio.checked) ?? enabled[0]);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
207
|
+
placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
|
|
212
208
|
}
|
|
213
209
|
return elements.filter((element) => {
|
|
214
210
|
if (element instanceof HTMLInputElement && element.type === "radio" && element.name) {
|
|
@@ -241,7 +237,7 @@ function sortByTabIndex(elements) {
|
|
|
241
237
|
* High-precision focus management utility with shadow DOM support.
|
|
242
238
|
* Handles complex focus rules including tabindex ordering, radio groups, etc.
|
|
243
239
|
*
|
|
244
|
-
* @version 2.1.
|
|
240
|
+
* @version 2.1.4
|
|
245
241
|
* @author Yusuke Kamiyamane
|
|
246
242
|
* @license MIT
|
|
247
243
|
* @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.4
|
|
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.4
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
19
19
|
}
|
|
20
20
|
} else if (node instanceof HTMLSlotElement) {
|
|
21
21
|
const assigned = node.assignedElements({ flatten: true });
|
|
22
|
-
if (assigned.length
|
|
22
|
+
if (assigned.length) {
|
|
23
23
|
for (let i = 0, l = assigned.length; i < l; i++) {
|
|
24
24
|
walk2(assigned[i]);
|
|
25
25
|
}
|
|
@@ -62,7 +62,7 @@ function hasFocusable(container = document.body, options = {}) {
|
|
|
62
62
|
console.warn("Invalid container element. Fallback: <body> element.");
|
|
63
63
|
container = document.body;
|
|
64
64
|
}
|
|
65
|
-
return getFocusables(container, options).length
|
|
65
|
+
return !!getFocusables(container, options).length;
|
|
66
66
|
}
|
|
67
67
|
function isFocusable(element) {
|
|
68
68
|
if (!(element instanceof HTMLElement)) {
|
|
@@ -72,9 +72,9 @@ function isFocusable(element) {
|
|
|
72
72
|
if (element.hasAttribute("hidden") || element.hasAttribute("inert")) {
|
|
73
73
|
return false;
|
|
74
74
|
}
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
77
|
-
if (Number(
|
|
75
|
+
const index = element.getAttribute("tabindex");
|
|
76
|
+
if (index) {
|
|
77
|
+
if (Number(index) < 0) {
|
|
78
78
|
return false;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -93,15 +93,15 @@ function isFocusable(element) {
|
|
|
93
93
|
}
|
|
94
94
|
return true;
|
|
95
95
|
}
|
|
96
|
-
function getRelativeFocusable(container, offset
|
|
96
|
+
function getRelativeFocusable(container, offset, options) {
|
|
97
97
|
const { active: a = null, composed = false, wrap = false } = options;
|
|
98
98
|
const focusables = getFocusables(container, { composed });
|
|
99
99
|
const { length } = focusables;
|
|
100
|
-
if (length
|
|
100
|
+
if (!length) {
|
|
101
101
|
return null;
|
|
102
102
|
}
|
|
103
103
|
const active = a ?? getActiveElement();
|
|
104
|
-
if (active
|
|
104
|
+
if (!active || !containsDeep(container, active)) {
|
|
105
105
|
return null;
|
|
106
106
|
}
|
|
107
107
|
const currentIndex = focusables.indexOf(active);
|
|
@@ -116,7 +116,7 @@ function getRelativeFocusable(container, offset = 0, options) {
|
|
|
116
116
|
}
|
|
117
117
|
function containsDeep(container, element) {
|
|
118
118
|
function walk(node) {
|
|
119
|
-
if (node
|
|
119
|
+
if (!node) {
|
|
120
120
|
return false;
|
|
121
121
|
}
|
|
122
122
|
if (node === container) {
|
|
@@ -131,7 +131,7 @@ function containsDeep(container, element) {
|
|
|
131
131
|
}
|
|
132
132
|
function getActiveElement() {
|
|
133
133
|
function walk(node) {
|
|
134
|
-
if (node
|
|
134
|
+
if (!node) {
|
|
135
135
|
return null;
|
|
136
136
|
}
|
|
137
137
|
const active = node.shadowRoot?.activeElement;
|
|
@@ -145,16 +145,16 @@ function getTabIndex(element) {
|
|
|
145
145
|
if (cached !== void 0) {
|
|
146
146
|
return cached;
|
|
147
147
|
}
|
|
148
|
-
const
|
|
149
|
-
tabIndexCache.set(element,
|
|
150
|
-
return
|
|
148
|
+
const index = Number(element.getAttribute("tabindex"));
|
|
149
|
+
tabIndexCache.set(element, index);
|
|
150
|
+
return index;
|
|
151
151
|
}
|
|
152
152
|
function isDisabled(element) {
|
|
153
153
|
return "disabled" in element && element.disabled;
|
|
154
154
|
}
|
|
155
155
|
function isDisabledDeep(element) {
|
|
156
156
|
function walk(node) {
|
|
157
|
-
if (node
|
|
157
|
+
if (!node) {
|
|
158
158
|
return false;
|
|
159
159
|
}
|
|
160
160
|
if (node instanceof ShadowRoot) {
|
|
@@ -187,26 +187,22 @@ function normalizeRadioGroup(elements) {
|
|
|
187
187
|
let map = null;
|
|
188
188
|
for (let i = 0, l = elements.length; i < l; i++) {
|
|
189
189
|
const element = elements[i];
|
|
190
|
-
if (element instanceof HTMLInputElement
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const group = map.get(key) ?? map.set(key, []).get(key);
|
|
196
|
-
group[group.length] = element;
|
|
190
|
+
if (!(element instanceof HTMLInputElement) || element.type !== "radio" || !element.name) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (!map) {
|
|
194
|
+
map = /* @__PURE__ */ new Map();
|
|
197
195
|
}
|
|
196
|
+
const key = `${element.form?.id ?? "no-form"}::${element.name}`;
|
|
197
|
+
const group = map.get(key) ?? map.set(key, []).get(key);
|
|
198
|
+
group[group.length] = element;
|
|
198
199
|
}
|
|
199
200
|
if (!map) {
|
|
200
201
|
return elements;
|
|
201
202
|
}
|
|
202
203
|
const placeholder = /* @__PURE__ */ new Set();
|
|
203
204
|
for (const group of map.values()) {
|
|
204
|
-
|
|
205
|
-
const enabled = group.filter(isFocusable);
|
|
206
|
-
if (enabled.length > 0) {
|
|
207
|
-
placeholder.add(enabled.find((radio) => radio.checked) ?? enabled[0]);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
205
|
+
placeholder.add(group.find((radio) => radio.checked) ?? group[0]);
|
|
210
206
|
}
|
|
211
207
|
return elements.filter((element) => {
|
|
212
208
|
if (element instanceof HTMLInputElement && element.type === "radio" && element.name) {
|
|
@@ -239,7 +235,7 @@ function sortByTabIndex(elements) {
|
|
|
239
235
|
* High-precision focus management utility with shadow DOM support.
|
|
240
236
|
* Handles complex focus rules including tabindex ordering, radio groups, etc.
|
|
241
237
|
*
|
|
242
|
-
* @version 2.1.
|
|
238
|
+
* @version 2.1.4
|
|
243
239
|
* @author Yusuke Kamiyamane
|
|
244
240
|
* @license MIT
|
|
245
241
|
* @copyright Copyright (c) Yusuke Kamiyamane
|