149 lines
3.8 KiB
TypeScript
149 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import {
|
|
Checkbox as HeroUICheckbox,
|
|
Description,
|
|
FieldError,
|
|
} from "@heroui/react";
|
|
|
|
import { FiCheck, FiMinus } from "react-icons/fi";
|
|
|
|
export interface CheckboxProps
|
|
extends Omit<
|
|
React.ComponentProps<typeof HeroUICheckbox>,
|
|
"children" | "defaultSelected" | "isDisabled" | "isInvalid" | "isIndeterminate"
|
|
> {
|
|
label?: React.ReactNode;
|
|
description?: string;
|
|
errorMessage?: string;
|
|
checked?: boolean;
|
|
disabled?: boolean;
|
|
indeterminate?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
export const Checkbox = ({
|
|
label,
|
|
description,
|
|
errorMessage,
|
|
indeterminate = false,
|
|
checked,
|
|
isSelected,
|
|
disabled = false,
|
|
className = "",
|
|
id,
|
|
name,
|
|
onChange,
|
|
...props
|
|
}: CheckboxProps) => {
|
|
return (
|
|
<HeroUICheckbox
|
|
{...props}
|
|
id={id}
|
|
name={name}
|
|
isSelected={checked}
|
|
isDisabled={disabled}
|
|
isIndeterminate={indeterminate}
|
|
isInvalid={Boolean(errorMessage)}
|
|
onChange={onChange}
|
|
className={className}
|
|
>
|
|
<HeroUICheckbox.Content className="items-start gap-2.5">
|
|
<HeroUICheckbox.Control
|
|
className={[
|
|
"mt-0.5 shrink-0 size-4 rounded-md",
|
|
"border",
|
|
"transition-all duration-200",
|
|
|
|
// Estado normal
|
|
"bg-white dark:bg-slate-900",
|
|
"border-slate-300 dark:border-slate-700",
|
|
|
|
// Hover
|
|
"group-hover:border-slate-400",
|
|
"dark:group-hover:border-slate-500",
|
|
|
|
// Focus
|
|
"data-[focus-visible=true]:ring-2",
|
|
"data-[focus-visible=true]:ring-cyan-500",
|
|
"data-[focus-visible=true]:ring-offset-2",
|
|
"data-[focus-visible=true]:ring-offset-white",
|
|
"dark:data-[focus-visible=true]:ring-offset-slate-950",
|
|
|
|
// Seleccionado
|
|
"data-[selected=true]:bg-cyan-500",
|
|
"data-[selected=true]:border-cyan-500",
|
|
"data-[selected=true]:text-slate-950",
|
|
"data-[selected=true]:shadow-sm",
|
|
"data-[selected=true]:shadow-cyan-500/20",
|
|
|
|
// Indeterminado
|
|
"data-[indeterminate=true]:bg-cyan-500",
|
|
"data-[indeterminate=true]:border-cyan-500",
|
|
"data-[indeterminate=true]:text-slate-950",
|
|
|
|
// Error
|
|
errorMessage
|
|
? "data-[invalid=true]:border-rose-500"
|
|
: "",
|
|
]
|
|
.filter(Boolean)
|
|
.join(" ")}
|
|
>
|
|
<HeroUICheckbox.Indicator>
|
|
{({ isSelected, isIndeterminate }) => {
|
|
if (isIndeterminate) {
|
|
return <FiMinus className="text-xs stroke-3" />;
|
|
}
|
|
|
|
if (isSelected) {
|
|
return <FiCheck className="text-xs stroke-3" />;
|
|
}
|
|
|
|
return null;
|
|
}}
|
|
</HeroUICheckbox.Indicator>
|
|
</HeroUICheckbox.Control>
|
|
|
|
{(label || description) && (
|
|
<div className="flex flex-col text-left">
|
|
{label && (
|
|
<span
|
|
className="
|
|
text-xs sm:text-sm font-medium
|
|
text-slate-800 dark:text-slate-200
|
|
group-hover:text-slate-950
|
|
dark:group-hover:text-white
|
|
transition-colors
|
|
"
|
|
>
|
|
{label}
|
|
</span>
|
|
)}
|
|
|
|
{description && (
|
|
<Description
|
|
className="
|
|
text-xs
|
|
text-slate-500 dark:text-slate-400
|
|
"
|
|
>
|
|
{description}
|
|
</Description>
|
|
)}
|
|
</div>
|
|
)}
|
|
</HeroUICheckbox.Content>
|
|
|
|
{errorMessage && (
|
|
<FieldError className="pl-6.5 text-xs">
|
|
{errorMessage}
|
|
</FieldError>
|
|
)}
|
|
</HeroUICheckbox>
|
|
);
|
|
};
|
|
|
|
Checkbox.displayName = "Checkbox";
|