File Coverage

blib/lib/Mojolicious/Plugin/FontAwesomeHelpers.pm
Criterion Covered Total %
statement 103 104 99.0
branch 62 66 93.9
condition 8 11 72.7
subroutine 13 13 100.0
pod 1 1 100.0
total 187 195 95.9


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::FontAwesomeHelpers;
2 1     1   1143060 use Mojo::Base 'Mojolicious::Plugin';
  1         4  
  1         10  
3              
4 1     1   260 use version; our $VERSION = version->declare('v0.1.0');
  1         3  
  1         9  
5              
6 1     1   171 use Carp ();
  1         2  
  1         40  
7 1     1   8 use Scalar::Util qw(blessed);
  1         2  
  1         90  
8 1     1   636 use subs qw(extract_block extract_flags);
  1         366  
  1         7  
9              
10             sub register {
11 1     1 1 55 my $self = shift;
12 1         2 my $app = shift;
13              
14 1         7 $app->helper('icon' => \&_icon);
15 1         147 $app->helper('fa_icon' => \&_icon);
16 1         91 $app->helper('fa.icon' => \&_icon);
17              
18 1         560 $app->helper('stacked_icon' => \&_stack);
19 1         117 $app->helper('fa_stack' => \&_stack);
20 1         88 $app->helper('fa.stack' => \&_stack);
21              
22 1     21   413 $app->helper('fa.class' => sub { shift; _fa_class(@_) });
  21         83920  
  21         69  
23             }
24              
25             sub _stack {
26 5     5   22572 my $c = shift;
27 5         34 my $content_class = _fa_class('fa-stack', @_);
28              
29 5         10 my $content;
30 5 50       14 if (@_ % 2 == 1) {
31 5         14 my $block = extract_block \@_;
32 5 100       17 $content = $block ? $block->() : pop;
33             }
34             else {
35 0         0 Carp::croak "content is required in the form of text or a block";
36             }
37              
38 5         17 my %html_options = @_;
39 5 100       14 $content_class .= " $html_options{class}" if $html_options{class};
40 5         11 $html_options{class} = $content_class;
41              
42 5         23 return $c->tag('span', class => $content_class, %html_options, $content);
43             }
44              
45             sub _icon {
46 9     9   40177 my $c = shift;
47 9         38 my $icon = shift;
48 9         27 my $content_class = _fa_class($icon, @_);
49              
50 9         20 my $text;
51 9 100       29 if (@_ % 2 == 1) {
52 4         15 my $block = extract_block \@_;
53 4 100       18 $text = $block ? $block->() : pop;
54             }
55              
56 9         30 my %html_options = @_;
57 9 100       30 $content_class .= " $html_options{class}" if $html_options{class};
58 9         24 $html_options{class} = $content_class;
59 9   50     59 $html_options{'aria-hidden'} //= 'true';
60              
61 9         49 my $html = $c->tag('i', %html_options);
62 9 100       1143 $html = $c->b($html . " $text") if $text;
63              
64 9         185 return $html;
65             }
66              
67             sub _fa_class {
68 35     35   68 my $icon = shift;
69 35 100       95 my $class = blessed($icon) ? _try($icon, 'fa_class') : $icon;
70 35         102 my %options = extract_flags \@_;
71              
72 35 100       120 $class .= " fa-$options{-size}" if $options{-size};
73 35 100       401 $class .= " fa-pull-$options{-pull}" if $options{-pull};
74 35 100       88 $class .= " fa-rotate-$options{-rotate}" if $options{-rotate};
75 35 100       130 $class .= " fa-stack-$options{-stack}" if $options{-stack};
76              
77 35   66     114 my $swap_opacity = defined $options{-opacity} && $options{-opacity} eq 'swap';
78 35 100       83 $class .= " fa-swap-opacity" if $swap_opacity;
79              
80 35   66     113 my $auto_width = defined $options{-width} && $options{-width} eq 'auto';
81 35 100       76 $class .= " fa-width-auto" if $auto_width;
82 35 100       90 $class .= " fa-inverse" if $options{-inverse};
83 35 100       75 $class .= " fa-bounce" if $options{-bounce};
84 35 100       80 $class .= " fa-shake" if $options{-shake};
85              
86              
87 35 100       90 if (my $spin = $options{-spin}) {
88 5 100       25 if ($spin eq 1) {
    100          
89 1         4 $class .= " fa-spin";
90             }
91             elsif (ref $spin eq 'ARRAY') {
92 1         6 $class .= " " . join(' ' => map {"fa-spin-$_"} @$spin);
  2         10  
93             }
94             else {
95 3         12 $class .= " fa-spin-$spin";
96             }
97             }
98              
99 35 100       82 if (my $flip = $options{-flip}) {
100 2 100       10 if ($flip eq 1) {
101 1         7 $class .= " fa-flip";
102             }
103             else {
104 1         4 $class .= " fa-flip-$flip";
105             }
106             }
107              
108 35 100 100     96 if ($options{-beat} && $options{-fade}) {
109 1         5 $class .= " fa-beat-fade";
110             }
111             else {
112 34 100       73 $class .= " fa-beat" if $options{-beat};
113 34 100       77 $class .= " fa-fade" if $options{-fade};
114             }
115              
116 35         139 $class;
117             }
118              
119             # Utils
120              
121             sub _try {
122 1     1   3 my ($value, $method_name) = @_;
123 1 50       4 return unless blessed($value);
124              
125 1 50       11 if (my $method = $value->can('fa_class')) {
126 1         5 return $value->$method();
127             }
128             }
129              
130             sub extract_block {
131 9     9   16 my $arrayref = shift;
132 9 100       32 return delete $arrayref->[-1] if ref $arrayref->[-1] eq 'CODE';
133 6         11 return undef;
134             }
135              
136             sub extract_flags {
137 35     35   66 my $arrayref = shift;
138 35         69 my %flags = ();
139              
140             ITEM:
141 35         124 for (my $i = 0; $i < @$arrayref; $i++) {
142 44         90 my $item = $arrayref->[$i];
143 44 100       167 if ($item =~ /^-/) {
144 15         57 $flags{$item} = $arrayref->[$i + 1];
145 15         31 splice @$arrayref, $i => 2;
146 15 100       49 last ITEM unless @$arrayref;
147 3         7 $i -= 2;
148 3         13 next ITEM;
149             }
150 29 100       99 if ($item =~ /^:/) {
151 9         35 $item =~ s/^:/-/;
152 9         33 $flags{"$item"} = 1;
153 9         24 splice @$arrayref, $i => 1;
154 9 100       58 last ITEM unless @$arrayref;
155 1         5 $i--;
156             }
157             }
158              
159 35 50       157 wantarray ? %flags : \%flags;
160             }
161              
162             1;
163              
164             =encoding utf8
165              
166             =head1 NAME
167              
168             Mojolicious::Plugin::FontAwesomeHelpers - Mojolicious helpers for Font Awesome icons
169              
170             =head1 SYNOPSIS
171              
172             # template.html.ep
173             %= icon 'fas fa-chevron' # =>
174             %= icon 'fas fa-chevron', 'Back' # => Back
175              
176             # ViewModel.pm
177             package ViewModel {
178             # ... other methods
179             sub fa_class { 'fas fa-circle' }
180             }
181              
182             # another_template.html.ep
183             %= icon ViewModel->new # =>
184             %= icon ViewModel->new, 'A Circle' # => A Circle
185              
186             =head1 HELPERS
187              
188             =head2 icon
189              
190             %= icon 'fas fa-chevron' # =>
191             %= icon 'fas fa-chevron', 'Back' # => Back
192              
193             %= icon ViewModel->new # =>
194             %= icon ViewModel->new, 'A Circle' # => A Circle
195              
196             %= icon 'fas fa-chevron', (id => 'back', class => 'fa-solid'), 'Back'
197             # => Back
198              
199             %= icon 'fas fa-chevron' => begin
200             Back
201             %= end
202             # => Back
203              
204             Renders a Font Awesome icon using an C tag. It will also handle accessibility
205             concerns.
206              
207             It can be used with two string arguments specifying the icon style and name
208             respectively. A third argument can be given to specify text that should be rendered
209             adjacent to the icon.
210              
211             If the first argument is an object that implements a C method then the
212             return value of that method will be used to specify the icon and a second argument,
213             if provided, will be rendered as text adjacent to the icon.
214              
215             All other arguments will be rendered as HTML attributes on the C tag.
216              
217             =head2 fa_icon
218              
219             %= fa_icon 'fas fa-circle' # => "
220              
221             An alias of L.
222              
223             =head2 fa->icon
224              
225             %= $c->fa->icon('fas fa-circle') # => "
226              
227             An alias of L.
228              
229             =head2 stacked_icon
230              
231             %= stacked_icon -size => '2x' => begin
232             %= icon 'fa-solid fa-square', -stack => '2x'
233             %= icon 'fab fa-twitter', -stack => '1x', :inverse
234             %= end
235              
236             =head2 fa_stack
237              
238             An alias of L
239              
240             =head2 fa->stack
241              
242             An alias of L
243              
244             =head2 fa->class
245              
246             %= $c->fa->class('fas fa-circle') # => "fas fa-circle"
247             %= $c->fa->class('fas fa-circle', -size => 'sm') # => "fas fa-circle fa-sm"
248              
249             %= $c->fa->class(ViewModel->new) # => "fas fa-circle"
250             %= $c->fa->class(ViewModel->new, -size => 'sm') # => "fas fa-circle fa-sm"
251              
252             =head3 Options
253              
254             =head4 -size
255              
256             =head4 -rotate
257              
258             =head4 -flip
259              
260             =head4 -stack
261              
262             =head4 -inverse
263              
264             =head4 -beat
265              
266             =head4 -fade
267              
268             =head4 -beat & -fade
269              
270             =head4 -bounce
271              
272             =head4 -shake
273              
274             =head4 -spin
275              
276             =head4 -pull
277              
278             =head4 -width
279              
280             =head4 -opacity
281              
282             =cut