diff --git a/foreman.toml b/foreman.toml index 2b14f229..53389e03 100644 --- a/foreman.toml +++ b/foreman.toml @@ -7,5 +7,5 @@ selene = { source = "Kampfkarren/selene", version = "0.27.1" } stylua = { source = "JohnnyMorganz/StyLua", version = "0.20.0" } tarmac = { source = "Roblox/tarmac", version = "0.7.0" } wally = { source = "UpliftGames/wally", version = "0.3.2" } -luau-lsp = { source = "JohnnyMorganz/luau-lsp", version = "1.32.3" } +luau-lsp = { source = "JohnnyMorganz/luau-lsp", version = "1.34.0" } wally-package-types = { source = "JohnnyMorganz/wally-package-types", version = "1.3.2" } diff --git a/src/Forms/InputField.luau b/src/Forms/InputField.luau index 8ecb47b1..fc93de21 100644 --- a/src/Forms/InputField.luau +++ b/src/Forms/InputField.luau @@ -27,7 +27,7 @@ local function InputField(providedProps: Props) local ref = React.useRef(nil :: TextBox?) local text, setText = React.useState("") - local isValid, setIsValid = React.useState(false) + local isValid, setIsValid = React.useState(true) local theme = useTheme() local onFocusLost = React.useCallback( @@ -41,7 +41,9 @@ local function InputField(providedProps: Props) end end, { + text, isValid, + props.onFocusLost, props.onSubmit, } :: { unknown } ) @@ -49,7 +51,9 @@ local function InputField(providedProps: Props) local onTextChanged = React.useCallback(function(rbx: TextBox) local newText = rbx.Text - if newText == text and newText ~= "" then + newText = newText:gsub("$%s+", ""):gsub("%s+^", "") + + if newText == text or newText == "" then return end diff --git a/src/Storybook/StoryControls.luau b/src/Storybook/StoryControls.luau index d30d51cf..e689f821 100644 --- a/src/Storybook/StoryControls.luau +++ b/src/Storybook/StoryControls.luau @@ -1,10 +1,12 @@ local React = require("@pkg/React") +local Sift = require("@pkg/Sift") local Checkbox = require("@root/Forms/Checkbox") local Dropdown = require("@root/Forms/Dropdown") local InputField = require("@root/Forms/InputField") local useTheme = require("@root/Common/useTheme") +local useMemo = React.useMemo local e = React.createElement type Props = { @@ -16,72 +18,131 @@ type Props = { local function StoryControls(props: Props) local theme = useTheme() - local controls: { [string]: React.Node } = {} - for key, value in props.controls do + local sortedControls: { { name: string, value: any } } = useMemo(function() + local result = {} + + for _, entry in Sift.Dictionary.entries(props.controls) do + table.insert(result, { + name = entry[1], + value = entry[2], + }) + end + + return Sift.List.sort(result, function(a, b) + return a.name < b.name + end) + end, { props.controls }) + + local controlElements: { [string]: React.Node } = {} + for index, control in sortedControls do local function setControl(newValue: any) - props.setControl(key, newValue) + local newValueAsNum = tonumber(newValue) + if newValueAsNum then + newValue = newValueAsNum + end + + props.setControl(control.name, newValue) end + local controlType = typeof(control.value) local option: React.Node - if typeof(value) == "boolean" then + if controlType == "boolean" then option = React.createElement(Checkbox, { - initialState = value, + initialState = control.value, onStateChange = setControl, }) - elseif typeof(value) == "table" then + elseif controlType == "table" then option = React.createElement(Dropdown, { - default = value[1], - options = value, + default = control.value[1], + options = control.value, onOptionChange = setControl, }) - else + elseif controlType == "number" or controlType == "string" then option = React.createElement(InputField, { - placeholder = value, - onTextChange = setControl, + placeholder = control.value, onSubmit = setControl, }) + else + option = React.createElement("TextLabel", { + Text = `ERR: Controls of type "{controlType}" are unsupported`, + AutomaticSize = Enum.AutomaticSize.XY, + BackgroundTransparency = 1, + Font = theme.font, + TextColor3 = theme.alert, + TextSize = theme.textSize, + TextXAlignment = Enum.TextXAlignment.Left, + TextYAlignment = Enum.TextYAlignment.Center, + TextTruncate = Enum.TextTruncate.AtEnd, + }) end - controls[key] = e("Frame", { - BackgroundTransparency = 1, + controlElements[control.name] = e("Frame", { + LayoutOrder = index, + BackgroundColor3 = theme.background, + BackgroundTransparency = if index % 2 == 0 then 1 else 0, + BorderSizePixel = 0, Size = UDim2.fromScale(1, 0), AutomaticSize = Enum.AutomaticSize.Y, }, { Layout = React.createElement("UIListLayout", { FillDirection = Enum.FillDirection.Horizontal, + SortOrder = Enum.SortOrder.LayoutOrder, + Padding = theme.padding, + }), + + Padding = e("UIPadding", { + PaddingTop = theme.padding, + PaddingRight = theme.padding, + PaddingBottom = theme.padding, + PaddingLeft = theme.padding, }), Name = e("TextLabel", { - Text = key, - Size = UDim2.fromScale(1 / 2, 0), + LayoutOrder = 1, + Text = control.name, + Size = UDim2.fromScale(1 / 4, 0), AutomaticSize = Enum.AutomaticSize.Y, BackgroundTransparency = 1, Font = theme.font, TextColor3 = theme.text, TextSize = theme.textSize, TextXAlignment = Enum.TextXAlignment.Left, - TextYAlignment = Enum.TextYAlignment.Top, + TextYAlignment = Enum.TextYAlignment.Center, + TextTruncate = Enum.TextTruncate.AtEnd, }), Option = e("Frame", { + LayoutOrder = 2, BackgroundTransparency = 1, - Size = UDim2.fromScale(1 / 2, 0), + Size = UDim2.fromScale(1, 0), AutomaticSize = Enum.AutomaticSize.Y, + }, { + Flex = e("UIFlexItem", { + FlexMode = Enum.UIFlexMode.Shrink, + }), }, option), }) end return e("Frame", { - BackgroundTransparency = 1, + BackgroundTransparency = 0.4, + BackgroundColor3 = theme.sidebar, + BorderSizePixel = 0, LayoutOrder = props.layoutOrder, - Size = UDim2.fromScale(1, 0), - AutomaticSize = Enum.AutomaticSize.Y, + Size = UDim2.fromScale(1, 1), }, { Layout = e("UIListLayout", { SortOrder = Enum.SortOrder.LayoutOrder, Padding = theme.padding, }), + Padding = e("UIPadding", { + PaddingTop = theme.padding, + PaddingRight = theme.padding, + PaddingBottom = theme.padding, + PaddingLeft = theme.padding, + }), + Title = e("TextLabel", { LayoutOrder = 1, Text = "Controls", @@ -101,12 +162,9 @@ local function StoryControls(props: Props) BackgroundTransparency = 1, }, { Layout = e("UIListLayout", { - SortOrder = Enum.SortOrder.Name, - Padding = theme.padding, + SortOrder = Enum.SortOrder.LayoutOrder, }), - - ControlsFragment = React.createElement(React.Fragment, nil, controls), - }), + }, controlElements), }) end diff --git a/src/Storybook/StoryControls.story.luau b/src/Storybook/StoryControls.story.luau index 24c496fd..ff871bc4 100644 --- a/src/Storybook/StoryControls.story.luau +++ b/src/Storybook/StoryControls.story.luau @@ -19,6 +19,8 @@ return { "Option 2", "Option 3", }, + onlyTwentyCharacters = "OnlyTwentyCharacters", + badControlType = function() end, }, setControl = function() end, }), diff --git a/src/Storybook/StoryPreview.luau b/src/Storybook/StoryPreview.luau index 34d9551f..4f7ffdb6 100644 --- a/src/Storybook/StoryPreview.luau +++ b/src/Storybook/StoryPreview.luau @@ -16,11 +16,12 @@ local defaultProps = { } export type Props = { - layoutOrder: number, story: types.Story, - ref: any, controls: { [string]: any }, storyModule: ModuleScript, + + ref: any, + layoutOrder: number?, } type InternalProps = Props & typeof(defaultProps) diff --git a/src/Storybook/StoryView.luau b/src/Storybook/StoryView.luau index 2e79c932..3359a9c8 100644 --- a/src/Storybook/StoryView.luau +++ b/src/Storybook/StoryView.luau @@ -183,17 +183,9 @@ local function StoryView(props: Props) onResize = onControlsResized, }, { ScrollingFrame = e(ScrollingFrame, { - LayoutOrder = 2, - BackgroundTransparency = 0.4, + BackgroundTransparency = 1, BackgroundColor3 = theme.sidebar, }, { - Padding = e("UIPadding", { - PaddingTop = theme.padding, - PaddingRight = theme.padding, - PaddingBottom = theme.padding, - PaddingLeft = theme.padding, - }), - StoryControls = e(StoryControls, { controls = controls, setControl = setControl,