Settings: Improve layout of a setting section and error

This commit is contained in:
Nuckyz 2025-07-16 18:15:52 -03:00
parent 828358bd2e
commit d0869c41cd
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
10 changed files with 77 additions and 33 deletions

View file

@ -116,7 +116,11 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
);
});
return <Flex flexDirection="column" style={{ gap: 12, marginBottom: 16 }}>{options}</Flex>;
return (
<div className="vc-plugins-settings">
{options}
</div>
);
}
function renderMoreUsers(_label: string, count: number) {

View file

@ -16,11 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { wordsFromCamel, wordsToTitle } from "@utils/text";
import { Switch } from "@components/settings/Switch";
import { PluginOptionBoolean } from "@utils/types";
import { Forms, React, Switch, useState } from "@webpack/common";
import { React, useState } from "@webpack/common";
import { resolveError, SettingProps } from "./Common";
import { resolveError, SettingProps, SettingsSection } from "./Common";
export function BooleanSetting({ option, pluginSettings, definedSettings, id, onChange }: SettingProps<PluginOptionBoolean>) {
const def = pluginSettings[id] ?? option.default;
@ -40,20 +40,9 @@ export function BooleanSetting({ option, pluginSettings, definedSettings, id, on
}
return (
<Forms.FormSection>
<Switch
value={state}
onChange={handleChange}
note={option.description}
disabled={option.disabled?.call(definedSettings) ?? false}
{...option.componentProps}
hideBorder
style={{ marginBottom: "0.5em" }}
>
{wordsToTitle(wordsFromCamel(id))}
</Switch>
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}
</Forms.FormSection>
<SettingsSection name={id} description={option.description} error={error} inlineSetting>
<Switch checked={state} onChange={handleChange} />
</SettingsSection>
);
}

View file

@ -4,12 +4,15 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Margins } from "@utils/margins";
import { classNameFactory } from "@api/Styles";
import { classes } from "@utils/misc";
import { wordsFromCamel, wordsToTitle } from "@utils/text";
import { DefinedSettings, PluginOptionBase } from "@utils/types";
import { Forms } from "@webpack/common";
import { Text } from "@webpack/common";
import { PropsWithChildren } from "react";
export const cl = classNameFactory("vc-plugins-setting-");
interface SettingBaseProps<T> {
option: T;
onChange(newValue: any): void;
@ -34,15 +37,20 @@ interface SettingsSectionProps extends PropsWithChildren {
name: string;
description: string;
error: string | null;
inlineSetting?: boolean;
}
export function SettingsSection({ name, description, error, children }: SettingsSectionProps) {
export function SettingsSection({ name, description, error, inlineSetting, children }: SettingsSectionProps) {
return (
<Forms.FormSection>
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(name))}</Forms.FormTitle>
<Forms.FormText className={Margins.bottom20} type="description">{description}</Forms.FormText>
{children}
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}
</Forms.FormSection>
<div className={cl("section")}>
<div className={classes(cl("content"), inlineSetting && cl("inline"))}>
<div className={cl("label")}>
{name && <Text className={cl("title")} variant="text-md/medium">{wordsToTitle(wordsFromCamel(name))}</Text>}
{description && <Text className={cl("description")} variant="text-sm/normal">{description}</Text>}
</div>
{children}
</div>
{error && <Text className={cl("error")} variant="text-sm/normal">{error}</Text>}
</div>
);
}

View file

@ -53,9 +53,9 @@ export function NumberSetting({ option, pluginSettings, definedSettings, id, onC
<TextInput
type="number"
pattern="-?[0-9]+"
placeholder={option.placeholder ?? "Enter a number"}
value={state}
onChange={handleChange}
placeholder={option.placeholder ?? "Enter a number"}
disabled={option.disabled?.call(definedSettings) ?? false}
{...option.componentProps}
/>

View file

@ -41,14 +41,14 @@ export function SelectSetting({ option, pluginSettings, definedSettings, onChang
return (
<SettingsSection name={id} description={option.description} error={error}>
<Select
isDisabled={option.disabled?.call(definedSettings) ?? false}
options={option.options}
placeholder={option.placeholder ?? "Select an option"}
options={option.options}
maxVisibleItems={5}
closeOnSelect={true}
select={handleChange}
isSelected={v => v === state}
serialize={v => String(v)}
isDisabled={option.disabled?.call(definedSettings) ?? false}
{...option.componentProps}
/>
</SettingsSection>

View file

@ -39,7 +39,6 @@ export function SliderSetting({ option, pluginSettings, definedSettings, id, onC
return (
<SettingsSection name={id} description={option.description} error={error}>
<Slider
disabled={option.disabled?.call(definedSettings) ?? false}
markers={option.markers}
minValue={option.markers[0]}
maxValue={option.markers[option.markers.length - 1]}
@ -47,6 +46,7 @@ export function SliderSetting({ option, pluginSettings, definedSettings, id, onC
onValueChange={handleChange}
onValueRender={(v: number) => String(v.toFixed(2))}
stickToMarkers={option.stickToMarkers ?? true}
disabled={option.disabled?.call(definedSettings) ?? false}
{...option.componentProps}
/>
</SettingsSection>

View file

@ -40,11 +40,11 @@ export function TextSetting({ option, pluginSettings, definedSettings, id, onCha
<SettingsSection name={id} description={option.description} error={error}>
<TextInput
type="text"
placeholder={option.placeholder ?? "Enter a value"}
value={state}
onChange={handleChange}
placeholder={option.placeholder ?? "Enter a value"}
disabled={option.disabled?.call(definedSettings) ?? false}
maxLength={null}
disabled={option.disabled?.call(definedSettings) ?? false}
{...option.componentProps}
/>
</SettingsSection>

View file

@ -16,6 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import "./styles.css";
import { OptionType } from "@utils/types";
import { ComponentType } from "react";

View file

@ -0,0 +1,35 @@
.vc-plugins-setting-section {
display: flex;
flex-direction: column;
gap: 0.5em;
}
.vc-plugins-setting-content {
display: flex;
flex-direction: column;
gap: 0.5em;
}
.vc-plugins-setting-inline {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.vc-plugins-setting-label {
display: flex;
flex-direction: column;
gap: 0.25em;
}
.vc-plugins-setting-title {
color: var(--header-primary);
}
.vc-plugins-setting-description {
color: var(--header-secondary);
}
.vc-plugins-setting-error {
color: var(--text-danger);
}

View file

@ -74,3 +74,9 @@
.vc-plugins-info-icon:not(:hover, :focus) {
color: var(--text-muted);
}
.vc-plugins-settings {
display: flex;
flex-direction: column;
gap: 1.25em;
}