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 SL::Presenter::Tag qw(html_tag);
 
  15 use constant HTML_CLASS => 'layout-actionbar';
 
  17 use Rose::Object::MakeMethods::Generic (
 
  18   'scalar --get_set_init' => [ qw(actions) ],
 
  21 my %class_descriptors = (
 
  22   action    => { class => 'SL::Layout::ActionBar::Action',    num_params => 1, },
 
  23   combobox  => { class => 'SL::Layout::ActionBar::ComboBox',  num_params => 1, },
 
  24   link      => { class => 'SL::Layout::ActionBar::Link',      num_params => 1, },
 
  25   separator => { class => 'SL::Layout::ActionBar::Separator', num_params => 0, },
 
  28 ###### Layout overrides
 
  33   my $content = join '', map { $_->render } @{ $self->actions };
 
  35   html_tag('div', $content, class => HTML_CLASS);
 
  38 sub javascripts_inline {
 
  39   join '', map { $_->script } @{ $_[0]->actions };
 
  42 sub static_javascripts {
 
  49   my ($self, @actions) = @_;
 
  51   push @{ $self->actions }, $self->parse_actions(@actions);
 
  53   return $self->actions->[-1];
 
  57   my ($self_or_class, @actions) = @_;
 
  61   while (my $type = shift(@actions)) {
 
  62     if (blessed($type) && $type->isa('SL::Layout::ActionBar::Action')) {
 
  67     my $descriptor = $class_descriptors{lc $type} || croak("Unknown action type '${type}'");
 
  68     my @params     = splice(@actions, 0, $descriptor->{num_params});
 
  70     push @parsed, $descriptor->{class}->from_params(@params);
 
  88 SL::Layout::ActionBar - Unified action buttons for controllers
 
  92   # short sugared syntax:
 
  93   for my $bar ($::request->layout->get('actionbar')) {
 
  97         call      => [ 'kivi.Javascript.function', @arguments ],
 
  99         disabled  => $tooltip_with_reason_or_falsish,
 
 100         only_if   => $precomputed_condition,
 
 101         not_if    => $precomputed_condition,
 
 102         id        => 'html-element-id',
 
 118   # full syntax without sugar
 
 119   for my $bar ($::request->layout->get('actionbar')) {
 
 121       (SL::Layout::ActionBar::Action->new(
 
 122         text => t8('Description'),
 
 124           call      => [ 'kivi.Javascript.function', @arguments ],
 
 125           accesskey => 'enter',
 
 126           disabled  => $tooltip_with_reason_or_falsish,
 
 128       )) x(!!$only_id && !$not_if),
 
 129       SL::Layout::ActionBar::ComboBox->new(
 
 131           SL::Layout::ActionBar::Action->new(...),
 
 132           SL::Layout::ActionBar::Action->new(...),
 
 133           SL::Layout::ActionBar::Action->new(...),
 
 134           SL::Layout::ActionBar::Action->new(...),
 
 137       SL::Layout::ActionBar::Link->new(
 
 138         text => t8('Description'),
 
 143       SL::Layout::ActionBar::Separator->new,
 
 149 This is a layout block that creates an action bar for any controller who
 
 150 wants to use it. It's designed to be rendered above the content and to be
 
 151 fixed when scrolling. It's structured as a container for elements that can be
 
 152 extended when needed.
 
 160 Will be used during initialization of the layout. You should never have to
 
 161 instanciate an action bar yourself. Get the current request instances from
 
 163   $::request->layout->get('actionbar')
 
 169 Add new elements to the bar. Can be instances of
 
 170 L<SL::Layout::ActionBar::Action> or scalar strings matching the sugar syntax
 
 171 which is described further down.
 
 175 =head1 SYNTACTIC SUGAR
 
 177 Instead of passing full objects to L</add>, you can instead pass the arguments
 
 178 to be used for instantiation to make the code easier to read. The short syntax
 
 182     localized_description,
 
 188 A string type, followed by the parameters needed for that type. Type may be one of:
 
 202 C<separator> will use no parameters, the other three will expect one arrayref.
 
 204 Two additional pseudo parameters are supported for those:
 
 214 These are meant to reduce enterprise operators (C<()x!!>) when conditionally adding lots
 
 217 The combobox element is in itself a container and will simply expect the same
 
 218 syntax in an arrayref.
 
 220 For the full list of parameters supported by the elements, see L<SL::Layout::ActionBar::Action/RECOGNIZED PARAMETERS>.
 
 225 The current implementation follows these design guidelines:
 
 231 Don't put too many elements into the action bar. Group into comboboxes if
 
 232 possible. Consider seven elements a reasonable limit.
 
 236 If you've got an update button, put it first and bind the enter accesskey to
 
 241 Put mutating actions (save, post, delete, check out, ship) before the separator
 
 242 and non mutating actions (export, search, history, workflow) after the
 
 243 separator. Combined actions (save and close) still mutate and go before the
 
 248 Avoid abusing the actionbar as a secondary menu. As a principle every action
 
 249 should act upon the current element or topic.
 
 253 Hide elements with C<only_if> if they are known to be useless for the current
 
 254 topic, but disable when they would be useful in principle but are not
 
 255 applicable right now. For example C<delete> does not make sense in a creating
 
 256 form, but makes still sense because the element can be deleted later. This
 
 257 keeps the actionbar stable and reduces surprising elements that only appear in
 
 262 Always add a tooltip when disabling an action.
 
 266 Try to always add a default action with accesskey enter. Since the actionbar
 
 267 lies outside of the main form, the usual submit on enter does not work out of
 
 272 =head1 DOM MODEL AND IMPLEMENTATION DETAILS
 
 274 The entire block is rendered into a div with the class 'layout-actionbar'. Each
 
 275 action will render itself and will get added to the div. To keep the DOM small
 
 276 and reduce startup overhead, the presentation is pure CSS and only the sticky
 
 277 expansion of comboboxes is done with javascript.
 
 279 To keep startup times and HTML parsing fast the action data is simply written
 
 280 into the data elements of the actions and handlers are added in a ready hook.
 
 288 L<SL::Layout::ActioBar::Base>,
 
 289 L<SL::Layout::ActioBar::Action>,
 
 290 L<SL::Layout::ActioBar::Submit>,
 
 291 L<SL::Layout::ActioBar::ComboBox>,
 
 292 L<SL::Layout::ActioBar::Separator>,
 
 293 L<SL::Layout::ActioBar::Link>,
 
 297 Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>