5.8 KiB
Control Flow
Eventually you will 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. These sources can be assigned as children, meaning the displayed children will update when the input source updates.
Control flow functions are special, because they run their components in a new reactive scope, which can be destroyed independently of the reactive scope that called the control flow function itself. This means that parts of your app can be independently created then destroyed and cleaned.
show()
The most basic control flow function is show(), which is used to conditionally
show a component.
local source = vide.source
local show = vide.show
local function JoinMenu()
local joined = source(false)
local function JoinButton()
return Button {
Activated = function() joined(true) end
}
end
return create "Frame" {
show(function() return not joined() end, JoinButton)
}
end
This will make a button to join if you have not joined already.
You can also pass a third argument, a fallback to show if the condition is falsey.
local function JoinMenu()
local joined = source(false)
local function JoinButton()
return Button {
Activated = function() joined(true) end
}
end
local function LeaveButton()
return Button {
Activated = function() joined(false) end
}
end
return create "Frame" {
show(joined, LeaveButton, JoinButton)
}
end
The reactive graph for the above example:
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#1B1B1F",
"primaryTextColor": "#fff",
"primaryBorderColor": "#1B1B1F",
"lineColor": "#79B8FF",
"tertiaryColor": "#161618",
"tertiaryBorderColor": "#1C1C1F"
}
}}%%
flowchart
subgraph root ["mount() scope"]
direction LR
joined --> show -.- subroot
subgraph subroot ["show() scope"]
direction LR
Button
end
end
The dotted line indicates that the new reactive scope isn't actually connected
to the show on the graph, it is only managed internally through code.
switch()
Similar to show(), switch(), also condtionally displays one instance at a
time. It is more flexible since it can show one of many components, based on a
table used to map a source value to a component.
local source = vide.source
local switch = vide.switch
local function JoinMenu()
local joined = source(false)
local function JoinButton()
return Button {
Activated = function() joined(true) end
}
end
local function LeaveButton()
return Button {
Activated = function() joined(false) end
}
end
return create "Frame" {
switch(joined) {
[true] = LeaveButton,
[false] = JoinButton
}
}
end
This example is equivalent to the previous one.
The switch can map any value to any component.
type ActiveMenu = "none" | "inventory" | "shop" | "settings"
local menu = source "none"
switch(menu) {
inventory = InventoryMenu,
shop = ShopMenu.
settings = SettingsMenu
}
The reactive graph for the above example:
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#1B1B1F",
"primaryTextColor": "#fff",
"primaryBorderColor": "#1B1B1F",
"lineColor": "#79B8FF",
"tertiaryColor": "#161618",
"tertiaryBorderColor": "#1C1C1F"
}
}}%%
flowchart
subgraph root ["mount() scope"]
direction LR
joined --> show -.- subroot
subgraph subroot ["switch() scope"]
direction LR
Button
end
end
indexes()
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, indexes() allows you to create an instance for each table index,
to display the value at that index.
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 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.
When the value at an index is changed, the function is not reran. Instead, the given source for that index is updated.
An element is only destroyed if the value of an index is set to nil.
The reactive graph for the above example:
%%{init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#1B1B1F",
"primaryTextColor": "#fff",
"primaryBorderColor": "#1B1B1F",
"lineColor": "#79B8FF",
"tertiaryColor": "#161618",
"tertiaryBorderColor": "#1C1C1F"
}
}}%%
flowchart
subgraph root ["mount() 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
Together, these control flow functions cover the majority of cases where you need to dynamically create and destroy parts of your UI.