File Coverage

lib/HTML/FormWidgets/Table.pm
Criterion Covered Total %
statement 49 50 98.0
branch 10 20 50.0
condition 0 2 0.0
subroutine 5 5 100.0
pod 2 2 100.0
total 66 79 83.5


line stmt bran cond sub pod time code
1             package HTML::FormWidgets::Table;
2              
3 1     1   724 use strict;
  1         3  
  1         26  
4 1     1   5 use warnings;
  1         2  
  1         30  
5 1     1   4 use parent 'HTML::FormWidgets';
  1         2  
  1         5  
6              
7             __PACKAGE__->mk_accessors( qw( data edit hclass number_rows
8             select sortable table_class ) );
9              
10             my $HASH_CHAR = chr 35;
11             my $NBSP = ' ';
12             my $NUL = q();
13             my $SPC = q( );
14             my $TTS = q( ~ );
15              
16             # Private functions
17             my $_even_or_odd = sub {
18                return ($_[ 0 ] + 1) % 2 == 0 ? ' even' : ' odd';
19             };
20              
21             my $_column_class = sub {
22                return $_even_or_odd->( $_[ 0 ] ).'_col';
23             };
24              
25             my $_row_class = sub {
26                return $_even_or_odd->( $_[ 0 ] ).'_row';
27             };
28              
29             # Private methods
30             my $_add_row_count = sub {
31                my ($self, $n_rows) = @_;
32              
33                return $self->add_hidden( '_'.$self->name.'_nrows', $n_rows );
34             };
35              
36             my $_check_box = sub {
37                my ($self, $r_no, $c_no, $val) = @_; my $hacc = $self->hacc;
38              
39                my $class = 'row_select'.$_column_class->( $c_no );
40                my $args = { name => $self->name.".select${r_no}" };
41              
42                defined $val->{id} and $args->{value} = $val->{id};
43              
44                exists $val->{_meta} and exists $val->{_meta}->{select}
45                   and defined $val->{_meta}->{select}
46                   and $val->{_meta}->{select} eq 'checked'
47                   and $args->{checked} = 'checked';
48              
49                return $hacc->td( { class => $class }, $hacc->checkbox( $args ) );
50             };
51              
52             my $_drag_icon = sub {
53                my ($self, $c_no) = @_; my $hacc = $self->hacc;
54              
55                my $span = $hacc->span( { class => 'drag_icon' } );
56                my $class = 'row_drag'.$_column_class->( $c_no );
57              
58                return $hacc->td( { class => $class }, $span );
59             };
60              
61             my $_drag_header = sub {
62                my ($self, $c_no) = @_; my $name = "col${c_no}";
63              
64                my $args = { class => $self->hclass.' minimal',
65                             id => $self->id.".${name}" };
66              
67                return $self->hacc->th( $args, $self->loc( 'Drag' ) );
68             };
69              
70             my $_editable_cell = sub {
71                my ($self, $data, $field, $args, $c_no) = @_; my $hacc = $self->hacc;
72              
73                $args->{class} = $data->{classes}->{ $field } // $self->class;
74              
75                exists $data->{maxlengths}->{ $field }
76                   and $args->{maxlength } = $data->{maxlengths}->{ $field };
77              
78                my $map = { date => 'textfield',
79                                   money => 'textfield', numeric => 'textfield' };
80                my $type = $data->{typelist}->{ $field } || 'textfield';
81                my $el_type = defined $map->{ $type } ? $map->{ $type } : $type;
82                my $field_type = defined $map->{ $type } ? $type : $NUL;
83              
84                if ($el_type eq 'textarea') {
85                   $args->{cols} = $data->{cols}->{ $field } // 60;
86                   $args->{rows} = $data->{rows}->{ $field } // 1;
87                }
88                elsif ($el_type eq 'textfield') {
89                   exists $data->{sizes}->{ $field }
90                      and $args->{size } = $data->{sizes}->{ $field };
91                }
92              
93                my $class = 'data_field'.($field_type ? " ${field_type}" : $NUL);
94                   $class .= $_column_class->( $c_no );
95              
96                return $hacc->td( { class => $class }, $hacc->$el_type( $args ) );
97             };
98              
99             my $_row_number = sub {
100                my ($self, $row, $col) = @_; my $args = { class => 'lineNumber minimal' };
101              
102                $args->{class} .= $_column_class->( $col );
103              
104                return $self->hacc->td( $args, $row );
105             };
106              
107             my $_select_header = sub {
108                my ($self, $c_no) = @_; my $name = "col${c_no}";
109              
110                my $args = { class => $self->hclass, id => $self->id.".${name}" };
111              
112                $args->{class} .= $self->edit ? ' select' : ' minimal';
113              
114                return $self->hacc->th( $args, $self->loc( 'Select' ) );
115             };
116              
117             my $_sort_tip = sub {
118                my $self = shift;
119              
120                return $self->hint_title.$TTS.$self->loc( 'Sort table rows' );
121             };
122              
123             my $_add_edit_row = sub {
124                my ($self, $data, $r_no) = @_; my $hacc = $self->hacc;
125              
126                my $cells = $NUL; my $c_no = 0;
127              
128                for (0 .. $#{ $data->{fields} }) {
129                   my $args = { id => $self->id."_add${_}" };
130                   my $field = $data->{fields}->[ $_ ];
131              
132                   $args->{name} = '_'.$self->name."_${field}";
133                   $cells .= $self->$_editable_cell( $data, $field, $args, $c_no );
134                   $c_no++;
135                }
136              
137                my $add_tip = $self->hint_title.$TTS.$self->loc( 'Add table row' );
138                my $text = $hacc->span( { class => 'add_item_icon' }, $SPC );
139                my $args = { class => 'icon_button tips add',
140                                id => $self->id.'_add',
141                                title => $add_tip };
142              
143                $text = $hacc->span( $args, $text );
144              
145                my $rm_tip = $self->hint_title.$TTS.$self->loc( 'Remove table row' );
146                my $text1 = $hacc->span( { class => 'remove_item_icon' }, $SPC );
147              
148                $args = { class => 'icon_button tips remove',
149                                id => $self->id.'_remove',
150                                title => $rm_tip };
151                $text .= $hacc->span( $args, $text1 );
152                $text = $hacc->span( { class => 'table_edit_buttons' }, $text );
153                $cells .= $hacc->td( $text );
154              
155                my $class = ($data->{class} // 'edit_row').$_row_class->( $r_no );
156              
157                $args = { class => $class, id => $self->id.'_edit' };
158              
159                return $hacc->tr( $args, $cells );
160             };
161              
162             my $_field_header = sub {
163                my ($self, $data, $field, $c_no) = @_; my $name = "col${c_no}";
164              
165                my $args = { class => $self->hclass }; my $type = $NUL;
166              
167                if (exists $data->{hclass}->{ $field }) {
168                   $data->{hclass}->{ $field } eq 'hide' and return;
169                   $args->{class} .= $SPC.$data->{hclass}->{ $field };
170                }
171              
172                exists $data->{widths }->{ $field }
173                   and $args->{style } = 'width: '.$data->{widths}->{ $field }.';';
174                exists $data->{wrap }->{ $field } or $args->{class} .= ' nowrap';
175                exists $data->{typelist}->{ $field }
176                   and $type = $data->{typelist}->{ $field };
177              
178                $args->{id} = $self->id.".${name}".($type ? ".${type}" : $NUL);
179              
180                if ($self->sortable) {
181                   $args->{class} .= ' sort tips'; $args->{title} = $self->$_sort_tip;
182                }
183              
184                my $label = $data->{labels}->{ $field } // ucfirst $field;
185              
186                return $self->hacc->th( $args, $label );
187             };
188              
189             my $_number_header = sub {
190                my ($self, $c_no) = @_; my $name = "col${c_no}";
191              
192                my $args = { class => $self->hclass.' minimal',
193                             id => $self->id.".${name}.numeric" };
194              
195                if ($self->sortable) {
196                   $args->{class} .= ' sort tips'; $args->{title} = $self->$_sort_tip;
197                }
198              
199                return $self->hacc->th( $args, $HASH_CHAR );
200             };
201              
202             my $_render_row = sub {
203                my ($self, $data, $val, $r_no) = @_; my $hacc = $self->hacc;
204              
205                my $c_no = 0; my $cells = $NUL; my $first_value = $data->{values}->[ 0 ];
206              
207                $self->number_rows and $cells .= $self->$_row_number( $r_no + 1, $c_no++ );
208              
209                $self->edit eq 'left' and $first_value
210                   and $cells .= $self->$_drag_icon( $c_no++ );
211              
212                $self->select eq 'left' and $first_value
213                   and $cells .= $self->$_check_box( $r_no, $c_no++, $val );
214              
215                for my $field (@{ $data->{fields} }) {
216                   my $args = {};
217              
218                   if ($self->edit) {
219                      $args->{default} = $val->{ $field };
220                      $args->{name } = $self->name."_${r_no}_${c_no}";
221                      $cells .= $self->$_editable_cell( $data, $field, $args, $c_no );
222                   }
223                   else {
224                      exists $data->{hclass}->{ $field }
225                         and $data->{hclass}->{ $field } eq 'hide' and next;
226              
227                      my $class = $data->{class} // {}; my $fval = $val->{ $field } // $NBSP;
228              
229                      $args->{class} = (ref $class eq 'HASH' && $class->{ $field })
230                                      ? $class->{ $field } : $self->table_class.'_cell';
231                      exists $data->{typelist}->{ $field }
232                         and $args->{class } .= $SPC.$data->{typelist}->{ $field };
233                      exists $val->{_meta} and exists $val->{_meta}->{ $field }
234                                           and defined $val->{_meta}->{ $field }
235                         and $args->{class } .= $SPC.$val->{_meta}->{ $field };
236                      $args->{class} .= $_column_class->( $c_no );
237              
238                      $cells .= $hacc->td( $args, $self->inflate( $fval ) );
239                   }
240              
241                   $c_no++;
242                }
243              
244                $self->select eq 'right' and $first_value
245                   and $cells .= $self->$_check_box( $r_no, $c_no++, $val );
246              
247                $self->edit eq 'right' and $first_value
248                   and $cells .= $self->$_drag_icon( $c_no++ );
249              
250                my $class = $self->table_class.'_row'.$_row_class->( $r_no );
251              
252                $self->sortable and $class .= ' sortable_row';
253              
254                my $args = { class => $class, id => $self->id.".row${r_no}" };
255              
256                return $hacc->tr( $args, "\n".$cells );
257             };
258              
259             # Public methods
260             sub init {
261 1     1 1 2    my ($self, $args) = @_;
262              
263 1         7    $self->class ( 'ifield' );
264 1         9    $self->container ( 0 );
265 1         7    $self->data ( { fields => [], values => [] } );
266 1         14    $self->edit ( 0 );
267 1         6    $self->hclass ( 'std_header' );
268 1         5    $self->number_rows( 0 );
269 1         7    $self->select ( 0 );
270 1         9    $self->sortable ( 0 );
271 1         6    $self->table_class( undef );
272 1         5    return;
273             }
274              
275             sub render_field {
276 1     1 1 3    my ($self, $args) = @_;
277              
278 1         1    my $c_no = 0;
279 1         2    my $cells = $NUL;
280 1         3    my $data = $self->data;
281 1         6    my $hacc = $self->hacc;
282 1 50       5    my $class = $self->prompt ? 'editable' : 'std';
283                my $caption = $data->{caption}
284 1 50       7                ? "\n".$hacc->caption( $data->{caption} ) : $NUL;
285              
286 1 50       4    $self->table_class or $self->table_class( $class );
287 1 50 0     11    $self->id or $self->id( $self->name || 'table' );
288 1 50       9    $self->number_rows and $cells .= $self->$_number_header( $c_no++ );
289 1 50       8    $self->edit eq 'left' and $cells .= $self->$_drag_header ( $c_no++ );
290 1 50       8    $self->select eq 'left' and $cells .= $self->$_select_header( $c_no++ );
291              
292 1         5    for (@{ $data->{fields} }) {
  1         8  
293 0         0       $cells .= $self->$_field_header( $data, $_, $c_no++ );
294                }
295              
296 1 50       3    $self->select eq 'right' and $cells .= $self->$_select_header( $c_no++ );
297 1 50       8    $self->edit eq 'right' and $cells .= $self->$_drag_header ( $c_no++ );
298 1         7    $args = { class => $self->table_class.'_row' };
299              
300 1         11    my $thead = $hacc->thead( $hacc->tr( $args, $cells ) );
301              
302 1         51    my $r_no = 0; my $rows;
  1         1  
303              
304 1         2    $rows .= $self->$_render_row( $data, $_, $r_no++ ) for (@{ $data->{values}});
  1         5  
305              
306 1         32    my $tbody = $hacc->tbody( $rows ); $self->$_add_row_count( $r_no );
  1         18  
307              
308 1         2    my $tfoot = $NUL;
309              
310 1 50       4    $self->edit
311                   and $tfoot = $hacc->tfoot( $self->$_add_edit_row( $data, $r_no ) );
312              
313 1         7    $self->add_literal_js( 'tables', $self->id, {
314                   editSide => '"'.$self->edit.'"', selectSide => '"'.$self->select.'"' } );
315              
316 1         5    $args = { cellspacing => 0,
317                          class => $self->table_class.'_table',
318                          id => $self->id };
319              
320 1         13    return $hacc->table( $args, "${caption}\n${thead}\n${tfoot}\n${tbody}" );
321             }
322              
323             1;
324              
325             # Local Variables:
326             # mode: perl
327             # tab-width: 3
328             # End:
329