Welcome to Chapter 4 of the Hierarchical Tree Selector tutorial!
In the previous chapter, Recursive Tree Flattening, we successfully took a complex, nested tree of data and turned it into a flat list of visible items.
Now, we need to display this list to the user. But we don't just want a boring text dump. We want an Ordered List (1, 2, 3...) that looks professional.
This chapter introduces the Auto-Numbering List Container: a smart wrapper that handles counting, formatting, and alignment automatically.
Creating a numbered list seems easy. You just loop through items and print index + 1, right?
Attempt 1:
8. Item H
9. Item I
10. Item J
Notice the problem? When we hit double digits (10), the text "Item J" gets pushed to the right. The dots don't align vertically. This looks messy in a terminal UI.
Goal (Aligned):
8. Item H
9. Item I
10. Item J
To achieve this, we need to know how many items there are in total before we start printing the first one, so we can calculate how much padding (space) to add.
OrderedList Container
We will build a component called OrderedList. It acts like a manager for its children.
It performs three main jobs:
<div>s) and only counts valid List Items.To solve the alignment problem, we can't just print items as we see them. We need to look at the children twice.
" 1.") based on the total count found in Pass 1.
We don't want to manually pass props like number="1" to every single item in our code. That is tedious.
Instead, we use React Context.
Before we apply this to our complex File Tree, let's understand it with a simple list of fruits.
Input Code:
<OrderedList>
<OrderedList.Item>Apple</OrderedList.Item>
<OrderedList.Item>Banana</OrderedList.Item>
{/* ... imagine 8 more items ... */}
<OrderedList.Item>Kiwi</OrderedList.Item>
</OrderedList>
Output (What the user sees): The container sees there are 11 items total. It knows it needs 2 digits of space.
1. Apple
2. Banana
...
11. Kiwi
Let's visualize the flow inside the OrderedList component when it receives children.
Let's look at OrderedList.tsx.
First, we create the "Loudspeaker". This is how the parent talks to the child.
// OrderedList.tsx
import React, { createContext } from 'react';
// The message we will send is just a string, e.g., " 1."
export const OrderedListContext = createContext({
marker: ''
});
Inside the component, we first count. We use React.Children.toArray to handle the children safely.
// Inside OrderedListComponent function
let numberOfItems = 0;
// Loop through all children provided to this component
for (const child of React.Children.toArray(children)) {
// Only count it if it is a valid React element
if (isValidElement(child) && child.type === OrderedListItem) {
numberOfItems++;
}
}
Now that we know we have numberOfItems, we calculate how wide our numbers need to be.
// If we have 5 items, length is 1.
// If we have 15 items, length is 2.
const maxMarkerWidth = String(numberOfItems).length;
Finally, we map over the children to render them. We use the JavaScript function padStart to add the spaces.
// Mapping over children to wrap them
return React.Children.map(children, (child, index) => {
// Logic to calculate the string
// Index 0 becomes "1". Pad it to match max width.
const padded = String(index + 1).padStart(maxMarkerWidth);
const finalMarker = `${padded}.`; // Result: " 1." or "10."
// Wrap the child in the Provider
return (
<OrderedListContext.Provider value={{ marker: finalMarker }}>
{child}
</OrderedListContext.Provider>
);
});
Note on Recursion: The code also supports nested lists (lists inside lists) by combining the
parentMarkerwith the current one, but for a beginner, focusing on the simple padding logic is most important!
In Hierarchical Tree Selector, we learned that our File Tree is just a list of nodes.
When we flatten the tree in Recursive Tree Flattening, we get an array of files. We wrap that entire array in this OrderedList.
This ensures that even if you have 100 files open in your tree, the numbering stays perfectly aligned on the left side of the terminal, keeping the UI clean and readable.
In this chapter, we built a "Smart Container" that:
padStart.However, the container only sends the signal. We still need a component to receive that signal and actually print the text to the screen.
In the next chapter, we will build the List Item component that listens to this context and displays the content.
Next Chapter: Context-Aware List Item
Generated by Code IQ