diff --git a/docs/tut/crash-course.md b/docs/tut/crash-course.md
index 95551aa..b148bc4 100644
--- a/docs/tut/crash-course.md
+++ b/docs/tut/crash-course.md
@@ -1,10 +1,9 @@
# Vide Crash Course
-Hello! This is a brief tutorial designed to give you a quick runthrough of the usage of Vide.
+This is a brief tutorial designed to give you a quick run through the usage of
+Vide.
-Vide is inspired by the popular libraries Vue and Fusion.
-
-- Note that this tutorial assumes that you are familiar with Luau and the Roblox UI system.
+Vide is largely inspired by Solid.
@@ -12,21 +11,22 @@ Vide is inspired by the popular libraries Vue and Fusion.
In Vide, it is intended to create all UI instances through code.
-Instances are created using [`vide.create`](../api/creation#create).
+Instances are created using [`vide.create()`](../api/creation#create).
```lua
-local vide = require(...)
+local vide = require(vide)
local create = vide.create
```
```lua
-local frame = create("Frame") {
+local frame = create "Frame" {
Name = "Background",
Position = UDim2.fromScale(0.5, 0.5)
}
```
-The function returns a constructor for a given class which then takes a table of properties to assign to create a new instance for that class.
+`create()` returns a constructor for a given class which then takes a table of
+properties to assign when creating a new instance for that class.
Sometimes you want to do more than setting properties, such as setting children or connecting to events.
Vide uses special keys called *symbols* which provide unique functionality like the above mentioned.
@@ -250,33 +250,33 @@ Often, you need components that maintain their own internal state, such as a tog
Below you can see how a simple counter component can be implemented.
```lua
-local function Counter(args)
+local function Counter(props: {
+ Layout: Layout
+})
-- create internal state unique to each component instance
- local count = wrap(0)
+ local count = source(0)
- return create("TextButton") {
- Name = "Counter",
-
- Text = "Count: " .. count
-
- [Event.Activated] = function()
- count.value += 1
+ return create "TextButton" {
+ Text = function()
+ return "Count: " .. count
end
- [Layout] = args[Layout]
+ Activated = function()
+ count(count() + 1)
+ end,
+
+ Layout = props.Layout
}
end
create "ScreenGui" {
Parent = game.StarterGui,
- [Children] = {
- Counter {
- [Layout] = {
- AnchorPoint = Vector2.new(0.5, 0),
- Position = UDim2.fromScale(0.5, 0),
- Size = UDim2.fromScale(0.3, 0.1)
- }
+ Counter {
+ Layout = {
+ AnchorPoint = Vector2.new(0.5, 0),
+ Position = UDim2.fromScale(0.5, 0),
+ Size = UDim2.fromScale(0.3, 0.1)
}
}
}
diff --git a/src/bind.lua b/src/bind.lua
index c6f840d..705985a 100644
--- a/src/bind.lua
+++ b/src/bind.lua
@@ -47,7 +47,7 @@ local function traceback() -- ensures trace begins outside of any vide library f
return debug.traceback("", s)
end
-function setup(instance: Instance, deriver: () -> unknown, setter: (Instance) -> ())
+function setup(instance: Instance, setter: (Instance) -> ())
if flags.strict then
local fn = setter
local trace = traceback()
@@ -70,7 +70,8 @@ function setup(instance: Instance, deriver: () -> unknown, setter: (Instance) ->
weak[key] = instance
local function ref()
- local _ = nodes -- prevent gc of state while instance exists
+ --todo:local _ = nodes -- prevent gc of state while instance exists
+ local _ = setter
local instance = weak[key] :: Instance
hold[key] = instance.Parent and instance or nil -- prevent gc of instance while parented
end
@@ -79,8 +80,10 @@ function setup(instance: Instance, deriver: () -> unknown, setter: (Instance) ->
instance:GetPropertyChangedSignal("Parent"):Connect(ref)
end
+-- todo: move `fn` as arg?
+
local function bind_property(instance: Instance, property: string, fn: () -> unknown)
- setup(instance, fn, function(instance_weak: any)
+ setup(instance, function(instance_weak: any)
instance_weak[property] = fn()
end)
end
@@ -89,7 +92,7 @@ local function bind_parent(instance: Instance, fn: () -> Instance?)
instance.Destroying:Connect(function()
instance= nil :: any -- allow gc when destroyed
end)
- setup(instance, fn, function(instance)
+ setup(instance, function(instance)
local _ = instance -- state will strongly reference instance when parent is bound
instance.Parent = fn()
end)
@@ -99,7 +102,7 @@ local function bind_children(parent: Instance, fn: () -> { Instance })
local currentChildrenSet: { [Instance]: true } = {} -- cache of all children parented before update
local newChildrenSet: { [Instance]: true } = {} -- cache of all children parented after update
- setup(parent, fn, function(parent_weak)
+ setup(parent, function(parent_weak)
local newChildren = fn() -- all (and only) children that should be parented after this update
if newChildren and type(newChildren) ~= "table" then
throw(`Cannot parent instance of type { type(newChildren) } `)
diff --git a/src/init.lua b/src/init.lua
index eddf429..18d9958 100644
--- a/src/init.lua
+++ b/src/init.lua
@@ -6,7 +6,7 @@
if not game then script = (require :: any) "test/wrap-require" end
local create = require(script.create)
-local wrap = require(script.wrap)
+local source = require(script.source)
local watch = require(script.watch)
local derive = require(script.derive)
local map = require(script.map)
@@ -14,18 +14,17 @@ local map = require(script.map)
-- local Changed = require(script.Change)
-- local Created = require(script.Created)
-local spring, updateSprings = require(script.spring)()
+local spring, update_springs = require(script.spring)()
local flags = require(script.flags)
type Map = { [K]: V }
-type Unwrapper = (T) -> T
type Setter = ( (new: T, force: true?) -> T ) & ( (update: (old: T) -> T, force: true?) -> T )
local vide = {
-- core
create = create,
- wrap = wrap,
+ source = source,
derive = derive,
map = map,
watch = watch,
@@ -45,7 +44,7 @@ local vide = {
-- test
step = function(dt: number)
- updateSprings(dt)
+ update_springs(dt)
end
}
diff --git a/src/wrap.lua b/src/source.lua
similarity index 91%
rename from src/wrap.lua
rename to src/source.lua
index b822720..e47cd9e 100644
--- a/src/wrap.lua
+++ b/src/source.lua
@@ -13,7 +13,7 @@ local set = graph.set
type State = (() -> T) & ((T) -> T)
-local function wrap(value: T | () -> T): State
+local function source(value: T | () -> T): State
if type(value) == "function" then value = value() end
local node = create(value :: T)
@@ -29,4 +29,4 @@ local function wrap(value: T | () -> T): State
end
end
-return wrap
+return source
diff --git a/src/spring.lua b/src/spring.lua
index 5ef50c5..8495897 100644
--- a/src/spring.lua
+++ b/src/spring.lua
@@ -29,18 +29,20 @@ local create = graph.create
local get = graph.get
local set = graph.set
+type Node = graph.Node
+
type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3
type SpringData = {
- Alpha: number;
- Duration: number;
- Period: number;
- Damping: number;
- Velocity: number;
- InitialVelocity: number;
- Initial: T;
- Target: T;
- Input: State;
+ alpha: number,
+ duration: number,
+ period: number,
+ damping_ratio: number,
+ velocity: number,
+ initial_velocity: number,
+ initial_position: T,
+ target_position: T,
+ target: () -> T
}
type Lerp = (initial: T, target: T, alpha: number) -> T
@@ -88,78 +90,76 @@ local lerpable: { [string]: Lerp } = {
end :: Lerp,
}
-local activeSprings: { [State]: SpringData } = {}
-setmetatable(activeSprings, { __mode = "ks" })
+local springs: { [SpringData]: Node } = {}
+setmetatable(springs, { __mode = "vs" })
-local function spring(input: MaybeState, period: number, damping: number?): State
- local initial = unwrap(input) :: T
- local output = create(initial)
+local function spring(target: () -> T, period: number, damping_ratio: number?): () -> T
+ local initial_position = target()
+ local node = create(initial_position)
- local springData: SpringData = {
- Alpha = 0,
- Duration = 0,
- Period = period,
- Damping = damping or 1,
- Velocity = 0,
- InitialVelocity = 0,
- Initial = initial,
- Target = initial,
- Input = input :: any,
- LastInput = initial,
- LastOutput = initial
+ local data: SpringData = {
+ alpha = 0,
+ duration = 0,
+ period = period,
+ damping_ratio = damping_ratio or 1,
+ velocity = 0,
+ initial_velocity = 0,
+ initial_position = initial_position,
+ target_position = initial_position,
+ target = target
}
- activeSprings[output] = springData
+ springs[data] = node
- return output
-end
-
-local function updateSprings(dt: number)
- for node, data in next, activeSprings do
- local currentTarget = get(data.Input)
- if currentTarget ~= data.Target then
- data.Target = currentTarget
- data.Initial = get(node)
- data.Target = get(data.Input)
- data.Alpha = 0
- data.Duration = 0
- data.InitialVelocity = data.Velocity
- end
-
- local initial: Animatable = data.Initial
- local target: Animatable = data.Target
- local targetType: string = typeof(target)
-
- if targetType ~= typeof(initial) then
- activeSprings[node] = nil
- warn(string.format(
- "Mismatched state value types, cancelling state update (initial value: %s, target value: %s)",
- typeof(initial),
- targetType
- ))
- throw(`Cannot tween type { typeof(initial) } and { targetType }`)
- continue
- end
-
- local lerp: Lerp = lerpable[targetType]
-
- if lerp == nil then
- activeSprings[node] = nil
- throw(`Cannot animate type { targetType }`)
- continue
- end
-
- local newTime = data.Duration + dt
- local newAlpha = solve(data.Period, data.Damping, data.InitialVelocity, newTime)
-
- data.Velocity = -(newAlpha - data.Alpha)/dt
- data.Alpha = newAlpha
- data.Duration = newTime
-
- local value = lerp(initial, target, newAlpha)
-
- set(node, value)
+ return function()
+ return get(node)
end
end
-return function() return spring, updateSprings end
+local function update_springs(dt: number)
+ for data, output in next, springs do
+ if data.target() ~= data.target_position then
+ data.target = data.target()
+ data.initial_position = get(output)
+ data.alpha = 0
+ data.duration = 0
+ data.initial_velocity = data.velocity
+ end
+
+ local initial_position: Animatable = data.initial_position
+ local target_position: Animatable = data.target_position
+ local target_type: string = typeof(target_position)
+
+ if target_type ~= typeof(initial_position) then
+ springs[data] = nil
+ warn(string.format(
+ "Mismatched state value types, cancelling state update (initial value: %s, target value: %s)",
+ typeof(initial_position),
+ target_type
+ ))
+ throw(`Cannot tween type { typeof(initial_position) } and { target_type }`)
+ continue
+ end
+
+ local lerp: Lerp = lerpable[target_type]
+
+ if lerp == nil then
+ springs[data] = nil
+ throw(`Cannot animate type { target_type }`)
+ continue
+ end
+
+ local new_time = data.duration + dt
+ local new_alpha = solve(data.period, data.damping_ratio, data.initial_velocity, new_time)
+
+ data.velocity = -(new_alpha - data.alpha)/dt
+ data.alpha = new_alpha
+ data.duration = new_time
+
+ local value = lerp(initial_position, target_position, new_alpha)
+
+ set(output, value)
+ end
+end
+
+return function() return spring, update_springs end
diff --git a/test/testkit.luau b/test/testkit.luau
index e7ba3c9..d244d7f 100644
--- a/test/testkit.luau
+++ b/test/testkit.luau
@@ -319,9 +319,10 @@ end
local function print2(v: unknown)
type Buffer = { n: number, [number]: string }
+ type Cyclic = { [{}]: true }
-- overkill concatenationless string buffer
- local function tos(value: any, stack: number, str: Buffer)
+ local function tos(value: any, stack: number, str: Buffer, cyclic: Cyclic)
local TAB = " "
local indent = table.concat(table.create(stack, TAB))
@@ -339,10 +340,18 @@ local function print2(v: unknown)
local n = str.n
str[n + 1] = "{}"
str.n = n + 1
- else
+ else -- is table
local tabbed_indent = indent .. TAB
str.n += 1
+
+ if cyclic[value] then
+ str[str.n] = color.gray "*cyclic reference*"
+ return
+ else
+ cyclic[value] = true
+ end
+
str[str.n] = "{\n"
local i, v = next(value, nil)
@@ -363,7 +372,7 @@ local function print2(v: unknown)
str[n + 1] = " = "
str.n = n + 1
- tos(v, stack + 1, str)
+ tos(v, stack + 1, str, cyclic)
i, v = next(value, i)
@@ -380,7 +389,8 @@ local function print2(v: unknown)
end
local str = { n = 0 }
- tos(v, 0, str)
+ local cyclic = {}
+ tos(v, 0, str, cyclic)
print(table.concat(str))
end
diff --git a/test/tests.luau b/test/tests.luau
index 7310f82..926d4c0 100644
--- a/test/tests.luau
+++ b/test/tests.luau
@@ -1,12 +1,12 @@
-----------------------------------------------------------------------------------------------------------------------
--- unit.lua
-----------------------------------------------------------------------------------------------------------------------
+local testkit = require("test/testkit")
+local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
-local TEST, CASE, CHECK, FINISH, SKIP = require("test/testkit").test()
-
-local mock = require "test/mock"
local Signal = require "test/goodsignal"
-local Instance, Vector3, Color3, Vector2, UDim2 = mock.Instance, mock.Vector3, mock.Color3, mock.Vector2, mock.UDim2
+local mock = require "test/mock"
+
+
+local Instance, Vector3, Color3, Vector2, UDim2 =
+ mock.Instance, mock.Vector3, mock.Color3, mock.Vector2, mock.UDim2
local vide = require "src/init"
@@ -187,23 +187,23 @@ TEST("graph", function()
end
end)
-TEST("wrap()", function()
- local wrap = vide.wrap
+TEST("source()", function()
+ local source = vide.source
local watch = vide.watch
- do CASE "Wrap value"
- local state = wrap(1)
+ do CASE "source value"
+ local state = source(1)
CHECK(state() == 1)
end
do CASE "Setter"
- local state = wrap(1)
+ local state = source(1)
state(2) -- set directly
CHECK(state() == 2)
end
do CASE "Does not update if same value"
- local state = wrap(1)
+ local state = source(1)
local updates = -1
watch(function()
@@ -218,11 +218,11 @@ TEST("wrap()", function()
end)
TEST("derive()", function()
- local wrap = vide.wrap
+ local source = vide.source
local derive = vide.derive
do CASE "Derive new value on state change"
- local state = wrap(1)
+ local state = source(1)
local derived = derive(function()
return tostring(state())
@@ -235,7 +235,7 @@ TEST("derive()", function()
do CASE "Derive from updated"
do
- local a = wrap(1)
+ local a = source(1)
local b = derive(function()
return a() + 1
@@ -253,7 +253,7 @@ TEST("derive()", function()
--[[
do CASE "Cleanup"
- local count, set = wrap(1)
+ local count, set = source(1)
local derived = derive(function(from)
return { Value = from(count), Destroyed = false }
@@ -271,7 +271,7 @@ TEST("derive()", function()
do CASE "Garbage collection"
do -- check that `b` does not allow gc of `a`
- local a = wrap(1)
+ local a = source(1)
local _b = derive(function()
return a()
@@ -286,7 +286,7 @@ TEST("derive()", function()
end
do -- check that `a` allows gc of `b`
- local a = wrap(1)
+ local a = source(1)
local b = derive(function()
return a()
@@ -303,7 +303,7 @@ TEST("derive()", function()
do CASE "Garbage collection 2"
-- creats a chain `a -> b -> c` where `a` is the root
local function setup()
- local a = wrap(1)
+ local a = source(1)
local b = derive(function()
return a()
@@ -350,12 +350,12 @@ TEST("derive()", function()
end)
TEST("watch()", function()
- local wrap = vide.wrap
+ local source = vide.source
local watch = vide.watch
do CASE "Capture states"
- local a = wrap(1)
- local b = wrap(1)
+ local a = source(1)
+ local b = source(1)
local runcount = 0
watch(function()
@@ -372,8 +372,8 @@ TEST("watch()", function()
end
do CASE "Stop watch"
- local a = wrap(1)
- local b = wrap(1)
+ local a = source(1)
+ local b = source(1)
local runcount = 0
local unwatch = watch(function()
@@ -388,7 +388,7 @@ TEST("watch()", function()
end
do CASE "Side-effect cleanup"
- local state = wrap(1)
+ local state = source(1)
local effect_runcount = 0
local cleanup_runcount = 0
@@ -417,7 +417,7 @@ TEST("watch()", function()
end
do -- state prevents gc of watcher
- local state = wrap(1)
+ local state = source(1)
local wref
@@ -433,7 +433,7 @@ TEST("watch()", function()
end
do -- watcher can gc if stopped
- local state = wrap(1)
+ local state = source(1)
local effect = factory(state)
local unwatch = watch(effect)
@@ -453,7 +453,7 @@ TEST("watch()", function()
local wref
do
- local state = wrap(1)
+ local state = source(1)
local effect = factory(state)
watch(effect)
wref = weak { state }
@@ -480,7 +480,7 @@ end)
TEST("create()", function()
local create = vide.create
- local wrap = vide.wrap
+ local source = vide.source
do CASE "Apply default properties"
local defaults = require("src/defaults")
@@ -528,8 +528,8 @@ TEST("create()", function()
end
do CASE "Binding properties to state"
- local name = wrap("Hi")
- local text = wrap("Bye")
+ local name = source("Hi")
+ local text = source("Bye")
local label = create "TextLabel" {
Name = name,
@@ -548,7 +548,7 @@ TEST("create()", function()
do CASE "Binding garbage collection"
do -- instance should gc despite property bound to state
- local state = wrap("Hi")
+ local state = source("Hi")
local wref = weak {
create "TextLabel" {
@@ -561,7 +561,7 @@ TEST("create()", function()
end
do -- instance should NOT gc despite property bound to state when parented
- local state = wrap("Hi")
+ local state = source("Hi")
local parent = create "Frame" {}
@@ -588,18 +588,20 @@ TEST("create()", function()
end
do -- state should not gc once exits scope while instance still exists
- local _label
+ local label
local wref
do
- local state = wrap("Hi")
- wref = weak { state }
- _label = create "TextLabel" {
+ local state = source("Hi")
+ label = create "TextLabel" {
Name = state,
}
+ wref = weak { state :: any, label }
+
end
gc()
+ CHECK(wref[2])
CHECK(wref[1])
end
@@ -607,7 +609,7 @@ TEST("create()", function()
local wref
do
- local text = wrap("Hi")
+ local text = source("Hi")
local box = create "TextLabel" {
Text = text,
@@ -624,7 +626,7 @@ TEST("create()", function()
--[[
do -- binding should gc despite state still existing after instance is gc
- local state = wrap("Hi")
+ local state = source("Hi")
do
local instance = create "TextLabel" {
@@ -650,7 +652,7 @@ TEST("create()", function()
end
do CASE "Bind same state to multiple instance properties"
- local state = wrap "1"
+ local state = source "1"
local text = create "TextBox" {
Name = state,
@@ -666,7 +668,7 @@ TEST("create()", function()
end
do CASE "Bind children"
- local state = wrap({} :: {}?)
+ local state = source({} :: {}?)
local a, b, c =
create "TextLabel" { Name = "A" },
@@ -699,7 +701,7 @@ TEST("create()", function()
do CASE "Parent set to nil by state does not allow gc"
-- this is technically a bug but we test for this anyways to confirm behavior
local parent = create "Frame" { Name = "Parent" }
- local state = wrap(parent :: Frame?)
+ local state = source(parent :: Frame?)
do
wref.child = create "Frame" { Parent = state, Name = "Child" } :: Frame?
@@ -721,35 +723,38 @@ TEST("create()", function()
]]
do CASE "GC test"
- local data = setmetatable({}, {})
- local proxy = setmetatable({}, { __mode = "v" })
+ local wref
- local ref = setmetatable({}, { __mode = "v" })
+ do
+ local data = setmetatable({}, {})
+ local proxy = setmetatable({}, { __mode = "v" })
- --proxy.data = data --? (this line should not affect outcome)
- -- `data` strongly references `proxy`
- data.connection = proxy
+ local ref = setmetatable({}, { __mode = "v" })
- -- `ref` strongly references `data`
- ref[data] = proxy
+ --proxy.data = data --? (this line should not affect outcome)
+ -- `data` strongly references `proxy`
+ data.connection = proxy
- -- although `ref` is weak to values, `data` keeps `proxy` alive
- -- this forms a sort of cyclic reference that the luau gc is unable to detect
+ -- `ref` strongly references `data`
+ ref[data] = proxy
- wref.data = data
- wref.proxy = proxy
- data = nil :: any
- proxy = nil :: any
+ -- although `ref` is weak to values, `data` keeps `proxy` alive
+ -- this forms a sort of cyclic reference that the luau gc is unable to detect
+
+ wref = { data = data, proxy = proxy }
+ end
gc()
CHECK(wref.data and wref.proxy)
end
+
+
end)
TEST("map()", function()
- local wrap = vide.wrap
- local unwrap = vide.unwrap
+ local source = vide.source
+ local unsource = vide.unsource
local map = vide.map
do CASE "Use integer"
@@ -777,7 +782,7 @@ TEST("map()", function()
end
do CASE "Use state"
- local state = wrap { 1, 2, 3 }
+ local state = source { 1, 2, 3 }
local derived = map(state, function(_, v)
return tostring(v)
@@ -791,7 +796,7 @@ TEST("map()", function()
end
do CASE "Cache result"
- local state, set = wrap { 1, 2, 3 }
+ local state, set = source { 1, 2, 3 }
local runcount = table.create(3, 0)
@@ -815,7 +820,7 @@ TEST("map()", function()
end
do CASE "Removal reflected"
- local state, set = wrap { 1, 2, 3 }
+ local state, set = source { 1, 2, 3 }
local derived = map(state, function(i, v)
return tostring(v)
@@ -835,7 +840,7 @@ TEST("map()", function()
local Children = vide.Children
do CASE "Bind children"
- local state, set = wrap { "A", "B", "C" }
+ local state, set = source { "A", "B", "C" }
local derived = map(state, function(i, v)
return create "TextLabel" {
@@ -866,7 +871,7 @@ TEST("map()", function()
end
do CASE "Use optional destructor"
- local state, set = wrap { 1, 2, 3 }
+ local state, set = source { 1, 2, 3 }
local derived = map(state, function(i, v)
return { Value = v, Destroyed = false }
end, function(v)
@@ -888,7 +893,7 @@ TEST("map()", function()
do CASE "Garbage collection"
do -- check that `derived` does not allow gc of `state`
- local state = wrap {}
+ local state = source {}
local derived = map(state, function(i, v)
return v
@@ -902,7 +907,7 @@ TEST("map()", function()
end
do -- check that `state` allows gc of `derived`
- local state = wrap {}
+ local state = source {}
local derived = map(state, function(i, v)
return i, v
@@ -917,33 +922,15 @@ TEST("map()", function()
end
end)
-TEST("apply()", function()
- local apply = vide.apply
-
- -- uses same application method as `create()` internally, further testing unnecessary
- do CASE "Apply properties"
- local part = Instance.new("Part") :: Part
-
- apply(part) {
- Position = Vector3.new(1, 1, 1),
- Color = Color3.new(1, 0, 0)
- }
-
- CHECK(part.Position == Vector3.new(1, 1, 1))
- CHECK(part.Color == Color3.new(1, 0, 0))
- end
-end)
-
TEST("spring()", function()
- local wrap = vide.wrap
- local unwrap = vide.unwrap
+ local source = vide.source
local spring = vide.spring
do CASE "Update state (on next hearbeat resumption cycle)"
- local number, set = wrap(10)
- local springed = spring(number, 1, 1)
+ local value = source(10)
+ local springed = spring(value, 1, 1)
- set(20)
+ value(20)
CHECK(springed() == 10)
vide.step(1/60)
CHECK(springed() ~= 10)
@@ -952,7 +939,7 @@ TEST("spring()", function()
do CASE "Garbage collection"
do -- `spring` should not allow gc of `state`
- local state = wrap(10)
+ local state = source(10)
local _springed = spring(state, 1, 1)
wref.state, state = state, nil :: any
@@ -962,9 +949,9 @@ TEST("spring()", function()
end
- do -- `number` should allow gc of `spring`
- local number = wrap(10)
- local springed = spring(number, 1, 1) :: State?
+ do -- `value` should allow gc of `spring`
+ local value = source(10)
+ local springed = spring(value, 1, 1) :: State?
wref.springed, springed = springed, nil
@@ -977,7 +964,7 @@ TEST("spring()", function()
local create = vide.create
do CASE "Garbage collection (binded)"
- local number = wrap(10)
+ local number = source(10)
local springed = spring(number, 1, 1) :: State?
local _label = create "TextLabel" {
@@ -994,7 +981,7 @@ end)
TEST("Event", function()
local create = vide.create
local Event = vide.Event
- local wrap = vide.wrap
+ local source = vide.source
do CASE "Connect event"
local connected = false
@@ -1018,7 +1005,7 @@ TEST("Event", function()
do CASE "Bind connection to state"
local countA = 0
local countB = 0
- local listener, set = wrap(function() countA += 1 end :: () -> ()?)
+ local listener, set = source(function() countA += 1 end :: () -> ()?)
local event = (Signal.new() :: any) :: RBXScriptSignal & { Fire: any }
@@ -1049,7 +1036,7 @@ end)
TEST("Changed", function()
local create = vide.create
local Changed = vide.Changed
- local wrap = vide.wrap
+ local source = vide.source
do CASE "Connects event"
local connected = false
@@ -1067,7 +1054,7 @@ TEST("Changed", function()
do CASE "Bind connection to state"
local countA = 0
local countB = 0
- local listener, set = wrap(function() countA += 1 end :: () -> ()?)
+ local listener, set = source(function() countA += 1 end :: () -> ()?)
local label = create "TextLabel" {
@@ -1118,13 +1105,13 @@ end)]]
TEST("strict", function()
vide.strict = true
- local wrap = vide.wrap
- local unwrap = vide.unwrap
+ local source = vide.source
+ local unsource = vide.unsource
local derive = vide.derive
local watch = vide.watch
do CASE "Error on derived callback yield"
- local state = wrap(1)
+ local state = source(1)
local ok = pcall(function()
local _derived = derive(function(from)
@@ -1137,7 +1124,7 @@ TEST("strict", function()
end
do CASE "Error on watcher callback yield"
- local state = wrap(1)
+ local state = source(1)
local ok = pcall(function()
local _derived = watch(function(from)
@@ -1150,7 +1137,7 @@ TEST("strict", function()
end
do CASE "Run derived callback twice"
- local state, set = wrap(1)
+ local state, set = source(1)
local runcount = 0
local derived = derive(function(from)
@@ -1165,7 +1152,7 @@ TEST("strict", function()
end
do CASE "Run watcher callback twice"
- local state, set = wrap(1)
+ local state, set = source(1)
local runcount = 0
watch(function(from)
@@ -1192,7 +1179,7 @@ TEST("strict", function()
end
do CASE "Does not allow same table set"
- local _, set = wrap()
+ local _, set = source()
local t = {}
diff --git a/todo.md b/todo.md
index 3c6a999..29e2de1 100644
--- a/todo.md
+++ b/todo.md
@@ -1,6 +1,60 @@
-# Todo
+```lua
+function TextInput(p: {
+ DefaultText: string,
+ Output: (string) -> ()
+} & Layout)
+ return create "TextBox" {
+ Layout = p.Layout,
+ Children = p.Children
+
+ BackgroundText = DefaultText,
+
+ [{"Changed"}] = function(self)
+ p.Output(self.Text)
+ end
+ }
+end
+
+function Counter()
+ local count = source(0)
+
+ return create "TextButton" {
+ Text = count
+ }
+end
+
+source
+derive
+map
+
+spring
+```
+
+onCleanup
+Index
+For
+untrack
+batch
+
+## version 1
+
+```lua
+map(items, function(item, i)
+ return Item {
+ Item = item, -- primitive
+ LayoutOrder = i
+ }
+end)
+```
+
+## version 2
+
+```lua
+each(items, function(item, i)
+ return Item {
+ Item = item, -- state
+ LayoutOrder = i
+ }
+end)
+```
-- investigate solid js `for` and batching
-- equality checking of derived state updates to prevent redundant updates of derived state
-- watcher callbacks when watcher watches a parent and child
-- stores