@vaadin/tooltip 24.3.0-alpha1 → 24.3.0-alpha3
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/package.json +12 -8
- package/src/vaadin-tooltip-mixin.d.ts +140 -0
- package/src/vaadin-tooltip-mixin.js +812 -0
- package/src/vaadin-tooltip-overlay-mixin.js +86 -0
- package/src/vaadin-tooltip-overlay-styles.d.ts +8 -0
- package/src/vaadin-tooltip-overlay-styles.js +42 -0
- package/src/vaadin-tooltip-overlay.js +5 -111
- package/src/vaadin-tooltip.d.ts +3 -125
- package/src/vaadin-tooltip.js +3 -796
- package/web-types.json +13 -13
- package/web-types.lit.json +9 -9
package/src/vaadin-tooltip.js
CHANGED
|
@@ -5,217 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import './vaadin-tooltip-overlay.js';
|
|
7
7
|
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
8
|
-
import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
|
|
9
|
-
import { microTask } from '@vaadin/component-base/src/async.js';
|
|
10
8
|
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js';
|
|
11
|
-
import { Debouncer } from '@vaadin/component-base/src/debounce.js';
|
|
12
9
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
13
|
-
import { addValueToAttribute, removeValueFromAttribute } from '@vaadin/component-base/src/dom-utils.js';
|
|
14
10
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
15
|
-
import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
|
|
16
|
-
import { SlotController } from '@vaadin/component-base/src/slot-controller.js';
|
|
17
|
-
import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js';
|
|
18
11
|
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
|
|
19
|
-
|
|
20
|
-
const DEFAULT_DELAY = 500;
|
|
21
|
-
|
|
22
|
-
let defaultFocusDelay = DEFAULT_DELAY;
|
|
23
|
-
let defaultHoverDelay = DEFAULT_DELAY;
|
|
24
|
-
let defaultHideDelay = DEFAULT_DELAY;
|
|
25
|
-
|
|
26
|
-
const closing = new Set();
|
|
27
|
-
|
|
28
|
-
let warmedUp = false;
|
|
29
|
-
let warmUpTimeout = null;
|
|
30
|
-
let cooldownTimeout = null;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Resets the global tooltip warmup and cooldown state.
|
|
34
|
-
* Only for internal use in tests.
|
|
35
|
-
* @private
|
|
36
|
-
*/
|
|
37
|
-
export function resetGlobalTooltipState() {
|
|
38
|
-
warmedUp = false;
|
|
39
|
-
clearTimeout(warmUpTimeout);
|
|
40
|
-
clearTimeout(cooldownTimeout);
|
|
41
|
-
closing.clear();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Controller for handling tooltip opened state.
|
|
46
|
-
*/
|
|
47
|
-
class TooltipStateController {
|
|
48
|
-
constructor(host) {
|
|
49
|
-
this.host = host;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** @private */
|
|
53
|
-
get openedProp() {
|
|
54
|
-
return this.host.manual ? 'opened' : '_autoOpened';
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** @private */
|
|
58
|
-
get focusDelay() {
|
|
59
|
-
const tooltip = this.host;
|
|
60
|
-
return tooltip.focusDelay != null && tooltip.focusDelay > 0 ? tooltip.focusDelay : defaultFocusDelay;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** @private */
|
|
64
|
-
get hoverDelay() {
|
|
65
|
-
const tooltip = this.host;
|
|
66
|
-
return tooltip.hoverDelay != null && tooltip.hoverDelay > 0 ? tooltip.hoverDelay : defaultHoverDelay;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** @private */
|
|
70
|
-
get hideDelay() {
|
|
71
|
-
const tooltip = this.host;
|
|
72
|
-
return tooltip.hideDelay != null && tooltip.hideDelay > 0 ? tooltip.hideDelay : defaultHideDelay;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Whether closing is currently in progress.
|
|
77
|
-
* @return {boolean}
|
|
78
|
-
*/
|
|
79
|
-
get isClosing() {
|
|
80
|
-
return closing.has(this.host);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Schedule opening the tooltip.
|
|
85
|
-
* @param {Object} options
|
|
86
|
-
*/
|
|
87
|
-
open(options = { immediate: false }) {
|
|
88
|
-
const { immediate, hover, focus } = options;
|
|
89
|
-
const isHover = hover && this.hoverDelay > 0;
|
|
90
|
-
const isFocus = focus && this.focusDelay > 0;
|
|
91
|
-
|
|
92
|
-
if (!immediate && (isHover || isFocus) && !this.__closeTimeout) {
|
|
93
|
-
this.__warmupTooltip(isFocus);
|
|
94
|
-
} else {
|
|
95
|
-
this.__showTooltip();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Schedule closing the tooltip.
|
|
101
|
-
* @param {boolean} immediate
|
|
102
|
-
*/
|
|
103
|
-
close(immediate) {
|
|
104
|
-
if (!immediate && this.hideDelay > 0) {
|
|
105
|
-
this.__scheduleClose();
|
|
106
|
-
} else {
|
|
107
|
-
this.__abortClose();
|
|
108
|
-
this._setOpened(false);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
this.__abortWarmUp();
|
|
112
|
-
|
|
113
|
-
if (warmedUp) {
|
|
114
|
-
// Re-start cooldown timer on each tooltip closing.
|
|
115
|
-
this.__abortCooldown();
|
|
116
|
-
this.__scheduleCooldown();
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** @private */
|
|
121
|
-
_isOpened() {
|
|
122
|
-
return this.host[this.openedProp];
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** @private */
|
|
126
|
-
_setOpened(opened) {
|
|
127
|
-
this.host[this.openedProp] = opened;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** @private */
|
|
131
|
-
__flushClosingTooltips() {
|
|
132
|
-
closing.forEach((tooltip) => {
|
|
133
|
-
tooltip._stateController.close(true);
|
|
134
|
-
closing.delete(tooltip);
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** @private */
|
|
139
|
-
__showTooltip() {
|
|
140
|
-
this.__abortClose();
|
|
141
|
-
this.__flushClosingTooltips();
|
|
142
|
-
|
|
143
|
-
this._setOpened(true);
|
|
144
|
-
warmedUp = true;
|
|
145
|
-
|
|
146
|
-
// Abort previously scheduled timers.
|
|
147
|
-
this.__abortWarmUp();
|
|
148
|
-
this.__abortCooldown();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** @private */
|
|
152
|
-
__warmupTooltip(isFocus) {
|
|
153
|
-
if (!this._isOpened()) {
|
|
154
|
-
// First tooltip is opened, warm up.
|
|
155
|
-
if (!warmedUp) {
|
|
156
|
-
this.__scheduleWarmUp(isFocus);
|
|
157
|
-
} else {
|
|
158
|
-
// Warmed up, show another tooltip.
|
|
159
|
-
this.__showTooltip();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** @private */
|
|
165
|
-
__abortClose() {
|
|
166
|
-
if (this.__closeTimeout) {
|
|
167
|
-
clearTimeout(this.__closeTimeout);
|
|
168
|
-
this.__closeTimeout = null;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/** @private */
|
|
173
|
-
__abortCooldown() {
|
|
174
|
-
if (cooldownTimeout) {
|
|
175
|
-
clearTimeout(cooldownTimeout);
|
|
176
|
-
cooldownTimeout = null;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** @private */
|
|
181
|
-
__abortWarmUp() {
|
|
182
|
-
if (warmUpTimeout) {
|
|
183
|
-
clearTimeout(warmUpTimeout);
|
|
184
|
-
warmUpTimeout = null;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/** @private */
|
|
189
|
-
__scheduleClose() {
|
|
190
|
-
if (this._isOpened()) {
|
|
191
|
-
closing.add(this.host);
|
|
192
|
-
|
|
193
|
-
this.__closeTimeout = setTimeout(() => {
|
|
194
|
-
closing.delete(this.host);
|
|
195
|
-
this.__closeTimeout = null;
|
|
196
|
-
this._setOpened(false);
|
|
197
|
-
}, this.hideDelay);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/** @private */
|
|
202
|
-
__scheduleCooldown() {
|
|
203
|
-
cooldownTimeout = setTimeout(() => {
|
|
204
|
-
cooldownTimeout = null;
|
|
205
|
-
warmedUp = false;
|
|
206
|
-
}, this.hideDelay);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/** @private */
|
|
210
|
-
__scheduleWarmUp(isFocus) {
|
|
211
|
-
const delay = isFocus ? this.focusDelay : this.hoverDelay;
|
|
212
|
-
warmUpTimeout = setTimeout(() => {
|
|
213
|
-
warmUpTimeout = null;
|
|
214
|
-
warmedUp = true;
|
|
215
|
-
this.__showTooltip();
|
|
216
|
-
}, delay);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
12
|
+
import { TooltipMixin } from './vaadin-tooltip-mixin.js';
|
|
219
13
|
|
|
220
14
|
/**
|
|
221
15
|
* `<vaadin-tooltip>` is a Web Component for creating tooltips.
|
|
@@ -259,10 +53,10 @@ class TooltipStateController {
|
|
|
259
53
|
* @extends HTMLElement
|
|
260
54
|
* @mixes ControllerMixin
|
|
261
55
|
* @mixes ElementMixin
|
|
262
|
-
* @mixes OverlayClassMixin
|
|
263
56
|
* @mixes ThemePropertyMixin
|
|
57
|
+
* @mixes TooltipMixin
|
|
264
58
|
*/
|
|
265
|
-
class Tooltip extends
|
|
59
|
+
class Tooltip extends TooltipMixin(ThemePropertyMixin(ElementMixin(ControllerMixin(PolymerElement)))) {
|
|
266
60
|
static get is() {
|
|
267
61
|
return 'vaadin-tooltip';
|
|
268
62
|
}
|
|
@@ -292,593 +86,6 @@ class Tooltip extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(Controll
|
|
|
292
86
|
<slot name="sr-label"></slot>
|
|
293
87
|
`;
|
|
294
88
|
}
|
|
295
|
-
|
|
296
|
-
static get properties() {
|
|
297
|
-
return {
|
|
298
|
-
/**
|
|
299
|
-
* Element used to link with the `aria-describedby`
|
|
300
|
-
* attribute. Supports array of multiple elements.
|
|
301
|
-
* When not set, defaults to `target`.
|
|
302
|
-
*/
|
|
303
|
-
ariaTarget: {
|
|
304
|
-
type: Object,
|
|
305
|
-
},
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Object with properties passed to `generator` and
|
|
309
|
-
* `shouldShow` functions for generating tooltip text
|
|
310
|
-
* or detecting whether to show the tooltip or not.
|
|
311
|
-
*/
|
|
312
|
-
context: {
|
|
313
|
-
type: Object,
|
|
314
|
-
value: () => {
|
|
315
|
-
return {};
|
|
316
|
-
},
|
|
317
|
-
},
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* The delay in milliseconds before the tooltip
|
|
321
|
-
* is opened on keyboard focus, when not in manual mode.
|
|
322
|
-
* @attr {number} focus-delay
|
|
323
|
-
*/
|
|
324
|
-
focusDelay: {
|
|
325
|
-
type: Number,
|
|
326
|
-
},
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* The id of the element used as a tooltip trigger.
|
|
330
|
-
* The element should be in the DOM by the time when
|
|
331
|
-
* the attribute is set, otherwise a warning is shown.
|
|
332
|
-
*/
|
|
333
|
-
for: {
|
|
334
|
-
type: String,
|
|
335
|
-
observer: '__forChanged',
|
|
336
|
-
},
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* The delay in milliseconds before the tooltip
|
|
340
|
-
* is closed on losing hover, when not in manual mode.
|
|
341
|
-
* On blur, the tooltip is closed immediately.
|
|
342
|
-
* @attr {number} hide-delay
|
|
343
|
-
*/
|
|
344
|
-
hideDelay: {
|
|
345
|
-
type: Number,
|
|
346
|
-
},
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* The delay in milliseconds before the tooltip
|
|
350
|
-
* is opened on hover, when not in manual mode.
|
|
351
|
-
* @attr {number} hover-delay
|
|
352
|
-
*/
|
|
353
|
-
hoverDelay: {
|
|
354
|
-
type: Number,
|
|
355
|
-
},
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* When true, the tooltip is controlled programmatically
|
|
359
|
-
* instead of reacting to focus and mouse events.
|
|
360
|
-
*/
|
|
361
|
-
manual: {
|
|
362
|
-
type: Boolean,
|
|
363
|
-
value: false,
|
|
364
|
-
},
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* When true, the tooltip is opened programmatically.
|
|
368
|
-
* Only works if `manual` is set to `true`.
|
|
369
|
-
*/
|
|
370
|
-
opened: {
|
|
371
|
-
type: Boolean,
|
|
372
|
-
value: false,
|
|
373
|
-
},
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Position of the tooltip with respect to its target.
|
|
377
|
-
* Supported values: `top-start`, `top`, `top-end`,
|
|
378
|
-
* `bottom-start`, `bottom`, `bottom-end`, `start-top`,
|
|
379
|
-
* `start`, `start-bottom`, `end-top`, `end`, `end-bottom`.
|
|
380
|
-
*/
|
|
381
|
-
position: {
|
|
382
|
-
type: String,
|
|
383
|
-
},
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Function used to detect whether to show the tooltip based on a condition,
|
|
387
|
-
* called every time the tooltip is about to be shown on hover and focus.
|
|
388
|
-
* The function takes two parameters: `target` and `context`, which contain
|
|
389
|
-
* values of the corresponding tooltip properties at the time of calling.
|
|
390
|
-
* The tooltip is only shown when the function invocation returns `true`.
|
|
391
|
-
*/
|
|
392
|
-
shouldShow: {
|
|
393
|
-
type: Object,
|
|
394
|
-
value: () => {
|
|
395
|
-
return (_target, _context) => true;
|
|
396
|
-
},
|
|
397
|
-
},
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Reference to the element used as a tooltip trigger.
|
|
401
|
-
* The target must be placed in the same shadow scope.
|
|
402
|
-
* Defaults to an element referenced with `for`.
|
|
403
|
-
*/
|
|
404
|
-
target: {
|
|
405
|
-
type: Object,
|
|
406
|
-
observer: '__targetChanged',
|
|
407
|
-
},
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* String used as a tooltip content.
|
|
411
|
-
*/
|
|
412
|
-
text: {
|
|
413
|
-
type: String,
|
|
414
|
-
observer: '__textChanged',
|
|
415
|
-
},
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Function used to generate the tooltip content.
|
|
419
|
-
* When provided, it overrides the `text` property.
|
|
420
|
-
* Use the `context` property to provide argument
|
|
421
|
-
* that can be passed to the generator function.
|
|
422
|
-
*/
|
|
423
|
-
generator: {
|
|
424
|
-
type: Object,
|
|
425
|
-
},
|
|
426
|
-
|
|
427
|
-
/**
|
|
428
|
-
* Set to true when the overlay is opened using auto-added
|
|
429
|
-
* event listeners: mouseenter and focusin (keyboard only).
|
|
430
|
-
* @protected
|
|
431
|
-
*/
|
|
432
|
-
_autoOpened: {
|
|
433
|
-
type: Boolean,
|
|
434
|
-
observer: '__autoOpenedChanged',
|
|
435
|
-
},
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Default value used when `position` property is not set.
|
|
439
|
-
* @protected
|
|
440
|
-
*/
|
|
441
|
-
_position: {
|
|
442
|
-
type: String,
|
|
443
|
-
value: 'bottom',
|
|
444
|
-
},
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Element used to link with the `aria-describedby`
|
|
448
|
-
* attribute. When not set, defaults to `target`.
|
|
449
|
-
* @protected
|
|
450
|
-
*/
|
|
451
|
-
_effectiveAriaTarget: {
|
|
452
|
-
type: Object,
|
|
453
|
-
computed: '__computeAriaTarget(ariaTarget, target)',
|
|
454
|
-
observer: '__effectiveAriaTargetChanged',
|
|
455
|
-
},
|
|
456
|
-
|
|
457
|
-
/** @private */
|
|
458
|
-
__effectivePosition: {
|
|
459
|
-
type: String,
|
|
460
|
-
computed: '__computePosition(position, _position)',
|
|
461
|
-
},
|
|
462
|
-
|
|
463
|
-
/** @private */
|
|
464
|
-
__isTargetHidden: {
|
|
465
|
-
type: Boolean,
|
|
466
|
-
value: false,
|
|
467
|
-
},
|
|
468
|
-
|
|
469
|
-
/** @private */
|
|
470
|
-
_isConnected: {
|
|
471
|
-
type: Boolean,
|
|
472
|
-
},
|
|
473
|
-
|
|
474
|
-
/** @private */
|
|
475
|
-
_srLabel: {
|
|
476
|
-
type: Object,
|
|
477
|
-
},
|
|
478
|
-
|
|
479
|
-
/** @private */
|
|
480
|
-
_overlayContent: {
|
|
481
|
-
type: String,
|
|
482
|
-
},
|
|
483
|
-
};
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
static get observers() {
|
|
487
|
-
return [
|
|
488
|
-
'__generatorChanged(_overlayElement, generator, context)',
|
|
489
|
-
'__updateSrLabelText(_srLabel, _overlayContent)',
|
|
490
|
-
];
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Sets the default focus delay to be used by all tooltip instances,
|
|
495
|
-
* except for those that have focus delay configured using property.
|
|
496
|
-
*
|
|
497
|
-
* @param {number} delay
|
|
498
|
-
*/
|
|
499
|
-
static setDefaultFocusDelay(focusDelay) {
|
|
500
|
-
defaultFocusDelay = focusDelay != null && focusDelay >= 0 ? focusDelay : DEFAULT_DELAY;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
/**
|
|
504
|
-
* Sets the default hide delay to be used by all tooltip instances,
|
|
505
|
-
* except for those that have hide delay configured using property.
|
|
506
|
-
*
|
|
507
|
-
* @param {number} hideDelay
|
|
508
|
-
*/
|
|
509
|
-
static setDefaultHideDelay(hideDelay) {
|
|
510
|
-
defaultHideDelay = hideDelay != null && hideDelay >= 0 ? hideDelay : DEFAULT_DELAY;
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
/**
|
|
514
|
-
* Sets the default hover delay to be used by all tooltip instances,
|
|
515
|
-
* except for those that have hover delay configured using property.
|
|
516
|
-
*
|
|
517
|
-
* @param {number} delay
|
|
518
|
-
*/
|
|
519
|
-
static setDefaultHoverDelay(hoverDelay) {
|
|
520
|
-
defaultHoverDelay = hoverDelay != null && hoverDelay >= 0 ? hoverDelay : DEFAULT_DELAY;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
constructor() {
|
|
524
|
-
super();
|
|
525
|
-
|
|
526
|
-
this._uniqueId = `vaadin-tooltip-${generateUniqueId()}`;
|
|
527
|
-
this._renderer = this.__tooltipRenderer.bind(this);
|
|
528
|
-
|
|
529
|
-
this.__onFocusin = this.__onFocusin.bind(this);
|
|
530
|
-
this.__onFocusout = this.__onFocusout.bind(this);
|
|
531
|
-
this.__onMouseDown = this.__onMouseDown.bind(this);
|
|
532
|
-
this.__onMouseEnter = this.__onMouseEnter.bind(this);
|
|
533
|
-
this.__onMouseLeave = this.__onMouseLeave.bind(this);
|
|
534
|
-
this.__onKeyDown = this.__onKeyDown.bind(this);
|
|
535
|
-
this.__onOverlayOpen = this.__onOverlayOpen.bind(this);
|
|
536
|
-
|
|
537
|
-
this.__targetVisibilityObserver = new IntersectionObserver(
|
|
538
|
-
(entries) => {
|
|
539
|
-
entries.forEach((entry) => this.__onTargetVisibilityChange(entry.isIntersecting));
|
|
540
|
-
},
|
|
541
|
-
{ threshold: 0 },
|
|
542
|
-
);
|
|
543
|
-
|
|
544
|
-
this._stateController = new TooltipStateController(this);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/** @protected */
|
|
548
|
-
connectedCallback() {
|
|
549
|
-
super.connectedCallback();
|
|
550
|
-
|
|
551
|
-
this._isConnected = true;
|
|
552
|
-
|
|
553
|
-
document.body.addEventListener('vaadin-overlay-open', this.__onOverlayOpen);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/** @protected */
|
|
557
|
-
disconnectedCallback() {
|
|
558
|
-
super.disconnectedCallback();
|
|
559
|
-
|
|
560
|
-
if (this._autoOpened) {
|
|
561
|
-
this._stateController.close(true);
|
|
562
|
-
}
|
|
563
|
-
this._isConnected = false;
|
|
564
|
-
|
|
565
|
-
document.body.removeEventListener('vaadin-overlay-open', this.__onOverlayOpen);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/** @protected */
|
|
569
|
-
ready() {
|
|
570
|
-
super.ready();
|
|
571
|
-
|
|
572
|
-
this._srLabelController = new SlotController(this, 'sr-label', 'div', {
|
|
573
|
-
initializer: (element) => {
|
|
574
|
-
element.id = this._uniqueId;
|
|
575
|
-
element.setAttribute('role', 'tooltip');
|
|
576
|
-
this._srLabel = element;
|
|
577
|
-
},
|
|
578
|
-
});
|
|
579
|
-
this.addController(this._srLabelController);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
/** @private */
|
|
583
|
-
__computeAriaTarget(ariaTarget, target) {
|
|
584
|
-
const isElementNode = (el) => el && el.nodeType === Node.ELEMENT_NODE;
|
|
585
|
-
const isAriaTargetSet = Array.isArray(ariaTarget) ? ariaTarget.some(isElementNode) : ariaTarget;
|
|
586
|
-
return isAriaTargetSet ? ariaTarget : target;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/** @private */
|
|
590
|
-
__computeHorizontalAlign(position) {
|
|
591
|
-
return ['top-end', 'bottom-end', 'start-top', 'start', 'start-bottom'].includes(position) ? 'end' : 'start';
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/** @private */
|
|
595
|
-
__computeNoHorizontalOverlap(position) {
|
|
596
|
-
return ['start-top', 'start', 'start-bottom', 'end-top', 'end', 'end-bottom'].includes(position);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/** @private */
|
|
600
|
-
__computeNoVerticalOverlap(position) {
|
|
601
|
-
return ['top-start', 'top-end', 'top', 'bottom-start', 'bottom', 'bottom-end'].includes(position);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
/** @private */
|
|
605
|
-
__computeVerticalAlign(position) {
|
|
606
|
-
return ['top-start', 'top-end', 'top', 'start-bottom', 'end-bottom'].includes(position) ? 'bottom' : 'top';
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/** @private */
|
|
610
|
-
__computeOpened(manual, opened, autoOpened, connected) {
|
|
611
|
-
return connected && (manual ? opened : autoOpened);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
/** @private */
|
|
615
|
-
__computePosition(position, defaultPosition) {
|
|
616
|
-
return position || defaultPosition;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
/** @private */
|
|
620
|
-
__tooltipRenderer(root) {
|
|
621
|
-
root.textContent = typeof this.generator === 'function' ? this.generator(this.context) : this.text;
|
|
622
|
-
|
|
623
|
-
// Update the sr-only label text content
|
|
624
|
-
this._overlayContent = root.textContent;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
/** @private */
|
|
628
|
-
__effectiveAriaTargetChanged(ariaTarget, oldAriaTarget) {
|
|
629
|
-
if (oldAriaTarget) {
|
|
630
|
-
[oldAriaTarget].flat().forEach((target) => {
|
|
631
|
-
removeValueFromAttribute(target, 'aria-describedby', this._uniqueId);
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
if (ariaTarget) {
|
|
636
|
-
[ariaTarget].flat().forEach((target) => {
|
|
637
|
-
addValueToAttribute(target, 'aria-describedby', this._uniqueId);
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
/** @private */
|
|
643
|
-
__autoOpenedChanged(opened, oldOpened) {
|
|
644
|
-
if (opened) {
|
|
645
|
-
document.addEventListener('keydown', this.__onKeyDown, true);
|
|
646
|
-
} else if (oldOpened) {
|
|
647
|
-
document.removeEventListener('keydown', this.__onKeyDown, true);
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
/** @private */
|
|
652
|
-
__forChanged(forId) {
|
|
653
|
-
if (forId) {
|
|
654
|
-
this.__setTargetByIdDebouncer = Debouncer.debounce(this.__setTargetByIdDebouncer, microTask, () =>
|
|
655
|
-
this.__setTargetById(forId),
|
|
656
|
-
);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
/** @private */
|
|
661
|
-
__setTargetById(targetId) {
|
|
662
|
-
if (!this.isConnected) {
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
const target = this.getRootNode().getElementById(targetId);
|
|
667
|
-
|
|
668
|
-
if (target) {
|
|
669
|
-
this.target = target;
|
|
670
|
-
} else {
|
|
671
|
-
console.warn(`No element with id="${targetId}" found to show tooltip.`);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
/** @private */
|
|
676
|
-
__targetChanged(target, oldTarget) {
|
|
677
|
-
if (oldTarget) {
|
|
678
|
-
oldTarget.removeEventListener('mouseenter', this.__onMouseEnter);
|
|
679
|
-
oldTarget.removeEventListener('mouseleave', this.__onMouseLeave);
|
|
680
|
-
oldTarget.removeEventListener('focusin', this.__onFocusin);
|
|
681
|
-
oldTarget.removeEventListener('focusout', this.__onFocusout);
|
|
682
|
-
oldTarget.removeEventListener('mousedown', this.__onMouseDown);
|
|
683
|
-
|
|
684
|
-
this.__targetVisibilityObserver.unobserve(oldTarget);
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
if (target) {
|
|
688
|
-
target.addEventListener('mouseenter', this.__onMouseEnter);
|
|
689
|
-
target.addEventListener('mouseleave', this.__onMouseLeave);
|
|
690
|
-
target.addEventListener('focusin', this.__onFocusin);
|
|
691
|
-
target.addEventListener('focusout', this.__onFocusout);
|
|
692
|
-
target.addEventListener('mousedown', this.__onMouseDown);
|
|
693
|
-
|
|
694
|
-
// Wait before observing to avoid Chrome issue.
|
|
695
|
-
requestAnimationFrame(() => {
|
|
696
|
-
this.__targetVisibilityObserver.observe(target);
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
/** @private */
|
|
702
|
-
__onFocusin(event) {
|
|
703
|
-
if (this.manual) {
|
|
704
|
-
return;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
// Only open on keyboard focus.
|
|
708
|
-
if (!isKeyboardActive()) {
|
|
709
|
-
return;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
// Do not re-open while focused if closed on Esc or mousedown.
|
|
713
|
-
if (this.target.contains(event.relatedTarget)) {
|
|
714
|
-
return;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
if (!this.__isShouldShow()) {
|
|
718
|
-
return;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
this.__focusInside = true;
|
|
722
|
-
|
|
723
|
-
if (!this.__isTargetHidden && (!this.__hoverInside || !this._autoOpened)) {
|
|
724
|
-
this._stateController.open({ focus: true });
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
/** @private */
|
|
729
|
-
__onFocusout(event) {
|
|
730
|
-
if (this.manual) {
|
|
731
|
-
return;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// Do not close when moving focus within a component.
|
|
735
|
-
if (this.target.contains(event.relatedTarget)) {
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
this.__focusInside = false;
|
|
740
|
-
|
|
741
|
-
if (!this.__hoverInside) {
|
|
742
|
-
this._stateController.close(true);
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/** @private */
|
|
747
|
-
__onKeyDown(event) {
|
|
748
|
-
if (event.key === 'Escape') {
|
|
749
|
-
event.stopPropagation();
|
|
750
|
-
this._stateController.close(true);
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
/** @private */
|
|
755
|
-
__onMouseDown() {
|
|
756
|
-
this._stateController.close(true);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
/** @private */
|
|
760
|
-
__onMouseEnter() {
|
|
761
|
-
if (this.manual) {
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
if (!this.__isShouldShow()) {
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
if (this.__hoverInside) {
|
|
770
|
-
// Already hovering inside the element, do nothing.
|
|
771
|
-
return;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
this.__hoverInside = true;
|
|
775
|
-
|
|
776
|
-
if (!this.__isTargetHidden && (!this.__focusInside || !this._autoOpened)) {
|
|
777
|
-
this._stateController.open({ hover: true });
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
/** @private */
|
|
782
|
-
__onMouseLeave(event) {
|
|
783
|
-
if (event.relatedTarget !== this._overlayElement) {
|
|
784
|
-
this.__handleMouseLeave();
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
/** @private */
|
|
789
|
-
__onOverlayMouseEnter() {
|
|
790
|
-
// Retain opened state when moving pointer over the overlay.
|
|
791
|
-
// Closing can start due to an offset between the target and
|
|
792
|
-
// the overlay itself. If that's the case, re-open overlay.
|
|
793
|
-
// See https://github.com/vaadin/web-components/issues/6316
|
|
794
|
-
if (this._stateController.isClosing) {
|
|
795
|
-
this._stateController.open({ immediate: true });
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
/** @private */
|
|
800
|
-
__onOverlayMouseLeave(event) {
|
|
801
|
-
if (event.relatedTarget !== this.target) {
|
|
802
|
-
this.__handleMouseLeave();
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
/** @private */
|
|
807
|
-
__handleMouseLeave() {
|
|
808
|
-
if (this.manual) {
|
|
809
|
-
return;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
this.__hoverInside = false;
|
|
813
|
-
|
|
814
|
-
if (!this.__focusInside) {
|
|
815
|
-
this._stateController.close();
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
/** @private */
|
|
820
|
-
__onOverlayOpen() {
|
|
821
|
-
if (this.manual) {
|
|
822
|
-
return;
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
// Close tooltip if another overlay is opened on top of the tooltip's overlay
|
|
826
|
-
if (this._overlayElement.opened && !this._overlayElement._last) {
|
|
827
|
-
this._stateController.close(true);
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
/** @private */
|
|
832
|
-
__onTargetVisibilityChange(isVisible) {
|
|
833
|
-
const oldHidden = this.__isTargetHidden;
|
|
834
|
-
this.__isTargetHidden = !isVisible;
|
|
835
|
-
|
|
836
|
-
// Open the overlay when the target becomes visible and has focus or hover.
|
|
837
|
-
if (oldHidden && isVisible && (this.__focusInside || this.__hoverInside)) {
|
|
838
|
-
this._stateController.open({ immediate: true });
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
// Close the overlay when the target is no longer fully visible.
|
|
843
|
-
if (!isVisible && this._autoOpened) {
|
|
844
|
-
this._stateController.close(true);
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
/** @private */
|
|
849
|
-
__isShouldShow() {
|
|
850
|
-
if (typeof this.shouldShow === 'function' && this.shouldShow(this.target, this.context) !== true) {
|
|
851
|
-
return false;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
return true;
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
/** @private */
|
|
858
|
-
__textChanged(text, oldText) {
|
|
859
|
-
if (this._overlayElement && (text || oldText)) {
|
|
860
|
-
this._overlayElement.requestContentUpdate();
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
/** @private */
|
|
865
|
-
__generatorChanged(overlayElement, generator, context) {
|
|
866
|
-
if (overlayElement) {
|
|
867
|
-
if (generator !== this.__oldTextGenerator || context !== this.__oldContext) {
|
|
868
|
-
overlayElement.requestContentUpdate();
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
this.__oldTextGenerator = generator;
|
|
872
|
-
this.__oldContext = context;
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
/** @private */
|
|
877
|
-
__updateSrLabelText(srLabel, textContent) {
|
|
878
|
-
if (srLabel) {
|
|
879
|
-
srLabel.textContent = textContent;
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
89
|
}
|
|
883
90
|
|
|
884
91
|
defineCustomElement(Tooltip);
|