diff --git a/tree/example_test.go b/tree/example_test.go index eb1e3fa7..33ce8226 100644 --- a/tree/example_test.go +++ b/tree/example_test.go @@ -68,7 +68,7 @@ func ExampleNewLeaf() { // } -func ExampleNodeChildren_Replace() { +func ExampleTree_Replace() { enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("63")).MarginRight(1) rootStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("35")) itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")) @@ -91,21 +91,13 @@ func ExampleNodeChildren_Replace() { RootStyle(rootStyle). ItemStyle(itemStyle) // Add a Tree as a Child of "Glossier" - // This rewrites all of the tree data to do one replace. Not the most - // efficient. Maybe we can improve on this. - // - // That is how we're handling any Child manipulation in the Child() func as - // well. Because the children are an interface it's a bit trickier. We need - // to do an assignment, can't just manipulate the children directly. - t.SetChildren(t.Children().(tree.NodeChildren). - Replace(0, t.Children().At(0).Child( - tree.Root("Apparel").Child("Pink Hoodie", "Baseball Cap"), - ))) + t.Replace(0, t.Children().At(0).Child( + tree.Root("Apparel").Child("Pink Hoodie", "Baseball Cap"), + )) // Add a Leaf as a Child of "Glossier" t.Children().At(0).Child("Makeup") fmt.Println(ansi.Strip(t.String())) - // Output: // ⁜ Makeup // ├── Glossier @@ -122,6 +114,55 @@ func ExampleNodeChildren_Replace() { // } +func ExampleTree_Insert() { + // Styles are here in case we want to test that styles are properly inherited... + enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("63")).MarginRight(1) + rootStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("35")) + itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")) + + t := tree. + Root("⁜ Makeup"). + Child( + "Glossier", + "Fenty Beauty", + tree.New().Child( + "Gloss Bomb Universal Lip Luminizer", + "Hot Cheeks Velour Blushlighter", + ), + "Nyx", + "Mac", + "Milk", + ). + Enumerator(tree.RoundedEnumerator). + EnumeratorStyle(enumeratorStyle). + RootStyle(rootStyle). + ItemStyle(itemStyle) + // Adds a new Tree Node after Fenty Beauty + t.Insert(2, tree.Root("Lancôme").Child("Juicy Tubes Lip Gloss", "Lash Idôle", "Teint Idôle Highlighter")) + + // Adds a new Tree Node in Fenty Beauty + t.Replace(1, t.Children().At(1).Insert(0, tree.NewLeaf("Blurring Skin Tint", false))) + + // Adds a new Tree Node to a Leaf (Mac) + t.Replace(4, t.Children().At(4).Insert(0, tree.NewLeaf("Glow Play Cushion Blush", false))) + fmt.Println(ansi.Strip(t.String())) + // Output: + //⁜ Makeup + //├── Glossier + //├── Fenty Beauty + //│ ├── Blurring Skin Tint + //│ ├── Gloss Bomb Universal Lip Luminizer + //│ ╰── Hot Cheeks Velour Blushlighter + //├── Lancôme + //│ ├── Juicy Tubes Lip Gloss + //│ ├── Lash Idôle + //│ ╰── Teint Idôle Highlighter + //├── Nyx + //├── Mac + //│ ╰── Glow Play Cushion Blush + //╰── Milk +} + // Tree Examples func ExampleTree_Hide() { diff --git a/tree/tree.go b/tree/tree.go index 1c47ace9..07234d54 100644 --- a/tree/tree.go +++ b/tree/tree.go @@ -41,6 +41,9 @@ type Node interface { SetHidden(bool) SetValue(any) Child(...any) *Tree + SetChildren(...any) *Tree + Insert(int, any) *Tree + Replace(int, any) *Tree } // Leaf is a node without children. @@ -89,6 +92,21 @@ func (s *Leaf) Child(children ...any) *Tree { return t.Child(children) } +// SetChildren turns the Leaf into a Tree with the given children. +func (s *Leaf) SetChildren(children ...any) *Tree { + return s.Child(children) +} + +// Replace turns the Leaf into a Tree with the given child. +func (s *Leaf) Replace(_ int, child any) *Tree { + return s.Child(child) +} + +// Insert turns the Leaf into a Tree with the given child. +func (s *Leaf) Insert(_ int, child any) *Tree { + return s.Child(child) +} + // Hidden returns whether a Leaf node is hidden. func (s Leaf) Hidden() bool { return s.hidden @@ -169,6 +187,57 @@ func (t *Tree) SetChildren(children ...any) *Tree { return t.Child(children) } +// Replace swaps the child at the given index with the given child. +func (t *Tree) Replace(index int, child any) *Tree { + nodes := t.anyToNode(child) + t.children = t.children.(NodeChildren).Replace(index, nodes[0]) + return t +} + +// Insert child at the given index. +func (t *Tree) Insert(index int, child any) *Tree { + nodes := t.anyToNode(child) + t.children = t.children.(NodeChildren).Insert(index, nodes[0]) + return t +} + +// TODO probably don't need this to be an []any +func (t *Tree) anyToNode(children ...any) []Node { + var nodes []Node + for _, child := range children { + switch item := child.(type) { + case *Tree: + child, _ := child.(*Tree) + nodes = append(nodes, child) + case Children: + for i := 0; i < item.Length(); i++ { + nodes = append(nodes, item.At(i)) + } + case Node: + nodes = append(nodes, item) + case fmt.Stringer: + s := Leaf{value: item.String()} + nodes = append(nodes, &s) + case string: + s := Leaf{value: item} + nodes = append(nodes, &s) + case []any: + return t.anyToNode(item...) + case []string: + ss := make([]any, 0, len(item)) + for _, s := range item { + ss = append(ss, s) + } + return t.anyToNode(ss...) + case nil: + continue + default: + return t.anyToNode(fmt.Sprintf("%v", item)) + } + } + return nodes +} + // Child adds a child to this Tree. // // If a Child Tree is passed without a root, it will be parented to it's sibling