import * as i0 from '@angular/core'; import { OnInit, AfterViewInit, OnDestroy, EventEmitter, QueryList, Type, ElementRef, ViewContainerRef, TemplateRef } from '@angular/core'; import * as _lucide_angular from '@lucide/angular'; import { LucideIconData } from '@lucide/angular'; import { ModalConfig, MnModalRef, ModalKind, WizardModalConfig, FormModalConfig, ConfirmationModalConfig, CustomModalConfig, ModalFooterAction, ActionStyle, FormRow, FormFieldGroup, FieldKind, ModalCloseReason, SelectOption, FormFieldConfig, FieldDataSource, FieldVisibilityCondition, FieldRequiredCondition, ModalInputMap, WizardResult, ModalStepId, WizardStepConfig } from 'mn-angular-lib/modal-core'; import { MnButtonTypes } from 'mn-angular-lib/button'; import { FormGroup, ValidatorFn } from '@angular/forms'; import { MnInputField, MnTextarea, MnSelectOption, MnSelectProps } from 'mn-angular-lib/forms'; import { TableDataSource } from 'mn-angular-lib/collection'; declare class MnModalShellComponent implements OnInit, AfterViewInit, OnDestroy { /** Lucide icons the template renders. */ protected readonly icons: Record<"X", _lucide_angular.LucideIconData>; private readonly lang; /** * Accessible name for this control. Resolved through the conventional * `mnModal.close` key so an app can translate it, falling back to English when the * key is not defined rather than leaking the raw key into the UI. */ get closeModalLabel(): string; private el; private cdr; config: ModalConfig; modalRef: MnModalRef; isClosing: boolean; /** * Whether another modal is stacked on top of this one. Set imperatively by * `MnModalService` on the already-rendered shell below the newly opened one, so it must * be a signal: mutating a plain field there changes the host `[class]` after the view was * checked (NG0100 ExpressionChangedAfterItHasBeenChecked) and does not schedule change * detection in a zoneless app. A signal write both notifies the host binding and schedules CD. */ readonly isStacked: i0.WritableSignal; readonly ModalKind: typeof ModalKind; /** The rendered wizard body, when this modal is a wizard — used to read the active step title. */ private readonly wizardBody; /** Tailwind's `sm` breakpoint — below this the modal presents as a bottom sheet. */ private static readonly SHEET_MAX_WIDTH; /** * Title of the wizard's current step, or undefined for non-wizard modals. * The template appends it to the modal title on small screens, where the * step labels under the progress circles are hidden. */ readonly wizardStepTitle: i0.Signal; private previouslyFocusedElement; private focusTrapListener; private pollingTimer; private pollAttempts; /** Upper bound for the close wait if no animation/transition end event fires * (e.g. an animation was suppressed). Must stay longer than the slowest close * path so it never preempts. */ private static readonly CLOSE_FALLBACK_MS; /** Live match of the sheet breakpoint, so the modal switches between the centered dialog * and the bottom sheet when the viewport crosses it (e.g. an orientation change). */ readonly isNarrow: i0.WritableSignal; /** The bottom sheet presenting this modal on mobile, absent on the desktop dialog path. */ private readonly bottomSheet; /** Optional native haptic engine. Absent on the web — every call is null-guarded. */ private haptics; private sheetMedia; private sheetMediaListener; /** Whether this modal is allowed to present as a bottom sheet on small screens (default: true). */ get isMobileSheet(): boolean; /** Whether the modal should currently render as a bottom sheet (mobile) rather than the * centered dialog (desktop). */ get showMobileSheet(): boolean; get hostClasses(): string; private setupFocusTrap; private removeFocusTrap; asWizard(config: ModalConfig): WizardModalConfig; asForm(config: ModalConfig): FormModalConfig; asConfirmation(config: ModalConfig): ConfirmationModalConfig; asCustom(config: ModalConfig): CustomModalConfig; /** Whether the modal can be dismissed at all (drives the sheet's swipe/backdrop arming). */ get canClose(): boolean; ngOnInit(): void; ngOnDestroy(): void; /** * Triggers the closing animation and resolves once it has actually finished. * * Deferred via setTimeout to avoid NG0100 when called during a CD cycle. On mobile the * exit is owned by the bottom sheet, so we delegate to its `startClosing()` (idempotent * with a swipe-dismiss already in flight); on desktop we wait for the dialog container's * `animationend`/`transitionend`. A fallback timeout guarantees resolution if no event * fires, and we short-circuit under reduced motion (the CSS collapses to instant). */ startClosing(): Promise; private prefersReducedMotion; onEscapeKey(event: Event): void; onBackdropClick(): void; onCloseButtonClick(): void; /** * Guard consulted by the bottom sheet before it commits a swipe/flick/backdrop dismissal. * Mirrors the DISABLED/GUARDED rules of {@link handleClose} so a swipe cannot escape a * modal that a button close could not. Bound as a field so the template passes it directly. */ readonly sheetDismissGuard: () => Promise; /** * Handles the sheet's `(dismiss)` — emitted only after its guard passed and its exit * animation finished. Dismisses the modal (no re-guard) with a confirming haptic. */ onSheetDismiss(): void; ngAfterViewInit(): void; /** Tracks the sheet breakpoint through `matchMedia` so the dialog/sheet fork re-renders * when the viewport crosses it. */ private startWatchingViewport; /** Tears down the breakpoint listener. Idempotent. */ private stopWatchingViewport; /** Attempts to dismiss the modal. Resolves true if it was actually dismissed, * false if blocked by a DISABLED close mode or a rejected close guard. */ private handleClose; get showBackdrop(): boolean; get containerSizeClass(): string; get containerHeightStyle(): string | null; get showCloseButton(): boolean; get hasCustomFooterActions(): boolean; get leftFooterActions(): ModalFooterAction[]; get rightFooterActions(): ModalFooterAction[]; onFooterAction(action: ModalFooterAction): Promise; getActionButtonColor(style?: ActionStyle): 'primary' | 'secondary' | 'danger' | 'warning' | 'success'; getActionButtonVariant(style?: ActionStyle): 'fill' | 'outline' | 'text'; private startPollingIfConfigured; private startPolling; private stopPolling; static ɵfac: i0.ɵɵFactoryDeclaration, never>; static ɵcmp: i0.ɵɵComponentDeclaration, "mn-modal-shell", never, { "config": { "alias": "config"; "required": false; }; "modalRef": { "alias": "modalRef"; "required": false; }; }, {}, never, never, true, never>; } /** * A structural "view" over the {@link FormFieldConfig} discriminated union that * exposes every member-specific property as optional. The template and several * helpers need to read properties that only exist on some union members (e.g. * `placeholder`, `swatches`, `mode`, `dataSource`, `validators`). Casting the * field to this view keeps that access type-checked without resorting to `any`. */ type FormFieldView = FormFieldConfig & { label?: string; placeholder?: string; validators?: ValidatorFn[]; asyncValidators?: ValidatorFn[]; updateOn?: 'change' | 'blur' | 'submit'; options?: SelectOption[]; dataSource?: FieldDataSource; disabled?: boolean; readOnly?: boolean; visible?: FieldVisibilityCondition; conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; defaultValue?: unknown; mask?: string; autocomplete?: string; minDate?: string; maxDate?: string; mode?: 'date' | 'time' | 'datetime-local'; min?: number | string; max?: number | string; step?: number; rows?: number; searchable?: boolean; searchPlaceholder?: string; maxSelections?: number; collapseThreshold?: number; collapsePlaceholder?: string; allSelectedPlaceholder?: string; swatches?: string[]; showValue?: boolean; unit?: string; accept?: string; multiple?: boolean; displayMode?: 'dropzone' | 'thumbnail' | 'list' | 'compact'; dropzoneHint?: string; dropActiveHint?: string; replaceLabel?: string; removeLabel?: string; currentUrl?: string | null; currentUrls?: string[] | null; onClear?: () => void; maxSize: number; maxFiles?: number; component: Type; inputs?: ModalInputMap; }; declare class MnFormBodyComponent implements OnInit, OnDestroy, AfterViewInit { private fb; /** * Change detector used to announce state this component mutates outside an Angular * event handler — after an `await`, or from an RxJS subscription. Consuming apps run * zoneless, where such a write notifies nothing: the view is never marked dirty, so it * keeps rendering the stale value and `checkNoChanges` then reports it as NG0100 * (e.g. the submit button staying disabled on "Submitting..." after a failed submit). * Every async mutation of {@link isSubmitting}, {@link fieldLoading}, {@link fieldOptions} * and {@link formErrors} must be followed by `markForCheck()`. */ private readonly cdr; config: FormModalConfig; modalRef: MnModalRef; hideFooter: boolean; hideCustomBody: boolean; formStatusChange: EventEmitter; inputFields?: QueryList; textareas?: QueryList; form: FormGroup; rows: FormRow[]; fieldGroups: FormFieldGroup[]; isSubmitting: boolean; readonly FieldKind: typeof FieldKind; readonly ModalCloseReason: typeof ModalCloseReason; /** Cross-field validation errors: { fieldKey: errorMessage } */ formErrors: Record; /** Track which fields are currently visible (for conditional fields) */ fieldVisibility: Record; /** Track which fields are currently conditionally required */ fieldConditionallyRequired: Record; /** Track loading state per field for async data sources */ fieldLoading: Record; /** Dynamic options loaded from data sources */ fieldOptions: Record; private valueChangesSubscription?; asField(field: FormFieldConfig): FormFieldView; asKey(key: keyof TModel | string): string; asAny(val: unknown): any; hasRequiredValidator(field: FormFieldConfig): boolean; /** Store table data sources keyed by field key for template access */ tableDataSources: Record>; private languageService; private static readonly DEFAULT_LABELS; private resolveLabel; /** Resolved i18n labels with defaults, falling back to translated keys */ get labels(): { submit: string; cancel: string; submitting: string; selectPlaceholder: string; loading: string; fileUploadPrompt: string; fieldRequired: string; loadingOptions: string; accepted: string; maxSize: string; }; ngOnInit(): void; ngAfterViewInit(): void; applyAutoFocus(): void; ngOnDestroy(): void; private initializeForm; isFieldReadOnly(field: FormFieldConfig): boolean; isFieldDisabled(field: FormFieldConfig): boolean; /** Track which field groups are currently visible */ groupVisibility: Record; private buildRows; private initializeGroupVisibility; private updateGroupVisibility; isGroupVisible(group: FormFieldGroup): boolean; private initializeVisibility; private updateVisibility; /** * Builds the full validator array for a field, including conditionallyRequired. * @param field The field configuration view. * @param formValue The current form values. * @returns Array of validators to apply. */ private buildValidators; /** * Updates the conditionallyRequired state for a single field and adjusts validators. * @param field The field configuration view. * @param formValue The current form values. */ private updateConditionallyRequired; isFieldVisible(field: FormFieldConfig): boolean; private runFormValidators; getFieldError(key: string): string | null; get hasFormErrors(): boolean; private initializeDataSources; isFieldLoading(key: string): boolean; /** Get options for a field — uses dataSource options if available, otherwise static options */ getFieldOptions(field: FormFieldConfig): SelectOption[]; /** Convert SelectOption[] to MnSelectOption[] for mn-lib-select */ getSelectOptions(field: FormFieldConfig): MnSelectOption[]; getSelectProps(field: FormFieldConfig): MnSelectProps; private loadFieldOptions; private initializeTableFields; onTableSelectionChange(field: FormFieldConfig, selectedRows: unknown[]): void; getRatingRange(field: FormFieldConfig): number[]; setRating(field: FormFieldConfig, value: number): void; getRatingValue(field: FormFieldConfig): number; onSliderChange(field: FormFieldConfig, event: Event): void; getSliderValue(field: FormFieldConfig): number; onColorChange(field: FormFieldConfig, event: Event): void; setColorFromSwatch(field: FormFieldConfig, color: string): void; getColorValue(field: FormFieldConfig): string; private subscribeToValueChanges; private previousFormValue; private reloadDependentDataSources; /** * Invokes a FILE field's `onClear` callback when its existing image is removed. * FILE fields render {@link MnFileInput}, which owns selection/validation and * writes the value (`File | File[] | null`) straight to the form control. * @param field The field whose existing image was cleared. */ onFileCleared(field: FormFieldConfig): void; /** Icon size (px) for the footer action buttons. */ readonly actionIconSize = 18; /** Whether action-button icons should render on this modal (defaults to true). */ get showActionIcons(): boolean; /** The leading icon for the submit button, or null when icons are disabled. */ get submitIcon(): LucideIconData | null; /** The leading icon for the cancel button, or null when icons are disabled. */ get cancelIcon(): LucideIconData | null; submit(): Promise; static ɵfac: i0.ɵɵFactoryDeclaration, never>; static ɵcmp: i0.ɵɵComponentDeclaration, "mn-form-body", never, { "config": { "alias": "config"; "required": false; }; "modalRef": { "alias": "modalRef"; "required": false; }; "hideFooter": { "alias": "hideFooter"; "required": false; }; "hideCustomBody": { "alias": "hideCustomBody"; "required": false; }; }, { "formStatusChange": "formStatusChange"; }, never, never, true, never>; } declare class MnWizardBodyComponent implements OnInit, AfterViewInit, OnDestroy { private cdr; config: WizardModalConfig; modalRef: MnModalRef; formBodies: QueryList; stepWrappers: QueryList>; /** * The step body's scroll container. Every step renders into this one element * (inactive steps are hidden, not destroyed), so its scroll offset is shared — * without an explicit reset, arriving on a new step lands you wherever the * previous step was scrolled to. See {@link setCurrentStep}. */ stepScroller?: ElementRef; currentStepId: ModalStepId; /** * Title of the currently active step, mirrored into a signal so the modal * shell can append it to the modal title on small screens (where the step * labels under the progress circles are hidden). A signal — not a getter — * because the shell lives outside this component's change-detection subtree, * so a plain field mutation here would not update the shell in a zoneless app. */ readonly currentStepTitle: i0.WritableSignal; visitedStepIds: ModalStepId[]; isCurrentStepValid: boolean; isCompleting: boolean; wizardErrors: Record; /** * Min-height (px) for the step container, sized to the tallest step so the * modal does not jump when navigating between steps. Monotonic — only grows. */ measuredMinHeight: number; private languageService; private static readonly DEFAULT_LABELS; private resolveLabel; /** Resolved i18n labels with defaults, falling back to translated keys */ get labels(): { next: string; back: string; close: string; complete: string; completing: string; }; /** Pre-built form configs keyed by step id — only for steps that have fields */ stepFormConfigs: Record>; /** * Pre-built host configs keyed by step id — only for steps whose body is a * component or template (not a plain string) and that have no form fields. * Drives the `mn-custom-body-host` rendered for those steps. */ stepBodyConfigs: Record; private statusSubscription?; private formBodiesSubscription?; ngOnInit(): void; ngAfterViewInit(): void; isTextBody(step: WizardStepConfig): boolean; /** * Builds the {@link CustomModalConfig} used to render a step whose body is a * component or template. Returns `undefined` for steps with no body or a * plain-string body (those render through their own template branches). * @param step The wizard step to inspect. */ private buildStepBodyConfig; goToStep(step: WizardStepConfig): Promise; ngOnDestroy(): void; /** * Measures every step's natural height and records the tallest as * {@link measuredMinHeight}. Monotonic: the value only ever grows, so a * re-measure can never shrink the modal. Skipped when the modal has an * explicit height or is full-size (those are already fixed-height). */ private measureTallestStep; /** Get visible steps (filtered by visibility condition) */ get visibleSteps(): WizardStepConfig[]; isStepVisible(step: WizardStepConfig): boolean; get currentStep(): WizardStepConfig | undefined; get currentVisibleIndex(): number; get currentStepIndex(): number; get canGoBack(): boolean; get canGoNext(): boolean; get isLastStep(): boolean; /** Icon size (px) for the wizard action buttons. */ readonly actionIconSize = 18; /** Whether action-button icons should render on this wizard (defaults to true). */ get showActionIcons(): boolean; /** * The leading icon for the back button, or null when icons are disabled. * Shows a back arrow when navigation is possible, otherwise a cross (the * button acts as "Close" on the first step). */ get backIcon(): LucideIconData | null; /** The trailing icon for the next button, or null when icons are disabled. */ get nextIcon(): LucideIconData | null; /** The leading icon for the complete button, or null when icons are disabled. */ get completeIcon(): LucideIconData | null; /** Index of the current step for the progress line */ get currentProgressIndex(): number; get isFreeFlow(): boolean; canNavigateToStep(step: WizardStepConfig): boolean; next(): Promise; /** Find the MnFormBodyComponent for the current step */ private getCurrentFormBody; private trackCurrentStepValidity; back(): Promise; /** * Activates a step and mirrors its title into {@link currentStepTitle} so the * shell can reflect the step name in the modal title on small screens. * @param stepId Id of the step to make current. */ private setCurrentStep; complete(): Promise; /** Collect form data from all form-driven steps, namespaced by step ID */ private getAggregatedData; /** * Maps a footer action's ActionStyle to mnButton data props. * @param action The footer action configuration. */ getFooterActionButtonData(action: ModalFooterAction): Partial; /** * Handles a custom footer action click. * @param action The footer action configuration. */ handleFooterAction(action: ModalFooterAction): Promise; private notifyStepChange; static ɵfac: i0.ɵɵFactoryDeclaration; static ɵcmp: i0.ɵɵComponentDeclaration; } declare class MnConfirmationBodyComponent implements OnInit { /** Lucide icons the template renders. */ protected readonly icons: Record<"CircleAlert" | "TriangleAlert", LucideIconData>; private cdr; config: ConfirmationModalConfig; modalRef: MnModalRef; formBody?: MnFormBodyComponent; confirmButtonStatus: string; hasFormFields: boolean; private languageService; ngOnInit(): void; onFormStatusChange(status: string): void; confirm(): Promise; cancel(): void; private resolveLabel; get confirmLabel(): string; get cancelLabel(): string; get confirmStyle(): ActionStyle; get cancelStyle(): ActionStyle; get toneClass(): string; getButtonColor(style: ActionStyle): 'primary' | 'secondary' | 'danger' | 'warning' | 'success'; getButtonVariant(style: ActionStyle): 'fill' | 'outline' | 'text'; get isConfirmDisabled(): boolean; /** Icon size (px) for the action buttons. */ readonly actionIconSize = 18; /** Whether action-button icons should render on this modal (defaults to true). */ get showActionIcons(): boolean; /** * The leading icon for the confirm button, or null when icons are disabled. * Uses the per-action override, else defaults by style (DANGER → trash, else check). */ get confirmIcon(): LucideIconData | null; /** The leading icon for the cancel button, or null when icons are disabled. */ get cancelIcon(): LucideIconData | null; static ɵfac: i0.ɵɵFactoryDeclaration, never>; static ɵcmp: i0.ɵɵComponentDeclaration, "mn-confirmation-body", never, { "config": { "alias": "config"; "required": false; }; "modalRef": { "alias": "modalRef"; "required": false; }; }, {}, never, never, true, never>; } declare class MnCustomBodyHostComponent implements OnInit { config: CustomModalConfig; modalRef: MnModalRef; container: ViewContainerRef; private componentRef?; ngOnInit(): void; private loadContent; attachComponent(component: Type): void; attachTemplate(template: TemplateRef): void; static ɵfac: i0.ɵɵFactoryDeclaration; static ɵcmp: i0.ɵɵComponentDeclaration; } export { MnConfirmationBodyComponent, MnCustomBodyHostComponent, MnFormBodyComponent, MnModalShellComponent, MnWizardBodyComponent };