@momentum-design/components 0.133.24 → 0.133.26

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.
@@ -111,6 +111,13 @@ declare class Searchpopover extends Searchfield {
111
111
  * @internal
112
112
  */
113
113
  protected get scrollContainer(): HTMLElement | null;
114
+ /**
115
+ * Handles the popover hidden event by focusing the input element.
116
+ * This replaces `focus-back-to-trigger` since the trigger is a non-focusable div.
117
+ * Only moves focus to the input if focus is currently within the searchpopover component.
118
+ * If focus is outside the component, it is left unchanged.
119
+ */
120
+ private handlePopoverHidden;
114
121
  protected renderInputElement(): import("lit-html").TemplateResult<1>;
115
122
  render(): import("lit-html").TemplateResult<1>;
116
123
  static styles: Array<CSSResult>;
@@ -129,6 +129,18 @@ class Searchpopover extends Searchfield {
129
129
  var _a, _b;
130
130
  return (_b = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('[part="filters-container"]')) !== null && _b !== void 0 ? _b : null;
131
131
  }
132
+ /**
133
+ * Handles the popover hidden event by focusing the input element.
134
+ * This replaces `focus-back-to-trigger` since the trigger is a non-focusable div.
135
+ * Only moves focus to the input if focus is currently within the searchpopover component.
136
+ * If focus is outside the component, it is left unchanged.
137
+ */
138
+ handlePopoverHidden() {
139
+ var _a;
140
+ if (this === document.activeElement || this.contains(document.activeElement)) {
141
+ (_a = this.inputElement) === null || _a === void 0 ? void 0 : _a.focus();
142
+ }
143
+ }
132
144
  renderInputElement() {
133
145
  var _a, _b;
134
146
  const placeholderText = this.hasChips ? '' : this.placeholder;
@@ -188,8 +200,8 @@ class Searchpopover extends Searchfield {
188
200
  ?visible=${this.displayPopover}
189
201
  hide-on-outside-click
190
202
  hide-on-escape
191
- focus-back-to-trigger
192
203
  size
204
+ @hidden=${this.handlePopoverHidden}
193
205
  placement="${this.placement}"
194
206
  aria-label="${ifDefined(this.popoverAriaLabel)}"
195
207
  disable-aria-expanded
@@ -40425,6 +40425,12 @@
40425
40425
  "module": "components/input/input.component.js"
40426
40426
  }
40427
40427
  },
40428
+ {
40429
+ "kind": "method",
40430
+ "name": "handlePopoverHidden",
40431
+ "privacy": "private",
40432
+ "description": "Handles the popover hidden event by focusing the input element.\nThis replaces `focus-back-to-trigger` since the trigger is a non-focusable div.\nOnly moves focus to the input if focus is currently within the searchpopover component.\nIf focus is outside the component, it is left unchanged."
40433
+ },
40428
40434
  {
40429
40435
  "kind": "field",
40430
40436
  "name": "helpText",
@@ -7,6 +7,12 @@ type FindFocusableOptions = {
7
7
  excludedElements?: HTMLElement[];
8
8
  /** Selectors to include in the search. */
9
9
  includeSelectors?: string[];
10
+ /**
11
+ * When true, elements with `tabindex="-1"` and their subtrees are excluded from the search.
12
+ * This supports composite widget patterns (e.g., roving tabindex in lists) where
13
+ * inactive items and their children should not be reachable via Tab navigation.
14
+ */
15
+ stopAtNonTabbable?: boolean;
10
16
  };
11
17
  /**
12
18
  * nodeB precedes nodeA in either a pre-order depth-first traversal of a tree containing both
package/dist/utils/dom.js CHANGED
@@ -166,17 +166,21 @@ export const isFocusable = (element) => !isDisabled(element) && isTabbable(eleme
166
166
  * @returns The list of focusable elements.
167
167
  */
168
168
  export const findFocusable = (root, options = {}) => {
169
- var _a, _b;
169
+ var _a, _b, _c;
170
170
  if (!root) {
171
171
  return [];
172
172
  }
173
173
  const excludesSet = new Set((_a = options === null || options === void 0 ? void 0 : options.excludedElements) !== null && _a !== void 0 ? _a : []);
174
174
  const includeSelectors = (_b = options === null || options === void 0 ? void 0 : options.includeSelectors) !== null && _b !== void 0 ? _b : [];
175
+ const stopAtNonTabbable = (_c = options === null || options === void 0 ? void 0 : options.stopAtNonTabbable) !== null && _c !== void 0 ? _c : false;
175
176
  const matches = new Set();
176
177
  const focusableCheck = (element) => {
177
178
  if (!(element instanceof HTMLSlotElement) && (isHidden(element) || isDisabled(element))) {
178
179
  return 'stop';
179
180
  }
181
+ if (stopAtNonTabbable && !(element instanceof HTMLSlotElement) && element.getAttribute('tabindex') === '-1') {
182
+ return 'stop';
183
+ }
180
184
  return isMatchAny(element, includeSelectors) || (isTabbable(element) && isInteractiveElement(element))
181
185
  ? 'focusable'
182
186
  : 'continue';
@@ -211,6 +215,11 @@ export const findFocusable = (root, options = {}) => {
211
215
  const assignedNodes = element.assignedElements({ flatten: true });
212
216
  assignedNodes.forEach(node => {
213
217
  if (node instanceof HTMLElement) {
218
+ // When stopAtNonTabbable is enabled, skip non-tabbable slotted elements and their
219
+ // subtrees to support composite widget patterns (e.g., roving tabindex in lists)
220
+ if (stopAtNonTabbable && !(node instanceof HTMLSlotElement) && node.getAttribute('tabindex') === '-1') {
221
+ return;
222
+ }
214
223
  finder(node);
215
224
  }
216
225
  });
@@ -55,7 +55,7 @@ export const FocusTrapMixin = (superClass) => {
55
55
  setFocusableElements() {
56
56
  if (!this.shadowRoot)
57
57
  return;
58
- this.focusableElements = findFocusable(this.shadowRoot);
58
+ this.focusableElements = findFocusable(this.shadowRoot, { stopAtNonTabbable: true });
59
59
  }
60
60
  /**
61
61
  * Sets the initial focus within the container.
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.133.24",
4
+ "version": "0.133.26",
5
5
  "engines": {
6
6
  "node": ">=20.0.0",
7
7
  "npm": ">=8.0.0"