Color Picker
color-pickeruiHex color input: a native color swatch, an optional hex text field and a preset swatch row. Controlled via value / valueChange and usable as a form control.
Installation
$pnpm dlx uipkge-ng@latest add color-picker$npx uipkge-ng@latest add color-picker$yarn dlx uipkge-ng@latest add color-picker$bunx uipkge-ng@latest add color-pickerManifest:
https://uipkge.dev/r/angular/color-picker.jsonInstalls to:src/app/components/ui/color-picker/Examples
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
valueCurrent hex value (React `value`). | string | null | undefined | — | optional |
valueChange | EventEmitter<string> | — | optional |
disabled | boolean | false | optional |
presetsOverride the swatches shown below the color input. Pass [] to hide entirely. | string[] | DEFAULT_PRESETS | optional |
hideHexInputHide the hex text field next to the color trigger. | boolean | false | optional |
class | string | — | optional |
npm dependencies
Files installed (2)
src/app/components/ui/color-picker/color-picker.component.ts5.2 kB
import { ChangeDetectorRef, Component, EventEmitter, Input, Output, booleanAttribute, forwardRef, inject, signal, ChangeDetectionStrategy, } from '@angular/core' import { NG_VALUE_ACCESSOR, type ControlValueAccessor } from '@angular/forms' import { cn } from '@/lib/utils' const DEFAULT_PRESETS = [ '#ef4444', '#f97316', '#eab308', '#22c55e', '#14b8a6', '#3b82f6', '#8b5cf6', '#ec4899', '#ffffff', '#d4d4d4', '#737373', '#171717', ] /** * Angular port of the React ColorPicker: a native colour input hidden over a swatch * (the swatch shows the current value), an optional hex text field, and a row of * preset swatches. Controlled like React: `value` / `valueChange` (`[(value)]`), plus * NG_VALUE_ACCESSOR for forms. */ @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-color-picker, [ui-color-picker]', standalone: true, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => UiColorPickerComponent), multi: true }], host: { 'data-uipkge': '', 'data-slot': 'color-picker', '[class]': 'hostClass', }, template: ` <div class="flex items-center gap-2"> <div class="border-input relative h-10 w-10 shrink-0 overflow-hidden rounded-md border shadow-xs" [style.background-color]="value || '#ffffff'" > <input type="color" [value]="safeColorValue" [disabled]="disabled" aria-label="Pick color" class="absolute inset-0 h-full w-full cursor-pointer opacity-0 disabled:cursor-not-allowed" (input)="emit($any($event.target).value)" /> </div> @if (!hideHexInput) { <input type="text" [value]="value || ''" placeholder="#000000" spellcheck="false" autocomplete="off" [disabled]="disabled" aria-label="Hex color" class="bg-background border-input text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 h-10 flex-1 rounded-md border px-3 text-sm uppercase shadow-xs outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50" (input)="emit($any($event.target).value)" (blur)="onTouched()" /> } </div> @if (swatches.length > 0) { <div class="flex flex-wrap gap-1.5"> @for (color of swatches; track color) { <button type="button" [disabled]="disabled" [attr.aria-label]="'Select ' + color" [style.background-color]="color" [class]="swatchClass(color)" (click)="emit(color)" ></button> } </div> } `, }) export class UiColorPickerComponent implements ControlValueAccessor { // Zoneless-safe: form writes happen outside template events, so schedule a repaint. private readonly cdr = inject(ChangeDetectorRef) private readonly _value = signal<string | undefined>(undefined) private formBound = false /** Current hex value (React `value`). */ @Input() set value(v: string | null | undefined) { this._value.set(v ?? undefined) } get value(): string | undefined { return this._value() } @Output() valueChange = new EventEmitter<string>() @Input({ transform: booleanAttribute }) disabled = false /** Override the swatches shown below the color input. Pass [] to hide entirely. */ @Input() presets: string[] = DEFAULT_PRESETS /** Hide the hex text field next to the color trigger. */ @Input({ transform: booleanAttribute }) hideHexInput = false @Input('class') className?: string onTouched: () => void = () => {} private onChange: (v: string) => void = () => {} get hostClass(): string { return cn('block space-y-3', this.className) } get swatches(): string[] { return this.presets ?? [] } /** Native <input type="color"> only accepts #rrggbb — keep a safe value while typing free-form hex. */ get safeColorValue(): string { return /^#[0-9a-fA-F]{6}$/.test(this.value || '') ? (this.value as string) : '#ffffff' } swatchClass(color: string): string { return cn( 'ring-offset-background focus-visible:ring-ring/40 size-6 shrink-0 rounded-md shadow-sm transition-transform outline-none hover:scale-110 focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:scale-100', this.value?.toLowerCase() === color.toLowerCase() ? 'ring-foreground ring-2 ring-offset-2' : 'ring-border/50 ring-1', color.toLowerCase() === '#ffffff' && 'ring-border', ) } emit(next: string): void { if (this.disabled) return // Controlled like React; a bound form control owns the value, so reflect it locally. if (this.formBound) this._value.set(next) this.valueChange.emit(next) this.onChange(next) } writeValue(v: string | null): void { this._value.set(v ?? undefined) this.cdr.markForCheck() } registerOnChange(fn: (v: string) => void): void { this.formBound = true this.onChange = fn } registerOnTouched(fn: () => void): void { this.onTouched = fn } setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled this.cdr.markForCheck() } }src/app/components/ui/color-picker/index.ts0.1 kB
export { UiColorPickerComponent } from './color-picker.component'
Raw manifest:https://uipkge.dev/r/angular/color-picker.json