{ "name": "auto-form", "type": "registry:ui", "dependencies": [ "vee-validate", "@vee-validate/zod", "zod" ], "registryDependencies": [ "form", "accordion", "button", "separator", "checkbox", "switch", "utils", "calendar", "popover", "label", "radio-group", "select", "input", "textarea" ], "files": [ { "path": "ui/auto-form/AutoForm.vue", "content": "\n\n\n \n \n \n \"\n :field-name=\"key.toString()\"\n :config=\"fieldConfig?.[key as keyof typeof fieldConfig] as ConfigItem\"\n >\n \n \n \n \n\n \n \n\n", "type": "registry:ui", "target": "auto-form/AutoForm.vue" }, { "path": "ui/auto-form/AutoFormField.vue", "content": "\n\n\n \n \n \n\n", "type": "registry:ui", "target": "auto-form/AutoFormField.vue" }, { "path": "ui/auto-form/AutoFormFieldArray.vue", "content": "\n\n\n \n \n \n \n \n \n \n {{ schema?.description || beautifyObjectName(fieldName) }}\n \n \n\n \n \n \n \n\n \n \n \n \n \n \n \n \n\n \n \n Add\n \n \n\n \n \n \n \n \n \n\n", "type": "registry:ui", "target": "auto-form/AutoFormFieldArray.vue" }, { "path": "ui/auto-form/AutoFormFieldBoolean.vue", "content": "\n\n\n \n \n \n \n \n \n \n \n \n {{ config?.label || beautifyObjectName(label ?? fieldName) }}\n \n \n\n \n {{ config.description }}\n \n \n \n \n\n", "type": "registry:ui", "target": "auto-form/AutoFormFieldBoolean.vue" }, { "path": "ui/auto-form/AutoFormFieldDate.vue", "content": "\n\n\n \n \n \n {{ config?.label || beautifyObjectName(label ?? fieldName) }}\n \n \n \n \n \n \n \n \n {{ slotProps.componentField.modelValue ? df.format(slotProps.componentField.modelValue.toDate(getLocalTimeZone())) : \"Pick a date\" }}\n \n \n \n \n \n \n \n \n \n\n \n {{ config.description }}\n \n \n \n \n\n", "type": "registry:ui", "target": "auto-form/AutoFormFieldDate.vue" }, { "path": "ui/auto-form/AutoFormFieldEnum.vue", "content": "\n\n\n \n \n \n {{ config?.label || beautifyObjectName(label ?? fieldName) }}\n \n \n \n \n \n \n {{ beautifyObjectName(option) }}\n \n \n\n \n \n \n \n \n \n {{ beautifyObjectName(option) }}\n \n \n \n \n \n\n \n {{ config.description }}\n \n \n \n \n\n", "type": "registry:ui", "target": "auto-form/AutoFormFieldEnum.vue" }, { "path": "ui/auto-form/AutoFormFieldFile.vue", "content": "\n\n\n \n \n \n {{ config?.label || beautifyObjectName(label ?? fieldName) }}\n \n \n \n {\n const file = (ev.target as HTMLInputElement).files?.[0]\n inputFile = file\n const parsed = await parseFileAsString(file)\n slotProps.componentField.onInput(parsed)\n }\"\n />\n \n {{ inputFile?.name }}\n {\n inputFile = undefined\n slotProps.componentField.onInput(undefined)\n }\"\n >\n \n \n \n \n \n \n {{ config.description }}\n \n \n \n \n\n", "type": "registry:ui", "target": "auto-form/AutoFormFieldFile.vue" }, { "path": "ui/auto-form/AutoFormFieldInput.vue", "content": "\n\n\n \n \n \n {{ config?.label || beautifyObjectName(label ?? fieldName) }}\n \n \n \n \n \n \n \n {{ config.description }}\n \n \n \n \n\n", "type": "registry:ui", "target": "auto-form/AutoFormFieldInput.vue" }, { "path": "ui/auto-form/AutoFormFieldNumber.vue", "content": "\n\n\n \n \n \n {{ config?.label || beautifyObjectName(label ?? fieldName) }}\n \n \n \n \n \n \n \n {{ config.description }}\n \n \n \n \n\n", "type": "registry:ui", "target": "auto-form/AutoFormFieldNumber.vue" }, { "path": "ui/auto-form/AutoFormFieldObject.vue", "content": "\n\n\n \n \n \n \n \n \n \n {{ schema?.description || beautifyObjectName(fieldName) }}\n \n \n \n \n \n \n \n \n \n \n \n \n\n", "type": "registry:ui", "target": "auto-form/AutoFormFieldObject.vue" }, { "path": "ui/auto-form/AutoFormLabel.vue", "content": "\n\n\n \n \n *\n \n\n", "type": "registry:ui", "target": "auto-form/AutoFormLabel.vue" }, { "path": "ui/auto-form/constant.ts", "content": "import type { InputComponents } from './interface'\nimport AutoFormFieldArray from './AutoFormFieldArray.vue'\nimport AutoFormFieldBoolean from './AutoFormFieldBoolean.vue'\nimport AutoFormFieldDate from './AutoFormFieldDate.vue'\nimport AutoFormFieldEnum from './AutoFormFieldEnum.vue'\nimport AutoFormFieldFile from './AutoFormFieldFile.vue'\nimport AutoFormFieldInput from './AutoFormFieldInput.vue'\nimport AutoFormFieldNumber from './AutoFormFieldNumber.vue'\nimport AutoFormFieldObject from './AutoFormFieldObject.vue'\n\nexport const INPUT_COMPONENTS: InputComponents = {\n date: AutoFormFieldDate,\n select: AutoFormFieldEnum,\n radio: AutoFormFieldEnum,\n checkbox: AutoFormFieldBoolean,\n switch: AutoFormFieldBoolean,\n textarea: AutoFormFieldInput,\n number: AutoFormFieldNumber,\n string: AutoFormFieldInput,\n file: AutoFormFieldFile,\n array: AutoFormFieldArray,\n object: AutoFormFieldObject,\n}\n\n/**\n * Define handlers for specific Zod types.\n * You can expand this object to support more types.\n */\nexport const DEFAULT_ZOD_HANDLERS: {\n [key: string]: keyof typeof INPUT_COMPONENTS\n} = {\n ZodString: 'string',\n ZodBoolean: 'checkbox',\n ZodDate: 'date',\n ZodEnum: 'select',\n ZodNativeEnum: 'select',\n ZodNumber: 'number',\n ZodArray: 'array',\n ZodObject: 'object',\n}\n", "type": "registry:ui", "target": "auto-form/constant.ts" }, { "path": "ui/auto-form/dependencies.ts", "content": "import type { Ref } from 'vue'\nimport type * as z from 'zod'\nimport { createContext } from 'reka-ui'\nimport { useFieldValue, useFormValues } from 'vee-validate'\nimport { computed, ref, watch } from 'vue'\nimport { type Dependency, DependencyType, type EnumValues } from './interface'\nimport { getFromPath, getIndexIfArray } from './utils'\n\nexport const [injectDependencies, provideDependencies] = createContext>>[] | undefined>>('AutoFormDependencies')\n\nexport default function useDependencies(\n fieldName: string,\n) {\n const form = useFormValues()\n // parsed test[0].age => test.age\n const currentFieldName = fieldName.replace(/\\[\\d+\\]/g, '')\n const currentFieldValue = useFieldValue(fieldName)\n\n if (!form)\n throw new Error('useDependencies should be used within ')\n\n const dependencies = injectDependencies()\n const isDisabled = ref(false)\n const isHidden = ref(false)\n const isRequired = ref(false)\n const overrideOptions = ref()\n\n const currentFieldDependencies = computed(() => dependencies.value?.filter(\n dependency => dependency.targetField === currentFieldName,\n ))\n\n function getSourceValue(dep: Dependency) {\n const source = dep.sourceField as string\n const index = getIndexIfArray(fieldName) ?? -1\n const [sourceLast, ...sourceInitial] = source.split('.').toReversed()\n const [_targetLast, ...targetInitial] = (dep.targetField as string).split('.').toReversed()\n\n if (index >= 0 && sourceInitial.join(',') === targetInitial.join(',')) {\n const [_currentLast, ...currentInitial] = fieldName.split('.').toReversed()\n return getFromPath(form.value, currentInitial.join('.') + sourceLast)\n }\n\n return getFromPath(form.value, source)\n }\n\n const sourceFieldValues = computed(() => currentFieldDependencies.value?.map(dep => getSourceValue(dep)))\n\n const resetConditionState = () => {\n isDisabled.value = false\n isHidden.value = false\n isRequired.value = false\n overrideOptions.value = undefined\n }\n\n watch([sourceFieldValues, dependencies], () => {\n resetConditionState()\n currentFieldDependencies.value?.forEach((dep) => {\n const sourceValue = getSourceValue(dep)\n const conditionMet = dep.when(sourceValue, currentFieldValue.value)\n\n switch (dep.type) {\n case DependencyType.DISABLES:\n if (conditionMet)\n isDisabled.value = true\n\n break\n case DependencyType.REQUIRES:\n if (conditionMet)\n isRequired.value = true\n\n break\n case DependencyType.HIDES:\n if (conditionMet)\n isHidden.value = true\n\n break\n case DependencyType.SETS_OPTIONS:\n if (conditionMet)\n overrideOptions.value = dep.options\n\n break\n }\n })\n }, { immediate: true, deep: true })\n\n return {\n isDisabled,\n isHidden,\n isRequired,\n overrideOptions,\n }\n}\n", "type": "registry:ui", "target": "auto-form/dependencies.ts" }, { "path": "ui/auto-form/index.ts", "content": "export { default as AutoForm } from './AutoForm.vue'\nexport { default as AutoFormField } from './AutoFormField.vue'\n\nexport { default as AutoFormFieldArray } from './AutoFormFieldArray.vue'\nexport { default as AutoFormFieldBoolean } from './AutoFormFieldBoolean.vue'\nexport { default as AutoFormFieldDate } from './AutoFormFieldDate.vue'\n\nexport { default as AutoFormFieldEnum } from './AutoFormFieldEnum.vue'\nexport { default as AutoFormFieldFile } from './AutoFormFieldFile.vue'\nexport { default as AutoFormFieldInput } from './AutoFormFieldInput.vue'\nexport { default as AutoFormFieldNumber } from './AutoFormFieldNumber.vue'\nexport { default as AutoFormFieldObject } from './AutoFormFieldObject.vue'\nexport { default as AutoFormLabel } from './AutoFormLabel.vue'\nexport type { Config, ConfigItem, FieldProps } from './interface'\nexport { getBaseSchema, getBaseType, getObjectFormSchema } from './utils'\n", "type": "registry:ui", "target": "auto-form/index.ts" }, { "path": "ui/auto-form/interface.ts", "content": "import type { Component, InputHTMLAttributes } from 'vue'\nimport type { z, ZodAny } from 'zod'\nimport type { INPUT_COMPONENTS } from './constant'\n\nexport interface FieldProps {\n fieldName: string\n label?: string\n required?: boolean\n config?: ConfigItem\n disabled?: boolean\n}\n\nexport interface Shape {\n type: string\n default?: any\n required?: boolean\n options?: string[]\n schema?: ZodAny\n}\n\nexport interface InputComponents {\n date: Component\n select: Component\n radio: Component\n checkbox: Component\n switch: Component\n textarea: Component\n number: Component\n string: Component\n file: Component\n array: Component\n object: Component\n};\n\nexport interface ConfigItem {\n /** Value for the `FormLabel` */\n label?: string\n /** Value for the `FormDescription` */\n description?: string\n /** Pick which component to be rendered. */\n component?: keyof typeof INPUT_COMPONENTS | Component\n /** Hide `FormLabel`. */\n hideLabel?: boolean\n inputProps?: InputHTMLAttributes\n}\n\n// Define a type to unwrap an array\ntype UnwrapArray = T extends (infer U)[] ? U : never\n\nexport type Config = {\n // If SchemaType.key is an object, create a nested Config, otherwise ConfigItem\n [Key in keyof SchemaType]?:\n SchemaType[Key] extends any[]\n ? UnwrapArray>\n : SchemaType[Key] extends object\n ? Config\n : ConfigItem;\n}\n\nexport enum DependencyType {\n DISABLES,\n REQUIRES,\n HIDES,\n SETS_OPTIONS,\n}\n\ninterface BaseDependency>> {\n sourceField: keyof SchemaType\n type: DependencyType\n targetField: keyof SchemaType\n when: (sourceFieldValue: any, targetFieldValue: any) => boolean\n}\n\nexport type ValueDependency>> =\n BaseDependency & {\n type:\n | DependencyType.DISABLES\n | DependencyType.REQUIRES\n | DependencyType.HIDES\n }\n\nexport type EnumValues = readonly [string, ...string[]]\n\nexport type OptionsDependency<\n SchemaType extends z.infer>,\n> = BaseDependency & {\n type: DependencyType.SETS_OPTIONS\n\n // Partial array of values from sourceField that will trigger the dependency\n options: EnumValues\n}\n\nexport type Dependency>> =\n | ValueDependency\n | OptionsDependency\n", "type": "registry:ui", "target": "auto-form/interface.ts" }, { "path": "ui/auto-form/utils.ts", "content": "import type { z } from 'zod'\n\n// TODO: This should support recursive ZodEffects but TypeScript doesn't allow circular type definitions.\nexport type ZodObjectOrWrapped =\n | z.ZodObject\n | z.ZodEffects>\n\n/**\n * Beautify a camelCase string.\n * e.g. \"myString\" -> \"My String\"\n */\nexport function beautifyObjectName(string: string) {\n // Remove bracketed indices\n // if numbers only return the string\n let output = string.replace(/\\[\\d+\\]/g, '').replace(/([A-Z])/g, ' $1')\n output = output.charAt(0).toUpperCase() + output.slice(1)\n return output\n}\n\n/**\n * Parse string and extract the index\n * @param string\n * @returns index or undefined\n */\nexport function getIndexIfArray(string: string) {\n const indexRegex = /\\[(\\d+)\\]/\n // Match the index\n const match = string.match(indexRegex)\n // Extract the index (number)\n const index = match ? Number.parseInt(match[1]) : undefined\n return index\n}\n\n/**\n * Get the lowest level Zod type.\n * This will unpack optionals, refinements, etc.\n */\nexport function getBaseSchema<\n ChildType extends z.ZodAny | z.AnyZodObject = z.ZodAny,\n>(schema: ChildType | z.ZodEffects): ChildType | null {\n if (!schema)\n return null\n if ('innerType' in schema._def)\n return getBaseSchema(schema._def.innerType as ChildType)\n\n if ('schema' in schema._def)\n return getBaseSchema(schema._def.schema as ChildType)\n\n return schema as ChildType\n}\n\n/**\n * Get the type name of the lowest level Zod type.\n * This will unpack optionals, refinements, etc.\n */\nexport function getBaseType(schema: z.ZodAny) {\n const baseSchema = getBaseSchema(schema)\n return baseSchema ? baseSchema._def.typeName : ''\n}\n\n/**\n * Search for a \"ZodDefault\" in the Zod stack and return its value.\n */\nexport function getDefaultValueInZodStack(schema: z.ZodAny): any {\n const typedSchema = schema as unknown as z.ZodDefault<\n z.ZodNumber | z.ZodString\n >\n\n if (typedSchema._def.typeName === 'ZodDefault')\n return typedSchema._def.defaultValue()\n\n if ('innerType' in typedSchema._def) {\n return getDefaultValueInZodStack(\n typedSchema._def.innerType as unknown as z.ZodAny,\n )\n }\n if ('schema' in typedSchema._def) {\n return getDefaultValueInZodStack(\n (typedSchema._def as any).schema as z.ZodAny,\n )\n }\n\n return undefined\n}\n\nexport function getObjectFormSchema(\n schema: ZodObjectOrWrapped,\n): z.ZodObject {\n if (schema?._def.typeName === 'ZodEffects') {\n const typedSchema = schema as z.ZodEffects>\n return getObjectFormSchema(typedSchema._def.schema)\n }\n return schema as z.ZodObject\n}\n\nfunction isIndex(value: unknown): value is number {\n return Number(value) >= 0\n}\n/**\n * Constructs a path with dot paths for arrays to use brackets to be compatible with vee-validate path syntax\n */\nexport function normalizeFormPath(path: string): string {\n const pathArr = path.split('.')\n if (!pathArr.length)\n return ''\n\n let fullPath = String(pathArr[0])\n for (let i = 1; i < pathArr.length; i++) {\n if (isIndex(pathArr[i])) {\n fullPath += `[${pathArr[i]}]`\n continue\n }\n\n fullPath += `.${pathArr[i]}`\n }\n\n return fullPath\n}\n\ntype NestedRecord = Record | { [k: string]: NestedRecord }\n/**\n * Checks if the path opted out of nested fields using `[fieldName]` syntax\n */\nexport function isNotNestedPath(path: string) {\n return /^\\[.+\\]$/.test(path)\n}\nfunction isObject(obj: unknown): obj is Record {\n return obj !== null && !!obj && typeof obj === 'object' && !Array.isArray(obj)\n}\nfunction isContainerValue(value: unknown): value is Record {\n return isObject(value) || Array.isArray(value)\n}\nfunction cleanupNonNestedPath(path: string) {\n if (isNotNestedPath(path))\n return path.replace(/\\[|\\]/g, '')\n\n return path\n}\n\n/**\n * Gets a nested property value from an object\n */\nexport function getFromPath(object: NestedRecord | undefined, path: string): TValue | undefined\nexport function getFromPath(\n object: NestedRecord | undefined,\n path: string,\n fallback?: TFallback,\n): TValue | TFallback\nexport function getFromPath(\n object: NestedRecord | undefined,\n path: string,\n fallback?: TFallback,\n): TValue | TFallback | undefined {\n if (!object)\n return fallback\n\n if (isNotNestedPath(path))\n return object[cleanupNonNestedPath(path)] as TValue | undefined\n\n const resolvedValue = (path || '')\n .split(/\\.|\\[(\\d+)\\]/)\n .filter(Boolean)\n .reduce((acc, propKey) => {\n if (isContainerValue(acc) && propKey in acc)\n return acc[propKey]\n\n return fallback\n }, object as unknown)\n\n return resolvedValue as TValue | undefined\n}\n", "type": "registry:ui", "target": "auto-form/utils.ts" } ] }
{{ inputFile?.name }}