115 lines
2.7 KiB
TypeScript
115 lines
2.7 KiB
TypeScript
// components/ui/LabeledSwitch.tsx
|
|
'use client'
|
|
|
|
import { useId, type ReactNode } from 'react'
|
|
import clsx from 'clsx'
|
|
import Switch, { type SwitchProps } from './Switch'
|
|
|
|
type Props = Omit<SwitchProps, 'ariaLabelledby' | 'ariaDescribedby' | 'ariaLabel'> & {
|
|
label: ReactNode
|
|
description?: ReactNode
|
|
labelPosition?: 'left' | 'right'
|
|
className?: string
|
|
}
|
|
|
|
export default function LabeledSwitch({
|
|
label,
|
|
description,
|
|
labelPosition = 'left',
|
|
id,
|
|
className,
|
|
compact = false,
|
|
...switchProps
|
|
}: Props) {
|
|
const reactId = useId()
|
|
const switchId = id ?? `sw-${reactId}`
|
|
const labelId = `${switchId}-label`
|
|
const descId = `${switchId}-desc`
|
|
|
|
if (labelPosition === 'right') {
|
|
return (
|
|
<div
|
|
className={clsx(
|
|
'flex items-center justify-between',
|
|
compact ? 'gap-2' : 'gap-3',
|
|
className
|
|
)}
|
|
>
|
|
<Switch
|
|
{...switchProps}
|
|
compact={compact}
|
|
id={switchId}
|
|
ariaLabelledby={labelId}
|
|
ariaDescribedby={description ? descId : undefined}
|
|
/>
|
|
|
|
<div className={compact ? 'text-xs' : 'text-sm'}>
|
|
<label
|
|
id={labelId}
|
|
htmlFor={switchId}
|
|
className={clsx(
|
|
'font-medium text-gray-900 dark:text-white',
|
|
compact ? 'leading-5' : ''
|
|
)}
|
|
>
|
|
{label}
|
|
</label>{' '}
|
|
{description ? (
|
|
<span
|
|
id={descId}
|
|
className={clsx(
|
|
'text-gray-500 dark:text-gray-400',
|
|
compact ? 'text-[11px]' : ''
|
|
)}
|
|
>
|
|
{description}
|
|
</span>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={clsx(
|
|
'flex items-center justify-between',
|
|
compact ? 'gap-2' : 'gap-3',
|
|
className
|
|
)}
|
|
>
|
|
<span className={clsx('flex grow flex-col min-w-0', compact ? 'pr-1' : 'pr-2')}>
|
|
<label
|
|
id={labelId}
|
|
htmlFor={switchId}
|
|
className={clsx(
|
|
'font-medium text-gray-900 dark:text-white truncate',
|
|
compact ? 'text-xs leading-5' : 'text-sm/6'
|
|
)}
|
|
>
|
|
{label}
|
|
</label>
|
|
|
|
{description ? (
|
|
<span
|
|
id={descId}
|
|
className={clsx(
|
|
'text-gray-500 dark:text-gray-400',
|
|
compact ? 'text-[11px]' : 'text-sm'
|
|
)}
|
|
>
|
|
{description}
|
|
</span>
|
|
) : null}
|
|
</span>
|
|
|
|
<Switch
|
|
{...switchProps}
|
|
compact={compact}
|
|
id={switchId}
|
|
ariaLabelledby={labelId}
|
|
ariaDescribedby={description ? descId : undefined}
|
|
/>
|
|
</div>
|
|
)
|
|
} |