# Dialog

Displays a modal surface over the current page, traps focus while open, and restores focus to the previously active element when it closes.

Use Dialog for confirmation flows, compact forms, link sharing, and long-form review tasks that should keep the user in the current context.

## Import

```ts
import { ButtonComponent } from '@edsis/component/button';
import {
  DialogCloseDirective,
  DialogComponent,
  DialogContentComponent,
  DialogDescriptionComponent,
  DialogFooterComponent,
  DialogHeaderComponent,
  DialogTitleComponent,
} from '@edsis/component/dialog';
import { InputComponent } from '@edsis/component/input';
import { LabelComponent } from '@edsis/component/label';
```

## Composition

The Angular composition tracks the shadcn structure, with one intentional mapping difference: shadcn `DialogTrigger` becomes any external control that toggles the `open` signal.

```text
button[Button] (external trigger; sets the open signal)
Dialog
├── built-in close button (optional)
├── DialogHeader
│   ├── DialogTitle
│   └── DialogDescription
├── DialogContent (optional body wrapper)
└── DialogFooter
    └── button[DialogClose] (optional custom close action)
```

## Basic usage

Use an external button or link to toggle the `open` signal, then declare the dialog structure inside `Dialog`.

```html
<button type="button" Button variant="outline" (click)="open.set(true)">Edit profile</button>

<dialog
  [(open)]="open"
  aria-labelledby="dialog-title"
  aria-describedby="dialog-description"
  class="sm:max-w-sm"
>
  <DialogHeader>
    <DialogTitle id="dialog-title">Edit profile</DialogTitle>
    <DialogDescription id="dialog-description">
      Make changes to your profile here. Click save when you are done.
    </DialogDescription>
  </DialogHeader>

  <DialogContent class="grid gap-4 py-2">
    <div class="grid gap-2">
      <label Label for="name">Name</label>
      <input Input id="name" value="Pedro Duarte" />
    </div>
    <div class="grid gap-2">
      <label Label for="username">Username</label>
      <input Input id="username" value="@peduarte" />
    </div>
  </DialogContent>

  <DialogFooter>
    <button type="button" Button variant="outline" DialogClose>Cancel</button>
    <button type="button" Button (click)="open.set(false)">Save changes</button>
  </DialogFooter>
</dialog>
```

## Common patterns

### Destructive confirmation

Use a short title, short body copy, and explicit cancel/confirm actions for destructive flows.

```html
<button type="button" Button variant="destructive" (click)="confirmOpen.set(true)">
  Delete project
</button>

<dialog [(open)]="confirmOpen">
  <DialogHeader>
    <DialogTitle>Delete this project?</DialogTitle>
    <DialogDescription>
      This action cannot be undone. This permanently deletes your project.
    </DialogDescription>
  </DialogHeader>
  <DialogFooter>
    <button type="button" Button variant="outline" DialogClose>Cancel</button>
    <button type="button" Button variant="destructive" (click)="confirmOpen.set(false)">
      Delete
    </button>
  </DialogFooter>
</dialog>
```

### Custom close button

Hide the built-in corner close control with `[showCloseButton]="false"` and place `button[DialogClose]` wherever dismissal should live.

```html
<dialog [(open)]="shareOpen" [showCloseButton]="false" class="sm:max-w-md">
  <DialogHeader>
    <DialogTitle>Share link</DialogTitle>
    <DialogDescription> Anyone who has this link can view this workspace. </DialogDescription>
  </DialogHeader>
  <DialogContent class="grid gap-4 py-2">
    <div class="grid gap-2">
      <label Label for="share-link">Link</label>
      <input
        Input
        id="share-link"
        readonly
        value="https://ui.shadcn.com/docs/components/radix/dialog"
      />
    </div>
  </DialogContent>
  <DialogFooter class="sm:justify-start">
    <button type="button" Button DialogClose>Close</button>
  </DialogFooter>
</dialog>
```

### No close button

Use `[showCloseButton]="false"` without an internal close action when Escape and backdrop dismissal are enough.

```html
<dialog [(open)]="noCloseOpen" [showCloseButton]="false">
  <DialogHeader>
    <DialogTitle>No Close Button</DialogTitle>
    <DialogDescription>
      Dismiss this dialog with Escape, the backdrop, or another action you control.
    </DialogDescription>
  </DialogHeader>
</dialog>
```

### Sticky footer for long content

Move the scroll region into `DialogContent` and keep the footer outside that overflow container so primary actions remain visible.

```html
<dialog
  [(open)]="stickyOpen"
  [showCloseButton]="false"
  class="max-h-[85vh] overflow-hidden p-0 sm:max-w-lg"
>
  <DialogHeader class="px-6 pt-6">
    <DialogTitle>Sticky Footer</DialogTitle>
    <DialogDescription>Keep actions visible while the content scrolls.</DialogDescription>
  </DialogHeader>
  <DialogContent class="max-h-[45vh] overflow-y-auto px-6 pb-6">
    <!-- long content -->
  </DialogContent>
  <DialogFooter class="border-t border-border bg-background px-6 py-4 sm:justify-start">
    <button type="button" Button variant="outline" DialogClose>Close</button>
    <button type="button" Button (click)="stickyOpen.set(false)">Save changes</button>
  </DialogFooter>
</dialog>
```

### RTL

Wrap the trigger and dialog in a `dir="rtl"` container or set direction globally in the app shell.

```html
<div dir="rtl" lang="ar" class="text-right">
  <button type="button" Button variant="outline" (click)="rtlOpen.set(true)">فتح الحوار</button>

  <dialog [(open)]="rtlOpen" [showCloseButton]="false" class="sm:max-w-sm">
    <DialogHeader>
      <DialogTitle>تعديل الملف الشخصي</DialogTitle>
      <DialogDescription>
        قم بإجراء تغييرات على ملفك الشخصي هنا. انقر فوق حفظ عند الانتهاء.
      </DialogDescription>
    </DialogHeader>
    <DialogContent class="grid gap-4 py-2">
      <div class="grid gap-2">...</div>
      <div class="grid gap-2">...</div>
    </DialogContent>
    <DialogFooter>
      <button type="button" Button variant="outline" DialogClose>إلغاء</button>
      <button type="button" Button (click)="rtlOpen.set(false)">حفظ التغييرات</button>
    </DialogFooter>
  </dialog>
</div>
```

## API reference

### `DialogComponent`

| Input                  | Type                               | Default         |
| ---------------------- | ---------------------------------- | --------------- |
| `open` (model)         | `boolean`                          | `false`         |
| `backdrop`             | `'dim' \| 'blur' \| 'transparent'` | `'transparent'` |
| `closeOnEscape`        | `boolean`                          | `true`          |
| `closeOnBackdropClick` | `boolean`                          | `true`          |
| `showCloseButton`      | `boolean`                          | `true`          |
| `closeButtonLabel`     | `string`                           | `'Close'`       |
| `aria-labelledby`      | `string \| null`                   | `null`          |
| `aria-describedby`     | `string \| null`                   | `null`          |
| `class`                | `string`                           | `''`            |

Output: `openedChange: boolean`. Method: `close()`.

### Parts

| Part                  | Purpose                                                  |
| --------------------- | -------------------------------------------------------- |
| `DialogHeader`        | Title and description wrapper                            |
| `DialogTitle`         | Primary dialog label                                     |
| `DialogDescription`   | Supporting copy announced by assistive tech              |
| `DialogContent`       | Optional body wrapper for forms, grids, and scroll areas |
| `DialogFooter`        | Action row                                               |
| `button[DialogClose]` | Custom close action mapped to shadcn `DialogClose`       |

Lower-level behavior is based on the Radix Dialog pattern: <https://www.radix-ui.com/primitives/docs/components/dialog#api-reference>.

## Styling and theming

The dialog surface uses the shared theme tokens for background, border, foreground, and ring colors. Pass `class` to `Dialog` when you need to adjust width, max height, overflow strategy, or padding for a specific modal pattern.

The `backdrop` input styles the overlay behind the surface: `'transparent'` (default) keeps the page fully visible while still capturing outside clicks, `'dim'` darkens it with the `--overlay-backdrop` theme token, and `'blur'` adds a frosted `backdrop-filter` on top of dim. The overlay element always carries the `dialog-backdrop` marker class plus a `dialog-backdrop--<appearance>` modifier for CSS targeting.

Use `DialogContent` and `DialogFooter` classes to create scroll regions, sticky action rows, or wider content layouts without forking the primitive.

## Accessibility

- The surface renders with `role="dialog"` and `aria-modal="true"`.
- Focus is trapped inside with `FocusTrap` from `@angular/cdk/a11y`.
- Focus returns to the previously active element when the dialog closes.
- Escape and backdrop dismissal are enabled by default but can be disabled for stricter flows.
- Keep titles and descriptions concise so screen readers announce useful context immediately.

## Keyboard interactions

- `Tab` and `Shift+Tab` stay inside the dialog while it is open.
- `Escape` closes the dialog unless `closeOnEscape` is disabled.
- Native button activation handles `Enter` and `Space` for dialog actions.

## Angular notes

- This implementation intentionally maps shadcn `DialogTrigger` to an external control that owns the `open` signal.
- `button[DialogClose]` is the Angular-friendly equivalent of shadcn `DialogClose` for internal dismissal actions.
- Seed open state with a signal in the owning component and keep the dialog content declarative.
- Hide the built-in close affordance with `[showCloseButton]="false"` when the footer or body owns dismissal explicitly.

## Source parity

This Angular implementation follows the shadcn Dialog information architecture and examples while translating the interaction model to Angular selectors, signal-driven state, and explicit ownership of the trigger outside the modal content.
