Projekt

Allgemein

Profil

Herunterladen (7,13 KB) Statistiken
| Zweig: | Markierung: | Revision:
ccf94c5d Sven Schöling
package SL::Layout::ActionBar;

use strict;
use parent qw(SL::Layout::Base);

ff424b75 Moritz Bunkus
use Carp;
use Scalar::Util qw(blessed);
e0a3b19e Sven Schöling
use SL::Layout::ActionBar::Action;
ff424b75 Moritz Bunkus
use SL::Layout::ActionBar::ComboBox;
c37fb9ef Moritz Bunkus
use SL::Layout::ActionBar::Link;
ff424b75 Moritz Bunkus
use SL::Layout::ActionBar::Separator;
e0a3b19e Sven Schöling
5a55ac86 Sven Schöling
use SL::Presenter::Tag qw(html_tag);

ccf94c5d Sven Schöling
use constant HTML_CLASS => 'layout-actionbar';

use Rose::Object::MakeMethods::Generic (
'scalar --get_set_init' => [ qw(actions) ],
);

ff424b75 Moritz Bunkus
my %class_descriptors = (
action => { class => 'SL::Layout::ActionBar::Action', num_params => 1, },
combobox => { class => 'SL::Layout::ActionBar::ComboBox', num_params => 1, },
c37fb9ef Moritz Bunkus
link => { class => 'SL::Layout::ActionBar::Link', num_params => 1, },
ff424b75 Moritz Bunkus
separator => { class => 'SL::Layout::ActionBar::Separator', num_params => 0, },
);
ccf94c5d Sven Schöling
###### Layout overrides

sub pre_content {
e0a3b19e Sven Schöling
my ($self) = @_;

my $content = join '', map { $_->render } @{ $self->actions };
0d36a7ed Sven Schöling
return if !$content;
5a55ac86 Sven Schöling
html_tag('div', $content, class => HTML_CLASS);
ccf94c5d Sven Schöling
}

e0a3b19e Sven Schöling
sub javascripts_inline {
join '', map { $_->script } @{ $_[0]->actions };
ccf94c5d Sven Schöling
}

a38da2a1 Sven Schöling
sub static_javascripts {
e0a3b19e Sven Schöling
'kivi.ActionBar.js'
ccf94c5d Sven Schöling
}

###### interface

ff424b75 Moritz Bunkus
sub add {
ccf94c5d Sven Schöling
my ($self, @actions) = @_;

ff424b75 Moritz Bunkus
push @{ $self->actions }, $self->parse_actions(@actions);

return $self->actions->[-1];
ccf94c5d Sven Schöling
}

ff424b75 Moritz Bunkus
sub parse_actions {
my ($self_or_class, @actions) = @_;

my @parsed;

while (my $type = shift(@actions)) {
if (blessed($type) && $type->isa('SL::Layout::ActionBar::Action')) {
push @parsed, $type;
b459f8fe Moritz Bunkus
next;
ff424b75 Moritz Bunkus
}
ccf94c5d Sven Schöling
ff424b75 Moritz Bunkus
my $descriptor = $class_descriptors{lc $type} || croak("Unknown action type '${type}'");
my @params = splice(@actions, 0, $descriptor->{num_params});
ccf94c5d Sven Schöling
ff424b75 Moritz Bunkus
push @parsed, $descriptor->{class}->from_params(@params);
}

return @parsed;
}

sub init_actions {
[]
}
ccf94c5d Sven Schöling
1;

__END__

=encoding utf-8

=head1 NAME

SL::Layout::ActionBar - Unified action buttons for controllers

cea0e38d Sven Schöling
=head1 SYNOPSIS

# short sugared syntax:
for my $bar ($::request->layout->get('actionbar')) {
$bar->add(
action => [
t8('Description'),
call => [ 'kivi.Javascript.function', @arguments ],
accesskey => 'enter',
disabled => $tooltip_with_reason_or_falsish,
only_if => $precomputed_condition,
not_if => $precomputed_condition,
id => 'html-element-id',
],
combobox => [
action => [...],
action => [...],
action => [...],
action => [...],
],
link => [
t8('Description'),
link => $url,
],
'separator',
);
}

# full syntax without sugar
for my $bar ($::request->layout->get('actionbar')) {
$bar->add(
(SL::Layout::ActionBar::Action->new(
text => t8('Description'),
params => {
call => [ 'kivi.Javascript.function', @arguments ],
accesskey => 'enter',
disabled => $tooltip_with_reason_or_falsish,
},
)) x(!!$only_id && !$not_if),
SL::Layout::ActionBar::ComboBox->new(
actions => [
SL::Layout::ActionBar::Action->new(...),
SL::Layout::ActionBar::Action->new(...),
SL::Layout::ActionBar::Action->new(...),
SL::Layout::ActionBar::Action->new(...),
],
),
SL::Layout::ActionBar::Link->new(
text => t8('Description'),
params => {
link => $url,
},
),
SL::Layout::ActionBar::Separator->new,
);
}

ccf94c5d Sven Schöling
=head1 CONCEPT

cea0e38d Sven Schöling
This is a layout block that creates an action bar for any controller who
ccf94c5d Sven Schöling
wants to use it. It's designed to be rendered above the content and to be
cea0e38d Sven Schöling
fixed when scrolling. It's structured as a container for elements that can be
extended when needed.
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
=head1 METHODS
ccf94c5d Sven Schöling
=over 4

cea0e38d Sven Schöling
=item * C<new>
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
Will be used during initialization of the layout. You should never have to
instanciate an action bar yourself. Get the current request instances from
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
$::request->layout->get('actionbar')
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
instead.
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
=item * C<add>
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
Add new elements to the bar. Can be instances of
L<SL::Layout::ActionBar::Action> or scalar strings matching the sugar syntax
which is described further down.
ccf94c5d Sven Schöling
=back

cea0e38d Sven Schöling
=head1 SYNTACTIC SUGAR
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
Instead of passing full objects to L</add>, you can instead pass the arguments
to be used for instantiation to make the code easier to read. The short syntax
looks like this:

type => [
localized_description,
param => value,
param => value,
...
]

A string type, followed by the parameters needed for that type. Type may be one of:

=over 4

=item * C<action>

=item * C<combobox>

=item * C<link>

=item * C<separator>

=back

C<separator> will use no parameters, the other three will expect one arrayref.

74ddcb2d Sven Schöling
Two additional pseudo parameters are supported for those:
ccf94c5d Sven Schöling
=over 4

cea0e38d Sven Schöling
=item * C<only_if>
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
=item * C<not_if>
ccf94c5d Sven Schöling
02f6397d Moritz Bunkus
=back

cea0e38d Sven Schöling
These are meant to reduce enterprise operators (C<()x!!>) when conditionally adding lots
of elements.
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
The combobox element is in itself a container and will simply expect the same
syntax in an arrayref.
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
For the full list of parameters supported by the elements, see L<SL::Layout::ActionBar::Action/RECOGNIZED PARAMETERS>.


=head1 GUIDELINES

The current implementation follows these design guidelines:
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
=over 4

=item *

Don't put too many elements into the action bar. Group into comboboxes if
possible. Consider seven elements a reasonable limit.

=item *

If you've got an update button, put it first and bind the enter accesskey to
it.

=item *

Put mutating actions (save, post, delete, check out, ship) before the separator
and non mutating actions (export, search, history, workflow) after the
separator. Combined actions (save and close) still mutate and go before the
separator.

=item *

Avoid abusing the actionbar as a secondary menu. As a principle every action
should act upon the current element or topic.
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
=item *

Hide elements with C<only_if> if they are known to be useless for the current
topic, but disable when they would be useful in principle but are not
applicable right now. For example C<delete> does not make sense in a creating
form, but makes still sense because the element can be deleted later. This
keeps the actionbar stable and reduces surprising elements that only appear in
rare situations.
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
=item *
ccf94c5d Sven Schöling
cea0e38d Sven Schöling
Always add a tooltip when disabling an action.

=item *

Try to always add a default action with accesskey enter. Since the actionbar
lies outside of the main form, the usual submit on enter does not work out of
the box.

=back

=head1 DOM MODEL AND IMPLEMENTATION DETAILS

The entire block is rendered into a div with the class 'layout-actionbar'. Each
action will render itself and will get added to the div. To keep the DOM small
and reduce startup overhead, the presentation is pure CSS and only the sticky
expansion of comboboxes is done with javascript.

To keep startup times and HTML parsing fast the action data is simply written
into the data elements of the actions and handlers are added in a ready hook.
ccf94c5d Sven Schöling
=head1 BUGS

none yet. :)

cea0e38d Sven Schöling
=head1 SEE ALSO

L<SL::Layout::ActioBar::Base>,
L<SL::Layout::ActioBar::Action>,
L<SL::Layout::ActioBar::Submit>,
L<SL::Layout::ActioBar::ComboBox>,
L<SL::Layout::ActioBar::Separator>,
L<SL::Layout::ActioBar::Link>,

ccf94c5d Sven Schöling
=head1 AUTHOR

Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>

=cut