main commit

This commit is contained in:
2026-06-21 11:27:44 +02:00
commit f45e5197a4
44 changed files with 4511 additions and 0 deletions
@@ -0,0 +1,59 @@
import { NotDerivate, useBox } from "@common/shared/utils/ui/vide";
import { Atom } from "@rbxts/charm";
import Vide, { Derivable, source } from "@rbxts/vide";
/**
* Props of a toggle.
*/
interface ToggleProps {
/**
* A charm atom linked to the toggle.
*/
Atom?: Atom<boolean>;
/**
* The position of the toggle in roblox ui.
*/
Position?: Derivable<UDim2>;
/**
* The size of the toggle
*/
Size?: Derivable<UDim2>;
}
/**
* A toggle is a UI component linked to an boolean.
* @param props Props of the toggle.
* @returns A Vide node with the toggle.
*/
export function Toggle(props: ToggleProps = {}) {
const [enable, setEnable] = useBox(props.Atom, false);
const sphereSize = source(0);
return (
<imagebutton
BackgroundColor3={new Color3(1, 1, 1)}
MouseButton1Click={() => setEnable(!enable())}
Position={props?.Position}
Size={props?.Size ?? UDim2.fromOffset(90, 30)}
>
<uicorner CornerRadius={new UDim(1, 0)} />
<uiaspectratioconstraint AspectRatio={1.8} />
<frame
BackgroundColor3={new Color3(0.5, 0.5, 0.5)}
Position={new UDim2(0, 2, 0, 2)}
Size={new UDim2(1, -4, 1, -4)}
Visible={NotDerivate(enable)}
>
<uicorner CornerRadius={new UDim(1, 0)} />
</frame>
<frame
AbsoluteSizeChanged={(value) => sphereSize(value.X)}
BackgroundColor3={new Color3(0, 0, 0)}
Position={() => (enable() ? new UDim2(1, -sphereSize() - 2, 0, 2) : new UDim2(0, 2, 0, 2))}
Size={new UDim2(1, -4, 1, -4)}
>
<uicorner CornerRadius={new UDim(1, 0)} />
<uiaspectratioconstraint AspectRatio={1} />
</frame>
</imagebutton>
);
}
@@ -0,0 +1,127 @@
import { SettingsController } from "@common/client/controllers/settings";
import { SettingDefinition, SettingRealType, SettingType } from "@common/shared/typings/settings";
import { Atom } from "@rbxts/charm";
import Vide, { Case, For, Source, source, Switch } from "@rbxts/vide";
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>
);
}
@@ -0,0 +1,5 @@
import { Toggle } from "@common/client/ui/components/global/toggle";
import { Hoarcekat } from "@common/shared/utils/ui/hoarcekat";
import { Node } from "@rbxts/vide";
export = Hoarcekat(Toggle as () => Node);