Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .moonwave/custom.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
td:nth-child(1) {
background-color: rgb(46, 46, 46);
background-color: rgb(60, 60, 61);
}

td:nth-child(2) {
Expand Down
Binary file added .moonwave/static/components/collapsible/dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .moonwave/static/components/collapsible/light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion moonwave.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
109 changes: 109 additions & 0 deletions src/Components/Collapsible/Header.luau
Original file line number Diff line number Diff line change
@@ -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
138 changes: 138 additions & 0 deletions src/Components/Collapsible/init.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
--[=[
@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",
Icon = {
Image = "path.to.icon",
UseThemeColor = false,
},
IsBlockStyle = true,
LayoutOrder = 1
}, {
-- 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, {
Title = "Graphics",
IsBlockStyle = true,
LayoutOrder = 2
}, {
-- graphics settings here
}),

Audio = e(StudioComponents.Collapsible, {
Title = "Audio",
IsBlockStyle = true,
LayoutOrder = 3
}, {
-- audio settings 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? -- if true, uses `Visible` based rendering instead of re-mounting
@field Icon IconProps?
@field Layout React.Element<UILayout>?
@field ContentPadding React.Element<UIPadding>?
]=]

local function Collapsible(props: CommonProps.T & {
Title: string,
IsBlockStyle: boolean?,
KeepContentMounted: boolean?,
Icon: {
Image: string?,
UseThemeColor: boolean?,
},
Layout: React.Element<UILayout>?,
ContentPadding: React.Element<UIPadding>?,
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
2 changes: 1 addition & 1 deletion src/Hooks/useTheme.luau
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
33 changes: 33 additions & 0 deletions src/Hooks/useToggleState.luau
Original file line number Diff line number Diff line change
@@ -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
Loading