1+ import { Color , HSL } from "three" ;
2+ import { InteractiveLayoutElement } from "../layout/InteractiveLayoutElement" ;
3+ import { IOverlayRenderer } from "../layout/IOverlayRenderer" ;
4+ import { Column , Layout , Row } from "../layout/Layout" ;
5+ import { LayoutElement } from "../layout/LayoutElement" ;
6+ import { Node } from "../nodes/Node" ;
7+ import { ColorWheel } from "./ColorWheel" ;
8+ import { HeaderElement } from "./Header" ;
9+ import { ColorSwatch } from "./ColorSwatch" ;
10+ import { DraggableValue } from "./DraggableValue" ;
11+ import { Theme } from "../colors/Theme" ;
12+ import { TextLabel } from "./TextLabel" ;
13+ import { Button } from "./Button" ;
14+
15+ export class ColorPicker extends InteractiveLayoutElement implements IOverlayRenderer {
16+
17+ private myTransform ! :DOMMatrix ;
18+ protected overlay :Layout ;
19+ protected _color = new Color ( ) ;
20+
21+ protected _opacity = 1 ;
22+ protected _saturation = 1 ;
23+
24+ protected colorWheel :ColorWheel ;
25+ protected colorSwatch :ColorSwatch ;
26+
27+ protected alphaSlider :DraggableValue ;
28+ protected RGBSliders :DraggableValue [ ] ;
29+ protected HSLSliders :DraggableValue [ ] ;
30+ protected hexBtn :Button ;
31+ private _emitInterval :number = 0 ;
32+
33+ private hsl :HSL = { h :0 , s :0 , l :0 } ;
34+
35+ constructor ( private onColorChange ?:( color :Color , opacity :number ) => void ) {
36+ super ( ) ;
37+ this . backgroundColor = "black" ;
38+
39+ this . colorWheel = new ColorWheel ( this . onColorWheel . bind ( this ) ) ;
40+ this . colorSwatch = new ColorSwatch ( ) ;
41+ this . alphaSlider = new DraggableValue ( "Alpha" , true , 0 , 1 , 0.01 , this . onAlphaSlider . bind ( this ) ) ;
42+
43+ this . RGBSliders = [ "R" , "G" , "B" ] . map ( ( name , i ) => new DraggableValue ( name , true , 0.001 , 1 , 0.01 , this . onRGBSlider . bind ( this , i ) ) )
44+ this . HSLSliders = [ "H" , "S" , "L" ] . map ( ( name , i ) => new DraggableValue ( name , true , 0.001 , 1 , 0.01 , this . onHSLSlider . bind ( this , i ) ) )
45+
46+ this . hexBtn = new Button ( "..." , this . manuallySetHex . bind ( this ) ) ;
47+
48+ this . overlay = new Column ( [
49+
50+ new Row ( [
51+
52+ new Column ( [
53+ this . colorSwatch ,
54+ this . colorWheel
55+ ] , {
56+ align :"stretch"
57+ } ) ,
58+
59+ new Column ( [
60+ this . alphaSlider ,
61+ ...this . RGBSliders ,
62+ ...this . HSLSliders
63+ ] , {
64+ justify :"space-around" ,
65+ align :"stretch" ,
66+ width : 100 ,
67+ gap :1 ,
68+ lineHeight : Theme . config . nodeRowHeight * 0.7 ,
69+ } )
70+
71+
72+ ] , {
73+ align :"stretch"
74+ } ) ,
75+
76+ this . hexBtn
77+ ] ) ;
78+
79+ this . overlay . boxShadowLevel = 8
80+ this . overlay . backgroundColor = "#111"
81+ this . overlay . parent = this ;
82+ this . overlay . xPadding = 2
83+ //
84+ // initial values...
85+ //
86+ this . opacity = 1 ;
87+ this . saturation = 1 ;
88+ this . color = new Color ( "black" ) ;
89+ }
90+
91+ get opacity ( ) { return this . _opacity ; }
92+ set opacity ( newOpacity :number )
93+ {
94+ this . _opacity = newOpacity ;
95+ this . colorSwatch . opacity = newOpacity ;
96+ this . alphaSlider . value = newOpacity ;
97+ this . emit ( ) ;
98+ }
99+
100+ get saturation ( ) { return this . _saturation ; }
101+ set saturation ( newSaturation :number )
102+ {
103+ this . _saturation = newSaturation ;
104+ this . colorWheel . saturation = newSaturation ;
105+ this . HSLSliders [ 1 ] . value = newSaturation ;
106+ this . emit ( ) ;
107+ }
108+
109+ get color ( ) { return this . _color }
110+ protected set color ( newColor :Color )
111+ {
112+ const c = this . _color ;
113+
114+ c . copy ( newColor ) ;
115+ c . getHSL ( this . hsl ) ;
116+
117+ this . hsl . s = this . _saturation ;
118+ c . setHSL ( this . hsl . h , this . hsl . s , this . hsl . l ) ;
119+
120+ this . _color = c ;
121+
122+ this . colorWheel . color = this . _color ;
123+ this . colorSwatch . color = this . _color ;
124+
125+ const rgb = [ c . r , c . g , c . b ] ;
126+ this . RGBSliders . forEach ( ( slider , i ) => slider . value = rgb [ i ] ) ;
127+
128+ const hsl :number [ ] = Object . values ( c . getHSL ( this . hsl ) ) ;
129+
130+ this . HSLSliders . forEach ( ( slider , i ) => slider . value = hsl [ i ] )
131+
132+ this . hexBtn . label = this . _color . getHexString ( ) ;
133+ this . emit ( ) ;
134+ }
135+
136+ protected override renderContents ( ctx : CanvasRenderingContext2D , maxWidth : number , maxHeight : number ) : void {
137+ this . myTransform = ctx . getTransform ( ) ;
138+ this . colorSwatch . render ( ctx , maxWidth , maxHeight ) ;
139+ }
140+
141+ renderOverlay ( ctx : CanvasRenderingContext2D ) : void {
142+ ctx . setTransform ( this . myTransform ) ;
143+ this . overlay . render ( ctx , this . overlay . width ( ctx ) , this . overlay . height ( ctx ) ) ;
144+ }
145+
146+ get overlayBody ( ) : LayoutElement {
147+ return this . overlay ;
148+ }
149+
150+ override width ( ctx : CanvasRenderingContext2D ) : number {
151+ return 50
152+ }
153+
154+ override onMouseDown ( cursorX : number , cursorY : number ) : void {
155+
156+ ( this . root as Node ) . editor . overlay = this ;
157+ }
158+
159+ protected onColorWheel ( color :Color ) {
160+
161+ this . color = color ;
162+ }
163+
164+ protected onAlphaSlider ( newAlpha :number ) {
165+ this . opacity = newAlpha ;
166+ }
167+
168+ protected onRGBSlider ( i :number , newValue :number ) {
169+
170+ const c = this . _color ;
171+ const rgb = [ c . r , c . g , c . b ] ;
172+
173+ rgb [ i ] = newValue ;
174+
175+ this . color = c . clone ( ) . setRGB ( rgb [ 0 ] , rgb [ 1 ] , rgb [ 2 ] ) ;
176+ }
177+
178+ protected onHSLSlider ( i :number , newValue :number ) {
179+
180+ const c = this . _color ;
181+ const hsl :number [ ] = Object . values ( c . getHSL ( this . hsl ) ) ;
182+
183+ hsl [ i ] = newValue ;
184+
185+ if ( i == 1 ) {
186+ this . saturation = newValue ;
187+ }
188+
189+ this . color = c . clone ( ) . setHSL ( hsl [ 0 ] , this . _saturation , hsl [ 2 ] ) ;
190+ }
191+
192+ protected manuallySetHex ( ) {
193+ const val = prompt ( "Type the HEX value..." , this . _color . getHexString ( ) ) ;
194+ if ( val )
195+ {
196+ this . color = this . _color . clone ( ) . setStyle ( "#" + val ) ;
197+ }
198+ }
199+
200+ private emit ( ) {
201+ clearInterval ( this . _emitInterval ) ;
202+ this . _emitInterval = setTimeout ( ( ) => {
203+ this . onColorChange ?.( this . color , this . opacity ) ;
204+ } , 0 ) ;
205+ }
206+ }
0 commit comments