From 52c4976fcf4ee255a3af68ff9778e4f5c4f84bd4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 11 Aug 2020 16:07:53 +0200 Subject: Use ng select for multiselect --- client/src/app/shared/shared-forms/select/index.ts | 4 ++ .../select/select-channel.component.html | 16 +++++ .../select/select-channel.component.ts | 58 +++++++++++++++++ .../select/select-checkbox.component.html | 41 ++++++++++++ .../select/select-checkbox.component.scss | 18 ++++++ .../select/select-checkbox.component.ts | 75 ++++++++++++++++++++++ .../select/select-options.component.html | 19 ++++++ .../select/select-options.component.ts | 49 ++++++++++++++ .../select/select-shared.component.scss | 32 +++++++++ .../shared-forms/select/select-tags.component.html | 13 ++++ .../shared-forms/select/select-tags.component.scss | 3 + .../shared-forms/select/select-tags.component.ts | 38 +++++++++++ 12 files changed, 366 insertions(+) create mode 100644 client/src/app/shared/shared-forms/select/index.ts create mode 100644 client/src/app/shared/shared-forms/select/select-channel.component.html create mode 100644 client/src/app/shared/shared-forms/select/select-channel.component.ts create mode 100644 client/src/app/shared/shared-forms/select/select-checkbox.component.html create mode 100644 client/src/app/shared/shared-forms/select/select-checkbox.component.scss create mode 100644 client/src/app/shared/shared-forms/select/select-checkbox.component.ts create mode 100644 client/src/app/shared/shared-forms/select/select-options.component.html create mode 100644 client/src/app/shared/shared-forms/select/select-options.component.ts create mode 100644 client/src/app/shared/shared-forms/select/select-shared.component.scss create mode 100644 client/src/app/shared/shared-forms/select/select-tags.component.html create mode 100644 client/src/app/shared/shared-forms/select/select-tags.component.scss create mode 100644 client/src/app/shared/shared-forms/select/select-tags.component.ts (limited to 'client/src/app/shared/shared-forms/select') diff --git a/client/src/app/shared/shared-forms/select/index.ts b/client/src/app/shared/shared-forms/select/index.ts new file mode 100644 index 000000000..33459b23b --- /dev/null +++ b/client/src/app/shared/shared-forms/select/index.ts @@ -0,0 +1,4 @@ +export * from './select-channel.component' +export * from './select-options.component' +export * from './select-tags.component' +export * from './select-checkbox.component' diff --git a/client/src/app/shared/shared-forms/select/select-channel.component.html b/client/src/app/shared/shared-forms/select/select-channel.component.html new file mode 100644 index 000000000..897d13ee7 --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-channel.component.html @@ -0,0 +1,16 @@ + + + + {{ channel.label }} + + diff --git a/client/src/app/shared/shared-forms/select/select-channel.component.ts b/client/src/app/shared/shared-forms/select/select-channel.component.ts new file mode 100644 index 000000000..1b0db9b6f --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-channel.component.ts @@ -0,0 +1,58 @@ +import { Component, forwardRef, Input } from '@angular/core' +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' +import { Actor } from '@app/shared/shared-main/account/actor.model' + +export type SelectChannelItem = { + id: number + label: string + support: string + avatarPath?: string +} + +@Component({ + selector: 'my-select-channel', + styleUrls: [ './select-shared.component.scss' ], + templateUrl: './select-channel.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SelectChannelComponent), + multi: true + } + ] +}) +export class SelectChannelComponent implements ControlValueAccessor { + @Input() items: SelectChannelItem[] = [] + + selectedId: number + + // ng-select options + bindLabel = 'label' + bindValue = 'id' + clearable = false + searchable = false + + get channels () { + return this.items.map(c => Object.assign(c, { + avatarPath: c.avatarPath ? c.avatarPath : Actor.GET_DEFAULT_AVATAR_URL() + })) + } + + propagateChange = (_: any) => { /* empty */ } + + writeValue (id: number) { + this.selectedId = id + } + + registerOnChange (fn: (_: any) => void) { + this.propagateChange = fn + } + + registerOnTouched () { + // Unused + } + + onModelChange () { + this.propagateChange(this.selectedId) + } +} diff --git a/client/src/app/shared/shared-forms/select/select-checkbox.component.html b/client/src/app/shared/shared-forms/select/select-checkbox.component.html new file mode 100644 index 000000000..3f81dd152 --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-checkbox.component.html @@ -0,0 +1,41 @@ + + + +
+ + + {{ item.group }} +
+
+ + +
+ + + {{ item.label }} +
+
+ +
diff --git a/client/src/app/shared/shared-forms/select/select-checkbox.component.scss b/client/src/app/shared/shared-forms/select/select-checkbox.component.scss new file mode 100644 index 000000000..145f6b26c --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-checkbox.component.scss @@ -0,0 +1,18 @@ +@import '_variables'; +@import '_mixins'; + +ng-select ::ng-deep { + .ng-option { + display: flex; + align-items: center; + } + + .form-group-checkbox { + display: flex; + align-items: center; + + input { + @include peertube-checkbox(1px); + } + } +} diff --git a/client/src/app/shared/shared-forms/select/select-checkbox.component.ts b/client/src/app/shared/shared-forms/select/select-checkbox.component.ts new file mode 100644 index 000000000..93653fef1 --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-checkbox.component.ts @@ -0,0 +1,75 @@ +import { Component, Input, forwardRef } from '@angular/core' +import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms' +import { SelectOptionsItem } from './select-options.component' + +export type ItemSelectCheckboxValue = { id?: string | number, group?: string } | string + +@Component({ + selector: 'my-select-checkbox', + styleUrls: [ './select-shared.component.scss', 'select-checkbox.component.scss' ], + templateUrl: './select-checkbox.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SelectCheckboxComponent), + multi: true + } + ] +}) +export class SelectCheckboxComponent implements ControlValueAccessor { + @Input() availableItems: SelectOptionsItem[] = [] + @Input() selectedItems: ItemSelectCheckboxValue[] = [] + @Input() selectableGroup: boolean + @Input() selectableGroupAsModel: boolean + @Input() maxSelectedItems: number + + propagateChange = (_: any) => { /* empty */ } + + writeValue (items: ItemSelectCheckboxValue[]) { + if (Array.isArray(items)) { + this.selectedItems = items.map(i => { + if (typeof i === 'string' || typeof i === 'number') { + return i + '' + } + + if (i.group) { + return { group: i.group } + } + + return { id: i.id + '' } + }) + } else { + this.selectedItems = items + } + + this.propagateChange(this.selectedItems) + } + + registerOnChange (fn: (_: any) => void) { + this.propagateChange = fn + } + + registerOnTouched () { + // Unused + } + + onModelChange () { + this.propagateChange(this.selectedItems) + } + + compareFn (item: SelectOptionsItem, selected: ItemSelectCheckboxValue) { + if (typeof selected === 'string') { + return item.id === selected + } + + if (this.selectableGroup && item.group && selected.group) { + return item.group === selected.group + } + + if (selected.id && item.id) { + return item.id === selected.id + } + + return false + } +} diff --git a/client/src/app/shared/shared-forms/select/select-options.component.html b/client/src/app/shared/shared-forms/select/select-options.component.html new file mode 100644 index 000000000..48eca1cf5 --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-options.component.html @@ -0,0 +1,19 @@ + + + {{ item.label }} + +
+ {{ item.description }} +
+
+
diff --git a/client/src/app/shared/shared-forms/select/select-options.component.ts b/client/src/app/shared/shared-forms/select/select-options.component.ts new file mode 100644 index 000000000..3ba24c732 --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-options.component.ts @@ -0,0 +1,49 @@ +import { Component, Input, forwardRef } from '@angular/core' +import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms' + +export type SelectOptionsItem = { + id: string | number + label: string + description?: string + group?: string + groupLabel?: string +} + +@Component({ + selector: 'my-select-options', + styleUrls: [ './select-shared.component.scss' ], + templateUrl: './select-options.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SelectOptionsComponent), + multi: true + } + ] +}) +export class SelectOptionsComponent implements ControlValueAccessor { + @Input() items: SelectOptionsItem[] = [] + @Input() clearable = false + @Input() searchable = false + @Input() groupBy: string + + selectedId: number | string + + propagateChange = (_: any) => { /* empty */ } + + writeValue (id: number | string) { + this.selectedId = id + } + + registerOnChange (fn: (_: any) => void) { + this.propagateChange = fn + } + + registerOnTouched () { + // Unused + } + + onModelChange () { + this.propagateChange(this.selectedId) + } +} 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 new file mode 100644 index 000000000..0b4c6b784 --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-shared.component.scss @@ -0,0 +1,32 @@ +@import '_variables'; +@import '_mixins'; + +$form-base-input-width: auto; + +ng-select { + width: $form-base-input-width; + + @media screen and (max-width: $form-base-input-width) { + width: 100%; + } +} + +ng-select ::ng-deep { + .ng-value-container { + max-height: 100px; + overflow-y: auto; + overflow-x: hidden; + } + + // make sure the image is vertically adjusted + .ng-value-label img { + position: relative; + top: -1px; + } + + img { + border-radius: 50%; + height: 20px; + width: 20px; + } +} diff --git a/client/src/app/shared/shared-forms/select/select-tags.component.html b/client/src/app/shared/shared-forms/select/select-tags.component.html new file mode 100644 index 000000000..e1cd50882 --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-tags.component.html @@ -0,0 +1,13 @@ + + diff --git a/client/src/app/shared/shared-forms/select/select-tags.component.scss b/client/src/app/shared/shared-forms/select/select-tags.component.scss new file mode 100644 index 000000000..ad76bc7ee --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-tags.component.scss @@ -0,0 +1,3 @@ +ng-select ::ng-deep .ng-arrow-wrapper { + display: none; +} diff --git a/client/src/app/shared/shared-forms/select/select-tags.component.ts b/client/src/app/shared/shared-forms/select/select-tags.component.ts new file mode 100644 index 000000000..93d199037 --- /dev/null +++ b/client/src/app/shared/shared-forms/select/select-tags.component.ts @@ -0,0 +1,38 @@ +import { Component, Input, forwardRef } from '@angular/core' +import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms' + +@Component({ + selector: 'my-select-tags', + styleUrls: [ './select-shared.component.scss', './select-tags.component.scss' ], + templateUrl: './select-tags.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SelectTagsComponent), + multi: true + } + ] +}) +export class SelectTagsComponent implements ControlValueAccessor { + @Input() availableItems: string[] = [] + @Input() selectedItems: string[] = [] + + propagateChange = (_: any) => { /* empty */ } + + writeValue (items: string[]) { + this.selectedItems = items + this.propagateChange(this.selectedItems) + } + + registerOnChange (fn: (_: any) => void) { + this.propagateChange = fn + } + + registerOnTouched () { + // Unused + } + + onModelChange () { + this.propagateChange(this.selectedItems) + } +} -- cgit v1.2.3