mirror of
https://github.com/centau/vide.git
synced 2026-08-20 23:01:37 +00:00
202 lines
5.3 KiB
Markdown
202 lines
5.3 KiB
Markdown
# Control Flow
|
|
|
|
Eventually you may need a way to dynamically create and destroy UI elements
|
|
resulting from source updates. Vide provides functions to help you do this,
|
|
known as *control flow* functions.
|
|
|
|
These functions return new sources, which hold the instances to be displayed.
|
|
|
|
Control flow functions run their components in a new stable scope, which can
|
|
be destroyed independently of the stable scope that called the control flow
|
|
function. This means parts of your app can be independently created and
|
|
destroyed.
|
|
|
|
## switch()
|
|
|
|
`switch()` condtionally displays one instance at a time. It uses a table to map
|
|
a source value to a component.
|
|
|
|
```lua
|
|
local source = vide.source
|
|
local switch = vide.switch
|
|
|
|
local function Button(props: {
|
|
Text: string,
|
|
Activated: () -> ()
|
|
})
|
|
local hovered = source(false)
|
|
|
|
return create "TextButton" {
|
|
Text = props.Text,
|
|
Activated = props.Activated,
|
|
|
|
TextColor3 = function()
|
|
return hovered() and Color3.new(1, 1, 1) or Color3.new(.7, .7, .7)
|
|
end,
|
|
|
|
MouseEnter = function() hovered(true) end,
|
|
MouseLeave = function() hovered(false) end
|
|
}
|
|
end
|
|
|
|
local function JoinMenu()
|
|
local joined = source(false)
|
|
|
|
local function JoinButton()
|
|
return Button {
|
|
Text = "Join",
|
|
Activated = function() joined(true) end
|
|
}
|
|
end
|
|
|
|
local function LeaveButton()
|
|
return Button {
|
|
Text = "Leave"
|
|
Activated = function() joined(false) end
|
|
}
|
|
end
|
|
|
|
return create "Frame" {
|
|
switch(joined) {
|
|
[true] = LeaveButton,
|
|
[false] = JoinButton
|
|
}
|
|
}
|
|
end
|
|
```
|
|
|
|
The reactive graph for the above example:
|
|
|
|
```mermaid
|
|
%%{init: {
|
|
"theme": "base",
|
|
"themeVariables": {
|
|
"primaryColor": "#1B1B1F",
|
|
"primaryTextColor": "#fff",
|
|
"primaryBorderColor": "#1B1B1F",
|
|
"lineColor": "#79B8FF",
|
|
"tertiaryColor": "#161618",
|
|
"tertiaryBorderColor": "#1C1C1F"
|
|
}
|
|
}}%%
|
|
|
|
graph
|
|
|
|
subgraph root["root scope"]
|
|
direction LR
|
|
joined --> switch -.- subroot
|
|
|
|
subgraph subroot["switch scope"]
|
|
direction LR
|
|
effect["TextColor3 effect"]
|
|
end
|
|
end
|
|
```
|
|
|
|
A `switch()` call creates a new effect and a new stable scope as seen in the
|
|
above graph. Whenever `menu` updates, it causes the `switch` effect to run,
|
|
which will destroy and recreate the switch scope with the new component.
|
|
|
|
This will also destroy the internal effect that the button uses to highlight
|
|
itself when it is hovered, each time the switch is rerun.
|
|
|
|
## indexes()
|
|
|
|
Often, you will have a table of values with each value displayed in a similar
|
|
manner. Rather than manually looping over each value to generate a corresponding
|
|
UI element, `indexes()` allows you to create elements each corresponding to a
|
|
table index, to display the value at that index.
|
|
|
|
```lua
|
|
local todoList = source {
|
|
"finish the crash course",
|
|
"star vide's GitHub"
|
|
}
|
|
|
|
local function TodoList(props: { list: () -> Array<string> })
|
|
return create "Frame" {
|
|
create "UIListLayout" {},
|
|
|
|
indexes(todoList, function(todo, i)
|
|
return create "TextLabel" {
|
|
Text = function()
|
|
return i .. ": " .. todo()
|
|
end,
|
|
|
|
LayoutOrder = i
|
|
}
|
|
end)
|
|
}
|
|
end
|
|
|
|
TodoList { list = todoList }
|
|
```
|
|
|
|
For each index in the given source table, the given function will be called
|
|
with:
|
|
|
|
1. a source containing the value of the index
|
|
2. the index itself
|
|
|
|
When the value at an index is changed, the function is not reran. Instead, the
|
|
given source for that index is updated.
|
|
|
|
Any time the input source table is updated, the given function will be ran for
|
|
any newly added indexes, while any removed indexes (indexes now with a `nil`
|
|
value), will have its corresponding reactive scope destroyed to clean up that
|
|
element.
|
|
|
|
`indexes()` is said to *map* each table index to a new UI element that can
|
|
update to display the current value at that index. Each table index is given a
|
|
single corresponding UI element.
|
|
|
|
The reactive graph for the above example:
|
|
|
|
```mermaid
|
|
%%{init: {
|
|
"theme": "base",
|
|
"themeVariables": {
|
|
"primaryColor": "#1B1B1F",
|
|
"primaryTextColor": "#fff",
|
|
"primaryBorderColor": "#1B1B1F",
|
|
"lineColor": "#79B8FF",
|
|
"tertiaryColor": "#161618",
|
|
"tertiaryBorderColor": "#1C1C1F"
|
|
}
|
|
}}%%
|
|
|
|
graph
|
|
|
|
subgraph root ["root scope"]
|
|
direction LR
|
|
todoList --> indexes -.- subroot1 & subroot2
|
|
|
|
subgraph subroot1 ["indexes scope 1"]
|
|
direction LR
|
|
value1[todo] --> prop1["prop binding"]
|
|
end
|
|
|
|
subgraph subroot2 ["indexes scope 2"]
|
|
direction LR
|
|
value2[todo] --> prop2[prop binding]
|
|
end
|
|
end
|
|
```
|
|
|
|
One thing to note regarding table sources, is that when you edit a table in a
|
|
source, you must set that table again to actually update the source.
|
|
|
|
```lua
|
|
local src = source { 1, 2 }
|
|
local data = src()
|
|
table.insert(data, 3) -- no effects will run
|
|
src(data) -- effects will run
|
|
```
|
|
|
|
Together, these control flow functions cover the majority of cases where you
|
|
need to dynamically create and destroy parts of your UI.
|
|
|
|
If you need to do something that these control flow functions cannot, you can
|
|
always use `mount()` within an effect to dynamically create and destroy
|
|
components on your own terms. Just remember to use `cleanup()` to unmount when
|
|
the effect reruns.
|