diff --git a/src/components/form/Switch/Switch.tsx b/src/components/form/Switch/Switch.tsx index 496052073..29983a36e 100644 --- a/src/components/form/Switch/Switch.tsx +++ b/src/components/form/Switch/Switch.tsx @@ -1,9 +1,9 @@ -import { cx } from 'class-variance-authority' +import { cva } from 'class-variance-authority' import { MouseEvent, useEffect, useRef, useState } from 'react' -import styled, { css } from 'styled-components' import { Icon, Typography } from '~/components/designSystem' import { theme } from '~/styles' +import { tw } from '~/styles/utils' enum LabelPositionEnum { left = 'left', @@ -21,6 +21,49 @@ export interface SwitchProps { onChange?: (value: boolean, e: MouseEvent) => Promise | void } +const switchVariants = cva( + 'relative flex h-8 w-15 min-w-15 max-w-15 items-center justify-between rounded-[32px] px-1 transition-[background-color] duration-250 ease-in-out', + { + variants: { + loading: { + true: 'justify-center bg-blue', + }, + checked: { + true: 'bg-blue', + false: 'bg-grey-200', + }, + disabled: { + true: 'bg-grey-100 text-grey-400', + }, + focused: { + true: '', + }, + }, + compoundVariants: [ + { + loading: false, + disabled: false, + className: 'cursor-pointer', + }, + { + disabled: false, + checked: true, + className: 'hover:bg-blue-700 active:hover:bg-blue-800', + }, + { + disabled: false, + checked: false, + className: 'hover:bg-grey-300 active:hover:bg-grey-400', + }, + { + disabled: false, + focused: true, + className: 'ring', + }, + ], + }, +) + export const Switch = ({ name, label, @@ -37,7 +80,7 @@ export const Switch = ({ const [loading, setLoading] = useState(false) useEffect(() => { - // This is for preventing setstate on unmounted component + // This is for preventing setState on unmounted component mountedRef.current = true return () => { @@ -46,8 +89,13 @@ export const Switch = ({ }, []) return ( - { if (mountedRef.current && realLoading) setLoading(true) }, 100) @@ -77,13 +125,17 @@ export const Switch = ({ } } > - setFocused(true)} onBlur={() => setFocused(false)} + className="absolute m-0 size-0 p-0 opacity-0" /> - {loading && } - + {loading && ( + + )} + On - - + + Off - - + + - - + + {(!!label || !!subLabel) && ( <> - - - {!!label && {label}} - {!!subLabel && {subLabel}} - +
+
+ {!!label && ( + + {label} + + )} + {!!subLabel && ( + + {subLabel} + + )} +
)} - +
) } - Switch.displayName = 'Switch' - -const Container = styled.div<{ $orientation: LabelPosition }>` - display: flex; - align-items: center; - flex-direction: ${({ $orientation }) => - $orientation === LabelPositionEnum.right ? 'row' : 'row-reverse'}; -` - -const LabelContainer = styled.div<{ $disabled?: boolean }>` - ${({ $disabled }) => - !$disabled && - css` - cursor: pointer; - `} -` - -const Space = styled.div` - width: ${theme.spacing(3)}; - min-width: ${theme.spacing(3)}; - min-height: 1px; -` - -const Loader = styled(Icon)` - position: absolute; - left: 20px; - transition: - left 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, - opacity 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; - opacity: 0; -` - -const SwitchElement = styled.svg<{ $checked: boolean }>` - position: absolute; - left: ${(props) => (props.$checked ? theme.spacing(8) : theme.spacing(1))}; - transition: - left 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, - opacity 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; - opacity: 1; -` - -const StyledTypography = styled(Typography)` - width: 24px; - text-align: center; - transition: - left 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, - opacity 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; - opacity: 1; -` - -const SwitchContainer = styled.div<{ $checked: boolean }>` - border-radius: 32px; - width: 60px; - max-width: 60px; - min-width: 60px; - box-sizing: border-box; - height: 32px; - position: relative; - transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 ${theme.spacing(1)}; - - &:not(.switchField--disabled):not(.switchField--loading) { - cursor: pointer; - background-color: ${(props) => - props.$checked ? theme.palette.primary.main : theme.palette.grey[200]}; - - &:hover { - background-color: ${(props) => - props.$checked ? theme.palette.primary[700] : theme.palette.grey[300]}; - } - - &:active { - background-color: ${(props) => - props.$checked ? theme.palette.primary[800] : theme.palette.grey[400]}; - } - - &.switchField--focused { - box-shadow: 0px 0px 0px 4px ${theme.palette.primary[200]}; - } - } - - &.switchField--loading { - justify-content: center; - background-color: ${theme.palette.primary.main}; - - ${StyledTypography}, ${SwitchElement} { - opacity: 0; - } - - ${Loader} { - opacity: 1; - } - } - - &.switchField--disabled { - background-color: ${theme.palette.grey[100]}; - color: ${theme.palette.grey[400]}; - } - - input { - opacity: 0; - position: absolute; - width: 0; - height: 0; - margin: 0; - padding: 0; - } -` diff --git a/tailwind.config.ts b/tailwind.config.ts index 6c3a1eb05..c5dd65c19 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -134,6 +134,9 @@ const config = { screens: { md: '776px', }, + transitionDuration: { + '250': '250ms', + }, spacing: { 7: '1.75rem', 13: '3.25rem',