@nectary/components 5.37.2 → 5.37.5

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.
@@ -7,6 +7,7 @@ export * from './types';
7
7
  export declare class AccordionItem extends NectaryElement {
8
8
  #private;
9
9
  constructor();
10
+ connectedCallback(): void;
10
11
  disconnectedCallback(): void;
11
12
  static get observedAttributes(): string[];
12
13
  attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
@@ -16,10 +17,12 @@ export declare class AccordionItem extends NectaryElement {
16
17
  get label(): string;
17
18
  set disabled(isDisabled: boolean);
18
19
  get disabled(): boolean;
19
- get status(): TSinchAccordionStatusType | null;
20
20
  set status(value: TSinchAccordionStatusType | null);
21
+ get status(): TSinchAccordionStatusType | null;
21
22
  set optionalText(value: string | null);
22
23
  get optionalText(): string | null;
24
+ set ellipsis(isEllipsis: boolean);
25
+ get ellipsis(): boolean;
23
26
  get focusable(): boolean;
24
27
  focus(): void;
25
28
  blur(): void;
@@ -1,16 +1,17 @@
1
1
  import "../icon/index.js";
2
2
  import "../text/index.js";
3
3
  import "../title/index.js";
4
- import { isAttrEqual, updateBooleanAttribute, isAttrTrue, updateExplicitBooleanAttribute, updateAttribute, getAttribute, getBooleanAttribute, getLiteralAttribute, updateLiteralAttribute } from "../utils/dom.js";
4
+ import { updateBooleanAttribute, isAttrEqual, isAttrTrue, updateExplicitBooleanAttribute, updateAttribute, getAttribute, getBooleanAttribute, updateLiteralAttribute, getLiteralAttribute } from "../utils/dom.js";
5
5
  import { defineCustomElement, NectaryElement } from "../utils/element.js";
6
6
  import { statusValues } from "./utils.js";
7
- const templateHTML = '<style>:host{display:block;outline:0;min-height:48px}#wrapper{display:flex;flex-direction:column;position:relative;width:100%;height:100%;box-sizing:border-box;overflow:hidden;border-bottom:1px solid var(--sinch-comp-accordion-color-default-border-initial)}:host(:last-child)>#wrapper{border-bottom:none}#button{all:initial;display:flex;position:relative;align-items:flex-start;gap:8px;box-sizing:border-box;width:100%;min-height:48px;padding:12px 4px 12px 8px;cursor:pointer;--sinch-global-color-icon:var(--sinch-comp-accordion-color-default-icon-initial);--sinch-global-size-icon:var(--sinch-comp-accordion-size-icon)}#button>*{pointer-events:none}#button:disabled{cursor:initial;--sinch-global-color-icon:var(--sinch-comp-accordion-color-disabled-icon-initial)}#button:focus-visible::after{content:"";position:absolute;inset:0;border:2px solid var(--sinch-comp-accordion-color-default-outline-focus);pointer-events:none}#status-wrapper{display:none;width:18px;height:24px;padding:8px 8px 8px 2px;box-sizing:border-box}#status{width:8px;height:8px;border-radius:50%}:host([status]:not([status=""])) #status-wrapper{display:block}:host([status=success]) #status{background-color:var(--sinch-comp-accordion-color-default-status-success)}:host([status=warn]) #status{background-color:var(--sinch-comp-accordion-color-default-status-warning)}:host([status=error]) #status{background-color:var(--sinch-comp-accordion-color-default-status-error)}:host([status=info]) #status{background-color:var(--sinch-comp-accordion-color-default-status-info)}#title{flex:1;min-width:0;--sinch-comp-title-font:var(--sinch-comp-accordion-font-title);--sinch-global-color-text:var(--sinch-comp-accordion-color-default-title-initial)}#button:disabled>#title{--sinch-global-color-text:var(--sinch-comp-accordion-color-disabled-title-initial)}#content{display:none;overflow-y:auto;flex-shrink:1;min-height:0;padding:0 8px 12px}#dropdown-icon{flex-shrink:0;margin-top:2px;transform:rotate(0);will-change:transform;transition:transform .25s ease-in-out}#button[aria-expanded=true]>#dropdown-icon{transform:rotate(180deg)}#button[aria-expanded=true]+#content{display:block}#optional{flex-shrink:0;--sinch-comp-text-font:var(--sinch-comp-accordion-font-optional-text);--sinch-global-color-text:var(--sinch-comp-accordion-color-default-optional-text-initial)}#button:disabled>#optional{--sinch-global-color-text:var(--sinch-comp-accordion-color-disabled-optional-text-initial)}</style><div id="wrapper"><button id="button" aria-controls="content" aria-expanded="false"><div id="status-wrapper"><div id="status"></div></div><slot name="icon"></slot><sinch-title id="title" level="3" type="m" ellipsis></sinch-title><sinch-text id="optional" type="m"></sinch-text><sinch-icon icons-version="2" name="fa-chevron-down" id="dropdown-icon"></sinch-icon></button><div id="content" role="region" aria-labelledby="button"><slot name="content"></slot></div></div>';
7
+ const templateHTML = '<style>:host{display:block;outline:0;min-height:48px}#wrapper{display:flex;flex-direction:column;position:relative;width:100%;height:100%;box-sizing:border-box;overflow:hidden;border-bottom:1px solid var(--sinch-comp-accordion-color-default-border-initial)}:host(:last-child)>#wrapper{border-bottom:none}#button{all:initial;display:flex;position:relative;align-items:flex-start;gap:8px;box-sizing:border-box;width:100%;min-height:48px;padding:12px 4px 12px 8px;cursor:pointer;--sinch-global-color-icon:var(--sinch-comp-accordion-color-default-icon-initial);--sinch-global-size-icon:var(--sinch-comp-accordion-size-icon)}#button>*{pointer-events:none}#button:disabled{cursor:initial;--sinch-global-color-icon:var(--sinch-comp-accordion-color-disabled-icon-initial)}#button:focus-visible::after{content:"";position:absolute;inset:0;border:2px solid var(--sinch-comp-accordion-color-default-outline-focus);pointer-events:none}#status-wrapper{display:none;width:18px;height:24px;padding:8px 8px 8px 2px;box-sizing:border-box}#status{width:8px;height:8px;border-radius:50%}:host([status]:not([status=""])) #status-wrapper{display:block}:host([status=success]) #status{background-color:var(--sinch-comp-accordion-color-default-status-success)}:host([status=warn]) #status{background-color:var(--sinch-comp-accordion-color-default-status-warning)}:host([status=error]) #status{background-color:var(--sinch-comp-accordion-color-default-status-error)}:host([status=info]) #status{background-color:var(--sinch-comp-accordion-color-default-status-info)}#title{flex:1;min-width:0;--sinch-comp-title-font:var(--sinch-comp-accordion-font-title);--sinch-global-color-text:var(--sinch-comp-accordion-color-default-title-initial)}#button:disabled>#title{--sinch-global-color-text:var(--sinch-comp-accordion-color-disabled-title-initial)}#content{display:none;overflow-y:auto;flex-shrink:1;min-height:0;padding:0 8px 12px}#dropdown-icon{flex-shrink:0;margin-top:2px;transform:rotate(0);will-change:transform;transition:transform .25s ease-in-out}#button[aria-expanded=true]>#dropdown-icon{transform:rotate(180deg)}#button[aria-expanded=true]+#content{display:block}#optional{flex-shrink:0;--sinch-comp-text-font:var(--sinch-comp-accordion-font-optional-text);--sinch-global-color-text:var(--sinch-comp-accordion-color-default-optional-text-initial)}#button:disabled>#optional{--sinch-global-color-text:var(--sinch-comp-accordion-color-disabled-optional-text-initial)}</style><div id="wrapper"><button id="button" aria-controls="content" aria-expanded="false"><div id="status-wrapper"><div id="status"></div></div><slot name="icon"></slot><sinch-title id="title" level="3" type="m"></sinch-title><sinch-text id="optional" type="m"></sinch-text><sinch-icon icons-version="2" name="fa-chevron-down" id="dropdown-icon"></sinch-icon></button><div id="content" role="region" aria-labelledby="button"><slot name="content"></slot></div></div>';
8
8
  const template = document.createElement("template");
9
9
  template.innerHTML = templateHTML;
10
10
  class AccordionItem extends NectaryElement {
11
11
  #$button;
12
12
  #$title;
13
13
  #$optionalText;
14
+ #hasExplicitEllipsis = false;
14
15
  constructor() {
15
16
  super();
16
17
  const shadowRoot = this.attachShadow({ delegatesFocus: true });
@@ -19,7 +20,14 @@ class AccordionItem extends NectaryElement {
19
20
  this.#$title = shadowRoot.querySelector("#title");
20
21
  this.#$optionalText = shadowRoot.querySelector("#optional");
21
22
  }
23
+ connectedCallback() {
24
+ super.connectedCallback();
25
+ if (this.hasAttribute("ellipsis") === false && this.#hasExplicitEllipsis === false) {
26
+ updateBooleanAttribute(this, "ellipsis", true);
27
+ }
28
+ }
22
29
  disconnectedCallback() {
30
+ super.disconnectedCallback();
23
31
  }
24
32
  static get observedAttributes() {
25
33
  return [
@@ -27,7 +35,6 @@ class AccordionItem extends NectaryElement {
27
35
  "disabled",
28
36
  "data-checked",
29
37
  "optionaltext",
30
- // eslint-disable-next-line @nectary/observed-attribute-accessor -- baseline backlog: ellipsis missing set/get pair, fixed by MR !597
31
38
  "ellipsis"
32
39
  ];
33
40
  }
@@ -58,6 +65,7 @@ class AccordionItem extends NectaryElement {
58
65
  break;
59
66
  }
60
67
  case "ellipsis": {
68
+ this.#hasExplicitEllipsis = true;
61
69
  updateBooleanAttribute(this.#$title, name, isAttrTrue(newVal));
62
70
  break;
63
71
  }
@@ -81,18 +89,25 @@ class AccordionItem extends NectaryElement {
81
89
  get disabled() {
82
90
  return getBooleanAttribute(this, "disabled");
83
91
  }
84
- get status() {
85
- return getLiteralAttribute(this, statusValues, "status", null);
86
- }
87
92
  set status(value) {
88
93
  updateLiteralAttribute(this, statusValues, "status", value);
89
94
  }
95
+ get status() {
96
+ return getLiteralAttribute(this, statusValues, "status", null);
97
+ }
90
98
  set optionalText(value) {
91
99
  updateAttribute(this, "optionaltext", value);
92
100
  }
93
101
  get optionalText() {
94
102
  return getAttribute(this, "optionaltext");
95
103
  }
104
+ set ellipsis(isEllipsis) {
105
+ this.#hasExplicitEllipsis = true;
106
+ updateBooleanAttribute(this, "ellipsis", isEllipsis);
107
+ }
108
+ get ellipsis() {
109
+ return getBooleanAttribute(this, "ellipsis");
110
+ }
96
111
  get focusable() {
97
112
  return true;
98
113
  }
@@ -1,6 +1,6 @@
1
1
  import { isSinchActionMenuOption } from "../action-menu-option/utils.js";
2
2
  import { subscribeContext } from "../utils/context.js";
3
- import { attrValueToPixels, updateIntegerAttribute, getIntegerAttribute, getBooleanAttribute, updateBooleanAttribute } from "../utils/dom.js";
3
+ import { attrValueToPixels, updateIntegerAttribute, getIntegerAttribute, getDeepActiveElement, getBooleanAttribute, updateBooleanAttribute } from "../utils/dom.js";
4
4
  import { defineCustomElement, NectaryElement } from "../utils/element.js";
5
5
  const templateHTML = '<style>:host{display:block;outline:0}#listbox{overflow-y:auto}</style><div id="listbox" role="presentation"><slot></slot></div>';
6
6
  const ITEM_HEIGHT = 40;
@@ -69,7 +69,7 @@ class ActionMenu extends NectaryElement {
69
69
  this.#selectOption(null);
70
70
  this.#$listbox.scrollTo({ top: 0, behavior: "auto" });
71
71
  } else {
72
- const activeElement = this.#getDeepActiveElement();
72
+ const activeElement = getDeepActiveElement(this.ownerDocument);
73
73
  const isTextInput = activeElement !== null && activeElement.tagName === "INPUT";
74
74
  if (!isTextInput) {
75
75
  this.focus();
@@ -146,13 +146,6 @@ class ActionMenu extends NectaryElement {
146
146
  }
147
147
  return this.#getLastOption();
148
148
  }
149
- #getDeepActiveElement() {
150
- let activeElement = this.ownerDocument.activeElement;
151
- while (activeElement !== null && activeElement.shadowRoot !== null && activeElement.shadowRoot.activeElement !== null) {
152
- activeElement = activeElement.shadowRoot.activeElement;
153
- }
154
- return activeElement;
155
- }
156
149
  #selectOption($option) {
157
150
  const hasRows = this.hasAttribute("rows");
158
151
  for (const $op of this.#getOptionElements()) {
package/bundle.js CHANGED
@@ -124,6 +124,32 @@ const cloneNode = (el, deep) => {
124
124
  return el.cloneNode(deep);
125
125
  };
126
126
  const shouldReduceMotion = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
127
+ const getDeepActiveElement = (ownerDocument = document) => {
128
+ let activeElement = ownerDocument.activeElement;
129
+ while (activeElement !== null && activeElement.shadowRoot !== null && activeElement.shadowRoot.activeElement !== null) {
130
+ activeElement = activeElement.shadowRoot.activeElement;
131
+ }
132
+ return activeElement;
133
+ };
134
+ const composedContains = (container, node) => {
135
+ let current = node;
136
+ while (current !== null) {
137
+ if (current === container) {
138
+ return true;
139
+ }
140
+ if (current.parentNode !== null) {
141
+ current = current.parentNode;
142
+ continue;
143
+ }
144
+ const root = current.getRootNode();
145
+ if (root instanceof ShadowRoot) {
146
+ current = root.host;
147
+ continue;
148
+ }
149
+ current = null;
150
+ }
151
+ return false;
152
+ };
127
153
  const isAttrEqual = (oldVal, newVal) => {
128
154
  return oldVal === newVal || newVal === null && oldVal === "false" || newVal === "" && oldVal === "true";
129
155
  };
@@ -436,13 +462,14 @@ class Title extends NectaryElement {
436
462
  }
437
463
  defineCustomElement("sinch-title", Title);
438
464
  const statusValues$1 = ["info", "success", "warn", "error"];
439
- const templateHTML$1g = '<style>:host{display:block;outline:0;min-height:48px}#wrapper{display:flex;flex-direction:column;position:relative;width:100%;height:100%;box-sizing:border-box;overflow:hidden;border-bottom:1px solid var(--sinch-comp-accordion-color-default-border-initial)}:host(:last-child)>#wrapper{border-bottom:none}#button{all:initial;display:flex;position:relative;align-items:flex-start;gap:8px;box-sizing:border-box;width:100%;min-height:48px;padding:12px 4px 12px 8px;cursor:pointer;--sinch-global-color-icon:var(--sinch-comp-accordion-color-default-icon-initial);--sinch-global-size-icon:var(--sinch-comp-accordion-size-icon)}#button>*{pointer-events:none}#button:disabled{cursor:initial;--sinch-global-color-icon:var(--sinch-comp-accordion-color-disabled-icon-initial)}#button:focus-visible::after{content:"";position:absolute;inset:0;border:2px solid var(--sinch-comp-accordion-color-default-outline-focus);pointer-events:none}#status-wrapper{display:none;width:18px;height:24px;padding:8px 8px 8px 2px;box-sizing:border-box}#status{width:8px;height:8px;border-radius:50%}:host([status]:not([status=""])) #status-wrapper{display:block}:host([status=success]) #status{background-color:var(--sinch-comp-accordion-color-default-status-success)}:host([status=warn]) #status{background-color:var(--sinch-comp-accordion-color-default-status-warning)}:host([status=error]) #status{background-color:var(--sinch-comp-accordion-color-default-status-error)}:host([status=info]) #status{background-color:var(--sinch-comp-accordion-color-default-status-info)}#title{flex:1;min-width:0;--sinch-comp-title-font:var(--sinch-comp-accordion-font-title);--sinch-global-color-text:var(--sinch-comp-accordion-color-default-title-initial)}#button:disabled>#title{--sinch-global-color-text:var(--sinch-comp-accordion-color-disabled-title-initial)}#content{display:none;overflow-y:auto;flex-shrink:1;min-height:0;padding:0 8px 12px}#dropdown-icon{flex-shrink:0;margin-top:2px;transform:rotate(0);will-change:transform;transition:transform .25s ease-in-out}#button[aria-expanded=true]>#dropdown-icon{transform:rotate(180deg)}#button[aria-expanded=true]+#content{display:block}#optional{flex-shrink:0;--sinch-comp-text-font:var(--sinch-comp-accordion-font-optional-text);--sinch-global-color-text:var(--sinch-comp-accordion-color-default-optional-text-initial)}#button:disabled>#optional{--sinch-global-color-text:var(--sinch-comp-accordion-color-disabled-optional-text-initial)}</style><div id="wrapper"><button id="button" aria-controls="content" aria-expanded="false"><div id="status-wrapper"><div id="status"></div></div><slot name="icon"></slot><sinch-title id="title" level="3" type="m" ellipsis></sinch-title><sinch-text id="optional" type="m"></sinch-text><sinch-icon icons-version="2" name="fa-chevron-down" id="dropdown-icon"></sinch-icon></button><div id="content" role="region" aria-labelledby="button"><slot name="content"></slot></div></div>';
465
+ const templateHTML$1g = '<style>:host{display:block;outline:0;min-height:48px}#wrapper{display:flex;flex-direction:column;position:relative;width:100%;height:100%;box-sizing:border-box;overflow:hidden;border-bottom:1px solid var(--sinch-comp-accordion-color-default-border-initial)}:host(:last-child)>#wrapper{border-bottom:none}#button{all:initial;display:flex;position:relative;align-items:flex-start;gap:8px;box-sizing:border-box;width:100%;min-height:48px;padding:12px 4px 12px 8px;cursor:pointer;--sinch-global-color-icon:var(--sinch-comp-accordion-color-default-icon-initial);--sinch-global-size-icon:var(--sinch-comp-accordion-size-icon)}#button>*{pointer-events:none}#button:disabled{cursor:initial;--sinch-global-color-icon:var(--sinch-comp-accordion-color-disabled-icon-initial)}#button:focus-visible::after{content:"";position:absolute;inset:0;border:2px solid var(--sinch-comp-accordion-color-default-outline-focus);pointer-events:none}#status-wrapper{display:none;width:18px;height:24px;padding:8px 8px 8px 2px;box-sizing:border-box}#status{width:8px;height:8px;border-radius:50%}:host([status]:not([status=""])) #status-wrapper{display:block}:host([status=success]) #status{background-color:var(--sinch-comp-accordion-color-default-status-success)}:host([status=warn]) #status{background-color:var(--sinch-comp-accordion-color-default-status-warning)}:host([status=error]) #status{background-color:var(--sinch-comp-accordion-color-default-status-error)}:host([status=info]) #status{background-color:var(--sinch-comp-accordion-color-default-status-info)}#title{flex:1;min-width:0;--sinch-comp-title-font:var(--sinch-comp-accordion-font-title);--sinch-global-color-text:var(--sinch-comp-accordion-color-default-title-initial)}#button:disabled>#title{--sinch-global-color-text:var(--sinch-comp-accordion-color-disabled-title-initial)}#content{display:none;overflow-y:auto;flex-shrink:1;min-height:0;padding:0 8px 12px}#dropdown-icon{flex-shrink:0;margin-top:2px;transform:rotate(0);will-change:transform;transition:transform .25s ease-in-out}#button[aria-expanded=true]>#dropdown-icon{transform:rotate(180deg)}#button[aria-expanded=true]+#content{display:block}#optional{flex-shrink:0;--sinch-comp-text-font:var(--sinch-comp-accordion-font-optional-text);--sinch-global-color-text:var(--sinch-comp-accordion-color-default-optional-text-initial)}#button:disabled>#optional{--sinch-global-color-text:var(--sinch-comp-accordion-color-disabled-optional-text-initial)}</style><div id="wrapper"><button id="button" aria-controls="content" aria-expanded="false"><div id="status-wrapper"><div id="status"></div></div><slot name="icon"></slot><sinch-title id="title" level="3" type="m"></sinch-title><sinch-text id="optional" type="m"></sinch-text><sinch-icon icons-version="2" name="fa-chevron-down" id="dropdown-icon"></sinch-icon></button><div id="content" role="region" aria-labelledby="button"><slot name="content"></slot></div></div>';
440
466
  const template$1g = document.createElement("template");
441
467
  template$1g.innerHTML = templateHTML$1g;
442
468
  class AccordionItem extends NectaryElement {
443
469
  #$button;
444
470
  #$title;
445
471
  #$optionalText;
472
+ #hasExplicitEllipsis = false;
446
473
  constructor() {
447
474
  super();
448
475
  const shadowRoot = this.attachShadow({ delegatesFocus: true });
@@ -451,7 +478,14 @@ class AccordionItem extends NectaryElement {
451
478
  this.#$title = shadowRoot.querySelector("#title");
452
479
  this.#$optionalText = shadowRoot.querySelector("#optional");
453
480
  }
481
+ connectedCallback() {
482
+ super.connectedCallback();
483
+ if (this.hasAttribute("ellipsis") === false && this.#hasExplicitEllipsis === false) {
484
+ updateBooleanAttribute(this, "ellipsis", true);
485
+ }
486
+ }
454
487
  disconnectedCallback() {
488
+ super.disconnectedCallback();
455
489
  }
456
490
  static get observedAttributes() {
457
491
  return [
@@ -459,7 +493,6 @@ class AccordionItem extends NectaryElement {
459
493
  "disabled",
460
494
  "data-checked",
461
495
  "optionaltext",
462
- // eslint-disable-next-line @nectary/observed-attribute-accessor -- baseline backlog: ellipsis missing set/get pair, fixed by MR !597
463
496
  "ellipsis"
464
497
  ];
465
498
  }
@@ -490,6 +523,7 @@ class AccordionItem extends NectaryElement {
490
523
  break;
491
524
  }
492
525
  case "ellipsis": {
526
+ this.#hasExplicitEllipsis = true;
493
527
  updateBooleanAttribute(this.#$title, name, isAttrTrue(newVal));
494
528
  break;
495
529
  }
@@ -513,18 +547,25 @@ class AccordionItem extends NectaryElement {
513
547
  get disabled() {
514
548
  return getBooleanAttribute(this, "disabled");
515
549
  }
516
- get status() {
517
- return getLiteralAttribute(this, statusValues$1, "status", null);
518
- }
519
550
  set status(value) {
520
551
  updateLiteralAttribute(this, statusValues$1, "status", value);
521
552
  }
553
+ get status() {
554
+ return getLiteralAttribute(this, statusValues$1, "status", null);
555
+ }
522
556
  set optionalText(value) {
523
557
  updateAttribute(this, "optionaltext", value);
524
558
  }
525
559
  get optionalText() {
526
560
  return getAttribute(this, "optionaltext");
527
561
  }
562
+ set ellipsis(isEllipsis2) {
563
+ this.#hasExplicitEllipsis = true;
564
+ updateBooleanAttribute(this, "ellipsis", isEllipsis2);
565
+ }
566
+ get ellipsis() {
567
+ return getBooleanAttribute(this, "ellipsis");
568
+ }
528
569
  get focusable() {
529
570
  return true;
530
571
  }
@@ -892,7 +933,7 @@ class ActionMenu extends NectaryElement {
892
933
  this.#selectOption(null);
893
934
  this.#$listbox.scrollTo({ top: 0, behavior: "auto" });
894
935
  } else {
895
- const activeElement = this.#getDeepActiveElement();
936
+ const activeElement = getDeepActiveElement(this.ownerDocument);
896
937
  const isTextInput = activeElement !== null && activeElement.tagName === "INPUT";
897
938
  if (!isTextInput) {
898
939
  this.focus();
@@ -969,13 +1010,6 @@ class ActionMenu extends NectaryElement {
969
1010
  }
970
1011
  return this.#getLastOption();
971
1012
  }
972
- #getDeepActiveElement() {
973
- let activeElement = this.ownerDocument.activeElement;
974
- while (activeElement !== null && activeElement.shadowRoot !== null && activeElement.shadowRoot.activeElement !== null) {
975
- activeElement = activeElement.shadowRoot.activeElement;
976
- }
977
- return activeElement;
978
- }
979
1013
  #selectOption($option) {
980
1014
  const hasRows = this.hasAttribute("rows");
981
1015
  for (const $op of this.#getOptionElements()) {
@@ -4266,7 +4300,7 @@ class Tooltip extends NectaryElement {
4266
4300
  this.#tooltipState.show();
4267
4301
  };
4268
4302
  #onFocusIn = () => {
4269
- if (this.#suppressFocusIn) {
4303
+ if (this.#suppressFocusIn || !this.#shouldOpenForFocus()) {
4270
4304
  return;
4271
4305
  }
4272
4306
  this.#closeFocusedTooltip();
@@ -4275,6 +4309,13 @@ class Tooltip extends NectaryElement {
4275
4309
  focusedTooltip = this;
4276
4310
  this.#tooltipState.show();
4277
4311
  };
4312
+ #shouldOpenForFocus() {
4313
+ const activeEl = getDeepActiveElement(this.ownerDocument);
4314
+ if (!(activeEl instanceof HTMLElement) || !this.#targetContains(activeEl)) {
4315
+ return false;
4316
+ }
4317
+ return activeEl.matches(":focus-visible");
4318
+ }
4278
4319
  #closeFocusedTooltip() {
4279
4320
  if (!this.#isOtherUncontrolledTooltip(focusedTooltip)) {
4280
4321
  return;
@@ -4385,7 +4426,7 @@ class Tooltip extends NectaryElement {
4385
4426
  });
4386
4427
  };
4387
4428
  #targetContains(node) {
4388
- return this.#$target.assignedElements().some((el) => el.contains(node));
4429
+ return this.#$target.assignedElements().some((el) => composedContains(el, node));
4389
4430
  }
4390
4431
  #getTargetElements() {
4391
4432
  return this.#$target.assignedElements().filter((el) => el instanceof HTMLElement);
@@ -13017,13 +13058,6 @@ class SegmentedControlOption extends NectaryElement {
13017
13058
  };
13018
13059
  }
13019
13060
  defineCustomElement("sinch-segmented-control-option", SegmentedControlOption);
13020
- function getActualActiveElement() {
13021
- let activeElement = document.activeElement;
13022
- while (activeElement?.shadowRoot?.activeElement != null) {
13023
- activeElement = activeElement.shadowRoot.activeElement;
13024
- }
13025
- return activeElement;
13026
- }
13027
13061
  function createKeyboardNavigation() {
13028
13062
  return {
13029
13063
  navigateToNextOption(enabledOptions, forward) {
@@ -13031,7 +13065,7 @@ function createKeyboardNavigation() {
13031
13065
  if (optionsLength === 0) {
13032
13066
  return;
13033
13067
  }
13034
- const currentIndex = enabledOptions.findIndex((option) => option === getActualActiveElement());
13068
+ const currentIndex = enabledOptions.findIndex((option) => option === getDeepActiveElement());
13035
13069
  let nextIndex;
13036
13070
  if (currentIndex !== -1) {
13037
13071
  if (forward) {
package/bundle.ts CHANGED
@@ -84,3 +84,4 @@ export * from './toast-manager/index.js'
84
84
  export * from './toast/index.js'
85
85
  export * from './toggle/index.js'
86
86
  export * from './tooltip/index.js'
87
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "5.37.2",
3
+ "version": "5.37.5",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@babel/runtime": "^7.22.15",
27
- "@nectary/assets": "3.6.11"
27
+ "@nectary/assets": "3.6.12"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@babel/cli": "^7.22.15",
package/tooltip/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import "../text/index.js";
2
2
  import "../pop/index.js";
3
- import { shouldReduceMotion, updateAttribute, updateBooleanAttribute, getAttribute, getBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, setClass } from "../utils/dom.js";
3
+ import { shouldReduceMotion, updateAttribute, updateBooleanAttribute, getAttribute, getBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, getDeepActiveElement, composedContains, setClass } from "../utils/dom.js";
4
4
  import { defineCustomElement, NectaryElement } from "../utils/element.js";
5
5
  import { rectOverlap } from "../utils/rect.js";
6
6
  import { getReactEventHandler } from "../utils/get-react-event-handler.js";
@@ -239,7 +239,7 @@ class Tooltip extends NectaryElement {
239
239
  this.#tooltipState.show();
240
240
  };
241
241
  #onFocusIn = () => {
242
- if (this.#suppressFocusIn) {
242
+ if (this.#suppressFocusIn || !this.#shouldOpenForFocus()) {
243
243
  return;
244
244
  }
245
245
  this.#closeFocusedTooltip();
@@ -248,6 +248,13 @@ class Tooltip extends NectaryElement {
248
248
  focusedTooltip = this;
249
249
  this.#tooltipState.show();
250
250
  };
251
+ #shouldOpenForFocus() {
252
+ const activeEl = getDeepActiveElement(this.ownerDocument);
253
+ if (!(activeEl instanceof HTMLElement) || !this.#targetContains(activeEl)) {
254
+ return false;
255
+ }
256
+ return activeEl.matches(":focus-visible");
257
+ }
251
258
  #closeFocusedTooltip() {
252
259
  if (!this.#isOtherUncontrolledTooltip(focusedTooltip)) {
253
260
  return;
@@ -358,7 +365,7 @@ class Tooltip extends NectaryElement {
358
365
  });
359
366
  };
360
367
  #targetContains(node) {
361
- return this.#$target.assignedElements().some((el) => el.contains(node));
368
+ return this.#$target.assignedElements().some((el) => composedContains(el, node));
362
369
  }
363
370
  #getTargetElements() {
364
371
  return this.#$target.assignedElements().filter((el) => el instanceof HTMLElement);
@@ -1,11 +1,5 @@
1
+ import { getDeepActiveElement } from "./dom.js";
1
2
  import { getTargetByAttribute } from "./event-target.js";
2
- function getActualActiveElement() {
3
- let activeElement = document.activeElement;
4
- while (activeElement?.shadowRoot?.activeElement != null) {
5
- activeElement = activeElement.shadowRoot.activeElement;
6
- }
7
- return activeElement;
8
- }
9
3
  function createKeyboardNavigation() {
10
4
  return {
11
5
  navigateToNextOption(enabledOptions, forward) {
@@ -13,7 +7,7 @@ function createKeyboardNavigation() {
13
7
  if (optionsLength === 0) {
14
8
  return;
15
9
  }
16
- const currentIndex = enabledOptions.findIndex((option) => option === getActualActiveElement());
10
+ const currentIndex = enabledOptions.findIndex((option) => option === getDeepActiveElement());
17
11
  let nextIndex;
18
12
  if (currentIndex !== -1) {
19
13
  if (forward) {
package/utils/dom.d.ts CHANGED
@@ -31,6 +31,8 @@ export declare const getCssVar: (element: Element, variableName: string) => stri
31
31
  export declare const getCssVars: (element: Element, variableNames: string[]) => (string | null)[];
32
32
  export declare const cloneNode: (el: Element, deep: boolean) => Element;
33
33
  export declare const shouldReduceMotion: () => boolean;
34
+ export declare const getDeepActiveElement: (ownerDocument?: Document) => Element | null;
35
+ export declare const composedContains: (container: Node, node: Node | null) => boolean;
34
36
  export declare const isAttrEqual: (oldVal: string | null, newVal: string | null) => boolean;
35
37
  export declare const getScrollableParents: (node: HTMLElement | null) => (HTMLElement | Document)[];
36
38
  export declare const isTransformedElement: (element: HTMLElement | null) => element is HTMLElement;
package/utils/dom.js CHANGED
@@ -134,6 +134,32 @@ const cloneNode = (el, deep) => {
134
134
  return el.cloneNode(deep);
135
135
  };
136
136
  const shouldReduceMotion = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches;
137
+ const getDeepActiveElement = (ownerDocument = document) => {
138
+ let activeElement = ownerDocument.activeElement;
139
+ while (activeElement !== null && activeElement.shadowRoot !== null && activeElement.shadowRoot.activeElement !== null) {
140
+ activeElement = activeElement.shadowRoot.activeElement;
141
+ }
142
+ return activeElement;
143
+ };
144
+ const composedContains = (container, node) => {
145
+ let current = node;
146
+ while (current !== null) {
147
+ if (current === container) {
148
+ return true;
149
+ }
150
+ if (current.parentNode !== null) {
151
+ current = current.parentNode;
152
+ continue;
153
+ }
154
+ const root = current.getRootNode();
155
+ if (root instanceof ShadowRoot) {
156
+ current = root.host;
157
+ continue;
158
+ }
159
+ current = null;
160
+ }
161
+ return false;
162
+ };
137
163
  const isAttrEqual = (oldVal, newVal) => {
138
164
  return oldVal === newVal || newVal === null && oldVal === "false" || newVal === "" && oldVal === "true";
139
165
  };
@@ -190,10 +216,12 @@ export {
190
216
  attrValueToPixels,
191
217
  clampNumber,
192
218
  cloneNode,
219
+ composedContains,
193
220
  getAttribute,
194
221
  getBooleanAttribute,
195
222
  getCssVar,
196
223
  getCssVars,
224
+ getDeepActiveElement,
197
225
  getIntegerAttribute,
198
226
  getLiteralAttribute,
199
227
  getScrollableParents,
package/utils/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Context, subscribeContext } from "./context.js";
2
2
  import { CSV_DELIMITER, getFirstCsvValue, packCsv, unpackCsv, updateCsv } from "./csv.js";
3
- import { attrValueToInteger, attrValueToPixels, clampNumber, cloneNode, getAttribute, getBooleanAttribute, getCssVar, getCssVars, getIntegerAttribute, getLiteralAttribute, getScrollableParents, getTransformedAncestor, hasClass, isAttrEqual, isAttrTrue, isLiteralValue, isTransformedElement, setClass, shouldReduceMotion, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateIntegerAttribute, updateLiteralAttribute } from "./dom.js";
3
+ import { attrValueToInteger, attrValueToPixels, clampNumber, cloneNode, composedContains, getAttribute, getBooleanAttribute, getCssVar, getCssVars, getDeepActiveElement, getIntegerAttribute, getLiteralAttribute, getScrollableParents, getTransformedAncestor, hasClass, isAttrEqual, isAttrTrue, isLiteralValue, isTransformedElement, setClass, shouldReduceMotion, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateIntegerAttribute, updateLiteralAttribute } from "./dom.js";
4
4
  import { NectaryElement, defineCustomElement, pascalToKebabCase, registerComponent, resetNectaryRegistry, setNectaryRegistry } from "./element.js";
5
5
  import { getRect, getTargetRect, rectOverlap } from "./rect.js";
6
6
  import { getFirstFocusableElement, getFirstSlotElement, isElementFocused } from "./slot.js";
@@ -18,6 +18,7 @@ export {
18
18
  attrValueToPixels,
19
19
  clampNumber,
20
20
  cloneNode,
21
+ composedContains,
21
22
  debounceAnimationFrame,
22
23
  debounceTimeout,
23
24
  defineCustomElement,
@@ -25,6 +26,7 @@ export {
25
26
  getBooleanAttribute,
26
27
  getCssVar,
27
28
  getCssVars,
29
+ getDeepActiveElement,
28
30
  getFirstCsvValue,
29
31
  getFirstFocusableElement,
30
32
  getFirstSlotElement,
@@ -1,3 +1,9 @@
1
+ const getNonEsmShModuleFile = (modulePath) => {
2
+ if (modulePath === "bundle") {
3
+ return "bundle.js";
4
+ }
5
+ return `${modulePath}/index.js`;
6
+ };
1
7
  const getImportPath = (cdnUrl, version, modulePath) => {
2
8
  if (cdnUrl.length === 0) {
3
9
  return null;
@@ -9,10 +15,11 @@ const getImportPath = (cdnUrl, version, modulePath) => {
9
15
  }
10
16
  return `${cdnUrl}/${modulePath}`;
11
17
  }
18
+ const moduleFile = getNonEsmShModuleFile(modulePath);
12
19
  if (version.length !== 0) {
13
- return `${cdnUrl}/${version}/${modulePath}.js`;
20
+ return `${cdnUrl}/${version}/${moduleFile}`;
14
21
  }
15
- return `${cdnUrl}/latest/${modulePath}.js`;
22
+ return `${cdnUrl}/latest/${moduleFile}`;
16
23
  };
17
24
  const getCssImportPath = (cdnUrl, version, modulePath) => {
18
25
  if (cdnUrl.length === 0) {