UNPKG

@limetech/lime-elements

Version:
231 lines (230 loc) • 9.87 kB
import { h } from "@stencil/core"; import { createRandomString } from "../../util/random-string"; import { getOwnerElement } from "./get-owner-element"; import { TooltipTimer } from "./tooltip-timer"; const DEFAULT_MAX_LENGTH = 50; /** * A tooltip can be used to display a descriptive text for any element. * The displayed content must be a brief and supplemental string of text, * identifying the element or describing its function for the user, * helping them better understand unfamiliar objects that aren't described * directly in the UI. * * ## Interaction * The tooltip appears after a slight delay, when the element is hovered; * and disappears as soon as the cursor leaves the element. * Therefore, users cannot interact with the tip, but if the trigger element * itself is interactive, it will remain interactible even with a tooltip bound * to it. * * :::note * In order to display the tooltip, the tooltip element and its trigger element * must be within the same document or document fragment (the same shadowRoot). * Often, it's easiest to just place them next to each other like in the example * below, but if you need to, you can place them differently. * * ```html * <limel-button icon="search" id="tooltip-example" /> * <limel-tooltip label="Search" elementId="tooltip-example" /> * ``` * ::: * * ## Usage * - Keep in mind that tooltips can be distracting, and can be perceived as an interruption. * Use them only when they add significant value. * - A good tip is concise, helpful, and informative. * Don't explain the obvious or simply repeat what is already on the screen. * When used correctly, supplemental info of a tooltip helps to [declutter the UI](#/DesignGuidelines/decluttering.md/). * - If the tip is essential to the primary tasks that the user is performing, * such as warnings or important notes, include the information directly in the * interface instead. * - When a component offers a helper text (e.g. [Input field](#/component/limel-input-field/)), * use that, not a tooltip. * - Make sure to use the tooltip on an element that users naturally and * effortlessly recognize can be hovered. * * @exampleComponent limel-example-tooltip-basic * @exampleComponent limel-example-tooltip-max-character * @exampleComponent limel-example-tooltip-composite */ export class Tooltip { constructor() { /** * The maximum amount of characters before rendering 'label' and * 'helperLabel' in two rows. */ this.maxlength = DEFAULT_MAX_LENGTH; /** * Decides the tooltip's location in relation to its trigger. */ this.openDirection = 'top'; this.showTooltip = () => { this.tooltipTimer.showAfterDelay(); }; this.hideTooltip = () => { this.tooltipTimer.hide(); }; this.portalId = createRandomString(); this.tooltipId = createRandomString(); this.tooltipTimer = new TooltipTimer(() => (this.open = true), () => (this.open = false)); } connectedCallback() { this.ownerElement = getOwnerElement(this.elementId, this.host); this.setOwnerAriaLabel(); this.addListeners(); } disconnectedCallback() { this.removeListeners(); } render() { const tooltipZIndex = getComputedStyle(this.host).getPropertyValue('--tooltip-z-index'); return (h("div", { key: 'ffc3b079a0fcb1a4065c176228d720c64ca57d2e', class: "trigger-anchor" }, h("limel-portal", { key: 'e18fa14f5c328485f6755bc62c402c16d6dd0fd1', openDirection: this.openDirection, visible: this.open, containerId: this.portalId, containerStyle: { 'z-index': tooltipZIndex, 'pointer-events': 'none', }, anchor: this.ownerElement }, h("limel-tooltip-content", { key: 'cc3a5f98a22082b4e8c2d0ec6bd71d5cf5770c0a', label: this.label, helperLabel: this.helperLabel, maxlength: this.maxlength, role: "tooltip", "aria-hidden": !this.open, id: this.tooltipId })))); } setOwnerAriaLabel() { var _a; (_a = this.ownerElement) === null || _a === void 0 ? void 0 : _a.setAttribute('aria-describedby', this.tooltipId); } addListeners() { var _a, _b, _c, _d; (_a = this.ownerElement) === null || _a === void 0 ? void 0 : _a.addEventListener('mouseover', this.showTooltip); (_b = this.ownerElement) === null || _b === void 0 ? void 0 : _b.addEventListener('mouseout', this.hideTooltip); (_c = this.ownerElement) === null || _c === void 0 ? void 0 : _c.addEventListener('focus', this.showTooltip); (_d = this.ownerElement) === null || _d === void 0 ? void 0 : _d.addEventListener('blur', this.hideTooltip); } removeListeners() { var _a, _b, _c, _d; (_a = this.ownerElement) === null || _a === void 0 ? void 0 : _a.removeEventListener('mouseover', this.showTooltip); (_b = this.ownerElement) === null || _b === void 0 ? void 0 : _b.removeEventListener('mouseout', this.hideTooltip); (_c = this.ownerElement) === null || _c === void 0 ? void 0 : _c.removeEventListener('focus', this.showTooltip); (_d = this.ownerElement) === null || _d === void 0 ? void 0 : _d.removeEventListener('blur', this.hideTooltip); } static get is() { return "limel-tooltip"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["tooltip.scss"] }; } static get styleUrls() { return { "$": ["tooltip.css"] }; } static get properties() { return { "elementId": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": true, "optional": false, "docs": { "tags": [], "text": "ID of the owner element that the tooltip should describe.\nMust be a child within the same document fragment as the tooltip element\nitself." }, "getter": false, "setter": false, "reflect": true, "attribute": "element-id" }, "label": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": true, "optional": false, "docs": { "tags": [], "text": "Short descriptive text of the owner element." }, "getter": false, "setter": false, "reflect": true, "attribute": "label" }, "helperLabel": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Additional helper text for the element.\nExample usage can be a keyboard shortcut to activate the function of the\nowner element." }, "getter": false, "setter": false, "reflect": true, "attribute": "helper-label" }, "maxlength": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "The maximum amount of characters before rendering 'label' and\n'helperLabel' in two rows." }, "getter": false, "setter": false, "reflect": true, "attribute": "maxlength", "defaultValue": "DEFAULT_MAX_LENGTH" }, "openDirection": { "type": "string", "mutable": false, "complexType": { "original": "OpenDirection", "resolved": "\"bottom\" | \"bottom-end\" | \"bottom-start\" | \"left\" | \"left-end\" | \"left-start\" | \"right\" | \"right-end\" | \"right-start\" | \"top\" | \"top-end\" | \"top-start\"", "references": { "OpenDirection": { "location": "import", "path": "../menu/menu.types", "id": "src/components/menu/menu.types.ts::OpenDirection", "referenceLocation": "OpenDirection" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Decides the tooltip's location in relation to its trigger." }, "getter": false, "setter": false, "reflect": true, "attribute": "open-direction", "defaultValue": "'top'" } }; } static get states() { return { "open": {} }; } static get elementRef() { return "host"; } }