@nectary/components 5.31.0 → 5.31.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 +301 -13
- package/package.json +1 -1
- package/pop/index.d.ts +1 -0
- package/pop/index.js +5 -2
- package/pop/types.d.ts +2 -0
- package/tooltip/index.js +293 -11
- package/tooltip/tooltip-state.d.ts +1 -0
- package/tooltip/tooltip-state.js +3 -0
package/bundle.js
CHANGED
|
@@ -3466,6 +3466,9 @@ class Pop extends NectaryElement {
|
|
|
3466
3466
|
get shouldCloseOnBackdropClick() {
|
|
3467
3467
|
return !getBooleanAttribute(this, "disable-backdrop-close");
|
|
3468
3468
|
}
|
|
3469
|
+
get shouldRestoreFocusOnClose() {
|
|
3470
|
+
return !getBooleanAttribute(this, "disable-focus-restore");
|
|
3471
|
+
}
|
|
3469
3472
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
3470
3473
|
if (isAttrEqual(oldVal, newVal)) {
|
|
3471
3474
|
return;
|
|
@@ -3651,7 +3654,7 @@ class Pop extends NectaryElement {
|
|
|
3651
3654
|
if (isNonModal && !effectiveAllowScroll) {
|
|
3652
3655
|
this.#restoreTransferredTarget();
|
|
3653
3656
|
}
|
|
3654
|
-
if (this.#targetActiveElement !== null) {
|
|
3657
|
+
if (this.shouldRestoreFocusOnClose && this.#targetActiveElement !== null) {
|
|
3655
3658
|
if (!isElementFocused(this.#targetActiveElement)) {
|
|
3656
3659
|
this.#$targetSlot.addEventListener("focus", this.#stopEventPropagation, true);
|
|
3657
3660
|
this.#targetActiveElement.focus({ preventScroll: true });
|
|
@@ -3666,9 +3669,9 @@ class Pop extends NectaryElement {
|
|
|
3666
3669
|
}
|
|
3667
3670
|
});
|
|
3668
3671
|
}
|
|
3669
|
-
this.#targetActiveElement = null;
|
|
3670
3672
|
}
|
|
3671
3673
|
}
|
|
3674
|
+
this.#targetActiveElement = null;
|
|
3672
3675
|
if (!effectiveAllowScroll) {
|
|
3673
3676
|
enableOverscroll();
|
|
3674
3677
|
} else {
|
|
@@ -3827,6 +3830,9 @@ class TooltipState {
|
|
|
3827
3830
|
#timerId = null;
|
|
3828
3831
|
#state = "hide";
|
|
3829
3832
|
#options;
|
|
3833
|
+
get isInHideState() {
|
|
3834
|
+
return this.#state === "hide";
|
|
3835
|
+
}
|
|
3830
3836
|
constructor(options) {
|
|
3831
3837
|
this.#options = options;
|
|
3832
3838
|
}
|
|
@@ -3983,10 +3989,15 @@ const SHOW_DELAY_SLOW = 1e3;
|
|
|
3983
3989
|
const SHOW_DELAY_FAST = 250;
|
|
3984
3990
|
const HIDE_DELAY = 0;
|
|
3985
3991
|
const ANIMATION_DURATION = 100;
|
|
3992
|
+
const FOCUSABLE_OUTSIDE_TARGET_SELECTOR = 'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"]), [contenteditable=""], [contenteditable="true"]';
|
|
3986
3993
|
const OVERLAP_TOLERANCE = 1;
|
|
3987
3994
|
const MAX_ZERO_DIMENSION_PLACEMENT_RETRIES = 8;
|
|
3988
3995
|
const MIN_FIRST_REVEAL_STABILITY_FRAMES = 3;
|
|
3989
3996
|
const MAX_FIRST_REVEAL_STABILITY_FRAMES = 6;
|
|
3997
|
+
let activeTooltip = null;
|
|
3998
|
+
let focusedTooltip = null;
|
|
3999
|
+
let hoveredTooltip = null;
|
|
4000
|
+
let suspendedFocusedTooltip = null;
|
|
3990
4001
|
const template$T = document.createElement("template");
|
|
3991
4002
|
template$T.innerHTML = templateHTML$T;
|
|
3992
4003
|
class Tooltip extends NectaryElement {
|
|
@@ -3996,10 +4007,15 @@ class Tooltip extends NectaryElement {
|
|
|
3996
4007
|
#$contentWrapper;
|
|
3997
4008
|
#$tip;
|
|
3998
4009
|
#$target;
|
|
4010
|
+
#targetElements = [];
|
|
3999
4011
|
#resizeObserver = null;
|
|
4000
4012
|
#tooltipState;
|
|
4001
4013
|
#animation = null;
|
|
4002
4014
|
#shouldReduceMotion = false;
|
|
4015
|
+
#hasFocus = false;
|
|
4016
|
+
#suppressFocusIn = false;
|
|
4017
|
+
#suppressedFocusOutArmed = false;
|
|
4018
|
+
#isSuspendedByHover = false;
|
|
4003
4019
|
#isSubscribed = false;
|
|
4004
4020
|
#controller;
|
|
4005
4021
|
#placementScheduled = false;
|
|
@@ -4036,6 +4052,7 @@ class Tooltip extends NectaryElement {
|
|
|
4036
4052
|
};
|
|
4037
4053
|
this.setAttribute("role", "tooltip");
|
|
4038
4054
|
this.#$pop.addEventListener("-close", this.#onPopClose, options);
|
|
4055
|
+
this.#$target.addEventListener("slotchange", this.#onTargetSlotChange, options);
|
|
4039
4056
|
this.addEventListener("-show", this.#onShowReactHandler, options);
|
|
4040
4057
|
this.addEventListener("-hide", this.#onHideReactHandler, options);
|
|
4041
4058
|
this.#resizeObserver = new ResizeObserver(() => {
|
|
@@ -4044,10 +4061,23 @@ class Tooltip extends NectaryElement {
|
|
|
4044
4061
|
this.#resizeObserver.observe(this.#$content);
|
|
4045
4062
|
updateAttribute(this.#$pop, "orientation", getPopOrientation$1(this.orientation));
|
|
4046
4063
|
updateBooleanAttribute(this.#$pop, "hide-outside-viewport", !this.showOutsideViewport);
|
|
4064
|
+
updateBooleanAttribute(this.#$pop, "disable-focus-restore", true);
|
|
4047
4065
|
this.#updateText();
|
|
4048
4066
|
}
|
|
4049
4067
|
disconnectedCallback() {
|
|
4050
4068
|
super.disconnectedCallback();
|
|
4069
|
+
if (activeTooltip === this) {
|
|
4070
|
+
activeTooltip = null;
|
|
4071
|
+
}
|
|
4072
|
+
if (focusedTooltip === this) {
|
|
4073
|
+
focusedTooltip = null;
|
|
4074
|
+
}
|
|
4075
|
+
if (hoveredTooltip === this) {
|
|
4076
|
+
hoveredTooltip = null;
|
|
4077
|
+
}
|
|
4078
|
+
if (suspendedFocusedTooltip === this) {
|
|
4079
|
+
suspendedFocusedTooltip = null;
|
|
4080
|
+
}
|
|
4051
4081
|
this.#tooltipState.destroy();
|
|
4052
4082
|
this.#controller.abort();
|
|
4053
4083
|
this.#controller = null;
|
|
@@ -4165,14 +4195,217 @@ class Tooltip extends NectaryElement {
|
|
|
4165
4195
|
this.#tooltipState.destroy();
|
|
4166
4196
|
};
|
|
4167
4197
|
#onMouseEnter = () => {
|
|
4198
|
+
this.#claimHoverOwnership();
|
|
4199
|
+
this.#suspendFocusedTooltip();
|
|
4200
|
+
this.#closeActiveTooltip();
|
|
4201
|
+
this.#tooltipState.show();
|
|
4202
|
+
};
|
|
4203
|
+
#onContentMouseEnter = () => {
|
|
4204
|
+
this.#claimHoverOwnership();
|
|
4205
|
+
if (this.#hasFocus) {
|
|
4206
|
+
return;
|
|
4207
|
+
}
|
|
4208
|
+
this.#suspendFocusedTooltip();
|
|
4209
|
+
this.#closeActiveTooltip();
|
|
4210
|
+
this.#tooltipState.show();
|
|
4211
|
+
};
|
|
4212
|
+
#onFocusIn = () => {
|
|
4213
|
+
if (this.#suppressFocusIn) {
|
|
4214
|
+
return;
|
|
4215
|
+
}
|
|
4216
|
+
this.#closeFocusedTooltip();
|
|
4217
|
+
this.#closeActiveTooltip();
|
|
4218
|
+
this.#hasFocus = true;
|
|
4219
|
+
focusedTooltip = this;
|
|
4168
4220
|
this.#tooltipState.show();
|
|
4169
4221
|
};
|
|
4170
|
-
#
|
|
4171
|
-
if (!this.#
|
|
4222
|
+
#closeFocusedTooltip() {
|
|
4223
|
+
if (!this.#isOtherUncontrolledTooltip(focusedTooltip)) {
|
|
4224
|
+
return;
|
|
4225
|
+
}
|
|
4226
|
+
const previousFocusedTooltip = focusedTooltip;
|
|
4227
|
+
previousFocusedTooltip.#clearTrackedFocus();
|
|
4228
|
+
previousFocusedTooltip.#tooltipState.destroy();
|
|
4229
|
+
}
|
|
4230
|
+
#closeActiveTooltip() {
|
|
4231
|
+
if (this.#isOtherUncontrolledTooltip(activeTooltip) && !activeTooltip.#hasFocus) {
|
|
4232
|
+
activeTooltip.#tooltipState.destroy();
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
#suspendFocusedTooltip() {
|
|
4236
|
+
if (this.#isOtherUncontrolledTooltip(focusedTooltip)) {
|
|
4237
|
+
suspendedFocusedTooltip = focusedTooltip;
|
|
4238
|
+
focusedTooltip.#suppressFocusIn = true;
|
|
4239
|
+
focusedTooltip.#isSuspendedByHover = true;
|
|
4240
|
+
focusedTooltip.#armSuppressedFocusOut();
|
|
4241
|
+
if (!focusedTooltip.#tooltipState.isInHideState) {
|
|
4242
|
+
focusedTooltip.#tooltipState.destroy();
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
#resumeFocusedTooltip() {
|
|
4247
|
+
const tooltipToResume = suspendedFocusedTooltip;
|
|
4248
|
+
if (tooltipToResume === null || tooltipToResume === this) {
|
|
4249
|
+
return;
|
|
4250
|
+
}
|
|
4251
|
+
if (hoveredTooltip !== null) {
|
|
4252
|
+
if (hoveredTooltip === this && !this.#isPointerWithinHoverSurface()) {
|
|
4253
|
+
this.#releaseHoverOwnership();
|
|
4254
|
+
} else {
|
|
4255
|
+
return;
|
|
4256
|
+
}
|
|
4257
|
+
}
|
|
4258
|
+
if (!tooltipToResume.#isSuspendedByHover) {
|
|
4259
|
+
if (suspendedFocusedTooltip === tooltipToResume) {
|
|
4260
|
+
suspendedFocusedTooltip = null;
|
|
4261
|
+
}
|
|
4262
|
+
return;
|
|
4263
|
+
}
|
|
4264
|
+
const activeEl = document.activeElement;
|
|
4265
|
+
if (activeEl instanceof HTMLElement && tooltipToResume.#isFocusableOutsideTarget(activeEl)) {
|
|
4266
|
+
tooltipToResume.#clearTrackedFocus();
|
|
4267
|
+
return;
|
|
4268
|
+
}
|
|
4269
|
+
tooltipToResume.#clearFocusSuppression();
|
|
4270
|
+
tooltipToResume.#hasFocus = true;
|
|
4271
|
+
focusedTooltip = tooltipToResume;
|
|
4272
|
+
suspendedFocusedTooltip = null;
|
|
4273
|
+
tooltipToResume.#tooltipState.show();
|
|
4274
|
+
}
|
|
4275
|
+
#isOtherUncontrolledTooltip(tooltip) {
|
|
4276
|
+
return tooltip !== null && tooltip !== this && tooltip.isOpenedControlled !== true;
|
|
4277
|
+
}
|
|
4278
|
+
#claimHoverOwnership() {
|
|
4279
|
+
hoveredTooltip = this;
|
|
4280
|
+
}
|
|
4281
|
+
#releaseHoverOwnership() {
|
|
4282
|
+
if (hoveredTooltip === this) {
|
|
4283
|
+
hoveredTooltip = null;
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
#isPointerWithinHoverSurface() {
|
|
4287
|
+
return this.#$contentWrapper.matches(":hover") || this.#$target.assignedElements().some((el) => el.matches(":hover"));
|
|
4288
|
+
}
|
|
4289
|
+
#finalizeFocusOut() {
|
|
4290
|
+
if (!this.isDomConnected) {
|
|
4291
|
+
return;
|
|
4292
|
+
}
|
|
4293
|
+
const activeEl = document.activeElement;
|
|
4294
|
+
if (activeEl instanceof Node) {
|
|
4295
|
+
if (this.#targetContains(activeEl) || this.shadowRoot?.contains(activeEl) === true) {
|
|
4296
|
+
return;
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
this.#clearTrackedFocus();
|
|
4300
|
+
this.#tooltipState.hide();
|
|
4301
|
+
}
|
|
4302
|
+
#onFocusOut = (e) => {
|
|
4303
|
+
if (this.#isSuspendedByHover) {
|
|
4304
|
+
return;
|
|
4305
|
+
}
|
|
4306
|
+
if (e.relatedTarget instanceof Node) {
|
|
4307
|
+
if (this.#targetContains(e.relatedTarget) || this.shadowRoot?.contains(e.relatedTarget) === true) {
|
|
4308
|
+
return;
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
requestAnimationFrame(() => {
|
|
4312
|
+
this.#finalizeFocusOut();
|
|
4313
|
+
});
|
|
4314
|
+
};
|
|
4315
|
+
#onMouseLeave = () => {
|
|
4316
|
+
requestAnimationFrame(() => {
|
|
4317
|
+
if (!this.isDomConnected) {
|
|
4318
|
+
return;
|
|
4319
|
+
}
|
|
4320
|
+
if (this.#isPointerWithinHoverSurface()) {
|
|
4321
|
+
this.#claimHoverOwnership();
|
|
4322
|
+
return;
|
|
4323
|
+
}
|
|
4324
|
+
this.#releaseHoverOwnership();
|
|
4325
|
+
if (this.#hasFocus) {
|
|
4326
|
+
return;
|
|
4327
|
+
}
|
|
4172
4328
|
this.#tooltipState.hide();
|
|
4329
|
+
});
|
|
4330
|
+
};
|
|
4331
|
+
#targetContains(node) {
|
|
4332
|
+
return this.#$target.assignedElements().some((el) => el.contains(node));
|
|
4333
|
+
}
|
|
4334
|
+
#getTargetElements() {
|
|
4335
|
+
return this.#$target.assignedElements().filter((el) => el instanceof HTMLElement);
|
|
4336
|
+
}
|
|
4337
|
+
#isFocusableOutsideTarget(el) {
|
|
4338
|
+
if (this.#targetContains(el) || this.shadowRoot?.contains(el) === true || this.#isFocusInFloatingLayer(el)) {
|
|
4339
|
+
return false;
|
|
4340
|
+
}
|
|
4341
|
+
return el.matches(FOCUSABLE_OUTSIDE_TARGET_SELECTOR);
|
|
4342
|
+
}
|
|
4343
|
+
#isFocusInFloatingLayer(el) {
|
|
4344
|
+
return el.tagName === "SINCH-POP" || el.tagName === "SINCH-TOOLTIP";
|
|
4345
|
+
}
|
|
4346
|
+
#clearFocusSuppression() {
|
|
4347
|
+
this.#suppressFocusIn = false;
|
|
4348
|
+
this.#isSuspendedByHover = false;
|
|
4349
|
+
if (this.#suppressedFocusOutArmed) {
|
|
4350
|
+
this.#$target.removeEventListener("focusout", this.#onSuppressedFocusOut);
|
|
4351
|
+
this.#suppressedFocusOutArmed = false;
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
#clearTrackedFocus() {
|
|
4355
|
+
this.#hasFocus = false;
|
|
4356
|
+
if (focusedTooltip === this) {
|
|
4357
|
+
focusedTooltip = null;
|
|
4358
|
+
}
|
|
4359
|
+
if (suspendedFocusedTooltip === this) {
|
|
4360
|
+
suspendedFocusedTooltip = null;
|
|
4361
|
+
}
|
|
4362
|
+
this.#clearFocusSuppression();
|
|
4363
|
+
}
|
|
4364
|
+
#armSuppressedFocusOut() {
|
|
4365
|
+
if (this.#suppressedFocusOutArmed || this.#controller === null) {
|
|
4366
|
+
return;
|
|
4173
4367
|
}
|
|
4368
|
+
this.#suppressedFocusOutArmed = true;
|
|
4369
|
+
const options = { signal: this.#controller.signal };
|
|
4370
|
+
this.#$target.addEventListener("focusout", this.#onSuppressedFocusOut, options);
|
|
4371
|
+
}
|
|
4372
|
+
#onSuppressedFocusOut = (e) => {
|
|
4373
|
+
if (e.relatedTarget instanceof Node) {
|
|
4374
|
+
if (this.#targetContains(e.relatedTarget) || this.shadowRoot?.contains(e.relatedTarget) === true) {
|
|
4375
|
+
return;
|
|
4376
|
+
}
|
|
4377
|
+
}
|
|
4378
|
+
requestAnimationFrame(() => {
|
|
4379
|
+
if (!this.isDomConnected) {
|
|
4380
|
+
return;
|
|
4381
|
+
}
|
|
4382
|
+
if (this.#targetContainsFocus()) {
|
|
4383
|
+
return;
|
|
4384
|
+
}
|
|
4385
|
+
if (this.#isSuspendedByHover) {
|
|
4386
|
+
if (e.relatedTarget instanceof HTMLElement && this.#isFocusInFloatingLayer(e.relatedTarget)) {
|
|
4387
|
+
return;
|
|
4388
|
+
}
|
|
4389
|
+
const activeEl2 = document.activeElement;
|
|
4390
|
+
if (activeEl2 instanceof HTMLElement && this.#isFocusInFloatingLayer(activeEl2)) {
|
|
4391
|
+
return;
|
|
4392
|
+
}
|
|
4393
|
+
if (activeEl2 instanceof HTMLElement && this.#isFocusableOutsideTarget(activeEl2)) {
|
|
4394
|
+
this.#clearTrackedFocus();
|
|
4395
|
+
}
|
|
4396
|
+
return;
|
|
4397
|
+
}
|
|
4398
|
+
const activeEl = document.activeElement;
|
|
4399
|
+
if (activeEl instanceof HTMLElement && this.#isFocusInFloatingLayer(activeEl)) {
|
|
4400
|
+
return;
|
|
4401
|
+
}
|
|
4402
|
+
this.#clearTrackedFocus();
|
|
4403
|
+
});
|
|
4174
4404
|
};
|
|
4175
4405
|
#onScroll = () => {
|
|
4406
|
+
if (this.#hasFocus) {
|
|
4407
|
+
return;
|
|
4408
|
+
}
|
|
4176
4409
|
this.#tooltipState.destroy();
|
|
4177
4410
|
};
|
|
4178
4411
|
// Tooltip begins to wait for SHOW_DELAY on mouseenter
|
|
@@ -4185,6 +4418,7 @@ class Tooltip extends NectaryElement {
|
|
|
4185
4418
|
// SHOW_DELAY ended, tooltip can be shown with animation
|
|
4186
4419
|
#onStateShowEnd = () => {
|
|
4187
4420
|
const revealRequestId = ++this.#revealRequestId;
|
|
4421
|
+
activeTooltip = this;
|
|
4188
4422
|
this.#dispatchShowEvent();
|
|
4189
4423
|
updateBooleanAttribute(this.#$pop, "open", true);
|
|
4190
4424
|
this.#schedulePlacement();
|
|
@@ -4258,12 +4492,30 @@ class Tooltip extends NectaryElement {
|
|
|
4258
4492
|
this.#dispatchHideEvent();
|
|
4259
4493
|
updateBooleanAttribute(this.#$pop, "open", false);
|
|
4260
4494
|
}
|
|
4495
|
+
if (activeTooltip === this) {
|
|
4496
|
+
activeTooltip = null;
|
|
4497
|
+
}
|
|
4498
|
+
if (!this.#isSuspendedByHover) {
|
|
4499
|
+
if (!this.#targetContainsFocus()) {
|
|
4500
|
+
this.#clearTrackedFocus();
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4261
4503
|
this.#resetTipOrientation();
|
|
4262
4504
|
this.#resetContentOffset();
|
|
4263
4505
|
this.#zeroDimensionPlacementRetries = 0;
|
|
4264
4506
|
this.#unsubscribeMouseLeaveEvents();
|
|
4265
4507
|
this.#unsubscribeScroll();
|
|
4508
|
+
if (!this.#isPointerWithinHoverSurface()) {
|
|
4509
|
+
this.#releaseHoverOwnership();
|
|
4510
|
+
}
|
|
4511
|
+
if (!this.#hasFocus) {
|
|
4512
|
+
this.#resumeFocusedTooltip();
|
|
4513
|
+
}
|
|
4266
4514
|
};
|
|
4515
|
+
#targetContainsFocus() {
|
|
4516
|
+
const activeEl = document.activeElement;
|
|
4517
|
+
return activeEl instanceof Node && this.#targetContains(activeEl);
|
|
4518
|
+
}
|
|
4267
4519
|
#resetTipOrientation() {
|
|
4268
4520
|
this.#$tip.style.top = "";
|
|
4269
4521
|
this.#$tip.style.left = "";
|
|
@@ -4410,28 +4662,64 @@ class Tooltip extends NectaryElement {
|
|
|
4410
4662
|
if (!this.isDomConnected || this.#isSubscribed) {
|
|
4411
4663
|
return;
|
|
4412
4664
|
}
|
|
4413
|
-
|
|
4414
|
-
|
|
4665
|
+
const options = { signal: this.#controller.signal };
|
|
4666
|
+
this.#targetElements = this.#getTargetElements();
|
|
4667
|
+
if (this.#targetElements.length === 0) {
|
|
4668
|
+
requestAnimationFrame(() => {
|
|
4669
|
+
if (!this.isDomConnected || this.#isSubscribed || this.text.length === 0) {
|
|
4670
|
+
return;
|
|
4671
|
+
}
|
|
4672
|
+
this.#subscribeMouseEnterEvent();
|
|
4673
|
+
});
|
|
4674
|
+
return;
|
|
4675
|
+
}
|
|
4676
|
+
this.#targetElements.forEach((el) => {
|
|
4677
|
+
el.addEventListener("mouseenter", this.#onMouseEnter, options);
|
|
4415
4678
|
});
|
|
4679
|
+
this.#$target.addEventListener("focusin", this.#onFocusIn, options);
|
|
4416
4680
|
this.#isSubscribed = true;
|
|
4417
4681
|
}
|
|
4418
4682
|
#unsubscribeMouseEnterEvent() {
|
|
4419
|
-
this
|
|
4683
|
+
this.#targetElements.forEach((el) => {
|
|
4684
|
+
el.removeEventListener("mouseenter", this.#onMouseEnter);
|
|
4685
|
+
});
|
|
4686
|
+
this.#$target.removeEventListener("focusin", this.#onFocusIn);
|
|
4687
|
+
this.#targetElements = [];
|
|
4420
4688
|
this.#isSubscribed = false;
|
|
4421
4689
|
}
|
|
4422
4690
|
#subscribeMouseLeaveEvents() {
|
|
4423
4691
|
const options = { signal: this.#controller.signal };
|
|
4424
|
-
this
|
|
4425
|
-
|
|
4426
|
-
|
|
4692
|
+
this.#targetElements.forEach((el) => {
|
|
4693
|
+
el.addEventListener("mousedown", this.#onMouseDown, options);
|
|
4694
|
+
el.addEventListener("mouseleave", this.#onMouseLeave, options);
|
|
4695
|
+
});
|
|
4696
|
+
this.#$target.addEventListener("focusout", this.#onFocusOut, options);
|
|
4697
|
+
this.#$contentWrapper.addEventListener("mouseenter", this.#onContentMouseEnter, options);
|
|
4427
4698
|
this.#$contentWrapper.addEventListener("mouseleave", this.#onMouseLeave, options);
|
|
4428
4699
|
}
|
|
4429
4700
|
#unsubscribeMouseLeaveEvents() {
|
|
4430
|
-
this
|
|
4431
|
-
|
|
4432
|
-
|
|
4701
|
+
this.#targetElements.forEach((el) => {
|
|
4702
|
+
el.removeEventListener("mousedown", this.#onMouseDown);
|
|
4703
|
+
el.removeEventListener("mouseleave", this.#onMouseLeave);
|
|
4704
|
+
});
|
|
4705
|
+
this.#$target.removeEventListener("focusout", this.#onFocusOut);
|
|
4706
|
+
this.#$contentWrapper.removeEventListener("mouseenter", this.#onContentMouseEnter);
|
|
4433
4707
|
this.#$contentWrapper.removeEventListener("mouseleave", this.#onMouseLeave);
|
|
4434
4708
|
}
|
|
4709
|
+
#onTargetSlotChange = () => {
|
|
4710
|
+
if (!this.isDomConnected || this.text.length === 0) {
|
|
4711
|
+
return;
|
|
4712
|
+
}
|
|
4713
|
+
const isOpen = this.#isOpen();
|
|
4714
|
+
if (this.#isSubscribed) {
|
|
4715
|
+
this.#unsubscribeMouseLeaveEvents();
|
|
4716
|
+
this.#unsubscribeMouseEnterEvent();
|
|
4717
|
+
}
|
|
4718
|
+
this.#subscribeMouseEnterEvent();
|
|
4719
|
+
if (isOpen) {
|
|
4720
|
+
this.#subscribeMouseLeaveEvents();
|
|
4721
|
+
}
|
|
4722
|
+
};
|
|
4435
4723
|
#subscribeScroll() {
|
|
4436
4724
|
window.addEventListener("wheel", this.#onScroll, true);
|
|
4437
4725
|
}
|
package/package.json
CHANGED
package/pop/index.d.ts
CHANGED
|
@@ -27,5 +27,6 @@ export declare class Pop extends NectaryElement {
|
|
|
27
27
|
get footprintRect(): TRect;
|
|
28
28
|
get popoverRect(): TRect;
|
|
29
29
|
get shouldCloseOnBackdropClick(): boolean;
|
|
30
|
+
get shouldRestoreFocusOnClose(): boolean;
|
|
30
31
|
attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
|
|
31
32
|
}
|
package/pop/index.js
CHANGED
|
@@ -128,6 +128,9 @@ class Pop extends NectaryElement {
|
|
|
128
128
|
get shouldCloseOnBackdropClick() {
|
|
129
129
|
return !getBooleanAttribute(this, "disable-backdrop-close");
|
|
130
130
|
}
|
|
131
|
+
get shouldRestoreFocusOnClose() {
|
|
132
|
+
return !getBooleanAttribute(this, "disable-focus-restore");
|
|
133
|
+
}
|
|
131
134
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
132
135
|
if (isAttrEqual(oldVal, newVal)) {
|
|
133
136
|
return;
|
|
@@ -313,7 +316,7 @@ class Pop extends NectaryElement {
|
|
|
313
316
|
if (isNonModal && !effectiveAllowScroll) {
|
|
314
317
|
this.#restoreTransferredTarget();
|
|
315
318
|
}
|
|
316
|
-
if (this.#targetActiveElement !== null) {
|
|
319
|
+
if (this.shouldRestoreFocusOnClose && this.#targetActiveElement !== null) {
|
|
317
320
|
if (!isElementFocused(this.#targetActiveElement)) {
|
|
318
321
|
this.#$targetSlot.addEventListener("focus", this.#stopEventPropagation, true);
|
|
319
322
|
this.#targetActiveElement.focus({ preventScroll: true });
|
|
@@ -328,9 +331,9 @@ class Pop extends NectaryElement {
|
|
|
328
331
|
}
|
|
329
332
|
});
|
|
330
333
|
}
|
|
331
|
-
this.#targetActiveElement = null;
|
|
332
334
|
}
|
|
333
335
|
}
|
|
336
|
+
this.#targetActiveElement = null;
|
|
334
337
|
if (!effectiveAllowScroll) {
|
|
335
338
|
enableOverscroll();
|
|
336
339
|
} else {
|
package/pop/types.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export type TSinchPopOrientation = 'top-left' | 'top-right' | 'bottom-left' | 'b
|
|
|
3
3
|
export type TSinchPopProps = {
|
|
4
4
|
/** Allow scrolling of the page when pop is open */
|
|
5
5
|
'allow-scroll'?: boolean;
|
|
6
|
+
/** Skip restoring the previously focused target when the pop closes */
|
|
7
|
+
'disable-focus-restore'?: boolean;
|
|
6
8
|
/** Open/close state */
|
|
7
9
|
open: boolean;
|
|
8
10
|
/** Orientation, where it *points to* from origin */
|
package/tooltip/index.js
CHANGED
|
@@ -13,10 +13,15 @@ const SHOW_DELAY_SLOW = 1e3;
|
|
|
13
13
|
const SHOW_DELAY_FAST = 250;
|
|
14
14
|
const HIDE_DELAY = 0;
|
|
15
15
|
const ANIMATION_DURATION = 100;
|
|
16
|
+
const FOCUSABLE_OUTSIDE_TARGET_SELECTOR = 'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"]), [contenteditable=""], [contenteditable="true"]';
|
|
16
17
|
const OVERLAP_TOLERANCE = 1;
|
|
17
18
|
const MAX_ZERO_DIMENSION_PLACEMENT_RETRIES = 8;
|
|
18
19
|
const MIN_FIRST_REVEAL_STABILITY_FRAMES = 3;
|
|
19
20
|
const MAX_FIRST_REVEAL_STABILITY_FRAMES = 6;
|
|
21
|
+
let activeTooltip = null;
|
|
22
|
+
let focusedTooltip = null;
|
|
23
|
+
let hoveredTooltip = null;
|
|
24
|
+
let suspendedFocusedTooltip = null;
|
|
20
25
|
const template = document.createElement("template");
|
|
21
26
|
template.innerHTML = templateHTML;
|
|
22
27
|
class Tooltip extends NectaryElement {
|
|
@@ -26,10 +31,15 @@ class Tooltip extends NectaryElement {
|
|
|
26
31
|
#$contentWrapper;
|
|
27
32
|
#$tip;
|
|
28
33
|
#$target;
|
|
34
|
+
#targetElements = [];
|
|
29
35
|
#resizeObserver = null;
|
|
30
36
|
#tooltipState;
|
|
31
37
|
#animation = null;
|
|
32
38
|
#shouldReduceMotion = false;
|
|
39
|
+
#hasFocus = false;
|
|
40
|
+
#suppressFocusIn = false;
|
|
41
|
+
#suppressedFocusOutArmed = false;
|
|
42
|
+
#isSuspendedByHover = false;
|
|
33
43
|
#isSubscribed = false;
|
|
34
44
|
#controller;
|
|
35
45
|
#placementScheduled = false;
|
|
@@ -66,6 +76,7 @@ class Tooltip extends NectaryElement {
|
|
|
66
76
|
};
|
|
67
77
|
this.setAttribute("role", "tooltip");
|
|
68
78
|
this.#$pop.addEventListener("-close", this.#onPopClose, options);
|
|
79
|
+
this.#$target.addEventListener("slotchange", this.#onTargetSlotChange, options);
|
|
69
80
|
this.addEventListener("-show", this.#onShowReactHandler, options);
|
|
70
81
|
this.addEventListener("-hide", this.#onHideReactHandler, options);
|
|
71
82
|
this.#resizeObserver = new ResizeObserver(() => {
|
|
@@ -74,10 +85,23 @@ class Tooltip extends NectaryElement {
|
|
|
74
85
|
this.#resizeObserver.observe(this.#$content);
|
|
75
86
|
updateAttribute(this.#$pop, "orientation", getPopOrientation(this.orientation));
|
|
76
87
|
updateBooleanAttribute(this.#$pop, "hide-outside-viewport", !this.showOutsideViewport);
|
|
88
|
+
updateBooleanAttribute(this.#$pop, "disable-focus-restore", true);
|
|
77
89
|
this.#updateText();
|
|
78
90
|
}
|
|
79
91
|
disconnectedCallback() {
|
|
80
92
|
super.disconnectedCallback();
|
|
93
|
+
if (activeTooltip === this) {
|
|
94
|
+
activeTooltip = null;
|
|
95
|
+
}
|
|
96
|
+
if (focusedTooltip === this) {
|
|
97
|
+
focusedTooltip = null;
|
|
98
|
+
}
|
|
99
|
+
if (hoveredTooltip === this) {
|
|
100
|
+
hoveredTooltip = null;
|
|
101
|
+
}
|
|
102
|
+
if (suspendedFocusedTooltip === this) {
|
|
103
|
+
suspendedFocusedTooltip = null;
|
|
104
|
+
}
|
|
81
105
|
this.#tooltipState.destroy();
|
|
82
106
|
this.#controller.abort();
|
|
83
107
|
this.#controller = null;
|
|
@@ -195,14 +219,217 @@ class Tooltip extends NectaryElement {
|
|
|
195
219
|
this.#tooltipState.destroy();
|
|
196
220
|
};
|
|
197
221
|
#onMouseEnter = () => {
|
|
222
|
+
this.#claimHoverOwnership();
|
|
223
|
+
this.#suspendFocusedTooltip();
|
|
224
|
+
this.#closeActiveTooltip();
|
|
225
|
+
this.#tooltipState.show();
|
|
226
|
+
};
|
|
227
|
+
#onContentMouseEnter = () => {
|
|
228
|
+
this.#claimHoverOwnership();
|
|
229
|
+
if (this.#hasFocus) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
this.#suspendFocusedTooltip();
|
|
233
|
+
this.#closeActiveTooltip();
|
|
198
234
|
this.#tooltipState.show();
|
|
199
235
|
};
|
|
200
|
-
#
|
|
201
|
-
if (
|
|
236
|
+
#onFocusIn = () => {
|
|
237
|
+
if (this.#suppressFocusIn) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
this.#closeFocusedTooltip();
|
|
241
|
+
this.#closeActiveTooltip();
|
|
242
|
+
this.#hasFocus = true;
|
|
243
|
+
focusedTooltip = this;
|
|
244
|
+
this.#tooltipState.show();
|
|
245
|
+
};
|
|
246
|
+
#closeFocusedTooltip() {
|
|
247
|
+
if (!this.#isOtherUncontrolledTooltip(focusedTooltip)) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const previousFocusedTooltip = focusedTooltip;
|
|
251
|
+
previousFocusedTooltip.#clearTrackedFocus();
|
|
252
|
+
previousFocusedTooltip.#tooltipState.destroy();
|
|
253
|
+
}
|
|
254
|
+
#closeActiveTooltip() {
|
|
255
|
+
if (this.#isOtherUncontrolledTooltip(activeTooltip) && !activeTooltip.#hasFocus) {
|
|
256
|
+
activeTooltip.#tooltipState.destroy();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
#suspendFocusedTooltip() {
|
|
260
|
+
if (this.#isOtherUncontrolledTooltip(focusedTooltip)) {
|
|
261
|
+
suspendedFocusedTooltip = focusedTooltip;
|
|
262
|
+
focusedTooltip.#suppressFocusIn = true;
|
|
263
|
+
focusedTooltip.#isSuspendedByHover = true;
|
|
264
|
+
focusedTooltip.#armSuppressedFocusOut();
|
|
265
|
+
if (!focusedTooltip.#tooltipState.isInHideState) {
|
|
266
|
+
focusedTooltip.#tooltipState.destroy();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
#resumeFocusedTooltip() {
|
|
271
|
+
const tooltipToResume = suspendedFocusedTooltip;
|
|
272
|
+
if (tooltipToResume === null || tooltipToResume === this) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (hoveredTooltip !== null) {
|
|
276
|
+
if (hoveredTooltip === this && !this.#isPointerWithinHoverSurface()) {
|
|
277
|
+
this.#releaseHoverOwnership();
|
|
278
|
+
} else {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (!tooltipToResume.#isSuspendedByHover) {
|
|
283
|
+
if (suspendedFocusedTooltip === tooltipToResume) {
|
|
284
|
+
suspendedFocusedTooltip = null;
|
|
285
|
+
}
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const activeEl = document.activeElement;
|
|
289
|
+
if (activeEl instanceof HTMLElement && tooltipToResume.#isFocusableOutsideTarget(activeEl)) {
|
|
290
|
+
tooltipToResume.#clearTrackedFocus();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
tooltipToResume.#clearFocusSuppression();
|
|
294
|
+
tooltipToResume.#hasFocus = true;
|
|
295
|
+
focusedTooltip = tooltipToResume;
|
|
296
|
+
suspendedFocusedTooltip = null;
|
|
297
|
+
tooltipToResume.#tooltipState.show();
|
|
298
|
+
}
|
|
299
|
+
#isOtherUncontrolledTooltip(tooltip) {
|
|
300
|
+
return tooltip !== null && tooltip !== this && tooltip.isOpenedControlled !== true;
|
|
301
|
+
}
|
|
302
|
+
#claimHoverOwnership() {
|
|
303
|
+
hoveredTooltip = this;
|
|
304
|
+
}
|
|
305
|
+
#releaseHoverOwnership() {
|
|
306
|
+
if (hoveredTooltip === this) {
|
|
307
|
+
hoveredTooltip = null;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
#isPointerWithinHoverSurface() {
|
|
311
|
+
return this.#$contentWrapper.matches(":hover") || this.#$target.assignedElements().some((el) => el.matches(":hover"));
|
|
312
|
+
}
|
|
313
|
+
#finalizeFocusOut() {
|
|
314
|
+
if (!this.isDomConnected) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const activeEl = document.activeElement;
|
|
318
|
+
if (activeEl instanceof Node) {
|
|
319
|
+
if (this.#targetContains(activeEl) || this.shadowRoot?.contains(activeEl) === true) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
this.#clearTrackedFocus();
|
|
324
|
+
this.#tooltipState.hide();
|
|
325
|
+
}
|
|
326
|
+
#onFocusOut = (e) => {
|
|
327
|
+
if (this.#isSuspendedByHover) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (e.relatedTarget instanceof Node) {
|
|
331
|
+
if (this.#targetContains(e.relatedTarget) || this.shadowRoot?.contains(e.relatedTarget) === true) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
requestAnimationFrame(() => {
|
|
336
|
+
this.#finalizeFocusOut();
|
|
337
|
+
});
|
|
338
|
+
};
|
|
339
|
+
#onMouseLeave = () => {
|
|
340
|
+
requestAnimationFrame(() => {
|
|
341
|
+
if (!this.isDomConnected) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (this.#isPointerWithinHoverSurface()) {
|
|
345
|
+
this.#claimHoverOwnership();
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
this.#releaseHoverOwnership();
|
|
349
|
+
if (this.#hasFocus) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
202
352
|
this.#tooltipState.hide();
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
#targetContains(node) {
|
|
356
|
+
return this.#$target.assignedElements().some((el) => el.contains(node));
|
|
357
|
+
}
|
|
358
|
+
#getTargetElements() {
|
|
359
|
+
return this.#$target.assignedElements().filter((el) => el instanceof HTMLElement);
|
|
360
|
+
}
|
|
361
|
+
#isFocusableOutsideTarget(el) {
|
|
362
|
+
if (this.#targetContains(el) || this.shadowRoot?.contains(el) === true || this.#isFocusInFloatingLayer(el)) {
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
return el.matches(FOCUSABLE_OUTSIDE_TARGET_SELECTOR);
|
|
366
|
+
}
|
|
367
|
+
#isFocusInFloatingLayer(el) {
|
|
368
|
+
return el.tagName === "SINCH-POP" || el.tagName === "SINCH-TOOLTIP";
|
|
369
|
+
}
|
|
370
|
+
#clearFocusSuppression() {
|
|
371
|
+
this.#suppressFocusIn = false;
|
|
372
|
+
this.#isSuspendedByHover = false;
|
|
373
|
+
if (this.#suppressedFocusOutArmed) {
|
|
374
|
+
this.#$target.removeEventListener("focusout", this.#onSuppressedFocusOut);
|
|
375
|
+
this.#suppressedFocusOutArmed = false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
#clearTrackedFocus() {
|
|
379
|
+
this.#hasFocus = false;
|
|
380
|
+
if (focusedTooltip === this) {
|
|
381
|
+
focusedTooltip = null;
|
|
382
|
+
}
|
|
383
|
+
if (suspendedFocusedTooltip === this) {
|
|
384
|
+
suspendedFocusedTooltip = null;
|
|
385
|
+
}
|
|
386
|
+
this.#clearFocusSuppression();
|
|
387
|
+
}
|
|
388
|
+
#armSuppressedFocusOut() {
|
|
389
|
+
if (this.#suppressedFocusOutArmed || this.#controller === null) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
this.#suppressedFocusOutArmed = true;
|
|
393
|
+
const options = { signal: this.#controller.signal };
|
|
394
|
+
this.#$target.addEventListener("focusout", this.#onSuppressedFocusOut, options);
|
|
395
|
+
}
|
|
396
|
+
#onSuppressedFocusOut = (e) => {
|
|
397
|
+
if (e.relatedTarget instanceof Node) {
|
|
398
|
+
if (this.#targetContains(e.relatedTarget) || this.shadowRoot?.contains(e.relatedTarget) === true) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
203
401
|
}
|
|
402
|
+
requestAnimationFrame(() => {
|
|
403
|
+
if (!this.isDomConnected) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
if (this.#targetContainsFocus()) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (this.#isSuspendedByHover) {
|
|
410
|
+
if (e.relatedTarget instanceof HTMLElement && this.#isFocusInFloatingLayer(e.relatedTarget)) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const activeEl2 = document.activeElement;
|
|
414
|
+
if (activeEl2 instanceof HTMLElement && this.#isFocusInFloatingLayer(activeEl2)) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (activeEl2 instanceof HTMLElement && this.#isFocusableOutsideTarget(activeEl2)) {
|
|
418
|
+
this.#clearTrackedFocus();
|
|
419
|
+
}
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const activeEl = document.activeElement;
|
|
423
|
+
if (activeEl instanceof HTMLElement && this.#isFocusInFloatingLayer(activeEl)) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
this.#clearTrackedFocus();
|
|
427
|
+
});
|
|
204
428
|
};
|
|
205
429
|
#onScroll = () => {
|
|
430
|
+
if (this.#hasFocus) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
206
433
|
this.#tooltipState.destroy();
|
|
207
434
|
};
|
|
208
435
|
// Tooltip begins to wait for SHOW_DELAY on mouseenter
|
|
@@ -215,6 +442,7 @@ class Tooltip extends NectaryElement {
|
|
|
215
442
|
// SHOW_DELAY ended, tooltip can be shown with animation
|
|
216
443
|
#onStateShowEnd = () => {
|
|
217
444
|
const revealRequestId = ++this.#revealRequestId;
|
|
445
|
+
activeTooltip = this;
|
|
218
446
|
this.#dispatchShowEvent();
|
|
219
447
|
updateBooleanAttribute(this.#$pop, "open", true);
|
|
220
448
|
this.#schedulePlacement();
|
|
@@ -288,12 +516,30 @@ class Tooltip extends NectaryElement {
|
|
|
288
516
|
this.#dispatchHideEvent();
|
|
289
517
|
updateBooleanAttribute(this.#$pop, "open", false);
|
|
290
518
|
}
|
|
519
|
+
if (activeTooltip === this) {
|
|
520
|
+
activeTooltip = null;
|
|
521
|
+
}
|
|
522
|
+
if (!this.#isSuspendedByHover) {
|
|
523
|
+
if (!this.#targetContainsFocus()) {
|
|
524
|
+
this.#clearTrackedFocus();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
291
527
|
this.#resetTipOrientation();
|
|
292
528
|
this.#resetContentOffset();
|
|
293
529
|
this.#zeroDimensionPlacementRetries = 0;
|
|
294
530
|
this.#unsubscribeMouseLeaveEvents();
|
|
295
531
|
this.#unsubscribeScroll();
|
|
532
|
+
if (!this.#isPointerWithinHoverSurface()) {
|
|
533
|
+
this.#releaseHoverOwnership();
|
|
534
|
+
}
|
|
535
|
+
if (!this.#hasFocus) {
|
|
536
|
+
this.#resumeFocusedTooltip();
|
|
537
|
+
}
|
|
296
538
|
};
|
|
539
|
+
#targetContainsFocus() {
|
|
540
|
+
const activeEl = document.activeElement;
|
|
541
|
+
return activeEl instanceof Node && this.#targetContains(activeEl);
|
|
542
|
+
}
|
|
297
543
|
#resetTipOrientation() {
|
|
298
544
|
this.#$tip.style.top = "";
|
|
299
545
|
this.#$tip.style.left = "";
|
|
@@ -440,28 +686,64 @@ class Tooltip extends NectaryElement {
|
|
|
440
686
|
if (!this.isDomConnected || this.#isSubscribed) {
|
|
441
687
|
return;
|
|
442
688
|
}
|
|
443
|
-
|
|
444
|
-
|
|
689
|
+
const options = { signal: this.#controller.signal };
|
|
690
|
+
this.#targetElements = this.#getTargetElements();
|
|
691
|
+
if (this.#targetElements.length === 0) {
|
|
692
|
+
requestAnimationFrame(() => {
|
|
693
|
+
if (!this.isDomConnected || this.#isSubscribed || this.text.length === 0) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
this.#subscribeMouseEnterEvent();
|
|
697
|
+
});
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
this.#targetElements.forEach((el) => {
|
|
701
|
+
el.addEventListener("mouseenter", this.#onMouseEnter, options);
|
|
445
702
|
});
|
|
703
|
+
this.#$target.addEventListener("focusin", this.#onFocusIn, options);
|
|
446
704
|
this.#isSubscribed = true;
|
|
447
705
|
}
|
|
448
706
|
#unsubscribeMouseEnterEvent() {
|
|
449
|
-
this
|
|
707
|
+
this.#targetElements.forEach((el) => {
|
|
708
|
+
el.removeEventListener("mouseenter", this.#onMouseEnter);
|
|
709
|
+
});
|
|
710
|
+
this.#$target.removeEventListener("focusin", this.#onFocusIn);
|
|
711
|
+
this.#targetElements = [];
|
|
450
712
|
this.#isSubscribed = false;
|
|
451
713
|
}
|
|
452
714
|
#subscribeMouseLeaveEvents() {
|
|
453
715
|
const options = { signal: this.#controller.signal };
|
|
454
|
-
this
|
|
455
|
-
|
|
456
|
-
|
|
716
|
+
this.#targetElements.forEach((el) => {
|
|
717
|
+
el.addEventListener("mousedown", this.#onMouseDown, options);
|
|
718
|
+
el.addEventListener("mouseleave", this.#onMouseLeave, options);
|
|
719
|
+
});
|
|
720
|
+
this.#$target.addEventListener("focusout", this.#onFocusOut, options);
|
|
721
|
+
this.#$contentWrapper.addEventListener("mouseenter", this.#onContentMouseEnter, options);
|
|
457
722
|
this.#$contentWrapper.addEventListener("mouseleave", this.#onMouseLeave, options);
|
|
458
723
|
}
|
|
459
724
|
#unsubscribeMouseLeaveEvents() {
|
|
460
|
-
this
|
|
461
|
-
|
|
462
|
-
|
|
725
|
+
this.#targetElements.forEach((el) => {
|
|
726
|
+
el.removeEventListener("mousedown", this.#onMouseDown);
|
|
727
|
+
el.removeEventListener("mouseleave", this.#onMouseLeave);
|
|
728
|
+
});
|
|
729
|
+
this.#$target.removeEventListener("focusout", this.#onFocusOut);
|
|
730
|
+
this.#$contentWrapper.removeEventListener("mouseenter", this.#onContentMouseEnter);
|
|
463
731
|
this.#$contentWrapper.removeEventListener("mouseleave", this.#onMouseLeave);
|
|
464
732
|
}
|
|
733
|
+
#onTargetSlotChange = () => {
|
|
734
|
+
if (!this.isDomConnected || this.text.length === 0) {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const isOpen = this.#isOpen();
|
|
738
|
+
if (this.#isSubscribed) {
|
|
739
|
+
this.#unsubscribeMouseLeaveEvents();
|
|
740
|
+
this.#unsubscribeMouseEnterEvent();
|
|
741
|
+
}
|
|
742
|
+
this.#subscribeMouseEnterEvent();
|
|
743
|
+
if (isOpen) {
|
|
744
|
+
this.#subscribeMouseLeaveEvents();
|
|
745
|
+
}
|
|
746
|
+
};
|
|
465
747
|
#subscribeScroll() {
|
|
466
748
|
window.addEventListener("wheel", this.#onScroll, true);
|
|
467
749
|
}
|