From 269cb8a9350b36822da799e2d507dce13400417f Mon Sep 17 00:00:00 2001 From: Andrii Kostenko Date: Wed, 30 Jul 2025 10:07:08 +0000 Subject: [PATCH 1/2] color picker improvements --- frontend/package.json | 1 + .../db-table-widgets.component.ts | 15 ++- .../color/color.component.html | 5 +- .../color/color.component.ts | 99 ++++++++++++++++++- .../color/color.component.html | 6 +- .../color/color.component.ts | 61 +++++++++++- frontend/yarn.lock | 17 ++++ 7 files changed, 191 insertions(+), 13 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index e8ffc613d..ab68b5031 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -38,6 +38,7 @@ "amplitude-js": "^8.21.9", "angular-password-strength-meter": "^12.0.0", "angulartics2": "^14.1.0", + "color-string": "^2.0.1", "convert": "^5.12.0", "date-fns": "^4.1.0", "ipaddr.js": "^2.2.0", diff --git a/frontend/src/app/components/dashboard/db-table-widgets/db-table-widgets.component.ts b/frontend/src/app/components/dashboard/db-table-widgets/db-table-widgets.component.ts index 55af1b0b0..d3f03ff8f 100644 --- a/frontend/src/app/components/dashboard/db-table-widgets/db-table-widgets.component.ts +++ b/frontend/src/app/components/dashboard/db-table-widgets/db-table-widgets.component.ts @@ -193,8 +193,19 @@ export class DbTableWidgetsComponent implements OnInit { "allow_negative": true } `, - Color: `// No settings required -// You can use this field to display colors in hex format, like #FF5733 or #333.`, + Color: `// Optional: Specify output format for color values +// Supported formats: "hex", "hex_hash" (default), "rgb", "hsl" +// Example configuration: + +{ + "format": "hex_hash" // Will display colors as "#FF5733" +} + +// Format options: +// - "hex": Display as "FF5733" (no hash) +// - "hex_hash": Display as "#FF5733" (default) +// - "rgb": Display as "rgb(255, 87, 51)" +// - "hsl": Display as "hsl(9, 100%, 60%)"`, } constructor( diff --git a/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.html b/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.html index 99e173663..e97f8054e 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.html +++ b/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.html @@ -5,12 +5,11 @@ [required]="required" [disabled]="disabled" [readonly]="readonly" attr.data-testid="record-{{label}}-color" [(ngModel)]="value" (ngModelChange)="onTextInputChange()" - placeholder="#000000" - pattern="^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"> + placeholder="e.g. #000000, rgb(0,0,0), hsl(0,0%,0%)">
diff --git a/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.ts index 16d8c5b66..ce38d366e 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.ts @@ -4,6 +4,7 @@ import { BaseEditFieldComponent } from '../base-row-field/base-row-field.compone import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import colorString from 'color-string'; @Injectable() @@ -20,13 +21,105 @@ export class ColorEditComponent extends BaseEditFieldComponent { get isValidColor(): boolean { if (!this.value) return false; - const colorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; - return colorRegex.test(this.value); + return this.parseColor(this.value) !== null; + } + + get normalizedColorForPicker(): string { + const parsed = this.parseColor(this.value); + if (parsed) { + const [r, g, b] = parsed.value; + return `#${this.toHex(r)}${this.toHex(g)}${this.toHex(b)}`; + } + return '#000000'; + } + + get formattedColorValue(): string { + const parsed = this.parseColor(this.value); + if (!parsed) return this.value; + + const format = this.widgetStructure?.widget_params?.format || 'hex_hash'; + const [r, g, b, a] = parsed.value; + + switch (format) { + case 'hex': + return colorString.to.hex(r, g, b, a).slice(1); // Remove # prefix + case 'hex_hash': + return colorString.to.hex(r, g, b, a); + case 'rgb': + return colorString.to.rgb(r, g, b, a); + case 'hsl': + // Convert RGB to HSL using built-in conversion + const hex = colorString.to.hex(r, g, b, a); + const hslParsed = colorString.get.hsl(hex); + if (hslParsed) { + const [h, s, l, alpha] = hslParsed; + return colorString.to.hsl(h, s, l, alpha); + } + return hex; + default: + return colorString.to.hex(r, g, b, a); + } + } + + private parseColor(color: string): any { + if (!color) return null; + + // Try parsing with color-string + const parsed = colorString.get(color); + if (parsed) return parsed; + + // Try hex without hash + if (/^[A-Fa-f0-9]{6}$|^[A-Fa-f0-9]{3}$/.test(color)) { + return colorString.get('#' + color); + } + + return null; + } + + private toHex(n: number): string { + const hex = n.toString(16); + return hex.length === 1 ? '0' + hex : hex; } onColorPickerChange(event: Event) { const target = event.target as HTMLInputElement; - this.value = target.value; + const pickerValue = target.value; + + // Convert picker value to desired format + const parsed = this.parseColor(pickerValue); + if (parsed) { + const format = this.widgetStructure?.widget_params?.format || 'hex_hash'; + + const [r, g, b, a] = parsed.value; + + switch (format) { + case 'hex': + this.value = colorString.to.hex(r, g, b, a).slice(1); + break; + case 'hex_hash': + this.value = colorString.to.hex(r, g, b, a); + break; + case 'rgb': + this.value = colorString.to.rgb(r, g, b, a); + break; + case 'hsl': + // Convert RGB to HSL using built-in conversion + const hex = colorString.to.hex(r, g, b, a); + const hslParsed = colorString.get.hsl(hex); + if (hslParsed) { + const [h, s, l, alpha] = hslParsed; + this.value = colorString.to.hsl(h, s, l, alpha); + } else { + this.value = hex; + } + break; + default: + this.value = colorString.to.hex(r, g, b, a); + } + } else { + this.value = pickerValue; + } + this.onFieldChange.emit(this.value); } diff --git a/frontend/src/app/components/ui-components/table-display-fields/color/color.component.html b/frontend/src/app/components/ui-components/table-display-fields/color/color.component.html index 331411389..e9adf02b6 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/color/color.component.html +++ b/frontend/src/app/components/ui-components/table-display-fields/color/color.component.html @@ -2,15 +2,15 @@
- {{value || '—'}} + {{formattedColorValue || '—'}}
- {{formattedColorValue || '—'}} + {{value || '—'}}