feat(components): 添加 Switch 组件
- 实现了一个新的 Switch 组件,用于在开启和关闭状态之间切换 - 组件支持 name、checked、disabled 和 onChange 属性 - 使用 Solid.js 的 createEffect、createMemo 和 createSignal 等功能实现内部状态管理 - 通过 CSS 类和属性动态控制组件样式和行为
This commit is contained in:
53
src/components/Switch.tsx
Normal file
53
src/components/Switch.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { isNotNil } from 'es-toolkit';
|
||||||
|
import { Component, createEffect, createMemo, createSignal, mergeProps, Show } from 'solid-js';
|
||||||
|
|
||||||
|
interface SwitchProps {
|
||||||
|
name?: string;
|
||||||
|
checked?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
onChange?: (checked: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Switcher: Component<SwitchProps> = (props) => {
|
||||||
|
const mProps = mergeProps<SwitchProps[]>(
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
props,
|
||||||
|
);
|
||||||
|
|
||||||
|
const originalChecked = createMemo(() => mProps.checked);
|
||||||
|
const [internalChecked, setInternalChecked] = createSignal<boolean | undefined>(undefined);
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (isNotNil(originalChecked()) && originalChecked() !== internalChecked()) {
|
||||||
|
setInternalChecked(originalChecked());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (mProps.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setInternalChecked((prev) => !prev);
|
||||||
|
mProps.onChange?.(internalChecked());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
aria-disabled={mProps.disabled}
|
||||||
|
class="inline-flex items-center gap-[1em] cursor-pointer rounded-sm aria-disabled:cursor-not-allowed group"
|
||||||
|
classList={{ 'bg-primary/38 aria-disabled:bg-on-surface/8': internalChecked() }}>
|
||||||
|
<div
|
||||||
|
class="relative h-[1em] w-[1.75em] border border-solid border-primary group-aria-disabled:border-on-surface/8 rounded-sm before:absolute before:translate-x-[2px] before:translate-y-[2px] before:h-[calc(1em-6px)] before:aspect-square before:rounded-xs before:bg-primary before:transition before:duration-200 before:ease-in-out group-aria-disabled:before:bg-on-surface/18"
|
||||||
|
classList={{ 'before:translate-x-[calc(0.75em+2px)]': internalChecked() }}
|
||||||
|
onClick={handleClick}
|
||||||
|
/>
|
||||||
|
<Show when={isNotNil(mProps.name)}>
|
||||||
|
<input type="hidden" name={mProps.name} value={internalChecked() ? 'true' : 'false'} />
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Switcher;
|
Reference in New Issue
Block a user