Skip to content

Commit d83d8ee

Browse files
committed
fix(menu): setOpen must be coordinated by the controller
1 parent dfb4fe2 commit d83d8ee

File tree

3 files changed

+75
-65
lines changed

3 files changed

+75
-65
lines changed

packages/core/src/components/menu-controller/menu-controller.ts

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Animation, AnimationBuilder, AnimationController, Menu } from '../../index';
21
import { Component, Method, Prop } from '@stencil/core';
2+
import { Animation, AnimationBuilder, Menu } from '../../index';
33

44
import MenuOverlayAnimation from './animations/overlay';
55
import MenuPushAnimation from './animations/push';
@@ -11,9 +11,9 @@ import MenuRevealAnimation from './animations/reveal';
1111
export class MenuController {
1212

1313
private menus: Menu[] = [];
14-
private menuAnimations: { [name: string]: AnimationBuilder } = {};
14+
private menuAnimations = new Map<string, AnimationBuilder>();
1515

16-
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: AnimationController;
16+
@Prop({ connect: 'ion-animation-controller' }) animationCtrl: HTMLIonAnimationControllerElement;
1717

1818
constructor() {
1919
this.registerAnimation('reveal', MenuRevealAnimation);
@@ -29,11 +29,7 @@ export class MenuController {
2929
@Method()
3030
open(menuId?: string): Promise<boolean> {
3131
const menu = this.get(menuId);
32-
if (menu && !this.isAnimating()) {
33-
const openedMenu = this.getOpen();
34-
if (openedMenu && menu !== openedMenu) {
35-
openedMenu.setOpen(false, false);
36-
}
32+
if (menu) {
3733
return menu.open();
3834
}
3935
return Promise.resolve(false);
@@ -58,7 +54,6 @@ export class MenuController {
5854
return Promise.resolve(false);
5955
}
6056

61-
6257
/**
6358
* Toggle the menu. If it's closed, it will open, and if opened, it
6459
* will close.
@@ -68,11 +63,7 @@ export class MenuController {
6863
@Method()
6964
toggle(menuId?: string): Promise<boolean> {
7065
const menu = this.get(menuId);
71-
if (menu && !this.isAnimating()) {
72-
const openedMenu = this.getOpen();
73-
if (openedMenu && menu !== openedMenu) {
74-
openedMenu.setOpen(false, false);
75-
}
66+
if (menu) {
7667
return menu.toggle();
7768
}
7869
return Promise.resolve(false);
@@ -148,14 +139,12 @@ export class MenuController {
148139
*/
149140
@Method()
150141
get(menuId?: string): HTMLIonMenuElement {
151-
let menu: Menu;
152-
153142
if (menuId === 'left' || menuId === 'right') {
154143
// there could be more than one menu on the same side
155144
// so first try to get the enabled one
156-
menu = this.menus.find(m => m.side === menuId && !m.disabled);
145+
const menu = this.find(m => m.side === menuId && !m.disabled);
157146
if (menu) {
158-
return menu.getElement();
147+
return menu;
159148
}
160149

161150
// didn't find a menu side that is enabled
@@ -169,13 +158,13 @@ export class MenuController {
169158
}
170159

171160
// return the first enabled menu
172-
menu = this.menus.find(m => !m.disabled);
161+
const menu = this.find(m => !m.disabled);
173162
if (menu) {
174-
return menu.getElement();
163+
return menu;
175164
}
176165

177166
// get the first menu in the array, if one exists
178-
return (this.menus.length > 0 ? this.menus[0].getElement() : null);
167+
return (this.menus.length > 0 ? this.menus[0].el : null);
179168
}
180169

181170
/**
@@ -191,7 +180,7 @@ export class MenuController {
191180
*/
192181
@Method()
193182
getMenus(): HTMLIonMenuElement[] {
194-
return this.menus.map(menu => menu.getElement());
183+
return this.menus.map(menu => menu.el);
195184
}
196185

197186
/**
@@ -238,23 +227,43 @@ export class MenuController {
238227
.map(m => m.disabled = true);
239228
}
240229

230+
/**
231+
* @hidden
232+
*/
233+
@Method()
234+
_setOpen(menu: Menu, shouldOpen: boolean, animated: boolean): Promise<boolean> {
235+
if (this.isAnimating()) {
236+
return Promise.resolve(false);
237+
}
238+
if (shouldOpen) {
239+
const openedMenu = this.getOpen();
240+
if (openedMenu && menu !== openedMenu) {
241+
openedMenu.setOpen(false, false);
242+
}
243+
}
244+
return menu._setOpen(shouldOpen, animated);
245+
}
246+
241247
/**
242248
* @hidden
243249
*/
244250
@Method()
245251
createAnimation(type: string, menuCmp: Menu): Promise<Animation> {
246-
const animationBuilder = this.menuAnimations[type];
252+
const animationBuilder = this.menuAnimations.get(type);
253+
if (!animationBuilder) {
254+
return Promise.reject('animation not registered');
255+
}
247256
return this.animationCtrl.create(animationBuilder, null, menuCmp);
248257
}
249258

250-
private registerAnimation(name: string, cls: AnimationBuilder) {
251-
this.menuAnimations[name] = cls;
259+
private registerAnimation(name: string, animation: AnimationBuilder) {
260+
this.menuAnimations.set(name, animation);
252261
}
253262

254263
private find(predicate: (menu: Menu) => boolean): HTMLIonMenuElement {
255264
const instance = this.menus.find(predicate);
256265
if (instance) {
257-
return instance.getElement();
266+
return instance.el;
258267
}
259268
return null;
260269
}

packages/core/src/components/menu/menu.tsx

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class Menu {
3030
contentEl: HTMLElement;
3131
menuCtrl: HTMLIonMenuControllerElement;
3232

33-
@Element() private el: HTMLIonMenuElement;
33+
@Element() el: HTMLIonMenuElement;
3434

3535
@State() isRightSide = false;
3636

@@ -41,7 +41,7 @@ export class Menu {
4141
/**
4242
* The content's id the menu should use.
4343
*/
44-
@Prop() content: string;
44+
@Prop() contentId: string;
4545

4646
/**
4747
* An id for the menu.
@@ -56,11 +56,12 @@ export class Menu {
5656
@Prop({ mutable: true }) type = 'overlay';
5757

5858
@Watch('type')
59-
typeChanged(type: string) {
60-
if (this.contentEl) {
61-
this.contentEl.classList.remove('menu-content-' + this.type);
62-
this.contentEl.classList.add('menu-content-' + type);
63-
this.contentEl.removeAttribute('style');
59+
typeChanged(type: string, oldType: string | null) {
60+
const contentEl = this.contentEl;
61+
if (contentEl && oldType) {
62+
contentEl.classList.remove(`menu-content-${oldType}`);
63+
contentEl.classList.add(`menu-content-${type}`);
64+
contentEl.removeAttribute('style');
6465
}
6566
if (this.menuInnerEl) {
6667
// Remove effects of previous animations
@@ -132,22 +133,19 @@ export class Menu {
132133
assert(!!this.menuCtrl, 'menucontroller was not initialized');
133134

134135
const el = this.el;
135-
const contentQuery = (this.content)
136-
? '#' + this.content
136+
const contentQuery = (this.contentId)
137+
? '#' + this.contentId
137138
: '[main]';
138139
const parent = el.parentElement;
139140
const content = this.contentEl = parent.querySelector(contentQuery) as HTMLElement;
140141
if (!content || !content.tagName) {
141142
// requires content element
142143
return console.error('Menu: must have a "content" element to listen for drag events on.');
143144
}
144-
this.menuInnerEl = el.querySelector('.menu-inner') as HTMLElement;
145-
this.backdropEl = el.querySelector('.menu-backdrop') as HTMLElement;
146-
147145
// add menu's content classes
148146
content.classList.add('menu-content');
149147

150-
this.typeChanged(this.type);
148+
this.typeChanged(this.type, null);
151149
this.sideChanged();
152150

153151
let isEnabled = !this.disabled;
@@ -188,17 +186,32 @@ export class Menu {
188186
}
189187
}
190188

191-
getElement(): HTMLIonMenuElement {
192-
return this.el;
193-
}
194-
195189
@Method()
196190
isOpen(): boolean {
197191
return this._isOpen;
198192
}
199193

194+
@Method()
195+
open(animated = true): Promise<boolean> {
196+
return this.setOpen(true, animated);
197+
}
198+
199+
@Method()
200+
close(animated = true): Promise<boolean> {
201+
return this.setOpen(false, animated);
202+
}
203+
204+
@Method()
205+
toggle(animated = true): Promise<boolean> {
206+
return this.setOpen(!this._isOpen, animated);
207+
}
208+
200209
@Method()
201210
setOpen(shouldOpen: boolean, animated = true): Promise<boolean> {
211+
return this.menuCtrl._setOpen(this, shouldOpen, animated);
212+
}
213+
214+
_setOpen(shouldOpen: boolean, animated = true): Promise<boolean> {
202215
// If the menu is disabled or it is currenly being animated, let's do nothing
203216
if (!this.isActive() || this.isAnimating || (shouldOpen === this._isOpen)) {
204217
return Promise.resolve(this._isOpen);
@@ -211,18 +224,8 @@ export class Menu {
211224
}
212225

213226
@Method()
214-
open(): Promise<boolean> {
215-
return this.setOpen(true);
216-
}
217-
218-
@Method()
219-
close(): Promise<boolean> {
220-
return this.setOpen(false);
221-
}
222-
223-
@Method()
224-
toggle(): Promise<boolean> {
225-
return this.setOpen(!this._isOpen);
227+
isActive(): boolean {
228+
return !this.disabled && !this.isPane;
226229
}
227230

228231
private loadAnimation(): Promise<void> {
@@ -259,10 +262,6 @@ export class Menu {
259262
return promise;
260263
}
261264

262-
private isActive(): boolean {
263-
return !this.disabled && !this.isPane;
264-
}
265-
266265
private canSwipe(): boolean {
267266
return this.swipeEnabled &&
268267
!this.isAnimating &&
@@ -428,24 +427,23 @@ export class Menu {
428427

429428
hostData() {
430429
const isRightSide = this.isRightSide;
431-
const typeClass = 'menu-type-' + this.type;
432430
return {
433431
role: 'complementary',
434432
class: {
433+
[`menu-type-${this.type}`]: true,
435434
'menu-enabled': !this.disabled,
436435
'menu-side-right': isRightSide,
437436
'menu-side-left': !isRightSide,
438-
[typeClass]: true,
439437
}
440438
};
441439
}
442440

443441
render() {
444442
return ([
445-
<div class='menu-inner page-inner'>
443+
<div class='menu-inner page-inner' ref={el => this.menuInnerEl = el}>
446444
<slot></slot>
447445
</div>,
448-
<ion-backdrop class='menu-backdrop'></ion-backdrop> ,
446+
<ion-backdrop class='menu-backdrop' ref={el => this.backdropEl = el}></ion-backdrop> ,
449447
<ion-gesture {...{
450448
'canStart': this.canStart.bind(this),
451449
'onWillStart': this.onWillStart.bind(this),

packages/core/src/components/menu/readme.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
## Properties
99

10-
#### content
10+
#### contentId
1111

1212
string
1313

@@ -65,7 +65,7 @@ see the `menuType` in the [config](../../config/Config). Available options:
6565

6666
## Attributes
6767

68-
#### content
68+
#### content-id
6969

7070
string
7171

@@ -144,6 +144,9 @@ Emitted when the menu is open.
144144
#### close()
145145

146146

147+
#### isActive()
148+
149+
147150
#### isOpen()
148151

149152

0 commit comments

Comments
 (0)