60 lines
1.8 KiB
TypeScript
60 lines
1.8 KiB
TypeScript
import clsx from "clsx";
|
|
import { ReactNode, useEffect, useState } from "react";
|
|
import HintTooltip from "../tooltips/HintTooltip";
|
|
import { Checkbox } from "~/ui/components/Checkbox";
|
|
|
|
interface Props {
|
|
name?: string;
|
|
title?: string | ReactNode;
|
|
description: string | ReactNode;
|
|
value?: boolean;
|
|
setValue?: React.Dispatch<React.SetStateAction<boolean>>;
|
|
className?: string;
|
|
help?: string;
|
|
disabled?: boolean;
|
|
autoFocus?: boolean;
|
|
}
|
|
export default function InputCheckboxWithDescription({ name, title, value, setValue, description, className, help, disabled = false, autoFocus }: Props) {
|
|
// Subframe's Checkbox is always controlled; keep the original internal state that also
|
|
// supports the uncontrolled (setValue-only) usage and mirrors changes back.
|
|
const [checked, setChecked] = useState(value ?? false);
|
|
|
|
useEffect(() => {
|
|
if (value !== checked) {
|
|
setChecked(value ?? false);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [value]);
|
|
|
|
useEffect(() => {
|
|
if (setValue && value !== checked) {
|
|
setValue(checked);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [checked]);
|
|
|
|
// Title + optional help tooltip rendered into Subframe's label slot.
|
|
const labelContent: ReactNode = (
|
|
<span className="flex items-center space-x-1">
|
|
<span>{title}</span>
|
|
{help && <HintTooltip text={help} />}
|
|
</span>
|
|
);
|
|
|
|
return (
|
|
<div className={clsx("relative flex items-start pb-4 pt-2", className)}>
|
|
<Checkbox
|
|
id={name}
|
|
name={name}
|
|
label={labelContent}
|
|
description={description}
|
|
checked={checked}
|
|
onCheckedChange={(next) => setChecked(Boolean(next))}
|
|
disabled={disabled}
|
|
autoFocus={autoFocus}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|