mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
245 lines
6.4 KiB
TypeScript
245 lines
6.4 KiB
TypeScript
import { AnimationBuilder, BackButtonEvent, MenuI } from '../../interface';
|
|
import { MENU_BACK_BUTTON_PRIORITY } from '../hardware-back-button';
|
|
|
|
import { menuOverlayAnimation } from './animations/overlay';
|
|
import { menuPushAnimation } from './animations/push';
|
|
import { menuRevealAnimation } from './animations/reveal';
|
|
|
|
const createMenuController = () => {
|
|
const menuAnimations = new Map<string, AnimationBuilder>();
|
|
const menus: MenuI[] = [];
|
|
|
|
const open = async (menu?: string | null): Promise<boolean> => {
|
|
const menuEl = await get(menu);
|
|
if (menuEl) {
|
|
return menuEl.open();
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const close = async (menu?: string | null): Promise<boolean> => {
|
|
const menuEl = await (menu !== undefined ? get(menu) : getOpen());
|
|
if (menuEl !== undefined) {
|
|
return menuEl.close();
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const toggle = async (menu?: string | null): Promise<boolean> => {
|
|
const menuEl = await get(menu);
|
|
if (menuEl) {
|
|
return menuEl.toggle();
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const enable = async (shouldEnable: boolean, menu?: string | null): Promise<HTMLIonMenuElement | undefined> => {
|
|
const menuEl = await get(menu);
|
|
if (menuEl) {
|
|
menuEl.disabled = !shouldEnable;
|
|
}
|
|
return menuEl;
|
|
};
|
|
|
|
const swipeGesture = async (shouldEnable: boolean, menu?: string | null): Promise<HTMLIonMenuElement | undefined> => {
|
|
const menuEl = await get(menu);
|
|
if (menuEl) {
|
|
menuEl.swipeGesture = shouldEnable;
|
|
}
|
|
return menuEl;
|
|
};
|
|
|
|
const isOpen = async (menu?: string | null): Promise<boolean> => {
|
|
if (menu != null) {
|
|
const menuEl = await get(menu);
|
|
return (menuEl !== undefined && menuEl.isOpen());
|
|
} else {
|
|
const menuEl = await getOpen();
|
|
return menuEl !== undefined;
|
|
}
|
|
};
|
|
|
|
const isEnabled = async (menu?: string | null): Promise<boolean> => {
|
|
const menuEl = await get(menu);
|
|
if (menuEl) {
|
|
return !menuEl.disabled;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const get = async (menu?: string | null): Promise<HTMLIonMenuElement | undefined> => {
|
|
await waitUntilReady();
|
|
|
|
if (menu === 'start' || menu === 'end') {
|
|
// there could be more than one menu on the same side
|
|
// so first try to get the enabled one
|
|
const menuRef = find(m => m.side === menu && !m.disabled);
|
|
if (menuRef) {
|
|
return menuRef;
|
|
}
|
|
|
|
// didn't find a menu side that is enabled
|
|
// so try to get the first menu side found
|
|
return find(m => m.side === menu);
|
|
|
|
} else if (menu != null) {
|
|
// the menuId was not left or right
|
|
// so try to get the menu by its "id"
|
|
return find(m => m.menuId === menu);
|
|
}
|
|
|
|
// return the first enabled menu
|
|
const menuEl = find(m => !m.disabled);
|
|
if (menuEl) {
|
|
return menuEl;
|
|
}
|
|
|
|
// get the first menu in the array, if one exists
|
|
return menus.length > 0 ? menus[0].el : undefined;
|
|
};
|
|
|
|
/**
|
|
* Get the instance of the opened menu. Returns `null` if a menu is not found.
|
|
*/
|
|
const getOpen = async (): Promise<HTMLIonMenuElement | undefined> => {
|
|
await waitUntilReady();
|
|
return _getOpenSync();
|
|
};
|
|
|
|
/**
|
|
* Get all menu instances.
|
|
*/
|
|
const getMenus = async (): Promise<HTMLIonMenuElement[]> => {
|
|
await waitUntilReady();
|
|
return getMenusSync();
|
|
};
|
|
|
|
/**
|
|
* Get whether or not a menu is animating. Returns `true` if any
|
|
* menu is currently animating.
|
|
*/
|
|
const isAnimating = async (): Promise<boolean> => {
|
|
await waitUntilReady();
|
|
return isAnimatingSync();
|
|
};
|
|
|
|
const registerAnimation = (name: string, animation: AnimationBuilder) => {
|
|
menuAnimations.set(name, animation);
|
|
};
|
|
|
|
const _register = (menu: MenuI) => {
|
|
if (menus.indexOf(menu) < 0) {
|
|
if (!menu.disabled) {
|
|
_setActiveMenu(menu);
|
|
}
|
|
menus.push(menu);
|
|
}
|
|
};
|
|
|
|
const _unregister = (menu: MenuI) => {
|
|
const index = menus.indexOf(menu);
|
|
if (index > -1) {
|
|
menus.splice(index, 1);
|
|
}
|
|
};
|
|
|
|
const _setActiveMenu = (menu: MenuI) => {
|
|
// if this menu should be enabled
|
|
// then find all the other menus on this same side
|
|
// and automatically disable other same side menus
|
|
const side = menu.side;
|
|
menus
|
|
.filter(m => m.side === side && m !== menu)
|
|
.forEach(m => m.disabled = true);
|
|
};
|
|
|
|
const _setOpen = async (menu: MenuI, shouldOpen: boolean, animated: boolean): Promise<boolean> => {
|
|
if (isAnimatingSync()) {
|
|
return false;
|
|
}
|
|
if (shouldOpen) {
|
|
const openedMenu = await getOpen();
|
|
if (openedMenu && menu.el !== openedMenu) {
|
|
await openedMenu.setOpen(false, false);
|
|
}
|
|
}
|
|
return menu._setOpen(shouldOpen, animated);
|
|
};
|
|
|
|
const _createAnimation = (type: string, menuCmp: MenuI) => {
|
|
const animationBuilder = menuAnimations.get(type) as any;
|
|
if (!animationBuilder) {
|
|
throw new Error('animation not registered');
|
|
}
|
|
|
|
const animation = animationBuilder(menuCmp);
|
|
return animation;
|
|
};
|
|
|
|
const _getOpenSync = (): HTMLIonMenuElement | undefined => {
|
|
return find(m => m._isOpen);
|
|
};
|
|
|
|
const getMenusSync = (): HTMLIonMenuElement[] => {
|
|
return menus.map(menu => menu.el);
|
|
};
|
|
|
|
const isAnimatingSync = (): boolean => {
|
|
return menus.some(menu => menu.isAnimating);
|
|
};
|
|
|
|
const find = (predicate: (menu: MenuI) => boolean): HTMLIonMenuElement | undefined => {
|
|
const instance = menus.find(predicate);
|
|
if (instance !== undefined) {
|
|
return instance.el;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const waitUntilReady = () => {
|
|
return Promise.all(
|
|
Array.from(document.querySelectorAll('ion-menu'))
|
|
.map(menu => menu.componentOnReady())
|
|
);
|
|
};
|
|
|
|
registerAnimation('reveal', menuRevealAnimation);
|
|
registerAnimation('push', menuPushAnimation);
|
|
registerAnimation('overlay', menuOverlayAnimation);
|
|
|
|
/* tslint:disable-next-line */
|
|
if (typeof document !== 'undefined') {
|
|
document.addEventListener('ionBackButton', (ev: any) => {
|
|
const openMenu = _getOpenSync();
|
|
if (openMenu) {
|
|
(ev as BackButtonEvent).detail.register(MENU_BACK_BUTTON_PRIORITY, () => {
|
|
return openMenu.close();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
return {
|
|
registerAnimation,
|
|
get,
|
|
getMenus,
|
|
getOpen,
|
|
isEnabled,
|
|
swipeGesture,
|
|
isAnimating,
|
|
isOpen,
|
|
enable,
|
|
toggle,
|
|
close,
|
|
open,
|
|
_getOpenSync,
|
|
_createAnimation,
|
|
_register,
|
|
_unregister,
|
|
_setOpen,
|
|
_setActiveMenu,
|
|
};
|
|
};
|
|
|
|
export const menuController = /*@__PURE__*/createMenuController();
|