File Coverage

blib/lib/Tags/HTML/Navigation/Grid.pm
Criterion Covered Total %
statement 54 54 100.0
branch 20 20 100.0
condition 6 6 100.0
subroutine 11 11 100.0
pod 1 1 100.0
total 92 92 100.0


line stmt bran cond sub pod time code
1             package Tags::HTML::Navigation::Grid;
2              
3 8     8   535157 use base qw(Tags::HTML);
  8         43  
  8         5814  
4 8     8   79593 use strict;
  8         20  
  8         185  
5 8     8   102 use warnings;
  8         13  
  8         2670  
6              
7 8     8   76 use Class::Utils qw(set_params split_params);
  8         20  
  8         449  
8 8     8   42 use Error::Pure qw(err);
  8         12  
  8         404  
9 8     8   48 use Scalar::Util qw(blessed);
  8         23  
  8         6497  
10              
11             our $VERSION = 0.02;
12              
13             # Constructor.
14             sub new {
15 22     22 1 2142253 my ($class, @params) = @_;
16              
17             # Create object.
18 22         141 my ($object_params_ar, $other_params_ar) = split_params(
19             ['css_class'], @params);
20 22         568 my $self = $class->SUPER::new(@{$other_params_ar});
  22         167  
21              
22             # CSS class.
23 18         693 $self->{'css_class'} = 'navigation';
24              
25             # Process params.
26 18         35 set_params($self, @{$object_params_ar});
  18         62  
27              
28 18 100       155 if (! defined $self->{'css_class'}) {
29 1         6 err "Parameter 'css_class' is required.";
30             }
31              
32             # Object.
33 17         131 return $self;
34             }
35              
36             sub _cleanup {
37 1     1   14 my $self = shift;
38              
39 1         3 delete $self->{'_items'};
40              
41 1         3 return;
42             }
43              
44             sub _init {
45 9     9   10686 my ($self, $items_ar) = @_;
46              
47 9 100       39 if (ref $items_ar ne 'ARRAY') {
48 1         13 err "Bad reference to array with items.";
49             }
50              
51 8         16 foreach my $item (@{$items_ar}) {
  8         21  
52 8 100 100     139 if (! defined $item
      100        
53             || ! blessed($item)
54             || ! $item->isa('Data::Navigation::Item')) {
55              
56 3         46 err "Item object must be a 'Data::Navigation::Item' instance.";
57             }
58             }
59              
60 5         18 $self->{'_items'} = $items_ar;
61              
62 5         14 return;
63             }
64              
65             # Process 'Tags'.
66             sub _process {
67 3     3   45 my $self = shift;
68              
69 3 100       12 if (! exists $self->{'_items'}) {
70 1         3 return;
71             }
72              
73             $self->{'tags'}->put(
74             ['b', 'nav'],
75 2         22 ['a', 'class', $self->{'css_class'}],
76             );
77 2         154 foreach my $item (@{$self->{'_items'}}) {
  2         6  
78 2 100       12 $self->{'tags'}->put(
    100          
    100          
    100          
    100          
79             ['b', 'div'],
80             defined $item->class ? (
81             ['a', 'class', $item->class],
82             ) : (
83             ['a', 'class', 'nav-item'],
84             ),
85             defined $item->location ? (
86             ['b', 'a'],
87             ['a', 'href', $item->location],
88             ) : (),
89             defined $item->image ? (
90             ['b', 'img'],
91             ['a', 'src', $item->image],
92             ['a', 'alt', $item->title],
93             ['e', 'img'],
94             ) : (),
95             ['b', 'div'],
96             ['a', 'class', 'title'],
97             ['d', $item->title],
98             ['e', 'div'],
99             defined $item->location ? (
100             ['e', 'a'],
101             ) : (),
102             defined $item->desc ? (
103             ['b', 'p'],
104             ['d', $item->desc],
105             ['e', 'p'],
106             ) : (),
107             ['e', 'div'],
108             );
109             }
110 2         623 $self->{'tags'}->put(
111             ['e', 'nav'],
112             );
113              
114 2         88 return;
115             }
116              
117             sub _process_css {
118 3     3   49 my $self = shift;
119              
120 3 100       11 if (! exists $self->{'_items'}) {
121 1         2 return;
122             }
123              
124             $self->{'css'}->put(
125 2         63 ['s', '.'.$self->{'css_class'}],
126             ['d', 'display', 'flex'],
127             ['d', 'flex-wrap', 'wrap'],
128             ['d', 'gap', '20px'],
129             ['d', 'padding', '20px'],
130             ['d', 'justify-content', 'center'],
131             ['e'],
132              
133             ['s', '.nav-item'],
134             ['d', 'display', 'flex'],
135             ['d', 'flex-direction', 'column'],
136             ['d', 'align-items', 'center'],
137             ['d', 'border', '2px solid #007BFF'],
138             ['d', 'border-radius', '15px'],
139             ['d', 'padding', '15px'],
140             ['d', 'width', '200px'],
141             ['e'],
142              
143             ['s', '.nav-item img'],
144             ['d', 'width', '100px'],
145             ['d', 'height', '100px'],
146             ['e'],
147              
148             ['s', '.nav-item div.title'],
149             ['d', 'margin', '10px 0'],
150             ['d', 'font-family', 'sans-serif'],
151             ['d', 'font-weight', 'bold'],
152             ['e'],
153              
154             ['s', '.nav-item '],
155             ['d', 'text-align', 'center'],
156             ['d', 'font-family', 'sans-serif'],
157             ['e'],
158             );
159              
160 2         1822 return;
161             }
162              
163             1;
164              
165             __END__
166              
167             =pod
168              
169             =encoding utf8
170              
171             =head1 NAME
172              
173             Tags::HTML::Navigation::Grid - Tags helper for navigation grid.
174              
175             =head1 SYNOPSIS
176              
177             use Tags::HTML::Navigation::Grid;
178              
179             my $obj = Tags::HTML::Navigation::Grid->new(%params);
180             $obj->cleanup;
181             $obj->init($items_ar);
182             $obj->prepare;
183             $obj->process;
184             $obj->process_css;
185              
186             =head1 DESCRIPTION
187              
188             L<Tags> helper to print HTML page of navigation grid.
189              
190             The page contains multiple boxes with title and optional image and description in box.
191             Each box could have link to other page.
192              
193             Items are defined by L<Data::Navigation::Item> instances.
194              
195             =head1 METHODS
196              
197             =head2 C<new>
198              
199             my $obj = Tags::HTML::Navigation::Grid->new(%params);
200              
201             Constructor.
202              
203             =over 8
204              
205             =item * C<css>
206              
207             L<CSS::Struct::Output> object for L<process_css> processing.
208              
209             Default value is undef.
210              
211             =item * C<css_class>
212              
213             CSS class for navigation grid.
214              
215             Default value is 'navigation'.
216              
217             =item * C<tags>
218              
219             L<Tags::Output> object.
220              
221             Default value is undef.
222              
223             =back
224              
225             Returns instance of object.
226              
227             =head2 C<cleanup>
228              
229             $obj->cleanup;
230              
231             Process cleanup after page run.
232              
233             Returns undef.
234              
235             =head2 C<init>
236              
237             $obj->init($items_ar);
238              
239             Initialize object.
240             Variable C<$items_ar> is reference to array with L<Data::Navigation::Item>
241             instances.
242              
243             Returns undef.
244              
245             =head2 C<prepare>
246              
247             $obj->prepare;
248              
249             Prepare object.
250             Do nothing in this object.
251              
252             Returns undef.
253              
254             =head2 C<process>
255              
256             $obj->process;
257              
258             Process L<Tags> structure for navigation grid.
259              
260             Returns undef.
261              
262             =head2 C<process_css>
263              
264             $obj->process_css;
265              
266             Process L<CSS::Struct> structure for navigation grid.
267              
268             Returns undef.
269              
270             =head1 ERRORS
271              
272             new():
273             From Class::Utils::set_params():
274             Unknown parameter '%s'.
275             From Tags::HTML::new():
276             Parameter 'css' must be a 'CSS::Struct::Output::*' class.
277             Parameter 'tags' must be a 'Tags::Output::*' class.
278              
279             init():
280             Bad reference to array with items.
281             Item object must be a 'Data::Navigation::Item' instance.
282              
283             process():
284             From Tags::HTML::process():
285             Parameter 'tags' isn't defined.
286              
287             process_css():
288             From Tags::HTML::process_css():
289             Parameter 'css' isn't defined.
290              
291             =head1 EXAMPLE1
292              
293             =for comment filename=print_grid_html_and_css.pl
294              
295             use strict;
296             use warnings;
297              
298             use CSS::Struct::Output::Indent;
299             use Data::Navigation::Item;
300             use Tags::HTML::Navigation::Grid;
301             use Tags::Output::Indent;
302              
303             # Object.
304             my $css = CSS::Struct::Output::Indent->new;
305             my $tags = Tags::Output::Indent->new;
306             my $obj = Tags::HTML::Navigation::Grid->new(
307             'css' => $css,
308             'tags' => $tags,
309             );
310              
311             my @items = (
312             Data::Navigation::Item->new(
313             'class' => 'nav-item1',
314             'desc' => 'This is description #1',
315             'id' => 1,
316             'image' => '/img/foo.png',
317             'location' => '/first',
318             'title' => 'First',
319             ),
320             Data::Navigation::Item->new(
321             'class' => 'nav-item2',
322             'desc' => 'This is description #2',
323             'id' => 2,
324             'image' => '/img/bar.png',
325             'location' => '/second',
326             'title' => 'Second',
327             ),
328             );
329             $obj->init(\@items);
330              
331             # Process login b.
332             $obj->process_css;
333             $obj->process;
334              
335             # Print out.
336             print "CSS\n";
337             print $css->flush."\n\n";
338             print "HTML\n";
339             print $tags->flush."\n";
340              
341             # Output:
342             # CSS
343             # .navigation {
344             # display: flex;
345             # flex-wrap: wrap;
346             # gap: 20px;
347             # padding: 20px;
348             # justify-content: center;
349             # }
350             # .nav-item {
351             # display: flex;
352             # flex-direction: column;
353             # align-items: center;
354             # border: 2px solid #007BFF;
355             # border-radius: 15px;
356             # padding: 15px;
357             # width: 200px;
358             # }
359             # .nav-item img {
360             # width: 100px;
361             # height: 100px;
362             # }
363             # .nav-item div.title {
364             # margin: 10px 0;
365             # font-family: sans-serif;
366             # font-weight: bold;
367             # }
368             # .nav-item {
369             # text-align: center;
370             # font-family: sans-serif;
371             # }
372             #
373             # HTML
374             # <nav class="navigation">
375             # <div class="nav-item1">
376             # <a href="/first">
377             # <img src="/img/foo.png" alt="First">
378             # </img>
379             # <div class="title">
380             # First
381             # </div>
382             # </a>
383             # <p>
384             # This is description #1
385             # </p>
386             # </div>
387             # <div class="nav-item2">
388             # <a href="/second">
389             # <img src="/img/bar.png" alt="Second">
390             # </img>
391             # <div class="title">
392             # Second
393             # </div>
394             # </a>
395             # <p>
396             # This is description #2
397             # </p>
398             # </div>
399             # </nav>
400              
401             =head1 EXAMPLE2
402              
403             =for comment filename=plack_app_nav_grid.pl
404              
405             use strict;
406             use warnings;
407            
408             use CSS::Struct::Output::Indent;
409             use Data::Navigation::Item;
410             use Plack::App::Tags::HTML;
411             use Plack::Builder;
412             use Plack::Runner;
413             use Tags::Output::Indent;
414              
415             # Plack application with foo SVG file.
416             my $svg_foo = <<'END';
417             <?xml version="1.0" ?>
418             <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="-1 -1 2 2">
419             <polygon points="0,-0.5 0.433,0.25 -0.433,0.25" fill="#FF6347"/>
420             <polygon points="0,-0.5 0.433,0.25 0,0.75" fill="#4682B4"/>
421             <polygon points="0.433,0.25 -0.433,0.25 0,0.75" fill="#32CD32"/>
422             <polygon points="0,-0.5 -0.433,0.25 0,0.75" fill="#FFD700"/>
423             </svg>
424             END
425             my $app_foo = sub {
426             return [
427             200,
428             ['Content-Type' => 'image/svg+xml'],
429             [$svg_foo],
430             ];
431             };
432              
433             # Plack application with bar SVG file.
434             my $svg_bar = <<'END';
435             <?xml version="1.0" ?>
436             <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
437             <polygon points="100,30 50,150 150,150" fill="#4682B4"/>
438             <polygon points="100,30 150,150 130,170" fill="#4682B4" opacity="0.9"/>
439             <polygon points="100,30 50,150 70,170" fill="#4682B4" opacity="0.9"/>
440             <polygon points="70,170 130,170 100,150" fill="#4682B4" opacity="0.8"/>
441             </svg>
442             END
443             my $app_bar = sub {
444             return [
445             200,
446             ['Content-Type' => 'image/svg+xml'],
447             [$svg_bar],
448             ];
449             };
450              
451             my $css = CSS::Struct::Output::Indent->new;
452             my $tags = Tags::Output::Indent->new(
453             'xml' => 1,
454             'preserved' => ['style'],
455             );
456              
457             # Navigation items.
458             my @items = (
459             Data::Navigation::Item->new(
460             'class' => 'nav-item',
461             'desc' => 'This is description #1',
462             'id' => 1,
463             'image' => '/img/foo.svg',
464             'location' => '/first',
465             'title' => 'First',
466             ),
467             Data::Navigation::Item->new(
468             'class' => 'nav-item',
469             'desc' => 'This is description #2',
470             'id' => 2,
471             'image' => '/img/bar.svg',
472             'location' => '/second',
473             'title' => 'Second',
474             ),
475             );
476              
477             # Plack application for grid.
478             my $app_grid = Plack::App::Tags::HTML->new(
479             'component' => 'Tags::HTML::Navigation::Grid',
480             'data_init' => [\@items],
481             'css' => $css,
482             'tags' => $tags,
483             )->to_app;
484              
485             # Runner.
486             my $builder = Plack::Builder->new;
487             $builder->mount('/img/foo.svg' => $app_foo);
488             $builder->mount('/img/bar.svg' => $app_bar);
489             $builder->mount('/' => $app_grid);
490             Plack::Runner->new->run($builder->to_app);
491              
492             # Output screenshot is in images/ directory.
493              
494             =begin html
495              
496             <a href="https://raw.githubusercontent.com/michal-josef-spacek/Tags-HTML-Navigation-Grid/master/images/plack_app_nav_grid.png">
497             <img src="https://raw.githubusercontent.com/michal-josef-spacek/Tags-HTML-Navigation-Grid/master/images/plack_app_nav_grid.png" alt="Web app example" width="300px" height="300px" />
498             </a>
499              
500             =end html
501              
502             =head1 DEPENDENCIES
503              
504             L<Class::Utils>,
505             L<Error::Pure>,
506             L<Scalar::Util>,
507             L<Tags::HTML>.
508              
509             =head1 SEE ALSO
510              
511             =over
512              
513             =item L<Tags::HTML::Login::Access>
514              
515             Tags helper for login access.
516              
517             =item L<Tags::HTML::Login::Button>
518              
519             Tags helper for login button.
520              
521             =item L<Tags::HTML::Login::Register>
522              
523             Tags helper for login register.
524              
525             =back
526              
527             =head1 REPOSITORY
528              
529             L<https://github.com/michal-josef-spacek/Tags-HTML-Navigation-Grid>
530              
531             =head1 AUTHOR
532              
533             Michal Josef Špaček L<mailto:skim@cpan.org>
534              
535             L<http://skim.cz>
536              
537             =head1 LICENSE AND COPYRIGHT
538              
539             © 2024 Michal Josef Špaček
540              
541             BSD 2-Clause License
542              
543             =head1 VERSION
544              
545             0.02
546              
547             =cut