> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Dialog – Code

> The Dialog provides a labeled modal surface with configured content and actions.

<Tabs>
  <Tab title="Implementation">
    ```tsx theme={null}
    import { Button, Dialog } from "@servicetitan/anvil2-rn";

    function App() {
      return (
        <Dialog
          title="Leave without saving?"
          content="Your changes haven't been saved. Leaving now will discard them."
          primaryAction={{ label: "Leave" }}
          secondaryAction={{ label: "Stay" }}
          trigger={<Button label="Open dialog" />}
        />
      );
    }
    ```

    ## Common Examples

    To use the `Dialog`, pass the required `title` and `content`. Open it with an Anvil2 RN `Button` through `trigger`, or control it with `isOpen` and `onOpenChange`.

    ```tsx theme={null}
    import { Button, Dialog } from "@servicetitan/anvil2-rn";

    function ExampleComponent() {
      return (
        <Dialog
          title="Leave without saving?"
          trigger={<Button label="Leave page" />}
          content="Your changes haven't been saved."
          primaryAction={{ label: "Leave" }}
          secondaryAction={{ label: "Stay" }}
        />
      );
    }
    ```

    ### Actions

    Pass `primaryAction` for the confirm action and `secondaryAction` for dismissal. The dialog fixes both buttons at medium size and fixes the secondary action to the secondary variant. The dialog closes after each footer press; the button's own `onPress` fires first.

    ```tsx theme={null}
    <Dialog
      title="You're all set"
      content="Your workspace is ready."
      primaryAction={{ label: "Got it" }}
      trigger={<Button label="Finish setup" />}
    />
    ```

    Use `variant: "danger"` on `primaryAction` for destructive confirmation.

    ```tsx theme={null}
    <Dialog
      title="Delete project?"
      content="This permanently deletes the project and everything in it."
      primaryAction={{ label: "Delete", variant: "danger" }}
      secondaryAction={{ label: "Cancel" }}
      trigger={<Button label="Delete project" variant="danger" />}
    />
    ```

    Omit both actions for a footerless dialog. The header close still dismisses.

    ### Open state

    When you pass `isOpen`, `onOpenChange` is required so the header close, scrim, Android back, and footer actions can request a close.

    ```tsx theme={null}
    const [isOpen, setOpen] = useState(false);

    <Dialog
      title="Leave without saving?"
      content="Your changes haven't been saved."
      isOpen={isOpen}
      onOpenChange={setOpen}
      primaryAction={{ label: "Leave" }}
    />
    ```

    Set `isDismissable={false}` to keep the scrim and Android back from closing the dialog. The header close still dismisses.

    ## Accessibility

    * The required `title` is a heading that names the modal. The dialog sets `accessibilityViewIsModal`.
    * The header close control is a button named `"Close"` (English until the kit gains i18n).
    * Long `content` scrolls inside the surface.
  </Tab>

  <Tab title="Dialog Props">
    ```tsx theme={null}
    <Dialog
      title="Leave without saving?"
      content="Your changes haven't been saved."
      primaryAction={{ label: "Leave" }}
      secondaryAction={{ label: "Stay" }}
      trigger={<Button label="Open dialog" />}
    />
    ```

    ## `Dialog` Props

    The `Dialog` accepts the following props. It does not forward remaining React Native `Modal` props. Open state is a discriminated union: pass `isOpen` with `onOpenChange`, or omit `isOpen` for the uncontrolled form.

    <ParamField path="content" type="ReactNode" required>
      Body content. A plain string uses the dialog's body typography.
    </ParamField>

    <ParamField path="title" type="string" required>
      Accessible title rendered in the header.
    </ParamField>

    <ParamField path="analyticsId" type="string">
      Opt-in telemetry id. When set under `Anvil2RNTelemetryProvider`, the
      primary action emits `press`; the header close, scrim, and secondary
      action emit `dismiss`.
    </ParamField>

    <ParamField path="defaultOpen" type="boolean">
      Whether the dialog starts open when uncontrolled. Unavailable when
      `isOpen` is set.
    </ParamField>

    <ParamField path="isDismissable" type="boolean" default="true">
      Whether tapping the scrim or using Android back closes the dialog.
    </ParamField>

    <ParamField path="isOpen" type="boolean">
      Controlled open state. Pair with `onOpenChange`.
    </ParamField>

    <ParamField path="onOpenChange" type="(isOpen: boolean) => void">
      Called when the open state changes. Required when `isOpen` is set.
    </ParamField>

    <ParamField path="primaryAction" type="DialogPrimaryAction">
      Confirm action in the footer. Omit with `secondaryAction` for a
      footerless dialog.
    </ParamField>

    <ParamField path="secondaryAction" type={`Omit<ButtonProps, "size" | "variant">`}>
      Dismiss action in the footer, always secondary at medium size.
    </ParamField>

    <ParamField path="testID" type="string">
      Test hook forwarded to the dialog surface.
    </ParamField>

    <ParamField path="trigger" type="ReactElement<ButtonProps>">
      Anvil2 RN `Button` that opens the dialog. Omit for controlled state.
    </ParamField>
  </Tab>

  <Tab title="DialogPrimaryAction">
    ```tsx theme={null}
    const primaryAction: DialogPrimaryAction = {
      label: "Delete",
      variant: "danger",
      onPress: () => {},
    };
    ```

    ## `DialogPrimaryAction`

    Confirm-button props. `size` is fixed at `"medium"`. Remaining `Button` props such as `icon`, `onPress`, `isDisabled`, and `testID` stay available.

    <ParamField path="label" type="string" required>
      Visible label and accessible name for the confirm button.
    </ParamField>

    <ParamField path="variant" type={`"primary" | "danger"`} default="primary">
      Confirm-button intent. `"danger"` is for destructive confirmation.
    </ParamField>
  </Tab>
</Tabs>
