增加Switch组件。
This commit is contained in:
parent
0b9903f4f3
commit
2491fe162f
48
src/components/Switch.module.css
Normal file
48
src/components/Switch.module.css
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
src/components/Switch.tsx
Normal file
34
src/components/Switch.tsx
Normal file
|
@ -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 (
|
||||||
|
<div className={styles.switch} aria-disabled={disabled}>
|
||||||
|
<div
|
||||||
|
className={cx(styles.switch_handle, isChecked && styles.checked)}
|
||||||
|
onClick={handleSwitch}></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user