Created
May 27, 2025 04:59
-
-
Save Finchasaurus/0240309e21131743a596bbdf13abf79e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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