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-part
attribute 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; }>
string
number
number
boolean
boolean
boolean
boolean
boolean
(details: OpenChangeDetails) => void
string
PositioningOptions
boolean
boolean
boolean
"ltr" | "rtl"
() => ShadowRoot | Node | Document
Machine API
The tooltip api
exposes the following methods:
boolean
(open: boolean) => void
(options?: Partial<PositioningOptions>) => void
Data Attributes
Trigger
data-scope
data-part
data-expanded
data-state
Content
data-scope
data-part
data-state
data-placement
Accessibility
Keyboard Interaction
Tab
Escape