Skip to content

Commit 25c602c

Browse files
authored
Adding support for making UI templates into components (#3)
1 parent 5817db0 commit 25c602c

File tree

2 files changed

+120
-22
lines changed

2 files changed

+120
-22
lines changed

froact.lua

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ type Element = any
2626
-- type Children = Element | { Element } | nil
2727
type Children = any? -- For simpler definitions, since it's the equivalent as above
2828

29-
local function newE(roact, hooks, defaults: { DefaultPropertyConfig })
29+
local function newE(roact: any, hooks, defaults: { DefaultPropertyConfig })
3030
return function(class: string, props: { [string]: any }, children: Children)
3131
for _, default in defaults do
3232
if isA(class, default.class) and not props[default.property] then
@@ -37,12 +37,64 @@ local function newE(roact, hooks, defaults: { DefaultPropertyConfig })
3737
end
3838
end
3939

40+
local function newTemplate(roact: any, unpureByDefault: boolean?)
41+
type CleanupMethod = () -> ()
42+
type UpdateMethod<Props> = (props: Props) -> ()
43+
type Constructor<Props> = (
44+
name: string,
45+
parent: Instance,
46+
onUpdate: (UpdateMethod<Props>) -> ()
47+
) -> CleanupMethod
48+
return function<Props>(config: ComponentConfig, f: Constructor<Props>)
49+
local isPure = if config.pure == nil
50+
then not unpureByDefault
51+
else config.pure
52+
local Component = if isPure
53+
then roact.PureComponent:extend(config.name or "Component")
54+
else roact.Component:extend(config.name or "Component")
55+
function Component:init()
56+
local _className, hostKey, hostParent, _children
57+
for name, field in self do
58+
if tostring(name) == "Symbol(InternalData)" then
59+
_className = field.componentClass
60+
hostKey = field.virtualNode.hostKey
61+
hostParent = field.virtualNode.hostParent
62+
_children = field.virtualNode.children
63+
end
64+
end
65+
self.updateCallbacks = {}
66+
self.cleanup = f(hostKey, hostParent, function(f)
67+
table.insert(self.updateCallbacks, f)
68+
end)
69+
end
70+
function Component:render()
71+
return roact.createFragment()
72+
end
73+
function Component:didMount()
74+
for _, callback in self.updateCallbacks do
75+
callback(self.props)
76+
end
77+
end
78+
function Component:didUpdate()
79+
for _, callback in self.updateCallbacks do
80+
callback(self.props)
81+
end
82+
end
83+
function Component:willUnmount()
84+
self.cleanup()
85+
end
86+
return function(props: Props, children)
87+
return roact.createElement(Component, props, children)
88+
end
89+
end
90+
end
91+
4092
-- stylua: ignore
4193
type ListConfig = (
4294
{ orderByName: boolean?, setOrder: true, initial: number?, key: string? }
4395
| { orderByName: boolean?, setOrder: false?, initial: nil, key: string? }
4496
)
45-
local function newList(roact)
97+
local function newList(roact: any)
4698
return function(config: ListConfig, elements: { [number]: Element })
4799
local index = if config.initial then config.initial else 0
48100
local count: { [string]: number } = {}
@@ -100,23 +152,19 @@ type HookFunction<Props, Hooks> = (
100152
) -> any
101153

102154
local function newC<Hooks>(
103-
roact,
155+
roact: any,
104156
hooks: HookFunction<any, Hooks>,
105157
unpureByDefault: boolean?
106158
)
107159
return function<Props>(
108160
config: ComponentConfig,
109161
body: (Props, Hooks) -> any
110162
): (Props, Children) -> any
111-
if not unpureByDefault then
112-
if config.pure == nil then
113-
config.pure = true
114-
end
115-
end
163+
local isPure = if config.pure == nil
164+
then not unpureByDefault
165+
else config.pure
116166
local Component = hooks(body, {
117-
componentType = if config.pure
118-
then "PureComponent"
119-
else "Component",
167+
componentType = if isPure then "PureComponent" else "Component",
120168
name = if config.name then config.name else "Component",
121169
})
122170
return function(props, children)
@@ -144,6 +192,7 @@ function froact.configure<Hooks>(config: {
144192
e = e,
145193
c = newC(config.Roact, config.Hooks, config.unpureByDefault),
146194
list = newList(config.Roact),
195+
template = newTemplate(config.Roact, config.unpureByDefault),
147196
-- FROACTFUL_FUNCTION_EXPORTS
148197
}
149198
end

froactful.lua

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type Element = any
2727
-- type Children = Element | { Element } | nil
2828
type Children = any? -- For simpler definitions, since it's the equivalent as above
2929

30-
local function newE(roact, hooks, defaults: { DefaultPropertyConfig })
30+
local function newE(roact: any, hooks, defaults: { DefaultPropertyConfig })
3131
return function(class: string, props: { [string]: any }, children: Children)
3232
for _, default in defaults do
3333
if isA(class, default.class) and not props[default.property] then
@@ -38,12 +38,64 @@ local function newE(roact, hooks, defaults: { DefaultPropertyConfig })
3838
end
3939
end
4040

41+
local function newTemplate(roact: any, unpureByDefault: boolean?)
42+
type CleanupMethod = () -> ()
43+
type UpdateMethod<Props> = (props: Props) -> ()
44+
type Constructor<Props> = (
45+
name: string,
46+
parent: Instance,
47+
onUpdate: (UpdateMethod<Props>) -> ()
48+
) -> CleanupMethod
49+
return function<Props>(config: ComponentConfig, f: Constructor<Props>)
50+
local isPure = if config.pure == nil
51+
then not unpureByDefault
52+
else config.pure
53+
local Component = if isPure
54+
then roact.PureComponent:extend(config.name or "Component")
55+
else roact.Component:extend(config.name or "Component")
56+
function Component:init()
57+
local _className, hostKey, hostParent, _children
58+
for name, field in self do
59+
if tostring(name) == "Symbol(InternalData)" then
60+
_className = field.componentClass
61+
hostKey = field.virtualNode.hostKey
62+
hostParent = field.virtualNode.hostParent
63+
_children = field.virtualNode.children
64+
end
65+
end
66+
self.updateCallbacks = {}
67+
self.cleanup = f(hostKey, hostParent, function(f)
68+
table.insert(self.updateCallbacks, f)
69+
end)
70+
end
71+
function Component:render()
72+
return roact.createFragment()
73+
end
74+
function Component:didMount()
75+
for _, callback in self.updateCallbacks do
76+
callback(self.props)
77+
end
78+
end
79+
function Component:didUpdate()
80+
for _, callback in self.updateCallbacks do
81+
callback(self.props)
82+
end
83+
end
84+
function Component:willUnmount()
85+
self.cleanup()
86+
end
87+
return function(props: Props, children)
88+
return roact.createElement(Component, props, children)
89+
end
90+
end
91+
end
92+
4193
-- stylua: ignore
4294
type ListConfig = (
4395
{ orderByName: boolean?, setOrder: true, initial: number?, key: string? }
4496
| { orderByName: boolean?, setOrder: false?, initial: nil, key: string? }
4597
)
46-
local function newList(roact)
98+
local function newList(roact: any)
4799
return function(config: ListConfig, elements: { [number]: Element })
48100
local index = if config.initial then config.initial else 0
49101
local count: { [string]: number } = {}
@@ -101,23 +153,19 @@ type HookFunction<Props, Hooks> = (
101153
) -> any
102154

103155
local function newC<Hooks>(
104-
roact,
156+
roact: any,
105157
hooks: HookFunction<any, Hooks>,
106158
unpureByDefault: boolean?
107159
)
108160
return function<Props>(
109161
config: ComponentConfig,
110162
body: (Props, Hooks) -> any
111163
): (Props, Children) -> any
112-
if not unpureByDefault then
113-
if config.pure == nil then
114-
config.pure = true
115-
end
116-
end
164+
local isPure = if config.pure == nil
165+
then not unpureByDefault
166+
else config.pure
117167
local Component = hooks(body, {
118-
componentType = if config.pure
119-
then "PureComponent"
120-
else "Component",
168+
componentType = if isPure then "PureComponent" else "Component",
121169
name = if config.name then config.name else "Component",
122170
})
123171
return function(props, children)
@@ -381,6 +429,7 @@ function froact.configure<Hooks>(config: {
381429
e = e,
382430
c = newC(config.Roact, config.Hooks, config.unpureByDefault),
383431
list = newList(config.Roact),
432+
template = newTemplate(config.Roact, config.unpureByDefault),
384433
Camera = Camera,
385434
CanvasGroup = CanvasGroup,
386435
Frame = Frame,

0 commit comments

Comments
 (0)