import type { SelectOption, } from '../types' import type { FormFieldType, } from '../types/form-builder' import { getTypeDefault, } from './formBuilderRegistry' import { humanize, } from './controlSchemaUtils' import type { ControlDefinition, ControlInput, ControlOption, ControlOptionValue, ControlSchema, ControlSchemaOptions, } from './controlSchemaTypes' export interface NormalizedControl { name: string definition: ControlDefinition type: FormFieldType sectionId: string viewId: string order: number } export function normalizeControls( controls: ControlSchema, options: ControlSchemaOptions = {}, ): NormalizedControl[] { return Object.entries(controls) .map(([name, input], index): NormalizedControl => { const definition = normalizeControlDefinition(input) const type = definition.type ?? inferControlType(definition) return { name, definition, type, sectionId: definition.section ?? options.section ?? 'controls', viewId: definition.view ?? options.view ?? 'default', order: definition.order ?? index, } }) .sort((a, b) => a.order - b.order) } export function normalizeControlDefinition(input: ControlInput): ControlDefinition { if (isControlOptionArray(input)) { if (input.length === 0) return { type: 'tags', default: [] } return { default: optionValue(input[0]), options: input, } } if (typeof input === 'string' || typeof input === 'number' || typeof input === 'boolean') { return { default: input } } return input } export function defaultValueForControl(definition: ControlDefinition, type: FormFieldType): unknown { if (definition.default !== undefined) return definition.default if (definition.defaultValue !== undefined) return definition.defaultValue return getTypeDefault(type) } export function normalizeOption(option: ControlOption): SelectOption { if (typeof option === 'object' && option !== null && 'label' in option) { return option } return { value: option, label: humanize(String(option)), } } function isControlOptionArray(input: ControlInput): input is readonly ControlOption[] { return Array.isArray(input) } function inferControlType(definition: ControlDefinition): FormFieldType { const value = definition.default ?? definition.defaultValue if (definition.options?.length) return Array.isArray(value) ? 'multiselect' : 'select' if (typeof value === 'boolean') return 'toggle' if (typeof value === 'number') return 'number' if (Array.isArray(value)) return 'tags' return 'text' } function optionValue(option: ControlOption): ControlOptionValue | unknown { if (typeof option === 'object' && option !== null && 'value' in option) { return option.value } return option }