Skip to content

Commit

Permalink
[VirtualTree] Add option to hide root node
Browse files Browse the repository at this point in the history
  • Loading branch information
hyazinthh committed Aug 28, 2023
1 parent adc45d3 commit adba7b0
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 33 deletions.
7 changes: 7 additions & 0 deletions src/Scratch/31 - VirtualTree/App.fs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ let view (model : AdaptiveModel) =
text "Scroll"
]

simplecheckbox {
attributes [clazz "ui inverted checkbox"; style "margin-left: 10px"]
state model.treeView.tree.showRoot
toggle (TreeViewAction TreeView.Message.ToggleRoot)
content [ text "Show root" ]
}

model.treeView |> TreeView.view TreeViewAction item
]
)
Expand Down
3 changes: 3 additions & 0 deletions src/Scratch/31 - VirtualTree/TreeView/TreeViewModel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ module TreeView =
static member inline UncollapseAll : Message<'Key> =
Virtual <| VirtualTree.Message.UncollapseAll

static member inline ToggleRoot : Message<'Key> =
Virtual <| VirtualTree.Message.ToggleRoot

let empty<'Key, 'Value> : TreeView<'Key, 'Value> =
{ tree = VirtualTree.empty
values = HashMap.empty
Expand Down
86 changes: 63 additions & 23 deletions src/Scratch/31 - VirtualTree/VirtualTree/VirtualTreeApp.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ module VirtualTree =
let rec update (message : VirtualTree.Message<'T>) (tree : VirtualTree<'T>) =
match message with
| VirtualTree.Message.OnResize height ->
{ tree with height = max tree.height height }
let height = { height with itemHeight = max height.itemHeight tree.height.itemHeight }
{ tree with height = height }

| VirtualTree.Message.OnScroll offset ->
{ tree with scrollOffset = offset }
Expand All @@ -44,8 +45,15 @@ module VirtualTree =
)

match tree.current |> FlatTree.tryIndexOf target with
| ValueSome i -> { tree with scrollTarget = i * tree.height.itemHeight }
| _ -> tree
| ValueSome i ->
let i = if tree.showRoot then i else i - 1

if i > 0 then
{ tree with scrollTarget = i * tree.height.itemHeight }
else
tree
| _ ->
tree

| VirtualTree.Message.Collapse node ->
let leaf = FlatTree.singleton node
Expand All @@ -58,22 +66,40 @@ module VirtualTree =
| VirtualTree.Message.CollapseAll ->
if tree.hierarchy.IsEmpty then tree
else
{ tree with
current = FlatTree.singleton tree.hierarchy.Root
collapsed = FlatTree.collapsed tree.hierarchy }
let collapsed =
{ tree with
current = FlatTree.singleton tree.hierarchy.Root
collapsed = FlatTree.collapsed tree.hierarchy }

if tree.showRoot then collapsed
else
let root = tree.hierarchy.Root
collapsed |> update (VirtualTree.Message.Uncollapse root)

| VirtualTree.Message.Uncollapse node ->
let subtree = tree.collapsed.[node]
match tree.collapsed |> HashMap.tryFindV node with
| ValueSome subtree ->
{ tree with
current = tree.current |> FlatTree.replace node subtree
collapsed = tree.collapsed |> HashMap.remove node }

{ tree with
current = tree.current |> FlatTree.replace node subtree
collapsed = tree.collapsed |> HashMap.remove node }
| _ ->
tree

| VirtualTree.Message.UncollapseAll ->
{ tree with
current = tree.hierarchy
collapsed = HashMap.empty }

| VirtualTree.Message.ToggleRoot ->
let tree = { tree with showRoot = not tree.showRoot }

if tree.showRoot || tree.hierarchy.IsEmpty then tree
else
let root = tree.hierarchy.Root
tree |> update (VirtualTree.Message.Uncollapse root)


let view (message : VirtualTree.Message<'T> -> 'msg)
(itemNode : VirtualTree.Item<'T> -> DomNode<'msg>)
(attributes : AttributeMap<'msg>) (tree : AdaptiveVirtualTree<'T, _, _>) : DomNode<'msg> =
Expand Down Expand Up @@ -128,36 +154,50 @@ module VirtualTree =

let elements =
alist {
let! showRoot = tree.showRoot
let! itemHeight = tree.height.itemHeight
let origin = if showRoot then 0 else 1

if itemHeight = 0 then
let! elements = tree.current

// We do not know the item height yet, render two elements to determine it.
// Also we need the client height of the list, expand it to the max with a big virtual item.
if elements.Count > 0 then
if elements.Count > origin then
yield virtualItem 9999

for i = 0 to (min elements.Count 2) - 1 do
yield item <| VirtualTree.Item(elements.[i], false)
for i = origin to (min elements.Count 2) - 1 do
let fi = elements.[i]
yield item <| VirtualTree.Item(fi.Value, fi.Depth - origin, VirtualTree.ItemKind.Uncollapsed)
else
let! elements = tree.current
let! clientHeight = tree.height.clientHeight
let! scrollOffset = tree.scrollOffset

let first = ((scrollOffset / itemHeight) - border) |> clamp 0 (elements.Count - 1)
let last = (first + (clientHeight / itemHeight) + 2 * border) |> min (elements.Count - 1)
if elements.Count > origin then
let first = (origin + (scrollOffset / itemHeight) - border) |> clamp origin (elements.Count - 1)
let last = (first + (clientHeight / itemHeight) + 2 * border) |> min (elements.Count - 1)

if first > origin then
yield virtualItem ((first - origin) * itemHeight)

for i = first to last do
let fi = elements.[i]

if first > 0 then
yield virtualItem (first * itemHeight)
let kind =
if fi.IsLeaf then
if tree.collapsed.ContainsKey fi.Value then
VirtualTree.ItemKind.Collapsed
else
VirtualTree.ItemKind.Leaf
else
VirtualTree.ItemKind.Uncollapsed

for i = first to last do
let fi = elements.[i]
let ti = VirtualTree.Item(fi, tree.collapsed.ContainsKey fi.Value)
yield item ti
let ti = VirtualTree.Item(fi.Value, fi.Depth - origin, kind)
yield item ti

if last < elements.Count - 1 then
yield virtualItem ((elements.Count - last - 1) * itemHeight)
if last < elements.Count - 1 then
yield virtualItem ((elements.Count - last - 1) * itemHeight)
}

let channels : (string * Channel) list = [
Expand Down
16 changes: 6 additions & 10 deletions src/Scratch/31 - VirtualTree/VirtualTree/VirtualTreeModel.fs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type VirtualTree<'T> =

[<NonAdaptive>]
collapsed : HashMap<'T, FlatTree<'T>> // Collapsed subtrees

showRoot : bool // Whether the root node should be displayed (false for forest view)
}


Expand Down Expand Up @@ -53,15 +55,6 @@ module VirtualTree =
Depth = depth
Kind = kind }

new (flat : FlatItem<'T>, collapsed : bool) =
let kind =
if flat.IsLeaf then
if collapsed then ItemKind.Collapsed else ItemKind.Leaf
else
ItemKind.Uncollapsed

Item(flat.Value, flat.Depth, kind)

[<RequireQualifiedAccess>]
type Message<'T> =
| OnResize of height: VirtualHeight
Expand All @@ -71,6 +64,7 @@ module VirtualTree =
| CollapseAll
| Uncollapse of key: 'T
| UncollapseAll
| ToggleRoot

let inline ofTree (tree : FlatTree<'T>) =
{ height = Unchecked.defaultof<_>
Expand All @@ -80,7 +74,9 @@ module VirtualTree =
current = tree
hierarchy = tree

collapsed = HashMap.empty }
collapsed = HashMap.empty

showRoot = false }

let inline empty<'T> : VirtualTree<'T> =
ofTree FlatTree.empty
Expand Down

0 comments on commit adba7b0

Please sign in to comment.