From 6076a4cbdbd58d51247661240d8a68783bf56a24 Mon Sep 17 00:00:00 2001 From: Nidoxs Date: Wed, 4 Jun 2025 20:01:50 +0100 Subject: [PATCH 1/9] Create `useToggleState` hook --- src/Hooks/useToggleState.luau | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/Hooks/useToggleState.luau diff --git a/src/Hooks/useToggleState.luau b/src/Hooks/useToggleState.luau new file mode 100644 index 0000000..dd7e0d2 --- /dev/null +++ b/src/Hooks/useToggleState.luau @@ -0,0 +1,33 @@ +local React = require("@pkg/@jsdotlua/react") + +local function useToggleState(default: boolean): { + on: boolean, + enable: () -> (), + disable: () -> (), + toggle: () -> (), +} + local toggled, setToggled = React.useState(default) + + local enable = React.useCallback(function() + setToggled(true) + end, {}) + + local disable = React.useCallback(function() + setToggled(false) + end, {}) + + local toggle = React.useCallback(function() + setToggled(function(currentToggled) + return not currentToggled + end) + end, {}) + + return { + on = toggled, + enable = enable, + disable = disable, + toggle = toggle, + } +end + +return useToggleState From 19d1c09bc7c7a55a4ea8c4a3065367a03e5fb066 Mon Sep 17 00:00:00 2001 From: Nidoxs Date: Thu, 5 Jun 2025 23:09:37 +0100 Subject: [PATCH 2/9] Work on `Collapsible` component --- src/Components/Collapsible/Header.luau | 109 +++++++++++++++ src/Components/Collapsible/init.luau | 177 +++++++++++++++++++++++++ src/Stories/Collapsible.story.luau | 84 ++++++++++++ 3 files changed, 370 insertions(+) create mode 100644 src/Components/Collapsible/Header.luau create mode 100644 src/Components/Collapsible/init.luau create mode 100644 src/Stories/Collapsible.story.luau diff --git a/src/Components/Collapsible/Header.luau b/src/Components/Collapsible/Header.luau new file mode 100644 index 0000000..7d5261c --- /dev/null +++ b/src/Components/Collapsible/Header.luau @@ -0,0 +1,109 @@ +local CommonProps = require(script.Parent.Parent.Parent.CommonProps) +local React = require("@pkg/@jsdotlua/react") + +local e = React.createElement +local useTheme = require("../../Hooks/useTheme") +local useToggleState = require("../../Hooks/useToggleState") + +local HEADER_HEIGHT = 24 +local ARROW_RIGHT = "rbxasset://textures/ui/MenuBar/arrow_right.png" +local ARROW_DOWN = "rbxasset://textures/ui/MenuBar/arrow_down.png" + +local function HeaderIcon(props: CommonProps.T & { Icon: { Image: string, UseThemeColor: boolean? } }) + local theme = useTheme() + + return e("ImageLabel", { + BackgroundTransparency = 1, + BorderSizePixel = 0, + LayoutOrder = props.LayoutOrder, + Size = props.Size or UDim2.fromOffset(HEADER_HEIGHT, HEADER_HEIGHT), + ImageColor3 = if props.Icon.UseThemeColor ~= true + then Color3.fromRGB(255, 255, 255) + else theme:GetColor(Enum.StudioStyleGuideColor.MainText), + ImageTransparency = if props.Disabled then 0.6 else 0, + Image = props.Icon.Image, + }) +end + +local function Header(props: CommonProps.T & { + Selected: boolean?, + Expanded: boolean?, + IsBlockStyle: boolean?, + OnActivated: () -> (), + Text: string, + Icon: { + Image: string?, + UseThemeColor: boolean?, + }, +}) + local theme = useTheme() + local hovering = useToggleState(false) + + local modifier = Enum.StudioStyleGuideModifier.Default + + return e("TextButton", { + AutomaticSize = Enum.AutomaticSize.X, + AutoButtonColor = false, + BackgroundColor3 = if hovering.on + then theme:GetColor(Enum.StudioStyleGuideColor.ViewPortBackground) + elseif props.IsBlockStyle then theme:GetColor(Enum.StudioStyleGuideColor.Titlebar) + else theme:GetColor(Enum.StudioStyleGuideColor.MainBackground), + BackgroundTransparency = 0, + BorderSizePixel = if props.IsBlockStyle then 1 else 0, + BorderColor3 = theme:GetColor(Enum.StudioStyleGuideColor.Border, modifier), + Size = UDim2.new(1, 0, 0, HEADER_HEIGHT), + LayoutOrder = props.LayoutOrder, + Text = "", + ClipsDescendants = true, + + [React.Event.MouseEnter] = if not props.Disabled then hovering.enable else nil, + [React.Event.MouseLeave] = if not props.Disabled then hovering.disable else nil, + [React.Event.Activated] = if not props.Disabled then props.OnActivated else nil, + }, { + Layout = e("UIListLayout", { + FillDirection = Enum.FillDirection.Horizontal, + Padding = UDim.new(0, 0), + HorizontalAlignment = Enum.HorizontalAlignment.Left, + VerticalAlignment = Enum.VerticalAlignment.Center, + SortOrder = Enum.SortOrder.LayoutOrder, + }), + + UIPadding = e("UIPadding", { + PaddingLeft = UDim.new(0, 5), + }), + + ArrowIconFrame = e(HeaderIcon, { + Icon = { + Image = if props.Expanded then ARROW_DOWN else ARROW_RIGHT, + UseThemeColor = true, + }, + Disabled = props.Disabled, + LayoutOrder = 1, + Size = UDim2.fromOffset(HEADER_HEIGHT * 0.75, HEADER_HEIGHT * 0.75), + }), + + IconFrame = props.Icon and e(HeaderIcon, { + Icon = props.Icon, + Disabled = props.Disabled, + LayoutOrder = 2, + }), + + Title = e("TextLabel", { + AutomaticSize = Enum.AutomaticSize.X, + BackgroundTransparency = 1, + BorderSizePixel = 0, + LayoutOrder = 3, + TextTransparency = if props.Disabled then 0.5 else 0, + Size = UDim2.fromOffset(0, HEADER_HEIGHT), + Text = props.Text, + TextColor3 = theme:GetColor(Enum.StudioStyleGuideColor.MainText), + TextXAlignment = Enum.TextXAlignment.Left, + }, { + UIPadding = e("UIPadding", { + PaddingLeft = UDim.new(0, 4), + }), + }), + }) +end + +return Header diff --git a/src/Components/Collapsible/init.luau b/src/Components/Collapsible/init.luau new file mode 100644 index 0000000..0505a97 --- /dev/null +++ b/src/Components/Collapsible/init.luau @@ -0,0 +1,177 @@ +--[=[ + @class Collapsible + A simple collapsible component that reveals content when clicked. + + | Dark | Light | + | - | - | + | ![Dark](/StudioComponents/components/collapsible/dark.png) | ![Light](/StudioComponents/components/collapsible/light.png) | + + ```lua + local function MySettingsPage() + return React.createElement(StudioComponents.Background, { + }, { + General = e(StudioComponents.Collapsible, { + Title = "General", + IsBlockStyle = true, + LayoutOrder = 1 + }, { + ... content here + }), + + Graphics = e(StudioComponents.Collapsible, { + Title = "Graphics", + IsBlockStyle = true, + LayoutOrder = 2 + }, { + ... content here + }), + + Audio = e(StudioComponents.Collapsible, { + Title = "Audio", + IsBlockStyle = true, + LayoutOrder = 3 + }, { + ... content here + }), + }) + end + ``` + + Collapsibles can also be nested. Pass custom children in at any level or continue to pass `Collapsisble`s as children to create a tree structure (like a file explorer): + ```lua + local function MyPluginExplorer() + return e(StudioComponents.Collapsible, { + Title = "Tree Level 1", + IsBlockStyle = true, + }, { + Workspace = e(StudioComponents.Collapsible, { + Title = "Workspace", + Icon = { Image = "path.to.icon" }, + IsBlockStyle = true, + }, { + -- fill with Workspace contents here + }), + + Players = e(StudioComponents.Collapsible, { + Title = "Players", + Icon = { Image = "path.to.icon" }, + IsBlockStyle = true, + }, { + -- fill with Players here + }), + + ReplicatedStorage = e(StudioComponents.Collapsible, { + Title = "ReplicatedStorage", + Icon = { Image = "path.to.icon" }, + IsBlockStyle = true, + }, { + -- A collapsible within a collapsible! + Assets = e(StudioComponents.Collapsible, { + Title = "Assets", + }, { + -- fill with Assets contents here + }) + }), + + Lighting = e(StudioComponents.Collapsible, { + Title = "Lighting", + Icon = { Image = "path.to.icon" }, + Disabled = true, + IsBlockStyle = true, + }, { + -- fill with Lighting contents here + }), + }) + end + ``` +]=] + +local React = require("@pkg/@jsdotlua/react") + +local e = React.createElement +local CommonProps = require(script.Parent.Parent.CommonProps) +local Header = require(script.Header) +local useToggleState = require("../Hooks/useToggleState") + +--[=[ + @within Collapsible + @interface IconProps + + @field Image string + @field UseThemeColor boolean? +]=] + +--[=[ + @within Collapsible + @interface Props + @tag Component Props + + @field ... CommonProps + @field Title string + @field IsBlockStyle boolean? + @field KeepContentMounted boolean? + @field Icon IconProps? + @field Layout React.Element? + @field ContentPadding React.Element? +]=] + +local function Collapsible(props: CommonProps.T & { + Title: string, + IsBlockStyle: boolean?, + KeepContentMounted: boolean?, + Icon: { + Image: string?, + UseThemeColor: boolean?, + }, + Layout: React.Element?, + ContentPadding: React.Element?, + children: React.ReactNode, +}) + local collapsible = useToggleState(false) + + return e("Frame", { + BackgroundTransparency = 1, + Size = UDim2.fromScale(1, 0), + AutomaticSize = Enum.AutomaticSize.Y, + LayoutOrder = props.LayoutOrder, + }, { + UIListLayout = e("UIListLayout", { + Padding = UDim.new(0, 0), + SortOrder = Enum.SortOrder.LayoutOrder, + }), + + Header = e(Header, { + OnActivated = collapsible.toggle, + IsBlockStyle = props.IsBlockStyle or false, + Expanded = collapsible.on, + Disabled = props.Disabled, + Icon = props.Icon, + Text = props.Title, + LayoutOrder = 1, + }), + + Content = if not props.KeepContentMounted and not (collapsible.on and props.Disabled ~= true) + then nil + else e("Frame", { + BackgroundTransparency = 1, + Position = UDim2.fromOffset(30, 0), + AutomaticSize = Enum.AutomaticSize.Y, + Size = UDim2.fromScale(1, 0), + AnchorPoint = Vector2.new(0, 0), + Visible = if props.KeepContentMounted then (collapsible.on and props.Disabled ~= true) else true, + LayoutOrder = 2, + }, { + UIListLayout = props.Layout or e("UIListLayout", { + Padding = UDim.new(0, 5), + SortOrder = Enum.SortOrder.LayoutOrder, + }), + + UIPadding = props.ContentPadding or e("UIPadding", { + PaddingLeft = UDim.new(0, 10), + PaddingTop = UDim.new(0, 5), + }), + }, props.children), + }) +end + +return Collapsible diff --git a/src/Stories/Collapsible.story.luau b/src/Stories/Collapsible.story.luau new file mode 100644 index 0000000..4916b39 --- /dev/null +++ b/src/Stories/Collapsible.story.luau @@ -0,0 +1,84 @@ +local Checkbox = require("../Components/Checkbox") +local React = require("@pkg/@jsdotlua/react") + +local Collapsible = require("../Components/Collapsible") +local createStory = require("./Helpers/createStory") + +local e = React.createElement + +local function Content() + local checked, setChecked = React.useState(false) + + return e(Checkbox, { + Value = checked, + Label = "Test", + OnChanged = function() + setChecked(not checked) + end, + }) +end + +local function RecursiveCollapsible(props: { number: number }) + return e(Collapsible, { + Title = if props.number == 1 then "Recursive Collapsible (Tree)" else `Collapsible {props.number - 1}`, + LayoutOrder = 3, + }, e(RecursiveCollapsible, { number = props.number + 1 })) +end + +local function Story() + return e("Frame", { + Position = UDim2.fromScale(0.5, 0.5), + AnchorPoint = Vector2.new(0.5, 0.5), + Size = UDim2.fromScale(1, 1), + BackgroundTransparency = 1, + BackgroundColor3 = Color3.fromRGB(255, 255, 255), + BorderSizePixel = 0, + }, { + UIListLayout = e("UIListLayout", { + Padding = UDim.new(0, 30), + SortOrder = Enum.SortOrder.LayoutOrder, + }), + + BlockCollapsible = e(Collapsible, { + Title = "Block Collapsible", + LayoutOrder = 1, + IsBlockStyle = true, + }, { + Content = e(Content), + }), + + CollapsibleWithIcon = e(Collapsible, { + Title = "Block Collapsible w/ Icon", + LayoutOrder = 1, + Icon = { + Image = "rbxasset://textures/TerrainTools/icon_shape_cube.png", + }, + IsBlockStyle = true, + }, { + Content = e(Content), + }), + + Collapsible = e(Collapsible, { + Title = "Collapsible", + LayoutOrder = 2, + }, { + EnabledCollapsible = e(Collapsible, { + Title = "Enabled Collapsible", + }, { + Content = e(Content), + }), + + DisabledCollapsible = e(Collapsible, { + Title = "Disabled Collapsible", + LayoutOrder = 2, + Disabled = true, + }), + }), + + CollapsibleTree = e(RecursiveCollapsible, { + number = 1, + }), + }) +end + +return createStory(Story) From 8da14666739cdbfe95838f995cfb926dd259eafd Mon Sep 17 00:00:00 2001 From: Nidoxs Date: Thu, 5 Jun 2025 23:09:46 +0100 Subject: [PATCH 3/9] Improve `useTheme` return type --- src/Hooks/useTheme.luau | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hooks/useTheme.luau b/src/Hooks/useTheme.luau index 0affba9..126a181 100644 --- a/src/Hooks/useTheme.luau +++ b/src/Hooks/useTheme.luau @@ -26,7 +26,7 @@ local React = require("@pkg/@jsdotlua/react") local ThemeContext = require("../Contexts/ThemeContext") -local function useTheme() +local function useTheme(): StudioTheme local theme = React.useContext(ThemeContext) local studioTheme, setStudioTheme = React.useState(Studio.Theme) From d63b6d5a0783e9f6f28d65548ce88c6ce7d04f67 Mon Sep 17 00:00:00 2001 From: Nidoxs Date: Thu, 5 Jun 2025 23:10:06 +0100 Subject: [PATCH 4/9] Add `dark.png` and `light.png` `Collapsible` previews for docs --- .../static/components/collapsible/dark.png | Bin 0 -> 28419 bytes .../static/components/collapsible/light.png | Bin 0 -> 21932 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .moonwave/static/components/collapsible/dark.png create mode 100644 .moonwave/static/components/collapsible/light.png diff --git a/.moonwave/static/components/collapsible/dark.png b/.moonwave/static/components/collapsible/dark.png new file mode 100644 index 0000000000000000000000000000000000000000..8fd42456de024f178059b49f7ca650b037ef658d GIT binary patch literal 28419 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 literal 0 HcmV?d00001 diff --git a/.moonwave/static/components/collapsible/light.png b/.moonwave/static/components/collapsible/light.png new file mode 100644 index 0000000000000000000000000000000000000000..b946b970a9c298c1b98e4e1ce94953af0f14341b GIT binary patch literal 21932 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 JJjLsQ{|ma09!>xN literal 0 HcmV?d00001 From 0836afefda9e5ab25b8e476905aa0d28bcf2f513 Mon Sep 17 00:00:00 2001 From: Nidoxs Date: Thu, 5 Jun 2025 23:10:19 +0100 Subject: [PATCH 5/9] Fix background colour for dark previews --- .moonwave/custom.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.moonwave/custom.css b/.moonwave/custom.css index 418ea09..79bdd17 100644 --- a/.moonwave/custom.css +++ b/.moonwave/custom.css @@ -1,5 +1,5 @@ td:nth-child(1) { - background-color: rgb(46, 46, 46); + background-color: rgb(60, 60, 61); } td:nth-child(2) { From 0a6eb5625f0428b2eca9643b90f5d2d736719a77 Mon Sep 17 00:00:00 2001 From: Nidoxs Date: Thu, 5 Jun 2025 23:10:34 +0100 Subject: [PATCH 6/9] Add to changelog (fix version of previous version) --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 990dc40..6feefbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog -## Unreleased +## 1.4.0 +- Components: `Collapsible` (for containing content in an expandable section) +- Hooks: `useToggleState` + +## 1.3.0 - Added an optional `DisplayTitle` prop to `TabContainer` children tabs to allow displaying custom text on tabs ## 1.2.0 From 47fed304eeed8b580c468ec1bbbcadc37fe9d6d4 Mon Sep 17 00:00:00 2001 From: Nidoxs Date: Thu, 5 Jun 2025 23:10:45 +0100 Subject: [PATCH 7/9] Add `Collapsible` to `moonwave.toml` classes --- moonwave.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moonwave.toml b/moonwave.toml index 1493673..25b6d14 100644 --- a/moonwave.toml +++ b/moonwave.toml @@ -22,7 +22,7 @@ classes = ["Constants", "CommonProps"] [[classOrder]] section = "Components" collapsed = false -classes = ["Background", "Button", "Checkbox", "ColorPicker", "DatePicker", "Dropdown", "DropShadowFrame", "Label", "LoadingDots", "MainButton", "NumberSequencePicker", "NumericInput", "PluginProvider", "ProgressBar", "RadioButton", "ScrollFrame", "Slider", "Splitter", "TabContainer", "TextInput"] +classes = ["Background", "Button", "Checkbox", "Collapsible", "ColorPicker", "DatePicker", "Dropdown", "DropShadowFrame", "Label", "LoadingDots", "MainButton", "NumberSequencePicker", "NumericInput", "PluginProvider", "ProgressBar", "RadioButton", "ScrollFrame", "Slider", "Splitter", "TabContainer", "TextInput"] [[classOrder]] section = "Hooks" From 1ea81ceb3a8df82a45d4b96affde48670ca6fb1a Mon Sep 17 00:00:00 2001 From: Nidoxs Date: Thu, 5 Jun 2025 23:12:38 +0100 Subject: [PATCH 8/9] Add `Collapsible` to library exports --- src/init.luau | 1 + 1 file changed, 1 insertion(+) diff --git a/src/init.luau b/src/init.luau index 233dfbc..38726e2 100644 --- a/src/init.luau +++ b/src/init.luau @@ -4,6 +4,7 @@ return { Background = require("./Components/Background"), Button = require("./Components/Button"), Checkbox = require("./Components/Checkbox"), + Collapsible = require("./Components/Collapsible"), ColorPicker = require("./Components/ColorPicker"), DatePicker = require("./Components/DatePicker"), Dropdown = require("./Components/Dropdown"), From 7e2867662892fe3480f85d1199891aaaa6ff692f Mon Sep 17 00:00:00 2001 From: Nidoxs Date: Thu, 5 Jun 2025 23:22:10 +0100 Subject: [PATCH 9/9] Refine moonwave doc comment for `Collapsible` --- src/Components/Collapsible/init.luau | 69 ++++++---------------------- 1 file changed, 15 insertions(+), 54 deletions(-) diff --git a/src/Components/Collapsible/init.luau b/src/Components/Collapsible/init.luau index 0505a97..bcac112 100644 --- a/src/Components/Collapsible/init.luau +++ b/src/Components/Collapsible/init.luau @@ -8,14 +8,23 @@ ```lua local function MySettingsPage() - return React.createElement(StudioComponents.Background, { - }, { + return React.createElement(StudioComponents.Background, {}, { General = e(StudioComponents.Collapsible, { Title = "General", + Icon = { + Image = "path.to.icon", + UseThemeColor = false, + }, IsBlockStyle = true, LayoutOrder = 1 }, { - ... content here + -- general settings here + + -- collapsibles can also be nested to create tree structures + AnotherCollapsible = e(StudioComponents.Collapsible, { + Title = "Another Collapsible", + IsBlockStyle = false, -- for this nested collapsible let's not use the block style + }), }), Graphics = e(StudioComponents.Collapsible, { @@ -23,7 +32,7 @@ IsBlockStyle = true, LayoutOrder = 2 }, { - ... content here + -- graphics settings here }), Audio = e(StudioComponents.Collapsible, { @@ -31,55 +40,7 @@ IsBlockStyle = true, LayoutOrder = 3 }, { - ... content here - }), - }) - end - ``` - - Collapsibles can also be nested. Pass custom children in at any level or continue to pass `Collapsisble`s as children to create a tree structure (like a file explorer): - ```lua - local function MyPluginExplorer() - return e(StudioComponents.Collapsible, { - Title = "Tree Level 1", - IsBlockStyle = true, - }, { - Workspace = e(StudioComponents.Collapsible, { - Title = "Workspace", - Icon = { Image = "path.to.icon" }, - IsBlockStyle = true, - }, { - -- fill with Workspace contents here - }), - - Players = e(StudioComponents.Collapsible, { - Title = "Players", - Icon = { Image = "path.to.icon" }, - IsBlockStyle = true, - }, { - -- fill with Players here - }), - - ReplicatedStorage = e(StudioComponents.Collapsible, { - Title = "ReplicatedStorage", - Icon = { Image = "path.to.icon" }, - IsBlockStyle = true, - }, { - -- A collapsible within a collapsible! - Assets = e(StudioComponents.Collapsible, { - Title = "Assets", - }, { - -- fill with Assets contents here - }) - }), - - Lighting = e(StudioComponents.Collapsible, { - Title = "Lighting", - Icon = { Image = "path.to.icon" }, - Disabled = true, - IsBlockStyle = true, - }, { - -- fill with Lighting contents here + -- audio settings here }), }) end @@ -109,7 +70,7 @@ local useToggleState = require("../Hooks/useToggleState") @field ... CommonProps @field Title string @field IsBlockStyle boolean? - @field KeepContentMounted boolean? + @field KeepContentMounted boolean? -- if true, uses `Visible` based rendering instead of re-mounting @field Icon IconProps? @field Layout React.Element? @field ContentPadding React.Element?