Skip to content

Commit

Permalink
Add theme browser UI
Browse files Browse the repository at this point in the history
  • Loading branch information
vocksel committed Jan 12, 2024
1 parent 7809f6d commit b90fbd6
Show file tree
Hide file tree
Showing 11 changed files with 670 additions and 52 deletions.
42 changes: 42 additions & 0 deletions plugin/src/applyTheme.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
local Root = script:FindFirstAncestor("rbxtheme")

local getThemeColors = require(Root.getThemeColors)
local types = require(Root.types)

local function applyTheme(theme: types.ExtensionTheme, studio: Studio): string?
local colors = getThemeColors(theme)
local problems = {}

if colors and colors.found then
for name, color in colors.found do
-- Discard the alpha component of the hexcode
if #color - 1 > 6 then
local colorNoAlpha = color:sub(1, 7)

-- TODO: Blend colors with the background to get rid of the
-- alpha component. That way we don't need to warn the user
table.insert(problems, `{name} uses unsupported alpha value. Truncating {color} to {colorNoAlpha}`)

color = colorNoAlpha
end

local success, result = pcall(function()
studio[name] = Color3.fromHex(color)
end)

if not success then
table.insert(problems, `Failed to set {name}: {result}`)
end
end
end

if #problems > 0 then
local problemsStr = ""
for index, problem in problems do
problemsStr ..= `{index}. {problem}\n`
end
return `{theme.name} was applied, but not all colors were set:\n{problemsStr}`
end
end

return applyTheme
76 changes: 39 additions & 37 deletions plugin/src/components/App.lua
Original file line number Diff line number Diff line change
@@ -1,53 +1,55 @@
local Root = script:FindFirstAncestor("rbxtheme")

local React = require(Root.Packages.React)
local fetchVisualStudioExtensions = require(Root.fetchVisualStudioExtensions)
local Sift = require(Root.Packages.Sift)
local types = require(Root.types)
local Home = require(Root.Components.Home)
local ThemeDetails = require(Root.Components.ThemeDetails)

type VsMarketplaceExtension = fetchVisualStudioExtensions.VsMarketplaceExtension

local useEffect = React.useState
local useCallback = React.useCallback
local useState = React.useState

type PublishedExtension = types.PublishedExtension
type ExtensionTheme = types.ExtensionTheme

export type View = "Home" | "ThemeDetails"

export type Props = {
plugin: Plugin,
}

local function App(_props: Props)
local extensions, setExtensions = useState({} :: { VsMarketplaceExtension })

useEffect(function()
fetchVisualStudioExtensions({
searchTerm = "theme",
}):andThen(function(newExtensions)
print("extensions", newExtensions)
setExtensions(newExtensions)
end)
local view, setView = useState("Home" :: View)
local viewParams, setViewParams = useState({})

local onBack = useCallback(function()
setViewParams({})
setView("Home")
end, {})

local onViewExtension = useCallback(function(extension: PublishedExtension, themes: { ExtensionTheme })
setViewParams({
extension = extension,
themes = themes,
})
setView("ThemeDetails")
end, {})

return React.createElement("Frame", {
Size = UDim2.fromScale(1, 1),
BackgroundTransparency = 1,
}, {
Layout = React.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
}),

SearchForm = React.createElement("Frame", {
LayoutOrder = 1,
Size = UDim2.fromScale(1, 1),
BackgroundTransparency = 1,
}, {
Input = React.createElement("TextBox", {
PlaceholderText = "Search themes...",
}),

ErrorMessage = React.createElement("TextLabel", {}),
}),

Extensions = React.createElement("TextLabel", {
LayoutOrder = 2,
Text = tostring(extensions),
}),
return React.createElement("Folder", nil, {
Home = if view == "Home"
then React.createElement(Home, {
onViewExtension = onViewExtension,
})
else nil,

ThemeDetails = if view == "ThemeDetails"
then React.createElement(
ThemeDetails,
Sift.Dictionary.join(viewParams, {
onBack = onBack,
})
)
else nil,
})
end

Expand Down
14 changes: 0 additions & 14 deletions plugin/src/components/App.story.lua

This file was deleted.

123 changes: 123 additions & 0 deletions plugin/src/components/ExtensionsList.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
local Root = script:FindFirstAncestor("rbxtheme")

local React = require(Root.Packages.React)
local types = require(Root.types)
local getLayoutOrder = require(Root.Components.getLayoutOrder)

type PublishedExtension = types.PublishedExtension

export type Props = {
extensions: { PublishedExtension },
onView: (extension: PublishedExtension) -> (),
LayoutOrder: number?,
}

local ACTION_BUTTON_WIDTH = 120
local PADDING = UDim.new(0, 8)

local function ExtensionsList(props: Props)
local children = {
Layout = React.createElement("UIListLayout", {
Padding = PADDING,
SortOrder = Enum.SortOrder.LayoutOrder,
}),
}

for i, extension in props.extensions do
local isEven = i % 2 == 0
local latestVersion = if extension.versions then extension.versions[1] else nil

children[extension.extensionName] = React.createElement("Frame", {
LayoutOrder = i,
BackgroundTransparency = if isEven then 1 else 0.2,
BackgroundColor3 = Color3.fromRGB(100, 100, 100),
BorderSizePixel = 0,
AutomaticSize = Enum.AutomaticSize.Y,
Size = UDim2.fromScale(1, 0),
}, {
Layout = React.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
FillDirection = Enum.FillDirection.Horizontal,
VerticalAlignment = Enum.VerticalAlignment.Center,
}),

Padding = React.createElement("UIPadding", {
PaddingTop = PADDING,
PaddingRight = PADDING,
PaddingBottom = PADDING,
PaddingLeft = PADDING,
}),

Main = React.createElement("Frame", {
LayoutOrder = getLayoutOrder(),
Size = UDim2.fromScale(1, 0) - UDim2.fromOffset(ACTION_BUTTON_WIDTH, 0),
AutomaticSize = Enum.AutomaticSize.Y,
BackgroundTransparency = 1,
}, {
Layout = React.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = PADDING,
}),

Name = React.createElement("TextLabel", {
LayoutOrder = getLayoutOrder(),
AutomaticSize = Enum.AutomaticSize.XY,
BackgroundTransparency = 1,
Text = `{extension.displayName} v{latestVersion.version}`,
TextSize = 16,
Font = Enum.Font.GothamMedium,
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Top,
TextColor3 = Color3.fromRGB(255, 255, 255),
TextTruncate = Enum.TextTruncate.AtEnd,
}),

Publisher = React.createElement("TextLabel", {
LayoutOrder = getLayoutOrder(),
Text = extension.publisher.publisherName,
TextSize = 14,
TextColor3 = Color3.fromRGB(200, 200, 200),
AutomaticSize = Enum.AutomaticSize.XY,
BackgroundTransparency = 1,
Font = Enum.Font.GothamMedium,
TextXAlignment = Enum.TextXAlignment.Left,
TextYAlignment = Enum.TextYAlignment.Top,
}),
}),

Action = React.createElement("TextButton", {
LayoutOrder = getLayoutOrder(),
Text = "View",
TextSize = 14,
TextColor3 = Color3.fromRGB(255, 255, 255),
BorderSizePixel = 0,
Font = Enum.Font.GothamMedium,
Size = UDim2.fromOffset(ACTION_BUTTON_WIDTH, 0),
AutomaticSize = Enum.AutomaticSize.Y,
[React.Event.Activated] = function()
props.onView(extension)
end,
}, {
Padding = React.createElement("UIPadding", {
PaddingTop = PADDING,
PaddingRight = PADDING,
PaddingBottom = PADDING,
PaddingLeft = PADDING,
}),

Corner = React.createElement("UICorner", {
CornerRadius = PADDING,
}),
}),
})
end

return React.createElement("Frame", {
LayoutOrder = props.LayoutOrder,
AutomaticSize = Enum.AutomaticSize.Y,
Size = UDim2.fromScale(1, 0),
BackgroundTransparency = 1,
}, children)
end

return ExtensionsList
Loading

0 comments on commit b90fbd6

Please sign in to comment.