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