Skip to content

Commit

Permalink
refactor(ui): port ui to vide
Browse files Browse the repository at this point in the history
  • Loading branch information
paradoxuum committed Jul 19, 2024
1 parent f967091 commit 21b430e
Show file tree
Hide file tree
Showing 53 changed files with 946 additions and 1,349 deletions.
13 changes: 6 additions & 7 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
},
"scripts": {
"prepack": "yarn build",
"build": "shx rm -rf out && rbxtsc --verbose"
"build": "shx rm -rf out && rbxtsc --verbose",
"dev": "rbxtsc --watch"
},
"devDependencies": {
"@rbxts/centurion": "workspace:^",
Expand All @@ -40,13 +41,11 @@
},
"dependencies": {
"@rbxts/beacon": "^2.1.1",
"@rbxts/pretty-react-hooks": "^0.5.2",
"@rbxts/react": "^0.4.0",
"@rbxts/react-reflex": "^0.3.4",
"@rbxts/react-roblox": "^0.4.0",
"@rbxts/reflex": "^4.3.1",
"@rbxts/charm": "^0.5.2",
"@rbxts/ripple": "^0.8.2",
"@rbxts/services": "^1.5.4"
"@rbxts/services": "^1.5.4",
"@rbxts/set-timeout": "^1.1.2",
"@rbxts/vide": "^0.4.1"
},
"peerDependencies": {
"@rbxts/centurion": "workspace:^"
Expand Down
34 changes: 10 additions & 24 deletions packages/ui/src/app/app.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
const config = script.Parent?.FindFirstChild("config");
if (config !== undefined && classIs(config, "ModuleScript")) {
require(config);
}
import "./config";

import { Signal } from "@rbxts/beacon";
import { ClientAPI } from "@rbxts/centurion";
import React, { StrictMode } from "@rbxts/react";
import { createPortal, createRoot } from "@rbxts/react-roblox";
import { ContentProvider, Players } from "@rbxts/services";
import Vide, { mount } from "@rbxts/vide";
import { DEFAULT_INTERFACE_OPTIONS } from "../constants/options";
import { RootProvider } from "../providers/root-provider";
import { useAPI } from "../hooks/use-api";
import { usePx } from "../hooks/use-px";
import { InterfaceOptions } from "../types";
import { TerminalApp } from "./terminal-app";

Expand All @@ -26,9 +23,6 @@ export namespace CenturionUI {
options: Partial<InterfaceOptions> = {},
): (api: ClientAPI) => void {
return (api) => {
const root = createRoot(new Instance("Folder"));
const target = Players.LocalPlayer.WaitForChild("PlayerGui");

// Attempt to preload font
task.spawn(() => {
const fontFamily = (
Expand All @@ -49,20 +43,12 @@ export namespace CenturionUI {
}
});

root.render(
createPortal(
<StrictMode>
<RootProvider
api={api}
options={{ ...DEFAULT_INTERFACE_OPTIONS, ...options }}
optionsChanged={optionsChanged}
>
<TerminalApp />
</RootProvider>
</StrictMode>,
target,
),
);
const target = Players.LocalPlayer.WaitForChild("PlayerGui");
mount(() => {
useAPI(api);
usePx();
return <TerminalApp />;
}, target);
};
}
}
7 changes: 3 additions & 4 deletions packages/ui/src/app/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
declare const _G: { __DEV__: boolean };

const RunService = game.GetService("RunService");
import { RunService } from "@rbxts/services";
import Vide from "@rbxts/vide";

if (RunService.IsStudio() && RunService.IsClient()) {
_G.__DEV__ = true;
Vide.strict = true;
}
52 changes: 25 additions & 27 deletions packages/ui/src/app/terminal-app.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,41 @@
import { useEventListener } from "@rbxts/pretty-react-hooks";
import React, { useContext, useMemo } from "@rbxts/react";
import { useSelector } from "@rbxts/react-reflex";
import { UserInputService } from "@rbxts/services";
import Vide, { derive } from "@rbxts/vide";
import Terminal from "../components/terminal";
import { Layer } from "../components/ui/layer";
import { OptionsContext } from "../providers/options-provider";
import { store } from "../store";
import { selectVisible } from "../store/app";
import { useAtom } from "../hooks/use-atom";
import { useEvent } from "../hooks/use-event";
import {
interfaceOptions,
interfaceVisible,
mouseOverInterface,
} from "../store";

export function TerminalApp() {
const options = useContext(OptionsContext);
const visible = useSelector(selectVisible);
const MOUSE_INPUT_TYPES = new Set<Enum.UserInputType>([
Enum.UserInputType.MouseButton1,
Enum.UserInputType.MouseButton2,
Enum.UserInputType.Touch,
]);

const validKeys = useMemo(() => {
return new Set(options.activationKeys);
}, [options]);
export function TerminalApp() {
const options = useAtom(interfaceOptions);
const visible = useAtom(interfaceVisible);

const mouseInputTypes = useMemo(() => {
return new Set<Enum.UserInputType>([
Enum.UserInputType.MouseButton1,
Enum.UserInputType.MouseButton2,
Enum.UserInputType.Touch,
]);
}, []);
const validKeys = derive(() => new Set(options().activationKeys));

useEventListener(UserInputService.InputBegan, (input, gameProcessed) => {
if (validKeys.has(input.KeyCode) && !gameProcessed) {
store.setVisible(!visible);
useEvent(UserInputService.InputBegan, (input, gameProcessed) => {
if (validKeys().has(input.KeyCode) && !gameProcessed) {
interfaceVisible(!visible());
} else if (
options.hideOnLostFocus &&
mouseInputTypes.has(input.UserInputType) &&
!options.isMouseOnGUI
options().hideOnLostFocus &&
MOUSE_INPUT_TYPES.has(input.UserInputType) &&
!mouseOverInterface()
) {
store.setVisible(false);
interfaceVisible(false);
}
});

return (
<Layer displayOrder={options.displayOrder} visible={visible}>
<Layer displayOrder={() => options().displayOrder ?? 0} visible={visible}>
<Terminal />
</Layer>
);
Expand Down
47 changes: 26 additions & 21 deletions packages/ui/src/components/history/history-line.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { HistoryEntry } from "@rbxts/centurion";
import { BindingOrValue } from "@rbxts/pretty-react-hooks";
import React, { useContext, useMemo } from "@rbxts/react";
import Vide, { Derivable, derive } from "@rbxts/vide";
import { HISTORY_TEXT_SIZE } from "../../constants/text";
import { usePx } from "../../hooks/use-px";
import { OptionsContext } from "../../providers/options-provider";
import { useAtom } from "../../hooks/use-atom";
import { px } from "../../hooks/use-px";
import { interfaceOptions } from "../../store";
import { Frame } from "../ui/frame";
import { Group } from "../ui/group";
import { Outline } from "../ui/outline";
Expand All @@ -12,60 +12,65 @@ import { TextField } from "../ui/text-field";

interface HistoryLineProps {
data: HistoryEntry;
size?: BindingOrValue<UDim2>;
position?: BindingOrValue<UDim2>;
order?: BindingOrValue<number>;
size?: Derivable<UDim2>;
position?: Derivable<UDim2>;
order?: Derivable<number>;
}

export function HistoryLine({ data, size, position, order }: HistoryLineProps) {
const options = useContext(OptionsContext);
const px = usePx();
const date = useMemo(() => {
const options = useAtom(interfaceOptions);

const date = derive(() => {
const dateTime = DateTime.fromUnixTimestamp(data.sentAt).FormatLocalTime(
"LT",
"en-us",
);
const dateParts = dateTime.split(" ");
return `<b>${dateParts[0]}</b> ${dateParts[1]}`;
}, [data]);
});

return (
<Group size={size} position={position} layoutOrder={order}>
<Frame
backgroundColor={options.palette.surface}
backgroundColor={() => options().palette.surface}
size={UDim2.fromOffset(px(76), px(HISTORY_TEXT_SIZE + 4))}
cornerRadius={new UDim(0, px(4))}
>
<Text
size={UDim2.fromScale(1, 1)}
text={date}
textColor={options.palette.text}
textSize={px(HISTORY_TEXT_SIZE)}
textColor={() => options().palette.text}
textSize={() => px(HISTORY_TEXT_SIZE)}
richText={true}
/>

<Outline
innerThickness={px(1)}
innerThickness={() => px(1)}
innerTransparency={0.25}
innerColor={
data.success ? options.palette.success : options.palette.error
}
innerColor={() => {
return data.success
? options().palette.success
: options().palette.error;
}}
outerThickness={0}
cornerRadius={new UDim(0, px(4))}
/>
</Frame>

<TextField
anchorPoint={new Vector2(1, 0)}
size={new UDim2(1, -px(84), 1, 0)}
size={() => new UDim2(1, -px(84), 1, 0)}
position={UDim2.fromScale(1, 0)}
text={data.text}
textSize={px(HISTORY_TEXT_SIZE)}
textColor={data.success ? options.palette.text : options.palette.error}
textColor={() => {
const palette = options().palette;
return data.success ? palette.text : palette.error;
}}
textEditable={false}
textXAlignment="Left"
clearTextOnFocus={false}
font={options.font.medium}
font={() => options().font.medium}
richText
/>
</Group>
Expand Down
65 changes: 32 additions & 33 deletions packages/ui/src/components/history/history-list.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BindingOrValue, mapBinding } from "@rbxts/pretty-react-hooks";
import React, { useBinding, useContext, useEffect } from "@rbxts/react";
import { usePx } from "../../hooks/use-px";
import { OptionsContext } from "../../providers/options-provider";
import Vide, { Derivable, effect, For, read, source } from "@rbxts/vide";
import { useAtom } from "../../hooks/use-atom";
import { px } from "../../hooks/use-px";
import { interfaceOptions } from "../../store";
import { HistoryLineData } from "../../types";
import { ScrollingFrame } from "../ui/scrolling-frame";
import { HistoryLine } from "./history-line";
Expand All @@ -12,11 +12,11 @@ export interface HistoryData {
}

interface HistoryListProps {
data: HistoryData;
size?: BindingOrValue<UDim2>;
position?: BindingOrValue<UDim2>;
data: Derivable<HistoryData>;
size?: Derivable<UDim2>;
position?: Derivable<UDim2>;
maxHeight?: number;
scrollingEnabled?: BindingOrValue<boolean>;
scrollingEnabled?: Derivable<boolean>;
}

export function HistoryList({
Expand All @@ -25,42 +25,41 @@ export function HistoryList({
position,
maxHeight,
}: HistoryListProps) {
const px = usePx();
const options = useContext(OptionsContext);
const options = useAtom(interfaceOptions);

const [scrollingEnabled, setScrollingEnabled] = useBinding(false);
const [canvasSize, setCanvasSize] = useBinding(new UDim2());
const [canvasPosition, setCanvasPosition] = useBinding(new Vector2());
const scrollingEnabled = source(false);
const canvasSize = source(new UDim2());
const canvasPos = source(new Vector2());

useEffect(() => {
const height = data.height - px(8);
setCanvasSize(new UDim2(0, 0, 0, height));
setCanvasPosition(new Vector2(0, height));
effect(() => {
const height = read(data).height - px(8);
canvasSize(new UDim2(0, 0, 0, height));
canvasPos(new Vector2(0, height));

if (maxHeight !== undefined) setScrollingEnabled(height > maxHeight);
}, [data, px]);
if (maxHeight !== undefined) scrollingEnabled(height > maxHeight);
});

return (
<ScrollingFrame
size={size}
position={position}
canvasSize={canvasSize}
canvasPosition={canvasPosition}
scrollBarColor={options.palette.subtext}
scrollBarThickness={mapBinding(scrollingEnabled, (val) => {
return val ? 10 : 0;
})}
canvasPosition={canvasPos}
scrollBarColor={() => options().palette.subtext}
scrollBarThickness={() => (scrollingEnabled() ? 10 : 0)}
scrollingEnabled={scrollingEnabled}
>
{data.lines.map((data, i) => {
return (
<HistoryLine
key={`${data.entry.sentAt}-${i}`}
size={new UDim2(1, 0, 0, data.height)}
data={data.entry}
/>
);
})}
<For each={() => read(data).lines}>
{(line: HistoryLineData, index: () => number) => {
return (
<HistoryLine
size={new UDim2(1, 0, 0, line.height)}
data={line.entry}
order={index}
/>
);
}}
</For>

<uilistlayout Padding={new UDim(0, px(8))} SortOrder="LayoutOrder" />
</ScrollingFrame>
Expand Down
Loading

0 comments on commit 21b430e

Please sign in to comment.