Collapsible
Toggle the visibility of a content region with a smooth height animation.
Installation
bin/rails g wabi:add collapsibleExample
Hidden content revealed with a height animation.
render Components::UI::Collapsible.new do
render Components::UI::CollapsibleTrigger.new(class: "w-full") { "Toggle details" }
render Components::UI::CollapsibleContent.new do
p(class: "py-2 text-sm text-muted-foreground") { "Hidden content revealed with a height animation." }
end
end
Source
app/components/ui/collapsible.rb
# frozen_string_literal: true
require "date" # Phlex 2.4 references Date/Time constants lazily when rendering data:{} hashes
module Components
module UI
class Collapsible < Wabi::Base
def initialize(id: nil, open: false, disabled: false, **attrs)
@id = id
@open = open
@disabled = disabled
@attrs = attrs
end
def view_template(&block)
user_class = @attrs.delete(:class)
div(
id: @id,
data: {
controller: "wabi--collapsible",
"wabi--collapsible-open-value": @open.to_s,
"wabi--collapsible-disabled-value": @disabled.to_s,
},
class: merge_class("w-full", user_class)
) do
yield if block_given?
end
end
end
end
end
app/components/ui/collapsible_trigger.rb
# frozen_string_literal: true
require "date" # Phlex 2.4 references Date/Time constants lazily when rendering data:{} hashes
module Components
module UI
class CollapsibleTrigger < Wabi::Base
def initialize(**attrs)
@attrs = attrs
end
def view_template(&block)
user_class = @attrs.delete(:class)
button(
type: "button",
data: { "wabi--collapsible-target": "trigger" },
class: merge_class(
"inline-flex items-center justify-between gap-2 text-sm font-medium " \
"ring-offset-background focus-visible:outline-none focus-visible:ring-2 " \
"focus-visible:ring-ring focus-visible:ring-offset-2 " \
"disabled:pointer-events-none disabled:opacity-50",
user_class
)
) do
yield if block_given?
end
end
end
end
end
app/components/ui/collapsible_content.rb
# frozen_string_literal: true
require "date" # Phlex 2.4 references Date/Time constants lazily when rendering data:{} hashes
module Components
module UI
class CollapsibleContent < Wabi::Base
def initialize(**attrs)
@attrs = attrs
end
def view_template(&block)
user_class = @attrs.delete(:class)
div(
data: { "wabi--collapsible-target": "content" },
"data-state": "closed",
class: merge_class(
"grid grid-rows-[0fr] transition-[grid-template-rows] duration-200 ease-out " \
"motion-reduce:transition-none data-[state=open]:grid-rows-[1fr]",
user_class
)
) do
div(class: "overflow-hidden") do
yield if block_given?
end
end
end
end
end
end
Accessibility
- Trigger exposes aria-expanded and aria-controls; content has a matching id.
- Height animation respects prefers-reduced-motion.