Web-Components
view release on metacpan or search on metacpan
lib/Web/Components/Navigation.pm view on Meta::CPAN
configure the JS message collection and display code. Attributes are;
=over 3
=item buffer-limit
Maximum number of messages to buffer. Defaults to C<3>
=item display-time
How long in seconds to display each message for. Defaults to C<20> seconds
=back
=cut
has 'messages' => is => 'ro', isa => HashRef, default => sub { {} };
=item C<model>
An immutable required object reference to the model component that is handling
the current request
=cut
has 'model' => is => 'ro', isa => Object, required => TRUE;
=item C<title>
An immutable string which defaults to null. If set will be displayed as the
application title along with the logo in the page header
=cut
has 'title' => is => 'ro', isa => Str, default => NUL;
=item C<title_abbrev>
An immutable string which defaults to C<Nav>. Used to set the pages C<title>
attribute in the HTML head. This is used in turn is used by the browser to
create history links (the back button). Would set this from configuration to
the abbreviation for the application
=cut
has 'title_abbrev' => is => 'ro', isa => Str, default => 'Nav';
=item C<title_entry>
A lazy string. The default constructor sets it to the current pages navigation
label. This is appended to C<title_abbrev> to form the labels in the browser
history
=cut
has 'title_entry' =>
is => 'lazy',
isa => Str,
default => sub {
my $self = shift;
my @parts = split m{ / }mx, $self->context->action;
my $label = $self->_get_nav_label($parts[0] . '/' . $parts[-1]);
return (split m{ \| }mx, $label)[0] // NUL;
};
# Private attributes
has '_base_url' =>
is => 'lazy',
isa => class_type('URI'),
default => sub {
return shift->context->request->uri_for(NUL);
};
has '_container' =>
is => 'lazy',
isa => Str,
default => sub {
my $self = shift;
my $tag = $self->container_tag;
return $self->_html->$tag($self->_data);
};
has '_data' =>
is => 'lazy',
isa => HashRef,
default => sub {
my $self = shift;
my $location = 'navigation-' . $self->menu_location;
my $display = 'link-display-' . $self->link_display;
return {
'id' => 'navigation',
'class' => "navigation ${location} ${display}",
'data-navigation-config' => $self->_json->encode({
'menus' => $self->_menus,
'messages' => $self->_messages,
'moniker' => $self->model->moniker,
'properties' => {
'base-url' => $self->_base_url,
'confirm' => $self->confirm_message,
'container-layout' => $self->container_layout,
'container-name' => $self->container_name,
'content-name' => $self->content_name,
'control-icon' => $self->control_icon,
'icons' => $self->icons,
'link-display' => $self->link_display,
'location' => $self->menu_location,
'logo' => $self->logo,
'media-break' => $self->media_break,
'skin' => $self->context->session->skin,
'title' => $self->title,
'title-abbrev' => $self->title_abbrev,
'verify-token' => $self->context->verification_token,
'version' => MCat->VERSION,
},
}),
};
};
has '_html' =>
is => 'ro',
isa => class_type('HTML::Tiny'),
lib/Web/Components/Navigation.pm view on Meta::CPAN
If C<is_script_request> is true then stash an OK HTTP return code. When using
JS navigation all HTTP responses must be OK or the browser (which sniffs the
fetch responses) will automatically navigate
=cut
sub finalise_script_request {
my $self = shift;
$self->context->stash(code => HTTP_OK) if $self->is_script_request;
return;
}
=item C<is_script_request>
Returns true if the request has come from the JS in the browser
=cut
sub is_script_request {
my $self = shift;
my $header = $self->context->request->header('x-requested-with') // NUL;
return lc $header eq 'xmlhttprequest' ? TRUE : FALSE;
}
=item C<item>
$self = $self->item('action path', $args, $params);
$self = $self->item(formpost, 'action path', $args, $params);
The first example will add a single link to the current C<list>. The display
text is set by the C<Nav> subroutine attribute of the endpoint, the C<href>
is supplied by C<context> C<uri_for_action>
In the second example C<formpost> is imported from L<Web::Components::Util>
and causes the rendered menu item to be a form with a button on it (it is
expected that this will be styled like a link). This is used for delete
operations since we don't do deletes with a GET
=cut
sub item {
my ($self, @args) = @_;
my $label;
if (is_hashref $args[0]) {
$label = shift @args;
$label->{name} = $self->_get_nav_label($args[0]);
}
else { $label = $self->_get_nav_label($args[0]) }
if ($self->model->is_authorised($self->context, $args[0])) {
my $list = $self->_lists->{$self->_name}->[1];
my ($text, $icon);
if (is_hashref $label) {
($text, $icon) = split m{ \| }mx, $label->{name};
$label->{name} = $text;
$text = $label;
}
else { ($text, $icon) = split m{ \| }mx, $label }
$icon = $self->context->request->uri_for($icon)
if $icon && $icon =~ m{ / }mx;
push @{$list}, [$text => $self->_uri(@args), $icon];
}
else { clear_redirect $self->context }
return $self;
}
=item C<list>
$self = $self->list('list name', 'optional title');
Sets the current list to the name provided. If this list does not exist it
is created. Once a list has been created C<item> is called to add entries to it
=cut
sub list {
my ($self, $name, $title) = @_;
$self->_set__name($name);
unless (exists $self->_lists->{$name}) {
$self->_lists->{$name} = [ $title // NUL, [] ];
push @{$self->_order}, $name;
}
return $self;
}
=item C<menu>
$self = $self->menu('list name');
If the named list exists add it to the current list. This is how you created
nested lists
=cut
sub menu {
my ($self, $name) = @_;
my $lists = $self->_lists;
push @{$lists->{$self->_name}->[1]}, $name if exists $lists->{$name};
return $self;
}
=item C<render>
Returns the HTML for inclusion on the web page
=cut
sub render {
my $self = shift;
my $output;
$self->_add_global;
try { $output = $self->_container }
catch { $output = $_ };
return $output;
}
# Private methods
sub _add_global {
my $self = shift;
my $list = $self->list('_global');
for my $action (@{$self->global}) {
my ($moniker, $method) = split m{ / }mx, $action;
if ($self->model->is_authorised($self->context, $action)) {
if ($method and $method eq 'menu') {
$self->context->models->{$moniker}->menu($self->context);
$self->_set__name('_global');
}
push @{$self->_lists->{$self->_name}->[1]}, $moniker
if exists $self->_lists->{$moniker};
$list->item($action);
}
else { clear_redirect $self->context }
}
return;
}
sub _get_nav_label {
my ($self, $action) = @_;
my $attr = try { $self->context->get_attributes($action) };
return $attr->{Nav}->[0] if $attr && defined $attr->{Nav};
return NUL;
}
sub _uri {
my ($self, @args) = @_;
my $action = $args[0];
return NUL if $action =~ m{ /menu \z }mx;
return $self->context->uri_for_action(@args);
}
use namespace::autoclean;
1;
__END__
=back
=head1 Diagnostics
None
=head1 Dependencies
=over 3
=item L<Moo>
=back
=head1 Incompatibilities
( run in 0.490 second using v1.01-cache-2.11-cpan-5511b514fd6 )