From 04e28f390d77302d9ead5c5a3d5a425ad7dd94f4 Mon Sep 17 00:00:00 2001 From: Aaron Smith <83140718+centau@users.noreply.github.com> Date: Wed, 9 Aug 2023 15:33:14 +0100 Subject: [PATCH] Improve docs --- docs/.vitepress/config.ts | 6 +- docs/api/animation.md | 3 +- docs/api/creation.md | 4 +- docs/api/reactivity-core.md | 67 +++++++++++-------- docs/api/reactivity-utility.md | 16 ++++- docs/tut/crash-course/2-creation.md | 16 ++--- docs/tut/crash-course/3-components.md | 12 ++-- .../crash-course/{4-state.md => 4-source.md} | 47 ++++++++----- ...5-derived-state.md => 5-derived-source.md} | 19 +++--- docs/tut/crash-course/6-table-source.md | 66 ++++++++++++++++++ docs/tut/crash-course/6-table-state.md | 35 ---------- docs/tut/crash-course/7-property-groups.md | 7 +- src/spring.luau | 4 ++ 13 files changed, 186 insertions(+), 116 deletions(-) rename docs/tut/crash-course/{4-state.md => 4-source.md} (52%) rename docs/tut/crash-course/{5-derived-state.md => 5-derived-source.md} (61%) create mode 100644 docs/tut/crash-course/6-table-source.md delete mode 100644 docs/tut/crash-course/6-table-state.md diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 1c88a9d..6d74ed6 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -39,9 +39,9 @@ export default defineConfig({ { 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: "Source", link: "/tut/crash-course/4-source" }, + { text: "Derived Source", link: "/tut/crash-course/5-derived-source" }, + { text: "Table Source", link: "/tut/crash-course/6-table-source" }, { text: "Property Groups", link: "/tut/crash-course/7-property-groups" }, ] }, diff --git a/docs/api/animation.md b/docs/api/animation.md index 5283743..f73c90e 100644 --- a/docs/api/animation.md +++ b/docs/api/animation.md @@ -18,8 +18,7 @@ Returns a new state with a dynamically animated value of the source. - **Details** - The output state value is updated every frame based on the source state - value. + The output state value is updated every frame based on the source value. The change is physically simulated according to a [spring](https://en.wikipedia.org/wiki/Simple_harmonic_motion). diff --git a/docs/api/creation.md b/docs/api/creation.md index b373726..122c1b6 100644 --- a/docs/api/creation.md +++ b/docs/api/creation.md @@ -10,7 +10,7 @@ Creates a new UI element, applying any given properties. ```lua function create(class: string): (Properties) -> Instance - function create(instance: Instace): (Properties) -> Instance + function create(instance: Instance): (Properties) -> Instance type Properties = Map ``` @@ -71,7 +71,7 @@ Creates a new UI element, applying any given properties. Color: Color3 }) return create "Frame" { - BackgroundColor3 = Color, + BackgroundColor3 = props.Color, props.Layout, props.Children } diff --git a/docs/api/reactivity-core.md b/docs/api/reactivity-core.md index 689864d..c8c80ad 100644 --- a/docs/api/reactivity-core.md +++ b/docs/api/reactivity-core.md @@ -4,7 +4,7 @@ ## source() -Creates a new source state with the given value. +Creates a new source with the given value. - **Type** @@ -14,11 +14,11 @@ Creates a new source state with the given value. - **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. - 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. + Reading from the source from within any reactive scope will cause changes + to that source to be tracked and anything depending on it to update. - **Example** @@ -32,48 +32,49 @@ Creates a new source state with the given value. ## watch() -Runs a callback on state change. +Runs a callback on source update. - **Type** ```lua - function watch(callback: () -> ()): Unwatch + function watch(source: () -> ()): Unwatch type Unwatch = () -> () ``` - **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 - reran. + Any time a source 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. + `source()` cannot yield. ::: - **Example** ```lua - local state = wrap(1) + local state = source(1) watch(function() - print(state.Value) + print(state()) end) -- prints 1 - state.Value += 1 + state(state() + 1) -- prints 2 ``` ## derive() -Derives a new state from existing states. +Derives a new source from existing sources. - **Type** @@ -83,13 +84,13 @@ Derives a new state from existing states. - **Details** - The derived state will have its value recalculated when any source state it - derives from is updated. + The derived source will have its value recalculated when any source source + 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 + Takes a callback that is immediately run to determine what sources are being referenced. ::: warning @@ -99,7 +100,7 @@ Derives a new state from existing states. - **Example** ```lua - local count = wrap(0) + local count = source(0) local text = derive(function() return `count: {count()}` end) text() -- "count: 0" @@ -111,7 +112,7 @@ Derives a new state from existing states. ## indexes() -Maps each index in a table to an object. +Maps each index in a table source to an object. - **Type** @@ -124,17 +125,18 @@ Maps each index in a table to an object. - **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. + source table. The first argument is a source 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 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 + instead the source value 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. + Returns a state containing an array of all objects returned by the + transform. ::: warning `transform()` cannot yield. @@ -170,7 +172,7 @@ Maps each index in a table to an object. ## values() -Maps each value in a table to an object. +Maps each value in a table source to an object. - **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 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 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 + rerun, instead the source index 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. + Returns a state containing an array of all objects returned by the + transform. ::: warning `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** The intended purpose of this function is to map each value in a table to diff --git a/docs/api/reactivity-utility.md b/docs/api/reactivity-utility.md index df73c13..604e507 100644 --- a/docs/api/reactivity-utility.md +++ b/docs/api/reactivity-utility.md @@ -16,10 +16,24 @@ Runs a callback anytime a reactive scope is re-ran. local data = source(1) watch(function() - local label = create "TextLabel" { Text = data } + local label = create "TextLabel" { Text = data() } cleanup(function() label:Destroy() end) end) ``` + + ```lua + local data = source(1) + + derive(function() + local label = create "TextLabel" { Text = data() } + + cleanup(function() + label:Destroy() + end) + + return label + end) + ``` diff --git a/docs/tut/crash-course/2-creation.md b/docs/tut/crash-course/2-creation.md index 18f98cb..8521641 100644 --- a/docs/tut/crash-course/2-creation.md +++ b/docs/tut/crash-course/2-creation.md @@ -7,9 +7,12 @@ local vide = require(path_to_vide) 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. +Luau allows us to omit parentheses `()` when calling functions with string or +table literals for brevity. + ```lua local frame = create "Frame" { 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. ```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. @@ -51,10 +54,3 @@ create "TextButton" { end } ``` - -In short: - -- String keys = properties - - Function values = events - - Non-function values = property values -- Numeric keys = children diff --git a/docs/tut/crash-course/3-components.md b/docs/tut/crash-course/3-components.md index 3f9b35a..5556f1d 100644 --- a/docs/tut/crash-course/3-components.md +++ b/docs/tut/crash-course/3-components.md @@ -2,16 +2,14 @@ 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. +By using components you can make your application more modular and better +organized. ```lua local function Button(props: { Position: UDim2, Text: string, - Callback: () -> () + Activated: () -> () }) return create "TextButton" { BackgroundColor3 = Color3.fromRGB(50, 50, 50), @@ -19,7 +17,7 @@ local function Button(props: { Position = props.Position, Text = props.Text, - Callback = props.Callback + Activated = props.Activated } end @@ -27,7 +25,7 @@ local button = Button { Position = UDim2.new(), Text = "Click me!", - Callback = function() + Activated = function() print "clicked" end } diff --git a/docs/tut/crash-course/4-state.md b/docs/tut/crash-course/4-source.md similarity index 52% rename from docs/tut/crash-course/4-state.md rename to docs/tut/crash-course/4-source.md index b8da21e..89e6f24 100644 --- a/docs/tut/crash-course/4-state.md +++ b/docs/tut/crash-course/4-source.md @@ -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 -update anything that is using it. - -A state object in Vide can be created using +A source 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 +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. ```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 local function Counter() @@ -39,12 +38,9 @@ local function Counter() 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 +whenever a source 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. @@ -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. 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 +} +``` diff --git a/docs/tut/crash-course/5-derived-state.md b/docs/tut/crash-course/5-derived-source.md similarity index 61% rename from docs/tut/crash-course/5-derived-state.md rename to docs/tut/crash-course/5-derived-source.md index 2e765c9..90ab2b3 100644 --- a/docs/tut/crash-course/5-derived-state.md +++ b/docs/tut/crash-course/5-derived-source.md @@ -1,10 +1,10 @@ -# Derived State +# Derived Source -You can create new state from existing states. This is known as *deriving -state*. +You can create new sources from existing sources. This is known as *deriving +sources*. -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 +A function that wraps a source effectively becomes a new source. If a source +used inside a function is updated, the whole function can be re-ran to recompute its value. ```lua @@ -22,11 +22,11 @@ create "TextLabel" { 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 +If you wrap a source 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. +return value will be cached, so that subsequent calls of this derived source +will return the same cached value until one of its input sources have changed. ```lua local derive = vide.derive @@ -44,7 +44,8 @@ local factorial = derive(function() 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 create "TextLabel" { diff --git a/docs/tut/crash-course/6-table-source.md b/docs/tut/crash-course/6-table-source.md new file mode 100644 index 0000000..9f14644 --- /dev/null +++ b/docs/tut/crash-course/6-table-source.md @@ -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) + +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. diff --git a/docs/tut/crash-course/6-table-state.md b/docs/tut/crash-course/6-table-state.md deleted file mode 100644 index 0ad5319..0000000 --- a/docs/tut/crash-course/6-table-state.md +++ /dev/null @@ -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) - -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. diff --git a/docs/tut/crash-course/7-property-groups.md b/docs/tut/crash-course/7-property-groups.md index cb498f6..1afea78 100644 --- a/docs/tut/crash-course/7-property-groups.md +++ b/docs/tut/crash-course/7-property-groups.md @@ -3,6 +3,8 @@ Often when creating components from existing components, you can find yourself repetitively passing through properties such as size or position. +Example below: + ```lua function Background(props: { Color: Color3, @@ -33,7 +35,7 @@ function Menu(props: { 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 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 @@ -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. 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 type Children = { diff --git a/src/spring.luau b/src/spring.luau index f018e5b..1049d5c 100644 --- a/src/spring.luau +++ b/src/spring.luau @@ -97,6 +97,10 @@ local springs: { [SpringData]: Node } = {} setmetatable(springs, { __mode = "vs" }) local function spring(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 output, output_get = create(initial_position)