On this pageFunctions
Ui/Tooltip
/** Creates an initial tooltip model from a config. Defaults to hidden. */
(config: InitConfig): Tooltip.Model/**
* Processes a tooltip message and returns the next model, commands, and
* an optional OutMessage. `Shown`/`Hidden` fire only on `isOpen`
* transitions, so consumers don't get spurious events for messages that
* only update hover/focus/delay state without changing visibility.
*/
(
model: Tooltip.Model,
message: {
_tag: "EnteredTrigger"
} | {
_tag: "LeftTrigger"
} | {
_tag: "FocusedTrigger"
} | {
_tag: "BlurredTrigger"
} | {
_tag: "PressedEscape"
} | {
_tag: "PressedPointerOnTrigger"
button: number
pointerType: string
} | {
_tag: "ElapsedShowDelay"
version: number
} | {
_tag: "CompletedAnchorTooltip"
}
): UpdateReturn/** Configuration for creating a tooltip model with `init`. */
type InitConfig = Readonly<{
id: string
showDelay: Duration.Input
}>/**
* Render-time payload published to the consumer's `toView`.
*
* - `trigger`: attribute bundle for the trigger element. Carries the
* hover/focus/keyboard handlers + ARIA `aria-describedby` linking to
* the panel.
* - `panel`: attribute bundle for the panel element. Carries the
* `role="tooltip"`, the anchor Mount that positions the panel via
* Floating UI, and a `data-open` attribute when visible.
* - `isVisible`: derived state. The consumer decides whether to render
* the panel conditionally on this.
*/
type RenderInfo = Readonly<{
isVisible: boolean
panel: ReadonlyArray<ChildAttribute>
trigger: ReadonlyArray<ChildAttribute>
}>/** Per-render view inputs passed to `view` via `h.submodel`'s `viewInputs` field. */
type ViewInputs = Readonly<{
anchor: AnchorConfig
isDisabled: boolean
toView: (render: RenderInfo) => Html
}>/** The anchor-positioning Mount this Tooltip renders on its panel. */
const AnchorTooltip: MountDefinitionWithArgs<"AnchorTooltip", {
anchor: Struct<{
gap: optional<Number>
offset: optional<Number>
padding: optional<Number>
placement: optional<Literals<readonly ["top", "right", "bottom", "left", "top-start", "top-end", "right-start", "right-end", "bottom-start", "bottom-end", "left-start", "left-end"]>>
portal: optional<Boolean>
}>
buttonId: String
}, {
_tag: "CompletedAnchorTooltip"
}>/** Sent when focus leaves the trigger. */
const BlurredTrigger: CallableTaggedStruct<"BlurredTrigger", {}>/** Sent when the tooltip panel mounts and Floating UI has positioned it. */
const CompletedAnchorTooltip: CallableTaggedStruct<"CompletedAnchorTooltip", {}>/** Sent when the show-delay timer fires. */
const ElapsedShowDelay: CallableTaggedStruct<"ElapsedShowDelay", {
version: Number
}>/** Sent when the pointer enters the tooltip trigger. */
const EnteredTrigger: CallableTaggedStruct<"EnteredTrigger", {}>/** Sent when focus enters the trigger. */
const FocusedTrigger: CallableTaggedStruct<"FocusedTrigger", {}>/** Emitted once the tooltip transitions to hidden (`isOpen` becomes false). */
const Hidden: CallableTaggedStruct<"Hidden", {}>/** Sent when the pointer leaves the tooltip trigger. */
const LeftTrigger: CallableTaggedStruct<"LeftTrigger", {}>/** Union of all messages the tooltip component can produce. */
const Message: S.Union<[typeof EnteredTrigger, typeof LeftTrigger, typeof FocusedTrigger, typeof BlurredTrigger, typeof PressedEscape, typeof PressedPointerOnTrigger, typeof ElapsedShowDelay, typeof CompletedAnchorTooltip]>/** Schema for the tooltip component's state. `isOpen` is visibility; `isHovered` tracks pointer on trigger; `isFocused` tracks tooltip-affirming focus on the trigger (focus arriving without a preceding mouse press, like keyboard, touch, or pen; mouse-click-induced focus is excluded since it doesn't affirm the user wants the tooltip visible); `isDismissed` suppresses re-opening after the user dismissed the tooltip (via Escape or left-click) until they disengage (leave or blur). `showDelay` is the hover-to-show duration. `maybeLastPointerType` records the most recent pointer type that pressed the trigger, so a mouse-click-induced focus can be distinguished from other focus. */
const Model: Struct<{
id: String
isDismissed: Boolean
isFocused: Boolean
isHovered: Boolean
isOpen: Boolean
maybeLastPointerType: Option<String>
pendingShowVersion: Number
showDelay: DurationFromMillis
}>/** Union of out-messages the tooltip component can produce. */
const OutMessage: Union<readonly [CallableTaggedStruct<"Shown", {}>, CallableTaggedStruct<"Hidden", {}>]>/** Sent when Escape is pressed while the tooltip is visible. */
const PressedEscape: CallableTaggedStruct<"PressedEscape", {}>/** Sent when a pointer presses the trigger. */
const PressedPointerOnTrigger: CallableTaggedStruct<"PressedPointerOnTrigger", {
button: Number
pointerType: String
}>/** Waits for the tooltip's show delay before emitting `ElapsedShowDelay`. */
const ShowAfterDelay: CommandDefinitionWithArgs<"ShowAfterDelay", {
delay: DurationFromMillis
version: Number
}, Effect<{
_tag: "ElapsedShowDelay"
version: number
}, never, never>>/**
* Emitted once the tooltip transitions to visible (`isOpen` becomes true).
* Consumers typically use this for analytics, instrumentation, or to
* coordinate with other transient UI.
*/
const Shown: CallableTaggedStruct<"Shown", {}>/**
* Reflects an externally-sourced hover show-delay onto the model without
* emitting an OutMessage. Use to mirror an external config value (a user
* preference, a restored setting) onto the tooltip.
*/
const reflectShowDelay: Reflect<Model, Duration.Input>/**
* Renders a headless tooltip with an anchored non-interactive panel.
* Shows on hover (after delay) or focus (from keyboard, touch, or pen;
* mouse-click focus is excluded); hides on leave, blur, Escape, or
* left-click of the trigger.
*/
const view: SubmodelView<Tooltip.Model, {
_tag: "EnteredTrigger"
} | {
_tag: "LeftTrigger"
} | {
_tag: "FocusedTrigger"
} | {
_tag: "BlurredTrigger"
} | {
_tag: "PressedEscape"
} | {
_tag: "PressedPointerOnTrigger"
button: number
pointerType: string
} | {
_tag: "ElapsedShowDelay"
version: number
} | {
_tag: "CompletedAnchorTooltip"
}, Readonly<{
anchor: {
gap: number
offset: number
padding: number
placement: "bottom" | "left" | "right" | "top" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end"
portal: boolean
}
isDisabled: boolean
toView: (render: RenderInfo) => Html
}>>