Refactor video miniature

Less dirty code, better responsive
Prepare for some regressions
Increase default miniature size
pull/3920/head
Chocobozzz 2021-04-01 11:10:27 +02:00
parent 33253c1aa6
commit 0f7407d926
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
38 changed files with 728 additions and 666 deletions

View File

@ -39,6 +39,7 @@
<my-video-miniature
*ngFor="let video of getVideosOf(videoChannel)"
[video]="video" [user]="userMiniature" [displayVideoActions]="true" [displayOptions]="miniatureDisplayOptions"
thumbnailSize="medium"
></my-video-miniature>
<div *ngIf="getTotalVideosOf(videoChannel)" class="miniature-show-channel">

View File

@ -3,7 +3,7 @@
@import '_miniature';
.margin-content {
@include fluid-videos-miniature-margins;
@include grid-videos-miniature-margins;
}
.channel {
@ -93,6 +93,8 @@ my-subscribe-button {
my-video-miniature {
margin-right: 15px;
min-width: $video-thumbnail-medium-width;
max-width: $video-thumbnail-medium-width;
}
.no-results {
@ -105,7 +107,7 @@ my-subscribe-button {
position: absolute;
right: 0;
background: linear-gradient(90deg, transparent 0, pvar(--channelBackgroundColor) 45px);
padding: ($video-thumbnail-height / 2 - 10px) 15px 0 60px;
padding: ($video-thumbnail-medium-height / 2 - 10px) 15px 0 60px;
z-index: z(miniature) + 1;
a {

View File

@ -15,7 +15,7 @@
}
.links {
@include fluid-videos-miniature-margins;
@include grid-videos-miniature-margins;
display: flex;
justify-content: space-between;
@ -44,7 +44,7 @@ my-user-moderation-dropdown,
}
.account-info {
@include fluid-videos-miniature-margins(false, 15px);
@include grid-videos-miniature-margins(false, 15px);
display: grid;
grid-template-columns: 1fr min-content;

View File

@ -4,7 +4,7 @@
</h1>
<div class="top-buttons">
<div>
<div class="search-wrapper">
<div class="input-group has-feedback has-clear">
<input
type="text" name="history-search" id="history-search" i18n-placeholder placeholder="Search your history"
@ -15,7 +15,7 @@
</div>
</div>
<div class="history-switch ml-auto mr-3">
<div class="history-switch">
<my-input-switch [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></my-input-switch>
<label i18n>Track watch history</label>
</div>

View File

@ -11,16 +11,24 @@
.top-buttons {
margin-bottom: 30px;
display: flex;
display: grid;
grid-template-columns: 250px 1fr auto auto;
align-items: center;
flex-wrap: wrap;
#history-search {
@include peertube-input-text(250px);
.search-wrapper {
grid-column: 1;
input {
@include peertube-input-text(250px);
}
}
.history-switch {
grid-column: 3;
display: flex;
margin-left: auto;
margin-right: 15px;
label {
margin: 0 0 0 5px;
@ -31,6 +39,8 @@
}
.delete-history {
grid-column: 4;
@include peertube-button;
@include grey-button;
@include button-with-icon;
@ -40,26 +50,27 @@
}
.video {
@include row-blocks;
.my-video-miniature {
flex-grow: 1;
}
@include row-blocks($column-responsive: false);
}
@media screen and (max-width: $mobile-view) {
@media screen and (max-width: $small-view) {
.top-buttons {
.history-switch label, .delete-history {
@include ellipsis;
}
grid-template-columns: auto 1fr auto;
row-gap: 20px;
.history-switch label {
width: 60%;
.history-switch {
grid-row: 1;
grid-column: 1;
margin: 0;
}
.delete-history {
margin-left: auto;
max-width: 32%;
grid-row: 1;
grid-column: 3;
}
.search-wrapper {
grid-column: 1 / 4;
}
}
}

View File

@ -6,7 +6,7 @@
</span>
</h1>
<div class="video-subscriptions-header d-flex justify-content-between">
<div class="video-subscriptions-header">
<div class="has-feedback has-clear">
<input type="text" placeholder="Search your subscriptions" i18n-placeholder [(ngModel)]="subscriptionsSearch"
(ngModelChange)="onSubscriptionsSearchChanged()" />

View File

@ -13,36 +13,36 @@ input[type=text] {
margin-right: 10px;
}
}
.video-channel-info {
flex-grow: 1;
.video-channel-info {
flex-grow: 1;
a.video-channel-names {
@include disable-default-a-behaviour;
a.video-channel-names {
@include disable-default-a-behaviour;
width: fit-content;
display: flex;
align-items: baseline;
color: pvar(--mainForegroundColor);
width: fit-content;
display: flex;
align-items: baseline;
color: pvar(--mainForegroundColor);
.video-channel-display-name {
font-weight: $font-semibold;
font-size: 18px;
}
.video-channel-display-name {
font-weight: $font-semibold;
font-size: 18px;
}
.video-channel-name {
font-size: 14px;
color: $grey-actor-name;
margin-left: 5px;
}
.video-channel-name {
font-size: 14px;
color: $grey-actor-name;
margin-left: 5px;
}
}
}
.actor-owner {
@include actor-owner;
.actor-owner {
@include actor-owner;
margin-top: 0;
}
margin-top: 0;
}
.video-subscriptions-header {
@ -50,32 +50,22 @@ input[type=text] {
}
@media screen and (max-width: $small-view) {
.video-channel {
.video-channel-info {
padding-bottom: 10px;
text-align: center;
.video-subscriptions-header input[type=text] {
width: 100% !important;
}
.video-channel-names {
flex-direction: column;
align-items: center !important;
margin: auto;
}
}
.video-channel-info {
padding-bottom: 10px;
text-align: center;
img {
margin-right: 0;
.video-channel-names {
flex-direction: column;
align-items: center !important;
margin: auto;
}
}
}
@media screen and (max-width: $mobile-view) {
.video-subscriptions-header {
flex-direction: column;
input[type=text] {
width: 100% !important;
}
img {
margin-right: 0;
}
}

View File

@ -1,6 +1,6 @@
<div class="row">
<div class="root">
<div class="playlist-info col-xs-12 col-md-5 col-xl-3">
<div class="playlist-info">
<my-video-playlist-miniature
*ngIf="playlist" [playlist]="playlist" [toManage]="false" [displayChannel]="true"
[displayDescription]="true" [displayPrivacy]="true"
@ -20,7 +20,7 @@
</div>
<div class="playlist-elements col-xs-12 col-md-7 col-xl-9">
<div class="playlist-elements">
<div class="no-results" *ngIf="pagination.totalItems === 0">
<div i18n>No videos in this playlist.</div>

View File

@ -2,17 +2,21 @@
@import '_mixins';
@import '_miniature';
.root {
display: grid;
grid-template-columns: auto 1fr;
}
.playlist-info {
grid-column: 1;
background-color: pvar(--submenuBackgroundColor);
margin-left: calc(#{pvar(--horizontalMarginContent)} * -1);
margin-top: -$sub-menu-margin-bottom;
padding: 10px;
padding: 15px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
/* fix ellipsis dots background color */
::ng-deep .miniature-name::after {
@ -59,15 +63,35 @@
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
@media screen and (max-width: $small-view) {
.playlist-elements {
grid-column: 2;
}
my-video-playlist-miniature {
width: $video-thumbnail-width;
}
@include on-small-main-col {
my-video-playlist-miniature {
width: $video-thumbnail-medium-width;
}
}
@include on-mobile-main-col {
.root {
display: block;
}
.playlist-info {
width: 100vw;
width: calc(100% + (2 * var(--horizontalMarginContent)));
padding-top: 20px;
margin-bottom: 10px;
}
.playlist-elements {
padding: 0 !important;
my-video-playlist-miniature,
.playlist-buttons {
margin-left: auto;
margin-right: auto;
}
::ng-deep my-video-playlist-element-miniature {

View File

@ -1,8 +1,6 @@
<h1>
<span>
<my-global-icon iconName="playlists" aria-hidden="true"></my-global-icon>
<ng-container i18n>My playlists</ng-container> <span class="badge badge-secondary">{{ pagination.totalItems }}</span>
</span>
<my-global-icon iconName="playlists" aria-hidden="true"></my-global-icon>
<ng-container i18n>My playlists</ng-container> <span class="badge badge-secondary">{{ pagination.totalItems }}</span>
</h1>
<div class="video-playlists-header d-flex justify-content-between">
@ -21,10 +19,10 @@
<div class="video-playlists" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
<div *ngFor="let playlist of videoPlaylists" class="video-playlist">
<div class="miniature-wrapper">
<my-video-playlist-miniature [playlist]="playlist" [toManage]="true" [displayChannel]="true" [displayDescription]="true" [displayPrivacy]="true"
></my-video-playlist-miniature>
</div>
<my-video-playlist-miniature
[playlist]="playlist" [toManage]="true" [displayChannel]="true"
[displayDescription]="true" [displayPrivacy]="true" [displayAsRow]="true"
></my-video-playlist-miniature>
<div *ngIf="isRegularPlaylist(playlist)" class="video-playlist-buttons">
<my-delete-button label (click)="deleteVideoPlaylist(playlist)"></my-delete-button>

View File

@ -1,6 +1,10 @@
@import '_variables';
@import '_mixins';
h1 {
display: flex;
}
.create-button {
@include create-button;
}
@ -9,64 +13,45 @@ input[type=text] {
@include peertube-input-text(300px);
}
::ng-deep .action-button {
&.action-button-delete {
margin-right: 10px;
}
.video-playlist {
@include row-blocks($column-responsive: false);
}
.video-playlist {
@include row-blocks;
.miniature-wrapper {
flex-grow: 1;
::ng-deep .miniature {
display: flex;
.miniature-info {
margin-left: 10px;
width: auto;
}
}
}
.video-playlist-buttons {
min-width: 190px;
height: max-content;
}
.video-playlist-buttons {
display: flex;
margin-left: 10px;
align-self: flex-end;
}
.video-playlists-header {
margin-bottom: 30px;
}
@media screen and (max-width: $small-view) {
my-video-playlist-miniature {
display: block;
flex-grow: 1;
}
my-delete-button {
margin-right: 10px;
}
@include on-small-main-col {
.video-playlists-header {
text-align: center;
}
.video-playlist {
.video-playlist-buttons {
margin-top: 10px;
}
flex-wrap: wrap;
}
my-video-playlist-miniature ::ng-deep .miniature {
flex-direction: column;
.miniature-info {
margin-left: 0 !important;
}
.miniature-name {
max-width: $video-thumbnail-width;
}
.video-playlist-buttons {
margin-top: 10px;
margin-left: auto;
}
}
@media screen and (max-width: $mobile-view) {
@include on-mobile-main-col {
.video-playlists-header {
flex-direction: column;
@ -75,4 +60,8 @@ input[type=text] {
margin-bottom: 12px;
}
}
.action-button {
margin-left: 0;
}
}

View File

@ -32,36 +32,9 @@ h1 {
}
}
::ng-deep {
.video {
flex-wrap: wrap;
}
.action-button span {
white-space: nowrap;
}
.video-miniature {
&.display-as-row {
// width: min-content !important;
width: 100% !important;
.video-bottom .video-miniature-information {
width: max-content !important;
min-width: unset !important;
}
}
.video-bottom {
max-width: 350px;
}
}
}
.action-button {
display: flex;
margin-left: 55px;
margin-top: 10px;
margin-left: 10px;
align-self: flex-end;
}
@ -69,7 +42,7 @@ my-edit-button {
margin-right: 10px;
}
@media screen and (max-width: $small-view) {
@include on-small-main-col {
h1 {
flex-direction: column;
@ -80,39 +53,12 @@ my-edit-button {
}
.action-button {
flex-direction: column;
align-self: center;
align-items: center;
margin-left: 0px;
}
my-edit-button {
margin: 15px 0 5px 0;
width: 100%;
text-align: center;
::ng-deep {
.action-button {
/* same width than a.video-thumbnail */
width: $video-thumbnail-width;
}
}
}
::ng-deep {
.video-miniature {
align-items: center;
.video-bottom,
.video-bottom .video-miniature-information {
/* same width than a.video-thumbnail */
max-width: $video-thumbnail-width !important;
}
}
margin-top: 10px;
margin-left: auto;
}
}
@media screen and (max-width: $mobile-view) {
@include on-mobile-main-col {
.videos-header {
flex-direction: column;
@ -120,4 +66,8 @@ my-edit-button {
width: 100% !important;
}
}
.action-button {
margin-left: 0;
}
}

View File

@ -1,13 +1,13 @@
<div class="margin-content">
<div i18n class="title-page title-page-single">
<div i18n class="title-page title-page-single" *ngIf="pagination.totalItems">
Created {pagination.totalItems, plural, =1 {1 playlist} other {{{ pagination.totalItems }} playlists}}
</div>
<div i18n class="no-results" *ngIf="pagination.totalItems === 0">This channel does not have playlists.</div>
<div class="video-playlist" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()">
<div *ngFor="let playlist of videoPlaylists" class="playlist-miniature-container">
<my-video-playlist-miniature [playlist]="playlist" [toManage]="false"></my-video-playlist-miniature>
<div class="playlists" myInfiniteScroller (nearOfBottom)="onNearOfBottom()" [autoInit]="true" [dataObservable]="onDataSubject.asObservable()">
<div *ngFor="let playlist of videoPlaylists" class="playlist-wrapper">
<my-video-playlist-miniature [playlist]="playlist" [toManage]="false" [displayAsRow]="displayAsRow()"></my-video-playlist-miniature>
</div>
</div>
</div>

View File

@ -1,14 +1,33 @@
.title-page {
margin-top: 0;
}
@import '_variables';
@import '_mixins';
@import '_miniature';
.video-playlist {
.playlists {
display: flex;
flex-wrap: wrap;
justify-content: center;
.playlist-miniature-container {
.playlist-wrapper {
margin-right: 15px;
margin-bottom: 30px;
}
}
.margin-content {
@include grid-videos-miniature-layout;
}
@media screen and (max-width: $mobile-view) {
.title-page {
display: block;
text-align: center;
}
.playlists {
text-align: left !important;
justify-content: left !important;
margin-left: pvar(--horizontalMarginContent) !important;
margin-right: var(--horizontalMarginContent) !important;
}
}

View File

@ -1,6 +1,6 @@
import { Subject, Subscription } from 'rxjs'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { ComponentPagination, hasMoreItems } from '@app/core'
import { ComponentPagination, hasMoreItems, ScreenService } from '@app/core'
import { VideoChannel, VideoChannelService } from '@app/shared/shared-main'
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
@ -25,7 +25,8 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy {
constructor (
private videoPlaylistService: VideoPlaylistService,
private videoChannelService: VideoChannelService
private videoChannelService: VideoChannelService,
private screenService: ScreenService
) {}
ngOnInit () {
@ -48,6 +49,10 @@ export class VideoChannelPlaylistsComponent implements OnInit, OnDestroy {
this.loadVideoPlaylists()
}
displayAsRow () {
return this.screenService.isInMobileView()
}
private loadVideoPlaylists () {
this.videoPlaylistService.listChannelPlaylists(this.videoChannel, this.pagination)
.subscribe(res => {

View File

@ -16,11 +16,11 @@
}
.links {
@include fluid-videos-miniature-margins;
@include grid-videos-miniature-margins;
}
.channel-info {
@include fluid-videos-miniature-margins(false, 15px);
@include grid-videos-miniature-margins(false, 15px);
display: grid;
grid-template-columns: 1fr auto;

View File

@ -1,4 +1,4 @@
<div class="other-videos">
<div class="other-videos" [ngClass]="{ 'display-as-row': displayAsRow }">
<ng-container *ngIf="hasVideos$ | async">
<div class="title-page-container">
<h2 i18n class="title-page title-page-single">
@ -14,7 +14,7 @@
<ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count">
<my-video-miniature
[displayOptions]="displayOptions" [video]="video" [user]="userMiniature"
[displayOptions]="displayOptions" [video]="video" [user]="userMiniature" [displayAsRow]="displayAsRow"
(videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()" (videoAccountMuted)="onVideoRemoved()">
</my-video-miniature>

View File

@ -1,3 +1,6 @@
@import '_variables';
@import '_mixins';
.title-page-container {
display: flex;
justify-content: space-between;
@ -11,6 +14,10 @@
}
}
.title-page {
margin-top: 0;
}
.title-page-autoplay {
display: flex;
width: max-content;
@ -29,3 +36,29 @@
hr {
margin-top: 0;
}
my-video-miniature {
display: block;
}
.other-videos:not(.display-as-row) my-video-miniature {
min-width: $video-thumbnail-medium-width;
max-width: $video-thumbnail-medium-width;
}
.display-as-row {
my-video-miniature {
margin-bottom: 20px;
}
hr {
display: none;
}
@media screen and (max-width: $mobile-view) {
my-video-miniature {
margin-bottom: 10px;
}
}
}

View File

@ -16,6 +16,8 @@ import { RecommendedVideosStore } from './recommended-videos.store'
export class RecommendedVideosComponent implements OnInit, OnChanges {
@Input() inputRecommendation: RecommendationInfo
@Input() playlist: VideoPlaylist
@Input() displayAsRow: boolean
@Output() gotRecommendations = new EventEmitter<Video[]>()
autoPlayNextVideo: boolean

View File

@ -289,6 +289,7 @@
</div>
<my-recommended-videos
[displayAsRow]="displayOtherVideosAsRow()"
[inputRecommendation]="{ uuid: video.uuid, tags: video.tags }"
[playlist]="playlist"
(gotRecommendations)="onRecommendations($event)"

View File

@ -407,37 +407,12 @@ $video-info-margin-left: 44px;
}
}
}
}
::ng-deep .other-videos {
padding-left: 15px;
min-width: $video-miniature-width;
@media screen and (min-width: 1800px - (3* $video-miniature-width)) {
width: min-content;
}
.title-page {
margin: 0 !important;
}
.video-miniature {
display: flex;
width: max-content;
height: 100%;
padding-bottom: 20px;
flex-wrap: wrap;
}
.video-bottom {
@media screen and (max-width: 1800px - (3* $video-miniature-width)) {
margin-left: 1rem;
}
@media screen and (max-width: 500px) {
margin-left: 0;
margin-top: .5rem;
}
}
}
my-recommended-videos {
display: block;
padding-left: 15px;
min-width: 250px;
}
my-video-comments {
@ -531,6 +506,7 @@ my-video-comments {
}
}
// Use the same breakpoint than in the typescript component to display the other video miniatures as row
@media screen and (max-width: 1100px) {
#video-wrapper {
flex-direction: column;
@ -543,15 +519,10 @@ my-video-comments {
.video-bottom {
flex-direction: column;
}
::ng-deep .other-videos {
padding-left: 0 !important;
::ng-deep .video-miniature {
flex-direction: row;
width: auto;
}
}
my-recommended-videos {
padding-left: 0;
}
}
@ -573,10 +544,6 @@ my-video-comments {
}
}
::ng-deep .other-videos .video-miniature {
flex-direction: column;
}
.privacy-concerns {
width: 100%;
}

View File

@ -398,6 +398,11 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
this.loadVideo(videoId)
}
displayOtherVideosAsRow () {
// Use the same value as in the SASS file
return this.screenService.getWindowInnerWidth() <= 1100
}
private loadVideo (videoId: string) {
// Video did not change
if (this.video && this.video.uuid === videoId) return

View File

@ -14,7 +14,7 @@
</h1>
<div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
<my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true">
<my-video-miniature [video]="video" [user]="userMiniature" [displayVideoActions]="true">
</my-video-miniature>
</div>
</div>
@ -25,7 +25,7 @@
</h2>
<div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
<my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true">
<my-video-miniature [video]="video" [user]="userMiniature" [displayVideoActions]="true">
</my-video-miniature>
</div>
</div>
@ -40,7 +40,7 @@
</div>
<div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
<my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="true">
<my-video-miniature [video]="video" [user]="userMiniature" [displayVideoActions]="true">
</my-video-miniature>
</div>
</div>

View File

@ -8,9 +8,84 @@
}
.margin-content {
@include fluid-videos-miniature-layout;
@include grid-videos-miniature-layout;
}
.section {
@include miniature-rows;
&:first-child {
padding-top: 30px;
.section-title {
border-top: none !important;
}
}
.section-title {
font-size: 24px;
font-weight: $font-semibold;
padding-top: 15px;
margin-bottom: 15px;
display: flex;
justify-content: space-between;
&:not(h2) {
border-top: 1px solid $separator-border-color;
}
a {
&:hover, &:focus:not(.focus-visible), &:active {
text-decoration: none;
outline: none;
}
color: pvar(--mainForegroundColor);
}
}
&.channel {
.section-title {
a {
display: flex;
width: fit-content;
align-items: center;
img {
@include channel-avatar(28px);
margin-right: 8px;
}
}
.followers {
color: pvar(--greyForegroundColor);
font-weight: normal;
font-size: 14px;
margin-left: 10px;
position: relative;
top: 2px;
}
}
}
.show-more {
position: relative;
top: -5px;
display: inline-block;
font-size: 16px;
text-transform: uppercase;
color: pvar(--greyForegroundColor);
margin-bottom: 10px;
font-weight: $font-semibold;
text-decoration: none;
}
@media screen and (max-width: $mobile-view) {
max-height: initial;
overflow: initial;
.section-title {
font-size: 17px;
margin-left: 10px;
}
}
}

View File

@ -38,11 +38,10 @@ export class ScreenService {
let numberOfVideos = 1
if (screenWidth > 1850) numberOfVideos = 7
else if (screenWidth > 1600) numberOfVideos = 6
else if (screenWidth > 1370) numberOfVideos = 5
else if (screenWidth > 1100) numberOfVideos = 4
else if (screenWidth > 850) numberOfVideos = 3
if (screenWidth > 1850) numberOfVideos = 5
else if (screenWidth > 1600) numberOfVideos = 4
else if (screenWidth > 1370) numberOfVideos = 3
else if (screenWidth > 1100) numberOfVideos = 2
return numberOfVideos
}

View File

@ -52,7 +52,7 @@
<div class="video-wrapper">
<my-video-miniature
[fitWidth]="true" [video]="video" [user]="userMiniature"
[video]="video" [user]="userMiniature"
[displayVideoActions]="displayVideoActions" [displayOptions]="displayOptions"
(videoBlocked)="removeVideoFromArray(video)" (videoRemoved)="removeVideoFromArray(video)"
>

View File

@ -69,7 +69,7 @@ $iconSize: 16px;
}
.margin-content {
@include fluid-videos-miniature-layout;
@include grid-videos-miniature-layout;
}
@media screen and (max-width: $mobile-view) {

View File

@ -1,4 +1,4 @@
<div class="video-miniature" [ngClass]="{ 'display-as-row': displayAsRow, 'fit-width': fitWidth }" (mouseenter)="loadActions()">
<div class="video-miniature" [ngClass]="getClasses()" (mouseenter)="loadActions()">
<my-video-thumbnail
[video]="video" [nsfw]="isVideoBlur" [videoRouterLink]="videoRouterLink" [videoHref]="videoHref" [videoTarget]="videoTarget"
[displayWatchLaterPlaylist]="isWatchLaterPlaylistDisplayed()" [inWatchLaterPlaylist]="inWatchLaterPlaylist" (watchLaterClick)="onWatchLaterClick($event)"

View File

@ -3,204 +3,206 @@
@import '_miniature';
$more-button-width: 40px;
$more-margin-right: 15px;
.video-miniature {
display: inline-flex;
flex-direction: column;
padding-bottom: $video-miniature-margin-bottom;
vertical-align: top;
.video-miniature-name {
@include miniature-name;
}
.video-bottom {
display: flex;
.video-miniature-information {
width: calc(100% - #{$more-button-width});
}
.video-miniature-information {
width: $video-miniature-width - $more-button-width - $more-margin-right;
line-height: normal;
.avatar {
margin: 10px 10px 0 0;
.avatar {
margin: 10px 10px 0 0;
img:not(.channel) {
@include avatar(40px);
}
img.channel {
@include channel-avatar(40px);
}
}
.video-miniature-name {
@include miniature-name;
word-wrap: break-word;
width: calc(100% - #{$more-button-width});
}
.video-miniature-meta {
width: calc(100% + #{$more-button-width});
overflow: hidden;
}
.video-miniature-created-at-views {
display: block;
font-size: 13px;
}
.video-miniature-account,
.video-miniature-channel {
@include disable-default-a-behaviour;
@include ellipsis;
display: block;
font-size: 13px;
color: pvar(--greyForegroundColor);
&:hover {
color: $grey-foreground-hover-color;
}
}
.video-info-privacy,
.video-info-blocked .blocked-label,
.video-info-nsfw {
font-weight: $font-semibold;
}
.video-info-blocked {
color: red;
.blocked-reason::before {
content: ' - ';
}
}
.video-info-nsfw {
color: red;
}
}
.video-actions {
margin-top: 3px;
width: $more-button-width;
height: 30px;
::ng-deep .dropdown-root:not(.show) {
opacity: 0;
}
::ng-deep .playlist-dropdown.show + my-action-dropdown .dropdown-root {
opacity: 1;
}
::ng-deep .more-icon {
opacity: .6;
&:hover {
opacity: 1;
}
}
}
@media screen and (max-width: $small-view) {
.video-miniature-information {
margin: 0 10px;
}
.video-actions {
margin: 0;
top: -3px;
::ng-deep .dropdown-root {
opacity: 1 !important;
}
}
}
img:not(.channel) {
@include avatar(40px);
}
&:hover ::ng-deep .video-thumbnail .video-thumbnail-actions-overlay,
&:hover .video-bottom .video-actions ::ng-deep .dropdown-root {
img.channel {
@include channel-avatar(40px);
}
}
.video-miniature-created-at-views {
font-size: 13px;
}
.video-miniature-account,
.video-miniature-channel {
@include disable-default-a-behaviour;
@include ellipsis;
display: block;
font-size: 13px;
color: pvar(--greyForegroundColor);
&:hover {
color: $grey-foreground-hover-color;
}
}
.video-info-privacy,
.video-info-blocked .blocked-label,
.video-info-nsfw {
font-weight: $font-semibold;
}
.video-info-blocked {
color: red;
.blocked-reason::before {
content: ' - ';
}
}
.video-info-nsfw {
color: red;
}
.video-actions {
width: $more-button-width;
height: 30px;
::ng-deep .dropdown-root:not(.show) {
opacity: 0;
}
::ng-deep .playlist-dropdown.show + my-action-dropdown .dropdown-root {
opacity: 1;
}
&.fit-width {
width: 100%;
::ng-deep .more-icon {
opacity: .6;
.video-bottom {
width: 100% !important;
.video-miniature-information {
width: calc(100% - #{$more-button-width}) !important;
}
}
my-video-thumbnail {
@include large-screen-ratio($selector: '::ng-deep .video-thumbnail');
}
}
&.display-as-row {
flex-direction: row;
padding-bottom: 0;
height: auto;
display: flex;
flex-grow: 1;
my-video-thumbnail {
margin-right: 10px;
}
.video-bottom {
.video-miniature-information {
@media screen and (min-width: $small-view) {
width: auto;
min-width: 500px;
}
.video-miniature-name {
@include ellipsis-multiline(1.3em, 2);
margin-top: 2px;
margin-bottom: 5px;
}
.video-miniature-created-at-views,
.video-miniature-account,
.video-miniature-channel {
font-size: 95%;
width: fit-content;
}
.video-miniature-created-at-views + .video-miniature-channel {
margin-top: 5px;
}
.video-info-privacy {
margin-top: 5px;
}
.video-info-blocked {
margin-top: 3px;
}
}
.video-actions {
margin: 0;
top: -3px;
}
}
@media screen and (max-width: $small-view) {
flex-direction: column;
height: auto;
my-video-thumbnail {
margin-right: 0;
}
.video-miniature-information {
min-width: initial;
}
&:hover {
opacity: 1;
}
}
}
.video-miniature {
&:hover ::ng-deep .video-thumbnail-actions-overlay,
&:hover .video-actions ::ng-deep .dropdown-root {
opacity: 1 !important;
}
}
// Grid mode
// Takes all the width on mobile
.video-miniature:not(.display-as-row) {
display: flex;
flex-direction: column;
padding-bottom: $video-miniature-margin-bottom;
width: 100%;
my-video-thumbnail {
@include large-screen-ratio($selector: '::ng-deep .video-thumbnail');
}
.video-bottom {
display: flex;
width: 100%;
}
.video-miniature-name {
margin-top: 10px;
margin-bottom: 5px;
}
.video-miniature-created-at-views {
display: block;
}
.video-actions {
margin-top: 3px;
}
@media screen and (max-width: $small-view) {
width: 100%;
margin-bottom: 25px;
.video-miniature-information {
margin: 0 10px;
width: 100%;
text-align: left;
}
.video-actions {
margin: 0;
top: -3px;
::ng-deep .dropdown-root {
opacity: 1 !important;
}
}
::ng-deep .video-thumbnail {
border-radius: 0;
}
}
}
.video-miniature.display-as-row {
--rowThumbnailWidth: #{$video-thumbnail-width};
--rowThumbnailHeight: #{$video-thumbnail-height};
display: flex;
flex-direction: row;
.video-bottom {
display: flex;
}
// We don't display avatar in row mode
.avatar {
display: none;
}
my-video-thumbnail {
min-width: var(--rowThumbnailWidth);
max-width: var(--rowThumbnailWidth);
height: var(--rowThumbnailHeight);
margin-right: 10px;
}
.video-miniature-name {
@include ellipsis-multiline(1.3em, 2);
}
.video-miniature-created-at-views,
.video-miniature-account,
.video-miniature-channel {
font-size: 14px;
}
.video-actions {
margin-top: -3px;
}
}
@include on-small-main-col {
.video-miniature.display-as-row {
--rowThumbnailWidth: #{$video-thumbnail-medium-width};
--rowThumbnailHeight: #{$video-thumbnail-medium-height};
}
}
@include on-mobile-main-col {
.video-miniature.display-as-row {
--rowThumbnailWidth: #{$video-thumbnail-small-width};
--rowThumbnailHeight: #{$video-thumbnail-small-height};
.video-miniature-name {
font-size: 14px;
}
.video-miniature-created-at-views,
.video-miniature-account,
.video-miniature-channel {
font-size: 12px;
}
}
}

View File

@ -16,7 +16,6 @@ import { Video } from '../shared-main'
import { VideoPlaylistService } from '../shared-video-playlist'
import { VideoActionsDisplayType } from './video-actions-dropdown.component'
export type OwnerDisplayType = 'account' | 'videoChannel'
export type MiniatureDisplayOptions = {
date?: boolean
views?: boolean
@ -50,9 +49,9 @@ export class VideoMiniatureComponent implements OnInit {
state: false,
blacklistInfo: false
}
@Input() displayAsRow = false
@Input() displayVideoActions = true
@Input() fitWidth = false
@Input() displayAsRow = false
@Input() videoLinkType: VideoLinkType = 'internal'
@ -243,6 +242,12 @@ export class VideoMiniatureComponent implements OnInit {
return this.displayVideoActions && this.isUserLoggedIn() && this.inWatchLaterPlaylist !== undefined
}
getClasses () {
return {
'display-as-row': this.displayAsRow
}
}
private setUpBy () {
const accountName = this.video.account.name

View File

@ -5,24 +5,24 @@
display: flex;
justify-content: flex-end;
flex-grow: 1;
}
.action-selection-mode-child {
position: fixed;
.action-selection-mode-child {
position: fixed;
.action-button {
display: block;
margin-left: 55px;
}
.action-button-cancel-selection {
@include peertube-button;
@include grey-button;
}
.action-button {
display: block;
margin-left: 55px;
}
}
.action-button-cancel-selection {
@include peertube-button;
@include grey-button;
}
.video {
@include row-blocks;
@include row-blocks($column-responsive: false);
&:first-child {
margin-top: 47px;
@ -40,18 +40,16 @@
}
}
@media screen and (max-width: $small-view) {
@include on-small-main-col {
.video {
flex-direction: column;
height: auto;
flex-wrap: wrap;
}
}
.checkbox-container {
display: none;
}
my-button {
margin-top: 10px;
}
@include on-mobile-main-col {
.checkbox-container {
display: none;
}
.action-selection-mode {

View File

@ -1,4 +1,4 @@
<div class="miniature" [ngClass]="{ 'no-videos': playlist.videosLength === 0, 'to-manage': toManage }">
<div class="miniature" [ngClass]="{ 'no-videos': playlist.videosLength === 0, 'to-manage': toManage, 'display-as-row': displayAsRow }">
<a
[routerLink]="getPlaylistUrl()" [attr.title]="playlist.description"
class="miniature-thumbnail"

View File

@ -4,6 +4,7 @@
.miniature {
display: inline-block;
width: 100%;
&.no-videos:not(.to-manage){
a {
@ -17,62 +18,92 @@
display: none;
}
}
}
.miniature-thumbnail {
@include miniature-thumbnail;
.miniature-thumbnail {
@include miniature-thumbnail;
.miniature-playlist-info-overlay {
@include static-thumbnail-overlay;
.miniature-playlist-info-overlay {
@include static-thumbnail-overlay;
position: absolute;
right: 0;
bottom: 0;
height: $video-thumbnail-height;
padding: 0 10px;
display: flex;
align-items: center;
font-size: 14px;
font-weight: $font-semibold;
}
}
.miniature-info {
width: 200px;
margin-top: 2px;
line-height: normal;
.miniature-name {
@include miniature-name;
@include ellipsis-multiline(1.3em, 2);
margin: 0;
}
.by {
@include disable-default-a-behaviour;
display: block;
color: pvar(--greyForegroundColor);
}
.privacy-date {
margin-top: 5px;
.video-info-privacy {
font-size: 14px;
font-weight: $font-semibold;
&::after {
content: '-';
margin: 0 3px;
}
}
}
.video-info-description {
margin-top: 10px;
color: pvar(--greyForegroundColor);
}
position: absolute;
right: 0;
bottom: 0;
height: 100%;
padding: 0 10px;
display: flex;
align-items: center;
font-size: 14px;
font-weight: $font-semibold;
}
}
.miniature-info {
.miniature-name {
@include miniature-name;
@include ellipsis-multiline(1.3em, 2);
margin: 0;
}
.by {
@include disable-default-a-behaviour;
display: block;
color: pvar(--greyForegroundColor);
}
.privacy-date {
margin-top: 5px;
.video-info-privacy {
font-size: 14px;
font-weight: $font-semibold;
&::after {
content: '-';
margin: 0 3px;
}
}
}
.video-info-description {
margin-top: 10px;
color: pvar(--greyForegroundColor);
}
}
.miniature:not(.display-as-row) {
.miniature-thumbnail {
margin-top: 10px;
margin-bottom: 5px;
}
}
.miniature.display-as-row {
--rowThumbnailWidth: #{$video-thumbnail-width};
--rowThumbnailHeight: #{$video-thumbnail-height};
display: flex;
.miniature-thumbnail {
width: var(--rowThumbnailWidth);
height: var(--rowThumbnailHeight);
margin-right: 10px;
}
}
@include on-small-main-col {
.miniature.display-as-row {
--rowThumbnailWidth: #{$video-thumbnail-medium-width};
--rowThumbnailHeight: #{$video-thumbnail-medium-height};
}
}
@include on-mobile-main-col {
.miniature.display-as-row {
--rowThumbnailWidth: #{$video-thumbnail-small-width};
--rowThumbnailHeight: #{$video-thumbnail-small-height};
}
}

View File

@ -12,6 +12,7 @@ export class VideoPlaylistMiniatureComponent {
@Input() displayChannel = false
@Input() displayDescription = false
@Input() displayPrivacy = false
@Input() displayAsRow = false
getPlaylistUrl () {
if (this.toManage) return [ '/my-library/video-playlists', this.playlist.uuid ]

View File

@ -58,6 +58,7 @@ body {
--activatedActionButtonColor: #{$activated-action-button-color};
--horizontalMarginContent: #{$not-expanded-horizontal-margins};
--videosHorizontalMarginContent: 6vw;
--mainColWidth: calc(100vw - #{$menu-width});
font-family: $main-fonts;
@ -332,8 +333,16 @@ ngx-loading-bar {
}
@media screen and (max-width: #{breakpoint(xxl)}) {
.main-col.expanded {
--horizontalMarginContent: #{$expanded-horizontal-margins/2};
.main-col {
& {
--horizontalMarginContent: #{$not-expanded-horizontal-margins / 2};
}
&.expanded {
--horizontalMarginContent: #{$expanded-horizontal-margins / 2};
}
--videosHorizontalMarginContent: #{pvar(--horizontalMarginContent)};
}
}
@ -341,7 +350,7 @@ ngx-loading-bar {
/* the following applies from 500px to 900px and is partially overriden from 500px to 800px by changes below to $small-view */
.main-col,
.main-col.expanded {
--horizontalMarginContent: #{$expanded-horizontal-margins/3};
--horizontalMarginContent: #{$expanded-horizontal-margins / 3};
.sub-menu {
padding-left: 50px;

View File

@ -5,11 +5,10 @@
@include ellipsis-multiline(1.1em, 2);
word-break: break-all;
word-wrap: break-word;
transition: color 0.2s;
font-weight: $font-semibold;
color: pvar(--mainForegroundColor);
margin-top: 10px;
margin-bottom: 5px;
&:hover {
text-decoration: none;
@ -21,20 +20,20 @@
}
}
$play-overlay-transition: 0.2s ease;
$play-overlay-height: 26px;
$play-overlay-width: 18px;
@mixin miniature-thumbnail {
@include disable-outline;
$play-overlay-transition: 0.2s ease;
$play-overlay-height: 26px;
$play-overlay-width: 18px;
display: flex;
flex-direction: column;
position: relative;
border-radius: 3px;
width: 100%;
height: 100%;
overflow: hidden;
width: $video-thumbnail-width;
height: $video-thumbnail-height;
background-color: #ececec;
transition: filter $play-overlay-transition;
@ -98,140 +97,36 @@ $play-overlay-width: 18px;
color: #fff;
}
@mixin miniature-rows {
&:first-child {
padding-top: 30px;
.section-title {
border-top: none !important;
}
}
.section-title {
font-size: 24px;
font-weight: $font-semibold;
padding-top: 15px;
margin-bottom: 15px;
display: flex;
justify-content: space-between;
&:not(h2) {
border-top: 1px solid $separator-border-color;
}
a {
&:hover, &:focus:not(.focus-visible), &:active {
text-decoration: none;
outline: none;
}
color: pvar(--mainForegroundColor);
}
}
&.channel {
.section-title {
a {
display: flex;
width: fit-content;
align-items: center;
img {
@include channel-avatar(28px);
margin-right: 8px;
}
}
.followers {
color: pvar(--greyForegroundColor);
font-weight: normal;
font-size: 14px;
margin-left: 10px;
position: relative;
top: 2px;
}
}
}
.show-more {
position: relative;
top: -5px;
display: inline-block;
font-size: 16px;
text-transform: uppercase;
color: pvar(--greyForegroundColor);
margin-bottom: 10px;
font-weight: $font-semibold;
text-decoration: none;
}
@media screen and (max-width: $mobile-view) {
max-height: initial;
overflow: initial;
.section-title {
font-size: 17px;
margin-left: 10px;
}
}
}
// Use margin by default, or padding if $margin is false
@mixin fluid-videos-miniature-margins ($margin: true, $min-margin: 0) {
--fluidVideosMiniatureMargins: #{pvar(--horizontalMarginContent)};
@mixin grid-videos-miniature-margins ($margin: true, $min-margin: 0) {
--gridVideosMiniatureMargins: #{pvar(--videosHorizontalMarginContent)};
@if $margin {
margin-left: var(--fluidVideosMiniatureMargins) !important;
margin-right: var(--fluidVideosMiniatureMargins) !important;
margin-left: var(--gridVideosMiniatureMargins) !important;
margin-right: var(--gridVideosMiniatureMargins) !important;
} @else {
padding-left: var(--fluidVideosMiniatureMargins) !important;
padding-right: var(--fluidVideosMiniatureMargins) !important;
padding-left: var(--gridVideosMiniatureMargins) !important;
padding-right: var(--gridVideosMiniatureMargins) !important;
}
@media screen and (max-width: $mobile-view) {
--fluidVideosMiniatureMargins: $min-margin;
--gridVideosMiniatureMargins: #{$min-margin};
width: auto;
}
}
@mixin fluid-videos-miniature-layout {
@include fluid-videos-miniature-margins;
@media screen and (max-width: $mobile-view) {
.videos {
text-align: center;
::ng-deep .video-miniature {
padding-right: 0;
height: auto;
width: 100%;
margin-bottom: 25px;
.video-miniature-information {
width: 100% !important;
text-align: left;
span {
width: 100%;
}
}
.video-thumbnail {
border-radius: 0;
}
}
}
}
@mixin grid-videos-miniature-layout {
@include grid-videos-miniature-margins;
@media screen and (min-width: $mobile-view) {
.videos {
--miniatureMinWidth: #{$video-thumbnail-width - 15px};
.videos,
.playlists {
--miniatureMinWidth: #{$video-thumbnail-width - 25px};
--miniatureMaxWidth: #{$video-thumbnail-width};
display: grid;
column-gap: 5px;
column-gap: 30px;
grid-template-columns: repeat(
auto-fill,
minmax(
@ -240,21 +135,33 @@ $play-overlay-width: 18px;
)
);
@media screen and (min-width: #{breakpoint(fhd)}) {
column-gap: 1%;
--miniatureMinWidth: #{$video-thumbnail-width};
}
.video-wrapper {
.video-wrapper,
.playlist-wrapper {
margin: 0 auto;
width: 100%;
my-video-miniature {
my-video-miniature,
my-video-playlist-miniature {
display: block;
min-width: var(--miniatureMinWidth);
max-width: var(--miniatureMaxWidth);
}
}
@media screen and (min-width: #{breakpoint(xm)}) {
column-gap: 15px;
}
@media screen and (min-width: #{breakpoint(fhd)}) {
column-gap: 2%;
}
}
}
@media screen and (max-width: $mobile-view) {
.videos,
.playlists {
text-align: center;
}
}
}

View File

@ -620,17 +620,23 @@
@include button-with-icon(20px, 5px, -1px);
}
@mixin row-blocks {
@mixin row-blocks ($column-responsive: true) {
display: flex;
min-height: 130px;
padding-bottom: 20px;
margin-bottom: 20px;
border-bottom: 1px solid #C6C6C6;
@media screen and (max-width: 800px) {
flex-direction: column;
height: auto;
align-items: center;
@media screen and (max-width: $small-view) {
@if $column-responsive {
flex-direction: column;
height: auto;
align-items: center;
} @else {
min-height: initial;
padding-bottom: 10px;
margin-bottom: 10px;
}
}
}
@ -932,3 +938,31 @@
border-left: $width solid rgba(255, 255, 255, 0.95);
}
@mixin on-small-main-col () {
:host-context(.main-col:not(.expanded)) {
@media screen and (max-width: $small-view + $menu-width) {
@content;
}
}
:host-context(.main-col.expanded) {
@media screen and (max-width: $small-view) {
@content;
}
}
}
@mixin on-mobile-main-col () {
:host-context(.main-col:not(.expanded)) {
@media screen and (max-width: $mobile-view + $menu-width) {
@content;
}
}
:host-context(.main-col.expanded) {
@media screen and (max-width: $mobile-view) {
@content;
}
}
}

View File

@ -34,7 +34,7 @@ $green: #39CC0B;
$grey-actor-name: #777272;
$expanded-horizontal-margins: 150px;
$not-expanded-horizontal-margins: 60px;
$not-expanded-horizontal-margins: 30px;
$button-height: 30px;
@ -59,10 +59,13 @@ $footer-margin: 30px;
$separator-border-color: rgba(0, 0, 0, 0.10);
$video-miniature-width: 238px;
$video-miniature-margin-bottom: 15px;
$video-thumbnail-height: 122px;
$video-thumbnail-width: 223px;
$video-thumbnail-height: 153px;
$video-thumbnail-width: 280px;
$video-thumbnail-medium-height: 114px;
$video-thumbnail-medium-width: 201px;
$video-thumbnail-small-height: 71px;
$video-thumbnail-small-width: 125px;
$theater-bottom-space: 115px;
@ -125,6 +128,7 @@ $variables: (
--embedBigPlayBackgroundColor: var(--embedBigPlayBackgroundColor),
--horizontalMarginContent: var(--horizontalMarginContent),
--videosHorizontalMarginContent: var(--videosHorizontalMarginContent),
--mainColWidth: var(--mainColWidth)
);