Browse Source

Moved from library RGBA and HSLA inputs for color picker

pull/15031/head
Maksym Tsymbarov 6 months ago
parent
commit
2a13590f7e
  1. 26
      ui-ngx/patches/@iplab+ngx-color-picker+18.0.1.patch
  2. 28
      ui-ngx/src/app/shared/components/color-picker/color-input.base.scss
  3. 2
      ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss
  4. 6
      ui-ngx/src/app/shared/components/color-picker/color-picker.component.html
  5. 4
      ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss
  6. 6
      ui-ngx/src/app/shared/components/color-picker/hex-input.component.scss
  7. 54
      ui-ngx/src/app/shared/components/color-picker/hsla-input.component.html
  8. 71
      ui-ngx/src/app/shared/components/color-picker/hsla-input.component.ts
  9. 45
      ui-ngx/src/app/shared/components/color-picker/input-change.directive.ts
  10. 52
      ui-ngx/src/app/shared/components/color-picker/rgba-input.component.html
  11. 70
      ui-ngx/src/app/shared/components/color-picker/rgba-input.component.ts
  12. 8
      ui-ngx/src/app/shared/shared.module.ts

26
ui-ngx/patches/@iplab+ngx-color-picker+18.0.1.patch

@ -0,0 +1,26 @@
diff --git a/node_modules/@iplab/ngx-color-picker/esm2022/lib/helpers/color.class.mjs b/node_modules/@iplab/ngx-color-picker/esm2022/lib/helpers/color.class.mjs
index 6c7f8b1..f390b3b 100644
--- a/node_modules/@iplab/ngx-color-picker/esm2022/lib/helpers/color.class.mjs
+++ b/node_modules/@iplab/ngx-color-picker/esm2022/lib/helpers/color.class.mjs
@@ -173,7 +173,7 @@ export class Color {
const s = (color.saturation / 100) * (l <= 1 ? l : 2 - l);
const value = (l + s) / 2;
const saturation = (2 * s) / (l + s) || 0;
- return new Hsva(hue, saturation, value, color.alpha);
+ return new Hsva(hue, saturation * 100, value * 100, color.alpha);
}
rgbaToHsva(color) {
const red = color.red / 255;
diff --git a/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs b/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs
index a3b270c..9a9b592 100644
--- a/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs
+++ b/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs
@@ -505,7 +505,7 @@ class Color {
const s = (color.saturation / 100) * (l <= 1 ? l : 2 - l);
const value = (l + s) / 2;
const saturation = (2 * s) / (l + s) || 0;
- return new Hsva(hue, saturation, value, color.alpha);
+ return new Hsva(hue, saturation * 100, value * 100, color.alpha);
}
rgbaToHsva(color) {
const red = color.red / 255;

28
ui-ngx/src/app/shared/components/color-picker/color-input.base.scss

@ -0,0 +1,28 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
.color-input-container {
display: flex;
gap: 4px;
align-items: center;
}
.color-input {
max-width: 72px;
margin-bottom: 4px;
}
}

2
ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss

@ -16,7 +16,7 @@
@import "../scss/constants";
.tb-color-picker-panel {
width: 342px;
width: 370px;
display: flex;
flex-direction: column;
max-height: calc(100vh - 24px);

6
ui-ngx/src/app/shared/components/color-picker/color-picker.component.html

@ -37,10 +37,8 @@
<mat-option [value]="2">HSLA</mat-option>
</mat-select>
<div class="color-input" [ngSwitch]="presentations[presentationControl.value]">
<rgba-input-component *ngSwitchCase="'rgba'" label
[(color)]="control.value"></rgba-input-component>
<hsla-input-component *ngSwitchCase="'hsla'" label
[(color)]="control.value"></hsla-input-component>
<tb-rgba-input *ngSwitchCase="'rgba'" labelVisible [(color)]="control.value"></tb-rgba-input>
<tb-hsla-input *ngSwitchCase="'hsla'" labelVisible [(color)]="control.value"></tb-hsla-input>
<tb-hex-input *ngSwitchCase="'hex'" [(color)]="control.value"></tb-hex-input>
</div>
</div>

4
ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss

@ -83,7 +83,7 @@
height: 56px;
display: flex;
align-items: center;
gap: 20px;
gap: 8px;
.presentation-select {
font-size: 14px;
@ -104,7 +104,7 @@
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
justify-content: center;
gap: 8px;
@media #{$mat-xs} {
flex-direction: column;

6
ui-ngx/src/app/shared/components/color-picker/hex-input.component.scss

@ -19,11 +19,11 @@
gap: 8px;
}
.hex-input {
max-width: 190px;
max-width: 220px;
}
.alpha-input {
min-width: 60px;
max-width: 60px;
min-width: 72px;
max-width: 72px;
}
::ng-deep {

54
ui-ngx/src/app/shared/components/color-picker/hsla-input.component.html

@ -0,0 +1,54 @@
<!--
Copyright © 2016-2026 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="color-input-container">
<div class="tb-form-row tb-flex no-gap no-border no-padding column align-center">
<mat-form-field class="color-input number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" [min]="0" [max]="360" [value]="value.getHue()" (inputChange)="onInputChange($event, 'H')"/>
</mat-form-field>
@if (labelVisible) {
<span>H</span>
}
</div>
<div class="tb-form-row tb-flex no-gap no-border no-padding column align-center">
<mat-form-field class="color-input number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" [min]="0" [max]="100" [value]="value.getSaturation()" (inputChange)="onInputChange($event, 'S')" />
<span matSuffix>{{suffixValue}}</span>
</mat-form-field>
@if (labelVisible) {
<span>S</span>
}
</div>
<div class="tb-form-row tb-flex no-gap no-border no-padding column align-center">
<mat-form-field class="color-input number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" [min]="0" [max]="100" [value]="value.getLightness()" (inputChange)="onInputChange($event,'L')" />
<span matSuffix>{{suffixValue}}</span>
</mat-form-field>
@if (labelVisible) {
<span>L</span>
}
</div>
<div class="tb-form-row tb-flex no-gap no-border no-padding column align-center">
<mat-form-field class="color-input number" appearance="outline" subscriptSizing="dynamic">
<input matInput type="number" [min]="0" [max]="100" [value]="alphaValue" (inputChange)="onAlphaInputChange($event)" />
<span matSuffix>{{suffixValue}}</span>
</mat-form-field>
@if (labelVisible) {
<span>A</span>
}
</div>
</div>

71
ui-ngx/src/app/shared/components/color-picker/hsla-input.component.ts

@ -0,0 +1,71 @@
///
/// Copyright © 2016-2026 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Color } from '@iplab/ngx-color-picker';
import { coerceBoolean } from '@shared/decorators/coercion';
type Channel = 'H' | 'S' | 'L';
@Component({
selector: 'tb-hsla-input',
templateUrl: './hsla-input.component.html',
styleUrl: './color-input.base.scss'
})
export class HslaInputComponent {
@Input()
public color: Color;
@Output()
public colorChange = new EventEmitter<Color>(false);
@Input()
@coerceBoolean()
public labelVisible = false;
@Input()
public suffixValue = '%';
public get value() {
return this.color.getHsla();
}
public get alphaValue(): number {
return this.color ? Math.round(this.color.getHsla().getAlpha() * 100) : 0;
}
public onAlphaInputChange(inputValue: number): void {
if (!this.color) return;
const hsla = this.color.getHsla();
const alpha = +inputValue / 100;
if (hsla.alpha !== alpha) {
const newColor = new Color().setHsla(hsla.getHue(), hsla.getSaturation(), hsla.getLightness(), alpha);
this.colorChange.emit(newColor);
}
}
public onInputChange(newValue: number, channel: Channel): void {
if (!this.color) return;
const hsla = this.value;
const hue = channel === 'H' ? +newValue : hsla.getHue();
const saturation = channel === 'S' ? +newValue : hsla.getSaturation();
const lightness = channel === 'L' ? +newValue : hsla.getLightness();
if (hue === hsla.getHue() && saturation === hsla.getSaturation() && lightness === hsla.getLightness()) return;
const newColor = new Color().setHsla(hue, saturation, lightness, hsla.getAlpha());
this.colorChange.emit(newColor);
}
}

45
ui-ngx/src/app/shared/components/color-picker/input-change.directive.ts

@ -0,0 +1,45 @@
///
/// Copyright © 2016-2026 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Directive, EventEmitter, HostBinding, HostListener, Input, numberAttribute, Output } from '@angular/core';
@Directive({
selector: '[inputChange]'
})
export class InputChangeDirective {
@Input({transform: numberAttribute})
@HostBinding('attr.min')
min = 0;
@Input({transform: numberAttribute})
@HostBinding('attr.max')
max = 255;
@Output()
public inputChange = new EventEmitter<number>();
@HostListener('input', ['$event'])
public inputChanges(event: any): void {
const element = event.target as HTMLInputElement || event.srcElement as HTMLInputElement;
const value = element.value;
const numeric = parseFloat(value);
if (!isNaN(numeric) && numeric >= this.min && numeric <= this.max) {
this.inputChange.emit(numeric);
}
}
}

52
ui-ngx/src/app/shared/components/color-picker/rgba-input.component.html

@ -0,0 +1,52 @@
<!--
Copyright © 2016-2026 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="color-input-container">
<div class="tb-form-row tb-flex no-gap no-border no-padding column align-center">
<mat-form-field class="color-input number" subscriptSizing="dynamic" appearance="outline">
<input matInput type="number" [min]="0" [max]="255" [value]="value?.getRed()" (inputChange)="onInputChange($event, 'R')" />
</mat-form-field>
@if (labelVisible) {
<span>R</span>
}
</div>
<div class="tb-form-row tb-flex no-gap no-border no-padding column align-center">
<mat-form-field class="color-input number" subscriptSizing="dynamic" appearance="outline">
<input matInput type="number" [min]="0" [max]="255" [value]="value?.getGreen()" (inputChange)="onInputChange($event, 'G')" />
</mat-form-field>
@if (labelVisible) {
<span>G</span>
}
</div>
<div class="tb-form-row tb-flex no-gap no-border no-padding column align-center">
<mat-form-field class="color-input number" subscriptSizing="dynamic" appearance="outline">
<input matInput type="number" [min]="0" [max]="255" [value]="value?.getBlue()" (inputChange)="onInputChange($event,'B')" />
</mat-form-field>
@if (labelVisible) {
<span>B</span>
}
</div>
<div class="tb-form-row tb-flex no-gap no-border no-padding column align-center">
<mat-form-field class="color-input number" subscriptSizing="dynamic" appearance="outline">
<input matInput type="number" [min]="0" [max]="100" [value]="alphaValue" (inputChange)="onAlphaInputChange($event)" />
<span matSuffix>{{suffixValue}}</span>
</mat-form-field>
@if (labelVisible) {
<span>A</span>
}
</div>
</div>

70
ui-ngx/src/app/shared/components/color-picker/rgba-input.component.ts

@ -0,0 +1,70 @@
///
/// Copyright © 2016-2026 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Color } from '@iplab/ngx-color-picker';
import { coerceBoolean } from '@shared/decorators/coercion';
type Channel = 'R' | 'G' | 'B' | 'A';
@Component({
selector: 'tb-rgba-input',
templateUrl: './rgba-input.component.html',
styleUrl: './color-input.base.scss'
})
export class RgbaInputComponent {
@Input()
public color: Color;
@Output()
public colorChange = new EventEmitter<Color>(false);
@Input()
@coerceBoolean()
public labelVisible = false;
@Input()
public suffixValue = '%';
public get value() {
return this.color.getRgba();
}
public get alphaValue(): string {
return this.color ? Math.round(this.color.getRgba().getAlpha() * 100).toString() : '';
}
public onAlphaInputChange(inputValue: number): void {
if (!this.color) return;
const color = this.color.getRgba();
const alpha = +inputValue / 100;
if (color.getAlpha() !== alpha) {
const newColor = new Color().setRgba(color.getRed(), color.getGreen(), color.getBlue(), alpha).toRgbaString();
this.colorChange.emit(new Color(newColor));
}
}
onInputChange(newValue: number, channel: Channel) {
if (!this.color) return;
const rgba = this.value;
const red = channel === 'R' ? newValue : rgba.getRed();
const green = channel === 'G' ? newValue : rgba.getGreen();
const blue = channel === 'B' ? newValue : rgba.getBlue();
if (red === rgba.getRed() && green === rgba.getGreen() && blue === rgba.getBlue()) return;
this.colorChange.emit(new Color().setRgba(red, green, blue, rgba.alpha));
}
}

8
ui-ngx/src/app/shared/shared.module.ts

@ -239,6 +239,9 @@ import { DateExpirationPipe } from '@shared/pipe/date-expiration.pipe';
import { EntityLimitExceededDialogComponent } from '@shared/components/dialog/entity-limit-exceeded-dialog.component';
import { DynamicMatDialogModule } from '@shared/components/dialog/dynamic/dynamic-dialog.module';
import { MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS } from '@angular/material/button-toggle';
import { RgbaInputComponent } from '@shared/components/color-picker/rgba-input.component';
import { HslaInputComponent } from '@shared/components/color-picker/hsla-input.component';
import { InputChangeDirective } from '@shared/components/color-picker/input-change.directive';
export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) {
return markedOptionsService;
@ -466,7 +469,10 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService)
MqttVersionSelectComponent,
PasswordRequirementsTooltipComponent,
TimeUnitInputComponent,
StringPatternAutocompleteComponent
StringPatternAutocompleteComponent,
RgbaInputComponent,
HslaInputComponent,
InputChangeDirective
],
imports: [
CommonModule,

Loading…
Cancel
Save