diff --git a/src/components/Switch.module.css b/src/components/Switch.module.css new file mode 100644 index 0000000..8433c91 --- /dev/null +++ b/src/components/Switch.module.css @@ -0,0 +1,48 @@ +@layer components { + .switch { + display: inline-flex; + align-items: center; + gap: 1em; + cursor: pointer; + &[aria-disabled] { + cursor: not-allowed; + } + } + .switch_handle { + position: relative; + width: calc(var(--spacing) * 18); + height: calc(var(--spacing) * 10); + border: 1px solid var(--color-primary); + border-radius: var(--border-radius-xxs); + background-color: oklch(from var(--color-primary-active) calc(l * 0.5) calc(c * 0.5) h); + &::before { + content: ''; + position: absolute; + transform: translate(1px, 2px); + width: calc(var(--spacing) * 7); + height: calc(var(--spacing) * 7); + background-color: var(--color-primary); + border-radius: var(--border-radius-xxs); + transition: transform 0.2s ease-in-out; + } + &.checked { + background-color: var(--color-primary-active); + &::before { + transform: translate(calc(var(--spacing) * 10 - 1px), 2px); + } + } + [aria-disabled] & { + --disabled-background: oklch( + from var(--color-primary-disabled) calc(l * 0.5) calc(c * 0.5) h + ); + background-color: var(--disabled-background); + border-color: var(--color-primary-disabled); + &.checked { + background-color: oklch(from var(--disabled-background) calc(l + (1 - l) * 0.1) c h); + } + &::before { + background-color: var(--color-primary-disabled); + } + } + } +} diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx new file mode 100644 index 0000000..73f1d61 --- /dev/null +++ b/src/components/Switch.tsx @@ -0,0 +1,34 @@ +import cx from 'clsx'; +import { isEqual } from 'lodash-es'; +import { useCallback, useEffect, useState } from 'react'; +import styles from './Switch.module.css'; + +type SwitchProps = { + checked?: boolean; + disabled?: boolean; + onChange?: (checked: boolean) => void; +}; + +export function Switch({ checked = false, disabled = false, onChange }: SwitchProps) { + const [isChecked, setIsChecked] = useState(checked); + const handleSwitch = useCallback(() => { + if (!disabled) { + setIsChecked((prev) => !prev); + onChange?.(!isChecked); + } + }, [onChange, disabled]); + + useEffect(() => { + if (!isEqual(checked, isChecked)) { + setIsChecked(checked); + } + }, [checked]); + + return ( +
+
+
+ ); +}