import { InjectionToken, Signal } from '@angular/core'; import { MatFormFieldAppearance } from '@angular/material/form-field'; import { FormControl, FormControlState, ValidatorFn, FormControlOptions, AsyncValidatorFn, AbstractControl, FormGroup, NgForm } from '@angular/forms'; import { Subject } from 'rxjs'; interface ISdFormConfiguration { appearance?: MatFormFieldAppearance; } declare const SD_FORM_CONFIGURATION: InjectionToken; declare class SdFormControl extends FormControl { sdChanges: Subject; untouchChanges: Subject; touchChanges: Subject; pristineChanges: Subject; constructor(formState?: FormControlState, validatorOrOpts?: ValidatorFn | ValidatorFn[] | FormControlOptions | null, asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[] | null); markAsUntouched(opts?: { onlySelf?: boolean; emitEvent?: boolean; }): void; markAsTouched(opts?: { onlySelf?: boolean; emitEvent?: boolean; }): void; markAsPristine(opts?: { onlySelf?: boolean; emitEvent?: boolean; }): void; } type SdCustomValidator = (value: any) => string | Promise; /** * Inline-error sentinel validator. Returns `{ inlineError: true }` so the form * template can render `{{ inlineError }}` whenever the * host component has a non-empty `[inlineError]` input. The error message * itself is read from the input — this validator only flags the state. * * why: each form component (input, textarea, select, checkbox, radio, switch, * date, datetime, input-number, autocomplete) previously declared the same * private `customInlineErrorValidator()` method. Centralized here so adding * another form component does not silently re-introduce the duplicate. */ declare const SdInlineErrorValidator: ValidatorFn; declare const HandleSdCustomValidator: (func: SdCustomValidator) => AsyncValidatorFn; interface SdSelectionData { multiple?: boolean; values: any[]; selectedItems: T[]; value?: any; selectedItem?: T; } interface SdSearchReq { type: 'SEARCH' | 'VALUE'; searchText?: string; searchFields?: string[]; value?: TData | TData[]; } type SdSearch = (args: SdSearchReq) => Promise; interface SdFormControlSnapshot { value: T | undefined; disabled: boolean; invalid: boolean; touched: boolean; } /** * Wrap an AbstractControl Signal into a reactive snapshot signal. * * Re-emits on every value, status, touched, or dirty change so * downstream consumers can derive `data-disabled`, `data-value`, * `data-empty`, and `data-invalid` host-binding attributes from a * single, lazily evaluated signal. * * "invalid" is intentionally gated on `touched || dirty` so validation * errors are not surfaced until the user has interacted with the field. * * Must be called inside an Angular injection context (constructor, * field initialiser, or `runInInjectionContext`). */ declare function sdFormControlState(control: Signal | null | undefined>): Signal>; /** * Three display states shared by sd-form-controls: * - `false` → full edit chrome (input / dropdown). * - `true` → static read-only view (`` text), no editor. * - `'inline'` → the editor is STILL rendered (so its panel works), but its chrome is * hidden; the `` text is the visible face / trigger. Click the text to open * the picker's panel. The text is retained while the panel is open — it only changes * when a new value is committed (JIRA-style click-to-edit). */ type SdViewed = boolean | 'inline'; type SdViewedInput = SdViewed | '' | null | undefined; /** * `viewed` input transform. Keeps `booleanAttribute` coercion so a bare attribute * (``) still resolves to `true`, but intercepts the literal * `'inline'` first — `booleanAttribute('inline')` would otherwise coerce it to `true`. */ declare function sdViewedTransform(v: SdViewedInput): SdViewed; interface SdViewedInlineApi { /** `viewed() === 'inline'` — editor rendered but chrome hidden; sd-view text is the trigger face. */ readonly isInline: Signal; /** `viewed() === true` — static read-only view (no editor rendered). */ readonly isViewed: Signal; /** Open the picker from the inline text face. No-op unless `'inline'`. */ enterInlineEdit(): void; } /** * Compose the tri-state `viewed` semantics into a control. `open` opens the control's * native picker (mat-select panel / mat-calendar / overlay). In `'inline'` mode the editor * is always rendered (chrome hidden via CSS), so `open()` can fire immediately on click — * no render-swap, the view text never disappears. * * @param viewed the control's `viewed` input signal. * @param open opens the control's picker; called by `enterInlineEdit`. * @param disabled the control's disabled state. why: a disabled `'inline'` field must behave * like `viewed=true` (static, NOT click-to-edit) — you can't edit a disabled control. */ declare function sdViewedInline(viewed: Signal, open?: () => void, disabled?: Signal): SdViewedInlineApi; type ɵSdFormControlParent = FormGroup | NgForm | { readonly form: unknown; } | null | undefined; interface ɵSdFormControlConnectorBaseOptions { /** Parent form source. NgForm and wrapper values are unwrapped on every rebind. */ readonly form: Signal<ɵSdFormControlParent>; /** Registration name. Empty names intentionally leave the control unregistered. */ readonly name: Signal; /** Canonical control registered in the parent form. */ readonly control: Signal>; readonly validators?: Signal; readonly asyncValidators?: Signal; /** Adds/removes Validators.required while preserving validators supplied above. */ readonly required?: Signal; readonly disabled?: Signal; /** UI-only read-only policy. This never disables the Angular control. */ readonly readonly?: Signal; /** Exact SDCoreJS display policy. This never disables the Angular control. */ readonly viewed?: Signal; /** Component-local validation message. Visibility is interaction-gated in state. */ readonly validationError?: Signal; } interface ɵSdFormControlRegistrationOptions extends ɵSdFormControlConnectorBaseOptions { readonly model?: never; readonly writeModel?: never; readonly modelToControl?: never; readonly controlToModel?: never; readonly modelEquals?: never; readonly controlEquals?: never; } interface ɵSdFormControlIdentityOptions extends ɵSdFormControlConnectorBaseOptions { readonly model: Signal; readonly writeModel: (value: TValue) => void; readonly modelToControl?: never; readonly controlToModel?: never; readonly modelEquals?: (left: TValue, right: TValue) => boolean; readonly controlEquals?: (left: TValue, right: TValue) => boolean; } interface ɵSdFormControlAdaptedOptions extends ɵSdFormControlConnectorBaseOptions { readonly model: Signal; readonly writeModel: (value: TModel) => void; readonly modelToControl: (value: TModel) => TControl; readonly controlToModel: (value: TControl) => TModel; readonly modelEquals?: (left: TModel, right: TModel) => boolean; readonly controlEquals?: (left: TControl, right: TControl) => boolean; } type ɵSdTypesExactlyMatch = [TLeft] extends [TRight] ? ([TRight] extends [TLeft] ? true : false) : false; /** * @internal Unstable connector contract for cross-entrypoint SDCoreJS controls. * Consumers must use registration-only, same-type identity binding, or provide * both adapters when model and control representations differ. */ type ɵSdFormControlConnectorOptions = ɵSdFormControlRegistrationOptions | ɵSdFormControlAdaptedOptions | (ɵSdTypesExactlyMatch extends true ? ɵSdFormControlIdentityOptions : never); interface ɵSdFormControlConnectorState { readonly value: TControl | undefined; readonly disabled: boolean; readonly invalid: boolean; readonly touched: boolean; readonly dirty: boolean; readonly required: boolean; readonly readonly: boolean; readonly viewed: SdViewed; readonly isViewed: boolean; readonly isInline: boolean; readonly showValidationError: boolean; readonly validationError: string | undefined; } interface ɵSdFormControlConnector { readonly state: Signal<ɵSdFormControlConnectorState>; markAsTouched(): void; markAsUntouched(): void; markAsDirty(): void; markAsPristine(): void; } /** Coerces the form shapes accepted by SDCoreJS controls into one FormGroup. */ declare function ɵsdCoerceFormGroup(value: unknown): FormGroup | undefined; /** * Connects the signal-based SDCoreJS model contract to an Angular control. * Registration and subscriptions are rebound transactionally, and cleanup only * removes a control while the connector still owns that exact registration. */ declare function ɵsdFormControlConnector(options: ɵSdFormControlConnectorOptions): ɵSdFormControlConnector; export { HandleSdCustomValidator, SD_FORM_CONFIGURATION, SdFormControl, SdInlineErrorValidator, sdFormControlState, sdViewedInline, sdViewedTransform, ɵsdCoerceFormGroup, ɵsdFormControlConnector }; export type { ISdFormConfiguration, SdCustomValidator, SdFormControlSnapshot, SdSearch, SdSearchReq, SdSelectionData, SdViewed, SdViewedInlineApi, SdViewedInput, ɵSdFormControlConnector, ɵSdFormControlConnectorOptions, ɵSdFormControlConnectorState, ɵSdFormControlParent };