mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-4579] Layout - RTL support (#4741)
* single contentAnimation state * fine tune contentAnimation state based on direction and position * tets * fix mock component inputs * use native direction attribute * update docs * pass direction to calculate sidenav layout * update test * fix unit test * remove dialogs directionality workaround * set document directionality service * remove context menu direction workaround * remove unneeded dependencies * remove direction style workaround * remove permission icon direction * remove sidenav layout direction attribute * update docs * update sidenav layout direction * test * remove document type * update test dependencies * clear storage before test * test * fix dl gap * try to fix Uncaught QuotaExceededError * fix QuotaExceededError * fix tests * fix tests
This commit is contained in:
committed by
Eugenio Romano
parent
f3e90298e6
commit
9cf6f5519c
@@ -10,7 +10,7 @@
|
||||
</mat-sidenav>
|
||||
|
||||
<div>
|
||||
<div class="adf-rtl-container-alignment adf-container-full-width" [@contentAnimationLeft]="getContentAnimationStateLeft()" [@contentAnimationRight]="getContentAnimationStateRight()">
|
||||
<div class="adf-container-full-width" [@contentAnimationLeft]="getContentAnimationState()">
|
||||
<ng-content select="[app-layout-content]"></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -9,14 +9,9 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[dir='rtl'] .adf-rtl-container-alignment {
|
||||
margin-left: 10px!important;
|
||||
}
|
||||
|
||||
.adf-container-full-width {
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
/* query for Microsoft IE 11*/
|
||||
@media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: none) {
|
||||
.adf-container-full-width {
|
||||
@@ -24,6 +19,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
mat-sidenav-content.mat-drawer-content.mat-sidenav-content {
|
||||
margin: 0!important;
|
||||
}
|
||||
|
||||
.adf-sidenav--hidden {
|
||||
visibility: hidden !important;
|
||||
width: 0 !important;
|
||||
|
@@ -0,0 +1,210 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { LayoutContainerComponent } from './layout-container.component';
|
||||
import { SimpleChange } from '@angular/core';
|
||||
|
||||
describe('LayoutContainerComponent', () => {
|
||||
let layoutContainerComponent: LayoutContainerComponent;
|
||||
|
||||
beforeEach(() => {
|
||||
layoutContainerComponent = new LayoutContainerComponent();
|
||||
layoutContainerComponent.sidenavMin = 70;
|
||||
layoutContainerComponent.sidenavMax = 200;
|
||||
layoutContainerComponent.mediaQueryList = {
|
||||
matches: false,
|
||||
addListener: () => {},
|
||||
removeListener: () => {}
|
||||
};
|
||||
});
|
||||
|
||||
describe('OnInit', () => {
|
||||
describe('Sidenav expanded', () => {
|
||||
beforeEach(() => {
|
||||
layoutContainerComponent.expandedSidenav = true;
|
||||
});
|
||||
|
||||
describe('Sidenav [start] position', () => {
|
||||
beforeEach(() => {
|
||||
layoutContainerComponent.position = 'start';
|
||||
});
|
||||
|
||||
it('should set `margin-left` equal to sidenavMax when direction is `ltr`', () => {
|
||||
layoutContainerComponent.direction = 'ltr';
|
||||
layoutContainerComponent.ngOnInit();
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState).toEqual({
|
||||
value: 'compact', params: { 'margin-left': layoutContainerComponent.sidenavMax}
|
||||
});
|
||||
});
|
||||
|
||||
it('should set `margin-right` equal to sidenavMax when direction is `rtl`', () => {
|
||||
layoutContainerComponent.direction = 'rtl';
|
||||
layoutContainerComponent.ngOnInit();
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState).toEqual({
|
||||
value: 'compact', params: { 'margin-right': layoutContainerComponent.sidenavMax}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sidenav [end] position', () => {
|
||||
beforeEach(() => {
|
||||
layoutContainerComponent.position = 'end';
|
||||
});
|
||||
|
||||
it('should set `margin-right` equal to sidenavMax when direction is `ltr`', () => {
|
||||
layoutContainerComponent.direction = 'ltr';
|
||||
layoutContainerComponent.ngOnInit();
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState).toEqual({
|
||||
value: 'compact', params: { 'margin-right': layoutContainerComponent.sidenavMax}
|
||||
});
|
||||
});
|
||||
|
||||
it('should set `margin-left` equal to sidenavMax when direction is `rtl`', () => {
|
||||
layoutContainerComponent.direction = 'rtl';
|
||||
layoutContainerComponent.ngOnInit();
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState).toEqual({
|
||||
value: 'compact', params: { 'margin-left': layoutContainerComponent.sidenavMax}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sidenav compact', () => {
|
||||
beforeEach(() => {
|
||||
layoutContainerComponent.expandedSidenav = false;
|
||||
});
|
||||
|
||||
describe('Sidenav [start] position', () => {
|
||||
beforeEach(() => {
|
||||
layoutContainerComponent.position = 'start';
|
||||
});
|
||||
|
||||
it('should set `margin-left` equal to sidenavMin when direction is `ltr`', () => {
|
||||
layoutContainerComponent.direction = 'ltr';
|
||||
layoutContainerComponent.ngOnInit();
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState).toEqual({
|
||||
value: 'expanded', params: { 'margin-left': layoutContainerComponent.sidenavMin}
|
||||
});
|
||||
});
|
||||
|
||||
it('should set `margin-right` equal to sidenavMin when direction is `rtl`', () => {
|
||||
layoutContainerComponent.direction = 'rtl';
|
||||
layoutContainerComponent.ngOnInit();
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState).toEqual({
|
||||
value: 'expanded', params: { 'margin-right': layoutContainerComponent.sidenavMin}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sidenav [end] position', () => {
|
||||
beforeEach(() => {
|
||||
layoutContainerComponent.position = 'end';
|
||||
});
|
||||
|
||||
it('should set `margin-right` equal to sidenavMin when direction is `ltr`', () => {
|
||||
layoutContainerComponent.direction = 'ltr';
|
||||
layoutContainerComponent.ngOnInit();
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState).toEqual({
|
||||
value: 'expanded', params: { 'margin-right': layoutContainerComponent.sidenavMin}
|
||||
});
|
||||
});
|
||||
|
||||
it('should set `margin-left` equal to sidenavMin when direction is `rtl`', () => {
|
||||
layoutContainerComponent.direction = 'rtl';
|
||||
layoutContainerComponent.ngOnInit();
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState).toEqual({
|
||||
value: 'expanded', params: { 'margin-left': layoutContainerComponent.sidenavMin}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('OnChange direction', () => {
|
||||
describe('Sidenav [start] position', () => {
|
||||
beforeEach(() => {
|
||||
layoutContainerComponent.position = 'start';
|
||||
});
|
||||
|
||||
it('should set `margin-left` when current direction is `ltr`', () => {
|
||||
layoutContainerComponent.direction = 'ltr';
|
||||
layoutContainerComponent.ngOnChanges({ direction: new SimpleChange('', '', false) });
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState.params['margin-left']).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should set `margin-right` when current direction is `rtl`', () => {
|
||||
layoutContainerComponent.direction = 'rtl';
|
||||
layoutContainerComponent.ngOnChanges({ direction: new SimpleChange('', '', false) });
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState.params['margin-right']).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sidenav [end] position', () => {
|
||||
beforeEach(() => {
|
||||
layoutContainerComponent.position = 'end';
|
||||
});
|
||||
|
||||
it('should set `margin-right` when current direction is `ltr`', () => {
|
||||
layoutContainerComponent.direction = 'ltr';
|
||||
layoutContainerComponent.ngOnChanges({ direction: new SimpleChange('', '', false) });
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState.params['margin-right']).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should set `margin-left` when current direction is `rtl`', () => {
|
||||
layoutContainerComponent.direction = 'rtl';
|
||||
layoutContainerComponent.ngOnChanges({ direction: new SimpleChange('', '', false) });
|
||||
|
||||
expect(layoutContainerComponent.contentAnimationState.params['margin-left']).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toggleMenu()', () => {
|
||||
it('should switch to sidenav to compact state', () => {
|
||||
layoutContainerComponent.expandedSidenav = true;
|
||||
layoutContainerComponent.ngOnInit();
|
||||
|
||||
layoutContainerComponent.toggleMenu();
|
||||
|
||||
expect(layoutContainerComponent.sidenavAnimationState).toEqual({
|
||||
value: 'compact', params: { width: layoutContainerComponent.sidenavMin }
|
||||
});
|
||||
});
|
||||
|
||||
it('should switch to sidenav to expanded state', () => {
|
||||
layoutContainerComponent.expandedSidenav = false;
|
||||
layoutContainerComponent.ngOnInit();
|
||||
|
||||
layoutContainerComponent.toggleMenu();
|
||||
|
||||
expect(layoutContainerComponent.sidenavAnimationState).toEqual({
|
||||
value: 'expanded', params: { width: layoutContainerComponent.sidenavMax }
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -15,18 +15,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Input, ViewChild, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core';
|
||||
import { Component, Input, ViewChild, OnInit, OnDestroy, ViewEncapsulation, OnChanges } from '@angular/core';
|
||||
import { MatSidenav } from '@angular/material';
|
||||
import { sidenavAnimation, contentAnimationLeft, contentAnimationRight } from '../../helpers/animations';
|
||||
import { sidenavAnimation, contentAnimation } from '../../helpers/animations';
|
||||
import { Direction } from '@angular/cdk/bidi';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-layout-container',
|
||||
templateUrl: './layout-container.component.html',
|
||||
styleUrls: ['./layout-container.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
animations: [sidenavAnimation, contentAnimationLeft, contentAnimationRight]
|
||||
animations: [sidenavAnimation, contentAnimation]
|
||||
})
|
||||
export class LayoutContainerComponent implements OnInit, OnDestroy {
|
||||
export class LayoutContainerComponent implements OnInit, OnDestroy, OnChanges {
|
||||
@Input() sidenavMin: number;
|
||||
@Input() sidenavMax: number;
|
||||
|
||||
@@ -39,6 +40,9 @@ export class LayoutContainerComponent implements OnInit, OnDestroy {
|
||||
/** The side that the drawer is attached to 'start' | 'end' page */
|
||||
@Input() position = 'start';
|
||||
|
||||
/** Layout text orientation 'ltr' | 'rtl' */
|
||||
@Input() direction: Direction = 'ltr';
|
||||
|
||||
@ViewChild(MatSidenav) sidenav: MatSidenav;
|
||||
|
||||
sidenavAnimationState: any;
|
||||
@@ -56,9 +60,7 @@ export class LayoutContainerComponent implements OnInit, OnDestroy {
|
||||
this.SIDENAV_STATES.EXPANDED = { value: 'expanded', params: { width: this.sidenavMax } };
|
||||
this.SIDENAV_STATES.COMPACT = { value: 'compact', params: { width: this.sidenavMin } };
|
||||
|
||||
this.CONTENT_STATES.MOBILE = { value: 'expanded', params: { margin: 0 } };
|
||||
this.CONTENT_STATES.EXPANDED = { value: 'expanded', params: { margin: this.sidenavMin } };
|
||||
this.CONTENT_STATES.COMPACT = { value: 'compact', params: { margin: this.sidenavMax } };
|
||||
this.CONTENT_STATES.MOBILE = { value: 'expanded' };
|
||||
|
||||
this.mediaQueryList.addListener(this.onMediaQueryChange);
|
||||
|
||||
@@ -67,10 +69,10 @@ export class LayoutContainerComponent implements OnInit, OnDestroy {
|
||||
this.contentAnimationState = this.CONTENT_STATES.MOBILE;
|
||||
} else if (this.expandedSidenav) {
|
||||
this.sidenavAnimationState = this.SIDENAV_STATES.EXPANDED;
|
||||
this.contentAnimationState = this.CONTENT_STATES.COMPACT;
|
||||
this.contentAnimationState = this.toggledContentAnimation;
|
||||
} else {
|
||||
this.sidenavAnimationState = this.SIDENAV_STATES.COMPACT;
|
||||
this.contentAnimationState = this.CONTENT_STATES.EXPANDED;
|
||||
this.contentAnimationState = this.toggledContentAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +80,12 @@ export class LayoutContainerComponent implements OnInit, OnDestroy {
|
||||
this.mediaQueryList.removeListener(this.onMediaQueryChange);
|
||||
}
|
||||
|
||||
ngOnChanges(changes) {
|
||||
if (changes && changes.direction) {
|
||||
this.contentAnimationState = this.toggledContentAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
toggleMenu(): void {
|
||||
if (this.isMobileScreenSize) {
|
||||
this.sidenav.toggle();
|
||||
@@ -87,6 +95,14 @@ export class LayoutContainerComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
get isMobileScreenSize(): boolean {
|
||||
return this.mediaQueryList.matches;
|
||||
}
|
||||
|
||||
getContentAnimationState() {
|
||||
return this.contentAnimationState;
|
||||
}
|
||||
|
||||
private get toggledSidenavAnimation() {
|
||||
return this.sidenavAnimationState === this.SIDENAV_STATES.EXPANDED
|
||||
? this.SIDENAV_STATES.COMPACT
|
||||
@@ -99,35 +115,43 @@ export class LayoutContainerComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
if (this.sidenavAnimationState === this.SIDENAV_STATES.EXPANDED) {
|
||||
return this.CONTENT_STATES.COMPACT;
|
||||
} else {
|
||||
return this.CONTENT_STATES.EXPANDED;
|
||||
}
|
||||
}
|
||||
if (this.position === 'start' && this.direction === 'ltr') {
|
||||
return { value: 'compact', params: { 'margin-left': this.sidenavMax } };
|
||||
}
|
||||
|
||||
get isMobileScreenSize(): boolean {
|
||||
return this.mediaQueryList.matches;
|
||||
if (this.position === 'start' && this.direction === 'rtl') {
|
||||
return { value: 'compact', params: { 'margin-right': this.sidenavMax } };
|
||||
}
|
||||
|
||||
if (this.position === 'end' && this.direction === 'ltr') {
|
||||
return { value: 'compact', params: { 'margin-right': this.sidenavMax } };
|
||||
}
|
||||
|
||||
if (this.position === 'end' && this.direction === 'rtl') {
|
||||
return { value: 'compact', params: { 'margin-left': this.sidenavMax } };
|
||||
}
|
||||
|
||||
} else {
|
||||
if (this.position === 'start' && this.direction === 'ltr') {
|
||||
return { value: 'expanded', params: { 'margin-left': this.sidenavMin } };
|
||||
}
|
||||
|
||||
if (this.position === 'start' && this.direction === 'rtl') {
|
||||
return { value: 'expanded', params: { 'margin-right': this.sidenavMin } };
|
||||
}
|
||||
|
||||
if (this.position === 'end' && this.direction === 'ltr') {
|
||||
return { value: 'expanded', params: { 'margin-right': this.sidenavMin } };
|
||||
}
|
||||
|
||||
if (this.position === 'end' && this.direction === 'rtl') {
|
||||
return { value: 'expanded', params: { 'margin-left': this.sidenavMin } };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onMediaQueryChange() {
|
||||
this.sidenavAnimationState = this.SIDENAV_STATES.EXPANDED;
|
||||
this.contentAnimationState = this.toggledContentAnimation;
|
||||
}
|
||||
|
||||
getContentAnimationStateLeft() {
|
||||
if (this.position === 'start') {
|
||||
return this.contentAnimationState;
|
||||
} else {
|
||||
return { value: 'compact', params: { width: this.sidenavMin } };
|
||||
}
|
||||
}
|
||||
|
||||
getContentAnimationStateRight() {
|
||||
if (this.position === 'end') {
|
||||
return this.contentAnimationState;
|
||||
|
||||
} else {
|
||||
return { value: 'compact', params: { width: this.sidenavMin } };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,11 @@
|
||||
<div [dir]="direction" class="adf-sidenav-layout-full-space">
|
||||
<div class="adf-sidenav-layout-full-space">
|
||||
<ng-container *ngIf="!isHeaderInside">
|
||||
<ng-container class="adf-sidenav-layout-outer-header"
|
||||
*ngTemplateOutlet="headerTemplate; context:templateContext"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<adf-layout-container #container
|
||||
[direction]="dir"
|
||||
[position]="position"
|
||||
[sidenavMin]="sidenavMin"
|
||||
[sidenavMax]="sidenavMax"
|
||||
|
@@ -27,7 +27,10 @@ import { MaterialModule } from '../../../material.module';
|
||||
import { SidenavLayoutContentDirective } from '../../directives/sidenav-layout-content.directive';
|
||||
import { SidenavLayoutHeaderDirective } from '../../directives/sidenav-layout-header.directive';
|
||||
import { SidenavLayoutNavigationDirective } from '../../directives/sidenav-layout-navigation.directive';
|
||||
import { UserPreferencesService } from '../../../services/user-preferences.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Direction } from '@angular/cdk/bidi';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-layout-container',
|
||||
@@ -39,6 +42,7 @@ export class DummyLayoutContainerComponent {
|
||||
@Input() sidenavMin: number;
|
||||
@Input() sidenavMax: number;
|
||||
@Input() position: string;
|
||||
@Input() direction: Direction;
|
||||
@Input() mediaQueryList: MediaQueryList;
|
||||
@Input() hideSidenav: boolean;
|
||||
@Input() expandedSidenav: boolean;
|
||||
@@ -67,7 +71,8 @@ describe('SidenavLayoutComponent', () => {
|
||||
SidenavLayoutNavigationDirective
|
||||
],
|
||||
providers: [
|
||||
MediaMatcher
|
||||
MediaMatcher,
|
||||
{ provide: UserPreferencesService, useValue: { select: () => of()} }
|
||||
]
|
||||
});
|
||||
}));
|
||||
|
@@ -29,10 +29,13 @@ import {
|
||||
ViewEncapsulation
|
||||
} from '@angular/core';
|
||||
import { MediaMatcher } from '@angular/cdk/layout';
|
||||
import { UserPreferencesService } from '../../../services/user-preferences.service';
|
||||
import { SidenavLayoutContentDirective } from '../../directives/sidenav-layout-content.directive';
|
||||
import { SidenavLayoutHeaderDirective } from '../../directives/sidenav-layout-header.directive';
|
||||
import { SidenavLayoutNavigationDirective } from '../../directives/sidenav-layout-navigation.directive';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import { Direction } from '@angular/cdk/bidi';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-sidenav-layout',
|
||||
@@ -45,7 +48,7 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
static STEP_OVER = 600;
|
||||
|
||||
/** The direction of the layout. 'ltr' or 'rtl' */
|
||||
@Input() direction = 'ltr';
|
||||
dir = 'ltr';
|
||||
|
||||
/** The side that the drawer is attached to. Possible values are 'start' and 'end'. */
|
||||
@Input() position = 'start';
|
||||
@@ -86,7 +89,9 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
isMenuMinimized: () => this.isMenuMinimized
|
||||
};
|
||||
|
||||
constructor(private mediaMatcher: MediaMatcher) {
|
||||
private onDestroy$ = new Subject<boolean>();
|
||||
|
||||
constructor(private mediaMatcher: MediaMatcher, private userPreferencesService: UserPreferencesService ) {
|
||||
this.onMediaQueryChange = this.onMediaQueryChange.bind(this);
|
||||
}
|
||||
|
||||
@@ -101,6 +106,13 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
|
||||
this.mediaQueryList = this.mediaMatcher.matchMedia(`(max-width: ${stepOver}px)`);
|
||||
this.mediaQueryList.addListener(this.onMediaQueryChange);
|
||||
|
||||
this.userPreferencesService
|
||||
.select('textOrientation')
|
||||
.pipe(takeUntil(this.onDestroy$))
|
||||
.subscribe((direction: Direction) => {
|
||||
this.dir = direction;
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
@@ -109,6 +121,8 @@ export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.mediaQueryList.removeListener(this.onMediaQueryChange);
|
||||
this.onDestroy$.next(true);
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
|
||||
toggleMenu() {
|
||||
|
Reference in New Issue
Block a user