File Coverage

blib/lib/HTML/FormFu/Role/Element/Input.pm
Criterion Covered Total %
statement 86 90 95.5
branch 24 36 66.6
condition n/a
subroutine 17 17 100.0
pod n/a
total 127 143 88.8


line stmt bran cond sub pod time code
1             package HTML::FormFu::Role::Element::Input;
2              
3 322     322   197693 use strict;
  322         487  
  322         13984  
4             our $VERSION = '2.05'; # VERSION
5              
6 322     322   1331 use Moose::Role;
  322         398  
  322         2394  
7              
8             with 'HTML::FormFu::Role::Element::Field',
9             'HTML::FormFu::Role::Element::FieldMethods' =>
10             { -excludes => 'nested_name' },
11             'HTML::FormFu::Role::Element::Coercible';
12              
13 322     322   1174172 use HTML::FormFu::Util qw( literal xml_escape );
  322         510  
  322         18010  
14 322     322   1426 use Clone ();
  322         412  
  322         7619  
15 322     322   1248 use List::Util 1.33 qw( none );
  322         9405  
  322         17785  
16 322     322   1386 use Scalar::Util qw( reftype );
  322         448  
  322         12640  
17 322     322   1333 use Carp qw( croak );
  322         417  
  322         12973  
18              
19 322         13619 use HTML::FormFu::Attribute qw(
20             mk_attr_accessors
21             mk_attr_bool_accessors
22 322     322   1372 );
  322         412  
23 322     322   1252 use HTML::FormFu::Constants qw( $EMPTY_STR );
  322         413  
  322         30782  
24 322     322   1299 use HTML::FormFu::Util qw( process_attrs xml_escape );
  322         486  
  322         360446  
25              
26             has field_type => (
27             is => 'rw',
28             );
29              
30             has datalist_id => ( is => 'rw' );
31              
32             has _datalist_options => (
33             is => 'rw',
34             default => sub { [] },
35             lazy => 1,
36             isa => 'ArrayRef',
37             );
38              
39             __PACKAGE__->mk_attr_accessors(qw(
40             alt autocomplete
41             checked maxlength
42             pattern placeholder
43             size
44             ));
45              
46             __PACKAGE__->mk_attr_bool_accessors(qw(
47             autofocus
48             multiple
49             required
50             ));
51              
52             my @ALLOWED_OPTION_KEYS = qw(
53             value
54             value_xml
55             value_loc
56             label
57             label_xml
58             label_loc
59             );
60              
61             sub datalist_options {
62 1     1   3 my ( $self, $arg ) = @_;
63 1         2 my ( @options, @new );
64              
65 1 50       6 return $self->_datalist_options if @_ == 1;
66              
67 1 50       4 croak "datalist_options argument must be a single array-ref" if @_ > 2;
68              
69 1 50       3 if ( defined $arg ) {
70 1 50       8 croak "datalist_options argument must be an array-ref"
71             if reftype($arg) ne 'ARRAY';
72              
73 1         4 @options = @$arg;
74              
75 1         21 for my $item (@options) {
76 4         14 push @new, $self->_parse_option($item);
77             }
78             }
79              
80 1         54 $self->_datalist_options( \@new );
81              
82 1         7 return $self;
83             }
84              
85             sub _parse_option {
86 4     4   7 my ( $self, $item ) = @_;
87              
88 4 100       42 if ( reftype($item) eq 'HASH' ) {
    50          
89 2         9 return $self->_parse_option_hashref($item);
90             }
91             elsif ( reftype($item) eq 'ARRAY' ) {
92             return {
93 2         13 value => $item->[0],
94             label => $item->[1],
95             };
96             }
97             else {
98 0         0 croak "each datalist_options argument must be a hash-ref or array-ref";
99             }
100             }
101              
102             sub _parse_option_hashref {
103 2     2   6 my ( $self, $item ) = @_;
104              
105             # sanity check options
106 2         17 my @keys = keys %$item;
107              
108 2         6 for my $key (@keys) {
109             croak "unknown option argument: '$key'"
110 4 50   13   33 if none { $key eq $_ } @ALLOWED_OPTION_KEYS;
  13         28  
111             }
112              
113 2 50       13 if ( defined $item->{label_xml} ) {
    100          
114 0         0 $item->{label} = literal( $item->{label_xml} );
115             }
116             elsif ( defined $item->{label_loc} ) {
117 1         17 $item->{label} = $self->form->localize( $item->{label_loc} );
118             }
119              
120 2 100       13 if ( defined $item->{value_xml} ) {
    50          
121 1         6 $item->{value} = literal( $item->{value_xml} );
122             }
123             elsif ( defined $item->{value_loc} ) {
124 0         0 $item->{value} = $self->form->localize( $item->{value_loc} );
125             }
126              
127 2 50       7 if ( !defined $item->{value} ) {
128 0         0 $item->{value} = '';
129             }
130              
131 2         9 return $item;
132             }
133              
134             sub datalist_values {
135 1     1   2 my ( $self, $arg ) = @_;
136              
137 1 50       4 croak "datalist_values argument must be a single array-ref of values" if @_ > 2;
138              
139 1         2 my @values;
140              
141 1 50       3 if ( defined $arg ) {
142 1 50       4 croak "datalist_values argument must be an array-ref"
143             if reftype($arg) ne 'ARRAY';
144              
145 1         4 @values = @$arg;
146             }
147              
148 1         1 my @new = map { {
149 3         10 value => $_,
150             label => ucfirst $_,
151             }
152             } @values;
153              
154 1         31 $self->_datalist_options( \@new );
155              
156 1         3 return $self;
157             }
158              
159             around prepare_id => sub {
160             my ( $orig, $self, $render ) = @_;
161              
162             $self->$orig($render);
163              
164             return if ! @{ $self->_datalist_options };
165              
166             if ( defined $render->{datalist_id} ) {
167             $render->{attributes}{list} = $render->{datalist_id};
168             }
169             elsif ( defined $self->auto_datalist_id
170             && length $self->auto_datalist_id )
171             {
172             my $form_name
173             = defined $self->form->id
174             ? $self->form->id
175             : $EMPTY_STR;
176              
177             my $field_name
178             = defined $render->{nested_name}
179             ? $render->{nested_name}
180             : $EMPTY_STR;
181              
182             my %string = (
183             f => $form_name,
184             n => $field_name,
185             );
186              
187             my $id = $self->auto_datalist_id;
188             $id =~ s/%([fn])/$string{$1}/g;
189              
190             if ( defined( my $count = $self->repeatable_count ) ) {
191             $id =~ s/%r/$count/g;
192             }
193              
194             $render->{attributes}{list} = $id;
195             }
196             else {
197             croak "either 'datalist_id' or 'auto_datalist_id' must be set when using a datalist";
198             }
199              
200             return;
201             };
202              
203             around render_data_non_recursive => sub {
204             my ( $orig, $self, $args ) = @_;
205              
206             my $render = $self->$orig( {
207             field_type => $self->field_type,
208             placeholder => $self->placeholder,
209             error_attributes => xml_escape( $self->error_attributes ),
210             error_container_attributes => xml_escape( $self->error_attributes ),
211             $args ? %$args : (),
212             } );
213              
214             if ( @{ $self->_datalist_options } ) {
215             $render->{datalist_options} = Clone::clone( $self->_datalist_options );
216             }
217              
218             $self->_quote_options( $render->{datalist_options} );
219              
220             return $render;
221             };
222              
223             sub _quote_options {
224 731     731   1557 my ( $self, $options ) = @_;
225              
226 731         1566 foreach my $opt (@$options) {
227 7         38 $opt->{label} = xml_escape( $opt->{label} );
228 7         14 $opt->{value} = xml_escape( $opt->{value} );
229             }
230             }
231              
232             sub _string_field {
233 578     578   647 my ( $self, $render ) = @_;
234              
235 578         696 my $html = "";
236              
237 578 100       1576 if ( $render->{datalist_options} ) {
238 2         12 $html .= sprintf qq{<datalist id="%s">\n}, $render->{attributes}{list};
239 2         63 for my $option ( @{ $render->{datalist_options} } ) {
  2         9  
240             $html .= sprintf qq{<option value="%s">%s</option>\n},
241             $option->{value},
242 7         18 $option->{label};
243             }
244 2         3 $html .= sprintf qq{</datalist>\n};
245             }
246              
247 578         792 $html .= "<input";
248              
249 578 100       1301 if ( defined $render->{nested_name} ) {
250 575         1614 $html .= sprintf qq{ name="%s"}, $render->{nested_name};
251             }
252              
253 578         1445 $html .= sprintf qq{ type="%s"}, $render->{field_type};
254              
255 578 100       2776 if ( defined $render->{value} ) {
256 355         685 $html .= sprintf qq{ value="%s"}, $render->{value};
257             }
258              
259 578         1468 $html .= sprintf "%s />", process_attrs( $render->{attributes} );
260              
261 578         1489 return $html;
262             }
263              
264             around clone => sub {
265             my ( $orig, $self ) = @_;
266              
267             my $clone = $self->$orig(@_);
268              
269             $clone->_datalist_options( Clone::clone( $self->_datalist_options ) );
270              
271             return $clone;
272             };
273              
274             1;
275              
276             __END__
277              
278             =head1 NAME
279              
280             HTML::FormFu::Role::Element::Input - Role for input fields
281              
282             =head1 VERSION
283              
284             version 2.05
285              
286             =head1 DESCRIPTION
287              
288             Base-class for L<HTML::FormFu::Element::Button>,
289             L<HTML::FormFu::Element::Checkbox>,
290             L<HTML::FormFu::Element::File>,
291             L<HTML::FormFu::Element::Hidden>,
292             L<HTML::FormFu::Element::Password>,
293             L<HTML::FormFu::Element::Radio>,
294             L<HTML::FormFu::Element::Text>.
295              
296             =head1 METHODS
297              
298             =head2 datalist_options
299              
300             Arguments: none
301              
302             Arguments: \@options
303              
304             Use either L</datalist_options> or L</datalist_values> to generate a
305             HTML5-compatible C<datalist> group of C<option> tags. This will be associated
306             with the C<input> element via a C<list> attribute on the C<input> tag.
307              
308             The C<datalist> ID attribute B<must> be set using either L</datalist_id>
309             or L</auto_datalist_id>.
310              
311             ---
312             elements:
313             - type: Text
314             name: foo
315             options:
316             - [ 01, January ]
317             - [ 02, February ]
318             - [ 03, March ]
319             - [ 04, April ]
320              
321             The syntax is similar to L<HTML::FormFu::Role::Element::Group/options>,
322             except hash-ref items only accept C<value> and C<label> keys (and their variants).
323              
324             If passed no arguments, it returns an arrayref of the currently set datalist options.
325              
326             Its arguments must be an array-ref of items. Each item may be an array ref
327             of the form C<[ $value, $label ]> or a hash-ref of the form
328             C<< { value => $value, label => $label } >>.
329              
330             When using the hash-ref construct, the C<label_xml> and C<label_loc>
331             variants of C<label> are supported, as are the C<value_xml> and C<value_loc>
332             variants of C<value>.
333              
334             =head2 datalist_values
335              
336             Arguments: \@values
337              
338             ---
339             elements:
340             - type: Radiogroup
341             name: foo
342             values:
343             - jan
344             - feb
345             - mar
346             - apr
347              
348             A more concise alternative to L</datalist_options>.
349              
350             Its arguments must be an array-ref of values. The labels used are the
351             result of C<ucfirst($value)>.
352              
353             =head2 datalist_id
354              
355             Arguments: [$string]
356              
357             Sets the C<datalist> ID attribute, and automatically sets this C<input> element's
358             C<list> ID to the same.
359              
360             Either L</datalist_id> or L</auto_datalist_id> is required,
361             if either L</datalist_options> or L</datalist_values> are set.
362              
363             =head2 auto_datalist_id
364              
365             See L<HTML::FormFu/auto_datalist_id> for details.
366              
367             =head1 ATTRIBUTE ACCESSORS
368              
369             Get / set input attributes directly with these methods.
370              
371             Arguments: [$string]
372              
373             Return Value: $string
374              
375             =head2 alt
376              
377             =head2 autocomplete
378              
379             =head2 checked
380              
381             =head2 maxlength
382              
383             =head2 pattern
384              
385             =head2 placeholder
386              
387             =head2 size
388              
389             =head1 BOOLEAN ATTRIBUTE ACCESSORS
390              
391             Arguments: [$bool]
392              
393             Return Value: $self
394             Return Value: $string
395             Return Value: undef
396              
397             Get / set boolean XHTML attributes such as C<required="required">.
398              
399             If given any true argument, the attribute value will be set equal to the attribute
400             key name. E.g. C<< $element->required(1) >> will set the attribute C<< required="required" >>.
401              
402             If given a false argument, the attribute key will be deleted.
403              
404             When used as a setter, the return value is C<< $self >> to allow chaining.
405              
406             =head2 autofocus
407              
408             =head2 multiple
409              
410             =head2 required
411              
412             =head1 SEE ALSO
413              
414             Is a sub-class of, and inherits methods from
415             L<HTML::FormFu::Role::Element::Field>, L<HTML::FormFu::Element>
416              
417             L<HTML::FormFu>
418              
419             =head1 AUTHOR
420              
421             Carl Franks, C<cfranks@cpan.org>
422              
423             =head1 LICENSE
424              
425             This library is free software, you can redistribute it and/or modify it under
426             the same terms as Perl itself.
427              
428             =cut