@limetech/lime-elements 40.11.2 → 40.11.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [40.11.3](https://github.com/Lundalogik/lime-elements/compare/v40.11.2...v40.11.3) (2026-10-19)
2
+
3
+ ### Bug Fixes
4
+
5
+
6
+ * **util:** return focus to the trigger with the shared focus lookup ([985f596](https://github.com/Lundalogik/lime-elements/commit/985f596b818d91b07c8d33f4fd85c458916acf6f))
7
+
1
8
  ## [40.11.2](https://github.com/Lundalogik/lime-elements/compare/v40.11.1...v40.11.2) (2026-10-19)
2
9
 
3
10
  ### Bug Fixes
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var _assignValue = require('./_assignValue-DQh76m39.js');
4
+ var getFocusTarget = require('./get-focus-target-DgqZ25gx.js');
4
5
 
5
6
  /**
6
7
  * This base implementation of `_.zipObject` which assigns values using `assignFunc`.
@@ -44,36 +45,24 @@ function zipObject(props, values) {
44
45
  return baseZipObject(props || [], values || [], _assignValue.assignValue);
45
46
  }
46
47
 
47
- const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
48
48
  const isDisabled = (element) => {
49
49
  return (element.disabled === true ||
50
50
  element.hasAttribute('disabled') ||
51
51
  element.getAttribute('aria-disabled') === 'true');
52
52
  };
53
53
  /**
54
- * Focuses the first focusable element inside a trigger element.
55
- * Supports custom elements by searching both the element's shadow root
56
- * and its light DOM.
54
+ * Focuses a trigger element, or the first focusable element inside it if the
55
+ * trigger can not take focus itself. Supports custom elements by searching
56
+ * both the element's shadow root and its light DOM.
57
57
  *
58
58
  * @param trigger - The trigger element to focus.
59
- * @returns `true` if focus was moved, otherwise `false`.
59
+ * @returns `false` if there is no trigger, or it is disabled. Otherwise `true`.
60
60
  */
61
61
  const focusTriggerElement = (trigger) => {
62
- var _a;
63
62
  if (!trigger || isDisabled(trigger)) {
64
63
  return false;
65
64
  }
66
- const shadowFocusable = (_a = trigger.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(FOCUSABLE_SELECTOR);
67
- if (shadowFocusable) {
68
- shadowFocusable.focus();
69
- return true;
70
- }
71
- const lightDomFocusable = trigger.querySelector(FOCUSABLE_SELECTOR);
72
- if (lightDomFocusable) {
73
- lightDomFocusable.focus();
74
- return true;
75
- }
76
- trigger.focus();
65
+ getFocusTarget.findFocusTarget(trigger).focus();
77
66
  return true;
78
67
  };
79
68
 
@@ -0,0 +1,105 @@
1
+ 'use strict';
2
+
3
+ const CONTROLS = ['button', 'input:not([type="hidden"])', 'select', 'textarea'];
4
+ const FOCUSABLE = [
5
+ 'a[href]',
6
+ ...CONTROLS.map((selector) => `${selector}:not([disabled])`),
7
+ 'summary',
8
+ '[contenteditable]:not([contenteditable="false"])',
9
+ '[tabindex]',
10
+ ]
11
+ .map((selector) => `${selector}:not([tabindex="-1"])`)
12
+ .join(', ');
13
+ const DISABLED = CONTROLS.map((selector) => `${selector}[disabled]:not([tabindex="-1"])`).join(', ');
14
+ /**
15
+ * Get the element that gets the focus when a user moves to `owner`. That is
16
+ * the owner itself if it can be focused, otherwise the first element that can
17
+ * be focused inside it, also inside shadow roots. A disabled control is only
18
+ * used when there is nothing that can be focused, and if there is no control
19
+ * at all, the owner itself is used.
20
+ *
21
+ * An element that passes the focus on to an element in its shadow root is
22
+ * never used itself, since the focus lands on the element inside.
23
+ *
24
+ * Waits for the components in the way to render, so use this for an owner
25
+ * that may not have rendered yet.
26
+ *
27
+ * @see findFocusTarget for an owner that has already rendered
28
+ * @param owner
29
+ */
30
+ async function getFocusTarget(owner) {
31
+ return runWalk(findTarget(owner));
32
+ }
33
+ async function runWalk(walk, step = walk.next()) {
34
+ if (step.done) {
35
+ return step.value;
36
+ }
37
+ await step.value.componentOnReady();
38
+ const found = await runWalk(walk, walk.next());
39
+ return found;
40
+ }
41
+ /**
42
+ * Same as `getFocusTarget`, for an owner that has already rendered, without
43
+ * waiting for any component.
44
+ *
45
+ * @see getFocusTarget for an owner that may not have rendered yet
46
+ * @param owner
47
+ */
48
+ function findFocusTarget(owner) {
49
+ const walk = findTarget(owner);
50
+ for (let step = walk.next();; step = walk.next()) {
51
+ if (step.done) {
52
+ return step.value;
53
+ }
54
+ }
55
+ }
56
+ function* findTarget(owner) {
57
+ var _a;
58
+ const search = {};
59
+ const found = yield* find(owner, search);
60
+ return (_a = found !== null && found !== void 0 ? found : search.disabled) !== null && _a !== void 0 ? _a : owner;
61
+ }
62
+ function* find(element, search) {
63
+ if (isFocusable(element)) {
64
+ return element;
65
+ }
66
+ rememberDisabled(element, search);
67
+ return yield* findInside(element, search);
68
+ }
69
+ function* findInside(element, search) {
70
+ if (isStencilComponent(element)) {
71
+ yield element;
72
+ }
73
+ const inShadowRoot = element.shadowRoot
74
+ ? yield* findAmong(element.shadowRoot.children, search)
75
+ : undefined;
76
+ return inShadowRoot !== null && inShadowRoot !== void 0 ? inShadowRoot : (yield* findAmong(element.children, search));
77
+ }
78
+ function* findAmong(elements, search) {
79
+ for (const element of elements) {
80
+ if (element.localName === 'limel-tooltip') {
81
+ continue;
82
+ }
83
+ const found = yield* find(element, search);
84
+ if (found) {
85
+ return found;
86
+ }
87
+ }
88
+ return undefined;
89
+ }
90
+ function isFocusable(element) {
91
+ var _a;
92
+ return !((_a = element.shadowRoot) === null || _a === void 0 ? void 0 : _a.delegatesFocus) && element.matches(FOCUSABLE);
93
+ }
94
+ function rememberDisabled(element, search) {
95
+ if (!search.disabled && element.matches(DISABLED)) {
96
+ search.disabled = element;
97
+ }
98
+ }
99
+ function isStencilComponent(element) {
100
+ return ('componentOnReady' in element &&
101
+ typeof element.componentOnReady === 'function');
102
+ }
103
+
104
+ exports.findFocusTarget = findFocusTarget;
105
+ exports.getFocusTarget = getFocusTarget;
@@ -9,7 +9,7 @@ var linkHelper = require('./link-helper-DwlfFJVL.js');
9
9
  var config = require('./config-dit--4m5.js');
10
10
  var debounce = require('./debounce-DgDnRSlt.js');
11
11
  var component = require('./component-Cz24PdGy.js');
12
- var focusTriggerElement = require('./focus-trigger-element-BfnJmxKO.js');
12
+ var focusTriggerElement = require('./focus-trigger-element-Sr7Yjtmk.js');
13
13
  var hotkeys = require('./hotkeys-D761KWnZ.js');
14
14
  var isFunction = require('./isFunction-RSHwsz1x.js');
15
15
  var ponyfill = require('./ponyfill-CHp7Zocr.js');
@@ -25,6 +25,7 @@ require('./_assignValue-DQh76m39.js');
25
25
  require('./_defineProperty-Csu6P9HT.js');
26
26
  require('./_getNative-Cf52giNE.js');
27
27
  require('./eq-DFcUoVzv.js');
28
+ require('./get-focus-target-DgqZ25gx.js');
28
29
 
29
30
  const breadcrumbsCss = () => `@charset "UTF-8";:host(limel-breadcrumbs){--limel-breadcrumbs-item-height:1.5rem;--limel-breadcrumbs-gap:0.75rem;--limel-breadcrumbs-gap:0.75rem;--limel-breadcrumbs-item-text-color:var( --breadcrumbs-item-text-color, rgb(var(--contrast-1500)) );--limel-overflow-mask-horizontal:linear-gradient( to right, transparent 0%, black calc(0% + var(--limel-left-edge-fade-width, 1rem)), black calc(100% - var(--limel-right-edge-fade-width, 1rem)), transparent 100% );-webkit-mask-image:var(--limel-overflow-mask-horizontal);mask-image:var(--limel-overflow-mask-horizontal);padding-left:var(--limel-left-edge-fade-width, 1rem);padding-right:var(--limel-right-edge-fade-width, 1rem);--limel-left-edge-fade-width:0.5rem;--limel-right-edge-fade-width:0.5rem}ol,li,.step{all:unset}*{box-sizing:border-box}ol{padding:0.5rem;gap:var(--limel-breadcrumbs-gap)}.step{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:0.25rem;max-width:var(--breadcrumbs-item-max-width, 10rem);height:var(--limel-breadcrumbs-item-height);color:var(--limel-breadcrumbs-item-text-color);border-radius:100vw;font-size:var(--limel-theme-default-font-size);padding:0 0.25rem}.step:not(:has(.text)){padding:0 0.125rem}.step:not(.last):focus{outline:none}.step:not(.last):focus-visible{outline:none;box-shadow:var(--shadow-depth-8-focused)}.step:not(.last):after{content:var(--limel-breadcrumbs-divider);display:flex;align-items:center;justify-content:center;width:var(--limel-breadcrumbs-gap);position:absolute;top:0;right:calc(var(--limel-breadcrumbs-gap) * -1);bottom:0;left:auto;text-align:center}a.step{position:relative;cursor:pointer;transition:color 0.2s ease;color:var(--limel-breadcrumbs-item-text-color)}a.step:before{transition:opacity 0.2s ease, transform 0.3s ease-out;content:"";position:absolute;inset:auto 0 0 0;width:calc(100% - 0.5rem);margin:auto;height:0.125rem;border-radius:1rem;background-color:currentColor;opacity:0;transform:scale(0.6)}a.step:hover{color:rgb(var(--color-blue-default))}a.step:hover:before{opacity:0.3;transform:scale(1)}button.step:not(.last){transition:color var(--limel-clickable-transition-speed, 0.4s) ease, background-color var(--limel-clickable-transition-speed, 0.4s) ease, box-shadow var(--limel-clickable-transform-speed, 0.4s) ease, transform var(--limel-clickable-transform-speed, 0.4s) var(--limel-clickable-transform-timing-function, ease);cursor:pointer;color:var(--limel-breadcrumbs-item-text-color);background-color:transparent}button.step:not(.last):hover,button.step:not(.last):focus,button.step:not(.last):focus-visible{will-change:color, background-color, box-shadow, transform}button.step:not(.last):hover,button.step:not(.last):focus-visible{transform:translate3d(0, 0.01rem, 0);color:var(--limel-theme-on-surface-color);background-color:var(--lime-elevated-surface-background-color)}button.step:not(.last):hover{box-shadow:var(--button-shadow-hovered)}button.step:not(.last):active{--limel-clickable-transform-timing-function:cubic-bezier( 0.83, -0.15, 0.49, 1.16 );transform:translate3d(0, 0.05rem, 0);box-shadow:var(--button-shadow-pressed)}button.step:not(.last):hover,button.step:not(.last):active{--limel-clickable-transition-speed:0.2s;--limel-clickable-transform-speed:0.16s}limel-icon{flex-shrink:0;width:calc(var(--limel-breadcrumbs-item-height) - 0.25rem);height:calc(var(--limel-breadcrumbs-item-height) - 0.25rem)}.text{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding:0 0.25rem}.last{opacity:0.7}:host(limel-breadcrumbs){box-sizing:border-box;display:flex;width:100%;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}:host(limel-breadcrumbs)::-webkit-scrollbar{display:none}:host(limel-breadcrumbs){direction:rtl}ol{display:flex;flex-direction:row-reverse;justify-content:flex-end;margin-right:auto}.step{direction:ltr}`;
30
31
 
@@ -4,6 +4,7 @@ var index = require('./index-BlsqxTB1.js');
4
4
  var hotkeys = require('./hotkeys-D761KWnZ.js');
5
5
  var device = require('./device-C9O7lYI9.js');
6
6
  var randomString = require('./random-string-CFCtFh5l.js');
7
+ var getFocusTarget = require('./get-focus-target-DgqZ25gx.js');
7
8
 
8
9
  // Note: this function handles the same key aliases as `KEY_ALIASES` in
9
10
  // `../../util/hotkeys.ts`, but maps them to display strings rather than
@@ -2344,76 +2345,6 @@ function normalize(text) {
2344
2345
  return text.replaceAll(/\s+/g, ' ').trim().toLowerCase();
2345
2346
  }
2346
2347
 
2347
- const CONTROLS = ['button', 'input:not([type="hidden"])', 'select', 'textarea'];
2348
- const FOCUSABLE = [
2349
- 'a[href]',
2350
- ...CONTROLS.map((selector) => `${selector}:not([disabled])`),
2351
- 'summary',
2352
- '[contenteditable]:not([contenteditable="false"])',
2353
- '[tabindex]',
2354
- ]
2355
- .map((selector) => `${selector}:not([tabindex="-1"])`)
2356
- .join(', ');
2357
- const DISABLED = CONTROLS.map((selector) => `${selector}[disabled]:not([tabindex="-1"])`).join(', ');
2358
- /**
2359
- * Get the element that a screen reader describes for the owner element of a
2360
- * tooltip. That is the owner itself if it can be focused, otherwise the first
2361
- * element that can be focused inside it, also inside shadow roots. A disabled
2362
- * control is only used when there is nothing that can be focused, and if
2363
- * there is no control at all, the owner itself is used.
2364
- *
2365
- * An element that passes the focus on to an element in its shadow root is
2366
- * never used itself, since the screen reader lands on the element inside.
2367
- *
2368
- * @param owner
2369
- */
2370
- async function getFocusTarget(owner) {
2371
- var _a, _b;
2372
- const search = {};
2373
- return (_b = (_a = (await find(owner, search))) !== null && _a !== void 0 ? _a : search.disabled) !== null && _b !== void 0 ? _b : owner;
2374
- }
2375
- async function find(element, search) {
2376
- if (isFocusable(element)) {
2377
- return element;
2378
- }
2379
- rememberDisabled(element, search);
2380
- return findInside(element, search);
2381
- }
2382
- async function findInside(element, search) {
2383
- if (isStencilComponent(element)) {
2384
- await element.componentOnReady();
2385
- }
2386
- const inShadowRoot = element.shadowRoot
2387
- ? await findAmong(element.shadowRoot.children, search)
2388
- : undefined;
2389
- return inShadowRoot !== null && inShadowRoot !== void 0 ? inShadowRoot : findAmong(element.children, search);
2390
- }
2391
- async function findAmong(elements, search) {
2392
- for (const element of elements) {
2393
- if (element.localName === 'limel-tooltip') {
2394
- continue;
2395
- }
2396
- const found = await find(element, search);
2397
- if (found) {
2398
- return found;
2399
- }
2400
- }
2401
- return undefined;
2402
- }
2403
- function isFocusable(element) {
2404
- var _a;
2405
- return !((_a = element.shadowRoot) === null || _a === void 0 ? void 0 : _a.delegatesFocus) && element.matches(FOCUSABLE);
2406
- }
2407
- function rememberDisabled(element, search) {
2408
- if (!search.disabled && element.matches(DISABLED)) {
2409
- search.disabled = element;
2410
- }
2411
- }
2412
- function isStencilComponent(element) {
2413
- return ('componentOnReady' in element &&
2414
- typeof element.componentOnReady === 'function');
2415
- }
2416
-
2417
2348
  /**
2418
2349
  *
2419
2350
  * @param id
@@ -2499,7 +2430,7 @@ const Tooltip = class {
2499
2430
  return;
2500
2431
  }
2501
2432
  const update = ++this.latestUpdate;
2502
- const element = await getFocusTarget(this.ownerElement);
2433
+ const element = await getFocusTarget.getFocusTarget(this.ownerElement);
2503
2434
  if (!this.host.isConnected || update !== this.latestUpdate) {
2504
2435
  return;
2505
2436
  }
@@ -3,13 +3,14 @@
3
3
  var index = require('./index-BlsqxTB1.js');
4
4
  var randomString = require('./random-string-CFCtFh5l.js');
5
5
  var keycodes = require('./keycodes-BQ_AERli.js');
6
- var focusTriggerElement = require('./focus-trigger-element-BfnJmxKO.js');
6
+ var focusTriggerElement = require('./focus-trigger-element-Sr7Yjtmk.js');
7
7
  require('./_assignValue-DQh76m39.js');
8
8
  require('./_defineProperty-Csu6P9HT.js');
9
9
  require('./_getNative-Cf52giNE.js');
10
10
  require('./isFunction-RSHwsz1x.js');
11
11
  require('./isObject-B6Osvvqp.js');
12
12
  require('./eq-DFcUoVzv.js');
13
+ require('./get-focus-target-DgqZ25gx.js');
13
14
 
14
15
  /**
15
16
  * Check if an element is a descendant of another
@@ -1,32 +1,21 @@
1
- const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
1
+ import { findFocusTarget } from "./get-focus-target";
2
2
  const isDisabled = (element) => {
3
3
  return (element.disabled === true ||
4
4
  element.hasAttribute('disabled') ||
5
5
  element.getAttribute('aria-disabled') === 'true');
6
6
  };
7
7
  /**
8
- * Focuses the first focusable element inside a trigger element.
9
- * Supports custom elements by searching both the element's shadow root
10
- * and its light DOM.
8
+ * Focuses a trigger element, or the first focusable element inside it if the
9
+ * trigger can not take focus itself. Supports custom elements by searching
10
+ * both the element's shadow root and its light DOM.
11
11
  *
12
12
  * @param trigger - The trigger element to focus.
13
- * @returns `true` if focus was moved, otherwise `false`.
13
+ * @returns `false` if there is no trigger, or it is disabled. Otherwise `true`.
14
14
  */
15
15
  export const focusTriggerElement = (trigger) => {
16
- var _a;
17
16
  if (!trigger || isDisabled(trigger)) {
18
17
  return false;
19
18
  }
20
- const shadowFocusable = (_a = trigger.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(FOCUSABLE_SELECTOR);
21
- if (shadowFocusable) {
22
- shadowFocusable.focus();
23
- return true;
24
- }
25
- const lightDomFocusable = trigger.querySelector(FOCUSABLE_SELECTOR);
26
- if (lightDomFocusable) {
27
- lightDomFocusable.focus();
28
- return true;
29
- }
30
- trigger.focus();
19
+ findFocusTarget(trigger).focus();
31
20
  return true;
32
21
  };
@@ -10,44 +10,75 @@ const FOCUSABLE = [
10
10
  .join(', ');
11
11
  const DISABLED = CONTROLS.map((selector) => `${selector}[disabled]:not([tabindex="-1"])`).join(', ');
12
12
  /**
13
- * Get the element that a screen reader describes for the owner element of a
14
- * tooltip. That is the owner itself if it can be focused, otherwise the first
15
- * element that can be focused inside it, also inside shadow roots. A disabled
16
- * control is only used when there is nothing that can be focused, and if
17
- * there is no control at all, the owner itself is used.
13
+ * Get the element that gets the focus when a user moves to `owner`. That is
14
+ * the owner itself if it can be focused, otherwise the first element that can
15
+ * be focused inside it, also inside shadow roots. A disabled control is only
16
+ * used when there is nothing that can be focused, and if there is no control
17
+ * at all, the owner itself is used.
18
18
  *
19
19
  * An element that passes the focus on to an element in its shadow root is
20
- * never used itself, since the screen reader lands on the element inside.
20
+ * never used itself, since the focus lands on the element inside.
21
21
  *
22
+ * Waits for the components in the way to render, so use this for an owner
23
+ * that may not have rendered yet.
24
+ *
25
+ * @see findFocusTarget for an owner that has already rendered
22
26
  * @param owner
23
27
  */
24
28
  export async function getFocusTarget(owner) {
25
- var _a, _b;
29
+ return runWalk(findTarget(owner));
30
+ }
31
+ async function runWalk(walk, step = walk.next()) {
32
+ if (step.done) {
33
+ return step.value;
34
+ }
35
+ await step.value.componentOnReady();
36
+ const found = await runWalk(walk, walk.next());
37
+ return found;
38
+ }
39
+ /**
40
+ * Same as `getFocusTarget`, for an owner that has already rendered, without
41
+ * waiting for any component.
42
+ *
43
+ * @see getFocusTarget for an owner that may not have rendered yet
44
+ * @param owner
45
+ */
46
+ export function findFocusTarget(owner) {
47
+ const walk = findTarget(owner);
48
+ for (let step = walk.next();; step = walk.next()) {
49
+ if (step.done) {
50
+ return step.value;
51
+ }
52
+ }
53
+ }
54
+ function* findTarget(owner) {
55
+ var _a;
26
56
  const search = {};
27
- return (_b = (_a = (await find(owner, search))) !== null && _a !== void 0 ? _a : search.disabled) !== null && _b !== void 0 ? _b : owner;
57
+ const found = yield* find(owner, search);
58
+ return (_a = found !== null && found !== void 0 ? found : search.disabled) !== null && _a !== void 0 ? _a : owner;
28
59
  }
29
- async function find(element, search) {
60
+ function* find(element, search) {
30
61
  if (isFocusable(element)) {
31
62
  return element;
32
63
  }
33
64
  rememberDisabled(element, search);
34
- return findInside(element, search);
65
+ return yield* findInside(element, search);
35
66
  }
36
- async function findInside(element, search) {
67
+ function* findInside(element, search) {
37
68
  if (isStencilComponent(element)) {
38
- await element.componentOnReady();
69
+ yield element;
39
70
  }
40
71
  const inShadowRoot = element.shadowRoot
41
- ? await findAmong(element.shadowRoot.children, search)
72
+ ? yield* findAmong(element.shadowRoot.children, search)
42
73
  : undefined;
43
- return inShadowRoot !== null && inShadowRoot !== void 0 ? inShadowRoot : findAmong(element.children, search);
74
+ return inShadowRoot !== null && inShadowRoot !== void 0 ? inShadowRoot : (yield* findAmong(element.children, search));
44
75
  }
45
- async function findAmong(elements, search) {
76
+ function* findAmong(elements, search) {
46
77
  for (const element of elements) {
47
78
  if (element.localName === 'limel-tooltip') {
48
79
  continue;
49
80
  }
50
- const found = await find(element, search);
81
+ const found = yield* find(element, search);
51
82
  if (found) {
52
83
  return found;
53
84
  }
@@ -1,4 +1,5 @@
1
1
  import { a as assignValue } from './_assignValue-CEFvyZxO.js';
2
+ import { f as findFocusTarget } from './get-focus-target-BhoYaGlg.js';
2
3
 
3
4
  /**
4
5
  * This base implementation of `_.zipObject` which assigns values using `assignFunc`.
@@ -42,36 +43,24 @@ function zipObject(props, values) {
42
43
  return baseZipObject(props || [], values || [], assignValue);
43
44
  }
44
45
 
45
- const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
46
46
  const isDisabled = (element) => {
47
47
  return (element.disabled === true ||
48
48
  element.hasAttribute('disabled') ||
49
49
  element.getAttribute('aria-disabled') === 'true');
50
50
  };
51
51
  /**
52
- * Focuses the first focusable element inside a trigger element.
53
- * Supports custom elements by searching both the element's shadow root
54
- * and its light DOM.
52
+ * Focuses a trigger element, or the first focusable element inside it if the
53
+ * trigger can not take focus itself. Supports custom elements by searching
54
+ * both the element's shadow root and its light DOM.
55
55
  *
56
56
  * @param trigger - The trigger element to focus.
57
- * @returns `true` if focus was moved, otherwise `false`.
57
+ * @returns `false` if there is no trigger, or it is disabled. Otherwise `true`.
58
58
  */
59
59
  const focusTriggerElement = (trigger) => {
60
- var _a;
61
60
  if (!trigger || isDisabled(trigger)) {
62
61
  return false;
63
62
  }
64
- const shadowFocusable = (_a = trigger.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(FOCUSABLE_SELECTOR);
65
- if (shadowFocusable) {
66
- shadowFocusable.focus();
67
- return true;
68
- }
69
- const lightDomFocusable = trigger.querySelector(FOCUSABLE_SELECTOR);
70
- if (lightDomFocusable) {
71
- lightDomFocusable.focus();
72
- return true;
73
- }
74
- trigger.focus();
63
+ findFocusTarget(trigger).focus();
75
64
  return true;
76
65
  };
77
66
 
@@ -0,0 +1,102 @@
1
+ const CONTROLS = ['button', 'input:not([type="hidden"])', 'select', 'textarea'];
2
+ const FOCUSABLE = [
3
+ 'a[href]',
4
+ ...CONTROLS.map((selector) => `${selector}:not([disabled])`),
5
+ 'summary',
6
+ '[contenteditable]:not([contenteditable="false"])',
7
+ '[tabindex]',
8
+ ]
9
+ .map((selector) => `${selector}:not([tabindex="-1"])`)
10
+ .join(', ');
11
+ const DISABLED = CONTROLS.map((selector) => `${selector}[disabled]:not([tabindex="-1"])`).join(', ');
12
+ /**
13
+ * Get the element that gets the focus when a user moves to `owner`. That is
14
+ * the owner itself if it can be focused, otherwise the first element that can
15
+ * be focused inside it, also inside shadow roots. A disabled control is only
16
+ * used when there is nothing that can be focused, and if there is no control
17
+ * at all, the owner itself is used.
18
+ *
19
+ * An element that passes the focus on to an element in its shadow root is
20
+ * never used itself, since the focus lands on the element inside.
21
+ *
22
+ * Waits for the components in the way to render, so use this for an owner
23
+ * that may not have rendered yet.
24
+ *
25
+ * @see findFocusTarget for an owner that has already rendered
26
+ * @param owner
27
+ */
28
+ async function getFocusTarget(owner) {
29
+ return runWalk(findTarget(owner));
30
+ }
31
+ async function runWalk(walk, step = walk.next()) {
32
+ if (step.done) {
33
+ return step.value;
34
+ }
35
+ await step.value.componentOnReady();
36
+ const found = await runWalk(walk, walk.next());
37
+ return found;
38
+ }
39
+ /**
40
+ * Same as `getFocusTarget`, for an owner that has already rendered, without
41
+ * waiting for any component.
42
+ *
43
+ * @see getFocusTarget for an owner that may not have rendered yet
44
+ * @param owner
45
+ */
46
+ function findFocusTarget(owner) {
47
+ const walk = findTarget(owner);
48
+ for (let step = walk.next();; step = walk.next()) {
49
+ if (step.done) {
50
+ return step.value;
51
+ }
52
+ }
53
+ }
54
+ function* findTarget(owner) {
55
+ var _a;
56
+ const search = {};
57
+ const found = yield* find(owner, search);
58
+ return (_a = found !== null && found !== void 0 ? found : search.disabled) !== null && _a !== void 0 ? _a : owner;
59
+ }
60
+ function* find(element, search) {
61
+ if (isFocusable(element)) {
62
+ return element;
63
+ }
64
+ rememberDisabled(element, search);
65
+ return yield* findInside(element, search);
66
+ }
67
+ function* findInside(element, search) {
68
+ if (isStencilComponent(element)) {
69
+ yield element;
70
+ }
71
+ const inShadowRoot = element.shadowRoot
72
+ ? yield* findAmong(element.shadowRoot.children, search)
73
+ : undefined;
74
+ return inShadowRoot !== null && inShadowRoot !== void 0 ? inShadowRoot : (yield* findAmong(element.children, search));
75
+ }
76
+ function* findAmong(elements, search) {
77
+ for (const element of elements) {
78
+ if (element.localName === 'limel-tooltip') {
79
+ continue;
80
+ }
81
+ const found = yield* find(element, search);
82
+ if (found) {
83
+ return found;
84
+ }
85
+ }
86
+ return undefined;
87
+ }
88
+ function isFocusable(element) {
89
+ var _a;
90
+ return !((_a = element.shadowRoot) === null || _a === void 0 ? void 0 : _a.delegatesFocus) && element.matches(FOCUSABLE);
91
+ }
92
+ function rememberDisabled(element, search) {
93
+ if (!search.disabled && element.matches(DISABLED)) {
94
+ search.disabled = element;
95
+ }
96
+ }
97
+ function isStencilComponent(element) {
98
+ return ('componentOnReady' in element &&
99
+ typeof element.componentOnReady === 'function');
100
+ }
101
+
102
+ export { findFocusTarget as f, getFocusTarget as g };
@@ -7,7 +7,7 @@ import { b as getHref, a as getTarget, g as getRel } from './link-helper-5KsJICv
7
7
  import { g as globalConfig } from './config-Dnt5w_Bp.js';
8
8
  import { d as debounce } from './debounce-B67JMchz.js';
9
9
  import { M as MDCTextField } from './component-CVihTrav.js';
10
- import { f as focusTriggerElement, z as zipObject } from './focus-trigger-element-BOFcjLq3.js';
10
+ import { f as focusTriggerElement, z as zipObject } from './focus-trigger-element-CIEHI_yk.js';
11
11
  import { h as hotkeyFromKeyboardEvent, t as tokenizeHotkeyString, n as normalizeHotkeyString } from './hotkeys-DjmlD0Mw.js';
12
12
  import { i as isFunction } from './isFunction-9zQTeQI8.js';
13
13
  import { _ as __extends, a as __assign, b as __values, M as MDCFoundation, c as MDCComponent, d as closest } from './ponyfill-6YuZiAPi.js';
@@ -23,6 +23,7 @@ import './_assignValue-CEFvyZxO.js';
23
23
  import './_defineProperty-C69Btzid.js';
24
24
  import './_getNative-COawFdVX.js';
25
25
  import './eq-CZ30nDJE.js';
26
+ import './get-focus-target-BhoYaGlg.js';
26
27
 
27
28
  const breadcrumbsCss = () => `@charset "UTF-8";:host(limel-breadcrumbs){--limel-breadcrumbs-item-height:1.5rem;--limel-breadcrumbs-gap:0.75rem;--limel-breadcrumbs-gap:0.75rem;--limel-breadcrumbs-item-text-color:var( --breadcrumbs-item-text-color, rgb(var(--contrast-1500)) );--limel-overflow-mask-horizontal:linear-gradient( to right, transparent 0%, black calc(0% + var(--limel-left-edge-fade-width, 1rem)), black calc(100% - var(--limel-right-edge-fade-width, 1rem)), transparent 100% );-webkit-mask-image:var(--limel-overflow-mask-horizontal);mask-image:var(--limel-overflow-mask-horizontal);padding-left:var(--limel-left-edge-fade-width, 1rem);padding-right:var(--limel-right-edge-fade-width, 1rem);--limel-left-edge-fade-width:0.5rem;--limel-right-edge-fade-width:0.5rem}ol,li,.step{all:unset}*{box-sizing:border-box}ol{padding:0.5rem;gap:var(--limel-breadcrumbs-gap)}.step{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:0.25rem;max-width:var(--breadcrumbs-item-max-width, 10rem);height:var(--limel-breadcrumbs-item-height);color:var(--limel-breadcrumbs-item-text-color);border-radius:100vw;font-size:var(--limel-theme-default-font-size);padding:0 0.25rem}.step:not(:has(.text)){padding:0 0.125rem}.step:not(.last):focus{outline:none}.step:not(.last):focus-visible{outline:none;box-shadow:var(--shadow-depth-8-focused)}.step:not(.last):after{content:var(--limel-breadcrumbs-divider);display:flex;align-items:center;justify-content:center;width:var(--limel-breadcrumbs-gap);position:absolute;top:0;right:calc(var(--limel-breadcrumbs-gap) * -1);bottom:0;left:auto;text-align:center}a.step{position:relative;cursor:pointer;transition:color 0.2s ease;color:var(--limel-breadcrumbs-item-text-color)}a.step:before{transition:opacity 0.2s ease, transform 0.3s ease-out;content:"";position:absolute;inset:auto 0 0 0;width:calc(100% - 0.5rem);margin:auto;height:0.125rem;border-radius:1rem;background-color:currentColor;opacity:0;transform:scale(0.6)}a.step:hover{color:rgb(var(--color-blue-default))}a.step:hover:before{opacity:0.3;transform:scale(1)}button.step:not(.last){transition:color var(--limel-clickable-transition-speed, 0.4s) ease, background-color var(--limel-clickable-transition-speed, 0.4s) ease, box-shadow var(--limel-clickable-transform-speed, 0.4s) ease, transform var(--limel-clickable-transform-speed, 0.4s) var(--limel-clickable-transform-timing-function, ease);cursor:pointer;color:var(--limel-breadcrumbs-item-text-color);background-color:transparent}button.step:not(.last):hover,button.step:not(.last):focus,button.step:not(.last):focus-visible{will-change:color, background-color, box-shadow, transform}button.step:not(.last):hover,button.step:not(.last):focus-visible{transform:translate3d(0, 0.01rem, 0);color:var(--limel-theme-on-surface-color);background-color:var(--lime-elevated-surface-background-color)}button.step:not(.last):hover{box-shadow:var(--button-shadow-hovered)}button.step:not(.last):active{--limel-clickable-transform-timing-function:cubic-bezier( 0.83, -0.15, 0.49, 1.16 );transform:translate3d(0, 0.05rem, 0);box-shadow:var(--button-shadow-pressed)}button.step:not(.last):hover,button.step:not(.last):active{--limel-clickable-transition-speed:0.2s;--limel-clickable-transform-speed:0.16s}limel-icon{flex-shrink:0;width:calc(var(--limel-breadcrumbs-item-height) - 0.25rem);height:calc(var(--limel-breadcrumbs-item-height) - 0.25rem)}.text{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding:0 0.25rem}.last{opacity:0.7}:host(limel-breadcrumbs){box-sizing:border-box;display:flex;width:100%;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}:host(limel-breadcrumbs)::-webkit-scrollbar{display:none}:host(limel-breadcrumbs){direction:rtl}ol{display:flex;flex-direction:row-reverse;justify-content:flex-end;margin-right:auto}.step{direction:ltr}`;
28
29