@wordpress/dom 4.55.0 → 4.55.2-next.v.202609171837.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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 4.55.1-next.0 (2026-09-17)
6
+
7
+ ### Bug Fixes
8
+
9
+ - `getRectangleFromRange`: Measure a collapsed range on an element at the text position next to it instead of inserting a temporary text node under the caret ([#82977](https://github.com/WordPress/gutenberg/pull/82977)).
10
+ - `focusable.find`: Return linked image-map areas when their mapped image is visible and outside an inert subtree ([#82736](https://github.com/WordPress/gutenberg/pull/82736)).
11
+
5
12
  ## 4.55.0 (2026-09-10)
6
13
 
7
14
  ### Bug Fixes
package/README.md CHANGED
@@ -20,11 +20,11 @@ Get the rectangle for the selection in a container.
20
20
 
21
21
  _Parameters_
22
22
 
23
- - _win_ `Window`: The window of the selection.
23
+ - _win_ `Window`: The window of the selection.
24
24
 
25
25
  _Returns_
26
26
 
27
- - `DOMRect | null`: The rectangle.
27
+ - `DOMRect | null`: The rectangle.
28
28
 
29
29
  ### documentHasSelection
30
30
 
@@ -32,11 +32,11 @@ Check whether the current document has a selection. This includes focus in input
32
32
 
33
33
  _Parameters_
34
34
 
35
- - _doc_ `Document`: The document to check.
35
+ - _doc_ `Document`: The document to check.
36
36
 
37
37
  _Returns_
38
38
 
39
- - `boolean`: True if there is selection, false if not.
39
+ - `boolean`: True if there is selection, false if not.
40
40
 
41
41
  ### documentHasTextSelection
42
42
 
@@ -46,11 +46,11 @@ See: <https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection#Relat
46
46
 
47
47
  _Parameters_
48
48
 
49
- - _doc_ `Document`: The document to check.
49
+ - _doc_ `Document`: The document to check.
50
50
 
51
51
  _Returns_
52
52
 
53
- - `boolean`: True if there is selection, false if not.
53
+ - `boolean`: True if there is selection, false if not.
54
54
 
55
55
  ### documentHasUncollapsedSelection
56
56
 
@@ -58,11 +58,11 @@ Check whether the current document has any sort of (uncollapsed) selection. This
58
58
 
59
59
  _Parameters_
60
60
 
61
- - _doc_ `Document`: The document to check.
61
+ - _doc_ `Document`: The document to check.
62
62
 
63
63
  _Returns_
64
64
 
65
- - `boolean`: Whether there is any recognizable text selection in the document.
65
+ - `boolean`: Whether there is any recognizable text selection in the document.
66
66
 
67
67
  ### focus
68
68
 
@@ -74,11 +74,11 @@ Gets all files from a DataTransfer object.
74
74
 
75
75
  _Parameters_
76
76
 
77
- - _dataTransfer_ `DataTransfer`: DataTransfer object to inspect.
77
+ - _dataTransfer_ `DataTransfer`: DataTransfer object to inspect.
78
78
 
79
79
  _Returns_
80
80
 
81
- - `File[]`: An array containing all files.
81
+ - `File[]`: An array containing all files.
82
82
 
83
83
  ### getOffsetParent
84
84
 
@@ -86,15 +86,15 @@ Returns the closest positioned element, or null under any of the conditions of t
86
86
 
87
87
  _Related_
88
88
 
89
- - <https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent>
89
+ - <https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent>
90
90
 
91
91
  _Parameters_
92
92
 
93
- - _node_ `Node`: Node from which to find offset parent.
93
+ - _node_ `Node`: Node from which to find offset parent.
94
94
 
95
95
  _Returns_
96
96
 
97
- - `Node | null`: Offset parent.
97
+ - `Node | null`: Offset parent.
98
98
 
99
99
  ### getPhrasingContentSchema
100
100
 
@@ -102,15 +102,15 @@ Get schema of possible paths for phrasing content.
102
102
 
103
103
  _Related_
104
104
 
105
- - <https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content>
105
+ - <https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content>
106
106
 
107
107
  _Parameters_
108
108
 
109
- - _context_ `[string]`: Set to "paste" to exclude invisible elements and sensitive data.
109
+ - _context_ `[string]`: Set to "paste" to exclude invisible elements and sensitive data.
110
110
 
111
111
  _Returns_
112
112
 
113
- - `Partial<ContentSchema>`: Schema.
113
+ - `Partial<ContentSchema>`: Schema.
114
114
 
115
115
  ### getRectangleFromRange
116
116
 
@@ -118,11 +118,11 @@ Get the rectangle of a given Range. Returns `null` if no suitable rectangle can
118
118
 
119
119
  _Parameters_
120
120
 
121
- - _range_ `Range`: The range.
121
+ - _range_ `Range`: The range.
122
122
 
123
123
  _Returns_
124
124
 
125
- - `DOMRect?`: The rectangle.
125
+ - `DOMRect?`: The rectangle.
126
126
 
127
127
  ### getScrollContainer
128
128
 
@@ -130,12 +130,12 @@ Given a DOM node, finds the closest scrollable container node or the node itself
130
130
 
131
131
  _Parameters_
132
132
 
133
- - _node_ `Element | null`: Node from which to start.
134
- - _direction_ `?string`: Direction of scrollable container to search for ('vertical', 'horizontal', 'all'). Defaults to 'vertical'.
133
+ - _node_ `Element | null`: Node from which to start.
134
+ - _direction_ `?string`: Direction of scrollable container to search for ('vertical', 'horizontal', 'all'). Defaults to 'vertical'.
135
135
 
136
136
  _Returns_
137
137
 
138
- - `Element | undefined`: Scrollable container node, if found.
138
+ - `Element | undefined`: Scrollable container node, if found.
139
139
 
140
140
  ### insertAfter
141
141
 
@@ -143,12 +143,12 @@ Given two DOM nodes, inserts the former in the DOM as the next sibling of the la
143
143
 
144
144
  _Parameters_
145
145
 
146
- - _newNode_ `Node`: Node to be inserted.
147
- - _referenceNode_ `Node`: Node after which to perform the insertion.
146
+ - _newNode_ `Node`: Node to be inserted.
147
+ - _referenceNode_ `Node`: Node after which to perform the insertion.
148
148
 
149
149
  _Returns_
150
150
 
151
- - `void`:
151
+ - `void`:
152
152
 
153
153
  ### isEmpty
154
154
 
@@ -156,11 +156,11 @@ Recursively checks if an element is empty. An element is not empty if it contain
156
156
 
157
157
  _Parameters_
158
158
 
159
- - _element_ `Element`: The element to check.
159
+ - _element_ `Element`: The element to check.
160
160
 
161
161
  _Returns_
162
162
 
163
- - `boolean`: Whether or not the element is empty.
163
+ - `boolean`: Whether or not the element is empty.
164
164
 
165
165
  ### isEntirelySelected
166
166
 
@@ -168,11 +168,11 @@ Check whether the contents of the element have been entirely selected. Returns t
168
168
 
169
169
  _Parameters_
170
170
 
171
- - _element_ `HTMLElement`: The element to check.
171
+ - _element_ `HTMLElement`: The element to check.
172
172
 
173
173
  _Returns_
174
174
 
175
- - `boolean`: True if entirely selected, false if not.
175
+ - `boolean`: True if entirely selected, false if not.
176
176
 
177
177
  ### isFormElement
178
178
 
@@ -180,11 +180,11 @@ Detects if element is a form element.
180
180
 
181
181
  _Parameters_
182
182
 
183
- - _element_ `Element`: The element to check.
183
+ - _element_ `Element`: The element to check.
184
184
 
185
185
  _Returns_
186
186
 
187
- - `boolean`: True if form element and false otherwise.
187
+ - `boolean`: True if form element and false otherwise.
188
188
 
189
189
  ### isHorizontalEdge
190
190
 
@@ -192,12 +192,12 @@ Check whether the selection is horizontally at the edge of the container.
192
192
 
193
193
  _Parameters_
194
194
 
195
- - _container_ `HTMLElement`: Focusable element.
196
- - _isReverse_ `boolean`: Set to true to check left, false for right.
195
+ - _container_ `HTMLElement`: Focusable element.
196
+ - _isReverse_ `boolean`: Set to true to check left, false for right.
197
197
 
198
198
  _Returns_
199
199
 
200
- - `boolean`: True if at the horizontal edge, false if not.
200
+ - `boolean`: True if at the horizontal edge, false if not.
201
201
 
202
202
  ### isNumberInput
203
203
 
@@ -205,11 +205,11 @@ Check whether the given element is an input field of type number.
205
205
 
206
206
  _Parameters_
207
207
 
208
- - _node_ `Node`: The HTML node.
208
+ - _node_ `Node`: The HTML node.
209
209
 
210
210
  _Returns_
211
211
 
212
- - `node is HTMLInputElement`: True if the node is number input.
212
+ - `node is HTMLInputElement`: True if the node is number input.
213
213
 
214
214
  ### isPhrasingContent
215
215
 
@@ -217,15 +217,15 @@ Find out whether or not the given node is phrasing content.
217
217
 
218
218
  _Related_
219
219
 
220
- - <https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content>
220
+ - <https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Phrasing_content>
221
221
 
222
222
  _Parameters_
223
223
 
224
- - _node_ `Node`: The node to test.
224
+ - _node_ `Node`: The node to test.
225
225
 
226
226
  _Returns_
227
227
 
228
- - `boolean`: True if phrasing content, false if not.
228
+ - `boolean`: True if phrasing content, false if not.
229
229
 
230
230
  ### isRTL
231
231
 
@@ -233,11 +233,11 @@ Whether the element's text direction is right-to-left.
233
233
 
234
234
  _Parameters_
235
235
 
236
- - _element_ `Element`: The element to check.
236
+ - _element_ `Element`: The element to check.
237
237
 
238
238
  _Returns_
239
239
 
240
- - `boolean`: True if rtl, false if ltr.
240
+ - `boolean`: True if rtl, false if ltr.
241
241
 
242
242
  ### isSelectionForward
243
243
 
@@ -245,25 +245,25 @@ Returns true if the given selection object is in the forward direction, or false
245
245
 
246
246
  _Related_
247
247
 
248
- - <https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition>
248
+ - <https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition>
249
249
 
250
250
  _Parameters_
251
251
 
252
- - _selection_ `Selection`: Selection object to check.
252
+ - _selection_ `Selection`: Selection object to check.
253
253
 
254
254
  _Returns_
255
255
 
256
- - `boolean`: Whether the selection is forward.
256
+ - `boolean`: Whether the selection is forward.
257
257
 
258
258
  ### isTextContent
259
259
 
260
260
  _Parameters_
261
261
 
262
- - _node_ `Node`:
262
+ - _node_ `Node`:
263
263
 
264
264
  _Returns_
265
265
 
266
- - `boolean`: Node is text content
266
+ - `boolean`: Node is text content
267
267
 
268
268
  ### isTextField
269
269
 
@@ -273,11 +273,11 @@ See: <https://html.spec.whatwg.org/#textFieldSelection>
273
273
 
274
274
  _Parameters_
275
275
 
276
- - _node_ `Node`: The HTML element.
276
+ - _node_ `Node`: The HTML element.
277
277
 
278
278
  _Returns_
279
279
 
280
- - `node is HTMLElement`: True if the element is an text field, false if not.
280
+ - `node is HTMLElement`: True if the element is an text field, false if not.
281
281
 
282
282
  ### isVerticalEdge
283
283
 
@@ -285,12 +285,12 @@ Check whether the selection is vertically at the edge of the container.
285
285
 
286
286
  _Parameters_
287
287
 
288
- - _container_ `HTMLElement`: Focusable element.
289
- - _isReverse_ `boolean`: Set to true to check top, false for bottom.
288
+ - _container_ `HTMLElement`: Focusable element.
289
+ - _isReverse_ `boolean`: Set to true to check top, false for bottom.
290
290
 
291
291
  _Returns_
292
292
 
293
- - `boolean`: True if at the vertical edge, false if not.
293
+ - `boolean`: True if at the vertical edge, false if not.
294
294
 
295
295
  ### placeCaretAtHorizontalEdge
296
296
 
@@ -298,8 +298,8 @@ Places the caret at start or end of a given element.
298
298
 
299
299
  _Parameters_
300
300
 
301
- - _container_ `HTMLElement`: Focusable element.
302
- - _isReverse_ `boolean`: True for end, false for start.
301
+ - _container_ `HTMLElement`: Focusable element.
302
+ - _isReverse_ `boolean`: True for end, false for start.
303
303
 
304
304
  ### placeCaretAtVerticalEdge
305
305
 
@@ -307,9 +307,9 @@ Places the caret at the top or bottom of a given element.
307
307
 
308
308
  _Parameters_
309
309
 
310
- - _container_ `HTMLElement`: Focusable element.
311
- - _isReverse_ `boolean`: True for bottom, false for top.
312
- - _rect_ `[DOMRect]`: The rectangle to position the caret with.
310
+ - _container_ `HTMLElement`: Focusable element.
311
+ - _isReverse_ `boolean`: True for bottom, false for top.
312
+ - _rect_ `[DOMRect]`: The rectangle to position the caret with.
313
313
 
314
314
  ### remove
315
315
 
@@ -317,11 +317,11 @@ Given a DOM node, removes it from the DOM.
317
317
 
318
318
  _Parameters_
319
319
 
320
- - _node_ `Node`: Node to be removed.
320
+ - _node_ `Node`: Node to be removed.
321
321
 
322
322
  _Returns_
323
323
 
324
- - `void`:
324
+ - `void`:
325
325
 
326
326
  ### removeInvalidHTML
327
327
 
@@ -329,13 +329,13 @@ Given a schema, unwraps or removes nodes, attributes and classes on HTML.
329
329
 
330
330
  _Parameters_
331
331
 
332
- - _HTML_ `string`: The HTML to clean up.
333
- - _schema_ `import('./clean-node-list').Schema`: Schema for the HTML.
334
- - _inline_ `boolean`: Whether to clean for inline mode.
332
+ - _HTML_ `string`: The HTML to clean up.
333
+ - _schema_ `import('./clean-node-list').Schema`: Schema for the HTML.
334
+ - _inline_ `boolean`: Whether to clean for inline mode.
335
335
 
336
336
  _Returns_
337
337
 
338
- - `string`: The cleaned up HTML.
338
+ - `string`: The cleaned up HTML.
339
339
 
340
340
  ### replace
341
341
 
@@ -343,12 +343,12 @@ Given two DOM nodes, replaces the former with the latter in the DOM.
343
343
 
344
344
  _Parameters_
345
345
 
346
- - _processedNode_ `Element`: Node to be removed.
347
- - _newNode_ `Element`: Node to be inserted in its place.
346
+ - _processedNode_ `Element`: Node to be removed.
347
+ - _newNode_ `Element`: Node to be inserted in its place.
348
348
 
349
349
  _Returns_
350
350
 
351
- - `void`:
351
+ - `void`:
352
352
 
353
353
  ### replaceTag
354
354
 
@@ -356,12 +356,12 @@ Replaces the given node with a new node with the given tag name.
356
356
 
357
357
  _Parameters_
358
358
 
359
- - _node_ `Element`: The node to replace
360
- - _tagName_ `string`: The new tag name.
359
+ - _node_ `Element`: The node to replace
360
+ - _tagName_ `string`: The new tag name.
361
361
 
362
362
  _Returns_
363
363
 
364
- - `Element`: The new node.
364
+ - `Element`: The new node.
365
365
 
366
366
  ### safeHTML
367
367
 
@@ -369,11 +369,11 @@ Strips scripts and on\* attributes from HTML.
369
369
 
370
370
  _Parameters_
371
371
 
372
- - _html_ `string`: HTML to sanitize.
372
+ - _html_ `string`: HTML to sanitize.
373
373
 
374
374
  _Returns_
375
375
 
376
- - `string`: The sanitized HTML.
376
+ - `string`: The sanitized HTML.
377
377
 
378
378
  ### unwrap
379
379
 
@@ -381,11 +381,11 @@ Unwrap the given node. This means any child nodes are moved to the parent.
381
381
 
382
382
  _Parameters_
383
383
 
384
- - _node_ `Node`: The node to unwrap.
384
+ - _node_ `Node`: The node to unwrap.
385
385
 
386
386
  _Returns_
387
387
 
388
- - `void`:
388
+ - `void`:
389
389
 
390
390
  ### wrap
391
391
 
@@ -393,8 +393,8 @@ Wraps the given node with a new node with the given tag name.
393
393
 
394
394
  _Parameters_
395
395
 
396
- - _newNode_ `Element`: The node to insert.
397
- - _referenceNode_ `Element`: The node to wrap.
396
+ - _newNode_ `Element`: The node to insert.
397
+ - _referenceNode_ `Element`: The node to wrap.
398
398
 
399
399
  <!-- END TOKEN(Autogenerated API docs) -->
400
400
 
@@ -64,19 +64,54 @@ function getRectangleFromRange(range) {
64
64
  furthestBottom - furthestTop
65
65
  );
66
66
  }
67
- const { startContainer } = range;
67
+ const { startContainer, startOffset } = range;
68
68
  const { ownerDocument } = startContainer;
69
- if (startContainer.nodeName === "BR") {
70
- const { parentNode } = startContainer;
71
- (0, import_assert_is_defined.assertIsDefined)(parentNode, "parentNode");
72
- const index = (
73
- /** @type {Node[]} */
74
- Array.from(parentNode.childNodes).indexOf(startContainer)
75
- );
76
- (0, import_assert_is_defined.assertIsDefined)(ownerDocument, "ownerDocument");
69
+ (0, import_assert_is_defined.assertIsDefined)(ownerDocument, "ownerDocument");
70
+ if (startContainer.nodeType !== startContainer.TEXT_NODE && !startContainer.childNodes.length && startContainer.parentNode) {
77
71
  range = ownerDocument.createRange();
78
- range.setStart(parentNode, index);
79
- range.setEnd(parentNode, index);
72
+ range.setStartBefore(startContainer);
73
+ range.collapse(true);
74
+ return getRectangleFromRange(range);
75
+ }
76
+ if (startContainer.nodeType !== startContainer.TEXT_NODE) {
77
+ let before = startContainer.childNodes[startOffset - 1];
78
+ while (before?.lastChild) {
79
+ before = before.lastChild;
80
+ }
81
+ let after = startContainer.childNodes[startOffset];
82
+ while (after?.firstChild) {
83
+ after = after.firstChild;
84
+ }
85
+ let beforeRange;
86
+ if (before && before.nodeType === before.TEXT_NODE) {
87
+ beforeRange = ownerDocument.createRange();
88
+ beforeRange.setStart(
89
+ before,
90
+ /** @type {Text} */
91
+ before.length
92
+ );
93
+ beforeRange.collapse(true);
94
+ }
95
+ let afterRange;
96
+ if (after && after.nodeType === after.TEXT_NODE) {
97
+ afterRange = ownerDocument.createRange();
98
+ afterRange.setStart(after, 0);
99
+ afterRange.collapse(true);
100
+ }
101
+ if (beforeRange && afterRange) {
102
+ const beforeRect = beforeRange.getClientRects()[0];
103
+ const afterRect = afterRange.getClientRects()[0];
104
+ if (beforeRect && afterRect && beforeRect.bottom <= afterRect.top) {
105
+ return null;
106
+ }
107
+ }
108
+ if (afterRange) {
109
+ range = afterRange;
110
+ } else if (beforeRange) {
111
+ range = beforeRange;
112
+ } else {
113
+ return null;
114
+ }
80
115
  }
81
116
  const rects = range.getClientRects();
82
117
  if (rects.length > 1) {
@@ -97,16 +132,6 @@ function getRectangleFromRange(range) {
97
132
  return null;
98
133
  }
99
134
  }
100
- let rect = rects[0];
101
- if (!rect || rect.height === 0) {
102
- (0, import_assert_is_defined.assertIsDefined)(ownerDocument, "ownerDocument");
103
- const padNode = ownerDocument.createTextNode("​");
104
- range = range.cloneRange();
105
- range.insertNode(padNode);
106
- rect = range.getClientRects()[0];
107
- (0, import_assert_is_defined.assertIsDefined)(padNode.parentNode, "padNode.parentNode");
108
- padNode.parentNode.removeChild(padNode);
109
- }
110
- return rect;
135
+ return rects[0] ?? null;
111
136
  }
112
137
  //# sourceMappingURL=get-rectangle-from-range.cjs.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/dom/get-rectangle-from-range.js"],
4
- "sourcesContent": ["import { assertIsDefined } from '../utils/assert-is-defined';\n\n/**\n * Get the rectangle of a given Range. Returns `null` if no suitable rectangle\n * can be found. Use instead of `Range.getBoundingClientRect()`, which is often\n * broken, especially for collapsed ranges.\n *\n * @param {Range} range The range.\n *\n * @return {DOMRect?} The rectangle.\n */\nexport default function getRectangleFromRange( range ) {\n\t// For uncollapsed ranges, get the rectangle that bounds the contents of the\n\t// range; this a rectangle enclosing the union of the bounding rectangles\n\t// for all the elements in the range.\n\tif ( ! range.collapsed ) {\n\t\tconst rects = Array.from( range.getClientRects() );\n\n\t\t// If there's just a single rect, return it.\n\t\tif ( rects.length === 1 ) {\n\t\t\treturn rects[ 0 ];\n\t\t}\n\n\t\t// Ignore tiny selection at the edge of a range.\n\t\tconst filteredRects = rects.filter( ( { width } ) => width > 1 );\n\n\t\t// If it's full of tiny selections, return browser default.\n\t\tif ( filteredRects.length === 0 ) {\n\t\t\treturn range.getBoundingClientRect();\n\t\t}\n\n\t\tif ( filteredRects.length === 1 ) {\n\t\t\treturn filteredRects[ 0 ];\n\t\t}\n\n\t\tlet {\n\t\t\ttop: furthestTop,\n\t\t\tbottom: furthestBottom,\n\t\t\tleft: furthestLeft,\n\t\t\tright: furthestRight,\n\t\t} = filteredRects[ 0 ];\n\n\t\tfor ( const { top, bottom, left, right } of filteredRects ) {\n\t\t\tif ( top < furthestTop ) {\n\t\t\t\tfurthestTop = top;\n\t\t\t}\n\t\t\tif ( bottom > furthestBottom ) {\n\t\t\t\tfurthestBottom = bottom;\n\t\t\t}\n\t\t\tif ( left < furthestLeft ) {\n\t\t\t\tfurthestLeft = left;\n\t\t\t}\n\t\t\tif ( right > furthestRight ) {\n\t\t\t\tfurthestRight = right;\n\t\t\t}\n\t\t}\n\n\t\treturn new window.DOMRect(\n\t\t\tfurthestLeft,\n\t\t\tfurthestTop,\n\t\t\tfurthestRight - furthestLeft,\n\t\t\tfurthestBottom - furthestTop\n\t\t);\n\t}\n\n\tconst { startContainer } = range;\n\tconst { ownerDocument } = startContainer;\n\n\t// Correct invalid \"BR\" ranges. The cannot contain any children.\n\tif ( startContainer.nodeName === 'BR' ) {\n\t\tconst { parentNode } = startContainer;\n\t\tassertIsDefined( parentNode, 'parentNode' );\n\t\tconst index = /** @type {Node[]} */ (\n\t\t\tArray.from( parentNode.childNodes )\n\t\t).indexOf( startContainer );\n\n\t\tassertIsDefined( ownerDocument, 'ownerDocument' );\n\t\trange = ownerDocument.createRange();\n\t\trange.setStart( parentNode, index );\n\t\trange.setEnd( parentNode, index );\n\t}\n\n\tconst rects = range.getClientRects();\n\n\t// If we have multiple rectangles for a collapsed range, there's no way to\n\t// know which it is, so don't return anything.\n\tif ( rects.length > 1 ) {\n\t\treturn null;\n\t}\n\n\t// A collapsed range at a soft line wrap is equally ambiguous: the same\n\t// position ends one line and starts the next. Some browsers (Gecko)\n\t// return a single rectangle for it, on the upper line, regardless of\n\t// where the caret is. Detect the boundary by measuring the characters\n\t// around the position: when they sit on different lines, there is no\n\t// way to know which line the caret is on, so don't return anything.\n\tif (\n\t\trects.length === 1 &&\n\t\tstartContainer.nodeType === startContainer.TEXT_NODE &&\n\t\trange.startOffset > 0 &&\n\t\trange.startOffset < /** @type {Text} */ ( startContainer ).length\n\t) {\n\t\tassertIsDefined( ownerDocument, 'ownerDocument' );\n\t\tconst measure = (\n\t\t\t/** @type {number} */ start,\n\t\t\t/** @type {number} */ end\n\t\t) => {\n\t\t\tconst charRange = ownerDocument.createRange();\n\t\t\tcharRange.setStart( startContainer, start );\n\t\t\tcharRange.setEnd( startContainer, end );\n\t\t\treturn charRange.getBoundingClientRect();\n\t\t};\n\t\tconst before = measure( range.startOffset - 1, range.startOffset );\n\t\tconst after = measure( range.startOffset, range.startOffset + 1 );\n\n\t\tif ( before.bottom <= after.top ) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tlet rect = rects[ 0 ];\n\n\t// If the collapsed range starts (and therefore ends) at an element node,\n\t// `getClientRects` can be empty in some browsers. This can be resolved\n\t// by adding a temporary text node with zero-width space to the range.\n\t//\n\t// See: https://stackoverflow.com/a/6847328/995445\n\tif ( ! rect || rect.height === 0 ) {\n\t\tassertIsDefined( ownerDocument, 'ownerDocument' );\n\t\tconst padNode = ownerDocument.createTextNode( '\\u200b' );\n\t\t// Do not modify the live range.\n\t\trange = range.cloneRange();\n\t\trange.insertNode( padNode );\n\t\trect = range.getClientRects()[ 0 ];\n\t\tassertIsDefined( padNode.parentNode, 'padNode.parentNode' );\n\t\tpadNode.parentNode.removeChild( padNode );\n\t}\n\n\treturn rect;\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+BAAgC;AAWjB,SAAR,sBAAwC,OAAQ;AAItD,MAAK,CAAE,MAAM,WAAY;AACxB,UAAMA,SAAQ,MAAM,KAAM,MAAM,eAAe,CAAE;AAGjD,QAAKA,OAAM,WAAW,GAAI;AACzB,aAAOA,OAAO,CAAE;AAAA,IACjB;AAGA,UAAM,gBAAgBA,OAAM,OAAQ,CAAE,EAAE,MAAM,MAAO,QAAQ,CAAE;AAG/D,QAAK,cAAc,WAAW,GAAI;AACjC,aAAO,MAAM,sBAAsB;AAAA,IACpC;AAEA,QAAK,cAAc,WAAW,GAAI;AACjC,aAAO,cAAe,CAAE;AAAA,IACzB;AAEA,QAAI;AAAA,MACH,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,IACR,IAAI,cAAe,CAAE;AAErB,eAAY,EAAE,KAAK,QAAQ,MAAM,MAAM,KAAK,eAAgB;AAC3D,UAAK,MAAM,aAAc;AACxB,sBAAc;AAAA,MACf;AACA,UAAK,SAAS,gBAAiB;AAC9B,yBAAiB;AAAA,MAClB;AACA,UAAK,OAAO,cAAe;AAC1B,uBAAe;AAAA,MAChB;AACA,UAAK,QAAQ,eAAgB;AAC5B,wBAAgB;AAAA,MACjB;AAAA,IACD;AAEA,WAAO,IAAI,OAAO;AAAA,MACjB;AAAA,MACA;AAAA,MACA,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,IAClB;AAAA,EACD;AAEA,QAAM,EAAE,eAAe,IAAI;AAC3B,QAAM,EAAE,cAAc,IAAI;AAG1B,MAAK,eAAe,aAAa,MAAO;AACvC,UAAM,EAAE,WAAW,IAAI;AACvB,kDAAiB,YAAY,YAAa;AAC1C,UAAM;AAAA;AAAA,MACL,MAAM,KAAM,WAAW,UAAW,EACjC,QAAS,cAAe;AAAA;AAE1B,kDAAiB,eAAe,eAAgB;AAChD,YAAQ,cAAc,YAAY;AAClC,UAAM,SAAU,YAAY,KAAM;AAClC,UAAM,OAAQ,YAAY,KAAM;AAAA,EACjC;AAEA,QAAM,QAAQ,MAAM,eAAe;AAInC,MAAK,MAAM,SAAS,GAAI;AACvB,WAAO;AAAA,EACR;AAQA,MACC,MAAM,WAAW,KACjB,eAAe,aAAa,eAAe,aAC3C,MAAM,cAAc,KACpB,MAAM;AAAA,EAAoC,eAAiB,QAC1D;AACD,kDAAiB,eAAe,eAAgB;AAChD,UAAM,UAAU,CACO,OACA,QAClB;AACJ,YAAM,YAAY,cAAc,YAAY;AAC5C,gBAAU,SAAU,gBAAgB,KAAM;AAC1C,gBAAU,OAAQ,gBAAgB,GAAI;AACtC,aAAO,UAAU,sBAAsB;AAAA,IACxC;AACA,UAAM,SAAS,QAAS,MAAM,cAAc,GAAG,MAAM,WAAY;AACjE,UAAM,QAAQ,QAAS,MAAM,aAAa,MAAM,cAAc,CAAE;AAEhE,QAAK,OAAO,UAAU,MAAM,KAAM;AACjC,aAAO;AAAA,IACR;AAAA,EACD;AAEA,MAAI,OAAO,MAAO,CAAE;AAOpB,MAAK,CAAE,QAAQ,KAAK,WAAW,GAAI;AAClC,kDAAiB,eAAe,eAAgB;AAChD,UAAM,UAAU,cAAc,eAAgB,GAAS;AAEvD,YAAQ,MAAM,WAAW;AACzB,UAAM,WAAY,OAAQ;AAC1B,WAAO,MAAM,eAAe,EAAG,CAAE;AACjC,kDAAiB,QAAQ,YAAY,oBAAqB;AAC1D,YAAQ,WAAW,YAAa,OAAQ;AAAA,EACzC;AAEA,SAAO;AACR;",
4
+ "sourcesContent": ["import { assertIsDefined } from '../utils/assert-is-defined';\n\n/**\n * Get the rectangle of a given Range. Returns `null` if no suitable rectangle\n * can be found. Use instead of `Range.getBoundingClientRect()`, which is often\n * broken, especially for collapsed ranges.\n *\n * @param {Range} range The range.\n *\n * @return {DOMRect?} The rectangle.\n */\nexport default function getRectangleFromRange( range ) {\n\t// For uncollapsed ranges, get the rectangle that bounds the contents of the\n\t// range; this a rectangle enclosing the union of the bounding rectangles\n\t// for all the elements in the range.\n\tif ( ! range.collapsed ) {\n\t\tconst rects = Array.from( range.getClientRects() );\n\n\t\t// If there's just a single rect, return it.\n\t\tif ( rects.length === 1 ) {\n\t\t\treturn rects[ 0 ];\n\t\t}\n\n\t\t// Ignore tiny selection at the edge of a range.\n\t\tconst filteredRects = rects.filter( ( { width } ) => width > 1 );\n\n\t\t// If it's full of tiny selections, return browser default.\n\t\tif ( filteredRects.length === 0 ) {\n\t\t\treturn range.getBoundingClientRect();\n\t\t}\n\n\t\tif ( filteredRects.length === 1 ) {\n\t\t\treturn filteredRects[ 0 ];\n\t\t}\n\n\t\tlet {\n\t\t\ttop: furthestTop,\n\t\t\tbottom: furthestBottom,\n\t\t\tleft: furthestLeft,\n\t\t\tright: furthestRight,\n\t\t} = filteredRects[ 0 ];\n\n\t\tfor ( const { top, bottom, left, right } of filteredRects ) {\n\t\t\tif ( top < furthestTop ) {\n\t\t\t\tfurthestTop = top;\n\t\t\t}\n\t\t\tif ( bottom > furthestBottom ) {\n\t\t\t\tfurthestBottom = bottom;\n\t\t\t}\n\t\t\tif ( left < furthestLeft ) {\n\t\t\t\tfurthestLeft = left;\n\t\t\t}\n\t\t\tif ( right > furthestRight ) {\n\t\t\t\tfurthestRight = right;\n\t\t\t}\n\t\t}\n\n\t\treturn new window.DOMRect(\n\t\t\tfurthestLeft,\n\t\t\tfurthestTop,\n\t\t\tfurthestRight - furthestLeft,\n\t\t\tfurthestBottom - furthestTop\n\t\t);\n\t}\n\n\tconst { startContainer, startOffset } = range;\n\tconst { ownerDocument } = startContainer;\n\tassertIsDefined( ownerDocument, 'ownerDocument' );\n\n\t// A range inside an element with no children, like a line break or the\n\t// placeholder, is the same spot as the position before that element.\n\tif (\n\t\tstartContainer.nodeType !== startContainer.TEXT_NODE &&\n\t\t! startContainer.childNodes.length &&\n\t\tstartContainer.parentNode\n\t) {\n\t\trange = ownerDocument.createRange();\n\t\trange.setStartBefore( startContainer );\n\t\trange.collapse( true );\n\t\treturn getRectangleFromRange( range );\n\t}\n\n\t// A collapsed range at an element offset has no rectangle. Translate it\n\t// to the text offset next to it: the end of the text before or the start\n\t// of the text after. When those sit on different lines the caret could\n\t// be on either, so return null.\n\tif ( startContainer.nodeType !== startContainer.TEXT_NODE ) {\n\t\tlet before = startContainer.childNodes[ startOffset - 1 ];\n\t\twhile ( before?.lastChild ) {\n\t\t\tbefore = before.lastChild;\n\t\t}\n\t\tlet after = startContainer.childNodes[ startOffset ];\n\t\twhile ( after?.firstChild ) {\n\t\t\tafter = after.firstChild;\n\t\t}\n\n\t\tlet beforeRange;\n\t\tif ( before && before.nodeType === before.TEXT_NODE ) {\n\t\t\tbeforeRange = ownerDocument.createRange();\n\t\t\tbeforeRange.setStart(\n\t\t\t\tbefore,\n\t\t\t\t/** @type {Text} */ ( before ).length\n\t\t\t);\n\t\t\tbeforeRange.collapse( true );\n\t\t}\n\t\tlet afterRange;\n\t\tif ( after && after.nodeType === after.TEXT_NODE ) {\n\t\t\tafterRange = ownerDocument.createRange();\n\t\t\tafterRange.setStart( after, 0 );\n\t\t\tafterRange.collapse( true );\n\t\t}\n\n\t\tif ( beforeRange && afterRange ) {\n\t\t\tconst beforeRect = beforeRange.getClientRects()[ 0 ];\n\t\t\tconst afterRect = afterRange.getClientRects()[ 0 ];\n\t\t\tif (\n\t\t\t\tbeforeRect &&\n\t\t\t\tafterRect &&\n\t\t\t\tbeforeRect.bottom <= afterRect.top\n\t\t\t) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tif ( afterRange ) {\n\t\t\trange = afterRange;\n\t\t} else if ( beforeRange ) {\n\t\t\trange = beforeRange;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tconst rects = range.getClientRects();\n\n\t// If we have multiple rectangles for a collapsed range, there's no way to\n\t// know which it is, so don't return anything.\n\tif ( rects.length > 1 ) {\n\t\treturn null;\n\t}\n\n\t// A collapsed range at a soft line wrap is equally ambiguous: the same\n\t// position ends one line and starts the next. Some browsers (Gecko)\n\t// return a single rectangle for it, on the upper line, regardless of\n\t// where the caret is. Detect the boundary by measuring the characters\n\t// around the position: when they sit on different lines, there is no\n\t// way to know which line the caret is on, so don't return anything.\n\tif (\n\t\trects.length === 1 &&\n\t\tstartContainer.nodeType === startContainer.TEXT_NODE &&\n\t\trange.startOffset > 0 &&\n\t\trange.startOffset < /** @type {Text} */ ( startContainer ).length\n\t) {\n\t\tassertIsDefined( ownerDocument, 'ownerDocument' );\n\t\tconst measure = (\n\t\t\t/** @type {number} */ start,\n\t\t\t/** @type {number} */ end\n\t\t) => {\n\t\t\tconst charRange = ownerDocument.createRange();\n\t\t\tcharRange.setStart( startContainer, start );\n\t\t\tcharRange.setEnd( startContainer, end );\n\t\t\treturn charRange.getBoundingClientRect();\n\t\t};\n\t\tconst before = measure( range.startOffset - 1, range.startOffset );\n\t\tconst after = measure( range.startOffset, range.startOffset + 1 );\n\n\t\tif ( before.bottom <= after.top ) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\treturn rects[ 0 ] ?? null;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["rects"]
7
7
  }
@@ -62,17 +62,11 @@ function isValidFocusableArea(element) {
62
62
  const img = element.ownerDocument.querySelector(
63
63
  'img[usemap="#' + map.name + '"]'
64
64
  );
65
- return !!img && isVisible(img);
65
+ return !!img && !img.closest("[inert]") && isVisible(img);
66
66
  }
67
67
  function find(context, { sequential = false } = {}) {
68
68
  const elements = context.querySelectorAll(buildSelector(sequential));
69
69
  return Array.from(elements).filter((element) => {
70
- if (!isVisible(element)) {
71
- return false;
72
- }
73
- if (element.closest("[inert]")) {
74
- return false;
75
- }
76
70
  const { nodeName } = element;
77
71
  if ("AREA" === nodeName) {
78
72
  return isValidFocusableArea(
@@ -80,7 +74,10 @@ function find(context, { sequential = false } = {}) {
80
74
  element
81
75
  );
82
76
  }
83
- return true;
77
+ if (element.closest("[inert]")) {
78
+ return false;
79
+ }
80
+ return isVisible(element);
84
81
  });
85
82
  }
86
83
  // Annotate the CommonJS export names for ESM import in node:
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/focusable.js"],
4
- "sourcesContent": ["/**\n * References:\n *\n * Focusable:\n * - https://www.w3.org/TR/html5/editing.html#focus-management\n *\n * Sequential focus navigation:\n * - https://www.w3.org/TR/html5/editing.html#sequential-focus-navigation-and-the-tabindex-attribute\n *\n * Disabled elements:\n * - https://www.w3.org/TR/html5/disabled-elements.html#disabled-elements\n *\n * getClientRects algorithm (requiring layout box):\n * - https://www.w3.org/TR/cssom-view-1/#extension-to-the-element-interface\n *\n * AREA elements associated with an IMG:\n * - https://w3c.github.io/html/editing.html#data-model\n */\n\n/**\n * Returns a CSS selector used to query for focusable elements.\n *\n * @param {boolean} sequential If set, only query elements that are sequentially\n * focusable. Non-interactive elements with a\n * negative `tabindex` are focusable but not\n * sequentially focusable.\n * https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute\n *\n * @return {string} CSS selector.\n */\nfunction buildSelector( sequential ) {\n\treturn [\n\t\tsequential ? '[tabindex]:not([tabindex^=\"-\"])' : '[tabindex]',\n\t\t'a[href]',\n\t\t'button:not([disabled])',\n\t\t'input:not([type=\"hidden\"]):not([disabled])',\n\t\t'select:not([disabled])',\n\t\t'textarea:not([disabled])',\n\t\t'iframe:not([tabindex^=\"-\"])',\n\t\t'object',\n\t\t'embed',\n\t\t'summary',\n\t\t'area[href]',\n\t\t'[contenteditable]:not([contenteditable=false])',\n\t].join( ',' );\n}\n\n/**\n * Returns true if the specified element has a layout box and is not hidden by\n * CSS visibility or content visibility.\n *\n * @param {HTMLElement} element DOM element to test.\n *\n * @return {boolean} Whether element is visible.\n */\nfunction isVisible( element ) {\n\tif ( typeof element.checkVisibility === 'function' ) {\n\t\tif ( ! element.checkVisibility( { visibilityProperty: true } ) ) {\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\tconst visibility =\n\t\t\telement.ownerDocument.defaultView?.getComputedStyle(\n\t\t\t\telement\n\t\t\t).visibility;\n\t\tif ( visibility === 'hidden' || visibility === 'collapse' ) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn (\n\t\telement.offsetWidth > 0 ||\n\t\telement.offsetHeight > 0 ||\n\t\telement.getClientRects().length > 0\n\t);\n}\n\n/**\n * Returns true if the specified area element is a valid focusable element, or\n * false otherwise. Area is only focusable if within a map where a named map\n * referenced by an image somewhere in the document.\n *\n * @param {HTMLAreaElement} element DOM area element to test.\n *\n * @return {boolean} Whether area element is valid for focus.\n */\nfunction isValidFocusableArea( element ) {\n\t/** @type {HTMLMapElement | null} */\n\tconst map = element.closest( 'map[name]' );\n\tif ( ! map ) {\n\t\treturn false;\n\t}\n\n\t/** @type {HTMLImageElement | null} */\n\tconst img = element.ownerDocument.querySelector(\n\t\t'img[usemap=\"#' + map.name + '\"]'\n\t);\n\treturn !! img && isVisible( img );\n}\n\n/**\n * Returns all focusable elements within a given context.\n *\n * @param {Element} context Element in which to search.\n * @param {Object} options\n * @param {boolean} [options.sequential] If set, only return elements that are\n * sequentially focusable.\n * Non-interactive elements with a\n * negative `tabindex` are focusable but\n * not sequentially focusable.\n * https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute\n *\n * @return {HTMLElement[]} Focusable elements.\n */\nexport function find( context, { sequential = false } = {} ) {\n\t/** @type {NodeListOf<HTMLElement>} */\n\tconst elements = context.querySelectorAll( buildSelector( sequential ) );\n\n\treturn Array.from( elements ).filter( ( element ) => {\n\t\tif ( ! isVisible( element ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Elements inside an inert subtree are not focusable.\n\t\tif ( element.closest( '[inert]' ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst { nodeName } = element;\n\t\tif ( 'AREA' === nodeName ) {\n\t\t\treturn isValidFocusableArea(\n\t\t\t\t/** @type {HTMLAreaElement} */ ( element )\n\t\t\t);\n\t\t}\n\n\t\treturn true;\n\t} );\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BA,SAAS,cAAe,YAAa;AACpC,SAAO;AAAA,IACN,aAAa,oCAAoC;AAAA,IACjD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,EAAE,KAAM,GAAI;AACb;AAUA,SAAS,UAAW,SAAU;AAC7B,MAAK,OAAO,QAAQ,oBAAoB,YAAa;AACpD,QAAK,CAAE,QAAQ,gBAAiB,EAAE,oBAAoB,KAAK,CAAE,GAAI;AAChE,aAAO;AAAA,IACR;AAAA,EACD,OAAO;AACN,UAAM,aACL,QAAQ,cAAc,aAAa;AAAA,MAClC;AAAA,IACD,EAAE;AACH,QAAK,eAAe,YAAY,eAAe,YAAa;AAC3D,aAAO;AAAA,IACR;AAAA,EACD;AAEA,SACC,QAAQ,cAAc,KACtB,QAAQ,eAAe,KACvB,QAAQ,eAAe,EAAE,SAAS;AAEpC;AAWA,SAAS,qBAAsB,SAAU;AAExC,QAAM,MAAM,QAAQ,QAAS,WAAY;AACzC,MAAK,CAAE,KAAM;AACZ,WAAO;AAAA,EACR;AAGA,QAAM,MAAM,QAAQ,cAAc;AAAA,IACjC,kBAAkB,IAAI,OAAO;AAAA,EAC9B;AACA,SAAO,CAAC,CAAE,OAAO,UAAW,GAAI;AACjC;AAgBO,SAAS,KAAM,SAAS,EAAE,aAAa,MAAM,IAAI,CAAC,GAAI;AAE5D,QAAM,WAAW,QAAQ,iBAAkB,cAAe,UAAW,CAAE;AAEvE,SAAO,MAAM,KAAM,QAAS,EAAE,OAAQ,CAAE,YAAa;AACpD,QAAK,CAAE,UAAW,OAAQ,GAAI;AAC7B,aAAO;AAAA,IACR;AAGA,QAAK,QAAQ,QAAS,SAAU,GAAI;AACnC,aAAO;AAAA,IACR;AAEA,UAAM,EAAE,SAAS,IAAI;AACrB,QAAK,WAAW,UAAW;AAC1B,aAAO;AAAA;AAAA,QAC2B;AAAA,MAClC;AAAA,IACD;AAEA,WAAO;AAAA,EACR,CAAE;AACH;",
4
+ "sourcesContent": ["/**\n * References:\n *\n * Focusable:\n * - https://www.w3.org/TR/html5/editing.html#focus-management\n *\n * Sequential focus navigation:\n * - https://www.w3.org/TR/html5/editing.html#sequential-focus-navigation-and-the-tabindex-attribute\n *\n * Disabled elements:\n * - https://www.w3.org/TR/html5/disabled-elements.html#disabled-elements\n *\n * getClientRects algorithm (requiring layout box):\n * - https://www.w3.org/TR/cssom-view-1/#extension-to-the-element-interface\n *\n * AREA elements associated with an IMG:\n * - https://w3c.github.io/html/editing.html#data-model\n */\n\n/**\n * Returns a CSS selector used to query for focusable elements.\n *\n * @param {boolean} sequential If set, only query elements that are sequentially\n * focusable. Non-interactive elements with a\n * negative `tabindex` are focusable but not\n * sequentially focusable.\n * https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute\n *\n * @return {string} CSS selector.\n */\nfunction buildSelector( sequential ) {\n\treturn [\n\t\tsequential ? '[tabindex]:not([tabindex^=\"-\"])' : '[tabindex]',\n\t\t'a[href]',\n\t\t'button:not([disabled])',\n\t\t'input:not([type=\"hidden\"]):not([disabled])',\n\t\t'select:not([disabled])',\n\t\t'textarea:not([disabled])',\n\t\t'iframe:not([tabindex^=\"-\"])',\n\t\t'object',\n\t\t'embed',\n\t\t'summary',\n\t\t'area[href]',\n\t\t'[contenteditable]:not([contenteditable=false])',\n\t].join( ',' );\n}\n\n/**\n * Returns true if the specified element has a layout box and is not hidden by\n * CSS visibility or content visibility.\n *\n * @param {HTMLElement} element DOM element to test.\n *\n * @return {boolean} Whether element is visible.\n */\nfunction isVisible( element ) {\n\tif ( typeof element.checkVisibility === 'function' ) {\n\t\tif ( ! element.checkVisibility( { visibilityProperty: true } ) ) {\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\tconst visibility =\n\t\t\telement.ownerDocument.defaultView?.getComputedStyle(\n\t\t\t\telement\n\t\t\t).visibility;\n\t\tif ( visibility === 'hidden' || visibility === 'collapse' ) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn (\n\t\telement.offsetWidth > 0 ||\n\t\telement.offsetHeight > 0 ||\n\t\telement.getClientRects().length > 0\n\t);\n}\n\n/**\n * Returns true if the specified area element is a valid focusable element, or\n * false otherwise. Area is only focusable if within a map where a named map\n * referenced by an image somewhere in the document.\n *\n * @param {HTMLAreaElement} element DOM area element to test.\n *\n * @return {boolean} Whether area element is valid for focus.\n */\nfunction isValidFocusableArea( element ) {\n\t/** @type {HTMLMapElement | null} */\n\tconst map = element.closest( 'map[name]' );\n\tif ( ! map ) {\n\t\treturn false;\n\t}\n\n\t/** @type {HTMLImageElement | null} */\n\tconst img = element.ownerDocument.querySelector(\n\t\t'img[usemap=\"#' + map.name + '\"]'\n\t);\n\treturn !! img && ! img.closest( '[inert]' ) && isVisible( img );\n}\n\n/**\n * Returns all focusable elements within a given context.\n *\n * @param {Element} context Element in which to search.\n * @param {Object} options\n * @param {boolean} [options.sequential] If set, only return elements that are\n * sequentially focusable.\n * Non-interactive elements with a\n * negative `tabindex` are focusable but\n * not sequentially focusable.\n * https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute\n *\n * @return {HTMLElement[]} Focusable elements.\n */\nexport function find( context, { sequential = false } = {} ) {\n\t/** @type {NodeListOf<HTMLElement>} */\n\tconst elements = context.querySelectorAll( buildSelector( sequential ) );\n\n\treturn Array.from( elements ).filter( ( element ) => {\n\t\tconst { nodeName } = element;\n\t\tif ( 'AREA' === nodeName ) {\n\t\t\t// The mapped image determines whether this region is visible or inert.\n\t\t\treturn isValidFocusableArea(\n\t\t\t\t/** @type {HTMLAreaElement} */ ( element )\n\t\t\t);\n\t\t}\n\n\t\t// Elements inside an inert subtree are not focusable.\n\t\tif ( element.closest( '[inert]' ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn isVisible( element );\n\t} );\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BA,SAAS,cAAe,YAAa;AACpC,SAAO;AAAA,IACN,aAAa,oCAAoC;AAAA,IACjD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,EAAE,KAAM,GAAI;AACb;AAUA,SAAS,UAAW,SAAU;AAC7B,MAAK,OAAO,QAAQ,oBAAoB,YAAa;AACpD,QAAK,CAAE,QAAQ,gBAAiB,EAAE,oBAAoB,KAAK,CAAE,GAAI;AAChE,aAAO;AAAA,IACR;AAAA,EACD,OAAO;AACN,UAAM,aACL,QAAQ,cAAc,aAAa;AAAA,MAClC;AAAA,IACD,EAAE;AACH,QAAK,eAAe,YAAY,eAAe,YAAa;AAC3D,aAAO;AAAA,IACR;AAAA,EACD;AAEA,SACC,QAAQ,cAAc,KACtB,QAAQ,eAAe,KACvB,QAAQ,eAAe,EAAE,SAAS;AAEpC;AAWA,SAAS,qBAAsB,SAAU;AAExC,QAAM,MAAM,QAAQ,QAAS,WAAY;AACzC,MAAK,CAAE,KAAM;AACZ,WAAO;AAAA,EACR;AAGA,QAAM,MAAM,QAAQ,cAAc;AAAA,IACjC,kBAAkB,IAAI,OAAO;AAAA,EAC9B;AACA,SAAO,CAAC,CAAE,OAAO,CAAE,IAAI,QAAS,SAAU,KAAK,UAAW,GAAI;AAC/D;AAgBO,SAAS,KAAM,SAAS,EAAE,aAAa,MAAM,IAAI,CAAC,GAAI;AAE5D,QAAM,WAAW,QAAQ,iBAAkB,cAAe,UAAW,CAAE;AAEvE,SAAO,MAAM,KAAM,QAAS,EAAE,OAAQ,CAAE,YAAa;AACpD,UAAM,EAAE,SAAS,IAAI;AACrB,QAAK,WAAW,UAAW;AAE1B,aAAO;AAAA;AAAA,QAC2B;AAAA,MAClC;AAAA,IACD;AAGA,QAAK,QAAQ,QAAS,SAAU,GAAI;AACnC,aAAO;AAAA,IACR;AAEA,WAAO,UAAW,OAAQ;AAAA,EAC3B,CAAE;AACH;",
6
6
  "names": []
7
7
  }