Finish migrating components
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
"use client";
|
||||
|
||||
import { memo, useCallback, useId, forwardRef } from "react";
|
||||
import { SwitchView } from "./Switch.view";
|
||||
import type { SwitchProps } from "./Switch.types";
|
||||
|
||||
const SwitchContainer = memo(
|
||||
forwardRef<HTMLButtonElement, SwitchProps>((props, ref) => {
|
||||
const {
|
||||
checked = false,
|
||||
onChange,
|
||||
onFocus,
|
||||
onBlur,
|
||||
state = "default",
|
||||
label,
|
||||
className = "",
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const switchId = useId();
|
||||
|
||||
const handleClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
if (onChange) {
|
||||
onChange(e);
|
||||
}
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent<HTMLButtonElement>) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.preventDefault();
|
||||
if (onChange) {
|
||||
onChange(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
const handleFocus = useCallback(
|
||||
(e: React.FocusEvent<HTMLButtonElement>) => {
|
||||
if (onFocus) {
|
||||
onFocus(e);
|
||||
}
|
||||
},
|
||||
[onFocus],
|
||||
);
|
||||
|
||||
const handleBlur = useCallback(
|
||||
(e: React.FocusEvent<HTMLButtonElement>) => {
|
||||
if (onBlur) {
|
||||
onBlur(e);
|
||||
}
|
||||
},
|
||||
[onBlur],
|
||||
);
|
||||
|
||||
// Switch track styles based on checked state
|
||||
const getTrackStyles = useCallback(() => {
|
||||
return checked
|
||||
? "bg-[var(--color-surface-inverse-tertiary)]"
|
||||
: "bg-[var(--color-surface-default-tertiary)]";
|
||||
}, [checked]);
|
||||
|
||||
// Switch thumb styles based on checked state
|
||||
const getThumbStyles = useCallback(() => {
|
||||
return "bg-[var(--color-gray-000)]";
|
||||
}, []);
|
||||
|
||||
// Focus styles
|
||||
const getFocusStyles = useCallback(() => {
|
||||
if (state === "focus") {
|
||||
return "shadow-[0_0_5px_3px_#3281F8] rounded-full";
|
||||
}
|
||||
return "";
|
||||
}, [state]);
|
||||
|
||||
const trackStyles = getTrackStyles();
|
||||
const thumbStyles = getThumbStyles();
|
||||
const focusStyles = getFocusStyles();
|
||||
|
||||
const switchClasses = `
|
||||
relative
|
||||
inline-flex
|
||||
items-center
|
||||
cursor-pointer
|
||||
transition-all
|
||||
duration-200
|
||||
focus:outline-none
|
||||
focus-visible:shadow-[0_0_5px_3px_#3281F8]
|
||||
focus-visible:rounded-full
|
||||
${focusStyles}
|
||||
${className}
|
||||
`
|
||||
.trim()
|
||||
.replace(/\s+/g, " ");
|
||||
|
||||
const trackClasses = `
|
||||
${trackStyles}
|
||||
w-[40px]
|
||||
h-[24px]
|
||||
rounded-full
|
||||
transition-all
|
||||
duration-200
|
||||
flex
|
||||
items-center
|
||||
${checked ? "justify-end" : "justify-start"}
|
||||
p-[2px]
|
||||
`
|
||||
.trim()
|
||||
.replace(/\s+/g, " ");
|
||||
|
||||
const thumbClasses = `
|
||||
${thumbStyles}
|
||||
w-[var(--measures-sizing-020)]
|
||||
h-[var(--measures-sizing-020)]
|
||||
rounded-[var(--measures-radius-xlarge)]
|
||||
transition-all
|
||||
duration-200
|
||||
shadow-sm
|
||||
`
|
||||
.trim()
|
||||
.replace(/\s+/g, " ");
|
||||
|
||||
const labelClasses = `
|
||||
ml-[var(--measures-spacing-008)]
|
||||
font-inter
|
||||
font-normal
|
||||
text-[14px]
|
||||
leading-[20px]
|
||||
text-[var(--color-content-default-primary)]
|
||||
`
|
||||
.trim()
|
||||
.replace(/\s+/g, " ");
|
||||
|
||||
return (
|
||||
<SwitchView
|
||||
ref={ref}
|
||||
switchId={switchId}
|
||||
checked={checked}
|
||||
state={state}
|
||||
label={label}
|
||||
className={className}
|
||||
switchClasses={switchClasses}
|
||||
trackClasses={trackClasses}
|
||||
thumbClasses={thumbClasses}
|
||||
labelClasses={labelClasses}
|
||||
onClick={handleClick}
|
||||
onKeyDown={handleKeyDown}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
SwitchContainer.displayName = "Switch";
|
||||
|
||||
export default SwitchContainer;
|
||||
@@ -0,0 +1,30 @@
|
||||
export interface SwitchProps
|
||||
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onChange"> {
|
||||
checked?: boolean;
|
||||
onChange?: (
|
||||
_e:
|
||||
| React.MouseEvent<HTMLButtonElement>
|
||||
| React.KeyboardEvent<HTMLButtonElement>,
|
||||
) => void;
|
||||
onFocus?: (_e: React.FocusEvent<HTMLButtonElement>) => void;
|
||||
onBlur?: (_e: React.FocusEvent<HTMLButtonElement>) => void;
|
||||
state?: "default" | "hover" | "focus";
|
||||
label?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export interface SwitchViewProps {
|
||||
switchId: string;
|
||||
checked: boolean;
|
||||
state: "default" | "hover" | "focus";
|
||||
label?: string;
|
||||
className: string;
|
||||
switchClasses: string;
|
||||
trackClasses: string;
|
||||
thumbClasses: string;
|
||||
labelClasses: string;
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
onKeyDown: (e: React.KeyboardEvent<HTMLButtonElement>) => void;
|
||||
onFocus: (e: React.FocusEvent<HTMLButtonElement>) => void;
|
||||
onBlur: (e: React.FocusEvent<HTMLButtonElement>) => void;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { forwardRef } from "react";
|
||||
import type { SwitchViewProps } from "./Switch.types";
|
||||
|
||||
export const SwitchView = forwardRef<HTMLButtonElement, SwitchViewProps>(
|
||||
(
|
||||
{
|
||||
switchId,
|
||||
checked,
|
||||
label,
|
||||
switchClasses,
|
||||
trackClasses,
|
||||
thumbClasses,
|
||||
labelClasses,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
onFocus,
|
||||
onBlur,
|
||||
...rest
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
ref={ref}
|
||||
id={switchId}
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={checked}
|
||||
aria-label={label || "Toggle switch"}
|
||||
onClick={onClick}
|
||||
onKeyDown={onKeyDown}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
className={switchClasses}
|
||||
{...rest}
|
||||
>
|
||||
<div className={trackClasses}>
|
||||
<div className={thumbClasses} />
|
||||
</div>
|
||||
</button>
|
||||
{label && <span className={labelClasses}>{label}</span>}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
SwitchView.displayName = "SwitchView";
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from "./Switch.container";
|
||||
export type { SwitchProps } from "./Switch.types";
|
||||
Reference in New Issue
Block a user