mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(split-panel): split panel support for ion-nav and ion-menu (#10343)
* feat(split-panel): split panel support for ion-nav and ion-menu Revert some changes adds support split-panel support for tabs Removes .orig removes e2e.ts Removes unneeded changes in menu.ts improves stuff * fix(split-panel): resize is called when menu wraps a ion-nav * test(split-panel): improves split-panel/basic test * feat(split-panel): ionChange is an ng2 @Output() * fix(split-panel): fix tabs as side content * fix(menu): forzedClose works as expected * feat(split-panel): split-panel works with several menus * chore(split-panel): renames to split-pane * refactor(split-pane): splitPane can be injected * fix(split-pane): it is a directive * fix(slides): integration with split-panel * Make gulp validate happy
This commit is contained in:
@@ -85,8 +85,9 @@ export class App {
|
||||
|
||||
runInDev(() => {
|
||||
// During developement, navPop can be triggered by calling
|
||||
if (!(<any>_plt.win())['HWBackButton']) {
|
||||
(<any>_plt.win())['HWBackButton'] = () => {
|
||||
const win = <any>_plt.win();
|
||||
if (!win['HWBackButton']) {
|
||||
win['HWBackButton'] = () => {
|
||||
let p = this.goBack();
|
||||
p && p.catch(() => console.debug('hardware go back cancelled'));
|
||||
return p;
|
||||
|
||||
@@ -309,6 +309,23 @@ export class MenuController {
|
||||
removeArrayItem(this._menus, menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_setActiveMenu(menu: Menu) {
|
||||
assert(menu.enabled);
|
||||
assert(this._menus.indexOf(menu) >= 0, 'menu is not registered');
|
||||
|
||||
// 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;
|
||||
this._menus
|
||||
.filter(m => m.side === side && m !== menu)
|
||||
.map(m => m.enable(false));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -129,7 +129,7 @@ export class MenuToggle {
|
||||
*/
|
||||
@HostListener('click')
|
||||
toggle() {
|
||||
let menu = this._menu.get(this.menuToggle);
|
||||
const menu = this._menu.get(this.menuToggle);
|
||||
menu && menu.toggle();
|
||||
}
|
||||
|
||||
@@ -137,13 +137,16 @@ export class MenuToggle {
|
||||
* @private
|
||||
*/
|
||||
get isHidden() {
|
||||
const menu = this._menu.get(this.menuToggle);
|
||||
if (!menu || !menu._canOpen()) {
|
||||
return true;
|
||||
}
|
||||
if (this._inNavbar && this._viewCtrl) {
|
||||
if (this._viewCtrl.isFirst()) {
|
||||
// this is the first view, so it should always show
|
||||
return false;
|
||||
}
|
||||
|
||||
let menu = this._menu.get(this.menuToggle);
|
||||
if (menu) {
|
||||
// this is not the root view, so see if this menu
|
||||
// is configured to still be enabled if it's not the root view
|
||||
|
||||
@@ -24,14 +24,14 @@ export class MenuType {
|
||||
}
|
||||
|
||||
setOpen(shouldOpen: boolean, animated: boolean, done: Function) {
|
||||
let ani = this.ani
|
||||
.onFinish(done, true)
|
||||
const ani = this.ani
|
||||
.onFinish(done, true, true)
|
||||
.reverse(!shouldOpen);
|
||||
|
||||
if (animated) {
|
||||
ani.play();
|
||||
} else {
|
||||
ani.play({ duration: 0 });
|
||||
ani.syncPlay();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EventEmitter, Input, NgZone, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EventEmitter, forwardRef, Input, NgZone, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
import { App } from '../app/app';
|
||||
import { Backdrop } from '../backdrop/backdrop';
|
||||
@@ -11,8 +11,10 @@ import { Keyboard } from '../../platform/keyboard';
|
||||
import { MenuContentGesture } from './menu-gestures';
|
||||
import { MenuController } from './menu-controller';
|
||||
import { MenuType } from './menu-types';
|
||||
import { Nav } from '../nav/nav';
|
||||
import { Platform } from '../../platform/platform';
|
||||
import { UIEventManager } from '../../gestures/ui-event-manager';
|
||||
import { RootNode } from '../split-pane/split-pane';
|
||||
|
||||
/**
|
||||
* @name Menu
|
||||
@@ -188,19 +190,21 @@ import { UIEventManager } from '../../gestures/ui-event-manager';
|
||||
},
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
providers: [{provide: RootNode, useExisting: forwardRef(() => Menu) }]
|
||||
})
|
||||
export class Menu {
|
||||
export class Menu implements RootNode {
|
||||
|
||||
private _cntEle: HTMLElement;
|
||||
private _gesture: MenuContentGesture;
|
||||
private _type: MenuType;
|
||||
private _isEnabled: boolean = true;
|
||||
private _isEnabled: boolean = false;
|
||||
private _isSwipeEnabled: boolean = true;
|
||||
private _isAnimating: boolean = false;
|
||||
private _isPersistent: boolean = false;
|
||||
private _init: boolean = false;
|
||||
private _events: UIEventManager;
|
||||
private _gestureBlocker: BlockerDelegate;
|
||||
private _isPane: boolean = false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -217,6 +221,11 @@ export class Menu {
|
||||
*/
|
||||
@ContentChild(Content) menuContent: Content;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@ContentChild(Nav) menuNav: Nav;
|
||||
|
||||
/**
|
||||
* @input {any} A reference to the content element the menu should use.
|
||||
*/
|
||||
@@ -248,8 +257,8 @@ export class Menu {
|
||||
}
|
||||
|
||||
set enabled(val: boolean) {
|
||||
this._isEnabled = isTrueProperty(val);
|
||||
this._setListeners();
|
||||
const isEnabled = isTrueProperty(val);
|
||||
this.enable(isEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,8 +270,8 @@ export class Menu {
|
||||
}
|
||||
|
||||
set swipeEnabled(val: boolean) {
|
||||
this._isSwipeEnabled = isTrueProperty(val);
|
||||
this._setListeners();
|
||||
const isEnabled = isTrueProperty(val);
|
||||
this.swipeEnable(isEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -344,22 +353,20 @@ export class Menu {
|
||||
// add the gestures
|
||||
this._gesture = new MenuContentGesture(this._plt, this, this._gestureCtrl, this._domCtrl);
|
||||
|
||||
// register listeners if this menu is enabled
|
||||
// check if more than one menu is on the same side
|
||||
let hasEnabledSameSideMenu = this._menuCtrl.getMenus().some(m => {
|
||||
return m.side === this.side && m.enabled;
|
||||
});
|
||||
if (hasEnabledSameSideMenu) {
|
||||
// auto-disable if another menu on the same side is already enabled
|
||||
this._isEnabled = false;
|
||||
}
|
||||
this._setListeners();
|
||||
|
||||
// add menu's content classes
|
||||
this._cntEle.classList.add('menu-content');
|
||||
this._cntEle.classList.add('menu-content-' + this.type);
|
||||
|
||||
// check if more than one menu is on the same side
|
||||
let shouldEnable = !this._menuCtrl.getMenus().some(m => {
|
||||
return m.side === this.side && m.enabled;
|
||||
});
|
||||
|
||||
// register this menu with the app's menu controller
|
||||
this._menuCtrl._register(this);
|
||||
|
||||
// mask it as enabled / disabled
|
||||
this.enable(shouldEnable);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,27 +378,6 @@ export class Menu {
|
||||
this._menuCtrl.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private _setListeners() {
|
||||
if (!this._init) {
|
||||
return;
|
||||
}
|
||||
const gesture = this._gesture;
|
||||
// only listen/unlisten if the menu has initialized
|
||||
if (this._isEnabled && this._isSwipeEnabled && !gesture.isListening) {
|
||||
// should listen, but is not currently listening
|
||||
console.debug('menu, gesture listen', this.side);
|
||||
gesture.listen();
|
||||
|
||||
} else if (gesture.isListening && (!this._isEnabled || !this._isSwipeEnabled)) {
|
||||
// should not listen, but is currently listening
|
||||
console.debug('menu, gesture unlisten', this.side);
|
||||
gesture.unlisten();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -411,13 +397,11 @@ export class Menu {
|
||||
*/
|
||||
setOpen(shouldOpen: boolean, animated: boolean = true): Promise<boolean> {
|
||||
// If the menu is disabled or it is currenly being animated, let's do nothing
|
||||
if ((shouldOpen === this.isOpen) || !this._isEnabled || this._isAnimating) {
|
||||
if ((shouldOpen === this.isOpen) || !this._canOpen() || this._isAnimating) {
|
||||
return Promise.resolve(this.isOpen);
|
||||
}
|
||||
|
||||
this._before();
|
||||
|
||||
return new Promise(resolve => {
|
||||
this._before();
|
||||
this._getType().setOpen(shouldOpen, animated, () => {
|
||||
this._after(shouldOpen);
|
||||
resolve(this.isOpen);
|
||||
@@ -425,13 +409,21 @@ export class Menu {
|
||||
});
|
||||
}
|
||||
|
||||
_forceClosing() {
|
||||
assert(this.isOpen, 'menu cannot be closed');
|
||||
this._isAnimating = true;
|
||||
this._getType().setOpen(false, false, () => {
|
||||
this._after(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
canSwipe(): boolean {
|
||||
return this._isEnabled &&
|
||||
this._isSwipeEnabled &&
|
||||
return this._isSwipeEnabled &&
|
||||
!this._isAnimating &&
|
||||
this._canOpen() &&
|
||||
this._app.isEnabled();
|
||||
}
|
||||
|
||||
@@ -442,6 +434,7 @@ export class Menu {
|
||||
return this._isAnimating;
|
||||
}
|
||||
|
||||
|
||||
_swipeBeforeStart() {
|
||||
if (!this.canSwipe()) {
|
||||
assert(false, 'canSwipe() has to be true');
|
||||
@@ -500,7 +493,7 @@ export class Menu {
|
||||
// this css class doesn't actually kick off any animations
|
||||
this.setElementClass('show-menu', true);
|
||||
this.backdrop.setElementClass('show-backdrop', true);
|
||||
this.menuContent && this.menuContent.resize();
|
||||
this.resize();
|
||||
this._keyboard.close();
|
||||
this._isAnimating = true;
|
||||
}
|
||||
@@ -554,6 +547,16 @@ export class Menu {
|
||||
return this.setOpen(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
resize() {
|
||||
const content: Content | Nav = this.menuContent
|
||||
? this.menuContent
|
||||
: this.menuNav;
|
||||
content && content.resize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -561,38 +564,82 @@ export class Menu {
|
||||
return this.setOpen(!this.isOpen);
|
||||
}
|
||||
|
||||
_canOpen(): boolean {
|
||||
return this._isEnabled && !this._isPane;
|
||||
}
|
||||
|
||||
_isSideContent(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateState() {
|
||||
const canOpen = this._canOpen();
|
||||
|
||||
// Close menu inmediately
|
||||
if (!canOpen && this.isOpen) {
|
||||
// close if this menu is open, and should not be enabled
|
||||
this._forceClosing();
|
||||
}
|
||||
|
||||
if (this._isEnabled && this._menuCtrl) {
|
||||
this._menuCtrl._setActiveMenu(this);
|
||||
}
|
||||
|
||||
if (!this._init) {
|
||||
return;
|
||||
}
|
||||
const gesture = this._gesture;
|
||||
// only listen/unlisten if the menu has initialized
|
||||
if (canOpen && this._isSwipeEnabled && !gesture.isListening) {
|
||||
// should listen, but is not currently listening
|
||||
console.debug('menu, gesture listen', this.side);
|
||||
gesture.listen();
|
||||
|
||||
} else if (gesture.isListening && (!canOpen || !this._isSwipeEnabled)) {
|
||||
// should not listen, but is currently listening
|
||||
console.debug('menu, gesture unlisten', this.side);
|
||||
gesture.unlisten();
|
||||
}
|
||||
if (this.isOpen || (this._isPane && this._isEnabled)) {
|
||||
this.resize();
|
||||
}
|
||||
assert(!this._isAnimating, 'can not be animating');
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
enable(shouldEnable: boolean): Menu {
|
||||
this.enabled = shouldEnable;
|
||||
if (!shouldEnable && this.isOpen) {
|
||||
// close if this menu is open, and should not be enabled
|
||||
this.close();
|
||||
}
|
||||
|
||||
if (shouldEnable) {
|
||||
// if this menu should be enabled
|
||||
// then find all the other menus on this same side
|
||||
// and automatically disable other same side menus
|
||||
this._menuCtrl.getMenus()
|
||||
.filter(m => m.side === this.side && m !== this)
|
||||
.map(m => m.enabled = false);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// what happens if menu is disabled while swipping?
|
||||
|
||||
this._isEnabled = shouldEnable;
|
||||
this.setElementClass('menu-enabled', shouldEnable);
|
||||
this._updateState();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
initPane(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
paneChanged(isPane: boolean) {
|
||||
this._isPane = isPane;
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
swipeEnable(shouldEnable: boolean): Menu {
|
||||
this.swipeEnabled = shouldEnable;
|
||||
// TODO
|
||||
// what happens if menu swipe is disabled while swipping?
|
||||
this._isSwipeEnabled = shouldEnable;
|
||||
this._updateState();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -652,6 +699,13 @@ export class Menu {
|
||||
this._renderer.setElementAttribute(this._elementRef.nativeElement, attributeName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getElementRef(): ElementRef {
|
||||
return this._elementRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
@@ -37,17 +37,11 @@ export class E2EApp {
|
||||
|
||||
menu1Active() {
|
||||
this.menuCtrl.enable(true, 'menu1');
|
||||
this.menuCtrl.enable(false, 'menu2');
|
||||
this.menuCtrl.enable(false, 'menu3');
|
||||
}
|
||||
menu2Active() {
|
||||
this.menuCtrl.enable(false, 'menu1');
|
||||
this.menuCtrl.enable(true, 'menu2');
|
||||
this.menuCtrl.enable(false, 'menu3');
|
||||
}
|
||||
menu3Active() {
|
||||
this.menuCtrl.enable(false, 'menu1');
|
||||
this.menuCtrl.enable(false, 'menu2');
|
||||
this.menuCtrl.enable(true, 'menu3');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, Component, ComponentFactoryResolver, ElementRef, Input, Optional, NgZone, Renderer, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||
import { AfterViewInit, Component, ComponentFactoryResolver, ElementRef, forwardRef, Input, Optional, NgZone, Renderer, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
import { App } from '../app/app';
|
||||
import { Config } from '../../config/config';
|
||||
@@ -13,6 +13,7 @@ import { NavOptions } from '../../navigation/nav-util';
|
||||
import { Platform } from '../../platform/platform';
|
||||
import { TransitionController } from '../../transitions/transition-controller';
|
||||
import { ViewController } from '../../navigation/view-controller';
|
||||
import { RootNode } from '../split-pane/split-pane';
|
||||
|
||||
/**
|
||||
* @name Nav
|
||||
@@ -52,8 +53,9 @@ import { ViewController } from '../../navigation/view-controller';
|
||||
'<div #viewport nav-viewport></div>' +
|
||||
'<div class="nav-decor"></div>',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
providers: [{provide: RootNode, useExisting: forwardRef(() => Nav) }]
|
||||
})
|
||||
export class Nav extends NavControllerBase implements AfterViewInit {
|
||||
export class Nav extends NavControllerBase implements AfterViewInit, RootNode {
|
||||
private _root: any;
|
||||
private _hasInit: boolean = false;
|
||||
|
||||
@@ -160,8 +162,19 @@ export class Nav extends NavControllerBase implements AfterViewInit {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
destroy() {
|
||||
ngOnDestroy() {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
initPane(): boolean {
|
||||
const isMain = this._elementRef.nativeElement.hasAttribute('main');
|
||||
return isMain;
|
||||
}
|
||||
|
||||
paneChanged(isPane: boolean) {
|
||||
if (isPane) {
|
||||
this.resize();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,5 +49,9 @@ export class OverlayPortal extends NavControllerBase {
|
||||
this._zIndexOffset = (val || 0);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -890,7 +890,14 @@ export class Slides extends Ion {
|
||||
|
||||
|
||||
|
||||
constructor(config: Config, private _plt: Platform, zone: NgZone, @Optional() viewCtrl: ViewController, elementRef: ElementRef, renderer: Renderer) {
|
||||
constructor(
|
||||
config: Config,
|
||||
private _plt: Platform,
|
||||
zone: NgZone,
|
||||
@Optional() viewCtrl: ViewController,
|
||||
elementRef: ElementRef,
|
||||
renderer: Renderer,
|
||||
) {
|
||||
super(config, elementRef, renderer, 'slides');
|
||||
|
||||
this._zone = zone;
|
||||
|
||||
@@ -85,7 +85,6 @@ export function initEvents(s: Slides, plt: Platform): Function {
|
||||
|
||||
// onresize
|
||||
let resizeObs = plt.resize.subscribe(() => onResize(s, plt, false));
|
||||
|
||||
// Next, Prev, Index
|
||||
if (s.nextButton) {
|
||||
plt.registerListener(s.nextButton, 'click', (ev) => {
|
||||
@@ -817,7 +816,18 @@ function onTouchEnd(s: Slides, plt: Platform, ev: SlideUIEvent) {
|
||||
/*=========================
|
||||
Resize Handler
|
||||
===========================*/
|
||||
let resizeId: number;
|
||||
function onResize(s: Slides, plt: Platform, forceUpdatePagination: boolean) {
|
||||
// TODO: hacky, we should use Resize Observer in the future
|
||||
if (resizeId) {
|
||||
plt.cancelTimeout(resizeId);
|
||||
resizeId = null;
|
||||
}
|
||||
resizeId = plt.timeout(() => doResize(s, plt, forceUpdatePagination), 200);
|
||||
}
|
||||
|
||||
function doResize(s: Slides, plt: Platform, forceUpdatePagination: boolean) {
|
||||
resizeId = null;
|
||||
// Disable locks on resize
|
||||
var allowSwipeToPrev = s._allowSwipeToPrev;
|
||||
var allowSwipeToNext = s._allowSwipeToNext;
|
||||
|
||||
12
src/components/split-pane/split-pane.ios.scss
Normal file
12
src/components/split-pane/split-pane.ios.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
@import "../../themes/ionic.globals.ios";
|
||||
|
||||
// Split Pane
|
||||
// --------------------------------------------------
|
||||
|
||||
.split-pane-ios.split-pane-visible >.split-pane-side {
|
||||
min-width: 270px;
|
||||
max-width: 28%;
|
||||
|
||||
border-right: $hairlines-width solid $list-ios-border-color;
|
||||
}
|
||||
13
src/components/split-pane/split-pane.md.scss
Normal file
13
src/components/split-pane/split-pane.md.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
@import "../../themes/ionic.globals.md";
|
||||
|
||||
// Split Pane
|
||||
// --------------------------------------------------
|
||||
|
||||
.split-pane-md.split-pane-visible >.split-pane-side {
|
||||
min-width: 270px;
|
||||
max-width: 28%;
|
||||
|
||||
border-right: 1px solid $list-md-border-color;
|
||||
}
|
||||
|
||||
74
src/components/split-pane/split-pane.scss
Normal file
74
src/components/split-pane/split-pane.scss
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
// Split Pane
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-split-pane {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
display: flex;
|
||||
|
||||
flex-wrap: nowrap;
|
||||
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
.split-pane-side:not(ion-menu) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.split-pane-visible >.split-pane-side,
|
||||
.split-pane-visible >.split-pane-main {
|
||||
// scss-lint:disable ImportantRule
|
||||
position: relative;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
|
||||
flex: 1;
|
||||
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.split-pane-visible >.split-pane-side {
|
||||
flex-shrink: 0;
|
||||
|
||||
order: -1;
|
||||
}
|
||||
|
||||
.split-pane-visible >.split-pane-main,
|
||||
.split-pane-visible >ion-nav.split-pane-side,
|
||||
.split-pane-visible >ion-tabs.split-pane-side,
|
||||
.split-pane-visible >ion-menu.menu-enabled {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.split-pane-visible >ion-split-pane.split-pane-side,
|
||||
.split-pane-visible >ion-split-pane.split-pane-main {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.split-pane-visible >ion-menu.menu-enabled {
|
||||
>.menu-inner {
|
||||
// scss-lint:disable ImportantRule
|
||||
right: 0;
|
||||
left: 0;
|
||||
|
||||
width: auto;
|
||||
|
||||
box-shadow: none !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
>.ion-backdrop {
|
||||
// scss-lint:disable ImportantRule
|
||||
display: hidden !important;
|
||||
}
|
||||
}
|
||||
173
src/components/split-pane/split-pane.ts
Normal file
173
src/components/split-pane/split-pane.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { ContentChildren, Directive, ElementRef, EventEmitter, forwardRef, Input, Output, QueryList, NgZone, Renderer } from '@angular/core';
|
||||
import { Ion } from '../ion';
|
||||
import { assert } from '../../util/util';
|
||||
import { Config } from '../../config/config';
|
||||
import { Platform } from '../../platform/platform';
|
||||
|
||||
const QUERY: { [key: string]: string } = {
|
||||
xs: '(min-width: 0px)',
|
||||
sm: '(min-width: 576px)',
|
||||
md: '(min-width: 768px)',
|
||||
lg: '(min-width: 992px)',
|
||||
xl: '(min-width: 1200px)',
|
||||
never: ''
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export abstract class RootNode {
|
||||
abstract getElementRef(): ElementRef;
|
||||
abstract initPane(): boolean;
|
||||
abstract paneChanged?(visible: boolean): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name SplitPane
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'ion-split-pane',
|
||||
providers: [{provide: RootNode, useExisting: forwardRef(() => SplitPane) }]
|
||||
})
|
||||
export class SplitPane extends Ion implements RootNode {
|
||||
|
||||
_rmListener: any;
|
||||
_visible: boolean = false;
|
||||
_init: boolean = false;
|
||||
_mediaQuery: string | boolean = QUERY['md'];
|
||||
_children: RootNode[];
|
||||
|
||||
sideContent: RootNode = null;
|
||||
mainContent: RootNode = null;
|
||||
|
||||
@ContentChildren(RootNode, { descendants: false })
|
||||
set _setchildren(query: QueryList<RootNode>) {
|
||||
const children = this._children = query.filter((child => child !== this));
|
||||
children.forEach(child => {
|
||||
var isMain = child.initPane();
|
||||
this._setPaneCSSClass(child.getElementRef(), isMain);
|
||||
});
|
||||
}
|
||||
|
||||
@Input()
|
||||
set when(query: string | boolean) {
|
||||
if (typeof query === 'boolean') {
|
||||
this._mediaQuery = query;
|
||||
} else {
|
||||
const defaultQuery = QUERY[query];
|
||||
this._mediaQuery = (defaultQuery)
|
||||
? defaultQuery
|
||||
: query;
|
||||
}
|
||||
this._update();
|
||||
}
|
||||
get when(): string | boolean {
|
||||
return this._mediaQuery;
|
||||
}
|
||||
|
||||
@Output() ionChange: EventEmitter<SplitPane> = new EventEmitter<SplitPane>();
|
||||
|
||||
constructor(
|
||||
private _zone: NgZone,
|
||||
private _plt: Platform,
|
||||
config: Config,
|
||||
elementRef: ElementRef,
|
||||
renderer: Renderer,
|
||||
) {
|
||||
super(config, elementRef, renderer, 'split-pane');
|
||||
}
|
||||
|
||||
_register(node: RootNode, isMain: boolean, callback: Function): boolean {
|
||||
if (this.getElementRef().nativeElement !== node.getElementRef().nativeElement.parentNode) {
|
||||
return false;
|
||||
}
|
||||
this._setPaneCSSClass(node.getElementRef(), isMain);
|
||||
if (callback) {
|
||||
this.ionChange.subscribe(callback);
|
||||
}
|
||||
if (isMain) {
|
||||
if (this.mainContent) {
|
||||
console.error('split pane: main content was already set');
|
||||
}
|
||||
this.mainContent = node;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this._init = true;
|
||||
this._update();
|
||||
}
|
||||
|
||||
_update() {
|
||||
if (!this._init) {
|
||||
return;
|
||||
}
|
||||
// Unlisten
|
||||
this._rmListener && this._rmListener();
|
||||
this._rmListener = null;
|
||||
|
||||
const query = this._mediaQuery;
|
||||
if (typeof query === 'boolean') {
|
||||
this._setVisible(query);
|
||||
return;
|
||||
}
|
||||
if (query && query.length > 0) {
|
||||
// Listen
|
||||
const callback = (query: MediaQueryList) => this._setVisible(query.matches);
|
||||
const mediaList = this._plt.win().matchMedia(query);
|
||||
mediaList.addListener(callback);
|
||||
this._setVisible(mediaList.matches);
|
||||
this._rmListener = function () {
|
||||
mediaList.removeListener(callback);
|
||||
};
|
||||
} else {
|
||||
this._setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
_updateChildren() {
|
||||
this.mainContent = null;
|
||||
this.sideContent = null;
|
||||
const visible = this._visible;
|
||||
this._children.forEach(child => child.paneChanged && child.paneChanged(visible));
|
||||
}
|
||||
|
||||
_setVisible(visible: boolean) {
|
||||
if (this._visible === visible) {
|
||||
return;
|
||||
}
|
||||
this._visible = visible;
|
||||
this.setElementClass('split-pane-visible', visible);
|
||||
this._updateChildren();
|
||||
this._zone.run(() => {
|
||||
this.ionChange.emit(this);
|
||||
});
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
setElementClass(className: string, add: boolean) {
|
||||
this._renderer.setElementClass(this._elementRef.nativeElement, className, add);
|
||||
}
|
||||
|
||||
_setPaneCSSClass(elementRef: ElementRef, isMain: boolean) {
|
||||
const ele = elementRef.nativeElement;
|
||||
this._renderer.setElementClass(ele, 'split-pane-main', isMain);
|
||||
this._renderer.setElementClass(ele, 'split-pane-side', !isMain);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
assert(this._rmListener, 'at this point _rmListerner should be valid');
|
||||
|
||||
this._rmListener && this._rmListener();
|
||||
this._rmListener = null;
|
||||
}
|
||||
|
||||
initPane(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
12
src/components/split-pane/split-pane.wp.scss
Normal file
12
src/components/split-pane/split-pane.wp.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
@import "../../themes/ionic.globals.wp";
|
||||
|
||||
// Split Pane
|
||||
// --------------------------------------------------
|
||||
|
||||
.split-pane-wp.split-pane-visible >.split-pane-side {
|
||||
min-width: 270px;
|
||||
max-width: 28%;
|
||||
|
||||
border-right: 1px solid $list-wp-border-color;
|
||||
}
|
||||
120
src/components/split-pane/test/basic/app.module.ts
Normal file
120
src/components/split-pane/test/basic/app.module.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Component, NgModule } from '@angular/core';
|
||||
import { IonicApp, IonicModule, NavController, MenuController, SplitPane } from '../../../../../ionic-angular';
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar><ion-title>Navigation</ion-title></ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item>Hola 1</ion-item>
|
||||
<ion-item>Hola 2</ion-item>
|
||||
<ion-item>Hola 3</ion-item>
|
||||
<button ion-item (click)="push()">Push</button>
|
||||
<ion-item>Hola</ion-item>
|
||||
<ion-item>Hola</ion-item>
|
||||
<ion-item>Hola</ion-item>
|
||||
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class SidePage {
|
||||
constructor(public navCtrl: NavController) { }
|
||||
push() {
|
||||
this.navCtrl.push(SidePage);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<button ion-button menuToggle>
|
||||
<ion-icon name="menu"></ion-icon>
|
||||
</button>
|
||||
<ion-title>Page 2</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<h1>Page 2</h1>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class E2EPage2 {}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<button ion-button menuToggle>
|
||||
<ion-icon name="menu"></ion-icon>
|
||||
</button>
|
||||
<ion-title>Navigation</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<h1>Page 1</h1>
|
||||
<button ion-button (click)="push()">Push</button>
|
||||
<button ion-button (click)="menu()">Disable/enable menu</button>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class E2EPage {
|
||||
constructor(
|
||||
public navCtrl: NavController,
|
||||
public menuCtrl: MenuController,
|
||||
) { }
|
||||
|
||||
push() {
|
||||
this.navCtrl.push(E2EPage2);
|
||||
}
|
||||
|
||||
menu() {
|
||||
this.menuCtrl.enable(!this.menuCtrl.isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
export class E2EApp {
|
||||
root = E2EPage;
|
||||
root2 = SidePage;
|
||||
|
||||
splitPaneChanged(splitPane: SplitPane) {
|
||||
console.log('Split pane changed, visible: ', splitPane.isVisible());
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
E2EApp,
|
||||
E2EPage,
|
||||
E2EPage2,
|
||||
SidePage,
|
||||
],
|
||||
imports: [
|
||||
IonicModule.forRoot(E2EApp, {
|
||||
swipeBackEnabled: true
|
||||
})
|
||||
],
|
||||
bootstrap: [IonicApp],
|
||||
entryComponents: [
|
||||
E2EApp,
|
||||
E2EPage,
|
||||
E2EPage2,
|
||||
SidePage,
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
1
src/components/split-pane/test/basic/e2e.ts
Normal file
1
src/components/split-pane/test/basic/e2e.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
7
src/components/split-pane/test/basic/main.html
Normal file
7
src/components/split-pane/test/basic/main.html
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
<ion-split-pane (ionChange)="splitPaneChanged($event)">
|
||||
<ion-menu [content]="content">
|
||||
<ion-nav [root]="root2"></ion-nav>
|
||||
</ion-menu>
|
||||
<ion-nav [root]="root" main #content></ion-nav>
|
||||
</ion-split-pane>
|
||||
102
src/components/split-pane/test/menus/app.module.ts
Normal file
102
src/components/split-pane/test/menus/app.module.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { Component, NgModule } from '@angular/core';
|
||||
import { IonicApp, IonicModule, NavController, MenuController, SplitPane } from '../../../../../ionic-angular';
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<button ion-button menuToggle>
|
||||
<ion-icon name="menu"></ion-icon>
|
||||
</button>
|
||||
<ion-title>Page 2</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<h1>Page 2</h1>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class E2EPage2 {}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<button ion-button menuToggle>
|
||||
<ion-icon name="menu"></ion-icon>
|
||||
</button>
|
||||
<ion-title>Navigation</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<h1>Page 1</h1>
|
||||
<button ion-button (click)="push()">Push</button>
|
||||
<button ion-button (click)="menu1Active()">Enable menu 1</button>
|
||||
<button ion-button (click)="menu2Active()">Enable menu 2</button>
|
||||
<button ion-button (click)="menu3Active()">Enable menu 3</button>
|
||||
<button ion-button (click)="disableAll()">Disable all</button>
|
||||
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class E2EPage {
|
||||
constructor(
|
||||
public navCtrl: NavController,
|
||||
public menuCtrl: MenuController,
|
||||
) { }
|
||||
|
||||
push() {
|
||||
this.navCtrl.push(E2EPage2);
|
||||
}
|
||||
menu1Active() {
|
||||
this.menuCtrl.enable(true, 'menu1');
|
||||
}
|
||||
menu2Active() {
|
||||
this.menuCtrl.enable(true, 'menu2');
|
||||
}
|
||||
menu3Active() {
|
||||
this.menuCtrl.enable(true, 'menu3');
|
||||
}
|
||||
disableAll() {
|
||||
this.menuCtrl.enable(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
export class E2EApp {
|
||||
root = E2EPage;
|
||||
|
||||
splitPaneChanged(splitPane: SplitPane) {
|
||||
console.log('Split pane changed, visible: ', splitPane.isVisible());
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
E2EApp,
|
||||
E2EPage,
|
||||
E2EPage2,
|
||||
],
|
||||
imports: [
|
||||
IonicModule.forRoot(E2EApp, {
|
||||
swipeBackEnabled: true
|
||||
})
|
||||
],
|
||||
bootstrap: [IonicApp],
|
||||
entryComponents: [
|
||||
E2EApp,
|
||||
E2EPage,
|
||||
E2EPage2,
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
1
src/components/split-pane/test/menus/e2e.ts
Normal file
1
src/components/split-pane/test/menus/e2e.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
52
src/components/split-pane/test/menus/main.html
Normal file
52
src/components/split-pane/test/menus/main.html
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
<ion-split-pane (ionChange)="splitPaneChanged($event)">
|
||||
<ion-menu [content]="content" id="menu1">
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar color="secondary">
|
||||
<ion-title>Menu 1</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item>Example</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
</ion-menu>
|
||||
|
||||
|
||||
<ion-menu [content]="content" id="menu2">
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar color="danger">
|
||||
<ion-title>Menu 2</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item>Example</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
</ion-menu>
|
||||
|
||||
<ion-menu [content]="content" id="menu3">
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar color="primary">
|
||||
<ion-title>Menu 3</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item>Example</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-menu>
|
||||
|
||||
<ion-nav [root]="root" main #content></ion-nav>
|
||||
</ion-split-pane>
|
||||
162
src/components/split-pane/test/nested/app.module.ts
Normal file
162
src/components/split-pane/test/nested/app.module.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { Component, NgModule } from '@angular/core';
|
||||
import { IonicApp, IonicModule, NavController } from '../../../../../ionic-angular';
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar><ion-title>Nested 1</ion-title></ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<button ion-button (click)="push()">Push</button>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class E2ENested {
|
||||
constructor(
|
||||
public navCtrl: NavController,
|
||||
) { }
|
||||
|
||||
push() {
|
||||
this.navCtrl.push(E2ENested);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar><ion-title>Nested 2</ion-title></ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<button ion-button (click)="push()">Push</button>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class E2ENested2 {
|
||||
constructor(
|
||||
public navCtrl: NavController,
|
||||
) { }
|
||||
|
||||
push() {
|
||||
this.navCtrl.push(E2ENested2);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<button ion-button menuToggle>
|
||||
<ion-icon name="menu"></ion-icon>
|
||||
</button>
|
||||
<ion-title>Nested 3</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<button ion-button (click)="push()">Push</button>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class E2ENested3 {
|
||||
constructor(
|
||||
public navCtrl: NavController,
|
||||
) { }
|
||||
|
||||
push() {
|
||||
this.navCtrl.push(E2ENested3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar><ion-title>Navigation</ion-title></ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item>Hola</ion-item>
|
||||
<ion-item>Hola</ion-item>
|
||||
<ion-item>Hola</ion-item>
|
||||
<button ion-item (click)="push()">Push</button>
|
||||
<ion-item>Hola</ion-item>
|
||||
<ion-item>Hola</ion-item>
|
||||
<ion-item>Hola</ion-item>
|
||||
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class SidePage {
|
||||
constructor(public navCtrl: NavController) { }
|
||||
push() {
|
||||
this.navCtrl.push(SidePage);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-split-pane>
|
||||
<ion-nav [root]="root"></ion-nav>
|
||||
|
||||
<ion-split-pane when="lg" main >
|
||||
<ion-nav [root]="root2"></ion-nav>
|
||||
<ion-nav [root]="root3" main ></ion-nav>
|
||||
</ion-split-pane>
|
||||
|
||||
</ion-split-pane>
|
||||
`
|
||||
})
|
||||
export class E2EPage {
|
||||
root = E2ENested;
|
||||
root2 = E2ENested2;
|
||||
root3 = E2ENested3;
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
export class E2EApp {
|
||||
root = E2EPage;
|
||||
root2 = SidePage;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
E2EApp,
|
||||
E2EPage,
|
||||
SidePage,
|
||||
E2ENested,
|
||||
E2ENested2,
|
||||
E2ENested3
|
||||
],
|
||||
imports: [
|
||||
IonicModule.forRoot(E2EApp, {
|
||||
swipeBackEnabled: true
|
||||
})
|
||||
],
|
||||
bootstrap: [IonicApp],
|
||||
entryComponents: [
|
||||
E2EApp,
|
||||
E2EPage,
|
||||
SidePage,
|
||||
E2ENested,
|
||||
E2ENested2,
|
||||
E2ENested3
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
1
src/components/split-pane/test/nested/e2e.ts
Normal file
1
src/components/split-pane/test/nested/e2e.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
10
src/components/split-pane/test/nested/main.html
Normal file
10
src/components/split-pane/test/nested/main.html
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
<ion-split-pane when="sm">
|
||||
|
||||
<ion-menu [content]="content">
|
||||
<ion-nav [root]="root2"></ion-nav>
|
||||
</ion-menu>
|
||||
|
||||
<ion-nav [root]="root" main #content></ion-nav>
|
||||
|
||||
</ion-split-pane>
|
||||
98
src/components/split-pane/test/tabs/app.module.ts
Normal file
98
src/components/split-pane/test/tabs/app.module.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Component, NgModule } from '@angular/core';
|
||||
import { IonicApp, IonicModule, NavController, MenuController } from '../../../../../ionic-angular';
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar><ion-title>Navigation</ion-title></ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-slides style="background: black"
|
||||
pager="true"
|
||||
effect="flip">
|
||||
|
||||
<ion-slide style="background: red; color: white;">
|
||||
<h1>Slide 1</h1>
|
||||
</ion-slide>
|
||||
|
||||
<ion-slide style="background: white; color: blue;">
|
||||
<h1>Slide 2</h1>
|
||||
</ion-slide>
|
||||
|
||||
<ion-slide style="background: blue; color: white;">
|
||||
<h1>Slide 3</h1>
|
||||
</ion-slide>
|
||||
|
||||
</ion-slides>
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class SidePage {
|
||||
constructor(public navCtrl: NavController) { }
|
||||
push() {
|
||||
this.navCtrl.push(SidePage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<ion-header>
|
||||
<ion-navbar>
|
||||
<button ion-button menuToggle>
|
||||
<ion-icon name="menu"></ion-icon>
|
||||
</button>
|
||||
<ion-title>Navigation</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<button ion-button (click)="push()">Push</button>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
<div f></div>
|
||||
|
||||
</ion-content>
|
||||
`
|
||||
})
|
||||
export class E2EPage {
|
||||
constructor(
|
||||
public navCtrl: NavController,
|
||||
public menuCtrl: MenuController,
|
||||
) { }
|
||||
|
||||
push() {
|
||||
this.navCtrl.push(E2EPage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
templateUrl: 'main.html'
|
||||
})
|
||||
export class E2EApp {
|
||||
root = E2EPage;
|
||||
root2 = SidePage;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
E2EApp,
|
||||
E2EPage,
|
||||
SidePage,
|
||||
],
|
||||
imports: [
|
||||
IonicModule.forRoot(E2EApp, {
|
||||
swipeBackEnabled: true
|
||||
})
|
||||
],
|
||||
bootstrap: [IonicApp],
|
||||
entryComponents: [
|
||||
E2EApp,
|
||||
E2EPage,
|
||||
SidePage,
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
1
src/components/split-pane/test/tabs/e2e.ts
Normal file
1
src/components/split-pane/test/tabs/e2e.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
8
src/components/split-pane/test/tabs/main.html
Normal file
8
src/components/split-pane/test/tabs/main.html
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
<ion-split-pane>
|
||||
<ion-nav [root]="root2" #content main></ion-nav>
|
||||
<ion-tabs>
|
||||
<ion-tab [root]="root" tabTitle="Page1"></ion-tab>
|
||||
<ion-tab [root]="root" tabTitle="Page2"></ion-tab>
|
||||
</ion-tabs>
|
||||
</ion-split-pane>
|
||||
@@ -314,17 +314,24 @@ export class Tab extends NavControllerBase {
|
||||
// to refresh the tabbar and content dimensions to be sure
|
||||
// they're lined up correctly
|
||||
this._dom.read(() => {
|
||||
const active = this.getActive();
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
const content = active.getIONContent();
|
||||
content && content.resize();
|
||||
this.resize();
|
||||
});
|
||||
done(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
resize() {
|
||||
const active = this.getActive();
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
const content = active.getIONContent();
|
||||
content && content.resize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -385,7 +392,7 @@ export class Tab extends NavControllerBase {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
destroy() {
|
||||
ngOnDestroy() {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ $tabs-ios-border: $hairlines-width solid $tabs-ios-border-colo
|
||||
$tabs-ios-tab-padding: 0 2px !default;
|
||||
|
||||
/// @prop - Max width of the tab button
|
||||
$tabs-ios-tab-max-width: 240px !default;
|
||||
$tabs-ios-tab-max-width: 110px !default;
|
||||
|
||||
/// @prop - Minimum height of the tab button
|
||||
$tabs-ios-tab-min-height: 49px !default;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, Output, Optional, Renderer, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||
import { AfterViewInit, Component, ElementRef, EventEmitter, forwardRef, Input, Output, Optional, Renderer, ViewChild, ViewContainerRef, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
import { App } from '../app/app';
|
||||
import { Config } from '../../config/config';
|
||||
@@ -8,6 +8,7 @@ import { isBlank, assert } from '../../util/util';
|
||||
import { NavController } from '../../navigation/nav-controller';
|
||||
import { NavControllerBase } from '../../navigation/nav-controller-base';
|
||||
import { getComponent, NavOptions, DIRECTION_SWITCH } from '../../navigation/nav-util';
|
||||
import { RootNode } from '../split-pane/split-pane';
|
||||
import { Platform } from '../../platform/platform';
|
||||
import { Tab } from './tab';
|
||||
import { TabHighlight } from './tab-highlight';
|
||||
@@ -161,8 +162,9 @@ import { ViewController } from '../../navigation/view-controller';
|
||||
'<ng-content></ng-content>' +
|
||||
'<div #portal tab-portal></div>',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
providers: [{provide: RootNode, useExisting: forwardRef(() => Tabs) }]
|
||||
})
|
||||
export class Tabs extends Ion implements AfterViewInit {
|
||||
export class Tabs extends Ion implements AfterViewInit, RootNode {
|
||||
/** @internal */
|
||||
_ids: number = -1;
|
||||
/** @internal */
|
||||
@@ -559,6 +561,31 @@ export class Tabs extends Ion implements AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
resize() {
|
||||
const tab = this.getSelected();
|
||||
tab && tab.resize();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
initPane(): boolean {
|
||||
const isMain = this._elementRef.nativeElement.hasAttribute('main');
|
||||
return isMain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
paneChanged(isPane: boolean) {
|
||||
if (isPane) {
|
||||
this.resize();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let tabIds = -1;
|
||||
|
||||
Reference in New Issue
Block a user