import * as legacy from '../../../core/platform/custom-objects/fields'; import type { TextFieldDefinition, NumberFieldDefinition, RadioFieldDefinition, } from '../../../core/platform/custom-objects/types'; import { defineComponent, type ComponentDefinition } from './component'; import { Field, type FieldProps } from '../field'; import { scopedDefinition, type ObjectScoped, manifestObject, manifestSection, trackFieldSection, fieldSectionReferences, type ObjectReference, type SectionReference, } from './custom-object'; export type DefineFieldProps = Omit & { section: SectionReference>; }; export function defineField( object: TObject, props: DefineFieldProps, ): ObjectScoped { const section = manifestSection(object, props.section); const field = scopedDefinition( object, trackFieldSection(new Field(manifestObject(object), { ...props, section }), section), ); return fieldComponent.register(field, object, props); } /** Field factories for reusable definitions. Existing standalone factories remain available. */ export const fields: Readonly< Omit & { text: typeof text; number: typeof number; choice: typeof choice } > = Object.freeze({ ...legacy, text, number, choice }); const fieldComponent: ComponentDefinition = defineComponent({ type: 'CUSTOM_OBJECT_FIELD', define: defineField, references: (definition, object, props) => () => [ object, ...fieldSectionReferences(definition), ...(props.summarySpec == null ? [] : [ props.summarySpec.aggregates, props.summarySpec.lookup, ...props.summarySpec.filters.map((filter) => filter.field), ]), ], }); interface Presentation { label: string; description?: string; nullable?: TNullable; section?: TSection; } type Required = TNullable extends true ? false : true; type Rules = { readonly nullable: TNullable }; function presentation( props: Presentation, ) { return { displayName: props.label, description: props.description ?? '', required: !props.nullable as Required, nullable: (props.nullable ?? false) as TNullable, ...(props.section !== undefined ? { section: props.section } : {}), }; } function text< const TNullable extends boolean = false, const TSection extends string | undefined = string | undefined, >( props: Presentation & { maxLength?: number; unique?: boolean }, ): TextFieldDefinition, TSection> & Rules { return legacy.text({ ...props, ...presentation(props) }) as TextFieldDefinition< Required, TSection > & Rules; } function number< const TNullable extends boolean = false, const TSection extends string | undefined = string | undefined, >( props: Presentation & { decimalPlaces?: number }, ): NumberFieldDefinition, TSection> & Rules { return legacy.number({ ...props, ...presentation(props) }) as NumberFieldDefinition< Required, TSection > & Rules; } function choice< const TOptions extends readonly string[], const TNullable extends boolean = false, const TSection extends string | undefined = string | undefined, >( props: Presentation & { options: TOptions }, ): Omit, TSection>, '__valueType__'> & { readonly __valueType__?: TOptions[number]; } & Rules { return legacy.radio({ ...props, ...presentation(props) }) as Omit< RadioFieldDefinition, TSection>, '__valueType__' > & { readonly __valueType__?: TOptions[number] } & Rules; }