import { equalColorObjects } from '../utils/compare.js' ; import { fire , tpl } from '../utils/dom.js' ; import type { AnyColor , ColorModel , HsvaColor } from '../types' ; import { Hue } from './hue.js' ; import { Saturation } from './saturation.js' ; import type { Slider } from './slider.js' ; import css from '../styles/color-picker.js' ; import hueCss from '../styles/hue.js' ; import saturationCss from '../styles/saturation.js' ; const $isSame = Symbol ( 'same' ) ; const $color = Symbol ( 'color' ) ; const $hsva = Symbol ( 'hsva' ) ; const $change = Symbol ( 'change' ) ; const $update = Symbol ( 'update' ) ; const $parts = Symbol ( 'parts' ) ; export const $css = Symbol ( 'css' ) ; export const $sliders = Symbol ( 'sliders' ) ; export type Sliders = Array < new ( root ) => Slider > ; export abstract class ColorPicker < C extends AnyColor > extends HTMLElement { static get observedAttributes ( ) : string [ ] { return [ 'color' ] ; } protected get [ $css ] ( ) : string [ ] { return [ css , hueCss , saturationCss ] ; } protected get [ $sliders ] ( ) : Sliders { return [ Saturation , Hue ] ; } protected abstract get colorModel ( ) : ColorModel < C > ; private declare [ $hsva ] : HsvaColor ; private declare [ $color ] : C ; private declare [ $parts ] : Slider [ ] ; get color ( ) : C { return this [ $color ] ; } set color ( newColor ) { if ( ! this [ $isSame ] ( newColor ) ) { const newHsva = this . colorModel . toHsva ( newColor ) ; this [ $update ] ( newHsva ) ; this [ $change ] ( newColor ) ; } } constructor ( ) { super ( ) ; const template = tpl ( `<style>${ this [ $css ] . join ( '' ) }</style>` ) ; const root = this . attachShadow ( { mode : 'open' } ) ; root . appendChild ( template . content . cloneNode ( true ) ) ; root . addEventListener ( 'move' , this ) ; this [ $parts ] = this [ $sliders ] . map ( ( slider ) => new slider ( root ) ) ; } connectedCallback ( ) { if ( this . hasOwnProperty ( 'color' ) ) { const value = this . color ; delete this [ 'color' as keyof this ] ; this . color = value ; } else if ( ! this . color ) { this . color = this . colorModel . defaultColor ; } } attributeChangedCallback ( _attr , _oldVal , newVal ) { const color = this . colorModel . fromAttr ( newVal ) ; if ( ! this [ $isSame ] ( color ) ) { this . color = color ; } } handleEvent ( event ) { const oldHsva = this [ $hsva ] ; const newHsva = { ... oldHsva , ... event . detail } ; this [ $update ] ( newHsva ) ; let newColor ; if ( ! equalColorObjects ( newHsva , oldHsva ) && ! this [ $isSame ] ( ( newColor = this . colorModel . fromHsva ( newHsva ) ) ) ) { this [ $change ] ( newColor ) ; } } private [ $isSame ] ( color ) { return this . color && this . colorModel . equal ( color , this . color ) ; } private [ $update ] ( hsva ) { this [ $hsva ] = hsva ; this [ $parts ] . forEach ( ( part ) => part . update ( hsva ) ) ; } private [ $change ] ( value ) { this [ $color ] = value ; fire ( this , 'color-changed' , { value } ) ; } }