local createSignal = require(script.Parent.createSignal)
local Symbol = require(script.Parent.Symbol)
local Type = require(script.Parent.Type)
local config = require(script.Parent.GlobalConfig).get()
local BindingImpl = Symbol.named("BindingImpl")
local BindingInternalApi = {}
local bindingPrototype = {}
function bindingPrototype:getValue()
return BindingInternalApi.getValue(self)
end
function bindingPrototype:map(predicate)
return BindingInternalApi.map(self, predicate)
end
local BindingPublicMeta = {
__index = bindingPrototype,
__tostring = function(self)
return string.format("RoactBinding(%s)", tostring(self:getValue()))
end,
}
function BindingInternalApi.update(binding, newValue)
return binding[BindingImpl].update(newValue)
end
function BindingInternalApi.subscribe(binding, callback)
return binding[BindingImpl].subscribe(callback)
end
function BindingInternalApi.getValue(binding)
return binding[BindingImpl].getValue()
end
function BindingInternalApi.create(initialValue)
local impl = {
value = initialValue,
changeSignal = createSignal(),
}
function impl.subscribe(callback)
return impl.changeSignal:subscribe(callback)
end
function impl.update(newValue)
impl.value = newValue
impl.changeSignal:fire(newValue)
end
function impl.getValue()
return impl.value
end
return setmetatable({
[Type] = Type.Binding,
[BindingImpl] = impl,
}, BindingPublicMeta), impl.update
end
function BindingInternalApi.map(upstreamBinding, predicate)
if config.typeChecks then
assert(Type.of(upstreamBinding) == Type.Binding, "Expected arg #1 to be a binding")
assert(typeof(predicate) == "function", "Expected arg #1 to be a function")
end
local impl = {}
function impl.subscribe(callback)
return BindingInternalApi.subscribe(upstreamBinding, function(newValue)
callback(predicate(newValue))
end)
end
function impl.update(_newValue)
error("Bindings created by Binding:map(fn) cannot be updated directly", 2)
end
function impl.getValue()
return predicate(upstreamBinding:getValue())
end
return setmetatable({
[Type] = Type.Binding,
[BindingImpl] = impl,
}, BindingPublicMeta)
end
function BindingInternalApi.join(upstreamBindings)
if config.typeChecks then
assert(typeof(upstreamBindings) == "table", "Expected arg #1 to be of type table")
for key, value in pairs(upstreamBindings) do
if Type.of(value) ~= Type.Binding then
local message = ("Expected arg #1 to contain only bindings, but key %q had a non-binding value"):format(
tostring(key)
)
error(message, 2)
end
end
end
local impl = {}
local function getValue()
local value = {}
for key, upstream in pairs(upstreamBindings) do
value[key] = upstream:getValue()
end
return value
end
function impl.subscribe(callback)
local disconnects = {}
for key, upstream in pairs(upstreamBindings) do
disconnects[key] = BindingInternalApi.subscribe(upstream, function(_newValue)
callback(getValue())
end)
end
return function()
if disconnects == nil then
return
end
for _, disconnect in pairs(disconnects) do
disconnect()
end
disconnects = nil :: any
end
end
function impl.update(_newValue)
error("Bindings created by joinBindings(...) cannot be updated directly", 2)
end
function impl.getValue()
return getValue()
end
return setmetatable({
[Type] = Type.Binding,
[BindingImpl] = impl,
}, BindingPublicMeta)
end
return BindingInternalApi