import type { PillNavItem, SidebarToolSection, SettingsModalSchema, TopBarSettingsConfig, } from '../types' import type { FormSectionSchema, } from '../types/form-builder' import { controlToFormField, } from './controlSchemaFormFields' import { controlViewItem, firstSidebarConfig, isSidebarEnabled, sectionConfig, } from './controlSchemaLayout' import { normalizeControls, } from './controlSchemaNormalize' import { humanize, orderedUnique, } from './controlSchemaUtils' import type { ControlFormSchema, ControlSchema, ControlSchemaOptions, } from './controlSchemaTypes' /** Convert a compact control schema into a FormBuilder schema. */ export function controlsToFormSchema( controls: ControlSchema, options: ControlSchemaOptions = {}, ): ControlFormSchema { const normalized = normalizeControls(controls, options) const sectionIds = orderedUnique(normalized.map(control => control.sectionId)) const sections = sectionIds.map((sectionId): FormSectionSchema => { const sectionControls = normalized.filter(control => control.sectionId === sectionId) const config = sectionConfig(sectionId, sectionControls, options) return { id: sectionId, title: config.title ?? config.label ?? humanize(sectionId), description: config.description, columns: config.columns ?? options.columns ?? 1, defaultOpen: config.defaultOpen, condition: config.condition, fields: sectionControls.map(controlToFormField), } }) return { sections, submitLabel: options.submitLabel, cancelLabel: options.cancelLabel, showCancel: options.showCancel, } } /** Convert controls into AppSidebar panels grouped by view and section. */ export function controlsToSidebarPanels( controls: ControlSchema, options: ControlSchemaOptions = {}, ): Record { const normalized = normalizeControls(controls, options) .filter(control => isSidebarEnabled(control.definition.sidebar)) const viewIds = orderedUnique(normalized.map(control => control.viewId)) const panels: Record = {} for (const viewId of viewIds) { const viewControls = normalized.filter(control => control.viewId === viewId) const sectionIds = orderedUnique(viewControls.map(control => control.sectionId)) panels[viewId] = sectionIds.map((sectionId): SidebarToolSection => { const sectionControls = viewControls.filter(control => control.sectionId === sectionId) const config = sectionConfig(sectionId, sectionControls, options) const sidebarConfig = firstSidebarConfig(sectionControls) return { id: sectionId, label: sidebarConfig?.label ?? config.label ?? config.title ?? humanize(sectionId), subtitle: sidebarConfig?.subtitle ?? config.subtitle, icon: sidebarConfig?.icon ?? config.icon, iconColor: sidebarConfig?.iconColor ?? config.iconColor, iconBg: sidebarConfig?.iconBg ?? config.iconBg, badge: sidebarConfig?.badge ?? config.badge, badgeTone: sidebarConfig?.badgeTone ?? config.badgeTone, actions: sidebarConfig?.actions ?? config.actions, defaultOpen: sidebarConfig?.defaultOpen ?? config.defaultOpen, showToggle: sidebarConfig?.showToggle ?? config.showToggle, } }) } return panels } /** Convert controls into a SettingsModal schema grouped by the same sections. */ export function controlsToSettingsSchema( controls: ControlSchema, options: ControlSchemaOptions = {}, ): SettingsModalSchema { const normalized = normalizeControls(controls, options) const sectionIds = orderedUnique(normalized.map(control => control.sectionId)) return { groups: sectionIds.map((sectionId) => { const sectionControls = normalized.filter(control => control.sectionId === sectionId) const config = sectionConfig(sectionId, sectionControls, options) return { id: sectionId, label: config.label ?? config.title ?? humanize(sectionId), description: config.description ?? config.subtitle, icon: typeof config.icon === 'string' ? config.icon : undefined, fields: sectionControls.map(controlToFormField), columns: config.columns ?? options.columns ?? 1, condition: config.condition, access: config.access, } }), } } /** Convert controls into an AppTopBar settingsConfig object that passes compact controls through to SettingsModal. */ export function controlsToTopBarSettingsConfig( controls: ControlSchema, options: ControlSchemaOptions = {}, config: Omit = {}, ): TopBarSettingsConfig { return { ...config, controls, controlOptions: options, } } /** Return generated control view IDs that have at least one sidebar panel. */ export function controlsToViewIds( controls: ControlSchema, options: ControlSchemaOptions = {}, ): string[] { return controlsToViewItems(controls, options).map(item => item.id) } /** Return AppTopBar pillNav-compatible view items for switching generated control sidebars. */ export function controlsToViewItems( controls: ControlSchema, options: ControlSchemaOptions = {}, ): PillNavItem[] { return Object.entries(controlsToSidebarPanels(controls, options)) .filter(([, sections]) => sections.length > 0) .map(([id]) => controlViewItem(id, options)) } /** Return the first generated sidebar view ID, or an empty string when controls render no sidebar panels. */ export function getDefaultControlView( controls: ControlSchema, options: ControlSchemaOptions = {}, ): string { return controlsToViewIds(controls, options)[0] ?? '' } /** Return a headerless single-section FormBuilder schema for rendering inside an AppSidebar section slot. */ export function controlsToSectionFormSchema( controls: ControlSchema, sectionId: string, options: ControlSchemaOptions = {}, ): ControlFormSchema { const schema = controlsToFormSchema(controls, options) return { sections: schema.sections .filter(section => section.id === sectionId) .map(section => ({ ...section, title: '', description: undefined })), submitLabel: schema.submitLabel, cancelLabel: schema.cancelLabel, showCancel: schema.showCancel, } } /** Return headerless FormBuilder schemas keyed by section ID for AppSidebar auto-rendering. */ export function controlsToSectionFormSchemas( controls: ControlSchema, options: ControlSchemaOptions = {}, ): Record { const schema = controlsToFormSchema(controls, options) const schemas: Record = {} for (const section of schema.sections) { schemas[section.id] = { sections: [{ ...section, title: '', description: undefined }], submitLabel: schema.submitLabel, cancelLabel: schema.cancelLabel, showCancel: schema.showCancel, } } return schemas }