Implement Custom Switch (#1013)

The `react-toggle` library is unmaintained and is preventing us from
properly updating React. I already wrote a Switch for the new Auto
Splitting IDE, so I converted it once again with Copilot.
This commit is contained in:
Christopher Serr
2025-02-16 14:01:16 +00:00
committed by GitHub
parent 2fbe28e4eb
commit 55162c047a
7 changed files with 84 additions and 123 deletions
+1 -27
View File
@@ -13,8 +13,7 @@
"react-dom": "^18.3.1",
"react-resizable": "^3.0.4",
"react-sidebar": "^3.0.2",
"react-toastify": "11.0.3",
"react-toggle": "^4.1.3"
"react-toastify": "11.0.3"
},
"devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.13",
@@ -25,7 +24,6 @@
"@types/react-dom": "^18.3.0",
"@types/react-resizable": "^3.0.3",
"@types/react-sidebar": "^3.0.2",
"@types/react-toggle": "^4.0.3",
"clean-webpack-plugin": "^4.0.0",
"css-loader": "7.1.2",
"eslint": "^9.20.1",
@@ -3420,16 +3418,6 @@
"@types/react": "*"
}
},
"node_modules/@types/react-toggle": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/react-toggle/-/react-toggle-4.0.5.tgz",
"integrity": "sha512-MHHEDe7GnF/EhLtI5sT70Dqab8rwlgjRZtu/u6gmfbYd+HeYxWiUSRog16+1BCfkz7Wy2VU6+TPU2oCsDtqDzA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
@@ -9105,20 +9093,6 @@
"react-dom": "^18 || ^19"
}
},
"node_modules/react-toggle": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.3.tgz",
"integrity": "sha512-WoPrvbwfQSvoagbrDnXPrlsxwzuhQIrs+V0I162j/s+4XPgY/YDAUmHSeWiroznfI73wj+MBydvW95zX8ABbSg==",
"license": "MIT",
"dependencies": {
"classnames": "^2.2.5"
},
"peerDependencies": {
"prop-types": ">= 15.3.0 < 19",
"react": ">= 15.3.0 < 19",
"react-dom": ">= 15.3.0 < 19"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+1 -3
View File
@@ -22,8 +22,7 @@
"react-dom": "^18.3.1",
"react-resizable": "^3.0.4",
"react-sidebar": "^3.0.2",
"react-toastify": "11.0.3",
"react-toggle": "^4.1.3"
"react-toastify": "11.0.3"
},
"devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.13",
@@ -34,7 +33,6 @@
"@types/react-dom": "^18.3.0",
"@types/react-resizable": "^3.0.3",
"@types/react-sidebar": "^3.0.2",
"@types/react-toggle": "^4.0.3",
"clean-webpack-plugin": "^4.0.0",
"css-loader": "7.1.2",
"eslint": "^9.20.1",
+49
View File
@@ -0,0 +1,49 @@
.switch {
position: relative;
width: 35px;
height: 20px;
display: inline-block;
span {
position: absolute;
background-color: #ffffff20;
border-radius: 20px;
top: 0;
left: 0;
right: 0;
bottom: 0;
transition: background-color .25s;
}
span::before {
background-color: white;
border-radius: 50%;
content: "";
position: absolute;
transition: all .25s;
left: 3px;
bottom: 3px;
height: 14px;
width: 14px;
}
&:hover input+span {
background-color: #ffffff40;
}
&:hover input:checked+span {
background-color: #ffffff80;
}
input:checked+span {
background-color: #ffffff60;
}
input:checked+span::before {
transform: translateX(15px);
}
input {
display: none;
}
}
-49
View File
@@ -1,49 +0,0 @@
@use 'variables';
$inactive-color: variables.$button-disabled-color;
$inactive-hover-color: variables.$hover-row-color;
$active-color: #1e4491;
$active-hover-color: #2356bd;
.toggle-checkbox {
display: flex;
justify-content: center;
align-items: center;
.react-toggle {
.react-toggle-track {
background: $inactive-color;
width: 36px;
height: variables.$settings-row-height;
border-radius: 20px;
}
&:hover:not(.react-toggle--disabled) {
.react-toggle-track {
background: $inactive-hover-color;
}
}
&--checked {
.react-toggle-track {
background: $active-color;
}
&:hover:not(.react-toggle--disabled) {
.react-toggle-track {
background: $active-hover-color;
}
}
.react-toggle-thumb {
left: 18px;
}
}
.react-toggle-thumb {
width: 17px;
height: 17px;
border: none;
box-shadow: none !important;
}
}
}
+16 -16
View File
@@ -3,13 +3,13 @@ import { Color, SettingsDescriptionValueJson } from "../livesplit-core";
import { assertNever, expect, Option } from "../util/OptionUtil";
import ColorPicker from "./ColorPicker";
import HotkeyButton from "./HotkeyButton";
import ToggleCheckbox from "./ToggleCheckbox";
import { UrlCache } from "../util/UrlCache";
import { FILE_EXT_IMAGES, openFileAsArrayBuffer } from "../util/FileUtil";
import * as FontList from "../util/FontList";
import { LiveSplitServer } from "../api/LiveSplitServer";
import { showDialog } from "./Dialog";
import { toast } from "react-toastify";
import Switch from "./Switch";
import "../css/Tooltip.scss";
import "../css/LiveSplitServerButton.scss";
@@ -202,9 +202,9 @@ export class SettingsComponent<T> extends React.Component<Props<T>> {
if ("Bool" in value) {
component = (
<div className="settings-value-box">
<ToggleCheckbox
value={value.Bool}
setValue={(value) => {
<Switch
checked={value.Bool}
setIsChecked={(value) => {
this.props.setValue(
valueIndex,
factory.fromBool(value)
@@ -323,9 +323,9 @@ export class SettingsComponent<T> extends React.Component<Props<T>> {
</div>;
} else {
const children = [
<ToggleCheckbox
value={value.OptionalString !== null}
setValue={(value) => {
<Switch
checked={value.OptionalString !== null}
setIsChecked={(value) => {
if (value) {
this.props.setValue(
valueIndex,
@@ -482,9 +482,9 @@ export class SettingsComponent<T> extends React.Component<Props<T>> {
component = (
<div className="settings-value-box optional-value">
<ToggleCheckbox
value={value.OptionalColor !== null}
setValue={(value) => {
<Switch
checked={value.OptionalColor !== null}
setIsChecked={(value) => {
if (value) {
this.props.setValue(
valueIndex,
@@ -743,9 +743,9 @@ export class SettingsComponent<T> extends React.Component<Props<T>> {
}
} else if ("OptionalTimingMethod" in value) {
const children = [
<ToggleCheckbox
value={value.OptionalTimingMethod !== null}
setValue={(value) => {
<Switch
checked={value.OptionalTimingMethod !== null}
setIsChecked={(value) => {
if (value) {
this.props.setValue(
valueIndex,
@@ -964,9 +964,9 @@ export class SettingsComponent<T> extends React.Component<Props<T>> {
);
} else if ("Font" in value) {
const children = [
<ToggleCheckbox
value={value.Font !== null}
setValue={(value) => {
<Switch
checked={value.Font !== null}
setIsChecked={(value) => {
if (value) {
this.props.setValue(
valueIndex,
+17
View File
@@ -0,0 +1,17 @@
import React from "react";
import "../css/Switch.scss";
export default function Switch({ checked, setIsChecked }: {
checked: boolean,
setIsChecked: (checked: boolean) => void,
}) {
return (
<label style={{ cursor: "pointer", display: "flex", justifyContent: "center", alignItems: "center" }}>
<div className="switch">
<input type="checkbox" checked={checked} onChange={(e) => setIsChecked(e.target.checked)} />
<span />
</div>
</label>
);
}
-28
View File
@@ -1,28 +0,0 @@
import * as React from "react";
import Toggle from 'react-toggle';
import "react-toggle/style.css";
import "../css/ToggleCheckbox.scss";
export interface Props {
value: boolean,
setValue: (value: boolean) => void,
}
export default class ToggleCheckbox extends React.Component<Props> {
constructor(props: Props) {
super(props);
}
public render() {
return (
<div className="toggle-checkbox">
<Toggle
checked={this.props.value}
icons={false}
onChange={(event) => this.props.setValue(event.target.checked)}
/>
</div>
);
}
}