149 lines
3.7 KiB
JavaScript
149 lines
3.7 KiB
JavaScript
import { css, LitElement } from 'lit';
|
|
import { ArrowMixin, OverlayMixin } from '@lion/ui/overlays.js';
|
|
|
|
/**
|
|
* @typedef {import('../../overlays/types/OverlayConfig.js').OverlayConfig} OverlayConfig
|
|
* @typedef {import('lit').CSSResult} CSSResult
|
|
* @typedef {import('lit').CSSResultArray} CSSResultArray
|
|
*/
|
|
|
|
/**
|
|
* @customElement lion-tooltip
|
|
*/
|
|
export class LionTooltip extends ArrowMixin(OverlayMixin(LitElement)) {
|
|
/** @type {any} */
|
|
static get properties() {
|
|
return {
|
|
invokerRelation: {
|
|
type: String,
|
|
attribute: 'invoker-relation',
|
|
},
|
|
};
|
|
}
|
|
|
|
static get styles() {
|
|
return [
|
|
...super.styles,
|
|
css`
|
|
:host {
|
|
display: inline-block;
|
|
}
|
|
|
|
:host([hidden]) {
|
|
display: none;
|
|
}
|
|
`,
|
|
];
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
/**
|
|
* Whether an arrow should be displayed
|
|
* @type {boolean}
|
|
*/
|
|
this.hasArrow = false;
|
|
/**
|
|
* Decides whether the tooltip invoker text should be considered a description
|
|
* (sets aria-describedby) or a label (sets aria-labelledby).
|
|
* @type {'label'|'description'}
|
|
*/
|
|
this.invokerRelation = 'description';
|
|
/** @protected */
|
|
this._mouseActive = false;
|
|
/** @protected */
|
|
this._keyActive = false;
|
|
}
|
|
|
|
/** @protected */
|
|
// eslint-disable-next-line class-methods-use-this
|
|
_defineOverlayConfig() {
|
|
return /** @type {OverlayConfig} */ ({
|
|
...super._defineOverlayConfig(),
|
|
placementMode: 'local',
|
|
elementToFocusAfterHide: undefined,
|
|
hidesOnEsc: true,
|
|
hidesOnOutsideEsc: true,
|
|
handlesAccessibility: true,
|
|
isTooltip: true,
|
|
invokerRelation: this.invokerRelation,
|
|
});
|
|
}
|
|
|
|
/** @protected */
|
|
_hasDisabledInvoker() {
|
|
if (this._overlayCtrl && this._overlayCtrl.invoker) {
|
|
return (
|
|
/** @type {HTMLElement & { disabled: boolean }} */ (this._overlayCtrl.invoker).disabled ||
|
|
this._overlayCtrl.invoker.getAttribute('aria-disabled') === 'true'
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** @protected */
|
|
_setupOpenCloseListeners() {
|
|
super._setupOpenCloseListeners();
|
|
this.__resetActive = this.__resetActive.bind(this);
|
|
this._overlayCtrl.addEventListener('hide', this.__resetActive);
|
|
|
|
this.addEventListener('mouseenter', this._showMouse);
|
|
this.addEventListener('mouseleave', this._hideMouse);
|
|
|
|
this._showKey = this._showKey.bind(this);
|
|
this._overlayInvokerNode.addEventListener('focusin', this._showKey);
|
|
|
|
this._hideKey = this._hideKey.bind(this);
|
|
this._overlayInvokerNode.addEventListener('focusout', this._hideKey);
|
|
}
|
|
|
|
/** @protected */
|
|
_teardownOpenCloseListeners() {
|
|
super._teardownOpenCloseListeners();
|
|
this._overlayCtrl.removeEventListener('hide', this.__resetActive);
|
|
this.removeEventListener('mouseenter', this._showMouse);
|
|
this.removeEventListener('mouseleave', this._hideMouse);
|
|
this._overlayInvokerNode.removeEventListener('focusin', this._showKey);
|
|
this._overlayInvokerNode.removeEventListener('focusout', this._hideKey);
|
|
}
|
|
|
|
/** @private */
|
|
__resetActive() {
|
|
this._mouseActive = false;
|
|
this._keyActive = false;
|
|
}
|
|
|
|
/** @protected */
|
|
_showMouse() {
|
|
if (!this._keyActive) {
|
|
this._mouseActive = true;
|
|
if (!this._hasDisabledInvoker()) {
|
|
this.opened = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @protected */
|
|
_hideMouse() {
|
|
if (!this._keyActive) {
|
|
this.opened = false;
|
|
}
|
|
}
|
|
|
|
/** @protected */
|
|
_showKey() {
|
|
if (!this._mouseActive) {
|
|
this._keyActive = true;
|
|
if (!this._hasDisabledInvoker()) {
|
|
this.opened = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @protected */
|
|
_hideKey() {
|
|
if (!this._mouseActive) {
|
|
this.opened = false;
|
|
}
|
|
}
|
|
}
|