diff options
Diffstat (limited to 'client/src/app/shared/shared-forms')
11 files changed, 188 insertions, 31 deletions
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.html b/client/src/app/shared/shared-forms/advanced-input-filter.component.html new file mode 100644 index 000000000..10d1296cf --- /dev/null +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.html | |||
@@ -0,0 +1,24 @@ | |||
1 | <div class="input-group has-feedback has-clear"> | ||
2 | <div *ngIf="hasFilters()" class="input-group-prepend c-hand" ngbDropdown placement="bottom-left auto" container="body"> | ||
3 | <div class="input-group-text" ngbDropdownToggle> | ||
4 | <span class="caret" aria-haspopup="menu" role="button"></span> | ||
5 | </div> | ||
6 | |||
7 | <div role="menu" ngbDropdownMenu> | ||
8 | <h6 class="dropdown-header" i18n>Advanced filters</h6> | ||
9 | |||
10 | <a *ngFor="let filter of filters" [routerLink]="[ '.' ]" [queryParams]="filter.queryParams" class="dropdown-item"> | ||
11 | {{ filter.label }} | ||
12 | </a> | ||
13 | </div> | ||
14 | </div> | ||
15 | |||
16 | <input | ||
17 | type="text" name="table-filter" id="table-filter" i18n-placeholder placeholder="Filter..." | ||
18 | [(ngModel)]="searchValue" | ||
19 | (keyup)="onInputSearch($event)" | ||
20 | > | ||
21 | |||
22 | <a class="glyphicon glyphicon-remove-sign form-control-feedback form-control-clear" (click)="onResetTableFilter()"></a> | ||
23 | <span class="sr-only" i18n>Clear filters</span> | ||
24 | </div> | ||
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.scss b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss new file mode 100644 index 000000000..7c2198927 --- /dev/null +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.scss | |||
@@ -0,0 +1,10 @@ | |||
1 | @import '_variables'; | ||
2 | @import '_mixins'; | ||
3 | |||
4 | input { | ||
5 | @include peertube-input-text(250px); | ||
6 | } | ||
7 | |||
8 | .input-group-text { | ||
9 | background-color: transparent; | ||
10 | } | ||
diff --git a/client/src/app/shared/shared-forms/advanced-input-filter.component.ts b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts new file mode 100644 index 000000000..c11f1ad1d --- /dev/null +++ b/client/src/app/shared/shared-forms/advanced-input-filter.component.ts | |||
@@ -0,0 +1,116 @@ | |||
1 | import * as debug from 'debug' | ||
2 | import { Subject } from 'rxjs' | ||
3 | import { debounceTime, distinctUntilChanged } from 'rxjs/operators' | ||
4 | import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | ||
5 | import { ActivatedRoute, Params, Router } from '@angular/router' | ||
6 | |||
7 | export type AdvancedInputFilter = { | ||
8 | label: string | ||
9 | queryParams: Params | ||
10 | } | ||
11 | |||
12 | const logger = debug('peertube:AdvancedInputFilterComponent') | ||
13 | |||
14 | @Component({ | ||
15 | selector: 'my-advanced-input-filter', | ||
16 | templateUrl: './advanced-input-filter.component.html', | ||
17 | styleUrls: [ './advanced-input-filter.component.scss' ] | ||
18 | }) | ||
19 | export class AdvancedInputFilterComponent implements OnInit, AfterViewInit { | ||
20 | @Input() filters: AdvancedInputFilter[] = [] | ||
21 | |||
22 | @Output() search = new EventEmitter<string>() | ||
23 | |||
24 | searchValue: string | ||
25 | |||
26 | private searchStream: Subject<string> | ||
27 | |||
28 | private viewInitialized = false | ||
29 | private emitSearchAfterViewInit = false | ||
30 | |||
31 | constructor ( | ||
32 | private route: ActivatedRoute, | ||
33 | private router: Router | ||
34 | ) { } | ||
35 | |||
36 | ngOnInit () { | ||
37 | this.initSearchStream() | ||
38 | this.listenToRouteSearchChange() | ||
39 | } | ||
40 | |||
41 | ngAfterViewInit () { | ||
42 | this.viewInitialized = true | ||
43 | |||
44 | // Init after view init to not send an event too early | ||
45 | if (this.emitSearchAfterViewInit) this.emitSearch() | ||
46 | } | ||
47 | |||
48 | onInputSearch (event: Event) { | ||
49 | this.scheduleSearchUpdate((event.target as HTMLInputElement).value) | ||
50 | } | ||
51 | |||
52 | onResetTableFilter () { | ||
53 | this.immediateSearchUpdate('') | ||
54 | } | ||
55 | |||
56 | hasFilters () { | ||
57 | return this.filters.length !== 0 | ||
58 | } | ||
59 | |||
60 | private scheduleSearchUpdate (value: string) { | ||
61 | this.searchValue = value | ||
62 | this.searchStream.next(this.searchValue) | ||
63 | } | ||
64 | |||
65 | private immediateSearchUpdate (value: string) { | ||
66 | this.searchValue = value | ||
67 | |||
68 | this.setQueryParams(this.searchValue) | ||
69 | this.emitSearch() | ||
70 | } | ||
71 | |||
72 | private listenToRouteSearchChange () { | ||
73 | this.route.queryParams | ||
74 | .subscribe(params => { | ||
75 | const search = params.search || '' | ||
76 | |||
77 | logger('On route search change "%s".', search) | ||
78 | |||
79 | this.searchValue = search | ||
80 | this.emitSearch() | ||
81 | }) | ||
82 | } | ||
83 | |||
84 | private initSearchStream () { | ||
85 | this.searchStream = new Subject() | ||
86 | |||
87 | this.searchStream | ||
88 | .pipe( | ||
89 | debounceTime(300), | ||
90 | distinctUntilChanged() | ||
91 | ) | ||
92 | .subscribe(() => { | ||
93 | this.setQueryParams(this.searchValue) | ||
94 | |||
95 | this.emitSearch() | ||
96 | }) | ||
97 | } | ||
98 | |||
99 | private emitSearch () { | ||
100 | if (!this.viewInitialized) { | ||
101 | this.emitSearchAfterViewInit = true | ||
102 | return | ||
103 | } | ||
104 | |||
105 | logger('On search "%s".', this.searchValue) | ||
106 | |||
107 | this.search.emit(this.searchValue) | ||
108 | } | ||
109 | |||
110 | private setQueryParams (search: string) { | ||
111 | const queryParams: Params = {} | ||
112 | |||
113 | if (search) Object.assign(queryParams, { search }) | ||
114 | this.router.navigate([ ], { queryParams }) | ||
115 | } | ||
116 | } | ||
diff --git a/client/src/app/shared/shared-forms/index.ts b/client/src/app/shared/shared-forms/index.ts index 1d859b991..727416a40 100644 --- a/client/src/app/shared/shared-forms/index.ts +++ b/client/src/app/shared/shared-forms/index.ts | |||
@@ -1,12 +1,14 @@ | |||
1 | export * from './form-validator.service' | 1 | export * from './advanced-input-filter.component' |
2 | export * from './form-reactive' | 2 | export * from './form-reactive' |
3 | export * from './select' | 3 | export * from './form-validator.service' |
4 | export * from './input-toggle-hidden.component' | 4 | export * from './form-validator.service' |
5 | export * from './input-switch.component' | 5 | export * from './input-switch.component' |
6 | export * from './input-toggle-hidden.component' | ||
6 | export * from './markdown-textarea.component' | 7 | export * from './markdown-textarea.component' |
7 | export * from './peertube-checkbox.component' | 8 | export * from './peertube-checkbox.component' |
8 | export * from './preview-upload.component' | 9 | export * from './preview-upload.component' |
9 | export * from './reactive-file.component' | 10 | export * from './reactive-file.component' |
11 | export * from './select' | ||
12 | export * from './shared-form.module' | ||
10 | export * from './textarea-autoresize.directive' | 13 | export * from './textarea-autoresize.directive' |
11 | export * from './timestamp-input.component' | 14 | export * from './timestamp-input.component' |
12 | export * from './shared-form.module' | ||
diff --git a/client/src/app/shared/shared-forms/input-switch.component.scss b/client/src/app/shared/shared-forms/input-switch.component.scss index c14950bd7..290a70db8 100644 --- a/client/src/app/shared/shared-forms/input-switch.component.scss +++ b/client/src/app/shared/shared-forms/input-switch.component.scss | |||
@@ -5,7 +5,7 @@ input { | |||
5 | position: absolute; | 5 | position: absolute; |
6 | visibility: hidden; | 6 | visibility: hidden; |
7 | 7 | ||
8 | & + label { | 8 | + label { |
9 | cursor: pointer; | 9 | cursor: pointer; |
10 | text-indent: -9999px; | 10 | text-indent: -9999px; |
11 | width: 35px; | 11 | width: 35px; |
@@ -16,7 +16,7 @@ input { | |||
16 | position: relative; | 16 | position: relative; |
17 | margin: 0; | 17 | margin: 0; |
18 | 18 | ||
19 | &:after { | 19 | &::after { |
20 | content: ''; | 20 | content: ''; |
21 | position: absolute; | 21 | position: absolute; |
22 | top: 3px; | 22 | top: 3px; |
@@ -28,7 +28,7 @@ input { | |||
28 | transition: 0.3s ease-out; | 28 | transition: 0.3s ease-out; |
29 | } | 29 | } |
30 | 30 | ||
31 | &:active:after { | 31 | &:active::after { |
32 | width: 40px; | 32 | width: 40px; |
33 | } | 33 | } |
34 | } | 34 | } |
@@ -36,7 +36,7 @@ input { | |||
36 | &:checked + label { | 36 | &:checked + label { |
37 | background: pvar(--mainColor); | 37 | background: pvar(--mainColor); |
38 | 38 | ||
39 | &:after { | 39 | &::after { |
40 | left: calc(100% - 3px); | 40 | left: calc(100% - 3px); |
41 | transform: translateX(-100%); | 41 | transform: translateX(-100%); |
42 | } | 42 | } |
diff --git a/client/src/app/shared/shared-forms/markdown-textarea.component.scss b/client/src/app/shared/shared-forms/markdown-textarea.component.scss index 8203c7d1c..1f72dbc32 100644 --- a/client/src/app/shared/shared-forms/markdown-textarea.component.scss +++ b/client/src/app/shared/shared-forms/markdown-textarea.component.scss | |||
@@ -18,7 +18,7 @@ $input-border-radius: 3px; | |||
18 | 18 | ||
19 | font-family: monospace; | 19 | font-family: monospace; |
20 | font-size: 13px; | 20 | font-size: 13px; |
21 | border-bottom: none; | 21 | border-bottom: 0; |
22 | border-bottom-left-radius: unset; | 22 | border-bottom-left-radius: unset; |
23 | border-bottom-right-radius: unset; | 23 | border-bottom-right-radius: unset; |
24 | } | 24 | } |
@@ -51,7 +51,8 @@ $input-border-radius: 3px; | |||
51 | opacity: 0.6; | 51 | opacity: 0.6; |
52 | } | 52 | } |
53 | 53 | ||
54 | &:hover, &:active { | 54 | &:hover, |
55 | &:active { | ||
55 | svg { | 56 | svg { |
56 | opacity: 1; | 57 | opacity: 1; |
57 | } | 58 | } |
@@ -105,6 +106,8 @@ $input-border-radius: 3px; | |||
105 | } | 106 | } |
106 | 107 | ||
107 | @mixin maximized-base { | 108 | @mixin maximized-base { |
109 | $nav-preview-vertical-padding: 40px; | ||
110 | |||
108 | flex-direction: row; | 111 | flex-direction: row; |
109 | z-index: #{z(header) - 1}; | 112 | z-index: #{z(header) - 1}; |
110 | position: fixed; | 113 | position: fixed; |
@@ -115,20 +118,18 @@ $input-border-radius: 3px; | |||
115 | width: calc(100% - #{$menu-width}); | 118 | width: calc(100% - #{$menu-width}); |
116 | height: calc(100vh - #{$header-height}) !important; | 119 | height: calc(100vh - #{$header-height}) !important; |
117 | 120 | ||
118 | $nav-preview-vertical-padding: 40px; | ||
119 | |||
120 | .nav-preview { | 121 | .nav-preview { |
121 | @include nav-preview-medium(); | 122 | @include nav-preview-medium(); |
122 | padding-top: #{$nav-preview-vertical-padding / 2}; | 123 | padding-top: #{$nav-preview-vertical-padding / 2}; |
123 | padding-bottom: #{$nav-preview-vertical-padding / 2}; | 124 | padding-bottom: #{$nav-preview-vertical-padding / 2}; |
124 | padding-left: 0px; | 125 | padding-left: 0; |
125 | padding-right: 0px; | 126 | padding-right: 0; |
126 | position: absolute; | 127 | position: absolute; |
127 | background-color: pvar(--mainBackgroundColor); | 128 | background-color: pvar(--mainBackgroundColor); |
128 | width: 100% !important; | 129 | width: 100% !important; |
129 | border-top: none; | 130 | border-top: 0; |
130 | border-left: none; | 131 | border-left: 0; |
131 | border-right: none; | 132 | border-right: 0; |
132 | 133 | ||
133 | :last-child { | 134 | :last-child { |
134 | margin-right: pvar(--horizontalMarginContent); | 135 | margin-right: pvar(--horizontalMarginContent); |
@@ -148,7 +149,7 @@ $input-border-radius: 3px; | |||
148 | margin-top: #{$nav-preview-tab-height + $nav-preview-vertical-padding} !important; | 149 | margin-top: #{$nav-preview-tab-height + $nav-preview-vertical-padding} !important; |
149 | height: calc(100vh - #{$header-height + $nav-preview-tab-height + $nav-preview-vertical-padding}) !important; | 150 | height: calc(100vh - #{$header-height + $nav-preview-tab-height + $nav-preview-vertical-padding}) !important; |
150 | width: 50% !important; | 151 | width: 50% !important; |
151 | border: none !important; | 152 | border: 0 !important; |
152 | border-radius: unset !important; | 153 | border-radius: unset !important; |
153 | } | 154 | } |
154 | 155 | ||
@@ -249,11 +250,11 @@ $input-border-radius: 3px; | |||
249 | } | 250 | } |
250 | 251 | ||
251 | @media only screen and (min-width: $small-view) { | 252 | @media only screen and (min-width: $small-view) { |
253 | @include maximized-in-medium-view(); | ||
254 | |||
252 | :host-context(.expanded) { | 255 | :host-context(.expanded) { |
253 | @include in-medium-view(); | 256 | @include in-medium-view(); |
254 | } | 257 | } |
255 | |||
256 | @include maximized-in-medium-view(); | ||
257 | } | 258 | } |
258 | 259 | ||
259 | @media only screen and (min-width: #{$small-view + $menu-width}) { | 260 | @media only screen and (min-width: #{$small-view + $menu-width}) { |
diff --git a/client/src/app/shared/shared-forms/peertube-checkbox.component.scss b/client/src/app/shared/shared-forms/peertube-checkbox.component.scss index cf8540dc3..203b82d0b 100644 --- a/client/src/app/shared/shared-forms/peertube-checkbox.component.scss +++ b/client/src/app/shared/shared-forms/peertube-checkbox.component.scss | |||
@@ -46,7 +46,7 @@ | |||
46 | line-height: 12px; | 46 | line-height: 12px; |
47 | font-weight: 500; | 47 | font-weight: 500; |
48 | color: pvar(--inputPlaceholderColor); | 48 | color: pvar(--inputPlaceholderColor); |
49 | background-color: rgba(217,225,232,.1); | 49 | background-color: rgba(217, 225, 232, .1); |
50 | border: 1px solid rgba(217,225,232,.5); | 50 | border: 1px solid rgba(217, 225, 232, .5); |
51 | } | 51 | } |
52 | } \ No newline at end of file | 52 | } |
diff --git a/client/src/app/shared/shared-forms/preview-upload.component.scss b/client/src/app/shared/shared-forms/preview-upload.component.scss index 88eccd5f7..c2ee0d6a9 100644 --- a/client/src/app/shared/shared-forms/preview-upload.component.scss +++ b/client/src/app/shared/shared-forms/preview-upload.component.scss | |||
@@ -21,7 +21,7 @@ | |||
21 | max-width: 100%; | 21 | max-width: 100%; |
22 | 22 | ||
23 | &.no-image { | 23 | &.no-image { |
24 | border: 2px solid grey; | 24 | border: 2px solid #808080; |
25 | background-color: pvar(--mainBackgroundColor); | 25 | background-color: pvar(--mainBackgroundColor); |
26 | } | 26 | } |
27 | } | 27 | } |
diff --git a/client/src/app/shared/shared-forms/select/select-shared.component.scss b/client/src/app/shared/shared-forms/select/select-shared.component.scss index 80196b8df..7006adab1 100644 --- a/client/src/app/shared/shared-forms/select/select-shared.component.scss +++ b/client/src/app/shared/shared-forms/select/select-shared.component.scss | |||
@@ -32,7 +32,7 @@ ng-select ::ng-deep { | |||
32 | } | 32 | } |
33 | 33 | ||
34 | .root { | 34 | .root { |
35 | display:flex; | 35 | display: flex; |
36 | align-items: center; | 36 | align-items: center; |
37 | 37 | ||
38 | > my-select-options { | 38 | > my-select-options { |
@@ -41,9 +41,9 @@ ng-select ::ng-deep { | |||
41 | } | 41 | } |
42 | 42 | ||
43 | my-select-options + input { | 43 | my-select-options + input { |
44 | margin-left: 5px; | ||
45 | |||
46 | @include peertube-input-text($form-base-input-width); | 44 | @include peertube-input-text($form-base-input-width); |
45 | |||
46 | margin-left: 5px; | ||
47 | display: block; | 47 | display: block; |
48 | } | 48 | } |
49 | 49 | ||
diff --git a/client/src/app/shared/shared-forms/shared-form.module.ts b/client/src/app/shared/shared-forms/shared-form.module.ts index 9bdd138a1..5417f7342 100644 --- a/client/src/app/shared/shared-forms/shared-form.module.ts +++ b/client/src/app/shared/shared-forms/shared-form.module.ts | |||
@@ -5,6 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms' | |||
5 | import { NgSelectModule } from '@ng-select/ng-select' | 5 | import { NgSelectModule } from '@ng-select/ng-select' |
6 | import { SharedGlobalIconModule } from '../shared-icons' | 6 | import { SharedGlobalIconModule } from '../shared-icons' |
7 | import { SharedMainModule } from '../shared-main/shared-main.module' | 7 | import { SharedMainModule } from '../shared-main/shared-main.module' |
8 | import { AdvancedInputFilterComponent } from './advanced-input-filter.component' | ||
8 | import { DynamicFormFieldComponent } from './dynamic-form-field.component' | 9 | import { DynamicFormFieldComponent } from './dynamic-form-field.component' |
9 | import { FormValidatorService } from './form-validator.service' | 10 | import { FormValidatorService } from './form-validator.service' |
10 | import { InputSwitchComponent } from './input-switch.component' | 11 | import { InputSwitchComponent } from './input-switch.component' |
@@ -52,7 +53,9 @@ import { TimestampInputComponent } from './timestamp-input.component' | |||
52 | SelectCheckboxComponent, | 53 | SelectCheckboxComponent, |
53 | SelectCustomValueComponent, | 54 | SelectCustomValueComponent, |
54 | 55 | ||
55 | DynamicFormFieldComponent | 56 | DynamicFormFieldComponent, |
57 | |||
58 | AdvancedInputFilterComponent | ||
56 | ], | 59 | ], |
57 | 60 | ||
58 | exports: [ | 61 | exports: [ |
@@ -78,7 +81,9 @@ import { TimestampInputComponent } from './timestamp-input.component' | |||
78 | SelectCheckboxComponent, | 81 | SelectCheckboxComponent, |
79 | SelectCustomValueComponent, | 82 | SelectCustomValueComponent, |
80 | 83 | ||
81 | DynamicFormFieldComponent | 84 | DynamicFormFieldComponent, |
85 | |||
86 | AdvancedInputFilterComponent | ||
82 | ], | 87 | ], |
83 | 88 | ||
84 | providers: [ | 89 | providers: [ |
diff --git a/client/src/app/shared/shared-forms/timestamp-input.component.scss b/client/src/app/shared/shared-forms/timestamp-input.component.scss index 66e9aa032..36f5711a6 100644 --- a/client/src/app/shared/shared-forms/timestamp-input.component.scss +++ b/client/src/app/shared/shared-forms/timestamp-input.component.scss | |||
@@ -4,8 +4,7 @@ p-inputmask { | |||
4 | ::ng-deep input { | 4 | ::ng-deep input { |
5 | width: 80px; | 5 | width: 80px; |
6 | font-size: 15px; | 6 | font-size: 15px; |
7 | 7 | border: 0; | |
8 | border: none; | ||
9 | 8 | ||
10 | &:focus-within, | 9 | &:focus-within, |
11 | &:focus { | 10 | &:focus { |