Dex
WARNING
Dex is still a work in progress and does not currently have a full release! Please avoiding Dex in production-bound projects, as the library is not fully tested, and the API may be subject to change
The Dex library contains a collection of objects for creating reactive UI components
For more information, see the Usage Guide
Properties
Clock
Observable number that derives its value from the current time in seconds
in UTC (i.e. the value returned by os.time()
)
While subscribed, Dex.Clock
automatically updates its value whenever the
value of os.time()
changes.
Nil
Dex.Nil:
Symbol
<
"Nil"
>
Symbol that represents a "Nil" value. Because "nil" values can't be
represented as values of a Luau table, Dex.Nil
can be used in its place.
Example usage:
return Dex.Premade("BillboardGui", {
-- Sets the Adornee property of the premade UI to "Nil" on mount
Adornee = Dex.Nil,
})
Functions
IsVirtualInstance
Dex.
IsVirtualInstance
(
passedIn:
unknown
) →
boolean
This function checks if the passed in value is a Dex VirtualInstance
IsState
Dex.
IsState
(
passedIn:
unknown
) →
boolean
This function checks if the passed in value is a State observable object
GetVirtualInstanceType
This function returns the constructor type for VirtualInstance ("New", "Clone", or "Premade"). Errors if the passed in value is not a VirtualInstance.
IsObservable
Dex.
IsObservable
(
passedIn:
unknown
) →
boolean
This function checks if the passed in value is an Observable.
IsStateRecord
Dex.
IsStateRecord
(
passedIn:
unknown
) →
boolean
This function checks if the passed in value is a [StateRecord] observable object
Root
Creates a new Root instance. The Root is a reference to a real Roblox Instance and is used to reconcile Virtual Instances.
FlattenObservable
If an Observable's current value can be another Observable object, flattens the final resolved value of the last returned Observable in the chain, and automatically subscribes to any observables returned in the chain.
Example:
local coins = Dex.State(Dex.State(3))
local coinsFlattened = Dex.FlattenObservable(coins)
print(coinsFlattened:Current()) -- 3
coins:Set(Dex.State(5))
print(coinsFlattened:Current()) -- 5
coins:Current():Set(7)
print(coinsFlattened:Current()) -- 7
Map
Returns a curryable mapping function, which in turn returns a derived observable from the dependent observables passed in.
Example:
local x = Dex.State(2)
local y = Dex.State(3)
local sum = Dex.Map(x, y)(function(currentX, currentY)
return currentX + currentY
end)
print(sum:Current()) -- 5
For Observables where the values are a vector or scalar type, math
operations can be used as an alias for Dex.Map
!
Example:
local x = Dex.State(2)
local y = Dex.State(3)
local sum = x + y
print(sum:Current()) -- 5
CoerceAsObservable
This function coerces the passed in value to an Observable. Useful for unwrapped the CanBeObservable type in a component's props.
Spring
Creates an easing observable that simulates the behavior of a critically damped spring. The spring simulates in realtime until the target value is reached.
CAUTION
Spring must have at least one Subscriber (or be mounted on at least one VirtualInstance) to simulate in realtime!
Tween
Creates a new Tween Observable object with the given initial value.
Dict
Creates a new Dict state observable with the given initial value.
Stopwatch
Creates a new Stopwatch Observable, which simulates in realtime while subscribed.
Props:
duration
is an optional prop that specifies the end time of the stopwatch.
Defaults to math.huge
.
isPlaying
specifies that stopwatch should play and stop depending on an
observable boolean value. If set to true
, the stopwatch will immediately
start playing.
playOnChange
specifies that the stopwatch should restart whenever an input
observable changes.
Eased
Creates an eased Observable that tweens its value in realtime every time the input Observable changes its value, based on the TweenInfo provided.
CAUTION
Eased must have at least one Subscriber (or be mounted on at least one VirtualInstance) to simulate in realtime!
State
Creates a new State Observable object with the given initial value.
IntSpring
Creates an easing observable that simulates the behavior of a critically damped spring, constrained to the Integer range. Rounds the current position towards the target value, which is useful for UI components like currency or ammo counters.
CAUTION
Springs must have at least one Subscriber (or be mounted on at least one VirtualInstance) to simulate in realtime!
CustomObservable
Dex.
CustomObservable
(
getCurrent:
(
)
→
T
,
createUpdateStream:
(
notifyChange:
(
)
→
(
)
)
→
(
(
)
→
(
)
)
) →
Observable
<
T
>
WARNING
Consider opting for other Dex constructs (like State) over custom observables.
When writing custom Dex observables, Make sure to implement the getCurrent
and createUpdateStream
parameters correctly, as failing to do so may
cause memory leaks or unresponsive UI.
Creates a new Dex Observable object. Observables are used to hold, derive, or map state within a Dex application.
The first parameter should be a function that always returns the current state of the observable whenever called.
For example, to observe the value of workspace.CurrentCamera.ViewportSize
:
local function getCurrent()
return workspace.CurrentCamera.ViewportSize
end
The second parameter is a callback which sets up any event handling required to notify whenever the current state changes, and returns a "cleanup" function to close the event handling.
For example, to observe the value of workspace.CurrentCamera.ViewportSize
:
local function createUpdateStream(notifyChange: () -> ())
-- Start tracking changes to ViewportSize, and
-- forward these to the `notifyChange` callback
local connection = workspace.CurrentCamera
:GetPropertyChangedSignal("ViewportSize")
:Connect(notifyChange)
-- Return a function which closes the update stream,
-- cleaning up our connection.
return function()
connection:Disconnect()
end
end
createUpdateStream
is automatically called by Dex the first time an
observable is subscribed (or used by a mounted VirtualInstance), and its
return function to close the update stream is automatically called when the
Observable's last subscriber is unsubscribed and/or the last VirtualInstance
utilizing it is unmounted.
Putting it all together, we can create a custom observable which tracks the ViewportSize of the player's camera:
local function getCurrent()
return workspace.CurrentCamera.ViewportSize
end
local function createUpdateStream(notifyChange: () -> ())
local connection = workspace.CurrentCamera
:GetPropertyChangedSignal("ViewportSize")
:Connect(notifyChange)
-- closeUpdateStream:
return function()
connection:Disconnect()
end
end
local playerViewportSize = Dex.CustomObservable(
getCurrent,
createUpdateStream
)
print(playerViewportSize:Current()) -- Output: 1920, 1080
Custom observables may be useful for connecting third party libraries or other systems in your game's codebase to a Dex UI application.
Timer
Creates a new Timer Observable, which simulates in realtime while subscribed.
Props:
duration
is a required prop that specifies the initial time of the timer.
isPlaying
specifies that timer should play and stop depending on an
observable boolean value. If set to true
, the timer will immediately start
playing.
playOnChange
specifies that the timer should restart whenever an input
observable changes.
AngleSpring
Creates an easing observable that simulates the behavior of a critically damped spring, wrapped around the range [-pi, pi]. The spring simulates in realtime until the target value is reached.
CAUTION
AngleSpring must have at least one Subscriber (or be mounted on at least one VirtualInstance) to simulate in realtime!
New
Creates a new VirtualInstance that represents a newly-created Roblox
Instance (via Instance.new(className)
).
Clone
Creates a new VirtualInstance that represents a cloned Roblox Instance
from a given template instance (via template:Clone()
).
Premade
Creates a new VirtualInstance that represents a pre-existing Roblox Instance to be modified by Dex.
If passed into the the Render function for a Dex.Root component, the root instance will be used used.
ObserveFusionState
Interoperability function that maps a Fusion StateObject to a Dex Observable object. The returned observable is garbage collected once dereferenced and unsubscribed/unmounted.
The Fusion library must be provided, as Fusion StateObjects only work when embedded in a Fusion runtime.