UNPKG

9.06 kBJavaScriptView Raw
1'use strict';
2
3const hardwareBackButton = require('./hardware-back-button-148ce546.js');
4const helpers = require('./helpers-d381ec4d.js');
5const ionicGlobal = require('./ionic-global-06f21c1a.js');
6const animation = require('./animation-13cbbb20.js');
7
8/**
9 * baseAnimation
10 * Base class which is extended by the various types. Each
11 * type will provide their own animations for open and close
12 * and registers itself with Menu.
13 */
14const baseAnimation = (isIos) => {
15 // https://material.io/guidelines/motion/movement.html#movement-movement-in-out-of-screen-bounds
16 // https://material.io/guidelines/motion/duration-easing.html#duration-easing-natural-easing-curves
17 /**
18 * "Apply the sharp curve to items temporarily leaving the screen that may return
19 * from the same exit point. When they return, use the deceleration curve. On mobile,
20 * this transition typically occurs over 300ms" -- MD Motion Guide
21 */
22 return animation.createAnimation().duration(isIos ? 400 : 300);
23};
24
25/**
26 * Menu Overlay Type
27 * The menu slides over the content. The content
28 * itself, which is under the menu, does not move.
29 */
30const menuOverlayAnimation = (menu) => {
31 let closedX;
32 let openedX;
33 const width = menu.width + 8;
34 const menuAnimation = animation.createAnimation();
35 const backdropAnimation = animation.createAnimation();
36 if (menu.isEndSide) {
37 // right side
38 closedX = width + 'px';
39 openedX = '0px';
40 }
41 else {
42 // left side
43 closedX = -width + 'px';
44 openedX = '0px';
45 }
46 menuAnimation
47 .addElement(menu.menuInnerEl)
48 .fromTo('transform', `translateX(${closedX})`, `translateX(${openedX})`);
49 const mode = ionicGlobal.getIonMode(menu);
50 const isIos = mode === 'ios';
51 const opacity = isIos ? 0.2 : 0.25;
52 backdropAnimation
53 .addElement(menu.backdropEl)
54 .fromTo('opacity', 0.01, opacity);
55 return baseAnimation(isIos).addAnimation([menuAnimation, backdropAnimation]);
56};
57
58/**
59 * Menu Push Type
60 * The content slides over to reveal the menu underneath.
61 * The menu itself also slides over to reveal its bad self.
62 */
63const menuPushAnimation = (menu) => {
64 let contentOpenedX;
65 let menuClosedX;
66 const mode = ionicGlobal.getIonMode(menu);
67 const width = menu.width;
68 if (menu.isEndSide) {
69 contentOpenedX = -width + 'px';
70 menuClosedX = width + 'px';
71 }
72 else {
73 contentOpenedX = width + 'px';
74 menuClosedX = -width + 'px';
75 }
76 const menuAnimation = animation.createAnimation()
77 .addElement(menu.menuInnerEl)
78 .fromTo('transform', `translateX(${menuClosedX})`, 'translateX(0px)');
79 const contentAnimation = animation.createAnimation()
80 .addElement(menu.contentEl)
81 .fromTo('transform', 'translateX(0px)', `translateX(${contentOpenedX})`);
82 const backdropAnimation = animation.createAnimation()
83 .addElement(menu.backdropEl)
84 .fromTo('opacity', 0.01, 0.32);
85 return baseAnimation(mode === 'ios').addAnimation([menuAnimation, contentAnimation, backdropAnimation]);
86};
87
88/**
89 * Menu Reveal Type
90 * The content slides over to reveal the menu underneath.
91 * The menu itself, which is under the content, does not move.
92 */
93const menuRevealAnimation = (menu) => {
94 const mode = ionicGlobal.getIonMode(menu);
95 const openedX = (menu.width * (menu.isEndSide ? -1 : 1)) + 'px';
96 const contentOpen = animation.createAnimation()
97 .addElement(menu.contentEl) // REVIEW
98 .fromTo('transform', 'translateX(0px)', `translateX(${openedX})`);
99 return baseAnimation(mode === 'ios').addAnimation(contentOpen);
100};
101
102const createMenuController = () => {
103 const menuAnimations = new Map();
104 const menus = [];
105 const open = async (menu) => {
106 const menuEl = await get(menu);
107 if (menuEl) {
108 return menuEl.open();
109 }
110 return false;
111 };
112 const close = async (menu) => {
113 const menuEl = await (menu !== undefined ? get(menu) : getOpen());
114 if (menuEl !== undefined) {
115 return menuEl.close();
116 }
117 return false;
118 };
119 const toggle = async (menu) => {
120 const menuEl = await get(menu);
121 if (menuEl) {
122 return menuEl.toggle();
123 }
124 return false;
125 };
126 const enable = async (shouldEnable, menu) => {
127 const menuEl = await get(menu);
128 if (menuEl) {
129 menuEl.disabled = !shouldEnable;
130 }
131 return menuEl;
132 };
133 const swipeGesture = async (shouldEnable, menu) => {
134 const menuEl = await get(menu);
135 if (menuEl) {
136 menuEl.swipeGesture = shouldEnable;
137 }
138 return menuEl;
139 };
140 const isOpen = async (menu) => {
141 if (menu != null) {
142 const menuEl = await get(menu);
143 return (menuEl !== undefined && menuEl.isOpen());
144 }
145 else {
146 const menuEl = await getOpen();
147 return menuEl !== undefined;
148 }
149 };
150 const isEnabled = async (menu) => {
151 const menuEl = await get(menu);
152 if (menuEl) {
153 return !menuEl.disabled;
154 }
155 return false;
156 };
157 const get = async (menu) => {
158 await waitUntilReady();
159 if (menu === 'start' || menu === 'end') {
160 // there could be more than one menu on the same side
161 // so first try to get the enabled one
162 const menuRef = find(m => m.side === menu && !m.disabled);
163 if (menuRef) {
164 return menuRef;
165 }
166 // didn't find a menu side that is enabled
167 // so try to get the first menu side found
168 return find(m => m.side === menu);
169 }
170 else if (menu != null) {
171 // the menuId was not left or right
172 // so try to get the menu by its "id"
173 return find(m => m.menuId === menu);
174 }
175 // return the first enabled menu
176 const menuEl = find(m => !m.disabled);
177 if (menuEl) {
178 return menuEl;
179 }
180 // get the first menu in the array, if one exists
181 return menus.length > 0 ? menus[0].el : undefined;
182 };
183 /**
184 * Get the instance of the opened menu. Returns `null` if a menu is not found.
185 */
186 const getOpen = async () => {
187 await waitUntilReady();
188 return _getOpenSync();
189 };
190 /**
191 * Get all menu instances.
192 */
193 const getMenus = async () => {
194 await waitUntilReady();
195 return getMenusSync();
196 };
197 /**
198 * Get whether or not a menu is animating. Returns `true` if any
199 * menu is currently animating.
200 */
201 const isAnimating = async () => {
202 await waitUntilReady();
203 return isAnimatingSync();
204 };
205 const registerAnimation = (name, animation) => {
206 menuAnimations.set(name, animation);
207 };
208 const _register = (menu) => {
209 if (menus.indexOf(menu) < 0) {
210 if (!menu.disabled) {
211 _setActiveMenu(menu);
212 }
213 menus.push(menu);
214 }
215 };
216 const _unregister = (menu) => {
217 const index = menus.indexOf(menu);
218 if (index > -1) {
219 menus.splice(index, 1);
220 }
221 };
222 const _setActiveMenu = (menu) => {
223 // if this menu should be enabled
224 // then find all the other menus on this same side
225 // and automatically disable other same side menus
226 const side = menu.side;
227 menus
228 .filter(m => m.side === side && m !== menu)
229 .forEach(m => m.disabled = true);
230 };
231 const _setOpen = async (menu, shouldOpen, animated) => {
232 if (isAnimatingSync()) {
233 return false;
234 }
235 if (shouldOpen) {
236 const openedMenu = await getOpen();
237 if (openedMenu && menu.el !== openedMenu) {
238 await openedMenu.setOpen(false, false);
239 }
240 }
241 return menu._setOpen(shouldOpen, animated);
242 };
243 const _createAnimation = (type, menuCmp) => {
244 const animationBuilder = menuAnimations.get(type);
245 if (!animationBuilder) {
246 throw new Error('animation not registered');
247 }
248 const animation = animationBuilder(menuCmp);
249 return animation;
250 };
251 const _getOpenSync = () => {
252 return find(m => m._isOpen);
253 };
254 const getMenusSync = () => {
255 return menus.map(menu => menu.el);
256 };
257 const isAnimatingSync = () => {
258 return menus.some(menu => menu.isAnimating);
259 };
260 const find = (predicate) => {
261 const instance = menus.find(predicate);
262 if (instance !== undefined) {
263 return instance.el;
264 }
265 return undefined;
266 };
267 const waitUntilReady = () => {
268 return Promise.all(Array.from(document.querySelectorAll('ion-menu'))
269 .map(menu => new Promise(resolve => helpers.componentOnReady(menu, resolve))));
270 };
271 registerAnimation('reveal', menuRevealAnimation);
272 registerAnimation('push', menuPushAnimation);
273 registerAnimation('overlay', menuOverlayAnimation);
274 /* tslint:disable-next-line */
275 if (typeof document !== 'undefined') {
276 document.addEventListener('ionBackButton', (ev) => {
277 const openMenu = _getOpenSync();
278 if (openMenu) {
279 ev.detail.register(hardwareBackButton.MENU_BACK_BUTTON_PRIORITY, () => {
280 return openMenu.close();
281 });
282 }
283 });
284 }
285 return {
286 registerAnimation,
287 get,
288 getMenus,
289 getOpen,
290 isEnabled,
291 swipeGesture,
292 isAnimating,
293 isOpen,
294 enable,
295 toggle,
296 close,
297 open,
298 _getOpenSync,
299 _createAnimation,
300 _register,
301 _unregister,
302 _setOpen,
303 _setActiveMenu,
304 };
305};
306const menuController = /*@__PURE__*/ createMenuController();
307
308exports.menuController = menuController;