fixed layout service + some auth pages + simple page layouts + mail tweaks

This commit is contained in:
Sercan Yemen 2017-07-27 18:05:50 +03:00
parent 6237b1132a
commit d44a76b8c6
57 changed files with 1621 additions and 190 deletions

View File

@ -5,7 +5,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';
import { RouterModule, Routes } from '@angular/router';
import 'hammerjs';
import { ChatModule } from './main/apps/chat/chat.module';
import { ProjectModule } from './main/apps/dashboards/project/project.module';
import { FuseLayoutService } from './core/services/layout.service';
import { FuseNavigationService } from './core/components/navigation/navigation.service';
@ -14,13 +13,13 @@ import { FuseMatchMedia } from './core/services/match-media.service';
import { FuseNavbarService } from './core/components/layout/navbar/navbar.service';
import { SharedModule } from './core/modules/shared.module';
import { FuseMdSidenavHelperService } from './core/directives/md-sidenav-helper/md-sidenav-helper.service';
import { UIPageLayoutsModule } from './main/ui/page-layouts/page-layouts.module';
import { FuseLayoutModule } from './core/components/layout/layout.module';
import { PerfectScrollbarConfigInterface, PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { HttpModule } from '@angular/http';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { FuseFakeDbService } from './fuse-fake-db/fuse-fake-db.service';
import { INTERNAL_BROWSER_DYNAMIC_PLATFORM_PROVIDERS } from '@angular/platform-browser-dynamic/src/platform_providers';
import { PagesModule } from './main/pages/pages.module';
import { UIModule } from './main/ui/ui.module';
const PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = {
suppressScrollX: true
@ -66,7 +65,8 @@ const appRoutes: Routes = [
ProjectModule,
UIPageLayoutsModule
PagesModule,
UIModule
],
providers : [
FuseNavigationService,

View File

@ -6,8 +6,7 @@
<div id="wrapper">
<!-- NAVIGATION: None -->
<ng-container *ngIf="layoutSettings.navigation === 'none'">
<fuse-navbar *ngIf="layoutSettings.navigation === 'left'"></fuse-navbar>
<div class="content-wrapper">
@ -27,60 +26,7 @@
</div>
</ng-container>
<!-- / NAVIGATION: None -->
<!-- NAVIGATION: Left -->
<ng-container *ngIf="layoutSettings.navigation === 'left'">
<fuse-navbar></fuse-navbar>
<div class="content-wrapper">
<!-- TOOLBAR: Below -->
<ng-container *ngIf="layoutSettings.toolbar === 'below'">
<fuse-toolbar class="below"></fuse-toolbar>
</ng-container>
<!-- / TOOLBAR: Below -->
<fuse-content perfect-scrollbar></fuse-content>
<!-- FOOTER: Below -->
<ng-container *ngIf="layoutSettings.footer === 'below'">
<fuse-footer class="below"></fuse-footer>
</ng-container>
<!-- / FOOTER: Below -->
</div>
</ng-container>
<!-- / NAVIGATION: Left -->
<!-- NAVIGATION: Right -->
<ng-container *ngIf="layoutSettings.navigation === 'right'">
<div class="content-wrapper">
<!-- TOOLBAR: Below -->
<ng-container *ngIf="layoutSettings.toolbar === 'below'">
<fuse-toolbar class="below"></fuse-toolbar>
</ng-container>
<!-- / TOOLBAR: Below -->
<fuse-content perfect-scrollbar></fuse-content>
<!-- FOOTER: Below -->
<ng-container *ngIf="layoutSettings.footer === 'below'">
<fuse-footer class="below"></fuse-footer>
</ng-container>
<!-- / FOOTER: Below -->
</div>
<fuse-navbar></fuse-navbar>
</ng-container>
<!-- / NAVIGATION: Right -->
<fuse-navbar *ngIf="layoutSettings.navigation === 'right'"></fuse-navbar>
</div>

View File

@ -1,5 +1,6 @@
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { FuseLayoutService } from '../../services/layout.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector : 'fuse-layout',
@ -7,24 +8,28 @@ import { FuseLayoutService } from '../../services/layout.service';
styleUrls : ['./layout.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class FuseLayoutComponent implements OnInit
export class FuseLayoutComponent implements OnInit, OnDestroy
{
onSettingsChanged: Subscription;
layoutSettings: { navigation: string, toolbar: string, footer: string };
constructor(private layoutService: FuseLayoutService)
{
this.layoutSettings = layoutService.getSettings();
}
ngOnInit()
{
this.onSettingsChanged =
this.layoutService.onSettingsChanged
.subscribe(
(newSettings) =>
{
(newSettings) => {
this.layoutSettings = newSettings;
}
);
}
ngOnInit()
{
}
ngOnDestroy()
{
this.onSettingsChanged.unsubscribe();
}
}

View File

@ -21,8 +21,7 @@ export class FuseNavCollapseComponent implements OnInit
* When navigation changed
*/
router.events.subscribe(
(event) =>
{
(event) => {
if ( event instanceof NavigationEnd )
{
/**
@ -30,6 +29,7 @@ export class FuseNavCollapseComponent implements OnInit
*/
if ( this.isUrlInChildren(this.item, event.urlAfterRedirects) )
{
// console.log(this.item);
this.expand();
}
else
@ -44,8 +44,7 @@ export class FuseNavCollapseComponent implements OnInit
* Whenever a navigaiton collapse item toggled
*/
this.navigationService.onNavCollapseToggled.subscribe(
(clickedItem) =>
{
(clickedItem) => {
if ( clickedItem.children )
{
/**
@ -116,20 +115,23 @@ export class FuseNavCollapseComponent implements OnInit
{
return false;
}
for ( let i = 0; i < arr.children.length; i++ )
{
if ( arr.children[i].children )
{
return this.isUrlInChildren(arr.children[i], url);
}
else
if ( this.isUrlInChildren(arr.children[i], url) )
{
return true;
}
}
if ( arr.children[i].url === url )
{
return true;
}
}
}
return false;
}

View File

@ -325,34 +325,24 @@ export class FuseNavigation
'url' : '/ui/page-layouts/simple/full-width'
},
{
'title': 'Left Sidebar',
'title': 'Left Sidenav',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/left-sidebar'
'url' : '/ui/page-layouts/simple/left-sidenav'
},
{
'title': 'Left Sidebar Inner',
'title': 'Left Sidenav 2',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/left-sidebar-inner'
'url' : '/ui/page-layouts/simple/left-sidenav-2'
},
{
'title': 'Left Sidebar Floating',
'title': 'Right Sidenav',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/left-sidebar-floating'
'url' : '/ui/page-layouts/simple/right-sidenav'
},
{
'title': 'Right Sidebar',
'title': 'Right Sidenav 2',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/right-sidebar'
},
{
'title': 'Right Sidebar Inner',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/sidebar-inner'
},
{
'title': 'Right Sidebar Floating',
'type' : 'nav-item',
'url' : '/ui/page-layouts/simple/right-sidebar-floating'
'url' : '/ui/page-layouts/simple/right-sidenav-2'
},
{
'title': 'Tabbed',

View File

@ -31,6 +31,7 @@
@import "partials/spacing";
@import "partials/global";
@import "partials/icons";
@import "partials/colors";
@import "partials/material";
@import "partials/angular-material-fix";
@import "partials/typography";

View File

View File

@ -95,7 +95,7 @@ $card-header-height-sm: $header-height-sm - $card-toolbar-height;
}
}
// Left sidenav
// Left sidenav - Right sidenav
&.left-sidenav,
&.right-sidenav {
@ -254,4 +254,127 @@ $card-header-height-sm: $header-height-sm - $card-toolbar-height;
}
}
}
// Simple layout
&.simple {
// Fullwidth
&.fullwidth,
&.inner-sidenav {
min-height: 100%;
> .header {
height: $header-height;
min-height: $header-height;
max-height: $header-height;
padding: 24px;
background-color: mat-color($accent);
}
> .content {
padding: 24px;
}
}
// Left sidenav - Right sidenav
&.left-sidenav,
&.right-sidenav {
min-height: 100%;
height: 100%;
// Single scroll
&.single-scroll {
height: auto;
> md-sidenav-container {
> .center {
overflow: hidden;
}
}
}
> md-sidenav-container {
.sidenav {
width: 240px;
min-width: 240px;
max-width: 240px;
padding: 24px;
z-index: 51;
@include mat-elevation(7);
&.md-is-locked-open {
width: 220px;
min-width: 220px;
max-width: 220px;
box-shadow: none;
background: transparent;
}
&.md-stop-transition {
~ .mat-sidenav-content {
transition: none;
}
}
}
.mat-sidenav-content {
overflow: visible;
.header {
height: $header-height;
min-height: $header-height;
max-height: $header-height;
padding: 24px;
background-color: mat-color($accent);
}
.center {
overflow: auto;
@include mat-elevation(7);
.content {
padding: 24px;
background: #FFFFFF;
}
}
}
}
}
// Tabbed
&.tabbed {
min-height: 100%;
.header {
height: $header-height;
min-height: $header-height;
max-height: $header-height;
padding: 24px;
background-color: mat-color($accent);
}
> .content {
.mat-tab-group {
.mat-tab-labels {
padding: 0 24px;
}
.mat-tab-body {
padding: 24px;
}
}
}
}
}
// Blank layout
&.blank {
min-height: 100%;
padding: 24px;
}
}

View File

@ -1,4 +1,3 @@
// Truncate
.text-truncate {
display: block;

View File

@ -1,13 +1,19 @@
import { EventEmitter, Injectable } from '@angular/core';
import { NavigationStart, Router } from '@angular/router';
import { Injectable } from '@angular/core';
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationStart, Router, NavigationEnd } from '@angular/router';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
@Injectable()
export class FuseLayoutService
{
defaultSettings: { navigation: string, toolbar: string, footer: string };
settings: { navigation: string, toolbar: string, footer: string };
onSettingsChanged = new EventEmitter<{ navigation: string, toolbar: string, footer: string }>();
// onSettingsChanged = new EventEmitter<{ navigation: string, toolbar: string, footer: string }>();
onSettingsChanged: BehaviorSubject<{ navigation: string, toolbar: string, footer: string }>;
/**
* @param router
@ -17,53 +23,47 @@ export class FuseLayoutService
// Set the default settings
this.defaultSettings = {
navigation: 'left', // 'right', 'left', 'top', none
toolbar : 'below', // 'above', 'below', none
toolbar : 'none', // 'above', 'below', none
footer : 'none' // 'above', 'below', none
};
// Assign default settings at the init
this.settings = {...this.defaultSettings};
this.onSettingsChanged = new BehaviorSubject(this.defaultSettings);
// Reset the default settings whenever navigation starts
router.events.subscribe(
(event) =>
{
(event) => {
if ( event instanceof NavigationStart )
{
this.settings = {...this.defaultSettings};
this.onSettingsChanged.emit(this.settings);
const routerConfig = router.config;
const currentRouteConfig = routerConfig.find((config) => {
return '/' + config.path === event.url;
});
if ( !currentRouteConfig || !currentRouteConfig.data )
{
return;
}
if ( currentRouteConfig.data.layout )
{
this.setSettings(currentRouteConfig.data.layout);
}
else
{
this.setSettings(this.defaultSettings);
}
}
}
);
}
/**
* Gets settings
* @returns {{navigation: string, toolbar: string, footer: string}}
*/
getSettings()
{
return {...this.settings};
}
/**
* Sets settings
* @param newSettings
* @param settings
*/
setSettings(newSettings)
setSettings(settings)
{
Object.assign(this.settings, newSettings);
this.onSettingsChanged.emit(this.settings);
}
/**
* Sets default settings
* @param newDefaultSettings
*/
setDefaultSettings(newDefaultSettings)
{
Object.assign(this.defaultSettings, newDefaultSettings);
// this.onSettingsChanged.emit(this.settings);
const newSettings = Object.assign({}, this.defaultSettings, settings);
this.onSettingsChanged.next(newSettings);
}
}

View File

@ -1,5 +1,5 @@
Navigation:
<md-radio-group [(ngModel)]="layoutSettings.navigation" (ngModelChange)="onSettingsChanged()">
<md-radio-group [(ngModel)]="layoutSettings.navigation" (ngModelChange)="onLayoutSettingsChanged()">
<md-radio-button value="left">Left</md-radio-button>
<md-radio-button value="right">Right</md-radio-button>
<md-radio-button value="none">None</md-radio-button>
@ -9,7 +9,7 @@ Navigation:
<br>
Toolbar:
<md-radio-group [(ngModel)]="layoutSettings.toolbar" (ngModelChange)="onSettingsChanged()">
<md-radio-group [(ngModel)]="layoutSettings.toolbar" (ngModelChange)="onLayoutSettingsChanged()">
<md-radio-button value="below">Below</md-radio-button>
<md-radio-button value="above">Above</md-radio-button>
<md-radio-button value="none">None</md-radio-button>
@ -19,7 +19,7 @@ Toolbar:
<br>
Footer:
<md-radio-group [(ngModel)]="layoutSettings.footer" (ngModelChange)="onSettingsChanged()">
<md-radio-group [(ngModel)]="layoutSettings.footer" (ngModelChange)="onLayoutSettingsChanged()">
<md-radio-button value="below">Below</md-radio-button>
<md-radio-button value="above">Above</md-radio-button>
<md-radio-button value="none">None</md-radio-button>

View File

@ -1,33 +1,39 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { FuseLayoutService } from '../../../../core/services/layout.service';
import { Subscription } from 'rxjs/Subscription';
@Component({
selector : 'fuse-project',
templateUrl: './project.component.html',
styleUrls : ['./project.component.scss']
})
export class ProjectComponent implements OnInit
export class ProjectComponent implements OnInit, OnDestroy
{
layoutSettings: any;
onSettingsChanged: Subscription;
layoutSettings: { navigation: string, toolbar: string, footer: string };
constructor(private layoutService: FuseLayoutService)
{
/*this.layoutService.setSettings({
navigation: 'left',
toolbar : 'above',
footer : 'above'
});*/
// this.layoutSettings = Object.assign(this.layoutSettings, this.layoutService.getSettings());
this.onSettingsChanged =
this.layoutService.onSettingsChanged
.subscribe(
(newSettings) => {
this.layoutSettings = newSettings;
}
onSettingsChanged()
{
this.layoutService.setSettings({...this.layoutSettings});
);
}
ngOnInit()
{
this.layoutSettings = this.layoutService.getSettings();
}
ngOnDestroy()
{
this.onSettingsChanged.unsubscribe();
}
onLayoutSettingsChanged()
{
this.layoutService.onSettingsChanged.next(this.layoutSettings);
}
}

View File

@ -1,6 +1,6 @@
<div *ngIf="!mail" fxLayout="column" fxLayoutAlign="center center" fxFlex>
<md-icon>email</md-icon>
<span class="hint-text">Select a message to read</span>
<md-icon class="s-128 mb-16">email</md-icon>
<span class="select-message-text hint-text">Select a message to read</span>
</div>
<div *ngIf="mail">

View File

@ -7,6 +7,11 @@
background: #FFFFFF;
padding: 24px;
.select-message-text {
font-size: 24px;
font-weight: 300;
}
.mail-header {
padding-bottom: 24px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);

View File

@ -1,5 +1,5 @@
<div *ngIf="mails.length === 0" fxLayout="column" fxLayoutAlign="center center" fxFlex>
<span class="hint-text">There are no messages!</span>
<span class="no-messages-text hint-text">There are no messages!</span>
</div>
<fuse-mail-list-item md-ripple *ngFor="let mail of mails" [mail]="mail" (click)="readMail(mail.id)"

View File

@ -5,4 +5,9 @@
position: relative;
padding: 0;
border-right: 1px solid rgba(0,0,0,.12);
.no-messages-text {
font-size: 24px;
font-weight: 300;
}
}

View File

@ -1,4 +1,4 @@
<div class="page-layout carded left-sidenav">
<div id="mail" class="page-layout carded left-sidenav">
<!-- TOP BACKGROUND -->
<div class="top-bg md-accent-bg"></div>

View File

@ -1,6 +1,6 @@
@import "src/app/core/scss/fuse";
:host, #mail {
:host {
width: 100%;
height: 100%;

View File

@ -0,0 +1,85 @@
<div id="login" fxLayout="row" fxLayoutAlign="start">
<div id="login-intro" fxFlex fxHide fxShow.gt-xs>
<div class="logo">
<span>F</span>
</div>
<div class="title">
Welcome to the FUSE!
</div>
<div class="description">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ullamcorper nisl erat,
vel convallis elit fermentum pellentesque. Sed mollis velit facilisis facilisis viverra.
</div>
</div>
<div id="login-form-wrapper" perfect-scrollbar>
<div id="login-form">
<div class="logo" fxHide.gt-xs>
<span>F</span>
</div>
<div class="title">LOGIN TO YOUR ACCOUNT</div>
<div class="description">Sed mollis velit facilisis facilisis viverra</div>
<form name="loginForm" [formGroup]="loginForm" novalidate>
<md-input-container>
<input mdInput placeholder="Email" formControlName="email">
<md-error *ngIf="loginFormErrors.email.required">
Email is required
</md-error>
<md-error *ngIf="!loginFormErrors.email.required && loginFormErrors.email.email">
Please enter a valid email address
</md-error>
</md-input-container>
<md-input-container>
<input mdInput placeholder="Password" formControlName="password">
<md-error *ngIf="loginFormErrors.password.required">
Password is required
</md-error>
</md-input-container>
<div class="remember-forgot-password" fxLayout="row" fxLayout.xs="column"
fxLayoutAlign="space-between center">
<md-checkbox class="remember-me" aria-label="Remember Me">
Remember Me
</md-checkbox>
<a class="forgot-password">Forgot Password?</a>
</div>
<button md-raised-button color="primary" class="submit-button" aria-label="LOG IN"
[disabled]="loginForm.invalid" type="submit">
LOGIN
</button>
</form>
<div class="separator">
<span class="text">OR</span>
</div>
<div fxLayout="column" fxLayoutAlign="start center">
<button md-raised-button class="google">
Log in with Google
</button>
<button md-raised-button class="facebook">
Log in with Facebook
</button>
</div>
<div class="register" fxLayout="column" fxLayoutAlign="center center">
<span class="text">Don't have an account?</span>
<a class="link">Create an account</a>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,215 @@
@import "src/app/core/scss/fuse";
:host {
#login {
height: 100%;
overflow: hidden;
background: url('/assets/images/backgrounds/march.jpg') no-repeat;
background-size: cover;
#login-intro {
padding: 128px;
@include media-breakpoint('sm') {
padding: 128px 64px;
}
.logo {
width: 128px;
height: 128px;
line-height: 128px;
font-size: 86px;
font-weight: 500;
margin-bottom: 32px;
color: #FFFFFF;
border-radius: 2px;
text-align: center;
background: mat-color($accent);
}
.title {
font-size: 42px;
font-weight: 300;
line-height: 1;
}
.description {
padding-top: 8px;
font-size: 14px;
max-width: 600px;
}
}
#login-form-wrapper {
width: 400px;
min-width: 400px;
max-width: 400px;
height: 100%;
background: #FFFFFF;
@include mat-elevation(7);
@include media-breakpoint('sm') {
width: 360px;
min-width: 360px;
max-width: 360px;
}
@include media-breakpoint('xs') {
width: 100%;
min-width: 100%;
max-width: 100%;
}
#login-form {
padding: 128px 48px 48px 48px;
@include media-breakpoint('xs') {
text-align: center;
padding: 24px;
}
.logo {
width: 128px;
height: 128px;
line-height: 128px;
font-size: 86px;
font-weight: 500;
text-align: center;
margin: 32px auto;
color: #FFFFFF;
border-radius: 2px;
background: mat-color($accent);
}
.title {
font-size: 21px;
}
.description {
padding-top: 8px;
}
form {
width: 100%;
padding-top: 32px;
md-input-container {
width: 100%;
@include media-breakpoint('xs') {
width: 80%;
}
}
md-checkbox {
margin: 0;
}
.remember-forgot-password {
font-size: 13px;
margin-top: 8px;
.remember-me {
margin-bottom: 16px
}
.forgot-password {
font-size: 13px;
font-weight: 500;
margin-bottom: 16px
}
}
.submit-button {
width: 100%;
margin: 16px auto;
display: block;
@include media-breakpoint('xs') {
width: 80%;
}
}
}
.separator {
font-size: 15px;
font-weight: 600;
margin: 24px auto;
position: relative;
overflow: hidden;
width: 100px;
text-align: center;
color: rgba(0, 0, 0, 0.54);
.text {
display: inline-flex;
position: relative;
padding: 0 8px;
z-index: 9999;
&:before, &:after {
content: '';
display: block;
width: 30px;
position: absolute;
top: 10px;
border-top: 1px solid rgba(0, 0, 0, 0.12);
}
&:before {
right: 100%;
}
&:after {
left: 100%;
}
}
}
button {
&.google,
&.facebook {
width: 70%;
text-transform: none;
color: #FFFFFF;
font-size: 13px;
@include media-breakpoint('xs') {
width: 60%;
}
md-icon {
color: #FFFFFF;
margin: 0 8px 0 0;
}
}
&.google {
background-color: #D73D32;
margin-bottom: 8px;
}
&.facebook {
background-color: rgb(63, 92, 154);
}
}
.register {
margin: 32px auto 24px auto;
width: 250px;
font-weight: 500;
.text {
margin-right: 8px;
}
.link {
}
}
}
}
}
}

View File

@ -0,0 +1,61 @@
import { Component, OnInit } from '@angular/core';
import { FuseLayoutService } from '../../../../core/services/layout.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector : 'fuse-login-2',
templateUrl: './login-2.component.html',
styleUrls : ['./login-2.component.scss']
})
export class Login2Component implements OnInit
{
loginForm: FormGroup;
loginFormErrors: any;
constructor(private layoutService: FuseLayoutService, private formBuilder: FormBuilder)
{
this.layoutService.setSettings({
navigation: 'none',
toolbar : 'none',
footer : 'none'
});
this.loginFormErrors = {
email : {},
password: {}
};
}
ngOnInit()
{
this.loginForm = this.formBuilder.group({
email : ['', [Validators.required, Validators.email]],
password: ['', Validators.required]
});
this.loginForm.valueChanges.subscribe(() => {
this.onLoginFormValuesChanged();
});
}
onLoginFormValuesChanged()
{
for ( const field in this.loginFormErrors )
{
if ( this.loginFormErrors.hasOwnProperty(field) )
{
// Clear previous errors
this.loginFormErrors[field] = {};
// Get the control
const control = this.loginForm.get(field);
if ( control && control.dirty && !control.valid )
{
this.loginFormErrors[field] = control.errors;
}
}
}
}
}

View File

@ -0,0 +1,65 @@
<div id="login" fxLayout="column" perfect-scrollbar>
<div id="login-form-wrapper" fxLayout="column" fxLayoutAlign="center center">
<div id="login-form">
<div class="logo md-accent-bg">
<span>F</span>
</div>
<div class="title">LOGIN TO YOUR ACCOUNT</div>
<form name="loginForm" [formGroup]="loginForm" novalidate>
<md-input-container>
<input mdInput placeholder="Email" formControlName="email">
<md-error *ngIf="loginFormErrors.email.required">
Email is required
</md-error>
<md-error *ngIf="!loginFormErrors.email.required && loginFormErrors.email.email">
Please enter a valid email address
</md-error>
</md-input-container>
<md-input-container>
<input mdInput placeholder="Password" formControlName="password">
<md-error *ngIf="loginFormErrors.password.required">
Password is required
</md-error>
</md-input-container>
<div class="remember-forgot-password" fxLayout="row" fxLayout.xs="column"
fxLayoutAlign="space-between center">
<md-checkbox class="remember-me" aria-label="Remember Me">
Remember Me
</md-checkbox>
<a class="forgot-password">Forgot Password?</a>
</div>
<button md-raised-button color="primary" class="submit-button" aria-label="LOG IN"
[disabled]="loginForm.invalid" type="submit">
LOGIN
</button>
</form>
<div class="separator">
<span class="text">OR</span>
</div>
<button md-raised-button class="google">
Log in with Google
</button>
<button md-raised-button class="facebook">
Log in with Facebook
</button>
<div class="register" fxLayout="column" fxLayoutAlign="center center">
<span class="text">Don't have an account?</span>
<a class="link" [routerLink]="'/pages/auth/register'">Create an account</a>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,155 @@
@import "src/app/core/scss/fuse";
:host {
#login {
height: 100%;
background: url('/assets/images/backgrounds/march.jpg') no-repeat;
background-size: cover;
#login-form-wrapper {
flex: 1 0 auto;
padding: 32px;
@include media-breakpoint('xs') {
padding: 16px;
}
#login-form {
width: 384px;
max-width: 384px;
padding: 32px;
background: #FFFFFF;
text-align: center;
@include mat-elevation(7);
@include media-breakpoint('xs') {
padding: 24px;
width: 100%;
}
.logo {
width: 128px;
height: 128px;
line-height: 128px;
font-size: 86px;
font-weight: 500;
margin: 32px auto;
color: #FFFFFF;
border-radius: 2px;
background: mat-color($accent);
}
.title {
font-size: 17px;
margin: 16px 0 32px 0;
}
form {
width: 100%;
text-align: left;
md-input-container {
width: 100%;
}
md-checkbox {
margin: 0;
}
.remember-forgot-password {
font-size: 13px;
margin-top: 8px;
.remember-me {
margin-bottom: 16px
}
.forgot-password {
font-size: 13px;
font-weight: 500;
margin-bottom: 16px
}
}
.submit-button {
width: 220px;
margin: 16px auto;
display: block;
@include media-breakpoint('xs') {
width: 90%;
}
}
}
.register {
margin: 32px auto 24px auto;
font-weight: 500;
.text {
margin-right: 8px;
}
}
.separator {
font-size: 15px;
font-weight: 600;
margin: 24px auto;
position: relative;
overflow: hidden;
width: 100px;
color: rgba(0, 0, 0, 0.54);
.text {
display: inline-flex;
position: relative;
padding: 0 8px;
z-index: 9999;
&:before, &:after {
content: '';
display: block;
width: 30px;
position: absolute;
top: 10px;
border-top: 1px solid rgba(0, 0, 0, 0.12);
}
&:before {
right: 100%;
}
&:after {
left: 100%;
}
}
}
button {
&.google,
&.facebook {
width: 192px;
text-transform: none;
color: #FFFFFF;
font-size: 13px;
}
@include media-breakpoint('xs') {
width: 80%;
}
&.google {
background-color: #D73D32;
margin-bottom: 8px;
}
&.facebook {
background-color: rgb(63, 92, 154);
}
}
}
}
}
}

View File

@ -0,0 +1,55 @@
import { Component, OnInit } from '@angular/core';
import { FuseLayoutService } from '../../../../core/services/layout.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector : 'fuse-login',
templateUrl: './login.component.html',
styleUrls : ['./login.component.scss']
})
export class LoginComponent implements OnInit
{
loginForm: FormGroup;
loginFormErrors: any;
constructor(private layoutService: FuseLayoutService, private formBuilder: FormBuilder)
{
this.loginFormErrors = {
email : {},
password: {}
};
}
ngOnInit()
{
this.loginForm = this.formBuilder.group({
email : ['', [Validators.required, Validators.email]],
password: ['', Validators.required]
});
this.loginForm.valueChanges.subscribe(() => {
this.onLoginFormValuesChanged();
});
}
onLoginFormValuesChanged()
{
for ( const field in this.loginFormErrors )
{
if ( this.loginFormErrors.hasOwnProperty(field) )
{
// Clear previous errors
this.loginFormErrors[field] = {};
// Get the control
const control = this.loginForm.get(field);
if ( control && control.dirty && !control.valid )
{
this.loginFormErrors[field] = control.errors;
}
}
}
}
}

View File

@ -0,0 +1,65 @@
<div id="register" fxLayout="column" perfect-scrollbar>
<div id="register-form-wrapper" fxLayout="column" fxLayoutAlign="center center">
<div id="register-form">
<div class="logo md-accent-bg">
<span>F</span>
</div>
<div class="title">CREATE AN ACCOUNT</div>
<form name="registerForm" [formGroup]="registerForm" novalidate>
<md-input-container>
<input mdInput placeholder="Name" formControlName="name">
<md-error *ngIf="registerFormErrors.name.required">
Name is required
</md-error>
</md-input-container>
<md-input-container>
<input mdInput placeholder="Email" formControlName="email">
<md-error *ngIf="registerFormErrors.email.required">
Email is required
</md-error>
<md-error *ngIf="!registerFormErrors.email.required && registerFormErrors.email.email">
Please enter a valid email address
</md-error>
</md-input-container>
<md-input-container>
<input mdInput placeholder="Password" formControlName="password">
<md-error *ngIf="registerFormErrors.password.required">
Password is required
</md-error>
</md-input-container>
<md-input-container>
<input mdInput placeholder="Password Confirm" formControlName="passwordConfirm">
<md-error *ngIf="registerFormErrors.passwordConfirm.required">
Password confirmation is required
</md-error>
</md-input-container>
<div class="terms" fxLayout="row" fxLayoutAlign="center center">
<md-checkbox name="terms" aria-label="I read and accept" required>
<span>I read and accept</span>
</md-checkbox>
<a href="#">terms and conditions</a>
</div>
<button md-raised-button color="primary" class="submit-button" aria-label="CREATE AN ACCOUNT"
[disabled]="registerForm.invalid" type="submit">
CREATE AN ACCOUNT
</button>
</form>
<div class="register" fxLayout="column" fxLayoutAlign="center center">
<span class="text">Already have an account?</span>
<a class="link" [routerLink]="'/pages/auth/login'">Login</a>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,149 @@
@import "src/app/core/scss/fuse";
:host {
#register {
height: 100%;
background: url('/assets/images/backgrounds/march.jpg') no-repeat;
background-size: cover;
#register-form-wrapper {
flex: 1 0 auto;
padding: 32px;
@include media-breakpoint('xs') {
padding: 16px;
}
#register-form {
width: 384px;
max-width: 384px;
padding: 32px;
background: #FFFFFF;
text-align: center;
@include mat-elevation(7);
@include media-breakpoint('xs') {
padding: 24px;
width: 100%;
}
.logo {
width: 128px;
height: 128px;
line-height: 128px;
font-size: 86px;
font-weight: 500;
margin: 32px auto;
color: #FFFFFF;
border-radius: 2px;
background: mat-color($accent);
}
.title {
font-size: 17px;
margin: 16px 0 32px 0;
}
form {
width: 100%;
text-align: left;
md-input-container {
width: 100%;
}
md-checkbox {
margin: 0;
}
.terms {
font-size: 13px;
margin: 16px 0 32px 0;
a {
margin-left: 4px;
}
}
.submit-button {
width: 220px;
margin: 16px auto;
display: block;
@include media-breakpoint('xs') {
width: 90%;
}
}
}
.register {
margin: 32px auto 24px auto;
font-weight: 500;
.text {
margin-right: 8px;
}
}
.separator {
font-size: 15px;
font-weight: 600;
margin: 24px auto;
position: relative;
overflow: hidden;
width: 100px;
color: rgba(0, 0, 0, 0.54);
.text {
display: inline-flex;
position: relative;
padding: 0 8px;
z-index: 9999;
&:before, &:after {
content: '';
display: block;
width: 30px;
position: absolute;
top: 10px;
border-top: 1px solid rgba(0, 0, 0, 0.12);
}
&:before {
right: 100%;
}
&:after {
left: 100%;
}
}
}
button {
&.google,
&.facebook {
width: 192px;
text-transform: none;
color: #FFFFFF;
font-size: 13px;
}
@include media-breakpoint('xs') {
width: 80%;
}
&.google {
background-color: #D73D32;
margin-bottom: 8px;
}
&.facebook {
background-color: rgb(63, 92, 154);
}
}
}
}
}
}

View File

@ -0,0 +1,60 @@
import { Component, OnInit } from '@angular/core';
import { FuseLayoutService } from '../../../../core/services/layout.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector : 'fuse-register',
templateUrl: './register.component.html',
styleUrls : ['./register.component.scss']
})
export class RegisterComponent implements OnInit
{
registerForm: FormGroup;
registerFormErrors: any;
constructor(private layoutService: FuseLayoutService, private formBuilder: FormBuilder)
{
this.registerFormErrors = {
name : {},
email : {},
password : {},
passwordConfirm: {}
};
}
ngOnInit()
{
this.registerForm = this.formBuilder.group({
name : ['', Validators.required],
email : ['', [Validators.required, Validators.email]],
password : ['', Validators.required],
passwordConfirm: ['', Validators.required]
});
this.registerForm.valueChanges.subscribe(() => {
this.onRegisterFormValuesChanged();
});
}
onRegisterFormValuesChanged()
{
for ( const field in this.registerFormErrors )
{
if ( this.registerFormErrors.hasOwnProperty(field) )
{
// Clear previous errors
this.registerFormErrors[field] = {};
// Get the control
const control = this.registerForm.get(field);
if ( control && control.dirty && !control.valid )
{
this.registerFormErrors[field] = control.errors;
}
}
}
}
}

View File

@ -0,0 +1,58 @@
import { NgModule } from '@angular/core';
import { SharedModule } from '../../core/modules/shared.module';
import { RouterModule } from '@angular/router';
import { LoginComponent } from './authentication/login/login.component';
import { Login2Component } from './authentication/login-2/login-2.component';
import { RegisterComponent } from './authentication/register/register.component';
const routes = [
{
path : 'pages/auth/login',
component: LoginComponent,
data : {
layout: {
navigation: 'none',
toolbar : 'none',
footer : 'none'
}
}
},
{
path : 'pages/auth/login-2',
component: Login2Component,
data : {
layout: {
navigation: 'none',
toolbar : 'none',
footer : 'none'
}
}
},
{
path : 'pages/auth/register',
component: RegisterComponent,
data : {
layout: {
navigation: 'none',
toolbar : 'none',
footer : 'none'
}
}
}
];
@NgModule({
imports : [
SharedModule,
RouterModule.forChild(routes)
],
declarations: [
LoginComponent,
Login2Component,
RegisterComponent
]
})
export class PagesModule
{
}

View File

@ -0,0 +1,5 @@
<div class="page-layout blank">
<h2>Blank Page</h2>
</div>

View File

@ -0,0 +1,13 @@
import { Component } from '@angular/core';
@Component({
selector : 'fuse-blank',
templateUrl: './blank.component.html',
styleUrls : ['./blank.component.scss']
})
export class BlankComponent
{
constructor()
{
}
}

View File

@ -1,7 +1,7 @@
<div class="page-layout carded fullwidth single-scroll">
<!-- TOP BACKGROUND -->
<div class="top-bg md-accent-bg"></div>
<div class="top-bg"></div>
<!-- / TOP BACKGROUND -->
<!-- CENTER -->

View File

@ -1,7 +1,7 @@
<div class="page-layout carded fullwidth">
<!-- TOP BACKGROUND -->
<div class="top-bg md-accent-bg"></div>
<div class="top-bg"></div>
<!-- / TOP BACKGROUND -->
<!-- CENTER -->

View File

@ -1,7 +1,7 @@
<div class="page-layout carded left-sidenav single-scroll">
<!-- TOP BACKGROUND -->
<div class="top-bg md-accent-bg"></div>
<div class="top-bg"></div>
<!-- / TOP BACKGROUND -->
<md-sidenav-container>

View File

@ -1,7 +1,7 @@
<div class="page-layout carded left-sidenav">
<!-- TOP BACKGROUND -->
<div class="top-bg md-accent-bg"></div>
<div class="top-bg"></div>
<!-- / TOP BACKGROUND -->
<md-sidenav-container>

View File

@ -1,7 +1,7 @@
<div class="page-layout carded right-sidenav">
<!-- TOP BACKGROUND -->
<div class="top-bg md-accent-bg"></div>
<div class="top-bg"></div>
<!-- / TOP BACKGROUND -->
<md-sidenav-container>

View File

@ -8,37 +8,66 @@ import { CardedLeftSidenavComponent } from './carded/left-sidenav/left-sidenav.c
import { CardedLeftSidenav2Component } from './carded/left-sidenav-2/left-sidenav-2.component';
import { CardedRightSidenavComponent } from './carded/right-sidenav/right-sidenav.component';
import { CardedRightSidenav2Component } from './carded/right-sidenav-2/right-sidenav-2.component';
import { SimpleFullWidthComponent } from './simple/fullwidth/fullwidth.component';
import { SimpleLeftSidenavComponent } from './simple/left-sidenav/left-sidenav.component';
import { SimpleLeftSidenav2Component } from './simple/left-sidenav-2/left-sidenav-2.component';
import { SimpleRightSidenavComponent } from './simple/right-sidenav/right-sidenav.component';
import { SimpleRightSidenav2Component } from './simple/right-sidenav-2/right-sidenav-2.component';
import { TabbedComponent } from './simple/tabbed/tabbed.component';
import { BlankComponent } from './blank/blank.component';
const routes: Routes = [
{
path : 'ui/page-layouts/carded/full-width',
component: CardedFullWidthComponent,
children : []
component: CardedFullWidthComponent
},
{
path : 'ui/page-layouts/carded/full-width-2',
component: CardedFullWidth2Component,
children : []
component: CardedFullWidth2Component
},
{
path : 'ui/page-layouts/carded/left-sidenav',
component: CardedLeftSidenavComponent,
children : []
component: CardedLeftSidenavComponent
},
{
path : 'ui/page-layouts/carded/left-sidenav-2',
component: CardedLeftSidenav2Component,
children : []
component: CardedLeftSidenav2Component
},
{
path : 'ui/page-layouts/carded/right-sidenav',
component: CardedRightSidenavComponent,
children : []
component: CardedRightSidenavComponent
},
{
path : 'ui/page-layouts/carded/right-sidenav-2',
component: CardedRightSidenav2Component,
children : []
component: CardedRightSidenav2Component
},
{
path : 'ui/page-layouts/simple/full-width',
component: SimpleFullWidthComponent
},
{
path : 'ui/page-layouts/simple/left-sidenav',
component: SimpleLeftSidenavComponent
},
{
path : 'ui/page-layouts/simple/left-sidenav-2',
component: SimpleLeftSidenav2Component
},
{
path : 'ui/page-layouts/simple/right-sidenav',
component: SimpleRightSidenavComponent
},
{
path : 'ui/page-layouts/simple/right-sidenav-2',
component: SimpleRightSidenav2Component
},
{
path : 'ui/page-layouts/simple/tabbed',
component: TabbedComponent
},
{
path : 'ui/page-layouts/blank',
component: BlankComponent
}
];
@ -54,7 +83,14 @@ const routes: Routes = [
CardedLeftSidenavComponent,
CardedLeftSidenav2Component,
CardedRightSidenavComponent,
CardedRightSidenav2Component
CardedRightSidenav2Component,
SimpleFullWidthComponent,
SimpleLeftSidenavComponent,
SimpleLeftSidenav2Component,
SimpleRightSidenavComponent,
SimpleRightSidenav2Component,
TabbedComponent,
BlankComponent
]
})
export class UIPageLayoutsModule

View File

@ -0,0 +1,17 @@
<div class="page-layout simple fullwidth">
<!-- HEADER -->
<div class="header">
<h2>Fullwidth</h2>
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content">
<fuse-demo-content></fuse-demo-content>
</div>
<!-- / CONTENT -->
</div>

View File

@ -0,0 +1,13 @@
import { Component } from '@angular/core';
@Component({
selector : 'fuse-simple-fullwidth',
templateUrl: './fullwidth.component.html',
styleUrls : ['./fullwidth.component.scss']
})
export class SimpleFullWidthComponent
{
constructor()
{
}
}

View File

@ -0,0 +1,46 @@
<div class="page-layout simple left-sidenav single-scroll" fxLayout="row">
<md-sidenav-container>
<!-- SIDENAV -->
<md-sidenav class="sidenav mat-sidenav-opened" align="start" opened="true" mode="side"
fuseMdSidenavHelper="simple-left-sidenav-2" md-is-locked-open="gt-md">
<fuse-demo-sidenav></fuse-demo-sidenav>
</md-sidenav>
<!-- / SIDENAV -->
<!-- CENTER -->
<div class="center" fxFlex perfect-scrollbar>
<!-- HEADER -->
<div class="header" fxLayout="row">
<button md-button class="mat-icon-button sidenav-toggle"
fuseMdSidenavToggler="simple-left-sidenav-2"
fxHide.gt-md>
<md-icon>menu</md-icon>
</button>
<div>
<h2>Left sidenav with page scroll</h2>
</div>
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content">
<fuse-demo-content></fuse-demo-content>
</div>
<!-- / CONTENT -->
</div>
<!-- / CENTER -->
</md-sidenav-container>
</div>

View File

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
@Component({
selector : 'fuse-simple-left-sidenav-2',
templateUrl: './left-sidenav-2.component.html',
styleUrls : ['./left-sidenav-2.component.scss']
})
export class SimpleLeftSidenav2Component
{
constructor()
{
}
}

View File

@ -0,0 +1,46 @@
<div class="page-layout simple left-sidenav" fxLayout="row">
<md-sidenav-container>
<!-- SIDENAV -->
<md-sidenav class="sidenav mat-sidenav-opened" align="start" opened="true" mode="side"
fuseMdSidenavHelper="simple-left-sidenav" md-is-locked-open="gt-md">
<fuse-demo-sidenav></fuse-demo-sidenav>
</md-sidenav>
<!-- / SIDENAV -->
<!-- CENTER -->
<div class="center" fxFlex perfect-scrollbar>
<!-- HEADER -->
<div class="header" fxLayout="row">
<button md-button class="mat-icon-button sidenav-toggle"
fuseMdSidenavToggler="simple-left-sidenav"
fxHide.gt-md>
<md-icon>menu</md-icon>
</button>
<div>
<h2>Left sidenav with content scroll</h2>
</div>
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content">
<fuse-demo-content></fuse-demo-content>
</div>
<!-- / CONTENT -->
</div>
<!-- / CENTER -->
</md-sidenav-container>
</div>

View File

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
@Component({
selector : 'fuse-simple-left-sidenav',
templateUrl: './left-sidenav.component.html',
styleUrls : ['./left-sidenav.component.scss']
})
export class SimpleLeftSidenavComponent
{
constructor()
{
}
}

View File

@ -0,0 +1,46 @@
<div class="page-layout simple right-sidenav single-scroll" fxLayout="row">
<md-sidenav-container>
<!-- SIDENAV -->
<md-sidenav class="sidenav mat-sidenav-opened" align="end" opened="true" mode="side"
fuseMdSidenavHelper="simple-right-sidenav-2" md-is-locked-open="gt-md">
<fuse-demo-sidenav></fuse-demo-sidenav>
</md-sidenav>
<!-- / SIDENAV -->
<!-- CENTER -->
<div class="center" fxFlex perfect-scrollbar>
<!-- HEADER -->
<div class="header" fxLayout="row">
<button md-button class="mat-icon-button sidenav-toggle"
fuseMdSidenavToggler="simple-right-sidenav-2"
fxHide.gt-md>
<md-icon>menu</md-icon>
</button>
<div>
<h2>Right sidenav with page scroll</h2>
</div>
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content">
<fuse-demo-content></fuse-demo-content>
</div>
<!-- / CONTENT -->
</div>
<!-- / CENTER -->
</md-sidenav-container>
</div>

View File

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
@Component({
selector : 'fuse-simple-right-sidenav-2',
templateUrl: './right-sidenav-2.component.html',
styleUrls : ['./right-sidenav-2.component.scss']
})
export class SimpleRightSidenav2Component
{
constructor()
{
}
}

View File

@ -0,0 +1,46 @@
<div class="page-layout simple right-sidenav" fxLayout="row">
<md-sidenav-container>
<!-- SIDENAV -->
<md-sidenav class="sidenav mat-sidenav-opened" align="end" opened="true" mode="side"
fuseMdSidenavHelper="simple-right-sidenav" md-is-locked-open="gt-md">
<fuse-demo-sidenav></fuse-demo-sidenav>
</md-sidenav>
<!-- / SIDENAV -->
<!-- CENTER -->
<div class="center" fxFlex perfect-scrollbar>
<!-- HEADER -->
<div class="header" fxLayout="row">
<button md-button class="mat-icon-button sidenav-toggle"
fuseMdSidenavToggler="simple-right-sidenav"
fxHide.gt-md>
<md-icon>menu</md-icon>
</button>
<div>
<h2>Right sidenav with content scroll</h2>
</div>
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content">
<fuse-demo-content></fuse-demo-content>
</div>
<!-- / CONTENT -->
</div>
<!-- / CENTER -->
</md-sidenav-container>
</div>

View File

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
@Component({
selector : 'fuse-simple-right-sidenav',
templateUrl: './right-sidenav.component.html',
styleUrls : ['./right-sidenav.component.scss']
})
export class SimpleRightSidenavComponent
{
constructor()
{
}
}

View File

@ -0,0 +1,31 @@
<div class="page-layout simple tabbed" fxLayout="column">
<!-- HEADER -->
<div class="header">
<h2>Tabbed</h2>
</div>
<!-- / HEADER -->
<!-- CONTENT -->
<div class="content">
<md-tab-group md-dynamic-height="true">
<md-tab label="Tab 1">
<fuse-demo-content></fuse-demo-content>
</md-tab>
<md-tab label="Tab 2">
<fuse-demo-content></fuse-demo-content>
</md-tab>
<md-tab label="Tab 3">
<fuse-demo-content></fuse-demo-content>
</md-tab>
</md-tab-group>
</div>
<!-- / CONTENT -->
</div>

View File

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
@Component({
selector : 'fuse-tabbed',
templateUrl: './tabbed.component.html',
styleUrls : ['./tabbed.component.scss']
})
export class TabbedComponent
{
constructor()
{
}
}

View File

@ -0,0 +1,12 @@
import { NgModule } from '@angular/core';
import { UIPageLayoutsModule } from './page-layouts/page-layouts.module';
@NgModule({
imports : [
UIPageLayoutsModule
]
})
export class UIModule
{
}