File Coverage

blib/lib/Data/MuForm/Renderer/Base.pm
Criterion Covered Total %
statement 414 445 93.0
branch 78 110 70.9
condition 53 102 51.9
subroutine 48 51 94.1
pod 9 48 18.7
total 602 756 79.6


line stmt bran cond sub pod time code
1             package Data::MuForm::Renderer::Base;
2             # ABSTRACT: Renderer
3              
4 92     92   54361 use Moo;
  92         132  
  92         538  
5 92     92   21064 use List::Util ('any');
  92         1667  
  92         8125  
6 92     92   387 use Scalar::Util ('weaken');
  92         103  
  92         323215  
7              
8              
9             has 'form' => ( is => 'ro', weak_ref => 1 );
10              
11             has 'localizer' => ( is => 'ro' );
12              
13             has 'standard_layout' => ( is => 'rw', default => 'lbl_ele_err' );
14              
15             has 'cb_layout' => ( is => 'rw', default => 'cbwrlr' );
16              
17             has 'rdgo_layout' => ( is => 'rw', default => 'labels_right' );
18              
19             has 'cbgo_layout' => ( is => 'rw', default => 'labels_right' );
20              
21             has 'display_layout' => ( is => 'rw', default => 'span' );
22              
23             has 'field_wrapper' => ( is => 'rw', default => 'simple' );
24              
25             has 'wrapper_tag' => ( is => 'rw', default => 'div' );
26              
27             has 'error_tag' => ( is => 'rw', default => 'span' );
28              
29             has 'error_class' => ( is => 'rw', default => 'error_message' );
30              
31             has 'render_element_errors' => ( is => 'rw', default => 0 );
32              
33             has 'is_html5' => ( is => 'rw', default => 0 );
34              
35             sub BUILD {
36 166     166 0 135106 my $self = shift;
37 166 100       1307 if ( $self->form ) {
38 133         2234 weaken($self->{localizer});
39             }
40             }
41              
42             sub render_hook {
43 229     229 0 195 my $self = shift;
44 229         699 return $self->form->render_hook($self, @_);
45             }
46              
47              
48             sub localize {
49 87     87 0 115 my ( $self, @message ) = @_;
50 87         269 return $self->localizer->loc_($message[0]);
51             }
52              
53             #==============================
54             # Forms
55             #==============================
56              
57             sub render_form {
58 6     6 0 11 my ($self, $rargs, $fields ) = @_;
59              
60 6         8 my $out = '';
61 6         22 $out .= $self->render_start($rargs);
62 6         22 $out .= $self->render_form_errors($rargs);
63              
64 6         11 foreach my $field ( @$fields ) {
65 16         86 $out .= $field->render;
66             }
67              
68 6         24 $out .= $self->render_end($rargs);
69 6         36 return $out;
70             }
71              
72             sub render_start {
73 7     7 0 17 my ($self, $rargs ) = @_;
74              
75 7         14 my $name = $rargs->{name};
76 7         11 my $method = $rargs->{method};
77 7         12 my $out = qq{<form };
78 7         19 $out .= qq{id="$name" };
79 7         32 $out .= process_attrs($rargs->{form_attr}, ['id','name']);
80 7         22 $out .= q{>};
81             }
82              
83             sub render_end {
84 6     6 0 9 my ($self, $rargs ) = @_;
85              
86 6         11 return q{</form>};
87             }
88              
89             sub render_form_errors {
90 6     6 0 9 my ( $self, $rargs ) = @_;
91 6         8 my $out = '';
92 6 50       9 if ( scalar @{$rargs->{form_errors}} ) {
  6         18  
93 0         0 $out .= q{<div class="form_errors>};
94 0   0     0 my $error_tag = $rargs->{error_tag} || $self->error_tag;
95 0   0     0 my $error_class = $rargs->{error_class} || $self->error_class;
96 0         0 foreach my $error ( @{$rargs->{form_errors}} ) {
  0         0  
97 0         0 $out .= qq{\n<$error_tag class="$error_class">$error</$error_tag>};
98             }
99 0         0 $out .= q{</div>};
100             }
101 6         10 return $out;
102             }
103              
104             #==============================
105             # Fields
106             #==============================
107              
108             sub render_field {
109 61     61 0 2478 my ( $self, $rargs ) = @_;
110              
111 61         108 $rargs->{rendering} = 'field';
112 61         128 $self->render_hook($rargs);
113              
114 61         94 my $layout_type = $rargs->{layout_type};
115              
116 61         48 my $out;
117 61 100       296 if ( $layout_type eq 'checkbox' ) {
    100          
    100          
    100          
    100          
    100          
118 8         22 $out = $self->render_layout_checkbox($rargs);
119             }
120             elsif ( $layout_type eq 'checkboxgroup' ) {
121 1         5 $out = $self->render_layout_checkboxgroup($rargs);
122             }
123             elsif ( $layout_type eq 'radiogroup' ) {
124 1         4 $out = $self->render_layout_radiogroup($rargs);
125             }
126             elsif ( $layout_type eq 'element' ) { # submit, reset, hidden
127 7         16 $out = $self->render_element($rargs);
128             }
129             elsif ( $layout_type eq 'list' ) { # list
130 1         5 $out = $self->render_layout_list($rargs);
131             }
132             elsif ( $layout_type eq 'display' ) {
133 1         3 $out = $self->render_layout_display($rargs);
134             }
135             else { # $layout_type eq 'standard'
136 42         98 $out = $self->render_layout_standard($rargs);
137             }
138              
139 61         129 return $self->wrap_field($rargs, $out);
140             }
141              
142             sub wrap_field {
143 72     72 0 81 my ( $self, $rargs, $rendered ) = @_;
144              
145             # wrap the field
146 72   66     264 my $wrapper = $rargs->{wrapper} || $self->field_wrapper;
147 72 100       196 return $rendered if $wrapper eq 'none';
148 63   50     237 my $wrapper_meth = $self->can("wrapper_$wrapper") || die "wrapper method '$wrapper' not found";
149 63         119 my $out = $wrapper_meth->($self, $rargs, $rendered);
150 63         325 return $out;
151             }
152              
153             sub render_compound {
154 7     7 0 1062 my ( $self, $rargs, $fields ) = @_;
155              
156 7         11 my $out = '';
157 7 100       39 if ( $rargs->{is_instance} ) {
158 4         11 add_to_class($rargs, 'wrapper_attr', 'repinst');
159             }
160 7         53 $out .= $self->render_label($rargs);
161 7         15 foreach my $field ( @$fields ) {
162 18         76 $out .= $field->render;
163             }
164 7         17 $out = $self->wrap_field($rargs, $out);
165 7         44 return $out;
166             }
167              
168             sub render_repeatable {
169 4     4 0 117 my ( $self, $rargs, $fields ) = @_;
170 4         8 my $out = '';
171 4         15 $out .= $self->render_label($rargs);
172              
173 4         10 foreach my $field ( @$fields ) {
174 8         32 $out .= $field->render;
175             }
176 4         10 $out = $self->wrap_field($rargs, $out);
177 4         25 return $out;
178             }
179              
180             #==============================
181             # Utility methods
182             #==============================
183              
184              
185             sub add_to_class {
186 10     10 1 15 my ( $href, $attr_key, $class ) = @_;
187              
188 10 50       18 return unless defined $class;
189 10 100 66     38 if ( exists $href->{$attr_key}{class} && ref $href->{$attr_key}{class} ne 'ARRAY' ) {
190 2         6 my @classes = split(' ', $href->{$attr_key}{class});
191 2         3 $href->{$attr_key}{class} = \@classes;
192             }
193 10 50 33     40 if ( $class && ref $class eq 'ARRAY' ) {
194 0         0 push @{$href->{$attr_key}{class}}, @$class;
  0         0  
195             }
196             else {
197 10         15 push @{$href->{$attr_key}{class}}, $class;
  10         29  
198             }
199             }
200              
201              
202             sub process_attrs {
203 230     230 1 265 my ($attrs, $skip) = @_;
204              
205 230   100     571 $skip ||= [];
206 230         172 my @use_attrs;
207             my %skip;
208 230         396 @skip{@$skip} = ();
209 230         537 for my $attr ( sort keys %$attrs ) {
210 132 100       208 next if exists $skip{$attr};
211 91 50       161 next if $attr eq 'rendering';
212 91         79 my $value = '';
213 91 50       160 if( defined $attrs->{$attr} ) {
214 91 100       148 if( ref $attrs->{$attr} eq 'ARRAY' ) {
215             # we don't want class="" if no classes specified
216 21 50       24 next unless scalar @{$attrs->{$attr}};
  21         50  
217 21         25 $value = join (' ', @{$attrs->{$attr}} );
  21         37  
218             }
219             else {
220 70         74 $value = $attrs->{$attr};
221             }
222             }
223 91         284 push @use_attrs, sprintf( '%s="%s"', $attr, $value );
224             }
225 230         309 my $out = join( ' ', @use_attrs );
226 230         453 return $out;
227             }
228              
229             #==============================
230             # Field form elements
231             #==============================
232              
233              
234             sub render_input {
235 53     53 1 61 my ( $self, $rargs ) = @_;
236              
237 53         52 my $input_type = $rargs->{input_type};
238 53 50 66     161 if ( $self->is_html5 && $rargs->{html5_input_type} ) {
239 4         6 $input_type = $rargs->{html5_input_type};
240             }
241             # checkboxes are special
242 53 100       104 return $self->render_checkbox($rargs) if $input_type eq 'checkbox';
243              
244 50         52 my $name = $rargs->{name};
245 50         50 my $id = $rargs->{id};
246 50         91 my $fif = html_filter($rargs->{fif});
247              
248 50         79 my $out = qq{\n<input type="$input_type" };
249 50         83 $out .= qq{name="$name" };
250 50         65 $out .= qq{id="$id" };
251 50         76 $out .= qq{value="$fif" };
252 50 100       38 add_to_class( $rargs, 'element_attr', 'error' ) if @{$rargs->{errors}};
  50         114  
253 50         106 $out .= process_attrs($rargs->{element_attr});
254 50         61 $out .= "/>";
255 50         68 return $out;
256             }
257              
258              
259             sub render_select {
260 5     5 1 9 my ( $self, $rargs ) = @_;
261              
262 5         9 my $id = $rargs->{id};
263 5         7 my $name = $rargs->{name};
264 5         8 my $size = $rargs->{size};
265              
266             # beginning of select
267 5         10 my $out = qq{\n<select };
268 5         8 $out .= qq{name="$name" };
269 5         10 $out .= qq{id="$id" };
270 5 100       13 $out .= qq{multiple="multiple" } if $rargs->{multiple};
271 5 50       10 $out .= qq{size="$size" } if $size;
272 5 50       6 add_to_class( $rargs, 'element_attr', 'error' ) if @{$rargs->{errors}};
  5         18  
273 5         14 $out .= process_attrs($rargs->{element_attr});
274 5         10 $out .= ">";
275              
276             # render empty_select
277 5 100       14 if ( exists $rargs->{empty_select} ) {
278 3         8 my $label = $self->localize($rargs->{empty_select});
279 3         8 $out .= qq{\n<option value="">$label</option>};
280             }
281              
282             # render options
283 5         9 my $options = $rargs->{options};
284 5         12 foreach my $option ( @$options ) {
285 14 100       24 if ( my $label = $option->{group} ) {
286 3         4 $label = $self->localize( $label );
287 3         12 $out .= qq{\n<optgroup label="$label">};
288 3         3 foreach my $group_opt ( @{ $option->{options} } ) {
  3         5  
289 9         12 $out .= $self->render_select_option($rargs, $group_opt);
290             }
291 3         4 $out .= qq{\n</optgroup>};
292             }
293             else {
294 11         25 $out .= $self->render_select_option($rargs, $option);
295             }
296             }
297              
298             # end of select
299 5         9 $out .= "\n</select>\n";
300 5         9 return $out;
301             }
302              
303             sub render_select_option {
304 20     20 0 20 my ( $self, $rargs, $option ) = @_;
305              
306             # prepare for selected attribute
307 20         21 my $value = $option->{value};
308 20         18 my $multiple = $rargs->{multiple};
309 20   100     40 my $fif = $rargs->{fif} || [];
310 20         15 my %fif_lookup;
311 20 100       30 @fif_lookup{@$fif} = () if $multiple;
312              
313 20         26 my $label = $self->localize($option->{label});
314 20         22 my $out = '';
315 20         19 $out .= qq{\n<option };
316 20         12 my $attrs = $option;
317 20 50       35 $attrs = {%$attrs, %{$option->{attributes}}} if exists $option->{attributes};
  0         0  
318 20         41 $out .= process_attrs($attrs, ['label', 'order']);
319 20 100 100     138 if ( defined $fif && ( ($multiple && exists $fif_lookup{$value}) || ( $fif eq $value ) ) ) {
      33        
320 3         5 $out .= q{selected="selected" };
321             }
322 20         26 $out .= qq{>$label</option>};
323 20         48 return $out;
324             }
325              
326              
327              
328             sub render_textarea {
329 3     3 1 4 my ( $self, $rargs ) = @_;
330              
331 3         7 my $name = $rargs->{name};
332 3         4 my $id = $rargs->{id};
333 3         7 my $fif = html_filter($rargs->{fif});
334              
335 3         5 my $out = "\n<textarea ";
336 3         7 $out .= qq{name="$name" };
337 3         8 $out .= qq{id="$id" };
338 3 50       3 add_to_class( $rargs, 'element_attr', 'error' ) if @{$rargs->{errors}};
  3         10  
339 3         7 $out .= process_attrs($rargs->{element_attr});
340 3         9 $out .= ">$fif</textarea>";
341 3         4 return $out;
342             }
343              
344              
345             sub render_element {
346 58     58 1 816 my ( $self, $rargs ) = @_;
347              
348 58         83 my $do_errors = delete $rargs->{do_errors};
349 58         71 $rargs->{rendering} = 'element';
350 58         77 $self->render_hook($rargs);
351 58         66 my $form_element = $rargs->{form_element};
352 58         71 my $meth = "render_$form_element";
353 58         134 my $out = $self->$meth($rargs);
354             # this enables doing field.render_element without having to
355             # render the errors for each field.
356 58 100 100     194 if ( $self->render_element_errors && $do_errors ) {
357 6         13 $out .= $self->render_errors($rargs);
358             }
359 58         142 return $out;
360             }
361              
362              
363             sub render_label {
364 64     64 1 85 my ( $self, $rargs, $left_of_label, $right_of_label ) = @_;
365              
366 64 100       141 return '' if $rargs->{no_label};
367 59 100       112 return '' if $rargs->{is_contains};
368 55 100 100     179 return '' if $rargs->{wrapper} && $rargs->{wrapper} eq 'fieldset'; # this is kludgy :(
369 51         63 $rargs->{rendering} = 'label';
370 51         90 $self->render_hook($rargs);
371 51   100     319 $right_of_label ||= '';
372 51   100     143 $left_of_label ||= '';
373              
374 51         59 my $id = $rargs->{id};
375 51   33     168 my $label = $rargs->{display_label} || $self->localize($rargs->{label});
376 51         67 my $out = qq{\n<label };
377 51         105 $out .= qq{for="$id"};
378 51         180 $out .= process_attrs($rargs->{label_attr});
379 51         83 $out .= qq{>};
380 51         76 $out .= qq{$left_of_label$label$right_of_label};
381 51         74 $out .= qq{</label>};
382 51         102 return $out
383             }
384              
385              
386             sub render_errors {
387 59     59 1 87 my ( $self, $rargs ) = @_;
388              
389 59         74 $rargs->{rendering} = 'errors';
390 59         85 $self->render_hook($rargs);
391 59   50     139 my $errors = $rargs->{errors} || [];
392 59         57 my $out = '';
393 59   33     241 my $error_tag = $rargs->{error_tag} || $self->error_tag;
394 59   33     223 my $error_class = $rargs->{error_class} || $self->error_class;
395 59         95 foreach my $error (@$errors) {
396 7         27 $out .= qq{\n<$error_tag class="$error_class">$error</$error_tag>};
397             }
398 59         95 return $out;
399             }
400              
401             sub html_filter {
402 64     64 0 58 my $string = shift;
403 64 50       115 return '' if (!defined $string);
404 64         75 $string =~ s/&/&amp;/g;
405 64         64 $string =~ s/</&lt;/g;
406 64         52 $string =~ s/>/&gt;/g;
407 64         49 $string =~ s/"/&quot;/g;
408 64         92 return $string;
409             }
410              
411             sub render_option {
412 0     0 0 0 my ( $self, $rargs, $option ) = @_;
413 0         0 my $out = '';
414 0 0       0 if ( $rargs->{layout_type} eq 'standard' ) {
    0          
    0          
415 0         0 $out .= $self->render_select_option($rargs, $option);
416             }
417             elsif ( $rargs->{layout_type} eq 'checkboxgroup' ) {
418 0         0 $out .= $self->render_checkbox_option($rargs, $option);
419             }
420             elsif ( $rargs->{layout_type} eq 'radiogroup' ) {
421 0         0 $out .= $self->render_radio_option($rargs, $option);
422             }
423 0         0 return $out;
424             }
425              
426             #==============================
427             # Radio, Radiogroup
428             #==============================
429              
430             sub render_layout_radiogroup {
431 1     1 0 2 my ( $self, $rargs ) = @_;
432              
433 1   33     11 my $rdgo_layout = $rargs->{rdgo_layout} || $self->rdgo_layout;
434 1 50       8 my $rdgo_layout_meth = $self->can("rdgo_layout_$rdgo_layout") or die "Radio layout '$rdgo_layout' not found";
435              
436 1         4 my $out = $self->render_label($rargs);
437             # render options
438 1         2 my $options = $rargs->{options};
439 1         1 foreach my $option ( @$options ) {
440 2         3 $out .= $rdgo_layout_meth->($self, $rargs, $option);
441             }
442             # TODO - is this the best place for error messages for radiogroups?
443 1         3 $out .= $self->render_errors($rargs);
444 1         5 return $out;
445             }
446              
447             sub render_radio_option {
448 2     2 0 2 my ( $self, $rargs, $option ) = @_;
449              
450 2         3 my $name = $rargs->{name};
451 2         2 my $order = $option->{order};
452 2         1 my $out = qq{<input type="radio" };
453 2         4 $out .= qq{name="$name" };
454 2 50       4 $out .= qq{id="$name$order" } unless $option->{id};
455 2         1 my $attrs = $option;
456 2 50       4 $attrs = {%$attrs, %{$option->{attributes}}} if exists $option->{attributes};
  0         0  
457 2         4 $out .= process_attrs($attrs, ['label', 'order']);
458 2 50       5 if ( $rargs->{fif} eq $option->{value} ) {
459 0         0 $out .= qq{checked="checked" };
460             }
461 2         4 $out .= q{/>};
462             }
463              
464             sub rdgo_layout_labels_left {
465 0     0 0 0 my ( $self, $rargs, $option ) = @_;
466 0         0 my $rd_element = $self->render_radio_option($rargs, $option);
467 0         0 my $rd = $self->render_radio_label($rargs, $option, '', $rd_element);
468 0         0 my $out = $self->element_wrapper($rargs, $rd);
469 0         0 return $out;
470             }
471              
472             sub rdgo_layout_labels_right {
473 2     2 0 2 my ( $self, $rargs, $option ) = @_;
474 2         4 my $rd_element = $self->render_radio_option($rargs, $option);
475 2         3 my $rd = $self->render_radio_label($rargs, $option, $rd_element, '');
476 2         29 my $out = $self->element_wrapper($rargs, $rd);
477 2         4 return $out;
478             }
479              
480             sub render_radio_label {
481 2     2 0 3 my ( $self, $rargs, $option, $left_of_label, $right_of_label ) = @_;
482              
483 2   50     8 $right_of_label ||= '';
484 2   50     3 $left_of_label ||= '';
485 2         5 my $label = $self->localize($option->{label});
486              
487 2         4 my $attrs = { class => ['radio'] };
488 2 50       5 $attrs->{for} = $option->{id} ? $option->{id} : $rargs->{name} . $option->{order};
489             # add_to_class( $attrs, $rargs->{radio_label_class} );
490              
491 2         2 my $out = qq{\n<label };
492 2         4 $out.= process_attrs($attrs);
493 2         15 $out .= q{>};
494 2         7 $out .= qq{$left_of_label $label $right_of_label};
495 2         6 $out .= qq{</label>};
496             }
497              
498             #==============================
499             # Checkboxes
500             #==============================
501              
502             sub render_layout_checkbox {
503 8     8 0 9 my ( $self, $rargs) = @_;
504              
505 8         18 my $cb_element = $self->render_checkbox($rargs);
506 8   66     35 my $cb_layout = $rargs->{cb_layout} || $self->cb_layout;
507 8   50     49 my $meth = $self->can("cb_layout_$cb_layout") || die "Checkbox layout '$cb_layout' not found";
508 8         13 my $out = '';
509 8         21 $out = $meth->($self, $rargs, $cb_element);
510 8         24 $out .= $self->render_errors($rargs);
511              
512 8         18 return $out;
513              
514             }
515              
516              
517             sub render_checkbox {
518 11     11 1 13 my ( $self, $rargs ) = @_;
519              
520 11         18 my $name = $rargs->{name};
521 11         13 my $id = $rargs->{name};
522 11         15 my $checkbox_value = $rargs->{checkbox_value};
523 11         25 my $fif = html_filter($rargs->{fif});
524              
525 11         15 my $out = qq{<input };
526 11         18 $out .= qq{type="checkbox" };
527 11         20 $out .= qq{name="$name" };
528 11         15 $out .= qq{id="$id" };
529 11         15 $out .= qq{value="$checkbox_value" };
530 11 100       26 $out .= qq{checked="checked" } if $fif eq $checkbox_value;
531 11 50       12 add_to_class( $rargs, 'element_attr', 'error' ) if @{$rargs->{errors}};
  11         30  
532 11         33 $out .= process_attrs($rargs->{element_attr});
533 11         19 $out .= "/>";
534 11         24 return $out;
535             }
536              
537             sub render_checkbox_option {
538 3     3 0 3 my ( $self, $rargs, $option ) = @_;
539              
540             # prepare for checked attribute
541 3         6 my $multiple = $rargs->{multiple};
542 3   50     10 my $fif = $rargs->{fif} || [];
543 3         2 my %fif_lookup;
544 3 50       7 @fif_lookup{@$fif} = () if $multiple;
545              
546              
547 3         4 my $name = $rargs->{name};
548 3         2 my $value = $option->{value};
549 3         3 my $order = $option->{order};
550 3         2 my $out = qq{<input };
551 3         4 $out .= qq{type="checkbox" };
552 3         4 $out .= qq{name="$name" };
553 3 50       7 $out .= qq{id="$name$order" } unless $option->{id};
554 3 50 33     18 if ( defined $fif && ( ($multiple && exists $fif_lookup{$value}) || ( $fif eq $value ) ) ) {
      33        
555 0         0 $out .= q{checked="checked" };
556             }
557 3         4 my $attrs = $option;
558 3 50       5 $attrs = {%$attrs, %{$option->{attributes}}} if exists $option->{attributes};
  0         0  
559 3         10 $out .= process_attrs($attrs, ['label', 'order']);
560 3         3 $out .= "/>";
561 3         5 return $out;
562             }
563              
564             sub render_layout_checkboxgroup {
565 1     1 0 1 my ( $self, $rargs ) = @_;
566              
567 1         4 my $out = $self->render_label($rargs);
568 1   33     7 my $cbgo_layout = $rargs->{cbgo_layout} || $self->cbgo_layout;
569 1   50     7 my $cbgo_layout_meth = $self->can("cbgo_layout_$cbgo_layout")
570             || die "Checkbox group option layout '$cbgo_layout' not found";;
571             # render options
572 1         2 my $options = $rargs->{options};
573 1         2 foreach my $option ( @$options ) {
574 3         4 $out .= $cbgo_layout_meth->($self, $rargs, $option);
575             }
576             # TODO - is this the best place for error messages?
577 1         4 $out .= $self->render_errors($rargs);
578 1         1 return $out;
579             }
580              
581             sub cbgo_layout_labels_right {
582 3     3 0 4 my ( $self, $rargs, $option ) = @_;
583              
584 3         6 my $cb_element = $self->render_checkbox_option($rargs, $option);
585 3         6 my $cb = $self->render_checkbox_label($rargs, $option, $cb_element, '');
586 3         9 my $out .= $self->element_wrapper($rargs, $cb);
587 3         5 return $out;
588             }
589              
590             sub cbgo_layout_labels_left {
591 0     0 0 0 my ( $self, $rargs, $option ) = @_;
592 0         0 my $cb_element = $self->render_checkbox_option($rargs, $option);
593 0         0 my $cb = $self->render_checkbox_label($rargs, $option, '', $cb_element);
594 0         0 my $out .= $self->element_wrapper($rargs, $cb);
595 0         0 return $out;
596             }
597              
598             sub render_checkbox_label {
599 3     3 0 4 my ( $self, $rargs, $option, $left_of_label, $right_of_label ) = @_;
600              
601 3   50     9 $right_of_label ||= '';
602 3   50     4 $left_of_label ||= '';
603 3         6 my $label = $self->localize($option->{label});
604              
605 3         6 my $attrs = { class => ['checkbox'] };
606 3 50       10 $attrs->{for} = $option->{id} ? $option->{id} : $rargs->{name} . $option->{order};
607             # add_to_class( $attrs, $rargs->{checkbox_label_class} );
608              
609 3         3 my $out = qq{\n<label };
610 3         5 $out.= process_attrs($attrs);
611 3         4 $out .= q{>};
612 3         7 $out .= qq{$left_of_label $label $right_of_label};
613 3         6 $out .= qq{</label>};
614             }
615              
616              
617             sub cb_layout_cbwrll {
618 4     4 0 7 my ( $self, $rargs, $cb_element ) = @_;
619              
620 4         10 my $out = $self->render_label($rargs, '', $cb_element);
621 4         8 return $out
622              
623             }
624              
625              
626             sub cb_layout_cbwrlr {
627 2     2 0 4 my ( $self, $rargs, $cb_element ) = @_;
628              
629 2         5 my $out = $self->render_label($rargs, $cb_element, '' );
630 2         4 return $out;
631             }
632              
633              
634             sub cb_layout_cbnowrll {
635 1     1 0 3 my ( $self, $rargs, $cb_element ) = @_;
636              
637 1         4 my $out = $self->render_label($rargs);
638 1         3 $out .= $cb_element;
639 1         2 return $out;
640             }
641              
642             sub cb_layout_cb2l {
643 1     1 0 2 my ( $self, $rargs, $cb_element ) = @_;
644              
645 1         4 my $out = $self->render_label($rargs);
646              
647 1         3 my $id = $rargs->{id};
648 1   50     4 my $option_label = $self->localize($rargs->{option_label}) || '';
649 1         4 $out .= qq{\n<label for="$id">$cb_element$option_label</label>};
650 1         2 return $out;
651             }
652              
653             #==============================
654             # Layouts
655             #==============================
656              
657             sub render_layout_standard {
658 42     42 0 38 my ( $self, $rargs ) = @_;
659              
660             # render the field layout
661 42   66     227 my $layout = $rargs->{layout} || $self->standard_layout;
662 42         153 my $layout_meth = $self->can("layout_$layout");
663 42 50       105 die "layout method '$layout' not found" unless $layout_meth;
664 42         50 my $out = '';
665 42         75 $out .= $layout_meth->($self, $rargs);
666 42         82 return $out;
667             }
668              
669             sub layout_lbl_ele_err {
670 40     40 0 43 my ( $self, $rargs ) = @_;
671              
672 40         37 my $out = '';
673 40         80 $out .= $self->render_label($rargs);
674 40         97 $out .= $self->render_element($rargs);
675 40         90 $out .= $self->render_errors($rargs);
676 40         60 return $out;
677             }
678              
679             sub layout_lbl_wrele_err {
680 1     1 0 1 my ( $self, $rargs ) = @_;
681              
682 1         2 my $out = '';
683 1         3 $out .= $self->render_label($rargs);
684 1         3 my $ele .= $self->render_element($rargs);
685 1         5 $out .= $self->element_wrapper($rargs, $ele);
686 1         3 $out .= $self->render_errors($rargs);
687 1         3 return $out;
688             }
689              
690             sub layout_no_label {
691 1     1 0 2 my ( $self, $rargs ) = @_;
692 1         2 my $out = '';
693 1         4 $out .= $self->render_element($rargs);
694 1         3 $out .= $self->render_errors($rargs);
695 1         3 return $out;
696             }
697              
698             #==============================
699             # Wrappers
700             #==============================
701              
702             sub wrapper_simple {
703 59     59 0 68 my ( $self, $rargs, $rendered ) = @_;
704              
705 59   33     285 my $tag = $rargs->{wrapper_attr}{tag} || $self->wrapper_tag;
706 59         93 my $out = qq{\n<$tag };
707 59         164 $out .= process_attrs($rargs->{wrapper_attr}, ['tag']);
708 59         89 $out .= qq{>};
709 59         71 $out .= $rendered;
710 59         84 $out .= qq{\n</$tag>};
711 59         88 return $out;
712             }
713              
714             sub wrapper_fieldset {
715 4     4 0 4 my ( $self, $rargs, $rendered ) = @_;
716              
717 4 100       10 my $id = $rargs->{id} if ($rargs->{is_compound});
718 4         9 my $label = $self->localize($rargs->{label});
719 4         6 my $out = qq{\n<fieldset };
720 4 100       13 $out .= qq{id="$id" } if $id;
721 4         10 $out .= process_attrs($rargs->{wrapper_attr});
722 4         8 $out .= qq{>};
723 4         8 $out .= qq{<legend class="label">$label</legend>};
724 4         5 $out .= $rendered;
725 4         5 $out .= qq{\n</fieldset>};
726 4         6 return $out;
727             }
728              
729             sub element_wrapper {
730 9     9 0 12 my ( $self, $rargs, $rendered ) = @_;
731 9         10 my $out = qq{\n<div };
732 9         18 $out .= process_attrs($rargs->{element_wrapper_attr});
733 9         20 $out .= qq{>$rendered</div>};
734 9         14 return $out;
735             }
736              
737             sub render_layout_list {
738 1     1 0 1 my ( $self, $rargs ) = @_;
739              
740 1   50     3 my $fif = $rargs->{fif} || [];
741 1         2 my $size = $rargs->{size};
742 1   50     3 $size ||= (scalar @{$fif} || 0) + ($rargs->{num_extra} || 0);
      50        
      33        
743 1   50     5 $size ||= 2;
744 1         5 my $out = $self->render_label($rargs);
745 1         2 my $index = 0;
746 1         2 while ( $size ) {
747 3         3 my $value = shift @$fif;
748 3 50       4 $value = defined $value ? $value : '';
749 3         29 my $element = $self->render_input({%$rargs, fif => $value, id => $rargs->{id} . $index++ });
750 3         12 $out .= $self->element_wrapper($rargs, $element);
751 3         5 $size--;
752             }
753 1         2 return $out;
754             }
755              
756             sub render_layout_display {
757 1     1 0 2 my ( $self, $rargs ) = @_;
758              
759             # render the field layout
760 1   33     4 my $layout = $rargs->{layout} || $self->display_layout;
761 1         5 my $layout_meth = $self->can("layout_$layout");
762 1 50       3 die "layout method '$layout' not found" unless $layout_meth;
763 1         2 my $out = '';
764 1         3 $out .= $layout_meth->($self, $rargs);
765 1         2 return $out;
766             }
767              
768             sub layout_span {
769 1     1 0 1 my ( $self, $rargs ) = @_;
770              
771 1         2 my $out= '<span';
772 1         3 $out .= ' id="' . $rargs->{id} . '"';
773 1         4 $out .= process_attrs($rargs->{element_attr});
774 1         2 $out .= '>';
775 1         1 $out .= $rargs->{fif};
776 1         2 $out .= '</span>';
777 1         2 return $out;
778             }
779              
780             1;
781              
782             __END__
783              
784             =pod
785              
786             =encoding UTF-8
787              
788             =head1 NAME
789              
790             Data::MuForm::Renderer::Base - Renderer
791              
792             =head1 VERSION
793              
794             version 0.04
795              
796             =head1 DESCRIPTION
797              
798             Base functionality for renderers, including rendering standard form controls.
799              
800             You can use this base renderer if it does everything that you need. The
801             various attributes can be set in a form class:
802              
803             sub build_renderer_args { { error_tag => 'label' } }
804              
805             In many cases you might want to create your own custom renderer class which inherits
806             from this one. In it you can set the various standard defaults for your
807             rendering, and override some of the methods, like 'render_errors'. You can
808             also create custom layouts using the layout sub naming convention so they can
809             be found.
810              
811             There is a 'render_hook' which can be used to customize things like classes and
812             attributes. It is called on every 'render_field', 'render_element', 'render_errors',
813             and 'render_label' call. The hook can be used in a custom renderer or in the form class,
814             whichever is most appropriate.
815              
816             This base renderer is supplied as a library of useful routines. You could replace it
817             entirely if you want, as long you implement the methods that are used in the form
818             and field classes.
819              
820             The rendering is always done using a 'render_args' hashref of the pieces of the form
821             and field that are needed for rendering. Instead of having lots of rendering attributes
822             in the fields, with additional rendering pieces and settings in other attributes (like
823             the 'tags' hashref in FormHandler, most of the rendering settings are passed along
824             as keys in the render_args hashref.
825              
826             The 'render_args' can be defined in a field definition:
827              
828             has_field 'foo' => ( type => 'Text', render_args => { element => { class => ['abc', 'def'] }} );
829             or
830             has_field 'foo' => ( type => 'Text', 'ra.ea.class' => ['abc', 'def'] );
831              
832             Or passed in on a render call:
833              
834             $field->render({ 'ea.class' => 'abc' });
835              
836             If you have custom rendering code that depends on some new flag, you
837             can just start using a new render_args hashref key in your custom code
838             without having to do anything special to get it there. You have to set it somewhere
839             of course, either in the field definition or passed in on the rendering calls.
840              
841             For a particular field, the field class will supply a 'base_render_args', which is
842             merged with the 'render_args' from the field definition, which is merged with
843             the 'render_args' from the actual rendering call.
844              
845             One of the main goals of this particular rendering iteration has been to make
846             it easy and seamless to limit rendering to only the field elements, so that all
847             of the complicated divs and classes that are necessary for recent 'responsive'
848             CSS frameworks can be done in the templates under the control of the frontend
849             programmers.
850              
851             [% form.field('foo').render_element({ class => 'mb10 tye', placeholder => 'Type...'}) %]
852              
853             Or render the element, the errors and the labels, and do all of the other formatting
854             in the template:
855              
856             [% field = form.field('foo') %]
857             <div class="sm tx10">
858             [% field.render_label({ class="cxx" }) %]
859             <div class="xxx">
860             [% field.render_element({ class => 'mb10 tye', placeholder => 'Type...'}) %]
861             </div>
862             <div class="field-errors">[% field.render_errors %]</div>
863             </div>
864              
865             Another goal has been to make it possible to render a form automatically
866             and have it just work.
867              
868             [% form.render %]
869              
870             =head2 Renderer attributes
871              
872             In a form class:
873              
874             sub build_renderer_args { { error_tag => 'label', cb_layout => 'cbwrll' } }
875              
876             In a Renderer subclass:
877              
878             has '+error_tag' => ( default => 'label' );
879             has '+cb_layout' => ( default => 'cbwrll' );
880              
881             =over 4
882              
883             =item standard_layout
884              
885             For normal inputs and select fields.
886              
887             Provided:
888              
889             lbl_ele_err - label, element, error
890             lbl_wrele_err - label, wrapped element, error
891              
892             Create new layouts starting with 'layout_<name>'.
893              
894             =item cb_layout
895              
896             Default checkbox layout. Create new checkbox layouts with methods starting
897             with 'cb_layout_<name>'.
898              
899             Provided:
900              
901             cbwrll - checkbox, wrapped, label left
902             cbwrlr - checkbox, wrapped, label right
903             cbnowrll - checkbox, not wrapped, label left
904             cb2l - checkbox, 2 labels
905              
906             =item rdgo_layout
907              
908             Default radiogroup option layout
909              
910             Provided:
911              
912             labels_left
913             labels_right
914              
915             Supply more with 'rdgo_layout_<name>'.
916              
917             =item cbgo_layout
918              
919             Default checkbox group option layout
920              
921             Provided:
922              
923             labels_left
924             labels_right
925              
926             Supply more with 'cbgo_layout_<name>'.
927              
928             =item display_layout
929              
930             Default 'display' field layout.
931              
932             Provided:
933              
934             span
935              
936             Provide more options with 'layout_<name>'.
937              
938             =item field_wrapper
939              
940             Default field wrapper. Supply more with 'wrapper_<name>'.
941              
942             Provided:
943              
944             simple
945             fieldset
946              
947             =item wrapper_tag
948              
949             Default wrapper tag. Default: 'div'.
950              
951             =item error_tag
952              
953             Default error tag.
954              
955             =item error_class
956              
957             The default class added to the rendered errors.
958              
959             =item render_element_errors
960              
961             This is for when you are just doing 'render_element', but want to also render
962             the errors, without having to do a separate call. It's off by default.
963              
964             =item is_html5
965              
966             Render using the html5_input_type for the field (if one exists). Currently
967             the following fields have an html5_input_type: Currency (number), Date (date),
968             Email (email), Integer (number), URL (url). You can set the html5_input_type
969             in the field definition.
970              
971             =back
972              
973             =head1 NAME
974              
975             Data::MuForm::Renderer::Base
976              
977             =head1 Layouts
978              
979             The 'standard' layouts are for all fields that don't have another layout type.
980             This includes text fields, selects, and textareas. Create a new layout with 'layout_<layout-name'.
981             Included layouts are 'lbl_ele_err' and 'no_label'. The default is 'lbl_ele_err', which renders
982             a label, the form control, and then error messages.
983              
984             The 'radiogroup' layouts are for radiogroups, which is a 'Select' field layout type.
985             Create a new layout with 'rd_layout_<layout name>'.
986             Radiogroup option layouts are named 'rdgo_layout_<layout name>'. The provided layouts
987             are 'right_label' and 'left_label'.
988              
989             Checkbox layout methods are named 'cb_layout_<layout name>'. The provided layouts are
990             'cbwrll' - checkbox wrapped, label left, 'cbwrlr' - checkbox wrapped, label right,
991             'cb2l' - checkbox with two labels, 'cbnowrll' - checkbox unwrapped, label left.
992              
993             The checkbox group layouts are another 'Select' field layout type.
994             Checkbox group options layouts are named 'cbgo_layout_<layout name>'.
995              
996             =head1 Form and Field methods
997              
998             In the field:
999              
1000             render (render_field, render_compound or render_repeatable in the renderer)
1001             render_element (for standard single elements)
1002             render_label
1003             render_errors
1004              
1005             In the Select field:
1006              
1007             render_option (for select, radio group and checkbox group)
1008              
1009             In the form:
1010              
1011             render (render_form in the renderer)
1012             render_start
1013             render_end
1014             render_errors (render_form_errors in the renderer)
1015              
1016             =head2 add_to_class
1017              
1018             Utility class used to add to the 'class' key of an attribute hashref,
1019             handling arrayref/not-arrayref, etc. Used to add 'error' and 'required'
1020             classes.
1021              
1022             =head2 process_attrs
1023              
1024             Takes a hashref of key-value pairs to be rendered into HTML attributes.
1025             Second param ($skip) is keys to skip in the hashref.
1026              
1027             All 'values' are html filtered.
1028              
1029             =head2 render_input
1030              
1031             =head2 render_select
1032              
1033             =head2 render_textarea
1034              
1035             =head2 render_element
1036              
1037             =head2 render_label
1038              
1039             =head2 render_errors
1040              
1041             =head2 render_checkbox
1042              
1043             =head2 cbwrll
1044              
1045             Checkbox, wrapped, label left
1046              
1047             <label class="checkbox" for="option1"><input id="option1" name="option1" type="checkbox" value="1" /> Option1 </label>
1048              
1049             =head2 cbwrlr
1050              
1051             Checkbox wrapped, label right
1052              
1053             =head2 cbnowrll
1054              
1055             Checkbox not wrapped, label left
1056              
1057             =head1 AUTHOR
1058              
1059             Gerda Shank
1060              
1061             =head1 COPYRIGHT AND LICENSE
1062              
1063             This software is copyright (c) 2017 by Gerda Shank.
1064              
1065             This is free software; you can redistribute it and/or modify it under
1066             the same terms as the Perl 5 programming language system itself.
1067              
1068             =cut