Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
Packages/
roblox.yml
wally.lock
sourcemap.json
sourcemap.json
*-sourcemap.json
6 changes: 6 additions & 0 deletions .luaurc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"languageMode": "nocheck",
"aliases": {
"lune": "~/.lune/.typedefs/0.10.4/"
}
}
61 changes: 61 additions & 0 deletions .lune/wallyInstall.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
local process = require("@lune/process")
local task = require("@lune/task")
local stdio = require("@lune/stdio")
local fs = require("@lune/fs")

type ChildProcess = process.ChildProcess
type ChildProcessReader = process.ChildProcessReader
type ChildProcessStatus = { ok: boolean, code: number }

local PROJECT_NAME = "model.project.json"
local PACKAGE_DIRECTORIES = { "Packages" }

local function pipe(child: ChildProcess): ChildProcessStatus
local function readStream(stream: ChildProcessReader, output: (string) -> ())
return task.spawn(function()
while true do
local chunk = stream:read()
if not chunk then
break
end

output(chunk)
end
end)
end

readStream(child.stdout, stdio.write)
readStream(child.stderr, stdio.write)

return child:status()
end

local function exec(cmdline: string)
local pieces = {}
for piece in string.gmatch(" " .. cmdline, "%s+([^%s]+)") do
table.insert(pieces, piece)
end

local program = table.remove(pieces, 1)
assert(program ~= nil, "cmdline must include a program")

local child = process.create(program, pieces)
local status = pipe(child)

if not status.ok then
process.exit(status.code)
end
end

exec("wally install")
exec(`rojo sourcemap -o {PROJECT_NAME}-sourcemap.json ./{PROJECT_NAME}`)

for _, packageDir in PACKAGE_DIRECTORIES do
if fs.isDir(`./{packageDir}`) then
exec(`wally-package-types --sourcemap {PROJECT_NAME}-sourcemap.json ./{packageDir}/`)
else
stdio.write(
`{stdio.color("yellow")}WARNING: Package directory "{packageDir}" does not exist!{stdio.color("reset")}\n`
)
end
end
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"luau-lsp.sourcemap.enabled": true,
"luau-lsp.sourcemap.autogenerate": true,
"luau-lsp.sourcemap.rojoProjectFile": "default.project.json",
"luau-lsp.completion.imports.stringRequires.enabled": true,
"stylua.targetReleaseVersion": "latest",
"luau-lsp.fflags.enableNewSolver": true
}
4 changes: 2 additions & 2 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 Jack Fox
Copyright (c) 2026 Jack Fox

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
131 changes: 130 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@
- [Hooks](#-hooks)
- [useSpring](#usespring)
- [useTween](#usetween)
- [useSpringValue](#usespringvalue)
- [useTweenValue](#usetweenvalue)
- [useGroupAnimation](#usegroupanimation)
- [useTransparencyModifier](#usetransparencymodifier)
- [Supported Value Types](#-supported-value-types)
- [Showcase](#-showcase)
- [Contribution](#-contribution)
Expand All @@ -57,7 +60,7 @@ Add React-Flow to your `wally.toml` file:

```toml
[dependencies]
ReactFlow = "outofbears/react-flow@0.4.0"
ReactFlow = "outofbears/react-flow@0.5.0"
```

Then install with:
Expand Down Expand Up @@ -169,6 +172,82 @@ return createElement("Frame", {

---

### `useSpringValue`

Declarative 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.

The `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.

**Arguments:**
- **config:** A configuration table with the following properties:
- **start:** Initial value of the animation (used only on mount)
- **target:** Current goal — changes between renders are followed automatically
- **speed:** Spring stiffness (live-updatable)
- **damper:** Damping ratio (live-updatable)

**Returns:**
A binding that updates as the animation progresses. No update or stop function — control is purely via props.

**Example:**
```lua
local useSpringValue = ReactFlow.useSpringValue

-- Inside your component:
local hovered, setHovered = React.useState(false)

local color = useSpringValue({
start = Color3.fromRGB(150, 150, 150),
target = if hovered then Color3.fromRGB(255, 255, 255) else Color3.fromRGB(150, 150, 150),

speed = 20,
damper = 0.8,
})

return createElement("TextButton", {
BackgroundColor3 = color,
[React.Event.MouseEnter] = function() setHovered(true) end,
[React.Event.MouseLeave] = function() setHovered(false) end,
})
```

---

### `useTweenValue`

Declarative 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.

As with `useSpringValue`, the `start` prop is only used on mount — re-renders tween from the current animated value, not from `start`.

**Arguments:**
- **config:** A configuration table with the following properties:
- **start:** Initial value of the animation (used only on mount)
- **target:** Current goal — changes between renders trigger a replay
- **info:** `TweenInfo` instance
- **delay:** Optional delay before the tween starts

**Returns:**
A binding that updates as the animation progresses.

**Example:**
```lua
local useTweenValue = ReactFlow.useTweenValue

-- Inside your component:
local visible, setVisible = React.useState(false)

local transparency = useTweenValue({
start = 1,
target = if visible then 0 else 1,
info = TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
})

return createElement("Frame", {
BackgroundTransparency = transparency,
})
```

---

### `useGroupAnimation`

Creates 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.
Expand Down Expand Up @@ -231,6 +310,56 @@ return createElement("Frame", {

---

### `useTransparencyModifier`

Composes 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.

The 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.

**Arguments:**
- **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.

**Returns:**
A 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:
- A `number` (e.g. `BackgroundTransparency = 0.2`)
- A `NumberSequence` (e.g. for `UIGradient.Transparency`)
- A `Binding` of either of the above (animated transparency continues to update with the modifier applied)
- `nil` (treated as `0`)

**Example:**
```lua
local useTransparencyModifier = ReactFlow.useTransparencyModifier
local useSpring = ReactFlow.useSpring

-- Inside your component:
local fade = useSpring({
start = 1, -- Start fully hidden
target = visible and 0 or 1,-- 0 = fully visible, 1 = fully hidden

speed = 18,
damper = 1,
})

local modifyTransparency = useTransparencyModifier(fade)

return createElement("Frame", {
BackgroundTransparency = modifyTransparency(0.2),
}, {
label = createElement("TextLabel", {
BackgroundTransparency = 1,
TextTransparency = modifyTransparency(0),
}),
gradient = createElement("UIGradient", {
Transparency = modifyTransparency(NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(1, 0.5),
})),
}),
})
```

---

### `TransitionFragment`

`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.
Expand Down
8 changes: 5 additions & 3 deletions aftman.toml → rokit.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

# To add a new tool, add an entry to this table.
[tools]
rojo = "rojo-rbx/rojo@7.4.1"
wally = "upliftgames/wally@0.3.2"
selene = "kampfkarren/selene@0.27.1"
lune = "filiptibell/lune@0.10.4"
rojo = "rojo-rbx/rojo@7.6.1"
wally = "UpliftGames/wally@0.3.2"
wally-package-types = "JohnnyMorganz/wally-package-types@1.4.2"
selene = "Kampfkarren/selene@0.31.0"
14 changes: 0 additions & 14 deletions src/Animations/Base.lua

This file was deleted.

21 changes: 21 additions & 0 deletions src/Animations/Base.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export type BaseAnimation = {
listener: ((any) -> ())?,
setListener: (BaseAnimation, (any) -> ()?) -> (),
}

local function setListener(self: BaseAnimation, callback: ((any) -> ())?)
self.listener = callback
end

local function BaseAnimation()
local object = {}

object.listener = nil
object.setListener = setListener

return object
end

return {
new = BaseAnimation,
}
Loading