[DEV] update display better

This commit is contained in:
Edouard DUPIN 2024-01-27 18:09:29 +01:00
parent 247064e412
commit 883866d5f6
14 changed files with 647 additions and 578 deletions

View File

@ -137,7 +137,7 @@ export class ElementPlayerAudioComponent implements OnInit {
self.mediaSource = self.httpService.createRESTCall2({
api: `data/${self.currentLMedia.dataId}/unknowMediaName.webm`, //${self.generatedName}`,
addURLToken: true,
});
}).replace("http://localhost:19080", "https://atria-soft.org");
}).catch(() => {
console.error("error not ùmanaged in audio player ... 111");
})

View File

@ -1,11 +1,6 @@
<div class="generic-page">
<div class="fill-title colomn_mutiple">
<div class="cover-area">
<div class="cover" *ngIf="covers" >
<img src="{{covers[0]}}"/>
</div>
</div>
<div [className]="covers ? 'description-area description-area-cover' : 'description-area description-area-no-cover'">
<div class="fill-title">
<div class="description-area">
<div class="title">
{{name}}
</div>
@ -15,18 +10,22 @@
<div class="description" *ngIf="albumDescription">
{{albumDescription}}
</div>
<div class="description" *ngIf="albumDescription">
{{albumDescription}}
</div>
<button class="button color-button color-shadow-black" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i> Play All
<div class="button-area">
<button class="circular-button" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i>
</button>
<button class="button color-button color-shadow-black" style="margin-left:10px;" (click)="playShuffe($event)" type="submit">
<i class="material-icons">shuffle</i> Shuffle
<button class="circular-button" (click)="playShuffle($event)" type="submit">
<i class="material-icons">shuffle</i>
</button>
</div>
</div>
<div class="fill-content colomn_mutiple" *ngIf="tracks">
<div class="cover-area">
<div class="cover" *ngIf="covers" >
<img src="{{covers[0]}}"/>
</div>
</div>
</div>
<div class="fill-content colomn_single" *ngIf="tracks">
<div class="clear"></div>
<div class="title" *ngIf="tracks.length > 1">Tracks:</div>
<div class="title" *ngIf="tracks.length == 1">Track:</div>

View File

@ -107,7 +107,7 @@
this.playerService.clear();
this.playerService.setNewPlaylist(elements);
}
playShuffe(event: any):void {
playShuffle(event: any): void {
let elements: number[] = [];
for (let iii = 0; iii < this.tracks.length; iii++) {
elements.push(this.tracks[iii].id);
@ -117,4 +117,3 @@
}
}

View File

@ -1,26 +1,28 @@
<div class="generic-page">
<div class="fill-title colomn_mutiple">
<div class="cover-area">
<div class="cover" *ngIf="covers" >
<img src="{{covers[0]}}"/>
</div>
</div>
<div [className]="covers ? 'description-area description-area-cover' : 'description-area description-area-no-cover'">
<div class="fill-title">
<div class="description-area">
<div class="title">
{{name}}
</div>
<div class="description" *ngIf="description">
{{description}}
</div>
<button class="button color-button color-shadow-black" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i> Play All
<div class="button-area">
<button class="circular-button" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i>
</button>
<button class="button color-button color-shadow-black" style="margin-left:10px;" (click)="playShuffe($event)" type="submit">
<i class="material-icons">shuffle</i> Shuffle
<button class="circular-button" (click)="playShuffle($event)" type="submit">
<i class="material-icons">shuffle</i>
</button>
</div>
</div>
<div class="fill-content colomn_mutiple" *ngIf="albums">
<div class="cover-area">
<div class="cover" *ngIf="covers" >
<img src="{{covers[0]}}"/>
</div>
</div>
</div>
<div class="fill-content colomn_single" *ngIf="albums">
<div class="clear"></div>
<div class="title" *ngIf="albums.length > 1">Albums:</div>
<div class="title" *ngIf="albums.length == 1">Album:</div>

View File

@ -101,7 +101,7 @@
console.log(`error to get list o ftrack ...`)
});
}
playShuffe(event: any):void {
playShuffle(event: any): void {
this.playerService.clear();
let self = this;
this.trackService.getData()
@ -118,4 +118,3 @@
}
}

View File

@ -1,30 +1,31 @@
<div class="generic-page">
<div class="fill-title colomn_mutiple">
<div class="cover-area">
<div class="cover" *ngIf="covers" >
<img src="{{covers[0]}}"/>
</div>
</div>
<div [className]="covers ? 'description-area description-area-cover' : 'description-area description-area-no-cover'">
<div class="title">
<div class="fill-title">
<div class="description-area">
<div class="shadow-text title">
{{name}}
</div>
<div class="title">
<div class="shadow-text sub-title-main">
{{albumName}}
</div>
<div class="description" *ngIf="albumDescription">
{{albumDescription}}
</div>
<div class="clear"></div>
<button class="button color-button color-shadow-black" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i> Play All
<div class="button-area">
<button class="circular-button" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i>
</button>
<button class="button color-button color-shadow-black" style="margin-left:10px;" (click)="playShuffe($event)" type="submit">
<i class="material-icons">shuffle</i> Shuffle
<button class="circular-button" (click)="playShuffle($event)" type="submit">
<i class="material-icons">shuffle</i>
</button>
</div>
</div>
<div class="fill-content colomn_mutiple" *ngIf="tracks">
<div class="cover-area" *ngIf="covers" >
<div class="cover">
<img src="{{covers[0]}}"/>
</div>
</div>
</div>
<div class="fill-content colomn_single" *ngIf="tracks">
<div class="clear"></div>
<div class="title" *ngIf="tracks.length > 1">Tracks:</div>
<div class="title" *ngIf="tracks.length == 1">Track:</div>

View File

@ -107,7 +107,7 @@ export class ArtistAlbumScene implements OnInit {
this.playerService.clear();
this.playerService.setNewPlaylist(elements);
}
playShuffe(event: any):void {
playShuffle(event: any): void {
let elements: number[] = [];
for (let iii = 0; iii < this.tracks.length; iii++) {
elements.push(this.tracks[iii].id);

View File

@ -1,26 +1,28 @@
<div class="generic-page">
<div class="fill-title colomn_mutiple">
<div class="cover-area">
<div class="cover" *ngIf="covers" >
<img src="{{covers[0]}}"/>
</div>
</div>
<div [className]="covers ? 'description-area description-area-cover' : 'description-area description-area-no-cover'">
<div class="title">
<div class="fill-title">
<div class="description-area ">
<div class="shadow-text title">
{{name}}
</div>
<div class="description" *ngIf="description">
{{description}}
</div>
<button class="button color-button color-shadow-black" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i> Play All
<div class="button-area">
<button class="circular-button" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i>
</button>
<button class="button color-button color-shadow-black" style="margin-left:10px;" (click)="playShuffe($event)" type="submit">
<i class="material-icons">shuffle</i> Shuffle
<button class="circular-button" (click)="playShuffle($event)" type="submit">
<i class="material-icons">shuffle</i>
</button>
</div>
</div>
<div class="fill-content colomn_mutiple" *ngIf="albums">
<div class="cover-area" *ngIf="covers" >
<div class="cover">
<img src="{{covers[0]}}"/>
</div>
</div>
</div>
<div class="fill-content colomn_single" *ngIf="albums">
<div class="clear"></div>
<div class="title" *ngIf="albums.length > 1">Albums:</div>
<div class="title" *ngIf="albums.length == 1">Album:</div>

View File

@ -111,7 +111,7 @@ export class ArtistScene implements OnInit {
console.log(`error to get list o ftrack ...`)
});
}
playShuffe(event: any):void {
playShuffle(event: any): void {
this.playerService.clear();
let self = this;
this.getAllTracksIds()

View File

@ -1,26 +1,28 @@
<div class="generic-page">
<div class="fill-title colomn_mutiple">
<div class="cover-area">
<div class="cover" *ngIf="covers" >
<img src="{{covers[0]}}"/>
</div>
</div>
<div class="fill-title">
<div [className]="covers ? 'description-area description-area-cover' : 'description-area description-area-no-cover'">
<div class="title">
<div class="shadow-text title">
{{name}}
</div>
<div class="description" *ngIf="description">
{{description}}
</div>
<button class="button color-button color-shadow-black" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i> Play All
<div class="button-area">
<button class="circular-button" (click)="playAll($event)" type="submit">
<i class="material-icons">play_arrow</i>
</button>
<button class="button color-button color-shadow-black" style="margin-left:10px;" (click)="playShuffe($event)" type="submit">
<i class="material-icons">shuffle</i> Shuffle
<button class="circular-button" (click)="playShuffle($event)" type="submit">
<i class="material-icons">shuffle</i>
</button>
</div>
</div>
<div class="fill-content colomn_mutiple" *ngIf="artists">
<div class="cover-area">
<div class="cover" *ngIf="covers" >
<img src="{{covers[0]}}"/>
</div>
</div>
</div>
<div class="fill-content colomn_single" *ngIf="artists">
<div class="clear"></div>
<div class="title" *ngIf="artists.length > 1">Artists:</div>
<div class="title" *ngIf="artists.length == 1">Artist:</div>

View File

@ -75,7 +75,7 @@ export class ArtistsScene implements OnInit {
console.log(`error to get list o ftrack ...`)
});
}
playShuffe(event: any):void {
playShuffle(event: any): void {
this.playerService.clear();
let self = this;
this.trackService.getData()

View File

@ -69,7 +69,7 @@ export class DataService {
}
let covers = [] as string[];
for (let iii = 0; iii < coverIds.length; iii++) {
covers.push(this.getCoverUrl(coverIds[iii]));
covers.push(this.getCoverUrl(coverIds[iii]).replace("http://localhost:19080", "https://atria-soft.org"));
}
return covers;
}

View File

@ -1,113 +1,111 @@
// landscape
@media screen and (orientation:landscape) {
.clear {
display: block;
float: left;
width: 100%;
clear: both;
text-align: center;
}
.clear-end {
display: block;
float: left;
width: 100%;
clear: both;
text-align: center;
height: 250px;
}
.button-area {
grid-gap: 50px;
display: flex;
flex-direction: row;
button {
padding-left: 10px;
}
}
.generic-page {
.fill-title {
display:block;
position:fixed;
left: 0px;
top: 50px;
width: 400px;
float: left;
padding: 1%;
display: flex;
flex-direction: row;
grid-gap: 5px;
padding-top: 10px;
padding-left: 10px;
padding-right: 10px;
}
.shadow-text {
text-align: left;
text-shadow: 1px 1px 2px black, 0 0 1em black, 0 0 0.2em black;
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
color: rgb(204, 204, 204);
}
.cover-area {
float: right;
width: 100%;
.cover {
align: center;
width: 60%;
margin: 0 auto;
display: flex;
align-items: flex-start;
flex: 1;
max-width: 400px;
img {
width: 100%;
box-shadow: 0 0 40px black;
border-radius: 25px;
min-width: 150px;
max-width: 400px;
max-height: 400px;
}
.cover {
max-width: 400px;
margin-left: auto;
}
}
}
.description-area-cover {
width: 100%;
}
.description-area-no-cover {
width: 100%;
}
.description-area {
float: left;
flex: 3;
min-width: 200px;
.title {
font-size: 45px;
font-size: 27px;
font-weight: bold;
line-height: 60px;
width:100%;
align: left;
text-align: left;
vertical-align: middle;
margin: 10px 0 10px 0;
text-shadow: 1px 1px 2px white, 0 0 1em white, 0 0 0.2em white;
//margin: 10px 0 10px 0;
text-transform: uppercase;
font-family: "Roboto","Helvetica","Arial",sans-serif;
}
.sub-title-main {
font-size: 33px;
font-size: 22px;
font-weight: bold;
line-height: 40px;
width: 100%;
align: center;
text-align: left;
vertical-align: middle;
margin: 10px 0 10px 0;
font-family: "Roboto","Helvetica","Arial",sans-serif;
//margin: 10px 0 10px 0;
}
.sub-title {
font-size: 33px;
font-size: 22px;
font-weight: bold;
line-height: 40px;
width: 100%;
align: left;
text-align: left;
vertical-align: middle;
margin: 10px 0 10px 0;
font-family: "Roboto","Helvetica","Arial",sans-serif;
}
.description {
//background-color: orange;
font-size: 24px;
width: ~"calc(100% - 14px * 2)";//calc(100% - 40px * 2 - 14px * 2);
font-size: 15px;
text-align: left;
vertical-align: middle;
//margin: 0 40px 10px 40px;
background: rgba(150, 150, 150, 0.9);
//border-radius: 7px;
padding: 14px;
font-family: "Roboto","Helvetica","Arial",sans-serif;
}
}
}
.fill-content {
display:block;
//position:relative;
margin: 0 0 0 430px;
padding: 0px;
float: left;
.title {
font-size: 33px;
font-weight: bold;
line-height: 40px;
width: 100%;
align: left;
text-align: left;
vertical-align: middle;
margin: 10px 0 10px 0;
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
}
.item {
font-size: 20px;
padding: 1px;
@ -125,13 +123,16 @@
cursor: pointer;
transition-duration: 0.4s;
float: left;
//display:inline;
&:hover {
box-shadow: 2px 2px 4px 3px rgba(0, 0, 0, 0.7);
}
.material-icons {
vertical-align: middle;
}
.material-icons {
position: absolute;
top: 50%;
@ -146,7 +147,7 @@
font-size: 20px;
padding: 1px;
height: 110px;
width: 80%;
width: 95%;
margin: 5px auto 5px auto;
padding: 5px;
overflow: hidden;
@ -159,9 +160,11 @@
cursor: pointer;
background: rgba(256, 256, 256, 0.3);
border-radius: 7px;
.material-icons {
vertical-align: middle;
}
.material-icons {
position: absolute;
top: 50%;
@ -172,13 +175,7 @@
}
}
}
.cover {
width: 30%;
//margin: 0 auto;
img {
width: 100%;
}
}
}
}
@ -196,36 +193,42 @@
}
}
@media all and (min-width: 3390px) and (max-width: 3700px) and (orientation:landscape) {
.colomn_mutiple {
width: ~"calc(210px * 14)";
margin: auto;
}
}
@media all and (min-width: 3180px) and (max-width: 3390px) and (orientation:landscape) {
.colomn_mutiple {
width: ~"calc(210px * 13)";
margin: auto;
}
}
@media all and (min-width: 2970px) and (max-width: 3180px) and (orientation:landscape) {
.colomn_mutiple {
width: ~"calc(210px * 12)";
margin: auto;
}
}
@media all and (min-width: 2760px) and (max-width: 2970px) and (orientation:landscape) {
.colomn_mutiple {
width: ~"calc(210px * 11)";
margin: auto;
}
}
@media all and (min-width: 2550px) and (max-width: 2760px) and (orientation:landscape) {
.colomn_mutiple {
width: ~"calc(210px * 10)";
margin: auto;
}
}
@media all and (min-width: 2340px) and (max-width: 2550px) and (orientation:landscape) {
.colomn_mutiple {
width: ~"calc(210px * 9)";
@ -281,6 +284,12 @@
}
}
.colomn_single {
margin-left: 50px;
margin-right: 50px;
}
// portrait
@media screen and (orientation:portrait) {
.clear {
@ -293,103 +302,101 @@
text-align: center;
height: 250px;
}
.button-area {
grid-gap: 5px;
display: flex;
flex-direction: row;
flex-flow: row wrap;
max-width: 150px;
}
.generic-page {
.fill-title {
display:block;
position:relative;
//width: 100%;
//height: 400px;
padding: 1%;
display: flex;
flex-direction: row;
grid-gap: 5px;
padding-top: 10px;
padding-left: 10px;
padding-right: 10px;
}
.shadow-text {
text-align: left;
text-shadow: 1px 1px 2px black, 0 0 1em black, 0 0 0.2em black;
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
color: rgb(204, 204, 204);
}
.cover-area {
//position:relative;
float: right;
//right: 0px;
width: 300px;
//height: 100%;
.cover {
width: 100%;
margin: auto;
display: flex;
align-items: flex-start;
flex: 1;
max-width: 400px;
img {
width: 100%;
box-shadow: 0 0 40px black;
border-radius: 25px;
min-width: 150px;
max-width: 400px;
max-height: 400px;
}
.cover {
max-width: 400px;
margin-left: auto;
}
}
}
.description-area-cover {
width: ~"calc( 100% - 302px)";
}
.description-area-no-cover {
width: 100%;
}
.description-area {
//position: sticky;
float: left;
//height: 100%;
//margin: 0;
//border: solid 1px;
//border-color: rgba(255, 0, 0, 0.7);
flex: 3;
min-width: 200px;
color: rgb(204, 204, 204);
.title {
//background-color: green;
font-size: 45px;
font-size: 27px;
font-weight: bold;
line-height: 60px;
width:100%;
align: left;
text-align: left;
vertical-align: middle;
margin: 10px 0 10px 0;
text-shadow: 1px 1px 2px white, 0 0 1em white, 0 0 0.2em white;
//margin: 10px 0 10px 0;
text-transform: uppercase;
font-family: "Roboto","Helvetica","Arial",sans-serif;
}
.sub-title-main {
font-size: 33px;
font-size: 22px;
font-weight: bold;
line-height: 40px;
width: 100%;
align: center;
text-align: left;
vertical-align: middle;
margin: 10px 0 10px 0;
font-family: "Roboto","Helvetica","Arial",sans-serif;
//margin: 10px 0 10px 0;
}
.sub-title {
font-size: 33px;
font-size: 22px;
font-weight: bold;
line-height: 40px;
width: 100%;
align: left;
text-align: left;
vertical-align: middle;
margin: 10px 0 10px 0;
font-family: "Roboto","Helvetica","Arial",sans-serif;
}
.description {
//background-color: orange;
font-size: 24px;
font-size: 20px;
text-align: left;
width: ~"calc(100% - 14px * 2)";//calc(100% - 40px * 2 - 14px * 2);
vertical-align: middle;
//margin: 0 40px 10px 40px;
background: rgba(150, 150, 150, 0.9);
//border-radius: 7px;
padding: 14px;
font-family: "Roboto","Helvetica","Arial",sans-serif;
}
}
}
.fill-content {
.title {
font-size: 33px;
font-weight: bold;
line-height: 40px;
width: 100%;
align: left;
text-align: left;
vertical-align: middle;
margin: 10px 0 10px 0;
font-family: "Roboto", "Helvetica", "Arial", sans-serif;
}
.item {
font-size: 20px;
padding: 1px;
@ -407,13 +414,16 @@
cursor: pointer;
transition-duration: 0.4s;
float: left;
//display:inline;
&:hover {
box-shadow: 2px 2px 4px 3px rgba(0, 0, 0, 0.7);
}
.material-icons {
vertical-align: middle;
}
.material-icons {
position: absolute;
top: 50%;
@ -439,11 +449,19 @@
will-change: box-shadow;
outline: none;
cursor: pointer;
background: rgba(256, 256, 256, 0.3);
background: rgba(156, 156, 156, 0.5);
border-radius: 7px;
.material-icons {
vertical-align: middle;
}
//display:inline;
&:hover {
background-color: rgba(156, 156, 156, 1);
box-shadow: 2px 2px 4px 3px rgba(0, 0, 0, 0.7);
}
.material-icons {
position: absolute;
top: 50%;
@ -454,15 +472,10 @@
}
}
}
.cover {
width: 30%;
//margin: 0 auto;
img {
width: 100%;
}
}
}
}
@media all and (min-width: 3510px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 15)";
@ -476,78 +489,91 @@
margin: auto;
}
}
@media all and (min-width: 2990px) and (max-width: 3300px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 14)";
margin: auto;
}
}
@media all and (min-width: 2780px) and (max-width: 2990px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 13)";
margin: auto;
}
}
@media all and (min-width: 2570px) and (max-width: 2780px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 12)";
margin: auto;
}
}
@media all and (min-width: 2360px) and (max-width: 2570px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 11)";
margin: auto;
}
}
@media all and (min-width: 2150px) and (max-width: 2360px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 10)";
margin: auto;
}
}
@media all and (min-width: 1940px) and (max-width: 2150px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 9)";
margin: auto;
}
}
@media all and (min-width: 1730px) and (max-width: 1940px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 8)";
margin: auto;
}
}
@media all and (min-width: 1520px) and (max-width: 1730px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 7)";
margin: auto;
}
}
@media all and (min-width: 1310px) and (max-width: 1520px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 6)";
margin: auto;
}
}
@media all and (min-width: 1100px) and (max-width: 1310px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 5)";
margin: auto;
}
}
@media all and (min-width: 890px) and (max-width: 1100px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 4)";
margin: auto;
}
}
@media all and (min-width: 680px) and (max-width: 890px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 3)";
margin: auto;
}
}
@media all and (max-width: 680px) and (orientation:portrait) {
.colomn_mutiple {
width: ~"calc(210px * 2)";
@ -590,10 +616,12 @@
font-size: 50px;
text-align: center;
}
.request_raw2 {
width: 90%;
margin: 0 auto;
height: 160px;
.label {
width: 15%;
margin-right: 10px;
@ -601,10 +629,12 @@
float: left;
display: block;
}
.input {
width: 75%;
float: left;
display: block;
textarea {
width: 100%;
font-size: 20px;
@ -612,10 +642,12 @@
}
}
}
.request_raw {
width: 90%;
margin: 0 auto;
height: 45px;
.label {
width: 15%;
margin-right: 10px;
@ -623,35 +655,42 @@
float: left;
display: block;
}
.input {
width: 75%;
float: left;
display: block;
input {
width: 100%;
font-size: 20px;
}
select {
width: 100%;
font-size: 20px;
}
textarea {
width: 100%;
font-size: 20px;
}
}
.input_add {
width: 5%;
float: right;
display: block;
}
}
.send_value {
width: 300px;
margin: 0 auto;
padding: 10px;
display: block;
}
.hide-element {
display: none;
}
@ -669,12 +708,16 @@
position: absolute;
width: 200px;
height: 250px;
img {
max-width: 100%;
max-height: 100%;
height: auto;
}
};
}
;
.cover-no-image {
position: absolute;
width: 190px;
@ -682,23 +725,32 @@
margin: 0 auto;
border: solid 5px;
border-color: rgba(0, 0, 0, 0.7);
};
}
;
.cover-button {
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
};
}
;
button {
border: none;
background: none;
};
}
;
.button-remove {
font-size: 75px;
color: #F00000;
}
.button-add {
font-size: 75px;
color: #00F000;

View File

@ -104,6 +104,7 @@ html {
.circular-button {
position: relative;
border-radius: 50%;
font-size: 24px;
height: 56px;
@ -294,7 +295,7 @@ label {
background-attachment: fixed;
background-position: 50% 50%;
z-index: -1;
z-index: -100;
}
@ -302,6 +303,18 @@ label {
* FLEX TOOLS
*****************************************************************************/
.flex-column {
display: flex;
flex-direction: column;
}
.flex-column-last {
margin-top: auto;
}
.flex-column-first {
margin-bottom: auto;
}
.flex-row {
display: flex;