File Coverage

blib/lib/Tags/HTML/Form.pm
Criterion Covered Total %
statement 97 110 88.1
branch 35 72 48.6
condition 11 24 45.8
subroutine 15 18 83.3
pod 1 1 100.0
total 159 225 70.6


line stmt bran cond sub pod time code
1             package Tags::HTML::Form;
2              
3 5     5   163004 use base qw(Tags::HTML);
  5         44  
  5         2481  
4 5     5   35451 use strict;
  5         10  
  5         88  
5 5     5   22 use warnings;
  5         9  
  5         126  
6              
7 5     5   25 use Class::Utils qw(set_params split_params);
  5         8  
  5         207  
8 5     5   2033 use Data::HTML::Button;
  5         23052  
  5         139  
9 5     5   1374 use Data::HTML::Form;
  5         3106  
  5         157  
10 5     5   32 use Error::Pure qw(err);
  5         10  
  5         199  
11 5     5   29 use List::Util qw(first);
  5         9  
  5         231  
12 5     5   28 use Scalar::Util qw(blessed);
  5         10  
  5         182  
13 5     5   2240 use Tags::HTML::Form::Input;
  5         14  
  5         133  
14 5     5   2167 use Tags::HTML::Form::Select;
  5         12  
  5         7862  
15              
16             our $VERSION = 0.08;
17              
18             # Constructor.
19             sub new {
20 18     18 1 34634 my ($class, @params) = @_;
21              
22             # Create object.
23 18         86 my ($object_params_ar, $other_params_ar) = split_params(
24             ['form', 'input', 'select', 'submit'], @params);
25 18         467 my $self = $class->SUPER::new(@{$other_params_ar});
  18         70  
26              
27             # Form.
28 18         643 $self->{'form'} = Data::HTML::Form->new(
29             'css_class' => 'form',
30             );
31              
32             # Input object.
33 18         1301 $self->{'input'} = undef;
34              
35             # Select object.
36 18         38 $self->{'select'} = undef;
37              
38             # Submit.
39 18         85 $self->{'submit'} = Data::HTML::Button->new(
40             'data' => [
41             ['d', 'Save'],
42             ],
43             'data_type' => 'tags',
44             'type' => 'submit',
45             );
46              
47             # Process params.
48 18         2889 set_params($self, @{$object_params_ar});
  18         63  
49              
50             # Check form.
51 18 100       227 if (! defined $self->{'form'}) {
52 1         12 err "Parameter 'form' is required.";
53             }
54 17 100 66     142 if (! blessed($self->{'form'})
55             || ! $self->{'form'}->isa('Data::HTML::Form')) {
56              
57 1         5 err "Parameter 'form' must be a 'Data::HTML::Form' instance.";
58             }
59 16 100       57 if (! defined $self->{'form'}->{'css_class'}) {
60 1         8 err "Parameter 'form' must define 'css_class' parameter.";
61             }
62              
63             # Check submit.
64 15 100       37 if (! defined $self->{'submit'}) {
65 1         6 err "Parameter 'submit' is required.";
66             }
67 14 100 66     146 if (! blessed($self->{'submit'})
      100        
68             || (! $self->{'submit'}->isa('Data::HTML::Form::Input')
69             && ! $self->{'submit'}->isa('Data::HTML::Button'))) {
70              
71 1         10 err "Parameter 'submit' must be a 'Data::HTML::Form::Input' instance.";
72             }
73 13 100       49 if ($self->{'submit'}->type ne 'submit') {
74 1         14 err "Parameter 'submit' instance has bad type.";
75             }
76              
77             # Input object.
78 12 50       92 if (! defined $self->{'input'}) {
79             $self->{'input'} = Tags::HTML::Form::Input->new(
80             'css' => $self->{'css'},
81 12         56 'tags' => $self->{'tags'},
82             );
83             } else {
84 0 0 0     0 if (! blessed($self->{'input'}) || $self->{'input'}->isa('Tags::HTML::Form::Input')) {
85 0         0 err "Parameter 'input' must be a 'Tags::HTML::Form::Input' instance.";
86             }
87             }
88              
89             # Select object.
90 12 50       415 if (! defined $self->{'select'}) {
91             $self->{'select'} = Tags::HTML::Form::Select->new(
92             'css' => $self->{'css'},
93 12         49 'tags' => $self->{'tags'},
94             ),
95             } else {
96 0 0 0     0 if (! blessed($self->{'select'}) || $self->{'select'}->isa('Tags::HTML::Form::Select')) {
97 0         0 err "Parameter 'select' must be a 'Tags::HTML::Form::Select' instance.";
98             }
99             }
100              
101             # Object.
102 12         176 return $self;
103             }
104              
105             # Process 'Tags'.
106             sub _process {
107 7     7   276 my ($self, @fields) = @_;
108              
109             # Check fields.
110 7         15 foreach my $field (@fields) {
111 2 50 66     30 if (! defined $field
      0        
      66        
112             || ! blessed($field)
113             || (! $field->isa('Data::HTML::Form::Input')
114             && ! $field->isa('Data::HTML::Textarea')
115             && ! $field->isa('Data::HTML::Form::Select'))) {
116              
117 1         5 err "Form item must be a 'Data::HTML::Form::Input', ".
118             "'Data::HTML::Textarea' or 'Data::HTML::Form::Select' instance.";
119             }
120             }
121              
122             $self->{'tags'}->put(
123             ['b', 'form'],
124             defined $self->{'form'}->css_class ? (
125             ['a', 'class', $self->{'form'}->css_class],
126             ) : (),
127             defined $self->{'form'}->action ? (
128             ['a', 'action', $self->{'form'}->action],
129             ) : (),
130             ['a', 'method', $self->{'form'}->method],
131              
132             defined $self->{'form'}->{'label'} ? (
133             ['b', 'fieldset'],
134             ['b', 'legend'],
135 6 50       25 ['d', $self->{'form'}->{'label'}],
    50          
    100          
136             ['e', 'legend'],
137             ) : (),
138             );
139              
140 6 100       862 if (@fields) {
141 1         4 $self->{'tags'}->put(
142             ['b', 'p'],
143             );
144             }
145              
146 6         46 foreach my $field (@fields) {
147             $self->{'tags'}->put(
148             defined $field->label ? (
149             ['b', 'label'],
150             $field->id ? (
151             ['a', 'for', $field->id],
152             ) : (),
153             ['d', $field->label],
154             $field->required ? (
155             ['b', 'span'],
156 1 0       6 ['a', 'class', $self->{'form'}->css_class.'-required'],
    0          
    50          
157             ['d', '*'],
158             ['e', 'span'],
159             ) : (),
160             ['e', 'label'],
161             ) : (),
162             );
163              
164 1 50       19 if ($field->isa('Data::HTML::Form::Input')) {
    0          
165 1         4 $self->{'input'}->process($field);
166             } elsif ($field->isa('Data::HTML::Form::Select')) {
167 0         0 $self->{'select'}->process($field);
168             } else {
169 0         0 $self->_tags_textarea($field);
170             }
171             }
172              
173 6 100       17 if (@fields) {
174 1         6 $self->{'tags'}->put(
175             ['e', 'p'],
176             );
177             }
178              
179 6         54 $self->{'tags'}->put(
180             ['b', 'p'],
181             );
182 6 100       223 if ($self->{'submit'}->isa('Data::HTML::Form::Input')) {
183 1         9 $self->{'input'}->process($self->{'submit'});
184             } else {
185 5         12 $self->_tags_button($self->{'submit'});
186             }
187             $self->{'tags'}->put(
188             ['e', 'p'],
189              
190 6 100       33 defined $self->{'form'}->{'label'} ? (
191             ['e', 'fieldset'],
192             ) : (),
193             ['e', 'form'],
194             );
195              
196 6         414 return;
197             }
198              
199             sub _process_css {
200 1     1   16 my ($self, @fields) = @_;
201              
202             $self->{'css'}->put(
203             ['s', '.'.$self->{'form'}->css_class],
204             ['d', 'border-radius', '5px'],
205             ['d', 'background-color', '#f2f2f2'],
206             ['d', 'padding', '20px'],
207             ['e'],
208              
209             ['s', '.'.$self->{'form'}->css_class.' fieldset'],
210             ['d', 'padding', '20px'],
211             ['d', 'border-radius', '15px'],
212             ['e'],
213              
214             ['s', '.'.$self->{'form'}->css_class.' legend'],
215             ['d', 'padding-left', '10px'],
216             ['d', 'padding-right', '10px'],
217             ['e'],
218              
219             ['s', '.'.$self->{'form'}->css_class.' textarea'],
220             ['d', 'width', '100%'],
221             ['d', 'padding', '12px 20px'],
222             ['d', 'margin', '8px 0'],
223             ['d', 'display', 'inline-block'],
224             ['d', 'border', '1px solid #ccc'],
225             ['d', 'border-radius', '4px'],
226             ['d', 'box-sizing', 'border-box'],
227             ['e'],
228              
229 1         6 ['s', '.'.$self->{'form'}->css_class.'-required'],
230             ['d', 'color', 'red'],
231             ['e'],
232             );
233              
234             # TODO Different objects and different CSS?
235 1     0   718 my $first_input = first { ref $_ eq 'Data::HTML::Form::Input' } @fields;
  0         0  
236 1 50       6 if (defined $first_input) {
237 0         0 $self->{'input'}->process_css($first_input);
238             }
239 1     0   5 my $first_select = first { ref $_ eq 'Data::HTML::Form::Select' } @fields;
  0         0  
240 1 50       4 if (defined $first_select) {
241 0         0 $self->{'select'}->process_css($first_select);
242             }
243              
244             # CSS style for button.
245             # XXX Duplicit with Tags::HTML::Form::Input for submit.
246             $self->{'css'}->put(
247             ['s', '.'.$self->{'form'}->css_class.' button'],
248             ['d', 'width', '100%'],
249             ['d', 'background-color', '#4CAF50'],
250             ['d', 'color', 'white'],
251             ['d', 'padding', '14px 20px'],
252             ['d', 'margin', '8px 0'],
253             ['d', 'border', 'none'],
254             ['d', 'border-radius', '4px'],
255             ['d', 'cursor', 'pointer'],
256             ['e'],
257              
258 1         5 ['s', '.'.$self->{'form'}->css_class.' button:hover'],
259             ['d', 'background-color', '#45a049'],
260             ['e'],
261             );
262              
263 1         349 return;
264             }
265              
266             sub _tags_button {
267 5     5   11 my ($self, $object) = @_;
268              
269 5 50       18 $self->{'tags'}->put(
    50          
270             ['b', 'button'],
271             ['a', 'type', $object->type],
272             defined $object->name ? (
273             ['a', 'name', $object->name],
274             ) : (),
275             defined $object->value ? (
276             ['a', 'value', $object->value],
277             ) : (),
278             );
279 5 100       362 if ($object->data_type eq 'tags') {
280 4         21 $self->{'tags'}->put(@{$object->data});
  4         11  
281             } else {
282             $self->{'tags'}->put(
283 1         12 map { (['d', $_]) } @{$object->data},
  2         11  
  1         5  
284             );
285             }
286 5         194 $self->{'tags'}->put(
287             ['e', 'button'],
288             );
289              
290 5         192 return;
291             }
292              
293             sub _tags_textarea {
294 0     0     my ($self, $object) = @_;
295              
296 0 0         $self->{'tags'}->put(
    0          
    0          
    0          
    0          
    0          
    0          
    0          
297             ['b', 'textarea'],
298             defined $object->css_class ? (
299             ['a', 'class', $object->css_class],
300             ) : (),
301             defined $object->id ? (
302             ['a', 'name', $object->id],
303             ['a', 'id', $object->id],
304             ) : (),
305             defined $object->placeholder ? (
306             ['a', 'placeholder', $object->placeholder],
307             ) : (),
308             defined $object->readonly ? (
309             ['a', 'readonly', 'readonly'],
310             ) : (),
311             defined $object->disabled ? (
312             ['a', 'disabled', 'disabled'],
313             ) : (),
314             defined $object->cols ? (
315             ['a', 'cols', $object->cols],
316             ) : (),
317             defined $object->rows ? (
318             ['a', 'rows', $object->rows],
319             ) : (),
320             defined $object->value ? (
321             ['d', $object->value],
322             ) : (),
323             ['e', 'textarea'],
324             );
325              
326 0           return;
327             }
328              
329             1;
330              
331             __END__
332              
333             =pod
334              
335             =encoding utf8
336              
337             =head1 NAME
338              
339             Tags::HTML::Form - Tags helper for form.
340              
341             =head1 SYNOPSIS
342              
343             use Tags::HTML::Form;
344              
345             my $obj = Tags::HTML::Form->new(%params);
346             $obj->process(@fields);
347             $obj->process_css;
348              
349             =head1 METHODS
350              
351             =head2 C<new>
352              
353             my $obj = Tags::HTML::Form->new(%params);
354              
355             Constructor.
356              
357             =over 8
358              
359             =item * C<css>
360              
361             'L<CSS::Struct::Output>' object for L</process_css> processing.
362              
363             Default value is undef.
364              
365             =item * C<form>
366              
367             Data object for form.
368              
369             Could be a 'L<Data::HTML::Form>' instance.
370              
371             Default value is instance with 'form' css class.
372              
373             =item * C<submit>
374              
375             Data object for submit.
376              
377             Could be a 'L<Data::HTML::Form::Input>' or 'L<Data::HTML::Button>' instance.
378              
379             Default value is instance with 'Save' submit value.
380              
381             =item * C<tags>
382              
383             'L<Tags::Output>' object for L</process> processing.
384              
385             Default value is undef.
386              
387             =back
388              
389             =head2 C<process>
390              
391             $obj->process(@fields);
392              
393             Process L<Tags> structure for fields defined in C<@fields> to output.
394              
395             Accepted items in C<@fields> are objects:
396              
397             =over
398              
399             =item * L<Data::HTML::Form::Input>
400              
401             =item * L<Data::HTML::Textarea>
402              
403             =back
404              
405             Returns undef.
406              
407             =head2 C<process_css>
408              
409             $obj->process_css;
410              
411             Process L<CSS::Struct> structure for output.
412              
413             Returns undef.
414              
415             =head1 ERRORS
416              
417             new():
418             From Class::Utils::set_params():
419             Unknown parameter '%s'.
420             From Tags::HTML::new():
421             Parameter 'css' must be a 'CSS::Struct::Output::*' class.
422             Parameter 'tags' must be a 'Tags::Output::*' class.
423             Parameter 'form' is required.
424             Parameter 'form' must be a 'Data::HTML::Form' instance.
425             Parameter 'form' must define 'css_class' parameter.
426             Parameter 'input' must be a 'Tags::HTML::Form::Input' instance.
427             Parameter 'submit' instance has bad type.
428             Parameter 'submit' is required.
429             Parameter 'submit' must be a 'Data::HTML::Form::Input' instance.
430              
431             process():
432             From Tags::HTML::process():
433             Parameter 'tags' isn't defined.
434             Form item must be a 'Data::HTML::Form::Input' instance.
435              
436             process_css():
437             From Tags::HTML::process_css():
438             Parameter 'css' isn't defined.
439              
440             =head1 EXAMPLE
441              
442             =for comment filename=default_form.pl
443              
444             use strict;
445             use warnings;
446              
447             use CSS::Struct::Output::Indent;
448             use Tags::HTML::Form;
449             use Tags::Output::Indent;
450              
451             # Object.
452             my $css = CSS::Struct::Output::Indent->new;
453             my $tags = Tags::Output::Indent->new;
454             my $obj = Tags::HTML::Form->new(
455             'css' => $css,
456             'tags' => $tags,
457             );
458              
459             # Process form.
460             $obj->process;
461             $obj->process_css;
462              
463             # Print out.
464             print $tags->flush;
465             print "\n\n";
466             print $css->flush;
467              
468             # Output:
469             # <form class="form" method="GET">
470             # <p>
471             # <button type="submit">
472             # Save
473             # </button>
474             # </p>
475             # </form>
476             #
477             # .form {
478             # border-radius: 5px;
479             # background-color: #f2f2f2;
480             # padding: 20px;
481             # }
482             # .form input[type=submit]:hover {
483             # background-color: #45a049;
484             # }
485             # .form input[type=submit] {
486             # width: 100%;
487             # background-color: #4CAF50;
488             # color: white;
489             # padding: 14px 20px;
490             # margin: 8px 0;
491             # border: none;
492             # border-radius: 4px;
493             # cursor: pointer;
494             # }
495             # .form input, select, textarea {
496             # width: 100%;
497             # padding: 12px 20px;
498             # margin: 8px 0;
499             # display: inline-block;
500             # border: 1px solid #ccc;
501             # border-radius: 4px;
502             # box-sizing: border-box;
503             # }
504             # .form-required {
505             # color: red;
506             # }
507              
508             =head1 DEPENDENCIES
509              
510             L<Class::Utils>,
511             L<Data::HTML::Form>,
512             L<Data::HTML::Button>,
513             L<Error::Pure>,
514             L<List::Util>,
515             L<Scalar::Util>,
516             L<Tags::HTML>,
517             L<Tags::HTML::Form::Input>,
518             L<Tags::HTML::Form::Select>.
519              
520             =head1 REPOSITORY
521              
522             L<https://github.com/michal-josef-spacek/Tags-HTML-Form>
523              
524             =head1 AUTHOR
525              
526             Michal Josef Špaček L<mailto:skim@cpan.org>
527              
528             L<http://skim.cz>
529              
530             =head1 LICENSE AND COPYRIGHT
531              
532             © 2022-2023 Michal Josef Špaček
533              
534             BSD 2-Clause License
535              
536             =head1 VERSION
537              
538             0.08
539              
540             =cut