@momentum-design/components 0.83.2 → 0.83.3

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.
@@ -4,18 +4,18 @@ export { default as Appheader } from './appheader';
4
4
  export { default as Avatar } from './avatar';
5
5
  export { default as AvatarButton } from './avatarbutton';
6
6
  export { default as Badge } from './badge';
7
+ export { default as Brandvisual } from './brandvisual';
7
8
  export { default as Bullet } from './bullet';
8
9
  export { default as Button } from './button';
9
- export { default as Brandvisual } from './brandvisual';
10
- export { default as ButtonLink } from './buttonlink';
11
10
  export { default as ButtonGroup } from './buttongroup';
11
+ export { default as ButtonLink } from './buttonlink';
12
12
  export { default as Buttonsimple } from './buttonsimple';
13
13
  export { default as Card } from './card';
14
14
  export { default as CardButton } from './cardbutton';
15
15
  export { default as CardCheckbox } from './cardcheckbox';
16
- export { default as Checkbox } from './checkbox';
17
16
  export { default as CardRadio } from './cardradio';
18
17
  export { default as Chip } from './chip';
18
+ export { default as Checkbox } from './checkbox';
19
19
  export { default as Coachmark } from './coachmark';
20
20
  export { default as Dialog } from './dialog';
21
21
  export { default as Divider } from './divider';
@@ -32,8 +32,8 @@ export { default as List } from './list';
32
32
  export { default as ListItem } from './listitem';
33
33
  export { default as Marker } from './marker';
34
34
  export { default as MenuBar } from './menubar';
35
- export { default as MenuItemCheckbox } from './menuitemcheckbox';
36
35
  export { default as MenuItem } from './menuitem';
36
+ export { default as MenuItemCheckbox } from './menuitemcheckbox';
37
37
  export { default as MenuItemRadio } from './menuitemradio';
38
38
  export { default as MenuPopover } from './menupopover';
39
39
  export { default as MenuSection } from './menusection';
@@ -4,18 +4,18 @@ export { default as Appheader } from './appheader';
4
4
  export { default as Avatar } from './avatar';
5
5
  export { default as AvatarButton } from './avatarbutton';
6
6
  export { default as Badge } from './badge';
7
+ export { default as Brandvisual } from './brandvisual';
7
8
  export { default as Bullet } from './bullet';
8
9
  export { default as Button } from './button';
9
- export { default as Brandvisual } from './brandvisual';
10
- export { default as ButtonLink } from './buttonlink';
11
10
  export { default as ButtonGroup } from './buttongroup';
11
+ export { default as ButtonLink } from './buttonlink';
12
12
  export { default as Buttonsimple } from './buttonsimple';
13
13
  export { default as Card } from './card';
14
14
  export { default as CardButton } from './cardbutton';
15
15
  export { default as CardCheckbox } from './cardcheckbox';
16
- export { default as Checkbox } from './checkbox';
17
16
  export { default as CardRadio } from './cardradio';
18
17
  export { default as Chip } from './chip';
18
+ export { default as Checkbox } from './checkbox';
19
19
  export { default as Coachmark } from './coachmark';
20
20
  export { default as Dialog } from './dialog';
21
21
  export { default as Divider } from './divider';
@@ -32,8 +32,8 @@ export { default as List } from './list';
32
32
  export { default as ListItem } from './listitem';
33
33
  export { default as Marker } from './marker';
34
34
  export { default as MenuBar } from './menubar';
35
- export { default as MenuItemCheckbox } from './menuitemcheckbox';
36
35
  export { default as MenuItem } from './menuitem';
36
+ export { default as MenuItemCheckbox } from './menuitemcheckbox';
37
37
  export { default as MenuItemRadio } from './menuitemradio';
38
38
  export { default as MenuPopover } from './menupopover';
39
39
  export { default as MenuSection } from './menusection';
@@ -2,9 +2,9 @@ import type { Component } from '../../models';
2
2
  import type { Constructor } from './index.types';
3
3
  export declare abstract class FocusTrapClassInterface {
4
4
  protected abstract focusTrap: boolean;
5
- enabledPreventScroll: boolean;
6
5
  setInitialFocus(elementIndexToReceiveFocus?: number): void;
7
6
  activateFocusTrap(): void;
8
7
  deactivateFocusTrap(): void;
8
+ private setIsFocusTrapActivated;
9
9
  }
10
- export declare const FocusTrapMixin: <T extends Constructor<Component>>(superClass: T) => Constructor<HTMLElement & FocusTrapClassInterface> & T;
10
+ export declare const FocusTrapMixin: <T extends Constructor<Component>>(superClass: T) => Constructor<Component & FocusTrapClassInterface> & T;
@@ -7,8 +7,69 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
+ /* eslint-disable no-use-before-define */
10
11
  /* eslint-disable max-classes-per-file */
11
12
  import { property } from 'lit/decorators.js';
13
+ /**
14
+ * FocusTrapStack manages a stack of active focus traps,
15
+ * ensuring only one focus trap is active at a time.
16
+ *
17
+ * This also makes sure there is only one keydown listener active at a time,
18
+ * which is necessary to handle focus trapping correctly.
19
+ */
20
+ class FocusTrapStack {
21
+ static get stackArray() {
22
+ return Array.from(this.stack);
23
+ }
24
+ static addKeydownListener(keydownListener) {
25
+ this.currentKeydownListener = keydownListener;
26
+ document.addEventListener('keydown', keydownListener);
27
+ }
28
+ static removeKeydownListener() {
29
+ if (this.currentKeydownListener) {
30
+ document.removeEventListener('keydown', this.currentKeydownListener);
31
+ }
32
+ }
33
+ /**
34
+ * Activates a focus trap by adding it to the stack.
35
+ * It deactivates all other traps in the stack to ensure only one trap is active
36
+ *
37
+ * @param trap - The focus trap to activate.
38
+ */
39
+ static activate(trap) {
40
+ // Deactivate all other traps
41
+ this.stackArray.forEach((activeTrap) => {
42
+ if (activeTrap !== trap) {
43
+ activeTrap.setIsFocusTrapActivated(false);
44
+ }
45
+ });
46
+ this.stack.add(trap);
47
+ // remove the current keydown listener if it exists
48
+ // and add a new one for the current trap
49
+ this.removeKeydownListener();
50
+ this.addKeydownListener(trap.handleTabKeydown.bind(trap));
51
+ }
52
+ /**
53
+ * Deactivates a focus trap by removing it from the stack.
54
+ * Activates the previous trap in the stack if any.
55
+ *
56
+ * @param trap - The focus trap to deactivate.
57
+ */
58
+ static deactivate(trap) {
59
+ this.stack.delete(trap);
60
+ this.removeKeydownListener();
61
+ // activate the previous trap in the stack if any
62
+ if (this.stack.size > 0) {
63
+ const lastTrap = this.stackArray.pop();
64
+ if (lastTrap) {
65
+ lastTrap.setIsFocusTrapActivated(true);
66
+ this.addKeydownListener(lastTrap.handleTabKeydown.bind(lastTrap));
67
+ }
68
+ }
69
+ }
70
+ }
71
+ FocusTrapStack.stack = new Set();
72
+ FocusTrapStack.currentKeydownListener = null;
12
73
  export const FocusTrapMixin = (superClass) => {
13
74
  class FocusTrap extends superClass {
14
75
  constructor() {
@@ -21,11 +82,6 @@ export const FocusTrapMixin = (superClass) => {
21
82
  * @default true
22
83
  */
23
84
  this.shouldFocusTrapWrap = true;
24
- /**
25
- * Prevent outside scrolling when element is shown.
26
- * @default false
27
- */
28
- this.enabledPreventScroll = false;
29
85
  /** @internal */
30
86
  this.focusTrapIndex = -1;
31
87
  /** @internal */
@@ -33,38 +89,23 @@ export const FocusTrapMixin = (superClass) => {
33
89
  /** @internal */
34
90
  this.isFocusTrapActivated = false;
35
91
  }
36
- connectedCallback() {
37
- super.connectedCallback();
38
- document.addEventListener('keydown', this.handleTabKeydown.bind(this));
39
- }
40
- disconnectedCallback() {
41
- super.disconnectedCallback();
42
- document.removeEventListener('keydown', this.handleTabKeydown.bind(this));
43
- }
44
- async updated(changedProperties) {
45
- super.updated(changedProperties);
46
- if (changedProperties.has('focusTrap')) {
47
- if (!this.focusTrap) {
48
- this.deactivateFocusTrap();
49
- }
50
- }
92
+ setIsFocusTrapActivated(isActivated) {
93
+ this.isFocusTrapActivated = isActivated;
51
94
  }
52
95
  /**
53
96
  * Activate the focus trap
54
- * This calculates the focusable elements within the component's shadow root
55
97
  */
56
98
  activateFocusTrap() {
57
- this.isFocusTrapActivated = !!this.focusTrap;
99
+ this.setIsFocusTrapActivated(true);
100
+ FocusTrapStack.activate(this);
58
101
  }
59
102
  /**
60
103
  * Deactivate the focus trap.
61
104
  */
62
105
  deactivateFocusTrap() {
63
- this.isFocusTrapActivated = false;
106
+ this.setIsFocusTrapActivated(false);
107
+ FocusTrapStack.deactivate(this);
64
108
  this.focusTrapIndex = -1;
65
- this.enabledPreventScroll = false;
66
- // todo: this should not override the body overflow style, but reset it instead
67
- document.body.style.overflow = '';
68
109
  }
69
110
  /**
70
111
  * Checks if the element has no client rectangles (not visible in the viewport).
@@ -233,12 +274,9 @@ export const FocusTrapMixin = (superClass) => {
233
274
  if (this.focusableElements.length === 0 || !this.focusTrap) {
234
275
  return;
235
276
  }
236
- if (this.enabledPreventScroll) {
237
- document.body.style.overflow = 'hidden';
238
- }
239
277
  if (this.focusableElements[elementIndexToReceiveFocus]) {
240
278
  this.focusTrapIndex = elementIndexToReceiveFocus;
241
- this.focusableElements[elementIndexToReceiveFocus].focus();
279
+ this.focusableElements[elementIndexToReceiveFocus].focus({ preventScroll: true });
242
280
  }
243
281
  }
244
282
  /**
@@ -325,7 +363,7 @@ export const FocusTrapMixin = (superClass) => {
325
363
  }
326
364
  const nextElement = this.focusableElements[this.focusTrapIndex];
327
365
  if (nextElement) {
328
- nextElement.focus();
366
+ nextElement.focus({ preventScroll: true });
329
367
  }
330
368
  }
331
369
  /**
@@ -333,6 +371,7 @@ export const FocusTrapMixin = (superClass) => {
333
371
  *
334
372
  * @param event - The keyboard event.
335
373
  */
374
+ // @ts-ignore - this is a method which will be called in the stack
336
375
  handleTabKeydown(event) {
337
376
  if (!this.isFocusTrapActivated) {
338
377
  return;
@@ -347,9 +386,5 @@ export const FocusTrapMixin = (superClass) => {
347
386
  property({ type: Boolean, reflect: true, attribute: 'should-focus-trap-wrap' }),
348
387
  __metadata("design:type", Boolean)
349
388
  ], FocusTrap.prototype, "shouldFocusTrapWrap", void 0);
350
- __decorate([
351
- property({ type: Boolean }),
352
- __metadata("design:type", Boolean)
353
- ], FocusTrap.prototype, "enabledPreventScroll", void 0);
354
389
  return FocusTrap;
355
390
  };
@@ -0,0 +1,9 @@
1
+ import { LitElement } from 'lit';
2
+ import type { Constructor } from './index.types';
3
+ import type { Component } from '../../models';
4
+ export declare abstract class PreventScrollMixinInterface {
5
+ protected abstract preventScroll?: boolean;
6
+ protected activatePreventScroll(): void;
7
+ protected deactivatePreventScroll(): void;
8
+ }
9
+ export declare const PreventScrollMixin: <T extends Constructor<LitElement>>(superClass: T) => Constructor<Component & PreventScrollMixinInterface> & T;
@@ -0,0 +1,32 @@
1
+ export const PreventScrollMixin = (superClass) => {
2
+ class PreventScroll extends superClass {
3
+ constructor() {
4
+ super(...arguments);
5
+ /**
6
+ * @internal
7
+ */
8
+ this.isPreventScrollActive = false;
9
+ /**
10
+ * @internal
11
+ */
12
+ this.previousDocumentBodyStyleOverflow = '';
13
+ }
14
+ activatePreventScroll() {
15
+ if (this.preventScroll && !this.isPreventScrollActive) {
16
+ this.isPreventScrollActive = true;
17
+ // Store the previous body overflow style
18
+ this.previousDocumentBodyStyleOverflow = document.body.style.overflow;
19
+ // Set body overflow to hidden to prevent scrolling
20
+ document.body.style.overflow = 'hidden';
21
+ }
22
+ }
23
+ deactivatePreventScroll() {
24
+ if (this.isPreventScrollActive) {
25
+ this.isPreventScrollActive = false;
26
+ // Restore the previous body overflow style
27
+ document.body.style.overflow = this.previousDocumentBodyStyleOverflow;
28
+ }
29
+ }
30
+ }
31
+ return PreventScroll;
32
+ };
package/package.json CHANGED
@@ -41,5 +41,5 @@
41
41
  "lottie-web": "^5.12.2",
42
42
  "uuid": "^11.0.5"
43
43
  },
44
- "version": "0.83.2"
44
+ "version": "0.83.3"
45
45
  }