@spectrum-web-components/reactive-controllers 0.43.0 → 0.44.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/package.json +2 -2
- package/src/FocusGroup.d.ts +2 -1
- package/src/FocusGroup.dev.js +17 -4
- package/src/FocusGroup.dev.js.map +2 -2
- package/src/FocusGroup.js +1 -1
- package/src/FocusGroup.js.map +3 -3
- package/src/RovingTabindex.dev.js.map +1 -1
- package/src/RovingTabindex.js.map +1 -1
- package/test/roving-tabindex-integration.test.js +108 -0
- package/test/roving-tabindex-integration.test.js.map +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spectrum-web-components/reactive-controllers",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"sideEffects": [
|
|
84
84
|
"./**/*.dev.js"
|
|
85
85
|
],
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "0002d42ce82463b85022e5aa5f7aba8484cba096"
|
|
87
87
|
}
|
package/src/FocusGroup.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare class FocusGroupController<T extends HTMLElement> implements Reac
|
|
|
14
14
|
get currentIndex(): number;
|
|
15
15
|
set currentIndex(currentIndex: number);
|
|
16
16
|
private _currentIndex;
|
|
17
|
+
private prevIndex;
|
|
17
18
|
get direction(): DirectionTypes;
|
|
18
19
|
_direction: () => DirectionTypes;
|
|
19
20
|
directionLength: number;
|
|
@@ -40,7 +41,7 @@ export declare class FocusGroupController<T extends HTMLElement> implements Reac
|
|
|
40
41
|
setCurrentIndexCircularly(diff: number): void;
|
|
41
42
|
hostContainsFocus(): void;
|
|
42
43
|
hostNoLongerContainsFocus(): void;
|
|
43
|
-
|
|
44
|
+
isRelatedTargetOrContainAnElement(event: FocusEvent): boolean;
|
|
44
45
|
handleFocusin: (event: FocusEvent) => void;
|
|
45
46
|
handleFocusout: (event: FocusEvent) => void;
|
|
46
47
|
acceptsEventCode(code: string): boolean;
|
package/src/FocusGroup.dev.js
CHANGED
|
@@ -17,6 +17,7 @@ export class FocusGroupController {
|
|
|
17
17
|
listenerScope
|
|
18
18
|
} = { elements: () => [] }) {
|
|
19
19
|
this._currentIndex = -1;
|
|
20
|
+
this.prevIndex = -1;
|
|
20
21
|
this._direction = () => "both";
|
|
21
22
|
this.directionLength = 5;
|
|
22
23
|
this.elementEnterAction = (_el) => {
|
|
@@ -34,7 +35,7 @@ export class FocusGroupController {
|
|
|
34
35
|
this.recentlyConnected = false;
|
|
35
36
|
this.handleFocusin = (event) => {
|
|
36
37
|
if (!this.isEventWithinListenerScope(event)) return;
|
|
37
|
-
if (this.
|
|
38
|
+
if (this.isRelatedTargetOrContainAnElement(event)) {
|
|
38
39
|
this.hostContainsFocus();
|
|
39
40
|
}
|
|
40
41
|
const path = event.composedPath();
|
|
@@ -43,10 +44,11 @@ export class FocusGroupController {
|
|
|
43
44
|
targetIndex = this.elements.indexOf(el);
|
|
44
45
|
return targetIndex !== -1;
|
|
45
46
|
});
|
|
47
|
+
this.prevIndex = this.currentIndex;
|
|
46
48
|
this.currentIndex = targetIndex > -1 ? targetIndex : this.currentIndex;
|
|
47
49
|
};
|
|
48
50
|
this.handleFocusout = (event) => {
|
|
49
|
-
if (this.
|
|
51
|
+
if (this.isRelatedTargetOrContainAnElement(event)) {
|
|
50
52
|
this.hostNoLongerContainsFocus();
|
|
51
53
|
}
|
|
52
54
|
};
|
|
@@ -55,6 +57,7 @@ export class FocusGroupController {
|
|
|
55
57
|
return;
|
|
56
58
|
}
|
|
57
59
|
let diff = 0;
|
|
60
|
+
this.prevIndex = this.currentIndex;
|
|
58
61
|
switch (event.code) {
|
|
59
62
|
case "ArrowRight":
|
|
60
63
|
diff += 1;
|
|
@@ -171,6 +174,7 @@ export class FocusGroupController {
|
|
|
171
174
|
this.manage();
|
|
172
175
|
}
|
|
173
176
|
focus(options) {
|
|
177
|
+
var _a;
|
|
174
178
|
const elements = this.elements;
|
|
175
179
|
if (!elements.length) return;
|
|
176
180
|
let focusElement = elements[this.currentIndex];
|
|
@@ -179,6 +183,8 @@ export class FocusGroupController {
|
|
|
179
183
|
focusElement = elements[this.currentIndex];
|
|
180
184
|
}
|
|
181
185
|
if (focusElement && this.isFocusableElement(focusElement)) {
|
|
186
|
+
(_a = elements[this.prevIndex]) == null ? void 0 : _a.setAttribute("tabindex", "-1");
|
|
187
|
+
focusElement.tabIndex = 0;
|
|
182
188
|
focusElement.focus(options);
|
|
183
189
|
}
|
|
184
190
|
}
|
|
@@ -197,6 +203,7 @@ export class FocusGroupController {
|
|
|
197
203
|
setCurrentIndexCircularly(diff) {
|
|
198
204
|
const { length } = this.elements;
|
|
199
205
|
let steps = length;
|
|
206
|
+
this.prevIndex = this.currentIndex;
|
|
200
207
|
let nextIndex = (length + this.currentIndex + diff) % length;
|
|
201
208
|
while (
|
|
202
209
|
// don't cycle the elements more than once
|
|
@@ -218,9 +225,15 @@ export class FocusGroupController {
|
|
|
218
225
|
this.host.removeEventListener("keydown", this.handleKeydown);
|
|
219
226
|
this.focused = false;
|
|
220
227
|
}
|
|
221
|
-
|
|
228
|
+
isRelatedTargetOrContainAnElement(event) {
|
|
222
229
|
const relatedTarget = event.relatedTarget;
|
|
223
|
-
|
|
230
|
+
const isRelatedTargetAnElement = this.elements.includes(
|
|
231
|
+
relatedTarget
|
|
232
|
+
);
|
|
233
|
+
const isRelatedTargetContainedWithinElements = this.elements.some(
|
|
234
|
+
(el) => el.contains(relatedTarget)
|
|
235
|
+
);
|
|
236
|
+
return !(isRelatedTargetAnElement || isRelatedTargetContainedWithinElements);
|
|
224
237
|
}
|
|
225
238
|
acceptsEventCode(code) {
|
|
226
239
|
if (code === "End" || code === "Home") {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["FocusGroup.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport type { ReactiveController, ReactiveElement } from 'lit';\n\ntype DirectionTypes = 'horizontal' | 'vertical' | 'both' | 'grid';\nexport type FocusGroupConfig<T> = {\n focusInIndex?: (_elements: T[]) => number;\n direction?: DirectionTypes | (() => DirectionTypes);\n elementEnterAction?: (el: T) => void;\n elements: () => T[];\n isFocusableElement?: (el: T) => boolean;\n listenerScope?: HTMLElement | (() => HTMLElement);\n};\n\nfunction ensureMethod<T, RT>(\n value: T | RT | undefined,\n type: string,\n fallback: T\n): T {\n if (typeof value === type) {\n return (() => value) as T;\n } else if (typeof value === 'function') {\n return value as T;\n }\n return fallback;\n}\n\nexport class FocusGroupController<T extends HTMLElement>\n implements ReactiveController\n{\n protected cachedElements?: T[];\n private mutationObserver: MutationObserver;\n\n get currentIndex(): number {\n if (this._currentIndex === -1) {\n this._currentIndex = this.focusInIndex;\n }\n return this._currentIndex - this.offset;\n }\n\n set currentIndex(currentIndex) {\n this._currentIndex = currentIndex + this.offset;\n }\n\n private _currentIndex = -1;\n\n get direction(): DirectionTypes {\n return this._direction();\n }\n\n _direction = (): DirectionTypes => 'both';\n\n public directionLength = 5;\n\n elementEnterAction = (_el: T): void => {\n return;\n };\n\n get elements(): T[] {\n if (!this.cachedElements) {\n this.cachedElements = this._elements();\n }\n return this.cachedElements;\n }\n\n private _elements!: () => T[];\n\n protected set focused(focused: boolean) {\n /* c8 ignore next 1 */\n if (focused === this.focused) return;\n this._focused = focused;\n }\n\n protected get focused(): boolean {\n return this._focused;\n }\n\n private _focused = false;\n\n get focusInElement(): T {\n return this.elements[this.focusInIndex];\n }\n\n get focusInIndex(): number {\n return this._focusInIndex(this.elements);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _focusInIndex = (_elements: T[]): number => 0;\n\n host: ReactiveElement;\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n isFocusableElement = (_el: T): boolean => true;\n\n isEventWithinListenerScope(event: Event): boolean {\n if (this._listenerScope() === this.host) return true;\n return event.composedPath().includes(this._listenerScope());\n }\n\n _listenerScope = (): HTMLElement => this.host;\n\n // When elements are virtualized, the delta between the first element\n // and the first rendered element.\n offset = 0;\n\n recentlyConnected = false;\n\n constructor(\n host: ReactiveElement,\n {\n direction,\n elementEnterAction,\n elements,\n focusInIndex,\n isFocusableElement,\n listenerScope,\n }: FocusGroupConfig<T> = { elements: () => [] }\n ) {\n this.mutationObserver = new MutationObserver(() => {\n this.handleItemMutation();\n });\n this.host = host;\n this.host.addController(this);\n this._elements = elements;\n this.isFocusableElement = isFocusableElement || this.isFocusableElement;\n this._direction = ensureMethod<() => DirectionTypes, DirectionTypes>(\n direction,\n 'string',\n this._direction\n );\n this.elementEnterAction = elementEnterAction || this.elementEnterAction;\n this._focusInIndex = ensureMethod<(_elements: T[]) => number, number>(\n focusInIndex,\n 'number',\n this._focusInIndex\n );\n this._listenerScope = ensureMethod<() => HTMLElement, HTMLElement>(\n listenerScope,\n 'object',\n this._listenerScope\n );\n }\n /* In handleItemMutation() method the first if condition is checking if the element is not focused or if the element's children's length is not decreasing then it means no element has been deleted and we must return.\n Then we are checking if the deleted element was the focused one before the deletion if so then we need to proceed else we casn return;\n */\n handleItemMutation(): void {\n if (\n this._currentIndex == -1 ||\n this.elements.length <= this._elements().length\n )\n return;\n const focusedElement = this.elements[this.currentIndex];\n this.clearElementCache();\n if (this.elements.includes(focusedElement)) return;\n const moveToNextElement = this.currentIndex !== this.elements.length;\n const diff = moveToNextElement ? 1 : -1;\n if (moveToNextElement) {\n this.setCurrentIndexCircularly(-1);\n }\n this.setCurrentIndexCircularly(diff);\n this.focus();\n }\n\n update({ elements }: FocusGroupConfig<T> = { elements: () => [] }): void {\n this.unmanage();\n this._elements = elements;\n this.clearElementCache();\n this.manage();\n }\n\n focus(options?: FocusOptions): void {\n const elements = this.elements;\n if (!elements.length) return;\n let focusElement = elements[this.currentIndex];\n if (!focusElement || !this.isFocusableElement(focusElement)) {\n this.setCurrentIndexCircularly(1);\n focusElement = elements[this.currentIndex];\n }\n if (focusElement && this.isFocusableElement(focusElement)) {\n focusElement.focus(options);\n }\n }\n\n clearElementCache(offset = 0): void {\n this.mutationObserver.disconnect();\n delete this.cachedElements;\n this.offset = offset;\n requestAnimationFrame(() => {\n this.elements.forEach((element) => {\n this.mutationObserver.observe(element, {\n attributes: true,\n });\n });\n });\n }\n\n setCurrentIndexCircularly(diff: number): void {\n const { length } = this.elements;\n let steps = length;\n // start at a possibly not 0 index\n let nextIndex = (length + this.currentIndex + diff) % length;\n while (\n // don't cycle the elements more than once\n steps &&\n this.elements[nextIndex] &&\n !this.isFocusableElement(this.elements[nextIndex])\n ) {\n nextIndex = (length + nextIndex + diff) % length;\n steps -= 1;\n }\n this.currentIndex = nextIndex;\n }\n\n hostContainsFocus(): void {\n this.host.addEventListener('focusout', this.handleFocusout);\n this.host.addEventListener('keydown', this.handleKeydown);\n this.focused = true;\n }\n\n hostNoLongerContainsFocus(): void {\n this.host.addEventListener('focusin', this.handleFocusin);\n this.host.removeEventListener('focusout', this.handleFocusout);\n this.host.removeEventListener('keydown', this.handleKeydown);\n this.focused = false;\n }\n\n isRelatedTargetAnElement(event: FocusEvent): boolean {\n const relatedTarget = event.relatedTarget as null | Element;\n return !this.elements.includes(relatedTarget as T);\n }\n\n handleFocusin = (event: FocusEvent): void => {\n if (!this.isEventWithinListenerScope(event)) return;\n if (this.isRelatedTargetAnElement(event)) {\n this.hostContainsFocus();\n }\n const path = event.composedPath() as T[];\n let targetIndex = -1;\n path.find((el) => {\n targetIndex = this.elements.indexOf(el);\n return targetIndex !== -1;\n });\n this.currentIndex = targetIndex > -1 ? targetIndex : this.currentIndex;\n };\n\n handleFocusout = (event: FocusEvent): void => {\n if (this.isRelatedTargetAnElement(event)) {\n this.hostNoLongerContainsFocus();\n }\n };\n\n acceptsEventCode(code: string): boolean {\n if (code === 'End' || code === 'Home') {\n return true;\n }\n switch (this.direction) {\n case 'horizontal':\n return code === 'ArrowLeft' || code === 'ArrowRight';\n case 'vertical':\n return code === 'ArrowUp' || code === 'ArrowDown';\n case 'both':\n case 'grid':\n return code.startsWith('Arrow');\n }\n }\n\n handleKeydown = (event: KeyboardEvent): void => {\n if (!this.acceptsEventCode(event.code) || event.defaultPrevented) {\n return;\n }\n let diff = 0;\n switch (event.code) {\n case 'ArrowRight':\n diff += 1;\n break;\n case 'ArrowDown':\n diff += this.direction === 'grid' ? this.directionLength : 1;\n break;\n case 'ArrowLeft':\n diff -= 1;\n break;\n case 'ArrowUp':\n diff -= this.direction === 'grid' ? this.directionLength : 1;\n break;\n case 'End':\n this.currentIndex = 0;\n diff -= 1;\n break;\n case 'Home':\n this.currentIndex = this.elements.length - 1;\n diff += 1;\n break;\n }\n event.preventDefault();\n if (this.direction === 'grid' && this.currentIndex + diff < 0) {\n this.currentIndex = 0;\n } else if (\n this.direction === 'grid' &&\n this.currentIndex + diff > this.elements.length - 1\n ) {\n this.currentIndex = this.elements.length - 1;\n } else {\n this.setCurrentIndexCircularly(diff);\n }\n // To allow the `focusInIndex` to be calculated with the \"after\" state of the keyboard interaction\n // do `elementEnterAction` _before_ focusing the next element.\n this.elementEnterAction(this.elements[this.currentIndex]);\n this.focus();\n };\n\n manage(): void {\n this.addEventListeners();\n }\n\n unmanage(): void {\n this.removeEventListeners();\n }\n\n addEventListeners(): void {\n this.host.addEventListener('focusin', this.handleFocusin);\n }\n\n removeEventListeners(): void {\n this.host.removeEventListener('focusin', this.handleFocusin);\n this.host.removeEventListener('focusout', this.handleFocusout);\n this.host.removeEventListener('keydown', this.handleKeydown);\n }\n\n hostConnected(): void {\n this.recentlyConnected = true;\n this.addEventListeners();\n }\n\n hostDisconnected(): void {\n this.mutationObserver.disconnect();\n this.removeEventListeners();\n }\n\n hostUpdated(): void {\n if (this.recentlyConnected) {\n this.recentlyConnected = false;\n this.elements.forEach((element) => {\n this.mutationObserver.observe(element, {\n attributes: true,\n });\n });\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";AAuBA,SAAS,aACL,OACA,MACA,UACC;AACD,MAAI,OAAO,UAAU,MAAM;AACvB,WAAQ,MAAM;AAAA,EAClB,WAAW,OAAO,UAAU,YAAY;AACpC,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAEO,aAAM,qBAEb;AAAA,
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport type { ReactiveController, ReactiveElement } from 'lit';\n\ntype DirectionTypes = 'horizontal' | 'vertical' | 'both' | 'grid';\nexport type FocusGroupConfig<T> = {\n focusInIndex?: (_elements: T[]) => number;\n direction?: DirectionTypes | (() => DirectionTypes);\n elementEnterAction?: (el: T) => void;\n elements: () => T[];\n isFocusableElement?: (el: T) => boolean;\n listenerScope?: HTMLElement | (() => HTMLElement);\n};\n\nfunction ensureMethod<T, RT>(\n value: T | RT | undefined,\n type: string,\n fallback: T\n): T {\n if (typeof value === type) {\n return (() => value) as T;\n } else if (typeof value === 'function') {\n return value as T;\n }\n return fallback;\n}\n\nexport class FocusGroupController<T extends HTMLElement>\n implements ReactiveController\n{\n protected cachedElements?: T[];\n private mutationObserver: MutationObserver;\n\n get currentIndex(): number {\n if (this._currentIndex === -1) {\n this._currentIndex = this.focusInIndex;\n }\n return this._currentIndex - this.offset;\n }\n\n set currentIndex(currentIndex) {\n this._currentIndex = currentIndex + this.offset;\n }\n\n private _currentIndex = -1;\n\n private prevIndex = -1;\n\n get direction(): DirectionTypes {\n return this._direction();\n }\n\n _direction = (): DirectionTypes => 'both';\n\n public directionLength = 5;\n\n elementEnterAction = (_el: T): void => {\n return;\n };\n\n get elements(): T[] {\n if (!this.cachedElements) {\n this.cachedElements = this._elements();\n }\n return this.cachedElements;\n }\n\n private _elements!: () => T[];\n\n protected set focused(focused: boolean) {\n /* c8 ignore next 1 */\n if (focused === this.focused) return;\n this._focused = focused;\n }\n\n protected get focused(): boolean {\n return this._focused;\n }\n\n private _focused = false;\n\n get focusInElement(): T {\n return this.elements[this.focusInIndex];\n }\n\n get focusInIndex(): number {\n return this._focusInIndex(this.elements);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _focusInIndex = (_elements: T[]): number => 0;\n\n host: ReactiveElement;\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n isFocusableElement = (_el: T): boolean => true;\n\n isEventWithinListenerScope(event: Event): boolean {\n if (this._listenerScope() === this.host) return true;\n return event.composedPath().includes(this._listenerScope());\n }\n\n _listenerScope = (): HTMLElement => this.host;\n\n // When elements are virtualized, the delta between the first element\n // and the first rendered element.\n offset = 0;\n\n recentlyConnected = false;\n\n constructor(\n host: ReactiveElement,\n {\n direction,\n elementEnterAction,\n elements,\n focusInIndex,\n isFocusableElement,\n listenerScope,\n }: FocusGroupConfig<T> = { elements: () => [] }\n ) {\n this.mutationObserver = new MutationObserver(() => {\n this.handleItemMutation();\n });\n this.host = host;\n this.host.addController(this);\n this._elements = elements;\n this.isFocusableElement = isFocusableElement || this.isFocusableElement;\n this._direction = ensureMethod<() => DirectionTypes, DirectionTypes>(\n direction,\n 'string',\n this._direction\n );\n this.elementEnterAction = elementEnterAction || this.elementEnterAction;\n this._focusInIndex = ensureMethod<(_elements: T[]) => number, number>(\n focusInIndex,\n 'number',\n this._focusInIndex\n );\n this._listenerScope = ensureMethod<() => HTMLElement, HTMLElement>(\n listenerScope,\n 'object',\n this._listenerScope\n );\n }\n /* In handleItemMutation() method the first if condition is checking if the element is not focused or if the element's children's length is not decreasing then it means no element has been deleted and we must return.\n Then we are checking if the deleted element was the focused one before the deletion if so then we need to proceed else we casn return;\n */\n handleItemMutation(): void {\n if (\n this._currentIndex == -1 ||\n this.elements.length <= this._elements().length\n )\n return;\n const focusedElement = this.elements[this.currentIndex];\n this.clearElementCache();\n if (this.elements.includes(focusedElement)) return;\n const moveToNextElement = this.currentIndex !== this.elements.length;\n const diff = moveToNextElement ? 1 : -1;\n if (moveToNextElement) {\n this.setCurrentIndexCircularly(-1);\n }\n this.setCurrentIndexCircularly(diff);\n this.focus();\n }\n\n update({ elements }: FocusGroupConfig<T> = { elements: () => [] }): void {\n this.unmanage();\n this._elements = elements;\n this.clearElementCache();\n this.manage();\n }\n\n focus(options?: FocusOptions): void {\n const elements = this.elements;\n if (!elements.length) return;\n let focusElement = elements[this.currentIndex];\n if (!focusElement || !this.isFocusableElement(focusElement)) {\n this.setCurrentIndexCircularly(1);\n focusElement = elements[this.currentIndex];\n }\n if (focusElement && this.isFocusableElement(focusElement)) {\n elements[this.prevIndex]?.setAttribute('tabindex', '-1');\n focusElement.tabIndex = 0;\n focusElement.focus(options);\n }\n }\n\n clearElementCache(offset = 0): void {\n this.mutationObserver.disconnect();\n delete this.cachedElements;\n this.offset = offset;\n requestAnimationFrame(() => {\n this.elements.forEach((element) => {\n this.mutationObserver.observe(element, {\n attributes: true,\n });\n });\n });\n }\n\n setCurrentIndexCircularly(diff: number): void {\n const { length } = this.elements;\n let steps = length;\n this.prevIndex = this.currentIndex;\n // start at a possibly not 0 index\n let nextIndex = (length + this.currentIndex + diff) % length;\n while (\n // don't cycle the elements more than once\n steps &&\n this.elements[nextIndex] &&\n !this.isFocusableElement(this.elements[nextIndex])\n ) {\n nextIndex = (length + nextIndex + diff) % length;\n steps -= 1;\n }\n this.currentIndex = nextIndex;\n }\n\n hostContainsFocus(): void {\n this.host.addEventListener('focusout', this.handleFocusout);\n this.host.addEventListener('keydown', this.handleKeydown);\n this.focused = true;\n }\n\n hostNoLongerContainsFocus(): void {\n this.host.addEventListener('focusin', this.handleFocusin);\n this.host.removeEventListener('focusout', this.handleFocusout);\n this.host.removeEventListener('keydown', this.handleKeydown);\n this.focused = false;\n }\n\n isRelatedTargetOrContainAnElement(event: FocusEvent): boolean {\n const relatedTarget = event.relatedTarget as null | Element;\n\n const isRelatedTargetAnElement = this.elements.includes(\n relatedTarget as T\n );\n const isRelatedTargetContainedWithinElements = this.elements.some(\n (el) => el.contains(relatedTarget)\n );\n return !(\n isRelatedTargetAnElement || isRelatedTargetContainedWithinElements\n );\n }\n\n handleFocusin = (event: FocusEvent): void => {\n if (!this.isEventWithinListenerScope(event)) return;\n if (this.isRelatedTargetOrContainAnElement(event)) {\n this.hostContainsFocus();\n }\n const path = event.composedPath() as T[];\n let targetIndex = -1;\n path.find((el) => {\n targetIndex = this.elements.indexOf(el);\n return targetIndex !== -1;\n });\n this.prevIndex = this.currentIndex;\n this.currentIndex = targetIndex > -1 ? targetIndex : this.currentIndex;\n };\n\n handleFocusout = (event: FocusEvent): void => {\n if (this.isRelatedTargetOrContainAnElement(event)) {\n this.hostNoLongerContainsFocus();\n }\n };\n\n acceptsEventCode(code: string): boolean {\n if (code === 'End' || code === 'Home') {\n return true;\n }\n switch (this.direction) {\n case 'horizontal':\n return code === 'ArrowLeft' || code === 'ArrowRight';\n case 'vertical':\n return code === 'ArrowUp' || code === 'ArrowDown';\n case 'both':\n case 'grid':\n return code.startsWith('Arrow');\n }\n }\n\n handleKeydown = (event: KeyboardEvent): void => {\n if (!this.acceptsEventCode(event.code) || event.defaultPrevented) {\n return;\n }\n let diff = 0;\n this.prevIndex = this.currentIndex;\n switch (event.code) {\n case 'ArrowRight':\n diff += 1;\n break;\n case 'ArrowDown':\n diff += this.direction === 'grid' ? this.directionLength : 1;\n break;\n case 'ArrowLeft':\n diff -= 1;\n break;\n case 'ArrowUp':\n diff -= this.direction === 'grid' ? this.directionLength : 1;\n break;\n case 'End':\n this.currentIndex = 0;\n diff -= 1;\n break;\n case 'Home':\n this.currentIndex = this.elements.length - 1;\n diff += 1;\n break;\n }\n event.preventDefault();\n if (this.direction === 'grid' && this.currentIndex + diff < 0) {\n this.currentIndex = 0;\n } else if (\n this.direction === 'grid' &&\n this.currentIndex + diff > this.elements.length - 1\n ) {\n this.currentIndex = this.elements.length - 1;\n } else {\n this.setCurrentIndexCircularly(diff);\n }\n // To allow the `focusInIndex` to be calculated with the \"after\" state of the keyboard interaction\n // do `elementEnterAction` _before_ focusing the next element.\n this.elementEnterAction(this.elements[this.currentIndex]);\n this.focus();\n };\n\n manage(): void {\n this.addEventListeners();\n }\n\n unmanage(): void {\n this.removeEventListeners();\n }\n\n addEventListeners(): void {\n this.host.addEventListener('focusin', this.handleFocusin);\n }\n\n removeEventListeners(): void {\n this.host.removeEventListener('focusin', this.handleFocusin);\n this.host.removeEventListener('focusout', this.handleFocusout);\n this.host.removeEventListener('keydown', this.handleKeydown);\n }\n\n hostConnected(): void {\n this.recentlyConnected = true;\n this.addEventListeners();\n }\n\n hostDisconnected(): void {\n this.mutationObserver.disconnect();\n this.removeEventListeners();\n }\n\n hostUpdated(): void {\n if (this.recentlyConnected) {\n this.recentlyConnected = false;\n this.elements.forEach((element) => {\n this.mutationObserver.observe(element, {\n attributes: true,\n });\n });\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/src/FocusGroup.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";function
|
|
1
|
+
"use strict";function o(i,e,t){return typeof i===e?()=>i:typeof i=="function"?i:t}export class FocusGroupController{constructor(e,{direction:t,elementEnterAction:n,elements:s,focusInIndex:r,isFocusableElement:h,listenerScope:c}={elements:()=>[]}){this._currentIndex=-1;this.prevIndex=-1;this._direction=()=>"both";this.directionLength=5;this.elementEnterAction=e=>{};this._focused=!1;this._focusInIndex=e=>0;this.isFocusableElement=e=>!0;this._listenerScope=()=>this.host;this.offset=0;this.recentlyConnected=!1;this.handleFocusin=e=>{if(!this.isEventWithinListenerScope(e))return;this.isRelatedTargetOrContainAnElement(e)&&this.hostContainsFocus();const t=e.composedPath();let n=-1;t.find(s=>(n=this.elements.indexOf(s),n!==-1)),this.prevIndex=this.currentIndex,this.currentIndex=n>-1?n:this.currentIndex};this.handleFocusout=e=>{this.isRelatedTargetOrContainAnElement(e)&&this.hostNoLongerContainsFocus()};this.handleKeydown=e=>{if(!this.acceptsEventCode(e.code)||e.defaultPrevented)return;let t=0;switch(this.prevIndex=this.currentIndex,e.code){case"ArrowRight":t+=1;break;case"ArrowDown":t+=this.direction==="grid"?this.directionLength:1;break;case"ArrowLeft":t-=1;break;case"ArrowUp":t-=this.direction==="grid"?this.directionLength:1;break;case"End":this.currentIndex=0,t-=1;break;case"Home":this.currentIndex=this.elements.length-1,t+=1;break}e.preventDefault(),this.direction==="grid"&&this.currentIndex+t<0?this.currentIndex=0:this.direction==="grid"&&this.currentIndex+t>this.elements.length-1?this.currentIndex=this.elements.length-1:this.setCurrentIndexCircularly(t),this.elementEnterAction(this.elements[this.currentIndex]),this.focus()};this.mutationObserver=new MutationObserver(()=>{this.handleItemMutation()}),this.host=e,this.host.addController(this),this._elements=s,this.isFocusableElement=h||this.isFocusableElement,this._direction=o(t,"string",this._direction),this.elementEnterAction=n||this.elementEnterAction,this._focusInIndex=o(r,"number",this._focusInIndex),this._listenerScope=o(c,"object",this._listenerScope)}get currentIndex(){return this._currentIndex===-1&&(this._currentIndex=this.focusInIndex),this._currentIndex-this.offset}set currentIndex(e){this._currentIndex=e+this.offset}get direction(){return this._direction()}get elements(){return this.cachedElements||(this.cachedElements=this._elements()),this.cachedElements}set focused(e){e!==this.focused&&(this._focused=e)}get focused(){return this._focused}get focusInElement(){return this.elements[this.focusInIndex]}get focusInIndex(){return this._focusInIndex(this.elements)}isEventWithinListenerScope(e){return this._listenerScope()===this.host?!0:e.composedPath().includes(this._listenerScope())}handleItemMutation(){if(this._currentIndex==-1||this.elements.length<=this._elements().length)return;const e=this.elements[this.currentIndex];if(this.clearElementCache(),this.elements.includes(e))return;const t=this.currentIndex!==this.elements.length,n=t?1:-1;t&&this.setCurrentIndexCircularly(-1),this.setCurrentIndexCircularly(n),this.focus()}update({elements:e}={elements:()=>[]}){this.unmanage(),this._elements=e,this.clearElementCache(),this.manage()}focus(e){var s;const t=this.elements;if(!t.length)return;let n=t[this.currentIndex];(!n||!this.isFocusableElement(n))&&(this.setCurrentIndexCircularly(1),n=t[this.currentIndex]),n&&this.isFocusableElement(n)&&((s=t[this.prevIndex])==null||s.setAttribute("tabindex","-1"),n.tabIndex=0,n.focus(e))}clearElementCache(e=0){this.mutationObserver.disconnect(),delete this.cachedElements,this.offset=e,requestAnimationFrame(()=>{this.elements.forEach(t=>{this.mutationObserver.observe(t,{attributes:!0})})})}setCurrentIndexCircularly(e){const{length:t}=this.elements;let n=t;this.prevIndex=this.currentIndex;let s=(t+this.currentIndex+e)%t;for(;n&&this.elements[s]&&!this.isFocusableElement(this.elements[s]);)s=(t+s+e)%t,n-=1;this.currentIndex=s}hostContainsFocus(){this.host.addEventListener("focusout",this.handleFocusout),this.host.addEventListener("keydown",this.handleKeydown),this.focused=!0}hostNoLongerContainsFocus(){this.host.addEventListener("focusin",this.handleFocusin),this.host.removeEventListener("focusout",this.handleFocusout),this.host.removeEventListener("keydown",this.handleKeydown),this.focused=!1}isRelatedTargetOrContainAnElement(e){const t=e.relatedTarget,n=this.elements.includes(t),s=this.elements.some(r=>r.contains(t));return!(n||s)}acceptsEventCode(e){if(e==="End"||e==="Home")return!0;switch(this.direction){case"horizontal":return e==="ArrowLeft"||e==="ArrowRight";case"vertical":return e==="ArrowUp"||e==="ArrowDown";case"both":case"grid":return e.startsWith("Arrow")}}manage(){this.addEventListeners()}unmanage(){this.removeEventListeners()}addEventListeners(){this.host.addEventListener("focusin",this.handleFocusin)}removeEventListeners(){this.host.removeEventListener("focusin",this.handleFocusin),this.host.removeEventListener("focusout",this.handleFocusout),this.host.removeEventListener("keydown",this.handleKeydown)}hostConnected(){this.recentlyConnected=!0,this.addEventListeners()}hostDisconnected(){this.mutationObserver.disconnect(),this.removeEventListeners()}hostUpdated(){this.recentlyConnected&&(this.recentlyConnected=!1,this.elements.forEach(e=>{this.mutationObserver.observe(e,{attributes:!0})}))}}
|
|
2
2
|
//# sourceMappingURL=FocusGroup.js.map
|
package/src/FocusGroup.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["FocusGroup.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport type { ReactiveController, ReactiveElement } from 'lit';\n\ntype DirectionTypes = 'horizontal' | 'vertical' | 'both' | 'grid';\nexport type FocusGroupConfig<T> = {\n focusInIndex?: (_elements: T[]) => number;\n direction?: DirectionTypes | (() => DirectionTypes);\n elementEnterAction?: (el: T) => void;\n elements: () => T[];\n isFocusableElement?: (el: T) => boolean;\n listenerScope?: HTMLElement | (() => HTMLElement);\n};\n\nfunction ensureMethod<T, RT>(\n value: T | RT | undefined,\n type: string,\n fallback: T\n): T {\n if (typeof value === type) {\n return (() => value) as T;\n } else if (typeof value === 'function') {\n return value as T;\n }\n return fallback;\n}\n\nexport class FocusGroupController<T extends HTMLElement>\n implements ReactiveController\n{\n protected cachedElements?: T[];\n private mutationObserver: MutationObserver;\n\n get currentIndex(): number {\n if (this._currentIndex === -1) {\n this._currentIndex = this.focusInIndex;\n }\n return this._currentIndex - this.offset;\n }\n\n set currentIndex(currentIndex) {\n this._currentIndex = currentIndex + this.offset;\n }\n\n private _currentIndex = -1;\n\n get direction(): DirectionTypes {\n return this._direction();\n }\n\n _direction = (): DirectionTypes => 'both';\n\n public directionLength = 5;\n\n elementEnterAction = (_el: T): void => {\n return;\n };\n\n get elements(): T[] {\n if (!this.cachedElements) {\n this.cachedElements = this._elements();\n }\n return this.cachedElements;\n }\n\n private _elements!: () => T[];\n\n protected set focused(focused: boolean) {\n /* c8 ignore next 1 */\n if (focused === this.focused) return;\n this._focused = focused;\n }\n\n protected get focused(): boolean {\n return this._focused;\n }\n\n private _focused = false;\n\n get focusInElement(): T {\n return this.elements[this.focusInIndex];\n }\n\n get focusInIndex(): number {\n return this._focusInIndex(this.elements);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _focusInIndex = (_elements: T[]): number => 0;\n\n host: ReactiveElement;\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n isFocusableElement = (_el: T): boolean => true;\n\n isEventWithinListenerScope(event: Event): boolean {\n if (this._listenerScope() === this.host) return true;\n return event.composedPath().includes(this._listenerScope());\n }\n\n _listenerScope = (): HTMLElement => this.host;\n\n // When elements are virtualized, the delta between the first element\n // and the first rendered element.\n offset = 0;\n\n recentlyConnected = false;\n\n constructor(\n host: ReactiveElement,\n {\n direction,\n elementEnterAction,\n elements,\n focusInIndex,\n isFocusableElement,\n listenerScope,\n }: FocusGroupConfig<T> = { elements: () => [] }\n ) {\n this.mutationObserver = new MutationObserver(() => {\n this.handleItemMutation();\n });\n this.host = host;\n this.host.addController(this);\n this._elements = elements;\n this.isFocusableElement = isFocusableElement || this.isFocusableElement;\n this._direction = ensureMethod<() => DirectionTypes, DirectionTypes>(\n direction,\n 'string',\n this._direction\n );\n this.elementEnterAction = elementEnterAction || this.elementEnterAction;\n this._focusInIndex = ensureMethod<(_elements: T[]) => number, number>(\n focusInIndex,\n 'number',\n this._focusInIndex\n );\n this._listenerScope = ensureMethod<() => HTMLElement, HTMLElement>(\n listenerScope,\n 'object',\n this._listenerScope\n );\n }\n /* In handleItemMutation() method the first if condition is checking if the element is not focused or if the element's children's length is not decreasing then it means no element has been deleted and we must return.\n Then we are checking if the deleted element was the focused one before the deletion if so then we need to proceed else we casn return;\n */\n handleItemMutation(): void {\n if (\n this._currentIndex == -1 ||\n this.elements.length <= this._elements().length\n )\n return;\n const focusedElement = this.elements[this.currentIndex];\n this.clearElementCache();\n if (this.elements.includes(focusedElement)) return;\n const moveToNextElement = this.currentIndex !== this.elements.length;\n const diff = moveToNextElement ? 1 : -1;\n if (moveToNextElement) {\n this.setCurrentIndexCircularly(-1);\n }\n this.setCurrentIndexCircularly(diff);\n this.focus();\n }\n\n update({ elements }: FocusGroupConfig<T> = { elements: () => [] }): void {\n this.unmanage();\n this._elements = elements;\n this.clearElementCache();\n this.manage();\n }\n\n focus(options?: FocusOptions): void {\n const elements = this.elements;\n if (!elements.length) return;\n let focusElement = elements[this.currentIndex];\n if (!focusElement || !this.isFocusableElement(focusElement)) {\n this.setCurrentIndexCircularly(1);\n focusElement = elements[this.currentIndex];\n }\n if (focusElement && this.isFocusableElement(focusElement)) {\n focusElement.focus(options);\n }\n }\n\n clearElementCache(offset = 0): void {\n this.mutationObserver.disconnect();\n delete this.cachedElements;\n this.offset = offset;\n requestAnimationFrame(() => {\n this.elements.forEach((element) => {\n this.mutationObserver.observe(element, {\n attributes: true,\n });\n });\n });\n }\n\n setCurrentIndexCircularly(diff: number): void {\n const { length } = this.elements;\n let steps = length;\n // start at a possibly not 0 index\n let nextIndex = (length + this.currentIndex + diff) % length;\n while (\n // don't cycle the elements more than once\n steps &&\n this.elements[nextIndex] &&\n !this.isFocusableElement(this.elements[nextIndex])\n ) {\n nextIndex = (length + nextIndex + diff) % length;\n steps -= 1;\n }\n this.currentIndex = nextIndex;\n }\n\n hostContainsFocus(): void {\n this.host.addEventListener('focusout', this.handleFocusout);\n this.host.addEventListener('keydown', this.handleKeydown);\n this.focused = true;\n }\n\n hostNoLongerContainsFocus(): void {\n this.host.addEventListener('focusin', this.handleFocusin);\n this.host.removeEventListener('focusout', this.handleFocusout);\n this.host.removeEventListener('keydown', this.handleKeydown);\n this.focused = false;\n }\n\n isRelatedTargetAnElement(event: FocusEvent): boolean {\n const relatedTarget = event.relatedTarget as null | Element;\n return !this.elements.includes(relatedTarget as T);\n }\n\n handleFocusin = (event: FocusEvent): void => {\n if (!this.isEventWithinListenerScope(event)) return;\n if (this.isRelatedTargetAnElement(event)) {\n this.hostContainsFocus();\n }\n const path = event.composedPath() as T[];\n let targetIndex = -1;\n path.find((el) => {\n targetIndex = this.elements.indexOf(el);\n return targetIndex !== -1;\n });\n this.currentIndex = targetIndex > -1 ? targetIndex : this.currentIndex;\n };\n\n handleFocusout = (event: FocusEvent): void => {\n if (this.isRelatedTargetAnElement(event)) {\n this.hostNoLongerContainsFocus();\n }\n };\n\n acceptsEventCode(code: string): boolean {\n if (code === 'End' || code === 'Home') {\n return true;\n }\n switch (this.direction) {\n case 'horizontal':\n return code === 'ArrowLeft' || code === 'ArrowRight';\n case 'vertical':\n return code === 'ArrowUp' || code === 'ArrowDown';\n case 'both':\n case 'grid':\n return code.startsWith('Arrow');\n }\n }\n\n handleKeydown = (event: KeyboardEvent): void => {\n if (!this.acceptsEventCode(event.code) || event.defaultPrevented) {\n return;\n }\n let diff = 0;\n switch (event.code) {\n case 'ArrowRight':\n diff += 1;\n break;\n case 'ArrowDown':\n diff += this.direction === 'grid' ? this.directionLength : 1;\n break;\n case 'ArrowLeft':\n diff -= 1;\n break;\n case 'ArrowUp':\n diff -= this.direction === 'grid' ? this.directionLength : 1;\n break;\n case 'End':\n this.currentIndex = 0;\n diff -= 1;\n break;\n case 'Home':\n this.currentIndex = this.elements.length - 1;\n diff += 1;\n break;\n }\n event.preventDefault();\n if (this.direction === 'grid' && this.currentIndex + diff < 0) {\n this.currentIndex = 0;\n } else if (\n this.direction === 'grid' &&\n this.currentIndex + diff > this.elements.length - 1\n ) {\n this.currentIndex = this.elements.length - 1;\n } else {\n this.setCurrentIndexCircularly(diff);\n }\n // To allow the `focusInIndex` to be calculated with the \"after\" state of the keyboard interaction\n // do `elementEnterAction` _before_ focusing the next element.\n this.elementEnterAction(this.elements[this.currentIndex]);\n this.focus();\n };\n\n manage(): void {\n this.addEventListeners();\n }\n\n unmanage(): void {\n this.removeEventListeners();\n }\n\n addEventListeners(): void {\n this.host.addEventListener('focusin', this.handleFocusin);\n }\n\n removeEventListeners(): void {\n this.host.removeEventListener('focusin', this.handleFocusin);\n this.host.removeEventListener('focusout', this.handleFocusout);\n this.host.removeEventListener('keydown', this.handleKeydown);\n }\n\n hostConnected(): void {\n this.recentlyConnected = true;\n this.addEventListeners();\n }\n\n hostDisconnected(): void {\n this.mutationObserver.disconnect();\n this.removeEventListeners();\n }\n\n hostUpdated(): void {\n if (this.recentlyConnected) {\n this.recentlyConnected = false;\n this.elements.forEach((element) => {\n this.mutationObserver.observe(element, {\n attributes: true,\n });\n });\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "aAuBA,SAASA,EACLC,EACAC,EACAC,EACC,CACD,OAAI,OAAOF,IAAUC,EACT,IAAMD,EACP,OAAOA,GAAU,WACjBA,EAEJE,CACX,CAEO,aAAM,oBAEb,
|
|
6
|
-
"names": ["ensureMethod", "value", "type", "fallback", "host", "direction", "elementEnterAction", "elements", "focusInIndex", "isFocusableElement", "listenerScope", "_el", "_elements", "event", "path", "targetIndex", "el", "diff", "currentIndex", "focused", "focusedElement", "moveToNextElement", "options", "focusElement", "offset", "element", "length", "steps", "nextIndex", "relatedTarget", "code"]
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport type { ReactiveController, ReactiveElement } from 'lit';\n\ntype DirectionTypes = 'horizontal' | 'vertical' | 'both' | 'grid';\nexport type FocusGroupConfig<T> = {\n focusInIndex?: (_elements: T[]) => number;\n direction?: DirectionTypes | (() => DirectionTypes);\n elementEnterAction?: (el: T) => void;\n elements: () => T[];\n isFocusableElement?: (el: T) => boolean;\n listenerScope?: HTMLElement | (() => HTMLElement);\n};\n\nfunction ensureMethod<T, RT>(\n value: T | RT | undefined,\n type: string,\n fallback: T\n): T {\n if (typeof value === type) {\n return (() => value) as T;\n } else if (typeof value === 'function') {\n return value as T;\n }\n return fallback;\n}\n\nexport class FocusGroupController<T extends HTMLElement>\n implements ReactiveController\n{\n protected cachedElements?: T[];\n private mutationObserver: MutationObserver;\n\n get currentIndex(): number {\n if (this._currentIndex === -1) {\n this._currentIndex = this.focusInIndex;\n }\n return this._currentIndex - this.offset;\n }\n\n set currentIndex(currentIndex) {\n this._currentIndex = currentIndex + this.offset;\n }\n\n private _currentIndex = -1;\n\n private prevIndex = -1;\n\n get direction(): DirectionTypes {\n return this._direction();\n }\n\n _direction = (): DirectionTypes => 'both';\n\n public directionLength = 5;\n\n elementEnterAction = (_el: T): void => {\n return;\n };\n\n get elements(): T[] {\n if (!this.cachedElements) {\n this.cachedElements = this._elements();\n }\n return this.cachedElements;\n }\n\n private _elements!: () => T[];\n\n protected set focused(focused: boolean) {\n /* c8 ignore next 1 */\n if (focused === this.focused) return;\n this._focused = focused;\n }\n\n protected get focused(): boolean {\n return this._focused;\n }\n\n private _focused = false;\n\n get focusInElement(): T {\n return this.elements[this.focusInIndex];\n }\n\n get focusInIndex(): number {\n return this._focusInIndex(this.elements);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _focusInIndex = (_elements: T[]): number => 0;\n\n host: ReactiveElement;\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n isFocusableElement = (_el: T): boolean => true;\n\n isEventWithinListenerScope(event: Event): boolean {\n if (this._listenerScope() === this.host) return true;\n return event.composedPath().includes(this._listenerScope());\n }\n\n _listenerScope = (): HTMLElement => this.host;\n\n // When elements are virtualized, the delta between the first element\n // and the first rendered element.\n offset = 0;\n\n recentlyConnected = false;\n\n constructor(\n host: ReactiveElement,\n {\n direction,\n elementEnterAction,\n elements,\n focusInIndex,\n isFocusableElement,\n listenerScope,\n }: FocusGroupConfig<T> = { elements: () => [] }\n ) {\n this.mutationObserver = new MutationObserver(() => {\n this.handleItemMutation();\n });\n this.host = host;\n this.host.addController(this);\n this._elements = elements;\n this.isFocusableElement = isFocusableElement || this.isFocusableElement;\n this._direction = ensureMethod<() => DirectionTypes, DirectionTypes>(\n direction,\n 'string',\n this._direction\n );\n this.elementEnterAction = elementEnterAction || this.elementEnterAction;\n this._focusInIndex = ensureMethod<(_elements: T[]) => number, number>(\n focusInIndex,\n 'number',\n this._focusInIndex\n );\n this._listenerScope = ensureMethod<() => HTMLElement, HTMLElement>(\n listenerScope,\n 'object',\n this._listenerScope\n );\n }\n /* In handleItemMutation() method the first if condition is checking if the element is not focused or if the element's children's length is not decreasing then it means no element has been deleted and we must return.\n Then we are checking if the deleted element was the focused one before the deletion if so then we need to proceed else we casn return;\n */\n handleItemMutation(): void {\n if (\n this._currentIndex == -1 ||\n this.elements.length <= this._elements().length\n )\n return;\n const focusedElement = this.elements[this.currentIndex];\n this.clearElementCache();\n if (this.elements.includes(focusedElement)) return;\n const moveToNextElement = this.currentIndex !== this.elements.length;\n const diff = moveToNextElement ? 1 : -1;\n if (moveToNextElement) {\n this.setCurrentIndexCircularly(-1);\n }\n this.setCurrentIndexCircularly(diff);\n this.focus();\n }\n\n update({ elements }: FocusGroupConfig<T> = { elements: () => [] }): void {\n this.unmanage();\n this._elements = elements;\n this.clearElementCache();\n this.manage();\n }\n\n focus(options?: FocusOptions): void {\n const elements = this.elements;\n if (!elements.length) return;\n let focusElement = elements[this.currentIndex];\n if (!focusElement || !this.isFocusableElement(focusElement)) {\n this.setCurrentIndexCircularly(1);\n focusElement = elements[this.currentIndex];\n }\n if (focusElement && this.isFocusableElement(focusElement)) {\n elements[this.prevIndex]?.setAttribute('tabindex', '-1');\n focusElement.tabIndex = 0;\n focusElement.focus(options);\n }\n }\n\n clearElementCache(offset = 0): void {\n this.mutationObserver.disconnect();\n delete this.cachedElements;\n this.offset = offset;\n requestAnimationFrame(() => {\n this.elements.forEach((element) => {\n this.mutationObserver.observe(element, {\n attributes: true,\n });\n });\n });\n }\n\n setCurrentIndexCircularly(diff: number): void {\n const { length } = this.elements;\n let steps = length;\n this.prevIndex = this.currentIndex;\n // start at a possibly not 0 index\n let nextIndex = (length + this.currentIndex + diff) % length;\n while (\n // don't cycle the elements more than once\n steps &&\n this.elements[nextIndex] &&\n !this.isFocusableElement(this.elements[nextIndex])\n ) {\n nextIndex = (length + nextIndex + diff) % length;\n steps -= 1;\n }\n this.currentIndex = nextIndex;\n }\n\n hostContainsFocus(): void {\n this.host.addEventListener('focusout', this.handleFocusout);\n this.host.addEventListener('keydown', this.handleKeydown);\n this.focused = true;\n }\n\n hostNoLongerContainsFocus(): void {\n this.host.addEventListener('focusin', this.handleFocusin);\n this.host.removeEventListener('focusout', this.handleFocusout);\n this.host.removeEventListener('keydown', this.handleKeydown);\n this.focused = false;\n }\n\n isRelatedTargetOrContainAnElement(event: FocusEvent): boolean {\n const relatedTarget = event.relatedTarget as null | Element;\n\n const isRelatedTargetAnElement = this.elements.includes(\n relatedTarget as T\n );\n const isRelatedTargetContainedWithinElements = this.elements.some(\n (el) => el.contains(relatedTarget)\n );\n return !(\n isRelatedTargetAnElement || isRelatedTargetContainedWithinElements\n );\n }\n\n handleFocusin = (event: FocusEvent): void => {\n if (!this.isEventWithinListenerScope(event)) return;\n if (this.isRelatedTargetOrContainAnElement(event)) {\n this.hostContainsFocus();\n }\n const path = event.composedPath() as T[];\n let targetIndex = -1;\n path.find((el) => {\n targetIndex = this.elements.indexOf(el);\n return targetIndex !== -1;\n });\n this.prevIndex = this.currentIndex;\n this.currentIndex = targetIndex > -1 ? targetIndex : this.currentIndex;\n };\n\n handleFocusout = (event: FocusEvent): void => {\n if (this.isRelatedTargetOrContainAnElement(event)) {\n this.hostNoLongerContainsFocus();\n }\n };\n\n acceptsEventCode(code: string): boolean {\n if (code === 'End' || code === 'Home') {\n return true;\n }\n switch (this.direction) {\n case 'horizontal':\n return code === 'ArrowLeft' || code === 'ArrowRight';\n case 'vertical':\n return code === 'ArrowUp' || code === 'ArrowDown';\n case 'both':\n case 'grid':\n return code.startsWith('Arrow');\n }\n }\n\n handleKeydown = (event: KeyboardEvent): void => {\n if (!this.acceptsEventCode(event.code) || event.defaultPrevented) {\n return;\n }\n let diff = 0;\n this.prevIndex = this.currentIndex;\n switch (event.code) {\n case 'ArrowRight':\n diff += 1;\n break;\n case 'ArrowDown':\n diff += this.direction === 'grid' ? this.directionLength : 1;\n break;\n case 'ArrowLeft':\n diff -= 1;\n break;\n case 'ArrowUp':\n diff -= this.direction === 'grid' ? this.directionLength : 1;\n break;\n case 'End':\n this.currentIndex = 0;\n diff -= 1;\n break;\n case 'Home':\n this.currentIndex = this.elements.length - 1;\n diff += 1;\n break;\n }\n event.preventDefault();\n if (this.direction === 'grid' && this.currentIndex + diff < 0) {\n this.currentIndex = 0;\n } else if (\n this.direction === 'grid' &&\n this.currentIndex + diff > this.elements.length - 1\n ) {\n this.currentIndex = this.elements.length - 1;\n } else {\n this.setCurrentIndexCircularly(diff);\n }\n // To allow the `focusInIndex` to be calculated with the \"after\" state of the keyboard interaction\n // do `elementEnterAction` _before_ focusing the next element.\n this.elementEnterAction(this.elements[this.currentIndex]);\n this.focus();\n };\n\n manage(): void {\n this.addEventListeners();\n }\n\n unmanage(): void {\n this.removeEventListeners();\n }\n\n addEventListeners(): void {\n this.host.addEventListener('focusin', this.handleFocusin);\n }\n\n removeEventListeners(): void {\n this.host.removeEventListener('focusin', this.handleFocusin);\n this.host.removeEventListener('focusout', this.handleFocusout);\n this.host.removeEventListener('keydown', this.handleKeydown);\n }\n\n hostConnected(): void {\n this.recentlyConnected = true;\n this.addEventListeners();\n }\n\n hostDisconnected(): void {\n this.mutationObserver.disconnect();\n this.removeEventListeners();\n }\n\n hostUpdated(): void {\n if (this.recentlyConnected) {\n this.recentlyConnected = false;\n this.elements.forEach((element) => {\n this.mutationObserver.observe(element, {\n attributes: true,\n });\n });\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["ensureMethod", "value", "type", "fallback", "host", "direction", "elementEnterAction", "elements", "focusInIndex", "isFocusableElement", "listenerScope", "_el", "_elements", "event", "path", "targetIndex", "el", "diff", "currentIndex", "focused", "focusedElement", "moveToNextElement", "options", "_a", "focusElement", "offset", "element", "length", "steps", "nextIndex", "relatedTarget", "isRelatedTargetAnElement", "isRelatedTargetContainedWithinElements", "code"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["RovingTabindex.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport { FocusGroupConfig, FocusGroupController } from './FocusGroup.dev.js'\n\nexport type RovingTabindexConfig<T> = FocusGroupConfig<T>;\ninterface UpdateTabIndexes {\n tabIndex: number;\n removeTabIndex?: boolean;\n}\n\nexport class RovingTabindexController<\n T extends HTMLElement
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport { FocusGroupConfig, FocusGroupController } from './FocusGroup.dev.js'\n\nexport type RovingTabindexConfig<T> = FocusGroupConfig<T>;\ninterface UpdateTabIndexes {\n tabIndex: number;\n removeTabIndex?: boolean;\n}\n\nexport class RovingTabindexController<\n T extends HTMLElement,\n> extends FocusGroupController<T> {\n protected override set focused(focused: boolean) {\n if (focused === this.focused) return;\n super.focused = focused;\n this.manageTabindexes();\n }\n\n protected override get focused(): boolean {\n return super.focused;\n }\n\n private managed = true;\n\n private manageIndexesAnimationFrame = 0;\n\n override clearElementCache(offset = 0): void {\n cancelAnimationFrame(this.manageIndexesAnimationFrame);\n super.clearElementCache(offset);\n if (!this.managed) return;\n\n this.manageIndexesAnimationFrame = requestAnimationFrame(() =>\n this.manageTabindexes()\n );\n }\n\n manageTabindexes(): void {\n if (this.focused) {\n this.updateTabindexes(() => ({ tabIndex: -1 }));\n } else {\n this.updateTabindexes((el: HTMLElement): UpdateTabIndexes => {\n return {\n removeTabIndex:\n el.contains(this.focusInElement) &&\n el !== this.focusInElement,\n tabIndex: el === this.focusInElement ? 0 : -1,\n };\n });\n }\n }\n\n updateTabindexes(getTabIndex: (el: HTMLElement) => UpdateTabIndexes): void {\n this.elements.forEach((el) => {\n const { tabIndex, removeTabIndex } = getTabIndex(el);\n if (!removeTabIndex) {\n el.tabIndex = tabIndex;\n return;\n }\n el.removeAttribute('tabindex');\n const updatable = el as unknown as {\n requestUpdate?: () => void;\n };\n if (updatable.requestUpdate) updatable.requestUpdate();\n });\n }\n\n override manage(): void {\n this.managed = true;\n this.manageTabindexes();\n super.manage();\n }\n\n override unmanage(): void {\n this.managed = false;\n this.updateTabindexes(() => ({ tabIndex: 0 }));\n super.unmanage();\n }\n\n override hostUpdated(): void {\n super.hostUpdated();\n if (!this.host.hasUpdated) {\n this.manageTabindexes();\n }\n }\n}\n"],
|
|
5
5
|
"mappings": ";AAWA,SAA2B,4BAA4B;AAQhD,aAAM,iCAEH,qBAAwB;AAAA,EAF3B;AAAA;AAaH,SAAQ,UAAU;AAElB,SAAQ,8BAA8B;AAAA;AAAA,EAZtC,IAAuB,QAAQ,SAAkB;AAC7C,QAAI,YAAY,KAAK,QAAS;AAC9B,UAAM,UAAU;AAChB,SAAK,iBAAiB;AAAA,EAC1B;AAAA,EAEA,IAAuB,UAAmB;AACtC,WAAO,MAAM;AAAA,EACjB;AAAA,EAMS,kBAAkB,SAAS,GAAS;AACzC,yBAAqB,KAAK,2BAA2B;AACrD,UAAM,kBAAkB,MAAM;AAC9B,QAAI,CAAC,KAAK,QAAS;AAEnB,SAAK,8BAA8B;AAAA,MAAsB,MACrD,KAAK,iBAAiB;AAAA,IAC1B;AAAA,EACJ;AAAA,EAEA,mBAAyB;AACrB,QAAI,KAAK,SAAS;AACd,WAAK,iBAAiB,OAAO,EAAE,UAAU,GAAG,EAAE;AAAA,IAClD,OAAO;AACH,WAAK,iBAAiB,CAAC,OAAsC;AACzD,eAAO;AAAA,UACH,gBACI,GAAG,SAAS,KAAK,cAAc,KAC/B,OAAO,KAAK;AAAA,UAChB,UAAU,OAAO,KAAK,iBAAiB,IAAI;AAAA,QAC/C;AAAA,MACJ,CAAC;AAAA,IACL;AAAA,EACJ;AAAA,EAEA,iBAAiB,aAA0D;AACvE,SAAK,SAAS,QAAQ,CAAC,OAAO;AAC1B,YAAM,EAAE,UAAU,eAAe,IAAI,YAAY,EAAE;AACnD,UAAI,CAAC,gBAAgB;AACjB,WAAG,WAAW;AACd;AAAA,MACJ;AACA,SAAG,gBAAgB,UAAU;AAC7B,YAAM,YAAY;AAGlB,UAAI,UAAU,cAAe,WAAU,cAAc;AAAA,IACzD,CAAC;AAAA,EACL;AAAA,EAES,SAAe;AACpB,SAAK,UAAU;AACf,SAAK,iBAAiB;AACtB,UAAM,OAAO;AAAA,EACjB;AAAA,EAES,WAAiB;AACtB,SAAK,UAAU;AACf,SAAK,iBAAiB,OAAO,EAAE,UAAU,EAAE,EAAE;AAC7C,UAAM,SAAS;AAAA,EACnB;AAAA,EAES,cAAoB;AACzB,UAAM,YAAY;AAClB,QAAI,CAAC,KAAK,KAAK,YAAY;AACvB,WAAK,iBAAiB;AAAA,IAC1B;AAAA,EACJ;AACJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["RovingTabindex.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport { FocusGroupConfig, FocusGroupController } from './FocusGroup.js';\n\nexport type RovingTabindexConfig<T> = FocusGroupConfig<T>;\ninterface UpdateTabIndexes {\n tabIndex: number;\n removeTabIndex?: boolean;\n}\n\nexport class RovingTabindexController<\n T extends HTMLElement
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport { FocusGroupConfig, FocusGroupController } from './FocusGroup.js';\n\nexport type RovingTabindexConfig<T> = FocusGroupConfig<T>;\ninterface UpdateTabIndexes {\n tabIndex: number;\n removeTabIndex?: boolean;\n}\n\nexport class RovingTabindexController<\n T extends HTMLElement,\n> extends FocusGroupController<T> {\n protected override set focused(focused: boolean) {\n if (focused === this.focused) return;\n super.focused = focused;\n this.manageTabindexes();\n }\n\n protected override get focused(): boolean {\n return super.focused;\n }\n\n private managed = true;\n\n private manageIndexesAnimationFrame = 0;\n\n override clearElementCache(offset = 0): void {\n cancelAnimationFrame(this.manageIndexesAnimationFrame);\n super.clearElementCache(offset);\n if (!this.managed) return;\n\n this.manageIndexesAnimationFrame = requestAnimationFrame(() =>\n this.manageTabindexes()\n );\n }\n\n manageTabindexes(): void {\n if (this.focused) {\n this.updateTabindexes(() => ({ tabIndex: -1 }));\n } else {\n this.updateTabindexes((el: HTMLElement): UpdateTabIndexes => {\n return {\n removeTabIndex:\n el.contains(this.focusInElement) &&\n el !== this.focusInElement,\n tabIndex: el === this.focusInElement ? 0 : -1,\n };\n });\n }\n }\n\n updateTabindexes(getTabIndex: (el: HTMLElement) => UpdateTabIndexes): void {\n this.elements.forEach((el) => {\n const { tabIndex, removeTabIndex } = getTabIndex(el);\n if (!removeTabIndex) {\n el.tabIndex = tabIndex;\n return;\n }\n el.removeAttribute('tabindex');\n const updatable = el as unknown as {\n requestUpdate?: () => void;\n };\n if (updatable.requestUpdate) updatable.requestUpdate();\n });\n }\n\n override manage(): void {\n this.managed = true;\n this.manageTabindexes();\n super.manage();\n }\n\n override unmanage(): void {\n this.managed = false;\n this.updateTabindexes(() => ({ tabIndex: 0 }));\n super.unmanage();\n }\n\n override hostUpdated(): void {\n super.hostUpdated();\n if (!this.host.hasUpdated) {\n this.manageTabindexes();\n }\n }\n}\n"],
|
|
5
5
|
"mappings": "aAWA,OAA2B,wBAAAA,MAA4B,kBAQhD,aAAM,iCAEHA,CAAwB,CAF3B,kCAaH,KAAQ,QAAU,GAElB,KAAQ,4BAA8B,EAZtC,IAAuB,QAAQC,EAAkB,CACzCA,IAAY,KAAK,UACrB,MAAM,QAAUA,EAChB,KAAK,iBAAiB,EAC1B,CAEA,IAAuB,SAAmB,CACtC,OAAO,MAAM,OACjB,CAMS,kBAAkBC,EAAS,EAAS,CACzC,qBAAqB,KAAK,2BAA2B,EACrD,MAAM,kBAAkBA,CAAM,EACzB,KAAK,UAEV,KAAK,4BAA8B,sBAAsB,IACrD,KAAK,iBAAiB,CAC1B,EACJ,CAEA,kBAAyB,CACjB,KAAK,QACL,KAAK,iBAAiB,KAAO,CAAE,SAAU,EAAG,EAAE,EAE9C,KAAK,iBAAkBC,IACZ,CACH,eACIA,EAAG,SAAS,KAAK,cAAc,GAC/BA,IAAO,KAAK,eAChB,SAAUA,IAAO,KAAK,eAAiB,EAAI,EAC/C,EACH,CAET,CAEA,iBAAiBC,EAA0D,CACvE,KAAK,SAAS,QAASD,GAAO,CAC1B,KAAM,CAAE,SAAAE,EAAU,eAAAC,CAAe,EAAIF,EAAYD,CAAE,EACnD,GAAI,CAACG,EAAgB,CACjBH,EAAG,SAAWE,EACd,MACJ,CACAF,EAAG,gBAAgB,UAAU,EAC7B,MAAMI,EAAYJ,EAGdI,EAAU,eAAeA,EAAU,cAAc,CACzD,CAAC,CACL,CAES,QAAe,CACpB,KAAK,QAAU,GACf,KAAK,iBAAiB,EACtB,MAAM,OAAO,CACjB,CAES,UAAiB,CACtB,KAAK,QAAU,GACf,KAAK,iBAAiB,KAAO,CAAE,SAAU,CAAE,EAAE,EAC7C,MAAM,SAAS,CACnB,CAES,aAAoB,CACzB,MAAM,YAAY,EACb,KAAK,KAAK,YACX,KAAK,iBAAiB,CAE9B,CACJ",
|
|
6
6
|
"names": ["FocusGroupController", "focused", "offset", "el", "getTabIndex", "tabIndex", "removeTabIndex", "updatable"]
|
|
7
7
|
}
|
|
@@ -4,6 +4,9 @@ import "@spectrum-web-components/action-group/sp-action-group.js";
|
|
|
4
4
|
import "@spectrum-web-components/tabs/sp-tab-panel.js";
|
|
5
5
|
import "@spectrum-web-components/tabs/sp-tab.js";
|
|
6
6
|
import "@spectrum-web-components/tabs/sp-tabs.js";
|
|
7
|
+
import "@spectrum-web-components/action-menu/sp-action-menu.js";
|
|
8
|
+
import "@spectrum-web-components/menu/sp-menu.js";
|
|
9
|
+
import "@spectrum-web-components/menu/sp-menu-item.js";
|
|
7
10
|
import { elementUpdated, expect, fixture, nextFrame } from "@open-wc/testing";
|
|
8
11
|
import { html } from "@spectrum-web-components/base";
|
|
9
12
|
import { sendKeys } from "@web/test-runner-commands";
|
|
@@ -60,6 +63,32 @@ const createTabs = async () => {
|
|
|
60
63
|
await elementUpdated(tabs);
|
|
61
64
|
return tabs;
|
|
62
65
|
};
|
|
66
|
+
const createGroup = async () => {
|
|
67
|
+
const group = await fixture(
|
|
68
|
+
html`
|
|
69
|
+
<sp-action-group>
|
|
70
|
+
<sp-action-button>Button 1</sp-action-button>
|
|
71
|
+
<sp-action-button>Longer Button 2</sp-action-button>
|
|
72
|
+
<sp-action-button>Short 3</sp-action-button>
|
|
73
|
+
<sp-action-menu label="More Actions">
|
|
74
|
+
<sp-menu-item>One</sp-menu-item>
|
|
75
|
+
<sp-menu-item>Two</sp-menu-item>
|
|
76
|
+
<sp-menu-item>Three</sp-menu-item>
|
|
77
|
+
<sp-menu-item>
|
|
78
|
+
Select some items
|
|
79
|
+
<sp-menu slot="submenu" selects="multiple">
|
|
80
|
+
<sp-menu-item>A</sp-menu-item>
|
|
81
|
+
<sp-menu-item selected>B</sp-menu-item>
|
|
82
|
+
<sp-menu-item>C</sp-menu-item>
|
|
83
|
+
</sp-menu>
|
|
84
|
+
</sp-menu-item>
|
|
85
|
+
</sp-action-menu>
|
|
86
|
+
</sp-action-group>
|
|
87
|
+
`
|
|
88
|
+
);
|
|
89
|
+
await elementUpdated(group);
|
|
90
|
+
return group;
|
|
91
|
+
};
|
|
63
92
|
describe("Action Group inside of Tabs", () => {
|
|
64
93
|
it("accurately navigates the desired element", async () => {
|
|
65
94
|
const el = await createTabs();
|
|
@@ -143,4 +172,83 @@ describe("Action Group inside of Tabs", () => {
|
|
|
143
172
|
expect(actionGroup1.contains(document.activeElement)).to.be.true;
|
|
144
173
|
});
|
|
145
174
|
});
|
|
175
|
+
describe("Action Menu inside of Action Group", () => {
|
|
176
|
+
it("accurately manages the tabindex of all the elements", async () => {
|
|
177
|
+
var _a, _b;
|
|
178
|
+
const el = await createGroup();
|
|
179
|
+
const actionButton1 = el.querySelector(
|
|
180
|
+
"sp-action-button:nth-child(1)"
|
|
181
|
+
);
|
|
182
|
+
const actionButton2 = el.querySelector(
|
|
183
|
+
"sp-action-button:nth-child(2)"
|
|
184
|
+
);
|
|
185
|
+
const actionButton3 = el.querySelector(
|
|
186
|
+
"sp-action-button:nth-child(3)"
|
|
187
|
+
);
|
|
188
|
+
const actionMenu = el.querySelector("sp-action-menu");
|
|
189
|
+
el.focus();
|
|
190
|
+
expect(document.activeElement === actionButton1).to.be.true;
|
|
191
|
+
await sendKeys({
|
|
192
|
+
press: "ArrowRight"
|
|
193
|
+
});
|
|
194
|
+
expect(document.activeElement === actionButton2).to.be.true;
|
|
195
|
+
expect(actionButton2.tabIndex).to.equal(0);
|
|
196
|
+
expect(actionButton1.tabIndex).to.equal(-1);
|
|
197
|
+
expect(actionButton3.tabIndex).to.equal(-1);
|
|
198
|
+
expect(actionMenu.tabIndex).to.equal(-1);
|
|
199
|
+
await sendKeys({
|
|
200
|
+
press: "ArrowRight"
|
|
201
|
+
});
|
|
202
|
+
expect(document.activeElement === actionButton3).to.be.true;
|
|
203
|
+
expect(actionButton3.tabIndex).to.equal(0);
|
|
204
|
+
expect(actionButton2.tabIndex).to.equal(-1);
|
|
205
|
+
expect(actionButton1.tabIndex).to.equal(-1);
|
|
206
|
+
expect(actionMenu.tabIndex).to.equal(-1);
|
|
207
|
+
await sendKeys({
|
|
208
|
+
press: "ArrowRight"
|
|
209
|
+
});
|
|
210
|
+
expect(document.activeElement === actionMenu).to.be.true;
|
|
211
|
+
expect(actionButton3.tabIndex).to.equal(-1);
|
|
212
|
+
expect(actionButton2.tabIndex).to.equal(-1);
|
|
213
|
+
expect(actionButton1.tabIndex).to.equal(-1);
|
|
214
|
+
expect((_a = actionMenu.shadowRoot) == null ? void 0 : _a.querySelector("sp-action-button")).to.exist;
|
|
215
|
+
expect(
|
|
216
|
+
((_b = actionMenu.shadowRoot) == null ? void 0 : _b.querySelector(
|
|
217
|
+
"sp-action-button"
|
|
218
|
+
)).tabIndex
|
|
219
|
+
).to.equal(0);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
describe("tabIndex is cached properly", () => {
|
|
223
|
+
it("cache is managed properly", async () => {
|
|
224
|
+
var _a, _b, _c, _d, _e;
|
|
225
|
+
const menuEl = await fixture(
|
|
226
|
+
html`
|
|
227
|
+
<sp-action-menu label="More Actions">
|
|
228
|
+
<sp-menu-item>One</sp-menu-item>
|
|
229
|
+
<sp-menu-item>Two</sp-menu-item>
|
|
230
|
+
<sp-menu-item>Three</sp-menu-item>
|
|
231
|
+
</sp-action-menu>
|
|
232
|
+
`
|
|
233
|
+
);
|
|
234
|
+
expect(menuEl.tabIndex).to.equal(0);
|
|
235
|
+
expect((_a = menuEl.focusElement) == null ? void 0 : _a.tabIndex).to.equal(0);
|
|
236
|
+
menuEl.tabIndex = 1;
|
|
237
|
+
await elementUpdated(menuEl);
|
|
238
|
+
expect(menuEl.tabIndex).to.equal(1);
|
|
239
|
+
expect((_b = menuEl.focusElement) == null ? void 0 : _b.tabIndex).to.equal(1);
|
|
240
|
+
menuEl.disabled = true;
|
|
241
|
+
await elementUpdated(menuEl);
|
|
242
|
+
expect(menuEl.tabIndex).to.equal(-1);
|
|
243
|
+
expect((_c = menuEl.focusElement) == null ? void 0 : _c.tabIndex).to.equal(-1);
|
|
244
|
+
menuEl.tabIndex = 2;
|
|
245
|
+
await elementUpdated(menuEl);
|
|
246
|
+
expect(menuEl.tabIndex).to.equal(-1);
|
|
247
|
+
expect((_d = menuEl.focusElement) == null ? void 0 : _d.tabIndex).to.equal(-1);
|
|
248
|
+
menuEl.disabled = false;
|
|
249
|
+
await elementUpdated(menuEl);
|
|
250
|
+
expect(menuEl.tabIndex).to.equal(2);
|
|
251
|
+
expect((_e = menuEl.focusElement) == null ? void 0 : _e.tabIndex).to.equal(2);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
146
254
|
//# sourceMappingURL=roving-tabindex-integration.test.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["roving-tabindex-integration.test.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport '@spectrum-web-components/action-button/sp-action-button.js';\nimport { ActionButton } from '@spectrum-web-components/action-button';\nimport '@spectrum-web-components/action-group/sp-action-group.js';\nimport { ActionGroup } from '@spectrum-web-components/action-group';\nimport '@spectrum-web-components/tabs/sp-tab-panel.js';\nimport '@spectrum-web-components/tabs/sp-tab.js';\nimport '@spectrum-web-components/tabs/sp-tabs.js';\nimport { Tab, TabPanel, Tabs } from '@spectrum-web-components/tabs';\nimport { elementUpdated, expect, fixture, nextFrame } from '@open-wc/testing';\nimport { html } from '@spectrum-web-components/base';\nimport { sendKeys } from '@web/test-runner-commands';\nimport { sendMouse } from '../../../test/plugins/browser.js';\n\nconst createTabs = async (): Promise<Tabs> => {\n const tabs = await fixture<Tabs>(\n html`\n <sp-tabs selected=\"second\">\n <sp-tab label=\"Tab 1\" value=\"first\"></sp-tab>\n <sp-tab label=\"Tab 2\" value=\"second\"></sp-tab>\n <sp-tab label=\"Tab 3\" value=\"third\"></sp-tab>\n <sp-tab-panel value=\"first\">\n <sp-action-group selects=\"single\">\n <sp-action-button selected value=\"1\">\n Single Button 1\n </sp-action-button>\n <sp-action-button value=\"2\">\n Single Button 2\n </sp-action-button>\n <sp-action-button value=\"3\">\n Single Button 3\n </sp-action-button>\n </sp-action-group>\n </sp-tab-panel>\n <sp-tab-panel value=\"second\">\n <sp-action-group selects=\"multiple\">\n <sp-action-button value=\"1\">\n Multiple Button 1\n </sp-action-button>\n <sp-action-button selected value=\"2\">\n Multiple Button 2\n </sp-action-button>\n <sp-action-button selected value=\"3\">\n Multiple Button 3\n </sp-action-button>\n </sp-action-group>\n </sp-tab-panel>\n <sp-tab-panel value=\"third\">\n <sp-action-group>\n <sp-action-button value=\"1\">\n None Button 1\n </sp-action-button>\n <sp-action-button value=\"2\">\n None Button 2\n </sp-action-button>\n <sp-action-button selected value=\"3\">\n None Button 3\n </sp-action-button>\n </sp-action-group>\n </sp-tab-panel>\n </sp-tabs>\n `\n );\n await elementUpdated(tabs);\n return tabs;\n};\n\ndescribe('Action Group inside of Tabs', () => {\n it('accurately navigates the desired element', async () => {\n const el = await createTabs();\n const tab1 = el.querySelector('sp-tab[value=\"first\"]') as Tab;\n const tab2 = el.querySelector('sp-tab[value=\"second\"]');\n const tab3 = el.querySelector('sp-tab[value=\"third\"]') as Tab;\n const tabPanel1 = el.querySelector(\n 'sp-tab-panel[value=\"first\"]'\n ) as TabPanel;\n const tabPanel2 = el.querySelector(\n 'sp-tab-panel[value=\"second\"]'\n ) as TabPanel;\n const tabPanel3 = el.querySelector(\n 'sp-tab-panel[value=\"third\"]'\n ) as TabPanel;\n const actionGroup1 = tabPanel1.querySelector(\n 'sp-action-group'\n ) as ActionGroup;\n const actionGroup2 = tabPanel2.querySelector(\n 'sp-action-group'\n ) as ActionGroup;\n const actionGroup3 = tabPanel3.querySelector(\n 'sp-action-group'\n ) as ActionGroup;\n const actionButton1 = actionGroup1.querySelector(\n '[selected]'\n ) as ActionButton;\n const actionButton2 = actionGroup2.querySelector(\n '[selected]'\n ) as ActionButton;\n const actionButton3 = actionGroup3.querySelector(\n '[selected]'\n ) as ActionButton;\n\n el.focus();\n expect(el.contains(document.activeElement)).to.be.true;\n expect(document.activeElement === tab2).to.be.true;\n\n actionGroup2.focus();\n expect(document.activeElement === actionButton2).to.be.true;\n\n await nextFrame();\n await sendKeys({\n press: 'ArrowLeft',\n });\n\n expect(document.activeElement === tab1).to.be.false;\n expect(actionGroup2.contains(document.activeElement)).to.be.true;\n\n el.focus();\n expect(document.activeElement === tab2).to.be.true;\n\n await sendKeys({\n press: 'ArrowRight',\n });\n\n expect(document.activeElement === tab3).to.be.true;\n\n await sendKeys({\n press: 'Enter',\n });\n\n expect(document.activeElement === tab3).to.be.true;\n\n actionGroup3.focus();\n expect(document.activeElement === actionButton3).to.be.true;\n\n await sendKeys({\n press: 'ArrowLeft',\n });\n\n expect(document.activeElement === tab2).to.be.false;\n expect(actionGroup3.contains(document.activeElement)).to.be.true;\n\n const boundingRect = tab1.getBoundingClientRect();\n // tab1.click() doesn't current reach into the focus management here.\n await sendMouse({\n steps: [\n {\n type: 'click',\n position: [\n boundingRect.left + boundingRect.width / 2,\n boundingRect.top + boundingRect.height / 2,\n ],\n },\n ],\n });\n expect(document.activeElement === tab1).to.be.true;\n\n actionGroup1.focus();\n expect(document.activeElement === actionButton1).to.be.true;\n\n await sendKeys({\n press: 'ArrowRight',\n });\n\n expect(document.activeElement === tab2).to.be.false;\n expect(actionGroup1.contains(document.activeElement)).to.be.true;\n });\n});\n"],
|
|
5
|
-
"mappings": ";AAWA,OAAO;AAEP,OAAO;
|
|
4
|
+
"sourcesContent": ["/*\nCopyright 2020 Adobe. All rights reserved.\nThis file is licensed to you under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License. You may obtain a copy\nof the License at http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\nOF ANY KIND, either express or implied. See the License for the specific language\ngoverning permissions and limitations under the License.\n*/\nimport '@spectrum-web-components/action-button/sp-action-button.js';\nimport { ActionButton } from '@spectrum-web-components/action-button';\nimport '@spectrum-web-components/action-group/sp-action-group.js';\nimport { ActionGroup } from '@spectrum-web-components/action-group';\nimport { ActionMenu } from '@spectrum-web-components/action-menu';\nimport '@spectrum-web-components/tabs/sp-tab-panel.js';\nimport '@spectrum-web-components/tabs/sp-tab.js';\nimport '@spectrum-web-components/tabs/sp-tabs.js';\nimport '@spectrum-web-components/action-menu/sp-action-menu.js';\nimport '@spectrum-web-components/menu/sp-menu.js';\nimport '@spectrum-web-components/menu/sp-menu-item.js';\nimport { Tab, TabPanel, Tabs } from '@spectrum-web-components/tabs';\nimport { elementUpdated, expect, fixture, nextFrame } from '@open-wc/testing';\nimport { html } from '@spectrum-web-components/base';\nimport { sendKeys } from '@web/test-runner-commands';\nimport { sendMouse } from '../../../test/plugins/browser.js';\n\nconst createTabs = async (): Promise<Tabs> => {\n const tabs = await fixture<Tabs>(\n html`\n <sp-tabs selected=\"second\">\n <sp-tab label=\"Tab 1\" value=\"first\"></sp-tab>\n <sp-tab label=\"Tab 2\" value=\"second\"></sp-tab>\n <sp-tab label=\"Tab 3\" value=\"third\"></sp-tab>\n <sp-tab-panel value=\"first\">\n <sp-action-group selects=\"single\">\n <sp-action-button selected value=\"1\">\n Single Button 1\n </sp-action-button>\n <sp-action-button value=\"2\">\n Single Button 2\n </sp-action-button>\n <sp-action-button value=\"3\">\n Single Button 3\n </sp-action-button>\n </sp-action-group>\n </sp-tab-panel>\n <sp-tab-panel value=\"second\">\n <sp-action-group selects=\"multiple\">\n <sp-action-button value=\"1\">\n Multiple Button 1\n </sp-action-button>\n <sp-action-button selected value=\"2\">\n Multiple Button 2\n </sp-action-button>\n <sp-action-button selected value=\"3\">\n Multiple Button 3\n </sp-action-button>\n </sp-action-group>\n </sp-tab-panel>\n <sp-tab-panel value=\"third\">\n <sp-action-group>\n <sp-action-button value=\"1\">\n None Button 1\n </sp-action-button>\n <sp-action-button value=\"2\">\n None Button 2\n </sp-action-button>\n <sp-action-button selected value=\"3\">\n None Button 3\n </sp-action-button>\n </sp-action-group>\n </sp-tab-panel>\n </sp-tabs>\n `\n );\n await elementUpdated(tabs);\n return tabs;\n};\n\nconst createGroup = async (): Promise<ActionGroup> => {\n const group = await fixture<ActionGroup>(\n html`\n <sp-action-group>\n <sp-action-button>Button 1</sp-action-button>\n <sp-action-button>Longer Button 2</sp-action-button>\n <sp-action-button>Short 3</sp-action-button>\n <sp-action-menu label=\"More Actions\">\n <sp-menu-item>One</sp-menu-item>\n <sp-menu-item>Two</sp-menu-item>\n <sp-menu-item>Three</sp-menu-item>\n <sp-menu-item>\n Select some items\n <sp-menu slot=\"submenu\" selects=\"multiple\">\n <sp-menu-item>A</sp-menu-item>\n <sp-menu-item selected>B</sp-menu-item>\n <sp-menu-item>C</sp-menu-item>\n </sp-menu>\n </sp-menu-item>\n </sp-action-menu>\n </sp-action-group>\n `\n );\n await elementUpdated(group);\n return group;\n};\n\ndescribe('Action Group inside of Tabs', () => {\n it('accurately navigates the desired element', async () => {\n const el = await createTabs();\n const tab1 = el.querySelector('sp-tab[value=\"first\"]') as Tab;\n const tab2 = el.querySelector('sp-tab[value=\"second\"]');\n const tab3 = el.querySelector('sp-tab[value=\"third\"]') as Tab;\n const tabPanel1 = el.querySelector(\n 'sp-tab-panel[value=\"first\"]'\n ) as TabPanel;\n const tabPanel2 = el.querySelector(\n 'sp-tab-panel[value=\"second\"]'\n ) as TabPanel;\n const tabPanel3 = el.querySelector(\n 'sp-tab-panel[value=\"third\"]'\n ) as TabPanel;\n const actionGroup1 = tabPanel1.querySelector(\n 'sp-action-group'\n ) as ActionGroup;\n const actionGroup2 = tabPanel2.querySelector(\n 'sp-action-group'\n ) as ActionGroup;\n const actionGroup3 = tabPanel3.querySelector(\n 'sp-action-group'\n ) as ActionGroup;\n const actionButton1 = actionGroup1.querySelector(\n '[selected]'\n ) as ActionButton;\n const actionButton2 = actionGroup2.querySelector(\n '[selected]'\n ) as ActionButton;\n const actionButton3 = actionGroup3.querySelector(\n '[selected]'\n ) as ActionButton;\n\n el.focus();\n expect(el.contains(document.activeElement)).to.be.true;\n expect(document.activeElement === tab2).to.be.true;\n\n actionGroup2.focus();\n expect(document.activeElement === actionButton2).to.be.true;\n\n await nextFrame();\n await sendKeys({\n press: 'ArrowLeft',\n });\n\n expect(document.activeElement === tab1).to.be.false;\n expect(actionGroup2.contains(document.activeElement)).to.be.true;\n\n el.focus();\n expect(document.activeElement === tab2).to.be.true;\n\n await sendKeys({\n press: 'ArrowRight',\n });\n\n expect(document.activeElement === tab3).to.be.true;\n\n await sendKeys({\n press: 'Enter',\n });\n\n expect(document.activeElement === tab3).to.be.true;\n\n actionGroup3.focus();\n expect(document.activeElement === actionButton3).to.be.true;\n\n await sendKeys({\n press: 'ArrowLeft',\n });\n\n expect(document.activeElement === tab2).to.be.false;\n expect(actionGroup3.contains(document.activeElement)).to.be.true;\n\n const boundingRect = tab1.getBoundingClientRect();\n // tab1.click() doesn't current reach into the focus management here.\n await sendMouse({\n steps: [\n {\n type: 'click',\n position: [\n boundingRect.left + boundingRect.width / 2,\n boundingRect.top + boundingRect.height / 2,\n ],\n },\n ],\n });\n expect(document.activeElement === tab1).to.be.true;\n\n actionGroup1.focus();\n expect(document.activeElement === actionButton1).to.be.true;\n\n await sendKeys({\n press: 'ArrowRight',\n });\n\n expect(document.activeElement === tab2).to.be.false;\n expect(actionGroup1.contains(document.activeElement)).to.be.true;\n });\n});\n\ndescribe('Action Menu inside of Action Group', () => {\n it('accurately manages the tabindex of all the elements', async () => {\n const el = await createGroup();\n const actionButton1 = el.querySelector(\n 'sp-action-button:nth-child(1)'\n ) as ActionButton;\n const actionButton2 = el.querySelector(\n 'sp-action-button:nth-child(2)'\n ) as ActionButton;\n const actionButton3 = el.querySelector(\n 'sp-action-button:nth-child(3)'\n ) as ActionButton;\n const actionMenu = el.querySelector('sp-action-menu') as ActionMenu;\n\n el.focus();\n expect(document.activeElement === actionButton1).to.be.true;\n\n await sendKeys({\n press: 'ArrowRight',\n });\n\n expect(document.activeElement === actionButton2).to.be.true;\n\n // expect the focused element to have a tabindex of 0 and everyone else to be -1\n expect(actionButton2.tabIndex).to.equal(0);\n expect(actionButton1.tabIndex).to.equal(-1);\n expect(actionButton3.tabIndex).to.equal(-1);\n expect(actionMenu.tabIndex).to.equal(-1);\n\n await sendKeys({\n press: 'ArrowRight',\n });\n\n expect(document.activeElement === actionButton3).to.be.true;\n\n // expect the focused element to have a tabindex of 0 and everyone else to be -1\n expect(actionButton3.tabIndex).to.equal(0);\n expect(actionButton2.tabIndex).to.equal(-1);\n expect(actionButton1.tabIndex).to.equal(-1);\n expect(actionMenu.tabIndex).to.equal(-1);\n\n await sendKeys({\n press: 'ArrowRight',\n });\n\n expect(document.activeElement === actionMenu).to.be.true;\n\n // expect the action-button inside of the shadow root of the action-menu to have a tabindex of 0 and everyone else to be -1\n expect(actionButton3.tabIndex).to.equal(-1);\n expect(actionButton2.tabIndex).to.equal(-1);\n expect(actionButton1.tabIndex).to.equal(-1);\n expect(actionMenu.shadowRoot?.querySelector('sp-action-button')).to\n .exist;\n expect(\n (\n actionMenu.shadowRoot?.querySelector(\n 'sp-action-button'\n ) as ActionButton\n ).tabIndex\n ).to.equal(0);\n });\n});\n\ndescribe('tabIndex is cached properly', () => {\n it('cache is managed properly', async () => {\n const menuEl = await fixture<ActionMenu>(\n html`\n <sp-action-menu label=\"More Actions\">\n <sp-menu-item>One</sp-menu-item>\n <sp-menu-item>Two</sp-menu-item>\n <sp-menu-item>Three</sp-menu-item>\n </sp-action-menu>\n `\n );\n\n expect(menuEl.tabIndex).to.equal(0);\n expect(menuEl.focusElement?.tabIndex).to.equal(0);\n\n menuEl.tabIndex = 1;\n\n await elementUpdated(menuEl);\n\n expect(menuEl.tabIndex).to.equal(1);\n expect(menuEl.focusElement?.tabIndex).to.equal(1);\n\n menuEl.disabled = true;\n\n await elementUpdated(menuEl);\n\n expect(menuEl.tabIndex).to.equal(-1);\n expect(menuEl.focusElement?.tabIndex).to.equal(-1);\n\n menuEl.tabIndex = 2;\n\n await elementUpdated(menuEl);\n\n expect(menuEl.tabIndex).to.equal(-1);\n expect(menuEl.focusElement?.tabIndex).to.equal(-1);\n\n menuEl.disabled = false;\n\n await elementUpdated(menuEl);\n\n expect(menuEl.tabIndex).to.equal(2);\n expect(menuEl.focusElement?.tabIndex).to.equal(2);\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|