feat: each settings component handles state, + fix selects again lol

This commit is contained in:
Lewis Crichton 2023-09-10 14:09:00 +01:00
parent 482caf0c5b
commit 74f9b1a022
No known key found for this signature in database
9 changed files with 88 additions and 62 deletions

View file

@ -243,9 +243,11 @@ function ThemesTab() {
switch (varInfo.type) { switch (varInfo.type) {
case "text": case "text":
case "color": case "color":
case "select":
normalizedValue = varInfo.default; normalizedValue = varInfo.default;
break; break;
case "select":
normalizedValue = varInfo.options.find(v => v.name === varInfo.default)!.value;
break;
case "checkbox": case "checkbox":
normalizedValue = varInfo.default ? "1" : "0"; normalizedValue = varInfo.default ? "1" : "0";
break; break;

View file

@ -7,7 +7,7 @@
import { useSettings } from "@api/Settings"; import { useSettings } from "@api/Settings";
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
import { Text, useState } from "@webpack/common"; import { Text } from "@webpack/common";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { UserstyleHeader } from "usercss-meta"; import { UserstyleHeader } from "usercss-meta";
@ -24,98 +24,79 @@ export function UserCSSSettingsModal({ modalProps, theme }: UserCSSSettingsModal
const controls: ReactNode[] = []; const controls: ReactNode[] = [];
function updateSetting(key: string, value: string, setValue: (value: string) => void) { function updateSetting(key: string, value: string) {
themeSettings[key] = value; themeSettings[key] = value;
setValue(value);
} }
for (const [name, varInfo] of Object.entries(theme.vars)) { for (const [name, varInfo] of Object.entries(theme.vars)) {
switch (varInfo.type) { switch (varInfo.type) {
case "text": { case "text": {
const [value, setValue] = useState(themeSettings[name]);
controls.push( controls.push(
<SettingTextComponent <SettingTextComponent
label={varInfo.label} label={varInfo.label}
name={name} name={name}
value={value} themeSettings={themeSettings}
onChange={v => updateSetting(name, v, setValue)}
/> />
); );
break; break;
} }
case "checkbox": { case "checkbox": {
const [value, setValue] = useState(themeSettings[name]);
controls.push( controls.push(
<SettingBooleanComponent <SettingBooleanComponent
label={varInfo.label} label={varInfo.label}
name={name} name={name}
value={value} themeSettings={themeSettings}
onChange={v => updateSetting(name, v, setValue)}
/> />
); );
break; break;
} }
case "color": { case "color": {
const [value, setValue] = useState(themeSettings[name]);
controls.push( controls.push(
<SettingColorComponent <SettingColorComponent
label={varInfo.label} label={varInfo.label}
name={name} name={name}
value={value} themeSettings={themeSettings}
onChange={v => updateSetting(name, v, setValue)}
/> />
); );
break; break;
} }
case "number": { case "number": {
const [value, setValue] = useState(themeSettings[name]);
controls.push( controls.push(
<SettingNumberComponent <SettingNumberComponent
label={varInfo.label} label={varInfo.label}
name={name} name={name}
value={value} themeSettings={themeSettings}
onChange={v => updateSetting(name, v, setValue)}
/> />
); );
break; break;
} }
case "select": { case "select": {
const [value, setValue] = useState(themeSettings[name]);
controls.push( controls.push(
<SettingSelectComponent <SettingSelectComponent
label={varInfo.label} label={varInfo.label}
name={name} name={name}
options={varInfo.options} options={varInfo.options}
value={value}
default={varInfo.default} default={varInfo.default}
onChange={v => updateSetting(name, v, setValue)} themeSettings={themeSettings}
/> />
); );
break; break;
} }
case "range": { case "range": {
const [value, setValue] = useState(themeSettings[name]);
controls.push( controls.push(
<SettingRangeComponent <SettingRangeComponent
label={varInfo.label} label={varInfo.label}
name={name} name={name}
value={value}
default={varInfo.default} default={varInfo.default}
min={varInfo.min} min={varInfo.min}
max={varInfo.max} max={varInfo.max}
step={varInfo.step} step={varInfo.step}
onChange={v => updateSetting(name, v, setValue)} themeSettings={themeSettings}
/> />
); );
break; break;

View file

@ -4,22 +4,31 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import { Forms, Switch } from "@webpack/common"; import { Forms, Switch, useState } from "@webpack/common";
interface Props { interface Props {
label: string; label: string;
name: string; name: string;
value: string; themeSettings: Record<string, string>;
onChange: (value: string) => void;
} }
export function SettingBooleanComponent({ label, name, value, onChange }: Props) { export function SettingBooleanComponent({ label, name, themeSettings }: Props) {
const [value, setValue] = useState(themeSettings[name]);
function handleChange(value: boolean) {
const corrected = value ? "1" : "0";
setValue(corrected);
themeSettings[name] = corrected;
}
return ( return (
<Forms.FormSection> <Forms.FormSection>
<Switch <Switch
key={name} key={name}
value={value === "1"} value={value === "1"}
onChange={v => onChange(v ? "1" : "0")} onChange={handleChange}
hideBorder hideBorder
style={{ marginBottom: "0.5em" }} style={{ marginBottom: "0.5em" }}
> >

View file

@ -55,11 +55,20 @@ function ColorPicker(props: ColorPickerProps) {
interface Props { interface Props {
label: string; label: string;
name: string; name: string;
value: string; themeSettings: Record<string, string>;
onChange: (value: string) => void;
} }
export function SettingColorComponent({ label, name, value, onChange }: Props) { export function SettingColorComponent({ label, name, themeSettings }: Props) {
const [value, setValue] = useState(themeSettings[name]);
function handleChange(value: number) {
const corrected = "#" + (value?.toString(16).padStart(6, "0") ?? "000000");
setValue(corrected);
themeSettings[name] = corrected;
}
const normalizedValue = TinyColor(value).toHex(); const normalizedValue = TinyColor(value).toHex();
return ( return (
@ -68,7 +77,7 @@ export function SettingColorComponent({ label, name, value, onChange }: Props) {
<ColorPicker <ColorPicker
key={name} key={name}
value={parseInt(normalizedValue, 16)} value={parseInt(normalizedValue, 16)}
onChange={v => onChange("#" + (v?.toString(16).padStart(6, "0") ?? "000000"))} onChange={handleChange}
/> />
</Forms.FormSection> </Forms.FormSection>
); );

View file

@ -4,16 +4,23 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import { Forms, TextInput } from "@webpack/common"; import { Forms, TextInput, useState } from "@webpack/common";
interface Props { interface Props {
label: string; label: string;
name: string; name: string;
value: string; themeSettings: Record<string, string>;
onChange: (value: string) => void;
} }
export function SettingNumberComponent({ label, name, value, onChange }: Props) { export function SettingNumberComponent({ label, name, themeSettings }: Props) {
const [value, setValue] = useState(themeSettings[name]);
function handleChange(value: string) {
setValue(value);
themeSettings[name] = value;
}
return ( return (
<Forms.FormSection> <Forms.FormSection>
<Forms.FormTitle tag="h5">{label}</Forms.FormTitle> <Forms.FormTitle tag="h5">{label}</Forms.FormTitle>
@ -22,7 +29,7 @@ export function SettingNumberComponent({ label, name, value, onChange }: Props)
pattern="-?[0-9]+" pattern="-?[0-9]+"
key={name} key={name}
value={value} value={value}
onChange={onChange} onChange={handleChange}
/> />
</Forms.FormSection> </Forms.FormSection>
); );

View file

@ -4,20 +4,29 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import { Forms, Slider } from "@webpack/common"; import { Forms, Slider, useState } from "@webpack/common";
interface Props { interface Props {
label: string; label: string;
name: string; name: string;
value: string;
default: number; default: number;
min?: number; min?: number;
max?: number; max?: number;
step?: number; step?: number;
onChange: (value: string) => void; themeSettings: Record<string, string>;
} }
export function SettingRangeComponent({ label, name, value, default: def, min, max, step, onChange }: Props) { export function SettingRangeComponent({ label, name, default: def, min, max, step, themeSettings }: Props) {
const [value, setValue] = useState(themeSettings[name]);
function handleChange(value: number) {
const corrected = value.toString();
setValue(corrected);
themeSettings[name] = corrected;
}
const markers: number[] = []; const markers: number[] = [];
// defaults taken from https://github.com/openstyles/stylus/wiki/Writing-UserCSS#default-value // defaults taken from https://github.com/openstyles/stylus/wiki/Writing-UserCSS#default-value
@ -31,7 +40,7 @@ export function SettingRangeComponent({ label, name, value, default: def, min, m
<Slider <Slider
initialValue={parseInt(value, 10)} initialValue={parseInt(value, 10)}
defaultValue={def} defaultValue={def}
onValueChange={v => onChange(v.toString())} onValueChange={handleChange}
minValue={min} minValue={min}
maxValue={max} maxValue={max}

View file

@ -5,7 +5,7 @@
*/ */
import { identity } from "@utils/misc"; import { identity } from "@utils/misc";
import { ComponentTypes, Forms, Select } from "@webpack/common"; import { ComponentTypes, Forms, Select, useState } from "@webpack/common";
interface Props { interface Props {
label: string; label: string;
@ -15,12 +15,19 @@ interface Props {
label: string; label: string;
value: string; value: string;
}[]; }[];
value: string;
default: string; default: string;
onChange: (value: string) => void; themeSettings: Record<string, string>;
} }
export function SettingSelectComponent({ label, name, options, value, default: def, onChange }: Props) { export function SettingSelectComponent({ label, name, options, default: def, themeSettings }: Props) {
const [value, setValue] = useState(themeSettings[name]);
function handleChange(value: string) {
setValue(value);
themeSettings[name] = value;
}
const opts = options.map(option => ({ const opts = options.map(option => ({
disabled: false, disabled: false,
@ -39,7 +46,7 @@ export function SettingSelectComponent({ label, name, options, value, default: d
options={opts} options={opts}
closeOnSelect={true} closeOnSelect={true}
select={onChange} select={handleChange}
isSelected={v => v === value} isSelected={v => v === value}
serialize={identity} serialize={identity}
/> />

View file

@ -4,23 +4,30 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import { Forms, TextInput } from "@webpack/common"; import { Forms, TextInput, useState } from "@webpack/common";
interface Props { interface Props {
label: string; label: string;
name: string; name: string;
value: string; themeSettings: Record<string, string>;
onChange: (value: string) => void;
} }
export function SettingTextComponent({ label, name, value, onChange }: Props) { export function SettingTextComponent({ label, name, themeSettings }: Props) {
const [value, setValue] = useState(themeSettings[name]);
function handleChange(value: string) {
setValue(value);
themeSettings[name] = value;
}
return ( return (
<Forms.FormSection> <Forms.FormSection>
<Forms.FormTitle tag="h5">{label}</Forms.FormTitle> <Forms.FormTitle tag="h5">{label}</Forms.FormTitle>
<TextInput <TextInput
key={name} key={name}
value={value} value={value}
onChange={onChange} onChange={handleChange}
/> />
</Forms.FormSection> </Forms.FormSection>
); );

View file

@ -88,11 +88,6 @@ export async function compileUsercss(fileName: string) {
varsToPass[k] = `${varsToPass[k]}${v.units ?? "px"}`; varsToPass[k] = `${varsToPass[k]}${v.units ?? "px"}`;
break; break;
} }
case "select": {
varsToPass[k] = v.options.find(opt => opt.name === varsToPass[k])!.value;
break;
}
} }
} }