@vaadin/tooltip
Advanced tools
Comparing version 24.2.0-beta2 to 24.2.0-beta3
{ | ||
"name": "@vaadin/tooltip", | ||
"version": "24.2.0-beta2", | ||
"version": "24.2.0-beta3", | ||
"publishConfig": { | ||
@@ -24,2 +24,5 @@ "access": "public" | ||
"src", | ||
"!src/vaadin-lit-tooltip-overlay.js", | ||
"!src/vaadin-lit-tooltip.d.ts", | ||
"!src/vaadin-lit-tooltip.js", | ||
"theme", | ||
@@ -38,9 +41,10 @@ "vaadin-*.d.ts", | ||
"dependencies": { | ||
"@open-wc/dedupe-mixin": "^1.3.0", | ||
"@polymer/polymer": "^3.0.0", | ||
"@vaadin/a11y-base": "24.2.0-beta2", | ||
"@vaadin/component-base": "24.2.0-beta2", | ||
"@vaadin/overlay": "24.2.0-beta2", | ||
"@vaadin/vaadin-lumo-styles": "24.2.0-beta2", | ||
"@vaadin/vaadin-material-styles": "24.2.0-beta2", | ||
"@vaadin/vaadin-themable-mixin": "24.2.0-beta2" | ||
"@vaadin/a11y-base": "24.2.0-beta3", | ||
"@vaadin/component-base": "24.2.0-beta3", | ||
"@vaadin/overlay": "24.2.0-beta3", | ||
"@vaadin/vaadin-lumo-styles": "24.2.0-beta3", | ||
"@vaadin/vaadin-material-styles": "24.2.0-beta3", | ||
"@vaadin/vaadin-themable-mixin": "24.2.0-beta3" | ||
}, | ||
@@ -56,3 +60,3 @@ "devDependencies": { | ||
], | ||
"gitHead": "4b852f9a12d4dade7f0fb3c73b7212436cebf310" | ||
"gitHead": "91ea11e7ad706065340acdb93b92316919ce5e69" | ||
} |
@@ -9,43 +9,7 @@ /** | ||
import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js'; | ||
import { OverlayMixin } from '@vaadin/overlay/src/vaadin-overlay-mixin.js'; | ||
import { PositionMixin } from '@vaadin/overlay/src/vaadin-overlay-position-mixin.js'; | ||
import { overlayStyles } from '@vaadin/overlay/src/vaadin-overlay-styles.js'; | ||
import { css, registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; | ||
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; | ||
import { TooltipOverlayMixin } from './vaadin-tooltip-overlay-mixin.js'; | ||
import { tooltipOverlayStyles } from './vaadin-tooltip-overlay-styles.js'; | ||
const tooltipOverlayStyles = css` | ||
:host { | ||
z-index: 1100; | ||
} | ||
[part='overlay'] { | ||
max-width: 40ch; | ||
} | ||
:host([position^='top'][top-aligned]) [part='overlay'], | ||
:host([position^='bottom'][top-aligned]) [part='overlay'] { | ||
margin-top: var(--vaadin-tooltip-offset-top, 0); | ||
} | ||
:host([position^='top'][bottom-aligned]) [part='overlay'], | ||
:host([position^='bottom'][bottom-aligned]) [part='overlay'] { | ||
margin-bottom: var(--vaadin-tooltip-offset-bottom, 0); | ||
} | ||
:host([position^='start'][start-aligned]) [part='overlay'], | ||
:host([position^='end'][start-aligned]) [part='overlay'] { | ||
margin-inline-start: var(--vaadin-tooltip-offset-start, 0); | ||
} | ||
:host([position^='start'][end-aligned]) [part='overlay'], | ||
:host([position^='end'][end-aligned]) [part='overlay'] { | ||
margin-inline-end: var(--vaadin-tooltip-offset-end, 0); | ||
} | ||
@media (forced-colors: active) { | ||
[part='overlay'] { | ||
outline: 1px dashed; | ||
} | ||
} | ||
`; | ||
registerStyles('vaadin-tooltip-overlay', [overlayStyles, tooltipOverlayStyles], { | ||
@@ -61,8 +25,7 @@ moduleId: 'vaadin-tooltip-overlay-styles', | ||
* @mixes DirMixin | ||
* @mixes OverlayMixin | ||
* @mixes PositionMixin | ||
* @mixes ThemableMixin | ||
* @mixes TooltipOverlayMixin | ||
* @private | ||
*/ | ||
class TooltipOverlay extends PositionMixin(OverlayMixin(DirMixin(ThemableMixin(PolymerElement)))) { | ||
class TooltipOverlay extends TooltipOverlayMixin(DirMixin(ThemableMixin(PolymerElement))) { | ||
static get is() { | ||
@@ -81,11 +44,2 @@ return 'vaadin-tooltip-overlay'; | ||
static get properties() { | ||
return { | ||
position: { | ||
type: String, | ||
reflectToAttribute: true, | ||
}, | ||
}; | ||
} | ||
/** @protected */ | ||
@@ -101,64 +55,4 @@ ready() { | ||
} | ||
requestContentUpdate() { | ||
super.requestContentUpdate(); | ||
this.toggleAttribute('hidden', this.textContent.trim() === ''); | ||
// Copy custom properties from the tooltip | ||
if (this.positionTarget && this.owner) { | ||
const style = getComputedStyle(this.owner); | ||
['top', 'bottom', 'start', 'end'].forEach((prop) => { | ||
this.style.setProperty( | ||
`--vaadin-tooltip-offset-${prop}`, | ||
style.getPropertyValue(`--vaadin-tooltip-offset-${prop}`), | ||
); | ||
}); | ||
} | ||
} | ||
/** | ||
* @protected | ||
* @override | ||
*/ | ||
_updatePosition() { | ||
super._updatePosition(); | ||
if (!this.positionTarget) { | ||
return; | ||
} | ||
// Center the tooltip overlay horizontally | ||
if (this.position === 'bottom' || this.position === 'top') { | ||
const targetRect = this.positionTarget.getBoundingClientRect(); | ||
const overlayRect = this.$.overlay.getBoundingClientRect(); | ||
const offset = targetRect.width / 2 - overlayRect.width / 2; | ||
if (this.style.left) { | ||
const left = overlayRect.left + offset; | ||
if (left > 0) { | ||
this.style.left = `${left}px`; | ||
} | ||
} | ||
if (this.style.right) { | ||
const right = parseFloat(this.style.right) + offset; | ||
if (right > 0) { | ||
this.style.right = `${right}px`; | ||
} | ||
} | ||
} | ||
// Center the tooltip overlay vertically | ||
if (this.position === 'start' || this.position === 'end') { | ||
const targetRect = this.positionTarget.getBoundingClientRect(); | ||
const overlayRect = this.$.overlay.getBoundingClientRect(); | ||
const offset = targetRect.height / 2 - overlayRect.height / 2; | ||
this.style.top = `${overlayRect.top + offset}px`; | ||
} | ||
} | ||
} | ||
defineCustomElement(TooltipOverlay); |
@@ -8,18 +8,6 @@ /** | ||
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js'; | ||
import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js'; | ||
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js'; | ||
import { TooltipMixin } from './vaadin-tooltip-mixin.js'; | ||
export type TooltipPosition = | ||
| 'bottom-end' | ||
| 'bottom-start' | ||
| 'bottom' | ||
| 'end-bottom' | ||
| 'end-top' | ||
| 'end' | ||
| 'start-bottom' | ||
| 'start-top' | ||
| 'start' | ||
| 'top-end' | ||
| 'top-start' | ||
| 'top'; | ||
export { TooltipPosition } from './vaadin-tooltip-mixin.js'; | ||
@@ -64,114 +52,4 @@ /** | ||
*/ | ||
declare class Tooltip extends OverlayClassMixin(ThemePropertyMixin(ControllerMixin(ElementMixin(HTMLElement)))) { | ||
/** | ||
* Sets the default focus delay to be used by all tooltip instances, | ||
* except for those that have focus delay configured using property. | ||
*/ | ||
static setDefaultFocusDelay(focusDelay: number): void; | ||
declare class Tooltip extends TooltipMixin(ThemePropertyMixin(ControllerMixin(ElementMixin(HTMLElement)))) {} | ||
/** | ||
* Sets the default hide delay to be used by all tooltip instances, | ||
* except for those that have hide delay configured using property. | ||
*/ | ||
static setDefaultHideDelay(hideDelay: number): void; | ||
/** | ||
* Sets the default hover delay to be used by all tooltip instances, | ||
* except for those that have hover delay configured using property. | ||
*/ | ||
static setDefaultHoverDelay(delay: number): void; | ||
/** | ||
* Element used to link with the `aria-describedby` | ||
* attribute. Supports array of multiple elements. | ||
* When not set, defaults to `target`. | ||
*/ | ||
ariaTarget: HTMLElement | HTMLElement[] | undefined; | ||
/** | ||
* Object with properties passed to `generator` and | ||
* `shouldShow` functions for generating tooltip text | ||
* or detecting whether to show the tooltip or not. | ||
*/ | ||
context: Record<string, unknown>; | ||
/** | ||
* The delay in milliseconds before the tooltip | ||
* is opened on keyboard focus, when not in manual mode. | ||
* @attr {number} focus-delay | ||
*/ | ||
focusDelay: number; | ||
/** | ||
* The id of the element used as a tooltip trigger. | ||
* The element should be in the DOM by the time when | ||
* the attribute is set, otherwise a warning is shown. | ||
*/ | ||
for: string | undefined; | ||
/** | ||
* The delay in milliseconds before the tooltip | ||
* is closed on losing hover, when not in manual mode. | ||
* On blur, the tooltip is closed immediately. | ||
* @attr {number} hide-delay | ||
*/ | ||
hideDelay: number; | ||
/** | ||
* The delay in milliseconds before the tooltip | ||
* is opened on hover, when not in manual mode. | ||
* @attr {number} hover-delay | ||
*/ | ||
hoverDelay: number; | ||
/** | ||
* When true, the tooltip is controlled programmatically | ||
* instead of reacting to focus and mouse events. | ||
*/ | ||
manual: boolean; | ||
/** | ||
* When true, the tooltip is opened programmatically. | ||
* Only works if `manual` is set to `true`. | ||
*/ | ||
opened: boolean; | ||
/** | ||
* Position of the tooltip with respect to its target. | ||
* Supported values: `top-start`, `top`, `top-end`, | ||
* `bottom-start`, `bottom`, `bottom-end`, `start-top`, | ||
* `start`, `start-bottom`, `end-top`, `end`, `end-bottom`. | ||
*/ | ||
position: TooltipPosition; | ||
/** | ||
* Function used to detect whether to show the tooltip based on a condition, | ||
* called every time the tooltip is about to be shown on hover and focus. | ||
* The function takes two parameters: `target` and `context`, which contain | ||
* values of the corresponding tooltip properties at the time of calling. | ||
* The tooltip is only shown when the function invocation returns `true`. | ||
*/ | ||
shouldShow: (target: HTMLElement, context?: Record<string, unknown>) => boolean; | ||
/** | ||
* Reference to the element used as a tooltip trigger. | ||
* The target must be placed in the same shadow scope. | ||
* Defaults to an element referenced with `for`. | ||
*/ | ||
target: HTMLElement | undefined; | ||
/** | ||
* String used as a tooltip content. | ||
*/ | ||
text: string | null | undefined; | ||
/** | ||
* Function used to generate the tooltip content. | ||
* When provided, it overrides the `text` property. | ||
* Use the `context` property to provide argument | ||
* that can be passed to the generator function. | ||
*/ | ||
generator: (context: Record<string, unknown>) => string; | ||
} | ||
declare global { | ||
@@ -178,0 +56,0 @@ interface HTMLElementTagNameMap { |
@@ -8,215 +8,9 @@ /** | ||
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js'; | ||
import { isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js'; | ||
import { microTask } from '@vaadin/component-base/src/async.js'; | ||
import { ControllerMixin } from '@vaadin/component-base/src/controller-mixin.js'; | ||
import { Debouncer } from '@vaadin/component-base/src/debounce.js'; | ||
import { defineCustomElement } from '@vaadin/component-base/src/define.js'; | ||
import { addValueToAttribute, removeValueFromAttribute } from '@vaadin/component-base/src/dom-utils.js'; | ||
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js'; | ||
import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js'; | ||
import { SlotController } from '@vaadin/component-base/src/slot-controller.js'; | ||
import { generateUniqueId } from '@vaadin/component-base/src/unique-id-utils.js'; | ||
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js'; | ||
import { TooltipMixin } from './vaadin-tooltip-mixin.js'; | ||
const DEFAULT_DELAY = 500; | ||
let defaultFocusDelay = DEFAULT_DELAY; | ||
let defaultHoverDelay = DEFAULT_DELAY; | ||
let defaultHideDelay = DEFAULT_DELAY; | ||
const closing = new Set(); | ||
let warmedUp = false; | ||
let warmUpTimeout = null; | ||
let cooldownTimeout = null; | ||
/** | ||
* Resets the global tooltip warmup and cooldown state. | ||
* Only for internal use in tests. | ||
* @private | ||
*/ | ||
export function resetGlobalTooltipState() { | ||
warmedUp = false; | ||
clearTimeout(warmUpTimeout); | ||
clearTimeout(cooldownTimeout); | ||
closing.clear(); | ||
} | ||
/** | ||
* Controller for handling tooltip opened state. | ||
*/ | ||
class TooltipStateController { | ||
constructor(host) { | ||
this.host = host; | ||
} | ||
/** @private */ | ||
get openedProp() { | ||
return this.host.manual ? 'opened' : '_autoOpened'; | ||
} | ||
/** @private */ | ||
get focusDelay() { | ||
const tooltip = this.host; | ||
return tooltip.focusDelay != null && tooltip.focusDelay > 0 ? tooltip.focusDelay : defaultFocusDelay; | ||
} | ||
/** @private */ | ||
get hoverDelay() { | ||
const tooltip = this.host; | ||
return tooltip.hoverDelay != null && tooltip.hoverDelay > 0 ? tooltip.hoverDelay : defaultHoverDelay; | ||
} | ||
/** @private */ | ||
get hideDelay() { | ||
const tooltip = this.host; | ||
return tooltip.hideDelay != null && tooltip.hideDelay > 0 ? tooltip.hideDelay : defaultHideDelay; | ||
} | ||
/** | ||
* Whether closing is currently in progress. | ||
* @return {boolean} | ||
*/ | ||
get isClosing() { | ||
return closing.has(this.host); | ||
} | ||
/** | ||
* Schedule opening the tooltip. | ||
* @param {Object} options | ||
*/ | ||
open(options = { immediate: false }) { | ||
const { immediate, hover, focus } = options; | ||
const isHover = hover && this.hoverDelay > 0; | ||
const isFocus = focus && this.focusDelay > 0; | ||
if (!immediate && (isHover || isFocus) && !this.__closeTimeout) { | ||
this.__warmupTooltip(isFocus); | ||
} else { | ||
this.__showTooltip(); | ||
} | ||
} | ||
/** | ||
* Schedule closing the tooltip. | ||
* @param {boolean} immediate | ||
*/ | ||
close(immediate) { | ||
if (!immediate && this.hideDelay > 0) { | ||
this.__scheduleClose(); | ||
} else { | ||
this.__abortClose(); | ||
this._setOpened(false); | ||
} | ||
this.__abortWarmUp(); | ||
if (warmedUp) { | ||
// Re-start cooldown timer on each tooltip closing. | ||
this.__abortCooldown(); | ||
this.__scheduleCooldown(); | ||
} | ||
} | ||
/** @private */ | ||
_isOpened() { | ||
return this.host[this.openedProp]; | ||
} | ||
/** @private */ | ||
_setOpened(opened) { | ||
this.host[this.openedProp] = opened; | ||
} | ||
/** @private */ | ||
__flushClosingTooltips() { | ||
closing.forEach((tooltip) => { | ||
tooltip._stateController.close(true); | ||
closing.delete(tooltip); | ||
}); | ||
} | ||
/** @private */ | ||
__showTooltip() { | ||
this.__abortClose(); | ||
this.__flushClosingTooltips(); | ||
this._setOpened(true); | ||
warmedUp = true; | ||
// Abort previously scheduled timers. | ||
this.__abortWarmUp(); | ||
this.__abortCooldown(); | ||
} | ||
/** @private */ | ||
__warmupTooltip(isFocus) { | ||
if (!this._isOpened()) { | ||
// First tooltip is opened, warm up. | ||
if (!warmedUp) { | ||
this.__scheduleWarmUp(isFocus); | ||
} else { | ||
// Warmed up, show another tooltip. | ||
this.__showTooltip(); | ||
} | ||
} | ||
} | ||
/** @private */ | ||
__abortClose() { | ||
if (this.__closeTimeout) { | ||
clearTimeout(this.__closeTimeout); | ||
this.__closeTimeout = null; | ||
} | ||
} | ||
/** @private */ | ||
__abortCooldown() { | ||
if (cooldownTimeout) { | ||
clearTimeout(cooldownTimeout); | ||
cooldownTimeout = null; | ||
} | ||
} | ||
/** @private */ | ||
__abortWarmUp() { | ||
if (warmUpTimeout) { | ||
clearTimeout(warmUpTimeout); | ||
warmUpTimeout = null; | ||
} | ||
} | ||
/** @private */ | ||
__scheduleClose() { | ||
if (this._isOpened()) { | ||
closing.add(this.host); | ||
this.__closeTimeout = setTimeout(() => { | ||
closing.delete(this.host); | ||
this.__closeTimeout = null; | ||
this._setOpened(false); | ||
}, this.hideDelay); | ||
} | ||
} | ||
/** @private */ | ||
__scheduleCooldown() { | ||
cooldownTimeout = setTimeout(() => { | ||
cooldownTimeout = null; | ||
warmedUp = false; | ||
}, this.hideDelay); | ||
} | ||
/** @private */ | ||
__scheduleWarmUp(isFocus) { | ||
const delay = isFocus ? this.focusDelay : this.hoverDelay; | ||
warmUpTimeout = setTimeout(() => { | ||
warmUpTimeout = null; | ||
warmedUp = true; | ||
this.__showTooltip(); | ||
}, delay); | ||
} | ||
} | ||
/** | ||
* `<vaadin-tooltip>` is a Web Component for creating tooltips. | ||
@@ -263,6 +57,6 @@ * | ||
* @mixes ElementMixin | ||
* @mixes OverlayClassMixin | ||
* @mixes ThemePropertyMixin | ||
* @mixes TooltipMixin | ||
*/ | ||
class Tooltip extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(ControllerMixin(PolymerElement)))) { | ||
class Tooltip extends TooltipMixin(ThemePropertyMixin(ElementMixin(ControllerMixin(PolymerElement)))) { | ||
static get is() { | ||
@@ -297,589 +91,2 @@ return 'vaadin-tooltip'; | ||
} | ||
static get properties() { | ||
return { | ||
/** | ||
* Element used to link with the `aria-describedby` | ||
* attribute. Supports array of multiple elements. | ||
* When not set, defaults to `target`. | ||
*/ | ||
ariaTarget: { | ||
type: Object, | ||
}, | ||
/** | ||
* Object with properties passed to `generator` and | ||
* `shouldShow` functions for generating tooltip text | ||
* or detecting whether to show the tooltip or not. | ||
*/ | ||
context: { | ||
type: Object, | ||
value: () => { | ||
return {}; | ||
}, | ||
}, | ||
/** | ||
* The delay in milliseconds before the tooltip | ||
* is opened on keyboard focus, when not in manual mode. | ||
* @attr {number} focus-delay | ||
*/ | ||
focusDelay: { | ||
type: Number, | ||
}, | ||
/** | ||
* The id of the element used as a tooltip trigger. | ||
* The element should be in the DOM by the time when | ||
* the attribute is set, otherwise a warning is shown. | ||
*/ | ||
for: { | ||
type: String, | ||
observer: '__forChanged', | ||
}, | ||
/** | ||
* The delay in milliseconds before the tooltip | ||
* is closed on losing hover, when not in manual mode. | ||
* On blur, the tooltip is closed immediately. | ||
* @attr {number} hide-delay | ||
*/ | ||
hideDelay: { | ||
type: Number, | ||
}, | ||
/** | ||
* The delay in milliseconds before the tooltip | ||
* is opened on hover, when not in manual mode. | ||
* @attr {number} hover-delay | ||
*/ | ||
hoverDelay: { | ||
type: Number, | ||
}, | ||
/** | ||
* When true, the tooltip is controlled programmatically | ||
* instead of reacting to focus and mouse events. | ||
*/ | ||
manual: { | ||
type: Boolean, | ||
value: false, | ||
}, | ||
/** | ||
* When true, the tooltip is opened programmatically. | ||
* Only works if `manual` is set to `true`. | ||
*/ | ||
opened: { | ||
type: Boolean, | ||
value: false, | ||
}, | ||
/** | ||
* Position of the tooltip with respect to its target. | ||
* Supported values: `top-start`, `top`, `top-end`, | ||
* `bottom-start`, `bottom`, `bottom-end`, `start-top`, | ||
* `start`, `start-bottom`, `end-top`, `end`, `end-bottom`. | ||
*/ | ||
position: { | ||
type: String, | ||
}, | ||
/** | ||
* Function used to detect whether to show the tooltip based on a condition, | ||
* called every time the tooltip is about to be shown on hover and focus. | ||
* The function takes two parameters: `target` and `context`, which contain | ||
* values of the corresponding tooltip properties at the time of calling. | ||
* The tooltip is only shown when the function invocation returns `true`. | ||
*/ | ||
shouldShow: { | ||
type: Object, | ||
value: () => { | ||
return (_target, _context) => true; | ||
}, | ||
}, | ||
/** | ||
* Reference to the element used as a tooltip trigger. | ||
* The target must be placed in the same shadow scope. | ||
* Defaults to an element referenced with `for`. | ||
*/ | ||
target: { | ||
type: Object, | ||
observer: '__targetChanged', | ||
}, | ||
/** | ||
* String used as a tooltip content. | ||
*/ | ||
text: { | ||
type: String, | ||
observer: '__textChanged', | ||
}, | ||
/** | ||
* Function used to generate the tooltip content. | ||
* When provided, it overrides the `text` property. | ||
* Use the `context` property to provide argument | ||
* that can be passed to the generator function. | ||
*/ | ||
generator: { | ||
type: Object, | ||
}, | ||
/** | ||
* Set to true when the overlay is opened using auto-added | ||
* event listeners: mouseenter and focusin (keyboard only). | ||
* @protected | ||
*/ | ||
_autoOpened: { | ||
type: Boolean, | ||
observer: '__autoOpenedChanged', | ||
}, | ||
/** | ||
* Default value used when `position` property is not set. | ||
* @protected | ||
*/ | ||
_position: { | ||
type: String, | ||
value: 'bottom', | ||
}, | ||
/** | ||
* Element used to link with the `aria-describedby` | ||
* attribute. When not set, defaults to `target`. | ||
* @protected | ||
*/ | ||
_effectiveAriaTarget: { | ||
type: Object, | ||
computed: '__computeAriaTarget(ariaTarget, target)', | ||
observer: '__effectiveAriaTargetChanged', | ||
}, | ||
/** @private */ | ||
__effectivePosition: { | ||
type: String, | ||
computed: '__computePosition(position, _position)', | ||
}, | ||
/** @private */ | ||
__isTargetHidden: { | ||
type: Boolean, | ||
value: false, | ||
}, | ||
/** @private */ | ||
_isConnected: { | ||
type: Boolean, | ||
}, | ||
/** @private */ | ||
_srLabel: { | ||
type: Object, | ||
}, | ||
/** @private */ | ||
_overlayContent: { | ||
type: String, | ||
}, | ||
}; | ||
} | ||
static get observers() { | ||
return [ | ||
'__generatorChanged(_overlayElement, generator, context)', | ||
'__updateSrLabelText(_srLabel, _overlayContent)', | ||
]; | ||
} | ||
/** | ||
* Sets the default focus delay to be used by all tooltip instances, | ||
* except for those that have focus delay configured using property. | ||
* | ||
* @param {number} delay | ||
*/ | ||
static setDefaultFocusDelay(focusDelay) { | ||
defaultFocusDelay = focusDelay != null && focusDelay >= 0 ? focusDelay : DEFAULT_DELAY; | ||
} | ||
/** | ||
* Sets the default hide delay to be used by all tooltip instances, | ||
* except for those that have hide delay configured using property. | ||
* | ||
* @param {number} hideDelay | ||
*/ | ||
static setDefaultHideDelay(hideDelay) { | ||
defaultHideDelay = hideDelay != null && hideDelay >= 0 ? hideDelay : DEFAULT_DELAY; | ||
} | ||
/** | ||
* Sets the default hover delay to be used by all tooltip instances, | ||
* except for those that have hover delay configured using property. | ||
* | ||
* @param {number} delay | ||
*/ | ||
static setDefaultHoverDelay(hoverDelay) { | ||
defaultHoverDelay = hoverDelay != null && hoverDelay >= 0 ? hoverDelay : DEFAULT_DELAY; | ||
} | ||
constructor() { | ||
super(); | ||
this._uniqueId = `vaadin-tooltip-${generateUniqueId()}`; | ||
this._renderer = this.__tooltipRenderer.bind(this); | ||
this.__onFocusin = this.__onFocusin.bind(this); | ||
this.__onFocusout = this.__onFocusout.bind(this); | ||
this.__onMouseDown = this.__onMouseDown.bind(this); | ||
this.__onMouseEnter = this.__onMouseEnter.bind(this); | ||
this.__onMouseLeave = this.__onMouseLeave.bind(this); | ||
this.__onKeyDown = this.__onKeyDown.bind(this); | ||
this.__onOverlayOpen = this.__onOverlayOpen.bind(this); | ||
this.__targetVisibilityObserver = new IntersectionObserver( | ||
(entries) => { | ||
entries.forEach((entry) => this.__onTargetVisibilityChange(entry.isIntersecting)); | ||
}, | ||
{ threshold: 0 }, | ||
); | ||
this._stateController = new TooltipStateController(this); | ||
} | ||
/** @protected */ | ||
connectedCallback() { | ||
super.connectedCallback(); | ||
this._isConnected = true; | ||
document.body.addEventListener('vaadin-overlay-open', this.__onOverlayOpen); | ||
} | ||
/** @protected */ | ||
disconnectedCallback() { | ||
super.disconnectedCallback(); | ||
if (this._autoOpened) { | ||
this._stateController.close(true); | ||
} | ||
this._isConnected = false; | ||
document.body.removeEventListener('vaadin-overlay-open', this.__onOverlayOpen); | ||
} | ||
/** @protected */ | ||
ready() { | ||
super.ready(); | ||
this._srLabelController = new SlotController(this, 'sr-label', 'div', { | ||
initializer: (element) => { | ||
element.id = this._uniqueId; | ||
element.setAttribute('role', 'tooltip'); | ||
this._srLabel = element; | ||
}, | ||
}); | ||
this.addController(this._srLabelController); | ||
} | ||
/** @private */ | ||
__computeAriaTarget(ariaTarget, target) { | ||
const isElementNode = (el) => el && el.nodeType === Node.ELEMENT_NODE; | ||
const isAriaTargetSet = Array.isArray(ariaTarget) ? ariaTarget.some(isElementNode) : ariaTarget; | ||
return isAriaTargetSet ? ariaTarget : target; | ||
} | ||
/** @private */ | ||
__computeHorizontalAlign(position) { | ||
return ['top-end', 'bottom-end', 'start-top', 'start', 'start-bottom'].includes(position) ? 'end' : 'start'; | ||
} | ||
/** @private */ | ||
__computeNoHorizontalOverlap(position) { | ||
return ['start-top', 'start', 'start-bottom', 'end-top', 'end', 'end-bottom'].includes(position); | ||
} | ||
/** @private */ | ||
__computeNoVerticalOverlap(position) { | ||
return ['top-start', 'top-end', 'top', 'bottom-start', 'bottom', 'bottom-end'].includes(position); | ||
} | ||
/** @private */ | ||
__computeVerticalAlign(position) { | ||
return ['top-start', 'top-end', 'top', 'start-bottom', 'end-bottom'].includes(position) ? 'bottom' : 'top'; | ||
} | ||
/** @private */ | ||
__computeOpened(manual, opened, autoOpened, connected) { | ||
return connected && (manual ? opened : autoOpened); | ||
} | ||
/** @private */ | ||
__computePosition(position, defaultPosition) { | ||
return position || defaultPosition; | ||
} | ||
/** @private */ | ||
__tooltipRenderer(root) { | ||
root.textContent = typeof this.generator === 'function' ? this.generator(this.context) : this.text; | ||
// Update the sr-only label text content | ||
this._overlayContent = root.textContent; | ||
} | ||
/** @private */ | ||
__effectiveAriaTargetChanged(ariaTarget, oldAriaTarget) { | ||
if (oldAriaTarget) { | ||
[oldAriaTarget].flat().forEach((target) => { | ||
removeValueFromAttribute(target, 'aria-describedby', this._uniqueId); | ||
}); | ||
} | ||
if (ariaTarget) { | ||
[ariaTarget].flat().forEach((target) => { | ||
addValueToAttribute(target, 'aria-describedby', this._uniqueId); | ||
}); | ||
} | ||
} | ||
/** @private */ | ||
__autoOpenedChanged(opened, oldOpened) { | ||
if (opened) { | ||
document.addEventListener('keydown', this.__onKeyDown, true); | ||
} else if (oldOpened) { | ||
document.removeEventListener('keydown', this.__onKeyDown, true); | ||
} | ||
} | ||
/** @private */ | ||
__forChanged(forId) { | ||
if (forId) { | ||
this.__setTargetByIdDebouncer = Debouncer.debounce(this.__setTargetByIdDebouncer, microTask, () => | ||
this.__setTargetById(forId), | ||
); | ||
} | ||
} | ||
/** @private */ | ||
__setTargetById(targetId) { | ||
if (!this.isConnected) { | ||
return; | ||
} | ||
const target = this.getRootNode().getElementById(targetId); | ||
if (target) { | ||
this.target = target; | ||
} else { | ||
console.warn(`No element with id="${targetId}" found to show tooltip.`); | ||
} | ||
} | ||
/** @private */ | ||
__targetChanged(target, oldTarget) { | ||
if (oldTarget) { | ||
oldTarget.removeEventListener('mouseenter', this.__onMouseEnter); | ||
oldTarget.removeEventListener('mouseleave', this.__onMouseLeave); | ||
oldTarget.removeEventListener('focusin', this.__onFocusin); | ||
oldTarget.removeEventListener('focusout', this.__onFocusout); | ||
oldTarget.removeEventListener('mousedown', this.__onMouseDown); | ||
this.__targetVisibilityObserver.unobserve(oldTarget); | ||
} | ||
if (target) { | ||
target.addEventListener('mouseenter', this.__onMouseEnter); | ||
target.addEventListener('mouseleave', this.__onMouseLeave); | ||
target.addEventListener('focusin', this.__onFocusin); | ||
target.addEventListener('focusout', this.__onFocusout); | ||
target.addEventListener('mousedown', this.__onMouseDown); | ||
// Wait before observing to avoid Chrome issue. | ||
requestAnimationFrame(() => { | ||
this.__targetVisibilityObserver.observe(target); | ||
}); | ||
} | ||
} | ||
/** @private */ | ||
__onFocusin(event) { | ||
if (this.manual) { | ||
return; | ||
} | ||
// Only open on keyboard focus. | ||
if (!isKeyboardActive()) { | ||
return; | ||
} | ||
// Do not re-open while focused if closed on Esc or mousedown. | ||
if (this.target.contains(event.relatedTarget)) { | ||
return; | ||
} | ||
if (!this.__isShouldShow()) { | ||
return; | ||
} | ||
this.__focusInside = true; | ||
if (!this.__isTargetHidden && (!this.__hoverInside || !this._autoOpened)) { | ||
this._stateController.open({ focus: true }); | ||
} | ||
} | ||
/** @private */ | ||
__onFocusout(event) { | ||
if (this.manual) { | ||
return; | ||
} | ||
// Do not close when moving focus within a component. | ||
if (this.target.contains(event.relatedTarget)) { | ||
return; | ||
} | ||
this.__focusInside = false; | ||
if (!this.__hoverInside) { | ||
this._stateController.close(true); | ||
} | ||
} | ||
/** @private */ | ||
__onKeyDown(event) { | ||
if (event.key === 'Escape') { | ||
event.stopPropagation(); | ||
this._stateController.close(true); | ||
} | ||
} | ||
/** @private */ | ||
__onMouseDown() { | ||
this._stateController.close(true); | ||
} | ||
/** @private */ | ||
__onMouseEnter() { | ||
if (this.manual) { | ||
return; | ||
} | ||
if (!this.__isShouldShow()) { | ||
return; | ||
} | ||
if (this.__hoverInside) { | ||
// Already hovering inside the element, do nothing. | ||
return; | ||
} | ||
this.__hoverInside = true; | ||
if (!this.__isTargetHidden && (!this.__focusInside || !this._autoOpened)) { | ||
this._stateController.open({ hover: true }); | ||
} | ||
} | ||
/** @private */ | ||
__onMouseLeave(event) { | ||
if (event.relatedTarget !== this._overlayElement) { | ||
this.__handleMouseLeave(); | ||
} | ||
} | ||
/** @private */ | ||
__onOverlayMouseEnter() { | ||
// Retain opened state when moving pointer over the overlay. | ||
// Closing can start due to an offset between the target and | ||
// the overlay itself. If that's the case, re-open overlay. | ||
// See https://github.com/vaadin/web-components/issues/6316 | ||
if (this._stateController.isClosing) { | ||
this._stateController.open({ immediate: true }); | ||
} | ||
} | ||
/** @private */ | ||
__onOverlayMouseLeave(event) { | ||
if (event.relatedTarget !== this.target) { | ||
this.__handleMouseLeave(); | ||
} | ||
} | ||
/** @private */ | ||
__handleMouseLeave() { | ||
if (this.manual) { | ||
return; | ||
} | ||
this.__hoverInside = false; | ||
if (!this.__focusInside) { | ||
this._stateController.close(); | ||
} | ||
} | ||
/** @private */ | ||
__onOverlayOpen() { | ||
if (this.manual) { | ||
return; | ||
} | ||
// Close tooltip if another overlay is opened on top of the tooltip's overlay | ||
if (this._overlayElement.opened && !this._overlayElement._last) { | ||
this._stateController.close(true); | ||
} | ||
} | ||
/** @private */ | ||
__onTargetVisibilityChange(isVisible) { | ||
const oldHidden = this.__isTargetHidden; | ||
this.__isTargetHidden = !isVisible; | ||
// Open the overlay when the target becomes visible and has focus or hover. | ||
if (oldHidden && isVisible && (this.__focusInside || this.__hoverInside)) { | ||
this._stateController.open({ immediate: true }); | ||
return; | ||
} | ||
// Close the overlay when the target is no longer fully visible. | ||
if (!isVisible && this._autoOpened) { | ||
this._stateController.close(true); | ||
} | ||
} | ||
/** @private */ | ||
__isShouldShow() { | ||
if (typeof this.shouldShow === 'function' && this.shouldShow(this.target, this.context) !== true) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
/** @private */ | ||
__textChanged(text, oldText) { | ||
if (this._overlayElement && (text || oldText)) { | ||
this._overlayElement.requestContentUpdate(); | ||
} | ||
} | ||
/** @private */ | ||
__generatorChanged(overlayElement, generator, context) { | ||
if (overlayElement) { | ||
if (generator !== this.__oldTextGenerator || context !== this.__oldContext) { | ||
overlayElement.requestContentUpdate(); | ||
} | ||
this.__oldTextGenerator = generator; | ||
this.__oldContext = context; | ||
} | ||
} | ||
/** @private */ | ||
__updateSrLabelText(srLabel, textContent) { | ||
if (srLabel) { | ||
srLabel.textContent = textContent; | ||
} | ||
} | ||
} | ||
@@ -886,0 +93,0 @@ |
{ | ||
"$schema": "https://json.schemastore.org/web-types", | ||
"name": "@vaadin/tooltip", | ||
"version": "24.2.0-beta2", | ||
"version": "24.2.0-beta3", | ||
"description-markup": "markdown", | ||
@@ -11,3 +11,3 @@ "contributions": { | ||
"name": "vaadin-tooltip", | ||
"description": "`<vaadin-tooltip>` is a Web Component for creating tooltips.\n\n```html\n<button id=\"confirm\">Confirm</button>\n<vaadin-tooltip text=\"Click to save changes\" for=\"confirm\"></vaadin-tooltip>\n```\n\n### Styling\n\n`<vaadin-tooltip>` uses `<vaadin-tooltip-overlay>` internal\nthemable component as the actual visible overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-beta2/#/elements/vaadin-overlay) documentation\nfor `<vaadin-tooltip-overlay>` parts.\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|----------------------------------------\n`position` | Reflects the `position` property value.\n\nNote: the `theme` attribute value set on `<vaadin-tooltip>` is\npropagated to the internal `<vaadin-tooltip-overlay>` component.\n\n### Custom CSS Properties\n\nThe following custom CSS properties are available on the `<vaadin-tooltip>` element:\n\nCustom CSS property | Description\n---------------------------------|-------------\n`--vaadin-tooltip-offset-top` | Used as an offset when the tooltip is aligned vertically below the target\n`--vaadin-tooltip-offset-bottom` | Used as an offset when the tooltip is aligned vertically above the target\n`--vaadin-tooltip-offset-start` | Used as an offset when the tooltip is aligned horizontally after the target\n`--vaadin-tooltip-offset-end` | Used as an offset when the tooltip is aligned horizontally before the target\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.", | ||
"description": "`<vaadin-tooltip>` is a Web Component for creating tooltips.\n\n```html\n<button id=\"confirm\">Confirm</button>\n<vaadin-tooltip text=\"Click to save changes\" for=\"confirm\"></vaadin-tooltip>\n```\n\n### Styling\n\n`<vaadin-tooltip>` uses `<vaadin-tooltip-overlay>` internal\nthemable component as the actual visible overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-beta3/#/elements/vaadin-overlay) documentation\nfor `<vaadin-tooltip-overlay>` parts.\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|----------------------------------------\n`position` | Reflects the `position` property value.\n\nNote: the `theme` attribute value set on `<vaadin-tooltip>` is\npropagated to the internal `<vaadin-tooltip-overlay>` component.\n\n### Custom CSS Properties\n\nThe following custom CSS properties are available on the `<vaadin-tooltip>` element:\n\nCustom CSS property | Description\n---------------------------------|-------------\n`--vaadin-tooltip-offset-top` | Used as an offset when the tooltip is aligned vertically below the target\n`--vaadin-tooltip-offset-bottom` | Used as an offset when the tooltip is aligned vertically above the target\n`--vaadin-tooltip-offset-start` | Used as an offset when the tooltip is aligned horizontally after the target\n`--vaadin-tooltip-offset-end` | Used as an offset when the tooltip is aligned horizontally before the target\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.", | ||
"attributes": [ | ||
@@ -183,2 +183,13 @@ { | ||
{ | ||
"name": "generator", | ||
"description": "Function used to generate the tooltip content.\nWhen provided, it overrides the `text` property.\nUse the `context` property to provide argument\nthat can be passed to the generator function.", | ||
"value": { | ||
"type": [ | ||
"Object", | ||
"null", | ||
"undefined" | ||
] | ||
} | ||
}, | ||
{ | ||
"name": "hideDelay", | ||
@@ -270,13 +281,2 @@ "description": "The delay in milliseconds before the tooltip\nis closed on losing hover, when not in manual mode.\nOn blur, the tooltip is closed immediately.", | ||
} | ||
}, | ||
{ | ||
"name": "generator", | ||
"description": "Function used to generate the tooltip content.\nWhen provided, it overrides the `text` property.\nUse the `context` property to provide argument\nthat can be passed to the generator function.", | ||
"value": { | ||
"type": [ | ||
"Object", | ||
"null", | ||
"undefined" | ||
] | ||
} | ||
} | ||
@@ -283,0 +283,0 @@ ], |
{ | ||
"$schema": "https://json.schemastore.org/web-types", | ||
"name": "@vaadin/tooltip", | ||
"version": "24.2.0-beta2", | ||
"version": "24.2.0-beta3", | ||
"description-markup": "markdown", | ||
@@ -19,3 +19,3 @@ "framework": "lit", | ||
"name": "vaadin-tooltip", | ||
"description": "`<vaadin-tooltip>` is a Web Component for creating tooltips.\n\n```html\n<button id=\"confirm\">Confirm</button>\n<vaadin-tooltip text=\"Click to save changes\" for=\"confirm\"></vaadin-tooltip>\n```\n\n### Styling\n\n`<vaadin-tooltip>` uses `<vaadin-tooltip-overlay>` internal\nthemable component as the actual visible overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-beta2/#/elements/vaadin-overlay) documentation\nfor `<vaadin-tooltip-overlay>` parts.\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|----------------------------------------\n`position` | Reflects the `position` property value.\n\nNote: the `theme` attribute value set on `<vaadin-tooltip>` is\npropagated to the internal `<vaadin-tooltip-overlay>` component.\n\n### Custom CSS Properties\n\nThe following custom CSS properties are available on the `<vaadin-tooltip>` element:\n\nCustom CSS property | Description\n---------------------------------|-------------\n`--vaadin-tooltip-offset-top` | Used as an offset when the tooltip is aligned vertically below the target\n`--vaadin-tooltip-offset-bottom` | Used as an offset when the tooltip is aligned vertically above the target\n`--vaadin-tooltip-offset-start` | Used as an offset when the tooltip is aligned horizontally after the target\n`--vaadin-tooltip-offset-end` | Used as an offset when the tooltip is aligned horizontally before the target\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.", | ||
"description": "`<vaadin-tooltip>` is a Web Component for creating tooltips.\n\n```html\n<button id=\"confirm\">Confirm</button>\n<vaadin-tooltip text=\"Click to save changes\" for=\"confirm\"></vaadin-tooltip>\n```\n\n### Styling\n\n`<vaadin-tooltip>` uses `<vaadin-tooltip-overlay>` internal\nthemable component as the actual visible overlay.\n\nSee [`<vaadin-overlay>`](https://cdn.vaadin.com/vaadin-web-components/24.2.0-beta3/#/elements/vaadin-overlay) documentation\nfor `<vaadin-tooltip-overlay>` parts.\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n-----------------|----------------------------------------\n`position` | Reflects the `position` property value.\n\nNote: the `theme` attribute value set on `<vaadin-tooltip>` is\npropagated to the internal `<vaadin-tooltip-overlay>` component.\n\n### Custom CSS Properties\n\nThe following custom CSS properties are available on the `<vaadin-tooltip>` element:\n\nCustom CSS property | Description\n---------------------------------|-------------\n`--vaadin-tooltip-offset-top` | Used as an offset when the tooltip is aligned vertically below the target\n`--vaadin-tooltip-offset-bottom` | Used as an offset when the tooltip is aligned vertically above the target\n`--vaadin-tooltip-offset-start` | Used as an offset when the tooltip is aligned horizontally after the target\n`--vaadin-tooltip-offset-end` | Used as an offset when the tooltip is aligned horizontally before the target\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.", | ||
"extension": true, | ||
@@ -73,2 +73,9 @@ "attributes": [ | ||
{ | ||
"name": ".generator", | ||
"description": "Function used to generate the tooltip content.\nWhen provided, it overrides the `text` property.\nUse the `context` property to provide argument\nthat can be passed to the generator function.", | ||
"value": { | ||
"kind": "expression" | ||
} | ||
}, | ||
{ | ||
"name": ".hideDelay", | ||
@@ -114,9 +121,2 @@ "description": "The delay in milliseconds before the tooltip\nis closed on losing hover, when not in manual mode.\nOn blur, the tooltip is closed immediately.", | ||
} | ||
}, | ||
{ | ||
"name": ".generator", | ||
"description": "Function used to generate the tooltip content.\nWhen provided, it overrides the `text` property.\nUse the `context` property to provide argument\nthat can be passed to the generator function.", | ||
"value": { | ||
"kind": "expression" | ||
} | ||
} | ||
@@ -123,0 +123,0 @@ ] |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
73108
19
1593
8
1
+ Added@open-wc/dedupe-mixin@^1.3.0
+ Added@vaadin/a11y-base@24.2.0-beta3(transitive)
+ Added@vaadin/component-base@24.2.0-beta3(transitive)
+ Added@vaadin/icon@24.2.0-beta3(transitive)
+ Added@vaadin/overlay@24.2.0-beta3(transitive)
+ Added@vaadin/vaadin-lumo-styles@24.2.0-beta3(transitive)
+ Added@vaadin/vaadin-material-styles@24.2.0-beta3(transitive)
+ Added@vaadin/vaadin-themable-mixin@24.2.0-beta3(transitive)
- Removed@vaadin/a11y-base@24.2.0-beta2(transitive)
- Removed@vaadin/component-base@24.2.0-beta2(transitive)
- Removed@vaadin/icon@24.2.0-beta2(transitive)
- Removed@vaadin/overlay@24.2.0-beta2(transitive)
- Removed@vaadin/vaadin-lumo-styles@24.2.0-beta2(transitive)
- Removed@vaadin/vaadin-material-styles@24.2.0-beta2(transitive)
- Removed@vaadin/vaadin-themable-mixin@24.2.0-beta2(transitive)
Updated@vaadin/overlay@24.2.0-beta3