11import React , { CSSProperties , useState } from 'react' ;
2+ import interpolate from "color-interpolate" ;
23
34interface ToggleSliderHandleProps extends CSSProperties {
45 size : number | string ,
@@ -45,6 +46,8 @@ interface ToggleSliderProps {
4546 barRendererActive ?: React . ReactNode ,
4647 flip ?: boolean ,
4748 active ?: boolean ,
49+ draggable ?: boolean ,
50+ barTransitionType ?: "fade" | "slide" ,
4851}
4952
5053function ToggleSlider ( {
@@ -72,13 +75,29 @@ function ToggleSlider({
7275 handleStyles,
7376 handleStylesActive,
7477 active : initialActive = false ,
78+ draggable = true ,
79+ barTransitionType = "fade" ,
7580 } : ToggleSliderProps ) {
7681
7782 const [ active , setActive ] = useState ( initialActive ) ;
83+ const [ progress , setProgressState ] = useState ( ( active && ! flip || ! active && flip ) ? 1 : 0 ) ;
84+ const [ currentBarColor , setCurrentBarColor ] = useState ( active ? barBackgroundColorActive : barBackgroundColor ) ;
85+ const [ dragging , setDragging ] = useState ( false ) ;
86+
87+ function interpolateColors ( start : string , end : string , pos : number ) {
88+ const colorMap = interpolate ( [ start , end ] ) ;
89+ return colorMap ( pos ) ;
90+ }
91+
92+ function setProgress ( p : number ) {
93+ const paddingLeft = Math . max ( padding , Math . min ( barWidth - padding - handleSize , p * barWidth - handleSize / 2 ) ) ;
94+ const paddingProgress = ( paddingLeft - padding ) / ( barWidth - padding * 2 - handleSize ) ;
95+ setProgressState ( p ) ;
96+
97+ if ( barTransitionType === "fade" ) {
98+ setCurrentBarColor ( interpolateColors ( barBackgroundColor , barBackgroundColorActive , paddingProgress ) ) ;
99+ }
78100
79- function onClick ( ) {
80- onToggle ( ! active ) ;
81- setActive ( ! active ) ;
82101 }
83102
84103 const overlayStyles : CSSProperties = {
@@ -90,33 +109,103 @@ function ToggleSlider({
90109 barStyles = active ? { ...barStyles , ...barStylesActive } : barStyles ;
91110 handleStyles = active ? { ...handleStyles , ...handleStylesActive } : handleStyles ;
92111
93-
94112 const bar = barRenderer ?? < ToggleSliderBar width = { barWidth } height = { barHeight }
95113 borderRadius = { barBorderRadius }
96- backgroundColor = { active ? barBackgroundColorActive : barBackgroundColor }
97- transition = { `all ${ barTransitionDuration ?? transitionDuration } ` }
114+ background = { currentBarColor }
115+ transition = { `all ${ dragging ? "0s" : ( barTransitionDuration ?? transitionDuration ) } ` }
98116 { ...barStyles } /> ;
99117 const handle = handleRenderer ?? < ToggleSliderHandle size = { handleSize }
100118 borderRadius = { handleBorderRadius }
101119 backgroundColor = { active ?
102120 ( handleBackgroundColorActive ?? handleBackgroundColor ) :
103121 handleBackgroundColor }
104- transition = { `all ${ handleTransitionDuration ?? transitionDuration } ` }
122+ transition = { `all ${ dragging ? "0s" : ( handleTransitionDuration ?? transitionDuration ) } ` }
105123 { ...handleStyles } /> ;
106124 const barActive = barRendererActive ?? bar ;
107125 const handleActive = handleRendererActive ?? handle ;
108126
127+ const [ down , setDown ] = useState ( false ) ;
128+
129+ function onPointerDown ( e : React . PointerEvent ) {
130+ setDown ( true ) ;
131+ }
132+
133+ function onPointerMove ( e : React . PointerEvent ) {
134+
135+ if ( ! down || ! draggable ) {
136+ return ;
137+ }
138+
139+ let localDragging = dragging ;
140+ if ( ! dragging ) {
141+ setDragging ( true ) ;
142+ localDragging = true ;
143+ }
144+
145+ if ( localDragging ) {
146+ const bounds = e . currentTarget . getBoundingClientRect ( ) ;
147+ const position = ( e . clientX - bounds . left ) / barWidth ;
148+ setProgress ( position ) ;
149+ }
150+
151+ }
152+
153+ function onPointerUp ( ) {
154+
155+ if ( ! down ) {
156+ return ;
157+ }
158+
159+ if ( ! dragging ) {
160+ onToggle ( ! active ) ;
161+ setProgress ( active ? 0 : 1 ) ;
162+ setActive ( ! active ) ;
163+ } else {
164+ const newActive = progress > 0.5 ;
165+ onToggle ( newActive ) ;
166+ setProgress ( newActive ? 1 : 0 ) ;
167+ setActive ( newActive ) ;
168+ }
169+ setDown ( false ) ;
170+ setDragging ( false )
171+ }
172+
109173 return (
110- < div style = { { display : "flex" , flexFlow : "row nowrap" , cursor : "pointer" , width : barWidth } } onClick = { onClick } >
111- < div style = { { paddingTop : Math . max ( handleSize - barHeight , 0 ) / 2 , transition : `all ${ transitionDuration } ` , ...overlayStyles } } >
174+ < div style = { { display : "flex" , flexFlow : "row nowrap" , cursor : "pointer" , width : barWidth , userSelect : "none" } }
175+ onPointerUp = { onPointerUp }
176+ onPointerDown = { onPointerDown }
177+ onPointerMove = { onPointerMove }
178+ onPointerLeave = { onPointerUp } >
179+ < div style = { {
180+ paddingTop : Math . max ( handleSize - barHeight , 0 ) / 2 , ...overlayStyles ,
181+ } } >
112182 { active ? barActive : bar }
113183 </ div >
184+ { barTransitionType === "slide" ? (
185+ < div style = { { marginLeft : "-100%" , ...overlayStyles } } >
186+ < div style = { {
187+ borderRadius : barBorderRadius ,
188+ width : barWidth ,
189+ height : barHeight ,
190+ marginTop : Math . max ( handleSize - barHeight , 0 ) / 2 ,
191+ // boxSizing: "border-box",
192+ overflow : "hidden" ,
193+ } } >
194+ < div style = { {
195+ transition : `all ${ dragging ? "0s" : transitionDuration } ` ,
196+ backgroundColor : barBackgroundColorActive ,
197+ width : progress * barWidth ,
198+ height : barHeight ,
199+ } } />
200+ </ div >
201+ </ div >
202+ ) : undefined }
114203 < div style = { { marginLeft : "-100%" , ...overlayStyles } } >
115204 < div
116205 style = { {
117- transition : `all ${ transitionDuration } ` ,
206+ transition : `all ${ dragging ? "0s" : transitionDuration } ` ,
118207 paddingTop : Math . max ( barHeight - handleSize , 0 ) / 2 ,
119- paddingLeft : ( ( active && ! flip || ! active && flip ) ? barWidth - padding - handleSize : padding ) ,
208+ paddingLeft : Math . max ( padding , Math . min ( barWidth - padding - handleSize , progress * barWidth - handleSize / 2 ) ) ,
120209 } } >
121210 { active ? handleActive : handle }
122211 </ div >
0 commit comments