fix(menu-toggle): autohides

This commit is contained in:
Manu Mtz.-Almeida
2018-02-12 21:44:53 +01:00
parent 8be158f256
commit 56a3c7a3ad
11 changed files with 142 additions and 96 deletions

View File

@ -1664,6 +1664,7 @@ declare global {
} }
namespace JSXElements { namespace JSXElements {
export interface IonMenuToggleAttributes extends HTMLAttributes { export interface IonMenuToggleAttributes extends HTMLAttributes {
autoHide?: boolean;
menu?: string; menu?: string;
} }
} }

View File

@ -0,0 +1,4 @@
ion-menu-toggle.menu-toggle-hidden {
display: none;
}

View File

@ -1,38 +1,66 @@
import { Component, Listen, Prop } from '@stencil/core'; import { Component, Listen, Prop, State } from '@stencil/core';
@Component({ @Component({
tag: 'ion-menu-toggle', tag: 'ion-menu-toggle',
styleUrls: { styleUrl: 'menu-toggle.scss'
ios: 'menu-toggle.ios.scss',
md: 'menu-toggle.md.scss'
},
host: {
theme: 'menu-toggle'
}
}) })
export class MenuToggle { export class MenuToggle {
@State() visible = false;
/** /**
* Optional property that maps to a Menu's `menuId` prop. Can also be `left` or `right` for the menu side. This is used to find the correct menu to toggle * Optional property that maps to a Menu's `menuId` prop. Can also be `left` or `right` for the menu side. This is used to find the correct menu to toggle
*/ */
@Prop() menu: string; @Prop() menu: string;
@Prop() autoHide = true;
componentDidLoad() {
this.updateVisibility();
}
@Listen('child:click') @Listen('child:click')
onClick() { onClick() {
const menuControllerElement = document.querySelector('ion-menu-controller'); getMenuController().then(menuCtrl => {
if (!menuControllerElement) { if (menuCtrl) {
return menuCtrl.toggle(this.menu);
}
return false;
});
}
@Listen('body:ionMenuDisable')
@Listen('body:ionSplitPaneVisible')
updateVisibility() {
getMenuController().then(menuCtrl => {
if (menuCtrl) {
const menu = menuCtrl.get(this.menu);
if (menu && menu.isActive()) {
this.visible = true;
return; return;
} }
menuControllerElement.componentOnReady().then(() => {
const menu = menuControllerElement.get(this.menu);
if (menu) {
menu.toggle();
} }
this.visible = false;
}); });
} }
hostData() {
const hidden = this.autoHide && !this.visible;
return {
class: {
'menu-toggle-hidden': hidden
}
};
}
render() { render() {
return <slot></slot>; return <slot></slot>;
} }
}
function getMenuController(): Promise<HTMLIonMenuControllerElement> {
const menuControllerElement = document.querySelector('ion-menu-controller');
if (!menuControllerElement) {
return Promise.resolve(null);
}
return menuControllerElement.componentOnReady();
} }

View File

@ -7,6 +7,11 @@
## Properties ## Properties
#### autoHide
boolean
#### menu #### menu
string string
@ -16,6 +21,11 @@ Optional property that maps to a Menu's `menuId` prop. Can also be `left` or `ri
## Attributes ## Attributes
#### auto-hide
boolean
#### menu #### menu
string string

View File

@ -1,5 +1,5 @@
import { Component, Element, Event, EventEmitter, EventListenerEnable, Listen, Method, Prop, State, Watch } from '@stencil/core'; import { Component, Element, Event, EventEmitter, EventListenerEnable, Listen, Method, Prop, State, Watch } from '@stencil/core';
import { Animation, Config, GestureDetail, SplitPaneAlert } from '../../index'; import { Animation, Config, GestureDetail } from '../../index';
import { Side, assert, checkEdgeSide, isRightSide } from '../../utils/helpers'; import { Side, assert, checkEdgeSide, isRightSide } from '../../utils/helpers';
@Component({ @Component({
@ -76,8 +76,9 @@ export class Menu {
@Prop({ mutable: true }) disabled = false; @Prop({ mutable: true }) disabled = false;
@Watch('disabled') @Watch('disabled')
protected enabledChanged() { protected disabledChanged(disabled: boolean) {
this.updateState(); this.updateState();
this.ionMenuDisable.emit({disabled});
} }
/** /**
@ -123,6 +124,9 @@ export class Menu {
*/ */
@Event() ionClose: EventEmitter; @Event() ionClose: EventEmitter;
@Event() protected ionMenuDisable: EventEmitter;
componentWillLoad() { componentWillLoad() {
return this.lazyMenuCtrl.componentOnReady().then(menu => { return this.lazyMenuCtrl.componentOnReady().then(menu => {
this.menuCtrl = menu as HTMLIonMenuControllerElement; this.menuCtrl = menu as HTMLIonMenuControllerElement;
@ -170,9 +174,9 @@ export class Menu {
this.contentEl = this.backdropEl = this.menuInnerEl = null; this.contentEl = this.backdropEl = this.menuInnerEl = null;
} }
@Listen('body:ionSplitPaneDidChange') @Listen('body:ionSplitPaneVisible')
splitPaneChanged(ev: SplitPaneAlert) { splitPaneChanged(ev: CustomEvent) {
this.isPane = ev.detail.splitPane.isPane(this.el); this.isPane = (ev.target as any).isPane(this.el);
this.updateState(); this.updateState();
} }

View File

@ -134,6 +134,9 @@ Emitted when the sliding position changes.
It reports the relative position. It reports the relative position.
#### ionMenuDisable
#### ionOpen #### ionOpen
Emitted when the menu is open. Emitted when the menu is open.

View File

@ -156,13 +156,16 @@ Can also be a boolean expression.
Emitted when the split pane is visible. Emitted when the split pane is visible.
#### ionSplitPaneDidChange #### ionSplitPaneVisible
Expression to be called when the split-pane visibility has changed Expression to be called when the split-pane visibility has changed
## Methods ## Methods
#### isPane()
#### isVisible() #### isVisible()

View File

@ -27,7 +27,7 @@ export class SplitPane {
private rmL: any; private rmL: any;
@Element() private el: HTMLElement; @Element() private el: HTMLElement;
@State() private visible = false; @State() visible = false;
/** /**
* If true, the split pane will be hidden. Defaults to `false`. * If true, the split pane will be hidden. Defaults to `false`.
@ -41,16 +41,23 @@ export class SplitPane {
*/ */
@Prop() when: string | boolean = QUERY['md']; @Prop() when: string | boolean = QUERY['md'];
/**
* Expression to be called when the split-pane visibility has changed
*/
@Event() ionSplitPaneDidChange: EventEmitter;
/** /**
* Emitted when the split pane is visible. * Emitted when the split pane is visible.
*/ */
@Event() ionChange: EventEmitter; @Event() ionChange: EventEmitter;
/**
* Expression to be called when the split-pane visibility has changed
*/
@Event() protected ionSplitPaneVisible: EventEmitter;
@Watch('visible')
visibleChanged(visible: boolean) {
const detail = { visible };
this.ionChange.emit(detail);
this.ionSplitPaneVisible.emit(detail);
}
componentDidLoad() { componentDidLoad() {
this._styleChildren(); this._styleChildren();
this.whenChanged(); this.whenChanged();
@ -61,6 +68,58 @@ export class SplitPane {
this.rmL = null; this.rmL = null;
} }
@Watch('when')
protected whenChanged() {
this.rmL && this.rmL();
this.rmL = null;
// Check if the split-pane is disabled
if (this.disabled) {
this.visible = false;
return;
}
// When query is a boolean
const query = this.when;
if (typeof query === 'boolean') {
this.visible = query;
return;
}
// When query is a string, let's find first if it is a shortcut
const defaultQuery = QUERY[query];
const mediaQuery = (defaultQuery)
? defaultQuery
: query;
// Media query is empty or null, we hide it
if (!mediaQuery || mediaQuery.length === 0) {
this.visible = false;
return;
}
// Listen on media query
const callback = (q: MediaQueryList) => this.visible = q.matches;
const mediaList = window.matchMedia(mediaQuery);
mediaList.addListener(callback);
this.rmL = () => mediaList.removeListener(callback);
this.visible = mediaList.matches;
}
@Method()
isVisible(): boolean {
return this.visible;
}
@Method()
isPane(element: HTMLElement): boolean {
if (!this.visible) {
return false;
}
return element.parentElement === this.el
&& element.classList.contains(SPLIT_PANE_SIDE);
}
private _styleChildren() { private _styleChildren() {
const children = this.el.children; const children = this.el.children;
const nu = this.el.childElementCount; const nu = this.el.childElementCount;
@ -82,66 +141,6 @@ export class SplitPane {
} }
} }
@Watch('when')
protected whenChanged() {
this.rmL && this.rmL();
this.rmL = null;
// Check if the split-pane is disabled
if (this.disabled) {
this._setVisible(false);
return;
}
// When query is a boolean
const query = this.when;
if (typeof query === 'boolean') {
this._setVisible(query);
return;
}
// When query is a string, let's find first if it is a shortcut
const defaultQuery = QUERY[query];
const mediaQuery = (defaultQuery)
? defaultQuery
: query;
// Media query is empty or null, we hide it
if (!mediaQuery || mediaQuery.length === 0) {
this._setVisible(false);
return;
}
// Listen on media query
const callback = (q: MediaQueryList) => this._setVisible(q.matches);
const mediaList = window.matchMedia(mediaQuery);
mediaList.addListener(callback);
this.rmL = () => mediaList.removeListener(callback);
this._setVisible(mediaList.matches);
}
private _setVisible(visible: boolean) {
if (this.visible !== visible) {
this.visible = visible;
const detail = { splitPane: this };
this.ionChange.emit(detail);
this.ionSplitPaneDidChange.emit(detail);
}
}
@Method()
isVisible(): boolean {
return this.visible;
}
isPane(element: HTMLElement): boolean {
if (!this.visible) {
return false;
}
return element.parentElement === this.el
&& element.classList.contains(SPLIT_PANE_SIDE);
}
hostData() { hostData() {
return { return {
class: { class: {
@ -153,14 +152,8 @@ export class SplitPane {
render() { render() {
return <slot></slot>; return <slot></slot>;
} }
} }
export interface SplitPaneAlert {
detail: {
splitPane: SplitPane
};
}
function setPaneClass(el: HTMLElement, isMain: boolean) { function setPaneClass(el: HTMLElement, isMain: boolean) {
let toAdd; let toAdd;

View File

@ -99,7 +99,7 @@ export { Slide } from './components/slide/slide';
export { Slides } from './components/slides/slides'; export { Slides } from './components/slides/slides';
export * from './components/spinner/spinner-configs'; export * from './components/spinner/spinner-configs';
export { Spinner } from './components/spinner/spinner'; export { Spinner } from './components/spinner/spinner';
export { SplitPane, SplitPaneAlert } from './components/split-pane/split-pane'; export { SplitPane } from './components/split-pane/split-pane';
export { Tab } from './components/tab/tab'; export { Tab } from './components/tab/tab';
export { TabButton } from './components/tab-button/tab-button'; export { TabButton } from './components/tab-button/tab-button';
export { Tabs } from './components/tabs/tabs'; export { Tabs } from './components/tabs/tabs';