Tooltip
A tooltip is a brief, informative message that appears when a user interacts with an element. Tooltips are usually initiated when a button is focused or hovered.
Features
Install
Install the component from your command line.
Anatomy
Import all parts and piece them together.
<script setup lang="ts">import * as tooltip from "@destyler/tooltip";import { normalizeProps, useMachine } from "@destyler/vue";import { computed, useId } from "vue";
const [state, send] = useMachine(tooltip.machine({ id: useId(), openDelay: 300, closeDelay: 100,}));const api = computed(() => tooltip.connect(state.value, send, normalizeProps));</script>
<template> <button v-bind="api.getTriggerProps()"></button> <div v-bind="api.getPositionerProps()"> <div v-bind="api.getContentProps()"></div> </div></template>import { normalizeProps, useMachine } from '@destyler/react'import * as tooltip from '@destyler/tooltip'import { useId } from 'react'
export default function TooltipDemo() { const id = useId() const [state, send] = useMachine(tooltip.machine({ id })) const api = tooltip.connect(state, send, normalizeProps)
return ( <> <button {...api.getTriggerProps()}></button> <div {...api.getPositionerProps()}> <div {...api.getContentProps()}></div> </div> </> )}<script lang="ts"> import * as tooltip from "@destyler/tooltip"; import { normalizeProps, useMachine } from "@destyler/svelte";
const id = $props.id(); const [state, send] = useMachine(tooltip.machine({ id }));
const api = $derived(tooltip.connect(state, send, normalizeProps));</script>
<button {...api.getTriggerProps()}></button><div {...api.getPositionerProps()}> <div {...api.getContentProps()}></div></div>import { normalizeProps, useMachine } from '@destyler/solid'import * as tooltip from '@destyler/tooltip'import { createMemo, createUniqueId } from 'solid-js'
export default function TooltipDemo() { const id = createUniqueId() const [state, send] = useMachine(tooltip.machine({ id })) const api = createMemo(()=>tooltip.connect(state, send, normalizeProps))
return ( <> <button {...api().getTriggerProps()}></button> <div {...api().getPositionerProps()}> <div {...api().getContentProps()}></div> </div> </> )}Customizing the timings
By default, the tooltip is designed to open after 1000ms and close after 500ms.
You can customize this by passing the openDelay and closeDelay context properties.
const [state, send] = useMachine( tooltip.machine({ openDelay: 500, closeDelay: 200, }),)Changing the placement
The tooltip uses floating-ui for dynamic positioning.
You can change the placement of the tooltip by passing the positioning.placement context property to the machine.
const [state, send] = useMachine( tooltip.machine({ positioning: { placement: "bottom-start", }, }),)You can configure other position-related properties in the positioning object.
Here’s what the positioning API looks like:
interface PositioningOptions { /** * Whether the popover should be hidden when the reference element is detached */ hideWhenDetached?: boolean | undefined; /** * The strategy to use for positioning */ strategy?: "absolute" | "fixed" | undefined; /** * The initial placement of the floating element */ placement?: Placement | undefined; /** * The offset of the floating element */ offset?: { mainAxis?: number; crossAxis?: number; } | undefined; /** * The main axis offset or gap between the reference and floating elements */ gutter?: number | undefined; /** * The secondary axis offset or gap between the reference and floating elements */ shift?: number | undefined; /** * The virtual padding around the viewport edges to check for overflow */ overflowPadding?: number | undefined; /** * The minimum padding between the arrow and the floating element's corner. * @default 4 */ arrowPadding?: number | undefined; /** * Whether to flip the placement */ flip?: boolean | Placement[] | undefined; /** * Whether the popover should slide when it overflows. */ slide?: boolean | undefined; /** * Whether the floating element can overlap the reference element * @default false */ overlap?: boolean | undefined; /** * Whether to make the floating element same width as the reference element */ sameWidth?: boolean | undefined; /** * Whether the popover should fit the viewport. */ fitViewport?: boolean | undefined; /** * The overflow boundary of the reference element */ boundary?: (() => Boundary) | undefined; /** * Options to activate auto-update listeners */ listeners?: boolean | AutoUpdateOptions | undefined; /** * Function called when the placement is computed */ onComplete?: ((data: ComputePositionReturn) => void) | undefined; /** * Function called when the floating element is positioned or not */ onPositioned?: ((data: { placed: boolean; }) => void) | undefined; /** * Function that returns the anchor rect */ getAnchorRect?: ((element: HTMLElement | VirtualElement | null) => AnchorRect | null) | undefined; /** * A callback that will be called when the popover needs to calculate its * position. */ updatePosition?: ((data: { updatePosition: () => Promise<void>; }) => void | Promise<void>) | undefined;}Adding an arrow
To render an arrow within the tooltip, use the api.getArrowProps() and api.getArrowTipProps().
<script setup lang="ts">import * as tooltip from "@destyler/tooltip";import { normalizeProps, useMachine } from "@destyler/vue";import { computed, useId } from "vue";
const [state, send] = useMachine(tooltip.machine({ id: useId(), openDelay: 300, closeDelay: 100,}));const api = computed(() => tooltip.connect(state.value, send, normalizeProps));</script>
<template> <button v-bind="api.getTriggerProps()"></button> <div v-bind="api.getPositionerProps()"> <div v-bind="api.getArrowProps()"> <div v-bind="api.getArrowTipProps()" /> </div> <div v-bind="api.getContentProps()"></div> </div></template>import { normalizeProps, useMachine } from '@destyler/react'import * as tooltip from '@destyler/tooltip'import { useId } from 'react'
export default function TooltipDemo() { const id = useId() const [state, send] = useMachine(tooltip.machine({ id })) const api = tooltip.connect(state, send, normalizeProps)
return ( <> <button {...api.getTriggerProps()}></button> <div {...api.getPositionerProps()}> <div {...api.getArrowProps()}> <div {...api.getArrowTipProps()} /> </div> <div {...api.getContentProps()}></div> </div> </> )}<script lang="ts"> import * as tooltip from "@destyler/tooltip"; import { normalizeProps, useMachine } from "@destyler/svelte";
const id = $props.id(); const [state, send] = useMachine(tooltip.machine({ id }));
const api = $derived(tooltip.connect(state, send, normalizeProps));</script>
<button {...api.getTriggerProps()}></button><div {...api.getPositionerProps()}> <div {...api.getArrowProps()}> <div {...api.getArrowTipProps()} /> </div> <div {...api.getContentProps()}></div></div>import { normalizeProps, useMachine } from '@destyler/solid'import * as tooltip from '@destyler/tooltip'import { createMemo, createUniqueId } from 'solid-js'
export default function TooltipDemo() { const id = createUniqueId() const [state, send] = useMachine(tooltip.machine({ id })) const api = createMemo(()=>tooltip.connect(state, send, normalizeProps))
return ( <> <button {...api().getTriggerProps()}></button> <div {...api().getPositionerProps()}> <div {...api().getArrowProps()}> <div {...api().getArrowTipProps()} /> </div> <div {...api().getContentProps()}></div> </div> </> )}Pointerdown behavior
By default, the tooltip will close when the pointer is down on its trigger.
To prevent this behavior,
pass the closeOnPointerDown context property and set it to false.
const [state, send] = useMachine( tooltip.machine({ closeOnPointerDown: false, }),)Close on esc
The tooltip is designed to close when the escape key is pressed.
To prevent this, pass the closeOnEscape context property and set it to false.
const [state, send] = useMachine( tooltip.machine({ closeOnEsc: false, }),)Making the tooltip interactive
Set the interactive context property to true to make them interactive.
When a tooltip is interactive, it’ll remain open even the pointer leaves the trigger and move into the tooltip’s content.
const [state, send] = useMachine( tooltip.machine({ interactive: true, }),)Listening for open state changes
When the tooltip is opened or closed, the onOpenChange callback is invoked.
const [state, send] = useMachine( tooltip.machine({ onOpenChange(details) { // details => { open: boolean } console.log(details.open) }, }),)Styling guide
Earlier, we mentioned that each Tooltip part has a
data-partattribute added to them to select and style them in the DOM.
[data-part="trigger"] { /* styles for the content */}
[data-part="content"] { /* styles for the content */}Open and close states
When the tooltip is open, the data-state attribute is added to the trigger
[data-part="trigger"][data-state="open"] { /* styles for the trigger's expanded state */}
[data-part="content"][data-state="open"] { /* styles for the trigger's expanded state */}Styling the arrow
When using arrows within the menu, you can style it using css variables.
[data-part="arrow"] { --arrow-size: 20px; --arrow-background: red;}Methods and Properties
Machine Context
The tooltip machine exposes the following context properties:
Partial<{ trigger: string; content: string; arrow: string; positioner: string; }>stringnumbernumberbooleanbooleanbooleanbooleanboolean(details: OpenChangeDetails) => voidstringPositioningOptionsbooleanbooleanboolean"ltr" | "rtl"() => ShadowRoot | Node | DocumentMachine API
The tooltip api exposes the following methods:
boolean(open: boolean) => void(options?: Partial<PositioningOptions>) => voidData Attributes
Trigger
data-scopedata-partdata-expandeddata-stateContent
data-scopedata-partdata-statedata-placementAccessibility
Keyboard Interaction
TabEscape