diff options
author | Rigel Kent <sendmemail@rigelk.eu> | 2020-02-05 20:54:37 +0100 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-02-13 10:25:22 +0100 |
commit | 24e7916c6897bbb38e057cdf1a102286006be964 (patch) | |
tree | 7621dd83d532ba04b725f4feeb902ccbdb6669ff | |
parent | eb7c7a517902eae425b05d1ca9cb7f99f76ee71f (diff) | |
download | PeerTube-24e7916c6897bbb38e057cdf1a102286006be964.tar.gz PeerTube-24e7916c6897bbb38e057cdf1a102286006be964.tar.zst PeerTube-24e7916c6897bbb38e057cdf1a102286006be964.zip |
Add ListOverflow component to prevent sub-menu overflow
26 files changed, 283 insertions, 53 deletions
diff --git a/client/src/app/+about/about-instance/contact-admin-modal.component.ts b/client/src/app/+about/about-instance/contact-admin-modal.component.ts index 2ed41e741..d5e146b82 100644 --- a/client/src/app/+about/about-instance/contact-admin-modal.component.ts +++ b/client/src/app/+about/about-instance/contact-admin-modal.component.ts | |||
@@ -51,7 +51,7 @@ export class ContactAdminModalComponent extends FormReactive implements OnInit { | |||
51 | } | 51 | } |
52 | 52 | ||
53 | show () { | 53 | show () { |
54 | this.openedModal = this.modalService.open(this.modal, { keyboard: false }) | 54 | this.openedModal = this.modalService.open(this.modal, { centered: true, keyboard: false }) |
55 | } | 55 | } |
56 | 56 | ||
57 | hide () { | 57 | hide () { |
diff --git a/client/src/app/+accounts/accounts.component.html b/client/src/app/+accounts/accounts.component.html index 915bee0a2..b982fba9a 100644 --- a/client/src/app/+accounts/accounts.component.html +++ b/client/src/app/+accounts/accounts.component.html | |||
@@ -38,12 +38,12 @@ | |||
38 | </div> | 38 | </div> |
39 | </div> | 39 | </div> |
40 | 40 | ||
41 | <div class="links"> | 41 | <div class="links w-100"> |
42 | <a i18n routerLink="video-channels" routerLinkActive="active" class="title-page">Video channels</a> | 42 | <ng-template #linkTemplate let-item="item"> |
43 | <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a> | ||
44 | </ng-template> | ||
43 | 45 | ||
44 | <a i18n routerLink="videos" routerLinkActive="active" class="title-page">Videos</a> | 46 | <list-overflow [items]="links" [itemTemplate]="linkTemplate"></list-overflow> |
45 | |||
46 | <a i18n routerLink="about" routerLinkActive="active" class="title-page">About</a> | ||
47 | </div> | 47 | </div> |
48 | </div> | 48 | </div> |
49 | 49 | ||
diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts index a8157de0e..4fea0e4ed 100644 --- a/client/src/app/+accounts/accounts.component.ts +++ b/client/src/app/+accounts/accounts.component.ts | |||
@@ -10,6 +10,7 @@ import { User, UserRight } from '../../../../shared' | |||
10 | import { I18n } from '@ngx-translate/i18n-polyfill' | 10 | import { I18n } from '@ngx-translate/i18n-polyfill' |
11 | import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' | 11 | import { VideoChannelService } from '@app/shared/video-channel/video-channel.service' |
12 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' | 12 | import { VideoChannel } from '@app/shared/video-channel/video-channel.model' |
13 | import { ListOverflowItem } from '@app/shared/misc/list-overflow.component' | ||
13 | 14 | ||
14 | @Component({ | 15 | @Component({ |
15 | templateUrl: './accounts.component.html', | 16 | templateUrl: './accounts.component.html', |
@@ -19,6 +20,7 @@ export class AccountsComponent implements OnInit, OnDestroy { | |||
19 | account: Account | 20 | account: Account |
20 | accountUser: User | 21 | accountUser: User |
21 | videoChannels: VideoChannel[] = [] | 22 | videoChannels: VideoChannel[] = [] |
23 | links: ListOverflowItem[] = [] | ||
22 | 24 | ||
23 | isAccountManageable = false | 25 | isAccountManageable = false |
24 | accountFollowerTitle = '' | 26 | accountFollowerTitle = '' |
@@ -70,6 +72,12 @@ export class AccountsComponent implements OnInit, OnDestroy { | |||
70 | 72 | ||
71 | err => this.notifier.error(err.message) | 73 | err => this.notifier.error(err.message) |
72 | ) | 74 | ) |
75 | |||
76 | this.links = [ | ||
77 | { label: this.i18n('Video channels'), routerLink: 'video-channels' }, | ||
78 | { label: this.i18n('Videos'), routerLink: 'videos' }, | ||
79 | { label: this.i18n('About'), routerLink: 'about' } | ||
80 | ] | ||
73 | } | 81 | } |
74 | 82 | ||
75 | ngOnDestroy () { | 83 | ngOnDestroy () { |
diff --git a/client/src/app/+admin/admin.component.html b/client/src/app/+admin/admin.component.html index 0d06aaedc..6c98fe453 100644 --- a/client/src/app/+admin/admin.component.html +++ b/client/src/app/+admin/admin.component.html | |||
@@ -1,28 +1,10 @@ | |||
1 | <div class="row"> | 1 | <div class="row"> |
2 | <div class="sub-menu"> | 2 | <div class="sub-menu"> |
3 | <a i18n *ngIf="hasUsersRight()" routerLink="/admin/users" routerLinkActive="active" class="title-page"> | 3 | <ng-template #linkTemplate let-item="item"> |
4 | Users | 4 | <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a> |
5 | </a> | 5 | </ng-template> |
6 | 6 | ||
7 | <a i18n *ngIf="hasServerFollowRight()" routerLink="/admin/follows" routerLinkActive="active" class="title-page"> | 7 | <list-overflow [items]="items" [itemTemplate]="linkTemplate"></list-overflow> |
8 | Follows & redundancies | ||
9 | </a> | ||
10 | |||
11 | <a i18n *ngIf="hasVideoAbusesRight() || hasVideoBlacklistRight()" routerLink="/admin/moderation" routerLinkActive="active" class="title-page"> | ||
12 | Moderation | ||
13 | </a> | ||
14 | |||
15 | <a i18n *ngIf="hasConfigRight()" routerLink="/admin/config" routerLinkActive="active" class="title-page"> | ||
16 | Configuration | ||
17 | </a> | ||
18 | |||
19 | <a i18n *ngIf="hasPluginsRight()" routerLink="/admin/plugins" routerLinkActive="active" class="title-page"> | ||
20 | Plugins/Themes | ||
21 | </a> | ||
22 | |||
23 | <a i18n *ngIf="hasJobsRight() || hasLogsRight() || hasDebugRight()" routerLink="/admin/system" routerLinkActive="active" class="title-page"> | ||
24 | System | ||
25 | </a> | ||
26 | </div> | 8 | </div> |
27 | 9 | ||
28 | <div class="margin-content"> | 10 | <div class="margin-content"> |
diff --git a/client/src/app/+admin/admin.component.ts b/client/src/app/+admin/admin.component.ts index b23999d40..9662522dc 100644 --- a/client/src/app/+admin/admin.component.ts +++ b/client/src/app/+admin/admin.component.ts | |||
@@ -1,12 +1,28 @@ | |||
1 | import { Component } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { UserRight } from '../../../../shared' | 2 | import { UserRight } from '../../../../shared' |
3 | import { AuthService } from '../core/auth/auth.service' | 3 | import { AuthService } from '../core/auth/auth.service' |
4 | import { ListOverflowItem } from '@app/shared/misc/list-overflow.component' | ||
5 | import { I18n } from '@ngx-translate/i18n-polyfill' | ||
4 | 6 | ||
5 | @Component({ | 7 | @Component({ |
6 | templateUrl: './admin.component.html' | 8 | templateUrl: './admin.component.html' |
7 | }) | 9 | }) |
8 | export class AdminComponent { | 10 | export class AdminComponent implements OnInit { |
9 | constructor (private auth: AuthService) {} | 11 | items: ListOverflowItem[] = [] |
12 | |||
13 | constructor ( | ||
14 | private auth: AuthService, | ||
15 | private i18n: I18n | ||
16 | ) {} | ||
17 | |||
18 | ngOnInit () { | ||
19 | if (this.hasUsersRight()) this.items.push({ label: this.i18n('Users'), routerLink: '/admin/users' }) | ||
20 | if (this.hasServerFollowRight()) this.items.push({ label: this.i18n('Follows & redundancies'), routerLink: '/admin/follows' }) | ||
21 | if (this.hasVideoAbusesRight() || this.hasVideoBlacklistRight()) this.items.push({ label: this.i18n('Moderation'), routerLink: '/admin/moderation' }) | ||
22 | if (this.hasConfigRight()) this.items.push({ label: this.i18n('Configuration'), routerLink: '/admin/config' }) | ||
23 | if (this.hasPluginsRight()) this.items.push({ label: this.i18n('Plugins/Themes'), routerLink: '/admin/plugins' }) | ||
24 | if (this.hasJobsRight() || this.hasLogsRight() || this.hasDebugRight()) this.items.push({ label: this.i18n('System'), routerLink: '/admin/system' }) | ||
25 | } | ||
10 | 26 | ||
11 | hasUsersRight () { | 27 | hasUsersRight () { |
12 | return this.auth.getUser().hasRight(UserRight.MANAGE_USERS) | 28 | return this.auth.getUser().hasRight(UserRight.MANAGE_USERS) |
diff --git a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts b/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts index f8a5ef8cb..29f90194b 100644 --- a/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts +++ b/client/src/app/+admin/moderation/video-abuse-list/moderation-comment-modal.component.ts | |||
@@ -38,7 +38,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI | |||
38 | 38 | ||
39 | openModal (abuseToComment: VideoAbuse) { | 39 | openModal (abuseToComment: VideoAbuse) { |
40 | this.abuseToComment = abuseToComment | 40 | this.abuseToComment = abuseToComment |
41 | this.openedModal = this.modalService.open(this.modal) | 41 | this.openedModal = this.modalService.open(this.modal, { centered: true }) |
42 | 42 | ||
43 | this.form.patchValue({ | 43 | this.form.patchValue({ |
44 | moderationComment: this.abuseToComment.moderationComment | 44 | moderationComment: this.abuseToComment.moderationComment |
diff --git a/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.ts b/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.ts index 6df929ec9..d5682914e 100644 --- a/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.ts +++ b/client/src/app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component.ts | |||
@@ -53,7 +53,7 @@ export class MyAccountAcceptOwnershipComponent extends FormReactive implements O | |||
53 | show (videoChangeOwnership: VideoChangeOwnership) { | 53 | show (videoChangeOwnership: VideoChangeOwnership) { |
54 | this.videoChangeOwnership = videoChangeOwnership | 54 | this.videoChangeOwnership = videoChangeOwnership |
55 | this.modalService | 55 | this.modalService |
56 | .open(this.modal) | 56 | .open(this.modal, { centered: true }) |
57 | .result | 57 | .result |
58 | .then(() => this.acceptOwnership()) | 58 | .then(() => this.acceptOwnership()) |
59 | .catch(() => this.videoChangeOwnership = undefined) | 59 | .catch(() => this.videoChangeOwnership = undefined) |
diff --git a/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts b/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts index 36d1ea091..f4e2b5955 100644 --- a/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts +++ b/client/src/app/+my-account/my-account-videos/video-change-ownership/video-change-ownership.component.ts | |||
@@ -43,7 +43,7 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni | |||
43 | show (video: Video) { | 43 | show (video: Video) { |
44 | this.video = video | 44 | this.video = video |
45 | this.modalService | 45 | this.modalService |
46 | .open(this.modal) | 46 | .open(this.modal, { centered: true }) |
47 | .result | 47 | .result |
48 | .then(() => this.changeOwnership()) | 48 | .then(() => this.changeOwnership()) |
49 | .catch((_) => _) // Called when closing (cancel) the modal without validating, do nothing | 49 | .catch((_) => _) // Called when closing (cancel) the modal without validating, do nothing |
diff --git a/client/src/app/+video-channels/video-channels.component.html b/client/src/app/+video-channels/video-channels.component.html index debda9948..735a8f2c8 100644 --- a/client/src/app/+video-channels/video-channels.component.html +++ b/client/src/app/+video-channels/video-channels.component.html | |||
@@ -29,10 +29,12 @@ | |||
29 | </div> | 29 | </div> |
30 | </div> | 30 | </div> |
31 | 31 | ||
32 | <div class="links"> | 32 | <div class="links w-100"> |
33 | <a i18n routerLink="videos" routerLinkActive="active" class="title-page">Videos</a> | 33 | <ng-template #linkTemplate let-item="item"> |
34 | <a i18n routerLink="video-playlists" routerLinkActive="active" class="title-page">Video playlists</a> | 34 | <a [routerLink]="item.routerLink" routerLinkActive="active" class="title-page">{{ item.label }}</a> |
35 | <a i18n routerLink="about" routerLinkActive="active" class="title-page">About</a> | 35 | </ng-template> |
36 | |||
37 | <list-overflow [items]="links" [itemTemplate]="linkTemplate"></list-overflow> | ||
36 | </div> | 38 | </div> |
37 | </div> | 39 | </div> |
38 | 40 | ||
diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts index 5ca9581a8..0889ca854 100644 --- a/client/src/app/+video-channels/video-channels.component.ts +++ b/client/src/app/+video-channels/video-channels.component.ts | |||
@@ -9,6 +9,7 @@ import { AuthService, Notifier } from '@app/core' | |||
9 | import { Hotkey, HotkeysService } from 'angular2-hotkeys' | 9 | import { Hotkey, HotkeysService } from 'angular2-hotkeys' |
10 | import { SubscribeButtonComponent } from '@app/shared/user-subscription/subscribe-button.component' | 10 | import { SubscribeButtonComponent } from '@app/shared/user-subscription/subscribe-button.component' |
11 | import { I18n } from '@ngx-translate/i18n-polyfill' | 11 | import { I18n } from '@ngx-translate/i18n-polyfill' |
12 | import { ListOverflowItem } from '@app/shared/misc/list-overflow.component' | ||
12 | 13 | ||
13 | @Component({ | 14 | @Component({ |
14 | templateUrl: './video-channels.component.html', | 15 | templateUrl: './video-channels.component.html', |
@@ -19,6 +20,7 @@ export class VideoChannelsComponent implements OnInit, OnDestroy { | |||
19 | 20 | ||
20 | videoChannel: VideoChannel | 21 | videoChannel: VideoChannel |
21 | hotkeys: Hotkey[] | 22 | hotkeys: Hotkey[] |
23 | links: ListOverflowItem[] = [] | ||
22 | isChannelManageable = false | 24 | isChannelManageable = false |
23 | 25 | ||
24 | private routeSub: Subscription | 26 | private routeSub: Subscription |
@@ -62,6 +64,12 @@ export class VideoChannelsComponent implements OnInit, OnDestroy { | |||
62 | }, undefined, this.i18n('Subscribe to the account')) | 64 | }, undefined, this.i18n('Subscribe to the account')) |
63 | ] | 65 | ] |
64 | if (this.isUserLoggedIn()) this.hotkeysService.add(this.hotkeys) | 66 | if (this.isUserLoggedIn()) this.hotkeysService.add(this.hotkeys) |
67 | |||
68 | this.links = [ | ||
69 | { label: this.i18n('Videos'), routerLink: 'videos' }, | ||
70 | { label: this.i18n('Video playlists'), routerLink: 'video-playlists' }, | ||
71 | { label: this.i18n('About'), routerLink: 'about' } | ||
72 | ] | ||
65 | } | 73 | } |
66 | 74 | ||
67 | ngOnDestroy () { | 75 | ngOnDestroy () { |
diff --git a/client/src/app/menu/language-chooser.component.ts b/client/src/app/menu/language-chooser.component.ts index 4a6e4c75a..43f622dfb 100644 --- a/client/src/app/menu/language-chooser.component.ts +++ b/client/src/app/menu/language-chooser.component.ts | |||
@@ -21,7 +21,7 @@ export class LanguageChooserComponent { | |||
21 | } | 21 | } |
22 | 22 | ||
23 | show () { | 23 | show () { |
24 | this.modalService.open(this.modal) | 24 | this.modalService.open(this.modal, { centered: true }) |
25 | } | 25 | } |
26 | 26 | ||
27 | buildLanguageLink (lang: { id: string }) { | 27 | buildLanguageLink (lang: { id: string }) { |
diff --git a/client/src/app/modal/instance-config-warning-modal.component.ts b/client/src/app/modal/instance-config-warning-modal.component.ts index 742a7dd41..5e1433548 100644 --- a/client/src/app/modal/instance-config-warning-modal.component.ts +++ b/client/src/app/modal/instance-config-warning-modal.component.ts | |||
@@ -24,7 +24,7 @@ export class InstanceConfigWarningModalComponent { | |||
24 | show (about: About) { | 24 | show (about: About) { |
25 | this.about = about | 25 | this.about = about |
26 | 26 | ||
27 | const ref = this.modalService.open(this.modal) | 27 | const ref = this.modalService.open(this.modal, { centered: true }) |
28 | 28 | ||
29 | ref.result.finally(() => { | 29 | ref.result.finally(() => { |
30 | if (this.stopDisplayModal === true) this.doNotOpenAgain() | 30 | if (this.stopDisplayModal === true) this.doNotOpenAgain() |
diff --git a/client/src/app/modal/welcome-modal.component.ts b/client/src/app/modal/welcome-modal.component.ts index 19a147b85..e022776e3 100644 --- a/client/src/app/modal/welcome-modal.component.ts +++ b/client/src/app/modal/welcome-modal.component.ts | |||
@@ -18,7 +18,8 @@ export class WelcomeModalComponent { | |||
18 | ) { } | 18 | ) { } |
19 | 19 | ||
20 | show () { | 20 | show () { |
21 | this.modalService.open(this.modal,{ | 21 | this.modalService.open(this.modal, { |
22 | centered: true, | ||
22 | backdrop: 'static', | 23 | backdrop: 'static', |
23 | keyboard: false, | 24 | keyboard: false, |
24 | size: 'lg' | 25 | size: 'lg' |
diff --git a/client/src/app/shared/misc/list-overflow.component.html b/client/src/app/shared/misc/list-overflow.component.html new file mode 100644 index 000000000..986572801 --- /dev/null +++ b/client/src/app/shared/misc/list-overflow.component.html | |||
@@ -0,0 +1,35 @@ | |||
1 | <div #itemsParent class="d-flex align-items-center text-nowrap w-100 list-overflow-parent"> | ||
2 | <span [id]="getId(id)" #itemsRendered *ngFor="let item of items; index as id"> | ||
3 | <ng-container *ngTemplateOutlet="itemTemplate; context: {item: item}"></ng-container> | ||
4 | </span> | ||
5 | |||
6 | <ng-container *ngIf="isMenuDisplayed()"> | ||
7 | <button *ngIf="isInMobileView" class="btn btn-outline-secondary btn-sm list-overflow-menu" (click)="toggleModal()"> | ||
8 | <span class="glyphicon glyphicon-chevron-down"></span> | ||
9 | </button> | ||
10 | |||
11 | <div *ngIf="!isInMobileView" class="list-overflow-menu" ngbDropdown container="body" #dropdown="ngbDropdown" (mouseleave)="closeDropdownIfHovered(dropdown)" (mouseenter)="openDropdownOnHover(dropdown)"> | ||
12 | <button class="btn btn-outline-secondary btn-sm" [ngClass]="{ routeActive: active }" | ||
13 | ngbDropdownAnchor (click)="dropdownAnchorClicked(dropdown)" role="button" | ||
14 | > | ||
15 | <span class="glyphicon glyphicon-chevron-down"></span> | ||
16 | </button> | ||
17 | |||
18 | <div ngbDropdownMenu> | ||
19 | <a *ngFor="let item of items | slice:showItemsUntilIndexExcluded:items.length" | ||
20 | [routerLink]="item.routerLink" routerLinkActive="active" class="dropdown-item"> | ||
21 | {{ item.label }} | ||
22 | </a> | ||
23 | </div> | ||
24 | </div> | ||
25 | </ng-container> | ||
26 | </div > | ||
27 | |||
28 | <ng-template #modal let-close="close" let-dismiss="dismiss"> | ||
29 | <div class="modal-body"> | ||
30 | <a *ngFor="let item of items | slice:showItemsUntilIndexExcluded:items.length" | ||
31 | [routerLink]="item.routerLink" routerLinkActive="active" (click)="dismissOtherModals()"> | ||
32 | {{ item.label }} | ||
33 | </a> | ||
34 | </div> | ||
35 | </ng-template> | ||
diff --git a/client/src/app/shared/misc/list-overflow.component.scss b/client/src/app/shared/misc/list-overflow.component.scss new file mode 100644 index 000000000..e26100aca --- /dev/null +++ b/client/src/app/shared/misc/list-overflow.component.scss | |||
@@ -0,0 +1,61 @@ | |||
1 | @import '_mixins'; | ||
2 | |||
3 | :host { | ||
4 | width: 100%; | ||
5 | } | ||
6 | |||
7 | .list-overflow-parent { | ||
8 | overflow: hidden; | ||
9 | } | ||
10 | |||
11 | .list-overflow-menu { | ||
12 | position: absolute; | ||
13 | right: 0; | ||
14 | } | ||
15 | |||
16 | button { | ||
17 | width: 30px; | ||
18 | border: none; | ||
19 | |||
20 | &::after { | ||
21 | display: none; | ||
22 | } | ||
23 | |||
24 | &.routeActive { | ||
25 | &::after { | ||
26 | display: inherit; | ||
27 | border: 2px solid var(--mainColor); | ||
28 | position: relative; | ||
29 | right: 95%; | ||
30 | top: 50%; | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | |||
35 | ::ng-deep .dropdown-menu { | ||
36 | margin-top: 0 !important; | ||
37 | position: static; | ||
38 | right: auto; | ||
39 | bottom: auto | ||
40 | } | ||
41 | |||
42 | .modal-body { | ||
43 | a { | ||
44 | @include disable-default-a-behaviour; | ||
45 | |||
46 | color: currentColor; | ||
47 | box-sizing: border-box; | ||
48 | display: block; | ||
49 | font-size: 1.2rem; | ||
50 | padding: 9px 12px; | ||
51 | text-align: initial; | ||
52 | text-transform: unset; | ||
53 | width: 100%; | ||
54 | |||
55 | &.active { | ||
56 | color: var(--mainBackgroundColor) !important; | ||
57 | background-color: var(--mainHoverColor); | ||
58 | opacity: .9; | ||
59 | } | ||
60 | } | ||
61 | } | ||
diff --git a/client/src/app/shared/misc/list-overflow.component.ts b/client/src/app/shared/misc/list-overflow.component.ts new file mode 100644 index 000000000..4f92c0f7c --- /dev/null +++ b/client/src/app/shared/misc/list-overflow.component.ts | |||
@@ -0,0 +1,114 @@ | |||
1 | import { | ||
2 | Component, | ||
3 | Input, | ||
4 | TemplateRef, | ||
5 | ViewChildren, | ||
6 | ViewChild, | ||
7 | QueryList, | ||
8 | ChangeDetectionStrategy, | ||
9 | ElementRef, | ||
10 | ChangeDetectorRef, | ||
11 | HostListener | ||
12 | } from '@angular/core' | ||
13 | import { NgbModal, NgbDropdown } from '@ng-bootstrap/ng-bootstrap' | ||
14 | import { uniqueId, lowerFirst } from 'lodash-es' | ||
15 | import { ScreenService } from './screen.service' | ||
16 | import { take } from 'rxjs/operators' | ||
17 | |||
18 | export interface ListOverflowItem { | ||
19 | label: string | ||
20 | routerLink: string | any[] | ||
21 | } | ||
22 | |||
23 | @Component({ | ||
24 | selector: 'list-overflow', | ||
25 | templateUrl: './list-overflow.component.html', | ||
26 | styleUrls: [ './list-overflow.component.scss' ], | ||
27 | changeDetection: ChangeDetectionStrategy.OnPush | ||
28 | }) | ||
29 | export class ListOverflowComponent<T extends ListOverflowItem> { | ||
30 | @ViewChild('modal', { static: true }) modal: ElementRef | ||
31 | @ViewChild('itemsParent', { static: true }) parent: ElementRef<HTMLDivElement> | ||
32 | @ViewChildren('itemsRendered') itemsRendered: QueryList<ElementRef> | ||
33 | @Input() items: T[] | ||
34 | @Input() itemTemplate: TemplateRef<{item: T}> | ||
35 | |||
36 | showItemsUntilIndexExcluded: number | ||
37 | active = false | ||
38 | isInTouchScreen = false | ||
39 | isInMobileView = false | ||
40 | |||
41 | private openedOnHover = false | ||
42 | |||
43 | constructor ( | ||
44 | private cdr: ChangeDetectorRef, | ||
45 | private modalService: NgbModal, | ||
46 | private screenService: ScreenService | ||
47 | ) {} | ||
48 | |||
49 | isMenuDisplayed () { | ||
50 | return !!this.showItemsUntilIndexExcluded | ||
51 | } | ||
52 | |||
53 | @HostListener('window:resize', ['$event']) | ||
54 | onWindowResize () { | ||
55 | this.isInTouchScreen = !!this.screenService.isInTouchScreen() | ||
56 | this.isInMobileView = !!this.screenService.isInMobileView() | ||
57 | |||
58 | const parentWidth = this.parent.nativeElement.getBoundingClientRect().width | ||
59 | let showItemsUntilIndexExcluded: number | ||
60 | let accWidth = 0 | ||
61 | |||
62 | for (const [index, el] of this.itemsRendered.toArray().entries()) { | ||
63 | accWidth += el.nativeElement.getBoundingClientRect().width | ||
64 | if (showItemsUntilIndexExcluded === undefined) { | ||
65 | showItemsUntilIndexExcluded = (parentWidth < accWidth) ? index : undefined | ||
66 | } | ||
67 | |||
68 | const e = document.getElementById(this.getId(index)) | ||
69 | const shouldBeVisible = showItemsUntilIndexExcluded ? index < showItemsUntilIndexExcluded : true | ||
70 | e.style.visibility = shouldBeVisible ? 'inherit' : 'hidden' | ||
71 | } | ||
72 | |||
73 | this.showItemsUntilIndexExcluded = showItemsUntilIndexExcluded | ||
74 | this.cdr.markForCheck() | ||
75 | } | ||
76 | |||
77 | openDropdownOnHover (dropdown: NgbDropdown) { | ||
78 | this.openedOnHover = true | ||
79 | dropdown.open() | ||
80 | |||
81 | // Menu was closed | ||
82 | dropdown.openChange | ||
83 | .pipe(take(1)) | ||
84 | .subscribe(() => this.openedOnHover = false) | ||
85 | } | ||
86 | |||
87 | dropdownAnchorClicked (dropdown: NgbDropdown) { | ||
88 | if (this.openedOnHover) { | ||
89 | this.openedOnHover = false | ||
90 | return | ||
91 | } | ||
92 | |||
93 | return dropdown.toggle() | ||
94 | } | ||
95 | |||
96 | closeDropdownIfHovered (dropdown: NgbDropdown) { | ||
97 | if (this.openedOnHover === false) return | ||
98 | |||
99 | dropdown.close() | ||
100 | this.openedOnHover = false | ||
101 | } | ||
102 | |||
103 | toggleModal () { | ||
104 | this.modalService.open(this.modal, { centered: true }) | ||
105 | } | ||
106 | |||
107 | dismissOtherModals () { | ||
108 | this.modalService.dismissAll() | ||
109 | } | ||
110 | |||
111 | getId (id: number | string = uniqueId()): string { | ||
112 | return lowerFirst(this.constructor.name) + '_' + id | ||
113 | } | ||
114 | } | ||
diff --git a/client/src/app/shared/moderation/user-ban-modal.component.ts b/client/src/app/shared/moderation/user-ban-modal.component.ts index cf0e1577a..1647e3691 100644 --- a/client/src/app/shared/moderation/user-ban-modal.component.ts +++ b/client/src/app/shared/moderation/user-ban-modal.component.ts | |||
@@ -39,7 +39,7 @@ export class UserBanModalComponent extends FormReactive implements OnInit { | |||
39 | 39 | ||
40 | openModal (user: User | User[]) { | 40 | openModal (user: User | User[]) { |
41 | this.usersToBan = user | 41 | this.usersToBan = user |
42 | this.openedModal = this.modalService.open(this.modal) | 42 | this.openedModal = this.modalService.open(this.modal, { centered: true }) |
43 | } | 43 | } |
44 | 44 | ||
45 | hide () { | 45 | hide () { |
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 759de7020..98211c727 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts | |||
@@ -5,6 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms' | |||
5 | import { RouterModule } from '@angular/router' | 5 | import { RouterModule } from '@angular/router' |
6 | import { MarkdownTextareaComponent } from '@app/shared/forms/markdown-textarea.component' | 6 | import { MarkdownTextareaComponent } from '@app/shared/forms/markdown-textarea.component' |
7 | import { HelpComponent } from '@app/shared/misc/help.component' | 7 | import { HelpComponent } from '@app/shared/misc/help.component' |
8 | import { ListOverflowComponent } from '@app/shared/misc/list-overflow.component' | ||
8 | import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive' | 9 | import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive' |
9 | import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' | 10 | import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' |
10 | import { SharedModule as PrimeSharedModule } from 'primeng/api' | 11 | import { SharedModule as PrimeSharedModule } from 'primeng/api' |
@@ -156,6 +157,7 @@ import { ClipboardModule } from '@angular/cdk/clipboard' | |||
156 | InfiniteScrollerDirective, | 157 | InfiniteScrollerDirective, |
157 | TextareaAutoResizeDirective, | 158 | TextareaAutoResizeDirective, |
158 | HelpComponent, | 159 | HelpComponent, |
160 | ListOverflowComponent, | ||
159 | 161 | ||
160 | ReactiveFileComponent, | 162 | ReactiveFileComponent, |
161 | PeertubeCheckboxComponent, | 163 | PeertubeCheckboxComponent, |
@@ -227,6 +229,7 @@ import { ClipboardModule } from '@angular/cdk/clipboard' | |||
227 | InfiniteScrollerDirective, | 229 | InfiniteScrollerDirective, |
228 | TextareaAutoResizeDirective, | 230 | TextareaAutoResizeDirective, |
229 | HelpComponent, | 231 | HelpComponent, |
232 | ListOverflowComponent, | ||
230 | InputReadonlyCopyComponent, | 233 | InputReadonlyCopyComponent, |
231 | 234 | ||
232 | ReactiveFileComponent, | 235 | ReactiveFileComponent, |
diff --git a/client/src/app/shared/video/modals/video-blacklist.component.ts b/client/src/app/shared/video/modals/video-blacklist.component.ts index bdd9c7b99..6ef9c250b 100644 --- a/client/src/app/shared/video/modals/video-blacklist.component.ts +++ b/client/src/app/shared/video/modals/video-blacklist.component.ts | |||
@@ -46,7 +46,7 @@ export class VideoBlacklistComponent extends FormReactive implements OnInit { | |||
46 | } | 46 | } |
47 | 47 | ||
48 | show () { | 48 | show () { |
49 | this.openedModal = this.modalService.open(this.modal, { keyboard: false }) | 49 | this.openedModal = this.modalService.open(this.modal, { centered: true, keyboard: false }) |
50 | } | 50 | } |
51 | 51 | ||
52 | hide () { | 52 | hide () { |
diff --git a/client/src/app/shared/video/modals/video-download.component.ts b/client/src/app/shared/video/modals/video-download.component.ts index c1ceca263..6909c4279 100644 --- a/client/src/app/shared/video/modals/video-download.component.ts +++ b/client/src/app/shared/video/modals/video-download.component.ts | |||
@@ -48,7 +48,7 @@ export class VideoDownloadComponent { | |||
48 | this.video = video | 48 | this.video = video |
49 | this.videoCaptions = videoCaptions && videoCaptions.length ? videoCaptions : undefined | 49 | this.videoCaptions = videoCaptions && videoCaptions.length ? videoCaptions : undefined |
50 | 50 | ||
51 | this.activeModal = this.modalService.open(this.modal) | 51 | this.activeModal = this.modalService.open(this.modal, { centered: true }) |
52 | 52 | ||
53 | this.resolutionId = this.getVideoFiles()[0].resolution.id | 53 | this.resolutionId = this.getVideoFiles()[0].resolution.id |
54 | if (this.videoCaptions) this.subtitleLanguageId = this.videoCaptions[0].language.id | 54 | if (this.videoCaptions) this.subtitleLanguageId = this.videoCaptions[0].language.id |
diff --git a/client/src/app/shared/video/modals/video-report.component.ts b/client/src/app/shared/video/modals/video-report.component.ts index ee991fade..988fa03d4 100644 --- a/client/src/app/shared/video/modals/video-report.component.ts +++ b/client/src/app/shared/video/modals/video-report.component.ts | |||
@@ -53,7 +53,7 @@ export class VideoReportComponent extends FormReactive implements OnInit { | |||
53 | } | 53 | } |
54 | 54 | ||
55 | show () { | 55 | show () { |
56 | this.openedModal = this.modalService.open(this.modal, { keyboard: false }) | 56 | this.openedModal = this.modalService.open(this.modal, { centered: true, keyboard: false }) |
57 | } | 57 | } |
58 | 58 | ||
59 | hide () { | 59 | hide () { |
diff --git a/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts index 1a9bf5171..9856aac9e 100644 --- a/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts +++ b/client/src/app/videos/+video-edit/shared/video-caption-add-modal.component.ts | |||
@@ -56,7 +56,7 @@ export class VideoCaptionAddModalComponent extends FormReactive implements OnIni | |||
56 | show () { | 56 | show () { |
57 | this.closingModal = false | 57 | this.closingModal = false |
58 | 58 | ||
59 | this.openedModal = this.modalService.open(this.modal, { keyboard: false }) | 59 | this.openedModal = this.modalService.open(this.modal, { centered: true, keyboard: false }) |
60 | } | 60 | } |
61 | 61 | ||
62 | hide () { | 62 | hide () { |
diff --git a/client/src/app/videos/+video-watch/modal/video-share.component.ts b/client/src/app/videos/+video-watch/modal/video-share.component.ts index 6bc9b09fa..5109bcd11 100644 --- a/client/src/app/videos/+video-watch/modal/video-share.component.ts +++ b/client/src/app/videos/+video-watch/modal/video-share.component.ts | |||
@@ -72,7 +72,7 @@ export class VideoShareComponent { | |||
72 | controls: true | 72 | controls: true |
73 | } | 73 | } |
74 | 74 | ||
75 | this.modalService.open(this.modal) | 75 | this.modalService.open(this.modal, { centered: true }) |
76 | } | 76 | } |
77 | 77 | ||
78 | getVideoIframeCode () { | 78 | getVideoIframeCode () { |
diff --git a/client/src/app/videos/+video-watch/modal/video-support.component.ts b/client/src/app/videos/+video-watch/modal/video-support.component.ts index b56a51fbf..0058172f2 100644 --- a/client/src/app/videos/+video-watch/modal/video-support.component.ts +++ b/client/src/app/videos/+video-watch/modal/video-support.component.ts | |||
@@ -21,7 +21,7 @@ export class VideoSupportComponent { | |||
21 | ) { } | 21 | ) { } |
22 | 22 | ||
23 | show () { | 23 | show () { |
24 | this.modalService.open(this.modal) | 24 | this.modalService.open(this.modal, { centered: true }) |
25 | 25 | ||
26 | this.markdownService.enhancedMarkdownToHTML(this.video.support) | 26 | this.markdownService.enhancedMarkdownToHTML(this.video.support) |
27 | .then(r => this.videoHTMLSupport = r) | 27 | .then(r => this.videoHTMLSupport = r) |
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index 995cc6025..e4840dd81 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss | |||
@@ -252,7 +252,7 @@ table { | |||
252 | padding-left: 50px; | 252 | padding-left: 50px; |
253 | 253 | ||
254 | .title-page { | 254 | .title-page { |
255 | font-size: 15px; | 255 | font-size: 17px; |
256 | } | 256 | } |
257 | } | 257 | } |
258 | } | 258 | } |
diff --git a/client/src/sass/bootstrap.scss b/client/src/sass/bootstrap.scss index 2aca8c380..035270e89 100644 --- a/client/src/sass/bootstrap.scss +++ b/client/src/sass/bootstrap.scss | |||
@@ -30,8 +30,10 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/'; | |||
30 | .dropdown-item { | 30 | .dropdown-item { |
31 | padding: 3px 15px; | 31 | padding: 3px 15px; |
32 | 32 | ||
33 | &:active { | 33 | &.active { |
34 | color: #000 !important; | 34 | color: var(--mainBackgroundColor) !important; |
35 | background-color: var(--mainHoverColor); | ||
36 | opacity: .9; | ||
35 | } | 37 | } |
36 | } | 38 | } |
37 | 39 | ||
@@ -48,14 +50,12 @@ $icon-font-path: '~@neos21/bootstrap3-glyphicons/assets/fonts/'; | |||
48 | 50 | ||
49 | @media screen and (min-width: 768px) { | 51 | @media screen and (min-width: 768px) { |
50 | .modal:before { | 52 | .modal:before { |
51 | display: inline-block; | ||
52 | vertical-align: middle; | 53 | vertical-align: middle; |
53 | content: " "; | 54 | content: " "; |
54 | height: 100%; | 55 | height: 100%; |
55 | } | 56 | } |
56 | 57 | ||
57 | .modal-dialog { | 58 | .modal-dialog { |
58 | display: inline-block; | ||
59 | text-align: left; | 59 | text-align: left; |
60 | vertical-align: middle; | 60 | vertical-align: middle; |
61 | min-width: 500px; | 61 | min-width: 500px; |