Support custom value in ng-select
This commit is contained in:
parent
b9c9fefe82
commit
ead64cdf8d
|
@ -445,13 +445,11 @@
|
|||
<span i18n>allows to import multiple videos in parallel. ⚠️ Requires a PeerTube restart.</span>
|
||||
</span>
|
||||
|
||||
<div class="peertube-select-container">
|
||||
<select id="importConcurrency" formControlName="concurrency" class="form-control">
|
||||
<option *ngFor="let option of concurrencyOptions" [value]="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
</select>
|
||||
<div class="number-with-unit">
|
||||
<input type="number" name="importConcurrency" formControlName="concurrency" />
|
||||
<span i18n>jobs in parallel</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="formErrors.import.concurrency" class="form-error">{{ formErrors.import.concurrency }}</div>
|
||||
</div>
|
||||
|
||||
|
@ -892,13 +890,13 @@
|
|||
<ng-container *ngIf="!getTotalTranscodingThreads().atMost" i18n>will claim at least {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with live transcoding</ng-container>
|
||||
</span>
|
||||
|
||||
<div class="peertube-select-container">
|
||||
<select id="transcodingThreads" formControlName="threads" class="form-control">
|
||||
<option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
|
||||
{{ transcodingThreadOption.label }} {transcodingThreadOption.value, plural, =0 {} =1 {thread} other {threads}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<my-select-custom-value
|
||||
id="transcodingThreads"
|
||||
[items]="transcodingThreadOptions"
|
||||
formControlName="threads"
|
||||
[clearable]="false"
|
||||
></my-select-custom-value>
|
||||
|
||||
<div *ngIf="formErrors.transcoding.threads" class="form-error">{{ formErrors.transcoding.threads }}</div>
|
||||
</div>
|
||||
|
||||
|
@ -908,13 +906,11 @@
|
|||
<span i18n>allows to transcode multiple files in parallel. ⚠️ Requires a PeerTube restart.</span>
|
||||
</span>
|
||||
|
||||
<div class="peertube-select-container">
|
||||
<select id="transcodingConcurrency" formControlName="concurrency" class="form-control">
|
||||
<option *ngFor="let option of concurrencyOptions" [value]="option">
|
||||
{{ option }}
|
||||
</option>
|
||||
</select>
|
||||
<div class="number-with-unit">
|
||||
<input type="number" name="transcodingConcurrency" formControlName="concurrency" />
|
||||
<span i18n>jobs in parallel</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="formErrors.transcoding.concurrency" class="form-error">{{ formErrors.transcoding.concurrency }}</div>
|
||||
</div>
|
||||
|
||||
|
@ -922,7 +918,7 @@
|
|||
<label i18n for="transcodingProfile">Transcoding profile</label>
|
||||
<span class="text-muted ml-1" i18n>new transcoding profiles can be added by PeerTube plugins</span>
|
||||
|
||||
<ng-select
|
||||
<my-select-options
|
||||
id="transcodingProfile"
|
||||
formControlName="profile"
|
||||
[items]="getAvailableTranscodingProfile('vod')"
|
||||
|
@ -935,7 +931,7 @@
|
|||
<span class="text-muted" i18n>x264, targeting maximum device compatibility</span>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</ng-select>
|
||||
</my-select-options>
|
||||
<div *ngIf="formErrors.transcoding.profile" class="form-error">{{ formErrors.transcoding.profile }}</div>
|
||||
</div>
|
||||
|
||||
|
@ -1007,10 +1003,10 @@
|
|||
<div class="form-group" [ngClass]="{ 'disabled-checkbox-extra': !isLiveEnabled() }">
|
||||
<label i18n for="liveMaxDuration">Max live duration</label>
|
||||
|
||||
<ng-select
|
||||
<my-select-options
|
||||
labelForId="liveMaxDuration" [items]="liveMaxDurationOptions" formControlName="maxDuration"
|
||||
bindLabel="label" bindValue="value" [clearable]="false" [searchable]="false"
|
||||
></ng-select>
|
||||
bindLabel="label" bindValue="value" [clearable]="false" [searchable]="true"
|
||||
></my-select-options>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
|
@ -1069,13 +1065,12 @@
|
|||
<ng-container *ngIf="!getTotalTranscodingThreads().atMost" i18n>will claim at least {{ getTotalTranscodingThreads().value }} {{ getTotalTranscodingThreads().unit }} with VOD transcoding</ng-container>
|
||||
</span>
|
||||
|
||||
<div class="peertube-select-container">
|
||||
<select id="liveTranscodingThreads" formControlName="threads" class="form-control">
|
||||
<option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
|
||||
{{ transcodingThreadOption.label }} {transcodingThreadOption.value, plural, =0 {} =1 {thread} other {threads}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<my-select-custom-value
|
||||
id="liveTranscodingThreads"
|
||||
[items]="transcodingThreadOptions"
|
||||
formControlName="threads"
|
||||
[clearable]="false"
|
||||
></my-select-custom-value>
|
||||
<div *ngIf="formErrors.live.transcoding.threads" class="form-error">{{ formErrors.live.transcoding.threads }}</div>
|
||||
</div>
|
||||
|
||||
|
@ -1083,7 +1078,7 @@
|
|||
<label i18n for="liveTranscodingProfile">Live transcoding profile</label>
|
||||
<span class="text-muted ml-1" i18n>new live transcoding profiles can be added by PeerTube plugins</span>
|
||||
|
||||
<ng-select
|
||||
<my-select-options
|
||||
id="liveTranscodingProfile"
|
||||
formControlName="profile"
|
||||
[items]="getAvailableTranscodingProfile('live')"
|
||||
|
@ -1096,7 +1091,7 @@
|
|||
<span class="text-muted" i18n>x264, targeting maximum device compatibility</span>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</ng-select>
|
||||
</my-select-options>
|
||||
<div *ngIf="formErrors.live.transcoding.profile" class="form-error">{{ formErrors.live.transcoding.profile }}</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -42,7 +42,8 @@ input[type=checkbox] {
|
|||
@include peertube-select-container($form-base-input-width);
|
||||
}
|
||||
|
||||
ng-select,
|
||||
my-select-options,
|
||||
my-select-custom-value,
|
||||
my-select-checkbox {
|
||||
@include responsive-width($form-base-input-width);
|
||||
|
||||
|
|
|
@ -37,12 +37,9 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A
|
|||
|
||||
resolutions: { id: string, label: string, description?: string }[] = []
|
||||
liveResolutions: { id: string, label: string, description?: string }[] = []
|
||||
concurrencyOptions: number[] = []
|
||||
transcodingThreadOptions: { label: string, value: number }[] = []
|
||||
liveMaxDurationOptions: { label: string, value: number }[] = []
|
||||
|
||||
vodTranscodingProfileOptions: string[] = []
|
||||
liveTranscodingProfileOptions: string[] = []
|
||||
transcodingThreadOptions: SelectOptionsItem[] = []
|
||||
liveMaxDurationOptions: SelectOptionsItem[] = []
|
||||
|
||||
languageItems: SelectOptionsItem[] = []
|
||||
categoryItems: SelectOptionsItem[] = []
|
||||
|
@ -99,23 +96,22 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A
|
|||
this.liveResolutions = this.resolutions.filter(r => r.id !== '0p')
|
||||
|
||||
this.transcodingThreadOptions = [
|
||||
{ value: 0, label: $localize`Auto (via ffmpeg)` },
|
||||
{ value: 1, label: '1' },
|
||||
{ value: 2, label: '2' },
|
||||
{ value: 4, label: '4' },
|
||||
{ value: 8, label: '8' }
|
||||
{ id: 0, label: $localize`Auto (via ffmpeg)` },
|
||||
{ id: 1, label: '1' },
|
||||
{ id: 2, label: '2' },
|
||||
{ id: 4, label: '4' },
|
||||
{ id: 8, label: '8' },
|
||||
{ id: 12, label: '12' },
|
||||
{ id: 16, label: '16' },
|
||||
{ id: 32, label: '32' }
|
||||
]
|
||||
this.concurrencyOptions = [ 1, 2, 3, 4, 5, 6 ]
|
||||
|
||||
this.vodTranscodingProfileOptions = [ 'default' ]
|
||||
this.liveTranscodingProfileOptions = [ 'default' ]
|
||||
|
||||
this.liveMaxDurationOptions = [
|
||||
{ value: -1, label: $localize`No limit` },
|
||||
{ value: 1000 * 3600, label: $localize`1 hour` },
|
||||
{ value: 1000 * 3600 * 3, label: $localize`3 hours` },
|
||||
{ value: 1000 * 3600 * 5, label: $localize`5 hours` },
|
||||
{ value: 1000 * 3600 * 10, label: $localize`10 hours` }
|
||||
{ id: -1, label: $localize`No limit` },
|
||||
{ id: 1000 * 3600, label: $localize`1 hour` },
|
||||
{ id: 1000 * 3600 * 3, label: $localize`3 hours` },
|
||||
{ id: 1000 * 3600 * 5, label: $localize`5 hours` },
|
||||
{ id: 1000 * 3600 * 10, label: $localize`10 hours` }
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -137,11 +133,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit, A
|
|||
}
|
||||
|
||||
getAvailableTranscodingProfile (type: 'live' | 'vod') {
|
||||
if (type === 'live') {
|
||||
return this.serverConfig.live.transcoding.availableProfiles
|
||||
}
|
||||
const profiles = type === 'live'
|
||||
? this.serverConfig.live.transcoding.availableProfiles
|
||||
: this.serverConfig.transcoding.availableProfiles
|
||||
|
||||
return this.serverConfig.transcoding.availableProfiles
|
||||
return profiles.map(p => ({ id: p, label: p }))
|
||||
}
|
||||
|
||||
getTotalTranscodingThreads () {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export * from './select-channel.component'
|
||||
export * from './select-checkbox.component'
|
||||
export * from './select-custom-value.component'
|
||||
export * from './select-options.component'
|
||||
export * from './select-tags.component'
|
||||
export * from './select-checkbox.component'
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<ng-select
|
||||
[(ngModel)]="selectedId"
|
||||
(ngModelChange)="onModelChange()"
|
||||
[bindLabel]="bindLabel"
|
||||
[bindValue]="bindValue"
|
||||
[clearable]="clearable"
|
||||
[searchable]="searchable"
|
||||
>
|
||||
<ng-option *ngFor="let item of items" [value]="item.id">
|
||||
{{ channel.label }}
|
||||
</ng-option>
|
||||
|
||||
<ng-content></ng-content>
|
||||
</ng-select>
|
|
@ -1,44 +0,0 @@
|
|||
import { Component, forwardRef, Input } from '@angular/core'
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||
|
||||
@Component({
|
||||
selector: 'my-select-custom-input',
|
||||
styleUrls: [ './select-custom-input.component.scss' ],
|
||||
templateUrl: './select-custom-input.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => SelectCustomInputComponent),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class SelectCustomInputComponent implements ControlValueAccessor {
|
||||
@Input() items: any[] = []
|
||||
|
||||
selectedId: number
|
||||
|
||||
// ng-select options
|
||||
bindLabel = 'label'
|
||||
bindValue = 'id'
|
||||
clearable = false
|
||||
searchable = false
|
||||
|
||||
propagateChange = (_: any) => { /* empty */ }
|
||||
|
||||
writeValue (id: number) {
|
||||
this.selectedId = id
|
||||
}
|
||||
|
||||
registerOnChange (fn: (_: any) => void) {
|
||||
this.propagateChange = fn
|
||||
}
|
||||
|
||||
registerOnTouched () {
|
||||
// Unused
|
||||
}
|
||||
|
||||
onModelChange () {
|
||||
this.propagateChange(this.selectedId)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<div class="root">
|
||||
<my-select-options
|
||||
[items]="itemsWithCustom"
|
||||
[clearable]="clearable"
|
||||
[searchable]="searchable"
|
||||
[groupBy]="groupBy"
|
||||
[labelForId]="labelForId"
|
||||
|
||||
[(ngModel)]="selectedId"
|
||||
(ngModelChange)="onModelChange()"
|
||||
></my-select-options>
|
||||
|
||||
<input *ngIf="isCustomValue()" [(ngModel)]="customValue" (ngModelChange)="onModelChange()" type="text" class="form-control" />
|
||||
</div>
|
|
@ -0,0 +1,76 @@
|
|||
import { Component, forwardRef, Input, OnChanges } from '@angular/core'
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||
import { SelectOptionsItem } from './select-options.component'
|
||||
|
||||
@Component({
|
||||
selector: 'my-select-custom-value',
|
||||
styleUrls: [ './select-shared.component.scss' ],
|
||||
templateUrl: './select-custom-value.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => SelectCustomValueComponent),
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class SelectCustomValueComponent implements ControlValueAccessor, OnChanges {
|
||||
@Input() items: SelectOptionsItem[] = []
|
||||
@Input() clearable = false
|
||||
@Input() searchable = false
|
||||
@Input() groupBy: string
|
||||
@Input() labelForId: string
|
||||
|
||||
customValue: number | string = ''
|
||||
selectedId: number | string
|
||||
|
||||
itemsWithCustom: SelectOptionsItem[] = []
|
||||
|
||||
ngOnChanges () {
|
||||
this.itemsWithCustom = this.getItems()
|
||||
}
|
||||
|
||||
propagateChange = (_: any) => { /* empty */ }
|
||||
|
||||
writeValue (id: number | string) {
|
||||
this.selectedId = id
|
||||
|
||||
if (this.isSelectedIdInItems() !== true) {
|
||||
this.selectedId = 'other'
|
||||
this.customValue = id
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange (fn: (_: any) => void) {
|
||||
this.propagateChange = fn
|
||||
}
|
||||
|
||||
registerOnTouched () {
|
||||
// Unused
|
||||
}
|
||||
|
||||
onModelChange () {
|
||||
if (this.selectedId === 'other') {
|
||||
return this.propagateChange(this.customValue)
|
||||
}
|
||||
|
||||
return this.propagateChange(this.selectedId)
|
||||
}
|
||||
|
||||
isSelectedIdInItems () {
|
||||
return !!this.items.find(i => i.id === this.selectedId)
|
||||
}
|
||||
|
||||
getItems () {
|
||||
const other: SelectOptionsItem = {
|
||||
id: 'other',
|
||||
label: $localize`Custom value...`
|
||||
}
|
||||
|
||||
return this.items.concat([ other ])
|
||||
}
|
||||
|
||||
isCustomValue () {
|
||||
return this.selectedId === 'other'
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
[clearable]="clearable"
|
||||
[labelForId]="labelForId"
|
||||
[searchable]="searchable"
|
||||
[searchFn]="searchFn"
|
||||
|
||||
bindLabel="label"
|
||||
bindValue="id"
|
||||
|
|
|
@ -27,6 +27,7 @@ export class SelectOptionsComponent implements ControlValueAccessor {
|
|||
@Input() searchable = false
|
||||
@Input() groupBy: string
|
||||
@Input() labelForId: string
|
||||
@Input() searchFn: any
|
||||
|
||||
selectedId: number | string
|
||||
|
||||
|
|
|
@ -30,3 +30,18 @@ ng-select ::ng-deep {
|
|||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.root {
|
||||
display:flex;
|
||||
|
||||
> my-select-options {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
margin-left: 5px;
|
||||
|
||||
@include peertube-input-text($form-base-input-width);
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -7,13 +7,19 @@ import { SharedGlobalIconModule } from '../shared-icons'
|
|||
import { SharedMainModule } from '../shared-main/shared-main.module'
|
||||
import { DynamicFormFieldComponent } from './dynamic-form-field.component'
|
||||
import { FormValidatorService } from './form-validator.service'
|
||||
import { InputToggleHiddenComponent } from './input-toggle-hidden.component'
|
||||
import { InputSwitchComponent } from './input-switch.component'
|
||||
import { InputToggleHiddenComponent } from './input-toggle-hidden.component'
|
||||
import { MarkdownTextareaComponent } from './markdown-textarea.component'
|
||||
import { PeertubeCheckboxComponent } from './peertube-checkbox.component'
|
||||
import { PreviewUploadComponent } from './preview-upload.component'
|
||||
import { ReactiveFileComponent } from './reactive-file.component'
|
||||
import { SelectChannelComponent, SelectCheckboxComponent, SelectOptionsComponent, SelectTagsComponent } from './select'
|
||||
import {
|
||||
SelectChannelComponent,
|
||||
SelectCheckboxComponent,
|
||||
SelectCustomValueComponent,
|
||||
SelectOptionsComponent,
|
||||
SelectTagsComponent
|
||||
} from './select'
|
||||
import { TextareaAutoResizeDirective } from './textarea-autoresize.directive'
|
||||
import { TimestampInputComponent } from './timestamp-input.component'
|
||||
|
||||
|
@ -44,6 +50,7 @@ import { TimestampInputComponent } from './timestamp-input.component'
|
|||
SelectOptionsComponent,
|
||||
SelectTagsComponent,
|
||||
SelectCheckboxComponent,
|
||||
SelectCustomValueComponent,
|
||||
|
||||
DynamicFormFieldComponent
|
||||
],
|
||||
|
@ -69,6 +76,7 @@ import { TimestampInputComponent } from './timestamp-input.component'
|
|||
SelectOptionsComponent,
|
||||
SelectTagsComponent,
|
||||
SelectCheckboxComponent,
|
||||
SelectCustomValueComponent,
|
||||
|
||||
DynamicFormFieldComponent
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue