@momentum-design/components 0.129.43 → 0.129.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.js +394 -394
- package/dist/browser/index.js.map +4 -4
- package/dist/components/accordionbutton/accordionbutton.component.d.ts +1 -1
- package/dist/components/accordionbutton/accordionbutton.component.js +4 -3
- package/dist/components/buttonsimple/buttonsimple.component.d.ts +1 -1
- package/dist/components/buttonsimple/buttonsimple.component.js +8 -6
- package/dist/components/cardcheckbox/cardcheckbox.component.d.ts +1 -1
- package/dist/components/cardcheckbox/cardcheckbox.component.js +7 -5
- package/dist/components/cardradio/cardradio.component.d.ts +1 -1
- package/dist/components/cardradio/cardradio.component.js +8 -7
- package/dist/components/checkbox/checkbox.component.d.ts +2 -1
- package/dist/components/checkbox/checkbox.component.js +7 -4
- package/dist/components/combobox/combobox.component.d.ts +1 -1
- package/dist/components/combobox/combobox.component.js +11 -11
- package/dist/components/dialog/dialog.component.d.ts +1 -1
- package/dist/components/dialog/dialog.component.js +1 -1
- package/dist/components/input/input.component.d.ts +1 -1
- package/dist/components/input/input.component.js +3 -3
- package/dist/components/list/list.component.d.ts +1 -1
- package/dist/components/listbox/listbox.component.d.ts +1 -1
- package/dist/components/listitem/listitem.component.d.ts +1 -1
- package/dist/components/listitem/listitem.component.js +11 -5
- package/dist/components/menubar/menubar.component.d.ts +2 -2
- package/dist/components/menubar/menubar.component.js +10 -20
- package/dist/components/menuitem/menuitem.component.js +3 -3
- package/dist/components/menupopover/menupopover.component.d.ts +0 -10
- package/dist/components/menupopover/menupopover.component.js +15 -36
- package/dist/components/popover/popover.component.d.ts +1 -1
- package/dist/components/popover/popover.component.js +4 -3
- package/dist/components/radio/radio.component.d.ts +1 -1
- package/dist/components/radio/radio.component.js +8 -7
- package/dist/components/searchfield/searchfield.component.d.ts +3 -0
- package/dist/components/searchfield/searchfield.component.js +18 -5
- package/dist/components/searchpopover/searchpopover.component.js +3 -4
- package/dist/components/select/select.component.d.ts +1 -1
- package/dist/components/select/select.component.js +9 -8
- package/dist/components/slider/slider.component.d.ts +2 -1
- package/dist/components/slider/slider.component.js +4 -3
- package/dist/components/stepperitem/stepperitem.component.d.ts +1 -1
- package/dist/components/stepperitem/stepperitem.component.js +8 -6
- package/dist/components/textarea/textarea.component.d.ts +1 -1
- package/dist/components/textarea/textarea.component.js +5 -4
- package/dist/components/toggle/toggle.component.d.ts +1 -1
- package/dist/components/toggle/toggle.component.js +5 -4
- package/dist/components/virtualizedlist/virtualizedlist.component.d.ts +1 -1
- package/dist/components/virtualizedlist/virtualizedlist.component.js +7 -6
- package/dist/custom-elements.json +1276 -4245
- package/dist/react/checkbox/index.d.ts +1 -0
- package/dist/react/checkbox/index.js +1 -0
- package/dist/utils/dom.d.ts +83 -0
- package/dist/utils/dom.js +164 -0
- package/dist/utils/mixins/KeyToActionMixin.d.ts +69 -0
- package/dist/utils/mixins/KeyToActionMixin.js +92 -0
- package/dist/utils/mixins/ListNavigationMixin.d.ts +2 -1
- package/dist/utils/mixins/ListNavigationMixin.js +10 -33
- package/dist/utils/mixins/{FocusTrapMixin.d.ts → focus/FocusTrapMixin.d.ts} +2 -2
- package/dist/utils/mixins/focus/FocusTrapMixin.js +190 -0
- package/dist/utils/mixins/focus/FocusTrapStack.d.ts +32 -0
- package/dist/utils/mixins/focus/FocusTrapStack.js +69 -0
- package/package.json +1 -1
- package/dist/utils/mixins/FocusTrapMixin.js +0 -418
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
/* eslint-disable no-use-before-define */
|
|
11
|
+
/* eslint-disable max-classes-per-file */
|
|
12
|
+
import { property } from 'lit/decorators.js';
|
|
13
|
+
import { findFocusable } from '../../dom';
|
|
14
|
+
import { FocusTrapStack } from './FocusTrapStack';
|
|
15
|
+
export const FocusTrapMixin = (superClass) => {
|
|
16
|
+
class FocusTrap extends superClass {
|
|
17
|
+
constructor() {
|
|
18
|
+
super(...arguments);
|
|
19
|
+
/**
|
|
20
|
+
* Determines whether focus should wrap around when reaching the first or last focusable element.
|
|
21
|
+
* If true, focus will cycle from end to start and vice versa.
|
|
22
|
+
*
|
|
23
|
+
* This only applies when `focusTrap` is true.
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
this.shouldFocusTrapWrap = true;
|
|
27
|
+
/** @internal */
|
|
28
|
+
this.focusTrapIndex = -1;
|
|
29
|
+
/** @internal */
|
|
30
|
+
this.focusableElements = [];
|
|
31
|
+
/** @internal */
|
|
32
|
+
this.isFocusTrapActivated = false;
|
|
33
|
+
}
|
|
34
|
+
setIsFocusTrapActivated(isActivated) {
|
|
35
|
+
this.isFocusTrapActivated = isActivated;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Activate the focus trap
|
|
39
|
+
*/
|
|
40
|
+
activateFocusTrap() {
|
|
41
|
+
this.setIsFocusTrapActivated(true);
|
|
42
|
+
FocusTrapStack.activate(this);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Deactivate the focus trap.
|
|
46
|
+
*/
|
|
47
|
+
deactivateFocusTrap() {
|
|
48
|
+
this.setIsFocusTrapActivated(false);
|
|
49
|
+
FocusTrapStack.deactivate(this);
|
|
50
|
+
this.focusTrapIndex = -1;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Updates the list of focusable elements within the component's shadow root.
|
|
54
|
+
*/
|
|
55
|
+
setFocusableElements() {
|
|
56
|
+
if (!this.shadowRoot)
|
|
57
|
+
return;
|
|
58
|
+
this.focusableElements = findFocusable(this.shadowRoot);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Sets the initial focus within the container.
|
|
62
|
+
*
|
|
63
|
+
* @param elementIndexToReceiveFocus - The index of the preferable element to focus.
|
|
64
|
+
*/
|
|
65
|
+
setInitialFocus(elementIndexToReceiveFocus = 0) {
|
|
66
|
+
this.setFocusableElements();
|
|
67
|
+
if (this.focusableElements.length === 0 || !this.focusTrap) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (this.focusableElements[elementIndexToReceiveFocus]) {
|
|
71
|
+
this.focusTrapIndex = elementIndexToReceiveFocus;
|
|
72
|
+
this.focusableElements[elementIndexToReceiveFocus].focus({ preventScroll: true });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Calculates the next index for the focus trap.
|
|
77
|
+
*
|
|
78
|
+
* @param currentIndex - The current index.
|
|
79
|
+
* @param step - The step to calculate the next index.
|
|
80
|
+
* @returns The next index.
|
|
81
|
+
*/
|
|
82
|
+
calculateNextIndex(currentIndex, step) {
|
|
83
|
+
const { length } = this.focusableElements;
|
|
84
|
+
if (currentIndex === -1) {
|
|
85
|
+
return step > 0 ? 0 : length - 1;
|
|
86
|
+
}
|
|
87
|
+
let nextIndex = currentIndex + step;
|
|
88
|
+
if (this.shouldFocusTrapWrap) {
|
|
89
|
+
if (nextIndex < 0)
|
|
90
|
+
nextIndex = length - 1;
|
|
91
|
+
if (nextIndex >= length)
|
|
92
|
+
nextIndex = 0;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
if (nextIndex < 0)
|
|
96
|
+
nextIndex = 0;
|
|
97
|
+
if (nextIndex >= length)
|
|
98
|
+
nextIndex = length - 1;
|
|
99
|
+
}
|
|
100
|
+
return nextIndex;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Returns the deepest active element in the shadow DOM.
|
|
104
|
+
*
|
|
105
|
+
* @returns The deepest active element.
|
|
106
|
+
*/
|
|
107
|
+
getDeepActiveElement() {
|
|
108
|
+
var _a;
|
|
109
|
+
let host = document.activeElement || document.body;
|
|
110
|
+
while (host instanceof HTMLElement && ((_a = host.shadowRoot) === null || _a === void 0 ? void 0 : _a.activeElement)) {
|
|
111
|
+
host = host.shadowRoot.activeElement;
|
|
112
|
+
}
|
|
113
|
+
return host || document.body;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Finds the index of the active element within the focusable elements.
|
|
117
|
+
*
|
|
118
|
+
* @param activeElement - The active element.
|
|
119
|
+
* @returns The index of the active element.
|
|
120
|
+
*/
|
|
121
|
+
findElement(activeElement) {
|
|
122
|
+
return this.focusableElements.findIndex(element => this.isEqualFocusNode(activeElement, element));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Checks if the active element is equal to the given element.
|
|
126
|
+
*
|
|
127
|
+
* @param activeElement - The active element.
|
|
128
|
+
* @param element - The element to compare.
|
|
129
|
+
* @returns True if the active element is equal to the given element.
|
|
130
|
+
*/
|
|
131
|
+
isEqualFocusNode(activeElement, element) {
|
|
132
|
+
if (activeElement.nodeType >= 0) {
|
|
133
|
+
return element.isEqualNode(activeElement) && element === activeElement;
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Traps focus within the container.
|
|
139
|
+
*
|
|
140
|
+
* @param direction - The direction of the focus trap.
|
|
141
|
+
* If true, the focus will be trapped in the previous element.
|
|
142
|
+
*/
|
|
143
|
+
trapFocus(event) {
|
|
144
|
+
// calculate the focusable elements
|
|
145
|
+
this.setFocusableElements();
|
|
146
|
+
if (this.focusableElements.length === 0) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const activeElement = this.getDeepActiveElement();
|
|
150
|
+
const activeIndex = this.findElement(activeElement);
|
|
151
|
+
const direction = event.shiftKey;
|
|
152
|
+
if (direction) {
|
|
153
|
+
this.focusTrapIndex = this.calculateNextIndex(activeIndex, -1);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
this.focusTrapIndex = this.calculateNextIndex(activeIndex, 1);
|
|
157
|
+
}
|
|
158
|
+
const nextElement = this.focusableElements[this.focusTrapIndex];
|
|
159
|
+
if (nextElement.tagName === 'IFRAME') {
|
|
160
|
+
// If the next element is an iframe we should not focus it manually
|
|
161
|
+
// but just let the browser handle it.
|
|
162
|
+
// this only works if there are focusable elements around the iframe!
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (nextElement) {
|
|
166
|
+
event.preventDefault();
|
|
167
|
+
nextElement.focus();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Traps focus within the container.
|
|
172
|
+
*
|
|
173
|
+
* @param event - The keyboard event.
|
|
174
|
+
*/
|
|
175
|
+
// @ts-ignore - this is a method which will be called in the stack
|
|
176
|
+
handleTabKeydown(event) {
|
|
177
|
+
if (!this.isFocusTrapActivated) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (event.key === 'Tab') {
|
|
181
|
+
this.trapFocus(event);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
__decorate([
|
|
186
|
+
property({ type: Boolean, reflect: true, attribute: 'should-focus-trap-wrap' }),
|
|
187
|
+
__metadata("design:type", Boolean)
|
|
188
|
+
], FocusTrap.prototype, "shouldFocusTrapWrap", void 0);
|
|
189
|
+
return FocusTrap;
|
|
190
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FocusTrapStack manages a stack of active focus traps,
|
|
3
|
+
* ensuring only one focus trap is active at a time.
|
|
4
|
+
*
|
|
5
|
+
* This also makes sure there is only one keydown listener active at a time,
|
|
6
|
+
* which is necessary to handle focus trapping correctly.
|
|
7
|
+
*
|
|
8
|
+
* Handling iFrames is supported, as long as there are focusable elements around the iFrame.
|
|
9
|
+
* Otherwise it will not work as expected.
|
|
10
|
+
*/
|
|
11
|
+
export declare class FocusTrapStack {
|
|
12
|
+
private static stack;
|
|
13
|
+
static get stackArray(): any[];
|
|
14
|
+
static getActiveTrap(): any;
|
|
15
|
+
private static currentKeydownListener;
|
|
16
|
+
private static addKeydownListener;
|
|
17
|
+
private static removeKeydownListener;
|
|
18
|
+
/**
|
|
19
|
+
* Activates a focus trap by adding it to the stack.
|
|
20
|
+
* It deactivates all other traps in the stack to ensure only one trap is active
|
|
21
|
+
*
|
|
22
|
+
* @param trap - The focus trap to activate.
|
|
23
|
+
*/
|
|
24
|
+
static activate(trap: any): void;
|
|
25
|
+
/**
|
|
26
|
+
* Deactivates a focus trap by removing it from the stack.
|
|
27
|
+
* Activates the previous trap in the stack if any.
|
|
28
|
+
*
|
|
29
|
+
* @param trap - The focus trap to deactivate.
|
|
30
|
+
*/
|
|
31
|
+
static deactivate(trap: any): void;
|
|
32
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FocusTrapStack manages a stack of active focus traps,
|
|
3
|
+
* ensuring only one focus trap is active at a time.
|
|
4
|
+
*
|
|
5
|
+
* This also makes sure there is only one keydown listener active at a time,
|
|
6
|
+
* which is necessary to handle focus trapping correctly.
|
|
7
|
+
*
|
|
8
|
+
* Handling iFrames is supported, as long as there are focusable elements around the iFrame.
|
|
9
|
+
* Otherwise it will not work as expected.
|
|
10
|
+
*/
|
|
11
|
+
export class FocusTrapStack {
|
|
12
|
+
static get stackArray() {
|
|
13
|
+
return Array.from(this.stack);
|
|
14
|
+
}
|
|
15
|
+
static getActiveTrap() {
|
|
16
|
+
return this.stackArray.at(-1);
|
|
17
|
+
}
|
|
18
|
+
static addKeydownListener(keydownListener) {
|
|
19
|
+
this.currentKeydownListener = keydownListener;
|
|
20
|
+
document.addEventListener('keydown', keydownListener);
|
|
21
|
+
}
|
|
22
|
+
static removeKeydownListener() {
|
|
23
|
+
if (this.currentKeydownListener) {
|
|
24
|
+
document.removeEventListener('keydown', this.currentKeydownListener);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Activates a focus trap by adding it to the stack.
|
|
29
|
+
* It deactivates all other traps in the stack to ensure only one trap is active
|
|
30
|
+
*
|
|
31
|
+
* @param trap - The focus trap to activate.
|
|
32
|
+
*/
|
|
33
|
+
static activate(trap) {
|
|
34
|
+
// Deactivate all other traps
|
|
35
|
+
this.stackArray.forEach(activeTrap => {
|
|
36
|
+
if (activeTrap !== trap) {
|
|
37
|
+
activeTrap.setIsFocusTrapActivated(false);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
this.stack.add(trap);
|
|
41
|
+
// remove the current keydown listener if it exists
|
|
42
|
+
// and add a new one for the current trap
|
|
43
|
+
this.removeKeydownListener();
|
|
44
|
+
this.addKeydownListener(trap.handleTabKeydown.bind(trap));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Deactivates a focus trap by removing it from the stack.
|
|
48
|
+
* Activates the previous trap in the stack if any.
|
|
49
|
+
*
|
|
50
|
+
* @param trap - The focus trap to deactivate.
|
|
51
|
+
*/
|
|
52
|
+
static deactivate(trap) {
|
|
53
|
+
if (!this.stack.has(trap)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.stack.delete(trap);
|
|
57
|
+
this.removeKeydownListener();
|
|
58
|
+
// activate the previous trap in the stack if any
|
|
59
|
+
if (this.stack.size > 0) {
|
|
60
|
+
const lastTrap = this.stackArray.pop();
|
|
61
|
+
if (lastTrap) {
|
|
62
|
+
lastTrap.setIsFocusTrapActivated(true);
|
|
63
|
+
this.addKeydownListener(lastTrap.handleTabKeydown.bind(lastTrap));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
FocusTrapStack.stack = new Set();
|
|
69
|
+
FocusTrapStack.currentKeydownListener = null;
|