index-18f31305.js 11 KB

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