@momentum-design/components 0.129.46 → 0.129.47

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.
Files changed (27) hide show
  1. package/dist/browser/index.js +264 -264
  2. package/dist/browser/index.js.map +4 -4
  3. package/dist/components/combobox/combobox.component.d.ts +1 -1
  4. package/dist/components/combobox/combobox.component.js +2 -5
  5. package/dist/components/dialog/dialog.component.d.ts +19 -7
  6. package/dist/components/dialog/dialog.component.js +40 -14
  7. package/dist/components/menubar/menubar.component.d.ts +6 -0
  8. package/dist/components/menubar/menubar.component.js +30 -26
  9. package/dist/components/menupopover/menupopover.component.d.ts +1 -1
  10. package/dist/components/menupopover/menupopover.component.js +19 -24
  11. package/dist/components/popover/popover.component.d.ts +14 -25
  12. package/dist/components/popover/popover.component.js +23 -32
  13. package/dist/components/searchpopover/searchpopover.component.d.ts +1 -1
  14. package/dist/components/searchpopover/searchpopover.component.js +2 -2
  15. package/dist/components/select/select.component.d.ts +1 -1
  16. package/dist/components/select/select.component.js +5 -3
  17. package/dist/components/tooltip/tooltip.component.d.ts +1 -0
  18. package/dist/components/tooltip/tooltip.component.js +17 -0
  19. package/dist/custom-elements.json +42 -59
  20. package/dist/utils/controllers/DepthManager.d.ts +202 -0
  21. package/dist/utils/controllers/DepthManager.js +259 -0
  22. package/dist/utils/mixins/BackdropMixin.js +19 -2
  23. package/dist/utils/mixins/FocusTrapMixin.d.ts +0 -0
  24. package/dist/utils/mixins/FocusTrapMixin.js +1 -0
  25. package/package.json +1 -1
  26. package/dist/components/popover/popover.stack.d.ts +0 -53
  27. package/dist/components/popover/popover.stack.js +0 -66
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Global stack of elements managed by DepthManager
3
+ * @internal
4
+ */
5
+ const elementStack = [];
6
+ /**
7
+ * Initial z-index for the first element in the stack
8
+ */
9
+ const BASE_Z_INDEX = 1000;
10
+ /**
11
+ * Number of z-index levels per element in the stack
12
+ * @internal
13
+ * - level -2: level of backdrop (if any)
14
+ * - level -1: level of trigger component (if any)
15
+ * - level 0: z-index of the overlay element (e.g. popover, dialog, etc.)
16
+ */
17
+ const NUMBER_OF_Z_INDEX_LEVELS_PER_ELEMENT = 3;
18
+ export const OVERLAY_BACKDROP_Z_INDEX_OFFSET = -2;
19
+ export const OVERLAY_TRIGGER_Z_INDEX_OFFSET = -1;
20
+ /**
21
+ * DepthManager is a controller that manages a stack of elements to control their depth (z-index).
22
+ *
23
+ * It uses a global stack to keep track of the order of elements, allowing for proper layering of overlays.
24
+ *
25
+ * ## Use Case
26
+ *
27
+ * ### Pop single overlay
28
+ *
29
+ * The easiest one, usually the host removes itself from the stack when it is closed, with the `popHost` method.
30
+ *
31
+ * ### Pop until specific overlay
32
+ *
33
+ * When the chain of nested popover (e.g.: submenus) opened and the user closes other than the last one,
34
+ * we have to close all overlays stacked above the specific one. In this case, the `popItem` or directly the `pupUntil` method is used.
35
+ *
36
+ * ### Closing "sibling" overlays
37
+ *
38
+ * In some cases, multiple overlays can be opened from the same trigger (e.g.: tooltip, context menu, etc.), we can not close
39
+ * all overlays above the specific one, because they independently opened.
40
+ *
41
+ * `popUntil` method can handle this case by skipping the overlays which share the same trigger ID as the specified one.
42
+ *
43
+ * ### Manually removing overlays
44
+ *
45
+ * When the user switch from one sub-menu to another, we need to close all sub-menus from common parent menu.
46
+ * DepthManager does not have built-in solution for this case. The host component need to make sure the submenu
47
+ * hide before the new one is shown.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * // Add and remove item based on the disabled state
52
+ * class Container extends Component {
53
+ * private this.depthManager = new DepthManager<>(this);
54
+ *
55
+ * constructor() {
56
+ * super();
57
+ * this.addEventListener('modified', this.handleModifiedEvent);
58
+ * }
59
+ *
60
+ * openOverlay() {
61
+ * this.depthManager.pushHost()
62
+ * }
63
+ *
64
+ * closeOverlay() {
65
+ * this.depthManager.popHost()
66
+ * }
67
+ *
68
+ * onComponentStackChanged (change: StackChange) {
69
+ * switch (change) {
70
+ * case 'added':
71
+ * return;
72
+ * case 'removed':
73
+ * return this.closeOverlay()
74
+ * case 'moved':
75
+ * return this.requestUpdate('zIndex');
76
+ * }
77
+ * }
78
+ * }
79
+ * ```
80
+ */
81
+ export class DepthManager {
82
+ /**
83
+ * Creates an instance of DepthManager.
84
+ */
85
+ constructor(host) {
86
+ this.host = host;
87
+ host.addController(this);
88
+ }
89
+ hostConnected() { }
90
+ hostDisconnected() {
91
+ // Remove this instance from the global stack on disconnect
92
+ this.remove([this.host]);
93
+ }
94
+ /**
95
+ * Gets the total number of elements in the stack
96
+ */
97
+ get length() {
98
+ return elementStack.length;
99
+ }
100
+ /**
101
+ * Adds host element to the stack
102
+ *
103
+ * @returns push was successful (true) or not (false)
104
+ */
105
+ pushHost() {
106
+ var _a, _b;
107
+ if (!this.has(this.host)) {
108
+ elementStack.push(this.host);
109
+ (_b = (_a = this.host).onComponentStackChanged) === null || _b === void 0 ? void 0 : _b.call(_a, 'added');
110
+ return true;
111
+ }
112
+ return false;
113
+ }
114
+ /**
115
+ * Pops all the items above the host and then pops the host itself
116
+ *
117
+ * @returns The host if it was in the stack, undefined otherwise
118
+ */
119
+ popHost() {
120
+ return this.popItem(this.host);
121
+ }
122
+ /**
123
+ * Pops all the items above the specified item and then pops the item itself
124
+ *
125
+ * @param item - The item to pop
126
+ * @returns The item if it was in the stack, undefined otherwise
127
+ */
128
+ popItem(item) {
129
+ if (this.has(item)) {
130
+ const untilItemIdx = elementStack.indexOf(item) - 1;
131
+ this.popUntil((it, idx) => {
132
+ // This can handle the case when multiple overlays share the same trigger (id), e.g.: tooltip, etc.
133
+ if (it !== item && it.triggerID === item.triggerID)
134
+ return 'skip';
135
+ return idx !== untilItemIdx;
136
+ });
137
+ }
138
+ return undefined;
139
+ }
140
+ /**
141
+ * Removes the last element from the stack
142
+ *
143
+ * @returns The last element in the stack
144
+ */
145
+ pop() {
146
+ var _a;
147
+ const popped = elementStack.pop();
148
+ (_a = popped === null || popped === void 0 ? void 0 : popped.onComponentStackChanged) === null || _a === void 0 ? void 0 : _a.call(popped, 'removed');
149
+ return popped;
150
+ }
151
+ /**
152
+ * Removes elements from the stack until the predicate function returns false
153
+ *
154
+ * Note: it will remove the
155
+ *
156
+ * @param predicateFn - The predicate function to test each element
157
+ * @returns The removed elements
158
+ */
159
+ popUntil(predicateFn) {
160
+ const poppedElements = [];
161
+ for (let i = elementStack.length - 1; i >= 0; i -= 1) {
162
+ const item = elementStack[i];
163
+ const result = predicateFn(item, i);
164
+ if (result === false)
165
+ break;
166
+ if (result !== 'skip')
167
+ poppedElements.push(item);
168
+ }
169
+ return this.remove(poppedElements);
170
+ }
171
+ /**
172
+ * Returns the last element in the stack
173
+ * without removing it
174
+ *
175
+ * @returns The last element in the stack
176
+ */
177
+ peek() {
178
+ return elementStack.at(-1);
179
+ }
180
+ /**
181
+ * Removes one or more elements from the stack without popping others.
182
+ *
183
+ * It notifies the elements on the stack which were removed and those which changed position.
184
+ * Items removed in bach, notify moved items only once.
185
+ *
186
+ * @param elements - Popover instance
187
+ * @returns undefined when the element was not found, the removed element otherwise
188
+ */
189
+ remove(elements) {
190
+ var _a, _b;
191
+ const removedElements = elements.filter(el => elementStack.includes(el));
192
+ const updateStackFrom = removedElements.reduce((idx, el) => Math.min(idx, elementStack.indexOf(el)), Infinity);
193
+ // Remove elements from the stack
194
+ removedElements.forEach(el => {
195
+ var _a;
196
+ elementStack.splice(elementStack.indexOf(el), 1);
197
+ (_a = el === null || el === void 0 ? void 0 : el.onComponentStackChanged) === null || _a === void 0 ? void 0 : _a.call(el, 'removed');
198
+ });
199
+ // Notify elements about the move
200
+ for (let i = updateStackFrom; i < elementStack.length; i += 1) {
201
+ (_b = (_a = elementStack[i]).onComponentStackChanged) === null || _b === void 0 ? void 0 : _b.call(_a, 'moved');
202
+ }
203
+ return removedElements;
204
+ }
205
+ /**
206
+ * Checks if the stack has a specific element
207
+ *
208
+ * @param element - Popover instance
209
+ * @returns True if the stack has the element, false otherwise
210
+ */
211
+ has(element) {
212
+ return elementStack.includes(element);
213
+ }
214
+ /**
215
+ * Gets the depth of the host element in the stack
216
+ */
217
+ getHostDepth() {
218
+ return this.getElementDepth(this.host);
219
+ }
220
+ /**
221
+ * Gets the depth of the element in the stack
222
+ * @param element - The element to get the depth of
223
+ */
224
+ getElementDepth(element) {
225
+ return elementStack.indexOf(element);
226
+ }
227
+ /**
228
+ * Gets the z-index of the host element in the stack
229
+ */
230
+ getHostZIndex() {
231
+ return this.getItemZIndex(this.host);
232
+ }
233
+ /**
234
+ * Gets the z-index of the element in the stack
235
+ * @param element - The element to get the z-index of
236
+ *
237
+ * @returns The z-index of the element if found, otherwise returns -1
238
+ */
239
+ getItemZIndex(element) {
240
+ const depth = this.getElementDepth(element);
241
+ return depth >= 0 ? BASE_Z_INDEX + depth * NUMBER_OF_Z_INDEX_LEVELS_PER_ELEMENT : -1;
242
+ }
243
+ /**
244
+ * Checks if host is at the top of the stack
245
+ *
246
+ * @returns True if host is on top, false otherwise
247
+ */
248
+ isHostOnTop() {
249
+ return this.peek() === this.host;
250
+ }
251
+ /**
252
+ * Clears the stack
253
+ *
254
+ * Pops all elements from the stack one-by-one.
255
+ */
256
+ clear() {
257
+ this.popUntil(() => true);
258
+ }
259
+ }
@@ -1,3 +1,4 @@
1
+ import { OVERLAY_BACKDROP_Z_INDEX_OFFSET, OVERLAY_TRIGGER_Z_INDEX_OFFSET } from '../controllers/DepthManager';
1
2
  export const BackdropMixin = (superClass) => {
2
3
  class Backdrop extends superClass {
3
4
  constructor() {
@@ -11,6 +12,20 @@ export const BackdropMixin = (superClass) => {
11
12
  this.isBackdropInvisible = false;
12
13
  /** @internal */
13
14
  this.backdropElement = null;
15
+ /** @internal */
16
+ this.triggerElementCache = null;
17
+ }
18
+ update(changedProperties) {
19
+ var _a;
20
+ super.update(changedProperties);
21
+ if (changedProperties.has('zIndex') && this.backdropElement) {
22
+ // Update the backdrop z-index if the zIndex property changes
23
+ this.backdropElement.style.zIndex = `${this.zIndex + OVERLAY_BACKDROP_Z_INDEX_OFFSET}`;
24
+ const triggerEl = (_a = this.triggerElementCache) === null || _a === void 0 ? void 0 : _a.deref();
25
+ if (triggerEl) {
26
+ triggerEl.style.zIndex = `${this.zIndex + OVERLAY_TRIGGER_Z_INDEX_OFFSET}`;
27
+ }
28
+ }
14
29
  }
15
30
  /**
16
31
  * Creates a backdrop element with the specified class name prefix.
@@ -30,7 +45,7 @@ export const BackdropMixin = (superClass) => {
30
45
  width: 100%;
31
46
  height: 100%;
32
47
  background: ${this.isBackdropInvisible ? `transparent` : `var(--mds-color-theme-common-overlays-secondary-normal)`};
33
- z-index: ${this.zIndex - 2};
48
+ z-index: ${this.zIndex + OVERLAY_BACKDROP_Z_INDEX_OFFSET};
34
49
  }
35
50
  `;
36
51
  backdrop.appendChild(styleElement);
@@ -63,13 +78,14 @@ export const BackdropMixin = (superClass) => {
63
78
  if (!element) {
64
79
  return;
65
80
  }
81
+ this.triggerElementCache = new WeakRef(element);
66
82
  // Store the original z-index and position of the element
67
83
  this.elementOriginalStyle = {
68
84
  zIndex: element.style.zIndex,
69
85
  position: element.style.position,
70
86
  };
71
87
  // Set the z-index and position to ensure the element is above the backdrop
72
- element.style.zIndex = `${this.zIndex - 1}`;
88
+ element.style.zIndex = `${this.zIndex + OVERLAY_TRIGGER_Z_INDEX_OFFSET}`;
73
89
  // Only set the position to relative if it is not already set to fixed or absolute
74
90
  if (!['fixed', 'absolute'].includes(window.getComputedStyle(element).position)) {
75
91
  element.style.position = 'relative';
@@ -93,6 +109,7 @@ export const BackdropMixin = (superClass) => {
93
109
  element.style.position = this.elementOriginalStyle.position;
94
110
  // Clear the stored original style
95
111
  this.elementOriginalStyle = undefined;
112
+ this.triggerElementCache = null;
96
113
  }
97
114
  }
98
115
  return Backdrop;
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@momentum-design/components",
3
3
  "packageManager": "yarn@3.2.4",
4
- "version": "0.129.46",
4
+ "version": "0.129.47",
5
5
  "engines": {
6
6
  "node": ">=20.0.0",
7
7
  "npm": ">=8.0.0"
@@ -1,53 +0,0 @@
1
- import type Popover from './popover.component';
2
- /**
3
- * Manages a stack of popovers to control their order and lifecycle.
4
- * This class allows adding, removing, and retrieving popovers
5
- * while maintaining their stacking behavior.
6
- *
7
- */
8
- declare class PopoverStack {
9
- /**
10
- * Stack to maintain the order of popovers
11
- * @internal
12
- */
13
- private stack;
14
- /**
15
- * Adds a popover to the stack
16
- *
17
- * @param popover - Popover instance
18
- * @returns The new depth of the stack
19
- */
20
- push(popover: Popover): number;
21
- /**
22
- * Removes the last popover from the stack
23
- *
24
- * @returns The last popover in the stack
25
- */
26
- pop(): Popover | undefined;
27
- /**
28
- * Returns the last popover in the stack
29
- * without removing it
30
- *
31
- * @returns The last popover in the stack
32
- */
33
- peek(): Popover | undefined;
34
- /**
35
- * Removes a popover from the stack
36
- *
37
- * @param popover - Popover instance
38
- */
39
- remove(popover: Popover): void;
40
- /**
41
- * Checks if the stack has a specific popover
42
- *
43
- * @param popover - Popover instance
44
- * @returns True if the stack has the popover, false otherwise
45
- */
46
- has(popover: Popover): boolean;
47
- /**
48
- * Clears the stack
49
- */
50
- clear(): void;
51
- }
52
- export declare const popoverStack: PopoverStack;
53
- export {};
@@ -1,66 +0,0 @@
1
- /**
2
- * Manages a stack of popovers to control their order and lifecycle.
3
- * This class allows adding, removing, and retrieving popovers
4
- * while maintaining their stacking behavior.
5
- *
6
- */
7
- class PopoverStack {
8
- constructor() {
9
- /**
10
- * Stack to maintain the order of popovers
11
- * @internal
12
- */
13
- this.stack = [];
14
- }
15
- /**
16
- * Adds a popover to the stack
17
- *
18
- * @param popover - Popover instance
19
- * @returns The new depth of the stack
20
- */
21
- push(popover) {
22
- this.stack.push(popover);
23
- return this.stack.length;
24
- }
25
- /**
26
- * Removes the last popover from the stack
27
- *
28
- * @returns The last popover in the stack
29
- */
30
- pop() {
31
- return this.stack.pop();
32
- }
33
- /**
34
- * Returns the last popover in the stack
35
- * without removing it
36
- *
37
- * @returns The last popover in the stack
38
- */
39
- peek() {
40
- return this.stack[this.stack.length - 1];
41
- }
42
- /**
43
- * Removes a popover from the stack
44
- *
45
- * @param popover - Popover instance
46
- */
47
- remove(popover) {
48
- this.stack = this.stack.filter(item => item !== popover);
49
- }
50
- /**
51
- * Checks if the stack has a specific popover
52
- *
53
- * @param popover - Popover instance
54
- * @returns True if the stack has the popover, false otherwise
55
- */
56
- has(popover) {
57
- return this.stack.includes(popover);
58
- }
59
- /**
60
- * Clears the stack
61
- */
62
- clear() {
63
- this.stack = [];
64
- }
65
- }
66
- export const popoverStack = new PopoverStack();