mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Initial commit
This commit is contained in:
commit
cb002f4f27
50 changed files with 4666 additions and 0 deletions
57
docs/.vitepress/config.ts
Normal file
57
docs/.vitepress/config.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { defineConfig } from "vitepress"
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: "Vide",
|
||||
description: "A declarative and reactive library for Luau.",
|
||||
base: "/vide/",
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
nav: [
|
||||
{ text: "Home", link: "/" },
|
||||
{ text: "Tutorials", link: "/tut/crash-course/1-introduction" },
|
||||
{ text: "API", link: "/api/reactivity-core"},
|
||||
{ text: "GitHub", link: "https://github.com/centau/vide" }
|
||||
],
|
||||
|
||||
sidebar: {
|
||||
"/api/": [
|
||||
{
|
||||
text: "API",
|
||||
items: [
|
||||
{ text: "Reactivity: Core", link: "/api/reactivity-core" },
|
||||
{ text: "Reactivity: Utility", link: "/api/reactivity-utility" },
|
||||
{ text: "Element Creation", link: "/api/creation" },
|
||||
{ text: "Animation", link: "/api/animation" },
|
||||
{ text: "Strict Mode", link: "/api/strict-mode" },
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
"/tut/": [
|
||||
{
|
||||
text: "Crash Course",
|
||||
items: [
|
||||
{ text: "Introduction", link: "/tut/crash-course/1-introduction" },
|
||||
{ text: "Element Creation", link: "/tut/crash-course/2-creation" },
|
||||
{ text: "Components", link: "/tut/crash-course/3-components" },
|
||||
{ text: "State", link: "/tut/crash-course/4-state" },
|
||||
{ text: "Derived State", link: "/tut/crash-course/5-derived-state" },
|
||||
{ text: "Table State", link: "/tut/crash-course/6-table-state" },
|
||||
{ text: "Property Groups", link: "/tut/crash-course/7-property-groups" },
|
||||
]
|
||||
},
|
||||
{
|
||||
text: "Tutorials",
|
||||
items: [
|
||||
{ text: "Crash Course", link: "/tut/crash-course/index" },
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
// socialLinks: [
|
||||
// { icon: "github", link: "https://github.com/centau/vide" }
|
||||
// ]
|
||||
}
|
||||
})
|
||||
4
docs/.vitepress/theme/index.js
Normal file
4
docs/.vitepress/theme/index.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// .vitepress/theme/index.js
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import './vars.css'
|
||||
export default DefaultTheme
|
||||
7
docs/.vitepress/theme/vars.css
Normal file
7
docs/.vitepress/theme/vars.css
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
:root {
|
||||
--vp-c-brand: #3086ff;
|
||||
|
||||
--vp-c-green-lighter: #02a5fd;
|
||||
--vp-c-green-dark: #2b4efd;
|
||||
--vp-c-green-darker: #5c2bfd;
|
||||
}
|
||||
37
docs/api/animation.md
Normal file
37
docs/api/animation.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Animation API
|
||||
|
||||
## spring()
|
||||
|
||||
Returns a new state with a dynamically animated value of the source.
|
||||
|
||||
- **Type**
|
||||
|
||||
```lua
|
||||
function spring<T>(
|
||||
source: () -> T & Animatable,
|
||||
period: number = 1,
|
||||
damping_ratio: number = 1
|
||||
): () -> T
|
||||
|
||||
type Animatable = number | CFrame | Color3 | UDim | UDim2 | Vector2 | Vector3
|
||||
```
|
||||
|
||||
- **Details**
|
||||
|
||||
The output state value is updated every frame based on the source state
|
||||
value.
|
||||
|
||||
The change is physically simulated according to a
|
||||
[spring](https://en.wikipedia.org/wiki/Simple_harmonic_motion).
|
||||
|
||||
`period` is the amount of time in seconds it takes for the spring to
|
||||
complete one full oscillation.
|
||||
|
||||
`damping_ratio` is the amount of resistance applied to the spring.
|
||||
|
||||
- \>1 = Overdamped (not currently supported).
|
||||
- 1 = Critically damped - reaches target without any overshoot.
|
||||
- <1 = Underdamped - reaches target with some overshoot.
|
||||
- 0 = Undamped - never stabilizes, oscillates forever.
|
||||
|
||||
Velocity is conserved between source state updates for smooth animation.
|
||||
79
docs/api/creation.md
Normal file
79
docs/api/creation.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Element Creation API
|
||||
|
||||
<br/>
|
||||
|
||||
## create()
|
||||
|
||||
Creates a new UI element, applying any given properties.
|
||||
|
||||
- ### Type
|
||||
|
||||
```lua
|
||||
function create(class: string): (Properties) -> Instance
|
||||
function create(instance: Instace): (Properties) -> Instance
|
||||
|
||||
type Properties = Map<string|number, any>
|
||||
```
|
||||
|
||||
- ### Details
|
||||
|
||||
The function can take either a `string` or an `Instance` as its first argument.
|
||||
|
||||
- If given a `string`, a new instance with the same class name will be created.
|
||||
- If given an `Instance`, a new instance that is a clone of the given instance
|
||||
will be created.
|
||||
|
||||
This returns another function that is used to apply any properties to the new
|
||||
instance.
|
||||
|
||||
- ### Property setting rules
|
||||
|
||||
- If a table value is another table, that nested table is processed so that
|
||||
any properties inside that table are also applied to the instance just
|
||||
like the outer table.
|
||||
- If a table index is a string:
|
||||
- If its value is a function then it will either bind that property to
|
||||
a state or connect it if the property type is a `RBXScriptSignal`.
|
||||
- If the value is not a function then the property will be set to that
|
||||
value.
|
||||
- If a table index is a number:
|
||||
- If its value is a function then it will parent any instances returned by
|
||||
that function as children.
|
||||
- If its value is an instance then it will be parented to the instance.
|
||||
|
||||
- ### Example
|
||||
|
||||
Basic element creation.
|
||||
|
||||
```lua
|
||||
local frame = create "Frame" {
|
||||
Name = "NewFrame",
|
||||
Position = UDim2.fromScale(1, 0)
|
||||
}
|
||||
```
|
||||
|
||||
A component using property nesting/grouping.
|
||||
|
||||
```lua
|
||||
type Layout = {
|
||||
Layout = {
|
||||
Position: UDim2?,
|
||||
Size: UDim2?,
|
||||
AnchorPoint: Vector2?
|
||||
}
|
||||
}
|
||||
|
||||
type Children = {
|
||||
Children = Array<Instance>
|
||||
}
|
||||
|
||||
function Background(props: Layout & Children & {
|
||||
Color: Color3
|
||||
})
|
||||
return create "Frame" {
|
||||
BackgroundColor3 = Color,
|
||||
props.Layout,
|
||||
props.Children
|
||||
}
|
||||
end
|
||||
```
|
||||
250
docs/api/reactivity-core.md
Normal file
250
docs/api/reactivity-core.md
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
# Reactivity API: Core
|
||||
|
||||
<br/>
|
||||
|
||||
## source()
|
||||
|
||||
Creates a new source state with the given value.
|
||||
|
||||
- **Type**
|
||||
|
||||
```lua
|
||||
function source<T>(value: T): (T?) -> T
|
||||
```
|
||||
|
||||
- **Details**
|
||||
|
||||
Calling the returned state with no arguments will return its stored value,
|
||||
calling with arguments will set a new value.
|
||||
|
||||
Reading from the state from within any reactive scope will cause changes
|
||||
to that state to be tracked and anything depending on it to update.
|
||||
|
||||
- **Example**
|
||||
|
||||
```lua
|
||||
local count = source(0)
|
||||
|
||||
count() -- 0
|
||||
|
||||
count(count() + 1) -- 1
|
||||
```
|
||||
|
||||
## watch()
|
||||
|
||||
Runs a callback on state change.
|
||||
|
||||
- **Type**
|
||||
|
||||
```lua
|
||||
function watch(callback: () -> ()): Unwatch
|
||||
|
||||
type Unwatch = () -> ()
|
||||
```
|
||||
|
||||
- **Details**
|
||||
|
||||
The callback is ran immediately to determine what states are referenced.
|
||||
|
||||
Any time a state referenced in the callback is changed, the callback will be
|
||||
reran.
|
||||
|
||||
Also returns a function that when called, stops the watcher immediately.
|
||||
|
||||
::: warning
|
||||
`callback()` cannot yield.
|
||||
:::
|
||||
|
||||
- **Example**
|
||||
|
||||
```lua
|
||||
local state = wrap(1)
|
||||
|
||||
watch(function()
|
||||
print(state.Value)
|
||||
end)
|
||||
|
||||
-- prints 1
|
||||
|
||||
state.Value += 1
|
||||
|
||||
-- prints 2
|
||||
```
|
||||
|
||||
## derive()
|
||||
|
||||
Derives a new state from existing states.
|
||||
|
||||
- **Type**
|
||||
|
||||
```lua
|
||||
function derive<T>(source: () -> T): () -> T
|
||||
```
|
||||
|
||||
- **Details**
|
||||
|
||||
The derived state will have its value recalculated when any source state it
|
||||
derives from is updated.
|
||||
|
||||
Anytime its value is recalculated it is also cached, subsequent calls will
|
||||
retun this cached value until it recalculates again.
|
||||
|
||||
Takes a callback that is immediately run to determine what states are being
|
||||
referenced.
|
||||
|
||||
::: warning
|
||||
`source()` cannot yield.
|
||||
:::
|
||||
|
||||
- **Example**
|
||||
|
||||
```lua
|
||||
local count = wrap(0)
|
||||
local text = derive(function() return `count: {count()}` end)
|
||||
|
||||
text() -- "count: 0"
|
||||
|
||||
count(1)
|
||||
|
||||
text() -- "count: 1"
|
||||
```
|
||||
|
||||
## indexes()
|
||||
|
||||
Maps each index in a table to an object.
|
||||
|
||||
- **Type**
|
||||
|
||||
```lua
|
||||
function indexes<KI, VI, VO>(
|
||||
source: () -> Map<KI, VI>,
|
||||
transform: (value: () -> VI, index: KI) -> VO
|
||||
): Array<VO>
|
||||
|
||||
- **Details**
|
||||
|
||||
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
|
||||
the second argument is just the index.
|
||||
|
||||
Anytime a new index is added, the transform function will be called again for
|
||||
that new index.
|
||||
|
||||
Anytime an existing index value changes, the transform function is not rerun,
|
||||
instead the passed state for that index will update, causing anything
|
||||
depending on it to update too.
|
||||
|
||||
Returns a state containing an array of all objects returned by the transform.
|
||||
|
||||
::: warning
|
||||
`transform()` cannot yield.
|
||||
:::
|
||||
|
||||
- **Example**
|
||||
|
||||
The intended purpose of this function is to map each index in a table to
|
||||
a UI element.
|
||||
|
||||
```lua
|
||||
type Item = {
|
||||
name: string,
|
||||
icon: number
|
||||
}
|
||||
|
||||
local items = source {} :: () -> Array<Item>
|
||||
|
||||
local displays = indexes(numbers, function(item, i)
|
||||
return ItemDisplay {
|
||||
Name = function()
|
||||
return item().name
|
||||
end,
|
||||
|
||||
Image = function()
|
||||
return "rbxassetid://" .. item().icon
|
||||
end,
|
||||
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
## values()
|
||||
|
||||
Maps each value in a table to an object.
|
||||
|
||||
- **Type**
|
||||
|
||||
```lua
|
||||
function values<KI, VI, VO>(
|
||||
source: () -> Map<KI, VI>,
|
||||
transform: (value: VI, index: () -> KI) -> VO
|
||||
): Array<VO>
|
||||
|
||||
- **Details**
|
||||
|
||||
The transform function is called only ever *once* for each value in the
|
||||
source table. The first argument is the index's value and
|
||||
the second argument is a state containing the index.
|
||||
|
||||
Anytime a new value is added, the transform function will be called again
|
||||
for that new value.
|
||||
|
||||
Anytime an existing value's index changes, the transform function is not
|
||||
rerun, instead the passed state for that value will update, causing anything
|
||||
depending on it to update too.
|
||||
|
||||
Returns a state containing an array of all objects returned by the transform.
|
||||
|
||||
::: warning
|
||||
`transform()` cannot yield.
|
||||
:::
|
||||
|
||||
- **Example**
|
||||
|
||||
The intended purpose of this function is to map each value in a table to
|
||||
a UI element.
|
||||
|
||||
```lua
|
||||
type Item = {
|
||||
name: string,
|
||||
icon: number
|
||||
}
|
||||
|
||||
local items = source {} :: () -> Array<Item>
|
||||
|
||||
local displays = values(numbers, function(item, i)
|
||||
return ItemDisplay {
|
||||
Name = item.Name
|
||||
|
||||
Image = "rbxassetid://" .. item.icon,
|
||||
|
||||
LayoutOrder = i
|
||||
}
|
||||
end)
|
||||
```
|
||||
|
||||
- **Extra**
|
||||
|
||||
When should you use `indexes()` and `values()`?
|
||||
|
||||
`values()` should be used when you have a fixed set of objects where the
|
||||
same objects can be re-arranged in the source table. It maps a value to a
|
||||
UI element.
|
||||
|
||||
e.g.
|
||||
- List of all players.
|
||||
- Inventory of items.
|
||||
- Chat message history.
|
||||
- Toast notifications.
|
||||
|
||||
`indexes()` should be used in other cases, especially when your source table
|
||||
has primitive value. It maps an index to a UI element.
|
||||
|
||||
e.g.
|
||||
- List of character or weapon stats.
|
||||
|
||||
In most cases, both functions will appear to have the same behavior.
|
||||
The main difference is performance, picking the right function to use can
|
||||
result in less property updates and less re-renders.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
25
docs/api/reactivity-utility.md
Normal file
25
docs/api/reactivity-utility.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Reactivity API: Utility
|
||||
|
||||
## cleanup()
|
||||
|
||||
Runs a callback anytime a reactive scope is re-ran.
|
||||
|
||||
- **Type**
|
||||
|
||||
```lua
|
||||
function cleanup(callback: () -> ())
|
||||
```
|
||||
|
||||
- **Example**
|
||||
|
||||
```lua
|
||||
local data = source(1)
|
||||
|
||||
watch(function()
|
||||
local label = create "TextLabel" { Text = data }
|
||||
|
||||
cleanup(function()
|
||||
label:Destroy()
|
||||
end)
|
||||
end)
|
||||
```
|
||||
25
docs/api/strict-mode.md
Normal file
25
docs/api/strict-mode.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Strict Mode
|
||||
|
||||
Vide has a special mode called "strict mode" which is used for debugging.
|
||||
|
||||
The purpose of strict mode is to help ensure stateful code is *pure*
|
||||
(deterministic and free from side effects) or if there are side-effects, that
|
||||
they are cleaned up correctly.
|
||||
|
||||
Vide is set to strict by doing:
|
||||
|
||||
```lua
|
||||
local vide = require(path_to_vide)
|
||||
vide.strict = true
|
||||
```
|
||||
|
||||
What strict mode will do:
|
||||
|
||||
1. Run derived callbacks twice when re-evaluating.
|
||||
2. Run watcher callbacks twice when a state changes.
|
||||
3. Throw an error if yields occur where they are not allowed.
|
||||
4. Checks for `map()` returning primitive values.
|
||||
5. Better error reporting and stack traces.
|
||||
|
||||
It is recommend to develop UI with strict mode and to disable it when pushing to
|
||||
production.
|
||||
21
docs/index.md
Normal file
21
docs/index.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
# https://vitepress.dev/reference/default-theme-home-page
|
||||
layout: home
|
||||
|
||||
hero:
|
||||
name: Vide
|
||||
text: ""
|
||||
tagline: A declarative and reactive library for Luau.
|
||||
actions:
|
||||
- theme: brand
|
||||
text: Tutorials
|
||||
link: /tut/crash-course/1-introduction
|
||||
- theme: alt
|
||||
text: API Reference
|
||||
link: /api/reactivity-core
|
||||
|
||||
features:
|
||||
- title: In Development
|
||||
details: Do not use for production
|
||||
---
|
||||
|
||||
13
docs/package.json
Normal file
13
docs/package.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"type": "module",
|
||||
|
||||
"scripts": {
|
||||
"docs:dev": "vitepress dev",
|
||||
"docs:build": "vitepress build",
|
||||
"docs:preview": "vitepress preview"
|
||||
},
|
||||
|
||||
"devDependencies": {
|
||||
"vitepress": "^1.0.0-beta.6"
|
||||
}
|
||||
}
|
||||
23
docs/tut/crash-course/1-introduction.md
Normal file
23
docs/tut/crash-course/1-introduction.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Introduction
|
||||
|
||||
This is a brief tutorial designed to give you a quick run through the usage of
|
||||
Vide.
|
||||
|
||||
Vide is largely inspired by other UI libraries such as Solid and Fusion.
|
||||
|
||||
## Why Vide?
|
||||
|
||||
Creating UI is a slow and tedious process. The purpose of Vide is to make UI
|
||||
declarative and concise, making it faster to create and more importantly easier
|
||||
to maintain. Vide achieves this using a reactive style of programming which
|
||||
allows you to focus on the flow of data through your application without
|
||||
worrying about manually updating UI instances.
|
||||
|
||||
Some of the main focuses behind Vide's design choices:
|
||||
|
||||
- Concise syntax to reduce verbosity as much as possible.
|
||||
- Reducing the amount of imports needed for usage by using Luau's syntax and
|
||||
semantics.
|
||||
- Being completely typecheckable.
|
||||
- Flexibility, particularly with integrating other libraries and allowing users
|
||||
to use their own patterns.
|
||||
60
docs/tut/crash-course/2-creation.md
Normal file
60
docs/tut/crash-course/2-creation.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Creating UI Elements
|
||||
|
||||
Instances are created using [`create()`](../../api/creation.md#create).
|
||||
|
||||
```lua
|
||||
local vide = require(path_to_vide)
|
||||
local create = vide.create
|
||||
```
|
||||
|
||||
`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.
|
||||
|
||||
```lua
|
||||
local frame = create "Frame" {
|
||||
Name = "Background",
|
||||
Position = UDim2.fromScale(0.5, 0.5)
|
||||
}
|
||||
```
|
||||
|
||||
String keys are assigned as properties and integer keys are assigned as child
|
||||
instances.
|
||||
|
||||
```lua
|
||||
create "ScreenGui" {
|
||||
Parent = game.StarterGui,
|
||||
|
||||
create "Frame" {
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Position = UDim2.fromScale(0.5, 0.5),
|
||||
Size = UDim2.fromScale(0.4, 0.7),
|
||||
|
||||
create "TextLabel" {
|
||||
Text = "hi"
|
||||
},
|
||||
|
||||
create"TextLabel" {
|
||||
Text = "bye"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To connect to an event, just set the event property name to a function.
|
||||
|
||||
All event arguments are passed into the function.
|
||||
|
||||
```lua
|
||||
create "TextButton" {
|
||||
Activated = function()
|
||||
print "clicked!"
|
||||
end
|
||||
}
|
||||
```
|
||||
|
||||
In short:
|
||||
|
||||
- String keys = properties
|
||||
- Function values = events
|
||||
- Non-function values = property values
|
||||
- Numeric keys = children
|
||||
47
docs/tut/crash-course/3-components.md
Normal file
47
docs/tut/crash-course/3-components.md
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Components
|
||||
|
||||
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.
|
||||
|
||||
Components leverage functions to create self-contained UI that can even have
|
||||
its own state and behavior.
|
||||
|
||||
```lua
|
||||
local function Button(props: {
|
||||
Position: UDim2,
|
||||
Text: string,
|
||||
Callback: () -> ()
|
||||
})
|
||||
return create "TextButton" {
|
||||
BackgroundColor3 = Color3.fromRGB(50, 50, 50),
|
||||
Size = UDim2.fromOffset(400, 250),
|
||||
|
||||
Position = props.Position,
|
||||
Text = props.Text,
|
||||
Callback = props.Callback
|
||||
}
|
||||
end
|
||||
|
||||
local button = Button {
|
||||
Position = UDim2.new(),
|
||||
Text = "Click me!",
|
||||
|
||||
Callback = function()
|
||||
print "clicked"
|
||||
end
|
||||
}
|
||||
```
|
||||
|
||||
Above is a simple example of a button component with its background color set to
|
||||
a dark grey and with a fixed size.
|
||||
|
||||
A single parameter `props` is used to pass properties to the component.
|
||||
|
||||
Components allow you to *encapsulate* behavior. You can only modify the
|
||||
component in ways that you allow in the component.
|
||||
|
||||
This also promotes code reusability. Anytime you want a new button all you do
|
||||
is call `Button {}` instead of creating and setting every property each time.
|
||||
|
||||
This can be extended to much more complicated UI.
|
||||
56
docs/tut/crash-course/4-state.md
Normal file
56
docs/tut/crash-course/4-state.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# State
|
||||
|
||||
State in Vide are the core of reactivity in Vide.
|
||||
|
||||
State contain values that can change, and when they do change, automatically
|
||||
update anything that is using it.
|
||||
|
||||
A state object in Vide can be created using
|
||||
[`source()`](../../api/reactivity-core.md#source).
|
||||
|
||||
```lua
|
||||
local source = vide.source
|
||||
```
|
||||
|
||||
```lua
|
||||
local count = source(0)
|
||||
```
|
||||
|
||||
The value of a state can be set by calling it with an argument, and can be read
|
||||
by calling it with no arguments.
|
||||
|
||||
```lua
|
||||
count(count() + 1) -- increment count state by 1
|
||||
```
|
||||
|
||||
Below is an example of a counter component that has state.
|
||||
|
||||
```lua
|
||||
local function Counter()
|
||||
local count = source(0)
|
||||
|
||||
return create "TextButton" {
|
||||
Text = count,
|
||||
|
||||
Activated = function()
|
||||
count(count() + 1)
|
||||
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
|
||||
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
|
||||
assigning its return value. You can only bind non-event
|
||||
properties, otherwise the function is connected as the event callback.
|
||||
|
||||
You as the programmer do not have to worry about manually updating variables or
|
||||
UI instances, you can just focus on defining how the data maps to UI and
|
||||
everything will update when changes occur.
|
||||
|
||||
Each call of `Counter {}` will create a new counter element, each with their own
|
||||
independent count state.
|
||||
58
docs/tut/crash-course/5-derived-state.md
Normal file
58
docs/tut/crash-course/5-derived-state.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Derived State
|
||||
|
||||
You can create new state from existing states. This is known as *deriving
|
||||
state*.
|
||||
|
||||
A function that wraps a state effectively becomes a state. If a state used
|
||||
inside a function is updated, the whole function can be re-ran to recompute
|
||||
its value.
|
||||
|
||||
```lua
|
||||
local count = source(0)
|
||||
|
||||
local function text()
|
||||
return "count: " .. count()
|
||||
end
|
||||
|
||||
create "TextLabel" {
|
||||
Text = text
|
||||
}
|
||||
```
|
||||
|
||||
Sometimes when using expensive computations to derive state, you only want to
|
||||
recalculate it once when a source state has changed
|
||||
|
||||
If you wrap a source state with a regular function, its value will be recomputed
|
||||
every time you call that function.
|
||||
[`derive()`](../../api/reactivity-core.md#derive) accepts a functions whose
|
||||
return value will be cached, so that subsequent calls of this derived state
|
||||
will return the same cached value until one of its source states have changed.
|
||||
|
||||
```lua
|
||||
local derive = vide.derive
|
||||
```
|
||||
|
||||
```lua
|
||||
local count = source(0)
|
||||
|
||||
local factorial = derive(function()
|
||||
local n = 1
|
||||
for i = 2, count() do
|
||||
n *= i
|
||||
end
|
||||
return n
|
||||
end)
|
||||
```
|
||||
|
||||
This can improve performance for expensive calculations.
|
||||
|
||||
```lua
|
||||
create "TextLabel" {
|
||||
Text = function()
|
||||
return "factorial squared: " .. factorial() * factorial()
|
||||
end
|
||||
}
|
||||
|
||||
count(3) -- displays "factorial squared: 36"
|
||||
count(4) -- displays "factorial squared: 576"
|
||||
```
|
||||
35
docs/tut/crash-course/6-table-state.md
Normal file
35
docs/tut/crash-course/6-table-state.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# 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.
|
||||
98
docs/tut/crash-course/7-property-groups.md
Normal file
98
docs/tut/crash-course/7-property-groups.md
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# Property Groups
|
||||
|
||||
Often when creating components from existing components, you can find yourself
|
||||
repetitively passing through properties such as size or position.
|
||||
|
||||
```lua
|
||||
function Background(props: {
|
||||
Color: Color3,
|
||||
AnchorPoint: UDim2,
|
||||
Position: UDim2,
|
||||
Size: UDim2
|
||||
})
|
||||
return create "Frame" {
|
||||
Color = props.Color
|
||||
AnchorPoint = props.AnchorPoint,
|
||||
Position = props.Position,
|
||||
Size = props.Size
|
||||
}
|
||||
end
|
||||
|
||||
function Menu(props: {
|
||||
Color = props.Color
|
||||
AnchorPoint: UDim2,
|
||||
Position: UDim2,
|
||||
Size: UDim2
|
||||
})
|
||||
return Background {
|
||||
Color = props.COlor,
|
||||
AnchorPoint = props.AnchorPoint,
|
||||
Position = props.Position,
|
||||
Size = props.Size
|
||||
}
|
||||
end
|
||||
```
|
||||
|
||||
One way this can be avoided is by using *property nesting*. In Vide, passign a
|
||||
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
|
||||
like the outer table is. Any properties in the nested table will be assigned
|
||||
to the instance just the same.
|
||||
|
||||
Below is an example of how you can use this to pass groups of similar properties
|
||||
together such as position and size, while also using typechecking.
|
||||
|
||||
```lua
|
||||
type Layout = {
|
||||
Layout = {
|
||||
Position: UDim2?,
|
||||
Size: UDim2?,
|
||||
AnchorPoint: Vector2?
|
||||
}
|
||||
}
|
||||
|
||||
function Background(props: Layout & { Color: Color3 })
|
||||
return create "Frame" {
|
||||
Color = props.Color,
|
||||
props.Layout
|
||||
}
|
||||
end
|
||||
|
||||
function Menu(props: Layout & { Color: Color3 })
|
||||
return Background {
|
||||
Color = props.Color,
|
||||
Layout = props.Layout
|
||||
}
|
||||
end
|
||||
```
|
||||
|
||||
Here we created a nested group with the key `Layout` that can accept
|
||||
layout-related properties. Any name could be chosen for the key.
|
||||
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.
|
||||
|
||||
```lua
|
||||
type Children = {
|
||||
Children = Array<Instance>
|
||||
}
|
||||
|
||||
local function List(props: Children & Layout)
|
||||
return create "Frame" {
|
||||
props.Layout,
|
||||
props.Children,
|
||||
create "UIListLayout" {}
|
||||
}
|
||||
end
|
||||
|
||||
List {
|
||||
Layout = {
|
||||
Position = UDim2.new()
|
||||
},
|
||||
|
||||
Children = {
|
||||
create "TextLabel" { Text = "1" },
|
||||
create "TextLabel" { Text = "2" }
|
||||
}
|
||||
}
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue