1- import { useEffect , useRef , useState } from 'react'
2- import { useToggleState } from 'react-stately'
3- import {
4- type AriaSwitchProps ,
5- VisuallyHidden ,
6- useFocusRing ,
7- useSwitch ,
8- } from 'react-aria'
1+ import { useEffect , useState } from 'react'
2+ import { VisuallyHidden } from 'react-aria'
93import styled , { keyframes , useTheme } from 'styled-components'
104
115import usePrevious from '../hooks/usePrevious'
126
7+ import { type SwitchProps , type SwitchStyleProps , useSwitch } from './Switch'
8+
139const HANDLE_SIZE = 16
1410const HANDLE_MARGIN = 4
1511const SWITCH_WIDTH = 42
@@ -49,7 +45,7 @@ const slideOffAnim = keyframes`
4945 }
5046`
5147
52- const MoonSC = styled . svg < { $selected : boolean } > ( ( { $selected } ) => ( {
48+ const MoonSC = styled . svg < SwitchStyleProps > ( ( { $checked } ) => ( {
5349 position : 'absolute' ,
5450 top : 6 ,
5551 left : 24 ,
@@ -59,21 +55,21 @@ const MoonSC = styled.svg<{ $selected: boolean }>(({ $selected }) => ({
5955 transition : 'all 0.15s ease 0.1s' ,
6056 } ,
6157 '.moonFill' : {
62- opacity : $selected ? 1 : 0 ,
58+ opacity : $checked ? 1 : 0 ,
6359 zIndex : 0 ,
6460 } ,
6561 '.moonOutline' : {
66- opacity : $selected ? 0 : 1 ,
62+ opacity : $checked ? 0 : 1 ,
6763 zIndex : 1 ,
6864 } ,
6965} ) )
7066
71- function Moon ( { selected } : { selected : boolean } ) {
67+ function Moon ( props : SwitchStyleProps ) {
7268 const theme = useTheme ( )
7369
7470 return (
7571 < MoonSC
76- $selected = { selected }
72+ { ... props }
7773 xmlns = "http://www.w3.org/2000/svg"
7874 viewBox = "0 0 12 12"
7975 >
@@ -91,7 +87,7 @@ function Moon({ selected }: { selected: boolean }) {
9187 )
9288}
9389
94- const SunSC = styled . svg < { $selected : boolean } > ( ( _ ) => ( {
90+ const SunSC = styled . svg < SwitchStyleProps > ( ( _ ) => ( {
9591 position : 'absolute' ,
9692 top : 6 ,
9793 left : 6 ,
@@ -102,15 +98,15 @@ const SunSC = styled.svg<{ $selected: boolean }>((_) => ({
10298 } ,
10399} ) )
104100
105- function Sun ( { selected } : { selected : boolean } ) {
101+ function Sun ( props : SwitchStyleProps ) {
106102 const theme = useTheme ( )
107- const color = selected
103+ const color = ! props . $checked
108104 ? theme . colors . yellow [ 500 ]
109105 : theme . colors [ 'text-primary-disabled' ]
110106
111107 return (
112108 < SunSC
113- $selected = { selected }
109+ { ... props }
114110 xmlns = "http://www.w3.org/2000/svg"
115111 viewBox = "0 0 12 12"
116112 >
@@ -142,52 +138,48 @@ function Sun({ selected }: { selected: boolean }) {
142138 )
143139}
144140
145- const SwitchSC = styled . label < {
146- $checked : boolean
147- $disabled : boolean
148- $readOnly : boolean
149- } > ( ( { $disabled, $readOnly, theme } ) => ( {
150- display : 'flex' ,
151- columnGap : theme . spacing . xsmall ,
152- alignItems : 'center' ,
153- ...theme . partials . text . body2 ,
154- cursor : $disabled ? 'not-allowed' : $readOnly ? 'default' : 'pointer' ,
155- color : theme . colors [ 'text-light' ] ,
156- ...( $disabled || $readOnly
157- ? { }
158- : {
159- '&:hover' : {
160- color : theme . colors . text ,
161- [ SwitchToggleSC ] : {
162- backgroundColor : theme . colors [ 'action-input-hover' ] ,
141+ const SwitchSC = styled . label < SwitchStyleProps > (
142+ ( { $disabled, $readOnly, theme } ) => ( {
143+ display : 'flex' ,
144+ columnGap : theme . spacing . xsmall ,
145+ alignItems : 'center' ,
146+ ...theme . partials . text . body2 ,
147+ cursor : $disabled ? 'not-allowed' : $readOnly ? 'default' : 'pointer' ,
148+ color : theme . colors [ 'text-light' ] ,
149+ ...( $disabled || $readOnly
150+ ? { }
151+ : {
152+ '&:hover' : {
153+ color : theme . colors . text ,
154+ [ SwitchToggleSC ] : {
155+ backgroundColor : theme . colors [ 'action-input-hover' ] ,
156+ } ,
163157 } ,
164- } ,
165- } ) ,
166- } ) )
167-
168- const SwitchToggleSC = styled . div < {
169- $disabled : boolean
170- $focused : boolean
171- $checked : boolean
172- } > ( ( { $focused, $disabled, theme } ) => ( {
173- position : 'relative' ,
174- width : 42 ,
175- height : 24 ,
176- borderRadius : 12 ,
177- backgroundColor : 'transparent' ,
178- outlineWidth : 1 ,
179- outlineStyle : 'solid' ,
180- outlineOffset : - 1 ,
181- outlineColor : $disabled
182- ? theme . colors [ 'border-disabled' ]
183- : $focused
184- ? theme . colors [ 'border-outline-focused' ]
185- : theme . colors [ 'border-input' ] ,
186- transition : 'all 0.15s ease' ,
187- } ) )
158+ } ) ,
159+ } )
160+ )
161+
162+ const SwitchToggleSC = styled . div < SwitchStyleProps > (
163+ ( { $focused, $disabled, theme } ) => ( {
164+ position : 'relative' ,
165+ width : 42 ,
166+ height : 24 ,
167+ borderRadius : 12 ,
168+ backgroundColor : 'transparent' ,
169+ outlineWidth : 1 ,
170+ outlineStyle : 'solid' ,
171+ outlineOffset : - 1 ,
172+ outlineColor : $disabled
173+ ? theme . colors [ 'border-disabled' ]
174+ : $focused
175+ ? theme . colors [ 'border-outline-focused' ]
176+ : theme . colors [ 'border-input' ] ,
177+ transition : 'all 0.15s ease' ,
178+ } )
179+ )
188180
189181const SwitchHandleSC = styled (
190- styled . div < { $checked : boolean ; $disabled : boolean ; $animate : boolean } > (
182+ styled . div < SwitchStyleProps & { $animate : boolean } > (
191183 ( { $checked, $disabled, theme } ) => ( {
192184 position : 'absolute' ,
193185 width : '100%' ,
@@ -222,29 +214,12 @@ const SwitchHandleSC = styled(
222214
223215export function LightDarkSwitch ( {
224216 children,
225- checked,
226- disabled,
227- readOnly,
217+ as,
218+ className,
228219 ...props
229- } : Omit <
230- AriaSwitchProps & Parameters < typeof useToggleState > [ 0 ] ,
231- 'isDisabled' | 'isReadonly' | 'isSelected'
232- > & { checked ?: boolean ; disabled ?: boolean ; readOnly ?: boolean } ) {
233- const ariaProps : AriaSwitchProps = {
234- isSelected : checked ,
235- isDisabled : disabled ,
236- isReadOnly : readOnly ,
237- ...props ,
238- }
239-
240- const state = useToggleState ( ariaProps )
241- const ref = useRef < HTMLInputElement > ( null )
242- const { inputProps, isSelected, isDisabled, isReadOnly } = useSwitch (
243- { ...ariaProps } ,
244- state ,
245- ref
246- )
247- const { focusProps, isFocusVisible } = useFocusRing ( )
220+ } : SwitchProps ) {
221+ const { inputProps, styleProps, state } = useSwitch ( props )
222+ const { isSelected } = state
248223 const wasSelected = usePrevious ( isSelected ) ?? isSelected
249224 const [ animate , setAnimate ] = useState ( false )
250225
@@ -256,27 +231,18 @@ export function LightDarkSwitch({
256231
257232 return (
258233 < SwitchSC
259- $disabled = { isDisabled }
260- $checked = { isSelected }
261- $readOnly = { isReadOnly }
234+ as = { as }
235+ className = { className }
236+ { ... styleProps }
262237 >
263238 < VisuallyHidden >
264- < input
265- { ...inputProps }
266- { ...focusProps }
267- ref = { ref }
268- />
239+ < input { ...inputProps } />
269240 </ VisuallyHidden >
270- < SwitchToggleSC
271- $focused = { isFocusVisible }
272- $disabled = { isDisabled }
273- $checked = { isSelected }
274- >
275- < Sun selected = { ! isSelected } />
276- < Moon selected = { isSelected } />
241+ < SwitchToggleSC { ...styleProps } >
242+ < Sun { ...styleProps } />
243+ < Moon { ...styleProps } />
277244 < SwitchHandleSC
278- $disabled = { isDisabled }
279- $checked = { isSelected }
245+ { ...styleProps }
280246 $animate = { animate }
281247 />
282248 </ SwitchToggleSC >
0 commit comments