fix(menu): rtl support

Squashed commit of the following:

commit 57b59eb373e76401caf1c42fa696a67e1e6a69b7
Merge: dcf80e416 f26c4b4fe
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Fri Apr 21 14:38:03 2017 +0300

    merge 'master'

commit dcf80e4161f33ca3ff4cea5ce0c6358107bbcdff
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Fri Apr 21 01:24:40 2017 +0300

    tests(menu): add menu rtl tests

commit d65575b9ca63402fdc49991349320ad1dd76f114
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Fri Apr 21 01:24:00 2017 +0300

    tests(mock): add platform to mockMenu

commit faad17ca3231d999296907a9892b3afdb48e124f
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Fri Apr 21 01:23:01 2017 +0300

    fix(menu): update gesture only if it exists

commit e50eb184ec5af3cfdb3634b1d7cfe1899da36054
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Fri Apr 21 00:55:50 2017 +0300

    refactor(menu): use magic get/set for side update

commit 89a1715661fa889157f39401c419fc55d4809f9e
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Thu Apr 20 19:31:49 2017 +0300

    revert(menu): revert removed dependency
    this PR is for a specific feature

commit 7c1263624c68fc2729b24f10956841b6ff0c22f6
Merge: 4465f82f6 860567288
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Thu Apr 20 19:26:10 2017 +0300

    Merge remote-tracking branch 'upstream/master' into menu

commit 4465f82f644a96830379fe3bef8e0fbe53e6f998
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Thu Apr 20 16:11:13 2017 +0300

    style(backdrop): fix code style

commit da4983a16133ee7d85f3393161d16fe1a33d2fa4
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Thu Apr 20 16:09:18 2017 +0300

    revert(renderer): back to renderer1

commit 80ab0941d6351e3787dba11c4a6398da7044fb24
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Sun Apr 16 11:34:34 2017 +0300

    fix(swipe-back): change side according to dir

commit f99cb9844f325f43d9fb7ca22394e7ccff89f3b3
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Sun Apr 16 11:26:49 2017 +0300

    feat(platform): add dir change event emitter

commit 90de5d1105f6554ad759f280d2ed96ff6b3ef28a
Author: Amit Moryossef <amitmoryossef@gmail.com>
Date:   Sat Apr 15 20:13:09 2017 +0300

    fix(menu): add gesture side real-time update
    migrated to Renderer2
This commit is contained in:
Manu Mtz.-Almeida
2017-04-23 18:06:04 +02:00
parent f26c4b4feb
commit 51d507998c
7 changed files with 131 additions and 19 deletions

View File

@ -1,5 +1,7 @@
import { Animation } from '../../animations/animation';
export type Side = 'left' | 'right' | 'start' | 'end';
export interface Menu {
setOpen(shouldOpen: boolean, animated: boolean): Promise<boolean>;
open(): Promise<boolean>;
@ -9,7 +11,7 @@ export interface Menu {
swipeEnable(shouldEnable: boolean): Menu;
isOpen: boolean;
enabled: boolean;
side: string;
side: Side;
id: string;
isAnimating(): boolean;
width(): number;

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EventEmitter, forwardRef, Input, NgZone, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
import { OnInit, OnDestroy, 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';
@ -9,7 +9,7 @@ import { GestureController, GESTURE_GO_BACK_SWIPE, BlockerDelegate } from '../..
import { isTrueProperty, assert } from '../../util/util';
import { Keyboard } from '../../platform/keyboard';
import { MenuContentGesture } from './menu-gestures';
import { Menu as MenuInterface } from '../app/menu-interface';
import {Menu as MenuInterface, Side} from '../app/menu-interface';
import { MenuController } from '../app/menu-controller';
import { MenuType } from './menu-types';
import { Nav } from '../nav/nav';
@ -193,7 +193,7 @@ import { RootNode } from '../split-pane/split-pane';
encapsulation: ViewEncapsulation.None,
providers: [{provide: RootNode, useExisting: forwardRef(() => Menu) }]
})
export class Menu implements RootNode, MenuInterface {
export class Menu implements RootNode, MenuInterface, OnInit, OnDestroy {
private _cntEle: HTMLElement;
private _gesture: MenuContentGesture;
@ -206,6 +206,7 @@ export class Menu implements RootNode, MenuInterface {
private _events: UIEventManager;
private _gestureBlocker: BlockerDelegate;
private _isPane: boolean = false;
private _side: Side = 'start';
/**
* @hidden
@ -237,11 +238,6 @@ export class Menu implements RootNode, MenuInterface {
*/
@Input() id: string;
/**
* @input {string} Which side of the view the menu should be placed. Default `"left"`.
*/
@Input() side: string;
/**
* @input {string} The display type of the menu. Default varies based on the mode,
* see the `menuType` in the [config](../../config/Config). Available options:
@ -262,6 +258,24 @@ export class Menu implements RootNode, MenuInterface {
this.enable(isEnabled);
}
/**
* @input {string} Which side of the view the menu should be placed. Default `"left"`.
*/
@Input()
get side() {
if (this._side === 'right' || (this._side === 'start' && this._plt.isRTL()) || (this._side === 'end' && !this._plt.isRTL())) {
return 'right';
}
return 'left';
}
set side(val: Side) {
this._side = val;
// Update gesture edge
if (this._gesture)
this._gesture.setEdges(this.side);
}
/**
* @input {boolean} If true, swiping the menu is enabled. Default `true`.
*/
@ -339,11 +353,7 @@ export class Menu implements RootNode, MenuInterface {
return console.error('Menu: must have a [content] element to listen for drag events on. Example:\n\n<ion-menu [content]="content"></ion-menu>\n\n<ion-nav #content></ion-nav>');
}
// normalize the "side"
if (this.side !== 'left' && this.side !== 'right') {
this.side = 'left';
}
this.setElementAttribute('side', this.side);
this.setElementAttribute('side', this._side);
// normalize the "type"
if (!this.type) {

View File

@ -1,6 +1,6 @@
import { MenuController } from '../../app/menu-controller';
import { mockMenu } from '../../../util/mock-providers';
import {mockMenu, mockPlatform} from '../../../util/mock-providers';
import {Side} from '../../app/menu-interface';
describe('MenuController', () => {
@ -116,6 +116,30 @@ describe('MenuController', () => {
expect(menu).toEqual(someMenu);
});
it('should get the only left menu on start ltr', () => {
let someMenu = mockMenu();
someMenu.side = 'start';
menuCtrl._register(someMenu);
let menu = menuCtrl.get('left');
expect(menu).toEqual(someMenu);
});
it('should get the only left menu on end rtl', () => {
let platform = mockPlatform();
platform.setDir('rtl', true);
expect(platform.dir()).toEqual('rtl');
let someMenu = mockMenu(platform);
someMenu.side = 'end';
menuCtrl._register(someMenu);
expect(someMenu.side).toEqual('left');
let menu = menuCtrl.get('left');
expect(menu).toEqual(someMenu);
});
it('should get the enabled left menu', () => {
let someMenu1 = mockMenu();
someMenu1.side = 'left';
@ -155,6 +179,30 @@ describe('MenuController', () => {
expect(menu).toEqual(someMenu);
});
it('should get the only right menu on end ltr', () => {
let someMenu = mockMenu();
someMenu.side = 'end';
menuCtrl._register(someMenu);
let menu = menuCtrl.get('right');
expect(menu).toEqual(someMenu);
});
it('should get the only right menu on start rtl', () => {
let platform = mockPlatform();
platform.setDir('rtl', true);
expect(platform.dir()).toEqual('rtl');
let someMenu = mockMenu(platform);
someMenu.side = 'start';
menuCtrl._register(someMenu);
expect(someMenu.side).toEqual('right');
let menu = menuCtrl.get('right');
expect(menu).toEqual(someMenu);
});
it('should get the menu by left with id', () => {
let someMenu = mockMenu();
someMenu.id = 'myMenu';
@ -165,6 +213,40 @@ describe('MenuController', () => {
expect(menu).toEqual(someMenu);
});
it('should switch menu side in runtime', () => {
let someMenu = mockMenu();
menuCtrl._register(someMenu);
['left', 'right'].forEach((side: Side) => {
someMenu.side = side;
expect(someMenu.side).toEqual(side);
let menu = menuCtrl.get(side);
expect(menu).toEqual(someMenu);
});
});
it('should switch menu side in runtime by direction', () => {
let platform = mockPlatform();
platform.setDir('ltr', true);
expect(platform.dir()).toEqual('ltr');
let someMenu = mockMenu(platform);
menuCtrl._register(someMenu);
expect(someMenu.side).toEqual('left');
let menu = menuCtrl.get('left');
expect(menu).toEqual(someMenu);
platform.setDir('rtl', true);
expect(someMenu.side).toEqual('right');
let menu2 = menuCtrl.get('right');
expect(menu2).toEqual(someMenu);
});
});
describe('enable()', () => {

View File

@ -18,10 +18,14 @@ export class SlideEdgeGesture extends SlideGesture {
});
super(plt, element, opts);
// Can check corners through use of eg 'left top'
this.edges = opts.edge.split(' ');
this.setEdges(opts.edge);
this.maxEdgeStart = opts.maxEdgeStart;
}
setEdges(edges: string) {
this.edges = edges.split(' ');
}
canStart(ev: any): boolean {
let coord = pointerCoord(ev);
this._d = this.getContainerDimensions();

View File

@ -30,6 +30,14 @@ export class SwipeBackGesture extends SlideEdgeGesture {
disableScroll: true
})
});
this.setSide(plt.dir());
Platform.dirChanged.subscribe(this.setSide.bind(this));
}
private setSide(dir: string) {
this.setEdges(dir === 'ltr' ? 'left' : 'right');
}
canStart(ev: any): boolean {

View File

@ -50,6 +50,8 @@ export class Platform {
private _isPortrait: boolean = null;
private _uiEvtOpts = false;
static dirChanged: EventEmitter<any> = new EventEmitter();
/** @hidden */
zone: NgZone;
@ -313,9 +315,12 @@ export class Platform {
* direction needs to be dynamically changed per user/session.
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
* @param {string} dir Examples: `rtl`, `ltr`
* @param {boolean} updateDocument
*/
setDir(dir: string, updateDocument: boolean) {
this._dir = (dir || '').toLowerCase();
Platform.dirChanged.emit(this._dir);
if (updateDocument !== false) {
this._doc['documentElement'].setAttribute('dir', dir);
}

View File

@ -543,13 +543,14 @@ export function mockTabs(app?: App): Tabs {
return new Tabs(null, null, app, config, elementRef, platform, renderer, linker);
}
export function mockMenu(): Menu {
export function mockMenu(platform: MockPlatform = null): Menu {
let app = mockApp();
let gestureCtrl = new GestureController(app);
let dom = mockDomController();
let elementRef = mockElementRef();
let renderer = mockRenderer();
return new Menu(null, elementRef, null, null, renderer, null, null, gestureCtrl, dom, app);
let plt = platform === null ? mockPlatform() : platform;
return new Menu(null, elementRef, null, plt, renderer, null, null, gestureCtrl, dom, app);
}
export function mockDeepLinkConfig(links?: any[]): DeepLinkConfig {