Angular - How to expand table - html

I need help expanding a table. For example, when I click on the full-screen icon the table covers the whole page. I'm trying to do this way but it's not working, I didn't get any errors, but the script is working because is changing the class name.
These are the codes. The card.component.html file is the model that complements the dashboard.component.html. The
card.component.ts has the full-screen code, and dashboard.component.html has the table.
File card.component.html
<div class="card {{ fullCard }} {{ cardLoad }}" [#cardClose]="cardClose"
[ngClass]="cardClass">
<div class="card-header" *ngIf="title">
<h5>{{ title }}</h5>
<span *ngIf="!classHeader">{{ headerContent }}</span>
<span *ngIf="classHeader">
<ng-content select=".code-header"></ng-content>
</span>
<div class="card-header-right">
<ul class="list-unstyled card-option">
<li *ngIf="!isCardToggled" (click)="this.isCardToggled=
!this.isCardToggled"><i class="icofont icofont-simple-left"></i></li>
<li *ngIf="isCardToggled" (click)="this.isCardToggled=
!this.isCardToggled"><i class="icofont icofont-simple-right"></i></li>
<li *ngIf="isCardToggled"><i class="icofont icofont-maximize {{
fullCardIcon }} full-card" (click)="fullScreen($event)"></i></li>
<li *ngIf="isCardToggled"><i class="icofont icofont-minus minimize-card"
appCardToggleEvent (click)="toggleCard($event)"></i></li>
</ul>
</div>
</div>
<div [#cardToggle]="cardToggle">
<div class="card-body" [ngClass]="blockClass">
<ng-content></ng-content>
</div>
</div>
<div class="card-loader" *ngIf="loadCard"><i class="icofont icofont-refresh
rotate-refresh"></i></div>
</div>
File card.component.ts
import { Component, OnInit, Input, ViewEncapsulation } from '#angular/core';
import {cardToggle, cardClose} from './card-animation';
#Component({
selector: 'app-card',
templateUrl: './card.component.html',
styleUrls: ['./card.component.scss'],
animations: [cardToggle, cardClose],
encapsulation: ViewEncapsulation.None
})
export class CardComponent implements OnInit {
#Input() headerContent: string;
#Input() title: string;
#Input() blockClass: string;
#Input() cardClass: string;
#Input() classHeader = false;
cardToggle = 'expanded';
cardClose = 'open';
fullCard: string;
fullCardIcon: string;
loadCard = false;
isCardToggled = false;
cardLoad: string;
constructor() { }
ngOnInit(): void {
}
toggleCard(event: any) {
this.cardToggle = this.cardToggle === 'collapsed' ? 'expanded' : 'collapsed';
}
closeCard(event: any) {
this.cardClose = this.cardClose === 'closed' ? 'open' : 'closed';
}
fullScreen(event: any) {
this.fullCard = this.fullCard === 'full-card' ? '' : 'full-card';
this.fullCardIcon = this.fullCardIcon === 'icofont-resize' ? '' : 'icofont-resize';
}
appCardRefresh() {
this.loadCard = true;
this.cardLoad = 'card-load';
setTimeout( () => {
this.cardLoad = '';
this.loadCard = false;
}, 3000);
}
}
File dashboard.component.html
<div class="col-md-12 col-xl-8 left">
<app-card [title]="'Squads Information'"
[cardClass]="'project-task'" [blockClass]="'p-b-10'">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Squad</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="task-contain">
<h6 class="bg-c-blue d-inline-block text-center">A</h6>
<p class="d-inline-block m-l-60 table-text">AG Technology I</p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<app-card>
</div
File styles.scss
.pcoded {
.card {
&.full-card {
position: fixed;
top: 80px;
z-index: 99999;
box-shadow: none;
right: 0;
border-radius: 0;
border: 1px solid #ddd;
width: calc(100vw - 287px);
height: calc(100vh - 80px);
&.card-load {
position: fixed;
}
}
&.card-load {
position: relative;
overflow: hidden;
.card-loader {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
background-color: rgba(255, 255, 255, 0.7);
z-index: 999;
i {
margin: 0 auto;
color: #ab7967;
font-size: 20px;
}
}
}
}
&[vertical-nav-type="expanded"] .card.full-card {
width: calc(100vw - 287px);
}
&[vertical-nav-type="collapsed"] .card.full-card {
width: calc(100vw - 97px);
}
&[vertical-nav-type="offcanvas"] .card.full-card {
width: 100vw;
}
}

Related

How to do a function call in HTML with vueJS

For my dev lessons, I need to create a social network for a company.
One of the functionalities is to display the comment number linked to a publication (a bit like on Facebook).
To get the comments, I am using vueX to get all data from one source. So, to display the comment count number, here is my idea : for each publication contained in an array, I get all publication comments in another array. So, if I do a {{comments.length}}, for example, this should display "3 comments"
So I wrote the following code :
<div v-if="publications.length > 0">
<AddPostForm />
<section
v-for="post in publications"
:key="post.postId"
class="publications"
>
<div v-bind:data-id="post.postId" class="publications__card">
<div class="publications__author" :data-user-id="post.userId">
<img :src="post.avatarUrl" alt="Photo de profil" />
<span
class="publications__author-profile"
#click="goToProfile(post.userId)"
>
{{ post.firstName }} {{ post.lastName }}
</span>
</div>
<div
class="publications__content"
v-if="post.postContent !== null && post.postContent !== ''"
>
<p>{{ post.postContent }}</p>
</div>
<div
class="publications__content"
v-else-if="
post.postContent === null ||
(post.postContent === '' &&
(post.imageUrl !== null || post.imageUrl !== ''))
"
>
<img :src="post.imageUrl" alt="Image de publication" />
</div>
<div
class="publications__content"
v-else-if="
(post.postContent !== null || post.postContent !== '') &&
(post.imageUrl !== null || post.imageUrl !== '')
"
>
<p>{{ post.postContent }}</p>
<img :src="post.imageUrl" alt="Image de publication" />
</div>
<div class="publications__date-time">
<p>PubliƩ le {{ post.post_date }}</p>
</div>
<div class="publications__delete" v-if="user.admin === 1">
<p class="publications__delete-txt" #click="deletePost(post.postId)">
Supprimer
</p>
</div>
<div
class="publications__like-comment-count"
v-if="post.comment_count > 0"
>
<div
class="publications__comment-count"
#click="goToComments(post.userId, post.postId)"
v-if="comments.length === 1"
>
{{ comments.length }} commentaire
</div>
<div
class="publications__comment-count"
#click="goToComments(post.userId, post.postId)"
v-else
>
{{ comments.length }} commentaires
</div>
</div>
<div class="publications__like-comment">
<div class="like-comment__like">
<FaSolidHeart />
<span class="icon__legend"> J'aime</span>
</div>
<div class="like-comment__comment" #click="comment = true">
<FaSolidComment />
<span
class="icon__legend"
#click="goToComments(post.userId, post.postId)"
> Commenter</span
>
</div>
</div>
</div>
</section>
</div>
<div v-else-if="publications.length === 0">
<p>Aucune publication pour le moment</p>
</div>
</template>
<script>
import axios from 'axios';
import { mapState } from 'vuex';
import FaSolidHeart from './Heart.vue';
import FaSolidComment from './CommentIcon.vue';
import AddPostForm from './AddPost.vue';
const userSessionData = JSON.parse(localStorage.getItem('userSession'));
const sessionToken = userSessionData.token;
if (sessionToken) {
axios.defaults.headers.common['Authorization'] = 'Bearer ' + sessionToken;
}
export default {
name: 'Wall',
components: {
FaSolidHeart,
FaSolidComment,
AddPostForm
},
data() {
return {
comment: false,
user: []
};
},
beforeMount() {
this.getResult();
this.getSession();
},
methods: {
getResult: function () {
this.$store.dispatch('setPublications');
},
goToProfile: function (userId) {
this.$router.push(`/profile/${userId}`);
},
goToComments: function (userId, postId) {
this.$router.push(`/comments/${userId}/${postId}`);
},
getSession: function () {
if (localStorage.userSession) {
this.user = JSON.parse(localStorage.userSession);
}
},
deletePost: function (postId) {
axios
.delete(`http://localhost:3000/api/publications/${postId}`)
.then(() => this.$router.go())
.catch((error) => console.log(error));
},
getComments: function (postId) {
// this.$store.dispatch('getPublicationComments', postId);
console.log(postId);
}
},
computed: {
...mapState(['publications', 'comments'])
}
};
</script>
<style scoped>
.publications {
width: 30%;
margin: 10px auto;
}
.publications__card {
width: 99%;
padding: 10px;
padding-left: 15px;
display: flex;
flex-direction: column;
justify-content: center;
background-color: rgba(138, 185, 241, 0.4);
}
.publications__card .publications__author:hover {
cursor: pointer;
}
.publications__card .publications__date-time {
margin-top: -20px;
font-size: 0.8rem;
color: grey;
}
.publications__card .publications__delete .publications__delete-txt {
margin-top: -5px;
font-size: 0.85rem;
}
.publications__card .publications__like-comment-count {
width: 92%;
height: fit-content;
margin: auto 0;
padding: 5px;
border-top: 1px solid white;
}
.publications__card .publications__comment-count {
margin-right: 15px;
text-align: right;
}
.publications__card .publications__like-comment {
width: 95%;
height: fit-content;
margin: auto 0;
padding-top: 10px;
display: flex;
justify-content: space-around;
border-top: 1px solid white;
}
.publications__card .like-comment__like:hover,
.publications__card .like-comment__comment:hover,
.publications__card .publications__comment-count:hover,
.publications__card .publications__delete .publications__delete-txt:hover {
cursor: pointer;
color: #4b77be;
}
.publications__card .publications__add-comment {
width: 95%;
padding-top: 5px;
border-top: 1px solid white;
}
.publications__card .publications__add-comment .add-comment__field {
width: 98%;
height: 25px;
margin-top: 5px;
padding-top: 0px;
padding: 13px;
border-radius: 50px;
resize: none;
font-family: 'poppins';
outline: none;
}
</style>
My question is how may I call the getComments function from the HTML so that I can display comment count number properly ?
Thank you so much for your help :)
You can simply do {{ getComments() }} in your markup.
I am not sure if this will work, but if you store the comments in vuex, you can use this:
<p>{{ this.$store.state.comments.length }}<p>
This only works if the comments is correctly stored in vuex, I hope this will work for you.

Unable to Navigate to other component page in angular 10

I am building a sample project named : Tennis Club Management using Angular 10. In this i have app component and dashboard component. In app.component.html, i have created login which takes 2 inputs username and password and contains login button. Now on login button click , it should validate the fields and navigate it to dashboard.html.
Below are the code files and screenshots
app.component.html
<div class="backgroundDiv">
<!-- <div>-->
<!-- <img class="img-fluid backgroundimage" src="https://rrtennis.co.uk/wp-content/uploads/2017/04/Restyled-Logo-2-SD-small.png" alt="Tennis Logo">-->
<!-- </div>-->
<div class="text-center">
<img class="rounded mx-auto d-block" src="https://rrtennis.co.uk/wp-content/uploads/2017/04/Restyled-Logo-2-SD-small.png" alt="Tennis Logo">
<div class="text-center">
<table class="table table-borderless">
<tr><h6>Admin Login</h6></tr>
<tbody>
<tr>
<th class="col-xs-5">Username</th>
<td class="col-xs-5"><input type="text" #username></td>
</tr>
<tr>
<th class="col-xs-5">Password</th>
<td class="col-xs-5"><input type="password" #password></td>
</tr>
<tr>
<td class="col-xs-5"><button class="btn btn-primary" (click)="validateLogin(username.value,password.value)">Login</button></td>
<td class="col-xs-5"><label class="forgot-password">Forgot Password ?</label></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- https://www.usta.com/content/dam/usta/Articles/article-primary/18309_C_NY_20_USTA_ZoomBackgroundsVisitOrlando_3.jpg -->
<!-- https://i.pinimg.com/originals/81/23/d4/8123d454ca0cc8f36d311cebbd5d3922.png -->
app.component.ts
import {Component} from '#angular/core';
import {Router} from '#angular/router';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Tennis-Angular';
constructor(private router: Router) {}
// tslint:disable-next-line:typedef
validateLogin(username: string, password: string) {
if (username === 'mohit' && password === 'sharma') {
// alert('Login Successful !');
this.router.navigate(['../dashboard']);
} else if (username === '' && password === '') {
alert('Email and Password cannot be blank !');
} else if (username === '') {
alert('Email cannot be blank !');
} else if (password === '') {
alert('Password cannot be blank !');
} else {
alert('Incorrect Email or Password !');
}
}
}
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { NgbModule } from '#ng-bootstrap/ng-bootstrap';
import {FormsModule} from '#angular/forms';
import { DashboardComponent } from './dashboard/dashboard.component';
import {RouterModule, Routes} from '#angular/router';
const appRoutes: Routes = [
{path: 'dashboard', component: DashboardComponent}
];
#NgModule({
declarations: [
AppComponent,
DashboardComponent
],
imports: [
BrowserModule,
NgbModule,
FormsModule,
RouterModule.forRoot(appRoutes)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.css
.backgroundDiv {
background: url('https://www.usta.com/content/dam/usta/Articles/article-primary/18309_C_NY_20_USTA_ZoomBackgroundsVisitOrlando_3.jpg') no-repeat center center fixed;
position: fixed;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
opacity: 0.80;
}
.table {
display: block;
margin: 0 auto;
height: auto;
width: 25%;
background-color: white;
/*margin-top: 15%;*/
text-align: center;
}
.btn-primary {
margin-left: 20%;
margin-top: 10%;
}
.forgot-password {
margin-top: 8%;
}
img {
margin: 0 auto;
height: auto;
position: relative;
margin-top: 5%;
margin-left: 50%;
max-width: 25%;
height: auto;
}
tbody {
background-color: white;
}
h6 {
margin-left: 100%;
width: 150%;
margin-top: 5%;
color: mediumseagreen;
text-shadow: mediumseagreen;
}
dashboard.component.html
<p>dashboard works!</p>
Screenshots
Home Page Login
Note : The problem is after login , the URL shows the navigation route(path), but the page is not getting displayed. Any solution please ?
There is no <router-outlet></router-outlet> in you app.component.html.
I would suggest create a login component and move your app component code to login component and have alone on your app.component.html.
So whenever you change route your previous component HTML won't render.

Implement Three Section header with sticky sections

I have to implement a 3 section sticky header. The first section is a header part, the second section is a hero component, the 3d part is a subheader. When the user starts to scroll the page, 2nd section (hero component) disappears behind the header. The 3d section sticks to the header. If the user continues scrolling and scrolled 30% of the page, the 3d section also starts to hide behind the header.
I have some questions:
1) How to better organize the code (split the header into components)
2) And how to implement the described logic using Angular 8?
Maybe there are some examples with Angular 2+
I have implemented a subheader that hides the hero part on scroll
HTML
<div class="subheader__wrapper">
<div class="subheader subheader__sticky"
[ngClass]="getSubheaderClassTop()"
>
<div class="row align-middle align-justify subheader-row-height u-grid-full-width">
<cc-breadcrumb class="columns"></cc-breadcrumb>
<a *ngIf="!noBackButton" (click)="onBack()"
id="form-dropdown-back-link"
class="columns small-1 mos-u-text-size--md mos-u-color-text--white"
>
<my-icon size="lg" icon="keyboard_arrow_left" theme="disabled-light" alt="Back"></my-icon>
<span class="anchor-icon-text-fix">{{ 'subheader.back' | translate }}</span>
</a>
</div>
</div>
</div>
<hero
*ngIf="isHeroVisible"
class="mos-c-position--relative"
[backgroundUrl]="heroImageURL"
[backgroundImage]="gradient"
blendMode="multiply"
backgroundPosition="top"
flexDirection="row"
minHeight="192">
<div *ngIf="!isBlendMode" class="subheader__gradient"></div>
<div class="subheader__content flex-child-grow">
<div class="row x-large-row c-position--relative">
<div class="column medium-8 small-12 u-color-text--white x-large-6">
<h2 class="o-subheader-2" data-id="subheader">
{{ heroSubtitle }} {{heroSubtitleDate}}
</h2>
<h2 class="mos-o-display-2" data-id="header">
{{ heroTitle }}
</h2>
<hr class="subheader__separator c-hero--separator">
</div>
</div>
</div>
</hero>
CSS
:host {
display: block;
}
.subheader {
width: 100%;
height: 52px;
background-color: color(sapphire, 500);
z-index: 100;
&-row-height {
height: 52px;
}
&__sticky {
position: fixed;
}
&__gradient {
pointer-events: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: linear-gradient(to right, rgba(0,0,0,0.8) 0%, rgba(255, 255, 255, 0.2) 100%);
}
&__content {
z-index: 1;
width: 100%;
justify-self: center;
.subheader__separator {
margin-right: auto;
}
}
}
.circle-step-position {
position: absolute;
top: 0;
left: 0;
z-index: 2;
}
JS
import {
AfterViewInit,
Component,
EventEmitter,
Input,
NgZone,
Output,
ElementRef,
ChangeDetectorRef,
ChangeDetectionStrategy,
} from '#angular/core';
import { TranslateService } from '#ngx-translate/core';
import { pathOr } from 'ramda';
import { AutoUnsubscribe } from '../shared/auto-unsubscribe.decorator';
import { WindowRef } from '../services/api/aot-ref/windowRef';
import { fromEvent } from 'rxjs';
import { takeUntil, distinctUntilChanged, map, throttleTime, startWith } from 'rxjs/operators';
#Component({
selector: 'cc-section-subheader',
templateUrl: './subheader.component.html',
styleUrls: ['./subheader.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
#AutoUnsubscribe
export class SubheaderComponent implements AfterViewInit {
componentDestroy;
#Input() subheader: string;
#Input() header: string;
#Input() country: string;
#Input() noBackButton: boolean;
#Input() heroTitle: string;
#Input() heroSubtitle: string;
#Input() heroSubtitleDate: string;
#Input() heroImageURL: string;
#Input() isHeroVisible = false;
#Input() isSummary = false;
#Input() isFileUpload = false;
#Input() isValidateData = false;
#Input() isJobMatch = false;
#Input() isAggregateValidation = false;
#Output() back = new EventEmitter<void>();
adjustSubheader = true;
lastScroll = 0;
lastHeight: string;
isExpanded = false;
isBlendMode = false;
constructor(
private ngZone: NgZone,
private window: WindowRef,
private subheaderElement: ElementRef,
private translate: TranslateService,
private cdr: ChangeDetectorRef,
) {
this.isBlendMode = !!('backgroundBlendMode' in document.body.style);
}
get areButtonsDisabled() {
return true;
}
get gradient(): string {
return this.isBlendMode
? 'linear-gradient(to right, black 0%, white 100%)'
: null;
}
ngAfterViewInit() {
this.ngZone.runOutsideAngular(() => {
fromEvent(this.window.nativeWindow, 'scroll')
.pipe(
map(() => this.isHeaderVisible()),
distinctUntilChanged(),
takeUntil(this.componentDestroy())
)
.subscribe((isHeaderVisible: boolean) => {
this.ngZone.run(() => {
this.adjustSubheader = isHeaderVisible;
this.cdr.detectChanges();
});
});
});
this.ngZone.runOutsideAngular(() => {
fromEvent(this.window.nativeWindow, 'resize').pipe(
startWith(null),
throttleTime(200),
takeUntil(this.componentDestroy()),
)
.subscribe(() => this.updateWrappersHeight());
});
}
/**
* Since sub-header has position set as fixed it removes the element from the normal document
* flow. To properly align content under it we need to set sub-header's height equal to
* sub-header.
*/
updateWrappersHeight(): void {
const el = this.subheaderElement.nativeElement;
const headerHeight = this.window.nativeWindow.getComputedStyle(el.querySelector('.subheader')).height;
if (headerHeight !== this.lastHeight) {
this.lastHeight = headerHeight;
el.querySelector('.subheader__wrapper').style.height = this.lastHeight;
}
}
onBack() {
this.back.emit();
}
getHtmlElementOffset(element: HTMLElement, offset = 0): number {
if (!element.offsetParent) {
return offset;
}
return this.getHtmlElementOffset(
element.offsetParent as HTMLElement,
offset + pathOr(0, ['offsetParent', 'offsetTop'], element),
);
}
getElementOffset(element: ElementRef): number {
if (!element) {
return 0;
}
return this.getHtmlElementOffset(
element.nativeElement,
element.nativeElement.offsetTop,
);
}
isHeaderVisible() {
const scrollTop = this.window.nativeWindow.pageYOffset;
const down = scrollTop > this.lastScroll;
const top = this.getElementOffset(this.subheaderElement);
this.lastScroll = scrollTop;
return scrollTop <= top;
}
getSubheaderClassTop(): string {
return this.adjustSubheader
? ''
: 'subheader__sticky--fixed-to-top';
}
}

How do I make a content div scrollable? React and CSS

Learning React.js framework and need some pointers on styling. CSS isn't my forte.
How do I style the static content div in the middle and make it scrollable only within the div?
No styling:
https://i.imgur.com/26wNAfH.jpg
How to style this?
https://i.imgur.com/c5nYCOz.jpg
Here's the scroll function:
https://storage.googleapis.com/hatchways-app.appspot.com/assessments/data/frontend/part%202.mp4
app.css
.name {
font-weight: bold;
font-size: 20px;
}
.centered {
margin: auto;
width: 50%;
border: 3px solid green;
padding: 10px;
}
.center {
position: fixed;
width: 500px;
height: 200px;
top: 50%;
left: 50%;
margin-top: -100px; /* Negative half of height. */
margin-left: -250px; /* Negative half of width. */
}
.content {
text-align: center;
border: 2px solid grey;
border-radius: 5px;
position: fixed;
/* center the div */
right: 0;
left: 0;
margin-right: auto;
margin-left: auto;
/* give it dimensions */
min-height: 10em;
width: 90%;
/* just for example presentation */
top: 5em;
background-color: white;
}
Output: https://i.imgur.com/Eyv6hab.png
HTML:
import React, { Component } from "react";
import "../App.css";
import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
const API = "https://www.hatchways.io/api/assessment/students";
class App extends Component {
constructor(props) {
super(props);
this.state = {
students: [],
isLoading: false,
error: null
};
}
componentDidMount() {
this.setState({ isLoading: true });
fetch(API)
.then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error("Something went wrong ...");
}
})
.then(data =>
this.setState({ students: data.students, isLoading: false })
)
.catch(error => this.setState({ error, isLoading: false }));
}
render() {
const { students, isLoading, error } = this.state;
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <p>Loading ...</p>;
}
return (
<body>
<div className="content">
<div>
{students.map(student => (
<div key={student.id}>
<p>
<img src={student.pic} />
</p>
<p className="name">
{student.firstName} {student.lastName}
</p>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p> Skill: {student.skill}</p>
<p>Average: {student.grades}</p>
</div>
))}
</div>
</div>
{/* <div class="card mb-3">
{students.map(student => (
<div class="row no-gutters">
<div class="col-md-4">
<img src={student.pic} class="card-img" alt="..." />
</div>
<div class="col-md-8">
<div class="card-body">
<h5 class="card-title">
{student.firstName} {student.lastName}
</h5>
<p class="card-text">
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p> Skill: {student.skill}</p>
<p>Average: {student.grades}</p>
</p>
</div>
</div>
</div>
))}
</div> */}
</body>
);
}
}
export default App;
This might not help I am unfamiliar with that JS framework. I am only posting this because nobody has answered and maybe this can help.
<style>
scroll
{
max-height: 400px;
overflow-y: scroll;
}
</style>
<div class="scroll">

Slide a div from the bottom underneath another div with angular animations

As you can see in the screenshot below, I hava a tab on the bottom of my page. When I click on it, I want it to slide underneath the <div> containing "Test" using angular animations. The problem is, that the pagesize should be responsive and therefore I cannot use px-values. I tried percentage as well, but that value refers to my tab-div, not the overall height.
Screenshot
My component:
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss'],
animations: [
trigger('tabState', [state('default', style({
transform: 'translateY(0)'
})
),
state('open', style({
transform: 'translateY(-100%)'
})),
transition('default <=> open', animate(500))
])
]})
export class TestComponent {
state = 'default';
onComeIn() {
this.state === 'default' ? this.state = 'open' : this.state = 'default';
}
}
My HTML:
<div class="mainContainer">
<mat-toolbar color="primary">
<div class="d-flex align-items-center justify-content-between">
<span>Test</span>
</div>
</mat-toolbar>
<div class="mainContentContainer">
<div class="d-flex flex-column" style="height: 100%">
<div>content</div>
<div class="mt-auto">
<div class="tabContainer" [#tabState]="state">
<div class="tab" (click)="onComeIn()">Tab</div>
</div>
</div>
</div>
And finally the css:
.tab {
box-sizing: border-box;
height: 4.2em;
width: 33%;
background-color: white;
padding: 1em 1.2em 0.45em 1.2em;
border-radius: 0.5em 0.5em 0 0;
box-shadow: 0 0.05em #b7b7b7;
}
.mainContainer {
width: 100%;
display: flex;
flex-direction: column;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.mainContentContainer {
flex: 1;
background-color: #455864;
}
The issue is more about css :
I changed the initial value of the tabContainer class to this :
.tabContainer {
position: fixed;
bottom: 0;
width: 100%;
}
Then in the animation definition, removed the bottom and added the top one :
state('open', style({
bottom: 'initial',
top: '20px'
})),
Here is the running example in editor.