inserting fixed footer (with @tsiegleauq)

This commit is contained in:
Jochen Saalfeld 2018-09-04 11:35:50 +02:00
parent 1b02b7c692
commit 9f226c75ee
No known key found for this signature in database
GPG Key ID: 8ACD4E8264B67DF4
15 changed files with 369 additions and 134 deletions

View File

@ -2,6 +2,8 @@ import { trigger, animate, transition, style, query, stagger, group } from '@ang
export const pageTransition = trigger('pageTransition', [
transition('* => *', [
/** this will avoid the dom-copy-effect */
query(':enter, :leave', style({ position: 'absolute', width: '100%' }), { optional: true }),
/** keep the dom clean - let all items "just" enter */
query(':enter mat-card', [style({ opacity: 0 })], { optional: true }),
query(':enter .on-transition-fade', [style({ opacity: 0 })], { optional: true }),
@ -11,19 +13,63 @@ export const pageTransition = trigger('pageTransition', [
/** parallel vanishing */
group([
/** animate fade out for the selected components */
query(':leave .on-transition-fade', [style({ opacity: 1 }), animate('0.2s', style({ opacity: 0 }))], {
optional: true
}),
query(
':leave .on-transition-fade',
[
style({ opacity: 1 }),
animate(
'200ms ease-in-out',
style({
transform: 'translateY(0%)',
opacity: 0
})
)
],
{ optional: true }
),
/** how the material cards are leaving */
query(':leave mat-card', [style({ opacity: 1 }), animate('0.2s', style({ opacity: 0 }))], {
optional: true
}),
query(':leave mat-row', [style({ opacity: 1 }), animate('0.2s', style({ opacity: 0 }))], {
optional: true
}),
query(':leave mat-expansion-panel', [style({ opacity: 1 }), animate('0.2s', style({ opacity: 0 }))], {
optional: true
})
query(
':leave mat-card',
[
style({ transform: 'translateY(0%)', opacity: 1 }),
animate(
'200ms ease-in-out',
style({
transform: 'translateY(0%)',
opacity: 0
})
)
],
{ optional: true }
),
query(
':leave mat-row',
[
style({ transform: 'translateY(0%)', opacity: 1 }),
animate(
'200ms ease-in-out',
style({
transform: 'translateY(0%)',
opacity: 0
})
)
],
{ optional: true }
),
query(
':leave mat-expansion-panel',
[
style({ transform: 'translateY(0%)', opacity: 1 }),
animate(
'200ms ease-in-out',
style({
transform: 'translateY(0%)',
opacity: 0
})
)
],
{ optional: true }
)
]),
/** parallel appearing */

View File

@ -0,0 +1,12 @@
<mat-toolbar color='primary' class="footer">
<mat-toolbar-row>
<button mat-button class="footer-link" [routerLink]="['/legalnotice']">
<span translate>Legal Notice</span>
</button>
<button mat-button class="footer-link" [routerLink]="['/privacypolicy']">
<span translate>Privacy Policy</span>
</button>
<span class="footer-right">© <span translate>Copyright by</span>&#160;<a href='https://openslides.org/'>OpenSlides</a>
</span>
</mat-toolbar-row>
</mat-toolbar>

View File

@ -0,0 +1,11 @@
.footer-link,
.footer-right {
font-size: 12px;
z-index: inherit;
}
.footer-right {
a {
color: white;
}
}

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterComponent } from './footer.component';
describe('FooterComponent', () => {
let component: FooterComponent;
let fixture: ComponentFixture<FooterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FooterComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,29 @@
import { Component, OnInit } from '@angular/core';
/**
* Reusable footer Apps.
*
* ## Examples:
*
* ### Usage of the selector:
*
* ```html
* <os-footer></os-footer>
* ```
*/
@Component({
selector: 'os-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss']
})
export class FooterComponent implements OnInit {
/**
* Empty constructor
*/
public constructor() {}
/**
* empty onInit
*/
public ngOnInit() {}
}

View File

@ -35,6 +35,9 @@ import { TranslateModule } from '@ngx-translate/core';
import { PermsDirective } from './directives/perms.directive';
import { DomChangeDirective } from './directives/dom-change.directive';
import { HeadBarComponent } from './components/head-bar/head-bar.component';
import { FooterComponent } from './components/footer/footer.component';
import { RouterModule } from '@angular/router';
library.add(fas);
@ -70,7 +73,8 @@ library.add(fas);
MatSnackBarModule,
MatDialogModule,
TranslateModule.forChild(),
FontAwesomeModule
FontAwesomeModule,
RouterModule
],
exports: [
FormsModule,
@ -96,8 +100,9 @@ library.add(fas);
TranslateModule,
PermsDirective,
DomChangeDirective,
FooterComponent,
HeadBarComponent
],
declarations: [PermsDirective, DomChangeDirective, HeadBarComponent]
declarations: [PermsDirective, DomChangeDirective, HeadBarComponent, FooterComponent]
})
export class SharedModule {}

View File

@ -1,31 +1,38 @@
<!-- The actual form -->
<mat-toolbar class="login-logo-bar" color="primary">
<img src='/assets/img/openslides-logo-h-dark-transparent.svg' alt='OpenSlides-logo'>
</mat-toolbar>
<div class="form-wrapper">
<header>
<mat-toolbar class="login-logo-bar" color="primary">
<img src='/assets/img/openslides-logo-h-dark-transparent.svg' alt='OpenSlides-logo'>
</mat-toolbar>
</header>
<main>
<div class="form-wrapper">
<mat-spinner *ngIf="inProcess"></mat-spinner>
<form [formGroup]="loginForm" class="login-form" (ngSubmit)="formLogin()">
<mat-form-field>
<input matInput required placeholder="User name" formControlName="username" [errorStateMatcher]="parentErrorStateMatcher">
</mat-form-field>
<br>
<mat-form-field>
<input matInput required placeholder="Password" formControlName="password" [type]="!hide ? 'password' : 'text'" [errorStateMatcher]="parentErrorStateMatcher">
<fa-icon matSuffix [icon]="!hide ? 'eye-slash' : 'eye'" (click)="hide = !hide"></fa-icon>
<mat-error>{{loginErrorMsg}}</mat-error>
</mat-form-field>
<mat-spinner *ngIf="inProcess"></mat-spinner>
<form [formGroup]="loginForm" class="login-form" (ngSubmit)="formLogin()">
<mat-form-field>
<input matInput required placeholder="User name" formControlName="username" [errorStateMatcher]="parentErrorStateMatcher">
</mat-form-field>
<br>
<mat-form-field>
<input matInput required placeholder="Password" formControlName="password" [type]="!hide ? 'password' : 'text'" [errorStateMatcher]="parentErrorStateMatcher">
<fa-icon matSuffix [icon]="!hide ? 'eye-slash' : 'eye'" (click)="hide = !hide"></fa-icon>
<mat-error>{{loginErrorMsg}}</mat-error>
</mat-form-field>
<!-- forgot password button -->
<br>
<button type="button" class='forgot-password-button' (click)="resetPassword()" mat-button>Forgot Password?</button>
<!-- forgot password button -->
<br>
<button type="button" class='forgot-password-button' (click)="resetPassword()" mat-button>Forgot Password?</button>
<!-- login button -->
<br>
<!-- TODO: Next to each other...-->
<button mat-raised-button color="primary" class='login-button' type="submit" translate>Login</button>
<button mat-raised-button *ngIf="areGuestsEnabled()" color="primary" class='login-button'
type="button" (click)="guestLogin()" translate>Login as Guest</button>
</form>
<!-- login button -->
<br>
<!-- TODO: Next to each other...-->
<button mat-raised-button color="primary" class='login-button' type="submit" translate>Login</button>
<button mat-raised-button *ngIf="areGuestsEnabled()" color="primary" class='login-button' type="button" (click)="guestLogin()" translate>Login as Guest</button>
</form>
</div>
</div>
</main>
<footer class="page-footer">
<os-footer></os-footer>
</footer>

View File

@ -2,14 +2,20 @@ mat-form-field {
width: 100%;
}
.login-logo-bar {
height: 45% !important;
img {
margin-left: auto;
margin-right: auto;
width: 90%;
max-width: 400px;
header {
width: 100%;
flex: 1;
mat-toolbar {
min-height: 200px !important;
}
.login-logo-bar {
img {
margin-left: auto;
margin-right: auto;
width: 90%;
height: 90%;
max-width: 400px;
}
}
}
@ -29,7 +35,6 @@ mat-form-field {
.form-wrapper {
padding-left: 30px;
padding-right: 30px;
mat-spinner {
position: absolute;
left: 0;
@ -44,3 +49,7 @@ mat-form-field {
margin: 0 auto;
max-width: 400px;
}
footer {
bottom: 0; //black magic to keep the toolbar active here
}

View File

@ -0,0 +1,3 @@
mat-card {
height: 100%;
}

View File

@ -80,44 +80,33 @@
<span translate>Projector</span>
</a>
</mat-nav-list>
<footer>
<button mat-button>
<a routerLink='/legalnotice' (click)='toggleSideNav()'>
<span translate>Legal Notice</span>
</a>
</button>
<button mat-button>
<a routerLink='/privacypolicy' (click)='toggleSideNav()'>
<span translate>Privacy Policy</span>
</a>
</button>
<br>
<span align="center">© Copyright by
<a href='https://openslides.org/'>OpenSlides</a>
</span>
</footer>
</mat-sidenav>
<div class="content">
<header>
<!-- the first toolbar row is (still) a global element
the second one shall be handled by the apps -->
<mat-toolbar color='primary'>
<!-- the first toolbar row is (still) a global element
the second one shall be handled by the apps -->
<mat-toolbar color='primary'>
<!-- show/hide menu button -->
<button mat-icon-button *ngIf="vp.isMobile" (click)='sideNav.toggle()'>
<fa-icon icon='bars'></fa-icon>
</button>
<!-- show/hide menu button -->
<button mat-icon-button *ngIf="vp.isMobile" (click)='sideNav.toggle()'>
<fa-icon icon='bars'></fa-icon>
</button>
<!-- glob search and generic menu on the right -->
<span class='spacer'></span>
<button mat-icon-button (click)='sideNav.toggle()'>
<fa-icon icon='search'></fa-icon>
</button>
</mat-toolbar>
</header>
<!-- glob search and generic menu on the right -->
<span class='spacer'></span>
<button mat-icon-button (click)='sideNav.toggle()'>
<fa-icon icon='search'></fa-icon>
</button>
</mat-toolbar>
<!-- continue with <mat-toolbar> in the app-->
<main [@pageTransition]="o.isActivated ? o.activatedRoute : ''">
<router-outlet #o="outlet"></router-outlet>
</main>
<div class="relax">
<main [@pageTransition]="o.isActivated ? o.activatedRoute : ''">
<router-outlet #o="outlet"></router-outlet>
</main>
<footer>
<os-footer></os-footer>
</footer>
</div>
</div>
</mat-sidenav-container>

View File

@ -1,12 +1,3 @@
mat-sidenav-container {
height: 100%;
main {
flex: 1;
position: relative;
}
}
.projector-button {
position: fixed;
bottom: 10px;
@ -25,14 +16,30 @@ mat-sidenav-container {
box-shadow: 3px 0px 10px 0px rgba(0, 0, 0, 0.2);
}
footer {
position: fixed;
bottom: 0;
.content {
min-height: 100%;
position: relative;
}
span {
// width: 100%;
mat-sidenav-container {
height: 100vh;
width: 100%;
}
.relax {
position: initial;
padding-bottom: 70px;
}
main {
display: flex;
flex-direction: column;
width: 100%;
position: relative;
z-index: 50;
flex: 1;
> *:not(router-outlet) {
flex: 1;
display: block;
text-align: center;
margin-bottom: 5px;
}
}

View File

@ -1,21 +1,56 @@
{
"Agenda": "Tagesordnung",
"Assignments": "Wahlen",
"Category": "",
"Change Password": "Passwort ändern",
"Content": "",
"Cookies": "",
"Copyright by": "Copyright by",
"Database": "",
"DeleteMotion": "",
"Deleting Files": "",
"Edit Profile": "Profil bearbeiten",
"English": "Englisch",
"Export As": {
"0": {
"0": {
"0": ""
}
}
},
"FILTER": "",
"Files": "Dateien",
"French": "Französisch",
"German": "Deutsch",
"Hello user": "Hallo {{user}}",
"Home": "Startseite",
"Identifier": "",
"Legal Notice": "Impressum",
"Log In": "Anmelden",
"Logfiles": "",
"Login": "",
"Login as Guest": "",
"Logout": "Abmelden",
"Meta information": "",
"Motion": "",
"Motions": "Anträge",
"OK": "",
"Offline mode: You can use OpenSlides but changes are not saved": {
"0": ""
},
"Origin": "",
"Participants": "Teilnehmer",
"Personal note": "",
"Privacy Policy": "Datenschutz",
"Project": "",
"Projector": "",
"Reason": "",
"Reset State": "",
"Reset recommendation": "",
"SORT": "",
"Settings": "Einstellungen",
"Users": "Benutzer",
"Welcome to OpenSlides": "Willkommen bei OpenSlides"
"State": "",
"Submitters": "",
"Supporters": "",
"The assembly may decide:": "",
"Welcome to OpenSlides": "Willkommen bei OpenSlides",
"by": ""
}

View File

@ -1,21 +1,56 @@
{
"Agenda": "",
"Assignments": "",
"Category": "",
"Change Password": "",
"Content": "",
"Cookies": "",
"Copyright by": "",
"Database": "",
"DeleteMotion": "",
"Deleting Files": "",
"Edit Profile": "",
"English": "",
"Export As": {
"0": {
"0": {
"0": ""
}
}
},
"FILTER": "",
"Files": "",
"French": "",
"German": "",
"Hello user": "Hello {{user}}",
"Home": "",
"Identifier": "",
"Legal Notice": "",
"Log In": "",
"Logfiles": "",
"Login": "",
"Login as Guest": "",
"Logout": "",
"Meta information": "",
"Motion": "",
"Motions": "",
"OK": "",
"Offline mode: You can use OpenSlides but changes are not saved": {
"0": ""
},
"Origin": "",
"Participants": "",
"Personal note": "",
"Privacy Policy": "",
"Project": "",
"Projector": "",
"Reason": "",
"Reset State": "",
"Reset recommendation": "",
"SORT": "",
"Settings": "",
"Users": "",
"Welcome to OpenSlides": ""
"State": "",
"Submitters": "",
"Supporters": "",
"The assembly may decide:": "",
"Welcome to OpenSlides": "",
"by": ""
}

View File

@ -1,21 +1,56 @@
{
"Agenda": "",
"Assignments": "",
"Category": "",
"Change Password": "",
"Content": "",
"Cookies": "",
"Copyright by": "",
"Database": "",
"DeleteMotion": "",
"Deleting Files": "",
"Edit Profile": "",
"English": "",
"Export As": {
"0": {
"0": {
"0": ""
}
}
},
"FILTER": "",
"Files": "",
"French": "",
"German": "",
"Hello user": "",
"Home": "",
"Identifier": "",
"Legal Notice": "",
"Log In": "",
"Logfiles": "",
"Login": "",
"Login as Guest": "",
"Logout": "",
"Meta information": "",
"Motion": "",
"Motions": "",
"OK": "",
"Offline mode: You can use OpenSlides but changes are not saved": {
"0": ""
},
"Origin": "",
"Participants": "",
"Personal note": "",
"Privacy Policy": "",
"Project": "",
"Projector": "",
"Reason": "",
"Reset State": "",
"Reset recommendation": "",
"SORT": "",
"Settings": "",
"Users": "",
"Welcome to OpenSlides": ""
"State": "",
"Submitters": "",
"Supporters": "",
"The assembly may decide:": "",
"Welcome to OpenSlides": "",
"by": ""
}

View File

@ -1,11 +1,9 @@
@import '~@angular/material/theming';
@include mat-core();
/** Import brand theme and (new) component themes */
@import './assets/styles/openslides-theme';
@import './app/site/site.component.scss-theme';
@mixin openslides-components-theme($theme) {
@include os-site-theme($theme);
/** More components are added here */
@ -13,35 +11,16 @@
@include angular-material-theme($openslides-theme);
@include openslides-components-theme($openslides-theme);
* {
font-family: Arial, Helvetica, sans-serif !important;
}
html,
body {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: 0;
padding: 0;
height: 100% !important;
}
body {
// background: #e8eaed;
margin: 0 auto;
padding: 0;
}
router-outlet ~ * {
position: absolute;
height: 100% !important;
width: 100%;
}
/**the plus button in Motion, Agenda, etc*/
.generic-plus-button {
bottom: -30px;
@ -52,6 +31,7 @@ router-outlet ~ * {
bottom: -28px;
z-index: 100;
}
.os-card {
max-width: 90%;
margin-top: 10px;
@ -83,3 +63,11 @@ router-outlet ~ * {
.on-transition-fade {
z-index: 100;
}
footer {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
z-index: 1;
}