import { Type, TemplateRef, InjectionToken, ComponentRef } from '@angular/core'; export { TemplateRef, Type } from '@angular/core'; import { Observable } from 'rxjs'; import { ValidatorFn, AsyncValidatorFn } from '@angular/forms'; import { LucideIconData } from '@lucide/angular'; export { LucideIconData } from '@lucide/angular'; import { TableDataSource } from 'mn-angular-lib/collection'; declare enum ModalKind { WIZARD = "wizard", FORM = "form", CONFIRMATION = "confirmation", CUSTOM = "custom" } declare enum ModalSize { SM = "sm", MD = "md", LG = "lg", XL = "xl", FULL = "full" } declare enum CloseMode { ALLOWED = "allowed", GUARDED = "guarded", DISABLED = "disabled" } declare enum BackdropMode { HIDE = "hide", STATIC = "static", CLOSABLE = "closable" } declare enum KeyboardMode { ENABLED = "enabled", DISABLED = "disabled" } declare enum ModalIntent { NEUTRAL = "neutral", INFO = "info", SUCCESS = "success", WARNING = "warning", DANGER = "danger" } declare enum WizardFlowMode { LINEAR = "linear", FREE = "free" } declare enum FormLayoutMode { SINGLE_COLUMN = "single-column", TWO_COLUMN = "two-column", INLINE = "inline" } declare enum SubmitMode { ONCE = "once", RETRYABLE = "retryable" } declare enum ConfirmationTone { DEFAULT = "default", WARNING = "warning", DANGER = "danger" } declare enum FieldKind { TEXT = "text", NUMBER = "number", SELECT = "select", CHECKBOX = "checkbox", DATE = "date", TEXTAREA = "textarea", DATETIME = "datetime", MULTI_SELECT = "multi-select", MULTI_SELECT_TABLE = "multi-select-table", SINGLE_SELECT_TABLE = "single-select-table", PASSWORD = "password", FILE = "file", COLOR = "color", RATING = "rating", SLIDER = "slider", CUSTOM = "custom" } declare enum FieldAppearance { OUTLINE = "outline", FILLED = "filled", GHOST = "ghost" } declare enum SelectionMode { SINGLE = "single", MULTIPLE = "multiple" } declare enum OptionState { ENABLED = "enabled", DISABLED = "disabled" } declare enum ActionStyle { PRIMARY = "primary", SECONDARY = "secondary", DANGER = "danger", GHOST = "ghost" } declare enum StepState { PENDING = "pending", ACTIVE = "active", COMPLETE = "complete", DISABLED = "disabled", HIDDEN = "hidden" } declare enum NavigationDirection { FORWARD = "forward", BACKWARD = "backward", DIRECT = "direct" } declare enum ModalCloseReason { COMPLETED = "completed", CANCELLED = "cancelled", DISMISSED = "dismissed", BACKDROP = "backdrop", ESCAPE = "escape", PROGRAMMATIC = "programmatic", GUARD_REJECTED = "guard_rejected" } declare enum ValidationStatus { VALID = "valid", INVALID = "invalid", PENDING = "pending" } declare enum ValidationCode { REQUIRED = "required", MIN = "min", MAX = "max", PATTERN = "pattern", CUSTOM = "custom" } type ModalStepId = string; type ModalInputMap = Record; /** * A condition function that receives the current form values and returns * whether the field should be visible. */ type FieldVisibilityCondition = (formValue: Partial) => boolean; /** * A condition function that receives the current form values and returns * whether the field should be required. */ type FieldRequiredCondition = (formValue: Partial) => boolean; /** * A form-level validator that receives the entire form value * and returns an error map or null. */ type FormValidator = (formValue: Partial) => Record | null; /** * A data source that asynchronously loads options for select/multi-select fields. * Can optionally depend on other field values to reload. */ type FieldDataSource = { /** Load options, optionally based on current form values */ load(formValue?: Partial): Promise[]> | SelectOption[]; /** Keys of other fields that trigger a reload when their value changes */ dependsOn?: KeyOf[]; }; type ValidationResult = { status: ValidationStatus; code?: ValidationCode; message?: string; }; type StepValidator = { validate(): Promise | ValidationResult; }; type FieldValidator = { validate(value: unknown): Promise | ValidationResult; }; type StepGuard = { canEnter(): Promise | boolean; canExit(): Promise | boolean; }; type ModalCloseEvent = { reason: ModalCloseReason; result?: TResult; }; type ModalRef = { afterClosed$: Observable>; close(result?: TResult): void; dismiss(reason: ModalCloseReason): void; update(config: Partial>): void; }; type ModalResultHandler = { handle(result: TResult): Promise | void; }; type WizardStepChangeEvent = { previousStepId?: ModalStepId; currentStepId: ModalStepId; direction: NavigationDirection; }; type WizardStepChangeHandler = { handle(event: WizardStepChangeEvent): Promise | void; }; type StepBodyConfig = Type | TemplateRef | string; type WizardStepConfig = { id: ModalStepId; title: string; state?: StepState; body?: StepBodyConfig; /** * Inputs passed to the step {@link body} when it is a component or template * (ignored for a plain-string body). Each key is assigned to the matching * `@Input()` on the rendered component; a `modalRef` property, if present, is * populated automatically. Mirrors {@link CustomModalConfig.inputs} but scoped * to a single wizard step. */ bodyInputs?: ModalInputMap; fields?: FormFieldConfig[]; rows?: FormRow[]; fieldGroups?: FormFieldGroup[]; formValidators?: FormValidator[]; groupValidators?: ValidatorFn[]; initialValue?: Partial; guard?: StepGuard; validators?: StepValidator[]; /** Custom label for the 'Next' button on this step */ nextLabel?: string; /** Custom label for the 'Back' button on this step */ backLabel?: string; /** Whether to hide the 'Back' button on this step */ hideBack?: boolean; /** Condition to show/hide this entire step based on aggregated wizard data */ visible?: (aggregatedData: Record>) => boolean; }; type WizardResult = { status: ModalCloseReason; visitedStepIds: ModalStepId[]; payload?: Record>; }; type KeyOf = unknown extends T ? string : keyof T & string; type SelectOption = { label: string; value: TValue; state?: OptionState; }; type TextFieldConfig = { kind: FieldKind.TEXT; key: KeyOf; label: string; appearance?: FieldAppearance; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; placeholder?: string; /** Whether this field is read-only (display only) */ readOnly?: boolean; /** Whether this field is disabled */ disabled?: boolean; /** Condition to show/hide this field based on other field values */ visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; /** Input mask (e.g., '(000) 000-0000') */ mask?: string; /** Autocomplete attribute */ autocomplete?: string; /** Whether to focus this field when the modal opens */ autoFocus?: boolean; /** When to update the form control value and run validation */ updateOn?: 'change' | 'blur' | 'submit'; }; type NumberFieldConfig = { kind: FieldKind.NUMBER; key: KeyOf; label: string; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; placeholder?: string; min?: number; max?: number; step?: number; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type SelectFieldConfig = { kind: FieldKind.SELECT; key: KeyOf; label: string; options: SelectOption[]; selectionMode?: SelectionMode; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; /** Async data source for loading options dynamically */ dataSource?: FieldDataSource; }; type CheckboxFieldConfig = { kind: FieldKind.CHECKBOX; key: KeyOf; label: string; defaultValue?: boolean; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type DateFieldConfig = { kind: FieldKind.DATE; key: KeyOf; label: string; placeholder?: string; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; minDate?: string; maxDate?: string; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type TextareaFieldConfig = { kind: FieldKind.TEXTAREA; key: KeyOf; label: string; placeholder?: string; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; rows?: number; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type DatetimeFieldConfig = { kind: FieldKind.DATETIME; key: KeyOf; label: string; placeholder?: string; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; mode?: 'date' | 'time' | 'datetime-local'; min?: string; max?: string; step?: number; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type MultiSelectFieldConfig = { kind: FieldKind.MULTI_SELECT; key: KeyOf; label: string; options: SelectOption[]; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; /** Placeholder shown in the trigger while nothing is selected. */ placeholder?: string; searchable?: boolean; searchPlaceholder?: string; maxSelections?: number; /** * Forwarded to the underlying multi-select: once more than this many options are selected the * trigger collapses to a summary instead of rendering every chip. */ collapseThreshold?: number; /** Forwarded summary text for the collapsed trigger; `{count}` is interpolated. */ collapsePlaceholder?: string; /** Forwarded summary text shown when every option is selected. */ allSelectedPlaceholder?: string; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; /** Async data source for loading options dynamically */ dataSource?: FieldDataSource; }; type PasswordFieldConfig = { kind: FieldKind.PASSWORD; key: KeyOf; label: string; placeholder?: string; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type MultiSelectTableFieldConfig = { kind: FieldKind.MULTI_SELECT_TABLE; key: KeyOf; label: string; /** The TableDataSource that powers the mn-table. selectionMode will be forced to 'multi'. */ tableDataSource: TableDataSource; /** Function to extract the value stored in the form from a selected row (default: getID) */ getRowValue?: (row: TRow) => unknown; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type SingleSelectTableFieldConfig = { kind: FieldKind.SINGLE_SELECT_TABLE; key: KeyOf; label: string; /** The TableDataSource that powers the mn-table. selectionMode will be forced to 'single'. */ tableDataSource: TableDataSource; /** Function to extract the value stored in the form from a selected row (default: getID) */ getRowValue?: (row: TRow) => unknown; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type ColorFieldConfig = { kind: FieldKind.COLOR; key: KeyOf; label: string; /** Default color value (hex string, e.g., '#ff0000') */ defaultValue?: string; /** Predefined color swatches to show */ swatches?: string[]; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type RatingFieldConfig = { kind: FieldKind.RATING; key: KeyOf; label: string; /** Maximum rating value (default: 5) */ max?: number; /** Icon to use for rating (default: 'star') */ icon?: 'star' | 'heart' | 'circle'; /** Allow half-star ratings (default: false) */ allowHalf?: boolean; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type SliderFieldConfig = { kind: FieldKind.SLIDER; key: KeyOf; label: string; /** Minimum value (default: 0) */ min?: number; /** Maximum value (default: 100) */ max?: number; /** Step increment (default: 1) */ step?: number; /** Whether to show the current value label (default: true) */ showValue?: boolean; /** Unit label displayed after the value (e.g., '%', 'px') */ unit?: string; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type FileFieldConfig = { kind: FieldKind.FILE; key: KeyOf; label: string; /** Accepted file types (e.g., '.pdf,.jpg,image/*') */ accept?: string; /** Allow multiple file selection */ multiple?: boolean; /** Maximum file size in bytes */ maxSize?: number; /** Maximum number of files (when multiple is true) */ maxFiles?: number; /** How the selection is displayed (default: 'dropzone') */ displayMode?: 'dropzone' | 'thumbnail' | 'list' | 'compact'; /** Hint shown inside the empty dropzone */ dropzoneHint?: string; /** Hint shown inside the dropzone while files are dragged over it */ dropActiveHint?: string; /** Label for the "choose/replace file" affordance */ replaceLabel?: string; /** Accessible label for the per-file remove button */ removeLabel?: string; /** URL of an already-saved image to preview when no new file is selected (single mode) */ currentUrl?: string | null; /** URLs of already-saved images to preview when nothing is selected (multiple mode) */ currentUrls?: string[] | null; /** * Called when the user removes an already-saved image (`currentUrl`/`currentUrls`). * The form value stays null; use this to record the removal intent (e.g. to send * a "clear image" instruction to the API on submit). */ onClear?: () => void; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; readOnly?: boolean; disabled?: boolean; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type CustomFieldConfig = { kind: FieldKind.CUSTOM; key: KeyOf; component: Type; inputs?: ModalInputMap; label?: string; validators?: ValidatorFn[]; asyncValidators?: AsyncValidatorFn[]; visible?: FieldVisibilityCondition; /** Condition to dynamically mark this field as required based on other field values */ conditionallyRequired?: FieldRequiredCondition; autoFocus?: boolean; updateOn?: 'change' | 'blur' | 'submit'; }; type FormFieldConfig = TextFieldConfig | NumberFieldConfig | SelectFieldConfig | CheckboxFieldConfig | DateFieldConfig | TextareaFieldConfig | DatetimeFieldConfig | MultiSelectFieldConfig | MultiSelectTableFieldConfig | SingleSelectTableFieldConfig | PasswordFieldConfig | FileFieldConfig | ColorFieldConfig | RatingFieldConfig | SliderFieldConfig | CustomFieldConfig; type AnimationOptions = { type: 'slide' | 'fade' | 'zoom'; duration?: number; }; type FormRowField = { field: FormFieldConfig; span?: number; }; type FormRow = { columns?: number; fields: FormRowField[]; }; type FormFieldGroup = { /** Section header title */ title: string; /** Optional description below the section header */ description?: string; /** Fields in this group */ fields: FormFieldConfig[]; /** Optional rows layout for this group */ rows?: FormRow[]; /** Condition to show/hide this entire group based on form values */ visible?: FieldVisibilityCondition; }; type ConfirmationActionConfig = { label: string; style?: ActionStyle; handler?: ModalResultHandler; /** * Overrides the default leading icon for this action button. Pass a Lucide icon's * static data (e.g. `LucideTrash2.icon`). Only rendered when the modal's * `showActionIcons` is not explicitly `false`. */ icon?: LucideIconData; }; type CancellationActionConfig = { label: string; style?: ActionStyle; reason?: ModalCloseReason; /** * Overrides the default leading icon for this action button. Pass a Lucide icon's * static data (e.g. `LucideX.icon`). Only rendered when the modal's * `showActionIcons` is not explicitly `false`. */ icon?: LucideIconData; }; type ModalFooterAction = { label: string; style?: ActionStyle; /** Position in the footer: 'left' or 'right' (default: 'right') */ position?: 'left' | 'right'; /** Whether this action closes the modal */ closesModal?: boolean; /** Close reason when this action closes the modal */ closeReason?: ModalCloseReason; /** Handler called when the action is clicked */ handler?: (modalRef: ModalRef) => Promise | void; /** Whether the button is disabled */ disabled?: boolean; /** * Overrides the default leading icon for this action button. Pass a Lucide icon's * static data (e.g. `LucideCheck.icon`). Defaults are derived from `style` when * omitted. Only rendered when the modal's `showActionIcons` is not explicitly `false`. */ icon?: LucideIconData; }; type ModalPollingConfig = { /** Polling interval in milliseconds */ interval: number; /** Function called on each poll tick */ onPoll: (modalRef: ModalRef) => Promise | boolean | void; /** Whether to start polling immediately (default: true) */ autoStart?: boolean; /** Maximum number of poll attempts (undefined = unlimited) */ maxAttempts?: number; }; type ModalI18nLabels = { /** Submit button label (default: 'Submit') */ submit?: string; /** Cancel button label (default: 'Cancel') */ cancel?: string; /** Next button label for wizard (default: 'Next') */ next?: string; /** Back button label for wizard (default: 'Back') */ back?: string; /** Close button label (default: 'Close') */ close?: string; /** Complete button label for wizard (default: 'Complete') */ complete?: string; /** Submitting state label (default: 'Submitting...') */ submitting?: string; /** Completing state label (default: 'Completing...') */ completing?: string; /** Loading label (default: 'Loading...') */ loading?: string; /** Select placeholder (default: 'Select...') */ selectPlaceholder?: string; /** File upload prompt (default: 'Click or drag files here') */ fileUploadPrompt?: string; /** Confirm button label (default: 'Confirm') */ confirm?: string; }; type ModalCancelHandler<_TResult = unknown> = (reason: ModalCloseReason) => Promise | void; type BaseModalConfig = { kind: ModalKind; title?: string; subtitle?: string; description?: string; /** Width of the modal (uses ModalSize enum) */ sizeWidth?: ModalSize; /** Height of the modal (uses ModalSize enum) */ sizeHeight?: ModalSize; closeMode?: CloseMode; closeGuard?: () => Promise | boolean; backdrop?: BackdropMode; keyboard?: KeyboardMode; intent?: ModalIntent; resultType?: TResult; /** Custom footer actions (overrides default footer) */ footerActions?: ModalFooterAction[]; /** Whether modal action buttons render their icons. Defaults to true. */ showActionIcons?: boolean; /** Polling configuration for periodic async operations */ polling?: ModalPollingConfig; /** Handler called when the modal is cancelled or dismissed */ readOnly?: boolean; disabled?: boolean; onCancel?: ModalCancelHandler; /** i18n labels for buttons and UI text */ i18n?: ModalI18nLabels; /** Animation configuration */ animation?: AnimationOptions | AnimationOptions['type']; /** * Whether the modal renders as a bottom sheet on small screens (< 640px). * Defaults to true. Set to false to keep it a centered dialog on mobile. */ mobileBottomSheet?: boolean; /** Custom component to render in the modal body */ component?: Type; /** Custom template to render in the modal body */ template?: TemplateRef; /** Inputs for the custom component */ inputs?: ModalInputMap; }; type WizardBeforeCompleteValidator = (payload: Record>) => Promise> | null> | Partial> | null; type WizardModalConfig = { kind: ModalKind.WIZARD; steps: WizardStepConfig[]; startStepId?: ModalStepId; flow?: WizardFlowMode; onStepChange?: WizardStepChangeHandler; onComplete?: ModalResultHandler; /** Cross-step validators run before wizard completion */ onBeforeComplete?: WizardBeforeCompleteValidator[]; /** Global initial values for all steps */ initialValue?: Partial; } & BaseModalConfig; type FormModalConfig = { kind: ModalKind.FORM; body?: StepBodyConfig; fields: FormFieldConfig[]; rows?: FormRow[]; layout?: FormLayoutMode; initialValue?: Partial; submitMode?: SubmitMode; onComplete?: ModalResultHandler; /** Form-level validators for cross-field validation */ formValidators?: FormValidator[]; /** Angular FormGroup-level validators (e.g., Validators.required on the group) */ groupValidators?: ValidatorFn[]; /** Field groups with section headers */ fieldGroups?: FormFieldGroup[]; } & BaseModalConfig; type ConfirmationModalConfig = { kind: ModalKind.CONFIRMATION; message: string; tone?: ConfirmationTone; confirm?: ConfirmationActionConfig; cancel?: CancellationActionConfig; body?: StepBodyConfig; fields?: FormFieldConfig[]; rows?: FormRow[]; fieldGroups?: FormFieldGroup[]; formValidators?: FormValidator[]; groupValidators?: ValidatorFn[]; initialValue?: Partial; } & BaseModalConfig; type CustomModalConfig = { kind: ModalKind.CUSTOM; onComplete?: ModalResultHandler; } & BaseModalConfig; type ModalConfig = WizardModalConfig | FormModalConfig | ConfirmationModalConfig | CustomModalConfig; /** * Default leading-icon size (px) for modal action buttons, matching the standard * (`md`) button. Small (`sm`) buttons use {@link MODAL_ACTION_ICON_SIZE_SM}. */ declare const MODAL_ACTION_ICON_SIZE = 18; /** Leading-icon size (px) for `sm`-sized modal action buttons. */ declare const MODAL_ACTION_ICON_SIZE_SM = 16; /** * The canonical Lucide icon data used as defaults across all modal action buttons. * Each value is a Lucide icon's static `.icon` data, rendered via the dynamic * `svg[lucideIcon]` directive so no icon has to be registered in `MN_ICON_MAP`. */ declare const MN_MODAL_ACTION_ICONS: { /** Affirmative action (confirm / submit / complete). */ readonly confirm: LucideIconData; /** Destructive action (danger style). */ readonly danger: LucideIconData; /** Cancel / dismiss / close action. */ readonly cancel: LucideIconData; /** Wizard forward navigation (rendered trailing). */ readonly next: LucideIconData; /** Wizard backward navigation. */ readonly back: LucideIconData; }; /** * Resolves the default action-button icon from its {@link ActionStyle}. Used by * generic footer actions and any confirm button that has no explicit icon: * `DANGER` → trash, `PRIMARY` → check, everything else (`GHOST`/`SECONDARY`) → cross. * @param style The action's style, if any. * @returns The Lucide icon data to render. */ declare function defaultIconForStyle(style?: ActionStyle): LucideIconData; /** * Intensity of a haptic impact. Mirrors the three impact weights exposed by most * native haptic engines (e.g. Capacitor Haptics `ImpactStyle`) without binding the * library to any particular implementation. */ type MnHapticStyle = 'light' | 'medium' | 'heavy'; /** * Abstraction over a native haptic feedback engine. * * The modal feature deliberately does NOT depend on Capacitor (or any other native * bridge) so the library stays usable in plain web apps. Consumers that run inside a * native shell provide an implementation of this handler through {@link MN_HAPTICS}; * when no handler is provided the modal simply skips haptic feedback. */ type MnHapticsHandler = { /** * Triggers a transient impact-style haptic. * @param style Intensity of the impact. */ impact(style: MnHapticStyle): void; }; /** * Optional DI token used by the bottom sheet to emit haptic feedback on native * platforms (sheet open, swipe-dismiss, and snap-back). Provide a {@link MnHapticsHandler} * at the application root to enable it; leave unprovided on the web (the modal injects it * with `{ optional: true }` and no-ops when absent). */ declare const MN_HAPTICS: InjectionToken; /** * Shared interface for configurations that support form layouts. */ type FormContainerConfig = { fields?: FormFieldConfig[]; rows?: FormRow[]; fieldGroups?: FormFieldGroup[]; formValidators?: FormValidator[]; groupValidators?: ValidatorFn[]; initialValue?: Partial; body?: StepBodyConfig; }; /** * A self-referential type so that field-group builder methods can be chained. * e.g. g.field(...).field(...).field(...) */ type ChainableGroupBuilder = FormLayoutBuilder>; /** * A builder class that provides form layout capabilities (fields, rows, groups). * This can be used as a delegate to avoid code duplication between FormModalBuilder and StepBuilder. */ declare class FormLayoutBuilder { private readonly config; private parent; private currentRow; private currentRowColumns; constructor(config: FormContainerConfig, parent: TParent); /** * Add a custom body/content to the form/step. */ body(body: StepBodyConfig): TParent; /** * Add a field as a full-width row (single column). */ field(field: FormFieldConfig): TParent; /** * Start a new row with the specified number of columns. * All subsequent `addToRow()` calls will add fields to this row. */ row(columns?: number): TParent; /** * Add a field to the current row started by `row()`. * @param field - The field configuration * @param span - How many columns this field should span (default: 1) */ addToRow(field: FormFieldConfig, span?: number): TParent; /** * Declarative way to add a row. * @example * .addRow(2, row => { * row.add({ kind: FieldKind.TEXT, key: 'first', ... }); * row.add({ kind: FieldKind.TEXT, key: 'last', ... }); * }) */ addRow(columns: number, buildFn: (row: { add: (field: FormFieldConfig, span?: number) => void; }) => void): TParent; /** * Add a field group with a section header. */ fieldGroup(group: FormFieldGroup): TParent; /** * Add a field group using a functional builder. */ fieldGroup(title: string, buildFn: (group: ChainableGroupBuilder) => void): TParent; /** * Add a field group with title, description, and a functional builder. */ fieldGroup(title: string, description: string, buildFn: (group: ChainableGroupBuilder) => void): TParent; private processFieldGroup; /** * Add form-level validators for cross-field validation. */ formValidators(validators: FormValidator[]): TParent; /** * Add Angular FormGroup-level validators. */ groupValidators(validators: ValidatorFn[]): TParent; /** * Set initial value for fields. */ initialValue(value: Partial): TParent; /** * Set the field to be focused when the form initializes. */ focus(key: keyof TModel): TParent; /** * Wraps a field with a fluent API for validation. */ fieldWithValidators(field: FormFieldConfig): FieldValidatorBuilder; /** * Flushes any pending fields in the current row to the configuration. */ flushCurrentRow(): void; } /** * A builder for adding validation rules to a field fluently. */ declare class FieldValidatorBuilder { private field; private parent; constructor(field: FormFieldConfig, parent: TParent); required(_message?: string): this; minLength(length: number): this; maxLength(length: number): this; pattern(pattern: string | RegExp): this; email(): this; min(value: number): this; max(value: number): this; /** * Add a custom validator. */ custom(validator: ValidatorFn): this; /** * Return to the parent builder. */ done(): TParent; } declare class StepBuilder { private config; private layoutBuilder; constructor(id: string, title: string); /** * Set the step body. Accepts plain text, a component, or a template. * @param body The step body content (string, component `Type`, or `TemplateRef`). * @param inputs Optional inputs for a component/template body (ignored for text). */ body(body: StepBodyConfig, inputs?: ModalInputMap): this; state(state: StepState): this; guard(guard: StepGuard): this; validators(validators: StepValidator[]): this; field(field: FormFieldConfig): this; row(columns?: number): this; addToRow(field: FormFieldConfig, span?: number): this; addRow(columns: number, buildFn: (row: { add: (field: FormFieldConfig, span?: number) => void; }) => void): this; fieldGroup(group: FormFieldGroup): this; fieldGroup(title: string, buildFn: (group: ChainableGroupBuilder) => void): this; fieldGroup(title: string, description: string, buildFn: (group: ChainableGroupBuilder) => void): this; formValidators(validators: FormValidator[]): this; groupValidators(validators: ValidatorFn[]): this; initialValue(value: Partial): this; visible(condition: (aggregatedData: Record>) => boolean): this; nextLabel(label: string): this; backLabel(label: string): this; hideBack(hide?: boolean): this; build(): WizardStepConfig; } declare abstract class BaseModalBuilder & FormContainerConfig, TResult = unknown, TModel = unknown> { protected config: TConfig; protected layoutBuilder: FormLayoutBuilder; protected constructor(initialConfig: TConfig); title(title: string): this; subtitle(subtitle: string): this; description(description: string): this; closeGuard(guard: () => Promise | boolean): this; /** Set the width of the modal */ sizeWidth(size: ModalSize): this; /** Set the height of the modal */ sizeHeight(size: ModalSize): this; closeMode(mode: CloseMode): this; backdrop(mode: BackdropMode): this; keyboard(mode: KeyboardMode): this; intent(intent: ModalIntent): this; readOnly(readOnly?: boolean): this; disabled(disabled?: boolean): this; footerActions(actions: ModalFooterAction[]): this; polling(config: ModalPollingConfig): this; onCancel(handler: ModalCancelHandler): this; i18n(labels: ModalI18nLabels): this; component(component: Type): this; template(template: TemplateRef): this; inputs(inputs: ModalInputMap): this; animation(animation: AnimationOptions | AnimationOptions['type']): this; /** * Control whether the modal renders as a bottom sheet on small screens (< 640px). * Enabled by default; pass `false` to keep a centered dialog on mobile. */ mobileBottomSheet(enabled?: boolean): this; /** * Add a custom body/content to the modal. */ body(body: StepBodyConfig): this; /** * Add a field. */ field(field: FormFieldConfig): this; /** * Add a field with a fluent validation builder. */ fieldWithValidators(field: FormFieldConfig): FieldValidatorBuilder; /** * Start a new row. */ row(columns?: number): this; /** * Add a field to the current row. */ addToRow(field: FormFieldConfig, span?: number): this; /** * Declarative way to add a row. */ addRow(columns: number, buildFn: (row: { add: (field: FormFieldConfig, span?: number) => void; }) => void): this; /** * Add a field group. */ fieldGroup(group: FormFieldGroup): this; fieldGroup(title: string, buildFn: (group: ChainableGroupBuilder) => void): this; fieldGroup(title: string, description: string, buildFn: (group: ChainableGroupBuilder) => void): this; groupValidators(validators: ValidatorFn[]): this; build(): Readonly; } declare class WizardModalBuilder extends BaseModalBuilder, TResult> { constructor(); /** * Set initial values for the entire wizard. * Note: This will be merged with individual step initial values. */ initialValue(value: Partial): this; /** * Description is not supported for wizard modals. * Use step-level body text instead. */ description(_description: string): this; step(step: WizardStepConfig | ((builder: StepBuilder) => void)): this; addStep(title: string, buildFn: (builder: StepBuilder) => void, id?: ModalStepId): this; startAt(stepId: ModalStepId): this; flow(mode: WizardFlowMode): this; onStepChange(handler: WizardStepChangeHandler): this; onComplete(handler: ModalResultHandler): this; onBeforeComplete(validators: WizardBeforeCompleteValidator[]): this; } declare class FormModalBuilder extends BaseModalBuilder, TResult, TModel> { constructor(); /** * Override to also accept table field configs with a concrete TRow (BehaviorSubject is invariant, * so TableDataSource is not assignable to TableDataSource). */ field(field: FormFieldConfig): this; field(field: MultiSelectTableFieldConfig | SingleSelectTableFieldConfig): this; layout(mode: FormLayoutMode): this; initialValue(value: Partial): this; submitMode(mode: SubmitMode): this; onComplete(handler: ModalResultHandler): this; formValidators(validators: FormValidator[]): this; groupValidators(validators: ValidatorFn[]): this; build(): Readonly>; } declare class ConfirmationModalBuilder extends BaseModalBuilder, TResult> { constructor(); message(text: string): this; tone(tone: ConfirmationTone): this; confirmAction(action: ConfirmationActionConfig): this; cancelAction(action: CancellationActionConfig): this; initialValue(value: Partial): this; formValidators(validators: FormValidator[]): this; groupValidators(validators: ValidatorFn[]): this; build(): Readonly>; } declare class CustomModalBuilder extends BaseModalBuilder, TResult> { constructor(); onComplete(handler: ModalResultHandler): this; } declare class ModalBuilder { static wizard(): WizardModalBuilder; static form(): FormModalBuilder; static confirmation(): ConfirmationModalBuilder; static custom(): CustomModalBuilder; } declare class MnModalRef implements ModalRef { private componentRef; private config; private readonly closeSubject; readonly afterClosed$: Observable>; /** Whether close() or dismiss() has run, so a shell that finishes loading afterwards is never shown. */ private closeRequested; /** * @param componentRef The rendered modal shell, or null while `MnModalService` is still * loading the modal components (`mn-angular-lib/modal-ui`); it is attached with {@link attach}. * @param config The modal's mutable working config, shared with the shell. */ constructor(componentRef: ComponentRef | null, config: BaseModalConfig); close(result?: TResult): void; dismiss(reason: ModalCloseReason): void; private animateAndDestroy; update(config: Partial>): void; /** The rendered shell component, or undefined while the modal components are still loading. */ get component(): unknown; /** Whether close() or dismiss() has been called. */ get isCloseRequested(): boolean; /** * Attaches the rendered shell once `MnModalService` has loaded the modal components. * @param componentRef The created shell. */ attach(componentRef: ComponentRef): void; private destroy; } export { ActionStyle, BackdropMode, BaseModalBuilder, CloseMode, ConfirmationModalBuilder, ConfirmationTone, CustomModalBuilder, FieldAppearance, FieldKind, FormLayoutMode, FormModalBuilder, KeyboardMode, MN_HAPTICS, MN_MODAL_ACTION_ICONS, MODAL_ACTION_ICON_SIZE, MODAL_ACTION_ICON_SIZE_SM, MnModalRef, ModalBuilder, ModalCloseReason, ModalIntent, ModalKind, ModalSize, NavigationDirection, OptionState, SelectionMode, StepBuilder, StepState, SubmitMode, ValidationCode, ValidationStatus, WizardFlowMode, WizardModalBuilder, defaultIconForStyle }; export type { AnimationOptions, BaseModalConfig, CancellationActionConfig, CheckboxFieldConfig, ColorFieldConfig, ConfirmationActionConfig, ConfirmationModalConfig, CustomFieldConfig, CustomModalConfig, DateFieldConfig, DatetimeFieldConfig, FieldDataSource, FieldRequiredCondition, FieldValidator, FieldVisibilityCondition, FileFieldConfig, FormFieldConfig, FormFieldGroup, FormModalConfig, FormRow, FormRowField, FormValidator, MnHapticStyle, MnHapticsHandler, ModalCancelHandler, ModalCloseEvent, ModalConfig, ModalFooterAction, ModalI18nLabels, ModalInputMap, ModalPollingConfig, ModalRef, ModalResultHandler, ModalStepId, MultiSelectFieldConfig, MultiSelectTableFieldConfig, NumberFieldConfig, PasswordFieldConfig, RatingFieldConfig, SelectFieldConfig, SelectOption, SingleSelectTableFieldConfig, SliderFieldConfig, StepBodyConfig, StepGuard, StepValidator, TextFieldConfig, TextareaFieldConfig, ValidationResult, WizardBeforeCompleteValidator, WizardModalConfig, WizardResult, WizardStepChangeEvent, WizardStepChangeHandler, WizardStepConfig };