refactor(ui): migrar componente Switch a HeroUI
This commit is contained in:
1 parent
2b53dd0a11
commit
64e54ea472
1 file changed
+101
-80
+101
-80
@@ -1,5 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React from 'react';
|
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Switch as HeroSwitch,
|
||||||
|
Label,
|
||||||
|
Description,
|
||||||
|
} from "@heroui/react";
|
||||||
|
|
||||||
export interface SwitchProps {
|
export interface SwitchProps {
|
||||||
label?: React.ReactNode;
|
label?: React.ReactNode;
|
||||||
@@ -7,7 +13,7 @@ export interface SwitchProps {
|
|||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
onChange?: (checked: boolean) => void;
|
onChange?: (checked: boolean) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
size?: 'sm' | 'md' | 'lg';
|
size?: "sm" | "md" | "lg";
|
||||||
checkedIcon?: React.ReactNode;
|
checkedIcon?: React.ReactNode;
|
||||||
uncheckedIcon?: React.ReactNode;
|
uncheckedIcon?: React.ReactNode;
|
||||||
id?: string;
|
id?: string;
|
||||||
@@ -16,21 +22,21 @@ export interface SwitchProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const trackSizes = {
|
const trackSizes = {
|
||||||
sm: 'w-8 h-4.5',
|
sm: "w-8 h-[18px]",
|
||||||
md: 'w-11 h-6',
|
md: "w-11 h-6",
|
||||||
lg: 'w-14 h-7.5',
|
lg: "w-14 h-[30px]",
|
||||||
};
|
};
|
||||||
|
|
||||||
const thumbSizes = {
|
const thumbSizes = {
|
||||||
sm: 'w-3.5 h-3.5',
|
sm: "size-3.5",
|
||||||
md: 'w-5 h-5',
|
md: "size-5",
|
||||||
lg: 'w-6.5 h-6.5',
|
lg: "size-[26px]",
|
||||||
};
|
};
|
||||||
|
|
||||||
const translateSizes = {
|
const translateSizes = {
|
||||||
sm: 'translate-x-3.5',
|
sm: "translate-x-3.5",
|
||||||
md: 'translate-x-5',
|
md: "translate-x-5",
|
||||||
lg: 'translate-x-6.5',
|
lg: "translate-x-[26px]",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Switch: React.FC<SwitchProps> = ({
|
export const Switch: React.FC<SwitchProps> = ({
|
||||||
@@ -39,85 +45,100 @@ export const Switch: React.FC<SwitchProps> = ({
|
|||||||
checked = false,
|
checked = false,
|
||||||
onChange,
|
onChange,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
size = 'md',
|
size = "md",
|
||||||
checkedIcon,
|
checkedIcon,
|
||||||
uncheckedIcon,
|
uncheckedIcon,
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
className = '',
|
className = "",
|
||||||
}) => {
|
}) => {
|
||||||
const switchId = id || name || (typeof label === 'string' ? label.toLowerCase().replace(/\s+/g, '-') : undefined);
|
const switchId =
|
||||||
|
id ||
|
||||||
const handleClick = () => {
|
name ||
|
||||||
if (!disabled && onChange) {
|
(typeof label === "string"
|
||||||
onChange(!checked);
|
? label.toLowerCase().replace(/\s+/g, "-")
|
||||||
}
|
: undefined);
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
||||||
if (e.key === ' ' || e.key === 'Enter') {
|
|
||||||
e.preventDefault();
|
|
||||||
handleClick();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`inline-flex items-start gap-3 select-none ${className}`}>
|
<HeroSwitch
|
||||||
{/* Switch Button */}
|
id={switchId}
|
||||||
<button
|
name={name}
|
||||||
id={switchId}
|
isSelected={checked}
|
||||||
type="button"
|
isDisabled={disabled}
|
||||||
role="switch"
|
size={size}
|
||||||
aria-checked={checked}
|
className={className}
|
||||||
disabled={disabled}
|
onChange={(e: any) => {
|
||||||
onClick={handleClick}
|
if (!onChange || disabled) return;
|
||||||
onKeyDown={handleKeyDown}
|
const val = typeof e === "boolean" ? e : Boolean(e?.target?.checked);
|
||||||
className={`
|
onChange(val);
|
||||||
relative shrink-0 inline-flex items-center rounded-full p-0.5 cursor-pointer transition-colors duration-200 ease-in-out
|
}}
|
||||||
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2
|
>
|
||||||
focus-visible:ring-cyan-500 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-950
|
<HeroSwitch.Content>
|
||||||
disabled:opacity-50 disabled:cursor-not-allowed
|
<HeroSwitch.Control
|
||||||
${trackSizes[size]}
|
|
||||||
${
|
|
||||||
checked
|
|
||||||
? 'bg-cyan-500 dark:bg-cyan-500'
|
|
||||||
: 'bg-slate-300 dark:bg-slate-700 hover:bg-slate-400 dark:hover:bg-slate-600'
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
{/* Thumb */}
|
|
||||||
<span
|
|
||||||
className={`
|
className={`
|
||||||
pointer-events-none flex items-center justify-center rounded-full bg-white shadow-md transform transition-transform duration-200 ease-in-out text-slate-800
|
${trackSizes[size]}
|
||||||
${thumbSizes[size]}
|
relative shrink-0 inline-flex items-center rounded-full p-0.5
|
||||||
${checked ? translateSizes[size] : 'translate-x-0'}
|
transition-colors duration-200 ease-in-out cursor-pointer
|
||||||
|
border-0 m-0 box-border outline-none
|
||||||
|
|
||||||
|
${
|
||||||
|
checked
|
||||||
|
? "bg-cyan-500"
|
||||||
|
: "bg-slate-300 hover:bg-slate-400 dark:bg-slate-700 dark:hover:bg-slate-600"
|
||||||
|
}
|
||||||
|
|
||||||
|
${disabled ? "cursor-not-allowed opacity-50" : ""}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{checked ? checkedIcon : uncheckedIcon}
|
<HeroSwitch.Thumb
|
||||||
</span>
|
className={`
|
||||||
</button>
|
${thumbSizes[size]}
|
||||||
|
${checked ? translateSizes[size] : "translate-x-0"}
|
||||||
|
|
||||||
{/* Label and description */}
|
border-0 m-0 pointer-events-none shrink-0 flex items-center justify-center
|
||||||
{(label || description) && (
|
rounded-full bg-white text-slate-800 shadow-md
|
||||||
<div className="flex flex-col text-left cursor-pointer" onClick={handleClick}>
|
transition-transform duration-200 ease-in-out
|
||||||
{label && (
|
`}
|
||||||
<span
|
>
|
||||||
className={`text-xs sm:text-sm font-medium transition-colors ${
|
{(checkedIcon || uncheckedIcon) && (
|
||||||
disabled
|
<HeroSwitch.Icon>
|
||||||
? 'text-slate-400 dark:text-slate-600'
|
{checked ? checkedIcon : uncheckedIcon}
|
||||||
: 'text-slate-800 dark:text-slate-200 hover:text-slate-950 dark:hover:text-white'
|
</HeroSwitch.Icon>
|
||||||
}`}
|
)}
|
||||||
>
|
</HeroSwitch.Thumb>
|
||||||
{label}
|
</HeroSwitch.Control>
|
||||||
</span>
|
|
||||||
)}
|
{/* TEXTOS */}
|
||||||
{description && (
|
{(label || description) && (
|
||||||
<span className="text-xs text-slate-500 dark:text-slate-400">
|
<div className="flex flex-col text-left">
|
||||||
{description}
|
{label && (
|
||||||
</span>
|
<Label
|
||||||
)}
|
className={`
|
||||||
</div>
|
text-xs sm:text-sm font-medium transition-colors cursor-pointer
|
||||||
)}
|
${
|
||||||
</div>
|
disabled
|
||||||
|
? "text-slate-400 dark:text-slate-600 cursor-not-allowed"
|
||||||
|
: "text-slate-800 hover:text-slate-950 dark:text-slate-200 dark:hover:text-white"
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{description && (
|
||||||
|
<Description
|
||||||
|
className={`
|
||||||
|
text-xs text-slate-500 dark:text-slate-400 cursor-pointer
|
||||||
|
${disabled ? "cursor-not-allowed" : ""}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{description}
|
||||||
|
</Description>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</HeroSwitch.Content>
|
||||||
|
</HeroSwitch>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Reference in new issue
Block a user