Skip to content

Commit 51d5079

Browse files
committed
fix(menu): rtl support
Squashed commit of the following: commit 57b59eb Merge: dcf80e4 f26c4b4 Author: Amit Moryossef <amitmoryossef@gmail.com> Date: Fri Apr 21 14:38:03 2017 +0300 merge 'master' commit dcf80e4 Author: Amit Moryossef <amitmoryossef@gmail.com> Date: Fri Apr 21 01:24:40 2017 +0300 tests(menu): add menu rtl tests commit d65575b Author: Amit Moryossef <amitmoryossef@gmail.com> Date: Fri Apr 21 01:24:00 2017 +0300 tests(mock): add platform to mockMenu commit faad17c Author: Amit Moryossef <amitmoryossef@gmail.com> Date: Fri Apr 21 01:23:01 2017 +0300 fix(menu): update gesture only if it exists commit e50eb18 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 89a1715 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 7c12636 Merge: 4465f82 8605672 Author: Amit Moryossef <amitmoryossef@gmail.com> Date: Thu Apr 20 19:26:10 2017 +0300 Merge remote-tracking branch 'upstream/master' into menu commit 4465f82 Author: Amit Moryossef <amitmoryossef@gmail.com> Date: Thu Apr 20 16:11:13 2017 +0300 style(backdrop): fix code style commit da4983a Author: Amit Moryossef <amitmoryossef@gmail.com> Date: Thu Apr 20 16:09:18 2017 +0300 revert(renderer): back to renderer1 commit 80ab094 Author: Amit Moryossef <amitmoryossef@gmail.com> Date: Sun Apr 16 11:34:34 2017 +0300 fix(swipe-back): change side according to dir commit f99cb98 Author: Amit Moryossef <amitmoryossef@gmail.com> Date: Sun Apr 16 11:26:49 2017 +0300 feat(platform): add dir change event emitter commit 90de5d1 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
1 parent f26c4b4 commit 51d5079

File tree

7 files changed

+131
-19
lines changed

7 files changed

+131
-19
lines changed

src/components/app/menu-interface.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Animation } from '../../animations/animation';
22

3+
export type Side = 'left' | 'right' | 'start' | 'end';
4+
35
export interface Menu {
46
setOpen(shouldOpen: boolean, animated: boolean): Promise<boolean>;
57
open(): Promise<boolean>;
@@ -9,7 +11,7 @@ export interface Menu {
911
swipeEnable(shouldEnable: boolean): Menu;
1012
isOpen: boolean;
1113
enabled: boolean;
12-
side: string;
14+
side: Side;
1315
id: string;
1416
isAnimating(): boolean;
1517
width(): number;

src/components/menu/menu.ts

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EventEmitter, forwardRef, Input, NgZone, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
1+
import { OnInit, OnDestroy, ChangeDetectionStrategy, Component, ContentChild, ElementRef, EventEmitter, forwardRef, Input, NgZone, Output, Renderer, ViewChild, ViewEncapsulation } from '@angular/core';
22

33
import { App } from '../app/app';
44
import { Backdrop } from '../backdrop/backdrop';
@@ -9,7 +9,7 @@ import { GestureController, GESTURE_GO_BACK_SWIPE, BlockerDelegate } from '../..
99
import { isTrueProperty, assert } from '../../util/util';
1010
import { Keyboard } from '../../platform/keyboard';
1111
import { MenuContentGesture } from './menu-gestures';
12-
import { Menu as MenuInterface } from '../app/menu-interface';
12+
import {Menu as MenuInterface, Side} from '../app/menu-interface';
1313
import { MenuController } from '../app/menu-controller';
1414
import { MenuType } from './menu-types';
1515
import { Nav } from '../nav/nav';
@@ -193,7 +193,7 @@ import { RootNode } from '../split-pane/split-pane';
193193
encapsulation: ViewEncapsulation.None,
194194
providers: [{provide: RootNode, useExisting: forwardRef(() => Menu) }]
195195
})
196-
export class Menu implements RootNode, MenuInterface {
196+
export class Menu implements RootNode, MenuInterface, OnInit, OnDestroy {
197197

198198
private _cntEle: HTMLElement;
199199
private _gesture: MenuContentGesture;
@@ -206,6 +206,7 @@ export class Menu implements RootNode, MenuInterface {
206206
private _events: UIEventManager;
207207
private _gestureBlocker: BlockerDelegate;
208208
private _isPane: boolean = false;
209+
private _side: Side = 'start';
209210

210211
/**
211212
* @hidden
@@ -237,11 +238,6 @@ export class Menu implements RootNode, MenuInterface {
237238
*/
238239
@Input() id: string;
239240

240-
/**
241-
* @input {string} Which side of the view the menu should be placed. Default `"left"`.
242-
*/
243-
@Input() side: string;
244-
245241
/**
246242
* @input {string} The display type of the menu. Default varies based on the mode,
247243
* see the `menuType` in the [config](../../config/Config). Available options:
@@ -262,6 +258,24 @@ export class Menu implements RootNode, MenuInterface {
262258
this.enable(isEnabled);
263259
}
264260

261+
/**
262+
* @input {string} Which side of the view the menu should be placed. Default `"left"`.
263+
*/
264+
@Input()
265+
get side() {
266+
if (this._side === 'right' || (this._side === 'start' && this._plt.isRTL()) || (this._side === 'end' && !this._plt.isRTL())) {
267+
return 'right';
268+
}
269+
return 'left';
270+
}
271+
272+
set side(val: Side) {
273+
this._side = val;
274+
// Update gesture edge
275+
if (this._gesture)
276+
this._gesture.setEdges(this.side);
277+
}
278+
265279
/**
266280
* @input {boolean} If true, swiping the menu is enabled. Default `true`.
267281
*/
@@ -339,11 +353,7 @@ export class Menu implements RootNode, MenuInterface {
339353
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>');
340354
}
341355

342-
// normalize the "side"
343-
if (this.side !== 'left' && this.side !== 'right') {
344-
this.side = 'left';
345-
}
346-
this.setElementAttribute('side', this.side);
356+
this.setElementAttribute('side', this._side);
347357

348358
// normalize the "type"
349359
if (!this.type) {

src/components/menu/test/menu.spec.ts

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { MenuController } from '../../app/menu-controller';
2-
import { mockMenu } from '../../../util/mock-providers';
3-
2+
import {mockMenu, mockPlatform} from '../../../util/mock-providers';
3+
import {Side} from '../../app/menu-interface';
44

55
describe('MenuController', () => {
66

@@ -116,6 +116,30 @@ describe('MenuController', () => {
116116
expect(menu).toEqual(someMenu);
117117
});
118118

119+
it('should get the only left menu on start ltr', () => {
120+
let someMenu = mockMenu();
121+
someMenu.side = 'start';
122+
menuCtrl._register(someMenu);
123+
124+
let menu = menuCtrl.get('left');
125+
expect(menu).toEqual(someMenu);
126+
});
127+
128+
it('should get the only left menu on end rtl', () => {
129+
let platform = mockPlatform();
130+
platform.setDir('rtl', true);
131+
expect(platform.dir()).toEqual('rtl');
132+
133+
let someMenu = mockMenu(platform);
134+
someMenu.side = 'end';
135+
menuCtrl._register(someMenu);
136+
137+
expect(someMenu.side).toEqual('left');
138+
139+
let menu = menuCtrl.get('left');
140+
expect(menu).toEqual(someMenu);
141+
});
142+
119143
it('should get the enabled left menu', () => {
120144
let someMenu1 = mockMenu();
121145
someMenu1.side = 'left';
@@ -155,6 +179,30 @@ describe('MenuController', () => {
155179
expect(menu).toEqual(someMenu);
156180
});
157181

182+
it('should get the only right menu on end ltr', () => {
183+
let someMenu = mockMenu();
184+
someMenu.side = 'end';
185+
menuCtrl._register(someMenu);
186+
187+
let menu = menuCtrl.get('right');
188+
expect(menu).toEqual(someMenu);
189+
});
190+
191+
it('should get the only right menu on start rtl', () => {
192+
let platform = mockPlatform();
193+
platform.setDir('rtl', true);
194+
expect(platform.dir()).toEqual('rtl');
195+
196+
let someMenu = mockMenu(platform);
197+
someMenu.side = 'start';
198+
menuCtrl._register(someMenu);
199+
200+
expect(someMenu.side).toEqual('right');
201+
202+
let menu = menuCtrl.get('right');
203+
expect(menu).toEqual(someMenu);
204+
});
205+
158206
it('should get the menu by left with id', () => {
159207
let someMenu = mockMenu();
160208
someMenu.id = 'myMenu';
@@ -165,6 +213,40 @@ describe('MenuController', () => {
165213
expect(menu).toEqual(someMenu);
166214
});
167215

216+
it('should switch menu side in runtime', () => {
217+
let someMenu = mockMenu();
218+
menuCtrl._register(someMenu);
219+
220+
['left', 'right'].forEach((side: Side) => {
221+
someMenu.side = side;
222+
expect(someMenu.side).toEqual(side);
223+
224+
let menu = menuCtrl.get(side);
225+
expect(menu).toEqual(someMenu);
226+
});
227+
});
228+
229+
it('should switch menu side in runtime by direction', () => {
230+
let platform = mockPlatform();
231+
platform.setDir('ltr', true);
232+
expect(platform.dir()).toEqual('ltr');
233+
234+
let someMenu = mockMenu(platform);
235+
menuCtrl._register(someMenu);
236+
237+
expect(someMenu.side).toEqual('left');
238+
239+
let menu = menuCtrl.get('left');
240+
expect(menu).toEqual(someMenu);
241+
242+
platform.setDir('rtl', true);
243+
244+
expect(someMenu.side).toEqual('right');
245+
246+
let menu2 = menuCtrl.get('right');
247+
expect(menu2).toEqual(someMenu);
248+
});
249+
168250
});
169251

170252
describe('enable()', () => {

src/gestures/slide-edge-gesture.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ export class SlideEdgeGesture extends SlideGesture {
1818
});
1919
super(plt, element, opts);
2020
// Can check corners through use of eg 'left top'
21-
this.edges = opts.edge.split(' ');
21+
this.setEdges(opts.edge);
2222
this.maxEdgeStart = opts.maxEdgeStart;
2323
}
2424

25+
setEdges(edges: string) {
26+
this.edges = edges.split(' ');
27+
}
28+
2529
canStart(ev: any): boolean {
2630
let coord = pointerCoord(ev);
2731
this._d = this.getContainerDimensions();

src/navigation/swipe-back.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ export class SwipeBackGesture extends SlideEdgeGesture {
3030
disableScroll: true
3131
})
3232
});
33+
34+
this.setSide(plt.dir());
35+
36+
Platform.dirChanged.subscribe(this.setSide.bind(this));
37+
}
38+
39+
private setSide(dir: string) {
40+
this.setEdges(dir === 'ltr' ? 'left' : 'right');
3341
}
3442

3543
canStart(ev: any): boolean {

src/platform/platform.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ export class Platform {
5050
private _isPortrait: boolean = null;
5151
private _uiEvtOpts = false;
5252

53+
static dirChanged: EventEmitter<any> = new EventEmitter();
54+
5355
/** @hidden */
5456
zone: NgZone;
5557

@@ -313,9 +315,12 @@ export class Platform {
313315
* direction needs to be dynamically changed per user/session.
314316
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
315317
* @param {string} dir Examples: `rtl`, `ltr`
318+
* @param {boolean} updateDocument
316319
*/
317320
setDir(dir: string, updateDocument: boolean) {
318321
this._dir = (dir || '').toLowerCase();
322+
Platform.dirChanged.emit(this._dir);
323+
319324
if (updateDocument !== false) {
320325
this._doc['documentElement'].setAttribute('dir', dir);
321326
}

src/util/mock-providers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,13 +543,14 @@ export function mockTabs(app?: App): Tabs {
543543
return new Tabs(null, null, app, config, elementRef, platform, renderer, linker);
544544
}
545545

546-
export function mockMenu(): Menu {
546+
export function mockMenu(platform: MockPlatform = null): Menu {
547547
let app = mockApp();
548548
let gestureCtrl = new GestureController(app);
549549
let dom = mockDomController();
550550
let elementRef = mockElementRef();
551551
let renderer = mockRenderer();
552-
return new Menu(null, elementRef, null, null, renderer, null, null, gestureCtrl, dom, app);
552+
let plt = platform === null ? mockPlatform() : platform;
553+
return new Menu(null, elementRef, null, plt, renderer, null, null, gestureCtrl, dom, app);
553554
}
554555

555556
export function mockDeepLinkConfig(links?: any[]): DeepLinkConfig {

0 commit comments

Comments
 (0)