feat:Change styles on dashboard, add animations

This commit is contained in:
Daniel Blumrich
2022-09-08 11:58:57 +00:00
committed by Thea Schöbl
parent 0d755bcbd3
commit 605aa1b782
12 changed files with 252 additions and 126 deletions

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Animation, AnimationController} from '@ionic/angular';
import {NgZone} from '@angular/core';
export class DashboardCollapse {
collapseAnimation: Animation;
nextFrame: number;
constructor(
private animationControl: AnimationController,
private zone: NgZone,
scrollContainer: HTMLElement,
toolbar: HTMLElement,
schedule: HTMLElement,
) {
this.zone
.runOutsideAngular(async () => {
this.collapseAnimation = this.animationControl
.create()
.duration(1000)
.addAnimation([
this.animationControl
.create()
.addElement(toolbar)
.fromTo('transform', 'translateY(0)', 'translateY(-32px)'),
this.animationControl
.create()
.addElement(toolbar.querySelector(':scope > ion-img')!)
.fromTo('transform', 'scale(1)', 'scale(0.35)'),
this.animationControl
.create()
.addElement(schedule)
.fromTo(
'transform',
'translateY(0) scaleY(1)',
'translateY(-75px) scaleY(0.8)',
),
this.animationControl
.create()
.addElement(schedule.querySelectorAll(':scope > a > *'))
.fromTo('transform', 'scaleY(1)', `scaleY(${1 / 0.8})`),
]);
this.collapseAnimation.progressStart(true, 0);
const element = scrollContainer;
let pos = element.scrollTop;
const animate = () => {
if (pos !== element.scrollTop) {
pos = element.scrollTop;
this.collapseAnimation.progressStep(element.scrollTop / 172);
}
this.nextFrame = requestAnimationFrame(animate);
};
this.nextFrame = requestAnimationFrame(animate);
})
.then();
}
destroy() {
cancelAnimationFrame(this.nextFrame);
}
}

View File

@@ -0,0 +1,47 @@
/*!
* Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
@import '../../../theme/util/mixins';
@include ion-md-down {
ion-toolbar, .logo, .schedule, .schedule > a > * {
will-change: transform;
}
.logo {
transform-origin: center right;
}
.schedule {
> a {
> * {
transform-origin: center;
}
> ion-label:first-child {
transform-origin: bottom;
}
> ion-label:last-child {
transform-origin: top;
}
> ion-icon:first-child {
transform-origin: top;
}
}
}
}

View File

@@ -1,54 +1,57 @@
<!-- <!--
~ Copyright (C) 2022 StApps ~ Copyright (C) 2022 StApps
~ This program is free software: you can redistribute it and/or modify it ~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free ~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3. ~ Software Foundation, version 3.
~ ~
~ This program is distributed in the hope that it will be useful, but WITHOUT ~ This program is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details. ~ more details.
~ ~
~ You should have received a copy of the GNU General Public License along with ~ You should have received a copy of the GNU General Public License along with
~ this program. If not, see <https://www.gnu.org/licenses/>. ~ this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<ion-header> <ion-header>
<div class="schedule"> <ion-toolbar #toolbar class="ion-hide-md-up">
<a [routerLink]="['/schedule/recurring']"> <ion-label slot="start">{{
<ion-icon size="40" weight="300" name="grid_view"></ion-icon> 'dashboard.header.title' | daytimeKey | translate
<ion-label>{{ 'schedule.recurring' | translate }}</ion-label> }}</ion-label>
</a> <ion-img src="assets/imgs/logo.png" class="logo"></ion-img>
<a </ion-toolbar>
*ngIf="nextEvent && nextEvent.event"
[routerLink]="['/data-detail', nextEvent.event.uid]"
class="schedule-item-button"
>
<ion-label>{{ 'dashboard.schedule.title' | translate }}</ion-label>
<ion-label>
{{ nextEvent.dates | nextDateInList | amDateFormat: 'll, HH:mm' }}
{{ 'timeSuffix' | translate }}
</ion-label>
<ion-label>{{ nextEvent.event.name }}</ion-label>
</a>
<a
*ngIf="!nextEvent || !nextEvent.event"
[routerLink]="['/schedule/recurring']"
class="schedule-item-button"
>
<ion-label>{{ 'dashboard.schedule.title' | translate }}</ion-label>
<ion-label>{{ 'dashboard.schedule.noEvent' | translate }}</ion-label>
<ion-label>{{ 'dashboard.schedule.noEventLink' | translate }}</ion-label>
</a>
</div>
</ion-header> </ion-header>
<div #schedule class="schedule">
<a [routerLink]="['/schedule/recurring']">
<ion-icon size="40" weight="300" name="grid_view"></ion-icon>
<ion-label>{{ 'schedule.recurring' | translate }}</ion-label>
</a>
<a
*ngIf="nextEvent && nextEvent.event"
[routerLink]="['/data-detail', nextEvent.event.uid]"
class="schedule-item-button"
>
<ion-label>{{ 'dashboard.schedule.title' | translate }}</ion-label>
<ion-label>
{{ nextEvent.dates | nextDateInList | amDateFormat: 'll, HH:mm' }}
{{ 'timeSuffix' | translate }}
</ion-label>
<ion-label>{{ nextEvent.event.name }}</ion-label>
</a>
<a
*ngIf="!nextEvent || !nextEvent.event"
[routerLink]="['/schedule/recurring']"
class="schedule-item-button"
>
<ion-label>{{ 'dashboard.schedule.title' | translate }}</ion-label>
<ion-label>{{ 'dashboard.schedule.noEvent' | translate }}</ion-label>
<ion-label>{{ 'dashboard.schedule.noEventLink' | translate }}</ion-label>
</a>
</div>
<ion-content> <ion-content fullscreen="true" #ionContent>
<div class="scrollable-container"> <stapps-navigation-section></stapps-navigation-section>
<stapps-navigation-section></stapps-navigation-section> <stapps-search-section></stapps-search-section>
<stapps-search-section></stapps-search-section> <stapps-news-section></stapps-news-section>
<stapps-news-section></stapps-news-section> <stapps-mensa-section></stapps-mensa-section>
<stapps-mensa-section></stapps-mensa-section> <stapps-favorites-section></stapps-favorites-section>
<stapps-favorites-section></stapps-favorites-section>
</div>
</ion-content> </ion-content>

View File

@@ -1,23 +1,28 @@
/*! /*!
* Copyright (C) 2022 StApps * Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
* *
* This program is distributed in the hope that it will be useful, but WITHOUT * This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details. * more details.
* *
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
@import '../../../theme/util/mixins'; @import '../../../theme/util/mixins';
:host ion-header {
z-index: 1;
}
:host ion-toolbar:last-of-type { :host ion-toolbar:last-of-type {
--padding-top: var(--spacing-md); --padding-top: var(--spacing-md);
--padding-bottom: 0; --padding-bottom: var(--spacing-md);
z-index: -1;
ion-icon { ion-icon {
margin-right: var(--spacing-sm); margin-right: var(--spacing-sm);
@@ -38,65 +43,27 @@
aspect-ratio: 1/1; aspect-ratio: 1/1;
object-position: right; object-position: right;
margin-left: auto; margin-left: auto;
margin-right: var(--spacing-sm); margin-right: 0;
@include phoneLandscape {
width: auto;
height: 50px;
}
@include phonePortraitSmall {
width: auto;
height: 50px;
}
} }
} }
ion-content { ion-content {
--background: var(--ion-color-light); --background: var(--ion-color-light);
--overflow: hidden;
.scrollable-container {
overflow: hidden auto;
height: 100%;
padding-top: 122px;
@media (max-width: 440px) {
padding-top: 140px;
}
@include ion-md-up {
padding-top: 0;
padding-bottom: 122px;
}
}
@include phoneLandscape {
--overflow: hidden auto;
.scrollable-container {
overflow: initial;
height: initial;
padding-top: 0;
padding-bottom: 0;
}
}
} }
.schedule { .schedule {
position: fixed;
width: 100%; width: 100%;
z-index: 3; z-index: 3;
background: var(--ion-color-primary); background: var(--ion-color-primary);
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
gap: var(--spacing-md); gap: var(--spacing-md);
padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-xl); padding: var(--spacing-sm);
padding-top: 0;
@include ion-md-up { @include ion-md-up {
position: unset; position: unset;
width: unset; width: unset;
z-index: unset;
height: calc(var(--tablet-top-bar-height) + (2 * var(--spacing-xl))); height: calc(var(--tablet-top-bar-height) + (2 * var(--spacing-xl)));
margin: 0; margin: 0;
padding: var(--spacing-xl); padding: var(--spacing-xl);
@@ -113,22 +80,25 @@ ion-content {
} }
a:first-child { a:first-child {
border: 2px solid var(--ion-color-primary-tint);
text-align: center; text-align: center;
flex: 0 0 auto; flex: 0 0 auto;
aspect-ratio: 1; aspect-ratio: 1;
box-sizing: content-box; box-sizing: content-box;
max-height: 60px; max-height: 60px;
overflow: hidden;
border: 2px solid var(--ion-color-primary-tint);
@include phoneLandscape { @include phoneLandscape {
height: auto; height: auto;
} }
}
a:first-child {
justify-content: space-around;
align-items: center;
gap: var(--spacing-sm);
ion-icon {
margin: auto auto var(--spacing-xs);
}
ion-label { ion-label {
margin: 0 auto auto;
font-size: var(--font-size-xxs); font-size: var(--font-size-xxs);
font-weight: var(--font-weight-semi-bold); font-weight: var(--font-weight-semi-bold);
} }
@@ -142,6 +112,7 @@ ion-content {
flex: 1 1 65%; flex: 1 1 65%;
background: var(--linear-gradient); background: var(--linear-gradient);
justify-content: center; justify-content: center;
z-index: 1;
@include ion-md-up { @include ion-md-up {
flex: 1 1 100%; flex: 1 1 100%;
@@ -156,10 +127,12 @@ ion-content {
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
line-height: 1.4; line-height: 1.4;
} }
ion-label:first-child { ion-label:first-child {
text-transform: uppercase; text-transform: uppercase;
color: var(--ion-color-secondary); color: var(--ion-color-secondary);
} }
ion-label:nth-child(2n) { ion-label:nth-child(2n) {
font-size: var(--font-size-lg); font-size: var(--font-size-lg);
font-weight: var(--font-weight-semi-bold); font-weight: var(--font-weight-semi-bold);
@@ -173,6 +146,7 @@ ion-content {
&.section-extended { &.section-extended {
padding-right: 0; padding-right: 0;
ion-icon[name='edit'] { ion-icon[name='edit'] {
margin-right: var(--spacing-md); margin-right: var(--spacing-md);
} }
@@ -232,6 +206,7 @@ ion-searchbar {
input { input {
padding: var(--spacing-lg); padding: var(--spacing-lg);
} }
ion-icon { ion-icon {
left: auto; left: auto;
right: var(--spacing-lg); right: var(--spacing-lg);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2021 StApps * Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -12,7 +12,14 @@
* You should have received a copy of the GNU General Public License along with * You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>. * this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import {Component, OnInit, OnDestroy} from '@angular/core'; import {
Component,
ElementRef,
NgZone,
OnDestroy,
OnInit,
ViewChild,
} from '@angular/core';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import {Location} from '@angular/common'; import {Location} from '@angular/common';
import {Subscription} from 'rxjs'; import {Subscription} from 'rxjs';
@@ -22,11 +29,18 @@ import {SplashScreen} from '@capacitor/splash-screen';
import {DataRoutingService} from '../data/data-routing.service'; import {DataRoutingService} from '../data/data-routing.service';
import {ScheduleProvider} from '../calendar/schedule.provider'; import {ScheduleProvider} from '../calendar/schedule.provider';
import {AnimationController, IonContent} from '@ionic/angular';
import {DashboardCollapse} from './dashboard-collapse';
// const scrollTimeline = new ScrollTimeline();
@Component({ @Component({
selector: 'app-dashboard', selector: 'app-dashboard',
templateUrl: './dashboard.component.html', templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'], styleUrls: [
'./dashboard.component.scss',
'/dashboard.collapse.component.scss',
],
}) })
export class DashboardComponent implements OnInit, OnDestroy { export class DashboardComponent implements OnInit, OnDestroy {
/** /**
@@ -34,6 +48,14 @@ export class DashboardComponent implements OnInit, OnDestroy {
*/ */
subscriptions: Subscription[] = []; subscriptions: Subscription[] = [];
@ViewChild('toolbar', {read: ElementRef}) toolbarRef: ElementRef;
@ViewChild('schedule', {read: ElementRef}) scheduleRef: ElementRef;
@ViewChild('ionContent') ionContentRef: IonContent;
collapseAnimation: DashboardCollapse;
/** /**
* UUID subscription * UUID subscription
*/ */
@@ -44,6 +66,11 @@ export class DashboardComponent implements OnInit, OnDestroy {
*/ */
private uuids: SCUuid[]; private uuids: SCUuid[];
/**
* Enable header animation
*/
isHeaderAnimated = true;
/** /**
* Next event in calendar * Next event in calendar
*/ */
@@ -66,6 +93,8 @@ export class DashboardComponent implements OnInit, OnDestroy {
private scheduleProvider: ScheduleProvider, private scheduleProvider: ScheduleProvider,
protected router: Router, protected router: Router,
public location: Location, public location: Location,
private animationControl: AnimationController,
private zone: NgZone,
) { ) {
this.subscriptions.push( this.subscriptions.push(
this.dataRoutingService.itemSelectListener().subscribe(item => { this.dataRoutingService.itemSelectListener().subscribe(item => {
@@ -82,6 +111,14 @@ export class DashboardComponent implements OnInit, OnDestroy {
}, },
); );
await SplashScreen.hide(); await SplashScreen.hide();
this.collapseAnimation = new DashboardCollapse(
this.animationControl,
this.zone,
await this.ionContentRef.getScrollElement(),
this.toolbarRef.nativeElement,
this.scheduleRef.nativeElement,
);
} }
async loadNextEvent() { async loadNextEvent() {
@@ -103,5 +140,6 @@ export class DashboardComponent implements OnInit, OnDestroy {
sub.unsubscribe(); sub.unsubscribe();
} }
this._uuidSubscription.unsubscribe(); this._uuidSubscription.unsubscribe();
this.collapseAnimation.destroy();
} }
} }

View File

@@ -17,7 +17,7 @@
:host { :host {
display: block; display: block;
padding: var(--spacing-md) var(--spacing-md) var(--spacing-sm); padding: var(--spacing-sm) var(--spacing-md) var(--spacing-sm);
@include ion-md-up { @include ion-md-up {
padding: var(--spacing-lg) var(--spacing-xxl) var(--spacing-sm); padding: var(--spacing-lg) var(--spacing-xxl) var(--spacing-sm);
@@ -40,28 +40,13 @@
} }
} }
// TODO
&:first-of-type {
padding-top: var(--spacing-lg);
}
& > ion-label:first-child { & > ion-label:first-child {
font-family: var(--headline-font-family); font-family: var(--headline-font-family);
font-size: var(--font-size-lg);
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
font-stretch: condensed;
text-transform: uppercase;
margin-bottom: var(--spacing-xs);
width: 100%;
display: flex;
flex-direction: revert;
justify-content: space-between;
ion-icon { ion-icon {
color: var(--ion-color-medium-shade); color: var(--ion-color-medium-shade);
position: relative; position: relative;
bottom: var(--spacing-sm);
margin-block: auto; margin-block: auto;
cursor: pointer; cursor: pointer;

View File

@@ -43,10 +43,10 @@ export class NewsSectionComponent extends NewsPageComponent implements OnInit {
enabled: true, enabled: true,
sticky: true, sticky: true,
}, },
width: 240, width: 300,
breakpoints: { breakpoints: {
768: { 768: {
width: 280, width: 380,
}, },
}, },
}; };

View File

@@ -26,7 +26,8 @@
[(ngModel)]="searchTerm" [(ngModel)]="searchTerm"
></ion-input> ></ion-input>
<ion-icon <ion-icon
size="25" size="35"
weight="300"
name="search" name="search"
(click)="onSubmitSearch()" (click)="onSubmitSearch()"
class="clickable" class="clickable"

View File

@@ -25,6 +25,7 @@
--padding-top: var(--spacing-xl); --padding-top: var(--spacing-xl);
--padding-bottom: var(--spacing-xl); --padding-bottom: var(--spacing-xl);
font-size: var(--font-size-xs); font-size: var(--font-size-xs);
--placeholder-font-weight: var(--font-weight-bold);
box-shadow: var(--shadow-default); box-shadow: var(--shadow-default);
} }
ion-icon { ion-icon {

View File

@@ -22,7 +22,6 @@
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
border-top: 1px solid var(--ion-color-medium);
max-height: calc(var(--ion-tabbar-height) + env(safe-area-inset-bottom)); max-height: calc(var(--ion-tabbar-height) + env(safe-area-inset-bottom));
padding-bottom: env(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);

View File

@@ -17,7 +17,7 @@
} }
.swiper-button-prev, .swiper-button-next { .swiper-button-prev, .swiper-button-next {
--swiper-navigation-size: 20px; --swiper-navigation-size: 20px;
top: calc(-1 * var(--spacing-xxl)); top: calc(-1 * var(--spacing-lg));
transform: translateY(0%); transform: translateY(0%);
font-weight: var(--font-weight-black); font-weight: var(--font-weight-black);
color: var(--ion-color-dark); color: var(--ion-color-dark);

View File

@@ -3,7 +3,7 @@
font-weight: var(--font-weight-black); font-weight: var(--font-weight-black);
font-stretch: condensed; font-stretch: condensed;
text-transform: uppercase; text-transform: uppercase;
margin-bottom: var(--spacing-xs); margin-bottom: var(--spacing-sm);
width: 100%; width: 100%;
display: flex; display: flex;