feat: Typesafe Settings Definitions (#403)

Co-authored-by: Ven <vendicated@riseup.net>
This commit is contained in:
Justice Almanzar 2023-01-13 17:15:45 -05:00 committed by GitHub
parent 6c5fcc4119
commit ea748dfb60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 288 additions and 180 deletions

View file

@ -144,6 +144,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
onChange={onChange}
onError={onError}
pluginSettings={pluginSettings}
definedSettings={plugin.settings}
/>
);
});

View file

@ -21,7 +21,7 @@ import { Forms, React, Select } from "@webpack/common";
import { ISettingElementProps } from ".";
export function SettingBooleanComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionBoolean>) {
export function SettingBooleanComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionBoolean>) {
const def = pluginSettings[id] ?? option.default;
const [state, setState] = React.useState(def ?? false);
@ -37,7 +37,7 @@ export function SettingBooleanComponent({ option, pluginSettings, id, onChange,
];
function handleChange(newValue: boolean): void {
const isValid = (option.isValid && option.isValid(newValue)) ?? true;
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
if (typeof isValid === "string") setError(isValid);
else if (!isValid) setError("Invalid input provided.");
else {
@ -51,7 +51,7 @@ export function SettingBooleanComponent({ option, pluginSettings, id, onChange,
<Forms.FormSection>
<Forms.FormTitle>{option.description}</Forms.FormTitle>
<Select
isDisabled={option.disabled?.() ?? false}
isDisabled={option.disabled?.call(definedSettings) ?? false}
options={options}
placeholder={option.placeholder ?? "Select an option"}
maxVisibleItems={5}

View file

@ -23,7 +23,7 @@ import { ISettingElementProps } from ".";
const MAX_SAFE_NUMBER = BigInt(Number.MAX_SAFE_INTEGER);
export function SettingNumericComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionNumber>) {
export function SettingNumericComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionNumber>) {
function serialize(value: any) {
if (option.type === OptionType.BIGINT) return BigInt(value);
return Number(value);
@ -37,7 +37,7 @@ export function SettingNumericComponent({ option, pluginSettings, id, onChange,
}, [error]);
function handleChange(newValue) {
const isValid = (option.isValid && option.isValid(newValue)) ?? true;
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
if (typeof isValid === "string") setError(isValid);
else if (!isValid) setError("Invalid input provided.");
else if (option.type === OptionType.NUMBER && BigInt(newValue) >= MAX_SAFE_NUMBER) {
@ -58,7 +58,7 @@ export function SettingNumericComponent({ option, pluginSettings, id, onChange,
value={state}
onChange={handleChange}
placeholder={option.placeholder ?? "Enter a number"}
disabled={option.disabled?.() ?? false}
disabled={option.disabled?.call(definedSettings) ?? false}
{...option.componentProps}
/>
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}

View file

@ -21,7 +21,7 @@ import { Forms, React, Select } from "@webpack/common";
import { ISettingElementProps } from ".";
export function SettingSelectComponent({ option, pluginSettings, onChange, onError, id }: ISettingElementProps<PluginOptionSelect>) {
export function SettingSelectComponent({ option, pluginSettings, definedSettings, onChange, onError, id }: ISettingElementProps<PluginOptionSelect>) {
const def = pluginSettings[id] ?? option.options?.find(o => o.default)?.value;
const [state, setState] = React.useState<any>(def ?? null);
@ -32,7 +32,7 @@ export function SettingSelectComponent({ option, pluginSettings, onChange, onErr
}, [error]);
function handleChange(newValue) {
const isValid = (option.isValid && option.isValid(newValue)) ?? true;
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
if (typeof isValid === "string") setError(isValid);
else if (!isValid) setError("Invalid input provided.");
else {
@ -45,7 +45,7 @@ export function SettingSelectComponent({ option, pluginSettings, onChange, onErr
<Forms.FormSection>
<Forms.FormTitle>{option.description}</Forms.FormTitle>
<Select
isDisabled={option.disabled?.() ?? false}
isDisabled={option.disabled?.call(definedSettings) ?? false}
options={option.options}
placeholder={option.placeholder ?? "Select an option"}
maxVisibleItems={5}

View file

@ -29,7 +29,7 @@ export function makeRange(start: number, end: number, step = 1) {
return ranges;
}
export function SettingSliderComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionSlider>) {
export function SettingSliderComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionSlider>) {
const def = pluginSettings[id] ?? option.default;
const [error, setError] = React.useState<string | null>(null);
@ -39,7 +39,7 @@ export function SettingSliderComponent({ option, pluginSettings, id, onChange, o
}, [error]);
function handleChange(newValue: number): void {
const isValid = (option.isValid && option.isValid(newValue)) ?? true;
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
if (typeof isValid === "string") setError(isValid);
else if (!isValid) setError("Invalid input provided.");
else {
@ -52,7 +52,7 @@ export function SettingSliderComponent({ option, pluginSettings, id, onChange, o
<Forms.FormSection>
<Forms.FormTitle>{option.description}</Forms.FormTitle>
<Slider
disabled={option.disabled?.() ?? false}
disabled={option.disabled?.call(definedSettings) ?? false}
markers={option.markers}
minValue={option.markers[0]}
maxValue={option.markers[option.markers.length - 1]}

View file

@ -21,7 +21,7 @@ import { Forms, React, TextInput } from "@webpack/common";
import { ISettingElementProps } from ".";
export function SettingTextComponent({ option, pluginSettings, id, onChange, onError }: ISettingElementProps<PluginOptionString>) {
export function SettingTextComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionString>) {
const [state, setState] = React.useState(pluginSettings[id] ?? option.default ?? null);
const [error, setError] = React.useState<string | null>(null);
@ -30,7 +30,7 @@ export function SettingTextComponent({ option, pluginSettings, id, onChange, onE
}, [error]);
function handleChange(newValue) {
const isValid = (option.isValid && option.isValid(newValue)) ?? true;
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
if (typeof isValid === "string") setError(isValid);
else if (!isValid) setError("Invalid input provided.");
else {
@ -47,7 +47,7 @@ export function SettingTextComponent({ option, pluginSettings, id, onChange, onE
value={state}
onChange={handleChange}
placeholder={option.placeholder ?? "Enter a value"}
disabled={option.disabled?.() ?? false}
disabled={option.disabled?.call(definedSettings) ?? false}
{...option.componentProps}
/>
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { PluginOptionBase } from "@utils/types";
import { DefinedSettings, PluginOptionBase } from "@utils/types";
export interface ISettingElementProps<T extends PluginOptionBase> {
option: T;
@ -27,6 +27,7 @@ export interface ISettingElementProps<T extends PluginOptionBase> {
};
id: string;
onError(hasError: boolean): void;
definedSettings?: DefinedSettings;
}
export * from "./BadgeComponent";