Skip to content

Commit 899db40

Browse files
committed
feat toggle
1 parent ef579ac commit 899db40

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Toggle } from './index'
2+
3+
export default {
4+
title: 'Devfive/Toggle',
5+
component: Toggle,
6+
tags: ['autodocs'],
7+
}
8+
9+
export const Default = {
10+
args: {
11+
disabled: false,
12+
variant: 'default',
13+
defaultValue: false,
14+
offText: 'OFF',
15+
onText: 'ON',
16+
color: 'var(--primary)',
17+
},
18+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
'use client'
2+
import { Box, Input } from '@devup-ui/react'
3+
import { useState } from 'react'
4+
5+
interface ToggleProps {
6+
defaultValue?: boolean | null
7+
value?: boolean | null
8+
onChange?: (value: boolean) => void
9+
disabled?: boolean
10+
variant?: 'default' | 'switch'
11+
className?: string
12+
style?: React.CSSProperties
13+
classNames?: {
14+
toggle?: string
15+
}
16+
styles?: {
17+
toggle?: React.CSSProperties
18+
}
19+
colors?: {
20+
primary?: string
21+
bg?: string
22+
hoverBg?: string
23+
primaryHoverBg?: string
24+
disabledBg?: string
25+
switchHoverOutline?: string
26+
switchShadow?: string
27+
}
28+
}
29+
30+
export function Toggle({
31+
defaultValue = null,
32+
value = null,
33+
onChange,
34+
disabled,
35+
className,
36+
style,
37+
variant = 'default',
38+
colors,
39+
classNames,
40+
styles,
41+
}: ToggleProps) {
42+
const [innerValue, setInnerValue] = useState<boolean>(
43+
value ?? defaultValue ?? false,
44+
)
45+
46+
const resultValue = value ?? innerValue
47+
48+
function handleToggle(value: boolean) {
49+
onChange?.(!value)
50+
setInnerValue((prev) => !prev)
51+
}
52+
53+
const isDefault = variant === 'default'
54+
55+
return (
56+
<>
57+
<Box
58+
aria-disabled={disabled}
59+
bg={
60+
resultValue
61+
? 'var(--primary)'
62+
: 'var(--bg, light-dark(#E4E4E4, #383838))'
63+
}
64+
borderRadius="500px"
65+
boxSizing="border-box"
66+
className="toggleSwitch"
67+
cursor="pointer"
68+
h={isDefault ? '28px' : '8px'}
69+
justifyContent={resultValue ? 'flex-end' : undefined}
70+
onClick={() => !disabled && handleToggle(resultValue)}
71+
p={isDefault ? 1 : undefined}
72+
position="relative"
73+
selectors={{
74+
'&[aria-disabled=true]': {
75+
cursor: 'not-allowed',
76+
bg: 'var(--disabledBg, light-dark(#D6D7DE, #373737))',
77+
},
78+
'&:hover:not([aria-disabled=true]):not(:disabled)': {
79+
bg: resultValue
80+
? `var(--primaryHoverBg, light-dark(color-mix(in srgb, var(--primary) 100%, #000 15%), color-mix(in srgb, var(--primary) 100%, #FFF 15%)))`
81+
: 'var(--hoverBg, light-dark(#C3C2C8, #696A6F))',
82+
},
83+
}}
84+
style={style}
85+
styleVars={{
86+
primary: colors?.primary,
87+
bg: colors?.bg,
88+
primaryHoverBg: colors?.primaryHoverBg,
89+
hoverBg: colors?.hoverBg,
90+
disabledBg: colors?.disabledBg,
91+
}}
92+
transition=".25s"
93+
w={isDefault ? '50px' : '40px'}
94+
>
95+
<Box
96+
backgroundColor="#fff"
97+
borderRadius="100%"
98+
boxSize="20px"
99+
className={classNames?.toggle}
100+
filter={
101+
isDefault
102+
? undefined
103+
: `drop-shadow(0px 0px 3px var(--switchShadow, rgba(0, 0, 0, 0.10)));`
104+
}
105+
outline="4px"
106+
pos="absolute"
107+
selectors={{
108+
'.toggleSwitch:hover[aria-disabled=false] > &': {
109+
outline: '4px solid',
110+
outlineColor: `var(--switchHoverOutline, light-dark(color-mix(in srgb, var(--primary) 20%, transparent), color-mix(in srgb, var(--primary) 50%, transparent)))`,
111+
},
112+
}}
113+
style={styles?.toggle}
114+
styleVars={{
115+
primary: colors?.primary,
116+
primaryHoverBg: colors?.primaryHoverBg,
117+
switchShadow: colors?.switchShadow,
118+
switchHoverOutline: colors?.switchHoverOutline,
119+
}}
120+
top={isDefault ? undefined : '-6px'}
121+
transform={resultValue ? 'translateX(calc(100% + 2px))' : undefined}
122+
transition={isDefault ? '.25s' : 'transform .25s'}
123+
/>
124+
</Box>
125+
<Input
126+
defaultValue={String(defaultValue)}
127+
type="hidden"
128+
value={String(resultValue)}
129+
/>
130+
</>
131+
)
132+
}

0 commit comments

Comments
 (0)