MaterialComponents Presenter
authorSven Schöling <s.schoeling@googlemail.com>
Fri, 5 Mar 2021 17:52:30 +0000 (18:52 +0100)
committerSven Schöling <s.schoeling@googlemail.com>
Fri, 25 Jun 2021 13:51:32 +0000 (15:51 +0200)
Erstmal für die wichtigsten Elemente:

- buttons
- submit
- input felder
- icons

Zusammen mit einem Testcontroller zum rendern

SL/Controller/MaterializeTest.pm [new file with mode: 0644]
SL/Presenter/ALL.pm
SL/Presenter/MaterialComponents.pm [new file with mode: 0644]
templates/mobile_webpages/test/components.html [new file with mode: 0644]

diff --git a/SL/Controller/MaterializeTest.pm b/SL/Controller/MaterializeTest.pm
new file mode 100644 (file)
index 0000000..de79182
--- /dev/null
@@ -0,0 +1,10 @@
+package SL::Controller::MaterializeTest;
+
+use strict;
+use parent qw(SL::Controller::Base);
+
+sub action_test {
+  $_[0]->render("test/components");
+}
+
+1;
index a4bd724..974b1a6 100644 (file)
@@ -23,6 +23,7 @@ use SL::Presenter::ShopOrder;
 use SL::Presenter::Text;
 use SL::Presenter::Tag;
 use SL::Presenter::BankAccount;
+use SL::Presenter::MaterialComponents;
 
 our %presenters = (
   chart                       => 'SL::Presenter::Chart',
@@ -46,6 +47,7 @@ our %presenters = (
   text                        => 'SL::Presenter::Text',
   tag                         => 'SL::Presenter::Tag',
   bank_account                => 'SL::Presenter::BankAccount',
+  M                           => 'SL::Presenter::MaterialComponents',
 );
 
 sub wrap {
diff --git a/SL/Presenter/MaterialComponents.pm b/SL/Presenter/MaterialComponents.pm
new file mode 100644 (file)
index 0000000..e3f0181
--- /dev/null
@@ -0,0 +1,183 @@
+package SL::Presenter::MaterialComponents;
+
+use strict;
+
+use SL::HTML::Restrict;
+use SL::Presenter::EscapedText qw(escape);
+use SL::Presenter::Tag qw(html_tag);
+use Scalar::Util qw(blessed);
+
+use Exporter qw(import);
+our @EXPORT_OK = qw(
+  button_tag
+);
+our %EXPORT_TAGS = (ALL => \@EXPORT_OK);
+
+use constant BUTTON          => 'btn';
+use constant BUTTON_FLAT     => 'btn-flat';
+use constant BUTTON_FLOATING => 'btn-floating';
+use constant BUTTON_LARGE    => 'btn-large';
+use constant BUTTON_SMALL    => 'btn-small';
+use constant DISABLED        => 'disabled';
+use constant LEFT            => 'left';
+use constant MATERIAL_ICONS  => 'material-icons';
+use constant RIGHT           => 'right';
+use constant LARGE           => 'large';
+use constant MEDIUM          => 'medium';
+use constant SMALL           => 'small';
+use constant TINY            => 'tiny';
+
+use constant WAVES_EFFECT    => 'waves-effect';
+use constant WAVES_LIGHT     => 'waves-light';
+
+
+my %optional_classes = (
+  button => {
+    disabled => DISABLED,
+    flat     => BUTTON_FLAT,
+    floating => BUTTON_FLOATING,
+    large    => BUTTON_LARGE,
+    small    => BUTTON_SMALL,
+  },
+  icon => {
+    left   => LEFT,
+    right  => RIGHT,
+    large  => LARGE,
+    medium => MEDIUM,
+    small  => SMALL,
+    tiny   => TINY,
+  },
+);
+
+use Carp;
+
+sub _confirm_js {
+  'if (!confirm("'. _J($_[0]) .'")) return false;'
+}
+
+sub _confirm_to_onclick {
+  my ($attributes, $onclick) = @_;
+
+  if ($attributes->{confirm}) {
+    $$onclick //= '';
+    $$onclick = _confirm_js(delete($attributes->{confirm})) . $attributes->{onlick};
+  }
+}
+
+# used to extract material properties that need to be translated to classes
+# supports prefixing for delegation
+# returns a list of classes, mutates the attributes
+sub _extract_attribute_classes {
+  my ($attributes, $type, $prefix) = @_;
+
+  my @classes;
+  my $attr;
+  for my $key (keys %$attributes) {
+    if ($prefix) {
+      next unless $key =~ /^${prefix}_(.*)/;
+      $attr = $1;
+    } else {
+      $attr = $key;
+    }
+
+    if ($optional_classes{$type}{$attr}) {
+      $attributes->{$key} = undef;
+      push @classes, $optional_classes{$type}{$attr};
+    }
+  }
+
+  # delete all undefined values
+  my @delete_keys = grep { !defined $attributes->{$_} } keys %$attributes;
+  delete $attributes->{$_} for @delete_keys;
+
+  @classes;
+}
+
+sub button_tag {
+  my ($onclick, $value, %attributes) = @_;
+
+  _set_id_attribute(\%attributes, $attributes{name}) if $attributes{name};
+  _confirm_to_onclick(\%attributes, \$onclick);
+
+  my @button_classes = _extract_attribute_classes(\%attributes, "button");
+  my @icon_classes   = _extract_attribute_classes(\%attributes, "icon", "icon");
+
+  $attributes{class} = [
+    grep { $_ } $attributes{class}, WAVES_EFFECT, WAVES_LIGHT, BUTTON, @button_classes
+  ];
+
+  if ($attributes{icon}) {
+    $value = icon(delete $attributes{icon}, class => \@icon_classes)
+           . $value;
+  }
+
+  html_tag('a', $value, %attributes, onclick => $onclick);
+}
+
+sub submit_tag {
+  my ($name, $value, %attributes) = @_;
+
+  _set_id_attribute(\%attributes, $attributes{name}) if $attributes{name};
+  _confirm_to_onclick(\%attributes, \($attributes{onclick} //= ''));
+
+  my @button_classes = _extract_attribute_classes(\%attributes, "button");
+  my @icon_classes   = _extract_attribute_classes(\%attributes, "icon", "icon");
+
+  $attributes{class} = [
+    grep { $_ } $attributes{class}, WAVES_EFFECT, WAVES_LIGHT, BUTTON, @button_classes
+  ];
+
+  if ($attributes{icon}) {
+    $value = icon(delete $attributes{icon}, class => \@icon_classes)
+           . $value;
+  }
+
+  html_tag('button', $value, type => 'submit',  %attributes);
+}
+
+
+sub icon {
+  my ($name, %attributes) = @_;
+
+  my @icon_classes = _extract_attribute_classes(\%attributes, "icon");
+
+  html_tag('i', $name, class => [ grep { $_ } MATERIAL_ICONS, @icon_classes, delete $attributes{class} ], %attributes);
+}
+
+
+sub input_tag {
+  my ($name, $value, %attributes) = @_;
+
+  # todo icons
+  # todo label/active
+  # todo validate
+
+  html_tag('input', $name, $value, %attributes) . html_tag('label', for => $attributes{id}, $name);
+}
+
+
+1;
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Presenter::MaterialComponents - MaterialCSS Component wrapper
+
+=head1 SYNOPSIS
+
+
+=head1 DESCRIPTION
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Sven Schöling E<lt>s.schoeling@googlemail.comE<gt>
+
+=cut
diff --git a/templates/mobile_webpages/test/components.html b/templates/mobile_webpages/test/components.html
new file mode 100644 (file)
index 0000000..6af2d2c
--- /dev/null
@@ -0,0 +1,29 @@
+[% USE P %]
+
+<h1>Material Components Tests</h1>
+
+
+<h2>Buttons</h2>
+
+[% P.M.button_tag("", "button") %]
+[% P.M.button_tag("", P.M.icon('add'), floating=1) %]
+[% P.M.submit_tag("", "submit") %]
+[% P.M.submit_tag("", "disabled submit", disabled=1) %]
+[% P.M.button_tag("", "button with left icon", icon="cloud", icon_left=1) %]
+[% P.M.button_tag("", "button with right icon", icon="save", icon_right=1) %]
+[% P.M.button_tag("", "flat button", flat=1) %]
+[% P.M.button_tag("", "large button", large=1) %]
+[% P.M.button_tag("", "small button", small=1) %]
+[% P.M.button_tag("", "disabled small button", disabled=1, small=1) %]
+
+<h2>Icons</h2>
+
+[% P.M.icon("alarm") %]
+[% P.M.icon("alarm", large=1) %]
+[% P.M.icon("alarm", medium=1) %]
+[% P.M.icon("alarm", small=1) %]
+[% P.M.icon("alarm", tiny=1) %]
+
+<h2>Inputs</h2>
+
+[% P.M.input_tag("", "test string") %]