Skip to content

Commit

Permalink
Add library prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
LastTalon committed Jun 10, 2024
1 parent 8187e6e commit 3f88edd
Show file tree
Hide file tree
Showing 17 changed files with 633 additions and 0 deletions.
34 changes: 34 additions & 0 deletions lib/RegisteredComponents.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
local Package = script.Parent
local Packages = Package.Parent

local React = require(Packages.React)
local Signal = require(Packages.Signal)

export type Component = React.ComponentType<unknown>
export type Components = { [string]: Component }

local components: Components = {}
local componentsChanged = Signal.new()

local function updateComponents(components: Components)
components = components
componentsChanged:Fire(components)
end

local function insertComponent(key: string, component: Component)
components[key] = component
updateComponents(components)
end

local function removeComponent(key: string)
components[key] = nil
updateComponents(components)
end

return {
Components = components,
ComponentsChanged = componentsChanged.Event,

insertComponent = insertComponent,
removeComponent = removeComponent,
}
64 changes: 64 additions & 0 deletions lib/components/RenderSystems.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
local Package = script.Parent.Parent
local Packages = Package.Parent

local React = require(Packages.React)
local RegisteredComponents = require(Package.RegisteredComponents)

local e = React.createElement
local Fragment = React.Fragment
local useEffect = React.useEffect
local useMemo = React.useMemo
local useState = React.useState

export type RenderProps<P> = {
child: React.ComponentType<P>,
props: P,
}

export type Props<P> = {
render: React.ComponentType<RenderProps<P>>?,
props: P,
}

type Components = RegisteredComponents.Components
type RenderedComponents = { [string]: React.ReactNode }

local function DefaultRender<P>(props: RenderProps<P>): React.ReactNode
return e(props.child, props.props)
end

local function RenderSystems<P>(props: Props<P>): React.ReactNode
local render = props.render or DefaultRender
local children: Components, updateChildren = useState({})

useEffect(function()
updateChildren(RegisteredComponents.Components)

local connection = RegisteredComponents.ComponentsChanged:Connect(
function(components: Components)
updateChildren(components)
end
)

return function()
connection.disconnect()
end
end, {})

local renderedChildren = useMemo(function(): RenderedComponents
local renderedChildren = {}

for key, component in children do
renderedChildren[key] = e(render, {
child = component,
props = props.props,
})
end

return renderedChildren
end, { children, render, props.props })

return e(Fragment, {}, renderedChildren)
end

return RenderSystems
87 changes: 87 additions & 0 deletions lib/components/SystemComponent.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
local Package = script.Parent.Parent
local Packages = Package.Parent
local Contexts = Package.contexts

local Params = require(Contexts.Params)
local React = require(Packages.React)
local Return = require(Contexts.Return)
local Signal = require(Packages.Signal)

local e = React.createElement
local useEffect = React.useEffect
local useState = React.useState

export type Complete<T> = Return.Complete<T>
export type PreviouslyComplete<T> = Return.PreviouslyComplete<T>
export type Incomplete = Return.Incomplete
export type Ready<T> = Return.Ready<T>

export type Passed<Params, Return> = {
params: Params,
ready: Ready<Return>,
}

local function SystemComponent<Props, Params, Return>(
component: React.ComponentType<Props>,
passed: Passed<Params, Return>,
signal: typeof(Signal.new())
): React.ComponentType<Props>
return function(props: Props): React.ReactNode
local passed, updatePassed = useState(passed)

local function updateReturn(value: Return | (value: Return?) -> Return)
if type(value) == "function" then
updatePassed(function(oldPassed: Passed<Params, Return>): Passed<Params, Return>
local newPassed = {
params = oldPassed.params,
ready = {
completed = true,
value = value(
if oldPassed.ready.completed then oldPassed.ready.value else nil
),
},
}

signal:Fire(newPassed)
return newPassed
end)
else
local newPassed = {
params = passed.params,
ready = {
completed = true,
value = value,
},
}

signal:Fire(newPassed)
end
end

useEffect(function()
local connection = signal:Connect(function(value: Passed<Params, Return>)
updatePassed(value)
end)

return function()
connection.disconnect()
end
end, {})

return e(Params.Provider, {
value = {
params = passed.params,
},
}, {
e(Return.Provider, {
value = {
updateReturn = updateReturn,
},
}, {
SystemComponent = e(component, props),
}),
})
end
end

return SystemComponent
16 changes: 16 additions & 0 deletions lib/contexts/Params.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
local Package = script.Parent.Parent
local Packages = Package.Parent

local React = require(Packages.React)

export type ParamType<T> = T & {}

export type Params<T> = {
params: ParamType<T>?,
}

local defaultParams: Params<unknown> = {}

local Params = React.createContext(defaultParams)

return Params
32 changes: 32 additions & 0 deletions lib/contexts/Return.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
local Package = script.Parent.Parent
local Packages = Package.Parent

local React = require(Packages.React)

export type Complete<T> = {
completed: true,
value: T,
}

export type PreviouslyComplete<T> = {
completed: false,
value: T,
}

export type Incomplete = {
completed: false,
}

export type Ready<T> = Complete<T> | PreviouslyComplete<T> | Incomplete

export type UpdateReturn<T> = (value: T | (value: T?) -> T) -> ()

export type Return<T> = {
updateReturn: UpdateReturn<T>?,
}

local defaultReturn: Return<unknown> = {}

local Return = React.createContext(defaultReturn)

return Return
30 changes: 30 additions & 0 deletions lib/diffTables.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
local function diffTables(left: { [unknown]: unknown }?, right: { [unknown]: unknown }?): boolean
if left and right then
if left == right then
return false
end

local size = 0

for key, value in left do
if value ~= right[key] then
return true
end
size += 1
end

for _ in right do
size -= 1
end

if size ~= 0 then
return true
end

return false
end

return true
end

return diffTables
75 changes: 75 additions & 0 deletions lib/diffTables.spec.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
local diffTables = require(script.Parent.diffTables)

return function()
describe("function", function()
it("should be true when both parameters are nil", function()
expect(diffTables()).to.equal(true)
end)

it("should be true when the first parameter is nil", function()
expect(diffTables(nil, {})).to.equal(true)
end)

it("should be true when the second parameter is nil", function()
expect(diffTables({})).to.equal(true)
end)

it("should be false with two different empty tables", function()
local left = {}
local right = {}
expect(diffTables(left, right)).to.equal(false)
end)

it("should be true with tables with different elements", function()
local left = {
1,
2,
3,
}
local right = {
"a",
"b",
"c",
}
expect(diffTables(left, right)).to.equal(true)
end)

it("should be true with tables with nearly the same elements", function()
local left = {
1,
2,
3,
}
local right = {
1,
2,
3,
4,
}
expect(diffTables(left, right)).to.equal(true)
end)

it("should be false with different identical tables", function()
local left = {
1,
2,
3,
}
local right = {
1,
2,
3,
}
expect(diffTables(left, right)).to.equal(false)
end)

it("should be false with the same table", function()
local left = {
1,
2,
3,
}
expect(diffTables(left, left)).to.equal(false)
end)
end)
end
Loading

0 comments on commit 3f88edd

Please sign in to comment.