Blend
Declarative UI system inspired by Fusion.
Functions
New
Creates a new function which will return an observable that, given the props in question, will construct a new instance and assign all props. This is the equivalent of a pipe-able Rx command.
local render = Blend.New "ScreenGui" {
Parent = game.Players.LocalPlayer.PlayerGui;
Blend.New "Frame" {
Size = UDim2.new(1, 0, 1, 0);
BackgroundTransparency = 0.5;
};
};
maid:GiveTask(render:Subscribe(function(gui)
print(gui)
end))
State
Creates a new Blend State which is actually just a ValueObject underneath.
Throttled
Throttles the update to the end of the defer lane. Can help optimize scenarios when Compute() can trigger multiple times per a frame.
Generally not needed.
Shared
Shares this observables state/computation with all down-stream observables. This can be useful when a very expensive computation was done and needs to be shared.
Generally not needed.
Computed
Takes a list of variables and uses them to compute an observable that will combine into any value. These variables can be any value, and if they can be converted into an Observable, they will be, which will be used to compute the value.
local verbState = Blend.State("hi")
local nameState = Blend.State("alice")
local computed = Blend.Computed(verbState, nameState, function(verb, name)
return verb .. " " .. name
end)
maid:GiveTask(computed:Subscribe(function(sentence)
print(sentence)
end)) --> "hi alice"
nameState.Value = "bob" --> "hi bob"
verbState.Value = "bye" --> "bye bob"
nameState.Value = "alice" --> "bye alice"
OnChange
Short hand to register a propertyEvent changing
Blend.mount(workspace, {
[Blend.OnChange "Name"] = function(name)
print(name)
end;
}) --> Immediately will print "Workspace"
workspace.Name = "Hello" --> Prints "Hello"
OnEvent
Short hand to register an event from the instance
Blend.mount(workspace, {
[Blend.OnEvent "ChildAdded"] = function(child)
print("Child added", child)
end;
})
local folder = Instance.new("Folder")
folder.Name = "Hi"
folder.Parent = workspace --> prints "Child added Hi"
Attached
Uses the constructor to attach a class or resource to the actual object for the lifetime of the subscription of that object.
return Blend.New "Frame" {
Parent = variables.Parent;
[Blend.Attached(function(parent)
local maid = Maid.new()
print("Got", parent)
maid:GiveTask(function()
print("Dead!")
end)
return maid
end)] = true;
}
ComputedPairs
Similiar to Fusion's ComputedPairs, where the changes are cached, and the lifetime limited.
AccelTween
Blend.
AccelTween
(
source:
any
,
--
Source observable (or convertable)
acceleration:
any
--
Source acceleration (or convertable)
) →
Observable
Like Blend.Spring, but for AccelTween
Spring
Converts this arbitrary value into an observable that will initialize a spring and interpolate it between values upon subscription.
local percentVisible = Blend.State(0)
local visibleSpring = Blend.Spring(percentVisible, 30)
local transparency = Blend.Computed(visibleSpring, function(percent)
return 1 - percent
end);
Blend.mount(frame, {
BackgroundTransparency = visibleSpring;
})
toPropertyObservable
Converts this arbitrary value into an observable suitable for use in properties.
toNumberObservable
Converts this arbitrary value into an observable that emits numbers.
toEventObservable
Converts this arbitrary value into an observable that can be used to emit events.
toEventHandler
Blend.
toEventHandler
(
value:
any
) →
function?
Converts this arbitrary value into an event handler, which can be subscribed to
Children
Mounts children to the parent and returns an object which will cleanup and delete all children when removed.
Note that this effectively recursively mounts children and their values, which is the heart of the reactive tree.
Blend.New "ScreenGui" {
Parent = game.Players.LocalPlayer.PlayerGui;
[Blend.Children] = {
Blend.New "Frame" {
Size = UDim2.new(1, 0, 1, 0);
BackgroundTransparency = 0.5;
};
};
};
Note since 6.14 you don't need to be explicit about Blend.Children. Any number-based index in the mounting process will be automatically inferred as children to mount.
Blend.New "ScreenGui" {
Parent = game.Players.LocalPlayer.PlayerGui;
Blend.New "Frame" {
Size = UDim2.new(1, 0, 1, 0);
BackgroundTransparency = 0.5;
};
};
Rules:
{ Instance }
- Tables of instances are all parented to the parent- Brio will last for the lifetime of the brio
- Brio<Observable> will last for the lifetime of the brio
- Brio<Signal> will also act as above
- Brio<Promise> will also act as above
- Brio<{ Instance } will also act as above
- Observable will parent to the parent
- Signal will act as Observable
- ValueObject will act as an Observable
- Promise will act as an Observable
- will parent all instances to the parent
- Observables may emit non-observables (in form of Computed/Dynamic)
- Observable<Brio> will last for the lifetime of the brio, and parent the instance.
- Observable<Observable> occurs when computed returns a value.
- ValueObject will switch to the current value
- function - Will be invoked as
func(parent)
and then the standard scheme will be applied
Cleanup:
- Instances will be cleaned up on unsubscribe
Tags
Allows you to add CollectionService tags to a Blend object.
Blend.New "ScreenGui" {
[Blend.Tags] = { "Hide", "ScreenGui" };
};
Find
Blend.
Find
(
className:
string
) →
function
Mounts Blend objects into an existing instance.
tip
Normally specifying ClassName as a property breaks mounting, since you can't write to ClassName. However, if you specify ClassName here, it will only listen to changes on children with that class name.
If multiple instances are named the same thing, then this will bind to both.
tip
This explicitly listens for any children underneath the mounted instance with the name passed in here. This is fine for small amounts of instances, like in most Gui hierarchies. However, it will be way less performance friendly for large class hierarchies.
maid:GiveTask(Blend.mount(frame, {
Size = UDim2.new(0.5, 0, 0.5, 0);
Blend.Find "UIScale" {
Scale = 2;
};
}))
tip
Instance
An event emitter that emits the instance that was actually created. This is useful for a variety of things.
Using this to track an instance
local currentCamera = Blend.State()
return Blend.New "ViewportFrame" {
CurrentCamera = currentCamera;
[Blend.Children] = {
self._current;
Blend.New "Camera" {
[Blend.Instance] = currentCamera;
};
};
};
Note that since 6.14 you should also be able to just use the reification scheme of Blend.Children implicitly in Blend.mount to get somewhat equivalent behavior.
Blend.mount(frame, {
-- Array indexed methods get treated as children-constructors, which get the parent
-- in them;
function(parent)
print("Got parent!", parent)
end;
})
You can also use this to execute code against an instance.
return Blend.New "Frame" {
[Blend.Instance] = function(frame)
print("We got a new frame!")
end;
};
Note that if you subscribe twice to the resulting observable, the internal function will execute twice.
Single
Ensures the computed version of a value is limited by lifetime instead of multiple. Used in conjunction with Blend.Children and Blend.Computed.
warning
In general, cosntructing new instances like this is a bad idea, so it's recommended against it.
local render = Blend.New "ScreenGui" {
Parent = game.Players.LocalPlayer.PlayerGui;
[Blend.Children] = {
Blend.Single(Blend.Computed(percentVisible, function()
-- you generally would not want to do this anyway because this reconstructs a new frame
-- every frame.
Blend.New "Frame" {
Size = UDim2.new(1, 0, 1, 0);
BackgroundTransparency = 0.5;
};
end)
};
};
maid:GiveTask(render:Subscribe(function(gui)
print(gui)
end))
_observeChildren
Observes children and ensures that the value is cleaned up afterwards.
mount
Mounts the instance to the props. This handles mounting children, and events.
The contract is that the props table is turned into observables. Note the following.
- Keys of strings are turned into properties
- If this can be turned into an observable, it will be used to subscribe to this event
- Otherwise, we assign directly
- Keys of functions are invoked on the instance in question
- `(instance, value) -> Observable
- If this returns an observable (or can be turned into one), we subscribe the event immediately
- Keys of numbers (array components) are treated as implicit children
- If the key is Blend.Children then we invoke mountChildren on it.
maid:GiveTask(Blend.mount(frame, {
BackgroundTransparency = 1;
-- All items named InventoryFrame
Blend.Find "Frame" {
Name = "InventoryFrame"
-- Apply the following properties
Blend.New "UIScale" {
Scale = 0.5;
};
};
}))