Computed Variables as seen in Weaver / Vue.js / Svelte / React
Untested, though in theory should work without issue. The syntax / api is as should be and will not change in future additions/improvements.
This commit is contained in:
commit
b584fe0ba6
1 changed files with 105 additions and 0 deletions
105
ModuleScripts/Computed.lua
Normal file
105
ModuleScripts/Computed.lua
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
local module = {}
|
||||||
|
|
||||||
|
local FF_COMPUTED_CONSTRUCTION_CALLBACK = false
|
||||||
|
local FF_COMPUTED_REFS = {}
|
||||||
|
|
||||||
|
local refValuesById = {}
|
||||||
|
|
||||||
|
function module.ref(value: any)
|
||||||
|
table.insert(refValuesById, { Value = value, OnSet = {} })
|
||||||
|
local id = #refValuesById
|
||||||
|
|
||||||
|
local ref = setmetatable({
|
||||||
|
Id = id,
|
||||||
|
get = function(self)
|
||||||
|
if FF_COMPUTED_CONSTRUCTION_CALLBACK then
|
||||||
|
table.insert(FF_COMPUTED_REFS, self.Id)
|
||||||
|
end;
|
||||||
|
|
||||||
|
return refValuesById[self.Id].Value;
|
||||||
|
end,
|
||||||
|
set = function(self, value: any)
|
||||||
|
self.Value = value
|
||||||
|
|
||||||
|
task.spawn(function()
|
||||||
|
local callbacks = refValuesById[self.Id].OnSet
|
||||||
|
for _, callback in callbacks do
|
||||||
|
callback(value)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
return ref
|
||||||
|
end
|
||||||
|
|
||||||
|
local computedsById = {}
|
||||||
|
|
||||||
|
function module.computed(computationCallback: () -> (any))
|
||||||
|
FF_COMPUTED_CONSTRUCTION_CALLBACK = true
|
||||||
|
|
||||||
|
table.insert(computedsById, { Value = computationCallback(), Callack = computationCallback, Refs = FF_COMPUTED_REFS, OnChange = {} })
|
||||||
|
|
||||||
|
local notifyChange = function(refId, compId)
|
||||||
|
return function()
|
||||||
|
local computedData: { Value: any, Callack: () -> any, OnChange: { } } = computedsById[refId]
|
||||||
|
local newComputedValue = computedData.Callack()
|
||||||
|
computedsById[compId].Value = newComputedValue
|
||||||
|
|
||||||
|
for _, callback in computedData.OnChange do
|
||||||
|
callback(newComputedValue)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local refs = FF_COMPUTED_REFS
|
||||||
|
if refs[1] then
|
||||||
|
for _, ref in refs do
|
||||||
|
table.insert(refValuesById[ref].OnSet, notifyChange(ref, #computedsById))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
FF_COMPUTED_CONSTRUCTION_CALLBACK = false
|
||||||
|
FF_COMPUTED_REFS = {}
|
||||||
|
|
||||||
|
return setmetatable({ Value = computationCallback(), }, { Id = #computedsById, __index = function(self, key)
|
||||||
|
if key:upper() == "VALUE" then
|
||||||
|
return computedsById[#computedsById].Value
|
||||||
|
else
|
||||||
|
return rawget(self, key)
|
||||||
|
end
|
||||||
|
end})
|
||||||
|
end
|
||||||
|
|
||||||
|
function module.listenEvent(computed: typeof(setmetatable({ Id = 1}, {})), callback: (newValue: any) -> ())
|
||||||
|
table.insert(computedsById[computed.Id].OnChange, callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local Computed = module.computed
|
||||||
|
local Ref = module.ref
|
||||||
|
local ListenTo = module.listenEvent
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local a = Ref(1)
|
||||||
|
local b = Ref(2)
|
||||||
|
|
||||||
|
local aAndB = Computed(function()
|
||||||
|
return a:get() * b:get()
|
||||||
|
end)
|
||||||
|
|
||||||
|
print(aAndB.Value) -- 2
|
||||||
|
|
||||||
|
ListenTo(aAndB, print)
|
||||||
|
|
||||||
|
a:set(2)
|
||||||
|
|
||||||
|
-- aAndB = 4
|
||||||
|
|
||||||
|
b:set(3)
|
||||||
|
|
||||||
|
-- aAndB = 6
|
||||||
|
|
||||||
|
return module
|
Loading…
Reference in a new issue