@nectary/components 5.41.0 → 5.41.2

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/bundle.js CHANGED
@@ -364,7 +364,7 @@ const typeValues$7 = ["m", "s", "xs", "xxs"];
364
364
  const templateHTML$1k = '<style>:host{text-align:var(--sinch-comp-text-align);display:block;font:var(--sinch-comp-text-font);color:var(--sinch-global-color-text,var(--sinch-sys-color-text-default));--sinch-comp-text-font:var(--sinch-sys-font-body-m);--sinch-comp-text-align:unset}:host([inline]){display:inline}:host([type="s"]){--sinch-comp-text-font:var(--sinch-sys-font-body-s)}:host([type=xs]){--sinch-comp-text-font:var(--sinch-sys-font-body-xs)}:host([type=xxs]){--sinch-comp-text-font:var(--sinch-sys-font-body-xxs)}:host([type="m"][emphasized]){--sinch-comp-text-font:var(--sinch-sys-font-body-emphasize)}:host([type="s"][emphasized]){--sinch-comp-text-font:var(--sinch-sys-font-body-emphasize-s)}:host([type=xs][emphasized]){--sinch-comp-text-font:var(--sinch-sys-font-body-emphasize-xs)}:host([ellipsis]){overflow:hidden;text-overflow:ellipsis;white-space:nowrap;--sinch-global-text-white-space:nowrap}</style><slot></slot>';
365
365
  const template$1k = document.createElement("template");
366
366
  template$1k.innerHTML = templateHTML$1k;
367
- class Text extends NectaryElement {
367
+ let Text$1 = class Text2 extends NectaryElement {
368
368
  constructor() {
369
369
  super();
370
370
  const shadowRoot = this.attachShadow();
@@ -423,8 +423,8 @@ class Text extends NectaryElement {
423
423
  getBooleanAttribute(this, "inline") ? "text" : "paragraph"
424
424
  );
425
425
  }
426
- }
427
- defineCustomElement("sinch-text", Text);
426
+ };
427
+ defineCustomElement("sinch-text", Text$1);
428
428
  const typeValues$6 = ["xl", "l", "m", "s", "xs"];
429
429
  const templateHTML$1j = '<style>:host{display:block;--sinch-comp-title-font:var(--sinch-sys-font-desktop-title-m)}:host([type=xl]){--sinch-comp-title-font:var(--sinch-sys-font-desktop-title-xl)}:host([type="l"]){--sinch-comp-title-font:var(--sinch-sys-font-desktop-title-l)}:host([type="m"]){--sinch-comp-title-font:var(--sinch-sys-font-desktop-title-m)}:host([type="s"]){--sinch-comp-title-font:var(--sinch-sys-font-desktop-title-s)}:host([type=xs]){--sinch-comp-title-font:var(--sinch-sys-font-desktop-title-xs)}#text{letter-spacing:-2%;color:var(--sinch-global-color-text,var(--sinch-sys-color-text-default));font:var(--sinch-comp-title-font)}:host([ellipsis]) #text{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}</style><span id="text"></span>';
430
430
  const template$1j = document.createElement("template");
@@ -3851,22 +3851,8 @@ class Pop extends NectaryElement {
3851
3851
  const clampedPosition = shouldClamp ? clampPosition({ x: xPos, y: yPos, ...localViewportInfos }) : { x: xPos, y: yPos };
3852
3852
  const clampedXPos = clampedPosition.x;
3853
3853
  const clampedYPos = clampedPosition.y;
3854
- if (this.hideOutsideViewport) {
3855
- const viewportPosition = getAnchorPosition(targetRectViewport, modalWidthViewport, modalHeightViewport, orient);
3856
- const visibilityViewportInfos = {
3857
- boundsWidth: window.innerWidth,
3858
- boundsHeight: window.innerHeight,
3859
- insetX: inset,
3860
- insetY: inset,
3861
- modalWidth: modalWidthViewport,
3862
- modalHeight: modalHeightViewport
3863
- };
3864
- const isOutOfViewport = this.#isOutsideViewport(viewportPosition.x, viewportPosition.y, visibilityViewportInfos);
3865
- if (isOutOfViewport) {
3866
- this.#$dialog.style.setProperty("visibility", "hidden");
3867
- } else {
3868
- this.#$dialog.style.removeProperty("visibility");
3869
- }
3854
+ if (this.hideOutsideViewport && this.#isTargetOutsideViewport(targetRectViewport, inset)) {
3855
+ this.#$dialog.style.setProperty("visibility", "hidden");
3870
3856
  } else {
3871
3857
  this.#$dialog.style.removeProperty("visibility");
3872
3858
  }
@@ -3951,10 +3937,8 @@ class Pop extends NectaryElement {
3951
3937
  this.#updateOrientation();
3952
3938
  }
3953
3939
  };
3954
- #isOutsideViewport(x, y, viewportInfos) {
3955
- const { boundsWidth, boundsHeight, insetX, insetY, modalWidth, modalHeight } = viewportInfos;
3956
- const clampedPosition = clampPosition({ x, y, boundsWidth, boundsHeight, insetX, insetY, modalWidth, modalHeight });
3957
- return Math.abs(clampedPosition.x - x) > 2 || Math.abs(clampedPosition.y - y) > 2;
3940
+ #isTargetOutsideViewport(targetRect, inset) {
3941
+ return targetRect.x + targetRect.width <= inset || targetRect.y + targetRect.height <= inset || targetRect.x >= window.innerWidth - inset || targetRect.y >= window.innerHeight - inset;
3958
3942
  }
3959
3943
  }
3960
3944
  defineCustomElement("sinch-pop", Pop);
@@ -4122,6 +4106,7 @@ const SHOW_DELAY_FAST = 250;
4122
4106
  const HIDE_DELAY = 0;
4123
4107
  const ANIMATION_DURATION = 100;
4124
4108
  const FOCUSABLE_OUTSIDE_TARGET_SELECTOR = 'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"]), [contenteditable=""], [contenteditable="true"]';
4109
+ const OPEN_FLOATING_LAYER_SELECTOR = "sinch-popover[open], sinch-pop[open], dialog[open]";
4125
4110
  const OVERLAP_TOLERANCE = 1;
4126
4111
  const MAX_ZERO_DIMENSION_PLACEMENT_RETRIES = 8;
4127
4112
  const MIN_FIRST_REVEAL_STABILITY_FRAMES = 3;
@@ -4150,13 +4135,14 @@ class Tooltip extends NectaryElement {
4150
4135
  #isSuspendedByHover = false;
4151
4136
  #isSubscribed = false;
4152
4137
  #controller;
4138
+ #childObserver = null;
4153
4139
  #placementScheduled = false;
4154
4140
  #zeroDimensionPlacementRetries = 0;
4155
4141
  #revealRequestId = 0;
4156
4142
  #hasCompletedFirstReveal = false;
4157
4143
  constructor() {
4158
4144
  super();
4159
- const shadowRoot = this.attachShadow();
4145
+ const shadowRoot = this.attachShadow({ slotAssignment: "manual" });
4160
4146
  shadowRoot.appendChild(template$Y.content.cloneNode(true));
4161
4147
  this.#$pop = shadowRoot.querySelector("#pop");
4162
4148
  this.#$tooltipText = shadowRoot.querySelector("#text");
@@ -4185,6 +4171,11 @@ class Tooltip extends NectaryElement {
4185
4171
  this.setAttribute("role", "tooltip");
4186
4172
  this.#$pop.addEventListener("-close", this.#onPopClose, options);
4187
4173
  this.#$target.addEventListener("slotchange", this.#onTargetSlotChange, options);
4174
+ this.#assignTargetNodes();
4175
+ this.#childObserver = new MutationObserver(() => {
4176
+ this.#assignTargetNodes();
4177
+ });
4178
+ this.#childObserver.observe(this, { childList: true });
4188
4179
  this.addEventListener("-show", this.#onShowReactHandler, options);
4189
4180
  this.addEventListener("-hide", this.#onHideReactHandler, options);
4190
4181
  this.#resizeObserver = new ResizeObserver(() => {
@@ -4216,6 +4207,8 @@ class Tooltip extends NectaryElement {
4216
4207
  this.#controller = null;
4217
4208
  this.#resizeObserver?.disconnect();
4218
4209
  this.#resizeObserver = null;
4210
+ this.#childObserver?.disconnect();
4211
+ this.#childObserver = null;
4219
4212
  }
4220
4213
  static get observedAttributes() {
4221
4214
  return [
@@ -4332,12 +4325,18 @@ class Tooltip extends NectaryElement {
4332
4325
  this.#tooltipState.destroy();
4333
4326
  };
4334
4327
  #onMouseEnter = () => {
4328
+ if (this.#targetOwnsOpenFloatingLayer()) {
4329
+ return;
4330
+ }
4335
4331
  this.#claimHoverOwnership();
4336
4332
  this.#suspendFocusedTooltip();
4337
4333
  this.#closeActiveTooltip();
4338
4334
  this.#tooltipState.show();
4339
4335
  };
4340
4336
  #onContentMouseEnter = () => {
4337
+ if (this.#targetOwnsOpenFloatingLayer()) {
4338
+ return;
4339
+ }
4341
4340
  this.#claimHoverOwnership();
4342
4341
  if (this.#hasFocus) {
4343
4342
  return;
@@ -4357,6 +4356,9 @@ class Tooltip extends NectaryElement {
4357
4356
  this.#tooltipState.show();
4358
4357
  };
4359
4358
  #shouldOpenForFocus() {
4359
+ if (this.#targetOwnsOpenFloatingLayer()) {
4360
+ return false;
4361
+ }
4360
4362
  const activeEl = getDeepActiveElement(this.ownerDocument);
4361
4363
  if (!(activeEl instanceof HTMLElement) || !this.#targetContains(activeEl)) {
4362
4364
  return false;
@@ -4472,6 +4474,12 @@ class Tooltip extends NectaryElement {
4472
4474
  this.#tooltipState.hide();
4473
4475
  });
4474
4476
  };
4477
+ #assignTargetNodes() {
4478
+ const nodes = Array.from(this.childNodes).filter(
4479
+ (node) => node instanceof Element || node instanceof Text
4480
+ );
4481
+ this.#$target.assign(...nodes);
4482
+ }
4475
4483
  #targetContains(node) {
4476
4484
  return this.#$target.assignedElements().some((el) => composedContains(el, node));
4477
4485
  }
@@ -4487,6 +4495,13 @@ class Tooltip extends NectaryElement {
4487
4495
  #isFocusInFloatingLayer(el) {
4488
4496
  return el.tagName === "SINCH-POP" || el.tagName === "SINCH-TOOLTIP";
4489
4497
  }
4498
+ // Suppress while a target-owned floating layer (e.g. a sinch-popover triggered by the
4499
+ // target) is open, so the tooltip doesn't flicker over it or steal its focus.
4500
+ #targetOwnsOpenFloatingLayer() {
4501
+ return this.#getTargetElements().some((el) => {
4502
+ return el.matches(OPEN_FLOATING_LAYER_SELECTOR) || el.querySelector(OPEN_FLOATING_LAYER_SELECTOR) !== null;
4503
+ });
4504
+ }
4490
4505
  #clearFocusSuppression() {
4491
4506
  this.#suppressFocusIn = false;
4492
4507
  this.#isSuspendedByHover = false;
@@ -4561,6 +4576,10 @@ class Tooltip extends NectaryElement {
4561
4576
  };
4562
4577
  // SHOW_DELAY ended, tooltip can be shown with animation
4563
4578
  #onStateShowEnd = () => {
4579
+ if (this.#targetOwnsOpenFloatingLayer()) {
4580
+ this.#tooltipState.destroy();
4581
+ return;
4582
+ }
4564
4583
  const revealRequestId = ++this.#revealRequestId;
4565
4584
  activeTooltip = this;
4566
4585
  this.#dispatchShowEvent();
@@ -9013,6 +9032,7 @@ const applyCountPlaceholder = (template2, count) => {
9013
9032
  return template2.replaceAll("{count}", count);
9014
9033
  };
9015
9034
  const sanitizeAttr = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;");
9035
+ const toReactCamelCaseHandlerName = (eventSuffix) => `on${eventSuffix.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}`;
9016
9036
  const isValidAction = (value) => {
9017
9037
  if (typeof value !== "object" || value === null) {
9018
9038
  return false;
@@ -9083,9 +9103,9 @@ class FloatingPanel extends NectaryElement {
9083
9103
  this.#resizeThrottle?.fn();
9084
9104
  });
9085
9105
  this.#resizeObserver.observe(this.#$dialog);
9086
- this.addEventListener("-close", this.#onCloseReact, { signal });
9087
- this.addEventListener("-select-all", this.#onSelectAllReact, { signal });
9088
- this.addEventListener("-action", this.#onActionReact, { signal });
9106
+ this.addEventListener("-close", this.#onCloseReactHandler, { signal });
9107
+ this.addEventListener("-select-all", this.#onSelectAllReactHandler, { signal });
9108
+ this.addEventListener("-action", this.#onActionReactHandler, { signal });
9089
9109
  if (!this.hasAttribute("actions")) {
9090
9110
  this.#renderActions(null);
9091
9111
  }
@@ -9286,7 +9306,7 @@ class FloatingPanel extends NectaryElement {
9286
9306
  * `-${action}` convenience event. The per-action event lets consumers bind
9287
9307
  * directly to a specific action (e.g. `on-archive`) without switching on
9288
9308
  * `e.detail` — and it works for any user-defined action, not just the
9289
- * built-in defaults. React handlers are forwarded by `#onActionReact`.
9309
+ * built-in defaults. React handlers are forwarded by `#onActionReactHandler`.
9290
9310
  */
9291
9311
  #dispatchAction(action) {
9292
9312
  this.dispatchEvent(new CustomEvent("-action", { detail: action }));
@@ -9392,16 +9412,20 @@ class FloatingPanel extends NectaryElement {
9392
9412
  #onActionsPopoverClose = () => {
9393
9413
  this.#closeOverflowMenu();
9394
9414
  };
9395
- #onCloseReact = (e) => {
9415
+ #onCloseReactHandler = (e) => {
9396
9416
  getReactEventHandler(this, "on-close")?.(e);
9417
+ getReactEventHandler(this, "onClose")?.(e);
9397
9418
  };
9398
- #onSelectAllReact = (e) => {
9419
+ #onSelectAllReactHandler = (e) => {
9399
9420
  getReactEventHandler(this, "on-select-all")?.(e);
9421
+ getReactEventHandler(this, "onSelectAll")?.(e);
9400
9422
  };
9401
- #onActionReact = (e) => {
9423
+ #onActionReactHandler = (e) => {
9402
9424
  getReactEventHandler(this, "on-action")?.(e);
9425
+ getReactEventHandler(this, "onAction")?.(e);
9403
9426
  if (e instanceof CustomEvent && typeof e.detail === "string") {
9404
9427
  getReactEventHandler(this, `on-${e.detail}`)?.(e);
9428
+ getReactEventHandler(this, toReactCamelCaseHandlerName(e.detail))?.(e);
9405
9429
  }
9406
9430
  };
9407
9431
  get panelRect() {
@@ -17882,7 +17906,7 @@ export {
17882
17906
  TabsIconOption,
17883
17907
  TabsOption,
17884
17908
  Tag,
17885
- Text,
17909
+ Text$1 as Text,
17886
17910
  Textarea,
17887
17911
  TimePicker,
17888
17912
  Title,
@@ -29,6 +29,7 @@ const applyCountPlaceholder = (template2, count) => {
29
29
  return template2.replaceAll("{count}", count);
30
30
  };
31
31
  const sanitizeAttr = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;");
32
+ const toReactCamelCaseHandlerName = (eventSuffix) => `on${eventSuffix.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}`;
32
33
  const isValidAction = (value) => {
33
34
  if (typeof value !== "object" || value === null) {
34
35
  return false;
@@ -99,9 +100,9 @@ class FloatingPanel extends NectaryElement {
99
100
  this.#resizeThrottle?.fn();
100
101
  });
101
102
  this.#resizeObserver.observe(this.#$dialog);
102
- this.addEventListener("-close", this.#onCloseReact, { signal });
103
- this.addEventListener("-select-all", this.#onSelectAllReact, { signal });
104
- this.addEventListener("-action", this.#onActionReact, { signal });
103
+ this.addEventListener("-close", this.#onCloseReactHandler, { signal });
104
+ this.addEventListener("-select-all", this.#onSelectAllReactHandler, { signal });
105
+ this.addEventListener("-action", this.#onActionReactHandler, { signal });
105
106
  if (!this.hasAttribute("actions")) {
106
107
  this.#renderActions(null);
107
108
  }
@@ -302,7 +303,7 @@ class FloatingPanel extends NectaryElement {
302
303
  * `-${action}` convenience event. The per-action event lets consumers bind
303
304
  * directly to a specific action (e.g. `on-archive`) without switching on
304
305
  * `e.detail` — and it works for any user-defined action, not just the
305
- * built-in defaults. React handlers are forwarded by `#onActionReact`.
306
+ * built-in defaults. React handlers are forwarded by `#onActionReactHandler`.
306
307
  */
307
308
  #dispatchAction(action) {
308
309
  this.dispatchEvent(new CustomEvent("-action", { detail: action }));
@@ -408,16 +409,20 @@ class FloatingPanel extends NectaryElement {
408
409
  #onActionsPopoverClose = () => {
409
410
  this.#closeOverflowMenu();
410
411
  };
411
- #onCloseReact = (e) => {
412
+ #onCloseReactHandler = (e) => {
412
413
  getReactEventHandler(this, "on-close")?.(e);
414
+ getReactEventHandler(this, "onClose")?.(e);
413
415
  };
414
- #onSelectAllReact = (e) => {
416
+ #onSelectAllReactHandler = (e) => {
415
417
  getReactEventHandler(this, "on-select-all")?.(e);
418
+ getReactEventHandler(this, "onSelectAll")?.(e);
416
419
  };
417
- #onActionReact = (e) => {
420
+ #onActionReactHandler = (e) => {
418
421
  getReactEventHandler(this, "on-action")?.(e);
422
+ getReactEventHandler(this, "onAction")?.(e);
419
423
  if (e instanceof CustomEvent && typeof e.detail === "string") {
420
424
  getReactEventHandler(this, `on-${e.detail}`)?.(e);
425
+ getReactEventHandler(this, toReactCamelCaseHandlerName(e.detail))?.(e);
421
426
  }
422
427
  };
423
428
  get panelRect() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "5.41.0",
3
+ "version": "5.41.2",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",
package/pop/index.js CHANGED
@@ -381,22 +381,8 @@ class Pop extends NectaryElement {
381
381
  const clampedPosition = shouldClamp ? clampPosition({ x: xPos, y: yPos, ...localViewportInfos }) : { x: xPos, y: yPos };
382
382
  const clampedXPos = clampedPosition.x;
383
383
  const clampedYPos = clampedPosition.y;
384
- if (this.hideOutsideViewport) {
385
- const viewportPosition = getAnchorPosition(targetRectViewport, modalWidthViewport, modalHeightViewport, orient);
386
- const visibilityViewportInfos = {
387
- boundsWidth: window.innerWidth,
388
- boundsHeight: window.innerHeight,
389
- insetX: inset,
390
- insetY: inset,
391
- modalWidth: modalWidthViewport,
392
- modalHeight: modalHeightViewport
393
- };
394
- const isOutOfViewport = this.#isOutsideViewport(viewportPosition.x, viewportPosition.y, visibilityViewportInfos);
395
- if (isOutOfViewport) {
396
- this.#$dialog.style.setProperty("visibility", "hidden");
397
- } else {
398
- this.#$dialog.style.removeProperty("visibility");
399
- }
384
+ if (this.hideOutsideViewport && this.#isTargetOutsideViewport(targetRectViewport, inset)) {
385
+ this.#$dialog.style.setProperty("visibility", "hidden");
400
386
  } else {
401
387
  this.#$dialog.style.removeProperty("visibility");
402
388
  }
@@ -481,10 +467,8 @@ class Pop extends NectaryElement {
481
467
  this.#updateOrientation();
482
468
  }
483
469
  };
484
- #isOutsideViewport(x, y, viewportInfos) {
485
- const { boundsWidth, boundsHeight, insetX, insetY, modalWidth, modalHeight } = viewportInfos;
486
- const clampedPosition = clampPosition({ x, y, boundsWidth, boundsHeight, insetX, insetY, modalWidth, modalHeight });
487
- return Math.abs(clampedPosition.x - x) > 2 || Math.abs(clampedPosition.y - y) > 2;
470
+ #isTargetOutsideViewport(targetRect, inset) {
471
+ return targetRect.x + targetRect.width <= inset || targetRect.y + targetRect.height <= inset || targetRect.x >= window.innerWidth - inset || targetRect.y >= window.innerHeight - inset;
488
472
  }
489
473
  }
490
474
  defineCustomElement("sinch-pop", Pop);
package/tooltip/index.js CHANGED
@@ -14,6 +14,7 @@ const SHOW_DELAY_FAST = 250;
14
14
  const HIDE_DELAY = 0;
15
15
  const ANIMATION_DURATION = 100;
16
16
  const FOCUSABLE_OUTSIDE_TARGET_SELECTOR = 'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"]), [contenteditable=""], [contenteditable="true"]';
17
+ const OPEN_FLOATING_LAYER_SELECTOR = "sinch-popover[open], sinch-pop[open], dialog[open]";
17
18
  const OVERLAP_TOLERANCE = 1;
18
19
  const MAX_ZERO_DIMENSION_PLACEMENT_RETRIES = 8;
19
20
  const MIN_FIRST_REVEAL_STABILITY_FRAMES = 3;
@@ -42,13 +43,14 @@ class Tooltip extends NectaryElement {
42
43
  #isSuspendedByHover = false;
43
44
  #isSubscribed = false;
44
45
  #controller;
46
+ #childObserver = null;
45
47
  #placementScheduled = false;
46
48
  #zeroDimensionPlacementRetries = 0;
47
49
  #revealRequestId = 0;
48
50
  #hasCompletedFirstReveal = false;
49
51
  constructor() {
50
52
  super();
51
- const shadowRoot = this.attachShadow();
53
+ const shadowRoot = this.attachShadow({ slotAssignment: "manual" });
52
54
  shadowRoot.appendChild(template.content.cloneNode(true));
53
55
  this.#$pop = shadowRoot.querySelector("#pop");
54
56
  this.#$tooltipText = shadowRoot.querySelector("#text");
@@ -77,6 +79,11 @@ class Tooltip extends NectaryElement {
77
79
  this.setAttribute("role", "tooltip");
78
80
  this.#$pop.addEventListener("-close", this.#onPopClose, options);
79
81
  this.#$target.addEventListener("slotchange", this.#onTargetSlotChange, options);
82
+ this.#assignTargetNodes();
83
+ this.#childObserver = new MutationObserver(() => {
84
+ this.#assignTargetNodes();
85
+ });
86
+ this.#childObserver.observe(this, { childList: true });
80
87
  this.addEventListener("-show", this.#onShowReactHandler, options);
81
88
  this.addEventListener("-hide", this.#onHideReactHandler, options);
82
89
  this.#resizeObserver = new ResizeObserver(() => {
@@ -108,6 +115,8 @@ class Tooltip extends NectaryElement {
108
115
  this.#controller = null;
109
116
  this.#resizeObserver?.disconnect();
110
117
  this.#resizeObserver = null;
118
+ this.#childObserver?.disconnect();
119
+ this.#childObserver = null;
111
120
  }
112
121
  static get observedAttributes() {
113
122
  return [
@@ -224,12 +233,18 @@ class Tooltip extends NectaryElement {
224
233
  this.#tooltipState.destroy();
225
234
  };
226
235
  #onMouseEnter = () => {
236
+ if (this.#targetOwnsOpenFloatingLayer()) {
237
+ return;
238
+ }
227
239
  this.#claimHoverOwnership();
228
240
  this.#suspendFocusedTooltip();
229
241
  this.#closeActiveTooltip();
230
242
  this.#tooltipState.show();
231
243
  };
232
244
  #onContentMouseEnter = () => {
245
+ if (this.#targetOwnsOpenFloatingLayer()) {
246
+ return;
247
+ }
233
248
  this.#claimHoverOwnership();
234
249
  if (this.#hasFocus) {
235
250
  return;
@@ -249,6 +264,9 @@ class Tooltip extends NectaryElement {
249
264
  this.#tooltipState.show();
250
265
  };
251
266
  #shouldOpenForFocus() {
267
+ if (this.#targetOwnsOpenFloatingLayer()) {
268
+ return false;
269
+ }
252
270
  const activeEl = getDeepActiveElement(this.ownerDocument);
253
271
  if (!(activeEl instanceof HTMLElement) || !this.#targetContains(activeEl)) {
254
272
  return false;
@@ -364,6 +382,12 @@ class Tooltip extends NectaryElement {
364
382
  this.#tooltipState.hide();
365
383
  });
366
384
  };
385
+ #assignTargetNodes() {
386
+ const nodes = Array.from(this.childNodes).filter(
387
+ (node) => node instanceof Element || node instanceof Text
388
+ );
389
+ this.#$target.assign(...nodes);
390
+ }
367
391
  #targetContains(node) {
368
392
  return this.#$target.assignedElements().some((el) => composedContains(el, node));
369
393
  }
@@ -379,6 +403,13 @@ class Tooltip extends NectaryElement {
379
403
  #isFocusInFloatingLayer(el) {
380
404
  return el.tagName === "SINCH-POP" || el.tagName === "SINCH-TOOLTIP";
381
405
  }
406
+ // Suppress while a target-owned floating layer (e.g. a sinch-popover triggered by the
407
+ // target) is open, so the tooltip doesn't flicker over it or steal its focus.
408
+ #targetOwnsOpenFloatingLayer() {
409
+ return this.#getTargetElements().some((el) => {
410
+ return el.matches(OPEN_FLOATING_LAYER_SELECTOR) || el.querySelector(OPEN_FLOATING_LAYER_SELECTOR) !== null;
411
+ });
412
+ }
382
413
  #clearFocusSuppression() {
383
414
  this.#suppressFocusIn = false;
384
415
  this.#isSuspendedByHover = false;
@@ -453,6 +484,10 @@ class Tooltip extends NectaryElement {
453
484
  };
454
485
  // SHOW_DELAY ended, tooltip can be shown with animation
455
486
  #onStateShowEnd = () => {
487
+ if (this.#targetOwnsOpenFloatingLayer()) {
488
+ this.#tooltipState.destroy();
489
+ return;
490
+ }
456
491
  const revealRequestId = ++this.#revealRequestId;
457
492
  activeTooltip = this;
458
493
  this.#dispatchShowEvent();