# useAdaptiveCard URL: /docs/web/hooks/use-adaptive-card Headless state, validation, visibility, and action dispatch for the Adaptive Cards 1.5 renderer. Drives the `AdaptiveCard` component and is exported so consumers can wire custom toolbars or server-side validation. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/hooks-use-adaptive-card ``` **Dependencies:** - [react](https://react.dev) ## Overview `useAdaptiveCard` owns the runtime state for an [`AdaptiveCard`](/docs/web/components/adaptive-card) instance: - **Inputs** — a registry of every visible `Input.*` element, keyed by `id`, with live values and per-input validation state. - **Visibility overrides** — the `Action.ToggleVisibility` map layered on top of static `isVisible` defaults. - **Show-card state** — open/closed flags for every `Action.ShowCard` button. - **Action dispatch** — typed event emission for `submit`, `execute`, `openUrl`, `toggleVisibility`, `showCard`. - **Submit payload assembly** — walks the visible tree honoring `associatedInputs: 'auto' \| 'none'`, merges `action.data`, and emits a strongly typed event. The `` component composes this hook internally. You only need to call it directly when: - You want to trigger actions programmatically (e.g. a custom toolbar that says "Submit & close"). - You need to read input state from outside the card (e.g. to disable a parent's submit button). - You want to wire server-side validation that returns errors per `inputId`. ## Usage ### Default integration (when `` isn't enough) ```tsx 'use client'; import { AdaptiveCardView, useAdaptiveCard, type AdaptiveCardActionEvent, type AdaptiveCardPayload } from '@docyrus/ui/components/adaptive-card'; export function CardWithToolbar({ payload }: { payload: AdaptiveCardPayload }) { function handleAction(event: AdaptiveCardActionEvent) { console.log(event); } const adaptive = useAdaptiveCard(payload, { onAction: handleAction }); return (
); } ``` ### Reading current input state ```tsx const adaptive = useAdaptiveCard(payload); // Live, controlled console.log(adaptive.state.inputs.email); console.log(adaptive.state.validations.email); ``` ### Programmatic submit ```tsx const submitAction = payload.actions?.find(a => a.type === 'Action.Submit'); if (submitAction && submitAction.type === 'Action.Submit') { adaptive.controls.submit(submitAction); } ``` ### Resetting after a successful save ```tsx async function handleAction(event: AdaptiveCardActionEvent) { if (event.type !== 'submit') return; await fetch('/api/forms', { method: 'POST', body: JSON.stringify(event.data) }); adaptive.controls.reset(); // clears inputs, visibility overrides, show-card state } ``` ## API Reference ### `UseAdaptiveCardOptions` | Option | Type | Default | Description | |--------|------|---------|-------------| | `onAction` | `(event: AdaptiveCardActionEvent) => void \| Promise` | — | Receives every dispatched action. | | `hostConfig` | `AdaptiveCardHostConfigOverride` | — | Deep-merged onto `defaultHostConfig`. | | `customElements` | `Record` | `{}` | Renderers for unknown `type` strings. | ### `UseAdaptiveCardReturn` | Field | Type | Description | |-------|------|-------------| | `card` | `AdaptiveCardPayload` | The original payload (post-`parseAdaptiveCard` normalization). | | `hostConfig` | `AdaptiveCardHostConfig` | The resolved host config (default deep-merged with override). | | `state.inputs` | `Record` | Live input values, keyed by `id`. | | `state.validations` | `Record` | Per-input validation, recomputed on every input change. | | `state.visibilityOverrides` | `Record` | `Action.ToggleVisibility` overrides keyed by element `id`. | | `state.showCardOpen` | `Record` | Open flags for `Action.ShowCard`, keyed by action `id`/`title`. | | `state.hasSubmittedOnce` | `boolean` | Becomes `true` after the first failed submit so per-input error messages render. | | `controls.setInput(id, value)` | `(id, value) => void` | Imperatively set an input. | | `controls.submit(action)` | `(action: AdaptiveCardActionSubmit) => void` | Validate + dispatch `'submit'`. | | `controls.execute(action)` | `(action: AdaptiveCardActionExecute) => void` | Validate + dispatch `'execute'` with `verb`. | | `controls.openUrl(action)` | `(action: AdaptiveCardActionOpenUrl) => void` | Dispatch `'openUrl'`. | | `controls.toggleVisibility(action)` | `(action: AdaptiveCardActionToggleVisibility) => void` | Apply visibility overrides + dispatch `'toggleVisibility'`. | | `controls.toggleShowCard(action)` | `(action: AdaptiveCardActionShowCard) => void` | Flip a show-card and dispatch `'showCard'`. | | `controls.validateAll()` | `() => { ok; firstInvalidId?; errors }` | Run validation immediately. Does not dispatch. | | `controls.reset()` | `() => void` | Clear inputs / overrides / show-card state. | | `cardProps` | `AdaptiveCardContextValue` | The projection consumed by ``. | ## Validation timing - **First submit** runs validation silently. If any input fails, the hook sets `state.hasSubmittedOnce = true`, focuses the first invalid input, and does **not** dispatch. - After the first failed submit, validations re-render live so error messages clear as the user fixes them. - This matches the reference renderer's behavior — users don't see errors before they've tried to submit. ## `associatedInputs` contract `Action.Submit` and `Action.Execute` walk the **currently visible** card tree to build the submit payload: - `'auto'` (default) — collect every `Input.*` whose `isVisible` evaluation is true under the current `visibilityOverrides` map. Hidden inputs are dropped. - `'none'` — skip inputs entirely; emit only the literal `action.data`. For `Action.ShowCard`-nested submits, the nested card's inputs merge into the parent's, mirroring the spec. ## Type Exports | Type | Description | |------|-------------| | `UseAdaptiveCardOptions` | Hook options. | | `UseAdaptiveCardReturn` | Full hook return. | | `AdaptiveCardInputValue` | `string \| Array \| boolean \| number \| null`. |