@nectary/components 5.37.3 → 5.37.6

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.
@@ -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
  };
@@ -907,7 +933,7 @@ class ActionMenu extends NectaryElement {
907
933
  this.#selectOption(null);
908
934
  this.#$listbox.scrollTo({ top: 0, behavior: "auto" });
909
935
  } else {
910
- const activeElement = this.#getDeepActiveElement();
936
+ const activeElement = getDeepActiveElement(this.ownerDocument);
911
937
  const isTextInput = activeElement !== null && activeElement.tagName === "INPUT";
912
938
  if (!isTextInput) {
913
939
  this.focus();
@@ -984,13 +1010,6 @@ class ActionMenu extends NectaryElement {
984
1010
  }
985
1011
  return this.#getLastOption();
986
1012
  }
987
- #getDeepActiveElement() {
988
- let activeElement = this.ownerDocument.activeElement;
989
- while (activeElement !== null && activeElement.shadowRoot !== null && activeElement.shadowRoot.activeElement !== null) {
990
- activeElement = activeElement.shadowRoot.activeElement;
991
- }
992
- return activeElement;
993
- }
994
1013
  #selectOption($option) {
995
1014
  const hasRows = this.hasAttribute("rows");
996
1015
  for (const $op of this.#getOptionElements()) {
@@ -4281,7 +4300,7 @@ class Tooltip extends NectaryElement {
4281
4300
  this.#tooltipState.show();
4282
4301
  };
4283
4302
  #onFocusIn = () => {
4284
- if (this.#suppressFocusIn) {
4303
+ if (this.#suppressFocusIn || !this.#shouldOpenForFocus()) {
4285
4304
  return;
4286
4305
  }
4287
4306
  this.#closeFocusedTooltip();
@@ -4290,6 +4309,13 @@ class Tooltip extends NectaryElement {
4290
4309
  focusedTooltip = this;
4291
4310
  this.#tooltipState.show();
4292
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
+ }
4293
4319
  #closeFocusedTooltip() {
4294
4320
  if (!this.#isOtherUncontrolledTooltip(focusedTooltip)) {
4295
4321
  return;
@@ -4400,7 +4426,7 @@ class Tooltip extends NectaryElement {
4400
4426
  });
4401
4427
  };
4402
4428
  #targetContains(node) {
4403
- return this.#$target.assignedElements().some((el) => el.contains(node));
4429
+ return this.#$target.assignedElements().some((el) => composedContains(el, node));
4404
4430
  }
4405
4431
  #getTargetElements() {
4406
4432
  return this.#$target.assignedElements().filter((el) => el instanceof HTMLElement);
@@ -13032,13 +13058,6 @@ class SegmentedControlOption extends NectaryElement {
13032
13058
  };
13033
13059
  }
13034
13060
  defineCustomElement("sinch-segmented-control-option", SegmentedControlOption);
13035
- function getActualActiveElement() {
13036
- let activeElement = document.activeElement;
13037
- while (activeElement?.shadowRoot?.activeElement != null) {
13038
- activeElement = activeElement.shadowRoot.activeElement;
13039
- }
13040
- return activeElement;
13041
- }
13042
13061
  function createKeyboardNavigation() {
13043
13062
  return {
13044
13063
  navigateToNextOption(enabledOptions, forward) {
@@ -13046,7 +13065,7 @@ function createKeyboardNavigation() {
13046
13065
  if (optionsLength === 0) {
13047
13066
  return;
13048
13067
  }
13049
- const currentIndex = enabledOptions.findIndex((option) => option === getActualActiveElement());
13068
+ const currentIndex = enabledOptions.findIndex((option) => option === getDeepActiveElement());
13050
13069
  let nextIndex;
13051
13070
  if (currentIndex !== -1) {
13052
13071
  if (forward) {
@@ -14318,7 +14337,7 @@ class Sheet extends NectaryElement {
14318
14337
  };
14319
14338
  }
14320
14339
  defineCustomElement("sinch-sheet", Sheet);
14321
- const templateHTML$f = '<style>:host{display:contents;--sinch-sheet-close-button-display:unset}#top{display:flex;flex-direction:row;align-items:center;margin-top:8px;gap:8px}#text{--sinch-global-color-text:var(--sinch-comp-sheet-color-title);--sinch-comp-title-font:var(--sinch-comp-sheet-font-title)}#description{--sinch-global-color-text:var(--sinch-comp-sheet-color-description);--sinch-comp-text-font:var(--sinch-comp-sheet-font-description)}#close{display:var(--sinch-sheet-close-button-display,initial);margin-left:auto}</style><div id="title"><div id="top"><slot id="icon" name="icon"></slot><sinch-title id="text" type="m" level="3"></sinch-title><sinch-button id="close" size="s" aria-label="Close"><sinch-icon icons-version="2" name="fa-xmark" id="icon-close" slot="icon"></sinch-icon></sinch-button></div><sinch-text id="description" type="m"></sinch-text></div>';
14340
+ const templateHTML$f = '<style>:host{display:contents;--sinch-sheet-close-button-display:unset}#top{display:flex;flex-direction:row;align-items:center;margin-top:8px;gap:var(--sinch-comp-sheet-size-title-gap)}#text{--sinch-global-color-text:var(--sinch-comp-sheet-color-title);--sinch-comp-title-font:var(--sinch-comp-sheet-font-title)}#description{--sinch-global-color-text:var(--sinch-comp-sheet-color-description);--sinch-comp-text-font:var(--sinch-comp-sheet-font-description)}#close{display:var(--sinch-sheet-close-button-display,initial);margin-left:auto}</style><div id="title"><div id="top"><slot id="icon" name="icon"></slot><sinch-title id="text" type="m" level="3"></sinch-title><sinch-button id="close" size="s" aria-label="Close"><sinch-icon icons-version="2" name="fa-xmark" id="icon-close" slot="icon"></sinch-icon></sinch-button></div><sinch-text id="description" type="m"></sinch-text></div>';
14322
14341
  const template$f = document.createElement("template");
14323
14342
  template$f.innerHTML = templateHTML$f;
14324
14343
  class SheetTitle extends NectaryElement {
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.3",
3
+ "version": "5.37.6",
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.13"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@babel/cli": "^7.22.15",
@@ -40,6 +40,6 @@
40
40
  "vite": "^7.0.6"
41
41
  },
42
42
  "peerDependencies": {
43
- "@nectary/theme-base": "1.16.1"
43
+ "@nectary/theme-base": "1.16.2"
44
44
  }
45
45
  }
@@ -3,7 +3,7 @@ import "../title/index.js";
3
3
  import { isAttrEqual, updateAttribute, getAttribute } from "../utils/dom.js";
4
4
  import { defineCustomElement, NectaryElement } from "../utils/element.js";
5
5
  import { getRect } from "../utils/rect.js";
6
- const templateHTML = '<style>:host{display:contents;--sinch-sheet-close-button-display:unset}#top{display:flex;flex-direction:row;align-items:center;margin-top:8px;gap:8px}#text{--sinch-global-color-text:var(--sinch-comp-sheet-color-title);--sinch-comp-title-font:var(--sinch-comp-sheet-font-title)}#description{--sinch-global-color-text:var(--sinch-comp-sheet-color-description);--sinch-comp-text-font:var(--sinch-comp-sheet-font-description)}#close{display:var(--sinch-sheet-close-button-display,initial);margin-left:auto}</style><div id="title"><div id="top"><slot id="icon" name="icon"></slot><sinch-title id="text" type="m" level="3"></sinch-title><sinch-button id="close" size="s" aria-label="Close"><sinch-icon icons-version="2" name="fa-xmark" id="icon-close" slot="icon"></sinch-icon></sinch-button></div><sinch-text id="description" type="m"></sinch-text></div>';
6
+ const templateHTML = '<style>:host{display:contents;--sinch-sheet-close-button-display:unset}#top{display:flex;flex-direction:row;align-items:center;margin-top:8px;gap:var(--sinch-comp-sheet-size-title-gap)}#text{--sinch-global-color-text:var(--sinch-comp-sheet-color-title);--sinch-comp-title-font:var(--sinch-comp-sheet-font-title)}#description{--sinch-global-color-text:var(--sinch-comp-sheet-color-description);--sinch-comp-text-font:var(--sinch-comp-sheet-font-description)}#close{display:var(--sinch-sheet-close-button-display,initial);margin-left:auto}</style><div id="title"><div id="top"><slot id="icon" name="icon"></slot><sinch-title id="text" type="m" level="3"></sinch-title><sinch-button id="close" size="s" aria-label="Close"><sinch-icon icons-version="2" name="fa-xmark" id="icon-close" slot="icon"></sinch-icon></sinch-button></div><sinch-text id="description" type="m"></sinch-text></div>';
7
7
  const template = document.createElement("template");
8
8
  template.innerHTML = templateHTML;
9
9
  class SheetTitle extends NectaryElement {
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) {