nsfwapp/frontend/src/components/ui/LabeledSwitch.tsx
2026-04-27 14:40:16 +02:00

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>
)
}