@patternfly/pfe-core 2.3.1 → 2.4.1
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/controllers/cascade-controller.js +1 -1
- package/controllers/cascade-controller.js.map +1 -1
- package/controllers/floating-dom-controller.d.ts +1 -1
- package/controllers/floating-dom-controller.js +81 -83
- package/controllers/floating-dom-controller.js.map +1 -1
- package/controllers/internals-controller.js +17 -20
- package/controllers/internals-controller.js.map +1 -1
- package/controllers/logger.js +1 -1
- package/controllers/logger.js.map +1 -1
- package/controllers/overflow-controller.js +42 -40
- package/controllers/overflow-controller.js.map +1 -1
- package/controllers/property-observer-controller.js +1 -1
- package/controllers/property-observer-controller.js.map +1 -1
- package/controllers/roving-tabindex-controller.js +114 -106
- package/controllers/roving-tabindex-controller.js.map +1 -1
- package/controllers/scroll-spy-controller.js +93 -91
- package/controllers/scroll-spy-controller.js.map +1 -1
- package/controllers/slot-controller.d.ts +2 -15
- package/controllers/slot-controller.js +47 -63
- package/controllers/slot-controller.js.map +1 -1
- package/controllers/timestamp-controller.d.ts +23 -0
- package/controllers/timestamp-controller.js +108 -0
- package/controllers/timestamp-controller.js.map +1 -0
- package/custom-elements.json +728 -627
- package/package.json +2 -1
|
@@ -1,78 +1,80 @@
|
|
|
1
|
-
var _OverflowController_instances, _OverflowController_container, _OverflowController_items, _OverflowController_scrollTimeoutDelay, _OverflowController_scrollTimeout, _OverflowController_hideOverflowButtons, _OverflowController_setOverflowState;
|
|
2
|
-
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
1
|
import { isElementInView } from '@patternfly/pfe-core/functions/isElementInView.js';
|
|
4
2
|
export class OverflowController {
|
|
3
|
+
/** Overflow container */
|
|
4
|
+
#container;
|
|
5
|
+
/** Children that can overflow */
|
|
6
|
+
#items;
|
|
7
|
+
#scrollTimeoutDelay;
|
|
8
|
+
#scrollTimeout;
|
|
9
|
+
/** Default state */
|
|
10
|
+
#hideOverflowButtons;
|
|
5
11
|
get firstItem() {
|
|
6
|
-
return
|
|
12
|
+
return this.#items.at(0);
|
|
7
13
|
}
|
|
8
14
|
get lastItem() {
|
|
9
|
-
return
|
|
15
|
+
return this.#items.at(-1);
|
|
10
16
|
}
|
|
11
17
|
constructor(host, options) {
|
|
12
|
-
_OverflowController_instances.add(this);
|
|
13
18
|
this.host = host;
|
|
14
19
|
this.options = options;
|
|
15
|
-
/** Overflow container */
|
|
16
|
-
_OverflowController_container.set(this, void 0);
|
|
17
20
|
/** Children that can overflow */
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
_OverflowController_scrollTimeout.set(this, void 0);
|
|
21
|
+
this.#items = [];
|
|
22
|
+
this.#scrollTimeoutDelay = 0;
|
|
21
23
|
/** Default state */
|
|
22
|
-
|
|
24
|
+
this.#hideOverflowButtons = false;
|
|
23
25
|
this.showScrollButtons = false;
|
|
24
26
|
this.overflowLeft = false;
|
|
25
27
|
this.overflowRight = false;
|
|
26
28
|
this.onScroll = () => {
|
|
27
|
-
clearTimeout(
|
|
28
|
-
|
|
29
|
+
clearTimeout(this.#scrollTimeout);
|
|
30
|
+
this.#scrollTimeout = setTimeout(() => this.#setOverflowState(), this.#scrollTimeoutDelay);
|
|
29
31
|
};
|
|
30
32
|
this.host.addController(this);
|
|
31
33
|
if (options?.hideOverflowButtons) {
|
|
32
|
-
|
|
34
|
+
this.#hideOverflowButtons = options?.hideOverflowButtons;
|
|
33
35
|
}
|
|
34
36
|
}
|
|
37
|
+
#setOverflowState() {
|
|
38
|
+
if (!this.firstItem || !this.lastItem || !this.#container) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.overflowLeft = !this.#hideOverflowButtons && !isElementInView(this.#container, this.firstItem);
|
|
42
|
+
this.overflowRight = !this.#hideOverflowButtons && !isElementInView(this.#container, this.lastItem);
|
|
43
|
+
let scrollButtonsWidth = 0;
|
|
44
|
+
if (this.overflowLeft || this.overflowRight) {
|
|
45
|
+
scrollButtonsWidth = (this.#container.parentElement?.querySelector('button')?.getBoundingClientRect().width || 0) * 2;
|
|
46
|
+
}
|
|
47
|
+
this.showScrollButtons = !this.#hideOverflowButtons &&
|
|
48
|
+
this.#container.scrollWidth > (this.#container.clientWidth + scrollButtonsWidth);
|
|
49
|
+
this.host.requestUpdate();
|
|
50
|
+
}
|
|
35
51
|
init(container, items) {
|
|
36
|
-
|
|
52
|
+
this.#container = container;
|
|
37
53
|
// convert HTMLCollection to HTMLElement[]
|
|
38
|
-
|
|
54
|
+
this.#items = items;
|
|
39
55
|
}
|
|
40
56
|
scrollLeft() {
|
|
41
|
-
if (!
|
|
57
|
+
if (!this.#container) {
|
|
42
58
|
return;
|
|
43
59
|
}
|
|
44
|
-
const leftScroll =
|
|
45
|
-
|
|
46
|
-
|
|
60
|
+
const leftScroll = this.#container.scrollLeft - this.#container.clientWidth;
|
|
61
|
+
this.#container.scroll({ left: leftScroll, behavior: 'smooth' });
|
|
62
|
+
this.#setOverflowState();
|
|
47
63
|
}
|
|
48
64
|
scrollRight() {
|
|
49
|
-
if (!
|
|
65
|
+
if (!this.#container) {
|
|
50
66
|
return;
|
|
51
67
|
}
|
|
52
|
-
const leftScroll =
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
const leftScroll = this.#container.scrollLeft + this.#container.clientWidth;
|
|
69
|
+
this.#container.scroll({ left: leftScroll, behavior: 'smooth' });
|
|
70
|
+
this.#setOverflowState();
|
|
55
71
|
}
|
|
56
72
|
update() {
|
|
57
|
-
|
|
73
|
+
this.#setOverflowState();
|
|
58
74
|
}
|
|
59
75
|
hostConnected() {
|
|
60
76
|
this.onScroll();
|
|
61
|
-
|
|
77
|
+
this.#setOverflowState();
|
|
62
78
|
}
|
|
63
79
|
}
|
|
64
|
-
_OverflowController_container = new WeakMap(), _OverflowController_items = new WeakMap(), _OverflowController_scrollTimeoutDelay = new WeakMap(), _OverflowController_scrollTimeout = new WeakMap(), _OverflowController_hideOverflowButtons = new WeakMap(), _OverflowController_instances = new WeakSet(), _OverflowController_setOverflowState = function _OverflowController_setOverflowState() {
|
|
65
|
-
if (!this.firstItem || !this.lastItem || !__classPrivateFieldGet(this, _OverflowController_container, "f")) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
this.overflowLeft = !__classPrivateFieldGet(this, _OverflowController_hideOverflowButtons, "f") && !isElementInView(__classPrivateFieldGet(this, _OverflowController_container, "f"), this.firstItem);
|
|
69
|
-
this.overflowRight = !__classPrivateFieldGet(this, _OverflowController_hideOverflowButtons, "f") && !isElementInView(__classPrivateFieldGet(this, _OverflowController_container, "f"), this.lastItem);
|
|
70
|
-
let scrollButtonsWidth = 0;
|
|
71
|
-
if (this.overflowLeft || this.overflowRight) {
|
|
72
|
-
scrollButtonsWidth = (__classPrivateFieldGet(this, _OverflowController_container, "f").parentElement?.querySelector('button')?.getBoundingClientRect().width || 0) * 2;
|
|
73
|
-
}
|
|
74
|
-
this.showScrollButtons = !__classPrivateFieldGet(this, _OverflowController_hideOverflowButtons, "f") &&
|
|
75
|
-
__classPrivateFieldGet(this, _OverflowController_container, "f").scrollWidth > (__classPrivateFieldGet(this, _OverflowController_container, "f").clientWidth + scrollButtonsWidth);
|
|
76
|
-
this.host.requestUpdate();
|
|
77
|
-
};
|
|
78
80
|
//# sourceMappingURL=overflow-controller.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overflow-controller.js","sourceRoot":"","sources":["overflow-controller.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"overflow-controller.js","sourceRoot":"","sources":["overflow-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nimport { isElementInView } from '@patternfly/pfe-core/functions/isElementInView.js';\n\nexport interface Options {\n hideOverflowButtons?: boolean;\n}\n\nexport class OverflowController implements ReactiveController {\n /** Overflow container */\n #container?: HTMLElement;\n /** Children that can overflow */\n #items: HTMLElement[] = [];\n\n #scrollTimeoutDelay = 0;\n #scrollTimeout?: ReturnType<typeof setTimeout>;\n\n /** Default state */\n #hideOverflowButtons = false;\n showScrollButtons = false;\n overflowLeft = false;\n overflowRight = false;\n\n get firstItem(): HTMLElement | undefined {\n return this.#items.at(0);\n }\n\n get lastItem(): HTMLElement | undefined {\n return this.#items.at(-1);\n }\n\n constructor(public host: ReactiveControllerHost & Element, private options?: Options) {\n this.host.addController(this);\n if (options?.hideOverflowButtons) {\n this.#hideOverflowButtons = options?.hideOverflowButtons;\n }\n }\n\n #setOverflowState(): void {\n if (!this.firstItem || !this.lastItem || !this.#container) {\n return;\n }\n this.overflowLeft = !this.#hideOverflowButtons && !isElementInView(this.#container, this.firstItem);\n this.overflowRight = !this.#hideOverflowButtons && !isElementInView(this.#container, this.lastItem);\n let scrollButtonsWidth = 0;\n if (this.overflowLeft || this.overflowRight) {\n scrollButtonsWidth = (this.#container.parentElement?.querySelector('button')?.getBoundingClientRect().width || 0) * 2;\n }\n this.showScrollButtons = !this.#hideOverflowButtons &&\n this.#container.scrollWidth > (this.#container.clientWidth + scrollButtonsWidth);\n this.host.requestUpdate();\n }\n\n init(container: HTMLElement, items: HTMLElement[]) {\n this.#container = container;\n // convert HTMLCollection to HTMLElement[]\n this.#items = items;\n }\n\n onScroll = () => {\n clearTimeout(this.#scrollTimeout);\n this.#scrollTimeout = setTimeout(() => this.#setOverflowState(), this.#scrollTimeoutDelay);\n };\n\n scrollLeft() {\n if (!this.#container) {\n return;\n }\n const leftScroll = this.#container.scrollLeft - this.#container.clientWidth;\n this.#container.scroll({ left: leftScroll, behavior: 'smooth' });\n this.#setOverflowState();\n }\n\n scrollRight() {\n if (!this.#container) {\n return;\n }\n const leftScroll = this.#container.scrollLeft + this.#container.clientWidth;\n this.#container.scroll({ left: leftScroll, behavior: 'smooth' });\n this.#setOverflowState();\n }\n\n update() {\n this.#setOverflowState();\n }\n\n hostConnected(): void {\n this.onScroll();\n this.#setOverflowState();\n }\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const observedController = Symbol('observed properties controller');
|
|
2
2
|
/** This controller holds a cache of observed property values which were set before the element updated */
|
|
3
3
|
class PropertyObserverController {
|
|
4
|
+
static { this.hosts = new WeakMap(); }
|
|
4
5
|
delete(key) {
|
|
5
6
|
this.values.delete(key);
|
|
6
7
|
}
|
|
@@ -29,6 +30,5 @@ class PropertyObserverController {
|
|
|
29
30
|
this.values.set(key, [methodName, vals]);
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
|
-
PropertyObserverController.hosts = new WeakMap();
|
|
33
33
|
export { PropertyObserverController };
|
|
34
34
|
//# sourceMappingURL=property-observer-controller.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"property-observer-controller.js","sourceRoot":"","sources":["property-observer-controller.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,gCAAgC,CAAC,CAAC;AAc3E,0GAA0G;AAC1G,MAAa,0BAA0B;
|
|
1
|
+
{"version":3,"file":"property-observer-controller.js","sourceRoot":"","sources":["property-observer-controller.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,gCAAgC,CAAC,CAAC;AAc3E,0GAA0G;AAC1G,MAAa,0BAA0B;aACtB,UAAK,GAAqD,IAAI,OAAO,EAAE,AAAlE,CAAmE;IAI/E,MAAM,CAAC,GAAW;QACxB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,YAAoB,IAAqB;QAArB,SAAI,GAAJ,IAAI,CAAiB;QANjC,WAAM,GAAG,IAAI,GAAG,EAA4D,CAAC;QAOnF,IAAI,0BAA0B,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YAC9C,OAAO,0BAA0B,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAA+B,CAAC;SACjF;QACD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACxB,IAA8C,CAAC,kBAAkB,CAAC,GAAG,IAAI,CAAC;IAC7E,CAAC;IAED,kFAAkF;IAClF,UAAU;QACR,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE;YAC/D,wCAAwC;YACxC,IAAI,CAAC,IAAI,CAAC,UAAmC,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACjE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;SAClB;IACH,CAAC;IAED,uFAAuF;IACvF,WAAW;QACT,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,GAAW,EAAE,UAAkB,EAAE,GAAG,IAAwB;QAChE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;IAC3C,CAAC;;SAjCU,0BAA0B","sourcesContent":["import type { ReactiveController, ReactiveElement } from 'lit';\n\nexport const observedController = Symbol('observed properties controller');\n\nexport type ChangeCallback<T = ReactiveElement> = (\n this: T,\n old?: T[keyof T],\n newV?: T[keyof T],\n) => void;\n\nexport type ChangeCallbackName = `_${string}Changed`;\n\nexport type PropertyObserverHost<T> = T & Record<ChangeCallbackName, ChangeCallback<T>> & {\n [observedController]: PropertyObserverController;\n}\n\n/** This controller holds a cache of observed property values which were set before the element updated */\nexport class PropertyObserverController implements ReactiveController {\n private static hosts: WeakMap<HTMLElement, PropertyObserverController> = new WeakMap();\n\n private values = new Map<string, [methodName: string, values: [unknown, unknown]]>();\n\n private delete(key: string) {\n this.values.delete(key);\n }\n\n constructor(private host: ReactiveElement) {\n if (PropertyObserverController.hosts.get(host)) {\n return PropertyObserverController.hosts.get(host) as PropertyObserverController;\n }\n host.addController(this);\n (host as PropertyObserverHost<ReactiveElement>)[observedController] = this;\n }\n\n /** Set any cached valued accumulated between constructor and connectedCallback */\n hostUpdate() {\n for (const [key, [methodName, [oldVal, newVal]]] of this.values) {\n // @ts-expect-error: be cool, typescript\n this.host[methodName as keyof ReactiveElement]?.(oldVal, newVal);\n this.delete(key);\n }\n }\n\n /** Once the element has updated, we no longer need this controller, so we remove it */\n hostUpdated() {\n this.host.removeController(this);\n }\n\n cache(key: string, methodName: string, ...vals: [unknown, unknown]) {\n this.values.set(key, [methodName, vals]);\n }\n}\n"]}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
var _RovingTabindexController_instances, _RovingTabindexController_activeItem, _RovingTabindexController_itemsContainer, _RovingTabindexController_items, _RovingTabindexController_focusableItems_get, _RovingTabindexController_activeIndex_get, _RovingTabindexController_itemIndex_get, _RovingTabindexController_onKeydown;
|
|
2
|
-
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
1
|
const isFocusableElement = (el) => !!el &&
|
|
4
2
|
!el.hasAttribute('disabled') &&
|
|
5
3
|
!el.ariaHidden &&
|
|
@@ -10,128 +8,145 @@ const isFocusableElement = (el) => !!el &&
|
|
|
10
8
|
* tabindex](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex)
|
|
11
9
|
*/
|
|
12
10
|
export class RovingTabindexController {
|
|
11
|
+
/** active focusable element */
|
|
12
|
+
#activeItem;
|
|
13
|
+
/** closest ancestor containing items */
|
|
14
|
+
#itemsContainer;
|
|
15
|
+
/** array of all focusable elements */
|
|
16
|
+
#items = [];
|
|
17
|
+
/**
|
|
18
|
+
* finds focusable items from a group of items
|
|
19
|
+
*/
|
|
20
|
+
get #focusableItems() {
|
|
21
|
+
return this.#items.filter(isFocusableElement);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* index of active item in array of focusable items
|
|
25
|
+
*/
|
|
26
|
+
get #activeIndex() {
|
|
27
|
+
return !!this.#focusableItems && !!this.activeItem ? this.#focusableItems.indexOf(this.activeItem) : -1;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* index of active item in array of items
|
|
31
|
+
*/
|
|
32
|
+
get #itemIndex() {
|
|
33
|
+
return this.activeItem ? this.#items.indexOf(this.activeItem) : -1;
|
|
34
|
+
}
|
|
13
35
|
/**
|
|
14
36
|
* active item of array of items
|
|
15
37
|
*/
|
|
16
38
|
get activeItem() {
|
|
17
|
-
return
|
|
39
|
+
return this.#activeItem;
|
|
18
40
|
}
|
|
19
41
|
/**
|
|
20
42
|
* first item in array of focusable items
|
|
21
43
|
*/
|
|
22
44
|
get firstItem() {
|
|
23
|
-
return
|
|
45
|
+
return this.#focusableItems[0];
|
|
24
46
|
}
|
|
25
47
|
/**
|
|
26
48
|
* last item in array of focusable items
|
|
27
49
|
*/
|
|
28
50
|
get lastItem() {
|
|
29
|
-
return
|
|
51
|
+
return this.#focusableItems.at(-1);
|
|
30
52
|
}
|
|
31
53
|
/**
|
|
32
54
|
* next item after active item in array of focusable items
|
|
33
55
|
*/
|
|
34
56
|
get nextItem() {
|
|
35
|
-
return (
|
|
36
|
-
:
|
|
57
|
+
return (this.#activeIndex >= this.#focusableItems.length - 1 ? this.firstItem
|
|
58
|
+
: this.#focusableItems[this.#activeIndex + 1]);
|
|
37
59
|
}
|
|
38
60
|
/**
|
|
39
61
|
* previous item after active item in array of focusable items
|
|
40
62
|
*/
|
|
41
63
|
get prevItem() {
|
|
42
|
-
return (
|
|
64
|
+
return (this.#activeIndex > 0 ? this.#focusableItems[this.#activeIndex - 1]
|
|
43
65
|
: this.lastItem);
|
|
44
66
|
}
|
|
45
67
|
constructor(host) {
|
|
46
|
-
_RovingTabindexController_instances.add(this);
|
|
47
68
|
this.host = host;
|
|
48
|
-
/** active focusable element */
|
|
49
|
-
_RovingTabindexController_activeItem.set(this, void 0);
|
|
50
|
-
/** closest ancestor containing items */
|
|
51
|
-
_RovingTabindexController_itemsContainer.set(this, void 0);
|
|
52
|
-
/** array of all focusable elements */
|
|
53
|
-
_RovingTabindexController_items.set(this, []);
|
|
54
|
-
/**
|
|
55
|
-
* handles keyboard navigation
|
|
56
|
-
*/
|
|
57
|
-
_RovingTabindexController_onKeydown.set(this, (event) => {
|
|
58
|
-
if (event.ctrlKey ||
|
|
59
|
-
event.altKey ||
|
|
60
|
-
event.metaKey ||
|
|
61
|
-
!__classPrivateFieldGet(this, _RovingTabindexController_instances, "a", _RovingTabindexController_focusableItems_get).length ||
|
|
62
|
-
!event.composedPath().some(x => __classPrivateFieldGet(this, _RovingTabindexController_instances, "a", _RovingTabindexController_focusableItems_get).includes(x))) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
const item = this.activeItem;
|
|
66
|
-
let shouldPreventDefault = false;
|
|
67
|
-
const horizontalOnly = !item ? false
|
|
68
|
-
: item.tagName === 'SELECT' ||
|
|
69
|
-
item.getAttribute('role') === 'spinbutton';
|
|
70
|
-
switch (event.key) {
|
|
71
|
-
case 'ArrowLeft':
|
|
72
|
-
this.focusOnItem(this.prevItem);
|
|
73
|
-
shouldPreventDefault = true;
|
|
74
|
-
break;
|
|
75
|
-
case 'ArrowRight':
|
|
76
|
-
this.focusOnItem(this.nextItem);
|
|
77
|
-
shouldPreventDefault = true;
|
|
78
|
-
break;
|
|
79
|
-
case 'ArrowUp':
|
|
80
|
-
if (horizontalOnly) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
this.focusOnItem(this.prevItem);
|
|
84
|
-
shouldPreventDefault = true;
|
|
85
|
-
break;
|
|
86
|
-
case 'ArrowDown':
|
|
87
|
-
if (horizontalOnly) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
this.focusOnItem(this.nextItem);
|
|
91
|
-
shouldPreventDefault = true;
|
|
92
|
-
break;
|
|
93
|
-
case 'Home':
|
|
94
|
-
this.focusOnItem(this.firstItem);
|
|
95
|
-
shouldPreventDefault = true;
|
|
96
|
-
break;
|
|
97
|
-
case 'PageUp':
|
|
98
|
-
if (horizontalOnly) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
this.focusOnItem(this.firstItem);
|
|
102
|
-
shouldPreventDefault = true;
|
|
103
|
-
break;
|
|
104
|
-
case 'End':
|
|
105
|
-
this.focusOnItem(this.lastItem);
|
|
106
|
-
shouldPreventDefault = true;
|
|
107
|
-
break;
|
|
108
|
-
case 'PageDown':
|
|
109
|
-
if (horizontalOnly) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
this.focusOnItem(this.lastItem);
|
|
113
|
-
shouldPreventDefault = true;
|
|
114
|
-
break;
|
|
115
|
-
default:
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
if (shouldPreventDefault) {
|
|
119
|
-
event.stopPropagation();
|
|
120
|
-
event.preventDefault();
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
69
|
this.host.addController(this);
|
|
124
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* handles keyboard navigation
|
|
73
|
+
*/
|
|
74
|
+
#onKeydown = (event) => {
|
|
75
|
+
if (event.ctrlKey ||
|
|
76
|
+
event.altKey ||
|
|
77
|
+
event.metaKey ||
|
|
78
|
+
!this.#focusableItems.length ||
|
|
79
|
+
!event.composedPath().some(x => this.#focusableItems.includes(x))) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const item = this.activeItem;
|
|
83
|
+
let shouldPreventDefault = false;
|
|
84
|
+
const horizontalOnly = !item ? false
|
|
85
|
+
: item.tagName === 'SELECT' ||
|
|
86
|
+
item.getAttribute('role') === 'spinbutton';
|
|
87
|
+
switch (event.key) {
|
|
88
|
+
case 'ArrowLeft':
|
|
89
|
+
this.focusOnItem(this.prevItem);
|
|
90
|
+
shouldPreventDefault = true;
|
|
91
|
+
break;
|
|
92
|
+
case 'ArrowRight':
|
|
93
|
+
this.focusOnItem(this.nextItem);
|
|
94
|
+
shouldPreventDefault = true;
|
|
95
|
+
break;
|
|
96
|
+
case 'ArrowUp':
|
|
97
|
+
if (horizontalOnly) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.focusOnItem(this.prevItem);
|
|
101
|
+
shouldPreventDefault = true;
|
|
102
|
+
break;
|
|
103
|
+
case 'ArrowDown':
|
|
104
|
+
if (horizontalOnly) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.focusOnItem(this.nextItem);
|
|
108
|
+
shouldPreventDefault = true;
|
|
109
|
+
break;
|
|
110
|
+
case 'Home':
|
|
111
|
+
this.focusOnItem(this.firstItem);
|
|
112
|
+
shouldPreventDefault = true;
|
|
113
|
+
break;
|
|
114
|
+
case 'PageUp':
|
|
115
|
+
if (horizontalOnly) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this.focusOnItem(this.firstItem);
|
|
119
|
+
shouldPreventDefault = true;
|
|
120
|
+
break;
|
|
121
|
+
case 'End':
|
|
122
|
+
this.focusOnItem(this.lastItem);
|
|
123
|
+
shouldPreventDefault = true;
|
|
124
|
+
break;
|
|
125
|
+
case 'PageDown':
|
|
126
|
+
if (horizontalOnly) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this.focusOnItem(this.lastItem);
|
|
130
|
+
shouldPreventDefault = true;
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
if (shouldPreventDefault) {
|
|
136
|
+
event.stopPropagation();
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
125
140
|
/**
|
|
126
141
|
* sets tabindex of item based on whether or not it is active
|
|
127
142
|
*/
|
|
128
143
|
updateActiveItem(item) {
|
|
129
144
|
if (item) {
|
|
130
|
-
if (!!
|
|
131
|
-
|
|
145
|
+
if (!!this.#activeItem && item !== this.#activeItem) {
|
|
146
|
+
this.#activeItem.tabIndex = -1;
|
|
132
147
|
}
|
|
133
148
|
item.tabIndex = 0;
|
|
134
|
-
|
|
149
|
+
this.#activeItem = item;
|
|
135
150
|
}
|
|
136
151
|
}
|
|
137
152
|
/**
|
|
@@ -139,34 +154,34 @@ export class RovingTabindexController {
|
|
|
139
154
|
*/
|
|
140
155
|
focusOnItem(item) {
|
|
141
156
|
this.updateActiveItem(item || this.firstItem);
|
|
142
|
-
|
|
157
|
+
this.#activeItem?.focus();
|
|
143
158
|
this.host.requestUpdate();
|
|
144
159
|
}
|
|
145
160
|
/**
|
|
146
161
|
* Focuses next focusable item
|
|
147
162
|
*/
|
|
148
163
|
updateItems(items) {
|
|
149
|
-
const sequence = [...items.slice(
|
|
150
|
-
const first = sequence.find(item =>
|
|
164
|
+
const sequence = [...items.slice(this.#itemIndex), ...items.slice(0, this.#itemIndex)];
|
|
165
|
+
const first = sequence.find(item => this.#focusableItems.includes(item));
|
|
151
166
|
this.focusOnItem(first || this.firstItem);
|
|
152
167
|
}
|
|
153
168
|
/**
|
|
154
169
|
* from array of HTML items, and sets active items
|
|
155
170
|
*/
|
|
156
171
|
initItems(items, itemsContainer = this.host) {
|
|
157
|
-
|
|
158
|
-
const focusableItems =
|
|
172
|
+
this.#items = items ?? [];
|
|
173
|
+
const focusableItems = this.#focusableItems;
|
|
159
174
|
const [focusableItem] = focusableItems;
|
|
160
|
-
|
|
175
|
+
this.#activeItem = focusableItem;
|
|
161
176
|
for (const item of focusableItems) {
|
|
162
|
-
item.tabIndex =
|
|
177
|
+
item.tabIndex = this.#activeItem === item ? 0 : -1;
|
|
163
178
|
}
|
|
164
179
|
/**
|
|
165
180
|
* removes listener on previous contained and applies it to new container
|
|
166
181
|
*/
|
|
167
|
-
if (!
|
|
168
|
-
|
|
169
|
-
|
|
182
|
+
if (!this.#itemsContainer || itemsContainer !== this.#itemsContainer) {
|
|
183
|
+
this.#itemsContainer?.removeEventListener('keydown', this.#onKeydown);
|
|
184
|
+
this.#itemsContainer = itemsContainer;
|
|
170
185
|
this.hostConnected();
|
|
171
186
|
}
|
|
172
187
|
}
|
|
@@ -174,20 +189,13 @@ export class RovingTabindexController {
|
|
|
174
189
|
* adds event listeners to items container
|
|
175
190
|
*/
|
|
176
191
|
hostConnected() {
|
|
177
|
-
|
|
192
|
+
this.#itemsContainer?.addEventListener('keydown', this.#onKeydown);
|
|
178
193
|
}
|
|
179
194
|
/**
|
|
180
195
|
* removes event listeners from items container
|
|
181
196
|
*/
|
|
182
197
|
hostDisconnected() {
|
|
183
|
-
|
|
198
|
+
this.#itemsContainer?.removeEventListener('keydown', this.#onKeydown);
|
|
184
199
|
}
|
|
185
200
|
}
|
|
186
|
-
_RovingTabindexController_activeItem = new WeakMap(), _RovingTabindexController_itemsContainer = new WeakMap(), _RovingTabindexController_items = new WeakMap(), _RovingTabindexController_onKeydown = new WeakMap(), _RovingTabindexController_instances = new WeakSet(), _RovingTabindexController_focusableItems_get = function _RovingTabindexController_focusableItems_get() {
|
|
187
|
-
return __classPrivateFieldGet(this, _RovingTabindexController_items, "f").filter(isFocusableElement);
|
|
188
|
-
}, _RovingTabindexController_activeIndex_get = function _RovingTabindexController_activeIndex_get() {
|
|
189
|
-
return !!__classPrivateFieldGet(this, _RovingTabindexController_instances, "a", _RovingTabindexController_focusableItems_get) && !!this.activeItem ? __classPrivateFieldGet(this, _RovingTabindexController_instances, "a", _RovingTabindexController_focusableItems_get).indexOf(this.activeItem) : -1;
|
|
190
|
-
}, _RovingTabindexController_itemIndex_get = function _RovingTabindexController_itemIndex_get() {
|
|
191
|
-
return this.activeItem ? __classPrivateFieldGet(this, _RovingTabindexController_items, "f").indexOf(this.activeItem) : -1;
|
|
192
|
-
};
|
|
193
201
|
//# sourceMappingURL=roving-tabindex-controller.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roving-tabindex-controller.js","sourceRoot":"","sources":["roving-tabindex-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nconst isFocusableElement = (el: Element): el is HTMLElement =>\n !!el &&\n !el.hasAttribute('disabled') &&\n !el.ariaHidden &&\n !el.hasAttribute('hidden');\n\n/**\n * Implements roving tabindex, as described in WAI-ARIA practices, [Managing Focus Within\n * Components Using a Roving\n * tabindex](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex)\n */\nexport class RovingTabindexController<\n ItemType extends HTMLElement = HTMLElement,\n> implements ReactiveController {\n /** active focusable element */\n #activeItem?: ItemType;\n\n /** closest ancestor containing items */\n #itemsContainer?: HTMLElement;\n\n /** array of all focusable elements */\n #items: ItemType[] = [];\n\n /**\n * finds focusable items from a group of items\n */\n get #focusableItems(): ItemType[] {\n return this.#items.filter(isFocusableElement);\n }\n\n /**\n * index of active item in array of focusable items\n */\n get #activeIndex(): number {\n return !!this.#focusableItems && !!this.activeItem ? this.#focusableItems.indexOf(this.activeItem) : -1;\n }\n\n /**\n * index of active item in array of items\n */\n get #itemIndex(): number {\n return this.activeItem ? this.#items.indexOf(this.activeItem) : -1;\n }\n\n /**\n * active item of array of items\n */\n get activeItem(): ItemType | undefined {\n return this.#activeItem;\n }\n\n /**\n * first item in array of focusable items\n */\n get firstItem(): ItemType | undefined {\n return this.#focusableItems[0];\n }\n\n /**\n * last item in array of focusable items\n */\n get lastItem(): ItemType | undefined {\n return this.#focusableItems.at(-1);\n }\n\n /**\n * next item after active item in array of focusable items\n */\n get nextItem(): ItemType | undefined {\n return (\n this.#activeIndex >= this.#focusableItems.length - 1 ? this.firstItem\n : this.#focusableItems[this.#activeIndex + 1]\n );\n }\n\n /**\n * previous item after active item in array of focusable items\n */\n get prevItem(): ItemType | undefined {\n return (\n this.#activeIndex > 0 ? this.#focusableItems[this.#activeIndex - 1]\n : this.lastItem\n );\n }\n\n constructor(public host: ReactiveControllerHost & HTMLElement) {\n this.host.addController(this);\n }\n\n /**\n * handles keyboard navigation\n */\n #onKeydown = (event: KeyboardEvent) => {\n if (event.ctrlKey ||\n event.altKey ||\n event.metaKey ||\n !this.#focusableItems.length ||\n !event.composedPath().some(x =>\n this.#focusableItems.includes(x as ItemType))) {\n return;\n }\n const item = this.activeItem;\n let shouldPreventDefault = false;\n const horizontalOnly =\n !item ? false\n : item.tagName === 'SELECT' ||\n item.getAttribute('role') === 'spinbutton';\n\n\n switch (event.key) {\n case 'ArrowLeft':\n this.focusOnItem(this.prevItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowRight':\n this.focusOnItem(this.nextItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowUp':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.prevItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowDown':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.nextItem);\n shouldPreventDefault = true;\n break;\n case 'Home':\n this.focusOnItem(this.firstItem);\n shouldPreventDefault = true;\n break;\n case 'PageUp':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.firstItem);\n shouldPreventDefault = true;\n break;\n case 'End':\n this.focusOnItem(this.lastItem);\n shouldPreventDefault = true;\n break;\n case 'PageDown':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.lastItem);\n shouldPreventDefault = true;\n break;\n default:\n break;\n }\n\n if (shouldPreventDefault) {\n event.stopPropagation();\n event.preventDefault();\n }\n };\n\n /**\n * sets tabindex of item based on whether or not it is active\n */\n updateActiveItem(item?: ItemType): void {\n if (item) {\n if (!!this.#activeItem && item !== this.#activeItem) {\n this.#activeItem.tabIndex = -1;\n }\n item.tabIndex = 0;\n this.#activeItem = item;\n }\n }\n\n /**\n * focuses on an item and sets it as active\n */\n focusOnItem(item?: ItemType): void {\n this.updateActiveItem(item || this.firstItem);\n this.#activeItem?.focus();\n this.host.requestUpdate();\n }\n\n /**\n * Focuses next focusable item\n */\n updateItems(items: ItemType[]) {\n const sequence = [...items.slice(this.#itemIndex), ...items.slice(0, this.#itemIndex)];\n const first = sequence.find(item => this.#focusableItems.includes(item));\n this.focusOnItem(first || this.firstItem);\n }\n\n /**\n * from array of HTML items, and sets active items\n */\n initItems(items: ItemType[], itemsContainer: HTMLElement = this.host) {\n this.#items = items ?? [];\n const focusableItems = this.#focusableItems;\n const [focusableItem] = focusableItems;\n this.#activeItem = focusableItem;\n for (const item of focusableItems) {\n item.tabIndex = this.#activeItem === item ? 0 : -1;\n }\n /**\n * removes listener on previous contained and applies it to new container\n */\n if (!this.#itemsContainer || itemsContainer !== this.#itemsContainer) {\n this.#itemsContainer?.removeEventListener('keydown', this.#onKeydown);\n this.#itemsContainer = itemsContainer;\n this.hostConnected();\n }\n }\n\n /**\n * adds event listeners to items container\n */\n hostConnected() {\n this.#itemsContainer?.addEventListener('keydown', this.#onKeydown);\n }\n\n /**\n * removes event listeners from items container\n */\n hostDisconnected() {\n this.#itemsContainer?.removeEventListener('keydown', this.#onKeydown);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"roving-tabindex-controller.js","sourceRoot":"","sources":["roving-tabindex-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nconst isFocusableElement = (el: Element): el is HTMLElement =>\n !!el &&\n !el.hasAttribute('disabled') &&\n !el.ariaHidden &&\n !el.hasAttribute('hidden');\n\n/**\n * Implements roving tabindex, as described in WAI-ARIA practices, [Managing Focus Within\n * Components Using a Roving\n * tabindex](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex)\n */\nexport class RovingTabindexController<\n ItemType extends HTMLElement = HTMLElement,\n> implements ReactiveController {\n /** active focusable element */\n #activeItem?: ItemType;\n\n /** closest ancestor containing items */\n #itemsContainer?: HTMLElement;\n\n /** array of all focusable elements */\n #items: ItemType[] = [];\n\n /**\n * finds focusable items from a group of items\n */\n get #focusableItems(): ItemType[] {\n return this.#items.filter(isFocusableElement);\n }\n\n /**\n * index of active item in array of focusable items\n */\n get #activeIndex(): number {\n return !!this.#focusableItems && !!this.activeItem ? this.#focusableItems.indexOf(this.activeItem) : -1;\n }\n\n /**\n * index of active item in array of items\n */\n get #itemIndex(): number {\n return this.activeItem ? this.#items.indexOf(this.activeItem) : -1;\n }\n\n /**\n * active item of array of items\n */\n get activeItem(): ItemType | undefined {\n return this.#activeItem;\n }\n\n /**\n * first item in array of focusable items\n */\n get firstItem(): ItemType | undefined {\n return this.#focusableItems[0];\n }\n\n /**\n * last item in array of focusable items\n */\n get lastItem(): ItemType | undefined {\n return this.#focusableItems.at(-1);\n }\n\n /**\n * next item after active item in array of focusable items\n */\n get nextItem(): ItemType | undefined {\n return (\n this.#activeIndex >= this.#focusableItems.length - 1 ? this.firstItem\n : this.#focusableItems[this.#activeIndex + 1]\n );\n }\n\n /**\n * previous item after active item in array of focusable items\n */\n get prevItem(): ItemType | undefined {\n return (\n this.#activeIndex > 0 ? this.#focusableItems[this.#activeIndex - 1]\n : this.lastItem\n );\n }\n\n constructor(public host: ReactiveControllerHost & HTMLElement) {\n this.host.addController(this);\n }\n\n /**\n * handles keyboard navigation\n */\n #onKeydown = (event: KeyboardEvent) => {\n if (event.ctrlKey ||\n event.altKey ||\n event.metaKey ||\n !this.#focusableItems.length ||\n !event.composedPath().some(x =>\n this.#focusableItems.includes(x as ItemType))) {\n return;\n }\n const item = this.activeItem;\n let shouldPreventDefault = false;\n const horizontalOnly =\n !item ? false\n : item.tagName === 'SELECT' ||\n item.getAttribute('role') === 'spinbutton';\n\n\n switch (event.key) {\n case 'ArrowLeft':\n this.focusOnItem(this.prevItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowRight':\n this.focusOnItem(this.nextItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowUp':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.prevItem);\n shouldPreventDefault = true;\n break;\n case 'ArrowDown':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.nextItem);\n shouldPreventDefault = true;\n break;\n case 'Home':\n this.focusOnItem(this.firstItem);\n shouldPreventDefault = true;\n break;\n case 'PageUp':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.firstItem);\n shouldPreventDefault = true;\n break;\n case 'End':\n this.focusOnItem(this.lastItem);\n shouldPreventDefault = true;\n break;\n case 'PageDown':\n if (horizontalOnly) {\n return;\n }\n this.focusOnItem(this.lastItem);\n shouldPreventDefault = true;\n break;\n default:\n break;\n }\n\n if (shouldPreventDefault) {\n event.stopPropagation();\n event.preventDefault();\n }\n };\n\n /**\n * sets tabindex of item based on whether or not it is active\n */\n updateActiveItem(item?: ItemType): void {\n if (item) {\n if (!!this.#activeItem && item !== this.#activeItem) {\n this.#activeItem.tabIndex = -1;\n }\n item.tabIndex = 0;\n this.#activeItem = item;\n }\n }\n\n /**\n * focuses on an item and sets it as active\n */\n focusOnItem(item?: ItemType): void {\n this.updateActiveItem(item || this.firstItem);\n this.#activeItem?.focus();\n this.host.requestUpdate();\n }\n\n /**\n * Focuses next focusable item\n */\n updateItems(items: ItemType[]) {\n const sequence = [...items.slice(this.#itemIndex), ...items.slice(0, this.#itemIndex)];\n const first = sequence.find(item => this.#focusableItems.includes(item));\n this.focusOnItem(first || this.firstItem);\n }\n\n /**\n * from array of HTML items, and sets active items\n */\n initItems(items: ItemType[], itemsContainer: HTMLElement = this.host) {\n this.#items = items ?? [];\n const focusableItems = this.#focusableItems;\n const [focusableItem] = focusableItems;\n this.#activeItem = focusableItem;\n for (const item of focusableItems) {\n item.tabIndex = this.#activeItem === item ? 0 : -1;\n }\n /**\n * removes listener on previous contained and applies it to new container\n */\n if (!this.#itemsContainer || itemsContainer !== this.#itemsContainer) {\n this.#itemsContainer?.removeEventListener('keydown', this.#onKeydown);\n this.#itemsContainer = itemsContainer;\n this.hostConnected();\n }\n }\n\n /**\n * adds event listeners to items container\n */\n hostConnected() {\n this.#itemsContainer?.addEventListener('keydown', this.#onKeydown);\n }\n\n /**\n * removes event listeners from items container\n */\n hostDisconnected() {\n this.#itemsContainer?.removeEventListener('keydown', this.#onKeydown);\n }\n}\n"]}
|