Skip to content

Instantly share code, notes, and snippets.

@Finchasaurus
Created May 27, 2025 04:59
Show Gist options
  • Select an option

  • Save Finchasaurus/0240309e21131743a596bbdf13abf79e to your computer and use it in GitHub Desktop.

Select an option

Save Finchasaurus/0240309e21131743a596bbdf13abf79e to your computer and use it in GitHub Desktop.
import React, { createRef, Element, useEffect, useState } from "@rbxts/react";
import { usePx } from "../hooks/use-px";
import { Empty } from "./empty";
import { Typeography } from "./typeography";
import { Widget } from "./widget";
function extractDigits(value: number) {
return tostring(value)
.split("")
.map((digit) => tonumber(digit)!);
}
// Component to display a single digit with animation
function NumDisplay({ value }: { value: number }) {
const px = usePx();
const layoutRef = createRef<UIPageLayout>();
// Premake 0-9 digits for display within the page layout
const nums = new Array<Element>();
for (let i = 0; i < 10; i++) {
nums.push(<Typeography key={i} text={tostring(i)} native={{ LayoutOrder: i }} />);
}
// Animate to correct digit when value changes
useEffect(() => {
layoutRef.current?.JumpToIndex(value);
}, [value]);
return (
<Widget aspectRatio={0.9} native={{ ClipsDescendants: true }}>
<uipagelayout
FillDirection={Enum.FillDirection.Vertical}
Padding={new UDim(0, px(4))}
HorizontalAlignment={"Center"}
Circular
ScrollWheelInputEnabled={false}
ref={layoutRef}
SortOrder={"LayoutOrder"}
/>
<uiaspectratioconstraint AspectRatio={1} />
{nums}
</Widget>
);
}
interface CounterProps {
value: number; // Current value to display
largest?: number; // Optional upper bound for value (used for sizing digits)
start_display_size?: number; // Minimum number of digits to display initially
}
// Counter component that displays multiple NumDisplay digits
export function Counter({ value, largest, start_display_size }: CounterProps) {
const px = usePx();
// Break value into individual digits
const currentDigits = extractDigits(value);
// Determine the maximum possible digit length from largest prop
const maxDigits = largest !== undefined ? extractDigits(largest).size() : undefined;
const [maxFound, setMaxFound] = useState(0);
// Cap to largest if exceeded
const digitsToDisplay = largest !== undefined && value > largest ? extractDigits(largest) : currentDigits;
// Determine how many digits to show based on props and state
const digitCount = math.max(digitsToDisplay.size(), maxFound, start_display_size ?? 1);
const maxDisplayCount = maxDigits !== undefined ? math.min(digitCount, maxDigits) : digitCount;
useEffect(() => {
setMaxFound(math.max(maxDisplayCount, maxFound));
}, [maxDisplayCount]);
// Prepend zeros for alignment
while (digitsToDisplay.size() < maxDisplayCount) {
digitsToDisplay.unshift(0);
}
const width = px(64) * digitsToDisplay.size();
const height = px(64);
return (
<Empty native={{ Size: UDim2.fromOffset(width, height), ClipsDescendants: false }}>
<uilistlayout
FillDirection={Enum.FillDirection.Horizontal}
HorizontalAlignment="Center"
VerticalAlignment="Center"
Padding={new UDim(0, px(8))}
HorizontalFlex="SpaceEvenly"
/>
{digitsToDisplay.map((digit, i) => (
<NumDisplay key={i} value={digit} />
))}
<uiaspectratioconstraint AspectRatio={width / height} />
<uiflexitem ItemLineAlignment="Center" />
</Empty>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment