Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ Ignite is a UI library for developing Roblox plugins. It includes 10+ goregeous

## Getting Started

1. Install Ignite via [Wally](https://wally.run/package/cameronpcampbell/ignite?version=1.0.0) or via [github releases](https://github.com/cameronpcampbell/Ignite/releases).
1. Install Ignite via [Wally](https://wally.run/package/cameronpcampbell/ignite?version=1.2.6) or via [github releases](https://github.com/cameronpcampbell/Ignite/releases).
```
ignite = "mightypart/ignite@1.0.0"
ignite = "mightypart/ignite@1.2.6"
```

2. Setup your `default.project.json` (if using rojo).
Expand Down
8 changes: 0 additions & 8 deletions aftman.toml

This file was deleted.

Binary file added ignite-test.rbxl
Binary file not shown.
8 changes: 8 additions & 0 deletions rokit.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This file lists tools managed by Rokit, a toolchain manager for Roblox projects.
# For more information, see https://github.com/rojo-rbx/rokit

# New tools can be added by running `rokit add <tool>` in a terminal.

[tools]
rojo = "rojo-rbx/rojo@7.4.3"
wally = "UpliftGames/wally@0.3.2"
2 changes: 1 addition & 1 deletion sourcemap.json

Large diffs are not rendered by default.

296 changes: 193 additions & 103 deletions src/Components/Button.luau
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
--!strict


--> Services ------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------


--> Modules -------------------------------------------------------------------------------------------
local Modules = script.Parent.Parent.Modules
local Component = require(Modules.Component)
Expand All @@ -13,23 +11,37 @@ local TableUtils = require(Modules.TableUtils)

local Packages = script.Parent.Parent.Packages
local Fusion = require(Packages.Fusion)
local Peek = Fusion.peek

local Components = Modules.Parent.Components
local CoreComponents = Components.Core
local Squircle = require(CoreComponents.Squircle)
local TextLabel = require(CoreComponents.TextLabel)
-------------------------------------------------------------------------------------------------------


--> Types ---------------------------------------------------------------------------------------------

type IconProperties = Fusion.UsedAs<{
AnchorPoint: Vector2?,
Image: string?,
Size: UDim2?,
Position: UDim2?,
ImageColor3: Color3?,
ImageTransparency: number?,
ImageRectOffset: Vector2?,
ImageRectSize: Vector2?,
ScaleType: Enum.ScaleType?,
ResampleMode: Enum.ResamplerMode?,
}>?

type ButtonProps = {
Width: Fusion.UsedAs<UDim | "Auto">?,
Content: Fusion.UsedAs<{ string } | string>?,
Variant: ("Primary" | "Secondary" | "Destructive")?
Width: Fusion.UsedAs<UDim | "Auto">?,
Content: Fusion.UsedAs<{ string } | string>?,
Variant: ("Primary" | "Secondary" | "Destructive")?,
IconProperties: IconProperties,
}
-------------------------------------------------------------------------------------------------------


--> Variables -----------------------------------------------------------------------------------------
local ComputeUDim2 = ComputeTransforms.UDim2

Expand All @@ -38,104 +50,182 @@ local TableTake = TableUtils.Take
local Children, OnEvent = Fusion.Children, Fusion.OnEvent
-------------------------------------------------------------------------------------------------------


--> Private Functions ---------------------------------------------------------------------------------
local function ParseContentItem(scope: Component.Scope, item: string)
local imageId = string.match(item, "rbxassetid://%d+")

if imageId then
return scope:New "ImageLabel" {
Size = UDim2.fromOffset(9, 9),
BackgroundTransparency = 1,
ImageColor3 = scope:GetThemeItem("Text/Title"),
Image = imageId
}
end

return TextLabel (scope, {
Text = item,
Focus = "Title" :: "Title",
Weight = Enum.FontWeight.SemiBold,
Size = UDim2.fromScale(0, 1),
AutomaticSize = Enum.AutomaticSize.X,
TextXAlignment = Enum.TextXAlignment.Center
})

local function setProperty_unsafe(instance: Instance, property: string, value: unknown)
(instance :: any)[property] = value
end
-------------------------------------------------------------------------------------------------------

local function ParseContentItem(scope: Component.Scope, item: string, IconProperties: IconProperties)
local imageId = string.match(item, "rbxassetid://%d+")

local peekProperties = Peek(IconProperties)

if not peekProperties then
peekProperties = {} :: {}
end

local amount = 0

for _, value in peekProperties do
amount += 1
end

if amount ~= 0 or imageId then
if not peekProperties.Image and not imageId then
imageId = nil
end

local imageLabel = scope:New("ImageLabel")({
BackgroundTransparency = 1,
ImageColor3 = scope:GetThemeItem("Text/Title"),
Size = UDim2.fromOffset(9, 9),
Image = imageId,
})

if peekProperties == {} then
return imageLabel
end

for Key, Property in peekProperties do
local success, err = pcall(function()
setProperty_unsafe(imageLabel, Key, Property)
end)

if success then
continue
end
warn(err)
end

return imageLabel
end

return TextLabel(scope, {
Text = item,
Focus = "Title" :: "Title",
Weight = Enum.FontWeight.SemiBold,
Size = UDim2.fromScale(0, 1),
AutomaticSize = Enum.AutomaticSize.X,
TextXAlignment = Enum.TextXAlignment.Center,
}) :: any
end
-------------------------------------------------------------------------------------------------------

return Component(function(scope, props: ButtonProps)
local width = TableTake(props, "Width", UDim.new(1,0)) :: Fusion.UsedAs<UDim>
local content = TableTake(props, "Content", "Button") :: Fusion.UsedAs<{ string } | string>
local variant = TableTake(props, "Variant", "Primary") :: Fusion.UsedAs<"Primary" | "Secondary" | "Destructive">

local isHoverState, isFocusState = scope:Value(false), scope:Value(false)

-- We need to ensure that `content` is an array so it can be used with `ForPairs`
local computeContentArray = scope:Computed(function(use)
local usedContent = use(content)
return if type(usedContent) == "table" then usedContent else { usedContent }
end)

-- Width may be "Auto" therefore we need to make the width of the button `UDim.new()` when this is the case.
local computeUDimWidth = scope:Computed(function(use)
local usedWidth = use(width)
return if usedWidth == "Auto" then UDim.new() else usedWidth
end)

local computeAutoSize = scope:Computed(function(use)
return if use(width) == "Auto" then Enum.AutomaticSize.X else Enum.AutomaticSize.None
end)

local computeBackgroundColor = scope:GetThemeItem(
scope:Computed(function(use) return `Accent/{use(variant) or "Primary"}` :: any end),
scope:Computed(function(use) return if use(isFocusState) then "Focus" elseif use(isHoverState) then "Hover" else nil :: any end)
)

-- Handles the edge case of the user dragging the cursor off of the button and then releasing the mouse button.
scope:AddRootEvent("InputEnded", function() isFocusState:set(false) end)

return Squircle (scope, {
As = "ImageButton" :: "ImageButton",
Size = ComputeUDim2(scope, computeUDimWidth, UDim.new(0, 25)),
AutomaticSize = computeAutoSize,
BackgroundColor3 = computeBackgroundColor,
ClipsDescendants = true,
Name = "Button",

[OnEvent "MouseEnter"] = function() isHoverState:set(true) end :: any,
[OnEvent "MouseLeave"] = function() isHoverState:set(false) end :: any,

[OnEvent "MouseButton1Down"] = function() isFocusState:set(true) end :: any,
[OnEvent "MouseButton1Up"] = function() isFocusState:set(false) end :: any,

[Children] = {
Squircle (scope, {
Size = UDim2.fromScale(1, 1),
Image = "rbxassetid://108824901287727",
Name = "Button:Highlight"
}),

scope:New "Frame" {
Size = UDim2.fromScale(1, 1),
BackgroundTransparency = 1,
Name = "Button:ContentWrapper",

[Children] = {
scope:ForPairs(computeContentArray, function(use, scope, idx, item)
return idx, ParseContentItem(scope, item)
end) :: any,

scope:New "UIListLayout" {
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Center,
VerticalAlignment = Enum.VerticalAlignment.Center,
Padding = UDim.new(0, 8)
},

scope:New "UIPadding" { PaddingLeft = UDim.new(0, 8), PaddingRight = UDim.new(0, 8) },
}
}
}
}, props :: any)
end)
local width = TableTake(props, "Width", UDim.new(1, 0)) :: Fusion.UsedAs<UDim>
local content = TableTake(props, "Content", "Button") :: Fusion.UsedAs<{ string } | string>
local variant = TableTake(props, "Variant", "Primary") :: Fusion.UsedAs<"Primary" | "Secondary" | "Destructive">
local iconProperties = TableTake(props, "IconProperties", {}) :: IconProperties

local isHoverState = scope:Value(false)
local isFocusState = scope:Value(false)

-- We need to ensure that `content` is an array so it can be used with `ForPairs`

local computeContentArray = scope:Computed(function(use)
local usedContent = use(content)
return if type(usedContent) == "table" then usedContent else { usedContent }
end)

-- Width may be "Auto" therefore we need to make the width of the button `UDim.new()` when this is the case.
local computeUDimWidth = scope:Computed(function(use)
local usedWidth = use(width)
return if usedWidth == "Auto" then UDim.new() else usedWidth
end)

local computeAutoSize = scope:Computed(function(use)
return if use(width) == "Auto" then Enum.AutomaticSize.X else Enum.AutomaticSize.None
end)

local computeProperties = scope:Computed(function(use)
return use(iconProperties) or {}
end)

local computeBackgroundColor = scope:GetThemeItem(
scope:Computed(function(use)
return `Accent/{use(variant) or "Primary"}` :: any
end),
scope:Computed(function(use)
return if use(isFocusState) then "Focus" elseif use(isHoverState) then "Hover" else nil :: any
end)
)

local computeChildren = scope:Computed(function(Use)
local text = ""
local image = ""

local children = {}

for _, content in Use(computeContentArray) do
if not string.find(content, "rbxassetid://") then
text = content
else
image = content
end
end

if image or computeProperties["Image"] then
table.insert(children, ParseContentItem(scope, image :: string, computeProperties))
end

table.insert(children, ParseContentItem(scope, text, {}))

return children
end)

-- Handles the edge case of the user dragging the cursor off of the button and then releasing the mouse button.
scope:AddRootEvent("InputEnded", function()
isFocusState:set(false)
end)

return Squircle(scope, {
As = "ImageButton" :: "ImageButton",
Size = ComputeUDim2(scope, computeUDimWidth, UDim.new(0, 25)),
AutomaticSize = computeAutoSize,
BackgroundColor3 = computeBackgroundColor,
ClipsDescendants = true,
Name = "Button",

[OnEvent("MouseEnter")] = function()
isHoverState:set(true)
end :: any,
[OnEvent("MouseLeave")] = function()
isHoverState:set(false)
end :: any,

[OnEvent("MouseButton1Down")] = function()
isFocusState:set(true)
end :: any,
[OnEvent("MouseButton1Up")] = function()
isFocusState:set(false)
end :: any,

[Children] = {
Squircle(scope, {
Size = UDim2.fromScale(1, 1),
Image = "rbxassetid://108824901287727",
Name = "Button:Highlight",
}),

scope:New("Frame")({
Size = UDim2.fromScale(1, 1),
BackgroundTransparency = 1,
Name = "Button:ContentWrapper",

[Children] = {
computeChildren,

scope:New("UIListLayout")({
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Center,
VerticalAlignment = Enum.VerticalAlignment.Center,
Padding = UDim.new(0, 8),
}),

scope:New("UIPadding")({ PaddingLeft = UDim.new(0, 8), PaddingRight = UDim.new(0, 8) }),
},
}),
},
}, props :: any)
end)
Loading