From 8aca00825f5cec7e678d2b6417fb9628ee3ee73f Mon Sep 17 00:00:00 2001 From: EgoMoose Date: Mon, 30 Dec 2024 18:08:03 -0500 Subject: [PATCH 1/6] Add spritesheet icon support --- src/Components/Button.luau | 3 ++ src/Components/Dropdown/DropdownItem.luau | 24 ++++++----- src/Components/Dropdown/Types.luau | 11 ++--- src/Components/Foundation/BaseButton.luau | 43 +++++++++++-------- src/Components/Foundation/BaseIcon.luau | 34 +++++++++++++++ src/Components/MainButton.luau | 3 ++ src/Stories/Button.story.luau | 16 +++---- .../Helpers/studiocomponents.storybook.luau | 13 ++++++ 8 files changed, 105 insertions(+), 42 deletions(-) create mode 100644 src/Components/Foundation/BaseIcon.luau create mode 100644 src/Stories/Helpers/studiocomponents.storybook.luau diff --git a/src/Components/Button.luau b/src/Components/Button.luau index c42d51e..c6c5fc8 100644 --- a/src/Components/Button.luau +++ b/src/Components/Button.luau @@ -46,6 +46,9 @@ local BaseButton = require("./Foundation/BaseButton") @field Color Color3? @field UseThemeColor boolean? @field Alignment HorizontalAlignment? + @field ResampleMode Enum.ResamplerMode?, + @field RectOffset Vector2?, + @field RectSize Vector2?, The `Alignment` prop is used to configure which side of any text the icon appears on. Left-alignment is the default and center-alignment is not supported. diff --git a/src/Components/Dropdown/DropdownItem.luau b/src/Components/Dropdown/DropdownItem.luau index 22d8d8c..371ec6e 100644 --- a/src/Components/Dropdown/DropdownItem.luau +++ b/src/Components/Dropdown/DropdownItem.luau @@ -3,6 +3,8 @@ local React = require("@pkg/@jsdotlua/react") local Constants = require("../../Constants") local useTheme = require("../../Hooks/useTheme") +local BaseIcon = require("../Foundation/BaseIcon") + local DropdownTypes = require("./Types") type DropdownItemProps = { @@ -27,13 +29,23 @@ local function DropdownItem(props: DropdownItemProps) modifier = Enum.StudioStyleGuideModifier.Hover end - local iconColor = Color3.fromRGB(255, 255, 255) + local iconNode: React.Node? if props.Icon then + local iconColor = Color3.fromRGB(255, 255, 255) if props.Icon.UseThemeColor then iconColor = theme:GetColor(Enum.StudioStyleGuideColor.MainText) elseif props.Icon.Color then iconColor = props.Icon.Color end + + local publicIconProps = table.clone(props.Icon) :: BaseIcon.PublicIconProps + publicIconProps.Color = iconColor + + iconNode = React.createElement(BaseIcon, { + Disabled = nil, + LayoutOrder = nil, + PublicIconProps = publicIconProps, + }) end return React.createElement("Frame", { @@ -66,15 +78,7 @@ local function DropdownItem(props: DropdownItemProps) PaddingLeft = UDim.new(0, props.TextInset), PaddingBottom = UDim.new(0, 2), }), - Icon = props.Icon and React.createElement("ImageLabel", { - AnchorPoint = Vector2.new(0, 0.5), - Position = UDim2.fromScale(0, 0.5), - Size = UDim2.fromOffset(props.Icon.Size.X, props.Icon.Size.Y), - BackgroundTransparency = 1, - Image = props.Icon.Image, - ImageTransparency = props.Icon.Transparency or 0, - ImageColor3 = iconColor, - }), + Icon = iconNode, Label = React.createElement("TextLabel", { BackgroundTransparency = 1, AnchorPoint = Vector2.new(1, 0), diff --git a/src/Components/Dropdown/Types.luau b/src/Components/Dropdown/Types.luau index 319d88c..a5f3c89 100644 --- a/src/Components/Dropdown/Types.luau +++ b/src/Components/Dropdown/Types.luau @@ -1,3 +1,5 @@ +local BaseIcon = require("../Foundation/BaseIcon") + --[=[ @within Dropdown @type DropdownItem string | DropdownItemDetail @@ -29,13 +31,12 @@ export type DropdownItemDetail = { @field Transparency number? @field Color Color3? @field UseThemeColor boolean? + @field ResampleMode Enum.ResamplerMode?, + @field RectOffset Vector2?, + @field RectSize Vector2?, ]=] -export type DropdownItemIcon = { - Image: string, - Size: Vector2, - Transparency: number?, - Color: Color3?, +export type DropdownItemIcon = BaseIcon.PublicIconProps & { UseThemeColor: boolean?, } diff --git a/src/Components/Foundation/BaseButton.luau b/src/Components/Foundation/BaseButton.luau index 61ae677..7408984 100644 --- a/src/Components/Foundation/BaseButton.luau +++ b/src/Components/Foundation/BaseButton.luau @@ -6,24 +6,24 @@ local Constants = require("../../Constants") local getTextSize = require("../../getTextSize") local useTheme = require("../../Hooks/useTheme") +local BaseIcon = require("./BaseIcon") + local PADDING_X = 8 local PADDING_Y = 4 local DEFAULT_HEIGHT = Constants.DefaultButtonHeight +type Icon = BaseIcon.PublicIconProps & { + UseThemeColor: boolean?, + Alignment: Enum.HorizontalAlignment?, +} + export type BaseButtonConsumerProps = CommonProps.T & { AutomaticSize: Enum.AutomaticSize?, OnActivated: (() -> ())?, Selected: boolean?, Text: string?, TextTransparency: number?, - Icon: { - Image: string, - Size: Vector2, - Transparency: number?, - Color: Color3?, - UseThemeColor: boolean?, - Alignment: Enum.HorizontalAlignment?, - }?, + Icon: Icon?, } export type BaseButtonProps = BaseButtonConsumerProps & { @@ -78,6 +78,21 @@ local function BaseButton(props: BaseButtonProps) size = UDim2.new(size.Width, UDim.new(0, math.max(DEFAULT_HEIGHT, contentHeight + PADDING_Y * 2))) end + local iconNode: React.Node? + if props.Icon then + local publicIconProps = table.clone(props.Icon) :: BaseIcon.PublicIconProps + publicIconProps.Color = if publicIconProps.Color + then publicIconProps.Color + elseif props.Icon.UseThemeColor then textColor + else nil + + iconNode = React.createElement(BaseIcon, { + Disabled = props.Disabled, + LayoutOrder = if props.Icon.Alignment == Enum.HorizontalAlignment.Right then 3 else 1, + PublicIconProps = publicIconProps, + }) + end + return React.createElement("TextButton", { AutoButtonColor = false, AnchorPoint = props.AnchorPoint, @@ -115,17 +130,7 @@ local function BaseButton(props: BaseButtonProps) HorizontalAlignment = Enum.HorizontalAlignment.Center, VerticalAlignment = Enum.VerticalAlignment.Center, }), - Icon = props.Icon and React.createElement("ImageLabel", { - Image = props.Icon.Image, - Size = UDim2.fromOffset(props.Icon.Size.X, props.Icon.Size.Y), - LayoutOrder = if props.Icon.Alignment == Enum.HorizontalAlignment.Right then 3 else 1, - BackgroundTransparency = 1, - ImageColor3 = if props.Icon.Color - then props.Icon.Color - elseif props.Icon.UseThemeColor then textColor - else nil, - ImageTransparency = 1 - (1 - (props.Icon.Transparency or 0)) * (1 - if props.Disabled then 0.2 else 0), - }), + Icon = iconNode, Label = props.Text and React.createElement("TextLabel", { TextColor3 = textColor, Font = Constants.DefaultFont, diff --git a/src/Components/Foundation/BaseIcon.luau b/src/Components/Foundation/BaseIcon.luau new file mode 100644 index 0000000..b94d86f --- /dev/null +++ b/src/Components/Foundation/BaseIcon.luau @@ -0,0 +1,34 @@ +local React = require("@pkg/@jsdotlua/react") + +export type PublicIconProps = { + Image: string, + Size: Vector2, + Transparency: number?, + Color: Color3?, + ResampleMode: Enum.ResamplerMode?, + RectOffset: Vector2?, + RectSize: Vector2?, +} + +export type BaseIconProps = { + Disabled: boolean?, + LayoutOrder: number?, + PublicIconProps: PublicIconProps, +} + +local function BaseIcon(props: BaseIconProps) + return React.createElement("ImageLabel", { + Image = props.PublicIconProps.Image, + Size = UDim2.fromOffset(props.PublicIconProps.Size.X, props.PublicIconProps.Size.Y), + LayoutOrder = props.LayoutOrder or 1, + BackgroundTransparency = 1, + ImageColor3 = props.PublicIconProps.Color, + ImageTransparency = 1 + - (1 - (props.PublicIconProps.Transparency or 0)) * (1 - if props.Disabled then 0.2 else 0), + ResampleMode = props.PublicIconProps.ResampleMode, + ImageRectOffset = props.PublicIconProps.RectOffset, + ImageRectSize = props.PublicIconProps.RectSize, + }) +end + +return BaseIcon diff --git a/src/Components/MainButton.luau b/src/Components/MainButton.luau index eb143a7..4c73cf9 100644 --- a/src/Components/MainButton.luau +++ b/src/Components/MainButton.luau @@ -25,6 +25,9 @@ local BaseButton = require("./Foundation/BaseButton") @field Color Color3? @field UseThemeColor boolean? @field Alignment HorizontalAlignment? + @field ResampleMode Enum.ResamplerMode?, + @field RectOffset Vector2?, + @field RectSize Vector2?, ]=] --[=[ diff --git a/src/Stories/Button.story.luau b/src/Stories/Button.story.luau index f851b6b..ff6d480 100644 --- a/src/Stories/Button.story.luau +++ b/src/Stories/Button.story.luau @@ -10,14 +10,14 @@ local function StoryButton(props: { }) return React.createElement(Button, { LayoutOrder = if props.Disabled then 2 else 1, - Icon = if props.HasIcon - then { - Image = "rbxasset://studio_svg_textures/Shared/InsertableObjects/Dark/Standard/Part.png", - Size = Vector2.one * 16, - UseThemeColor = true, - Alignment = Enum.HorizontalAlignment.Left, - } - else nil, + Icon = props.HasIcon and { + --Image = "rbxasset://studio_svg_textures/Shared/InsertableObjects/Dark/Standard/Part.png", + Image = "rbxassetid://18786011824", + Size = Vector2.new(16, 16), + Alignment = Enum.HorizontalAlignment.Left, + RectOffset = Vector2.new(1000, 0), + RectSize = Vector2.new(16, 16), + } :: any, Text = props.Text, OnActivated = if not props.Disabled then function() end else nil, Disabled = props.Disabled, diff --git a/src/Stories/Helpers/studiocomponents.storybook.luau b/src/Stories/Helpers/studiocomponents.storybook.luau new file mode 100644 index 0000000..4b75db2 --- /dev/null +++ b/src/Stories/Helpers/studiocomponents.storybook.luau @@ -0,0 +1,13 @@ +local React = require("@pkg/@jsdotlua/react") +local ReactRoblox = require("@pkg/@jsdotlua/react-roblox") + +return { + name = "StudioComponents", + storyRoots = { + script.Parent.Parent, + }, + packages = { + React = React, + ReactRoblox = ReactRoblox, + }, +} From 5617ff5232bb4460ed1a1bc6812a52a432ba65d2 Mon Sep 17 00:00:00 2001 From: sircfenner Date: Fri, 3 Jan 2025 19:21:49 +0000 Subject: [PATCH 2/6] refactor baseicon --- src/Components/Dropdown/DropdownItem.luau | 13 +++++----- src/Components/Dropdown/Types.luau | 11 +++++--- src/Components/Foundation/BaseButton.luau | 29 +++++++++++---------- src/Components/Foundation/BaseIcon.luau | 31 +++++++++++------------ src/Stories/Button.story.luau | 2 +- 5 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/Components/Dropdown/DropdownItem.luau b/src/Components/Dropdown/DropdownItem.luau index 371ec6e..86a0f37 100644 --- a/src/Components/Dropdown/DropdownItem.luau +++ b/src/Components/Dropdown/DropdownItem.luau @@ -38,14 +38,13 @@ local function DropdownItem(props: DropdownItemProps) iconColor = props.Icon.Color end - local publicIconProps = table.clone(props.Icon) :: BaseIcon.PublicIconProps - publicIconProps.Color = iconColor + local iconProps = (table.clone(props.Icon) :: any) :: BaseIcon.BaseIconProps + iconProps.Color = iconColor + iconProps.Size = UDim2.fromOffset(props.Icon.Size.X, props.Icon.Size.Y) + iconProps.Disabled = nil + iconProps.LayoutOrder = nil - iconNode = React.createElement(BaseIcon, { - Disabled = nil, - LayoutOrder = nil, - PublicIconProps = publicIconProps, - }) + iconNode = React.createElement(BaseIcon, iconProps) end return React.createElement("Frame", { diff --git a/src/Components/Dropdown/Types.luau b/src/Components/Dropdown/Types.luau index a5f3c89..4377f18 100644 --- a/src/Components/Dropdown/Types.luau +++ b/src/Components/Dropdown/Types.luau @@ -1,5 +1,3 @@ -local BaseIcon = require("../Foundation/BaseIcon") - --[=[ @within Dropdown @type DropdownItem string | DropdownItemDetail @@ -36,7 +34,14 @@ export type DropdownItemDetail = { @field RectSize Vector2?, ]=] -export type DropdownItemIcon = BaseIcon.PublicIconProps & { +export type DropdownItemIcon = { + Image: string, + Size: Vector2, + Transparency: number?, + Color: Color3?, + ResampleMode: Enum.ResamplerMode?, + RectOffset: Vector2?, + RectSize: Vector2?, UseThemeColor: boolean?, } diff --git a/src/Components/Foundation/BaseButton.luau b/src/Components/Foundation/BaseButton.luau index 7408984..0e130db 100644 --- a/src/Components/Foundation/BaseButton.luau +++ b/src/Components/Foundation/BaseButton.luau @@ -12,7 +12,14 @@ local PADDING_X = 8 local PADDING_Y = 4 local DEFAULT_HEIGHT = Constants.DefaultButtonHeight -type Icon = BaseIcon.PublicIconProps & { +export type BaseButtonIconProps = { + Image: string, + Size: Vector2, + Transparency: number?, + Color: Color3?, + ResampleMode: Enum.ResamplerMode?, + RectOffset: Vector2?, + RectSize: Vector2?, UseThemeColor: boolean?, Alignment: Enum.HorizontalAlignment?, } @@ -23,7 +30,7 @@ export type BaseButtonConsumerProps = CommonProps.T & { Selected: boolean?, Text: string?, TextTransparency: number?, - Icon: Icon?, + Icon: BaseButtonIconProps?, } export type BaseButtonProps = BaseButtonConsumerProps & { @@ -80,17 +87,13 @@ local function BaseButton(props: BaseButtonProps) local iconNode: React.Node? if props.Icon then - local publicIconProps = table.clone(props.Icon) :: BaseIcon.PublicIconProps - publicIconProps.Color = if publicIconProps.Color - then publicIconProps.Color - elseif props.Icon.UseThemeColor then textColor - else nil - - iconNode = React.createElement(BaseIcon, { - Disabled = props.Disabled, - LayoutOrder = if props.Icon.Alignment == Enum.HorizontalAlignment.Right then 3 else 1, - PublicIconProps = publicIconProps, - }) + local iconProps = (table.clone(props.Icon) :: any) :: BaseIcon.BaseIconProps + iconProps.Disabled = props.Disabled + iconProps.Color = iconProps.Color or if props.Icon.UseThemeColor then textColor else nil + iconProps.LayoutOrder = if props.Icon.Alignment == Enum.HorizontalAlignment.Right then 3 else 1 + iconProps.Size = UDim2.fromOffset(props.Icon.Size.X, props.Icon.Size.Y) + + iconNode = React.createElement(BaseIcon, iconProps) end return React.createElement("TextButton", { diff --git a/src/Components/Foundation/BaseIcon.luau b/src/Components/Foundation/BaseIcon.luau index b94d86f..4dd7b55 100644 --- a/src/Components/Foundation/BaseIcon.luau +++ b/src/Components/Foundation/BaseIcon.luau @@ -1,8 +1,9 @@ local React = require("@pkg/@jsdotlua/react") -export type PublicIconProps = { +local CommonProps = require("../../CommonProps") + +export type BaseIconConsumerProps = CommonProps.T & { Image: string, - Size: Vector2, Transparency: number?, Color: Color3?, ResampleMode: Enum.ResamplerMode?, @@ -10,24 +11,22 @@ export type PublicIconProps = { RectSize: Vector2?, } -export type BaseIconProps = { - Disabled: boolean?, - LayoutOrder: number?, - PublicIconProps: PublicIconProps, -} +export type BaseIconProps = BaseIconConsumerProps local function BaseIcon(props: BaseIconProps) return React.createElement("ImageLabel", { - Image = props.PublicIconProps.Image, - Size = UDim2.fromOffset(props.PublicIconProps.Size.X, props.PublicIconProps.Size.Y), - LayoutOrder = props.LayoutOrder or 1, + Size = props.Size, + Position = props.Position, + AnchorPoint = props.AnchorPoint, + LayoutOrder = props.LayoutOrder, + ZIndex = props.ZIndex, BackgroundTransparency = 1, - ImageColor3 = props.PublicIconProps.Color, - ImageTransparency = 1 - - (1 - (props.PublicIconProps.Transparency or 0)) * (1 - if props.Disabled then 0.2 else 0), - ResampleMode = props.PublicIconProps.ResampleMode, - ImageRectOffset = props.PublicIconProps.RectOffset, - ImageRectSize = props.PublicIconProps.RectSize, + Image = props.Image, + ImageColor3 = props.Color, + ImageTransparency = 1 - (1 - (props.Transparency or 0)) * (1 - if props.Disabled then 0.2 else 0), + ImageRectOffset = props.RectOffset, + ImageRectSize = props.RectSize, + ResampleMode = props.ResampleMode, }) end diff --git a/src/Stories/Button.story.luau b/src/Stories/Button.story.luau index ff6d480..74245e1 100644 --- a/src/Stories/Button.story.luau +++ b/src/Stories/Button.story.luau @@ -11,8 +11,8 @@ local function StoryButton(props: { return React.createElement(Button, { LayoutOrder = if props.Disabled then 2 else 1, Icon = props.HasIcon and { - --Image = "rbxasset://studio_svg_textures/Shared/InsertableObjects/Dark/Standard/Part.png", Image = "rbxassetid://18786011824", + UseThemeColor = true, Size = Vector2.new(16, 16), Alignment = Enum.HorizontalAlignment.Left, RectOffset = Vector2.new(1000, 0), From 66ffed702d4046cc07069545554ef98ddfaf6246 Mon Sep 17 00:00:00 2001 From: sircfenner Date: Fri, 3 Jan 2025 19:24:55 +0000 Subject: [PATCH 3/6] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7a6294..f97623c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Added RectSize, RectOffset, and ResampleMode to icon props available in Button, MainButton, and Dropdown + ## 1.1.0 - Fixed image links in documentation From 315bf199e5529026c9f6b710e0b491b258279e42 Mon Sep 17 00:00:00 2001 From: sircfenner Date: Fri, 3 Jan 2025 19:25:51 +0000 Subject: [PATCH 4/6] fix storybook for luau-analyze --- src/Stories/Helpers/studiocomponents.storybook.luau | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Stories/Helpers/studiocomponents.storybook.luau b/src/Stories/Helpers/studiocomponents.storybook.luau index 4b75db2..8ed2195 100644 --- a/src/Stories/Helpers/studiocomponents.storybook.luau +++ b/src/Stories/Helpers/studiocomponents.storybook.luau @@ -1,3 +1,5 @@ +--!nocheck + local React = require("@pkg/@jsdotlua/react") local ReactRoblox = require("@pkg/@jsdotlua/react-roblox") From 689bf40c61fc60e640a2d4305a375c55e81709f7 Mon Sep 17 00:00:00 2001 From: sircfenner Date: Fri, 3 Jan 2025 19:27:45 +0000 Subject: [PATCH 5/6] fix doc comments --- src/Components/Button.luau | 6 +++--- src/Components/Dropdown/Types.luau | 6 +++--- src/Components/MainButton.luau | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Components/Button.luau b/src/Components/Button.luau index c6c5fc8..8d757c9 100644 --- a/src/Components/Button.luau +++ b/src/Components/Button.luau @@ -46,9 +46,9 @@ local BaseButton = require("./Foundation/BaseButton") @field Color Color3? @field UseThemeColor boolean? @field Alignment HorizontalAlignment? - @field ResampleMode Enum.ResamplerMode?, - @field RectOffset Vector2?, - @field RectSize Vector2?, + @field ResampleMode Enum.ResamplerMode? + @field RectOffset Vector2? + @field RectSize Vector2? The `Alignment` prop is used to configure which side of any text the icon appears on. Left-alignment is the default and center-alignment is not supported. diff --git a/src/Components/Dropdown/Types.luau b/src/Components/Dropdown/Types.luau index 4377f18..13b4e11 100644 --- a/src/Components/Dropdown/Types.luau +++ b/src/Components/Dropdown/Types.luau @@ -29,9 +29,9 @@ export type DropdownItemDetail = { @field Transparency number? @field Color Color3? @field UseThemeColor boolean? - @field ResampleMode Enum.ResamplerMode?, - @field RectOffset Vector2?, - @field RectSize Vector2?, + @field ResampleMode Enum.ResamplerMode? + @field RectOffset Vector2? + @field RectSize Vector2? ]=] export type DropdownItemIcon = { diff --git a/src/Components/MainButton.luau b/src/Components/MainButton.luau index 4c73cf9..47d4631 100644 --- a/src/Components/MainButton.luau +++ b/src/Components/MainButton.luau @@ -25,9 +25,9 @@ local BaseButton = require("./Foundation/BaseButton") @field Color Color3? @field UseThemeColor boolean? @field Alignment HorizontalAlignment? - @field ResampleMode Enum.ResamplerMode?, - @field RectOffset Vector2?, - @field RectSize Vector2?, + @field ResampleMode Enum.ResamplerMode? + @field RectOffset Vector2? + @field RectSize Vector2? ]=] --[=[ From 1cb0a890dcbdbad4d012b54baf1fc3f9a172b86b Mon Sep 17 00:00:00 2001 From: sircfenner Date: Fri, 3 Jan 2025 19:32:12 +0000 Subject: [PATCH 6/6] fix dropdown item icon alignment --- src/Components/Dropdown/DropdownItem.luau | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Components/Dropdown/DropdownItem.luau b/src/Components/Dropdown/DropdownItem.luau index 86a0f37..fadb530 100644 --- a/src/Components/Dropdown/DropdownItem.luau +++ b/src/Components/Dropdown/DropdownItem.luau @@ -40,6 +40,8 @@ local function DropdownItem(props: DropdownItemProps) local iconProps = (table.clone(props.Icon) :: any) :: BaseIcon.BaseIconProps iconProps.Color = iconColor + iconProps.AnchorPoint = Vector2.new(0, 0.5) + iconProps.Position = UDim2.fromScale(0, 0.5) iconProps.Size = UDim2.fromOffset(props.Icon.Size.X, props.Icon.Size.Y) iconProps.Disabled = nil iconProps.LayoutOrder = nil