You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
First off all, sorry to hijack the issues tab but seeing as discussions isn't enabled on this repo I thought what the heck.
Intro
I needed a bit more modifications and needed the component to work in React 19. I also love Typescript so added that too. A few subjective improvements as well. The API should remain the same. I haven't tested all functionality but it seems to work just fine on my end.
Updates
Refactor to use React hooks
Add Typescript
Add CSS variables support for colors
Code
getBackgroundColor.ts
/**
* Converts a shorthand hex color to a full 6-digit hex color.
* @param color The shorthand hex color string (e.g., "#abc").
* @returns The full 6-digit hex color string (e.g., "#aabbcc").
*/
export function convertShorthandColor(color: string): string {
if (color.length === 7) {
return color;
}
let sixDigitColor = "#";
for (let i = 1; i < 4; i += 1) {
sixDigitColor += color[i] + color[i];
}
return sixDigitColor;
}
/**
* Converts an RGB color string to a hex color string.
* @param rgb The RGB color string (e.g., "rgb(255, 255, 255)").
* @returns The hex color string (e.g., "#ffffff").
*/
function rgbToHex(rgb: string): string {
const result = /^rgba?\((\d+),\s*(\d+),\s*(\d+)/i.exec(rgb);
if (!result) {
console.warn(`Invalid RGB color format: "${rgb}". Using fallback #000000.`);
return "#000000";
}
const r = parseInt(result[1], 10);
const g = parseInt(result[2], 10);
const b = parseInt(result[3], 10);
return `#${[r, g, b].map((x) => x.toString(16).padStart(2, "0")).join("")}`;
}
/**
* Resolves a color string that may include CSS variables to its computed hex value.
* Handles both hexadecimal and RGB color formats.
* @param color The color string, either a hex code or a CSS variable (e.g., "var(--gray-200)").
* @returns The resolved hex color string, or a fallback if resolution fails.
*/
export function resolveColor(color: string): string {
if (color.startsWith("var(")) {
if (typeof window === "undefined" || !document) {
console.warn(
`Cannot resolve CSS variables on the server. Using fallback color: "${color}".`
);
return "#000000"; // Fallback color
}
try {
const variableName = color.slice(4, -1).trim();
const resolved = getComputedStyle(document.documentElement)
.getPropertyValue(variableName)
.trim();
if (!resolved) {
console.warn(
`CSS variable "${variableName}" is not defined. Using fallback color #000000.`
);
return "#000000"; // Fallback color
}
// If the resolved color is another CSS variable, attempt to resolve it recursively
if (resolved.startsWith("var(")) {
return resolveColor(resolved);
}
// Handle RGB and HEX formats
if (/^rgb/i.test(resolved)) {
return rgbToHex(resolved);
}
if (/^#([0-9A-F]{3}){1,2}$/i.test(resolved)) {
return convertShorthandColor(resolved);
}
console.warn(
`Resolved color "${resolved}" for variable "${variableName}" is not a valid HEX or RGB color. Using fallback #000000.`
);
return "#000000"; // Fallback color
} catch (error) {
console.error(
`Failed to resolve color: "${color}". Using fallback #000000.`,
error
);
return "#000000";
}
}
// If the color is in RGB format, convert it to HEX
if (/^rgb/i.test(color)) {
return rgbToHex(color);
}
// If the color is a shorthand hex, convert it to 6-digit hex
if (/^#([0-9A-F]{3})$/i.test(color)) {
return convertShorthandColor(color);
}
// If the color is a full hex, return it as is
if (/^#([0-9A-F]{6})$/i.test(color)) {
return color;
}
console.warn(`Unsupported color format: "${color}". Using fallback #000000.`);
return "#000000"; // Fallback color
}
/**
* Calculates the interpolated background color based on the position.
* @param pos Current position of the handle.
* @param checkedPos Position when the switch is checked.
* @param uncheckedPos Position when the switch is unchecked.
* @param offColor Hex color when the switch is off.
* @param onColor Hex color when the switch is on.
* @returns The interpolated hex color string.
*/
export function createBackgroundColor(
pos: number,
checkedPos: number,
uncheckedPos: number,
offColor: string,
onColor: string
): string {
const relativePos = (pos - uncheckedPos) / (checkedPos - uncheckedPos);
if (relativePos <= 0) {
return offColor;
}
if (relativePos >= 1) {
return onColor;
}
let newColor = "#";
for (let i = 1; i < 6; i += 2) {
const offComponent = parseInt(offColor.substr(i, 2), 16);
const onComponent = parseInt(onColor.substr(i, 2), 16);
const weightedValue = Math.round(
(1 - relativePos) * offComponent + relativePos * onComponent
);
let newComponent = weightedValue.toString(16);
if (newComponent.length === 1) {
newComponent = `0${newComponent}`;
}
newColor += newComponent;
}
return newColor;
}
/**
* Gets the background color based on the current position and color settings.
* Resolves CSS variables to their computed hex values before interpolation.
* @param pos Current position of the handle.
* @param checkedPos Position when the switch is checked.
* @param uncheckedPos Position when the switch is unchecked.
* @param offColor Hex color or CSS variable when the switch is off.
* @param onColor Hex color or CSS variable when the switch is on.
* @returns The calculated background color as a hex string.
*/
export default function getBackgroundColor(
pos: number,
checkedPos: number,
uncheckedPos: number,
offColor: string,
onColor: string
): string {
const resolvedOffColor = resolveColor(offColor);
const resolvedOnColor = resolveColor(onColor);
const sixDigitOffColor = convertShorthandColor(resolvedOffColor);
const sixDigitOnColor = convertShorthandColor(resolvedOnColor);
return createBackgroundColor(
pos,
checkedPos,
uncheckedPos,
sixDigitOffColor,
sixDigitOnColor
);
}
Intro
I needed a bit more modifications and needed the component to work in React 19. I also love Typescript so added that too. A few subjective improvements as well. The API should remain the same. I haven't tested all functionality but it seems to work just fine on my end.
Updates
Code
getBackgroundColor.ts
Switch.tsx
The text was updated successfully, but these errors were encountered: