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
```
### Without arrow
```heex
<.tooltip id="my-tooltip" show_arrow={false}>
<:trigger>Hover me
<:content>No arrow
```
### Controlled
```heex
<.tooltip
id="my-tooltip"
controlled
open={@tooltip_open}
on_open_change="tooltip_changed">
<:trigger>Hover me
<:content>Tooltip content
```
## 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"`.
'''
@doc type: :component
use Phoenix.Component
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(:open, :boolean,
default: false,
doc: "The initial open state or the controlled open state"
)
attr(:controlled, :boolean,
default: false,
doc: "Whether the tooltip is controlled. In LiveView, on_open_change is required when true"
)
attr(:disabled, :boolean, default: false, doc: "Whether the tooltip is disabled")
attr(:dir, :string,
default: "ltr",
values: ["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(:placement, :string,
default: "bottom",
doc: "Placement of the tooltip (e.g. bottom, top-start). Uses floating-ui"
)
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. Default true"
)
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(: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