import { validHex } from './utils' ; export * from './utils' ; export * from './getContrastingColor' ; const RGB_MAX = 255 ; const HUE_MAX = 360 ; const SV_MAX = 100 ; export type ObjectColor = RgbColor | HslColor | HsvColor | RgbaColor | HslaColor | HsvaColor ; export type ColorResult = { rgb : RgbColor ; hsl : HslColor ; hsv : HsvColor ; rgba : RgbaColor ; hsla : HslaColor ; hsva : HsvaColor ; hex : string ; hexa : string ; } ; export interface HsvColor { h : number ; s : number ; v : number ; } export interface HsvaColor extends HsvColor { a : number ; } export interface RgbColor { r : number ; g : number ; b : number ; } export interface RgbaColor extends RgbColor { a : number ; } /**
 * ```js
 * rgbaToHsva({ r: 255, g: 255, b: 255, a: 1 }) //=> { h: 0, s: 0, v: 100, a: 1 }
 * ```
 */ export const rgbaToHsva = ( { r , g , b , a } ) : HsvaColor => { const max = Math . max ( r , g , b ) ; const delta = max - Math . min ( r , g , b ) ; const hh = delta ? max === r ? ( g - b ) / delta : max === g ? 2 + ( b - r ) / delta : 4 + ( r - g ) / delta : 0 ; return { h : 60 * ( hh < 0 ? hh + 6 : hh ) , s : max ? ( delta / max ) * SV_MAX : 0 , v : ( max / RGB_MAX ) * SV_MAX , a , } ; } ; export const hsvaToHslString = ( hsva ) : string => { const { h , s , l } = hsvaToHsla ( hsva ) ; return `hsl(${ h }, ${ Math . round ( s ) }%, ${ Math . round ( l ) }%)` ; } ; export const hsvaToHsvString = ( { h , s , v } ) : string => { return `hsv(${ h }, ${ s }%, ${ v }%)` ; } ; export const hsvaToHsvaString = ( { h , s , v , a } ) : string => { return `hsva(${ h }, ${ s }%, ${ v }%, ${ a })` ; } ; export const hsvaToHslaString = ( hsva ) : string => { const { h , s , l , a } = hsvaToHsla ( hsva ) ; return `hsla(${ h }, ${ s }%, ${ l }%, ${ a })` ; } ; export const hslStringToHsla = ( str ) : HslaColor => { const [ h , s , l , a ] = ( str . match ( /\d+/g ) || [ ] ) . map ( Number ) ; return { h , s , l , a } ; } ; export const hslaStringToHsva = ( hslString ) : HsvaColor => { const matcher = /hsla?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i ; const match = matcher . exec ( hslString ) ; if ( ! match ) return { h : 0 , s : 0 , v : 0 , a : 1 } ; return hslaToHsva ( { h : parseHue ( match [ 1 ] , match [ 2 ] ) , s : Number ( match [ 3 ] ) , l : Number ( match [ 4 ] ) , a : match [ 5 ] === undefined ? 1 : Number ( match [ 5 ] ) / ( match [ 6 ] ? 100 : 1 ) , } ) ; } ; export const hslStringToHsva = hslaStringToHsva ; export const hslaToHsva = ( { h , s , l , a } ) : HsvaColor => { s *= ( l < 50 ? l : SV_MAX - l ) / SV_MAX ; return { h : h , s : s > 0 ? ( ( 2 * s ) / ( l + s ) ) * SV_MAX : 0 , v : l + s , a , } ; } ; export interface HslColor { h : number ; s : number ; l : number ; } export interface HslaColor extends HslColor { a : number ; } export const hsvaToHsla = ( { h , s , v , a } ) : HslaColor => { const hh = ( ( 200 - s ) * v ) / SV_MAX ; return { h , s : hh > 0 && hh < 200 ? ( ( s * v ) / SV_MAX / ( hh <= SV_MAX ? hh : 200 - hh ) ) * SV_MAX : 0 , l : hh / 2 , a , } ; } ; export const hsvaStringToHsva = ( hsvString ) : HsvaColor => { const matcher = /hsva?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i ; const match = matcher . exec ( hsvString ) ; if ( ! match ) return { h : 0 , s : 0 , v : 0 , a : 1 } ; return { h : parseHue ( match [ 1 ] , match [ 2 ] ) , s : Number ( match [ 3 ] ) , v : Number ( match [ 4 ] ) , a : match [ 5 ] === undefined ? 1 : Number ( match [ 5 ] ) / ( match [ 6 ] ? SV_MAX : 1 ) , } ; } ; /**
 * Valid CSS <angle> units.
 * https://developer.mozilla.org/en-US/docs/Web/CSS/angle
 */ const angleUnits = { grad : HUE_MAX / 400 , turn : HUE_MAX , rad : HUE_MAX / ( Math . PI * 2 ) , } ; export const parseHue = ( value , unit = 'deg' ) : number => { return Number ( value ) * ( angleUnits [ unit ] || 1 ) ; } ; export const hsvStringToHsva = hsvaStringToHsva ; export const rgbaStringToHsva = ( rgbaString ) : HsvaColor => { const matcher = /rgba?\(?\s*(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i ; const match = matcher . exec ( rgbaString ) ; if ( ! match ) return { h : 0 , s : 0 , v : 0 , a : 1 } ; return rgbaToHsva ( { r : Number ( match [ 1 ] ) / ( match [ 2 ] ? SV_MAX / RGB_MAX : 1 ) , g : Number ( match [ 3 ] ) / ( match [ 4 ] ? SV_MAX / RGB_MAX : 1 ) , b : Number ( match [ 5 ] ) / ( match [ 6 ] ? SV_MAX / RGB_MAX : 1 ) , a : match [ 7 ] === undefined ? 1 : Number ( match [ 7 ] ) / ( match [ 8 ] ? SV_MAX : 1 ) , } ) ; } ; export const rgbStringToHsva = rgbaStringToHsva ; /** Converts an RGBA color plus alpha transparency to hex */ export const rgbaToHex = ( { r , g , b } ) : string => { let bin = ( r << 16 ) | ( g << 8 ) | b ; return `#${ ( ( h ) => new Array ( 7 - h . length ) . join ( '0' ) + h ) ( bin . toString ( 16 ) ) }` ; } ; export const rgbaToHexa = ( { r , g , b , a } ) : string => { const alpha = typeof a === 'number' && ( ( a * 255 ) | ( 1 << 8 ) ) . toString ( 16 ) . slice ( 1 ) ; return `${ rgbaToHex ( { r , g , b , a } ) }${ alpha ? alpha : '' }` ; } ; export const hexToHsva = ( hex ) : HsvaColor => rgbaToHsva ( hexToRgba ( hex ) ) ; export const hexToRgba = ( hex ) : RgbaColor => { const htemp = hex . replace ( '#' , '' ) ; if ( /^#?/ . test ( hex ) && htemp . length === 3 ) { hex = `#${ htemp . charAt ( 0 ) }${ htemp . charAt ( 0 ) }${ htemp . charAt ( 1 ) }${ htemp . charAt ( 1 ) }${ htemp . charAt ( 2 ) }${ htemp . charAt ( 2 ) }` ; } const reg = new RegExp ( `[A-Za-z0-9]{2}` , 'g' ) ; const [ r , g , b = 0 , a ] = hex . match ( reg ) ! . map ( ( v ) => parseInt ( v , 16 ) ) ; return { r , g , b , a : a ? a / RGB_MAX : 1 , } ; } ; param color export const hsvaToRgba = ( { h , s , v , a } ) : RgbaColor => { let _h = h / 60 , _s = s / SV_MAX , _v = v / SV_MAX , hi = Math . floor ( _h ) % 6 ; let f = _h - Math . floor ( _h ) , _p = RGB_MAX * _v * ( 1 - _s ) , _q = RGB_MAX * _v * ( 1 - _s * f ) , _t = RGB_MAX * _v * ( 1 - _s * ( 1 - f ) ) ; _v *= RGB_MAX ; const rgba = { } as RgbaColor ; switch ( hi ) { case 0 : rgba . r = _v ; rgba . g = _t ; rgba . b = _p ; break ; case 1 : rgba . r = _q ; rgba . g = _v ; rgba . b = _p ; break ; case 2 : rgba . r = _p ; rgba . g = _v ; rgba . b = _t ; break ; case 3 : rgba . r = _p ; rgba . g = _q ; rgba . b = _v ; break ; case 4 : rgba . r = _t ; rgba . g = _p ; rgba . b = _v ; break ; case 5 : rgba . r = _v ; rgba . g = _p ; rgba . b = _q ; break ; } rgba . r = Math . round ( rgba . r ) ; rgba . g = Math . round ( rgba . g ) ; rgba . b = Math . round ( rgba . b ) ; return { ... rgba , a } ; } ; export const hsvaToRgbString = ( hsva ) : string => { const { r , g , b } = hsvaToRgba ( hsva ) ; return `rgb(${ r }, ${ g }, ${ b })` ; } ; export const hsvaToRgbaString = ( hsva ) : string => { const { r , g , b , a } = hsvaToRgba ( hsva ) ; return `rgba(${ r }, ${ g }, ${ b }, ${ a })` ; } ; export const rgbaToRgb = ( { r , g , b } ) : RgbColor => ( { r , g , b } ) ; export const hslaToHsl = ( { h , s , l } ) : HslColor => ( { h , s , l } ) ; export const hsvaToHex = ( hsva ) : string => rgbaToHex ( hsvaToRgba ( hsva ) ) ; export const hsvaToHexa = ( hsva ) : string => rgbaToHexa ( hsvaToRgba ( hsva ) ) ; export const hsvaToHsv = ( { h , s , v } ) : HsvColor => ( { h , s , v } ) ; export const color = ( str ) : ColorResult => { let rgb ! ; let hsl ! ; let hsv ! ; let rgba ! ; let hsla ! ; let hsva ! ; let hex ! ; let hexa ! ; if ( typeof str === 'string' && validHex ( str ) ) { hsva = hexToHsva ( str ) ; hex = str ; } else if ( typeof str !== 'string' ) { hsva = str ; } if ( hsva ) { hsv = hsvaToHsv ( hsva ) ; hsla = hsvaToHsla ( hsva ) ; rgba = hsvaToRgba ( hsva ) ; hexa = rgbaToHexa ( rgba ) ; hex = hsvaToHex ( hsva ) ; hsl = hslaToHsl ( hsla ) ; rgb = rgbaToRgb ( rgba ) ; } return { rgb , hsl , hsv , rgba , hsla , hsva , hex , hexa } ; } ;