defmodule Corex.Tooltip do
@moduledoc ~S'''
Phoenix implementation of [Zag.js Tooltip](https://zagjs.com/components/react/tooltip).
## Examples
### Basic
```heex
<.tooltip id="my-tooltip">
<:trigger>Hover me
<:content>Tooltip content
```
### Multiple triggers (Zag)
One tooltip content, several triggers. Each `value` must be a stable non-empty string and unique within the component.
```heex
<.tooltip id="shared-tip" show_arrow={false}>
<:trigger value="a">First anchor
<:trigger value="b">Second anchor
<:content>Same panel for both triggers
```
For **multi-trigger** tooltips whose `<:content>` should follow the active trigger in **LiveView**, set **`on_trigger_value_change`** to a `handle_event/3` name and update assigns used inside `<:content>` (see the e2e **Tooltip · Pattern** page).
### Without arrow
```heex
<.tooltip id="my-tooltip" show_arrow={false}>
<:trigger>Hover me
<:content>No arrow
```
## API Control
Use an id on the component for API control.
**Client-side**
```heex
```
**Server-side**
```elixir
def handle_event("open_tooltip", _, socket) do
{:noreply, Corex.Tooltip.set_open(socket, "my-tooltip", true)}
end
```
## Styling
Target parts with `data-scope="tooltip"` and `data-part`:
```css
[data-scope="tooltip"][data-part="trigger"] {}
[data-scope="tooltip"][data-part="positioner"] {}
[data-scope="tooltip"][data-part="content"] {}
[data-scope="tooltip"][data-part="arrow"] {}
```
Trigger and content have `data-state="open"` or `data-state="closed"`.
On the root element, optional modifier classes:
- `tooltip--{semantic}` - surface and ink (for example `tooltip--accent`, `tooltip--brand`)
- `tooltip--size-{sm|md|lg|xl}` - max width, padding, and arrow scale
- `tooltip--text-{scale}` - content font size only (for example `tooltip--text-sm`, `tooltip--text-xl`)
'''
@doc type: :component
use Phoenix.Component
alias Corex.Positioning
alias Corex.Tooltip.Anatomy.{Arrow, ArrowTip, Content, Positioner, Props, Trigger}
alias Corex.Tooltip.Connect
alias Phoenix.LiveView
alias Phoenix.LiveView.JS
attr(:id, :string,
required: false,
doc: "The id of the tooltip, useful for API to identify the tooltip"
)
attr(:disabled, :boolean, default: false, doc: "Whether the tooltip is disabled")
attr(:dir, :string,
default: nil,
values: [nil, "ltr", "rtl"],
doc: "The direction of the tooltip. When nil, derived from document"
)
attr(:orientation, :string,
default: "horizontal",
values: ["horizontal", "vertical"],
doc: "Layout orientation for CSS."
)
attr(:open_delay, :integer,
default: 0,
doc: "Delay in ms before opening. Default from Zag is 400"
)
attr(:close_delay, :integer,
default: 0,
doc: "Delay in ms before closing. Default from Zag is 150"
)
attr(:positioning, Positioning,
default: %Positioning{},
doc: "Positioning options for the floating content"
)
attr(:close_on_escape, :boolean,
default: true,
doc: "Whether to close on Escape. Default true"
)
attr(:close_on_click, :boolean,
default: true,
doc: "Whether to close on click. Default true"
)
attr(:close_on_pointer_down, :boolean,
default: false,
doc: "Whether to close on pointer down on the trigger. Default false"
)
attr(:close_on_scroll, :boolean,
default: false,
doc: "Whether to close on scroll. Default false"
)
attr(:interactive, :boolean,
default: true,
doc: "Whether the tooltip content is interactive (stays open when hovering content)"
)
attr(:on_open_change, :string,
default: nil,
doc: "The server event name when the open state changes"
)
attr(:on_open_change_client, :string,
default: nil,
doc: "The client event name when the open state changes"
)
attr(:on_trigger_value_change, :string,
default: nil,
doc:
"LiveView event when the active trigger value changes (multi-trigger). Params include id (tooltip root) and value (trigger value string)."
)
attr(:show_arrow, :boolean,
default: true,
doc: "Whether to show an arrow pointing to the trigger"
)
attr(:trigger_tag, :atom,
default: :button,
values: [:button, :span],
doc:
"Use :span when the tooltip sits inside another button-like control (e.g. tree view row) to avoid nested