70 lines
2.6 KiB
TypeScript
70 lines
2.6 KiB
TypeScript
"use client";
|
|
|
|
import { InputHTMLAttributes, forwardRef, useState } from "react";
|
|
|
|
interface CheckboxProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
label?: string | React.ReactNode;
|
|
error?: string;
|
|
}
|
|
|
|
const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
({ label, error, className = "", onChange, checked: controlledChecked, ...props }, ref) => {
|
|
const [isChecked, setIsChecked] = useState(controlledChecked || false);
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
setIsChecked(e.target.checked);
|
|
if (onChange) {
|
|
onChange(e);
|
|
}
|
|
};
|
|
|
|
const checked = controlledChecked !== undefined ? controlledChecked : isChecked;
|
|
|
|
return (
|
|
<div className="w-full">
|
|
<label className="flex items-start gap-3 cursor-pointer group">
|
|
<div className="relative flex items-center justify-center mt-0.5">
|
|
<input
|
|
ref={ref}
|
|
type="checkbox"
|
|
className={`
|
|
appearance-none w-[18px] h-[18px] border rounded-sm
|
|
border-[#E5E5E5] dark:border-gray-600 bg-white dark:bg-gray-700
|
|
checked:border-[#FF3A05]
|
|
focus:outline-none focus:border-[#FF3A05]
|
|
transition-colors cursor-pointer
|
|
${className}
|
|
`}
|
|
style={{
|
|
background: checked ? 'var(--gradient)' : undefined,
|
|
}}
|
|
checked={checked}
|
|
onChange={handleChange}
|
|
{...props}
|
|
/>
|
|
<i
|
|
className={`ri-check-line absolute text-white text-[14px] pointer-events-none transition-opacity ${checked ? 'opacity-100' : 'opacity-0'
|
|
}`}
|
|
/>
|
|
</div>
|
|
{label && (
|
|
<span className="text-[14px] text-[#000000] dark:text-white select-none">
|
|
{label}
|
|
</span>
|
|
)}
|
|
</label>
|
|
{error && (
|
|
<p className="mt-1 text-[13px] text-[#FF3A05] flex items-center gap-1">
|
|
<i className="ri-error-warning-line" />
|
|
{error}
|
|
</p>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
);
|
|
|
|
Checkbox.displayName = "Checkbox";
|
|
|
|
export default Checkbox;
|