aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared/shared-main/misc
diff options
context:
space:
mode:
Diffstat (limited to 'client/src/app/shared/shared-main/misc')
-rw-r--r--client/src/app/shared/shared-main/misc/help.component.html40
-rw-r--r--client/src/app/shared/shared-main/misc/help.component.scss42
-rw-r--r--client/src/app/shared/shared-main/misc/help.component.ts94
-rw-r--r--client/src/app/shared/shared-main/misc/index.ts2
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.html35
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.scss61
-rw-r--r--client/src/app/shared/shared-main/misc/list-overflow.component.ts120
7 files changed, 394 insertions, 0 deletions
diff --git a/client/src/app/shared/shared-main/misc/help.component.html b/client/src/app/shared/shared-main/misc/help.component.html
new file mode 100644
index 000000000..9a6d3e48e
--- /dev/null
+++ b/client/src/app/shared/shared-main/misc/help.component.html
@@ -0,0 +1,40 @@
1<ng-template #tooltipTemplate>
2 <p *ngIf="preHtmlTemplate">
3 <ng-template *ngTemplateOutlet="preHtmlTemplate"></ng-template>
4 </p>
5
6 <ng-container *ngIf="preHtmlTemplate && (customHtmlTemplate || mainHtml || postHtmlTemplate)">
7 <br /><br />
8 </ng-container>
9
10 <p *ngIf="customHtmlTemplate">
11 <ng-template *ngTemplateOutlet="customHtmlTemplate"></ng-template>
12 </p>
13
14 <p *ngIf="mainHtml" [innerHTML]="mainHtml"></p>
15
16 <ng-container *ngIf="(customHtmlTemplate || mainHtml) && postHtmlTemplate">
17 <br /><br />
18 </ng-container>
19
20 <p *ngIf="postHtmlTemplate">
21 <ng-template *ngTemplateOutlet="postHtmlTemplate"></ng-template>
22 </p>
23</ng-template>
24
25<span
26 role="button"
27 class="help-tooltip-button"
28 container="body"
29 title="Get help"
30 i18n-title
31 popoverClass="help-popover"
32 [attr.aria-pressed]="isPopoverOpened"
33 [ngbPopover]="tooltipTemplate"
34 [placement]="tooltipPlacement"
35 autoClose="outside"
36 (onHidden)="onPopoverHidden()"
37 (onShown)="onPopoverShown()"
38>
39 <my-global-icon iconName="help"></my-global-icon>
40</span>
diff --git a/client/src/app/shared/shared-main/misc/help.component.scss b/client/src/app/shared/shared-main/misc/help.component.scss
new file mode 100644
index 000000000..43f33a53a
--- /dev/null
+++ b/client/src/app/shared/shared-main/misc/help.component.scss
@@ -0,0 +1,42 @@
1@import '_variables';
2@import '_mixins';
3
4.help-tooltip-button {
5 cursor: pointer;
6 border: none;
7
8 my-global-icon {
9 width: 17px;
10 position: relative;
11 top: -2px;
12 margin: 5px;
13
14 @include apply-svg-color(pvar(--mainForegroundColor))
15 }
16}
17
18::ng-deep {
19 .help-popover {
20 z-index: z(help-popover) !important;
21 max-width: 300px;
22
23 .popover-body {
24 font-family: $main-fonts;
25 text-align: left;
26 padding: 10px;
27 font-size: 13px;
28 background-color: pvar(--mainBackgroundColor);
29 color: pvar(--mainForegroundColor);
30 border-radius: 3px;
31
32 p {
33 margin-bottom: 0;
34 }
35
36 ul {
37 padding-left: 20px;
38 margin-bottom: 0;
39 }
40 }
41 }
42}
diff --git a/client/src/app/shared/shared-main/misc/help.component.ts b/client/src/app/shared/shared-main/misc/help.component.ts
new file mode 100644
index 000000000..0825b96de
--- /dev/null
+++ b/client/src/app/shared/shared-main/misc/help.component.ts
@@ -0,0 +1,94 @@
1import { AfterContentInit, Component, ContentChildren, Input, OnChanges, OnInit, QueryList, TemplateRef } from '@angular/core'
2import { MarkdownService } from '@app/core'
3import { I18n } from '@ngx-translate/i18n-polyfill'
4import { PeerTubeTemplateDirective } from '../angular'
5
6@Component({
7 selector: 'my-help',
8 styleUrls: [ './help.component.scss' ],
9 templateUrl: './help.component.html'
10})
11
12export class HelpComponent implements OnInit, OnChanges, AfterContentInit {
13 @Input() helpType: 'custom' | 'markdownText' | 'markdownEnhanced' = 'custom'
14 @Input() tooltipPlacement = 'right auto'
15
16 @ContentChildren(PeerTubeTemplateDirective) templates: QueryList<PeerTubeTemplateDirective<'preHtml' | 'customHtml' | 'postHtml'>>
17
18 isPopoverOpened = false
19 mainHtml = ''
20
21 preHtmlTemplate: TemplateRef<any>
22 customHtmlTemplate: TemplateRef<any>
23 postHtmlTemplate: TemplateRef<any>
24
25 constructor (private i18n: I18n) { }
26
27 ngOnInit () {
28 this.init()
29 }
30
31 ngAfterContentInit () {
32 {
33 const t = this.templates.find(t => t.name === 'preHtml')
34 if (t) this.preHtmlTemplate = t.template
35 }
36
37 {
38 const t = this.templates.find(t => t.name === 'customHtml')
39 if (t) this.customHtmlTemplate = t.template
40 }
41
42 {
43 const t = this.templates.find(t => t.name === 'postHtml')
44 if (t) this.postHtmlTemplate = t.template
45 }
46 }
47
48 ngOnChanges () {
49 this.init()
50 }
51
52 onPopoverHidden () {
53 this.isPopoverOpened = false
54 }
55
56 onPopoverShown () {
57 this.isPopoverOpened = true
58 }
59
60 private init () {
61 if (this.helpType === 'markdownText') {
62 this.mainHtml = this.formatMarkdownSupport(MarkdownService.TEXT_RULES)
63 return
64 }
65
66 if (this.helpType === 'markdownEnhanced') {
67 this.mainHtml = this.formatMarkdownSupport(MarkdownService.ENHANCED_RULES)
68 return
69 }
70 }
71
72 private formatMarkdownSupport (rules: string[]) {
73 // tslint:disable:max-line-length
74 return this.i18n('<a href="https://en.wikipedia.org/wiki/Markdown#Example" target="_blank" rel="noopener noreferrer">Markdown</a> compatible that supports:') +
75 this.createMarkdownList(rules)
76 }
77
78 private createMarkdownList (rules: string[]) {
79 const rulesToText = {
80 'emphasis': this.i18n('Emphasis'),
81 'link': this.i18n('Links'),
82 'newline': this.i18n('New lines'),
83 'list': this.i18n('Lists'),
84 'image': this.i18n('Images')
85 }
86
87 const bullets = rules.map(r => rulesToText[r])
88 .filter(text => text)
89 .map(text => '<li>' + text + '</li>')
90 .join('')
91
92 return '<ul>' + bullets + '</ul>'
93 }
94}
diff --git a/client/src/app/shared/shared-main/misc/index.ts b/client/src/app/shared/shared-main/misc/index.ts
new file mode 100644
index 000000000..d3e7e4be7
--- /dev/null
+++ b/client/src/app/shared/shared-main/misc/index.ts
@@ -0,0 +1,2 @@
1export * from './help.component'
2export * from './list-overflow.component'
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.html b/client/src/app/shared/shared-main/misc/list-overflow.component.html
new file mode 100644
index 000000000..986572801
--- /dev/null
+++ b/client/src/app/shared/shared-main/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/shared-main/misc/list-overflow.component.scss b/client/src/app/shared/shared-main/misc/list-overflow.component.scss
new file mode 100644
index 000000000..1ec044489
--- /dev/null
+++ b/client/src/app/shared/shared-main/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: 25px;
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 pvar(--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: pvar(--mainBackgroundColor) !important;
57 background-color: pvar(--mainHoverColor);
58 opacity: .9;
59 }
60 }
61}
diff --git a/client/src/app/shared/shared-main/misc/list-overflow.component.ts b/client/src/app/shared/shared-main/misc/list-overflow.component.ts
new file mode 100644
index 000000000..144e0f156
--- /dev/null
+++ b/client/src/app/shared/shared-main/misc/list-overflow.component.ts
@@ -0,0 +1,120 @@
1import { lowerFirst, uniqueId } from 'lodash-es'
2import { take } from 'rxjs/operators'
3import {
4 AfterViewInit,
5 ChangeDetectionStrategy,
6 ChangeDetectorRef,
7 Component,
8 ElementRef,
9 HostListener,
10 Input,
11 QueryList,
12 TemplateRef,
13 ViewChild,
14 ViewChildren
15} from '@angular/core'
16import { ScreenService } from '@app/core'
17import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'
18
19export interface ListOverflowItem {
20 label: string
21 routerLink: string | any[]
22}
23
24@Component({
25 selector: 'list-overflow',
26 templateUrl: './list-overflow.component.html',
27 styleUrls: [ './list-overflow.component.scss' ],
28 changeDetection: ChangeDetectionStrategy.OnPush
29})
30export class ListOverflowComponent<T extends ListOverflowItem> implements AfterViewInit {
31 @Input() items: T[]
32 @Input() itemTemplate: TemplateRef<{item: T}>
33
34 @ViewChild('modal', { static: true }) modal: ElementRef
35 @ViewChild('itemsParent', { static: true }) parent: ElementRef<HTMLDivElement>
36 @ViewChildren('itemsRendered') itemsRendered: QueryList<ElementRef>
37
38 showItemsUntilIndexExcluded: number
39 active = false
40 isInTouchScreen = false
41 isInMobileView = false
42
43 private openedOnHover = false
44
45 constructor (
46 private cdr: ChangeDetectorRef,
47 private modalService: NgbModal,
48 private screenService: ScreenService
49 ) {}
50
51 ngAfterViewInit () {
52 setTimeout(() => this.onWindowResize(), 0)
53 }
54
55 isMenuDisplayed () {
56 return !!this.showItemsUntilIndexExcluded
57 }
58
59 @HostListener('window:resize')
60 onWindowResize () {
61 this.isInTouchScreen = !!this.screenService.isInTouchScreen()
62 this.isInMobileView = !!this.screenService.isInMobileView()
63
64 const parentWidth = this.parent.nativeElement.getBoundingClientRect().width
65 let showItemsUntilIndexExcluded: number
66 let accWidth = 0
67
68 for (const [index, el] of this.itemsRendered.toArray().entries()) {
69 accWidth += el.nativeElement.getBoundingClientRect().width
70 if (showItemsUntilIndexExcluded === undefined) {
71 showItemsUntilIndexExcluded = (parentWidth < accWidth) ? index : undefined
72 }
73
74 const e = document.getElementById(this.getId(index))
75 const shouldBeVisible = showItemsUntilIndexExcluded ? index < showItemsUntilIndexExcluded : true
76 e.style.visibility = shouldBeVisible ? 'inherit' : 'hidden'
77 }
78
79 this.showItemsUntilIndexExcluded = showItemsUntilIndexExcluded
80 this.cdr.markForCheck()
81 }
82
83 openDropdownOnHover (dropdown: NgbDropdown) {
84 this.openedOnHover = true
85 dropdown.open()
86
87 // Menu was closed
88 dropdown.openChange
89 .pipe(take(1))
90 .subscribe(() => this.openedOnHover = false)
91 }
92
93 dropdownAnchorClicked (dropdown: NgbDropdown) {
94 if (this.openedOnHover) {
95 this.openedOnHover = false
96 return
97 }
98
99 return dropdown.toggle()
100 }
101
102 closeDropdownIfHovered (dropdown: NgbDropdown) {
103 if (this.openedOnHover === false) return
104
105 dropdown.close()
106 this.openedOnHover = false
107 }
108
109 toggleModal () {
110 this.modalService.open(this.modal, { centered: true })
111 }
112
113 dismissOtherModals () {
114 this.modalService.dismissAll()
115 }
116
117 getId (id: number | string = uniqueId()): string {
118 return lowerFirst(this.constructor.name) + '_' + id
119 }
120}