Module: IconHelper

Overview

View helper: icon.

Constant Summary collapse

DEFAULT_FAMILY =

Default family.

'sharp_regular'
CUSTOM_SVG_DIR =

Custom svg dir.

Rails.root.join('app/assets/images/svgs/custom')
CUSTOM_ICON_MAP =

Mapping of custom icon.

if Dir.exist?(CUSTOM_SVG_DIR)
  Dir.glob(File.join(CUSTOM_SVG_DIR, '*.svg'))
     .map { |file| File.basename(file, '.svg') }
     .sort
     .freeze
else
  [].freeze
end

Instance Method Summary collapse

Instance Method Details

#account_nav_icon(**opts) ⇒ Object

Navbar account icon. When the session is a masquerade (employee "Login as
this user" flow), renders a warning-coloured circular badge with an
inverted user-secret glyph, so the employee sees at a glance that they are
not in their real account. Falls through to the regular circle-user icon
otherwise.

Sized to occupy the same 1em footprint as circle-user via
.masquerade-account-icon in
client/stylesheets/www/02-objects/navbar.scss. fa-stack was avoided here
because its 2em default footprint would visibly inflate the navbar slot.



127
128
129
130
131
132
133
# File 'app/helpers/icon_helper.rb', line 127

def (**opts)
  if respond_to?(:account_impersonated?) && 
    (:span, **masquerade_icon_wrapper_opts(opts)) { fa_icon('user-secret', family: 'fas') }
  else
    fa_icon('circle-user', **opts.reverse_merge(family: 'far'))
  end
end

#fa_icon(icon = nil, options = {}) ⇒ ActiveSupport::SafeBuffer

Render a Font Awesome icon as <i> markup, or inline a local SVG
for the :custom family. Single entry point for every server-rendered
FA icon in this app.

Family handling

:family value Output
omitted (default) / :sharp_regular / 'far' <i class="fa-sharp fa-regular fa-<icon>">
:solid / :sharp_solid / 'fas' / 'fa-solid' <i class="fa-sharp fa-solid fa-<icon>">
:brands / 'fab' / 'fa-brands' <i class="fa-brands fa-<icon>">
:custom Inlined <svg> from app/assets/images/svgs/custom/<icon>.svg

Examples:

Default sharp-regular family

fa_icon('trash')
# => <i class="fa-sharp fa-regular fa-trash"></i>

Inline a local custom SVG

fa_icon('thermostat', family: :custom)
# => <svg class="custom-svg-icon" aria-hidden="true" role="img">…</svg>

With trailing text wrapped in a copy-target span

fa_icon('check', family: :solid, text: 'Done')
# => <i class="fa-sharp fa-solid fa-check"></i> <span class="copy-target">Done</span>

Parameters:

  • icon (String, Symbol, nil) (defaults to: nil)

    icon name in canonical FA6+ form
    (e.g. 'gear', 'circle-info'); underscores are normalized to
    hyphens. Defaults to 'question' when nil.

  • options (Hash) (defaults to: {})

    rendering options.

Options Hash (options):

  • :family (Symbol, String)

    one of the values listed above.
    Unrecognized values fall through to the default sharp-regular family.

  • :class (String, Array<String>)

    extra CSS class(es)
    appended to the icon element.

  • :text (String, ActiveSupport::SafeBuffer)

    optional text
    rendered after the icon inside a <span class="copy-target"> so
    "copy text" UI affordances can target it.

  • :* (Object)

    any other key is splatted onto tag.i as
    an HTML attribute ('aria-hidden': 'true', :title, :data, etc.).

Returns:

  • (ActiveSupport::SafeBuffer)

    HTML-safe icon markup, optionally
    followed by a <span class="copy-target"> if :text was supplied.

See Also:



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'app/helpers/icon_helper.rb', line 84

def fa_icon(icon = nil, options = {})
  icon ||= 'question'
  opts = options.dup
  text_value = opts.delete(:text)
  text = text_value.is_a?(String) ? text_value.html_safe : text_value

  requested_family = (opts.delete(:family) || DEFAULT_FAMILY).to_s

  # Custom icons: inline the local SVG from CUSTOM_SVG_DIR.
  # `:kit` / `'fa-kit'` were historical aliases tied to the
  # `kit.fontawesome.com` loader script, which this project never
  # ships. Use `:custom` only.
  if requested_family == 'custom'
    icon_tag = inline_custom_svg(icon.to_s.tr('_', '-'), opts)
    return append_text(icon_tag, text)
  end

  family_classes = case requested_family
                   when 'fab', 'brands', 'fa-brands'
                     ['fa-brands']
                   when 'fas', 'solid', 'fa-solid', 'sharp-solid', 'sharp_solid', 'fa-sharp fa-solid'
                     %w[fa-sharp fa-solid]
                   else
                     %w[fa-sharp fa-regular]
                   end

  icon_class = "fa-#{icon.to_s.gsub(/_/m, '-')}"
  css_classes = [family_classes, icon_class, opts.delete(:class)].flatten.compact.join(' ')

  icon_tag = ActionController::Base.helpers.tag.i(nil, **opts, class: css_classes)
  append_text(icon_tag, text)
end

#star_rating_html(rating, icon_class: nil, rounding: :nearest) ⇒ Object

Discrete Font Awesome stars (full / half / empty) for ratings 0..5.

Parameters:

  • rounding (Symbol) (defaults to: :nearest)

    :nearest (half-star) or :floor

  • rating (Object)
  • icon_class (Object, nil) (defaults to: nil)


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'app/helpers/icon_helper.rb', line 25

def star_rating_html(rating, icon_class: nil, rounding: :nearest)
  clamped = rating.to_f.clamp(0.0, 5.0)
  rounded = case rounding
            when :floor then ((clamped * 2).floor / 2.0)
            else ((clamped * 2).round / 2.0)
            end
  full  = rounded.floor
  half  = (rounded - full) >= 0.5
  empty = 5 - full - (half ? 1 : 0)

  nodes = []
  full.times { nodes << fa_icon('star', family: :sharp_solid, class: icon_class) }
  nodes << fa_icon('star-half-stroke', family: :sharp_solid, class: icon_class) if half
  empty.times { nodes << fa_icon('star', family: :sharp_regular, class: icon_class) }
  safe_join(nodes)
end