@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 +56 -32
- package/floating-panel/index.js +12 -7
- package/package.json +1 -1
- package/pop/index.js +4 -20
- package/tooltip/index.js +36 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
3955
|
-
|
|
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("&", "&").replaceAll('"', """).replaceAll("<", "<");
|
|
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.#
|
|
9087
|
-
this.addEventListener("-select-all", this.#
|
|
9088
|
-
this.addEventListener("-action", this.#
|
|
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 `#
|
|
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
|
-
#
|
|
9415
|
+
#onCloseReactHandler = (e) => {
|
|
9396
9416
|
getReactEventHandler(this, "on-close")?.(e);
|
|
9417
|
+
getReactEventHandler(this, "onClose")?.(e);
|
|
9397
9418
|
};
|
|
9398
|
-
#
|
|
9419
|
+
#onSelectAllReactHandler = (e) => {
|
|
9399
9420
|
getReactEventHandler(this, "on-select-all")?.(e);
|
|
9421
|
+
getReactEventHandler(this, "onSelectAll")?.(e);
|
|
9400
9422
|
};
|
|
9401
|
-
#
|
|
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,
|
package/floating-panel/index.js
CHANGED
|
@@ -29,6 +29,7 @@ const applyCountPlaceholder = (template2, count) => {
|
|
|
29
29
|
return template2.replaceAll("{count}", count);
|
|
30
30
|
};
|
|
31
31
|
const sanitizeAttr = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<");
|
|
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.#
|
|
103
|
-
this.addEventListener("-select-all", this.#
|
|
104
|
-
this.addEventListener("-action", this.#
|
|
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 `#
|
|
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
|
-
#
|
|
412
|
+
#onCloseReactHandler = (e) => {
|
|
412
413
|
getReactEventHandler(this, "on-close")?.(e);
|
|
414
|
+
getReactEventHandler(this, "onClose")?.(e);
|
|
413
415
|
};
|
|
414
|
-
#
|
|
416
|
+
#onSelectAllReactHandler = (e) => {
|
|
415
417
|
getReactEventHandler(this, "on-select-all")?.(e);
|
|
418
|
+
getReactEventHandler(this, "onSelectAll")?.(e);
|
|
416
419
|
};
|
|
417
|
-
#
|
|
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
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
|
-
|
|
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
|
-
#
|
|
485
|
-
|
|
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();
|