(FuseMaterialColorPicker) Greatly simplified the color picker, added reactive forms support and improved its design, closes #79

This commit is contained in:
Sercan Yemen 2018-07-26 14:10:35 +03:00
parent bbd59ab6c3
commit 0f1048cb3c
5 changed files with 196 additions and 217 deletions

View File

@ -1,20 +1,20 @@
<button mat-icon-button
type="button"
class="mat-elevation-z1"
[matMenuTriggerFor]="colorMenu"
(menuOpened)="onMenuOpen()"
[ngClass]="'mat-'+selectedPalette+'-'+selectedHue+'-bg'">
[ngClass]="selectedPalette + '-' + selectedHue">
<mat-icon>palette</mat-icon>
</button>
<mat-menu #colorMenu="matMenu" class="fuse-material-color-picker-menu">
<mat-menu #colorMenu="matMenu" class="fuse-material-color-picker-menu mat-elevation-z8">
<header [ngClass]="selectedColor?.class || 'mat-accent-bg'" class="mat-elevation-z4"
<header [ngClass]="selectedColor?.class || 'accent'" class="mat-elevation-z4"
fxLayout="row" fxLayoutAlign="space-between center">
<button mat-icon-button
[style.visibility]="view==='hues'?'visible':'hidden'"
(click)="$event.stopPropagation();backToPaletteSelection()" aria-label="Palette">
class="secondary-text"
[style.visibility]="view === 'hues' ? 'visible' : 'hidden'"
(click)="goToPalettesView($event)" aria-label="Palette">
<mat-icon class="s-20">arrow_back</mat-icon>
</button>
@ -23,45 +23,40 @@
</span>
<span *ngIf="!selectedColor?.palette">
Select Color
Select a Color
</span>
<button mat-icon-button
class="remove-color-button"
(click)="$event.stopPropagation();removeColor()"
aria-label="Remove Color">
class="remove-color-button secondary-text"
(click)="removeColor($event)"
aria-label="Remove color"
matTooltip="Remove color">
<mat-icon class="s-20">delete</mat-icon>
</button>
</header>
<div [ngSwitch]="view" class="views">
<div class="view" *ngSwitchCase="'palettes'">
<div fxLayout="row wrap" fxLayoutAlign="start start" class="colors" fusePerfectScrollbar>
<div class="color"
[ngClass]="'mat-'+color.key+'-bg'"
<div class="color" fxLayout="row" fxLayoutAlign="center center"
*ngFor="let color of (colors | keys)"
(click)="$event.stopPropagation();selectPalette(color.key)"
fxLayout="row" fxLayoutAlign="start end" mat-ink-ripple>
<span class="label">
{{color.key}}
</span>
[ngClass]="color.key"
[class.selected]="selectedPalette === color.key"
(click)="selectPalette($event, color.key)">
</div>
</div>
</div>
<div class="view" *ngSwitchCase="'hues'" >
<div class="view" *ngSwitchCase="'hues'">
<div fxLayout="row wrap" fxLayoutAlign="start start" class="colors" fusePerfectScrollbar>
<div class="color" *ngFor="let hue of hues"
[fxHide]="selectedPalette === 'white' && hue !== '500'|| selectedPalette === 'black' && hue !== '500'"
[ngClass]="'mat-'+selectedPalette+'-'+hue+'-bg'"
(click)="selectHue(hue)" fxLayout="row" fxLayoutAlign="start end" mat-ink-ripple>
<span class="label">
{{hue}}
</span>
<mat-icon *ngIf="selectedHue === hue" class="s-16">check</mat-icon>
<div class="color" fxLayout="row" fxLayoutAlign="center center"
*ngFor="let hue of hues"
[fxHide]="selectedPalette === 'fuse-white' && hue !== '500' || selectedPalette === 'fuse-black' && hue !== '500'"
[ngClass]="selectedPalette + '-' + hue"
[class.selected]="selectedHue === hue"
(click)="selectHue($event, hue)">
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
.fuse-material-color-picker-menu {
width: 208px;
width: 245px;
.mat-menu-content {
padding: 0;
@ -7,44 +7,29 @@
.views {
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
min-height: 258px;
height: 308px;
background-color: #F7F7F7;
min-height: 165px;
.view {
position: absolute;
width: 208px;
height: 100%;
bottom: 0;
left: 0;
right: 0;
top: 0;
overflow: hidden;
.colors {
position: relative;
padding: 4px;
padding: 1px 0 0 0;
margin-left: -1px;
.color {
position: relative;
width: 46px;
height: 46px;
margin: 2px;
width: 40px;
height: 40px;
margin: 0 0 1px 1px;
border-radius: 0;
cursor: pointer;
transition: border-radius .4s cubic-bezier(.25, .8, .25, 1);
.label {
padding: 2px;
font-size: 10px;
&:hover {
border-radius: 20%;
}
mat-icon {
position: absolute;
top: 2px;
right: 2px;
font-size: 16px;
opacity: 0.7;
&.selected {
border-radius: 50% !important;
}
}
}

View File

@ -1,55 +1,40 @@
import { Component, EventEmitter, Input, OnChanges, Output, ViewEncapsulation } from '@angular/core';
import { Component, EventEmitter, forwardRef, Input, Output, ViewEncapsulation } from '@angular/core';
import { fuseAnimations } from '@fuse/animations';
import { MatColors } from '@fuse/mat-colors';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
export const FUSE_MATERIAL_COLOR_PICKER_VALUE_ACCESSOR: any = {
provide : NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => FuseMaterialColorPickerComponent),
multi : true
};
@Component({
selector : 'fuse-material-color-picker',
templateUrl : './material-color-picker.component.html',
styleUrls : ['./material-color-picker.component.scss'],
animations : fuseAnimations,
encapsulation: ViewEncapsulation.None
encapsulation: ViewEncapsulation.None,
providers : [FUSE_MATERIAL_COLOR_PICKER_VALUE_ACCESSOR]
})
export class FuseMaterialColorPickerComponent implements OnChanges
export class FuseMaterialColorPickerComponent implements ControlValueAccessor
{
colors: any;
hues: string[];
selectedColor: any;
view: string;
@Input()
selectedColor: any;
selectedPalette: string;
@Input()
selectedHue: string;
@Input()
selectedFg: string;
@Input()
value: any;
// Color changed
@Output()
onValueChange: EventEmitter<any>;
@Output()
selectedPaletteChange: EventEmitter<any>;
@Output()
selectedHueChange: EventEmitter<any>;
@Output()
selectedClassChange: EventEmitter<any>;
@Output()
selectedBgChange: EventEmitter<any>;
@Output()
selectedFgChange: EventEmitter<any>;
colorChanged: EventEmitter<any>;
// Private
_selectedClass: string;
_selectedBg: string;
private _color: string;
private _modelChange: (value: any) => void;
private _modelTouched: (value: any) => void;
/**
* Constructor
@ -57,23 +42,18 @@ export class FuseMaterialColorPickerComponent implements OnChanges
constructor()
{
// Set the defaults
this.colorChanged = new EventEmitter();
this.colors = MatColors.all;
this.hues = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', 'A100', 'A200', 'A400', 'A700'];
this.selectedFg = '';
this.selectedHue = '';
this.selectedPalette = '';
this.selectedHue = '500';
this.view = 'palettes';
this.onValueChange = new EventEmitter();
this.selectedPaletteChange = new EventEmitter();
this.selectedHueChange = new EventEmitter();
this.selectedClassChange = new EventEmitter();
this.selectedBgChange = new EventEmitter();
this.selectedFgChange = new EventEmitter();
// Set the private defaults
this._selectedClass = '';
this._selectedBg = '';
this._color = '';
this._modelChange = () => {
};
this._modelTouched = () => {
};
}
// -----------------------------------------------------------------------------------------------------
@ -86,88 +66,76 @@ export class FuseMaterialColorPickerComponent implements OnChanges
* @param value
*/
@Input()
set selectedClass(value)
set color(value)
{
if ( value && value !== '' && this._selectedClass !== value )
if ( !value || value === '' || this._color === value )
{
const color = value.split('-');
if ( color.length >= 5 )
{
this.selectedPalette = color[1] + '-' + color[2];
this.selectedHue = color[3];
}
else
{
this.selectedPalette = color[1];
this.selectedHue = color[2];
}
}
this._selectedClass = value;
}
get selectedClass(): string
{
return this._selectedClass;
}
/**
* Selected bg
*
* @param value
*/
@Input()
set selectedBg(value)
{
if ( value && value !== '' && this._selectedBg !== value )
{
for ( const palette in this.colors )
{
if ( !this.colors.hasOwnProperty(palette) )
{
continue;
}
for ( const hue of this.hues )
{
if ( this.colors[palette][hue] === value )
{
this.selectedPalette = palette;
this.selectedHue = hue;
break;
}
}
}
}
this._selectedBg = value;
}
get selectedBg(): string
{
return this._selectedBg;
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On changes
*
* @param changes
*/
ngOnChanges(changes: any): void
{
if ( changes.selectedBg && changes.selectedBg.currentValue === '' ||
changes.selectedClass && changes.selectedClass.currentValue === '' ||
changes.selectedPalette && changes.selectedPalette.currentValue === '' )
{
this.removeColor();
return;
}
if ( changes.selectedPalette || changes.selectedHue || changes.selectedClass || changes.selectedBg )
{
this.updateSelectedColor();
// Split the color value (red-400, blue-500, fuse-navy-700 etc.)
const colorParts = value.split('-');
// Take the very last part as the selected hue value
this.selectedHue = colorParts[colorParts.length - 1];
// Remove the last part
colorParts.pop();
// Rejoin the remaining parts as the selected palette name
this.selectedPalette = colorParts.join('-');
// Store the color value
this._color = value;
}
get color(): string
{
return this._color;
}
// -----------------------------------------------------------------------------------------------------
// @ Control Value Accessor implementation
// -----------------------------------------------------------------------------------------------------
/**
* Register on change function
*
* @param fn
*/
registerOnChange(fn: any): void
{
this._modelChange = fn;
}
/**
* Register on touched function
*
* @param fn
*/
registerOnTouched(fn: any): void
{
this._modelTouched = fn;
}
/**
* Write value to the view from model
*
* @param color
*/
writeValue(color: any): void
{
// Return if null
if ( !color )
{
return;
}
// Set the color
this.color = color;
// Update the selected color
this.updateSelectedColor();
}
// -----------------------------------------------------------------------------------------------------
@ -177,35 +145,61 @@ export class FuseMaterialColorPickerComponent implements OnChanges
/**
* Select palette
*
* @param event
* @param palette
*/
selectPalette(palette): void
selectPalette(event, palette): void
{
this.selectedPalette = palette;
this.updateSelectedColor();
// Stop propagation
event.stopPropagation();
// Go to 'hues' view
this.view = 'hues';
// Update the selected palette
this.selectedPalette = palette;
// Update the selected color
this.updateSelectedColor();
}
/**
* Select hue
*
* @param event
* @param hue
*/
selectHue(hue): void
selectHue(event, hue): void
{
// Stop propagation
event.stopPropagation();
// Update the selected huse
this.selectedHue = hue;
// Update the selected color
this.updateSelectedColor();
}
/**
* Remove color
*
* @param event
*/
removeColor(): void
removeColor(event): void
{
// Stop propagation
event.stopPropagation();
// Return to the 'palettes' view
this.view = 'palettes';
// Clear the selected palette and hue
this.selectedPalette = '';
this.selectedHue = '';
// Update the selected color
this.updateSelectedColor();
this.view = 'palettes';
}
/**
@ -213,49 +207,40 @@ export class FuseMaterialColorPickerComponent implements OnChanges
*/
updateSelectedColor(): void
{
setTimeout(() => {
if ( this.selectedColor && this.selectedPalette === this.selectedColor.palette && this.selectedHue === this.selectedColor.hue )
if ( this.selectedColor && this.selectedColor.palette === this.selectedPalette && this.selectedColor.hue === this.selectedHue )
{
return;
}
if ( this.selectedPalette !== '' && this.selectedHue !== '' )
{
this.selectedBg = MatColors.getColor(this.selectedPalette)[this.selectedHue];
this.selectedFg = MatColors.getColor(this.selectedPalette).contrast[this.selectedHue];
this.selectedClass = 'mat-' + this.selectedPalette + '-' + this.selectedHue + '-bg';
}
else
{
this.selectedBg = '';
this.selectedFg = '';
}
// Set the selected color object
this.selectedColor = {
palette: this.selectedPalette,
hue : this.selectedHue,
class : this.selectedClass,
bg : this.selectedBg,
fg : this.selectedFg
class : this.selectedPalette + '-' + this.selectedHue,
bg : this.selectedPalette === '' ? '' : MatColors.getColor(this.selectedPalette)[this.selectedHue],
fg : this.selectedPalette === '' ? '' : MatColors.getColor(this.selectedPalette).contrast[this.selectedHue]
};
this.selectedPaletteChange.emit(this.selectedPalette);
this.selectedHueChange.emit(this.selectedHue);
this.selectedClassChange.emit(this.selectedClass);
this.selectedBgChange.emit(this.selectedBg);
this.selectedFgChange.emit(this.selectedFg);
// Emit the color changed event
this.colorChanged.emit(this.selectedColor);
this.value = this.selectedColor;
this.onValueChange.emit(this.selectedColor);
});
// Mark the model as touched
this._modelTouched(this.selectedColor.class);
// Update the model
this._modelChange(this.selectedColor.class);
}
/**
* Go back to palette selection
* Go to palettes view
*
* @param event
*/
backToPaletteSelection(): void
goToPalettesView(event): void
{
// Stop propagation
event.stopPropagation();
this.view = 'palettes';
}

View File

@ -1,7 +1,7 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatButtonModule, MatIconModule, MatMenuModule, MatRippleModule } from '@angular/material';
import { MatButtonModule, MatIconModule, MatMenuModule, MatTooltipModule } from '@angular/material';
import { FusePipesModule } from '@fuse/pipes/pipes.module';
@ -19,7 +19,7 @@ import { FuseMaterialColorPickerComponent } from '@fuse/components/material-colo
MatButtonModule,
MatIconModule,
MatMenuModule,
MatRippleModule,
MatTooltipModule,
FusePipesModule
],

View File

@ -0,0 +1,14 @@
@mixin fuse-material-color-picker-theme($theme) {
$background: map_get($theme, background);
.fuse-material-color-picker-menu {
.mat-menu-content {
.views {
background: #303030;
}
}
}
}