UNPKG

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