import { useCallback, useLayoutEffect, useState } from 'react'; import tinycolor, { ColorFormats } from 'tinycolor2'; import { useSilkeCSSContext } from '../silke-css-number-field'; import { getColorVariableValue, isColorVariable, rgbToHsv } from './color'; export type HueType = number | string | ColorFormats.RGBA | ColorFormats.HSV; function stringToRgb(value: string, variables: { [key: string]: string }): ColorFormats.RGBA { if (!value) { return { r: 0, b: 0, g: 0, a: 0 }; } if (isColorVariable(value)) { return tinycolor(getColorVariableValue(value, variables)).toRgb(); } return tinycolor(value).toRgb(); } function isRgb(value: HueType): value is ColorFormats.RGBA { return typeof value === 'object' && 'r' in value; } export function convertToHue( value: HueType, prevHue: ColorFormats.HSV | undefined, variables: { [key: string]: string }, ): ColorFormats.HSV { if (typeof value === 'number') return { h: value, s: 0, v: 0 }; if (typeof value === 'string') value = stringToRgb(value, variables); if (isRgb(value)) return rgbToHsv(prevHue || { h: 0 }, value); return value; } export function convertFromHue(ogValue: T, value: ColorFormats.HSV): T { if (typeof ogValue === 'number') return value.h as T; if (typeof ogValue === 'string') return tinycolor(value).toRgbString() as T; if (isRgb(ogValue)) return tinycolor(value).toRgb() as T; return value as T; } function getValueList(value?: T | T[]): T[] { return (!value ? [0] : Array.isArray(value) ? value : [value]) as T[]; } export function useHue( value?: U, ): [hue: number[], setHue: (hue: number[]) => U] { const { variables } = useSilkeCSSContext(); const [hsv, setHSV] = useState(() => getValueList(value).map((v) => convertToHue(v, undefined, variables)), ); useLayoutEffect(() => { const valueList = (!value ? [0] : Array.isArray(value) ? value : [value]) as T[]; setHSV((hsv) => valueList.map((v, i) => convertToHue(v, hsv[i], variables))); }, [value, variables]); const handleChange = useCallback( (hue: number[]) => { const newHSV = hsv.map((h, i) => ({ ...h, h: hue[i] })); setHSV(newHSV); const newValueList = getValueList(value).map((v, i) => convertFromHue(v, newHSV[i])); if (Array.isArray(value)) return newValueList as U; return newValueList[0] as U; }, [value, hsv], ); return [hsv.map((h) => h.h), handleChange]; }