Improve docs

This commit is contained in:
Aaron Smith 2023-08-09 15:33:14 +01:00
parent 0e6e8300fb
commit 04e28f390d
13 changed files with 186 additions and 116 deletions

View file

@ -39,9 +39,9 @@ export default defineConfig({
{ text: "Introduction", link: "/tut/crash-course/1-introduction" }, { text: "Introduction", link: "/tut/crash-course/1-introduction" },
{ text: "Element Creation", link: "/tut/crash-course/2-creation" }, { text: "Element Creation", link: "/tut/crash-course/2-creation" },
{ text: "Components", link: "/tut/crash-course/3-components" }, { text: "Components", link: "/tut/crash-course/3-components" },
{ text: "State", link: "/tut/crash-course/4-state" }, { text: "Source", link: "/tut/crash-course/4-source" },
{ text: "Derived State", link: "/tut/crash-course/5-derived-state" }, { text: "Derived Source", link: "/tut/crash-course/5-derived-source" },
{ text: "Table State", link: "/tut/crash-course/6-table-state" }, { text: "Table Source", link: "/tut/crash-course/6-table-source" },
{ text: "Property Groups", link: "/tut/crash-course/7-property-groups" }, { text: "Property Groups", link: "/tut/crash-course/7-property-groups" },
] ]
}, },

View file

@ -18,8 +18,7 @@ Returns a new state with a dynamically animated value of the source.
- **Details** - **Details**
The output state value is updated every frame based on the source state The output state value is updated every frame based on the source value.
value.
The change is physically simulated according to a The change is physically simulated according to a
[spring](https://en.wikipedia.org/wiki/Simple_harmonic_motion). [spring](https://en.wikipedia.org/wiki/Simple_harmonic_motion).

View file

@ -10,7 +10,7 @@ Creates a new UI element, applying any given properties.
```lua ```lua
function create(class: string): (Properties) -> Instance function create(class: string): (Properties) -> Instance
function create(instance: Instace): (Properties) -> Instance function create(instance: Instance): (Properties) -> Instance
type Properties = Map<string|number, any> type Properties = Map<string|number, any>
``` ```
@ -71,7 +71,7 @@ Creates a new UI element, applying any given properties.
Color: Color3 Color: Color3
}) })
return create "Frame" { return create "Frame" {
BackgroundColor3 = Color, BackgroundColor3 = props.Color,
props.Layout, props.Layout,
props.Children props.Children
} }

View file

@ -4,7 +4,7 @@
## source() ## source()
Creates a new source state with the given value. Creates a new source with the given value.
- **Type** - **Type**
@ -14,11 +14,11 @@ Creates a new source state with the given value.
- **Details** - **Details**
Calling the returned state with no arguments will return its stored value, Calling the returned source with no arguments will return its stored value,
calling with arguments will set a new value. calling with arguments will set a new value.
Reading from the state from within any reactive scope will cause changes Reading from the source from within any reactive scope will cause changes
to that state to be tracked and anything depending on it to update. to that source to be tracked and anything depending on it to update.
- **Example** - **Example**
@ -32,48 +32,49 @@ Creates a new source state with the given value.
## watch() ## watch()
Runs a callback on state change. Runs a callback on source update.
- **Type** - **Type**
```lua ```lua
function watch(callback: () -> ()): Unwatch function watch(source: () -> ()): Unwatch
type Unwatch = () -> () type Unwatch = () -> ()
``` ```
- **Details** - **Details**
The callback is ran immediately to determine what states are referenced. The source callback is ran immediately to determine what states are
referenced.
Any time a state referenced in the callback is changed, the callback will be Any time a source referenced in the callback is changed, the callback will
reran. be reran.
Also returns a function that when called, stops the watcher immediately. Also returns a function that when called, stops the watcher immediately.
::: warning ::: warning
`callback()` cannot yield. `source()` cannot yield.
::: :::
- **Example** - **Example**
```lua ```lua
local state = wrap(1) local state = source(1)
watch(function() watch(function()
print(state.Value) print(state())
end) end)
-- prints 1 -- prints 1
state.Value += 1 state(state() + 1)
-- prints 2 -- prints 2
``` ```
## derive() ## derive()
Derives a new state from existing states. Derives a new source from existing sources.
- **Type** - **Type**
@ -83,13 +84,13 @@ Derives a new state from existing states.
- **Details** - **Details**
The derived state will have its value recalculated when any source state it The derived source will have its value recalculated when any source source
derives from is updated. it derives from is updated.
Anytime its value is recalculated it is also cached, subsequent calls will Anytime its value is recalculated it is also cached, subsequent calls will
retun this cached value until it recalculates again. retun this cached value until it recalculates again.
Takes a callback that is immediately run to determine what states are being Takes a callback that is immediately run to determine what sources are being
referenced. referenced.
::: warning ::: warning
@ -99,7 +100,7 @@ Derives a new state from existing states.
- **Example** - **Example**
```lua ```lua
local count = wrap(0) local count = source(0)
local text = derive(function() return `count: {count()}` end) local text = derive(function() return `count: {count()}` end)
text() -- "count: 0" text() -- "count: 0"
@ -111,7 +112,7 @@ Derives a new state from existing states.
## indexes() ## indexes()
Maps each index in a table to an object. Maps each index in a table source to an object.
- **Type** - **Type**
@ -124,17 +125,18 @@ Maps each index in a table to an object.
- **Details** - **Details**
The transform function is called only ever *once* for each index in the The transform function is called only ever *once* for each index in the
source table. The first argument is a state containing the index's value and source table. The first argument is a source containing the index's value
the second argument is just the index. and the second argument is just the index.
Anytime a new index is added, the transform function will be called again for Anytime a new index is added, the transform function will be called again
that new index. for that new index.
Anytime an existing index value changes, the transform function is not rerun, Anytime an existing index value changes, the transform function is not rerun,
instead the passed state for that index will update, causing anything instead the source value for that index will update, causing anything
depending on it to update too. depending on it to update too.
Returns a state containing an array of all objects returned by the transform. Returns a state containing an array of all objects returned by the
transform.
::: warning ::: warning
`transform()` cannot yield. `transform()` cannot yield.
@ -170,7 +172,7 @@ Maps each index in a table to an object.
## values() ## values()
Maps each value in a table to an object. Maps each value in a table source to an object.
- **Type** - **Type**
@ -184,21 +186,28 @@ Maps each value in a table to an object.
The transform function is called only ever *once* for each value in the The transform function is called only ever *once* for each value in the
source table. The first argument is the index's value and source table. The first argument is the index's value and
the second argument is a state containing the index. the second argument is a source containing the index.
Anytime a new value is added, the transform function will be called again Anytime a new value is added, the transform function will be called again
for that new value. for that new value.
Anytime an existing value's index changes, the transform function is not Anytime an existing value's index changes, the transform function is not
rerun, instead the passed state for that value will update, causing anything rerun, instead the source index for that value will update, causing anything
depending on it to update too. depending on it to update too.
Returns a state containing an array of all objects returned by the transform. Returns a state containing an array of all objects returned by the
transform.
::: warning ::: warning
`transform()` cannot yield. `transform()` cannot yield.
::: :::
::: warning
Having primitive values in the source table can cause unexpected behavior,
as duplicate primitives can result in multiple index sources being bound
to the same UI element.
:::
- **Example** - **Example**
The intended purpose of this function is to map each value in a table to The intended purpose of this function is to map each value in a table to

View file

@ -16,10 +16,24 @@ Runs a callback anytime a reactive scope is re-ran.
local data = source(1) local data = source(1)
watch(function() watch(function()
local label = create "TextLabel" { Text = data } local label = create "TextLabel" { Text = data() }
cleanup(function() cleanup(function()
label:Destroy() label:Destroy()
end) end)
end) end)
``` ```
```lua
local data = source(1)
derive(function()
local label = create "TextLabel" { Text = data() }
cleanup(function()
label:Destroy()
end)
return label
end)
```

View file

@ -7,9 +7,12 @@ local vide = require(path_to_vide)
local create = vide.create local create = vide.create
``` ```
`create()` returns a constructor for a given class which then takes a table of `create()` returns a constructor for a class which then takes a table of
properties to assign when creating a new instance for that class. properties to assign when creating a new instance for that class.
Luau allows us to omit parentheses `()` when calling functions with string or
table literals for brevity.
```lua ```lua
local frame = create "Frame" { local frame = create "Frame" {
Name = "Background", Name = "Background",
@ -17,7 +20,7 @@ local frame = create "Frame" {
} }
``` ```
String keys are assigned as properties and integer keys are assigned as child String keys are treated as properties and integer keys are treated as child
instances. instances.
```lua ```lua
@ -40,7 +43,7 @@ create "ScreenGui" {
} }
``` ```
To connect to an event, just set the event property name to a function. To connect to an event, just assign the event property a function.
All event arguments are passed into the function. All event arguments are passed into the function.
@ -51,10 +54,3 @@ create "TextButton" {
end end
} }
``` ```
In short:
- String keys = properties
- Function values = events
- Non-function values = property values
- Numeric keys = children

View file

@ -2,16 +2,14 @@
Components are custom-made reusable pieces of UI made from other pieces of UI. Components are custom-made reusable pieces of UI made from other pieces of UI.
Using components you make your application more modular and better organized. By using components you can make your application more modular and better
organized.
Components leverage functions to create self-contained UI that can even have
its own state and behavior.
```lua ```lua
local function Button(props: { local function Button(props: {
Position: UDim2, Position: UDim2,
Text: string, Text: string,
Callback: () -> () Activated: () -> ()
}) })
return create "TextButton" { return create "TextButton" {
BackgroundColor3 = Color3.fromRGB(50, 50, 50), BackgroundColor3 = Color3.fromRGB(50, 50, 50),
@ -19,7 +17,7 @@ local function Button(props: {
Position = props.Position, Position = props.Position,
Text = props.Text, Text = props.Text,
Callback = props.Callback Activated = props.Activated
} }
end end
@ -27,7 +25,7 @@ local button = Button {
Position = UDim2.new(), Position = UDim2.new(),
Text = "Click me!", Text = "Click me!",
Callback = function() Activated = function()
print "clicked" print "clicked"
end end
} }

View file

@ -1,29 +1,28 @@
# State # Source
State in Vide are the core of reactivity in Vide. *Sources* in Vide are special objects that store a single value. They are the
core of reactivity in Vide, as updates to a source can automatically update
properties or other sources depending on that source.
State contain values that can change, and when they do change, automatically A source in Vide can be created using
update anything that is using it.
A state object in Vide can be created using
[`source()`](../../api/reactivity-core.md#source). [`source()`](../../api/reactivity-core.md#source).
```lua ```lua
local source = vide.source local source = vide.source
```
```lua
local count = source(0) local count = source(0)
``` ```
The value of a state can be set by calling it with an argument, and can be read The value passed to `source()` is the initial value of the source.
The value of a source can be set by calling it with an argument, and can be read
by calling it with no arguments. by calling it with no arguments.
```lua ```lua
count(count() + 1) -- increment count state by 1 count(count() + 1) -- increment source by 1
``` ```
Below is an example of a counter component that has state. Below is an example of a stateful counter component.
```lua ```lua
local function Counter() local function Counter()
@ -39,12 +38,9 @@ local function Counter()
end end
``` ```
Any time the source value is set, anything depending on it will automatically be
updated using the new value.
Vide detects when you assign a function to a property. This is known Vide detects when you assign a function to a property. This is known
as *binding* and doing so will cause the property to *automatically* update as *binding* and doing so will cause the property to *automatically* update
whenever a state in that function is updated, by rerunning the function and whenever a source in that function is updated, by rerunning the function and
assigning its return value. You can only bind non-event assigning its return value. You can only bind non-event
properties, otherwise the function is connected as the event callback. properties, otherwise the function is connected as the event callback.
@ -53,4 +49,23 @@ UI instances, you can just focus on defining how the data maps to UI and
everything will update when changes occur. everything will update when changes occur.
Each call of `Counter {}` will create a new counter element, each with their own Each call of `Counter {}` will create a new counter element, each with their own
independent count state. independent count.
Since sources are just functions, you can pass an external source to a component
like so:
```lua
local function Text(p: {
Text: () -> string
})
return create "TextLabel" {
Text = p.Text
}
end
local text = source "hi"
Text {
Text = text
}
```

View file

@ -1,10 +1,10 @@
# Derived State # Derived Source
You can create new state from existing states. This is known as *deriving You can create new sources from existing sources. This is known as *deriving
state*. sources*.
A function that wraps a state effectively becomes a state. If a state used A function that wraps a source effectively becomes a new source. If a source
inside a function is updated, the whole function can be re-ran to recompute used inside a function is updated, the whole function can be re-ran to recompute
its value. its value.
```lua ```lua
@ -22,11 +22,11 @@ create "TextLabel" {
Sometimes when using expensive computations to derive state, you only want to Sometimes when using expensive computations to derive state, you only want to
recalculate it once when a source state has changed recalculate it once when a source state has changed
If you wrap a source state with a regular function, its value will be recomputed If you wrap a source with a regular function, its value will be recomputed
every time you call that function. every time you call that function.
[`derive()`](../../api/reactivity-core.md#derive) accepts a functions whose [`derive()`](../../api/reactivity-core.md#derive) accepts a functions whose
return value will be cached, so that subsequent calls of this derived state return value will be cached, so that subsequent calls of this derived source
will return the same cached value until one of its source states have changed. will return the same cached value until one of its input sources have changed.
```lua ```lua
local derive = vide.derive local derive = vide.derive
@ -44,7 +44,8 @@ local factorial = derive(function()
end) end)
``` ```
This can improve performance for expensive calculations. This can improve performance in cases where a source is read from multiple times
between recalculations, like in the example below:
```lua ```lua
create "TextLabel" { create "TextLabel" {

View file

@ -0,0 +1,66 @@
# Table Source
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 = indexes(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.

View file

@ -1,35 +0,0 @@
# Table State
Vide has functions for dealing with table states.
Below is an example using the above `List` class.
```lua
type Item = {
Name: string,
Icon: number
}
local items = source({} :: Array<Item>)
List {
Children = indexes(items, function(item, i)
return create "ImageLabel" {
Image = function()
return "rbxassetid://" .. item().Icon
end,
LayoutOrder = i
}
end)
}
```
Here we map each element in `items` to a value returned by a callback.
The callback is called only *once* per key. The first argument given to the
callback is a state that has the value of the table key's value.
Anytime the value of the corresponding table key changes, the state value
changes too. This saves us from having to recreate a UI element any time a
table index changes.

View file

@ -3,6 +3,8 @@
Often when creating components from existing components, you can find yourself Often when creating components from existing components, you can find yourself
repetitively passing through properties such as size or position. repetitively passing through properties such as size or position.
Example below:
```lua ```lua
function Background(props: { function Background(props: {
Color: Color3, Color: Color3,
@ -33,7 +35,7 @@ function Menu(props: {
end end
``` ```
One way this can be avoided is by using *property nesting*. In Vide, passign a One way this can be avoided is by using *property nesting*. In Vide, passing a
table value inside `props` has special semantics. Any key with a table value is table value inside `props` has special semantics. Any key with a table value is
not assigned like a property, instead the table is iterated and processed just not assigned like a property, instead the table is iterated and processed just
like the outer table is. Any properties in the nested table will be assigned like the outer table is. Any properties in the nested table will be assigned
@ -70,7 +72,8 @@ Here we created a nested group with the key `Layout` that can accept
layout-related properties. Any name could be chosen for the key. layout-related properties. Any name could be chosen for the key.
This allows us to write much more concise syntax that is also typecheckable. This allows us to write much more concise syntax that is also typecheckable.
The same can be done for properties such as children to pass table of instances. In another example we use a key named `Children` to pass arrays of instances to
be parented.
```lua ```lua
type Children = { type Children = {

View file

@ -97,6 +97,10 @@ local springs: { [SpringData<any>]: Node<any> } = {}
setmetatable(springs, { __mode = "vs" }) setmetatable(springs, { __mode = "vs" })
local function spring<T>(target: () -> T, period: number?, damping_ratio: number?): () -> T local function spring<T>(target: () -> T, period: number?, damping_ratio: number?): () -> T
if damping_ratio and damping_ratio > 1 then
throw "damping ratio cannot be greater than 1"
end
local inputs, initial_position = capture(target) local inputs, initial_position = capture(target)
local output, output_get = create(initial_position) local output, output_get = create(initial_position)