Skip to content

Commit a0c3b51

Browse files
committed
Converts confirm dialog composable to TypeScript
Migrates the confirm dialog utility from JavaScript to TypeScript, adding comprehensive type definitions and improved error handling. Introduces detailed interfaces for dialog options based on Bootstrap Vue modal specifications, enabling better IDE support and compile-time validation. Adds runtime validation for message parameters and graceful error handling for component failures, improving reliability and developer experience.
1 parent 9495aa9 commit a0c3b51

File tree

2 files changed

+267
-29
lines changed

2 files changed

+267
-29
lines changed

client/src/composables/confirmDialog.js

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
import { type Ref, ref } from "vue";
2+
3+
/**
4+
* Bootstrap Vue variants for styling components.
5+
*/
6+
export type BootstrapVariant =
7+
| "primary"
8+
| "secondary"
9+
| "success"
10+
| "warning"
11+
| "danger"
12+
| "info"
13+
| "light"
14+
| "dark"
15+
| "outline-primary"
16+
| "outline-secondary"
17+
| "outline-success"
18+
| "outline-warning"
19+
| "outline-danger"
20+
| "outline-info"
21+
| "outline-light"
22+
| "outline-dark";
23+
24+
/**
25+
* Bootstrap Vue modal message box options interface.
26+
* Based on https://bootstrap-vue.org/docs/components/modal#modal-message-boxes
27+
*/
28+
export interface ConfirmDialogOptions {
29+
/**
30+
* Unique identifier for the modal
31+
*/
32+
id?: string;
33+
/**
34+
* Modal title
35+
* @default "Please Confirm"
36+
*/
37+
title?: string;
38+
/**
39+
* CSS classes for title
40+
* @default "h-md"
41+
*/
42+
titleClass?: string;
43+
/**
44+
* Modal size: 'sm', 'lg', 'xl'
45+
* @default undefined (medium)
46+
*/
47+
size?: "sm" | "lg" | "xl";
48+
/**
49+
* Center modal vertically
50+
* @default true
51+
*/
52+
centered?: boolean;
53+
/**
54+
* Make modal body scrollable
55+
* @default false
56+
*/
57+
scrollable?: boolean;
58+
/**
59+
* Text for OK button
60+
* @default "OK"
61+
*/
62+
okTitle?: string;
63+
/**
64+
* Bootstrap variant for OK button
65+
* @default "primary"
66+
*/
67+
okVariant?: BootstrapVariant;
68+
/**
69+
* Text for cancel button
70+
* @default "Cancel"
71+
*/
72+
cancelTitle?: string;
73+
/**
74+
* Bootstrap variant for cancel button
75+
* @default "outline-primary"
76+
*/
77+
cancelVariant?: BootstrapVariant;
78+
/**
79+
* Footer button size: 'sm', 'lg'
80+
* @default undefined (normal)
81+
*/
82+
buttonSize?: "sm" | "lg";
83+
/**
84+
* Hide header close button
85+
* @default false
86+
*/
87+
hideHeaderClose?: boolean;
88+
/**
89+
* CSS classes for header
90+
*/
91+
headerClass?: string;
92+
/**
93+
* Background variant for header
94+
*/
95+
headerBgVariant?: BootstrapVariant;
96+
/**
97+
* Text variant for header
98+
*/
99+
headerTextVariant?: BootstrapVariant;
100+
/**
101+
* CSS classes for modal body
102+
*/
103+
bodyClass?: string;
104+
/**
105+
* Background variant for body
106+
*/
107+
bodyBgVariant?: BootstrapVariant;
108+
/**
109+
* Text variant for body
110+
*/
111+
bodyTextVariant?: BootstrapVariant;
112+
/**
113+
* CSS classes for footer
114+
*/
115+
footerClass?: string;
116+
/**
117+
* Background variant for footer
118+
*/
119+
footerBgVariant?: BootstrapVariant;
120+
/**
121+
* Text variant for footer
122+
*/
123+
footerTextVariant?: BootstrapVariant;
124+
/**
125+
* Prevent closing on backdrop click
126+
* @default false
127+
*/
128+
noCloseOnBackdrop?: boolean;
129+
/**
130+
* Prevent closing on ESC key
131+
* @default false
132+
*/
133+
noCloseOnEsc?: boolean;
134+
/**
135+
* Auto-focus button: 'ok', 'cancel', 'close'
136+
* @default undefined
137+
*/
138+
autoFocusButton?: "ok" | "cancel" | "close";
139+
/**
140+
* Element to return focus to
141+
* @default undefined
142+
*/
143+
returnFocus?: string | HTMLElement;
144+
/**
145+
* CSS classes for modal container
146+
* @default undefined
147+
*/
148+
modalClass?: string;
149+
/**
150+
* CSS classes for modal content
151+
* @default undefined
152+
*/
153+
contentClass?: string;
154+
/**
155+
* Disable fade animation
156+
* @default false
157+
*/
158+
noFade?: boolean;
159+
/**
160+
* Hide modal backdrop
161+
* @default false
162+
*/
163+
hideBackdrop?: boolean;
164+
}
165+
166+
/**
167+
* Interface for the confirm dialog component reference.
168+
*/
169+
interface ConfirmDialogComponent {
170+
confirm(message: string, options?: ConfirmDialogOptions): Promise<boolean>;
171+
}
172+
173+
/**
174+
* Default configuration for confirm dialogs.
175+
* Can be modified to change application-wide defaults.
176+
*/
177+
export const DEFAULT_CONFIRM_OPTIONS: Partial<ConfirmDialogOptions> = {
178+
title: "Please Confirm",
179+
titleClass: "h-md",
180+
centered: true,
181+
okTitle: "OK",
182+
cancelTitle: "Cancel",
183+
okVariant: "primary",
184+
cancelVariant: "outline-primary",
185+
buttonSize: undefined, // Uses Bootstrap default size
186+
hideHeaderClose: false,
187+
noCloseOnBackdrop: false,
188+
noCloseOnEsc: false,
189+
noFade: false,
190+
hideBackdrop: false,
191+
scrollable: false,
192+
} as const;
193+
194+
/**
195+
* Reference to the confirm dialog component instance
196+
*/
197+
let confirmDialogRef: Ref<ConfirmDialogComponent | null> = ref(null);
198+
199+
/**
200+
* Sets the confirm dialog component reference.
201+
* @param newRef - The new component reference
202+
*/
203+
export const setConfirmDialogComponentRef = (newRef: ConfirmDialogComponent | null): void => {
204+
// eslint-disable-next-line vue/no-ref-as-operand
205+
confirmDialogRef = ref(newRef);
206+
};
207+
208+
/**
209+
* Internal function to validate and normalize confirm dialog options.
210+
* @param options - Raw options to normalize
211+
* @returns Normalized options with defaults applied
212+
*/
213+
function normalizeConfirmOptions(options: ConfirmDialogOptions | string): ConfirmDialogOptions {
214+
// Handle backward compatibility: if options is a string, treat it as title
215+
const baseOptions: ConfirmDialogOptions = typeof options === "string" ? { title: options } : options;
216+
217+
return {
218+
...DEFAULT_CONFIRM_OPTIONS,
219+
...baseOptions,
220+
};
221+
}
222+
223+
/**
224+
* Direct export for Options API components.
225+
* For Composition API, use `useConfirmDialog` instead.
226+
*/
227+
export const ConfirmDialog = {
228+
/**
229+
* Shows a confirmation dialog to the user.
230+
* @param message - The confirmation message to display
231+
* @param options - Dialog options object or title string (for backward compatibility)
232+
* @returns Promise resolving to `true` if confirmed, `false` if cancelled/closed
233+
* @throws {Error} When confirm dialog component reference is not set
234+
*/
235+
async confirm(message: string, options: ConfirmDialogOptions | string = {}): Promise<boolean> {
236+
if (!confirmDialogRef.value) {
237+
throw new Error(
238+
"Confirm dialog component reference not set. " +
239+
"Call setConfirmDialogComponentRef() during app initialization.",
240+
);
241+
}
242+
243+
if (typeof message !== "string" || message.trim().length === 0) {
244+
throw new Error("Confirm dialog message must be a non-empty string.");
245+
}
246+
247+
const normalizedOptions = normalizeConfirmOptions(options);
248+
249+
try {
250+
return await confirmDialogRef.value.confirm(message, normalizedOptions);
251+
} catch (error) {
252+
console.error("Confirm dialog error:", error);
253+
// Gracefully handle component errors by returning false (cancelled)
254+
return false;
255+
}
256+
},
257+
};
258+
259+
/**
260+
* Composable for using confirm dialog in Composition API components.
261+
* Returns a stable reference to avoid unnecessary re-renders.
262+
*
263+
* @returns Object containing confirm dialog methods
264+
*/
265+
export function useConfirmDialog() {
266+
return { ...ConfirmDialog };
267+
}

0 commit comments

Comments
 (0)