import { type Ref, ref } from "vue"; import type { BootstrapSize, BootstrapVariant } from "@/components/Common"; /** * Bootstrap Vue modal message box options interface. * Based on https://bootstrap-vue.org/docs/components/modal#modal-message-boxes */ export interface ConfirmDialogOptions { /** * Unique identifier for the modal */ id?: string; /** * Modal title * @default "Please Confirm" */ title?: string; /** * CSS classes for title * @default "h-md" */ titleClass?: string; /** * Modal size: 'sm', 'lg', 'xl' * @default undefined (medium) */ size?: BootstrapSize; /** * Center modal vertically * @default true */ centered?: boolean; /** * Make modal body scrollable * @default false */ scrollable?: boolean; /** * Text for OK button * @default "OK" */ okTitle?: string; /** * Bootstrap variant for OK button * @default "primary" */ okVariant?: BootstrapVariant; /** * Text for cancel button * @default "Cancel" */ cancelTitle?: string; /** * Bootstrap variant for cancel button * @default "outline-primary" */ cancelVariant?: BootstrapVariant; /** * Footer button size: 'sm', 'lg' * @default undefined (normal) */ buttonSize?: BootstrapSize; /** * Hide header close button * @default false */ hideHeaderClose?: boolean; /** * CSS classes for header */ headerClass?: string; /** * Background variant for header */ headerBgVariant?: BootstrapVariant; /** * Text variant for header */ headerTextVariant?: BootstrapVariant; /** * CSS classes for modal body */ bodyClass?: string; /** * Background variant for body */ bodyBgVariant?: BootstrapVariant; /** * Text variant for body */ bodyTextVariant?: BootstrapVariant; /** * CSS classes for footer */ footerClass?: string; /** * Background variant for footer */ footerBgVariant?: BootstrapVariant; /** * Text variant for footer */ footerTextVariant?: BootstrapVariant; /** * Prevent closing on backdrop click * @default false */ noCloseOnBackdrop?: boolean; /** * Prevent closing on ESC key * @default false */ noCloseOnEsc?: boolean; /** * Auto-focus button: 'ok', 'cancel', 'close' * @default undefined */ autoFocusButton?: "ok" | "cancel" | "close"; /** * Element to return focus to * @default undefined */ returnFocus?: string | HTMLElement; /** * CSS classes for modal container * @default undefined */ modalClass?: string; /** * CSS classes for modal dialog wrapper * @default undefined */ dialogClass?: string; /** * CSS classes for modal content * @default undefined */ contentClass?: string; /** * Disable fade animation * @default false */ noFade?: boolean; /** * Hide modal backdrop * @default false */ hideBackdrop?: boolean; } /** * Interface for the confirm dialog component reference. */ interface ConfirmDialogComponent { confirm(message: string, options?: ConfirmDialogOptions): Promise; } /** * Default configuration for confirm dialogs. * Can be modified to change application-wide defaults. */ export const DEFAULT_CONFIRM_OPTIONS: Partial = { title: "Please Confirm", titleClass: "h-md", centered: true, okTitle: "OK", cancelTitle: "Cancel", okVariant: "primary", cancelVariant: "outline-primary", buttonSize: undefined, // Uses Bootstrap default size hideHeaderClose: false, noCloseOnBackdrop: false, noCloseOnEsc: false, noFade: false, hideBackdrop: false, scrollable: false, } as const; /** * Reference to the confirm dialog component instance */ let confirmDialogRef: Ref = ref(null); /** * Sets the confirm dialog component reference. * @param newRef - The new component reference */ export const setConfirmDialogComponentRef = (newRef: ConfirmDialogComponent | null): void => { // eslint-disable-next-line vue/no-ref-as-operand confirmDialogRef = ref(newRef); }; /** * Internal function to validate and normalize confirm dialog options. * @param options - Raw options to normalize * @returns Normalized options with defaults applied */ function normalizeConfirmOptions(options: ConfirmDialogOptions | string): ConfirmDialogOptions { // Handle backward compatibility: if options is a string, treat it as title const baseOptions: ConfirmDialogOptions = typeof options === "string" ? { title: options } : options; return { ...DEFAULT_CONFIRM_OPTIONS, ...baseOptions, }; } /** * Direct export for Options API components. * For Composition API, use `useConfirmDialog` instead. */ export const ConfirmDialog = { /** * Shows a confirmation dialog to the user. * @param message - The confirmation message to display * @param options - Dialog options object or title string (for backward compatibility) * @returns Promise resolving to `true` if confirmed, `false` if cancelled/closed * @throws {Error} When confirm dialog component reference is not set */ async confirm(message: string, options: ConfirmDialogOptions | string = {}): Promise { if (!confirmDialogRef.value) { throw new Error( "Confirm dialog component reference not set. " + "Call setConfirmDialogComponentRef() during app initialization.", ); } if (typeof message !== "string" || message.trim().length === 0) { throw new Error("Confirm dialog message must be a non-empty string."); } const normalizedOptions = normalizeConfirmOptions(options); try { return await confirmDialogRef.value.confirm(message, normalizedOptions); } catch (error) { console.error("Confirm dialog error:", error); // Gracefully handle component errors by returning false (cancelled) return false; } }, }; /** * Composable for using confirm dialog in Composition API components. * Returns a stable reference to avoid unnecessary re-renders. * * @returns Object containing confirm dialog methods */ export function useConfirmDialog() { return { ...ConfirmDialog }; }