mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
This commit is contained in:
parent
bdd725659e
commit
d77fe0f92f
34 changed files with 1215 additions and 566 deletions
66
docs/tut/control-flow/indexes.md
Normal file
66
docs/tut/control-flow/indexes.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Control Flow
|
||||
|
||||
Vide has specific functions for dealing with sources that store a table value.
|
||||
|
||||
Often, you will have a table of values that will be displayed in a similar
|
||||
manner. Rather than manually looping over each value to generate a corresponding
|
||||
UI element, Vide provides functions `indexes()` and `values()` to do this for
|
||||
you.
|
||||
|
||||
`indexes()` maps each *index* in a table to a UI element.
|
||||
|
||||
```lua
|
||||
local names = source { "a", "b", "c" }
|
||||
|
||||
local elements = indexes(names, function(name, i)
|
||||
return create "TextLabel" {
|
||||
Text = function()
|
||||
return "Name: " .. name()
|
||||
end,
|
||||
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
What happens here is the given callback is only ever ran *once* for each index
|
||||
in the table. The callback receives two arguments, a *source* containing the
|
||||
index's value and then the index itself.
|
||||
|
||||
Anytime the value at a corresponding index changes, the source for that index
|
||||
value is updated, causing the UI element depending on it to update too.
|
||||
|
||||
`values()` behaves similarly, except it maps each *value* in a table to a UI
|
||||
element.
|
||||
|
||||
```lua
|
||||
type Item = {
|
||||
Name: string,
|
||||
Icon: number
|
||||
}
|
||||
|
||||
local items = source({} :: Array<Item>)
|
||||
|
||||
local elements = values(items, function(item, i)
|
||||
return create "ImageLabel" {
|
||||
Image = "rbxassetid://" .. item.Icon,
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
The callback is again only ever ran *once* for each value in the table. The
|
||||
callback receives two arguments, a value in the table and then a *source*
|
||||
containing the value's corresponding index.
|
||||
|
||||
Any time a value in a table changes index, the source for that value is updated,
|
||||
causing the UI element position to change.
|
||||
|
||||
In certain cases `values()` can cause less recalculation and rerenders than
|
||||
`indexes()` like when items are re-arranged and shifted within a table.
|
||||
|
||||
It is important that each value in a table is unique when using `values()`,
|
||||
and for this reason always using `indexes()` if a table contains primitive
|
||||
values.
|
||||
|
||||
Both `indexes()` and `values()` return an array of all mapped UI elements.
|
||||
66
docs/tut/control-flow/switch.md
Normal file
66
docs/tut/control-flow/switch.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Control Flow
|
||||
|
||||
Vide has specific functions for dealing with sources that store a table value.
|
||||
|
||||
Often, you will have a table of values that will be displayed in a similar
|
||||
manner. Rather than manually looping over each value to generate a corresponding
|
||||
UI element, Vide provides functions `indexes()` and `values()` to do this for
|
||||
you.
|
||||
|
||||
`indexes()` maps each *index* in a table to a UI element.
|
||||
|
||||
```lua
|
||||
local names = source { "a", "b", "c" }
|
||||
|
||||
local elements = indexes(names, function(name, i)
|
||||
return create "TextLabel" {
|
||||
Text = function()
|
||||
return "Name: " .. name()
|
||||
end,
|
||||
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
What happens here is the given callback is only ever ran *once* for each index
|
||||
in the table. The callback receives two arguments, a *source* containing the
|
||||
index's value and then the index itself.
|
||||
|
||||
Anytime the value at a corresponding index changes, the source for that index
|
||||
value is updated, causing the UI element depending on it to update too.
|
||||
|
||||
`values()` behaves similarly, except it maps each *value* in a table to a UI
|
||||
element.
|
||||
|
||||
```lua
|
||||
type Item = {
|
||||
Name: string,
|
||||
Icon: number
|
||||
}
|
||||
|
||||
local items = source({} :: Array<Item>)
|
||||
|
||||
local elements = values(items, function(item, i)
|
||||
return create "ImageLabel" {
|
||||
Image = "rbxassetid://" .. item.Icon,
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
The callback is again only ever ran *once* for each value in the table. The
|
||||
callback receives two arguments, a value in the table and then a *source*
|
||||
containing the value's corresponding index.
|
||||
|
||||
Any time a value in a table changes index, the source for that value is updated,
|
||||
causing the UI element position to change.
|
||||
|
||||
In certain cases `values()` can cause less recalculation and rerenders than
|
||||
`indexes()` like when items are re-arranged and shifted within a table.
|
||||
|
||||
It is important that each value in a table is unique when using `values()`,
|
||||
and for this reason always using `indexes()` if a table contains primitive
|
||||
values.
|
||||
|
||||
Both `indexes()` and `values()` return an array of all mapped UI elements.
|
||||
66
docs/tut/control-flow/values.md
Normal file
66
docs/tut/control-flow/values.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Control Flow
|
||||
|
||||
Vide has specific functions for dealing with sources that store a table value.
|
||||
|
||||
Often, you will have a table of values that will be displayed in a similar
|
||||
manner. Rather than manually looping over each value to generate a corresponding
|
||||
UI element, Vide provides functions `indexes()` and `values()` to do this for
|
||||
you.
|
||||
|
||||
`indexes()` maps each *index* in a table to a UI element.
|
||||
|
||||
```lua
|
||||
local names = source { "a", "b", "c" }
|
||||
|
||||
local elements = indexes(names, function(name, i)
|
||||
return create "TextLabel" {
|
||||
Text = function()
|
||||
return "Name: " .. name()
|
||||
end,
|
||||
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
What happens here is the given callback is only ever ran *once* for each index
|
||||
in the table. The callback receives two arguments, a *source* containing the
|
||||
index's value and then the index itself.
|
||||
|
||||
Anytime the value at a corresponding index changes, the source for that index
|
||||
value is updated, causing the UI element depending on it to update too.
|
||||
|
||||
`values()` behaves similarly, except it maps each *value* in a table to a UI
|
||||
element.
|
||||
|
||||
```lua
|
||||
type Item = {
|
||||
Name: string,
|
||||
Icon: number
|
||||
}
|
||||
|
||||
local items = source({} :: Array<Item>)
|
||||
|
||||
local elements = values(items, function(item, i)
|
||||
return create "ImageLabel" {
|
||||
Image = "rbxassetid://" .. item.Icon,
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
The callback is again only ever ran *once* for each value in the table. The
|
||||
callback receives two arguments, a value in the table and then a *source*
|
||||
containing the value's corresponding index.
|
||||
|
||||
Any time a value in a table changes index, the source for that value is updated,
|
||||
causing the UI element position to change.
|
||||
|
||||
In certain cases `values()` can cause less recalculation and rerenders than
|
||||
`indexes()` like when items are re-arranged and shifted within a table.
|
||||
|
||||
It is important that each value in a table is unique when using `values()`,
|
||||
and for this reason always using `indexes()` if a table contains primitive
|
||||
values.
|
||||
|
||||
Both `indexes()` and `values()` return an array of all mapped UI elements.
|
||||
|
|
@ -19,11 +19,11 @@ Some of the main focuses behind Vide's design choices:
|
|||
- Reducing the amount of imports needed for usage by leveraging Luau's syntax
|
||||
and semantics.
|
||||
- Being completely typecheckable.
|
||||
- Flexibility, particularly with integrating other libraries and allowing users
|
||||
to use their own patterns.
|
||||
- Flexibility with integrating other libraries and allowing users to use their
|
||||
own patterns.
|
||||
- Independence from instance lifetimes.
|
||||
- A powerful reactive system that can surgically update properties as a result
|
||||
of state changes.
|
||||
- A powerful reactive system that can update specific properties as a result of
|
||||
state changes, updates are immediate with no diffing needed.
|
||||
|
||||
## Structure Of A Vide App
|
||||
|
||||
|
|
|
|||
|
|
@ -40,3 +40,6 @@ the updating of the Text property anytime the count is changed.
|
|||
|
||||
All observable changes to the user are considered to be side-effects of the
|
||||
reactive system.
|
||||
|
||||
You should not update other sources using an effect. Improper usage can lead to
|
||||
unecessary updates and infinite loops.
|
||||
|
|
|
|||
|
|
@ -1,66 +1,95 @@
|
|||
# Control Flow
|
||||
|
||||
Vide has specific functions for dealing with sources that store a table value.
|
||||
Eventually you will need a way to dynamically create and destroy UI elements
|
||||
resulting from state changes. Vide provides functions to help you do this,
|
||||
known as *control flow* functions.
|
||||
|
||||
These functions return a new source, which holds the instances to be displayed.
|
||||
These sources can be assigned as children, meaning the displayed children
|
||||
will update when the input source updates.
|
||||
|
||||
One of these functions is `switch()`, used to conditionally show one of a set of
|
||||
components.
|
||||
|
||||
```lua
|
||||
local vide = require(vide)
|
||||
local source = vide.source
|
||||
local switch = vide.switch
|
||||
|
||||
local function ToggleButton(p: {
|
||||
Text: string,
|
||||
Toggle: (boolean) -> boolean
|
||||
})
|
||||
return create "TextButton" {
|
||||
Size = UDim2.fromOffset(300, 300),
|
||||
Text = p.Text,
|
||||
Activated = function()
|
||||
p.Toggle(not p.Toggle())
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
local loggedIn = source(false)
|
||||
|
||||
local function LoginMenu()
|
||||
return Frame {
|
||||
switch(loggedIn) {
|
||||
[true] = function()
|
||||
return ToggleButton { Text = "Log out", Toggle = loggedIn }
|
||||
end,
|
||||
|
||||
[false] = function()
|
||||
return ToggleButton { Text = "Log in", Toggle = loggedIn }
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
mount(function() return create "ScreenGui" { LoginMenu {} } end, game.StarterGui)
|
||||
```
|
||||
|
||||
Above is an example of using a switch to create a login menu. Each time
|
||||
`loggedIn` toggles, the current button will be destroyed, and a new button
|
||||
created, which the text to represent the current action, to log in or log out.
|
||||
|
||||
Another control flow function, `indexes()`, is used to create elements from an
|
||||
input table.
|
||||
|
||||
Often, you will have a table of values that will be displayed in a similar
|
||||
manner. Rather than manually looping over each value to generate a corresponding
|
||||
UI element, Vide provides functions `indexes()` and `values()` to do this for
|
||||
you.
|
||||
|
||||
`indexes()` maps each *index* in a table to a UI element.
|
||||
UI element, `indexes()` can autmatically run a transform function for each
|
||||
index and value, generating a UI element.
|
||||
|
||||
```lua
|
||||
local names = source { "a", "b", "c" }
|
||||
local todoList = {
|
||||
"Finish the crash course",
|
||||
"Star vide's GitHub"
|
||||
}
|
||||
|
||||
local elements = indexes(names, function(name, i)
|
||||
local elements = indexes(todoList, function(todo, i)
|
||||
return create "TextLabel" {
|
||||
Text = function()
|
||||
return "Name: " .. name()
|
||||
return i .. ": " .. todo()
|
||||
end,
|
||||
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
What happens here is the given callback is only ever ran *once* for each index
|
||||
in the table. The callback receives two arguments, a *source* containing the
|
||||
index's value and then the index itself.
|
||||
|
||||
Anytime the value at a corresponding index changes, the source for that index
|
||||
value is updated, causing the UI element depending on it to update too.
|
||||
|
||||
`values()` behaves similarly, except it maps each *value* in a table to a UI
|
||||
element.
|
||||
|
||||
```lua
|
||||
type Item = {
|
||||
Name: string,
|
||||
Icon: number
|
||||
}
|
||||
|
||||
local items = source({} :: Array<Item>)
|
||||
|
||||
local elements = values(items, function(item, i)
|
||||
return create "ImageLabel" {
|
||||
Image = "rbxassetid://" .. item.Icon,
|
||||
LayoutOrder = i
|
||||
mount(function()
|
||||
return create "ScreenGui" {
|
||||
create "UIListLayout" {}, elements
|
||||
}
|
||||
end)
|
||||
end, game.StarterGui)
|
||||
```
|
||||
|
||||
The callback is again only ever ran *once* for each value in the table. The
|
||||
callback receives two arguments, a value in the table and then a *source*
|
||||
containing the value's corresponding index.
|
||||
For each unique index in the passed table, the transform function will be called
|
||||
with 1. a source containing the value of the index, 2. the index itself.
|
||||
|
||||
Any time a value in a table changes index, the source for that value is updated,
|
||||
causing the UI element position to change.
|
||||
When the value at an index is changed, the function is not reran. Instead, the
|
||||
given source is updated instead.
|
||||
|
||||
In certain cases `values()` can cause less recalculation and rerenders than
|
||||
`indexes()` like when items are re-arranged and shifted within a table.
|
||||
`indexes()` is said to map each *index* in a table to a UI element, each index
|
||||
has a single corresponding element.
|
||||
|
||||
It is important that each value in a table is unique when using `values()`,
|
||||
and for this reason always using `indexes()` if a table contains primitive
|
||||
values.
|
||||
|
||||
Both `indexes()` and `values()` return an array of all mapped UI elements.
|
||||
An element is only destroyed if the value of an index is set to `nil`.
|
||||
|
|
|
|||
61
docs/tut/reactive-scoping.md
Normal file
61
docs/tut/reactive-scoping.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Reactive Scoping
|
||||
|
||||
This is a brief document designed to give the user more insight into how Vide's
|
||||
reactive graph works.
|
||||
|
||||
Each time you create and derive sources, a new node representing that source is
|
||||
created and added to the reactive graph. Each node stores a value and a
|
||||
side-effect function. Each node also keeps track of its parents and children,
|
||||
as well as any cleanups registered.
|
||||
|
||||
Any time a node is updated, Vide will traverse and update that node's children,
|
||||
its children's children, etc, until all nodes descending from that node has been
|
||||
updated. Traversal will stop at a node if that node's cached value does not
|
||||
change after an update.
|
||||
|
||||
For every node that is updated, a scope is opened for that node. These scopes
|
||||
are referred to as "reactive scopes". Any source read from within a node's scope
|
||||
will that node as a child. This is similar to cleanups, anytime a cleanup is
|
||||
registered, it is added to the node of the currently active scope.
|
||||
|
||||
The way Vide tracks reactive scopes, is by using a stack of nodes. The current
|
||||
active reactive scope is the node at the top of this stack.
|
||||
|
||||
When destroying a node, its descendents are traversed and also destroyed.
|
||||
When being destroyed, a node's connections (parents and children) are cleared,
|
||||
and any pending cleanup functions are ran.
|
||||
|
||||
The purpose of `root()` (which is called internally by `mount()`) is to setup
|
||||
the root node which will track any node created or derived inside its scope, or
|
||||
any cleanups registered. Without it, nodes could be garbage collected without a
|
||||
chance to run pending cleanups which can cause memory leakage.
|
||||
|
||||
Control flow functions in Vide are special, as they can dynamically create and
|
||||
destroy new root scopes.
|
||||
|
||||
It is the combination of the above which allows us to write components like so:
|
||||
|
||||
```lua
|
||||
local function Counter()
|
||||
local count = source(0)
|
||||
|
||||
local connection = stepped:Connect(function() count(count() + 1) end)
|
||||
|
||||
cleanup(function() connection:Disconnect() end)
|
||||
effect(function() print(count()) end)
|
||||
|
||||
return create "TextLabel" { Text = count }
|
||||
end
|
||||
```
|
||||
|
||||
Vide doesn't recognise this as a "component", that is a user abstraction. Vide
|
||||
just sees this as a function that creates nodes in the reactive graph.
|
||||
|
||||
Whenever the reactive scope that calls this function is destroyed, like by a
|
||||
control flow function, the registered cleanup will be called, and the effect
|
||||
(which is just a node on the reactive graph) is destroyed. The returned instance
|
||||
and the bound `count` source is just considered to be a side-effect, and with
|
||||
the reactive scope from which the side-effects stem from destroyed, the instance
|
||||
can be garbage collected - everything is nicely cleaned up.
|
||||
|
||||
> todo: add graphics
|
||||
Loading…
Add table
Add a link
Reference in a new issue