prefer-modal-launcher
Require
presentModalcalls to live inside alaunch*launcher function.
- ⭐️ This rule is included in
plugin:@rdlabo/rules/recommendedpreset.
Call sites must use await launchXxxPage(helper, props) instead of inlining helper.presentModal(...).
Each modal page should export:
XxxProps/XxxDismisstypeslaunchXxxPage(helper, props)that wrapspresentModal
Pair with @rdlabo/rules/deny-overlay-create and @rdlabo/rules/deny-element.
Rule Details
❌ Incorrect: inline presentModal at the call site
export class ExamplePage {
readonly helper = inject(HelperService);
async open() {
await this.helper.presentModal(OtherPage, {}); // error
}
}
Also incorrect:
export async function openModal(overlay: Helper) {
await overlay.presentModal(ExamplePage, {}); // error — name does not match /^launch/
}
const show = () => overlay.presentModal(ExamplePage, {}); // error
✅ Correct: presentModal only inside a launcher
export interface OtherProps {
id: number;
}
export type OtherDismiss = { saved: boolean } | undefined;
export const launchOtherPage = (
helper: HelperService,
props: OtherProps,
): Promise<OtherDismiss> => {
return helper.presentModal(OtherPage, props, { watchKeyboard: false });
};
export class ExamplePage {
readonly helper = inject(HelperService);
async open() {
const data = await launchOtherPage(this.helper, { id: 1 });
if (data?.saved) {
// ...
}
}
}
Nested calls inside a launcher are fine:
export const launchExamplePage = (overlay: Helper, props: Props) => {
const run = () => overlay.presentModal(ExamplePage, props);
return run();
};
Options
{
// Method names treated as overlay presenters.
// default: ['presentModal']
presentMethodNames?: string[];
// RegExp source for allowed enclosing function / method names.
// default: '^launch'
launcherNamePattern?: string;
}
'@rdlabo/rules/prefer-modal-launcher': [
'error',
{
presentMethodNames: ['presentModal'],
launcherNamePattern: '^launch',
},
],
If your project uses open* launchers:
{
launcherNamePattern: '^(launch|open)';
}