# useFocusTrap

## Usage

Traps focus within a container element following the WAI-ARIA dialog focus trap pattern. While the trap is active it listens to `keydown` on the document and wraps Tab and Shift+Tab, so focus cycles inside the container instead of leaving it. Turning the trap on also moves focus in: the element returned by `initialFocus`, or else the first focusable element. Turning it off is the whole teardown, so a dialog can toggle the state without unmounting the hook.

When the trap deactivates or unmounts, focus goes back to the element that was focused before activation. It stays put when focus was already moved elsewhere, or when focus never entered the trap — so a popup that keeps focus on its trigger, like a combobox, is left alone. Focus returned after a pointer close lands on the trigger without a focus ring; after a keyboard close the ring shows as usual.

Tab is cancelled only while focus is inside the container, so a layer that is open while focus legitimately sits outside it does not take Tab away from the page. When initial focus sits on the container itself, Tab moves into the tabbable cycle. An Escape that cancels an input-method composition does not call `onEscape`. Mouse clicks outside the container are not intercepted; use a light-dismiss handler for that.

```ts
import { useFocusTrap } from '@astryx-vue/core'
```

```vue
<script setup lang="ts">
import { useFocusTrap } from '@astryx-vue/core'
import { ref } from 'vue'

const containerRef = ref<HTMLElement | null>(null)
const isOpen = ref(false)

const trap = useFocusTrap({
  container: containerRef,
  isActive: isOpen,
  onEscape: () => { isOpen.value = false },
})
</script>

<template>
  <div ref="containerRef" role="dialog">
    <button @click="trap.focusFirst()">
      First field
    </button>
  </div>
</template>
```

## Best practices

| Guidance | Practices |
| --- | --- |
| Do | Call `focusFirst()` when opening a dialog/modal to move focus into the trapped region. |
| Do | Provide an `onEscape` callback to close the dialog when Escape is pressed. |
| Do | Rely on the built-in focus restoration on close; only add your own focus handling when you need to send focus somewhere other than the previously-focused element. |
| Don't | Use on non-modal content like tooltips or dropdowns; those need light-dismiss, not focus trapping. |

## Parameters

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `options` | `UseFocusTrapOptions` | `{}` | Configuration object for the focus trap. Every field inside it is optional. |
| `options.container` | `Ref` | — | The element that traps focus. Bind the same template ref in the template. Leave it out and the hook creates its own, returned as `containerRef`. |
| `options.isActive` | `MaybeRefOrGetter<boolean>` | — | Whether the focus trap is currently active. Turning it on installs the listeners and moves focus in; turning it off removes them and restores focus. `isEnabled` is the same switch under its other name; either one being true activates the trap. |
| `options.isEnabled` | `MaybeRefOrGetter<boolean>` | — | The same switch as `isActive`, for callers that read better with it. Pass either name, not both. |
| `options.onEscape` | `() => void` | — | Callback when Escape is pressed inside the trapped container. The trap does not consume the key: a light-dismiss handler beside it still sees the event. |
| `options.initialFocus` | `() => HTMLElement \| null` | — | Element to focus when the trap turns on. Return `null` to fall back to the first focusable element. |

## Returns

| Name | Type | Description |
| --- | --- | --- |
| `containerRef` | `Ref` | Ref to attach to the container element that should trap focus. It is the ref you passed as `container`, or the one the hook made for you. |
| `focusFirst` | `() => void` | Focuses the first focusable element inside the container. |
| `focusLast` | `() => void` | Focuses the last focusable element inside the container. |
| `update` | `() => void` | Kept for callers that re-measure after the container's children change. It does nothing: the focusable elements are read on each key press. |
