Files
community-rule/app/components/Switch.js
T
adilallo c4a631a5d8
CI Pipeline / test (20) (pull_request) Successful in 2m55s
CI Pipeline / test (18) (pull_request) Successful in 3m32s
CI Pipeline / e2e (webkit) (pull_request) Has been cancelled
CI Pipeline / visual-regression (pull_request) Has been cancelled
CI Pipeline / performance (pull_request) Has been cancelled
CI Pipeline / storybook (pull_request) Has been cancelled
CI Pipeline / lint (pull_request) Has been cancelled
CI Pipeline / build (pull_request) Has been cancelled
CI Pipeline / e2e (chromium) (pull_request) Has been cancelled
CI Pipeline / e2e (firefox) (pull_request) Has been cancelled
Cleanup code and tests
2025-10-14 17:34:05 -06:00

164 lines
3.5 KiB
JavaScript

import React, { memo, useCallback, useId, forwardRef } from "react";
const Switch = memo(
forwardRef((props, ref) => {
const {
checked = false,
onChange,
onFocus,
onBlur,
state = "default",
label,
className = "",
...rest
} = props;
const switchId = useId();
const handleClick = useCallback(
(e) => {
if (onChange) {
onChange(e);
}
},
[onChange],
);
const handleKeyDown = useCallback(
(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
if (onChange) {
onChange(e);
}
}
},
[onChange],
);
const handleFocus = useCallback(
(e) => {
if (onFocus) {
onFocus(e);
}
},
[onFocus],
);
const handleBlur = useCallback(
(e) => {
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 (
<div className="flex items-center">
<button
ref={ref}
id={switchId}
type="button"
role="switch"
aria-checked={checked}
aria-label={label || "Toggle switch"}
onClick={handleClick}
onKeyDown={handleKeyDown}
onFocus={handleFocus}
onBlur={handleBlur}
className={switchClasses}
{...rest}
>
<div className={trackClasses}>
<div className={thumbClasses} />
</div>
</button>
{label && <span className={labelClasses}>{label}</span>}
</div>
);
}),
);
Switch.displayName = "Switch";
export default Switch;