1+ import React , { useState , useRef } from "react" ;
2+ import { SwitchTickIcon , SwitchCrossIcon } from "../SvgIcons" ;
3+ import { StyleServices } from "@formsflow/service" ;
4+
5+ interface SwitchProps {
6+ checked ?: boolean ;
7+ disabled ?: boolean ;
8+ withIcon ?: boolean ;
9+ onChange ?: ( checked : boolean ) => void ;
10+ id ?: string ;
11+ className ?: string ;
12+ label ?: string ;
13+ type ?: string ; // default|primary|binary
14+ }
15+
16+ export const Switch : React . FC < SwitchProps > = ( {
17+ checked = false ,
18+ disabled = false ,
19+ withIcon = false ,
20+ onChange,
21+ id,
22+ className = "" ,
23+ label,
24+ type = "default"
25+ } ) => {
26+ const [ isChecked , setIsChecked ] = useState ( checked ) ;
27+ const [ isFocused , setIsFocused ] = useState ( false ) ;
28+ const switchRef = useRef < HTMLButtonElement > ( null ) ;
29+
30+ // Use CSS variables for colors
31+ const colorSuccess = StyleServices . getCSSVariable ( '--ff-success' ) ; // for #00C49A
32+ const colorPrimaryLight = StyleServices . getCSSVariable ( '--ff-primary-light' ) ; // for #B8ABFF
33+ const colorDanger = StyleServices . getCSSVariable ( '--ff-danger' ) ; // for #E57373
34+ const colorGrayLight = StyleServices . getCSSVariable ( '--ff-gray-light' ) ; // for #E5E5E5
35+
36+ const renderIcon = ( ) => {
37+ let fillColor = colorSuccess ;
38+
39+ if ( isChecked ) {
40+ if ( type . toLowerCase ( ) === 'primary' ) fillColor = colorPrimaryLight ;
41+ return (
42+ < span className = "custom-switch-icon" >
43+ < SwitchTickIcon fillColor = { fillColor } />
44+ </ span >
45+ ) ;
46+ } else {
47+ if ( type . toLowerCase ( ) === 'binary' ) fillColor = colorDanger ;
48+ else fillColor = colorGrayLight ;
49+ return (
50+ < span className = "custom-switch-icon" >
51+ < SwitchCrossIcon fillColor = { fillColor } />
52+ </ span >
53+ ) ;
54+ }
55+ } ;
56+
57+ const renderClass = ( ) => {
58+ let switchClass = 'custom-switch' ;
59+
60+ if ( isChecked ) {
61+ if ( type . toLowerCase ( ) === 'primary' ) switchClass += ' custom-switch-on-primary' ;
62+ else switchClass += ' custom-switch-on' ;
63+ } else {
64+ if ( type . toLowerCase ( ) === 'binary' ) {
65+ switchClass += ' custom-switch-off-binary' ;
66+ } else {
67+ switchClass += ' custom-switch-off' ;
68+ }
69+ }
70+
71+ if ( isFocused ) switchClass += ' custom-switch-focused' ;
72+ if ( disabled ) switchClass += ' custom-switch-disabled' ;
73+
74+ return switchClass ;
75+ }
76+
77+ const handleToggle = ( ) => {
78+ if ( disabled ) return ;
79+ setIsChecked ( ( prev ) => {
80+ const newChecked = ! prev ;
81+ onChange ?.( newChecked ) ;
82+ return newChecked ;
83+ } ) ;
84+ } ;
85+
86+ const handleKeyDown = ( e : React . KeyboardEvent < HTMLButtonElement > ) => {
87+ if ( disabled ) return ;
88+ if ( e . key === " " || e . key === "Spacebar" ) {
89+ e . preventDefault ( ) ;
90+ handleToggle ( ) ;
91+ }
92+ } ;
93+
94+ const handleFocus = ( ) => setIsFocused ( true ) ;
95+ const handleBlur = ( ) => setIsFocused ( false ) ;
96+
97+ return (
98+ < div className = { `custom-switch-wrapper ${ className } ` } >
99+ { label && (
100+ < label htmlFor = { id } className = "custom-switch-label" >
101+ { label }
102+ </ label >
103+ ) }
104+ < button
105+ type = "button"
106+ id = { id ? `${ id } -label` : undefined }
107+ ref = { switchRef }
108+ role = "switch"
109+ aria-checked = { isChecked }
110+ aria-disabled = { disabled }
111+ aria-labelledby = { label && id ? `${ id } -label` : undefined }
112+ aria-label = { ! label ? label ?? 'Toggle' : undefined }
113+ tabIndex = { disabled ? - 1 : 0 }
114+ className = { renderClass ( ) }
115+ onClick = { handleToggle }
116+ onKeyDown = { handleKeyDown }
117+ onFocus = { handleFocus }
118+ onBlur = { handleBlur }
119+ disabled = { disabled }
120+ >
121+ < span className = "custom-switch-slider" >
122+ { withIcon && renderIcon ( ) }
123+ </ span >
124+ </ button >
125+ </ div >
126+ ) ;
127+ } ;
0 commit comments