66 lines
1.8 KiB
TypeScript
66 lines
1.8 KiB
TypeScript
/**
|
||
* YCode UI Switch(P2-B)。
|
||
*
|
||
* 结构与几何来源:`docs/界面基础组件规范-2026-10-18.md` 的 Switch 章节,
|
||
* 对应 GeneralSettingsPanel 本地 Switch 与恢复样式 source-general-settings-recovered.css
|
||
* 的 `.wb-switch*` 闭包(track 24×14 / 26×16 / 34×20,thumb 10 / 12 / 16)。
|
||
* 只消费 ui/tokens.css 的 `--yc-*` L2 token,不写裸值。
|
||
*/
|
||
import type { ChangeEvent } from 'react'
|
||
import './switch.css'
|
||
|
||
export type SwitchSize = 'sm' | 'md' | 'lg'
|
||
|
||
export interface SwitchProps {
|
||
checked: boolean
|
||
onCheckedChange?(checked: boolean): void
|
||
disabled?: boolean
|
||
readOnly?: boolean
|
||
size?: SwitchSize
|
||
'aria-label': string
|
||
className?: string
|
||
}
|
||
|
||
function classNames(...values: Array<string | false | null | undefined>): string {
|
||
return values.filter(Boolean).join(' ')
|
||
}
|
||
|
||
export function Switch({
|
||
checked,
|
||
onCheckedChange,
|
||
disabled = false,
|
||
readOnly = false,
|
||
size = 'md',
|
||
className,
|
||
'aria-label': ariaLabel
|
||
}: SwitchProps): React.JSX.Element {
|
||
const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {
|
||
if (readOnly) return
|
||
onCheckedChange?.(event.target.checked)
|
||
}
|
||
|
||
return (
|
||
<span
|
||
className={classNames('yc-switch', `yc-switch--${size}`, className)}
|
||
data-disabled={disabled ? 'true' : undefined}
|
||
data-readonly={readOnly ? 'true' : undefined}
|
||
>
|
||
<span className="yc-switch__control">
|
||
<input
|
||
className="yc-switch__input"
|
||
type="checkbox"
|
||
role="switch"
|
||
aria-label={ariaLabel}
|
||
aria-readonly={readOnly || undefined}
|
||
checked={checked}
|
||
disabled={disabled}
|
||
onChange={handleChange}
|
||
/>
|
||
<span className="yc-switch__track">
|
||
<span className="yc-switch__thumb" />
|
||
</span>
|
||
</span>
|
||
</span>
|
||
)
|
||
}
|