listview ported bar pager
matthewt [Thu, 29 Nov 2007 19:05:13 +0000 (19:05 +0000)]
16 files changed:
Makefile.PL
lib/Reaction/UI/LayoutSet.pm
lib/Reaction/UI/ViewPort/GridView/Role/Order.pm
lib/Reaction/UI/Widget.pm
lib/Reaction/UI/Widget/GridView.pm
lib/Reaction/UI/Widget/GridView/Action.pm
lib/Reaction/UI/Widget/GridView/Entity.pm
lib/Reaction/UI/Widget/GridView/Entity/WithActions.pm
lib/Reaction/UI/Widget/ListView.pm
lib/Reaction/UI/Widget/Value.pm
lib/Reaction/UI/Widget/Value/Collection.pm
share/skin/default/layout/grid_view.tt
share/skin/default/layout/grid_view/action.tt
share/skin/default/layout/grid_view/entity.tt
share/skin/default/layout/grid_view/entity/with_actions.tt
share/skin/default/layout/list_view.tt

index ee71807..335d847 100644 (file)
@@ -28,7 +28,7 @@ requires 'Email::Send';
 requires 'Email::MIME';
 requires 'Email::MIME::Creator';
 requires 'Text::CSV_XS';
-requires 'Devel::Declare' => '0.001004';
+requires 'Devel::Declare' => '0.001006';
 
 catalyst;
 
index 68d23d1..4a9c533 100644 (file)
@@ -12,7 +12,11 @@ class LayoutSet which {
   has 'source_file' => (is => 'rw', lazy_fail => 1);
   has 'file_extension'=> (isa => 'Str', is => 'rw', lazy_build => 1);
 
-  has 'widget_class' => (is => 'rw', lazy_fail => 1);
+  has 'widget_class' => (
+    is => 'rw', lazy_fail => 1, predicate => 'has_widget_class'
+  );
+
+  has 'super' => (is => 'rw', predicate => 'has_super');
 
   implements _build_file_extension => as { 'html' };
 
@@ -20,46 +24,72 @@ class LayoutSet which {
     my ($self, $args) = @_;
     my @path = @{$args->{search_path}||[]};
     confess "No search_path provided" unless @path;
+    confess "No view object provided" unless $args->{view};
     my $found;
     my $ext = $self->file_extension;
     SEARCH: foreach my $path (@path) {
       my $cand = $path->file($self->name . ".${ext}");
       #print STDERR $cand,"\n";
       if ($cand->stat) {
-        $self->_load_file($cand);
+        $self->_load_file($cand, $args);
         $found = 1;
         last SEARCH;
       }
     }
     confess "Unable to load file for LayoutSet ".$self->name unless $found;
-    confess "No view object provided" unless $args->{view};
-    $self->widget_class($args->{view}->widget_class_for($self));
+    unless ($self->has_widget_class) {
+      $self->widget_class($args->{view}->widget_class_for($self));
+    }
   };
 
   implements 'widget_order_for' => as {
     my ($self, $name) = @_;
-    if ($self->has_layout($name)) {
-      return ([ $self->widget_class, $self ]);
-    } else {
-      return ();
-    }
+    return (
+      ($self->has_layout($name)
+        ? ([ $self->widget_class, $self ]) #;
+        : ()),
+      ($self->has_super
+        ? ($self->super->widget_order_for($name))
+        : ()),
+    );
   };
 
-  implements 'layout_names' => as { [ keys %{shift->layouts} ] };
+  implements 'layout_names' => as {
+    my ($self) = @_;
+    my %seen;
+    return [
+      grep { !$seen{$_}++ }
+        keys %{shift->layouts},
+        ($self->has_super
+          ? (@{$self->super->layout_names})
+          : ())
+    ];
+  };
 
   implements 'has_layout' => as { exists $_[0]->layouts->{$_[1]} };
 
   implements '_load_file' => as {
-    my ($self, $file) = @_;
+    my ($self, $file, $build_args) = @_;
     my $data = $file->slurp;
     my $layouts = $self->layouts;
     # cheesy match for "=for layout name ... =something"
     # final split group also handles last in file, (?==) is lookahead
     # assertion for '=' so "=for layout name1 ... =for layout name2"
     # doesn't have the match pos go past the latter = and lose name2
-    while ($data =~ m/=for layout (.*?)\n(.+?)(?:\n(?==)|$)/sg) {
-      my ($fname, $text) = ($1, $2);
-      $layouts->{$fname} = $text;
+    while ($data =~ m/=(.*?)\n(.*?)(?:\n(?==)|$)/sg) {
+      my ($data, $text) = ($1, $2);
+
+      if ($data =~ /^for layout (\S+)/) {
+        my $fname = $1;
+        $layouts->{$fname} = $text;
+      } elsif ($data =~ /^extends (\S+)/) {
+        my $super_name = $1;
+        $self->super($build_args->{view}->create_layout_set($super_name))
+      } elsif ($data =~ /^cut/) {
+        # no-op
+      } else {
+        confess "Unparseable directive ${data}";
+      }
     }
     $self->source_file($file);
   };
index 86b505e..2c5768d 100644 (file)
@@ -7,6 +7,17 @@ role Order, which {
   has order_by      => (isa => 'Str', is => 'rw', trigger_adopt('order_by'));
   has order_by_desc => (isa => 'Int', is => 'rw', trigger_adopt('order_by'), lazy_build => 1);
 
+  before order_by => sub {
+    if (@_ > 1) {
+      my ($self, $val) = @_;
+      confess "invalid column name for order_by"
+        unless grep { $val eq $_ } $self->collection
+                                        ->_source_resultset
+                                        ->result_source
+                                        ->columns;
+    }
+  };
+
   implements _build_order_by_desc => as { 0 };
 
   implements adopt_order_by => as {
index a5171d1..9d1e08e 100644 (file)
@@ -93,6 +93,7 @@ class Widget which {
     my ($self, $do_render, $args, $new_args) = @_;
     my $vp = $args->{'_'};
     my ($widget, $merge_args) = $self->view->render_viewport_args($vp);
+    delete @{$new_args}{keys %$new_args}; # fresh start
     @{$new_args}{keys %$merge_args} = values %$merge_args;
     $do_render->(Widget, $widget, 'widget');
   };
@@ -100,6 +101,7 @@ class Widget which {
   implements '_fragment_widget' => as {
     my ($self, $do_render, $args, $new_args) = @_;
     my $merge = $self->basic_layout_args;
+#warn "Merge: ".join(', ', keys %$merge)." into: ".join(', ', keys %$new_args);
     delete @{$merge}{keys %$new_args}; # nuke 'self' and 'viewport'
     @{$new_args}{keys %$merge} = values %$merge;
   };
index 1f73adb..c7c2530 100644 (file)
@@ -3,19 +3,23 @@ package Reaction::UI::Widget::GridView;
 use Reaction::UI::WidgetClass;
 
 class GridView, which {
-  fragment widget [ qw/header body footer/ ];
 
-  fragment header      [ 'header_row' ];
-  fragment header_row  [ header_cell => over func('viewport', 'field_order'),
-                        { labels => func(viewport => 'field_labels') } ];
-  fragment header_cell [ string { $_{labels}->{$_} } ], { field_name => $_ };
+  implements fragment header_cells {
+    arg 'labels' => $_{viewport}->field_labels;
+    render header_cell => over $_{viewport}->field_order;
+  };
 
-  fragment footer      [ 'footer_row' ];
-  fragment footer_row  [ footer_cell => over func('viewport', 'field_order'),
-                        { labels => func(viewport => 'field_labels') } ];
-  fragment footer_cell [ string { $_{labels}->{$_} } ], { field_name => $_ };
+  implements fragment body_rows {
+    render body_row => over $_{viewport}->entities;
+  };
 
-  fragment body        [ viewport => over func('viewport','entities')];
+  implements fragment body_row {
+    render 'viewport';
+  };
+
+  implements fragment header_cell {
+    arg label => $_{labels}->{$_};
+  };
 
 };
 
index a1988e5..d5336f3 100644 (file)
@@ -3,8 +3,12 @@ package Reaction::UI::Widget::GridView::Action;
 use Reaction::UI::WidgetClass;
 
 class Action, which {
-  fragment widget [ string{ "DUMMY" } ],
-    { uri => func(viewport => 'uri'), label => func(viewport => 'label') };
+
+  before fragment widget {
+    arg uri => $_{viewport}->uri;
+    arg label => $_{viewport}->label;
+  };
+
 };
 
 1;
index 1530bf5..3efc1a4 100644 (file)
@@ -3,12 +3,15 @@ package Reaction::UI::Widget::GridView::Entity;
 use Reaction::UI::WidgetClass;
 
 class Entity, which {
-  #this could be flattened if i could do:
-  # fragment widget [field => over sub{ $_{self}->viewport->fields } ];
-  #to be honest, I think that the key viewport should be available by default in %_
-  fragment widget     [ 'field_list' ];
-  fragment field_list [ field => over func('viewport', 'fields') ];
-  fragment field      [ 'viewport' ];
+
+  implements fragment field_list {
+    render 'field' => over $_{viewport}->fields;
+  };
+
+  implements fragment field {
+    render 'viewport';
+  };
+
 };
 
 1;
index 66db4b9..1684356 100644 (file)
@@ -2,14 +2,16 @@ package Reaction::UI::Widget::GridView::Entity::WithActions;
 
 use Reaction::UI::WidgetClass;
 
-#should I use inheritance here??
-class WithActions, which {
-  fragment widget     [ qw(field_list actions) ];
-  fragment field_list [ field => over func('viewport', 'fields') ];
-  fragment field      [ 'viewport' ];
-
-  fragment actions [ action => over func(viewport => 'actions')];
-  fragment action  [ 'viewport' ];
+class WithActions, is 'Reaction::UI::Widget::GridView::Entity', which {
+
+  implements fragment actions {
+    render action => over $_{viewport}->actions;
+  };
+  
+  implements fragment action {
+    render 'viewport';
+  };
+
 };
 
 1;
index 81b652e..9562900 100644 (file)
@@ -3,68 +3,32 @@ package Reaction::UI::Widget::ListView;
 use Reaction::UI::WidgetClass;
 
 class ListView is 'Reaction::UI::Widget::GridView', which {
-  fragment widget [ qw/pager header body footer actions/,
-                   {
-                    pager               => sub{ $_{viewport}->pager },
-                    object_action_count => sub{ $_{viewport}->object_action_count },
-                    #^^  it's ugly, i know, but i gotsto
-                   }
-                 ];
 
-  fragment pager
-    [ qw/first_page previous_page current_page next_page last_page page_list/,
-      {
-       first_page    => sub{ $_{pager}->first_page    },
-       previous_page => sub{ $_{pager}->previous_page || $_{pager}->last_page },
-       current_page  => sub{ $_{pager}->current_page  },
-       next_page     => sub{ $_{pager}->next_page || $_{pager}->first_page },
-       last_page     => sub{ $_{pager}->last_page     },
-       page_list     => sub{ [$_{pager}->first_page .. $_{pager}->last_page] },
-      }
-    ];
-
-  fragment first_page    [ string{ "First" } ],
-    { uri => sub{ $_{self}->connect_uri( {page => $_{first_page} }, $_{viewport} )    } };
-
-  fragment previous_page [ string{ "Previous" } ],
-    { uri => sub{ $_{self}->connect_uri( {page => $_{previous_page} }, $_{viewport} ) } };
-
-  fragment current_page  [ string{ "Current" } ],
-    { uri => sub{ $_{self}->connect_uri( {page => $_{current_page} }, $_{viewport} )  } };
-
-  fragment next_page     [ string{ "Next" } ],
-    { uri => sub{ $_{self}->connect_uri( {page => $_{next_page} }, $_{viewport} )     } };
-
-  fragment last_page     [ string{ "Last" } ],
-    { uri => sub{ $_{self}->connect_uri( {page => $_{last_page} }, $_{viewport} )     } };
-
-  fragment page_list [ page => over $_{page_list} ];
-  fragment page      [ string{ $_ } ],
-    { uri => sub{ $_{self}->connect_uri( {page => $_ }, $_{viewport} ) } };
+  implements fragment actions {
+    render action => over $_{viewport}->actions;
+  };
 
-  fragment actions [ action => over func(viewport => 'actions') ];
-  fragment action  [ 'viewport' ];
+  implements fragment action {
+    render 'viewport';
+  };
 
-  fragment header_cell [ string { $_{labels}->{$_} } ],
-    { uri => sub{
-        my $ev = {order_by => $_, order_by_desc => $_{viewport}->order_by_desc ? 0 : 1 };
-        return $_{self}->connect_uri($ev, $_{viewport});
-      }
+  around fragment header_cell {
+    arg order_uri => event_uri {
+      order_by => $_,
+      order_by_desc => ((($_{viewport}->order_by||'') ne $_
+                        || $_{viewport}->order_by_desc) ? 0 : 1)
     };
+    call_next;
+  };
 
-  fragment footer_cell [ string { $_{labels}->{$_} } ],
-    { uri => sub{
-        my $ev = {order_by => $_, order_by_desc => $_{viewport}->order_by_desc ? 0 : 1 };
-        return $_{self}->connect_uri($ev, $_{viewport});
-      }
-    };
+  after fragment header_cells {
+    if ($_{viewport}->object_action_count) {
+      render 'header_action_cell';
+    }
+  };
 
-  #this needs to be cleaned up and moved out
-  implements connect_uri => as{
-    my ($self, $events, $vp) = @_;
-    my $ctx  = $vp->ctx;
-    my %args = map{ $vp->event_id_for($_) => $events->{$_} } keys %$events;
-    return $ctx->req->uri_with(\%args);
+  implements fragment header_action_cell {
+    arg 'col_count' => $_{viewport}->object_action_count;
   };
 
 };
index 88d7eb6..48d362d 100644 (file)
@@ -3,12 +3,11 @@ package Reaction::UI::Widget::Value;
 use Reaction::UI::WidgetClass;
 
 class Value, which {
-  fragment widget [ string {""} ],
-    { value => sub{
-        my $vp = $_{viewport};
-        $vp->can('value_string') ? $vp->value_string : $vp->value;
-      }
-    };
+
+  before fragment widget {
+    arg value => $_{viewport}->value_string;
+  };
+
 };
 
 1;
index e87d881..d081216 100644 (file)
@@ -3,9 +3,19 @@ package Reaction::UI::Widget::Value::Collection;
 use Reaction::UI::WidgetClass;
 
 class Collection, which {
-  fragment widget [ qw/list/ ];
-  fragment list   [ item => over func('viewport', 'value_names') ];
-  fragment item   [ string {""} ], { value => $_ };
+
+  before fragment widget {
+    arg 'label' => $_{viewport}->label;
+  };
+
+  implements fragment list {
+    render 'item' => over $_{viewport}->value_names;
+  };
+
+  implements fragment item {
+    arg 'name' => $_;
+  };
+
 };
 
 1;
index 0015713..310fe66 100644 (file)
@@ -9,37 +9,37 @@
 =for layout header
 
 <thead>
-  [% content %]
+  [% header_row %]
 </thead>
 
 =for layout header_row
 
 <tr>
-  [% content %]
+  [% header_cells %]
 </tr>
 
 =for layout header_cell
 
-<th> [% content %] </th>
+<th> [% header_cell_contents %] </th>
+
+=for layout header_cell_contents
+
+[% label %]
 
 =for layout footer
 
 <tfoot>
-  [% content %]
+  [% footer_row %]
 </tfoot>
 
 =for layout footer_row
 
-<tr> [% content %] </tr>
-
-=for layout footer_cell
-
-<td> [% content %] </td>
+[% header_row %]
 
 =for layout body
 
 <tbody>
-  [% content %]
+  [% body_rows %]
 </tbody>
 
 =cut
index 4de7797..7c07d31 100644 (file)
@@ -1,3 +1,3 @@
 =for layout widget
-  <a href="[% uri %]">[% label | html %]</a>
+  <a href="[% uri %]">[% label %]</a>
 =cut
index c66af93..9cf24e2 100644 (file)
@@ -1,15 +1,11 @@
 =for layout widget
 
 <tr>
-  [% content %]
-<tr>
-
-=for layout field_list
-
-[% content %]
+  [% field_list %]
+</tr>
 
 =for layout field
 
-<td>[% content %]</td>
+<td>[% call_next %]</td>
 
 =cut
index 438a168..7f84147 100644 (file)
@@ -1,25 +1,12 @@
-=for layout widget
-
-<tr>
-  [% field_list %]
-  [% actions    %]
-<tr>
+=extends grid_view/entity
 
 =for layout field_list
 
-[% content %]
-
-=for layout field
-
-<td>[% content %]</td>
-
-=for layout actions
-
-[% content %]
+[% call_next %]
+[% actions %]
 
 =for layout action
 
-<td>[% content %]</td>
-
+<td>[% call_next %]</td>
 
 =cut
index 7cd3cc4..d74e2b4 100644 (file)
+=extends grid_view
+
 =for layout widget
 
 [% pager %]
 
-<table>
-  [% header %]
-  [% body   %]
-  [% footer %]
-</table>
+[% call_next %]
 
 [% pager %]
 
 [% actions %]
 
-=for layout header
-
-<thead>
-  [% content %]
-</thead>
-
-=for layout header_row
-
-<tr>
-  [% content %]
-  [% IF object_action_count %]
-  <th colspan="[% object_action_count %]"> Actions </th>
-  [% END %]
-</tr>
-
-=for layout header_cell
-
-<th> <a href="[% uri %]"> [% content | html %] </a> </th>
-
-=for layout footer
+=for layout header_action_cell
 
-<tfoot>
-  [% content %]
-</tfoot>
+<th colspan="[% col_count %]"> Actions </th>
 
-=for layout footer_row
+=for layout header_cell_contents
 
-<tr> 
-  [% content %] 
-  [% IF object_action_count %]
-  <th colspan="[% object_action_count %]"> Actions </th>
-  [% END %]
-</tr>
-
-=for layout footer_cell
-
-<th> <a href="[% uri %]"> [% content | html %] </a> </th>
-
-=for layout body
-
-<tbody>
-  [% content %]
-</tbody>
-
-=for layout pager
-
-<ul class="pager">
-  <li>[% first_page %]</li>
-  <li>[% previous_page %]</li>
-  <li>[% page_list %]</li>
-  <li>[% next_page %]</li>
-  <li>[% last_page %]</li>
-</ul>
-
-=for layout page_list
-[% content %]
-
-=for layout page
-<li> <a href="[% uri | html %]">[% content | html %]</a> </li>
-
-=for layout first_page
-<a href="[% uri | html %]">[% content | html %]</a>
-
-=for layout previous_page
-<a href="[% uri | html %]">[% content | html %]</a>
-
-=for layout current_page
-<a href="[% uri | html %]">[% content | html %]</a>
-
-=for layout next_page
-<a href="[% uri | html %]">[% content | html %]</a>
-
-=for layout last_page
-<a href="[% uri | html %]">[% content | html %]</a>
+<a href="[% order_uri %]">[% call_next %]</a>
 
 =for layout actions
 <div class="collection_actions">
 <ul>
-  [% content %]
+  [% call_next %]
 </ul>
 </div>
 
 =for layout action
-<li>[% content %]</li>
+<li>[% call_next %]</li>
+
+=for layout pager
+
+<p>Pager would be here. But it isn't.</p>
 
 =cut