import { Component } from 'vue'; import { IconType } from '../types'; type Primitive = string | number | boolean | null | undefined | Date | ((...args: any[]) => any); /** Option object format for select/radio/multiselect fields */ export interface SelectOption { label: string; value: T; icon?: IconType; } /** Async options loader function */ export type AsyncOptionsLoader = (query: string) => Promise<(T | SelectOption)[]>; /** Options can be string array, object array, or async loader function */ export type SelectOptions = T[] | readonly T[] | SelectOption[] | readonly SelectOption[] | AsyncOptionsLoader; /** * Extract all possible nested keys from a type, including: * - Dot notation paths: 'meta.bio' * - Array index paths: 'rules[0].name' * * @example * interface Form { * name: string * meta: { bio: string } * rules: Array<{ field: string, value: number }> * } * * type Keys = NestedKeyOf
* // 'name' | 'meta' | 'meta.bio' | 'rules' | 'rules[0]' | 'rules[0].field' | 'rules[0].value' */ export type NestedKeyOf = T extends Primitive ? never : { [K in keyof T & string]: K | (T[K] extends Primitive ? never : T[K] extends Array ? `${K}[${number}]` | (U extends Primitive ? never : `${K}[${number}].${NestedKeyOf}`) : `${K}.${NestedKeyOf}`); }[keyof T & string]; interface BaseFieldConfig { label?: string; required?: boolean; placeholder?: string; helpText?: string; underlined?: boolean; default?: any; class?: string; } interface TextFieldConfig { multiline?: boolean; pattern?: string; icon?: IconType; rows?: number; autofocus?: boolean; } /** * Syntactic sugar for required: true * @example $.email({ required }) instead of $.email({ required: true }) */ export declare const required = true; export interface FieldBuilder { _type: string; _config: BaseFieldConfig & Record; _condition?: string; _validations?: Array<(value: TValue) => true | string>; _onUpdate?: (value: TValue, formData: Record) => void; _class?: string; _slots?: Array; _component?: ComponentConfig; if: (condition: string) => FieldBuilder; validate: (fn: (value: TValue) => true | string) => FieldBuilder; onUpdate: (fn: (value: TValue, formData: Record) => void) => FieldBuilder; class: (className: string) => FieldBuilder; required: (message?: string) => FieldBuilder; slot: { (component: Component | string, props?: Record): FieldBuilder; (slotName: string, component: Component | string, props?: Record): FieldBuilder; }; } interface SlotConfig { name?: string; component: Component | string; props?: Record; } interface ComponentConfig { component: Component | string; props?: Record; } export interface SchemaDefinition = Record> { /** Phantom type — not present at runtime, used for FormFlow generic inference */ readonly __type: T; _isSchema: true; _label?: string; _fields: Record; _condition?: string; _class?: string; if: (condition: string) => SchemaDefinition; class: (className: string) => SchemaDefinition; /** Set default values for multiple fields at once. Field-level defaults take precedence. */ defaults: (values: Partial) => SchemaDefinition; /** Returns the default values extracted from field configs. */ getDefaults: () => Partial; toJSONSchema: () => any; } export declare const $: { text(labelOrConfig?: string | (BaseFieldConfig & TextFieldConfig), config?: BaseFieldConfig & TextFieldConfig): FieldBuilder; email(labelOrConfig?: string | BaseFieldConfig, config?: BaseFieldConfig): FieldBuilder; password(labelOrConfig?: string | BaseFieldConfig, config?: BaseFieldConfig): FieldBuilder; number(labelOrConfig?: string | (BaseFieldConfig & { min?: number; max?: number; step?: number; }), config?: BaseFieldConfig & { min?: number; max?: number; step?: number; }): FieldBuilder; tel(labelOrConfig?: string | (BaseFieldConfig & { onlyCountries?: string[]; defaultCountry?: string; }), config?: BaseFieldConfig & { onlyCountries?: string[]; defaultCountry?: string; }): FieldBuilder; url(labelOrConfig?: string | BaseFieldConfig, config?: BaseFieldConfig): FieldBuilder; date(labelOrConfig?: string | (BaseFieldConfig & { min?: string; max?: string; }), config?: BaseFieldConfig & { min?: string; max?: string; }): FieldBuilder; time(labelOrConfig?: string | BaseFieldConfig, config?: BaseFieldConfig): FieldBuilder; datetime(labelOrConfig?: string | BaseFieldConfig, config?: BaseFieldConfig): FieldBuilder; textarea(labelOrConfig?: string | (BaseFieldConfig & { rows?: number; cols?: number; }), config?: BaseFieldConfig & { rows?: number; cols?: number; }): FieldBuilder; richtext(labelOrConfig?: string | (BaseFieldConfig & { autoheight?: boolean; basic?: boolean; simple?: boolean; fontSize?: number | string; }), config?: BaseFieldConfig & { autoheight?: boolean; basic?: boolean; simple?: boolean; fontSize?: number | string; }): FieldBuilder; json(labelOrConfig?: string | (BaseFieldConfig & { language?: string; height?: string; }), config?: BaseFieldConfig & { language?: string; height?: string; }): FieldBuilder>; checkbox(labelOrConfig?: string | BaseFieldConfig, config?: BaseFieldConfig): FieldBuilder; toggle(labelOrConfig?: string | BaseFieldConfig, config?: BaseFieldConfig): FieldBuilder; radio(optionsOrLabel: SelectOptions | string, labelOrOptionsOrConfig?: string | SelectOptions | BaseFieldConfig, config?: BaseFieldConfig): FieldBuilder; select(optionsOrLabel: SelectOptions | string, labelOrOptionsOrConfig?: string | SelectOptions | (BaseFieldConfig & { searchable?: boolean; display?: "btn"; thin?: boolean; outline?: boolean; }), config?: BaseFieldConfig & { searchable?: boolean; display?: "btn"; thin?: boolean; outline?: boolean; }): FieldBuilder; multiselect(optionsOrLabel: SelectOptions | string, labelOrOptionsOrConfig?: string | SelectOptions | (BaseFieldConfig & { searchable?: boolean; display?: "btn"; thin?: boolean; outline?: boolean; }), config?: BaseFieldConfig & { searchable?: boolean; display?: "btn"; thin?: boolean; outline?: boolean; }): FieldBuilder; color(labelOrConfig?: string | BaseFieldConfig, config?: BaseFieldConfig): FieldBuilder; range(labelOrConfig?: string | (BaseFieldConfig & { min?: number; max?: number; step?: number; multiRange?: boolean; }), config?: BaseFieldConfig & { min?: number; max?: number; step?: number; multiRange?: boolean; }): FieldBuilder; upload(labelOrConfig?: string | (BaseFieldConfig & { multiple?: boolean; accept?: string; capture?: boolean | "user" | "environment"; dirPath?: string; width?: string; height?: string; icon?: IconType; theme?: "dropzone" | "basic"; fill?: boolean; oval?: boolean; placeholder?: string; noFilePlaceholder?: string; btnPlaceholder?: string; style?: string; }), config?: BaseFieldConfig & { multiple?: boolean; accept?: string; capture?: boolean | "user" | "environment"; dirPath?: string; width?: string; icon?: IconType; height?: string; theme?: "dropzone" | "basic"; fill?: boolean; oval?: boolean; placeholder?: string; noFilePlaceholder?: string; btnPlaceholder?: string; style?: string; }): FieldBuilder; array(labelOrSchemaOrComponent?: string | SchemaDefinition | Component, schemaOrComponentOrConfig?: SchemaDefinition | Component | string | (BaseFieldConfig & { allowAdd?: boolean; allowDelete?: boolean; allowReorder?: boolean; collapsible?: boolean; min?: number; max?: number; simple?: boolean; }), maybeConfig?: BaseFieldConfig & { allowAdd?: boolean; allowDelete?: boolean; allowReorder?: boolean; collapsible?: boolean; min?: number; max?: number; simple?: boolean; }): FieldBuilder; component(component: Component | string, props?: Record): FieldBuilder; /** * Display-only component that receives formData but doesn't bind to a field value. * Useful for summaries, previews, info panels, etc. * * @example * $.display(Summary) * $.display(Summary, { showPrice: true }) * * // The component receives: { formData, ...props } */ display(component: Component | string, props?: Record): FieldBuilder; }; /** * Define a single-step form schema or a step in a multi-step form * * Supports both direct keys and nested paths (dot notation): * - Direct: { email: $.email() } * - Nested: { 'meta.bio': $.text() } * * @example * // Single-step form * const userSchema = defineSchema({ * email: $.email({ required: true }), * firstName: $.text({ required: true }), * 'meta.bio': $.richtext() // Nested path - type-checked! * }) * * @example * // Multi-step form step (with label) * const step = defineSchema('Personal Info', { * firstName: $.text({ required: true }), * lastName: $.text({ required: true }) * }) */ export declare function defineSchema>(labelOrFields: string | Partial, FieldBuilder>>, maybeFields?: Partial, FieldBuilder>>): SchemaDefinition; /** * Multi-step schema type with embedded form data type * The __type property is a phantom type for TypeScript inference */ export type MultiStepSchemaDefinition = Record & { /** Phantom type for form data inference (not present at runtime) */ __type?: T; }; /** * Define a multi-step form schema * * @param T - The final merged form data type across all steps * * @example * interface OnboardingData { * userType: 'individual' | 'business' * firstName: string * lastName: string * companyName?: string * } * * const onboardingFlow = multiStepSchema({ * user_type: schema('Who are you?', { * userType: $.radio(['individual', 'business'], { required: true }) * }), * personal_info: schema('Personal Information', { * firstName: $.text({ required: true }), * lastName: $.text({ required: true }) * }), * business_info: schema('Company Details', { * companyName: $.text({ required: true }) * }).if('userType eq "business"') * }) * * // Later, extract the type: * type FormData = NonNullable */ export declare function multiStepSchema = Record>(steps: Record): MultiStepSchemaDefinition; interface JSONSchemaProperty { 'type'?: string; 'title'?: string; 'description'?: string; 'default'?: any; 'enum'?: any[]; 'format'?: string; 'minimum'?: number; 'maximum'?: number; 'minLength'?: number; 'maxLength'?: number; '$ref'?: string; 'items'?: JSONSchemaProperty; 'anyOf'?: JSONSchemaProperty[]; 'oneOf'?: JSONSchemaProperty[]; 'allOf'?: JSONSchemaProperty[]; 'additionalProperties'?: boolean | JSONSchemaProperty; 'x-ui'?: { fieldType?: string; placeholder?: string; disabled?: boolean; readonly?: boolean; autoheight?: boolean; rows?: number; cols?: number; class?: string; condition?: string; allowAdd?: boolean; allowDelete?: boolean; component?: string; componentProps?: Record; }; } interface JSONSchemaObject { 'type'?: string; 'title'?: string; 'properties'?: Record; 'required'?: string[]; '$defs'?: Record; 'definitions'?: Record; 'x-ui'?: { condition?: string; class?: string; }; } /** * Create a schema from a JSONSchema object * * This allows you to save schemas to a database and load them later. * * @example * // Save to database * const jsonSchema = mySchema.toJSONSchema() * await db.schemas.insert({ name: 'user-form', schema: jsonSchema }) * * // Load from database * const saved = await db.schemas.findOne({ name: 'user-form' }) * const loadedSchema = fromJSONSchema(saved.schema) * * // Use in component * */ export declare function fromJSONSchema(jsonSchema: JSONSchemaObject): SchemaDefinition; /** * Infer the TypeScript type from a schema definition */ export type InferSchemaType = { [K in keyof S['_fields']]: InferFieldType; }; type InferFieldType = F extends FieldBuilder ? T : any; export { schemaToFields, fieldsToSchema, initFormData, validateFormData, useSchemaToFields, inferFieldType as inferSchemaFieldType, defaultRowSpan } from './schema-fields'; export type { FormField, FormFieldType, FormFieldValidation, JSONSchemaObject, JSONSchemaProperty, SelectOption as SchemaSelectOption } from './schema-fields'; //# sourceMappingURL=form-flow.d.ts.map