1 package SL::Presenter::MaterialComponents;
5 use SL::HTML::Restrict;
6 use SL::Presenter::EscapedText qw(escape);
7 use SL::Presenter::Tag qw(html_tag);
8 use Scalar::Util qw(blessed);
10 use Exporter qw(import);
14 our %EXPORT_TAGS = (ALL => \@EXPORT_OK);
16 use constant BUTTON => 'btn';
17 use constant BUTTON_FLAT => 'btn-flat';
18 use constant BUTTON_FLOATING => 'btn-floating';
19 use constant BUTTON_LARGE => 'btn-large';
20 use constant BUTTON_SMALL => 'btn-small';
21 use constant DISABLED => 'disabled';
22 use constant LEFT => 'left';
23 use constant MATERIAL_ICONS => 'material-icons';
24 use constant RIGHT => 'right';
25 use constant LARGE => 'large';
26 use constant MEDIUM => 'medium';
27 use constant SMALL => 'small';
28 use constant TINY => 'tiny';
29 use constant INPUT_FIELD => 'input-field';
30 use constant DATEPICKER => 'datepicker';
32 use constant WAVES_EFFECT => 'waves-effect';
33 use constant WAVES_LIGHT => 'waves-light';
36 my %optional_classes = (
40 floating => BUTTON_FLOATING,
41 large => BUTTON_LARGE,
42 small => BUTTON_SMALL,
57 'if (!confirm("'. _J($_[0]) .'")) return false;'
60 sub _confirm_to_onclick {
61 my ($attributes, $onclick) = @_;
63 if ($attributes->{confirm}) {
65 $$onclick = _confirm_js(delete($attributes->{confirm})) . $attributes->{onlick};
69 # used to extract material properties that need to be translated to classes
70 # supports prefixing for delegation
71 # returns a list of classes, mutates the attributes
72 sub _extract_attribute_classes {
73 my ($attributes, $type, $prefix) = @_;
77 for my $key (keys %$attributes) {
79 next unless $key =~ /^${prefix}_(.*)/;
85 if ($optional_classes{$type}{$attr}) {
86 $attributes->{$key} = undef;
87 push @classes, $optional_classes{$type}{$attr};
91 # delete all undefined values
92 my @delete_keys = grep { !defined $attributes->{$_} } keys %$attributes;
93 delete $attributes->{$_} for @delete_keys;
98 sub _set_id_attribute {
99 my ($attributes, $name, $unique) = @_;
101 if (!delete($attributes->{no_id}) && !$attributes->{id}) {
102 $attributes->{id} = name_to_id($name);
103 $attributes->{id} .= '_' . $attributes->{value} if $unique;
109 { # This will give you an id for identifying html tags and such.
110 # It's guaranteed to be unique unless you exceed 10 mio calls per request.
111 # Do not use these id's to store information across requests.
112 my $_id_sequence = int rand 1e7;
114 return ( $_id_sequence = ($_id_sequence + 1) % 1e7 );
122 return "id_" . _id();
125 $name =~ s/\[\+?\]/ _id() /ge; # give constructs with [] or [+] unique ids
126 $name =~ s/[^\w_]/_/g;
133 my ($onclick, $value, %attributes) = @_;
135 _set_id_attribute(\%attributes, $attributes{name}) if $attributes{name};
136 _confirm_to_onclick(\%attributes, \$onclick);
138 my @button_classes = _extract_attribute_classes(\%attributes, "button");
139 my @icon_classes = _extract_attribute_classes(\%attributes, "icon", "icon");
141 $attributes{class} = [
142 grep { $_ } $attributes{class}, WAVES_EFFECT, WAVES_LIGHT, BUTTON, @button_classes
145 if ($attributes{icon}) {
146 $value = icon(delete $attributes{icon}, class => \@icon_classes)
150 html_tag('a', $value, %attributes, onclick => $onclick);
154 my ($name, $value, %attributes) = @_;
156 _set_id_attribute(\%attributes, $attributes{name}) if $attributes{name};
157 _confirm_to_onclick(\%attributes, \($attributes{onclick} //= ''));
159 my @button_classes = _extract_attribute_classes(\%attributes, "button");
160 my @icon_classes = _extract_attribute_classes(\%attributes, "icon", "icon");
162 $attributes{class} = [
163 grep { $_ } $attributes{class}, WAVES_EFFECT, WAVES_LIGHT, BUTTON, @button_classes
166 if ($attributes{icon}) {
167 $value = icon(delete $attributes{icon}, class => \@icon_classes)
171 html_tag('button', $value, type => 'submit', %attributes);
176 my ($name, %attributes) = @_;
178 my @icon_classes = _extract_attribute_classes(\%attributes, "icon");
180 html_tag('i', $name, class => [ grep { $_ } MATERIAL_ICONS, @icon_classes, delete $attributes{class} ], %attributes);
185 my ($name, $value, %attributes) = @_;
187 _set_id_attribute(\%attributes, $attributes{name});
189 my $class = delete %attributes{class};
190 my $icon = $attributes{icon}
191 ? icon(delete $attributes{icon}, class => 'prefix')
194 my $label = $attributes{label}
195 ? html_tag('label', delete $attributes{label}, for => $attributes{id})
198 $attributes{type} //= 'text';
202 html_tag('input', undef, value => $value, %attributes, name => $name) .
204 class => [ grep $_, $class, INPUT_FIELD ],
209 my ($name, $value, %attributes) = @_;
211 _set_id_attribute(\%attributes, $name);
213 my $icon = $attributes{icon}
214 ? icon(delete $attributes{icon}, class => 'prefix')
217 my $label = $attributes{label}
218 ? html_tag('label', delete $attributes{label}, for => $attributes{id})
221 $attributes{type} = 'text'; # required for materialize
223 my @onchange = $attributes{onchange} ? (onChange => delete $attributes{onchange}) : ();
224 my @classes = (delete $attributes{class});
226 $::request->layout->add_javascripts('kivi.Validator.js');
227 $::request->presenter->need_reinit_widgets($attributes{id});
229 $attributes{'data-validate'} = join(' ', "date", grep { $_ } (delete $attributes{'data-validate'}));
234 blessed($value) ? $value->to_lxoffice : $value,
235 size => 11, type => 'text', name => $name,
237 class => DATEPICKER, @onchange,
240 class => [ grep $_, @classes, INPUT_FIELD ],
256 SL::Presenter::MaterialComponents - MaterialCSS Component wrapper
269 Sven Schöling E<lt>s.schoeling@googlemail.comE<gt>