import { LitElement, customElement, html, property, query, unsafeCSS } from 'lit-element';
import { classMap } from 'lit-html/directives/class-map';
import { event, EventEmitter } from '../../internal/event';
import styles from 'sass:./tooltip.scss';
import Popover from '../../internal/popover';
let id = 0;
/**
* @since 2.0
* @status stable
*
* @slot - The tooltip's target element. Only the first element will be used as the target.
* @slot content - The tooltip's content. Alternatively, you can use the content prop.
*
* @part base - The component's base wrapper.
*/
@customElement('sl-tooltip')
export class SlTooltip extends LitElement {
static styles = unsafeCSS(styles);
@query('.tooltip-positioner') positioner: HTMLElement;
@query('.tooltip') tooltip: HTMLElement;
private componentId = `tooltip-${++id}`;
private target: HTMLElement;
private popover: Popover;
private isVisible = false;
/** The tooltip's content. Alternatively, you can use the content slot. */
@property() content = '';
/**
* The preferred placement of the tooltip. Note that the actual placement may vary as needed to keep the tooltip
* inside of the viewport.
*/
@property() placement:
| 'top'
| 'top-start'
| 'top-end'
| 'right'
| 'right-start'
| 'right-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'left'
| 'left-start'
| 'left-end' = 'top';
/** Disables the tooltip so it won't show when triggered. */
@property({ type: Boolean }) disabled = false;
/** The distance in pixels from which to offset the tooltip away from its target. */
@property({ type: Number }) distance = 10;
/** Indicates whether or not the tooltip is open. You can use this in lieu of the show/hide methods. */
@property({ type: Boolean }) open = false;
/** The distance in pixels from which to offset the tooltip along its target. */
@property({ type: Number }) skidding = 0;
/**
* Controls how the tooltip is activated. Possible options include `click`, `hover`, `focus`, and `manual`. Multiple
* options can be passed by separating them with a space. When manual is used, the tooltip must be activated
* programmatically.
*/
@property() trigger = 'hover focus';
/** Emitted when the tooltip begins to show. Calling `event.preventDefault()` will prevent it from being shown. */
@event('sl-show') slShow: EventEmitter;
/** Emitted after the tooltip has shown and all transitions are complete. */
@event('sl-after-show') slAfterShow: EventEmitter;
/** Emitted when the tooltip begins to hide. Calling `event.preventDefault()` will prevent it from being hidden. */
@event('sl-hide') slHide: EventEmitter;
/** Emitted after the tooltip has hidden and all transitions are complete. */
@event('sl-after-hide') slAfterHide: EventEmitter;
firstUpdated() {
this.target = this.getTarget();
this.popover = new Popover(this.target, this.positioner);
this.syncOptions();
this.addEventListener('blur', this.handleBlur.bind(this), true);
this.addEventListener('click', this.handleClick.bind(this), true);
this.addEventListener('focus', this.handleFocus.bind(this), true);
this.addEventListener('keydown', this.handleKeyDown.bind(this), true);
this.addEventListener('mouseover', this.handleMouseOver.bind(this), true);
this.addEventListener('mouseout', this.handleMouseOut.bind(this), true);
// Show on init if open
this.positioner.hidden = !this.open;
if (this.open) {
this.show();
}
}
update(changedProps: Map) {
super.update(changedProps);
if (['placement', 'disabled', 'distance', 'skidding'].find(prop => changedProps.has(prop))) {
this.syncOptions();
}
if (changedProps.has('open')) {
this.open ? this.show() : this.hide();
}
}
disconnectedCallback() {
super.disconnectedCallback();
this.popover.destroy();
this.removeEventListener('blur', this.handleBlur, true);
this.removeEventListener('click', this.handleClick, true);
this.removeEventListener('focus', this.handleFocus, true);
}
/** Shows the tooltip. */
show() {
// Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
if (this.isVisible) {
return;
}
const slShow = this.slShow.emit();
if (slShow.defaultPrevented) {
this.open = false;
return;
}
this.isVisible = true;
this.open = true;
this.popover.show();
}
/** Shows the tooltip. */
hide() {
// Prevent subsequent calls to the method, whether manually or triggered by the `open` watcher
if (!this.isVisible) {
return;
}
const slHide = this.slHide.emit();
if (slHide.defaultPrevented) {
this.open = true;
return;
}
this.isVisible = false;
this.open = false;
this.popover.hide();
}
getTarget() {
// Get the first child that isn't a