Module: Layered::Ui::TagHelper

Defined in:
app/helpers/layered/ui/tag_helper.rb

Defined Under Namespace

Classes: TagBuilder

Instance Method Summary collapse

Instance Method Details

#l_ui_tag(rounded: false, container: {}, &block) ⇒ Object

Renders an interactive tag - e.g. an email recipient or an active filter. Visually related to l-ui-badge, but a distinct control: a badge is a static styled span, while a tag is a container whose segments (label, remove) are separately interactive.

<%= l_ui_tag do |t| %>
<% t.text "alice@example.com" %>
<% t.remove aria: { label: "Remove alice@example.com" } %>
<% end %>

A tag may carry a popover. The whole tag becomes the placement target (so the popover aligns with the tag rather than the segment inside it), and button segments open it via popovertarget:

<%= l_ui_tag do |t| %>
<% t.button(aria: { label: "Edit status filter" }) do %>
  Status: Active
<% end %>
<% t.remove remove_filter_path, aria: { label: "Remove status filter" } %>
<% t.popover do %>
  <p>Filter controls.</p>
<% end %>
<% end %>

Note: use <% t.button %> (without the equals sign) so segment content is captured by the builder rather than written to the body buffer.

Segments render in the order they are declared; the popover element is always appended last. Give icon-only segments (such as t.remove) an aria-label so they have an accessible name.

Options:

rounded:   (Boolean) Pill shape (matching +l-ui-badge--rounded+); defaults to false for the subtle badge shape.
container: (Hash)    Extra HTML attributes for the wrapping <div>.

Builder methods:

t.text(content = nil, **options, &block)  Static label segment ().
t.button(**options, &block)               Button segment; opens the tag's popover when one is declared.
t.link(url, **options, &block)            Link segment, styled like a button segment.
t.remove(url = nil, **options, &block)    Trailing remove segment: a link when +url+ is given, otherwise a
                                        button. Renders a ✕ icon unless the block supplies custom content.
t.popover(id: nil, placement: :bottom, align: :start, open: false, &block)
                                        Attaches a popover to the tag; the block is the popover body.
                                        Options match +l_ui_popover+.


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'app/helpers/layered/ui/tag_helper.rb', line 48

def l_ui_tag(rounded: false, container: {}, &block)
  builder = TagBuilder.new(self)
  capture { block.call(builder) }

  container_attrs = container.deep_dup
  container_attrs[:class] = class_names("l-ui-tag", ("l-ui-tag--rounded" if rounded), container_attrs[:class])

  if builder.popover?
    container_data = container_attrs[:data] || {}
    existing_controller = container_data.delete(:controller) || container_data.delete("controller")
    container_data[:controller] = [existing_controller, "l-ui--popover"].compact.reject(&:empty?).join(" ")
    container_data[:"l-ui--popover-target"] = "trigger"
    container_data[:"l-ui--popover-placement-value"] = builder.popover_placement
    container_data[:"l-ui--popover-align-value"] = builder.popover_align
    container_data[:"l-ui--popover-open-value"] = true if builder.popover_open
    container_attrs[:data] = container_data
  end

  tag.div(**container_attrs) do
    parts = builder.segments.map { |segment| l_ui_tag_segment(builder, segment) }
    parts << l_ui_tag_popover(builder) if builder.popover?
    safe_join(parts)
  end
end