Resizable
Panels with draggable handles for resizing adjacent regions.
Installation
Included in poetry-ui — available as
poetry_resizable
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 resizable
Default
<%= poetry_resizable(grip: true, class: "h-48 max-w-md rounded-lg border") do |group| %>
<% group.with_panel(default_size: 25, min_size: 15) do %>
<div class="flex h-full items-center justify-center p-6 text-sm">Sidebar</div>
<% end %>
<% group.with_panel do %>
<div class="flex h-full items-center justify-center p-6 text-sm">Content</div>
<% end %>
<% end %>
Handle
<%# grip: true renders the visible pill on every separator - without it
the handle is a bare hairline (still draggable and keyboard-
operable; the grip only changes what you see). %>
<%= poetry_resizable(grip: true, class: "h-48 max-w-sm rounded-lg border") do |group| %>
<% group.with_panel(default_size: 25) do %>
<div class="flex h-full items-center justify-center p-6 text-sm font-medium">Sidebar</div>
<% end %>
<% group.with_panel(default_size: 75) do %>
<div class="flex h-full items-center justify-center p-6 text-sm font-medium">Content</div>
<% end %>
<% end %>
Nested
<%= poetry_resizable(class: "h-48 max-w-md rounded-lg border") do |group| %>
<% group.with_panel(default_size: 40) do %>
<div class="flex h-full items-center justify-center p-6 text-sm">Left</div>
<% end %>
<% group.with_panel do %>
<%= poetry_resizable(direction: :vertical) do |inner| %>
<% inner.with_panel do %>
<div class="flex h-full items-center justify-center p-6 text-sm">Top</div>
<% end %>
<% inner.with_panel do %>
<div class="flex h-full items-center justify-center p-6 text-sm">Bottom</div>
<% end %>
<% end %>
<% end %>
<% end %>
Three panels
<%= poetry_resizable(class: "h-48 max-w-2xl rounded-lg border") do |group| %>
<% group.with_panel(default_size: 25, min_size: 15) do %>
<div class="flex h-full items-center justify-center p-6 text-sm">Files</div>
<% end %>
<% group.with_panel(default_size: 50) do %>
<div class="flex h-full items-center justify-center p-6 text-sm">Editor</div>
<% end %>
<% group.with_panel(default_size: 25, min_size: 15) do %>
<div class="flex h-full items-center justify-center p-6 text-sm">Preview</div>
<% end %>
<% end %>
Vertical
<%= poetry_resizable(direction: :vertical, grip: true, class: "h-48 max-w-md rounded-lg border") do |group| %>
<% group.with_panel(default_size: 30) do %>
<div class="flex h-full items-center justify-center p-6 text-sm">Header</div>
<% end %>
<% group.with_panel do %>
<div class="flex h-full items-center justify-center p-6 text-sm">Content</div>
<% end %>
<% end %>
API
Poetry::Ui::Resizable::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 |
|---|---|---|---|
| direction: | Symbol | one of: horizontal, vertical; defaults to :horizontal |
The group axis: :horizontal lays panels side by side, :vertical stacks them. |
| grip: | Boolean | defaults to false |
Renders the grip dots on each handle. |
Slots
| Writer | Description |
|---|---|
| with_panel | One panel per call: default_size/min_size/max_size are percentages of the group; the content block is required. |
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=resizable-panel-group] | The flex group root - the splitter controller rides here |
| [data-slot=resizable-panel] | One flex child whose flex-grow IS its percentage - the controller rewrites the inline flex on every resize |
| [data-slot=resizable-handle] | The role=separator splitter between panels - drag and keyboard resizing live here; its aria-valuenow tracks the preceding panel |
State attributes
| Part | Attribute | Condition | Values |
|---|---|---|---|
| resizable-panel-group | data-orientation | the group axis | horizontal · vertical |
| resizable-panel | data-min-size | the panel's minimum percentage, rendered when with_panel passes min_size: (the controller's clamp floor) | — |
| resizable-panel | data-max-size | the panel's maximum percentage, rendered when with_panel passes max_size: (the controller's clamp ceiling) | — |
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--resizable | registers · value orientation |
| handle | poetry--core--resizable | dragStart on pointerdown · dragMove on pointermove · dragEnd on pointerup · keydown on keydown |