@nectary/components 5.41.0 → 5.41.1

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();
@@ -17882,7 +17901,7 @@ export {
17882
17901
  TabsIconOption,
17883
17902
  TabsOption,
17884
17903
  Tag,
17885
- Text,
17904
+ Text$1 as Text,
17886
17905
  Textarea,
17887
17906
  TimePicker,
17888
17907
  Title,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "5.41.0",
3
+ "version": "5.41.1",
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();