Brickrouge is an object-oriented toolkit for PHP5.3+ that helps you create inputs, widgets, forms and many other common elements, with all the CSS and JavaScript needed to make them beautiful and magical.
Brickrouge uses Bootstrap from twitter for its style, and MooTools for its magic. Ready in a minute, you'll have everything you need to create beautiful and clean webapps.
Together with ICanBoogie, Brickrouge is one of the precious components that make the CMS Icybee, but it remains a standalone project for everyone to enjoy!
View project on GitHub Download as a Phar<?php require_once 'Brickrouge.phar'; Brickrouge\register_autoloader();
The form here against is entirely created, filled and validated using Brickrouge.
Every elements of the form are objects instanciated from the Element class or one of its subclasses.
Submit the form and see how it repopulates its fields with the values you submited, and how it handles errors.
The source code of the form is available here after.
<?php
namespace Brickrouge;
use ICanBoogie\Errors;
$form = new Form
(
array
(
Form::VALUES => $_POST,
Form::RENDERER => 'Simple',
Form::ACTIONS => array
(
new Button
(
'Save Changes', array
(
'type' => 'submit',
'class' => 'btn-primary'
)
),
new Button
(
'Cancel', array
(
'type' => 'reset'
)
)
),
Element::GROUPS => array
(
'one' => array
(
'title' => 'Basics'
),
'two' => array
(
'title' => 'Required and validated'
),
'three' => array
(
'title' => 'Example form legend'
)
),
Element::CHILDREN => array
(
'input' => new Text
(
array
(
Form::LABEL => "Input",
Element::INLINE_HELP => "With a small snippet of help text",
Element::GROUP => 'one'
)
),
'select' => new Element
(
'select', array
(
Form::LABEL => 'Select',
Element::GROUP => 'one',
Element::OPTIONS => array(1 => 1, 2, 3, 4, 5)
)
),
new Text
(
array
(
Form::LABEL => 'Uneditable Input',
Element::GROUP => 'one',
'readonly' => true,
'value' => 'Some Value Here'
)
),
'disabledInput' => new Text
(
array
(
Form::LABEL => "Disabled Input",
Element::GROUP => 'one',
'disabled' => true,
'placeholder' => "Disabled input here… carry on."
)
),
'text' => new Element
(
'textarea', array
(
Form::LABEL => 'Textarea',
Element::GROUP => 'one',
Element::DESCRIPTION => "Block of help text to describe the field above if need be.",
'rows' => 3,
'class' => 'span5'
)
),
'checkbox' => new Element
(
Element::TYPE_CHECKBOX, array
(
Element::LABEL => 'A single checkbox',
Element::GROUP => 'one'
)
),
/*
* TWO
*/
'required' => new Text
(
array
(
Form::LABEL => 'Required value',
Element::GROUP => 'two',
Element::REQUIRED => true
)
),
'email' => new Text
(
array
(
Form::LABEL => 'Email',
Element::GROUP => 'two',
Element::VALIDATOR => array('Brickrouge\Form::validate_email'),
Element::DEFAULT_VALUE => 'invalid-email'
)
),
'url' => new Text
(
array
(
Form::LABEL => 'URL',
Element::GROUP => 'two',
Element::VALIDATOR => array('Brickrouge\Form::validate_url'),
Element::DEFAULT_VALUE => 'invalid-url'
)
),
'birthday' => new Text
(
array
(
Form::LABEL => 'Date',
Element::GROUP => 'two',
Element::VALIDATOR => array('Brickrouge\Form::validate_string', array('regex' => '#^\d{4}-\d{2}-\d{2}$#')),
Element::INLINE_HELP => 'YYYY-MM-DD',
Element::DESCRIPTION => 'We can validate patterns with the <q>string</q> validator.',
Element::DEFAULT_VALUE => 'invalid-birthday'
)
),
'length-minmax' => new Text
(
array
(
Form::LABEL => 'Length min/max',
Element::GROUP => 'two',
Element::VALIDATOR => array('Brickrouge\Form::validate_string', array('length-min' => 3, 'length-max' => 6)),
Element::INLINE_HELP => 'Between 3 and 6 characters',
Element::DESCRIPTION => 'We can also validate the number of characters with the <q>string</q> validator.'
)
),
/*
* THREE
*/
'optionsCheckboxes' => new Element
(
Element::TYPE_CHECKBOX_GROUP, array
(
Form::LABEL => 'List of Options',
Element::DEFAULT_VALUE => array('option1' => true, 'option2' => true, 'option3' => true),
Element::DESCRIPTION => '<strong>Note:</strong> Labels surround all the options for much larger click areas and a more usable form.',
Element::GROUP => 'three',
Element::OPTIONS => array
(
'option1' => "Option one is this and that—be sure to include why it’s great",
'option2' => "Option two can also be checked and included in form results",
'option3' => "Option three can—yes, you guessed it—also be checked and included in form results",
'option4' => "Option four cannot be checked as it is disabled."
),
Element::OPTIONS_DISABLED => array('option4' => true),
'class' => 'inputs-list'
)
),
'disabledOptionsCheckboxes' => new Element
(
Element::TYPE_CHECKBOX_GROUP, array
(
Form::LABEL => 'Disabled List of Options',
Element::DEFAULT_VALUE => array('option1' => true, 'option2' => true, 'option3' => true),
Element::DESCRIPTION => 'This group is disabled.',
Element::GROUP => 'three',
Element::OPTIONS => array
(
'option1' => "Option one is this and that—be sure to include why it’s great",
'option2' => "Option two can also be checked and included in form results",
'option3' => "Option three can—yes, you guessed it—also be checked and included in form results",
'option4' => "Option four cannot be checked as it is disabled."
),
Element::OPTIONS_DISABLED => array('option4' => true),
'class' => 'inputs-list',
'disabled' => true
)
),
'optionsRadios' => new Element
(
Element::TYPE_RADIO_GROUP, array
(
Form::LABEL => 'List of Options',
Element::DEFAULT_VALUE => 'option2',
Element::GROUP => 'three',
Element::OPTIONS => array
(
'option1' => "Option one is this and that—be sure to include why it’s great",
'option2' => "Option two can is something else and selecting it will deselect options 1"
),
'class' => 'inputs-list'
)
),
'disabledOptionsRadios' => new Element
(
Element::TYPE_RADIO_GROUP, array
(
Form::LABEL => 'Disabled List of Options',
Element::DEFAULT_VALUE => 'option2',
Element::DESCRIPTION => 'This group is disabled.',
Element::GROUP => 'three',
Element::OPTIONS => array
(
'option1' => "Option one is this and that—be sure to include why it’s great",
'option2' => "Option two can is something else and selecting it will deselect options 1"
),
'class' => 'inputs-list',
'disabled' => true
)
)
),
'class' => 'form-horizontal'
)
); You can ask a form to validate the data provided by the user. Errors are collected in a
ICanBoogie\Errors object. The method search for required values before
validation.
<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
$errors = new \ICanBoogie\Errors();
$form->validate($_POST, $errors);
}
Forms are traversed recursively. This comes handy when you need to alter a bunch of elements, or simply display their lovely names as the example below.
<?php
foreach ($form as $element)
{
echo ' ' . ($element['name'] ?: '<em>a nameless element</em>');
}
A form and all of its elements can be easily disabled using the disabled
attribute:
<?php $form['disabled'] = true;
Usually elements within a form are grouped according to the GROUP
attribute. But groups can be as easilly created using the Group
class.
The Group class wraps its children in a FIELDSET element,
an optionnal legend can be used to provide the group with a title. Each child is wrapped
in a DIV.field element and the form label is distinguished from the input
itself.
The Group class supports the WEIGHT attribute and displays
ordered children.
<?php
namespace Brickrouge;
echo new Group(array(
Element::LEGEND => "What's your name ?",
Element::CHILDREN => array
(
'lastname' => new Text(array(
Form::LABEL => 'Lastname',
Element::WEIGHT => '1',
'type' => 'text',
'value' => 'Laviale'
)),
'firstname' => new Text(array(
Form::LABEL => 'Firstname',
'type' => 'text',
'value' => 'Olivier'
)),
'salutation' => new Salutation(array(
Element::WEIGHT => 'firstname:before',
'value' => 2
))
),
'class' => 'form-horizontal'
)); If there is no rendered defined for the form, its children and simply concatened when it's rendered.
<?php
namespace Brickrouge;
echo new Form(array(
Element::CHILDREN => array
(
new Text(array(Element::LABEL => "Label name", Element::LABEL_POSITION => 'above', 'class' => "span3", 'placeholder' => "Type something...")),
new Element(Element::TYPE_CHECKBOX, array(Element::LABEL => "Check me out")),
new Button("Submit", array('type' => 'submit'))
),
'class' => "well"
));Reflecting default WebKit styles, just add .form-search for extra rounded search fields.
<?php
namespace Brickrouge;
echo new Form(array(
Element::CHILDREN => array
(
new Text(array('class' => 'input-medium search-query')),
' ' .
new Button("search", array('type' => 'submit'))
),
'class' => "well form-search"
));Inputs are block level to start. For .form-inline and .form-horizontal, we use inline-block.
<?php
namespace Brickrouge;
echo new Form(array(
Element::CHILDREN => array
(
new Text(array('class' => 'input-small', 'placeholder' => "Email")), ' ',
new Text(array('class' => 'input-small', 'placeholder' => "Password", 'type' => 'password')), ' ',
new Button("Go", array('type' => 'submit'))
),
'class' => "well form-inline"
));<?php
namespace Brickrouge;
echo new Form(array(
Form::ACTIONS => array
(
new Button('Save changes', array('type' => 'submit', 'class' => 'btn-primary')),
new Button('Cancel', array('type' => 'reset'))
),
Element::CHILDREN => array(new Group(array(
Element::LEGEND => "Controls Bootstrap supports",
Element::CHILDREN => array
(
'input01' => new Text
(
array
(
Form::LABEL => "Text input",
Element::DESCRIPTION => "In addition to freeform text, any HTML5 text-based input appears like so.",
'class' => 'xlarge'
)
),
'optionsCheckbox' => new Element
(
Element::TYPE_CHECKBOX, array
(
Form::LABEL => "Checkbox",
Element::LABEL => "Option one is this and that—be sure to include why it’s great"
)
),
'select01' => new Element
(
'select', array
(
Form::LABEL => "Select list",
Element::OPTIONS => array_combine(range(1, 5), range(1, 5))
)
),
'multiSelect' => new Element
(
'select', array
(
Form::LABEL => "Multi-select",
Element::OPTIONS => array_combine(range(1, 5), range(1, 5)),
'multiple' => true
)
),
'fileInput' => new Element
(
'input', array
(
Form::LABEL => "File input",
'type' => 'file',
'class' => 'input-file'
)
),
'textarea' => new Element
(
'textarea', array
(
Form::LABEL => "Textarea",
'rows' => 3,
'class' => 'input-xlarge'
)
)
)))
),
'class' => 'form-horizontal'
));Shown on the left are all the default form controls we support. Here's the bulleted list:
<?php
namespace Brickrouge;
echo new Form(array(
Form::ACTIONS => array
(
new Button('Save changes', array('type' => 'submit', 'class' => 'btn-primary')),
new Button('Cancel', array('type' => 'reset'))
),
Element::CHILDREN => array(new Group(array(
Element::LEGEND => "Form control states",
Element::CHILDREN => array
(
'focusedInput' => new Text
(
array
(
Form::LABEL => "Focused input",
'class' => 'input-xlarge focused',
'value' => "This is focused..."
)
),
'disabledInput' => new Text
(
array
(
Form::LABEL => "Disabled input",
'class' => 'input-xlarge disabled',
'disabled' => true,
'placeholder' => "Disabled input here..."
)
),
'optionsCheckbox' => new Element
(
Element::TYPE_CHECKBOX, array
(
Form::LABEL => "Disabled checkbox",
Element::LABEL => "This is a disabled checkbox",
'disabled' => true
)
),
'inputWarning' => new Text
(
array
(
Form::LABEL => "Input with warning",
Element::INLINE_HELP => "Something may have gone wrong",
Element::STATE => 'warning'
)
),
'inputError' => new Text
(
array
(
Form::LABEL => "Input with error",
Element::INLINE_HELP => "Please correct the error",
Element::STATE => 'error'
)
),
'inputSuccess' => new Text
(
array
(
Form::LABEL => "Input with success",
Element::INLINE_HELP => "Woohoo!",
Element::STATE => 'success'
)
),
'selectSuccess' => new Element
(
'select', array
(
Form::LABEL => "Input with success",
Element::INLINE_HELP => "Woohoo!",
Element::STATE => 'success',
Element::OPTIONS => array(1 => 1, 2, 3, 4, 5)
)
)
)
))),
'class' => 'form-horizontal'
));
The STATE attribute defines state of the input element,
possible values are null, error,
warning or success. Group elements use
the corresponding class to highlight the element.
<?php echo new Text(array(Element::STATE => 'error', Element::INLINE_HELP => "The value should not be blue"));
When the form is validated using its
validate()
method it automatically sets the state of its elements.
Text inputs—with appended or prepended text—provide an easy way to give more
context for your inputs. Great examples include the @ sign for Twitter usernames or $ for
finances.
Use the ADDON attribute to define the add-on text, and the
ADDON_POSITION attribute to define its position: before
or after the text input.
Create checkbox groups and radio groups very easily using the
TYPE_CHECKBOX_GROUP and TYPE_RADIO_GROUP special
types.
Inline checkboxes and radios are also supported. Just add
.inline-inputs to the group element and you're done.
<?php
namespace Brickrouge;
echo new Form(array(
Form::ACTIONS => array(
new Button('Save changes', array('type' => 'submit', 'class' => 'btn-primary')),
new Button('Cancel', array('type' => 'reset'))
),
Element::CHILDREN => array(new Group(array(
Element::LEGEND => "Extending form controls",
Element::CHILDREN => array(
new Element('div', array(
Form::LABEL => "Form sizes",
Element::CHILDREN => array(
new Text(array('placeholder' => ".span2", 'class' => 'span2')),
new Text(array('placeholder' => ".span3", 'class' => 'span3')),
new Text(array('placeholder' => ".span4", 'class' => 'span4'))
),
Element::DESCRIPTION => "Use the same <code>.span*</code> classes from the grid system for input sizes.",
'class' => 'docs-input-sizes'
)),
'prependedInput' => new Text(array(
Form::LABEL => "Prepended text",
Text::ADDON => "@",
Text::ADDON_POSITION => 'before',
Element::DESCRIPTION => "Here's some help text",
'size' => 16
)),
'appendedInput' => new Text(array(
Form::LABEL => "Appended text",
Text::ADDON => "€",
Text::ADDON_POSITION => 'after',
Element::DESCRIPTION => "Here's some help text",
'size' => 16
)),
'inlineCheckbox1' => new Element(Element::TYPE_CHECKBOX_GROUP, array(
Form::LABEL => "Inline checkboxes",
Element::OPTIONS => array_combine(range(1, 3), range(1, 3)),
'class' => 'inline-inputs'
)),
'optionsCheckboxes' => new Element(Element::TYPE_CHECKBOX_GROUP, array(
Form::LABEL => "Checkboxes",
Element::OPTIONS => array(
'option1' => "Option one is this and that—be sure to include why it’s great",
'option2' => "Option two can also be checked and included in form results",
'option3' => "Option three can—yes, you guessed it—also be checked and included in form results"
),
Element::DESCRIPTION => "<strong>Note:</strong> Labels surround all the options for much larger click areas and a more usable form."
)),
'optionsRadios' => new Element(Element::TYPE_RADIO_GROUP, array(
Form::LABEL => "Radio buttons",
Element::OPTIONS => array(
'option1' => "Option one is this and that—be sure to include why it’s great",
'option2' => "Option two can is something else and selecting it will deselect option one"
),
'value' => 'option1'
))
)
))),
'class' => 'form-horizontal'
));Add small overlays of content to any element for housing secondary information.
Hover over the button to trigger the popover.
<a href="javascript://" class="btn btn-danger" rel="popover" data-title="A title" data-content="And here's some amazing content. It's very engaging. right?" data-animate="true" data-placement="after">hover for popover</a>
Popovers are usually anchored to an element and can follow it on the page, wherever it goes, adjusting their box trying to stay as much visible as possible.
<?php
namespace Brickrouge;
echo new PopoverWidget
(
array
(
Popover::TITLE => "Popover",
Popover::INNER_HTML => "Etiam porta sem malesuada magna mollis euismod. Maecenas faucibus mollis interdum. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.",
Popover::ANCHOR => "popover-anchor-0",
Popover::PLACEMENT => 'before',
PopoverWidget::VISIBLE => true
)
); Popover elements can be placed all around their anchor: before, after, above or below.
Popover elements are automatically repositioned to follow their anchor and may change their placement according to the available space around it.
If the there is not enough available space for the right popover it will jump to the left of the anchor.
<?php
namespace Brickrouge;
echo new PopoverWidget
(
array
(
Popover::INNER_HTML => "Popover above, without title.",
Popover::ANCHOR => "popover-anchor-1",
Popover::PLACEMENT => 'above',
PopoverWidget::VISIBLE => true
)
);
echo new PopoverWidget
(
array
(
Popover::TITLE => "Popover after",
Popover::INNER_HTML => "Pellentesque vitae ligula et est consequat accumsan.",
Popover::ANCHOR => "popover-anchor-1",
Popover::PLACEMENT => 'after',
PopoverWidget::VISIBLE => true
)
);
echo new PopoverWidget
(
array
(
Popover::TITLE => "Popover below",
Popover::INNER_HTML => "Morbi convallis vehicula bibendum. Morbi vestibulum, augue a blandit blandit.",
Popover::ANCHOR => "popover-anchor-1",
Popover::PLACEMENT => 'below',
PopoverWidget::VISIBLE => true
)
); Contrast can be used to indicate that the content of the popover is not directly related to the current page.
Popovers can be used to provide a way to edit complex data which are presented more simply on the page.
<?php
namespace Brickrouge;
echo new PopoverWidget
(
array
(
Popover::TITLE => "Caching options",
Popover::INNER_HTML => new Form
(
array
(
Form::RENDERER => 'Simple',
Form::CHILDREN => array
(
new Text
(
array
(
Form::LABEL => 'Maximum size of the cache',
Text::ADDON => 'Mo',
'class' => 'measure',
'size' => 4,
'value' => 8
)
),
new Text
(
array
(
Form::LABEL => 'Interval between cleanup',
Text::ADDON => 'minutes',
'class' => 'measure',
'size' => 4,
'value' => 15
)
)
),
'class' => 'stacked'
)
),
Popover::ANCHOR => "popover-anchor-options",
Popover::ACTIONS => 'boolean',
PopoverWidget::VISIBLE => true,
'class' => 'widget-popover popover contrast fit-content'
)
);
?>
<button class="spinner" id="popover-anchor-options" title="Configure caching options">The cache size is 8Mo and it's cleaned up every 15 minutes</button> For a more durable component with less code, we've removed the differentiating look for block
alerts, messages that com with more padding and typically more text. The class also has changed
to .alert-block.
Wrap your message and an optional close icon in a div with simple class.
<?php
namespace Brickrouge;
echo new Alert("<strong>Warning!</strong> Best check yo self, you’re not looking too good."); Brickrouge comes with the Javascript needed to support alert messages. Clicking the close button removes the alert, and if it's attached to a form it also clears the errors on the form.
<?php
namespace Brickrouge;
$values = array('input1' => 'Brickrouge');
$form = new Form(array
(
Element::CHILDREN => array
(
'input0' => new Text(array(Form::LABEL => 'Required', 'required' => true)),
'input1' => new Text(array(Form::LABEL => 'Optionnal'))
),
Form::VALUES => $values,
Form::RENDERER => 'Simple',
'class' => 'form-horizontal'
));
$errors = new \ICanBoogie\Errors();
$form->validate($values, $errors);
echo $form; Easily extend the standard alert message with two optional classes:
.alert-block for more padding and text controls and
.alert-heading for a matching heading.
Use the HEADING attribute to define the heading. The .alert-block
class is automatically added if the HEADING attribute is defined.
Best check yo self, you’re not looking too good. Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.
<?php
namespace Brickrouge;
echo new Alert
(
"<p>Best check yo self, you’re not looking too good. Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.</p>", array
(
Alert::HEADING => "Warning!"
)
); <?php
namespace Brickrouge;
echo new Alert("<strong>Oh snap!</strong> Change a few things up and try submitting again.", array(Alert::CONTEXT => 'error')); <?php
namespace Brickrouge;
echo new Alert("<strong>Well done!</strong> You successfully read this important alert message.", array(Alert::CONTEXT => 'success')); <?php
namespace Brickrouge;
echo new Alert("<strong>Heads up!</strong> This alert needs your attention, but it’s not super important.", array(Alert::CONTEXT => 'info')); The Alert class can also render error collections.
Login failed!
Unknown username/password combination.
Funny username by the way ;-)
<?php namespace Brickrouge; $errors = new \ICanBoogie\Errors(); $errors['username'] = 'Unknown username/password combination.'; $errors['username'] = 'Funny username by the way ;-)'; $errors[] = 'Login failed!'; echo new Alert($errors);