Free Signal Strength ViewComponent for Ruby On Rails

Recently I had the need for a signal strength UI indicator in a ruby on rails app and I didn’t see one anywhere so I had to make one from completely from scratch. In the spirit of giving back to the community, here is that code as a viewcomponent. It may need some tweaks to serve your own purposes but you may find it to be a great starting point.

Heres the final output:

#### signal_strength_component.rb

class SignalStrengthComponent < ViewComponent::Base
def initialize(signal_strength:, reported_at: nil)
super
@signal_strength = signal_strength.to_i # Ensure it's an integer
@reported_at = reported_at ? reported_at&.strftime('%B %e, %Y') : I18n.t('view_components.signal_strength.unavailable')
@cursor = 'text'

if @signal_strength.between?(0, 4)
@color_class = case @signal_strength
when 0, 1 then 'fill-red-600' # Low signal, red
when 2, 3 then 'fill-yellow-600' # Medium signal, yellow (or orange)
when 4 then 'fill-green-400' # Strong signal, green
else 'fill-gray-400' # Default if somehow out of range
end
@tooltip = I18n.t('view_components.signal_strength.reported_at', date: @reported_at, signal: @signal_strength)
else
@color_class = nil
@tooltip = nil
end
end

def signal_bar_class(bar_index)
@signal_strength >= bar_index ? @color_class : 'fill-gray-300'
end
end




#### signal_strength_component.html.erb

<% if @signal_strength.between?(0, 4) %>
<button data-tooltip-theme-value="light" data-tooltip-target="trigger" data-controller="tooltip" data-tooltip-content-value="<%= @tooltip %>" type="button" class="cursor-<%= @cursor %> inline-flex items-center text-gray-600 text-xs">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" class="h-4 w-4" stroke="currentColor" fill="currentColor">
<defs>
<style>
.fill-gray-300 { fill: rgba(209, 213, 219, 1); } /* Light gray for inactive bars */
.fill-red-600 { fill: rgba(220, 38, 38, 1); }
.fill-yellow-600 { fill: rgba(202, 138, 4, 1); } /* Adjusted to be more distinct than orange */
.fill-green-400 { fill: rgba(52, 211, 153, 1); }
</style>
</defs>

<%# Bar 1 (smallest) %>
<rect x="0" y="70" width="15" height="30" rx="3" class="<%= signal_bar_class(1) %>"/>
<%# Bar 2 %>
<rect x="25" y="50" width="15" height="50" rx="3" class="<%= signal_bar_class(2) %>"/>
<%# Bar 3 %>
<rect x="50" y="30" width="15" height="70" rx="3" class="<%= signal_bar_class(3) %>"/>
<%# Bar 4 (largest) %>
<rect x="75" y="10" width="15" height="90" rx="3" class="<%= signal_bar_class(4) %>"/>

<% if @signal_strength == 0 %>
<%# No signal icon (e.g., a cross or a specific icon indicating no signal) %>
<%# For now, just show all bars gray, but you could add a specific path here %>
<% end %>
</svg>
<span class="ml-2"><%= t('view_components.signal_strength.level', signal: @signal_strength) %></span>
</button>
<% else %>
<span class="z-badge z-badge-gray cursor-<%= @cursor %>"><%= t('view_components.signal_strength.not_available') %></span>
<% end %>

#### i18n

signal_strength:
unavailable: "Not available"
not_available: "N/A"
level: "%{signal} Bars"
reported_at: "Signal Strength: %{signal} Bars on %{date}"

#### /show.html.erb

<%= render SignalStrengthComponent.new(signal_strength: 4, reported_at: 1.day.ago) %>

<%= render SignalStrengthComponent.new(signal_strength: 3, reported_at: 2.days.ago) %>

<%= render SignalStrengthComponent.new(signal_strength: 2, reported_at: 3.days.ago) %>

<%= render SignalStrengthComponent.new(signal_strength: 1, reported_at: 4.days.ago) %>

<%= render SignalStrengthComponent.new(signal_strength: 0, reported_at: 5.days.ago) %>