import * as React from 'react'; import React__default, { ComponentType, RefObject } from 'react'; import { z } from 'zod'; import { M as Method, b as AdapterResult, A as AdapterKey, f as AdapterProps, i as AdapterSubmit } from './adapter-CvjXO9Gi.mjs'; import * as react_jsx_runtime from 'react/jsx-runtime'; import { DayPicker } from 'react-day-picker'; import * as class_variance_authority_types from 'class-variance-authority/types'; import { VariantProps } from 'class-variance-authority'; import * as SwitchPrimitive from '@radix-ui/react-switch'; /** * Size hint for field variants. * * Presets can interpret these however they like (font size, padding, etc.). */ type FieldSize = "sm" | "md" | "lg"; /** * Density hint for field variants. * * - "compact" → tight vertical spacing * - "comfortable" → default spacing * - "loose" → extra breathing room */ type FieldDensity = "compact" | "comfortable" | "loose"; /** * Logical source of a change event. * * Variants and utilities can tag changes to help the host reason * about where a value came from. */ type ChangeSource = "variant" | "paste" | "programmatic" | "util" | (string & {}); /** * Additional context passed along with value changes. */ interface ChangeDetail { /** * Logical source for this change. */ source: ChangeSource; /** * Optional raw input that produced this value. * * Example: original keyboard input or pasted string. */ raw?: TRaw; /** * Raw selected option entries as provided by the variant input. * * For primitive options this contains primitives, for object options * it contains the original objects. */ selectedOptions?: unknown[]; nativeEvent?: React__default.SyntheticEvent; /** * Variant-specific metadata (e.g. cursor position). */ meta?: TMeta; } /** * Base props shared by all variant components. * * Each variant module will extend this with its own props type. */ interface VariantBaseProps { /** * Current logical value for this field. */ value?: TValue | undefined; /** * Called whenever the variant wants to update the value. * * The detail payload describes where the change came from. */ onValue?(value: TValue | undefined, detail?: ChangeDetail): void; /** * State flags. */ disabled?: boolean; defaultValue?: any; readOnly?: boolean; required?: boolean; alias?: string; main?: boolean; /** * Current error message for this field, if any. */ error?: string; /** * Size & density hints. * * Variants are free to ignore these, but presets (e.g. Shadcn) * will typically honour them. */ size?: FieldSize; density?: FieldDensity; } interface Extras { trailingIcons?: React__default.ReactNode[]; leadingIcons?: React__default.ReactNode[]; icon?: React__default.ReactNode; iconGap?: number; trailingIconSpacing?: number; leadingIconSpacing?: number; trailingControl?: React__default.ReactNode; leadingControl?: React__default.ReactNode; /** * Optional className applied to the container that wraps the leading control. * This does not affect the control node itself, only the wrapper div. */ leadingControlClassName?: string; /** * Optional className applied to the container that wraps the trailing control. * This does not affect the control node itself, only the wrapper div. */ trailingControlClassName?: string; px?: number; py?: number; pb?: number; pe?: number; ps?: number; } type ExtraFieldProps = Extras & Props; /** * Result type for validation hooks. * * Used by: * - variant modules (`validate`) * - per-field `onValidate` (InputField) */ type ValidateResult = boolean | string | string[] | null | void; /** * Placement of the main label relative to the field control. * * This is a macro layout decision: where the label block lives * compared to the input/control block. */ type LabelPlacement = "top" | "left" | "right" | "hidden"; /** * Shared placement for helper slots relative to their *root*. * * Example: * - "above" → above the label root or input root * - "below" → below the label root or input root * - "left" → left of the label root or input root * - "right" → right of the label root or input root * - "hidden" → not rendered */ type SlotPlacement = "left" | "right" | "above" | "below" | "hidden"; /** * Placement of the sublabel relative to its root block. */ type SublabelPlacement = SlotPlacement; /** * Placement for the longer description block. */ type DescriptionPlacement = SlotPlacement; /** * Placement for helper text (typically small, subtle text). */ type HelpTextPlacement = SlotPlacement; /** * Placement for explicit error text (visual error copy). */ type ErrorTextPlacement = SlotPlacement; /** * Registry of all logical "slots" a field can render. * * Hosts can extend this via declaration merging, e.g.: * * declare module "@/schema/input-field" { * interface FieldSlots { * charCounter: true; * } * } */ interface FieldSlots { /** The main label text. */ label: true; /** Optional smaller label text. */ sublabel: true; /** Longer, usually multi-line description. */ description: true; /** Small helper text, usually subtle. */ helpText: true; /** Error text (validation message) when present. */ errorText: true; /** The actual control/input element. */ input: true; /**tags */ tags: true; } /** * Registry of logical "roots" / anchor blocks. * * Other slots are positioned relative to one of these. */ interface FieldRoots { /** Label root block. */ label: true; /** Input/control root block. */ input: true; } type FieldSlotId = keyof FieldSlots; type FieldRootId = keyof FieldRoots; /** * Map of which root each *non-root* slot belongs to. * * Example: * relativeRoots: { * sublabel: "label", * description: "input", * helpText: "input", * errorText: "input", * } */ type RelativeRootsMap = Partial, // non-root slots only FieldRootId>>; /** * Relative ordering of *non-root* slots per root. * * This is *not* about placement; it only decides "who comes first" * when multiple slots share the same: * - root (label/input), and * - placement (above/below/left/right) * * Example: * ordering: { * input: ["errorText", "description", "helpText"], * } * * If description and helpText are both "below" the input, then the * above config means: * - errorText (below input) first, * - then description (below input), * - then helpText (below input). */ type FieldOrdering = Partial[]>>; /** * Layout defaults for a field/variant. * * Variants can provide these as defaults; InputField merges them * with per-field overrides. * * The high-level placement props remain the main public API. * `relativeRoots` and `ordering` provide a lower-level layout graph * that InputField can use to render slots relative to "label" or * "input" in a predictable order. */ interface FieldLayoutConfig { /** * Where to render the main label relative to the control. */ labelPlacement?: LabelPlacement; /** * Where to render the sublabel relative to its root. */ sublabelPlacement?: SublabelPlacement; /** * Where to render the description block relative to its root. */ descriptionPlacement?: DescriptionPlacement; /** * Where to render helper text relative to its root. */ helpTextPlacement?: HelpTextPlacement; /** * Where to render error text (if any) relative to its root. */ errorTextPlacement?: ErrorTextPlacement; /**Where to render the tags (if any) relative to ites root */ tagPlacement?: SlotPlacement; /** * Hint that the field should render inline with other controls. */ inline?: boolean; /** * Hint that the field should stretch to the full available width. */ fullWidth?: boolean; /** * Optional default size/density hints. * * These are advisory; variants/presets may override them. */ defaultSize?: FieldSize; defaultDensity?: FieldDensity; /** * Which root each non-root slot is attached to. * * If omitted, InputField can infer reasonable defaults, e.g.: * - sublabel → "label" * - description → "input" * - helpText → "input" * - errorText → "input" */ relativeRoots?: RelativeRootsMap; /** * Relative ordering of non-root slots per root. * * Used only when multiple slots share the same * root + placement combination. */ ordering?: FieldOrdering; } /** * Effective layout for a field after merging: * - variant defaults, and * - per-field overrides. */ interface EffectiveFieldLayout extends FieldLayoutConfig { /** * Concrete size/density after merging defaults + overrides. */ size?: FieldSize; density?: FieldDensity; } /** * Context passed to a variant's layout resolver. * * - `defaults`: layout defaults defined by the variant module. * - `overrides`: only the layout keys explicitly set on . * - `props`: the raw props for this field. * * The resolver MUST respect host overrides: if a key is present in * `overrides`, it should not change it. */ interface LayoutResolveContext { defaults: FieldLayoutConfig; overrides: Partial; props: T; } /** * Variant-level layout resolver. * * This allows variants to implement mapping rules such as: * - "if labelPlacement is left ⇒ inline=true, error below, etc." * while still allowing host overrides to win. * * Variants may also fill in `relativeRoots` and `ordering` to define * how slots are attached to "label" vs "input" and in what relative * order they should render. */ type LayoutResolver = (ctx: LayoutResolveContext) => FieldLayoutConfig; type MaskMode = "raw" | "masked"; /** * Mask-related props for the Shadcn text variant. * * These are forwarded to the underlying , which in turn wires * them into the InputMask implementation. */ interface ShadcnTextMaskProps { /** * Mask pattern – Primereact style. * Example: "99/99/9999", "(999) 999-9999" */ mask?: string; /** * Per-symbol definitions for slots. * Kept for future custom engine; not used by the current * react-input-mask implementation. */ maskDefinitions?: Record; /** * Character used to visually represent an empty slot. * Default: "_". */ slotChar?: string; /** * If true, when the value is effectively "empty" (no unmasked chars), * we emit an empty string "" instead of a fully-masked placeholder. * * NOTE: This behaviour is implemented in the variant, not Input, * so we preserve your existing semantics. */ autoClear?: boolean; /** * Whether the *model* value is raw or masked. * * - "raw" or true → onValue receives unmasked value * - "masked" or false/undefined → onValue receives full masked string * * NOTE: detail.raw is **always** the masked string. */ unmask?: MaskMode | boolean; /** * Placeholder for future caret-mode logic when we go back * to a custom engine. Currently unused, kept for API compatibility. */ maskInsertMode?: "stream" | "caret"; } /** * Extra UI props for the Shadcn text input (pure HTML-level). * * These are forwarded straight to the underlying . */ type ShadcnTextUiProps = Omit, "value" | "defaultValue" | "onChange" | "size"> & { /** * Extra classes applied only to the *inner* input element * (the actual , not the wrapper box). */ inputClassName?: string; /** * Fixed prefix rendered as part of the input value, NOT as an icon. * E.g. "₦", "ID: ". * * The underlying will: * - take the model value (without prefix), * - render prefix + value, * - expose the full visible string in event.target.value. */ prefix?: string; /** * Fixed suffix rendered as part of the input value, NOT as an icon. * E.g. "%", "kg". */ suffix?: string; /** * If true (default), we strip the prefix from the value * before emitting it via `onValue`. */ stripPrefix?: boolean; /** * If true (default), we strip the suffix from the value * before emitting it via `onValue`. */ stripSuffix?: boolean; } & ShadcnTextMaskProps; /** * Props for the Shadcn-based text variant. * * This is a *form* wrapper around the base : * - Handles value ↔ ChangeDetail mapping. * - Delegates all visual concerns (masking, affixes, icons, controls, * size, density) to the Input component. */ type ShadcnTextVariantProps = ExtraFieldProps> & { /** * If true and there are controls, the input + controls share one box * (borders, radius, focus states). * * Delegated to the underlying . */ joinControls?: boolean; /** * When joinControls is true, whether the box styling extends over controls * (true) or controls are visually separate (false). */ extendBoxToControls?: boolean; } & ShadcnTextUiProps; type InputRef = HTMLInputElement; interface InputNumberValueChangeEvent { originalEvent: React.SyntheticEvent | null; value: number | null; stopPropagation(): void; preventDefault(): void; target: { name?: string | null; id?: string | null; value: number | null; }; } interface InputNumberProps extends Omit, Omit, "value" | "defaultValue" | "onChange" | "onInput" | "onKeyDown" | "onKeyUp" | "size" | "max" | "min"> { onKeyUp?(event: React.KeyboardEvent): unknown; onKeyDown?(event: React.KeyboardEvent): unknown; value?: number | null; /** Emitted when the numeric value changes (Prime-style). */ onValueChange?: (e: InputNumberValueChangeEvent) => void; /** Optional simple change handler (numeric value). */ onChange?: (e: { originalEvent: React.SyntheticEvent; value: number | null; }) => void; locale?: string; localeMatcher?: Intl.NumberFormatOptions["localeMatcher"]; mode?: "decimal" | "currency"; currency?: string; currencyDisplay?: Intl.NumberFormatOptions["currencyDisplay"]; useGrouping?: boolean; minFractionDigits?: number; maxFractionDigits?: number; roundingMode?: Intl.NumberFormatOptions["roundingMode"]; min?: number | null; max?: number | null; step?: number; allowEmpty?: boolean; /** If false: show raw (no Intl formatting) even when blurred */ format?: boolean; inputId?: string; inputClassName?: string; inputStyle?: React.CSSProperties; inputRef?: React.Ref | null; prefix?: string; suffix?: string; size?: FieldSize; invalid?: boolean; } declare const InputNumber: React.MemoExoticComponent>>; type ShadcnNumberVariantProps = Omit & { /** * Show +/- buttons around the numeric field. * Defaults to false. */ showButtons?: boolean; /** * How the step buttons are laid out when showButtons is true. * * - 'inline': "-" on the left, "+" on the right * - 'stacked': vertical +/- stack on the right */ buttonLayout?: "inline" | "stacked"; }; type BaseProps$7 = VariantBaseProps; /** * Single-country phone config. * * - code: ISO 3166-1 alpha-2 ("NG", "US", "GB", ...) * - dial: dial code without "+" ("234", "1", "44", ...) * - mask: NATIONAL portion mask only (no dial), e.g. "999 999 9999" */ interface PhoneCountry { code: string; label: string; dial: string; mask: string; flag?: React.ReactNode; } /** * How the variant emits the form value. * * - "masked" → "+234 801 234 5678" * - "e164" → "2348012345678" (dial + national digits, no "+") * - "national"→ "8012345678" */ type PhoneValueMode = "masked" | "e164" | "national"; interface PhoneSpecificProps { countries?: PhoneCountry[]; defaultCountry?: string; onCountryChange?: (country: PhoneCountry) => void; showCountry?: boolean; countryPlaceholder?: string; showFlag?: boolean; showSelectedLabel?: boolean; showSelectedDial?: boolean; showDialInList?: boolean; /** * Controls how the emitted value is shaped. * * Default mirrors legacy autoUnmask=true + emitE164=true → "e164". */ valueMode?: PhoneValueMode; /** * When true, the national mask keeps placeholder characters * for not-yet-filled positions. When false, trailing mask * fragments are omitted. */ keepCharPositions?: boolean; /** * Style hooks for the internal country selector. */ countrySelectClassName?: string; countryTriggerClassName?: string; countryValueClassName?: string; countryContentClassName?: string; countryItemClassName?: string; } type TextUiProps$4 = Omit; /** * Full props for the phone variant as seen by the form runtime. * * - Keeps the same `value`/`onValue` contract as other variants. * - Inherits visual/behavioural text props (size, density, className, etc.). * - Adds phone-specific configuration (countries, valueMode, etc.). */ type ShadcnPhoneVariantProps = TextUiProps$4 & PhoneSpecificProps & Pick; type BaseProps$6 = VariantBaseProps; /** * Extra options specific to the color variant. */ interface ColorSpecificProps { /** * If false, we hide the colour preview box. * Default: true */ showPreview?: boolean; /** * If false, we hide the picker toggle control/icon. * Default: true */ showPickerToggle?: boolean; /** * Size of the colour swatch in pixels. * Default: 18 */ previewSize?: number; /** * Optional className for the outer wrapper that hosts * the Input + hidden color input. */ wrapperClassName?: string; /** * Optional className for the preview button itself (around the swatch). */ previewButtonClassName?: string; /** * Optional className for the swatch box inside the preview button. */ previewSwatchClassName?: string; /** * Optional className for the hidden ``. * * By default this input is visually hidden and only used * to invoke the browser/OS colour picker, but you can override * this class to make it visible and style it. */ pickerInputClassName?: string; /** * Custom icon shown in the trailing control as the picker toggle. * If omitted, a tiny ▾ triangle is used. */ pickerToggleIcon?: React.ReactNode; className?: string; } /** * We inherit the *visual/behavioural* props from ShadcnTextVariant, * but control value / onValue / type / inputMode / leadingControl / trailingControl ourselves. */ type TextUiProps$3 = Omit; /** * Full props for the color variant as seen by the form runtime. */ type ShadcnColorVariantProps = TextUiProps$3 & ColorSpecificProps & Pick; type BaseProps$5 = VariantBaseProps; /** * Options for the built-in password strength meter. * * NOTE: Score is always in the range 0–4 (inclusive). */ interface StrengthOptions { /** * Custom scoring function. * Return a number in the range 0–4 (inclusive) where 0 = weakest, 4 = strongest. */ calc?: (value: string) => number; /** * Labels for each score bucket (index 0..4). * Defaults to: ["Very weak", "Weak", "Okay", "Good", "Strong"] */ labels?: [string, string, string, string, string]; /** * Thresholds for score steps using a 0–100 bar. * Defaults to [0, 25, 50, 75, 100] mapping to scores 0..4 respectively. */ thresholds?: [number, number, number, number, number]; /** * Minimum score required to consider the password acceptable (0–4). * This is purely visual unless you enforce it in validate/onChange. * Default: 2 */ minScore?: number | 2; /** * Whether to show the textual label next to/under the bar. * Default: true */ showLabel?: boolean; /** * Where to render the meter. * - "inline" → compact row under the input * - "block" → stacked with more spacing * Default: "inline" */ display?: "inline" | "block"; } interface PasswordRuleConfig { /** * Pattern used to decide if the rule passes. */ pattern: RegExp; /** * If true, the rule is considered optional (recommendation). * Default: false unless the rule name is not prefixed with "!". */ optional?: boolean; /** * Weight in the scoring (relative importance). * Default: 1, doubled if the use key is prefixed with "!". */ weight?: number; /** * Short label for the rule (e.g. "At least 8 characters"). * Defaults to the map key if omitted. */ label?: string; /** * Longer description, used in detailed rule view. */ description?: string; } /** * A definition entry can be: * - string → treated as a regex source * - RegExp → used directly * - full config */ type PasswordRuleDefinition = string | RegExp | PasswordRuleConfig; /** * Map of alias/keys → definition entries. */ type PasswordDefinitionMap = Record; /** * Internal normalized state for a single rule. */ interface NormalizedRuleState { key: string; label: string; description?: string; optional: boolean; required: boolean; weight: number; passed: boolean; } /** * Props passed to custom meter renderers. */ interface PasswordMeterRenderProps { /** Raw password value. */ value: string; /** Bucket score 0..4 based on percent + thresholds. */ score: number; /** 0–100 progress used for the bar. */ percent: number; /** Human label for the current score. */ label: string; /** Whether score >= minScore. */ passed: boolean; /** Effective minScore after normalization. */ minScore: number; /** Effective thresholds used for bucketing. */ thresholds: [number, number, number, number, number]; /** Effective labels used. */ labels: [string, string, string, string, string]; /** Rule-level details when using a definition map. */ rules: NormalizedRuleState[]; } /** * Password-only props (on top of Shadcn text UI props & VariantBaseProps). * * This is what the form runtime sees as VariantPropsFor<"password">. */ interface PasswordVariantProps { /** Maximum number of characters permitted. */ maxLength?: number; /** Browser autocomplete hint (e.g., "current-password", "new-password"). */ autoComplete?: string; /** Show an eye button to toggle between obscured/plain text. (default: true) */ revealToggle?: boolean; /** Start in the revealed (plain text) state. */ defaultRevealed?: boolean; /** Called whenever the reveal state changes. */ onRevealChange?(revealed: boolean): void; /** Override the icons used for hide/show. */ renderToggleIcon?(revealed: boolean): React.ReactNode; /** Accessible label for the toggle button. */ toggleAriaLabel?(revealed: boolean): string; /** * Extra className for the reveal toggle button. */ toggleButtonClassName?: string; /** * Enable the built-in strength meter (boolean or options). * * - false / undefined → no built-in meter is shown * - true → use defaults * - object → merge with defaults */ strengthMeter?: boolean | StrengthOptions; /** * Optional rule definition map. */ ruleDefinitions?: PasswordDefinitionMap; /** * Selection of rule aliases to apply. * * - "length" → use ruleDefinitions["length"] with default importance * - "!length" → same rule but treated as more important */ ruleUses?: string[]; /** * Built-in meter style: * - "simple" → single bar + label * - "rules" → bar + per-rule checklist * Default: "simple" */ meterStyle?: "simple" | "rules"; /** * Optional custom meter renderer. */ renderMeter?(props: PasswordMeterRenderProps): React.ReactNode; /** * ClassNames for the meter and rules UI. */ meterWrapperClassName?: string; meterContainerClassName?: string; meterBarClassName?: string; meterLabelClassName?: string; rulesWrapperClassName?: string; rulesHeadingClassName?: string; rulesListClassName?: string; ruleItemClassName?: string; ruleIconClassName?: string; ruleLabelClassName?: string; /** * Extra className for the outer field wrapper. */ className?: string; } type TextUiProps$2 = Omit; /** * Full props for the Shadcn-based password variant. */ type ShadcnPasswordVariantProps = TextUiProps$2 & PasswordVariantProps & Pick; declare const buttonVariants: (props?: ({ variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined; size?: "sm" | "lg" | "default" | "icon" | "icon-sm" | "icon-lg" | null | undefined; } & class_variance_authority_types.ClassProp) | undefined) => string; declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps & { asChild?: boolean; }): react_jsx_runtime.JSX.Element; declare function Calendar({ className, classNames, showOutsideDays, captionLayout, buttonVariant, formatters, components, ...props }: React.ComponentProps & { buttonVariant?: React.ComponentProps["variant"]; }): react_jsx_runtime.JSX.Element; type DateMode = "single" | "range"; interface DateRange { from?: Date; to?: Date; } type DateValue = Date | DateRange | undefined; type BaseProps$4 = VariantBaseProps; type DisabledDays = React.ComponentProps["disabled"]; /** * Logical temporal "kind" for the field. * * This controls the default mask + formatting/parsing. * * - "date" → yyyy-MM-dd (default) * - "datetime" → yyyy-MM-dd HH:mm * - "time" → HH:mm * - "hour" → HH * - "monthYear" → MM/yyyy * - "year" → yyyy */ type DateKind = "date" | "datetime" | "time" | "hour" | "monthYear" | "year" | (string & {}); /** * Public props for the date variant (legacy + mask extensions). */ interface DateVariantProps { mode?: DateMode; placeholder?: React.ReactNode; clearable?: boolean; minDate?: Date; maxDate?: Date; disabledDays?: DisabledDays; /** * Pattern for single dates. * * Tokens: * - yyyy → full year * - MM → month (01–12) * - dd → day (01–31) * - HH → hours (00–23) * - mm → minutes (00–59) * * Default depends on `kind`: * - date → "yyyy-MM-dd" * - datetime → "yyyy-MM-dd HH:mm" * - time → "HH:mm" * - hour → "HH" * - monthYear → "MM/yyyy" * - year → "yyyy" */ formatSingle?: string; /** * String pattern or custom formatter for ranges. * * - string → same token rules as formatSingle, applied to both ends * - function → full control over display text */ formatRange?: string | ((range: DateRange | undefined) => string); /** * Separator when formatRange is a string pattern. * Default: " – " */ rangeSeparator?: string; /** * When true, keep the calendar open after a selection. * * For range mode, the picker also stays open until both * `from` and `to` are chosen. */ stayOpenOnSelect?: boolean; /** * Controlled open state for the popover. */ open?: boolean; onOpenChange?(o: boolean): void; /** * Temporal kind (controls default mask + formatting/parsing). * * Default: "date". */ kind?: DateKind; /** * Optional explicit input mask pattern for the text input. * * If omitted, a sensible default based on `kind` is used. * * Mask tokens follow the same rules as the underlying Input mask: * 9 = digit, a = letter, * = alphanumeric. */ inputMask?: string; /** * Whether to render the calendar popover. * * Defaults: * - true for `kind` = "date" | "datetime" * - false for time-only kinds ("time", "hour", "monthYear", "year") */ showCalendar?: boolean; } /** * We still reuse the Shadcn text UI props (size, density, icons, etc.), * but we take over type/value/onValue and the controls. */ type TextUiProps$1 = Omit; /** * Full props for the Shadcn-based date variant. */ type ShadcnDateVariantProps = TextUiProps$1 & DateVariantProps & Pick; type ChipsValue = string[] | undefined; type BaseProps$3 = VariantBaseProps; /** * How we split text into chips when committing. */ type ChipsSeparator = string | RegExp | (string | RegExp)[]; /** * Placement of chips relative to the entry control. * * - "inline" → inside the same visual box (Input) or in the textarea toolbox. * - "below" → chips rendered as a block underneath the field. */ type ChipsPlacement = "inline" | "below"; /** * Chips-only props, on top of the injected ones. */ interface ChipsVariantProps { /** * Placeholder shown when there are no chips and input is empty. */ placeholder?: string; /** * Separators used to split raw input into chips. * * - string → split on that string * - RegExp → split with regex * - array → try each in order * * Default: [",", ";"] */ separators?: ChipsSeparator; /** * When true, pressing Enter commits the current input as chips. * Default: true */ addOnEnter?: boolean; /** * When true, pressing Tab commits the current input as chips. * Default: true */ addOnTab?: boolean; /** * When true, blurring the field commits any remaining input as chips. * Default: true */ addOnBlur?: boolean; /** * When false, duplicate chips are ignored. * Default: false */ allowDuplicates?: boolean; /** * Maximum number of chips allowed. * Undefined → unlimited. */ maxChips?: number; /** * When true, Backspace on empty input removes the last chip. * Default: true */ backspaceRemovesLast?: boolean; /** * Show a small clear-all button. * Default: false */ clearable?: boolean; /** * Called when chips are added. */ onAddChips?(added: string[], next: string[]): void; /** * Called when chips are removed. */ onRemoveChips?(removed: string[], next: string[]): void; /** * Optional custom chip renderer. * * If provided, you are responsible for calling onRemove(index) * from your UI when you want to remove a chip. */ renderChip?(chip: string, index: number, ctx: { remove(): void; chips: string[]; }): React.ReactNode; /** * Optional custom overflow chip renderer. * * Receives the hidden count and the full chip list. */ renderOverflowChip?(hiddenCount: number, chips: string[]): React.ReactNode; /** * Max number of chips to *render*. * Extra chips are summarized as "+N more". */ maxVisibleChips?: number; /** * Max number of characters to *display* per chip. * The underlying value is not truncated. */ maxChipChars?: number; /** * CSS max-width for chip labels (e.g. 160 or "12rem"). */ maxChipWidth?: number | string; /** * When true, the entry control is a Textarea instead of Input. * Good for comment-style chip entry. */ textareaMode?: boolean; /** * Where chips are rendered relative to the entry. * * Default: * - Input mode → "inline" * - Textarea mode → "inline" */ placement?: ChipsPlacement; className?: string; chipsClassName?: string; chipClassName?: string; chipLabelClassName?: string; chipRemoveClassName?: string; inputClassName?: string; } /** * We still type against ShadcnTextVariantProps so chips can reuse * size/density/icon props etc. We take control of: * - type / value / onValue * - leadingControl / trailingControl */ type TextUiProps = Omit; /** * Full props for the Shadcn-based chips variant. */ type ShadcnChipsVariantProps = TextUiProps & ChipsVariantProps & Pick; interface TextareaIconControlProps { leadingIcons?: React.ReactNode[]; trailingIcons?: React.ReactNode[]; icon?: React.ReactNode; iconGap?: number; leadingIconSpacing?: number; trailingIconSpacing?: number; leadingControl?: React.ReactNode; trailingControl?: React.ReactNode; leadingControlClassName?: string; trailingControlClassName?: string; /** * If true, move the visual box (border, bg, radius, focus) from * `textarea-field` to `textarea-inner` so the side controls are * inside the same frame. * * Default: false (controls sit outside the border). */ extendBoxToControls?: boolean; /** * If true, move the visual box all the way up to `textarea-box`, * so the upper toolbox and the inner row share a single frame. * * When this is true, it overrides `extendBoxToControls`. * * Default: false. */ extendBoxToToolbox?: boolean; /** * Extra padding knobs (same semantics as Input). * * px → symmetric horizontal padding * py → symmetric vertical padding * ps/pe → logical start/end padding adjustments * pb → extra bottom padding (stacked with py) */ px?: number; py?: number; ps?: number; pe?: number; pb?: number; /** * Extra classes merged into the raw