Skip to content

Commit

Permalink
Filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
vocksel committed Dec 2, 2024
1 parent b71841c commit e326bae
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 20 deletions.
5 changes: 4 additions & 1 deletion src/TreeView/TreeView.luau
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ type PartialTreeNode = types.PartialTreeNode
type TreeNode = types.TreeNode
type Tree = types.Tree

local function TreeView()
local function TreeView(props: {
LayoutOrder: number?,
})
local treeViewContext = TreeViewContext.use()

local children: { [string]: React.Node } = {}
Expand All @@ -22,6 +24,7 @@ local function TreeView()
return React.createElement("Frame", {
BackgroundTransparency = 1,
AutomaticSize = Enum.AutomaticSize.XY,
LayoutOrder = props.LayoutOrder,
}, {
Layout = React.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
Expand Down
19 changes: 18 additions & 1 deletion src/TreeView/TreeView.story.luau
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ local React = require("@pkg/React")

local ContextProviders = require("@root/Common/ContextProviders")
local MockPlugin = require("@root/Testing/MockPlugin")
local Searchbar = require("@root/Forms/Searchbar")
local TreeView = require("./TreeView")
local TreeViewContext = require("@root/TreeView/TreeViewContext")
local types = require("./types")

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

type PartialTreeNode = types.PartialTreeNode
type TreeNode = types.TreeNode
Expand Down Expand Up @@ -104,14 +106,29 @@ local function Story()
treeViewContext.setRoots(roots)
end, {})

local searchTerm, setSearchTerm = useState(nil :: string?)

useEffect(function()
treeViewContext.search(searchTerm)
end, { treeViewContext, searchTerm } :: { unknown })

return React.createElement("Frame", {
Size = UDim2.fromOffset(300, 0),
AutomaticSize = Enum.AutomaticSize.Y,
BackgroundTransparency = 1,
}, {
Layout = React.createElement("UIListLayout", {
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 16),
}),

Search = React.createElement(Searchbar, {
onSearchChanged = setSearchTerm,
LayoutOrder = 1,
}),

TreeView = React.createElement(TreeView, {
roots = roots,
LayoutOrder = 2,
}),
})
end
Expand Down
53 changes: 35 additions & 18 deletions src/TreeView/TreeViewContext.luau
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,25 @@ local React = require("@pkg/React")
local Sift = require("@pkg/Sift")

local createTreeNodesFromPartials = require("@root/TreeView/createTreeNodesFromPartials")
local getAncestry = require("@root/TreeView/getAncestry")
local reduceTree = require("@root/TreeView/reduceTree")
local types = require("@root/TreeView/types")

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

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

local function getAncestry(node: TreeNode): { TreeNode }
local ancestry = {}
local parent = node.parent
while parent do
table.insert(ancestry, parent)
parent = parent.parent
end
return ancestry
end

type TreeViewContext = {
setRoots: (nodes: { PartialTreeNode }) -> (),
getRoots: () -> { TreeNode },
activateNode: (node: TreeNode) -> (),
isExpanded: (node: TreeNode) -> boolean,
isSelected: (node: TreeNode) -> boolean,
search: (searchTerm: string) -> (),
}

local TreeViewContext = React.createContext(nil)
Expand All @@ -41,6 +35,10 @@ local function TreeNodeProvider(props: {

local expandedNodes, setExpandedNodes = useState({} :: { TreeNode })
local selectedNode, setSelectedNode = useState(nil :: TreeNode?)
local searchTerm: string?, setSearchTerm = useState(nil :: string?)

-- TODO: Expand to a specific node
-- TODO: Always put folders at the top of the list

local expand = useCallback(function(node: TreeNode)
setExpandedNodes(function(prev)
Expand All @@ -67,6 +65,24 @@ local function TreeNodeProvider(props: {
end)
end, {})

local filteredRoots = useMemo(function()
if searchTerm then
return reduceTree(nodes.roots, function(node)
return node.label:lower():match(searchTerm:lower()) ~= nil
end)
else
return nodes.roots
end
end, { nodes.roots, searchTerm } :: { unknown })

local search = useCallback(function(newSearchTerm: string)
if newSearchTerm ~= "" then
setSearchTerm(newSearchTerm)
else
setSearchTerm(nil)
end
end, {})

local setRoots = useCallback(function(partials: { PartialTreeNode })
local newNodes = createTreeNodesFromPartials(partials)

Expand All @@ -81,16 +97,16 @@ local function TreeNodeProvider(props: {
end, {})

local getRoots = useCallback(function()
return nodes.roots
end, { nodes })

-- local getLeaves = useCallback(function()
-- return nodes.leaves
-- end, { nodes })
return filteredRoots
end, { filteredRoots })

local isExpanded = useCallback(function(node: TreeNode): boolean
return table.find(expandedNodes, node) ~= nil
end, { expandedNodes })
if searchTerm then
return true
else
return table.find(expandedNodes, node) ~= nil
end
end, { expandedNodes, searchTerm } :: { unknown })

local isSelected = useCallback(function(node: TreeNode): boolean
return selectedNode == node
Expand Down Expand Up @@ -118,6 +134,7 @@ local function TreeNodeProvider(props: {
activateNode = activateNode,
isExpanded = isExpanded,
isSelected = isSelected,
search = search,
}

return React.createElement(TreeViewContext.Provider, {
Expand Down
15 changes: 15 additions & 0 deletions src/TreeView/getAncestry.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
local types = require("./types")

type TreeNode = types.TreeNode

local function getAncestry(node: TreeNode): { TreeNode }
local ancestry = {}
local parent = node.parent
while parent do
table.insert(ancestry, parent)
parent = parent.parent
end
return ancestry
end

return getAncestry
34 changes: 34 additions & 0 deletions src/TreeView/reduceTree.luau
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
local Sift = require("@pkg/Sift")

local types = require("@root/TreeView/types")

type TreeNode = types.TreeNode
type SearchMatch = (node: TreeNode) -> boolean

local function reduceTree(nodes: { TreeNode }, searchMatch: SearchMatch): { TreeNode }
local function reduceNodes(accumulator: { TreeNode }, node: TreeNode)
if searchMatch(node) then
table.insert(accumulator, node)
return accumulator
end

if #node.children > 0 then
local children = Sift.List.reduce(node.children, reduceNodes, {})

if #children > 0 then
table.insert(
accumulator,
Sift.Dictionary.join(node, {
children = children,
})
)
end
end

return accumulator
end

return Sift.List.reduce(nodes, reduceNodes, {})
end

return reduceTree

0 comments on commit e326bae

Please sign in to comment.