Files
Common/src/client/ui/components/settings/settings.tsx
T
2026-06-29 14:22:14 +02:00

128 lines
4.5 KiB
TypeScript

import { Atom } from "@rbxts/charm";
import Vide, { Case, For, Source, source, Switch } from "@rbxts/vide";
import { SettingDefinition, SettingRealType, SettingType } from "../../../../shared/typings/settings";
import { SettingsController } from "../../../controllers/settings";
import { Toggle } from "../global/toggle";
/**
* The settings menu as vide components.
* @param settingsController The settings Controller for fetching settings.
* @returns A Vide node with the settings menu.
*/
export function Settings(settingsController: SettingsController) {
const keys = source(settingsController.getSections());
const panel = source(keys()[0]);
return (
<frame
BackgroundColor3={new Color3(0, 0, 0)}
Position={UDim2.fromScale(0.05, 0.05)}
Size={UDim2.fromScale(0.9, 0.9)}
>
<uicorner />
<uistroke Color={new Color3(1, 1, 1)} />
<scrollingframe BackgroundTransparency={1} Size={UDim2.fromScale(0.3, 1)}>
<uigridlayout CellPadding={UDim2.fromOffset(0, 0)} CellSize={new UDim2(1, 0, 0, 40)} />
<For each={keys}>{(group) => <SettingsGroup name={group} panelSource={panel} />}</For>
</scrollingframe>
<frame Position={UDim2.fromScale(0.3, 0)} Size={new UDim2(0, 1, 1, 0)} />
<frame BackgroundTransparency={1} Position={new UDim2(0.3, 10, 0, 0)} Size={new UDim2(0.7, -10, 1, 0)}>
<uilistlayout Padding={new UDim(0, 10)} />
<For each={() => settingsController.getSectionWithDef(panel())}>
{([def, atom]) => <SettingsEntry atom={atom} definition={def} />}
</For>
</frame>
</frame>
);
}
/**
* An entry in the settings menu.
* @param props Properties of the entry.
* @param props.definition Setting definition.
* @param props.atom Charm atom with the value of the setting.
* @returns A setting entry.
*/
export function SettingsEntry<T extends SettingType>(props: {
/**
* The atom with the value of the setting.
*/
atom: Atom<SettingRealType<T>>;
/**
* The definition of the setting.
*/
definition: SettingDefinition<T>;
}) {
return (
<frame AutomaticSize={Enum.AutomaticSize.Y} BackgroundTransparency={1}>
<uilistlayout />
<textlabel
AutomaticSize={Enum.AutomaticSize.Y}
BackgroundTransparency={1}
Text={props.definition.name}
TextColor3={new Color3(1, 1, 1)}
TextSize={30}
TextXAlignment={Enum.TextXAlignment.Left}
/>
<textlabel
AutomaticSize={Enum.AutomaticSize.Y}
BackgroundTransparency={1}
Text={props.definition.description}
TextColor3={new Color3(1, 1, 1)}
TextSize={25}
TextTransparency={0.5}
TextXAlignment={Enum.TextXAlignment.Left}
/>
<Switch condition={() => props.definition.type}>
<Case match={SettingType.Boolean}>
{() => (
<>
<frame Size={UDim2.fromOffset(0, 10)} />
<Toggle Atom={props.atom as unknown as Atom<boolean>} />
</>
)}
</Case>
</Switch>
</frame>
);
}
/**
* A button for each setting section the settings menu.
* @param props Properties of the button.
* @param props.name The name of the entry.
* @param props.panelSource A vide source for the current panel.
* @returns The Settings group button.
*/
export function SettingsGroup(props: {
/**
* The name of the settings group.
*/
name: string;
/**
* A vide source with the name of the current panel.
*/
panelSource: Source<string>;
}) {
return (
<frame BackgroundTransparency={1}>
<textbutton
BackgroundTransparency={1}
FontFace={() =>
props.name === props.panelSource()
? Font.fromEnum(Enum.Font.SourceSansBold)
: Font.fromEnum(Enum.Font.SourceSans)
}
MouseButton1Click={() => {
props.panelSource(props.name);
}}
Size={UDim2.fromScale(1, 1)}
Text={props.name}
TextColor3={new Color3(1, 1, 1)}
TextScaled={true}
/>
</frame>
);
}