# or
yarn add @zag-js/popover @zag-js/vue
```

## Anatomy

To set up the popover correctly, you'll need to understand its anatomy and how
we name its parts.

> Each part includes a `data-part` attribute to help identify them in the DOM.



## Usage

First, import the popover package into your project

```jsx
import * as popover from "@zag-js/popover"
```

The popover package exports two key functions:

- `machine` â The state machine logic for the popover widget.
- `connect` â The function that translates the machine's state to JSX attributes
  and event handlers.

> You'll need to provide a unique `id` to the `useMachine` hook. This is used to
> ensure that every part has a unique identifier.

Next, import the required hooks and functions for your framework and use the
popover machine in your project ð¥

```html
<script setup>
  import * as popover from "@zag-js/popover"
  import { normalizeProps, useMachine } from "@zag-js/vue"
  import { computed, Teleport, Fragment } from "vue"

  const service = useMachine(popover.machine, { id: "1" })
  const api = computed(() => popover.connect(service, normalizeProps))
</script>

<template>
  <div ref="ref">
    <button v-bind="api.getTriggerProps()">Click me</button>
    <Teleport to="body" :disabled="!api.portalled">
      <div v-bind="api.getPositionerProps()">
        <div v-bind="api.getContentProps()">
          <div v-bind="api.getTitleProps()">Presenters</div>
          <div v-bind="api.getDescriptionProps()">Description</div>
          <button>Action Button</button>
          <button v-bind="api.getCloseTriggerProps()">X</button>
        </div>
      </div>
    </Teleport>
  </div>
</template>
```

### Rendering the popover in a portal

By default, the popover is rendered in the same DOM hierarchy as the trigger. To
render the popover within a portal, pass `portalled: true` property to the
machine's context.

> Note: This requires that you render the component within a `Portal` based on
> the framework you use.

```html
<script setup>
  import * as popover from "@zag-js/popover"
  import { normalizeProps, useMachine } from "@zag-js/vue"
  import { computed, Teleport } from "vue"

  const service = useMachine(popover.machine, { id: "1" })

  const api = computed(() => popover.connect(service, normalizeProps))
</script>

<template>
  <div ref="ref">
    <button v-bind="api.getTriggerProps()">Click me</button>
    <Teleport to="body">
      <div v-bind="api.getPositionerProps()">
        <div v-bind="api.getContentProps()">
          <div v-bind="api.getTitleProps()">Presenters</div>
          <div v-bind="api.getDescriptionProps()">Description</div>
          <button>Action Button</button>
          <button v-bind="api.getCloseTriggerProps()">X</button>
        </div>
      </div>
    </Teleport>
  </div>
</template>
```

### Managing focus within popover

When the popover open, focus is automatically set to the first focusable element
within the popover. To customize the element that should get focus, set the
`initialFocusEl` property in the machine's context.

```html
<script setup>
  import { ref } from "vue"

  // initial focused element ref
  const inputRef = ref(null)

  const service = useMachine(popover.machine, {
    initialFocusEl: () => inputRef.value,
  })
</script>

<template>
  <input ref="inputRef" />
</template>
```

### Changing the modality

In some cases, you might want the popover to be **modal**. This means that
it'll:

- trap focus within its content
- block scrolling on the `body`
- disable pointer interactions outside the popover
- hide content behind the popover from screen readers

To make the popover modal, set the `modal: true` property in the machine's
context. When `modal: true`, we set the `portalled` attribute to `true` as well.

> **Note**: This requires that you render the component within a `Portal`.

```jsx {2}
const service = useMachine(popover.machine, {
  modal: true,
})
```

### Close behavior

The popover is designed to close on blur and when the `esc` key is pressed.

To prevent it from closing on blur (clicking or focusing outside), pass the
`closeOnInteractOutside` property and set it to `false`.

```jsx {2}
const service = useMachine(popover.machine, {
  closeOnInteractOutside: true,
})
```

To prevent it from closing when the `esc` key is pressed, pass the
`closeOnEscape` property and set it to `false`.

```jsx {2}
const service = useMachine(popover.machine, {
  closeOnEscape: true,
})
```

### Adding an arrow

To render an arrow within the popover, use the `api.getArrowProps()` and
`api.getArrowTipProps()`.

```jsx {6-8}
//...
const api = popover.connect(service, normalizeProps)
//...
return (
  <div {...api.getPositionerProps()}>
    <div {...api.getContentProps()}>
      <div {...api.getArrowProps()}>
        <div {...api.getArrowTipProps()} />
      </div>
      //...
    </div>
  </div>
)
//...
```

### Changing the placement

To change the placement of the popover, set the `positioning.placement` property
in the machine's context.

```jsx {2-4}
const service = useMachine(popover.machine, {
  positioning: {
    placement: "top-start",
  },
})
```

### Listening for open state changes

When the popover is opened or closed, the `onOpenChange` callback is invoked.

```jsx {2-7}
const service = useMachine(popover.machine, {
  onOpenChange(details) {
    // details => { open: boolean }
    console.log("Popover", details.open)
  },
})
```

### Usage within dialog

When using the popover within a dialog, avoid rendering the popover in a
`Portal` or `Teleport`. This is because the dialog will trap focus within it,
and the popover will be rendered outside the dialog.

## Styling guide

Earlier, we mentioned that each popover part has a `data-part` attribute added
to them to select and style them in the DOM.

### Open and closed state

When the popover is expanded, we add a `data-state` and `data-placement`
attribute to the trigger.

```css
[data-part="trigger"][data-state="open|closed"] {
  /* styles for the expanded state */
}

[data-part="content"][data-state="open|closed"] {
  /* styles for the expanded state */
}

[data-part="trigger"][data-placement="(top|bottom)-(start|end)"] {
  /* styles for computed placement */
}
```

### Position aware

When the popover is expanded, we add a `data-state` and `data-placement`
attribute to the trigger.

```css
[data-part="trigger"][data-placement="(top|bottom)-(start|end)"] {
  /* styles for computed placement */
}

[data-part="content"][data-placement="(top|bottom)-(start|end)"] {
  /* styles for computed placement */
}
```

### Arrow

The arrow element requires specific css variables to be set for it to show
correctly.

```css
[data-part="arrow"] {
  --arrow-background: white;
  --arrow-size: 16px;
}
```

A common technique for adding a shadow to the arrow is to use set
`filter: drop-down(...)` css property on the content element. Alternatively, you
can use the `--arrow-shadow-color` variable.

```css
[data-part="arrow"] {
  --arrow-shadow-color: gray;
}
```

## Methods and Properties

### Machine Context

The popover machine exposes the following context properties:

**`ids`**
Type: `Partial<{ anchor: string; trigger: string; content: string; title: string; description: string; closeTrigger: string; positioner: string; arrow: string; }>`
Description: The ids of the elements in the popover. Useful for composition.

**`modal`**
Type: `boolean`
Description: Whether the popover should be modal. When set to `true`:
- interaction with outside elements will be disabled
- only popover content will be visible to screen readers
- scrolling is blocked
- focus is trapped within the popover

**`portalled`**
Type: `boolean`
Description: Whether the popover is portalled. This will proxy the tabbing behavior regardless of the DOM position
of the popover content.

**`autoFocus`**
Type: `boolean`
Description: Whether to automatically set focus on the first focusable
content within the popover when opened.

**`initialFocusEl`**
Type: `() => HTMLElement`
Description: The element to focus on when the popover is opened.

**`closeOnInteractOutside`**
Type: `boolean`
Description: Whether to close the popover when the user clicks outside of the popover.

**`closeOnEscape`**
Type: `boolean`
Description: Whether to close the popover when the escape key is pressed.

**`onOpenChange`**
Type: `(details: OpenChangeDetails) => void`
Description: Function invoked when the popover opens or closes

**`positioning`**
Type: `PositioningOptions`
Description: The user provided options used to position the popover content

**`open`**
Type: `boolean`
Description: The controlled open state of the popover

**`defaultOpen`**
Type: `boolean`
Description: The initial open state of the popover when rendered.
Use when you don't need to control the open state of the popover.

**`id`**
Type: `string`
Description: The unique identifier of the machine.

**`getRootNode`**
Type: `() => Node | ShadowRoot | Document`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.

**`dir`**
Type: `"ltr" | "rtl"`
Description: The document's text/writing direction.

**`onEscapeKeyDown`**
Type: `(event: KeyboardEvent) => void`
Description: Function called when the escape key is pressed

**`onRequestDismiss`**
Type: `(event: LayerDismissEvent) => void`
Description: Function called when this layer is closed due to a parent layer being closed

**`onPointerDownOutside`**
Type: `(event: PointerDownOutsideEvent) => void`
Description: Function called when the pointer is pressed down outside the component

**`onFocusOutside`**
Type: `(event: FocusOutsideEvent) => void`
Description: Function called when the focus is moved outside the component

**`onInteractOutside`**
Type: `(event: InteractOutsideEvent) => void`
Description: Function called when an interaction happens outside the component

**`persistentElements`**
Type: `(() => Element)[]`
Description: Returns the persistent elements that:
- should not have pointer-events disabled
- should not trigger the dismiss event

### Machine API

The popover `api` exposes the following methods:

**`portalled`**
Type: `boolean`
Description: Whether the popover is portalled.

**`open`**
Type: `boolean`
Description: Whether the popover is open

**`setOpen`**
Type: `(open: boolean) => void`
Description: Function to open or close the popover

**`reposition`**
Type: `(options?: Partial<PositioningOptions>) => void`
Description: Function to reposition the popover

### Data Attributes

**`Trigger`**

**`data-scope`**: popover
**`data-part`**: trigger
**`data-placement`**: The placement of the trigger
**`data-state`**: "open" | "closed"

**`Indicator`**

**`data-scope`**: popover
**`data-part`**: indicator
**`data-state`**: "open" | "closed"

**`Content`**

**`data-scope`**: popover
**`data-part`**: content
**`data-state`**: "open" | "closed"
**`data-nested`**: popover
**`data-has-nested`**: popover
**`data-expanded`**: Present when expanded
**`data-placement`**: The placement of the content

### CSS Variables

<CssVarTable name="popover" />

## Accessibility

Adheres to the
[Dialog WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal).

### Keyboard Interactions

**`Space`**
Description: Opens/closes the popover.

**`Enter`**
Description: Opens/closes the popover.

**`Tab`**
Description: <span>Moves focus to the next focusable element within the content.<br /><strong>Note:</strong> If there are no focusable elements, focus is moved to the next focusable element after the trigger.</span>

**`Shift + Tab`**
Description: <span>Moves focus to the previous focusable element within the content<br /><strong>Note:</strong> If there are no focusable elements, focus is moved to the trigger.</span>

**`Esc`**
Description: <span>Closes the popover and moves focus to the trigger.</span>
The presence machine is a state machine that helps you manage exit animations in
your project.

In the DOM, when a component is removed from the tree or hidden, it's removed
instantly. The presence machine suspends the component to allow its exit
animation before removing or hiding it.

> The presence machine requires using **CSS animations** to animate the
> component's exit.

## Resources


[Latest version: v1.33.1](https://www.npmjs.com/package/@zag-js/presence)
[Logic Visualizer](https://zag-visualizer.vercel.app/presence)
[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/presence)



## Installation

To use the presence machine in your project, run the following command in your
command line:

```bash
npm install @zag-js/presence @zag-js/vue