Module: UI::SharedAsChildBehavior
- Included in:
- DialogTrigger, DrawerTrigger, DropdownMenu, DropdownMenuComponent, DropdownMenuTrigger, Item, ItemComponent, NavigationMenuLink, NavigationMenuLinkComponent, ScrollArea, ScrollAreaCorner, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectTrigger, SheetClose, SheetTrigger
- Defined in:
- app/behaviors/ui/shared_as_child_behavior.rb
Overview
AsChildBehavior provides the ‘asChild` composition pattern Inspired by Radix UI’s Slot component
When ‘as_child: true`, components don’t render their own wrapper element. Instead, they yield attributes to the block, allowing the child to receive them.
Example:
render UI::DialogTrigger.new(as_child: true) do |attrs|
render UI::Button.new(**attrs, variant: :outline) do
"Open"
end
end
Instance Method Summary collapse
-
#merge_attributes(parent_attrs, child_attrs) ⇒ Hash
Merges parent component attributes with child component attributes.
Instance Method Details
#merge_attributes(parent_attrs, child_attrs) ⇒ Hash
Merges parent component attributes with child component attributes
Uses Rails’ deep_merge for nested hashes (like data attributes) Uses TailwindMerge for CSS classes to resolve conflicts Concatenates Stimulus actions so both handlers execute
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'app/behaviors/ui/shared_as_child_behavior.rb', line 41 def merge_attributes(parent_attrs, child_attrs) # Use Rails' deep_merge for automatic nested hash merging merged = parent_attrs.deep_merge(child_attrs) # CSS Classes: Use TailwindMerge to resolve conflicts intelligently # Example: "p-4 text-sm" + "p-2 text-lg" => "p-2 text-lg" # (later values override earlier conflicting values) if parent_attrs[:class] && child_attrs[:class] merged[:class] = TailwindMerge::Merger.new.merge( [parent_attrs[:class], child_attrs[:class]].join(" ") ) end # Stimulus Actions: Concatenate so both handlers execute # This is critical for composition - both parent and child actions should run if parent_attrs.dig(:data, :action) && child_attrs.dig(:data, :action) merged[:data][:action] = [ parent_attrs.dig(:data, :action), child_attrs.dig(:data, :action) ].join(" ") end # Styles: Concatenate with semicolon (if both exist) # Example: "padding: 8px" + "background: blue" => "padding: 8px; background: blue" if parent_attrs[:style] && child_attrs[:style] merged[:style] = "#{parent_attrs[:style]}; #{child_attrs[:style]}" end merged end |