Refresh Soma form import docs

active-uix
dev 5 months ago
parent 96edd41545
commit 959ad3b1f9

@ -17,9 +17,7 @@ Three things make an `AlertDialog` different from a plain `Dialog`:
<AlertDialog.Overlay /> <AlertDialog.Overlay />
<AlertDialog.Content> <AlertDialog.Content>
<AlertDialog.Title>Delete account?</AlertDialog.Title> <AlertDialog.Title>Delete account?</AlertDialog.Title>
<AlertDialog.Description> <AlertDialog.Description>This is permanent and cannot be undone.</AlertDialog.Description>
This is permanent and cannot be undone.
</AlertDialog.Description>
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel> <AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
<AlertDialog.Action onclick={deleteAccount}>Delete</AlertDialog.Action> <AlertDialog.Action onclick={deleteAccount}>Delete</AlertDialog.Action>
</AlertDialog.Content> </AlertDialog.Content>
@ -32,37 +30,37 @@ By convention, place `Cancel` before `Action` in DOM order: the focus scope land
## Parts ## Parts
| Part | Element | Origin | Description | | Part | Element | Origin | Description |
| ------------- | ---------- | ------------ | --------------------------------------------------------------------------- | | ------------- | ---------- | ----------- | ---------------------------------------------------------------------------- |
| `Provider` | none | AlertDialog | Root context. Forces `variant='alertdialog'` + `modal=true` on Dialog. | | `Provider` | none | AlertDialog | Root context. Forces `variant='alertdialog'` + `modal=true` on Dialog. |
| `Trigger` | `<button>` | Dialog | Opens the dialog on click. | | `Trigger` | `<button>` | Dialog | Opens the dialog on click. |
| `Content` | `<div>` | Dialog | Dialog container. Focus trap, scroll lock, dismissal layers integrated. | | `Content` | `<div>` | Dialog | Dialog container. Focus trap, scroll lock, dismissal layers integrated. |
| `Overlay` | `<div>` | Dialog | Backdrop. Click-outside does NOT close (forced by modal=true + alertdialog). | | `Overlay` | `<div>` | Dialog | Backdrop. Click-outside does NOT close (forced by modal=true + alertdialog). |
| `Title` | `<div>` | Dialog | Dialog heading. Linked via `aria-labelledby`. | | `Title` | `<div>` | Dialog | Dialog heading. Linked via `aria-labelledby`. |
| `Description` | `<div>` | Dialog | Supporting text. Linked via `aria-describedby`. | | `Description` | `<div>` | Dialog | Supporting text. Linked via `aria-describedby`. |
| `Action` | `<button>` | AlertDialog | Primary / destructive confirmation. Closes the dialog on click. | | `Action` | `<button>` | AlertDialog | Primary / destructive confirmation. Closes the dialog on click. |
| `Cancel` | `<button>` | AlertDialog | Safe escape button. Closes the dialog on click. | | `Cancel` | `<button>` | AlertDialog | Safe escape button. Closes the dialog on click. |
## Props ## Props
### `Provider` ### `Provider`
| Prop | Type | Default | Description | | Prop | Type | Default | Description |
| ---------------------- | ----------------------------- | ------- | ---------------------------------------------------------- | | ---------------------- | ---------------------- | ------- | ----------------------------------------------- |
| `id` | `string` | auto | DOM id for the root. | | `id` | `string` | auto | DOM id for the root. |
| `open` | `boolean` | `false` | Bindable open state. | | `open` | `boolean` | `false` | Bindable open state. |
| `onOpenChange` | `(v: boolean) => void` | — | Called when open state changes. | | `onOpenChange` | `(v: boolean) => void` | — | Called when open state changes. |
| `onOpenChangeComplete` | `(v: boolean) => void` | — | Called after the open/close animation settles. | | `onOpenChangeComplete` | `(v: boolean) => void` | — | Called after the open/close animation settles. |
| `disabled` | `boolean` | `false` | Disables the trigger (the dialog stays closed). | | `disabled` | `boolean` | `false` | Disables the trigger (the dialog stays closed). |
### `Action` / `Cancel` ### `Action` / `Cancel`
Both accept every `<button>` attribute plus: Both accept every `<button>` attribute plus:
| Prop | Type | Default | Description | | Prop | Type | Default | Description |
| ------------ | ---------- | ----------------------- | --------------------------------------------------- | | ------------ | -------- | ------------------------ | -------------------------------------------------- |
| `id` | `string` | auto | DOM id. | | `id` | `string` | auto | DOM id. |
| `aria-label` | `string` | `"Confirm"` / `"Cancel"` | Accessible name. Overrides the translated default. | | `aria-label` | `string` | `"Confirm"` / `"Cancel"` | Accessible name. Overrides the translated default. |
`Trigger`, `Content`, `Overlay`, `Title`, `Description` are re-exported from `Dialog` — see [Dialog's props](../dialog/README.md) for the complete list. Of particular interest: `Trigger`, `Content`, `Overlay`, `Title`, `Description` are re-exported from `Dialog` — see [Dialog's props](../dialog/README.md) for the complete list. Of particular interest:
@ -71,49 +69,49 @@ Both accept every `<button>` attribute plus:
## ARIA ## ARIA
| Part | Attribute | Value | | Part | Attribute | Value |
| ------- | ------------------- | ------------------------------------------- | | ------- | ------------------ | ------------------------------------------ |
| Trigger | `aria-haspopup` | `dialog` | | Trigger | `aria-haspopup` | `dialog` |
| Trigger | `aria-expanded` | `true` \| `false` | | Trigger | `aria-expanded` | `true` \| `false` |
| Trigger | `aria-controls` | ID of Content | | Trigger | `aria-controls` | ID of Content |
| Content | `role` | `alertdialog` | | Content | `role` | `alertdialog` |
| Content | `aria-modal` | `true` | | Content | `aria-modal` | `true` |
| Content | `aria-labelledby` | ID of Title | | Content | `aria-labelledby` | ID of Title |
| Content | `aria-describedby` | ID of Description | | Content | `aria-describedby` | ID of Description |
| Overlay | `aria-hidden` | `true` | | Overlay | `aria-hidden` | `true` |
| Title | `role` | `heading` | | Title | `role` | `heading` |
| Title | `aria-level` | Number (default `2`) | | Title | `aria-level` | Number (default `2`) |
| Action | `aria-label` | Translated `'Confirm'` (override via prop) | | Action | `aria-label` | Translated `'Confirm'` (override via prop) |
| Cancel | `aria-label` | Translated `'Cancel'` (override via prop) | | Cancel | `aria-label` | Translated `'Cancel'` (override via prop) |
## Data Attributes ## Data Attributes
| Part | Attribute | Values | | Part | Attribute | Values |
| -------- | ---------------------------- | ------------------------------------------------------ | | ------- | -------------------------- | --------------------------------------------- |
| Trigger | `data-dialog-trigger` | Always present | | Trigger | `data-dialog-trigger` | Always present |
| Trigger | `data-state` | `open` \| `closed` | | Trigger | `data-state` | `open` \| `closed` |
| Content | `data-dialog-content` | Always present | | Content | `data-dialog-content` | Always present |
| Content | `data-state` | `open` \| `closed` | | Content | `data-state` | `open` \| `closed` |
| Content | `data-nested` | Present when nested | | Content | `data-nested` | Present when nested |
| Content | `data-nested-open` | Present when a child dialog is open | | Content | `data-nested-open` | Present when a child dialog is open |
| Content | `data-starting-style` | Present during the open animation (one frame) | | Content | `data-starting-style` | Present during the open animation (one frame) |
| Content | `data-ending-style` | Present during the close animation | | Content | `data-ending-style` | Present during the close animation |
| Overlay | `data-dialog-overlay` | Always present | | Overlay | `data-dialog-overlay` | Always present |
| Overlay | `data-state` | `open` \| `closed` | | Overlay | `data-state` | `open` \| `closed` |
| Action | `data-alert-dialog-action` | Always present | | Action | `data-alert-dialog-action` | Always present |
| Cancel | `data-alert-dialog-cancel` | Always present | | Cancel | `data-alert-dialog-cancel` | Always present |
Because `AlertDialog` composes `Dialog`, the Trigger / Content / Overlay / Title / Description emit the `data-dialog-*` attributes inherited from that component. The two AlertDialog-specific parts (`Action`, `Cancel`) emit their own `data-alert-dialog-*` attributes so themes can style the primary vs secondary buttons independently. Because `AlertDialog` composes `Dialog`, the Trigger / Content / Overlay / Title / Description emit the `data-dialog-*` attributes inherited from that component. The two AlertDialog-specific parts (`Action`, `Cancel`) emit their own `data-alert-dialog-*` attributes so themes can style the primary vs secondary buttons independently.
## Keyboard ## Keyboard
| Key | Action | | Key | Action |
| ----------- | -------------------------------------------------------------------------------------- | | ----------- | ------------------------------------------------------------------------------------------------------------------ |
| `Escape` | Closes (acts as Cancel). Default `escapeKeydownBehavior='close'`. Pass `'ignore'` to force explicit Action/Cancel. | | `Escape` | Closes (acts as Cancel). Default `escapeKeydownBehavior='close'`. Pass `'ignore'` to force explicit Action/Cancel. |
| `Tab` | Cycle focus within the dialog (trapped). | | `Tab` | Cycle focus within the dialog (trapped). |
| `Shift+Tab` | Cycle focus backwards. | | `Shift+Tab` | Cycle focus backwards. |
| `Enter` | Fires `onclick` on the focused button. | | `Enter` | Fires `onclick` on the focused button. |
| `Space` | Fires `onclick` on the focused button. | | `Space` | Fires `onclick` on the focused button. |
Focus lands on the first focusable child when the dialog opens (`Cancel` by DOM order convention). Focus returns to the trigger on close. Focus lands on the first focusable child when the dialog opens (`Cancel` by DOM order convention). Focus returns to the trigger on close.
@ -121,28 +119,28 @@ Focus lands on the first focusable child when the dialog opens (`Cancel` by DOM
Default button labels resolve through the UIX lang system. The component catalog is owned by `morfo.translations` under `components.alert-dialog`. Default button labels resolve through the UIX lang system. The component catalog is owned by `morfo.translations` under `components.alert-dialog`.
| Key | English | Spanish | | Key | English | Spanish |
| -------- | --------- | ------------ | | -------- | --------- | ----------- |
| `action` | `Confirm` | `Confirmar` | | `action` | `Confirm` | `Confirmar` |
| `cancel` | `Cancel` | `Cancelar` | | `cancel` | `Cancel` | `Cancelar` |
Consumers override per-instance via the `aria-label` prop on `Action` / `Cancel`, or the visible label via `children`. Consumers override per-instance via the `aria-label` prop on `Action` / `Cancel`, or the visible label via `children`.
## Comparison ## Comparison
| Feature | Soma | Radix | Ark UI | bits-ui | | Feature | Soma | Radix | Ark UI | bits-ui |
| ---------------------------------------------- | :--: | :---: | :----: | :-----: | | --------------------------------------------- | :--: | :---: | :----: | :-----: |
| `role="alertdialog"` emitted | ✅ | ✅ | ✅ | ✅ | | `role="alertdialog"` emitted | ✅ | ✅ | ✅ | ✅ |
| Always modal | ✅ | ✅ | ✅ | ✅ | | Always modal | ✅ | ✅ | ✅ | ✅ |
| `Action` + `Cancel` as first-class parts | ✅ | ✅ | ✅ | ✅ | | `Action` + `Cancel` as first-class parts | ✅ | ✅ | ✅ | ✅ |
| Click-outside disabled | ✅ | ✅ | ✅ | ✅ | | Click-outside disabled | ✅ | ✅ | ✅ | ✅ |
| Escape closes by default | ✅ | ✅ | ✅ | ✅ | | Escape closes by default | ✅ | ✅ | ✅ | ✅ |
| Translated default button labels | ✅ | ❌ | ⚠️² | ❌ | | Translated default button labels | ✅ | ❌ | ⚠️² | ❌ |
| Nested alert dialogs | ✅ | ✅ | ✅ | ✅ | | Nested alert dialogs | ✅ | ✅ | ✅ | ✅ |
| Portal-agnostic (consumer picks) | ✅ | ✅ | ✅ | ✅ | | Portal-agnostic (consumer picks) | ✅ | ✅ | ✅ | ✅ |
| `data-*` attribute contract | ✅ | ✅ | ✅ | ✅ | | `data-*` attribute contract | ✅ | ✅ | ✅ | ✅ |
| Shared anatomy with plain Dialog | ✅ | ❌ | ✅ | ✅ | | Shared anatomy with plain Dialog | ✅ | ❌ | ✅ | ✅ |
| Built-in Form integration (confirm on submit) | ✅³ | ❌ | ❌ | ❌ | | Built-in Form integration (confirm on submit) | ✅³ | ❌ | ❌ | ❌ |
² Ark has a machine-level translator seam but no built-in English/Spanish strings. ² Ark has a machine-level translator seam but no built-in English/Spanish strings.
³ Demonstrated on the test page: `onValidSubmit` returns a Promise gated on a confirmation dialog. ³ Demonstrated on the test page: `onValidSubmit` returns a Promise gated on a confirmation dialog.
@ -167,9 +165,7 @@ Consumers override per-instance via the `aria-label` prop on `Action` / `Cancel`
<AlertDialog.Overlay /> <AlertDialog.Overlay />
<AlertDialog.Content> <AlertDialog.Content>
<AlertDialog.Title>Delete account?</AlertDialog.Title> <AlertDialog.Title>Delete account?</AlertDialog.Title>
<AlertDialog.Description> <AlertDialog.Description>All your data will be permanently removed.</AlertDialog.Description>
All your data will be permanently removed.
</AlertDialog.Description>
<AlertDialog.Cancel>Keep it</AlertDialog.Cancel> <AlertDialog.Cancel>Keep it</AlertDialog.Cancel>
<AlertDialog.Action onclick={deleteAccount}>Delete</AlertDialog.Action> <AlertDialog.Action onclick={deleteAccount}>Delete</AlertDialog.Action>
</AlertDialog.Content> </AlertDialog.Content>
@ -179,9 +175,7 @@ Consumers override per-instance via the `aria-label` prop on `Action` / `Cancel`
### Force explicit Action/Cancel (Escape ignored) ### Force explicit Action/Cancel (Escape ignored)
```svelte ```svelte
<AlertDialog.Content escapeKeydownBehavior="ignore"> <AlertDialog.Content escapeKeydownBehavior="ignore">…</AlertDialog.Content>
…
</AlertDialog.Content>
``` ```
### Gating a form submission ### Gating a form submission
@ -189,7 +183,7 @@ Consumers override per-instance via the `aria-label` prop on `Action` / `Cancel`
```svelte ```svelte
<script lang="ts"> <script lang="ts">
import { AlertDialog, Form, Field } from '$soma/components'; import { AlertDialog, Form, Field } from '$soma/components';
import { createForm } from '$soma/components/form'; import { createForm } from '$libs/forms';
let confirmOpen = $state(false); let confirmOpen = $state(false);
let resolve: ((ok: boolean) => void) | null = null; let resolve: ((ok: boolean) => void) | null = null;
@ -208,14 +202,17 @@ Consumers override per-instance via the `aria-label` prop on `Action` / `Cancel`
<Form.Provider {form}>…<Form.Submit>Delete</Form.Submit></Form.Provider> <Form.Provider {form}>…<Form.Submit>Delete</Form.Submit></Form.Provider>
<AlertDialog.Provider bind:open={confirmOpen} <AlertDialog.Provider bind:open={confirmOpen} onOpenChange={(v) => !v && resolve?.(false)}>
onOpenChange={(v) => !v && resolve?.(false)}
>
<AlertDialog.Overlay /> <AlertDialog.Overlay />
<AlertDialog.Content> <AlertDialog.Content>
<AlertDialog.Title>Really?</AlertDialog.Title> <AlertDialog.Title>Really?</AlertDialog.Title>
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel> <AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
<AlertDialog.Action onclick={() => { resolve?.(true); confirmOpen = false; }}> <AlertDialog.Action
onclick={() => {
resolve?.(true);
confirmOpen = false;
}}
>
Confirm Confirm
</AlertDialog.Action> </AlertDialog.Action>
</AlertDialog.Content> </AlertDialog.Content>

Loading…
Cancel
Save

Powered by TurnKey Linux.