Tree
A hierarchical list of expandable, selectable nodes.
Installation
Included in poetry-ui — available as
poetry_tree
the moment you've installed Poetry,
with no per-component step. To own the source and edit it, copy it into your app:
bin/rails g poetry:add tree
Default
<%# Top-anchored constant-height box: expanding rows grow DOWNWARD
inside it. Bare in the centered example shell, every toggle would
re-center the tree - rows jumping under the pointer mid-click. %>
<div class="h-56 w-72 overflow-auto">
<%= poetry_tree(label: "Files", class: "w-72") do |tree| %>
<% tree.with_item(text: "docs", value: "docs", expanded: true) do |docs| %>
<% docs.with_item(text: "guides", value: "guides") do |guides| %>
<% guides.with_item(text: "intro.md", value: "intro") %>
<% end %>
<% docs.with_item(text: "README.md", value: "readme") %>
<% end %>
<% tree.with_item(text: "src", value: "src") do |src| %>
<% src.with_item(text: "main.rb", value: "main") %>
<% end %>
<% end %>
</div>
With links
<%# Top-anchored constant-height box: expanding rows grow DOWNWARD
inside it. Bare in the centered example shell, every toggle would
re-center the tree - rows jumping under the pointer mid-click. %>
<div class="h-56 w-72 overflow-auto">
<%# href: items render their labels as links; persist expansion by
listening for poetry:tree:toggle and re-rendering with expanded:. %>
<%= poetry_tree(label: "Pages", class: "w-72") do |tree| %>
<% tree.with_item(text: "Getting started", value: "start", expanded: true) do |start| %>
<% start.with_item(text: "Install", value: "install", href: "#install") %>
<% start.with_item(text: "Theming", value: "theming", href: "#theming") %>
<% end %>
<% end %>
</div>
API
Poetry::Ui::Tree::Component — options are
constructor keywords (the poetry_* helper forwards them);
slots are composed inside the block. Generated from the gem's source documentation.
| Option | Type | Details | Description |
|---|---|---|---|
| label: | String | required | The tree's accessible name. Required. |
Methods
| Method | Description |
|---|---|
| #with_item(**, &block) | Declares one row. Nest children by calling with_item again on the yielded builder. Keywords: text: (the row's label, required), value: (the toggle event's identity, defaults to text:), expanded: (render the subtree open), disabled:, href: (the label renders as a link). |
Styling
Every part carries a stable data-slot attribute — target
[data-slot=…] from your own CSS to restyle it. State rides
data attributes on the parts below. This contract is verified against rendered DOM in CI.
| Part | Description |
|---|---|
| [data-slot=tree] | The treegrid container (role=treegrid, the accessible name) - roving focus, expansion keys, and typeahead ride here; rows are FLAT siblings |
| [data-slot=tree-item] | One row (role=row > gridcell) - hierarchy in aria-level/posinset/setsize, indentation via --poetry-tree-level; rows under a collapsed ancestor render hidden |
| [data-slot=tree-item-toggle] | The chevron (parents only): tabindex -1, never steals focus, aria-label flips Expand/Collapse |
| [data-slot=tree-item-label] | The row's text - a link when href: is given |
State attributes
| Part | Attribute | Condition | Values |
|---|---|---|---|
| tree-item | data-expanded | the row's subtree is open (parents only; aria-expanded is the canonical twin) | — |
| tree-item | data-disabled | the item is disabled (skipped by arrows and typeahead) | — |
| tree-item | data-value | always - the toggle event's identity | — |
| tree-item | data-level | always - the 1-based depth (aria-level's twin; --poetry-tree-level drives the indent) | — |
| tree-item-toggle | data-expand-label | always - the localized Expand string the controller swaps in on collapse | — |
| tree-item-toggle | data-collapse-label | always - the localized Collapse string the controller swaps in on expand | — |
CSS variables
| Part | Variable | Description |
|---|---|---|
| tree-item | --poetry-tree-level | the 1-based depth - indentation is calc((level - 1) * step) in the dictionary |
Wiring
The Stimulus surface each element carries — declared in the component, verified against rendered DOM in CI. Bare actions fire on the element's default event.
| Element | Controller | Wiring |
|---|---|---|
| root | poetry--core--tree | registers · keydown on keydown · press on click |
| toggle | poetry--core--tree | pressStart on pointerdown · toggle on click |