All files / packages/adapters/home-assistant/src color.ts

89.47% Statements 17/19
87.5% Branches 14/16
100% Functions 2/2
89.47% Lines 17/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64                  3x 3x                   2523x 1x     2522x 2522x 4x       4x     4x             2518x 2518x 2512x 2512x     2512x     6x                     2509x 2509x    
// SPDX-License-Identifier: MIT
/**
 * Color helpers for the Home Assistant adapter.
 *
 * Home Assistant expects some frontend variables (the `rgb-*` family) to be a
 * bare `"R, G, B"` triplet rather than a `rgb()`/hex value, because it composes
 * them into `rgba(var(--rgb-primary-color), 0.5)` style expressions internally.
 */
 
const SHORT_HEX = /^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/;
const LONG_HEX = /^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/;
 
/**
 * Parse a `#rgb` or `#rrggbb` hex string into its red/green/blue components.
 *
 * @param hex - Hex color string, with a leading `#`.
 * @returns The `[r, g, b]` channel values (0-255).
 * @throws If the input is not a valid 3- or 6-digit hex color.
 */
export function hexToRgb(hex: string): [number, number, number] {
  if (typeof hex !== 'string') {
    throw new TypeError(`Invalid hex color: expected string, received ${typeof hex}`);
  }
 
  const shortMatch = SHORT_HEX.exec(hex);
  if (shortMatch) {
    const [, r, g, b] = shortMatch;
    // Capture groups type as `string | undefined`, and interpolating an
    // `undefined` here would yield `parseInt('undefinedundefined')` — a silent
    // NaN channel rather than a rejected colour. Take the throw path instead.
    Iif (r === undefined || g === undefined || b === undefined) {
      throw new Error(`Invalid hex color: "${hex}"`);
    }
    return [
      Number.parseInt(`${r}${r}`, 16),
      Number.parseInt(`${g}${g}`, 16),
      Number.parseInt(`${b}${b}`, 16),
    ];
  }
 
  const longMatch = LONG_HEX.exec(hex);
  if (longMatch) {
    const [, r, g, b] = longMatch;
    Iif (r === undefined || g === undefined || b === undefined) {
      throw new Error(`Invalid hex color: "${hex}"`);
    }
    return [Number.parseInt(r, 16), Number.parseInt(g, 16), Number.parseInt(b, 16)];
  }
 
  throw new Error(`Invalid hex color: "${hex}"`);
}
 
/**
 * Convert a hex color to a bare `"R, G, B"` triplet string (no `rgb()` wrapper).
 *
 * @param hex - Hex color string, with a leading `#`.
 * @returns The comma-separated triplet, e.g. `"137, 180, 250"`.
 * @throws If the input is not a valid 3- or 6-digit hex color.
 */
export function hexToRgbTriplet(hex: string): string {
  const [r, g, b] = hexToRgb(hex);
  return `${r}, ${g}, ${b}`;
}