Skip to content

Instantly share code, notes, and snippets.

@cameronpcampbell
Created August 22, 2024 20:47
Show Gist options
  • Save cameronpcampbell/c0b2e187444d94cd0d6b95b51a648c63 to your computer and use it in GitHub Desktop.
Save cameronpcampbell/c0b2e187444d94cd0d6b95b51a648c63 to your computer and use it in GitHub Desktop.
Component Helper (Fusion)
--!strict
--> Modules -------------------------------------------------------------------------------------------
local Fusion = require(script.Parent.Fusion) -- Replace with path to fusion.
-------------------------------------------------------------------------------------------------------
--> Types ---------------------------------------------------------------------------------------------
type Scope = Fusion.Scope<typeof(Fusion)>
type BaseProps = {
Scope: Fusion.Scope<typeof(Fusion)>,
[string | Fusion.SpecialKey]: unknown?
}
type BasePropsOptional = {
Scope: (Fusion.Scope<typeof(Fusion)>)?,
[string | Fusion.SpecialKey]: unknown?
}
-------------------------------------------------------------------------------------------------------
--> Variables -----------------------------------------------------------------------------------------
local Children = Fusion.Children
-------------------------------------------------------------------------------------------------------
--> Functions -----------------------------------------------------------------------------------------
local function TableTake(tble: { [any]: any }, keyOrIndex: any, default: any?)
local value = tble[keyOrIndex]
tble[keyOrIndex] = nil
if value == nil then return default end
return value
end
local function CombineProps(...: Fusion.PropertyTable): Fusion.PropertyTable
local propTablesLength = select("#", ...)
if propTablesLength == 1 then return ... end
local combinedProps: Fusion.PropertyTable = {}
local combinedChildren: { Fusion.Child } = {}
for i = 1, propTablesLength do
local propTble = select(i, ...)
for propName,propValue in propTble do
if propName == Children then
if typeof(propValue) == "table" and not propValue.kind then
for _,child in propValue do table.insert(combinedChildren, child) end
else
table.insert(combinedChildren, propValue)
end
continue
end
combinedProps[propName] = propValue
end
end
if #combinedChildren then
combinedProps[Children] = combinedChildren
end
return combinedProps
end
-------------------------------------------------------------------------------------------------------
return {
Component = function <Props>(fn: (scope: Scope, props: Props) -> Fusion.Child)
return function(props: Props & BaseProps, ...: Props & BasePropsOptional)
local combinedProps = CombineProps(props, ...) :: Props & BaseProps
local scope = TableTake(combinedProps, "Scope") :: Scope
return fn(scope, combinedProps)
end
end,
ComponentExplicitScope = function <Props>(fn: (scope: Scope, props: Props) -> Fusion.Child)
return function(scope: Scope, ...: Props & BasePropsOptional)
local combinedProps = CombineProps(...) :: Props & BaseProps
scope = TableTake(combinedProps, "Scope", scope) :: Scope
return fn(scope, combinedProps)
end
end,
}
@cameronpcampbell
Copy link
Author

Example Usage

--!strict

local RepStorage = game:GetService("ReplicatedStorage")
local Fusion = require(RepStorage.Fusion)
local Component = require(RepStorage.Component)

local MyScope = Fusion.scoped(Fusion)

--> Component -----------------------------------------------------------------------------------------
local SliderA = Component.Component(function(scope, props: { Value: Fusion.UsedAs<number>, Step: Fusion.UsedAs<number> })
  return scope:New "Frame" {

  }
end)

-- Multiple property tables are combined into one.
local sliderInstanceA = SliderA ({
  Scope = MyScope,
  Value = .5,
  Step = 2
}, {
  Value = .1,
  Step = MyScope:Value(.23),
})
-------------------------------------------------------------------------------------------------------

--> Component (With Explicit Scope Argument) ----------------------------------------------------------
local SliderB = Component.ComponentExplicitScope(function(scope, props: { Value: Fusion.UsedAs<number>, Step: Fusion.UsedAs<number> })
  return scope:New "Frame" {

  }
end)


local sliderInstanceB = SliderB(MyScope, {
  Value = .5,
  Step = 2
})
-------------------------------------------------------------------------------------------------------

@znotfireman
Copy link

why not leave the scope as the first arg for the component so it can be used in scooed syntax

@cameronpcampbell
Copy link
Author

ComponentExplicitScope does this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment