diff --git a/.gitignore b/.gitignore
index 455f8050e..c5d885dad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,7 @@
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
+.history
# misc
/.sass-cache
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..acadd47de
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "cSpell.words": [
+ "sidenav"
+ ]
+}
\ No newline at end of file
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 4e9e807b7..3f799a26d 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -44,7 +44,6 @@ import { RecentFilesComponent } from './components/recent-files/recent-files.com
import { SharedFilesComponent } from './components/shared-files/shared-files.component';
import { TrashcanComponent } from './components/trashcan/trashcan.component';
import { LayoutComponent } from './components/layout/layout.component';
-import { LayoutContainerComponent } from './components/layout/layout-container.component';
import { HeaderComponent } from './components/header/header.component';
import { CurrentUserComponent } from './components/current-user/current-user.component';
import { SearchComponent } from './components/search/search.component';
@@ -66,6 +65,12 @@ import { ContentManagementService } from './common/services/content-management.s
import { NodeActionsService } from './common/services/node-actions.service';
import { MatMenuModule, MatIconModule, MatButtonModule, MatDialogModule, MatInputModule } from '@angular/material';
+import { LayoutContainerComponent } from './components/adf-layout/layout-container.component';
+import { SidenavLayoutComponent } from './components/adf-layout/sidenav-layout.component';
+import { AppLayoutHeaderDirective } from './components/adf-layout/app-layout-header.directive';
+import { AppLayoutNavigationDirective } from './components/adf-layout/app-layout-navigation.directive';
+import { AppLayoutContentDirective } from './components/adf-layout/app-layout-content.directive';
+
@NgModule({
imports: [
BrowserModule,
@@ -90,6 +95,10 @@ import { MatMenuModule, MatIconModule, MatButtonModule, MatDialogModule, MatInpu
LoginComponent,
LayoutComponent,
LayoutContainerComponent,
+ SidenavLayoutComponent,
+ AppLayoutHeaderDirective,
+ AppLayoutNavigationDirective,
+ AppLayoutContentDirective,
HeaderComponent,
CurrentUserComponent,
SearchComponent,
diff --git a/src/app/components/layout/animations.ts b/src/app/components/adf-layout/animations.ts
similarity index 100%
rename from src/app/components/layout/animations.ts
rename to src/app/components/adf-layout/animations.ts
diff --git a/src/app/components/adf-layout/app-layout-content.directive.ts b/src/app/components/adf-layout/app-layout-content.directive.ts
new file mode 100644
index 000000000..3a5f8b235
--- /dev/null
+++ b/src/app/components/adf-layout/app-layout-content.directive.ts
@@ -0,0 +1,31 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { Directive } from '@angular/core';
+
+@Directive({
+ selector: '[appLayoutContent]'
+})
+export class AppLayoutContentDirective {}
diff --git a/src/app/components/adf-layout/app-layout-header.directive.ts b/src/app/components/adf-layout/app-layout-header.directive.ts
new file mode 100644
index 000000000..218a9d637
--- /dev/null
+++ b/src/app/components/adf-layout/app-layout-header.directive.ts
@@ -0,0 +1,31 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { Directive } from '@angular/core';
+
+@Directive({
+ selector: '[appLayoutHeader]'
+})
+export class AppLayoutHeaderDirective {}
diff --git a/src/app/components/adf-layout/app-layout-navigation.directive.ts b/src/app/components/adf-layout/app-layout-navigation.directive.ts
new file mode 100644
index 000000000..dd2ee8b31
--- /dev/null
+++ b/src/app/components/adf-layout/app-layout-navigation.directive.ts
@@ -0,0 +1,31 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { Directive } from '@angular/core';
+
+@Directive({
+ selector: '[appLayoutNavigation]'
+})
+export class AppLayoutNavigationDirective {}
diff --git a/src/app/components/layout/layout-container.component.html b/src/app/components/adf-layout/layout-container.component.html
similarity index 81%
rename from src/app/components/layout/layout-container.component.html
rename to src/app/components/adf-layout/layout-container.component.html
index 7eda7146f..83856bbd3 100644
--- a/src/app/components/layout/layout-container.component.html
+++ b/src/app/components/adf-layout/layout-container.component.html
@@ -2,8 +2,8 @@
+ [opened]="!isMobileScreenSize"
+ [mode]="isMobileScreenSize ? 'over' : 'side'">
diff --git a/src/app/components/layout/layout-container.component.scss b/src/app/components/adf-layout/layout-container.component.scss
similarity index 100%
rename from src/app/components/layout/layout-container.component.scss
rename to src/app/components/adf-layout/layout-container.component.scss
diff --git a/src/app/components/adf-layout/layout-container.component.ts b/src/app/components/adf-layout/layout-container.component.ts
new file mode 100644
index 000000000..e9dec5f7d
--- /dev/null
+++ b/src/app/components/adf-layout/layout-container.component.ts
@@ -0,0 +1,113 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { Component, Input, ViewChild, OnInit, OnDestroy } from '@angular/core';
+import { MatSidenav } from '@angular/material';
+import { sidenavAnimation, contentAnimation } from './animations';
+
+@Component({
+ selector: 'app-layout-container',
+ templateUrl: './layout-container.component.html',
+ styleUrls: ['./layout-container.component.scss'],
+ animations: [ sidenavAnimation, contentAnimation ]
+})
+export class LayoutContainerComponent implements OnInit, OnDestroy {
+ @Input()
+ sidenavMin: number;
+
+ @Input()
+ sidenavMax: number;
+
+ @Input()
+ mediaQueryList: MediaQueryList;
+
+ @Input()
+ hideSidenav = false;
+
+ @ViewChild(MatSidenav) sidenav: MatSidenav;
+
+ sidenavAnimationState: any;
+ contentAnimationState: any;
+
+ SIDENAV_STATES = { EXPANDED: {}, COMPACT: {} };
+ CONTENT_STATES = { MOBILE: {}, EXPANDED: {}, COMPACT: {} };
+
+ constructor() {
+ this.onMediaQueryChange = this.onMediaQueryChange.bind(this);
+ }
+
+ ngOnInit() {
+ 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: { marginLeft: 0 } };
+ this.CONTENT_STATES.EXPANDED = { value: 'expanded', params: { marginLeft: this.sidenavMin } };
+ this.CONTENT_STATES.COMPACT = { value: 'compact', params: { marginLeft: this.sidenavMax } };
+
+ this.mediaQueryList.addListener(this.onMediaQueryChange);
+
+ this.sidenavAnimationState = this.SIDENAV_STATES.EXPANDED;
+ this.contentAnimationState = this.isMobileScreenSize ? this.CONTENT_STATES.MOBILE : this.CONTENT_STATES.COMPACT;
+ }
+
+ ngOnDestroy(): void {
+ this.mediaQueryList.removeListener(this.onMediaQueryChange);
+ }
+
+ toggleMenu(): void {
+ if (this.isMobileScreenSize) {
+ this.sidenav.toggle();
+ } else {
+ this.sidenavAnimationState = this.toggledSidenavAnimation;
+ this.contentAnimationState = this.toggledContentAnimation;
+ }
+ }
+
+ private get toggledSidenavAnimation() {
+ return this.sidenavAnimationState === this.SIDENAV_STATES.EXPANDED
+ ? this.SIDENAV_STATES.COMPACT
+ : this.SIDENAV_STATES.EXPANDED;
+ }
+
+ private get toggledContentAnimation() {
+ if (this.isMobileScreenSize) {
+ return this.CONTENT_STATES.MOBILE;
+ }
+
+ if (this.sidenavAnimationState === this.SIDENAV_STATES.EXPANDED) {
+ return this.CONTENT_STATES.COMPACT;
+ } else {
+ return this.CONTENT_STATES.EXPANDED;
+ }
+ }
+
+ get isMobileScreenSize(): boolean {
+ return this.mediaQueryList.matches;
+ }
+
+ private onMediaQueryChange() {
+ this.sidenavAnimationState = this.SIDENAV_STATES.EXPANDED;
+ this.contentAnimationState = this.toggledContentAnimation;
+ }
+}
diff --git a/src/app/components/adf-layout/sidenav-layout.component.html b/src/app/components/adf-layout/sidenav-layout.component.html
new file mode 100644
index 000000000..b807a65dd
--- /dev/null
+++ b/src/app/components/adf-layout/sidenav-layout.component.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/components/adf-layout/sidenav-layout.component.scss b/src/app/components/adf-layout/sidenav-layout.component.scss
new file mode 100644
index 000000000..5247cb8e7
--- /dev/null
+++ b/src/app/components/adf-layout/sidenav-layout.component.scss
@@ -0,0 +1,12 @@
+:host {
+ display: flex;
+ flex: 1;
+
+ .sidenav-layout {
+ width: 100%;
+ }
+
+ router-outlet {
+ flex: 0 0;
+ }
+}
diff --git a/src/app/components/adf-layout/sidenav-layout.component.spec.ts b/src/app/components/adf-layout/sidenav-layout.component.spec.ts
new file mode 100644
index 000000000..6932ccff8
--- /dev/null
+++ b/src/app/components/adf-layout/sidenav-layout.component.spec.ts
@@ -0,0 +1,269 @@
+/*!
+ * @license
+ * Copyright 2016 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.
+ */
+
+/*tslint:disable: ban*/
+
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { SidenavLayoutComponent } from './sidenav-layout.component';
+import { Component, Input } from '@angular/core';
+import { LayoutModule, MediaMatcher } from '@angular/cdk/layout';
+import { PlatformModule } from '@angular/cdk/platform';
+import { AppLayoutNavigationDirective } from './app-layout-navigation.directive';
+import { AppLayoutHeaderDirective } from './app-layout-header.directive';
+import { AppLayoutContentDirective } from './app-layout-content.directive';
+import { CommonModule } from '@angular/common';
+
+@Component({
+ selector: 'app-layout-container',
+ template: `
+
+ `
+})
+export class DummyLayoutContainerComponent {
+ @Input() sidenavMin: number;
+ @Input() sidenavMax: number;
+ @Input() mediaQueryList: MediaQueryList;
+ @Input() hideSidenav: boolean;
+ toggleMenu () {}
+}
+
+describe('SidenavLayoutComponent', () => {
+
+ let fixture: ComponentFixture,
+ mediaMatcher: MediaMatcher,
+ mediaQueryList: any;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ CommonModule,
+ PlatformModule,
+ LayoutModule
+ ],
+ declarations: [
+ DummyLayoutContainerComponent,
+ SidenavLayoutComponent,
+ AppLayoutNavigationDirective,
+ AppLayoutHeaderDirective,
+ AppLayoutContentDirective
+ ],
+ providers: [
+ MediaMatcher
+ ]
+ });
+ }));
+
+ beforeEach(() => {
+ mediaQueryList = {
+ matches: false,
+ addListener: () => {},
+ removeListener: () => {}
+ };
+ });
+
+ afterEach(() => {
+ fixture.destroy();
+ TestBed.resetTestingModule();
+ });
+
+ describe('Template transclusion', () => {
+
+ @Component({
+ selector: 'app-test-component-for-sidenav',
+ template: `
+
+
+
+
+
+
+
+ {{ isMenuMinimized !== undefined ? 'variable-is-injected' : 'variable-is-not-injected' }}
+
+
+
+
+
+ `
+ })
+ class SidenavLayoutTesterComponent {}
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({ declarations: [ SidenavLayoutTesterComponent ] }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ mediaMatcher = TestBed.get(MediaMatcher);
+ spyOn(mediaMatcher, 'matchMedia').and.returnValue(mediaQueryList);
+
+ fixture = TestBed.createComponent(SidenavLayoutTesterComponent);
+ fixture.detectChanges();
+ });
+
+ describe('appLayoutNavigation', () => {
+
+ const injectedElementSelector = By.css('[data-automation-id="adf-layout-container"] #nav-test');
+
+ it('should contain the transcluded side navigation template', () => {
+ const injectedElement = fixture.debugElement.query(injectedElementSelector);
+
+ expect(injectedElement === null).toBe(false);
+ });
+
+ it('should let the isMenuMinimized property of component to be accessed by the transcluded template', () => {
+ const injectedElement = fixture.debugElement.query(injectedElementSelector);
+
+ expect(injectedElement.nativeElement.innerText.trim()).toBe('variable-is-injected');
+ });
+ });
+
+ describe('appLayoutHeader', () => {
+
+ const outerHeaderSelector = By.css('.sidenav-layout > #header-test'),
+ innerHeaderSelector = By.css('.sidenav-layout [data-automation-id="adf-layout-container"] #header-test');
+
+ it('should contain the transcluded header template outside of the layout-container', () => {
+ mediaQueryList.matches = false;
+ fixture.detectChanges();
+ const outerHeaderElement = fixture.debugElement.query(outerHeaderSelector);
+ const innerHeaderElement = fixture.debugElement.query(innerHeaderSelector);
+
+ expect(outerHeaderElement === null).toBe(false, 'Outer header should be shown');
+ expect(innerHeaderElement === null).toBe(true, 'Inner header should not be shown');
+ });
+
+ it('should contain the transcluded header template inside of the layout-container', () => {
+ mediaQueryList.matches = true;
+ fixture.detectChanges();
+ const outerHeaderElement = fixture.debugElement.query(outerHeaderSelector);
+ const innerHeaderElement = fixture.debugElement.query(innerHeaderSelector);
+
+ expect(outerHeaderElement === null).toBe(true, 'Outer header should not be shown');
+ expect(innerHeaderElement === null).toBe(false, 'Inner header should be shown');
+ });
+
+ it('should call through the layout container\'s toggleMenu method', () => {
+ mediaQueryList.matches = false;
+ fixture.detectChanges();
+ const layoutContainerComponent = fixture.debugElement.query(By.directive(DummyLayoutContainerComponent)).componentInstance;
+ spyOn(layoutContainerComponent, 'toggleMenu');
+
+ const outerHeaderElement = fixture.debugElement.query(outerHeaderSelector);
+ outerHeaderElement.triggerEventHandler('click', {});
+
+ expect(layoutContainerComponent.toggleMenu).toHaveBeenCalled();
+ });
+ });
+
+ describe('appLayoutContent', () => {
+
+ const injectedElementSelector = By.css('[data-automation-id="adf-layout-container"] #content-test');
+
+ it('should contain the transcluded content template', () => {
+ const injectedElement = fixture.debugElement.query(injectedElementSelector);
+
+ expect(injectedElement === null).toBe(false);
+ });
+ });
+ });
+
+ describe('General behaviour', () => {
+
+ let component: SidenavLayoutComponent;
+
+ beforeEach(async(() => {
+ TestBed.compileComponents();
+ }));
+
+ beforeEach(() => {
+ mediaMatcher = TestBed.get(MediaMatcher);
+ spyOn(mediaMatcher, 'matchMedia').and.callFake((mediaQuery) => {
+ mediaQueryList.originalMediaQueryPassed = mediaQuery;
+ spyOn(mediaQueryList, 'addListener').and.stub();
+ spyOn(mediaQueryList, 'removeListener').and.stub();
+ return mediaQueryList;
+ });
+
+ fixture = TestBed.createComponent(SidenavLayoutComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should pass through input parameters', () => {
+ component.sidenavMin = 1;
+ component.sidenavMax = 2;
+ component.hideSidenav = true;
+ fixture.detectChanges();
+
+ const layoutContainerComponent = fixture.debugElement.query(By.directive(DummyLayoutContainerComponent)).componentInstance;
+
+ expect(layoutContainerComponent.sidenavMin).toBe(component.sidenavMin);
+ expect(layoutContainerComponent.sidenavMax).toBe(component.sidenavMax);
+ expect(layoutContainerComponent.hideSidenav).toBe(component.hideSidenav);
+ expect(layoutContainerComponent.mediaQueryList.originalMediaQueryPassed).toBe(`(max-width: 600px)`);
+ });
+
+ it('addListener of mediaQueryList should have been called', () => {
+ expect(mediaQueryList.addListener).toHaveBeenCalledTimes(1);
+ expect(mediaQueryList.addListener).toHaveBeenCalledWith(component.onMediaQueryChange);
+ });
+
+ it('addListener of mediaQueryList should have been called', () => {
+ fixture.destroy();
+
+ expect(mediaQueryList.removeListener).toHaveBeenCalledTimes(1);
+ expect(mediaQueryList.removeListener).toHaveBeenCalledWith(component.onMediaQueryChange);
+ });
+ });
+
+ describe('toggleMenu', () => {
+
+ let component;
+
+ beforeEach(async(() => {
+ TestBed.compileComponents();
+ }));
+
+ beforeEach(() => {
+ mediaMatcher = TestBed.get(MediaMatcher);
+ spyOn(mediaMatcher, 'matchMedia').and.returnValue(mediaQueryList);
+
+ fixture = TestBed.createComponent(SidenavLayoutComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should toggle the isMenuMinimized if the mediaQueryList.matches is false (we are on desktop)', () => {
+ mediaQueryList.matches = false;
+ component.isMenuMinimized = false;
+
+ component.toggleMenu();
+
+ expect(component.isMenuMinimized).toBe(true);
+ });
+
+ it('should set the isMenuMinimized to false if the mediaQueryList.matches is true (we are on mobile)', () => {
+ mediaQueryList.matches = true;
+ component.isMenuMinimized = true;
+
+ component.toggleMenu();
+
+ expect(component.isMenuMinimized).toBe(false);
+ });
+ });
+});
diff --git a/src/app/components/adf-layout/sidenav-layout.component.ts b/src/app/components/adf-layout/sidenav-layout.component.ts
new file mode 100644
index 000000000..fba62e4f5
--- /dev/null
+++ b/src/app/components/adf-layout/sidenav-layout.component.ts
@@ -0,0 +1,107 @@
+/*!
+ * @license
+ * Alfresco Example Content Application
+ *
+ * Copyright (C) 2005 - 2018 Alfresco Software Limited
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+import { Component, ContentChild, Input, OnInit, TemplateRef, AfterViewInit, ViewChild, OnDestroy } from '@angular/core';
+import { MediaMatcher } from '@angular/cdk/layout';
+import { AppLayoutHeaderDirective } from './app-layout-header.directive';
+import { AppLayoutNavigationDirective } from './app-layout-navigation.directive';
+import { AppLayoutContentDirective } from './app-layout-content.directive';
+
+@Component({
+ selector: 'app-sidenav-layout',
+ templateUrl: './sidenav-layout.component.html',
+ styleUrls: ['./sidenav-layout.component.scss']
+})
+export class SidenavLayoutComponent implements OnInit, AfterViewInit, OnDestroy {
+
+ static STEP_OVER = 600;
+
+ @Input()
+ sidenavMin: number;
+
+ @Input()
+ sidenavMax: number;
+
+ @Input()
+ stepOver: number;
+
+ @Input()
+ hideSidenav = false;
+
+ @ContentChild(AppLayoutHeaderDirective, {read: TemplateRef})
+ headerTemplate: TemplateRef;
+
+ @ContentChild(AppLayoutNavigationDirective, {read: TemplateRef})
+ navigationTemplate: TemplateRef;
+
+ @ContentChild(AppLayoutContentDirective, {read: TemplateRef})
+ contentTemplate: TemplateRef;
+
+ @ViewChild('container')
+ container: any;
+
+ mediaQueryList: MediaQueryList;
+ isMenuMinimized = false;
+ templateContext = {
+ toggleMenu: () => {},
+ isMenuMinimized: () => this.isMenuMinimized
+ };
+
+ constructor(private mediaMatcher: MediaMatcher) {
+ this.onMediaQueryChange = this.onMediaQueryChange.bind(this);
+ }
+
+ ngOnInit() {
+ const stepOver = this.stepOver || SidenavLayoutComponent.STEP_OVER;
+ this.mediaQueryList = this.mediaMatcher.matchMedia(`(max-width: ${stepOver}px)`);
+ this.mediaQueryList.addListener(this.onMediaQueryChange);
+ }
+
+ ngAfterViewInit() {
+ this.templateContext.toggleMenu = this.toggleMenu.bind(this);
+ }
+
+ ngOnDestroy(): void {
+ this.mediaQueryList.removeListener(this.onMediaQueryChange);
+ }
+
+ toggleMenu() {
+ if (!this.mediaQueryList.matches) {
+ this.isMenuMinimized = !this.isMenuMinimized;
+ } else {
+ this.isMenuMinimized = false;
+ }
+
+ this.container.toggleMenu();
+ }
+
+ get isHeaderInside() {
+ return this.mediaQueryList.matches;
+ }
+
+ onMediaQueryChange() {
+ this.isMenuMinimized = false;
+ }
+}
diff --git a/src/app/components/layout/layout-container.component.ts b/src/app/components/layout/layout-container.component.ts
deleted file mode 100644
index 4a696e940..000000000
--- a/src/app/components/layout/layout-container.component.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-/*!
- * @license
- * Alfresco Example Content Application
- *
- * Copyright (C) 2005 - 2018 Alfresco Software Limited
- *
- * This file is part of the Alfresco Example Content Application.
- * If the software was purchased under a paid Alfresco license, the terms of
- * the paid license agreement will prevail. Otherwise, the software is
- * provided under the following open source license terms:
- *
- * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The Alfresco Example Content Application is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- */
-
-import { Component, Input, ViewChild, OnInit, OnDestroy } from '@angular/core';
-import { MatSidenav } from '@angular/material';
-import {MediaMatcher} from '@angular/cdk/layout';
-import { sidenavAnimation, contentAnimation } from './animations';
-
-@Component({
- selector: 'app-layout-container',
- templateUrl: './layout-container.component.html',
- styleUrls: ['./layout-container.component.scss'],
- animations: [ sidenavAnimation, contentAnimation ]
-})
-export class LayoutContainerComponent implements OnInit, OnDestroy {
- static STEP_OVER = 600;
-
- @Input() sidenavMin: number;
- @Input() sidenavMax: number;
- @Input() stepOver: number;
- @Input() hideSidenav: boolean = false;
-
- @ViewChild(MatSidenav) sidenav: MatSidenav;
-
- sidenavAnimationState: any;
- contentAnimationState: any;
- isMenuMinimized = false;
- mobileQuery: MediaQueryList;
-
- constructor(private mediaMatcher: MediaMatcher) {
- this.mobileQueryListener = this.mobileQueryListener.bind(this);
- }
-
- ngOnInit() {
- const stepOver = this.stepOver || LayoutContainerComponent.STEP_OVER;
-
- this.mobileQuery = this.mediaMatcher.matchMedia(`(max-width: ${stepOver}px)`);
- this.mobileQuery.addListener(this.mobileQueryListener);
- this.sidenavAnimationState = { value: 'expanded', params: { width: this.sidenavMax } };
- this.contentAnimationState = { value: 'compact', params: {marginLeft: this.sidenavMax } };
- }
-
- ngOnDestroy(): void {
- this.mobileQuery.removeListener(this.mobileQueryListener);
- }
-
- toggleMenu(): void {
- if (!this.mobileQuery.matches) {
- this.isMenuMinimized = !this.isMenuMinimized;
- this.sidenavAnimationState = this.sidenavAnimation();
- this.contentAnimationState = this.contentAnimation();
- } else {
- this.isMenuMinimized = false;
- this.sidenav.toggle();
- }
- }
-
- sidenavAnimation() {
- return this.sidenavAnimationState.value === 'expanded'
- ? { value: 'compact', params: {width: this.sidenavMin } }
- : { value: 'expanded', params: { width: this.sidenavMax } };
- }
-
- contentAnimation() {
- if (this.mobileQuery.matches) {
- return { value: 'expanded', params: { marginLeft: 0 } };
- }
-
- if (this.sidenavAnimationState.value === 'expanded') {
- return { value: 'compact', params: { marginLeft: this.sidenavMax } };
- }
-
- if (this.sidenavAnimationState.value === 'compact') {
- return { value: 'expanded', params: { marginLeft: this.sidenavMin } };
- }
- }
-
- private mobileQueryListener() {
- this.isMenuMinimized = false;
- this.sidenavAnimationState = { value: 'expanded', params: { width: this.sidenavMax } };
- this.contentAnimationState = this.contentAnimation();
- }
-}
diff --git a/src/app/components/layout/layout.component.html b/src/app/components/layout/layout.component.html
index b62133652..b3d7d2399 100644
--- a/src/app/components/layout/layout.component.html
+++ b/src/app/components/layout/layout.component.html
@@ -3,22 +3,24 @@
[parentId]="node?.id"
[disabled]="!canCreateContent(node)">
-
-
-
+ [hideSidenav]="isPreview">
-
-
+
+
+
-
-
+
+
+
+
+
+
+
+
diff --git a/src/app/components/layout/layout.component.scss b/src/app/components/layout/layout.component.scss
index c3aa6941d..bfe4ce972 100644
--- a/src/app/components/layout/layout.component.scss
+++ b/src/app/components/layout/layout.component.scss
@@ -1,8 +1,4 @@
:host {
display: flex;
flex: 1;
-
- router-outlet {
- flex: 0 0;
- }
}
diff --git a/src/app/components/layout/layout.component.ts b/src/app/components/layout/layout.component.ts
index a098e2d0e..411ec04f4 100644
--- a/src/app/components/layout/layout.component.ts
+++ b/src/app/components/layout/layout.component.ts
@@ -37,7 +37,7 @@ import { BrowsingFilesService } from '../../common/services/browsing-files.servi
})
export class LayoutComponent implements OnInit, OnDestroy {
node: MinimalNodeEntryEntity;
- isPreview: boolean = false;
+ isPreview = false;
private subscriptions: Subscription[] = [];