UNPKG

@kitn.ai/ui

Version:

Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. Authored in SolidJS.

105 lines (104 loc) 5 kB
import { JSX } from 'solid-js'; import { CardEnvelope, CardHost, CardResolution } from '../primitives/card-contract'; export interface ChoiceOptionMedia { image?: string; imageAlt?: string; icon?: string; } export interface ChoiceOption { id: string; label: string; description?: string; media?: ChoiceOptionMedia; meta?: string; recommended?: boolean; disabled?: boolean; payload?: unknown; } export type ChoiceAllowOther = boolean | { label?: string; placeholder?: string; }; export interface ChoiceCardData { prompt?: string; options: ChoiceOption[]; allowOther?: ChoiceAllowOther; submitLabel?: string; dismissible?: boolean; } export type ChoiceCardEnvelope = CardEnvelope<'choice', ChoiceCardData>; export declare const CHOICE_CARD_TYPE: "choice"; /** The reserved action id emitted by the `allowOther` free-text submit. */ export declare const OTHER_ACTION: "__other__"; /** De-dupe options by id (first wins) and validate their shape. Returns the usable * list + an optional error message when there's nothing renderable. Modeled on * `normalizeActions`. */ export declare function normalizeOptions(options: unknown): { options: ChoiceOption[]; error?: string; }; /** Resolve the `allowOther` data field to a config (or null when off). */ export declare function resolveOtherConfig(allowOther: ChoiceAllowOther | undefined): { label: string; placeholder: string | undefined; } | null; /** Index of the next non-disabled option, moving by `dir` (+1/-1) from `from`, * wrapping. Returns `from` when nothing else is focusable. */ export declare function nextEnabledIndex(options: ChoiceOption[], from: number, dir: 1 | -1): number; /** The first non-disabled option index (the initial roving tab stop), or -1. */ export declare function firstEnabledIndex(options: ChoiceOption[]): number; /** Imperative handle exposed via `controllerRef` — surfaces the choice card's * latent selection/submit/dismiss capabilities so the `<kai-choice>` facade can * forward them as instance methods (focus/select/send/dismiss/reopen). */ export interface ChoiceController { /** Focus the radiogroup roving tab stop (or the Other input when selected). */ focus(options?: FocusOptions): void; /** Select an option by id locally (no emit) — same as a row click. */ select(optionId: string): void; /** Submit the current selection — emits the `action` verb + resolves single-shot. */ send(): void; /** Dismiss the card — emits `dismiss` + optimistically collapses to the stub. */ dismiss(): void; /** Re-open a dismissed card from its stub — emits `reopen`. */ reopen(): void; } export interface ChoiceCardProps { /** The choice definition (CardEnvelope.data). */ data?: ChoiceCardData; /** The card id used to correlate every emitted CardEvent. */ cardId?: string; /** The envelope title rendered in the card chrome. */ heading?: string; /** Optional explicit CardHost (otherwise read from a CardProvider, otherwise the * bubbling `kai-card` CustomEvent off `hostElement`). */ host?: CardHost; /** The custom-element host node, for the bubbling `kai-card` fallback emit. */ hostElement?: HTMLElement; class?: string; /** When set, render the chromed read-only view instead of the interactive radiogroup. */ resolution?: CardResolution; /** Controlled selection (option id). When set, the consumer owns the current pick. */ value?: string; /** Option id to pre-select on mount (uncontrolled seed). */ defaultValue?: string; /** Disable the whole radiogroup + Submit (e.g. while the agent is busy). */ disabled?: boolean; /** Fires when the selection changes BEFORE submit (row click or select()). */ onValueChange?: (value: string) => void; /** Receive the imperative controller once mounted. The `<kai-choice>` facade * forwards these as element methods (focus/select/send/dismiss/reopen). */ controllerRef?: (controller: ChoiceController) => void; } /** * `ChoiceCard` — a single-select "pick one of N rich options" card (plans, products, * flights, quick replies) inside `Card` chrome. The options are a WAI-ARIA radiogroup * (list rows) with roving tabindex: clicking a row (or Space/Enter on the focused row) * **selects** it locally without emitting; a **Submit** button below the list then emits * the Card contract's **`action`** verb (`{ kind:'action', cardId, action: option.id, * payload }`) and resolves the card (chosen option shown read-only) so the same pick can't * double-fire. An optional `allowOther` free-text escape appends a selectable "Other…" row; * selecting it reveals an inline text input, and the same Submit emits `action:'__other__'` * with `{ text }`. Emits `ready` on mount and `error` for an unusable definition (inline * error state). */ export declare function ChoiceCard(props: ChoiceCardProps): JSX.Element;