UI: Refactoring color picker

This commit is contained in:
Vladyslav_Prykhodko 2023-04-17 14:01:15 +03:00
parent 64264a7834
commit 9d86100a6b
5 changed files with 105 additions and 106 deletions

View File

@ -17,21 +17,25 @@
--> -->
<saturation-component class="saturation-component" [hue]="control.hue" [(color)]="control.value"></saturation-component> <saturation-component class="saturation-component" [hue]="control.hue" [(color)]="control.value"></saturation-component>
<div fxFlex fxLayout="row" class="control-component" fxLayoutGap="16px"> <div class="control-component">
<indicator-component [colorType]="presentations[selectedPresentation]" [color]="control.value"></indicator-component> <indicator-component class="indicator-component"
<div fxFlex fxLayout="column" fxLayoutAlign="space-between" class="hue-alpha-range"> [colorType]="presentations[selectedPresentation]"
[color]="control.value">
</indicator-component>
<div class="hue-alpha-range">
<hue-component [(hue)]="control.hue" [(color)]="control.value"></hue-component> <hue-component [(hue)]="control.hue" [(color)]="control.value"></hue-component>
<alpha-component *ngIf="control.alphaChannelVisibilityChanges | async" [(color)]="control.value"></alpha-component> <alpha-component [(color)]="control.value"></alpha-component>
</div> </div>
</div> </div>
<div fxFlex fxLayout="row"> <div class="color-input-block">
<div fxFlex [ngSwitch]="presentations[selectedPresentation]"> <div class="color-input" [ngSwitch]="presentations[selectedPresentation]">
<rgba-input-component *ngSwitchCase="'rgba'" [alpha]="control.alphaChannelVisibilityChanges | async" label [(color)]="control.value" [(hue)]="control.hue"></rgba-input-component> <rgba-input-component *ngSwitchCase="'rgba'" label
<hsla-input-component *ngSwitchCase="'hsla'" [alpha]="control.alphaChannelVisibilityChanges | async" label [(color)]="control.value" [(hue)]="control.hue"></hsla-input-component> [(color)]="control.value" [(hue)]="control.hue"></rgba-input-component>
<hex-input-component *ngSwitchCase="'hex'" label prefix="#" [(color)]="control.value" [(hue)]="control.hue"></hex-input-component> <hsla-input-component *ngSwitchCase="'hsla'" label
</div> [(color)]="control.value" [(hue)]="control.hue"></hsla-input-component>
<div> <hex-input-component *ngSwitchCase="'hex'" label prefix="#" [(color)]="control.value"
<span class="type-btn" (click)="changePresentation()"></span> [(hue)]="control.hue"></hex-input-component>
</div> </div>
<div class="type-btn" (click)="changePresentation()"></div>
</div> </div>

View File

@ -17,15 +17,64 @@
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px;
::ng-deep {
.saturation-component { .saturation-component {
height: 100%; height: 100%;
min-height: 200px; min-height: 200px;
max-height: 300px; max-height: 300px;
border-radius: 8px; border-radius: 8px;
margin-bottom: 16px; }
div.pointer {
.control-component {
max-height: 48px;
display: flex;
gap: 16px;
.indicator-component {
height: 48px;
width: 48px;
border-radius: 8px;
background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAAAAAClZ7nPAAAAAnRSTlMUAFg9Gm0AAAAPSURBVAjXY2D4jxXhEgYAfr8P8QhChVEAAAAASUVORK5CYII=') repeat;
}
.hue-alpha-range {
display: flex;
justify-content: space-between;
flex-direction: column;
flex: 1;
> * {
height: 18px;
border-radius: 9px;
border: 1px solid rgba(0, 0, 0, 0.1);
}
}
}
.color-input-block {
display: flex;
.color-input {
flex: 1;
}
.type-btn {
height: 26px;
width: 20px;
background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAgCAMAAAAootjDAAAAM1BMVEUAAAAzMzMzMzMzMzMzMzM0NDQzMzMzMzM0NDQzMzMzMzM0NDQzMzMzMzMyMjIrKyszMzPF8UZlAAAAEHRSTlMA1fHr4ZxxSRP45sG+sCkGH2+Z6QAAAHJJREFUKM+9kkkSgCAQA0FEVLb5/2tViqgQvNrHviSzKGCt6nDGuNass8i8NsrLiX+bZbrUtDwm7VLYE0zWUtEZ+RvUZpEvN8YhH9QmQRoC8kFpEnVHVP/DJUZVeSAem5fDKxwtms/BR+PT8gN8vwk/0wE1gQzNVYryIwAAAABJRU5ErkJggg==') no-repeat center;
background-size: 6px 12px;
&:hover {
background-color: #eee;
}
}
}
}
:host ::ng-deep {
.saturation-component {
.pointer {
border-width: 2px; border-width: 2px;
width: 16px; width: 16px;
height: 16px; height: 16px;
@ -40,8 +89,6 @@
.hue-alpha-range { .hue-alpha-range {
alpha-component, hue-component { alpha-component, hue-component {
height: 18px;
border: 1px solid rgba(0, 0, 0, 0.1);
.pointer { .pointer {
height: 18px; height: 18px;
width: 18px; width: 18px;
@ -49,43 +96,8 @@
border: 2px solid #fff; border: 2px solid #fff;
} }
.gradient-color { .gradient-color {
border-radius: 24px; border-radius: 9px;
} }
} }
} }
} }
}
.control-component {
max-height: 48px;
margin-bottom: 16px;
}
indicator-component {
height: 48px;
width: 48px;
border-radius: 8px;
background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAAAAAClZ7nPAAAAAnRSTlMUAFg9Gm0AAAAPSURBVAjXY2D4jxXhEgYAfr8P8QhChVEAAAAASUVORK5CYII=') repeat;
}
.hue-alpha-range {
hue-component, alpha-component {
border-radius: 24px;
}
}
color-presets-component {
border-top: 1px solid #d0d0d0;
}
.type-btn {
display: inline-block;
height: 20px;
width: 20px;
background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAgCAMAAAAootjDAAAAM1BMVEUAAAAzMzMzMzMzMzMzMzM0NDQzMzMzMzM0NDQzMzMzMzM0NDQzMzMzMzMyMjIrKyszMzPF8UZlAAAAEHRSTlMA1fHr4ZxxSRP45sG+sCkGH2+Z6QAAAHJJREFUKM+9kkkSgCAQA0FEVLb5/2tViqgQvNrHviSzKGCt6nDGuNass8i8NsrLiX+bZbrUtDwm7VLYE0zWUtEZ+RvUZpEvN8YhH9QmQRoC8kFpEnVHVP/DJUZVeSAem5fDKxwtms/BR+PT8gN8vwk/0wE1gQzNVYryIwAAAABJRU5ErkJggg==') no-repeat center;
background-size: 6px 12px;
&:hover {
background-color: #eee;
}
}

View File

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, OnDestroy, OnInit } from '@angular/core'; import { Component, forwardRef, OnDestroy } from '@angular/core';
import { Color, ColorPickerControl } from '@iplab/ngx-color-picker'; import { Color, ColorPickerControl } from '@iplab/ngx-color-picker';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@ -41,7 +41,7 @@ export enum ColorType {
} }
] ]
}) })
export class ColorPickerComponent implements OnInit, ControlValueAccessor, OnDestroy { export class ColorPickerComponent implements ControlValueAccessor, OnDestroy {
selectedPresentation = 0; selectedPresentation = 0;
presentations = [ColorType.hex, ColorType.rgba, ColorType.hsla]; presentations = [ColorType.hex, ColorType.rgba, ColorType.hsla];
@ -51,16 +51,17 @@ export class ColorPickerComponent implements OnInit, ControlValueAccessor, OnDes
private subscriptions: Array<Subscription> = []; private subscriptions: Array<Subscription> = [];
private propagateChange = null; private propagateChange = (v: any) => {};
private setValue = false;
constructor() { constructor() {
}
public ngOnInit(): void {
this.subscriptions.push( this.subscriptions.push(
this.control.valueChanges.subscribe(value => { this.control.valueChanges.subscribe(() => {
if (this.modelValue) { if (!this.setValue) {
this.updateModel(); this.updateModel();
} else {
this.setValue = false;
} }
}) })
); );
@ -74,6 +75,7 @@ export class ColorPickerComponent implements OnInit, ControlValueAccessor, OnDes
} }
writeValue(value: string): void { writeValue(value: string): void {
this.setValue = !!value;
this.control.setValueFrom(value || '#fff'); this.control.setValueFrom(value || '#fff');
this.modelValue = value; this.modelValue = value;

View File

@ -15,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<form [formGroup]="colorPickerFormGroup" (ngSubmit)="select()"> <form [formGroup]="colorPickerFormGroup" (ngSubmit)="select()" style="width: 320px">
<div mat-dialog-content style="padding: 16px; width: 320px; min-width: 100%; max-width: 100%;" fxLayout="row" fxLayoutAlign="center"> <div mat-dialog-content style="padding: 16px; display: flex">
<tb-color-picker formControlName="color"></tb-color-picker> <tb-color-picker formControlName="color"></tb-color-picker>
</div> </div>
<div mat-dialog-actions fxLayout="row"> <div mat-dialog-actions fxLayout="row">

View File

@ -14,19 +14,11 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; import { Component, Inject, OnInit } from '@angular/core';
import { ErrorStateMatcher } from '@angular/material/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { import { FormBuilder, FormGroup, Validators } from '@angular/forms';
FormGroupDirective,
NgForm,
UntypedFormBuilder,
UntypedFormControl,
UntypedFormGroup,
Validators
} from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DialogComponent } from '@shared/components/dialog.component'; import { DialogComponent } from '@shared/components/dialog.component';
@ -37,22 +29,18 @@ export interface ColorPickerDialogData {
@Component({ @Component({
selector: 'tb-color-picker-dialog', selector: 'tb-color-picker-dialog',
templateUrl: './color-picker-dialog.component.html', templateUrl: './color-picker-dialog.component.html',
providers: [{provide: ErrorStateMatcher, useExisting: ColorPickerDialogComponent}],
styleUrls: [] styleUrls: []
}) })
export class ColorPickerDialogComponent extends DialogComponent<ColorPickerDialogComponent, string> export class ColorPickerDialogComponent extends DialogComponent<ColorPickerDialogComponent, string>
implements OnInit, ErrorStateMatcher { implements OnInit {
colorPickerFormGroup: UntypedFormGroup; colorPickerFormGroup: FormGroup;
submitted = false;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
protected router: Router, protected router: Router,
@Inject(MAT_DIALOG_DATA) public data: ColorPickerDialogData, @Inject(MAT_DIALOG_DATA) public data: ColorPickerDialogData,
@SkipSelf() private errorStateMatcher: ErrorStateMatcher,
public dialogRef: MatDialogRef<ColorPickerDialogComponent, string>, public dialogRef: MatDialogRef<ColorPickerDialogComponent, string>,
public fb: UntypedFormBuilder,) { public fb: FormBuilder) {
super(store, router, dialogRef); super(store, router, dialogRef);
} }
@ -62,18 +50,11 @@ export class ColorPickerDialogComponent extends DialogComponent<ColorPickerDialo
}); });
} }
isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean {
const originalErrorState = this.errorStateMatcher.isErrorState(control, form);
const customErrorState = !!(control && control.invalid && this.submitted);
return originalErrorState || customErrorState;
}
cancel(): void { cancel(): void {
this.dialogRef.close(null); this.dialogRef.close(null);
} }
select(): void { select(): void {
this.submitted = true;
const color: string = this.colorPickerFormGroup.get('color').value; const color: string = this.colorPickerFormGroup.get('color').value;
this.dialogRef.close(color); this.dialogRef.close(color);
} }