Skip to content

Commit

Permalink
Good foundation
Browse files Browse the repository at this point in the history
  • Loading branch information
vocksel committed Nov 23, 2024
1 parent 6c127b1 commit 1f17252
Show file tree
Hide file tree
Showing 8 changed files with 354 additions and 66 deletions.
75 changes: 49 additions & 26 deletions src/TreeView/TreeNode.luau
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
local React = require("@pkg/React")

local Sprite = require("@root/Common/Sprite")
local treeViewTypes = require("@root/TreeView/types")
local types = require("@root/TreeView/types")
local useTreeNodeIcon = require("@root/TreeView/useTreeNodeIcon")
local useTheme = require("@root/Common/useTheme")
local assets = require("@root/assets")

local useCallback = React.useCallback
local useMemo = React.useMemo
local useState = React.useState

type TreeNode = treeViewTypes.TreeNode
type TreeNode = types.TreeNode

export type Props = {
node: TreeNode,
Expand All @@ -18,12 +20,15 @@ export type Props = {

local function TreeNode(props: Props)
local theme = useTheme()
local icon, iconColor = useTreeNodeIcon(props.node.icon)

local isExpanded, setIsExpanded = useState(props.node.isExpanded)

local children = useMemo(function()
local elements: { [string]: React.Node } = {}
if props.node.children then
for index, child in props.node.children do
elements[child.id] = React.createElement(TreeNode, {
elements[child.label] = React.createElement(TreeNode, {
layoutOrder = index,
node = child,
})
Expand All @@ -36,18 +41,23 @@ local function TreeNode(props: Props)
if props.onActivated then
props.onActivated()
end

setIsExpanded(function(prev)
return not prev
end)
end, { props.onActivated })

return React.createElement("ImageButton", {
LayoutOrder = props.layoutOrder,
AutoButtonColor = false,
AutomaticSize = Enum.AutomaticSize.XY,
ClipsDescendants = true,

BackgroundTransparency = 1,
[React.Event.Activated] = onActivated,
}, {
Layout = React.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = theme.paddingSmall,
}),

Node = React.createElement("Frame", {
Expand All @@ -62,9 +72,14 @@ local function TreeNode(props: Props)
HorizontalFlex = Enum.UIFlexAlignment.Fill,
}),

Padding = React.createElement("UIPadding", {
PaddingTop = theme.paddingSmall,
PaddingBottom = theme.paddingSmall,
}),

Icon = React.createElement(Sprite, {
image = assets.Component,
color = theme.story,
image = icon,
color = iconColor,
layoutOrder = 1,
size = UDim2.fromOffset(16, 16),
}),
Expand All @@ -85,35 +100,43 @@ local function TreeNode(props: Props)
}),
}),

Toggle = if props.node.children
then React.createElement("ImageButton", {
Toggle = if #props.node.children > 0
then React.createElement("Frame", {
LayoutOrder = 3,
BackgroundTransparency = 1,
Size = UDim2.fromOffset(16, 16),
AutomaticSize = Enum.AutomaticSize.XY,
}, {
Icon = React.createElement(Sprite, {
image = assets.ChevronRight,
color = theme.text,
size = UDim2.fromScale(1, 1),
RotationWrapper = React.createElement("Frame", {
BackgroundTransparency = 1,
AutomaticSize = Enum.AutomaticSize.XY,
Rotation = if isExpanded then 90 else 0,
}, {
Icon = React.createElement(Sprite, {
image = assets.ChevronRight,
color = theme.text,
size = UDim2.fromOffset(16, 16),
}),
}),
})
else nil,
}),

Children = React.createElement("Frame", {
LayoutOrder = 2,
AutomaticSize = Enum.AutomaticSize.XY,
BackgroundTransparency = 1,
}, {
Padding = React.createElement("UIPadding", {
PaddingLeft = theme.padding,
}),
Children = if isExpanded
then React.createElement("Frame", {
LayoutOrder = 2,
AutomaticSize = Enum.AutomaticSize.XY,
BackgroundTransparency = 1,
}, {

Layout = React.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = theme.paddingSmall,
}),
}, children),
Padding = React.createElement("UIPadding", {
PaddingLeft = theme.padding,
}),

Layout = React.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
}),
}, children)
else nil,
})
end

Expand Down
22 changes: 10 additions & 12 deletions src/TreeView/TreeView.luau
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
local React = require("@pkg/React")

local useTheme = require("@root/Common/useTheme")
local treeViewTypes = require("@root/TreeView/types")
local types = require("@root/TreeView/types")
local TreeNode = require("@root/TreeView/TreeNode")
local createTreeNodesFromPartials = require("@root/TreeView/createTreeNodesFromPartials")

local useCallback = React.useCallback
local useMemo = React.useMemo

type TreeNode = treeViewTypes.TreeNode
type Tree = treeViewTypes.Tree
type PartialTreeNode = types.PartialTreeNode
type TreeNode = types.TreeNode
type Tree = types.Tree

export type Props = {
roots: { TreeNode },
roots: { PartialTreeNode },
expandedNodes: { TreeNode }?,
onActivated: ((node: TreeNode) -> ())?,
filter: ((node: TreeNode) -> boolean)?,
}

local function TreeView(props: Props)
local theme = useTheme()

local nodesById = useMemo(function() end, { props.roots })

local leafNodes = useMemo(function() end, { props.roots })
local roots = useMemo(function(): { TreeNode }
return createTreeNodesFromPartials(props.roots)
end, { props.roots })

local onNodeActivated = useCallback(function(node: TreeNode)
if props.onActivated then
Expand All @@ -31,7 +30,7 @@ local function TreeView(props: Props)
end, { props.onActivated })

local children: { [string]: React.Node } = {}
for index, node in props.roots do
for index, node in roots do
children[node.label] = React.createElement(TreeNode, {
layoutOrder = index,
node = node,
Expand All @@ -47,7 +46,6 @@ local function TreeView(props: Props)
}, {
Layout = React.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = theme.paddingSmall,
}),
}, children)
end
Expand Down
115 changes: 93 additions & 22 deletions src/TreeView/TreeView.story.luau
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,111 @@ local MockPlugin = require("@root/Testing/MockPlugin")
local TreeView = require("./TreeView")
local types = require("./types")

type PartialTreeNode = types.PartialTreeNode

return {
story = function()
local tree: types.TreeNode = {
id = "1",
label = "Top",
children = {
{
id = "2",
label = "Child",
isExpanded = true,
children = {},
local roots: { PartialTreeNode } = {
{
label = "Pinned Storybooks",
icon = types.TreeNodeIcon.None,
isExpanded = true,
children = {
{
label = "Storybook 1",
icon = types.TreeNodeIcon.Storybook,
isExpanded = false,
children = {
-- ...
},
},
},
},
{
label = "Storybook 1",
icon = types.TreeNodeIcon.Storybook,
isExpanded = false,
children = {
-- ...
},
},

{
id = "3",
label = "Sibling",
children = {
{
id = "4",
label = "Descendant",
isExpanded = false,
children = {},
{
label = "Storybook 2",
icon = types.TreeNodeIcon.Storybook,
isExpanded = true,
children = {
{
label = "Folder",
icon = types.TreeNodeIcon.Folder,
isExpanded = true,
children = {
{
label = "Story 1",
icon = types.TreeNodeIcon.Story,
},
{
label = "Story 2",
icon = types.TreeNodeIcon.Story,
},
},
} :: PartialTreeNode,
{
label = "Story 1",
icon = types.TreeNodeIcon.Story,
},
{
label = "Story 2",
icon = types.TreeNodeIcon.Story,
},
{
label = "Story 3",
icon = types.TreeNodeIcon.Story,
},
},
},
{
label = "Storybook 3",
icon = types.TreeNodeIcon.Storybook,
children = {
-- ...
},
},
{
label = "Unnamed Storybook",
icon = types.TreeNodeIcon.Storybook,
children = {
-- ...
},
},
{
label = "Unknown Stories",
icon = types.TreeNodeIcon.Storybook,
isExpanded = true,
children = {
{
label = "Story 1",
icon = types.TreeNodeIcon.Story,
},
{
label = "Story 2",
icon = types.TreeNodeIcon.Story,
},
},
},
}
return React.createElement(ContextProviders, {
plugin = MockPlugin.new(),
}, {
TreeView = React.createElement(TreeView, {
roots = {
tree,
},
Wrapper = React.createElement("Frame", {
Size = UDim2.fromOffset(300, 0),
AutomaticSize = Enum.AutomaticSize.Y,
BackgroundTransparency = 1,
}, {

TreeView = React.createElement(TreeView, {
roots = roots,
}),
}),
})
end,
Expand Down
36 changes: 36 additions & 0 deletions src/TreeView/createTreeNodesFromPartials.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
local Sift = require("@pkg/Sift")

local types = require("./types")

type PartialTreeNode = types.PartialTreeNode
type TreeNode = types.TreeNode

local function createTreeNodesFromPartials(partialRoots: { PartialTreeNode }): { TreeNode }
local function process(partials: { PartialTreeNode }, parent: TreeNode?): { TreeNode }
local siblings: { TreeNode } = {}

for _, partial in partials do
local base: TreeNode = {
label = "Unknown",
icon = "none",
children = {},
parent = parent,
isExpanded = false,
}

local node = Sift.Dictionary.join(base, partial)

if partial.children then
node.children = if partial.children then process(partial.children, node) else {}
end

table.insert(siblings, node)
end

return siblings
end

return process(partialRoots)
end

return createTreeNodesFromPartials
Loading

0 comments on commit 1f17252

Please sign in to comment.