Skip to content

Commit ee2fa19

Browse files
brandyscarneyTanner Reits
authored andcommitted
feat(menu): pass role to ionWillClose and ionDidClose events (#29954)
- Adds the `MenuCloseEventDetail` interface which includes an optional `role` property - The `ionWillClose` and `ionDidClose` emit the `role` property for the following scenarios: - A role of `'gesture'` when dragging the menu closed - A role of `'backdrop'` when clicking on the backdrop to close the menu - A role of `'backdrop'` when the the menu is closed using the escape key - A role of `undefined` when the menu is closed from a button inside of the menu
1 parent 2d6eeee commit ee2fa19

File tree

10 files changed

+163
-42
lines changed

10 files changed

+163
-42
lines changed

core/api.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,15 +1000,15 @@ ion-menu,prop,menuId,string | undefined,undefined,false,true
10001000
ion-menu,prop,side,"end" | "start",'start',false,true
10011001
ion-menu,prop,swipeGesture,boolean,true,false,false
10021002
ion-menu,prop,type,"overlay" | "push" | "reveal" | undefined,undefined,false,false
1003-
ion-menu,method,close,close(animated?: boolean) => Promise<boolean>
1003+
ion-menu,method,close,close(animated?: boolean, role?: string) => Promise<boolean>
10041004
ion-menu,method,isActive,isActive() => Promise<boolean>
10051005
ion-menu,method,isOpen,isOpen() => Promise<boolean>
10061006
ion-menu,method,open,open(animated?: boolean) => Promise<boolean>
1007-
ion-menu,method,setOpen,setOpen(shouldOpen: boolean, animated?: boolean) => Promise<boolean>
1007+
ion-menu,method,setOpen,setOpen(shouldOpen: boolean, animated?: boolean, role?: string) => Promise<boolean>
10081008
ion-menu,method,toggle,toggle(animated?: boolean) => Promise<boolean>
1009-
ion-menu,event,ionDidClose,void,true
1009+
ion-menu,event,ionDidClose,MenuCloseEventDetail,true
10101010
ion-menu,event,ionDidOpen,void,true
1011-
ion-menu,event,ionWillClose,void,true
1011+
ion-menu,event,ionWillClose,MenuCloseEventDetail,true
10121012
ion-menu,event,ionWillOpen,void,true
10131013
ion-menu,css-prop,--background,ios
10141014
ion-menu,css-prop,--background,md

core/src/components.d.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { ScrollBaseDetail, ScrollDetail } from "./components/content/content-int
1818
import { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
1919
import { SpinnerTypes } from "./components/spinner/spinner-configs";
2020
import { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface";
21-
import { MenuChangeEventDetail, MenuType, Side } from "./components/menu/menu-interface";
21+
import { MenuChangeEventDetail, MenuCloseEventDetail, MenuType, Side } from "./components/menu/menu-interface";
2222
import { ModalBreakpointChangeEventDetail, ModalHandleBehavior } from "./components/modal/modal-interface";
2323
import { NavComponent, NavComponentWithProps, NavOptions, RouterOutletOptions, SwipeGestureHandler, TransitionDoneFn, TransitionInstruction } from "./components/nav/nav-interface";
2424
import { ViewController } from "./components/nav/view-controller";
@@ -53,7 +53,7 @@ export { ScrollBaseDetail, ScrollDetail } from "./components/content/content-int
5353
export { DatetimeChangeEventDetail, DatetimeHighlight, DatetimeHighlightCallback, DatetimeHourCycle, DatetimePresentation, FormatOptions, TitleSelectedDatesFormatter } from "./components/datetime/datetime-interface";
5454
export { SpinnerTypes } from "./components/spinner/spinner-configs";
5555
export { InputChangeEventDetail, InputInputEventDetail } from "./components/input/input-interface";
56-
export { MenuChangeEventDetail, MenuType, Side } from "./components/menu/menu-interface";
56+
export { MenuChangeEventDetail, MenuCloseEventDetail, MenuType, Side } from "./components/menu/menu-interface";
5757
export { ModalBreakpointChangeEventDetail, ModalHandleBehavior } from "./components/modal/modal-interface";
5858
export { NavComponent, NavComponentWithProps, NavOptions, RouterOutletOptions, SwipeGestureHandler, TransitionDoneFn, TransitionInstruction } from "./components/nav/nav-interface";
5959
export { ViewController } from "./components/nav/view-controller";
@@ -1596,7 +1596,7 @@ export namespace Components {
15961596
/**
15971597
* Closes the menu. If the menu is already closed or it can't be closed, it returns `false`.
15981598
*/
1599-
"close": (animated?: boolean) => Promise<boolean>;
1599+
"close": (animated?: boolean, role?: string) => Promise<boolean>;
16001600
/**
16011601
* The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`.
16021602
*/
@@ -1628,7 +1628,7 @@ export namespace Components {
16281628
/**
16291629
* Opens or closes the button. If the operation can't be completed successfully, it returns `false`.
16301630
*/
1631-
"setOpen": (shouldOpen: boolean, animated?: boolean) => Promise<boolean>;
1631+
"setOpen": (shouldOpen: boolean, animated?: boolean, role?: string) => Promise<boolean>;
16321632
/**
16331633
* Which side of the view the menu should be placed.
16341634
*/
@@ -3969,9 +3969,9 @@ declare global {
39693969
};
39703970
interface HTMLIonMenuElementEventMap {
39713971
"ionWillOpen": void;
3972-
"ionWillClose": void;
3972+
"ionWillClose": MenuCloseEventDetail;
39733973
"ionDidOpen": void;
3974-
"ionDidClose": void;
3974+
"ionDidClose": MenuCloseEventDetail;
39753975
"ionMenuChange": MenuChangeEventDetail;
39763976
}
39773977
interface HTMLIonMenuElement extends Components.IonMenu, HTMLStencilElement {
@@ -6364,7 +6364,7 @@ declare namespace LocalJSX {
63646364
/**
63656365
* Emitted when the menu is closed.
63666366
*/
6367-
"onIonDidClose"?: (event: IonMenuCustomEvent<void>) => void;
6367+
"onIonDidClose"?: (event: IonMenuCustomEvent<MenuCloseEventDetail>) => void;
63686368
/**
63696369
* Emitted when the menu is open.
63706370
*/
@@ -6376,7 +6376,7 @@ declare namespace LocalJSX {
63766376
/**
63776377
* Emitted when the menu is about to be closed.
63786378
*/
6379-
"onIonWillClose"?: (event: IonMenuCustomEvent<void>) => void;
6379+
"onIonWillClose"?: (event: IonMenuCustomEvent<MenuCloseEventDetail>) => void;
63806380
/**
63816381
* Emitted when the menu is about to be opened.
63826382
*/

core/src/components/menu/menu-interface.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export interface MenuI {
2222
close(animated?: boolean): Promise<boolean>;
2323
toggle(animated?: boolean): Promise<boolean>;
2424
setOpen(shouldOpen: boolean, animated?: boolean): Promise<boolean>;
25-
_setOpen(shouldOpen: boolean, animated?: boolean): Promise<boolean>;
25+
_setOpen(shouldOpen: boolean, animated?: boolean, role?: string): Promise<boolean>;
2626
}
2727

2828
export interface MenuControllerI {
@@ -42,14 +42,18 @@ export interface MenuControllerI {
4242
_createAnimation(type: string, menuCmp: MenuI): Promise<Animation>;
4343
_register(menu: MenuI): void;
4444
_unregister(menu: MenuI): void;
45-
_setOpen(menu: MenuI, shouldOpen: boolean, animated: boolean): Promise<boolean>;
45+
_setOpen(menu: MenuI, shouldOpen: boolean, animated: boolean, role?: string): Promise<boolean>;
4646
}
4747

4848
export interface MenuChangeEventDetail {
4949
disabled: boolean;
5050
open: boolean;
5151
}
5252

53+
export interface MenuCloseEventDetail {
54+
role?: string;
55+
}
56+
5357
export interface MenuCustomEvent<T = any> extends CustomEvent {
5458
detail: T;
5559
target: HTMLIonMenuElement;

core/src/components/menu/menu.tsx

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import { shouldUseCloseWatcher } from '@utils/hardware-back-button';
77
import type { Attributes } from '@utils/helpers';
88
import { inheritAriaAttributes, assert, clamp, isEndSide as isEnd } from '@utils/helpers';
99
import { menuController } from '@utils/menu-controller';
10-
import { getPresentedOverlay } from '@utils/overlays';
10+
import { BACKDROP, GESTURE, getPresentedOverlay } from '@utils/overlays';
1111
import { hostContext } from '@utils/theme';
1212

1313
import { config } from '../../global/config';
1414
import { getIonMode } from '../../global/ionic-global';
1515
import type { Animation, Gesture, GestureDetail } from '../../interface';
1616

17-
import type { MenuChangeEventDetail, MenuI, MenuType, Side } from './menu-interface';
17+
import type { MenuChangeEventDetail, MenuCloseEventDetail, MenuI, MenuType, Side } from './menu-interface';
1818

1919
const iosEasing = 'cubic-bezier(0.32,0.72,0,1)';
2020
const mdEasing = 'cubic-bezier(0.0,0.0,0.2,1)';
@@ -179,7 +179,7 @@ export class Menu implements ComponentInterface, MenuI {
179179
/**
180180
* Emitted when the menu is about to be closed.
181181
*/
182-
@Event() ionWillClose!: EventEmitter<void>;
182+
@Event() ionWillClose!: EventEmitter<MenuCloseEventDetail>;
183183
/**
184184
* Emitted when the menu is open.
185185
*/
@@ -188,7 +188,7 @@ export class Menu implements ComponentInterface, MenuI {
188188
/**
189189
* Emitted when the menu is closed.
190190
*/
191-
@Event() ionDidClose!: EventEmitter<void>;
191+
@Event() ionDidClose!: EventEmitter<MenuCloseEventDetail>;
192192

193193
/**
194194
* Emitted when the menu state is changed.
@@ -331,14 +331,14 @@ export class Menu implements ComponentInterface, MenuI {
331331
if (shouldClose) {
332332
ev.preventDefault();
333333
ev.stopPropagation();
334-
this.close();
334+
this.close(undefined, BACKDROP);
335335
}
336336
}
337337
}
338338

339339
onKeydown(ev: KeyboardEvent) {
340340
if (ev.key === 'Escape') {
341-
this.close();
341+
this.close(undefined, BACKDROP);
342342
}
343343
}
344344

@@ -375,8 +375,8 @@ export class Menu implements ComponentInterface, MenuI {
375375
* it returns `false`.
376376
*/
377377
@Method()
378-
close(animated = true): Promise<boolean> {
379-
return this.setOpen(false, animated);
378+
close(animated = true, role?: string): Promise<boolean> {
379+
return this.setOpen(false, animated, role);
380380
}
381381

382382
/**
@@ -393,8 +393,8 @@ export class Menu implements ComponentInterface, MenuI {
393393
* If the operation can't be completed successfully, it returns `false`.
394394
*/
395395
@Method()
396-
setOpen(shouldOpen: boolean, animated = true): Promise<boolean> {
397-
return menuController._setOpen(this, shouldOpen, animated);
396+
setOpen(shouldOpen: boolean, animated = true, role?: string): Promise<boolean> {
397+
return menuController._setOpen(this, shouldOpen, animated, role);
398398
}
399399

400400
private trapKeyboardFocus(ev: Event, doc: Document) {
@@ -438,13 +438,13 @@ export class Menu implements ComponentInterface, MenuI {
438438
}
439439
}
440440

441-
async _setOpen(shouldOpen: boolean, animated = true): Promise<boolean> {
441+
async _setOpen(shouldOpen: boolean, animated = true, role?: string): Promise<boolean> {
442442
// If the menu is disabled or it is currently being animated, let's do nothing
443443
if (!this._isActive() || this.isAnimating || shouldOpen === this._isOpen) {
444444
return false;
445445
}
446446

447-
this.beforeAnimation(shouldOpen);
447+
this.beforeAnimation(shouldOpen, role);
448448

449449
await this.loadAnimation();
450450
await this.startAnimation(shouldOpen, animated);
@@ -459,7 +459,7 @@ export class Menu implements ComponentInterface, MenuI {
459459
return false;
460460
}
461461

462-
this.afterAnimation(shouldOpen);
462+
this.afterAnimation(shouldOpen, role);
463463

464464
return true;
465465
}
@@ -542,7 +542,7 @@ export class Menu implements ComponentInterface, MenuI {
542542
}
543543

544544
private onWillStart(): Promise<void> {
545-
this.beforeAnimation(!this._isOpen);
545+
this.beforeAnimation(!this._isOpen, GESTURE);
546546
return this.loadAnimation();
547547
}
548548

@@ -624,11 +624,11 @@ export class Menu implements ComponentInterface, MenuI {
624624

625625
this.animation
626626
.easing('cubic-bezier(0.4, 0.0, 0.6, 1)')
627-
.onFinish(() => this.afterAnimation(shouldOpen), { oneTimeCallback: true })
627+
.onFinish(() => this.afterAnimation(shouldOpen, GESTURE), { oneTimeCallback: true })
628628
.progressEnd(playTo ? 1 : 0, this._isOpen ? 1 - newStepValue : newStepValue, 300);
629629
}
630630

631-
private beforeAnimation(shouldOpen: boolean) {
631+
private beforeAnimation(shouldOpen: boolean, role?: string) {
632632
assert(!this.isAnimating, '_before() should not be called while animating');
633633

634634
// this places the menu into the correct location before it animates in
@@ -671,11 +671,11 @@ export class Menu implements ComponentInterface, MenuI {
671671
if (shouldOpen) {
672672
this.ionWillOpen.emit();
673673
} else {
674-
this.ionWillClose.emit();
674+
this.ionWillClose.emit({ role });
675675
}
676676
}
677677

678-
private afterAnimation(isOpen: boolean) {
678+
private afterAnimation(isOpen: boolean, role?: string) {
679679
// keep opening/closing the menu disabled for a touch more yet
680680
// only add listeners/css if it's enabled and isOpen
681681
// and only remove listeners/css if it's not open
@@ -731,7 +731,7 @@ export class Menu implements ComponentInterface, MenuI {
731731
}
732732

733733
// emit close event
734-
this.ionDidClose.emit();
734+
this.ionDidClose.emit({ role });
735735

736736
// undo focus trapping so multiple menus don't collide
737737
document.removeEventListener('focus', this.handleFocus, true);
@@ -767,7 +767,7 @@ export class Menu implements ComponentInterface, MenuI {
767767
* If the menu is disabled then we should
768768
* forcibly close the menu even if it is open.
769769
*/
770-
this.afterAnimation(false);
770+
this.afterAnimation(false, GESTURE);
771771
}
772772
}
773773

core/src/components/menu/test/basic/index.html

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@
5151
</ion-header>
5252
<ion-content>
5353
<ion-list>
54-
<ion-button id="start-menu-button">Button</ion-button>
54+
<ion-menu-toggle>
55+
<ion-button id="start-menu-button">Button</ion-button>
56+
</ion-menu-toggle>
5557
<ion-item>Menu Item</ion-item>
5658
<ion-item>Menu Item</ion-item>
5759
<ion-item>Menu Item</ion-item>
@@ -125,6 +127,19 @@
125127
</ion-app>
126128

127129
<script>
130+
window.addEventListener('ionWillOpen', function (e) {
131+
console.log('ionWillOpen', e);
132+
});
133+
window.addEventListener('ionDidOpen', function (e) {
134+
console.log('ionDidOpen', e);
135+
});
136+
window.addEventListener('ionWillClose', function (e) {
137+
console.log('ionWillClose', e);
138+
});
139+
window.addEventListener('ionDidClose', function (e) {
140+
console.log('ionDidClose', e);
141+
});
142+
128143
async function openStart() {
129144
// Open the menu by menu-id
130145
await menuController.enable(true, 'start-menu');

0 commit comments

Comments
 (0)