Created
May 29, 2012 17:06
-
-
Save jshirley/2829556 to your computer and use it in GitHub Desktop.
Bootstrap Form Macros. Use these in combination with Twitter Bootstrap CSS, Catalyst::Plugin::MessageStack and Data::Manager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[%~ | |
# Some illustrious documentation to get you started: | |
# | |
# The macros defined are to generate markup to save people from copy and pasting | |
# markup. They copy and paste hashes instead, which should be safer (in theory) | |
# | |
# The macros for forms are: | |
# * text_field - A simple text field, pass in type to override input type | |
# * password_field - A password field, equivalent to | |
# text_field({ type => 'password', ... }) | |
# * textarea_field - A textarea field | |
# * readonly_field - A display-only field, with a hidden input | |
# * select_field - A <select> field, with options passed in the `array` | |
# parameter. | |
# * checkboxes - A group of fields, displayed in nice fashion. If a | |
# radio is needed, pass type => 'radio'. | |
# * checkbox - A single checkbox field | |
# If a radio is needed, pass type => 'radio'. | |
# * form_actions - Use this method to cap off the form actions and end | |
# a form. | |
# | |
# These use Twitter Bootstrap markup structure for the markup. Without the | |
# bootstrap CSS, things will not look correct. You can read more about the | |
# Bootstrap forms at http://twitter.github.com/bootstrap/base-css.html#forms | |
# | |
# The basic structure is a containing div and the label and input is in a | |
# separate div. | |
# | |
# Additionally, there is a pretty_date formatter that will display dates | |
# in a human friendly format. Things like, '24 minutes ago' versus a silly | |
# date string. | |
# | |
# It is recommended to look at /guide/form to see this stuff in action. | |
# | |
# Integration with Catalyst::Plugin::MessageStack and Data::Manager. This again | |
# is better demonstrated looking at /guide/form. | |
# | |
# To automatically look inside the `messages` and `results` objects in the | |
# stash, simply set `context.scope` to the current scope of the results. | |
# After that, messaging and sticky values should co-exist happily. | |
USE time_dir = Time.Duration; | |
MACRO system_localize(str) BLOCK; | |
IF str.match('^\@'); c.loc(str); ELSE; str | html; END; | |
END; | |
MACRO pretty_date(dt) BLOCK; | |
SET now_secs = now.epoch; | |
SET then_secs = dt.epoch || now_secs; | |
IF(now_secs > then_secs); | |
time_dir.ago(now_secs - then_secs); | |
ELSE; | |
time_dir.from_now(then_secs - now_secs); | |
END; | |
END; | |
MACRO form_actions(actions) BLOCK; | |
IF !actions; actions = [ { } ]; END; | |
%] | |
<div class="form-actions"> | |
[% FOREACH action IN actions; | |
IF action.type == 'cancel' %] | |
<button class="btn"><i class="icon-remove"></i> [% c.loc(action.label || 'Cancel') %]</button> | |
[% ELSE %] | |
<button class="btn btn-primary"><i class="icon-[% action.icon || 'ok' %] icon-white"></i> [% c.loc(action.label || 'Save') %]</button> | |
[% END %] | |
[% END %] | |
</div>[% | |
END; | |
MACRO label_field(info) BLOCK; | |
IF !info.error && stack && stack.count; | |
error_name = info.name; | |
IF context.scope; | |
error_name = error_name.replace("${context.scope}.", ""); | |
END; | |
info.error = c.loc( stack.for_subject(error_name).for_level('error').first_message.id ); | |
END %] | |
<label class="control-label" for="[% info.id %]" | |
[%~ IF info.tooltip %] data-tooltip="[% info.tooltip | html %]"[% END ~%] | |
> | |
[%~ c.loc(info.label, info.params) ~%] | |
</label> | |
[% END; | |
MACRO readonly_field(info) BLOCK; | |
IF info.filter && info.value; | |
f = info.filter; | |
IF f == "join"; | |
info.value = info.value.join(', '); | |
ELSE; | |
TRY; info.value = FILTER $f; info.value; END; CATCH; "Error!"; END; | |
END; | |
END; | |
%] | |
<div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]"> | |
[% label_field(info) %] | |
<div class="controls ro"> | |
<input type="hidden" id="form_[% info.name %]" name="[% info.name %]" value="[% info.value | html %]"/><span> | |
<span class="uneditable-input">[% info.value %]</span> | |
[% IF info.link || info.required || info.error || info.success || info.warning %] | |
<span class="help-inline"> | |
[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %] | |
[%~ IF info.link %]<a href="[% info.link %]">[%~ END ~%] | |
</span> | |
[% END %] | |
[% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %] | |
[%~ IF info.link ~%]</a>[%~ END ~%]</span> | |
</div> | |
</div> | |
[% END; | |
MACRO select_field(info) BLOCK; | |
IF !info.defined('value_method'); info.value_method = 'value'; END; | |
IF !info.defined('label_method'); info.label_method = 'label'; END; | |
IF !info.value && results.${context.scope}; | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("${context.scope}.", ""); END; | |
info.value = results.${context.scope}.get_original_value(value_name); | |
END; | |
IF !info.error && messages.for_scope(context.scope); | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
info.error = messages.for_scope(context.scope).for_subject(value_name).first_message.id; | |
END; | |
%] | |
[%~ IF info.dynamic_array =%]<script type="text/javascript">if ( typeof dynamic_forms === 'undefined' ) dynamic_forms = {}; dynamic_forms['[% info.value_from %]'] = { 'source': '[% info.dynamic_array %]', 'update': '[% info.name %]', 'value': "[% info.value || html %]" };</script>[% IF info.options.size == 0; info.options.push(c.loc('Please select [_1]', [ info.value_from ] ) ); END; END %] | |
<div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]"> | |
[% label_field(info) %] | |
<div class="select controls[% IF info.container_classes; " "; info.container_classes.join(" "); END %]"> | |
<[% IF info.type == 'radio'; "div"; ELSE; "select"; END %] id="form_[% info.name %]" name="[% info.name %]" | |
[%~ IF info.classes %] class="[% info.classes.join(' ') %]"[%~ END ~%] | |
[%~ IF info.disabled %] disabled="disabled"[%~ END ~%] | |
[%~ FOREACH datum IN info.data.pairs %] data-[% datum.key %]="[% datum.value %]"[%~ END ~%]> | |
[% IF info.type != "radio" && info.default_option %] | |
[% IF info.type == 'radio' %] | |
[% ELSE %] | |
<option value="[% info.default_option.0 | html %]">[% info.default_option.1 %]</option> | |
[% END %] | |
[% END %] | |
[% IF info.optgroups; | |
info.options = []; | |
DEFAULT info.children = 'children'; | |
FOREACH parent IN info.optgroups; | |
info.options.push({ optgroup => parent.${info.label_method} }); | |
FOREACH child IN parent.children; | |
info.options.push(child); | |
END; | |
END; | |
END %] | |
[% has_optgroup = 0; | |
FOREACH item = info.options; | |
IF item.optgroup; has_optgroup = 1; | |
IF !loop.first %]</optgroup>[% END; | |
%]<optgroup label="[% item.optgroup | html %]">[% | |
ELSE; | |
IF info.defined('value_method') && item.keys.defined; | |
SET this_value = item.${info.value_method}; | |
ELSE; | |
SET this_value = item; | |
END; | |
IF info.defined('label_method') && item.keys.defined; | |
SET this_name = item.${info.label_method}; | |
ELSE; | |
SET this_name = item; | |
END; | |
IF info.defined('elide'); | |
this_name = string_trunc.elide(this_name, info.elide); | |
END; | |
%][% IF info.type == "radio" %]<label><input type="radio" name="[% info.name %]" value="[% this_value | html %]"[% IF info.value == this_value %] checked="checked"[% END %]> [% c.loc(this_name) %]</label>[% info.radio_trailing %] | |
[% ELSE %]<option value="[% this_value | html %]" [% IF this_value == info.value %]selected="selected"[% END %]>[% c.loc(this_name) %]</option>[% END %] | |
[% END; # Not an optgroup %] | |
[% END; | |
IF has_optgroup %]</optgroup>[% END %] | |
[% IF info.type != 'radio' %]</select>[% END %] | |
[% IF info.type == 'radio' %] | |
[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END %] | |
</div> | |
[% ELSE %] | |
[% IF info.required || info.error || info.success || info.warning %] | |
[% END %] | |
<span class="help-inline">[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]</span> | |
[% END %] | |
[% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %] | |
</div> | |
</div> | |
[% END; | |
MACRO password_field(info) BLOCK; | |
info.type = 'password'; | |
info.value = ''; # Force values to not be set. | |
text_field(info); | |
END; | |
MACRO datetime_field(info) BLOCK; | |
IF !info.defined('type'); info.type = 'date'; END; | |
IF !info.defined('value'); | |
IF results.${context.scope}; | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
info.value = results.${context.scope}.get_original_value(value_name); | |
END; | |
IF !info.defined('value') && info.default_value; | |
info.value = info.default_value; | |
END; | |
END; | |
IF !info.error && messages.for_scope(context.scope); | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
message = messages.for_scope(context.scope).for_subject(value_name).first_message; | |
IF message; info.${message.level} = c.loc(message.id); END; | |
END; | |
IF info.filter && info.value; | |
f = info.filter; | |
IF f == "join"; | |
info.value = info.value.join(', '); | |
ELSE; | |
TRY; info.value = FILTER $f; info.value; END; CATCH; "Error applying filter to ${info.name}!"; END; | |
END; | |
END; | |
IF info.defined('value') && info.defined('timezone'); | |
info.value = set_timezone(info.value, info.timezone); | |
END; | |
DEFAULT info.id = "form_" _ info.name.replace('\.', '_'); | |
%] | |
<div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]"> | |
[% label_field(info) %] | |
<div class="[% info.type || 'datetime' %] controls row"> | |
[%~ IF info.prepend || info.append ~%]<div class="[% IF info.prepend %]input-prepend[% END; IF info.append %] input-append[% END %]"> | |
[%~ IF info.prepend ~%]<span class="add-on">[% info.prepend %]</span>[%~ END ~%] | |
[%~ END ~%] | |
<input style="width: 90px;" [%~ ~%] | |
type="[% info.type || 'date' %]"[%~ ~%] | |
id="[% info.id %]" name="[% info.name %].date"[%~ =%] | |
[% IF info.pattern %] pattern="[% info.pattern %]" [% END =%] | |
value="[% info.value | html %]" | |
[%~ IF info.classes %] class="[% info.classes.join(" ") %]"[% END ~%] | |
[% # The unicode char below is purely to make the placeholder junk work. %] | |
[%~ IF info.hint %] placeholder="◦ [% c.loc(info.hint) %]"[% END ~%] | |
[%~ IF info.autocomplete == 0 %] autocomplete="false"[% END ~%] | |
[%~ IF info.disabled == 1 %] disabled="disabled"[% END ~%] | |
[%~ IF info.tabindex %] tabindex="[% info.tabindex %]"[% END ~%] | |
[%~ IF info.maxlength %] maxlength="[% info.maxlength %]"[% END ~%]> | |
[%~ IF info.append ~%]<span class="add-on">[% info.append %]</span>[%~ END ~%] | |
[%~ IF info.prepend || info.append ~%]</div>[%~ END ~%] | |
<select name="[% info.name %].hour" style="width: 5em;"> | |
[% FOREACH i IN [0..23] %] | |
<option value="[% i | format("%02d") %]"[% IF info.value.hour && info.value.hour == i %] selected="selected"[% END %]> | |
[% IF info.military_time %] | |
[% i | format("%02d") %] | |
[% ELSE; | |
IF i == 0; c.loc("[_1]AM", 12); | |
ELSIF i < 12; c.loc("[_1]AM", i); | |
ELSIF i == 12; c.loc("[_1]PM", 12); | |
ELSE; c.loc("[_1]PM", i - 12); | |
END %] | |
[% END %] | |
</option> | |
[% END %] | |
</select> | |
<select name="[% info.name %].minute" style="width: 5em;"> | |
[% FOR i IN [0, 15, 30, 45] %] | |
<option value="[% i | format("%02d") %]"[% IF info.value.minute && info.value.minute >= i && info.value.minute < i + 15 %] selected="selected"[% END %]>[% i | format("%02d") %]</option> | |
[% END %] | |
</select> | |
[% IF info.required || info.error || info.success || info.warning %] | |
<span class="help-inline">[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]</span> | |
[% END %] | |
[% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %] | |
</div> | |
</div> | |
[% END; | |
MACRO text_field(info) BLOCK; | |
IF !info.defined('value'); | |
IF results.${context.scope}; | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
info.value = results.${context.scope}.get_original_value(value_name); | |
END; | |
IF !info.defined('value') && info.default_value; | |
info.value = info.default_value; | |
END; | |
END; | |
IF !info.error && messages.for_scope(context.scope); | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
message = messages.for_scope(context.scope).for_subject(value_name).first_message; | |
IF message; info.${message.level} = c.loc(message.id); END; | |
END; | |
IF info.filter && info.value; | |
f = info.filter; | |
IF f == "join"; | |
info.value = info.value.join(', '); | |
ELSE; | |
TRY; info.value = FILTER $f; info.value; END; CATCH; "Error applying filter to ${info.name}!"; END; | |
END; | |
END; | |
DEFAULT info.id = "form_" _ info.name.replace('\.', '_'); | |
%] | |
<div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]"> | |
[% label_field(info) %] | |
<div class="[% info.type || 'text' %] controls"> | |
[%~ IF info.prepend || info.append ~%]<div class="[% IF info.prepend %]input-prepend[% END; IF info.append %] input-append[% END %]"> | |
[%~ IF info.prepend ~%]<span class="add-on">[% info.prepend %]</span>[%~ END ~%] | |
[%~ END ~%] | |
<input type="[% info.type || 'text' %]" id="[% info.id %]" name="[% info.name %]" | |
[% IF info.pattern %] pattern="[% info.pattern %]" [% END %] | |
value="[% info.value | html %]" | |
[%~ IF info.classes %] class="[% info.classes.join(" ") %]"[% END ~%] | |
[% # The unicode char below is purely to make the placeholder junk work. %] | |
[%~ IF info.hint %] placeholder="◦ [% c.loc(info.hint) %]"[% END ~%] | |
[%~ IF info.autocomplete == 0 %] autocomplete="false"[% END ~%] | |
[%~ IF info.disabled == 1 %] disabled="disabled"[% END ~%] | |
[%~ IF info.tabindex %] tabindex="[% info.tabindex %]"[% END ~%] | |
[%~ IF info.maxlength %] maxlength="[% info.maxlength %]"[% END ~%]> | |
[%~ IF info.append ~%]<span class="add-on">[% info.append %]</span>[%~ END ~%] | |
[%~ IF info.prepend || info.append ~%]</div>[%~ END ~%] | |
[% IF info.required || info.error || info.success || info.warning %] | |
<span class="help-inline">[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]</span> | |
[% END %] | |
[% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %] | |
</div> | |
</div> | |
[% END; | |
MACRO textarea_field(info) BLOCK; | |
IF !info.defined('value'); | |
IF results.${context.scope}; | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
info.value = results.${context.scope}.get_original_value(value_name); | |
END; | |
IF !info.defined('value') && info.default_value; | |
info.value = info.default_value; | |
END; | |
END; | |
IF !info.error && messages.for_scope(context.scope); | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
message = messages.for_scope(context.scope).for_subject(value_name).first_message; | |
IF message; info.${message.level} = c.loc(message.id); END; | |
END; | |
%] | |
<div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]"> | |
[% label_field(info) %] | |
<div class="[% info.type || 'textarea' %] controls"> | |
<textarea [%= ~%] | |
id="form_[% info.name %]" name="[% info.name %]" | |
[% IF info.hint %] placeholder="[% c.loc(info.hint) %]"[% END %] | |
[%~ IF info.classes %] class="[% info.classes.join(' ') %]"[% END ~%] | |
[%~ IF info.disabled == 1 %] disabled="disabled"[% END ~%] | |
[%~ IF info.autocomplete == 0 %] autocomplete="false"[% END ~%] | |
[%~ IF info.tabindex %] tabindex="[% info.tabindex %]"[% END ~%] | |
[%~ IF info.rows %] rows="[% info.rows %]"[% END ~%] | |
[%~ IF info.cols %] cols="[% info.cols %]"[% END ~%] | |
> | |
[%~ info.value | html ~%] | |
</textarea> | |
[% IF info.error || info.success || info.warning %] | |
<span class="help-block">[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]</span> | |
[% END %] | |
[% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %] | |
</div> | |
</div> | |
[% END; | |
MACRO checkboxes(info) BLOCK; | |
IF !info.defined('value') && !info.defined('selected'); | |
temp = []; | |
IF results.${context.scope}; | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
temp = results.${context.scope}.get_original_value(value_name); | |
END; | |
IF temp.size == 0 && ( info.defined('default_value') || info.defined('default_selected') ); | |
temp = info.default_selected || info.default_value; | |
END; | |
info.selected = {}; | |
FOREACH v IN temp; | |
info.selected.$v = v; | |
END; | |
END; | |
IF !info.error && messages.for_scope(context.scope); | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
IF message; info.${message.level} = c.loc(message.id); END; | |
END; | |
%] | |
<div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]"> | |
[% label_field(info) %] | |
<div class="checkboxes controls"> | |
[% FOREACH item = info.options; | |
IF info.defined('value_method'); | |
SET this_value = item.${info.value_method}; | |
ELSE; | |
SET this_value = item; | |
END; | |
IF info.defined('value_method'); | |
SET this_name = item.${info.label_method}; | |
ELSE; | |
SET this_name = item; | |
END; | |
%]<label class="[% IF info.type == 'radio'; "radio"; ELSE; "checkbox"; END %][% IF info.inline %] inline[% END %]"><input type="[% info.type || 'checkbox' %]" name="[% info.name %]" value="[% this_value | html %]"[% IF info.value == this_value || info.selected.defined(this_value) %] checked="checked"[% END %]> [% this_name %]</label> | |
[% END %] | |
[% IF info.required || info.error || info.success || info.warning %] | |
<p class="help-block"> | |
[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END %] | |
[% info.error; info.warning; info.success %]</span> | |
</p> | |
[% END %] | |
</div> | |
</div> | |
[% END ~%] | |
[%~ MACRO checkbox(info) BLOCK; | |
IF !info.defined('checked') && results.${context.scope}; | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("${context.scope}.", ""); END; | |
info.checked = results.${context.scope}.get_original_value(value_name); | |
END; | |
IF !info.defined('checked'); | |
IF results.${context.scope}; | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
info.checked = results.${context.scope}.get_original_value(value_name); | |
END; | |
IF !info.defined('checked') && info.default_checked; | |
info.checked = info.default_checked; | |
END; | |
END; | |
IF !info.error && messages.for_scope(context.scope); | |
value_name = info.name; | |
IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END; | |
IF message; info.${message.level} = c.loc(message.id); END; | |
END; | |
%] | |
<div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]"> | |
[% label_field(info) %] | |
<div class="[% info.type || 'text' %] controls"> | |
<input type="[% info.type || 'checkbox' %]" id="[% info.id %]" name="[% info.name %]" | |
[% IF info.pattern %] pattern="[% info.pattern %]" [% END %] | |
value="[% info.value | html %]" | |
[%~ IF info.classes %] class="[% info.classes.join(" ") %]"[% END ~%] | |
[%~ IF info.disabled == 1 %] disabled="disabled"[% END ~%] | |
[%~ IF info.checked == 1 %] checked="checked"[% END ~%] | |
[%~ IF info.tabindex %] tabindex="[% info.tabindex %]"[% END ~%] | |
[%~ IF info.maxlength %] maxlength="[% info.maxlength %]"[% END ~%]> | |
[% IF info.required || info.error || info.success || info.warning %] | |
<span class="help-inline">[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]</span> | |
[% END %] | |
[% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %] | |
</div> | |
</div> | |
[% END ~%] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment