aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared/misc
diff options
context:
space:
mode:
authorRigel Kent <sendmemail@rigelk.eu>2020-02-05 20:54:37 +0100
committerChocobozzz <chocobozzz@cpy.re>2020-02-13 10:25:22 +0100
commit24e7916c6897bbb38e057cdf1a102286006be964 (patch)
tree7621dd83d532ba04b725f4feeb902ccbdb6669ff /client/src/app/shared/misc
parenteb7c7a517902eae425b05d1ca9cb7f99f76ee71f (diff)
downloadPeerTube-24e7916c6897bbb38e057cdf1a102286006be964.tar.gz
PeerTube-24e7916c6897bbb38e057cdf1a102286006be964.tar.zst
PeerTube-24e7916c6897bbb38e057cdf1a102286006be964.zip
Add ListOverflow component to prevent sub-menu overflow
Diffstat (limited to 'client/src/app/shared/misc')
-rw-r--r--client/src/app/shared/misc/list-overflow.component.html35
-rw-r--r--client/src/app/shared/misc/list-overflow.component.scss61
-rw-r--r--client/src/app/shared/misc/list-overflow.component.ts114
3 files changed, 210 insertions, 0 deletions
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
16button {
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 @@
1import {
2 Component,
3 Input,
4 TemplateRef,
5 ViewChildren,
6 ViewChild,
7 QueryList,
8 ChangeDetectionStrategy,
9 ElementRef,
10 ChangeDetectorRef,
11 HostListener
12} from '@angular/core'
13import { NgbModal, NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
14import { uniqueId, lowerFirst } from 'lodash-es'
15import { ScreenService } from './screen.service'
16import { take } from 'rxjs/operators'
17
18export 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})
29export 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}