{"id":"outofbears/react-flow","name":"react-flow","scope":"outofbears","platform":"roblox","description":"React Animation Library for Roblox","version":"0.5.0","latest":"0.5.0","versions":["0.1.2","0.1.3","0.1.5","0.1.6","0.1.7","0.1.8","0.2.0","0.3.0","0.4.0","0.5.0-rc","0.5.0","0.5.1-rc","0.5.2-rc"],"license":"MIT","licenseRating":"safe","licenseCaveats":["License identified from the packaged LICENSE file; the manifest declared none."],"licenseVerified":true,"dependencies":{"evaera/promise":{"version":"^4.0.0","alias":"Promise"},"jsdotlua/react":{"version":"^17.2.1","alias":"React"},"jsdotlua/react-roblox":{"version":"^17.2.1","alias":"ReactRoblox"}},"integrity":"84393b332aa891b33e27f67e98b710a0e23da4337ea7200ed74f32df80b7bd40","likes":0,"downloads":0,"install":"forest install outofbears/react-flow","url":"https://forest.dev/p/roblox/outofbears/react-flow","files":"https://api.forest.dev/ai/package/roblox/outofbears/react-flow/files","readme":"\n<h3 align=\"center\">\n    <img src=\"https://i.imgur.com/I1CRYmc.png\" alt=\"Slither Icon\" width=\"160\" />\n    <br />\n\t<br />\n\tReact-Flow\n</h3>\n\n<div align=\"center\">\n⚡ A blazing fast animation library for React-Lua interfaces, providing stateful animations with unrestricted flexibility and performance. 🤌\n</div>\n\n<div align=\"center\">\n<br />\n\n[![Version](https://img.shields.io/github/v/release/outofbears/react-flow.svg?style=flat-square)](https://github.com/outofbears/react-flow/releases)\n[![License](https://img.shields.io/github/license/outofbears/react-flow.svg?style=flat-square)](https://github.com/outofbears/react-flow/blob/main/LICENSE.md)\n[![Stars](https://img.shields.io/github/stars/outofbears/react-flow.svg?style=flat-square)](https://github.com/outofbears/react-flow/stargazers)\n[![Forks](https://img.shields.io/github/forks/outofbears/react-flow.svg?style=flat-square)](https://github.com/outofbears/react-flow/network/members)\n[![Watchers](https://img.shields.io/github/watchers/outofbears/react-flow.svg?style=flat-square)](https://github.com/outofbears/react-flow/watchers)\n[![Issues](https://img.shields.io/github/issues/outofbears/react-flow.svg?style=flat-square)](https://github.com/outofbears/react-flow/issues)\n[![Pull Requests](https://img.shields.io/github/issues-pr/outofbears/react-flow.svg?style=flat-square)](https://github.com/outofbears/react-flow/pulls)\n[![Last Commit](https://img.shields.io/github/last-commit/outofbears/react-flow.svg?style=flat-square)](https://github.com/outofbears/react-flow/commits/main)\n\n\n</div>\n\n\n## 📋 Table of Contents\n\n- [Features](#-features)\n- [Installation](#-installation)\n  - [Using Wally](#using-wally-recommended)\n  - [Manual Installation](#manual-installation)\n- [Hooks](#-hooks)\n  - [useSpring](#usespring)\n  - [useTween](#usetween)\n  - [useSpringValue](#usespringvalue)\n  - [useTweenValue](#usetweenvalue)\n  - [useGroupAnimation](#usegroupanimation)\n  - [useTransparencyModifier](#usetransparencymodifier)\n- [Supported Value Types](#-supported-value-types)\n- [Showcase](#-showcase)\n- [Contribution](#-contribution)\n- [License](#-license)\n\n## ✨ Features\n\n- 🔄 **Stateful Animations** - Animations that automatically respond to your component's state changes, ensuring UI and state stay perfectly synchronized\n- ⛓️ **Chainable Animations** - Effortlessly build complex animation sequences that flow naturally from one to another\n- 🔀 **Interruptible Flows** - Gracefully handle user interactions by modifying animations mid-flight without jarring visual transitions\n- 🧩 **Composable System** - Create reusable animation components that can be combined in endless ways for consistent motion design\n- 🛡️ **Memory-Safe Design** - Built with React's lifecycle in mind to prevent memory leaks and ensure proper cleanup\n\n## 📦 Installation\n\n### Using Wally (Recommended)\n\nAdd React-Flow to your `wally.toml` file:\n\n```toml\n[dependencies]\nReactFlow = \"outofbears/react-flow@0.5.0\"\n```\n\nThen install with:\n```bash\nwally install\n```\n\n### Manual Installation\n\nSimply clone the repository and include it in your project structure.\n\n### Requiring the Module\n\nOnce installed, require React-Flow in your code:\n\n```lua\n-- For common Roblox setups:\nlocal ReactFlow = require(ReplicatedStorage.Packages.ReactFlow)\n```\n\n## 🔧 Hooks\n\n### `useSpring`\n\nCreates spring-based physics animations with React bindings. Springs provide natural, bouncy motion that reacts to changes dynamically.\n\n**Arguments:**\n- **config:** A configuration table with the following properties:\n  - **start:** Initial value of the animation (required)\n  - **target:** Target value to animate toward (optional)\n  - **speed:** Spring stiffness - higher values create faster motion (default: 10)\n  - **damper:** Damping ratio - higher values reduce bouncing (default: 1)\n\n**Returns:**  \nA binding that updates as the animation progresses, and an update function to modify the animation.\n\n**Example:**\n```lua\nlocal useSpring = ReactFlow.useSpring\n\n-- Inside your component:\nlocal position, updatePosition = useSpring({\n    start = UDim2.fromScale(0, 0),           -- Initial Value (required)\n    target = UDim2.fromScale(0.5, 0.5),      -- Target value (optional)\n\n    speed = 20,\n    damper = 0.8,\n})\n\n-- Later, update the spring with new parameters:\nupdatePosition({\n    target = UDim2.fromScale(0.5, 0.5),\n\n    speed = 15,\n    damper = 0.7,\n})\n\n-- Use in your component:\nreturn createElement(\"Frame\", {\n    Position = position, -- Use binding directly in property\n})\n```\n\n---\n\n### `useTween`\n\nCreates tween-based animations that follow a specific timing curve. Ideal for animations that need precise timing or easing effects.\n\n**Arguments:**\n- **config:** A configuration table with the following properties:\n  - **start:** Initial value of the animation (required)\n  - **target:** Target value to animate toward (optional)\n  - **info:** TweenInfo instance (required)\n\n**Returns:**  \nA binding that updates as the animation progresses, and an update function to modify the animation.\n\n**Example:**\n```lua\nlocal useTween = ReactFlow.useTween\n\n-- Inside your component:\nlocal transparency, updateTransparency = useTween({\n    start = 1,      -- Initial value (required)\n    target = 0,     -- Target value (optional)\n\n    -- TweenInfo - controls duration, easing style, and behavior\n    info = TweenInfo.new(\n        0.5,\n        Enum.EasingStyle.Quad,\n        Enum.EasingDirection.Out, \n    )\n})\n\n-- Later, update the tween:\nupdateTransparency({\n    target = 0,     -- New target value\n\n    -- Optional: update tween configuration\n    info = TweenInfo.new(0.3, Enum.EasingStyle.Quart, Enum.EasingDirection.InOut),\n})\n\n-- Use in your component:\nreturn createElement(\"Frame\", {\n    BackgroundTransparency = transparency,\n})\n```\n\n---\n\n### `useSpringValue`\n\nDeclarative variant of [`useSpring`](#usespring). Instead of returning an update function, the hook watches the `target`, `speed`, and `damper` props on each render and re-targets the spring automatically when they change. Use this when your spring goal is a direct function of React state and you don't need imperative control.\n\nThe `start` prop seeds the initial value only — it is **not** re-applied on subsequent renders, so the spring smoothly retargets from its current value rather than snapping back to the start.\n\n**Arguments:**\n- **config:** A configuration table with the following properties:\n  - **start:** Initial value of the animation (used only on mount)\n  - **target:** Current goal — changes between renders are followed automatically\n  - **speed:** Spring stiffness (live-updatable)\n  - **damper:** Damping ratio (live-updatable)\n\n**Returns:**  \nA binding that updates as the animation progresses. No update or stop function — control is purely via props.\n\n**Example:**\n```lua\nlocal useSpringValue = ReactFlow.useSpringValue\n\n-- Inside your component:\nlocal hovered, setHovered = React.useState(false)\n\nlocal color = useSpringValue({\n    start = Color3.fromRGB(150, 150, 150),\n    target = if hovered then Color3.fromRGB(255, 255, 255) else Color3.fromRGB(150, 150, 150),\n\n    speed = 20,\n    damper = 0.8,\n})\n\nreturn createElement(\"TextButton\", {\n    BackgroundColor3 = color,\n    [React.Event.MouseEnter] = function() setHovered(true) end,\n    [React.Event.MouseLeave] = function() setHovered(false) end,\n})\n```\n\n---\n\n### `useTweenValue`\n\nDeclarative variant of [`useTween`](#usetween). The hook watches the `target` prop on each render and replays the tween from the binding's current value to the new target. Use this when your tween goal is driven by React state.\n\nAs with `useSpringValue`, the `start` prop is only used on mount — re-renders tween from the current animated value, not from `start`.\n\n**Arguments:**\n- **config:** A configuration table with the following properties:\n  - **start:** Initial value of the animation (used only on mount)\n  - **target:** Current goal — changes between renders trigger a replay\n  - **info:** `TweenInfo` instance\n  - **delay:** Optional delay before the tween starts\n\n**Returns:**  \nA binding that updates as the animation progresses.\n\n**Example:**\n```lua\nlocal useTweenValue = ReactFlow.useTweenValue\n\n-- Inside your component:\nlocal visible, setVisible = React.useState(false)\n\nlocal transparency = useTweenValue({\n    start = 1,\n    target = if visible then 0 else 1,\n    info = TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),\n})\n\nreturn createElement(\"Frame\", {\n    BackgroundTransparency = transparency,\n})\n```\n\n---\n\n### `useGroupAnimation`\n\nCreates a group of animations that are managed together as a single entity. With `useGroupAnimation`, you can define multiple animation states by combining the following animation primitives: `useAnimation`, `useSpringAnimation`, `useSequenceAnimation`, and `useTweenAnimation`. This allows you to define complex animation states and switch between them seamlessly at runtime, providing an elegant way to handle UI state transitions.\n\n**Arguments:**\n- **animations:** A table mapping state names (e.g., \"active\", \"inactive\") to their animation definitions. Each definition can mix multiple animation types.\n- **defaults:** A table providing the default initial values for each animation property.\n\n**Returns:**  \nA table of bindings for each animation property, and a function (commonly named `playAnimation`) that accepts a state name to switch between the defined animation groups.\n\n**Example:**\n\n```lua\nlocal useGroupAnimation = ReactFlow.useGroupAnimation\nlocal useSequenceAnimation = ReactFlow.useSequenceAnimation\nlocal useAnimation = ReactFlow.useAnimation\n\nlocal Spring = ReactFlow.Spring\nlocal Tween = ReactFlow.Tween\n\n-- Inside your component:\nlocal animations, playAnimation = useGroupAnimation({\n    enable = useSequenceAnimation({\n        {\n            timestamp = 0,\n            transparency = Tween({target = 0, info = TweenInfo.new(0.2)}),\n        },\n        {\n            timestamp = 0.2,\n            position = Spring({target = UDim2.fromScale(0.5, 0.5), speed = 20}),\n        },\n    }),\n    disable = useAnimation({\n        transparency = Tween({target = 1, info = TweenInfo.new(0.1)}),\n        position = Spring({target = UDim2.fromScale(0.5, 1), speed = 25}),\n    }),\n}, {\n    transparency = 1\n    position = UDim2.fromScale(0.5, 1),\n})\n\n-- Play the animation with the specificed name:\nif enabled then\n    playAnimation(\"enable\")\nelse\n    playAnimation(\n        \"disable\",\n        true -- Optional second argument to play animation immediately\n    )\nend\n\n-- Use the animation bindings in your component:\nreturn createElement(\"Frame\", {\n    Size = UDim2.new(0, 100, 0, 100),\n    BackgroundTransparency = animations.transparency,\n    Position = animations.position,\n})\n```\n\n---\n\n### `useTransparencyModifier`\n\nComposes a transparency binding with a modifier so transparency values can be uniformly faded toward fully transparent. Useful for hover, disabled, or fade-in/out states that need to dim a whole element (or subtree) without rewriting every individual transparency value.\n\nThe modifier is applied multiplicatively against visibility: a modifier of `0` leaves transparency untouched, a modifier of `1` makes the element fully transparent, and values in between blend smoothly. Both plain `number` transparencies and `NumberSequence` transparencies (e.g. for `UIGradient.Transparency`) are supported, as are static values and bindings.\n\n**Arguments:**\n- **modifier:** A binding holding a `number` between `0` and `1` representing the additional transparency to apply. A value of `0` is a no-op; `1` fully hides the target.\n\n**Returns:**  \nA function that takes a transparency value and returns a binding with the modifier applied. The returned function can be called repeatedly for each property you want to modify, and accepts:\n- A `number` (e.g. `BackgroundTransparency = 0.2`)\n- A `NumberSequence` (e.g. for `UIGradient.Transparency`)\n- A `Binding` of either of the above (animated transparency continues to update with the modifier applied)\n- `nil` (treated as `0`)\n\n**Example:**\n```lua\nlocal useTransparencyModifier = ReactFlow.useTransparencyModifier\nlocal useSpring = ReactFlow.useSpring\n\n-- Inside your component:\nlocal fade = useSpring({\n    start = 1,                  -- Start fully hidden\n    target = visible and 0 or 1,-- 0 = fully visible, 1 = fully hidden\n\n    speed = 18,\n    damper = 1,\n})\n\nlocal modifyTransparency = useTransparencyModifier(fade)\n\nreturn createElement(\"Frame\", {\n    BackgroundTransparency = modifyTransparency(0.2),\n}, {\n    label = createElement(\"TextLabel\", {\n        BackgroundTransparency = 1,\n        TextTransparency = modifyTransparency(0),\n    }),\n    gradient = createElement(\"UIGradient\", {\n        Transparency = modifyTransparency(NumberSequence.new({\n            NumberSequenceKeypoint.new(0, 0),\n            NumberSequenceKeypoint.new(1, 0.5),\n        })),\n    }),\n})\n```\n\n---\n\n### `TransitionFragment`\n\n`TransitionFragment` is a component that allows elements to be animated on transition in and out by preserving their presence in a cached fragment during enter and leave operations. When children are added or removed, the component maintains them in the DOM while injecting transition state props, enabling easy enter and exit animations.\n\nThe component automatically injects the following props into child elements:\n\n- **entering**: `boolean` - True when the element is being added\n- **exiting**: `boolean` - True when the element is being removed\n- **onEnterComplete**: `() -> ()` - Callback when enter animation completes\n- **onExitComplete**: `() -> ()` - Callback when exit animation completes\n\nThis allows child components to respond to transition states and perform appropriate animations while `TransitionFragment` handles the lifecycle management.\n\n**Arguments:**\n\n- **children**: A table containing the elements to be managed with transitions. Elements are automatically cached and transitioned when added or removed.\n\n**Returns:**\n\nA `TransitionFragment` component that handles the transition lifecycle and injects transition props into its children.\n\n**Example:**\n\n```lua\nlocal function Entry(props: {\n    entering: boolean,\n    exiting: boolean,\n    onEnterComplete: () -> (),\n    onExitComplete: () -> ()\n})\n    useEffect(function()\n        if props.entering then\n            -- Play enter animation and wait for animation to complete\n            props.onEnterComplete()\n        end\n    end, {props.entering})\n\n    useEffect(function()\n        if props.exiting then\n            -- Play enter animation and wait for animation to complete\n            props.onExitComplete()\n        end\n    end, {props.exiting})\n\n    return ...\nend\n\nreturn createElement(ExampleContainer, {}, {\n    entries = createElement(TransitionFragment, {}, {\n        -- list of Entries\n    })\n})\n```\n\n## 📊 Supported Value Types\n\nReact-Flow supports animating the following userdata and native types:\n\n### Basic Types\n- `number` - Numeric values\n- `UDim2` - 2D positioning (scale and offset)\n- `UDim` - 1D positioning (scale and offset)\n- `Vector2` - 2D vectors\n- `Vector3` - 3D vectors\n- `Color3` - RGB color values\n\n### Advanced Types\n- `CFrame` - Position and orientation \n- `ColorSequenceKeypoint` - Color gradient keypoints\n- `NumberSequenceKeypoint` - Number gradient keypoints\n- `BrickColor` - Legacy colors\n- `NumberRange` - Min/max ranges\n- `PhysicalProperties` - Physics simulation properties\n- `Ray` - Line segments\n- `Region3` - 3D spatial regions\n- `Region3int16` - Integer-based 3D regions\n\n## 🎬 Showcase\n\n<div align=\"center\">\n    <img src=\"https://i.imgur.com/y1On24b.gif\" alt=\"RoundControl\" style=\"width: 600px\" />\n    <p style=\"font-size: 1.5em; font-weight: 500\">Round Control Interface</p>\n</div>\n\n<div align=\"center\" style=\"margin-top: 2rem\">\n    <img src=\"https://i.imgur.com/tdhyG9f.gif\" alt=\"TowerUpgrade\" style=\"width: 600px\"/>\n    <p style=\"font-size: 1.5em; font-weight: 500\">Tower Upgrade Interface</p>\n</div>\n\n<div align=\"center\" style=\"margin-top: 2rem\">\n    <img src=\"https://i.imgur.co","readmeTruncated":true,"readmeFull":"https://api.forest.dev/v1/package/outofbears/roblox/react-flow/0.5.0/readme"}