1 package SL::Layout::ActionBar;
 
   4 use parent qw(SL::Layout::Base);
 
   7 use Scalar::Util qw(blessed);
 
   8 use SL::Layout::ActionBar::Action;
 
   9 use SL::Layout::ActionBar::ComboBox;
 
  10 use SL::Layout::ActionBar::Link;
 
  11 use SL::Layout::ActionBar::Separator;
 
  13 use constant HTML_CLASS => 'layout-actionbar';
 
  15 use Rose::Object::MakeMethods::Generic (
 
  16   'scalar --get_set_init' => [ qw(actions) ],
 
  19 my %class_descriptors = (
 
  20   action    => { class => 'SL::Layout::ActionBar::Action',    num_params => 1, },
 
  21   combobox  => { class => 'SL::Layout::ActionBar::ComboBox',  num_params => 1, },
 
  22   link      => { class => 'SL::Layout::ActionBar::Link',      num_params => 1, },
 
  23   separator => { class => 'SL::Layout::ActionBar::Separator', num_params => 0, },
 
  26 ###### Layout overrides
 
  31   my $content = join '', map { $_->render } @{ $self->actions };
 
  33   $::request->presenter->html_tag('div', $content, class => HTML_CLASS);
 
  36 sub javascripts_inline {
 
  37   join '', map { $_->script } @{ $_[0]->actions };
 
  47   my ($self, @actions) = @_;
 
  49   push @{ $self->actions }, $self->parse_actions(@actions);
 
  51   return $self->actions->[-1];
 
  55   my ($self_or_class, @actions) = @_;
 
  59   while (my $type = shift(@actions)) {
 
  60     if (blessed($type) && $type->isa('SL::Layout::ActionBar::Action')) {
 
  65     my $descriptor = $class_descriptors{lc $type} || croak("Unknown action type '${type}'");
 
  66     my @params     = splice(@actions, 0, $descriptor->{num_params});
 
  68     push @parsed, $descriptor->{class}->from_params(@params);
 
  86 SL::Layout::ActionBar - Unified action buttons for controllers
 
  90   # short sugared syntax:
 
  91   for my $bar ($::request->layout->get('actionbar')) {
 
  95         call      => [ 'kivi.Javascript.function', @arguments ],
 
  97         disabled  => $tooltip_with_reason_or_falsish,
 
  98         only_if   => $precomputed_condition,
 
  99         not_if    => $precomputed_condition,
 
 100         id        => 'html-element-id',
 
 116   # full syntax without sugar
 
 117   for my $bar ($::request->layout->get('actionbar')) {
 
 119       (SL::Layout::ActionBar::Action->new(
 
 120         text => t8('Description'),
 
 122           call      => [ 'kivi.Javascript.function', @arguments ],
 
 123           accesskey => 'enter',
 
 124           disabled  => $tooltip_with_reason_or_falsish,
 
 126       )) x(!!$only_id && !$not_if),
 
 127       SL::Layout::ActionBar::ComboBox->new(
 
 129           SL::Layout::ActionBar::Action->new(...),
 
 130           SL::Layout::ActionBar::Action->new(...),
 
 131           SL::Layout::ActionBar::Action->new(...),
 
 132           SL::Layout::ActionBar::Action->new(...),
 
 135       SL::Layout::ActionBar::Link->new(
 
 136         text => t8('Description'),
 
 141       SL::Layout::ActionBar::Separator->new,
 
 147 This is a layout block that creates an action bar for any controller who
 
 148 wants to use it. It's designed to be rendered above the content and to be
 
 149 fixed when scrolling. It's structured as a container for elements that can be
 
 150 extended when needed.
 
 158 Will be used during initialization of the layout. You should never have to
 
 159 instanciate an action bar yourself. Get the current request instances from
 
 161   $::request->layout->get('actionbar')
 
 167 Add new elements to the bar. Can be instances of
 
 168 L<SL::Layout::ActionBar::Action> or scalar strings matching the sugar syntax
 
 169 which is described further down.
 
 173 =head1 SYNTACTIC SUGAR
 
 175 Instead of passing full objects to L</add>, you can instead pass the arguments
 
 176 to be used for instantiation to make the code easier to read. The short syntax
 
 180     localized_description,
 
 186 A string type, followed by the parameters needed for that type. Type may be one of:
 
 200 C<separator> will use no parameters, the other three will expect one arrayref.
 
 202 Two additional pseudo parameters are supported for those:
 
 212 These are meant to reduce enterprise operators (C<()x!!>) when conditionally adding lots
 
 215 The combobox element is in itself a container and will simply expect the same
 
 216 syntax in an arrayref.
 
 218 For the full list of parameters supported by the elements, see L<SL::Layout::ActionBar::Action/RECOGNIZED PARAMETERS>.
 
 223 The current implementation follows these design guidelines:
 
 229 Don't put too many elements into the action bar. Group into comboboxes if
 
 230 possible. Consider seven elements a reasonable limit.
 
 234 If you've got an update button, put it first and bind the enter accesskey to
 
 239 Put mutating actions (save, post, delete, check out, ship) before the separator
 
 240 and non mutating actions (export, search, history, workflow) after the
 
 241 separator. Combined actions (save and close) still mutate and go before the
 
 246 Avoid abusing the actionbar as a secondary menu. As a principle every action
 
 247 should act upon the current element or topic.
 
 251 Hide elements with C<only_if> if they are known to be useless for the current
 
 252 topic, but disable when they would be useful in principle but are not
 
 253 applicable right now. For example C<delete> does not make sense in a creating
 
 254 form, but makes still sense because the element can be deleted later. This
 
 255 keeps the actionbar stable and reduces surprising elements that only appear in
 
 260 Always add a tooltip when disabling an action.
 
 264 Try to always add a default action with accesskey enter. Since the actionbar
 
 265 lies outside of the main form, the usual submit on enter does not work out of
 
 270 =head1 DOM MODEL AND IMPLEMENTATION DETAILS
 
 272 The entire block is rendered into a div with the class 'layout-actionbar'. Each
 
 273 action will render itself and will get added to the div. To keep the DOM small
 
 274 and reduce startup overhead, the presentation is pure CSS and only the sticky
 
 275 expansion of comboboxes is done with javascript.
 
 277 To keep startup times and HTML parsing fast the action data is simply written
 
 278 into the data elements of the actions and handlers are added in a ready hook.
 
 286 L<SL::Layout::ActioBar::Base>,
 
 287 L<SL::Layout::ActioBar::Action>,
 
 288 L<SL::Layout::ActioBar::Submit>,
 
 289 L<SL::Layout::ActioBar::ComboBox>,
 
 290 L<SL::Layout::ActioBar::Separator>,
 
 291 L<SL::Layout::ActioBar::Link>,
 
 295 Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>