Created
February 6, 2024 18:53
-
-
Save metatablecat/e44a2099aef5ad4133ae76e901bb6088 to your computer and use it in GitHub Desktop.
A tiny tree object creator dervived from Create.lua syntax, RBXCreate is provided as an example.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local Create = {} | |
local function fastMixedTableSplit(t) | |
local l = #t | |
if l == 0 then return {} end | |
local out = table.move(t, 1, l, 1, {}) | |
table.move({}, 1, l, 1, t) | |
return out | |
end | |
local function componentProcessor(autoMapChildren, processorFunc) | |
return function(props) | |
local children = fastMixedTableSplit(props) | |
local com = processorFunc(props, children) | |
if autoMapChildren then | |
for _, v in children do | |
v.Parent = com | |
end | |
end | |
return com | |
end | |
end | |
function Create.component<A, R>( | |
autoMapChildren: boolean?, | |
processor: (props: A, children: {any}) -> R | |
): (A) -> R | |
-- splits numeric indexes from string indexes, then passes both tables into the processor | |
-- returns a callback to spawn this behaviour (for generic typing) | |
return componentProcessor(autoMapChildren, processor) | |
end | |
setmetatable(Create, { | |
__tostring = function() return "Module<Create>" end | |
}) | |
table.freeze(Create) | |
return Create |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Roblox component creator for Create | |
local RBXCreate = {} | |
local Create = require(script.Parent.Create) | |
local SYMBOL_HEADER = newproxy(false) | |
local function extract(t,k) | |
local v = t[k] | |
t[k] = nil | |
return v | |
end | |
type Symbol = {id: string, name: string} | |
local function Symbol(id): (string) -> Symbol | |
return function(name) | |
return { | |
[SYMBOL_HEADER] = true, | |
id = id, | |
name = name | |
} | |
end | |
end | |
local function mapAttributes(obj, attributes) | |
for k, v in attributes do | |
obj:SetAttribute(k, v) | |
end | |
end | |
type EventParams<A> = { | |
Sender: A, | |
Connection: RBXScriptConnection | |
} | |
local function createEventHandler(sender: Instance, event: RBXScriptSignal, callback, stack) | |
local eventParams = { | |
Sender = sender, | |
Connection = nil, | |
} | |
eventParams.Connection = event:Connect(function(...) | |
if stack then | |
callback(eventParams, stack, ...) | |
else | |
callback(eventParams, ...) | |
end | |
end) | |
return eventParams.Connection | |
end | |
RBXCreate.AttributeChange = Symbol("Attribute") | |
RBXCreate.Change = Symbol("Change") | |
local function ChangeHandler(eParams, stack) | |
local i = eParams.Sender | |
local name = stack.Name | |
local signal = stack.Signal | |
local symId = stack.ID | |
local value = if symId == "Attribute" then i:GetAttribute(name) else i[name] | |
signal(i, name, value) | |
end | |
-- exports for **better**, singleton based, change signals | |
-- here to stop the typing engine dying | |
function RBXCreate:ConnectAttributeChange<A>( | |
obj: A, | |
attribName: string, | |
callback: (senderParams: EventParams<A>, attribName: string, attribVal: any) -> () | |
): RBXScriptConnection | |
return createEventHandler(obj, obj:GetAttributeChangedSignal(attribName), ChangeHandler, { | |
Name = attribName, | |
Signal = callback, | |
ID = "Attribute" | |
}) | |
end | |
function RBXCreate:ConnectPropertyChange<A>( | |
obj: A, | |
propName: string, | |
callback: (senderParams: EventParams<A>, propName: string, propVal: any) -> () | |
): RBXScriptConnection | |
return createEventHandler(obj, obj:GetPropertyChangedSignal(propName), ChangeHandler, { | |
Name = propName, | |
Signal = callback, | |
ID = "Change" | |
}) | |
end | |
local function handleSymbolicArg(symbol: Symbol, signal: any, obj: Instance) | |
local symId = symbol.id | |
local name = symbol.name | |
local f = if symId == "Attribute" then RBXCreate.AttributeChangeSignal else RBXCreate.PropertyChangeSignal | |
f(RBXCreate, obj, name, signal) | |
end | |
local rbxCreateInstanceObj = Create.component(true, function(props) | |
local className = extract(props, "ClassName") | |
local parent = extract(props, "Parent") | |
local attribs = extract(props, "Attributes") | |
local tags = extract(props, "Tags") | |
local obj = Instance.new(className) | |
for k, v in props do | |
if type(k) == "table" and k[SYMBOL_HEADER] then | |
handleSymbolicArg(k, v, obj) | |
continue | |
end | |
local propVal = obj[k] | |
if typeof(propVal) == "RBXScriptSignal" then | |
createEventHandler(obj, propVal, v) | |
continue | |
end | |
obj[k] = v | |
end | |
if attribs then mapAttributes(obj, attribs) end | |
if tags then for _, v in tags do obj:AddTag(v) end end | |
if parent then obj.Parent = parent end | |
return obj | |
end) | |
setmetatable(RBXCreate, { | |
__index = function(self, k) | |
return function(props) | |
props.ClassName = k -- storing the class name here prevents pollution of components | |
-- and allows a single component instead | |
return rbxCreateInstanceObj(props) | |
end | |
end, | |
}) | |
return RBXCreate |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment