From 24e7916c6897bbb38e057cdf1a102286006be964 Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Wed, 5 Feb 2020 20:54:37 +0100 Subject: Add ListOverflow component to prevent sub-menu overflow --- .../app/shared/misc/list-overflow.component.html | 35 +++++++ .../app/shared/misc/list-overflow.component.scss | 61 +++++++++++ .../src/app/shared/misc/list-overflow.component.ts | 114 +++++++++++++++++++++ .../shared/moderation/user-ban-modal.component.ts | 2 +- client/src/app/shared/shared.module.ts | 3 + .../video/modals/video-blacklist.component.ts | 2 +- .../video/modals/video-download.component.ts | 2 +- .../shared/video/modals/video-report.component.ts | 2 +- 8 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 client/src/app/shared/misc/list-overflow.component.html create mode 100644 client/src/app/shared/misc/list-overflow.component.scss create mode 100644 client/src/app/shared/misc/list-overflow.component.ts (limited to 'client/src/app/shared') 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 @@ +
+ + + + + + + +
+ + +
+ + {{ item.label }} + +
+
+
+
+ + + + 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 @@ +@import '_mixins'; + +:host { + width: 100%; +} + +.list-overflow-parent { + overflow: hidden; +} + +.list-overflow-menu { + position: absolute; + right: 0; +} + +button { + width: 30px; + border: none; + + &::after { + display: none; + } + + &.routeActive { + &::after { + display: inherit; + border: 2px solid var(--mainColor); + position: relative; + right: 95%; + top: 50%; + } + } +} + +::ng-deep .dropdown-menu { + margin-top: 0 !important; + position: static; + right: auto; + bottom: auto +} + +.modal-body { + a { + @include disable-default-a-behaviour; + + color: currentColor; + box-sizing: border-box; + display: block; + font-size: 1.2rem; + padding: 9px 12px; + text-align: initial; + text-transform: unset; + width: 100%; + + &.active { + color: var(--mainBackgroundColor) !important; + background-color: var(--mainHoverColor); + opacity: .9; + } + } +} 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 @@ +import { + Component, + Input, + TemplateRef, + ViewChildren, + ViewChild, + QueryList, + ChangeDetectionStrategy, + ElementRef, + ChangeDetectorRef, + HostListener +} from '@angular/core' +import { NgbModal, NgbDropdown } from '@ng-bootstrap/ng-bootstrap' +import { uniqueId, lowerFirst } from 'lodash-es' +import { ScreenService } from './screen.service' +import { take } from 'rxjs/operators' + +export interface ListOverflowItem { + label: string + routerLink: string | any[] +} + +@Component({ + selector: 'list-overflow', + templateUrl: './list-overflow.component.html', + styleUrls: [ './list-overflow.component.scss' ], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class ListOverflowComponent { + @ViewChild('modal', { static: true }) modal: ElementRef + @ViewChild('itemsParent', { static: true }) parent: ElementRef + @ViewChildren('itemsRendered') itemsRendered: QueryList + @Input() items: T[] + @Input() itemTemplate: TemplateRef<{item: T}> + + showItemsUntilIndexExcluded: number + active = false + isInTouchScreen = false + isInMobileView = false + + private openedOnHover = false + + constructor ( + private cdr: ChangeDetectorRef, + private modalService: NgbModal, + private screenService: ScreenService + ) {} + + isMenuDisplayed () { + return !!this.showItemsUntilIndexExcluded + } + + @HostListener('window:resize', ['$event']) + onWindowResize () { + this.isInTouchScreen = !!this.screenService.isInTouchScreen() + this.isInMobileView = !!this.screenService.isInMobileView() + + const parentWidth = this.parent.nativeElement.getBoundingClientRect().width + let showItemsUntilIndexExcluded: number + let accWidth = 0 + + for (const [index, el] of this.itemsRendered.toArray().entries()) { + accWidth += el.nativeElement.getBoundingClientRect().width + if (showItemsUntilIndexExcluded === undefined) { + showItemsUntilIndexExcluded = (parentWidth < accWidth) ? index : undefined + } + + const e = document.getElementById(this.getId(index)) + const shouldBeVisible = showItemsUntilIndexExcluded ? index < showItemsUntilIndexExcluded : true + e.style.visibility = shouldBeVisible ? 'inherit' : 'hidden' + } + + this.showItemsUntilIndexExcluded = showItemsUntilIndexExcluded + this.cdr.markForCheck() + } + + openDropdownOnHover (dropdown: NgbDropdown) { + this.openedOnHover = true + dropdown.open() + + // Menu was closed + dropdown.openChange + .pipe(take(1)) + .subscribe(() => this.openedOnHover = false) + } + + dropdownAnchorClicked (dropdown: NgbDropdown) { + if (this.openedOnHover) { + this.openedOnHover = false + return + } + + return dropdown.toggle() + } + + closeDropdownIfHovered (dropdown: NgbDropdown) { + if (this.openedOnHover === false) return + + dropdown.close() + this.openedOnHover = false + } + + toggleModal () { + this.modalService.open(this.modal, { centered: true }) + } + + dismissOtherModals () { + this.modalService.dismissAll() + } + + getId (id: number | string = uniqueId()): string { + return lowerFirst(this.constructor.name) + '_' + id + } +} 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 { openModal (user: User | User[]) { this.usersToBan = user - this.openedModal = this.modalService.open(this.modal) + this.openedModal = this.modalService.open(this.modal, { centered: true }) } 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' import { RouterModule } from '@angular/router' import { MarkdownTextareaComponent } from '@app/shared/forms/markdown-textarea.component' import { HelpComponent } from '@app/shared/misc/help.component' +import { ListOverflowComponent } from '@app/shared/misc/list-overflow.component' import { InfiniteScrollerDirective } from '@app/shared/video/infinite-scroller.directive' import { BytesPipe, KeysPipe, NgPipesModule } from 'ngx-pipes' import { SharedModule as PrimeSharedModule } from 'primeng/api' @@ -156,6 +157,7 @@ import { ClipboardModule } from '@angular/cdk/clipboard' InfiniteScrollerDirective, TextareaAutoResizeDirective, HelpComponent, + ListOverflowComponent, ReactiveFileComponent, PeertubeCheckboxComponent, @@ -227,6 +229,7 @@ import { ClipboardModule } from '@angular/cdk/clipboard' InfiniteScrollerDirective, TextareaAutoResizeDirective, HelpComponent, + ListOverflowComponent, InputReadonlyCopyComponent, 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 { } show () { - this.openedModal = this.modalService.open(this.modal, { keyboard: false }) + this.openedModal = this.modalService.open(this.modal, { centered: true, keyboard: false }) } 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 { this.video = video this.videoCaptions = videoCaptions && videoCaptions.length ? videoCaptions : undefined - this.activeModal = this.modalService.open(this.modal) + this.activeModal = this.modalService.open(this.modal, { centered: true }) this.resolutionId = this.getVideoFiles()[0].resolution.id 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 { } show () { - this.openedModal = this.modalService.open(this.modal, { keyboard: false }) + this.openedModal = this.modalService.open(this.modal, { centered: true, keyboard: false }) } hide () { -- cgit v1.2.3