migrate to new switch style (#669)

This commit is contained in:
prism 2026-01-24 02:02:15 +01:00 • committed by GitHub
parent 08174fb27e
commit cf250d8372
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 105 additions and 68 deletions

View file

@ -1,42 +1,64 @@
.vc-switch-container {
background: var(--primary-400);
border: 1px solid transparent;
border-radius: 16px;
box-sizing: border-box;
cursor: pointer;
height: 28px;
display: flex;
align-items: center;
position: relative;
width: 44px;
.high-contrast-mode & {
border-color: var(--border-strong);
}
}
.vc-switch-checked {
background: var(--brand-500);
border-color: var(--control-primary-border-default);
.vc-switch-indicator {
background: var(--switch-background-default);
border: 1px solid var(--switch-border-default);
border-radius: var(--radius-lg);
box-sizing: border-box;
cursor: pointer;
height: 24px;
position: relative;
width: 48px;
transition: background-color 200ms ease, border-color 200ms ease;
}
.vc-switch-disabled {
cursor: not-allowed;
opacity: 0.3;
opacity: 0.5;
}
.vc-switch-focusVisible {
/* stylelint-disable-next-line custom-property-pattern */
box-shadow: 0 0 0 4px var(--__adaptive-focus-ring-color, var(--border-focus, #00b0f4));
.vc-switch-disabled .vc-switch-indicator {
cursor: not-allowed;
}
.vc-switch-slider {
display: block;
height: 20px;
left: 0;
margin: 3px;
.vc-switch-focusVisible .vc-switch-indicator {
box-shadow: 0 0 0 2px var(--border-focus, #00a8fc);
}
.vc-switch-indicator:hover {
background: var(--switch-background-hover);
border-color: var(--switch-border-hover);
}
.vc-switch-checked .vc-switch-indicator {
background: var(--switch-background-selected-default);
border-color: var(--switch-border-selected-default);
}
.vc-switch-checked .vc-switch-indicator:hover {
background: var(--switch-background-selected-hover);
border-color: var(--switch-border-selected-hover);
}
.vc-switch-thumb {
align-items: center;
display: flex;
height: 24px;
justify-content: center;
left: 1px;
margin-left: -1px;
margin-top: -1px;
position: absolute;
width: 28px;
transition: 100ms transform ease-in-out;
overflow: visible;
width: 24px;
transition: left 150ms ease-in-out;
}
.vc-switch-checked .vc-switch-thumb {
left: 24px;
}
.vc-switch-input {
@ -54,4 +76,4 @@
pointer-events: none;
cursor: not-allowed
}
}
}

View file

@ -19,22 +19,23 @@
import "./Switch.css";
import { classNameFactory } from "@utils/css";
import { classes } from "@utils/misc";
import { useState } from "@webpack/common";
import type { FocusEvent } from "react";
import type { FocusEvent, Ref } from "react";
const switchCls = classNameFactory("vc-switch-");
const SWITCH_ON = "var(--brand-500)";
const SWITCH_OFF = "var(--primary-400)";
export interface SwitchProps {
disabled?: boolean;
checked: boolean;
onChange: (checked: boolean) => void;
disabled?: boolean;
id?: string;
hasIcon?: boolean;
innerRef?: Ref<HTMLInputElement>;
"aria-describedby"?: string;
"aria-labelledby"?: string;
}
export function Switch({ checked, onChange, disabled }: SwitchProps) {
export function Switch({ checked, onChange, disabled, id, hasIcon = false, innerRef, ...ariaProps }: SwitchProps) {
const [focusVisible, setFocusVisible] = useState(false);
// Due to how we wrap the invisible input, there is no good way to do this with css.
@ -48,44 +49,58 @@ export function Switch({ checked, onChange, disabled }: SwitchProps) {
};
return (
<div>
<div className={classes(switchCls("container", { checked, disabled, focusVisible }))}>
<label className={switchCls("container", { checked, disabled, focusVisible })}>
<div
className={switchCls("indicator")}
data-mana-component="switch"
>
<svg
className={switchCls("slider")}
viewBox="0 0 28 20"
preserveAspectRatio="xMinYMid meet"
className={switchCls("thumb")}
viewBox="0 0 24 24"
preserveAspectRatio="xMidYMin meet"
aria-hidden="true"
style={{
transform: checked ? "translateX(12px)" : "translateX(-3px)",
}}
>
<rect fill="white" x="4" y="0" height="20" width="20" rx="10" />
<svg viewBox="0 0 20 20" fill="none">
{checked ? (
<>
<path fill={SWITCH_ON} d="M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z" />
<path fill={SWITCH_ON} d="M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z" />
</>
) : (
<>
<path fill={SWITCH_OFF} d="M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z" />
<path fill={SWITCH_OFF} d="M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z" />
</>
)}
</svg>
<rect
fill="var(--switch-thumb-background-default)"
x={4}
y={4}
width={16}
height={16}
rx={8}
/>
{hasIcon && (
<svg viewBox="0 0 24 24" fill="none">
<g transform="translate(4 4)">
{checked ? (
<>
<path fill="var(--switch-thumb-icon-active)" d="M6.31649 11.88304L5.0437 10.61032L11.44792 4.20604L12.72072 5.47883L6.31649 11.88304Z" />
<path fill="var(--switch-thumb-icon-active)" d="M3.26914 8.87224L4.54194 7.59943L7.5588 10.61632L6.28601 11.88912L3.26914 8.87224Z" />
</>
) : (
<>
<path fill="var(--switch-thumb-icon-default)" d="M4.10585 5.3837L5.37864 4.11091L11.884 10.61632L10.6112 11.88912L4.10585 5.3837Z" />
<path fill="var(--switch-thumb-icon-default)" d="M10.61632 4.11091L11.88912 5.3837L5.3837 11.88912L4.11091 10.61632L10.61632 4.11091Z" />
</>
)}
</g>
</svg>
)}
</svg>
<input
onFocus={handleFocusChange}
onBlur={handleFocusChange}
disabled={disabled}
type="checkbox"
className={switchCls("input")}
tabIndex={0}
checked={checked}
onChange={e => onChange(e.currentTarget.checked)}
/>
</div>
</div>
<input
ref={innerRef}
id={id}
type="checkbox"
role="switch"
className={switchCls("input")}
tabIndex={0}
checked={checked}
disabled={disabled}
onChange={e => onChange(e.currentTarget.checked)}
onFocus={handleFocusChange}
onBlur={handleFocusChange}
{...ariaProps}
/>
</label>
);
}