primeng
Version:
PrimeNG is a premium UI library for Angular featuring a rich set of 90+ components, a theme designer, various theme alternatives such as Material, Bootstrap, Tailwind, premium templates and professional support. In addition, it integrates with PrimeBlock,
1 lines • 170 kB
Source Map (JSON)
{"version":3,"file":"primeng-inputcolor.mjs","sources":["../../src/inputcolor/color-manager.ts","../../src/inputcolor/inputcolor.token.ts","../../src/inputcolor/inputcolor-area.ts","../../src/inputcolor/inputcolor-area-background.ts","../../src/inputcolor/inputcolor-area-handle.ts","../../src/inputcolor/inputcolor-eyedropper.ts","../../src/inputcolor/inputcolor-input.ts","../../src/inputcolor/inputcolor-slider.ts","../../src/inputcolor/inputcolor-slider-handle.ts","../../src/inputcolor/inputcolor-slider-track.ts","../../src/inputcolor/inputcolor-swatch.ts","../../src/inputcolor/inputcolor-swatch-background.ts","../../src/inputcolor/inputcolor-transparency-grid.ts","../../src/inputcolor/style/inputcolorstyle.ts","../../src/inputcolor/inputcolor.ts","../../src/inputcolor/primeng-inputcolor.ts"],"sourcesContent":["export type ColorSpace = 'hsba' | 'hsla' | 'rgba' | 'hexa' | 'oklch';\n\nexport type ColorOutputFormat = ColorSpace | 'hex' | 'rgb' | 'hsl' | 'hsb' | 'oklcha' | 'css';\n\nexport type ColorChannel = 'hue' | 'saturation' | 'brightness' | 'lightness' | 'red' | 'green' | 'blue' | 'alpha' | 'hex' | 'L' | 'C' | 'H' | 'oklchLightness' | 'oklchChroma' | 'oklchHue';\n\nexport type ColorSliderChannel = 'hue' | 'saturation' | 'brightness' | 'lightness' | 'red' | 'green' | 'blue' | 'alpha' | 'L' | 'C' | 'H' | 'oklchLightness' | 'oklchChroma' | 'oklchHue';\n\nexport type ColorInputChannel = ColorChannel | 'css';\n\nexport interface ColorChannelRange {\n minValue: number;\n maxValue: number;\n step: number;\n pageStep: number;\n}\n\nexport interface Color2DAxes {\n xChannel: ColorSliderChannel;\n yChannel: ColorSliderChannel;\n}\n\nexport interface Color3DAxes extends Color2DAxes {\n zChannel: ColorSliderChannel;\n}\n\nexport interface ColorOutput {\n toString(format?: ColorOutputFormat): string;\n toHex(): string;\n toHexa(): string;\n toHexInt(): number;\n toRgbString(): string;\n toHslString(): string;\n toHsbString(): string;\n}\n\nexport type ColorInstance = HSBColor | HSLColor | RGBColor | OKLCHColor;\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction round(value: number, decimals: number = 0): number {\n const factor = Math.pow(10, decimals);\n return Math.round(value * factor) / factor;\n}\n\nfunction multiplyMatrices(matrix: number[], vector: number[]): number[] {\n return [matrix[0] * vector[0] + matrix[1] * vector[1] + matrix[2] * vector[2], matrix[3] * vector[0] + matrix[4] * vector[1] + matrix[5] * vector[2], matrix[6] * vector[0] + matrix[7] * vector[1] + matrix[8] * vector[2]];\n}\n\nfunction mod(n: number, m: number): number {\n return ((n % m) + m) % m;\n}\n\n/**\n * Snaps a value to the nearest step increment within min/max bounds.\n * @example snap(23.7, 0, 100, 5) => 25\n * @example snap(0.234, 0, 1, 0.01) => 0.23\n */\nexport function snapValue(value: number, min: number, max: number, step: number): number {\n const clamped = clamp(value, min, max);\n const offset = clamped - min;\n const steps = Math.round(offset / step);\n const snapped = min + steps * step;\n const precision = (step.toString().split('.')[1] || '').length;\n return parseFloat(snapped.toFixed(precision));\n}\n\nexport abstract class Color implements ColorOutput {\n abstract readonly colorSpace: ColorSpace;\n\n abstract getChannelValue(channel: ColorChannel): number;\n abstract setChannelValue(channel: ColorChannel, value: number): ColorInstance;\n abstract toHSB(): HSBColor;\n abstract toHSL(): HSLColor;\n abstract toRGB(): RGBColor;\n abstract toOKLCH(): OKLCHColor;\n abstract clone(): ColorInstance;\n abstract toJSON(): Record<string, number>;\n abstract getFormat(): ColorSpace;\n abstract getChannels(): [ColorChannel, ColorChannel, ColorChannel];\n abstract getChannelRange(channel: ColorChannel): ColorChannelRange;\n\n get alpha(): number {\n return this.getChannelValue('alpha');\n }\n\n getSpaceAxes(xyChannels: Color2DAxes): Color3DAxes {\n const { xChannel, yChannel } = xyChannels;\n if (xChannel === yChannel) {\n throw new Error('xChannel and yChannel cannot be the same');\n }\n const zChannel = this.getChannels().find((channel) => channel !== xChannel && channel !== yChannel);\n if (!zChannel) {\n throw new Error('zChannel not found');\n }\n return { xChannel, yChannel, zChannel: zChannel as ColorSliderChannel };\n }\n\n incChannelValue(channel: ColorChannel, step: number): ColorInstance {\n const range = this.getChannelRange(channel);\n const currentValue = this.getChannelValue(channel);\n const newValue = snapValue(clamp(currentValue + step, range.minValue, range.maxValue), range.minValue, range.maxValue, range.step);\n return this.setChannelValue(channel, newValue);\n }\n\n decChannelValue(channel: ColorChannel, step: number): ColorInstance {\n return this.incChannelValue(channel, -step);\n }\n\n toHex(): string {\n const rgb = this.toRGB();\n const r = Math.round(rgb.red).toString(16).padStart(2, '0');\n const g = Math.round(rgb.green).toString(16).padStart(2, '0');\n const b = Math.round(rgb.blue).toString(16).padStart(2, '0');\n return `#${r}${g}${b}`;\n }\n\n toHexa(): string {\n const hex = this.toHex();\n if (this.alpha < 1) {\n const a = Math.round(this.alpha * 255)\n .toString(16)\n .padStart(2, '0');\n return `${hex}${a}`;\n }\n return hex;\n }\n\n toHexInt(): number {\n const rgb = this.toRGB();\n return (Math.round(rgb.red) << 16) | (Math.round(rgb.green) << 8) | Math.round(rgb.blue);\n }\n\n toRgbString(): string {\n const rgb = this.toRGB();\n const r = Math.round(rgb.red);\n const g = Math.round(rgb.green);\n const b = Math.round(rgb.blue);\n if (this.alpha < 1) {\n return `rgba(${r}, ${g}, ${b}, ${round(this.alpha, 2)})`;\n }\n return `rgb(${r}, ${g}, ${b})`;\n }\n\n toHslString(): string {\n const hsl = this.toHSL();\n const h = Math.round(hsl.hue) % 360;\n const s = Math.round(hsl.saturation);\n const l = Math.round(hsl.lightness);\n if (this.alpha < 1) {\n return `hsla(${h}, ${s}%, ${l}%, ${round(this.alpha, 2)})`;\n }\n return `hsl(${h}, ${s}%, ${l}%)`;\n }\n\n toHsbString(): string {\n const hsb = this.toHSB();\n const h = Math.round(hsb.hue) % 360;\n const s = Math.round(hsb.saturation);\n const b = Math.round(hsb.brightness);\n if (this.alpha < 1) {\n return `hsba(${h}, ${s}%, ${b}%, ${round(this.alpha, 2)})`;\n }\n return `hsb(${h}, ${s}%, ${b}%)`;\n }\n\n toString(format?: ColorOutputFormat): string {\n const f = format || this.colorSpace;\n switch (f) {\n case 'hex':\n return this.toHex();\n case 'hexa':\n return this.toHexa();\n case 'rgb':\n case 'rgba':\n return this.toRgbString();\n case 'hsl':\n case 'hsla':\n return this.toHslString();\n case 'hsb':\n case 'hsba':\n return this.toHsbString();\n case 'oklch':\n return this.toOKLCH().toString('oklch');\n case 'oklcha':\n return this.toOKLCH().toString('oklcha');\n case 'css':\n return this.toHex();\n default:\n return this.toHex();\n }\n }\n\n toFormat(format: ColorSpace): ColorInstance {\n switch (format) {\n case 'hsba':\n return this.toHSB();\n case 'hsla':\n return this.toHSL();\n case 'rgba':\n return this.toRGB();\n case 'hexa':\n return this.toRGB();\n case 'oklch':\n return this.toOKLCH();\n default:\n return this.toHSB();\n }\n }\n}\n\nexport class HSBColor extends Color {\n readonly colorSpace: ColorSpace = 'hsba';\n\n constructor(\n public hue: number,\n public saturation: number,\n public brightness: number,\n private _alpha: number = 1\n ) {\n super();\n this.hue = hue >= 0 && hue <= 360 ? hue : mod(hue, 360);\n this.saturation = clamp(saturation, 0, 100);\n this.brightness = clamp(brightness, 0, 100);\n this._alpha = clamp(_alpha, 0, 1);\n }\n\n getChannelValue(channel: ColorChannel): number {\n switch (channel) {\n case 'hue':\n return this.hue;\n case 'saturation':\n return this.saturation;\n case 'brightness':\n return this.brightness;\n case 'alpha':\n return this._alpha;\n case 'lightness':\n return this.toHSL().getChannelValue(channel);\n case 'L':\n case 'C':\n case 'H':\n case 'oklchLightness':\n case 'oklchChroma':\n case 'oklchHue':\n return this.toOKLCH().getChannelValue(channel);\n default:\n return this.toRGB().getChannelValue(channel);\n }\n }\n\n setChannelValue(channel: ColorChannel, value: number): ColorInstance {\n switch (channel) {\n case 'hue':\n return new HSBColor(value, this.saturation, this.brightness, this._alpha);\n case 'saturation':\n return new HSBColor(this.hue, value, this.brightness, this._alpha);\n case 'brightness':\n return new HSBColor(this.hue, this.saturation, value, this._alpha);\n case 'alpha':\n return new HSBColor(this.hue, this.saturation, this.brightness, value);\n case 'lightness': {\n const hsl = this.toHSL().setChannelValue(channel, value);\n return hsl.toHSB();\n }\n case 'L':\n case 'C':\n case 'H':\n case 'oklchLightness':\n case 'oklchChroma':\n case 'oklchHue': {\n const oklch = this.toOKLCH().setChannelValue(channel, value);\n return oklch.toHSB();\n }\n default: {\n const rgb = this.toRGB().setChannelValue(channel, value);\n return rgb.toHSB();\n }\n }\n }\n\n toHSB(): HSBColor {\n return this.clone();\n }\n\n toHSL(): HSLColor {\n const h = this.hue;\n const s = this.saturation / 100;\n const b = this.brightness / 100;\n\n const l = b * (1 - s / 2);\n const sl = l === 0 || l === 1 ? 0 : ((b - l) / Math.min(l, 1 - l)) * 100;\n\n return new HSLColor(h, sl, l * 100, this._alpha);\n }\n\n toRGB(): RGBColor {\n const h = this.hue / 360;\n const s = this.saturation / 100;\n const b = this.brightness / 100;\n\n let r: number, g: number, bl: number;\n\n const i = Math.floor(h * 6);\n const f = h * 6 - i;\n const p = b * (1 - s);\n const q = b * (1 - f * s);\n const t = b * (1 - (1 - f) * s);\n\n switch (i % 6) {\n case 0:\n r = b;\n g = t;\n bl = p;\n break;\n case 1:\n r = q;\n g = b;\n bl = p;\n break;\n case 2:\n r = p;\n g = b;\n bl = t;\n break;\n case 3:\n r = p;\n g = q;\n bl = b;\n break;\n case 4:\n r = t;\n g = p;\n bl = b;\n break;\n case 5:\n r = b;\n g = p;\n bl = q;\n break;\n default:\n r = 0;\n g = 0;\n bl = 0;\n }\n\n return new RGBColor(r * 255, g * 255, bl * 255, this._alpha);\n }\n\n toOKLCH(): OKLCHColor {\n return this.toRGB().toOKLCH();\n }\n\n clone(): HSBColor {\n return new HSBColor(this.hue, this.saturation, this.brightness, this._alpha);\n }\n\n toJSON(): Record<string, number> {\n return {\n hue: this.hue,\n saturation: this.saturation,\n brightness: this.brightness,\n alpha: this._alpha\n };\n }\n\n getFormat(): ColorSpace {\n return 'hsba';\n }\n\n getChannels(): [ColorChannel, ColorChannel, ColorChannel] {\n return ['hue', 'saturation', 'brightness'];\n }\n\n getChannelRange(channel: ColorChannel): ColorChannelRange {\n switch (channel) {\n case 'hue':\n return { minValue: 0, maxValue: 360, step: 1, pageStep: 15 };\n case 'saturation':\n case 'brightness':\n return { minValue: 0, maxValue: 100, step: 1, pageStep: 10 };\n case 'alpha':\n return { minValue: 0, maxValue: 1, step: 0.01, pageStep: 0.1 };\n case 'lightness':\n return this.toHSL().getChannelRange(channel);\n case 'L':\n case 'C':\n case 'H':\n case 'oklchLightness':\n case 'oklchChroma':\n case 'oklchHue':\n return this.toOKLCH().getChannelRange(channel);\n case 'red':\n case 'green':\n case 'blue':\n return this.toRGB().getChannelRange(channel);\n default:\n throw new Error(`Unknown color channel: ${channel}`);\n }\n }\n}\n\nexport class HSLColor extends Color {\n readonly colorSpace: ColorSpace = 'hsla';\n\n constructor(\n public hue: number,\n public saturation: number,\n public lightness: number,\n private _alpha: number = 1\n ) {\n super();\n this.hue = hue >= 0 && hue <= 360 ? hue : mod(hue, 360);\n this.saturation = clamp(saturation, 0, 100);\n this.lightness = clamp(lightness, 0, 100);\n this._alpha = clamp(_alpha, 0, 1);\n }\n\n getChannelValue(channel: ColorChannel): number {\n switch (channel) {\n case 'hue':\n return this.hue;\n case 'saturation':\n return this.saturation;\n case 'lightness':\n return this.lightness;\n case 'alpha':\n return this._alpha;\n case 'brightness':\n return this.toHSB().getChannelValue(channel);\n case 'L':\n case 'C':\n case 'H':\n case 'oklchLightness':\n case 'oklchChroma':\n case 'oklchHue':\n return this.toOKLCH().getChannelValue(channel);\n default:\n return this.toRGB().getChannelValue(channel);\n }\n }\n\n setChannelValue(channel: ColorChannel, value: number): ColorInstance {\n switch (channel) {\n case 'hue':\n return new HSLColor(value, this.saturation, this.lightness, this._alpha);\n case 'saturation':\n return new HSLColor(this.hue, value, this.lightness, this._alpha);\n case 'lightness':\n return new HSLColor(this.hue, this.saturation, value, this._alpha);\n case 'alpha':\n return new HSLColor(this.hue, this.saturation, this.lightness, value);\n case 'brightness': {\n const hsb = this.toHSB().setChannelValue(channel, value);\n return hsb.toHSL();\n }\n case 'L':\n case 'C':\n case 'H':\n case 'oklchLightness':\n case 'oklchChroma':\n case 'oklchHue': {\n const oklch = this.toOKLCH().setChannelValue(channel, value);\n return oklch.toHSL();\n }\n default: {\n const rgb = this.toRGB().setChannelValue(channel, value);\n return rgb.toHSL();\n }\n }\n }\n\n toHSB(): HSBColor {\n const h = this.hue;\n const s = this.saturation / 100;\n const l = this.lightness / 100;\n\n const b = l + s * Math.min(l, 1 - l);\n const sb = b === 0 ? 0 : 2 * (1 - l / b);\n\n return new HSBColor(h, sb * 100, b * 100, this._alpha);\n }\n\n toHSL(): HSLColor {\n return this.clone();\n }\n\n toRGB(): RGBColor {\n const h = this.hue;\n const s = this.saturation / 100;\n const l = this.lightness / 100;\n\n const a = s * Math.min(l, 1 - l);\n const f = (n: number) => {\n const k = (n + h / 30) % 12;\n return l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);\n };\n\n return new RGBColor(f(0) * 255, f(8) * 255, f(4) * 255, this._alpha);\n }\n\n toOKLCH(): OKLCHColor {\n return this.toRGB().toOKLCH();\n }\n\n clone(): HSLColor {\n return new HSLColor(this.hue, this.saturation, this.lightness, this._alpha);\n }\n\n toJSON(): Record<string, number> {\n return {\n hue: this.hue,\n saturation: this.saturation,\n lightness: this.lightness,\n alpha: this._alpha\n };\n }\n\n getFormat(): ColorSpace {\n return 'hsla';\n }\n\n getChannels(): [ColorChannel, ColorChannel, ColorChannel] {\n return ['hue', 'saturation', 'lightness'];\n }\n\n getChannelRange(channel: ColorChannel): ColorChannelRange {\n switch (channel) {\n case 'hue':\n return { minValue: 0, maxValue: 360, step: 1, pageStep: 15 };\n case 'saturation':\n case 'lightness':\n return { minValue: 0, maxValue: 100, step: 1, pageStep: 10 };\n case 'alpha':\n return { minValue: 0, maxValue: 1, step: 0.01, pageStep: 0.1 };\n case 'brightness':\n return this.toHSB().getChannelRange(channel);\n case 'L':\n case 'C':\n case 'H':\n case 'oklchLightness':\n case 'oklchChroma':\n case 'oklchHue':\n return this.toOKLCH().getChannelRange(channel);\n case 'red':\n case 'green':\n case 'blue':\n return this.toRGB().getChannelRange(channel);\n default:\n throw new Error(`Unknown color channel: ${channel}`);\n }\n }\n}\n\nexport class RGBColor extends Color {\n readonly colorSpace: ColorSpace = 'rgba';\n\n constructor(\n public red: number,\n public green: number,\n public blue: number,\n private _alpha: number = 1\n ) {\n super();\n this.red = clamp(red, 0, 255);\n this.green = clamp(green, 0, 255);\n this.blue = clamp(blue, 0, 255);\n this._alpha = clamp(_alpha, 0, 1);\n }\n\n getChannelValue(channel: ColorChannel): number {\n switch (channel) {\n case 'red':\n return this.red;\n case 'green':\n return this.green;\n case 'blue':\n return this.blue;\n case 'alpha':\n return this._alpha;\n case 'hue':\n case 'saturation':\n case 'brightness':\n return this.toHSB().getChannelValue(channel);\n case 'lightness':\n return this.toHSL().getChannelValue(channel);\n case 'L':\n case 'C':\n case 'H':\n case 'oklchLightness':\n case 'oklchChroma':\n case 'oklchHue':\n return this.toOKLCH().getChannelValue(channel);\n default:\n return this.toHSB().getChannelValue(channel);\n }\n }\n\n setChannelValue(channel: ColorChannel, value: number): ColorInstance {\n switch (channel) {\n case 'red':\n return new RGBColor(value, this.green, this.blue, this._alpha);\n case 'green':\n return new RGBColor(this.red, value, this.blue, this._alpha);\n case 'blue':\n return new RGBColor(this.red, this.green, value, this._alpha);\n case 'alpha':\n return new RGBColor(this.red, this.green, this.blue, value);\n case 'hue':\n case 'saturation':\n case 'brightness': {\n const hsb = this.toHSB().setChannelValue(channel, value);\n return hsb.toRGB();\n }\n case 'lightness': {\n const hsl = this.toHSL().setChannelValue(channel, value);\n return hsl.toRGB();\n }\n case 'L':\n case 'C':\n case 'H':\n case 'oklchLightness':\n case 'oklchChroma':\n case 'oklchHue': {\n const oklch = this.toOKLCH().setChannelValue(channel, value);\n return oklch.toRGB();\n }\n default: {\n const hsb = this.toHSB().setChannelValue(channel, value);\n return hsb.toRGB();\n }\n }\n }\n\n toHSB(): HSBColor {\n const r = this.red / 255;\n const g = this.green / 255;\n const b = this.blue / 255;\n\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const d = max - min;\n\n let h = 0;\n const s = max === 0 ? 0 : d / max;\n const v = max;\n\n if (d !== 0) {\n switch (max) {\n case r:\n h = ((g - b) / d + (g < b ? 6 : 0)) / 6;\n break;\n case g:\n h = ((b - r) / d + 2) / 6;\n break;\n case b:\n h = ((r - g) / d + 4) / 6;\n break;\n }\n }\n\n return new HSBColor(h * 360, s * 100, v * 100, this._alpha);\n }\n\n toHSL(): HSLColor {\n const r = this.red / 255;\n const g = this.green / 255;\n const b = this.blue / 255;\n\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const d = max - min;\n const l = (max + min) / 2;\n\n let h = 0;\n let s = 0;\n\n if (d !== 0) {\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n switch (max) {\n case r:\n h = ((g - b) / d + (g < b ? 6 : 0)) / 6;\n break;\n case g:\n h = ((b - r) / d + 2) / 6;\n break;\n case b:\n h = ((r - g) / d + 4) / 6;\n break;\n }\n }\n\n return new HSLColor(h * 360, s * 100, l * 100, this._alpha);\n }\n\n toRGB(): RGBColor {\n return this.clone();\n }\n\n toOKLCH(): OKLCHColor {\n const rgb = [this.red / 255, this.green / 255, this.blue / 255];\n const rgbLinear = rgb.map((c) => (Math.abs(c) <= 0.04045 ? c / 12.92 : (c < 0 ? -1 : 1) * ((Math.abs(c) + 0.055) / 1.055) ** 2.4));\n\n const xyz = multiplyMatrices([0.41239079926595934, 0.357584339383878, 0.1804807884018343, 0.21263900587151027, 0.715168678767756, 0.07219231536073371, 0.01933081871559182, 0.11919477979462598, 0.9505321522496607], rgbLinear);\n const LMS = multiplyMatrices([0.819022437996703, 0.3619062600528904, -0.1288737815209879, 0.0329836539323885, 0.9292868615863434, 0.0361446663506424, 0.0481771893596242, 0.2642395317527308, 0.6335478284694309], xyz);\n const LMSg = LMS.map((val) => Math.cbrt(val));\n\n const [L, a, b] = multiplyMatrices([0.210454268309314, 0.7936177747023054, -0.0040720430116193, 1.9779985324311684, -2.4285922420485799, 0.450593709617411, 0.0259040424655478, 0.7827717124575296, -0.8086757549230774], LMSg);\n\n const C = Math.sqrt(a ** 2 + b ** 2);\n const H = Math.abs(a) < 0.0002 && Math.abs(b) < 0.0002 ? NaN : ((((Math.atan2(b, a) * 180) / Math.PI) % 360) + 360) % 360;\n\n const outL = Number(Math.min(1, Math.max(0, L)).toFixed(4));\n const outC = Number(C.toFixed(4));\n const outH = Number.isNaN(H) ? NaN : Number(H.toFixed(2));\n\n return new OKLCHColor(outL, outC, outH, Number(this._alpha.toFixed(2)));\n }\n\n clone(): RGBColor {\n return new RGBColor(this.red, this.green, this.blue, this._alpha);\n }\n\n toJSON(): Record<string, number> {\n return {\n red: this.red,\n green: this.green,\n blue: this.blue,\n alpha: this._alpha\n };\n }\n\n getFormat(): ColorSpace {\n return 'rgba';\n }\n\n getChannels(): [ColorChannel, ColorChannel, ColorChannel] {\n return ['red', 'green', 'blue'];\n }\n\n getChannelRange(channel: ColorChannel): ColorChannelRange {\n switch (channel) {\n case 'red':\n case 'green':\n case 'blue':\n return { minValue: 0, maxValue: 255, step: 1, pageStep: 17 };\n case 'alpha':\n return { minValue: 0, maxValue: 1, step: 0.01, pageStep: 0.1 };\n case 'hue':\n case 'saturation':\n case 'brightness':\n return this.toHSB().getChannelRange(channel);\n case 'lightness':\n return this.toHSL().getChannelRange(channel);\n case 'L':\n case 'C':\n case 'H':\n case 'oklchLightness':\n case 'oklchChroma':\n case 'oklchHue':\n return this.toOKLCH().getChannelRange(channel);\n default:\n throw new Error(`Unknown color channel: ${channel}`);\n }\n }\n}\n\nexport class OKLCHColor extends Color {\n readonly colorSpace: ColorSpace = 'oklch';\n\n constructor(\n public L: number,\n public C: number,\n public H: number,\n private _alpha: number = 1\n ) {\n super();\n this.L = clamp(L, 0, 1);\n this.C = clamp(C, 0, 0.4);\n this.H = Number.isNaN(H) ? NaN : H >= 0 && H <= 360 ? H : mod(H, 360);\n this._alpha = clamp(_alpha, 0, 1);\n }\n\n getChannelValue(channel: ColorChannel): number {\n switch (channel) {\n case 'L':\n case 'oklchLightness':\n return this.L;\n case 'C':\n case 'oklchChroma':\n return this.C;\n case 'H':\n case 'oklchHue':\n return this.H;\n case 'alpha':\n return this._alpha;\n default:\n return this.toRGB().getChannelValue(channel);\n }\n }\n\n setChannelValue(channel: ColorChannel, value: number): ColorInstance {\n switch (channel) {\n case 'L':\n case 'oklchLightness':\n return new OKLCHColor(value, this.C, this.H, this._alpha);\n case 'C':\n case 'oklchChroma':\n return new OKLCHColor(this.L, value, this.H, this._alpha);\n case 'H':\n case 'oklchHue':\n return new OKLCHColor(this.L, this.C, value, this._alpha);\n case 'alpha':\n return new OKLCHColor(this.L, this.C, this.H, value);\n default: {\n const rgb = this.toRGB().setChannelValue(channel, value);\n return rgb.toOKLCH();\n }\n }\n }\n\n toHSB(): HSBColor {\n return this.toRGB().toHSB();\n }\n\n toHSL(): HSLColor {\n return this.toRGB().toHSL();\n }\n\n toRGB(): RGBColor {\n const hRad = (this.H * Math.PI) / 180;\n const a = Number.isNaN(this.H) ? 0 : this.C * Math.cos(hRad);\n const b = Number.isNaN(this.H) ? 0 : this.C * Math.sin(hRad);\n\n const l_ = this.L + 0.3963377774 * a + 0.2158037573 * b;\n const m_ = this.L - 0.1055613458 * a - 0.0638541728 * b;\n const s_ = this.L - 0.0894841775 * a - 1.291485548 * b;\n\n const l = l_ * l_ * l_;\n const m = m_ * m_ * m_;\n const s = s_ * s_ * s_;\n\n const lr = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;\n const lg = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;\n const lb = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s;\n\n const toSrgb = (c: number) => {\n if (c <= 0.0031308) return c * 12.92;\n return 1.055 * Math.pow(c, 1 / 2.4) - 0.055;\n };\n\n return new RGBColor(clamp(toSrgb(lr) * 255, 0, 255), clamp(toSrgb(lg) * 255, 0, 255), clamp(toSrgb(lb) * 255, 0, 255), this._alpha);\n }\n\n toOKLCH(): OKLCHColor {\n return this.clone();\n }\n\n override toString(format?: ColorOutputFormat): string {\n const f = format || this.colorSpace;\n switch (f) {\n case 'oklch': {\n const l = Number.isNaN(this.L) ? 0 : Number((this.L * 100).toFixed(2));\n const c = Number.isNaN(this.C) ? 0 : Number(this.C.toFixed(4));\n const h = Number.isNaN(this.H) ? 0 : Number(this.H.toFixed(2));\n return `oklch(${l}% ${c} ${h})`;\n }\n case 'oklcha':\n case 'css': {\n const l = Number.isNaN(this.L) ? 0 : Number((this.L * 100).toFixed(2));\n const c = Number.isNaN(this.C) ? 0 : Number(this.C.toFixed(4));\n const h = Number.isNaN(this.H) ? 0 : Number(this.H.toFixed(2));\n const a = Number.isNaN(this._alpha) ? 1 : Number(this._alpha.toFixed(2));\n return `oklch(${l}% ${c} ${h} / ${a})`;\n }\n default:\n return super.toString(f);\n }\n }\n\n clone(): OKLCHColor {\n return new OKLCHColor(this.L, this.C, this.H, this._alpha);\n }\n\n toJSON(): Record<string, number> {\n return {\n L: this.L,\n C: this.C,\n H: this.H,\n alpha: this._alpha\n };\n }\n\n getFormat(): ColorSpace {\n return 'oklch';\n }\n\n getChannels(): [ColorChannel, ColorChannel, ColorChannel] {\n return ['L', 'C', 'H'];\n }\n\n getChannelRange(channel: ColorChannel): ColorChannelRange {\n switch (channel) {\n case 'L':\n case 'oklchLightness':\n return { minValue: 0, maxValue: 1, step: 0.01, pageStep: 0.1 };\n case 'C':\n case 'oklchChroma':\n return { minValue: 0, maxValue: 0.4, step: 0.01, pageStep: 0.05 };\n case 'H':\n case 'oklchHue':\n return { minValue: 0, maxValue: 360, step: 1, pageStep: 15 };\n case 'alpha':\n return { minValue: 0, maxValue: 1, step: 0.01, pageStep: 0.1 };\n case 'hue':\n case 'saturation':\n case 'brightness':\n return this.toHSB().getChannelRange(channel);\n case 'lightness':\n return this.toHSL().getChannelRange(channel);\n case 'red':\n case 'green':\n case 'blue':\n return this.toRGB().getChannelRange(channel);\n default:\n throw new Error(`Unknown color channel: ${channel}`);\n }\n }\n}\n\nexport function parseColor(value: string | ColorInstance | null | undefined): ColorInstance | null {\n if (!value) return null;\n\n if (value instanceof Color) {\n return value as ColorInstance;\n }\n\n if (typeof value !== 'string') return null;\n\n const str = value.trim();\n\n const hexMatch = str.match(/^#?([0-9a-f]{3,8})$/i);\n if (hexMatch) {\n let hex = hexMatch[1];\n if (hex.length === 3) {\n hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];\n } else if (hex.length === 4) {\n hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];\n }\n\n if (hex.length === 6) {\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n return new RGBColor(r, g, b, 1);\n }\n\n if (hex.length === 8) {\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n const a = parseInt(hex.substring(6, 8), 16) / 255;\n return new RGBColor(r, g, b, a);\n }\n }\n\n const rgbMatch = str.match(/^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(?:,\\s*([\\d.]+))?\\s*\\)$/i);\n if (rgbMatch) {\n return new RGBColor(parseInt(rgbMatch[1]), parseInt(rgbMatch[2]), parseInt(rgbMatch[3]), rgbMatch[4] !== undefined ? parseFloat(rgbMatch[4]) : 1);\n }\n\n const hslMatch = str.match(/^hsla?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)%?\\s*,\\s*([\\d.]+)%?\\s*(?:,\\s*([\\d.]+))?\\s*\\)$/i);\n if (hslMatch) {\n return new HSLColor(parseFloat(hslMatch[1]), parseFloat(hslMatch[2]), parseFloat(hslMatch[3]), hslMatch[4] !== undefined ? parseFloat(hslMatch[4]) : 1);\n }\n\n const hsbMatch = str.match(/^hsba?\\(\\s*([\\d.]+)\\s*,\\s*([\\d.]+)%?\\s*,\\s*([\\d.]+)%?\\s*(?:,\\s*([\\d.]+))?\\s*\\)$/i);\n if (hsbMatch) {\n return new HSBColor(parseFloat(hsbMatch[1]), parseFloat(hsbMatch[2]), parseFloat(hsbMatch[3]), hsbMatch[4] !== undefined ? parseFloat(hsbMatch[4]) : 1);\n }\n\n const oklchMatch = str.match(/^oklch\\(\\s*([\\d.]+)(%?)\\s+([\\d.]+)\\s+([\\d.]+)\\s*(?:\\/\\s*([\\d.]+))?\\s*\\)$/i);\n if (oklchMatch) {\n const lVal = parseFloat(oklchMatch[1]);\n const l = oklchMatch[2] === '%' ? lVal / 100 : lVal;\n return new OKLCHColor(l, parseFloat(oklchMatch[3]), parseFloat(oklchMatch[4]), oklchMatch[5] !== undefined ? parseFloat(oklchMatch[5]) : 1);\n }\n\n return null;\n}\n\nexport function getChannelRange(channel: ColorSliderChannel): ColorChannelRange {\n switch (channel) {\n case 'hue':\n return { minValue: 0, maxValue: 360, step: 1, pageStep: 15 };\n case 'H':\n case 'oklchHue':\n return { minValue: 0, maxValue: 360, step: 1, pageStep: 15 };\n case 'saturation':\n case 'brightness':\n case 'lightness':\n return { minValue: 0, maxValue: 100, step: 1, pageStep: 10 };\n case 'red':\n case 'green':\n case 'blue':\n return { minValue: 0, maxValue: 255, step: 1, pageStep: 17 };\n case 'alpha':\n return { minValue: 0, maxValue: 1, step: 0.01, pageStep: 0.1 };\n case 'L':\n case 'oklchLightness':\n return { minValue: 0, maxValue: 1, step: 0.01, pageStep: 0.1 };\n case 'C':\n case 'oklchChroma':\n return { minValue: 0, maxValue: 0.4, step: 0.01, pageStep: 0.05 };\n default:\n return { minValue: 0, maxValue: 100, step: 1, pageStep: 10 };\n }\n}\n\nexport function getInputChannelRange(channel: ColorInputChannel): ColorChannelRange {\n if (channel === 'hex') {\n return { minValue: 0, maxValue: 0xffffff, step: 1, pageStep: 1 };\n }\n return getChannelRange(channel as ColorSliderChannel);\n}\n\nexport function getChannelGradient(color: ColorInstance, channel: ColorSliderChannel, orientation: 'horizontal' | 'vertical' = 'horizontal'): string {\n const range = getChannelRange(channel);\n const direction = orientation === 'horizontal' ? 'right' : 'top';\n\n switch (channel) {\n case 'hue':\n case 'H':\n case 'oklchHue':\n return `linear-gradient(to ${direction}, rgb(255, 0, 0) 0%, rgb(255, 255, 0) 17%, rgb(0, 255, 0) 33%, rgb(0, 255, 255) 50%, rgb(0, 0, 255) 67%, rgb(255, 0, 255) 83%, rgb(255, 0, 0) 100%)`;\n\n case 'lightness':\n case 'L':\n case 'oklchLightness': {\n const start = color.setChannelValue(channel as ColorChannel, range.minValue).toRgbString();\n const middle = color.setChannelValue(channel as ColorChannel, (range.maxValue - range.minValue) / 2).toRgbString();\n const end = color.setChannelValue(channel as ColorChannel, range.maxValue).toRgbString();\n return `linear-gradient(to ${direction}, ${start}, ${middle}, ${end})`;\n }\n\n case 'alpha': {\n const start = color.setChannelValue('alpha', range.minValue).toRgbString();\n const end = color.setChannelValue('alpha', range.maxValue).toRgbString();\n return `linear-gradient(to ${direction}, ${start}, ${end})`;\n }\n\n default: {\n const start = color.setChannelValue(channel as ColorChannel, range.minValue).toRgbString();\n const end = color.setChannelValue(channel as ColorChannel, range.maxValue).toRgbString();\n return `linear-gradient(to ${direction}, ${start}, ${end})`;\n }\n }\n}\n\nconst channelGenerators: Record<string, (color: ColorInstance) => string> = {\n hue: (color) => [0, 60, 120, 180, 240, 300, 360].map((h) => color.setChannelValue('hue', h).toRgbString()).join(', '),\n saturation: (color) => `${color.setChannelValue('saturation', 0).toRgbString()}, transparent`,\n lightness: () => 'black, transparent, white',\n brightness: () => 'black, transparent'\n};\n\nexport function getAreaGradient(color: ColorInstance, xChannel: ColorSliderChannel, yChannel: ColorSliderChannel, format: ColorSpace): string {\n const axes3d = getDefault3DAxes(format);\n const zChannel = axes3d.zChannel;\n const zValue = color.getChannelValue(zChannel as ColorChannel);\n\n const isHSL = format === 'hsla';\n\n let base: ColorInstance;\n if (isHSL) {\n base = (parseColor('hsl(0, 100%, 50%)') as ColorInstance).setChannelValue(zChannel as ColorChannel, zValue);\n } else {\n base = (parseColor('hsb(0, 100%, 100%)') as ColorInstance).setChannelValue(zChannel as ColorChannel, zValue);\n }\n\n const channels = [xChannel, yChannel];\n const direction = (c: string) => (c === xChannel ? 'right' : 'top');\n\n const layers = channels\n .map((c) => {\n const gen = channelGenerators[c];\n if (!gen) return null;\n return `linear-gradient(to ${direction(c)}, ${gen(base)})`;\n })\n .filter(Boolean)\n .reverse() as string[];\n\n const isHueZ = zChannel === 'hue' || zChannel === 'H' || zChannel === 'oklchHue';\n if (isHueZ) {\n layers.push(base.toRgbString());\n }\n\n return layers.join(', ');\n}\n\nexport function getChannelColor(color: ColorInstance, channel: ColorChannel): ColorInstance {\n switch (channel) {\n case 'hue':\n return parseColor(`hsl(${color.getChannelValue('hue')}, 100%, 50%)`)!;\n case 'red':\n case 'green':\n case 'blue':\n case 'lightness':\n case 'brightness':\n case 'saturation':\n return color.setChannelValue('alpha', 1);\n case 'alpha':\n return color;\n default:\n return color.setChannelValue('alpha', 1);\n }\n}\n\nfunction toCssString(color: ColorInstance, format: ColorSpace): string {\n const a = round(color.alpha, 2);\n switch (format) {\n case 'rgba':\n case 'hexa': {\n const rgb = color.toRGB();\n return `rgba(${Math.round(rgb.red)}, ${Math.round(rgb.green)}, ${Math.round(rgb.blue)}, ${a})`;\n }\n case 'hsla': {\n const hsl = color.toHSL();\n return `hsla(${Math.round(hsl.hue) % 360}, ${hsl.saturation.toFixed(2)}%, ${hsl.lightness.toFixed(2)}%, ${a})`;\n }\n case 'hsba': {\n // CSS doesn't support hsb, convert to hsl\n const hsl = color.toHSL();\n return `hsla(${Math.round(hsl.hue) % 360}, ${hsl.saturation.toFixed(2)}%, ${hsl.lightness.toFixed(2)}%, ${a})`;\n }\n case 'oklch': {\n return color.toOKLCH().toString('css');\n }\n default:\n return color.toOKLCH().toString('css');\n }\n}\n\nexport function getInputChannelValue(color: ColorInstance, channel: ColorInputChannel, format: ColorSpace = 'hsba'): string {\n if (channel === 'hex') {\n const alphaRange = getChannelRange('alpha');\n if (color.alpha < alphaRange.maxValue) {\n return color.toHexa();\n }\n return color.toHex();\n }\n if (channel === 'css') {\n return toCssString(color, format);\n }\n const value = color.getChannelValue(channel);\n if (Number.isNaN(value)) return 'NaN';\n const range = getChannelRange(channel as ColorSliderChannel);\n if (channel === 'H' || channel === 'oklchHue') {\n const rounded = round(value, 2);\n return rounded.toString();\n }\n if (channel === 'hue') {\n const rounded = Math.round(value);\n return rounded.toString();\n }\n if (range.step >= 1) {\n return Math.round(value).toString();\n }\n return round(value, 4).toString();\n}\n\nexport function getDefault2DAxes(format: ColorSpace): Color2DAxes {\n switch (format) {\n case 'hsla':\n return { xChannel: 'saturation', yChannel: 'lightness' };\n case 'hsba':\n case 'rgba':\n case 'oklch':\n default:\n return { xChannel: 'saturation', yChannel: 'brightness' };\n }\n}\n\nexport function getDefault3DAxes(format: ColorSpace): Color3DAxes {\n switch (format) {\n case 'hsla':\n return { xChannel: 'saturation', yChannel: 'lightness', zChannel: 'hue' };\n case 'hsba':\n case 'rgba':\n case 'oklch':\n default:\n return { xChannel: 'saturation', yChannel: 'brightness', zChannel: 'hue' };\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport type { InputColor } from './inputcolor';\nimport type { InputColorSlider } from './inputcolor-slider';\n\nexport const INPUT_COLOR_INSTANCE = new InjectionToken<InputColor>('INPUT_COLOR_INSTANCE');\nexport const INPUT_COLOR_SLIDER_INSTANCE = new InjectionToken<InputColorSlider>('INPUT_COLOR_SLIDER_INSTANCE');\n","import { ChangeDetectionStrategy, Component, computed, inject, OnDestroy, ViewEncapsulation } from '@angular/core';\nimport { BaseComponent, PARENT_INSTANCE } from 'primeng/basecomponent';\nimport { Bind } from 'primeng/bind';\nimport { ColorChannel, getAreaGradient, getChannelRange, snapValue } from './color-manager';\nimport { INPUT_COLOR_INSTANCE } from './inputcolor.token';\nimport type { InputColorAreaPassThrough } from 'primeng/types/inputcolor';\n\n/**\n * InputColorArea is a helper component for InputColor component.\n * @group Components\n */\n@Component({\n selector: 'p-inputcolor-area',\n standalone: true,\n template: `<ng-content></ng-content>`,\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': '$pc.cx(\"area\")',\n '[style.--px-area-gradient]': '$areaGradient()',\n '[style.--px-handle-background]': '$handleBackground()',\n '[style.--px-handle-position-left]': '$handleLeft()',\n '[style.--px-handle-position-top]': '$handleTop()',\n '(pointerdown)': 'onPointerDown($event)'\n },\n providers: [{ provide: PARENT_INSTANCE, useExisting: InputColorArea }],\n hostDirectives: [Bind]\n})\nexport class InputColorArea extends BaseComponent<InputColorAreaPassThrough> implements OnDestroy {\n componentName = 'InputColorArea';\n\n bindDirectiveInstance = inject(Bind, { self: true });\n\n $pc = inject(INPUT_COLOR_INSTANCE);\n\n $xChannel = computed(() => this.$pc.$axes().xChannel);\n $yChannel = computed(() => this.$pc.$axes().yChannel);\n\n $areaGradient = computed(() => {\n const color = this.$pc.$color();\n const xCh = this.$xChannel();\n const yCh = this.$yChannel();\n const format = this.$pc.format();\n return getAreaGradient(color, xCh, yCh, format);\n });\n\n $handleBackground = computed(() => this.$pc.$color().setChannelValue('alpha', 1).toRgbString());\n\n $handleLeft = computed(() => {\n const xCh = this.$xChannel();\n const range = getChannelRange(xCh);\n const value = this.$pc.$color().getChannelValue(xCh as ColorChannel);\n const pct = ((value - range.minValue) / (range.maxValue - range.minValue)) * 100;\n return `${pct}%`;\n });\n\n $handleTop = computed(() => {\n const yCh = this.$yChannel();\n const range = getChannelRange(yCh);\n const value = this.$pc.$color().getChannelValue(yCh as ColorChannel);\n const pct = (1 - (value - range.minValue) / (range.maxValue - range.minValue)) * 100;\n return `${pct}%`;\n });\n\n private dragging = false;\n private dragOffset = { x: 0, y: 0 };\n private cleanupDrag: (() => void) | null = null;\n\n onAfterViewChecked() {\n this.bindDirectiveInstance.setAttrs(this.ptms(['host', 'root']));\n }\n\n onPointerDown(event: PointerEvent) {\n if (this.$pc.$disabled()) return;\n event.preventDefault();\n this.dragging = true;\n\n const el = this.el.nativeElement as HTMLElement;\n el.setPointerCapture(event.pointerId);\n\n const handle = (event.target as HTMLElement)?.closest('[data-pc-section=\"areahandle\"], .p-inputcolor-area-handle') as HTMLElement | null;\n if (handle) {\n const handleRect = handle.getBoundingClientRect();\n this.dragOffset = {\n x: event.clientX - (handleRect.left + handleRect.width / 2),\n y: event.clientY - (handleRect.top + handleRect.height / 2)\n };\n } else {\n this.dragOffset = { x: 0, y: 0 };\n }\n\n this.updateFromPointer(event);\n\n const onMove = (e: PointerEvent) => {\n if (!this.dragging) return;\n e.preventDefault();\n this.updateFromPointer(e);\n };\n\n const cleanup = () => {\n this.dragging = false;\n this.cleanupDrag = null;\n el.removeEventListener('pointermove', onMove);\n el.removeEventListener('pointerup', onUp);\n el.removeEventListener('pointercancel', onCancel);\n el.removeEventListener('lostpointercapture', onLostCapture);\n };\n\n this.cleanupDrag = cleanup;\n\n const onUp = (e: PointerEvent) => {\n if (!this.dragging) return;\n this.updateFromPointer(e, true);\n this.dragOffset = { x: 0, y: 0 };\n cleanup();\n };\n\n const onCancel = () => {\n cleanup();\n };\n\n const onLostCapture = () => {\n cleanup();\n };\n\n el.addEventListener('pointermove', onMove);\n el.addEventListener('pointerup', onUp);\n el.addEventListener('pointercancel', onCancel);\n el.addEventListener('lostpointercapture', onLostCapture);\n }\n\n ngOnDestroy() {\n this.cleanupDrag?.();\n }\n\n private updateFromPointer(event: PointerEvent, isEnd: boolean = false) {\n const el = this.el.nativeElement as HTMLElement;\n const rect = el.getBoundingClientRect();\n\n const xPercent = Math.max(0, Math.min(1, (event.clientX - this.dragOffset.x - rect.left) / rect.width));\n const yPercent = Math.max(0, Math.min(1, (event.clientY - this.dragOffset.y - rect.top) / rect.height));\n\n const xCh = this.$xChannel();\n const yCh = this.$yChannel();\n const xRange = getChannelRange(xCh);\n const yRange = getChannelRange(yCh);\n\n const xValue = snapValue(xRange.minValue + xPercent * (xRange.maxValue - xRange.minValue), xRange.minValue, xRange.maxValue, xRange.step);\n const yValue = snapValue(yRange.maxValue - yPercent * (yRange.maxValue - yRange.minValue), yRange.minValue, yRange.maxValue, yRange.step);\n\n const nativeColor = this.$pc.toChannelNativeFormat(this.$pc.$color(), xCh as ColorChannel);\n let color = nativeColor.setChannelValue(xCh as ColorChannel, xValue);\n color = color.setChannelValue(yCh as ColorChannel, yValue);\n\n this.$pc.updateColor(color, event, isEnd);\n }\n}\n","import { ChangeDetectionStrategy, Component, inject, ViewEncapsulation } from '@angular/core';\nimport { BaseComponent, PARENT_INSTANCE } from 'primeng/basecomponent';\nimport { Bind } from 'primeng/bind';\nimport { INPUT_COLOR_INSTANCE } from './inputcolor.token';\nimport type { InputColorAreaBackgroundPassThrough } from 'primeng/types/inputcolor';\n\n/**\n * InputColorAreaBackground is a helper component for InputColor component.\n * @group Components\n */\n@Component({\n selector: 'p-inputcolor-area-background',\n standalone: true,\n template: `<ng-content></ng-content>`,\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': '$pc.cx(\"areaBackground\")'\n },\n providers: [{ provide: PARENT_INSTANCE, useExisting: InputColorAreaBackground }],\n