UNPKG

@ngxmc/color-picker

Version:
1 lines 97.1 kB
{"version":3,"file":"ngxmc-color-picker.mjs","sources":["../../../../projects/color-picker/src/lib/helpers/color-helpers.ts","../../../../projects/color-picker/src/lib/models/color.model.ts","../../../../projects/color-picker/src/lib/components/color-canvas/base-color-canvas.ts","../../../../projects/color-picker/src/lib/components/color-canvas/color-slider/color-slider.component.ts","../../../../projects/color-picker/src/lib/components/color-canvas/color-slider/color-slider.component.html","../../../../projects/color-picker/src/lib/components/color-canvas/color-canvas.component.ts","../../../../projects/color-picker/src/lib/components/color-canvas/color-canvas.component.html","../../../../projects/color-picker/src/lib/components/color-collection/color-collection.component.ts","../../../../projects/color-picker/src/lib/components/color-collection/color-collection.component.html","../../../../projects/color-picker/src/lib/components/color-palette/color-palette.component.ts","../../../../projects/color-picker/src/lib/components/color-palette/color-palette.component.html","../../../../projects/color-picker/src/lib/services/color-adapter.ts","../../../../projects/color-picker/src/lib/services/color-formats.ts","../../../../projects/color-picker/src/lib/components/color-picker/color-picker.component.ts","../../../../projects/color-picker/src/lib/components/color-picker/color-picker-content.component.html","../../../../projects/color-picker/src/lib/components/color-picker/color-input.component.ts","../../../../projects/color-picker/src/lib/components/color-toggle/color-toggle.component.ts","../../../../projects/color-picker/src/lib/components/color-toggle/color-toggle.component.html","../../../../projects/color-picker/src/lib/directives/numeric-color-input.directive.ts","../../../../projects/color-picker/src/public-api.ts","../../../../projects/color-picker/src/ngxmc-color-picker.ts"],"sourcesContent":["const trimLeft = /^\\s+/;\nconst trimRight = /\\s+$/;\nconst tinyCounter = 0;\nconst mathRound = Math.round;\nconst mathMin = Math.min;\nconst mathMax = Math.max;\nconst mathRandom = Math.random;\n\nexport const NUMERIC_REGEX = /[^0-9]/g;\nexport const MAX_RGB = 255;\nexport const MIN_RGB = 0;\n\n/** List basic colors */\nexport const BASIC_COLORS = [\n '#ffffff',\n '#ffff00',\n '#ff00ff',\n '#ff0000',\n '#c0c0c0',\n '#808080',\n '#808000',\n '#800080',\n '#800000',\n '#00ffff',\n '#00ff00',\n '#008080',\n '#008000',\n '#0000ff',\n '#000080',\n '#000000',\n];\n\n/**\n * Get color at position\n * @param ctx\n * @param x\n * @param y\n */\nexport function getColorAtPosition(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n): { r: number; g: number; b: number } {\n const imageData: Uint8ClampedArray = ctx.getImageData(x, y, 1, 1).data;\n return { r: imageData[0], g: imageData[1], b: imageData[2] };\n}\n\n// `rgbaToHex`\n// Converts an RGBA color plus alpha transparency to hex\n// Assumes r, g, b are contained in the set [0, 255] and\n// a in [0, 1]. Returns a 4 or 8 character rgba hex\nexport function rgbaToHex(\n r: number,\n g: number,\n b: number,\n a: number,\n allow4Char?: boolean,\n): string {\n var hex = [\n pad2(mathRound(r).toString(16)),\n pad2(mathRound(g).toString(16)),\n pad2(mathRound(b).toString(16)),\n pad2(convertDecimalToHex(a)),\n ];\n\n // Return a 4 character hex if possible\n if (\n allow4Char &&\n hex[0].charAt(0) == hex[0].charAt(1) &&\n hex[1].charAt(0) == hex[1].charAt(1) &&\n hex[2].charAt(0) == hex[2].charAt(1) &&\n hex[3].charAt(0) == hex[3].charAt(1)\n ) {\n return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);\n }\n\n return hex.join('');\n}\n\n// Force a hex value to have 2 characters\nexport function pad2(c): string {\n return c.length == 1 ? '0' + c : '' + c;\n}\n\n// Converts a decimal to a hex value\nexport function convertDecimalToHex(d) {\n return Math.round(parseFloat(d) * 255).toString(16);\n}\n\n// Converts a hex value to a decimal\nfunction convertHexToDecimal(h) {\n return parseIntFromHex(h) / 255;\n}\n\n// Parse a base-16 hex value into a base-10 integer\nfunction parseIntFromHex(val) {\n return parseInt(val, 16);\n}\n\n// `rgbToHex`\n// Converts an RGB color to hex\n// Assumes r, g, and b are contained in the set [0, 255]\n// Returns a 3 or 6 character hex\nexport function rgbToHex(r: number, g: number, b: number, allow3Char?: boolean) {\n var hex = [\n pad2(mathRound(r).toString(16)),\n pad2(mathRound(g).toString(16)),\n pad2(mathRound(b).toString(16)),\n ];\n\n // Return a 3 character hex if possible\n if (\n allow3Char &&\n hex[0].charAt(0) == hex[0].charAt(1) &&\n hex[1].charAt(0) == hex[1].charAt(1) &&\n hex[2].charAt(0) == hex[2].charAt(1)\n ) {\n return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);\n }\n\n return hex.join('');\n}\n\n// Actual matching.\n// Parentheses and commas are optional, but not required.\n// Whitespace can take the place of commas or opening parent\nconst CSS_INTEGER = '[-\\\\+]?\\\\d+%?';\nconst CSS_NUMBER = '[-\\\\+]?\\\\d*\\\\.\\\\d+%?';\nconst CSS_UNIT = '(?:' + CSS_NUMBER + ')|(?:' + CSS_INTEGER + ')';\nconst PERMISSIVE_MATCH3 =\n '[\\\\s|\\\\(]+(' + CSS_UNIT + ')[,|\\\\s]+(' + CSS_UNIT + ')[,|\\\\s]+(' + CSS_UNIT + ')\\\\s*\\\\)?';\nconst PERMISSIVE_MATCH4 =\n '[\\\\s|\\\\(]+(' +\n CSS_UNIT +\n ')[,|\\\\s]+(' +\n CSS_UNIT +\n ')[,|\\\\s]+(' +\n CSS_UNIT +\n ')[,|\\\\s]+(' +\n CSS_UNIT +\n ')\\\\s*\\\\)?';\n\nexport const matchers = {\n CSS_UNIT: new RegExp(CSS_UNIT),\n rgb: new RegExp('rgb' + PERMISSIVE_MATCH3),\n rgba: new RegExp('rgba' + PERMISSIVE_MATCH4),\n hsl: new RegExp('hsl' + PERMISSIVE_MATCH3),\n hsla: new RegExp('hsla' + PERMISSIVE_MATCH4),\n hsv: new RegExp('hsv' + PERMISSIVE_MATCH3),\n hsva: new RegExp('hsva' + PERMISSIVE_MATCH4),\n hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,\n hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,\n hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,\n hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,\n};\n\n// `stringInputToObject`\n// Permissive string parsing. Take in a number of formats, and output an object\n// based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`\nexport function stringInputToObject(color: string): {\n r: number;\n g: number;\n b: number;\n a: number;\n} {\n color = color.replace(trimLeft, '').replace(trimRight, '').toLowerCase();\n\n // Try to match string input using regular expressions.\n // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]\n // Just return an object and let the conversion functions handle that.\n // This way the result will be the same whether the tinycolor is initialized with string or object.\n let match;\n let obj;\n if ((match = matchers.rgb.exec(color))) {\n return { r: match[1], g: match[2], b: match[3], a: 1 };\n }\n if ((match = matchers.rgba.exec(color))) {\n return { r: match[1], g: match[2], b: match[3], a: match[4] };\n }\n\n if ((match = matchers.hex8.exec(color))) {\n return {\n r: parseIntFromHex(match[1]),\n g: parseIntFromHex(match[2]),\n b: parseIntFromHex(match[3]),\n a: convertHexToDecimal(match[4]),\n };\n }\n if ((match = matchers.hex6.exec(color))) {\n return {\n r: parseIntFromHex(match[1]),\n g: parseIntFromHex(match[2]),\n b: parseIntFromHex(match[3]),\n a: 1,\n };\n }\n if ((match = matchers.hex4.exec(color))) {\n return {\n r: parseIntFromHex(match[1] + '' + match[1]),\n g: parseIntFromHex(match[2] + '' + match[2]),\n b: parseIntFromHex(match[3] + '' + match[3]),\n a: convertHexToDecimal(match[4] + '' + match[4]),\n };\n }\n if ((match = matchers.hex3.exec(color))) {\n return {\n r: parseIntFromHex(match[1] + '' + match[1]),\n g: parseIntFromHex(match[2] + '' + match[2]),\n b: parseIntFromHex(match[3] + '' + match[3]),\n a: 1,\n };\n }\n\n return null;\n}\n\nexport function createMissingDateImplError(provider: string) {\n return Error(\n `NgxMatColorPicker: No provider found for ${provider}. You must define MAT_COLOR_FORMATS in your module`,\n );\n}\n","import { MAX_RGB, rgbaToHex, rgbToHex } from '../helpers';\nimport { ColorInputFormat } from './color-input-format';\n\nexport class Color {\n public r: number;\n public g: number;\n public b: number;\n public a: number;\n public roundA: number;\n\n public hex: string;\n public rgba: string;\n\n constructor(_r: number, _g: number, _b: number, _a?: number) {\n this.r = _r > MAX_RGB ? MAX_RGB : _r;\n this.g = _g > MAX_RGB ? MAX_RGB : _g;\n this.b = _b > MAX_RGB ? MAX_RGB : _b;\n if (_a != null) {\n this.a = _a > 1 ? 1 : _a;\n } else {\n this.a = 1;\n }\n this.roundA = Math.round(this.a);\n this.hex = rgbToHex(this.r, this.g, this.b);\n this.rgba = this.toRgba();\n }\n\n public toHex(allow3Char?: boolean): string {\n return rgbToHex(this.r, this.g, this.b, allow3Char);\n }\n\n public toRgba(): string {\n return `rgba(${this.r},${this.g},${this.b},${this.a})`;\n }\n\n public toHexString(allow3Char?: boolean): string {\n return '#' + this.toHex(allow3Char);\n }\n\n public toRgbString(): string {\n return this.a === 1\n ? 'rgb(' + Math.round(this.r) + ', ' + Math.round(this.g) + ', ' + Math.round(this.b) + ')'\n : 'rgba(' +\n Math.round(this.r) +\n ', ' +\n Math.round(this.g) +\n ', ' +\n Math.round(this.b) +\n ', ' +\n this.roundA +\n ')';\n }\n\n public toHex8(allow4Char): string {\n return rgbaToHex(this.r, this.g, this.b, this.a, allow4Char);\n }\n\n public toHex8String(allow4Char?: boolean): string {\n return '#' + this.toHex8(allow4Char);\n }\n\n public toString(format: ColorInputFormat): string {\n let formatSet = !!format;\n\n let formattedString;\n let hasAlpha = this.a < 1 && this.a >= 0;\n let needsAlphaFormat =\n !formatSet &&\n hasAlpha &&\n (format === 'hex' ||\n format === 'hex6' ||\n format === 'hex3' ||\n format === 'hex4' ||\n format === 'hex8');\n\n if (needsAlphaFormat) {\n return this.toRgbString();\n }\n if (format === 'rgb') {\n formattedString = this.toRgbString();\n }\n if (format === 'hex' || format === 'hex6') {\n formattedString = this.toHexString();\n }\n if (format === 'hex3') {\n formattedString = this.toHexString(true);\n }\n if (format === 'hex4') {\n formattedString = this.toHex8String(true);\n }\n if (format === 'hex8') {\n formattedString = this.toHex8String();\n }\n\n return formattedString || this.toHexString();\n }\n}\n","import {\n AfterViewInit,\n Directive,\n Input,\n input,\n NgZone,\n OnDestroy,\n output,\n signal,\n} from '@angular/core';\nimport { ThemePalette } from '@angular/material/core';\nimport { Subject } from 'rxjs';\nimport { Color } from '../../models';\n\n@Directive()\nexport abstract class NgxMatBaseColorCanvas implements OnDestroy, AfterViewInit {\n readonly colorChanged = output<Color>();\n readonly theme = input<ThemePalette>();\n readonly color = signal<Color>(null);\n\n @Input({\n alias: 'color',\n })\n set setColor(color: Color) {\n this.color.set(color);\n }\n\n canvas: HTMLCanvasElement;\n\n elementId: string;\n\n ctx: CanvasRenderingContext2D;\n width: number;\n height: number;\n\n x: number = 0;\n y: number = 0;\n\n drag = false;\n\n protected _destroyed: Subject<void> = new Subject<void>();\n\n constructor(\n protected zone: NgZone,\n elementId: string,\n ) {\n this.elementId = elementId;\n }\n\n ngOnDestroy(): void {\n this._destroyed.next();\n this._destroyed.complete();\n }\n\n ngAfterViewInit(): void {\n this.canvas = <HTMLCanvasElement>document.getElementById(this.elementId);\n this.ctx = this.canvas.getContext('2d');\n this.width = this.canvas.width;\n this.height = this.canvas.height;\n this.draw();\n }\n\n protected draw() {\n this.ctx.clearRect(0, 0, this.width, this.height);\n this.ctx.rect(0, 0, this.width, this.height);\n this.fillGradient();\n if (this.y != 0) {\n this.redrawIndicator(this.x, this.y);\n }\n }\n\n public onMousedown(e: MouseEvent) {\n this.drag = true;\n this.changeColor(e);\n\n this.zone.runOutsideAngular(() => {\n this.canvas.addEventListener('mousemove', this.onMousemove.bind(this));\n });\n }\n\n public onMousemove(e: MouseEvent) {\n if (this.drag) {\n this.zone.run(() => {\n this.changeColor(e);\n });\n }\n }\n\n public onMouseup(e: MouseEvent) {\n this.drag = false;\n this.canvas.removeEventListener('mousemove', this.onMousemove);\n }\n\n public emitChange(color: Color) {\n this.colorChanged.emit(color);\n }\n\n abstract changeColor(e: MouseEvent): void;\n abstract fillGradient(): void;\n abstract redrawIndicator(x: number, y: number): void;\n}\n","import { Component, NgZone } from '@angular/core';\nimport { getColorAtPosition } from '../../../helpers';\nimport { Color } from '../../../models';\nimport { NgxMatBaseColorCanvas } from '../base-color-canvas';\n\n@Component({\n selector: 'ngx-mat-color-slider',\n templateUrl: './color-slider.component.html',\n styleUrls: ['./color-slider.component.scss'],\n})\nexport class NgxMatColorSliderComponent extends NgxMatBaseColorCanvas {\n constructor(protected zone: NgZone) {\n super(zone, 'color-strip');\n }\n\n ngAfterViewInit(): void {\n super.ngAfterViewInit();\n }\n\n public fillGradient() {\n const grd = this.ctx.createLinearGradient(0, 0, 0, this.height);\n grd.addColorStop(0, 'rgba(255, 0, 0, 1)');\n grd.addColorStop(0.17, 'rgba(255, 255, 0, 1)');\n grd.addColorStop(0.34, 'rgba(0, 255, 0, 1)');\n grd.addColorStop(0.51, 'rgba(0, 255, 255, 1)');\n grd.addColorStop(0.68, 'rgba(0, 0, 255, 1)');\n grd.addColorStop(0.85, 'rgba(255, 0, 255, 1)');\n grd.addColorStop(1, 'rgba(255, 0, 0, 1)');\n\n this.ctx.fillStyle = grd;\n this.ctx.fill();\n }\n\n public redrawIndicator(x: number, y: number) {\n this.ctx.beginPath();\n this.ctx.strokeStyle = 'white';\n this.ctx.lineWidth = 2;\n this.ctx.arc(7.5, y, 7.5, 0, 2 * Math.PI, false);\n this.ctx.stroke();\n this.ctx.closePath();\n }\n\n public changeColor(e: MouseEvent) {\n this.x = e.offsetX;\n this.y = e.offsetY;\n this.draw();\n const { r, g, b } = getColorAtPosition(this.ctx, e.offsetX, e.offsetY);\n this.emitChange(new Color(r, g, b));\n }\n}\n","<canvas\n id=\"color-strip\"\n class=\"zone-strip\"\n (mousedown)=\"onMousedown($event)\"\n (mouseup)=\"onMouseup($event)\"\n width=\"15\"\n height=\"234\"\n></canvas>\n","import {\n AfterViewInit,\n Component,\n NgZone,\n OnChanges,\n OnDestroy,\n OnInit,\n SimpleChanges,\n ViewEncapsulation,\n} from '@angular/core';\nimport {\n AbstractControl,\n FormControl,\n FormGroup,\n ReactiveFormsModule,\n Validators,\n} from '@angular/forms';\nimport { MatFormFieldModule } from '@angular/material/form-field';\nimport { MatInputModule } from '@angular/material/input';\nimport { merge } from 'rxjs';\nimport { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';\nimport { getColorAtPosition, matchers, stringInputToObject } from '../../helpers';\nimport { Color } from '../../models';\nimport { NgxMatBaseColorCanvas } from './base-color-canvas';\nimport { NgxMatColorSliderComponent } from './color-slider/color-slider.component';\n\nconst RADIUS_NOB = 5;\n\n@Component({\n selector: 'ngx-mat-color-canvas',\n templateUrl: './color-canvas.component.html',\n styleUrls: ['./color-canvas.component.scss'],\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'ngx-mat-color-canvas',\n },\n imports: [MatFormFieldModule, MatInputModule, NgxMatColorSliderComponent, ReactiveFormsModule],\n})\nexport class NgxMatColorCanvasComponent\n extends NgxMatBaseColorCanvas\n implements OnInit, AfterViewInit, OnChanges, OnDestroy\n{\n private _baseColor: Color;\n\n get rCtrl(): AbstractControl {\n return this.formGroup.get('r');\n }\n\n get gCtrl(): AbstractControl {\n return this.formGroup.get('g');\n }\n\n get bCtrl(): AbstractControl {\n return this.formGroup.get('b');\n }\n\n get aCtrl(): AbstractControl {\n return this.formGroup.get('a');\n }\n\n get hexCtrl(): AbstractControl {\n return this.formGroup.get('hex');\n }\n\n _resetBaseColor = true;\n\n formGroup: FormGroup;\n\n rgba: string;\n\n constructor(protected zone: NgZone) {\n super(zone, 'color-block');\n this.formGroup = new FormGroup({\n r: new FormControl(null, [Validators.required]),\n g: new FormControl(null, [Validators.required]),\n b: new FormControl(null, [Validators.required]),\n a: new FormControl(null, [Validators.required]),\n hex: new FormControl(null, [Validators.required, Validators.pattern(matchers.hex6)]),\n });\n }\n\n ngOnInit() {\n const rgbaCtrl$ = merge(\n this.rCtrl.valueChanges,\n this.gCtrl.valueChanges,\n this.bCtrl.valueChanges,\n this.aCtrl.valueChanges,\n );\n rgbaCtrl$.pipe(takeUntil(this._destroyed), debounceTime(400)).subscribe((_) => {\n const color = new Color(\n Number(this.rCtrl.value),\n Number(this.gCtrl.value),\n Number(this.bCtrl.value),\n Number(this.aCtrl.value),\n );\n this.emitChange(color);\n });\n\n const hexCtrl$ = this.hexCtrl.valueChanges;\n hexCtrl$\n .pipe(takeUntil(this._destroyed), debounceTime(400), distinctUntilChanged())\n .subscribe((hex) => {\n const obj = stringInputToObject(hex);\n if (obj != null) {\n const color = new Color(obj.r, obj.g, obj.b, obj.a);\n this.emitChange(color);\n }\n });\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes.color && changes.color.currentValue) {\n this.updateForm(changes.color.currentValue);\n if (this._resetBaseColor) {\n this._baseColor = changes.color.currentValue;\n }\n\n this._resetBaseColor = true;\n\n if (!changes.color.firstChange) {\n this.draw();\n }\n }\n }\n\n private updateForm(val: Color): void {\n const config = { emitEvent: false };\n this.rCtrl.setValue(val.r, config);\n this.gCtrl.setValue(val.g, config);\n this.bCtrl.setValue(val.b, config);\n this.aCtrl.setValue(val.a, config);\n this.hexCtrl.setValue(val.hex, config);\n }\n\n public redrawIndicator(x: number, y: number) {\n this.ctx.beginPath();\n this.ctx.strokeStyle = 'white';\n this.ctx.arc(x, y, RADIUS_NOB, 0, 2 * Math.PI, false);\n this.ctx.stroke();\n this.ctx.closePath();\n }\n\n public fillGradient() {\n this.ctx.fillStyle = this._baseColor ? this._baseColor.rgba : 'rgba(255,255,255,1)';\n this.ctx.fillRect(0, 0, this.width, this.height);\n\n const grdWhite = this.ctx.createLinearGradient(0, 0, this.width, 0);\n grdWhite.addColorStop(0, 'rgba(255,255,255,1)');\n grdWhite.addColorStop(1, 'rgba(255,255,255,0)');\n this.ctx.fillStyle = grdWhite;\n this.ctx.fillRect(0, 0, this.width, this.height);\n\n const grdBlack = this.ctx.createLinearGradient(0, 0, 0, this.height);\n grdBlack.addColorStop(0, 'rgba(0,0,0,0)');\n grdBlack.addColorStop(1, 'rgba(0,0,0,1)');\n this.ctx.fillStyle = grdBlack;\n this.ctx.fillRect(0, 0, this.width, this.height);\n }\n\n public onSliderColorChanged(c: Color) {\n this._baseColor = c;\n this.color.set(c);\n this.fillGradient();\n this.emitChange(c);\n }\n\n public changeColor(e: MouseEvent): void {\n this.x = e.offsetX;\n this.y = e.offsetY;\n this._resetBaseColor = false;\n this.draw();\n const { r, g, b } = getColorAtPosition(this.ctx, e.offsetX, e.offsetY);\n this.emitChange(new Color(r, g, b));\n }\n}\n","<form [formGroup]=\"formGroup\">\n <div class=\"color-canvas-row\">\n <div class=\"zone-canvas\">\n <canvas\n id=\"color-block\"\n class=\"zone-block\"\n (mousedown)=\"onMousedown($event)\"\n (mouseup)=\"onMouseup($event)\"\n width=\"200\"\n height=\"235\"\n ></canvas>\n <ngx-mat-color-slider (colorChanged)=\"onSliderColorChanged($event)\" />\n </div>\n\n <div class=\"zone-inputs\">\n <mat-form-field [color]=\"theme\">\n <mat-label>R</mat-label>\n <input\n matInput\n formControlName=\"r\"\n ngxMatNumericColorInput\n autocomplete=\"off\"\n />\n </mat-form-field>\n\n <mat-form-field [color]=\"theme\">\n <mat-label>G</mat-label>\n <input\n matInput\n formControlName=\"g\"\n ngxMatNumericColorInput\n autocomplete=\"off\"\n />\n </mat-form-field>\n\n <mat-form-field [color]=\"theme\">\n <mat-label>B</mat-label>\n <input\n matInput\n formControlName=\"b\"\n ngxMatNumericColorInput\n autocomplete=\"off\"\n />\n </mat-form-field>\n </div>\n </div>\n\n <div class=\"color-canvas-row\">\n <button\n mat-mini-fab\n [style.background-color]=\"color()?.rgba || 'transparent'\"\n class=\"preview\"\n ></button>\n <mat-form-field [color]=\"theme\">\n <mat-label>HEX6</mat-label>\n <mat-label matPrefix class=\"symbol\">#&nbsp;</mat-label>\n <input matInput formControlName=\"hex\" autocomplete=\"off\" />\n </mat-form-field>\n <mat-form-field class=\"input-opacity\" [color]=\"theme\">\n <mat-label>A</mat-label>\n <input\n matInput\n formControlName=\"a\"\n type=\"number\"\n min=\"0\"\n max=\"1\"\n step=\"0.1\"\n autocomplete=\"off\"\n />\n </mat-form-field>\n </div>\n</form>\n","import { NgClass } from '@angular/common';\nimport { Component, Input, output, signal, ViewEncapsulation } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { BASIC_COLORS, stringInputToObject } from '../../helpers';\nimport { Color } from '../../models';\n\n@Component({\n selector: 'ngx-mat-color-collection',\n templateUrl: './color-collection.component.html',\n styleUrls: ['./color-collection.component.scss'],\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'ngx-mat-color-collection',\n },\n imports: [MatButtonModule, NgClass],\n})\nexport class NgxMatColorCollectionComponent {\n readonly colorChanged = output<Color>();\n\n @Input()\n set color(c: Color) {\n if (c) {\n this.selectedColor.set(c.toHexString());\n }\n }\n\n readonly selectedColor = signal<string>('');\n\n colors1: string[] = BASIC_COLORS.slice(0, 8);\n colors2: string[] = BASIC_COLORS.slice(8, 16);\n\n select(hex: string) {\n this.selectedColor.set(hex);\n const { r, g, b, a } = stringInputToObject(hex);\n this.colorChanged.emit(new Color(r, g, b, a));\n }\n}\n","<div class=\"color-collection-row\">\n @for (c of colors1; track c) {\n <button\n mat-mini-fab\n [style.background-color]=\"c\"\n class=\"btn-color\"\n (click)=\"select(c)\"\n [ngClass]=\"{ active: selectedColor() === c }\"\n [disableRipple]=\"true\"></button>\n }\n</div>\n<div class=\"color-collection-row\">\n @for (c of colors2; track c) {\n <button\n mat-mini-fab\n [style.background-color]=\"c\"\n class=\"btn-color\"\n (click)=\"select(c)\"\n [ngClass]=\"{ active: selectedColor() === c }\"\n [disableRipple]=\"true\"></button>\n }\n</div>\n","import { Component, ViewEncapsulation, input, output } from '@angular/core';\nimport { ThemePalette } from '@angular/material/core';\nimport { Color } from '../../models';\nimport { NgxMatColorCanvasComponent } from '../color-canvas/color-canvas.component';\nimport { NgxMatColorCollectionComponent } from '../color-collection/color-collection.component';\n\n@Component({\n selector: 'ngx-mat-color-palette',\n templateUrl: 'color-palette.component.html',\n styleUrls: ['color-palette.component.scss'],\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'ngx-mat-color-palette',\n },\n imports: [NgxMatColorCollectionComponent, NgxMatColorCanvasComponent],\n})\nexport class NgxMatColorPaletteComponent {\n readonly colorChanged = output<Color>();\n\n readonly color = input<Color>();\n readonly theme = input<ThemePalette>();\n\n public handleColorChanged(color: Color) {\n this.colorChanged.emit(color);\n }\n}\n","<ngx-mat-color-canvas\n (colorChanged)=\"handleColorChanged($event)\"\n [color]=\"color()\"\n [theme]=\"theme()\"\n/>\n\n<ngx-mat-color-collection\n (colorChanged)=\"handleColorChanged($event)\"\n [color]=\"color()\"\n/>\n","import { Injectable } from '@angular/core';\nimport { stringInputToObject } from '../helpers';\nimport { Color, ColorInputFormat } from '../models';\n\n@Injectable()\nexport class ColorAdapter {\n sameColor(a: Color, b: Color) {\n if (a == null && b == null) return true;\n if (a != null && b != null) return a.rgba === b.rgba;\n return false;\n }\n\n format(c: Color, format: ColorInputFormat): string {\n return c.toString(format);\n }\n\n parse(value: string): Color | null {\n const obj = stringInputToObject(value);\n if (obj) {\n return new Color(obj.r, obj.g, obj.b, obj.a);\n }\n return null;\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport { ColorInputFormat } from '../models';\n\nexport type MatColorFormats = {\n display: {\n colorInput: ColorInputFormat;\n };\n};\n\nexport const NGX_MAT_COLOR_FORMATS: MatColorFormats = {\n display: {\n colorInput: 'hex',\n },\n};\n\nexport const MAT_COLOR_FORMATS = new InjectionToken<MatColorFormats>('mat-color-formats');\n","import { Directionality } from '@angular/cdk/bidi';\nimport { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { ESCAPE, UP_ARROW } from '@angular/cdk/keycodes';\nimport {\n Overlay,\n OverlayConfig,\n OverlayRef,\n PositionStrategy,\n ScrollStrategy,\n} from '@angular/cdk/overlay';\nimport { ComponentPortal } from '@angular/cdk/portal';\nimport { DOCUMENT } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n ComponentRef,\n EventEmitter,\n HostBinding,\n Inject,\n InjectionToken,\n Input,\n NgZone,\n OnDestroy,\n Optional,\n Output,\n ViewContainerRef,\n ViewEncapsulation,\n viewChild,\n} from '@angular/core';\nimport { ThemePalette } from '@angular/material/core';\nimport { matDatepickerAnimations } from '@angular/material/datepicker';\nimport { MatDialog, MatDialogRef } from '@angular/material/dialog';\nimport { Subject, Subscription, merge } from 'rxjs';\nimport { filter, take } from 'rxjs/operators';\nimport { Color } from '../../models';\nimport { ColorAdapter } from '../../services';\nimport { NgxMatColorPaletteComponent } from '../color-palette/color-palette.component';\nimport { NgxMatColorPickerInput } from './color-input.component';\n\n/** Injection token that determines the scroll handling while the calendar is open. */\nexport const NGX_MAT_COLOR_PICKER_SCROLL_STRATEGY = new InjectionToken<() => ScrollStrategy>(\n 'ngx-mat-colorpicker-scroll-strategy',\n);\n\nexport function NGX_MAT_COLOR_PICKER_SCROLL_STRATEGY_FACTORY(\n overlay: Overlay,\n): () => ScrollStrategy {\n return () => overlay.scrollStrategies.reposition();\n}\n\nexport const NGX_MAT_COLOR_PICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {\n provide: NGX_MAT_COLOR_PICKER_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: NGX_MAT_COLOR_PICKER_SCROLL_STRATEGY_FACTORY,\n};\n\n@Component({\n selector: 'ngx-mat-color-picker-content',\n templateUrl: './color-picker-content.component.html',\n styleUrls: ['color-picker-content.component.scss'],\n host: {\n class: 'ngx-mat-colorpicker-content',\n '[@transformPanel]': '\"enter\"',\n '[class.ngx-mat-colorpicker-content-touch]': 'picker.touchUi',\n },\n animations: [matDatepickerAnimations.transformPanel, matDatepickerAnimations.fadeInCalendar],\n exportAs: 'ngxMatColorPickerContent',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n inputs: ['color'],\n imports: [NgxMatColorPaletteComponent],\n})\nexport class NgxMatColorPickerContentComponent {\n /** Reference to the internal calendar component. */\n _palette = viewChild(NgxMatColorPaletteComponent);\n\n picker: NgxMatColorPickerComponent;\n _isAbove: boolean;\n color: ThemePalette;\n\n constructor() {}\n}\n\n@Component({\n selector: 'ngx-mat-color-picker',\n template: '',\n exportAs: 'ngxMatColorPicker',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n providers: [ColorAdapter, NGX_MAT_COLOR_PICKER_SCROLL_STRATEGY_FACTORY_PROVIDER],\n})\nexport class NgxMatColorPickerComponent implements OnDestroy {\n private _scrollStrategy: () => ScrollStrategy;\n\n /** Emits when the datepicker has been opened. */\n @Output('opened') openedStream: EventEmitter<void> = new EventEmitter<void>();\n\n /** Emits when the datepicker has been closed. */\n @Output('closed') closedStream: EventEmitter<void> = new EventEmitter<void>();\n\n @Input() get disabled() {\n return this._disabled === undefined && this._pickerInput\n ? this._pickerInput.disabled\n : !!this._disabled;\n }\n set disabled(value: boolean) {\n const newValue = coerceBooleanProperty(value);\n\n if (newValue !== this._disabled) {\n this._disabled = newValue;\n this._disabledChange.next(newValue);\n }\n }\n private _disabled: boolean;\n\n @Input()\n get touchUi(): boolean {\n return this._touchUi;\n }\n set touchUi(value: boolean) {\n this._touchUi = coerceBooleanProperty(value);\n }\n private _touchUi = false;\n\n /** Whether the calendar is open. */\n @Input()\n get opened(): boolean {\n return this._opened;\n }\n set opened(value: boolean) {\n value ? this.open() : this.close();\n }\n private _opened = false;\n\n /** Default Color palette to use on the datepicker's calendar. */\n @Input()\n get defaultColor(): ThemePalette {\n return this._defaultColor;\n }\n set defaultColor(value: ThemePalette) {\n this._defaultColor = value;\n }\n _defaultColor: ThemePalette = 'primary';\n\n /** Color palette to use on the datepicker's calendar. */\n @Input()\n get color(): ThemePalette {\n return this._color || (this._pickerInput ? this._pickerInput.getThemePalette() : undefined);\n }\n set color(value: ThemePalette) {\n this._color = value;\n }\n _color: ThemePalette;\n\n @HostBinding('class.mat-primary') get isPrimary() {\n return this.color === 'primary';\n }\n @HostBinding('class.mat-accent') get isAccent() {\n return this.color === 'accent';\n }\n @HostBinding('class.mat-warn') get isWarn() {\n return this.color === 'warn';\n }\n\n /** The currently selected date. */\n get _selected(): Color {\n return this._validSelected;\n }\n set _selected(value: Color) {\n this._validSelected = value;\n }\n private _validSelected: Color = null;\n\n _pickerInput: NgxMatColorPickerInput;\n /** A reference to the overlay when the picker is opened as a popup. */\n _popupRef: OverlayRef;\n\n /** A reference to the dialog when the picker is opened as a dialog. */\n private _dialogRef: MatDialogRef<NgxMatColorPickerContentComponent> | null;\n /** Reference to the component instantiated in popup mode. */\n private _popupComponentRef: ComponentRef<NgxMatColorPickerContentComponent> | null;\n /** A portal containing the content for this picker. */\n private _portal: ComponentPortal<NgxMatColorPickerContentComponent>;\n\n /** Emits when the datepicker is disabled. */\n readonly _disabledChange = new EventEmitter<boolean>();\n\n /** The element that was focused before the datepicker was opened. */\n private _focusedElementBeforeOpen: HTMLElement | null = null;\n\n /** Subscription to value changes in the associated input element. */\n private _inputSubscription = Subscription.EMPTY;\n\n /** Emits new selected date when selected date changes. */\n readonly _selectedChanged = new Subject<Color>();\n\n constructor(\n private _dialog: MatDialog,\n private _overlay: Overlay,\n private _zone: NgZone,\n private _adapter: ColorAdapter,\n @Optional() private _dir: Directionality,\n @Inject(NGX_MAT_COLOR_PICKER_SCROLL_STRATEGY) scrollStrategy: any,\n @Optional() @Inject(DOCUMENT) private _document: any,\n private _viewContainerRef: ViewContainerRef,\n ) {\n this._scrollStrategy = scrollStrategy;\n }\n\n ngOnDestroy() {\n this.close();\n this._inputSubscription.unsubscribe();\n this._disabledChange.complete();\n\n if (this._popupRef) {\n this._popupRef.dispose();\n this._popupComponentRef = null;\n }\n }\n\n /** Selects the given date */\n select(nextVal: Color): void {\n let oldValue = this._selected;\n this._selected = nextVal;\n if (!this._adapter.sameColor(oldValue, this._selected)) {\n this._selectedChanged.next(nextVal);\n }\n }\n\n /**\n * Register an input with this datepicker.\n * @param input The datepicker input to register with this datepicker.\n */\n registerInput(input: NgxMatColorPickerInput): void {\n if (this._pickerInput) {\n throw Error('A ColorPicker can only be associated with a single input.');\n }\n this._pickerInput = input;\n this._inputSubscription = this._pickerInput._valueChange.subscribe(\n (value: Color) => (this._selected = value),\n );\n }\n\n public open(): void {\n if (this._opened || this.disabled) {\n return;\n }\n if (!this._pickerInput) {\n throw Error('Attempted to open an ColorPicker with no associated input.');\n }\n\n if (this._document) {\n this._focusedElementBeforeOpen = this._document.activeElement;\n }\n\n this.touchUi ? this._openAsDialog() : this._openAsPopup();\n this._opened = true;\n this.openedStream.emit();\n }\n\n /** Open the calendar as a dialog. */\n private _openAsDialog(): void {\n if (this._dialogRef) {\n this._dialogRef.close();\n }\n\n this._dialogRef = this._dialog.open<NgxMatColorPickerContentComponent>(\n NgxMatColorPickerContentComponent,\n {\n direction: this._dir ? this._dir.value : 'ltr',\n viewContainerRef: this._viewContainerRef,\n panelClass: 'ngx-mat-colorpicker-dialog',\n },\n );\n\n this._dialogRef.afterClosed().subscribe(() => this.close());\n this._dialogRef.componentInstance.picker = this;\n this._setColor();\n }\n\n /** Open the calendar as a popup. */\n private _openAsPopup(): void {\n if (!this._portal) {\n this._portal = new ComponentPortal<NgxMatColorPickerContentComponent>(\n NgxMatColorPickerContentComponent,\n this._viewContainerRef,\n );\n }\n\n if (!this._popupRef) {\n this._createPopup();\n }\n\n if (!this._popupRef.hasAttached()) {\n this._popupComponentRef = this._popupRef.attach(this._portal);\n this._popupComponentRef.instance.picker = this;\n this._setColor();\n\n // Update the position once the calendar has rendered.\n this._zone.onStable\n .asObservable()\n .pipe(take(1))\n .subscribe(() => {\n this._popupRef.updatePosition();\n });\n }\n }\n\n /** Create the popup. */\n private _createPopup(): void {\n const overlayConfig = new OverlayConfig({\n positionStrategy: this._createPopupPositionStrategy(),\n hasBackdrop: true,\n backdropClass: 'mat-overlay-transparent-backdrop',\n direction: this._dir,\n scrollStrategy: this._scrollStrategy(),\n panelClass: 'mat-colorpicker-popup',\n });\n\n this._popupRef = this._overlay.create(overlayConfig);\n this._popupRef.overlayElement.setAttribute('role', 'dialog');\n\n merge(\n this._popupRef.backdropClick(),\n this._popupRef.detachments(),\n this._popupRef.keydownEvents().pipe(\n filter((event) => {\n // Closing on alt + up is only valid when there's an input associated with the datepicker.\n return (\n event.keyCode === ESCAPE ||\n (this._pickerInput && event.altKey && event.keyCode === UP_ARROW)\n );\n }),\n ),\n ).subscribe((event) => {\n if (event) {\n event.preventDefault();\n }\n\n this.close();\n });\n }\n\n close(): void {\n if (!this._opened) {\n return;\n }\n if (this._popupRef && this._popupRef.hasAttached()) {\n this._popupRef.detach();\n }\n if (this._dialogRef) {\n this._dialogRef.close();\n this._dialogRef = null;\n }\n if (this._portal && this._portal.isAttached) {\n this._portal.detach();\n }\n\n const completeClose = () => {\n // The `_opened` could've been reset already if\n // we got two events in quick succession.\n if (this._opened) {\n this._opened = false;\n this.closedStream.emit();\n this._focusedElementBeforeOpen = null;\n }\n };\n\n if (\n this._focusedElementBeforeOpen &&\n typeof this._focusedElementBeforeOpen.focus === 'function'\n ) {\n // Because IE moves focus asynchronously, we can't count on it being restored before we've\n // marked the datepicker as closed. If the event fires out of sequence and the element that\n // we're refocusing opens the datepicker on focus, the user could be stuck with not being\n // able to close the calendar at all. We work around it by making the logic, that marks\n // the datepicker as closed, async as well.\n this._focusedElementBeforeOpen.focus();\n setTimeout(completeClose);\n } else {\n completeClose();\n }\n }\n\n /** Passes the current theme color along to the calendar overlay. */\n private _setColor(): void {\n const color = this.color;\n if (this._popupComponentRef) {\n this._popupComponentRef.instance.color = color;\n }\n if (this._dialogRef) {\n this._dialogRef.componentInstance.color = color;\n }\n }\n\n /** Create the popup PositionStrategy. */\n private _createPopupPositionStrategy(): PositionStrategy {\n return this._overlay\n .position()\n .flexibleConnectedTo(this._pickerInput.getConnectedOverlayOrigin())\n .withTransformOriginOn('.ngx-mat-colorpicker-content')\n .withFlexibleDimensions(false)\n .withViewportMargin(8)\n .withLockedPosition()\n .withPositions([\n {\n originX: 'start',\n originY: 'bottom',\n overlayX: 'start',\n overlayY: 'top',\n },\n {\n originX: 'start',\n originY: 'top',\n overlayX: 'start',\n overlayY: 'bottom',\n },\n {\n originX: 'end',\n originY: 'bottom',\n overlayX: 'end',\n overlayY: 'top',\n },\n {\n originX: 'end',\n originY: 'top',\n overlayX: 'end',\n overlayY: 'bottom',\n },\n ]);\n }\n}\n","<ngx-mat-color-palette\n (colorChanged)=\"picker.select($event)\"\n [color]=\"picker._selected\"\n [theme]=\"color\"></ngx-mat-color-palette>\n","import { coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { DOWN_ARROW } from '@angular/cdk/keycodes';\nimport {\n Directive,\n ElementRef,\n EventEmitter,\n forwardRef,\n Inject,\n Input,\n OnDestroy,\n OnInit,\n Optional,\n output,\n} from '@angular/core';\nimport {\n AbstractControl,\n ControlValueAccessor,\n NG_VALIDATORS,\n NG_VALUE_ACCESSOR,\n ValidationErrors,\n Validator,\n ValidatorFn,\n Validators,\n} from '@angular/forms';\nimport { ThemePalette } from '@angular/material/core';\nimport { MatFormField } from '@angular/material/form-field';\nimport { MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input';\nimport { Subscription } from 'rxjs';\nimport { createMissingDateImplError } from '../../helpers';\nimport { Color } from '../../models';\nimport { ColorAdapter, MAT_COLOR_FORMATS, MatColorFormats } from '../../services';\nimport { NgxMatColorPickerComponent } from './color-picker.component';\n\nexport class NgxMatColorPickerInputEvent {\n /** The new value for the target colorpicker input. */\n value: Color | null;\n\n constructor(\n /** Reference to the colorpicker input component that emitted the event. */\n public target: NgxMatColorPickerInput,\n /** Reference to the native input element associated with the colorpicker input. */\n public targetElement: HTMLElement,\n ) {\n this.value = this.target.value;\n }\n}\n\nexport const MAT_COLORPICKER_VALUE_ACCESSOR: any = {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => NgxMatColorPickerInput),\n multi: true,\n};\n\nexport const MAT_COLORPICKER_VALIDATORS: any = {\n provide: NG_VALIDATORS,\n useExisting: forwardRef(() => NgxMatColorPickerInput),\n multi: true,\n};\n\n@Directive({\n selector: 'input[ngxMatColorPicker]',\n providers: [\n { provide: MAT_INPUT_VALUE_ACCESSOR, useExisting: NgxMatColorPickerInput },\n ColorAdapter,\n MAT_COLORPICKER_VALIDATORS,\n MAT_COLORPICKER_VALUE_ACCESSOR,\n ],\n host: {\n '[attr.aria-haspopup]': '_picker ? \"dialog\" : null',\n '[attr.aria-owns]': '(_picker?.opened && _picker.id) || null',\n '[disabled]': 'disabled',\n '(input)': '_onInput($event.target.value)',\n '(change)': '_onChange()',\n '(blur)': '_onBlur()',\n '(keydown)': '_onKeydown($event)',\n },\n exportAs: 'ngxMatColorPickerInput',\n})\nexport class NgxMatColorPickerInput implements ControlValueAccessor, OnInit, OnDestroy, Validator {\n @Input()\n set ngxMatColorPicker(value: NgxMatColorPickerComponent) {\n if (!value) {\n return;\n }\n\n this._picker = value;\n this._picker.registerInput(this);\n this._pickerSubscription.unsubscribe();\n\n this._pickerSubscription = this._picker._selectedChanged.subscribe((selected: Color) => {\n this.value = selected;\n this._cvaOnChange(selected);\n this._onTouched();\n this.colorInput.emit(new NgxMatColorPickerInputEvent(this, this._elementRef.nativeElement));\n this.colorChange.emit(new NgxMatColorPickerInputEvent(this, this._elementRef.nativeElement));\n });\n }\n _picker: NgxMatColorPickerComponent;\n\n /** Whether the colorpicker-input is disabled. */\n @Input()\n get disabled(): boolean {\n return !!this._disabled;\n }\n set disabled(value: boolean) {\n const newValue = coerceBooleanProperty(value);\n const element = this._elementRef.nativeElement;\n\n if (this._disabled !== newValue) {\n this._disabled = newValue;\n this._disabledChange.emit(newValue);\n }\n\n // We need to null check the `blur` method, because it's undefined during SSR.\n if (newValue && element.blur) {\n // Normally, native input elements automatically blur if they turn disabled. This behavior\n // is problematic, because it would mean that it triggers another change detection cycle,\n // which then causes a changed after checked error if the input element was focused before.\n element.blur();\n }\n }\n private _disabled: boolean;\n\n /** The value of the input. */\n @Input()\n get value(): Color | null {\n return this._value;\n }\n set value(value: Color | null) {\n const oldValue = this.value;\n this._value = value;\n this._formatValue(value);\n\n if (!this._adapter.sameColor(oldValue, value)) {\n this._valueChange.emit(value);\n }\n }\n private _value: Color | null;\n\n /** Emits when a `change` event is fired on this `<input>`. */\n readonly colorChange = output<NgxMatColorPickerInputEvent>();\n\n /** Emits when an `input` event is fired on this `<input>`. */\n readonly colorInput = output<NgxMatColorPickerInputEvent>();\n\n /** Emits when the disabled state has changed */\n _disabledChange = new EventEmitter<boolean>();\n\n /** Emits when the value changes (either due to user input or programmatic change). */\n _valueChange = new EventEmitter<Color>();\n\n _onTouched = () => {};\n\n private _cvaOnChange: (value: any) => void = () => {};\n\n private _validatorOnChange = () => {};\n\n private _pickerSubscription = Subscription.EMPTY;\n\n /** The combined form control validator for this input. */\n private _validator: ValidatorFn | null = Validators.compose([]);\n\n /** Whether the last value set on the input was valid. */\n private _lastValueValid = false;\n\n constructor(\n private _elementRef: ElementRef<HTMLInputElement>,\n @Optional() private _formField: MatFormField,\n @Optional()\n @Inject(MAT_COLOR_FORMATS)\n private _colorFormats: MatColorFormats,\n private _adapter: ColorAdapter,\n ) {\n if (!this._colorFormats) {\n throw createMissingDateImplError('MAT_COLOR_FORMATS');\n }\n }\n\n /** Returns the palette used by the input's form field, if any. */\n public getThemePalette(): ThemePalette {\n return this._formField ? this._formField.color : undefined;\n }\n\n registerOnValidatorChange(fn: () => void): void {\n this._validatorOnChange = fn;\n }\n\n validate(c: AbstractControl): ValidationErrors | null {\n return this._validator ? this._validator(c) : null;\n }\n\n /**\n * @deprecated\n * @breaking-change 8.0.0 Use `getConnectedOverlayOrigin` instead\n */\n getPopupConnectionElementRef(): ElementRef {\n return this.getConnectedOverlayOrigin();\n }\n\n /**\n * Gets the element that the colorpicker popup should be connected to.\n * @return The element to connect the popup to.\n */\n getConnectedOverlayOrigin(): ElementRef {\n return this._formField ? this._formField.getConnectedOverlayOrigin() : this._elementRef;\n }\n\n ngOnInit() {}\n\n ngOnDestroy(): void {\n this._pickerSubscription.unsubscribe();\n this._valueChange.complete();\n this._disabledChange.complete();\n }\n\n // Implemented as part of ControlValueAccessor.\n writeValue(value: Color): void {\n this.value = value;\n }\n\n // Implemented as part of ControlValueAccessor.\n registerOnChange(fn: (value: any) => void): void {\n this._cvaOnChange = fn;\n }\n\n // Implemented as part of ControlValueAccessor.\n registerOnTouched(fn: () => void): void {\n this._onTouched = fn;\n }\n\n // Implemented as part of ControlValueAccessor.\n setDisabledState(isDisabled: boolean): void {\n this.disabled = isDisabled;\n }\n\n _onChange() {\n this.colorChange.emit(new NgxMatColorPickerInputEvent(this, this._elementRef.nativeElement));\n }\n\n _onKeydown(event: KeyboardEvent) {\n const isAltDownArrow = event.altKey && event.keyCode === DOWN_ARROW;\n\n if (this._picker && isAltDownArrow && !this._elementRef.nativeElement.readOnly) {\n this._picker.open();\n event.preventDefault();\n }\n }\n\n /** Handles blur events on the input. */\n _onBlur() {\n // Reformat the input only if we have a valid value.\n if (this.value) {\n this._formatValue(this.value);\n }\n\n this._onTouched();\n }\n\n /** Formats a value and sets it on the input element. */\n private _formatValue(value: Color | null) {\n this._elementRef.nativeElement.value = value\n ? this._adapter.format(value, this._colorFormats.display.colorInput)\n : '';\n }\n\n _onInput(value: string) {\n const lastValueWasValid = this._lastValueValid;\n const nextValue = this._adapter.parse(value);\n\n if (!this._adapter.sameColor(nextValue, this._value)) {\n this._value = nextValue;\n this._cvaOnChange(nextValue);\n this._valueChange.emit(nextValue);\n this.colorInput.emit(new NgxMatColorPickerInputEvent(this, this._elementRef.nativeElement));\n } else if (lastValueWasValid !== this._lastValueValid) {\n this._validatorOnChange();\n }\n }\n}\n","import {\n AfterContentInit,\n ChangeDetectorRef,\n Component,\n Directive,\n EventEmitter,\n Input,\n OnDestroy,\n ViewEncapsulation,\n effect,\n input,\n untracked,\n viewChild,\n} from '@angular/core';\nimport { MatButton, MatButtonModule } from '@angular/material/button';\nimport { MatIconModule } from '@angular/material/icon';\nimport { Subscription, merge, of } from 'rxjs';\nimport { NgxMatColorPickerComponent } from '../color-picker/color-picker.component';\n\n@Directive({\n selector: '[ngxMatColorpickerToggleIcon]',\n})\nexport class NgxMatColorpickerToggleIcon {}\n\n@Component({\n selector: 'ngx-mat-color-toggle',\n templateUrl: './color-toggle.component.html',\n styleUrls: ['./color-toggle.component.scss'],\n host: {\n class: 'ngx-mat-color-toggle',\n // Always set the tabindex to -1 so that it doesn't overlap with any custom tabindex the\n // consumer may have provided, while still being able to receive focus.\n '[attr.tabindex]': '-1',\n '[class.ngx-mat-color-toggle-active]': 'picker && picker.opened',\n '[class.mat-accent]': 'picker && picker.color === \"accent\"',\n '[class.mat-warn]': 'picker && picker.color === \"warn\"',\n '(focus)': '_button.focus()',\n },\n exportAs: 'ngxMatColorPickerToggle',\n encapsulation: ViewEncapsulation.None,\n imports: [MatButtonModule, MatIconModule],\n})\nexport class NgxMatColorToggleComponent implements AfterContentInit, OnDestroy {\n private _stateChanges = Subscription.EMPTY;\n\n picker = input<NgxMatColorPickerComponent>(undefined, { alias: 'for' });\n tabIndex = input<number>();\n\n @Input() get disabled(): boolean {\n if (this._disabled == null && this.picker()) {\n return this.pick