Notes for developing custom components
Intro
Custom components are a way for clients to develop their own components to be embedded into Pages. These are available via Admin->Pages->Components. They are a collection of HTML, JavaScript, CSS and standard Pages options. For security reasons there is currently no server cacheing available for these components so using a public REST API may start to hit usage limits fairly quickly. Browser local storage can be used to help this a little though.
Options
Each option added will be available to Pages editors when adding your component to a page. The values set there will be passed on to the JavaScript code to be used.
Component header
By default, the custom component will show the component name in a header bar. However if an option named title
exists that value will be used instead. Setting the value of title
and the label to an empty string will hide the header. To make this header option non-editable from the Pages front-end, use an input type of none
.
JavaScript
Code is run inside a closure once Pages has added the component to the page and the DOM is ready. Two variables are exposed -
element
A jQuery object for the element containing your HTML.options
An object containing your option fields and values.
jQuery is available as jQuery
or $
. Requirejs is also available for loading external libraries if needed.
Events
Pages has some events that can be sent to each component when being manipulated. They are sent to a parent element of the component and can be listened to like this - element.closest('.js-tile').on('pages-tile-resize', function(event){/*some_code_here*/})
pages-tile-move
Called when this component is moved around in Pages edit mode.pages-tile-delete
Called when this component is removed in Pages edit mode.pages-tile-resize
Called when this component changes size in Pages edit mode or the browser is resized.
These may be needed by your component if it has to be responsive. Embedding Google Maps for example requires resetting it in JS as the component resizes to take into account its new shape.
Note that currently the move and resize events are sent as soon as the mouse button is released, but before Pages animates/snaps them to their final positions. If your event listener needs to know the exact size of the component you will need to check it after a short delay. We use setTimeout(function(){your_code_here}, 300);
inside the event listener to give enough time for the position/size to settle down.
There is also another event pages-tile-add
that all custom component JavaScript code already runs inside so this does not have to be listened for. This is triggered when this component is added to the DOM, either loading the page or adding this component to an existing page.
HTML
It is recommended not to use id attributes because we don't know how many times this component might be added to a page. Using element.find('.<class-name>');
from JavaScript to target elements works as a way around this. At Claromentis we add a js-
prefix to this class name which lets our designers know that these are not CSS classes.
CSS
The CSS class mentioned while editing a custom component will be automatically added to a parent element that contains this component. This allows you to control styles for the entire component (header bar and any decoration) rather than just the element that is created in the HTML editor. So far we have only used this to turn the background transparent if a component is made of multiple visible parts (news list, fast access buttons, etc)
Bootstrap 3 is used throughout Claromentis and will already be loaded on any Pages page.
Examples
Customizing the header
In a custom component add a string option with an internal name of title
and with suitable placeholder and default values.
Add this component to a page and the header should update when you change options and submit. Set the value to nothing to hide the header completely.
Getting option values
In a custom component add a select option with an internal name of option_1
and a default value of first
Option values
{
"first": "First",
"second": "Second",
"third": "Third",
"fourth": "Fourth",
"last": "Last"
}
Also set the maximum component height higher than 1 so that the header doesn't take up all of the room. Or hide it as mentioned in the previous example.
HTML
<div class="panel-body">
You chose <span class="js-choice"></span>
</div>
JS
element.find('.js-choice').text(options.option_1);
Add this component to a page and in edit-mode the component text will update when this option is changed and submitted. Of course this variable will be used to control program flow in a real component rather than just being displayed.
A component that uses the Claromentis REST API
This is a component that will show any documents that a user has checked out and allow them to open the documents page in a new tab, or cancel their checkout for it.
Create a new custom components named "My checked out documents" or something similar. Or call it anything and use a title option to control what the header says. No other options are needed.
HTML
<div class="panel-body">
<div class="js-loading">
<img src="/images/default/ajax-loader.gif" />\
</div>
<div class="js-no-results" style="display: none;">
<h3>
No checked out documents
</h3>
</div>
<table class="doc-table js-doc-table" style="display: none;">
<tbody class="js-doc-list">
</tbody>
</table>
</div>
CSS
.doc-table th {
padding: 0 10px;
}
.doc-table td {
padding: 0 10px;
text-align: left;
}
.doc-table td:not(:last-child) {
border-right: 1px #ddd solid;
}
JavaScript
var $loading = element.find('.js-loading');
var $no_results = element.find('.js-no-results');
var $table = element.find('.js-doc-table');
var $table_rows = element.find('.js-doc-list');
var row_template = '<tr><td>{{filename}}</td><td><a href="/intranet/documents/{{parent_id}}/{{doc_id}}" target="_blank"><span class="glyphicons glyphicons-new-window"></span> View</a></td><td><a href="#" class="js-cancel" data-doc-id="{{doc_id}}"><span class="glyphicons glyphicons-ban-circle"></span> Cancel</a></td></tr>';
$table_rows.on('click', '.js-cancel', function()
{
var doc_id = $(this).data('doc-id');
$.ajax('/intranet/rest/documents/checkin/' + doc_id, {method: 'DELETE', success: function()
{
cla.showMessage('Check-out cancelled');
update();
}});
});
function update()
{
$table.hide();
$no_results.hide();
$loading.show();
$table_rows.empty();
$.get('/intranet/rest/documents/checkin', function(response)
{
$loading.hide();
if (response.length === 0)
{
$no_results.show();
return;
}
$table.show();
for (var doc in response)
{
var tmpl = row_template;
tmpl = tmpl.replace(/{{filename}}/g, response[doc].title);
tmpl = tmpl.replace(/{{doc_id}}/g, response[doc].doc_id);
tmpl = tmpl.replace(/{{parent_id}}/g, response[doc].parent_id);
tmpl = tmpl.replace(/{{checkin_link}}/g, response[doc].checkin_URI);
$table_rows.append(tmpl);
}
});
}
update();
Add this to a page then check out a few files in Documents to see it in action.
A component using options and an event
This is a variant on a component that we use each year in Claromentis at holiday time. It adds a snow effect to the homepage and can be configured.
Options
-
- Label:
<empty>
- Internal name:
title
- Type:
String
- Input control:
none
- Default value:
<empty>
- Label:
-
- Label:
Style
- Internal name:
style%
- Type:
String
- Input control:
select
- Options:
{"normal":
"Normal",
"round":
"Rounded
flakes",
"shadow":
"Flake
shadows",
"round_shadow":
"Rounded
flakes
with
shadows"}
- Default value:
normal
- Label:
-
- Label:
Number of flakes
- Internal name:
flakeCount
- Type:
Integer
- Input control:
int
- Default value:
500
- Minimum:
100
- Maximum:
1000
- Label:
-
- Label:
Flake maximum speed
- Internal name:
maxSpeed
- Type:
Integer
- Input control:
int
- Default value:
3
- Minimum:
1
- Maximum:
10
- Label:
-
- Label:
Flake maximum size
- Internal name:
maxSize
- Type:
Integer
- Input control:
int
- Default value:
3
- Minimum:
1
- Maximum:
10
- Label:
HTML
<!--Using an external script from https://github.com/loktar00/JQuery-Snowfall-->
<script src="https://loktar00.github.io/JQuery-Snowfall/snowfall.jquery.min.js"></script>
<a href="#" class="btn btn-default snow-toggle js-toggle">
<span class="glyphicons glyphicons-pause large-button-text js-button-icon"></span> <span class="js-button-text">Pause snowfall</span>
</a>
CSS
.snowfallexample .panel{
background-color: rgba(0, 0, 0, 0);
box-shadow: none;
}
.snow-toggle {
width: 100%;
line-height: 32px;
}
.large-button-text {
font-size: 25px!important;
}
JavaScript
var round = (options.style === 'round' || options.style === 'round_shadow');
var shadow = (options.style === 'shadow' || options.style === 'round_shadow');
var snowOptions = {
flakeCount : options.flakeCount,
maxSpeed : options.maxSpeed,
maxSize : options.maxSize,
round: round,
shadow: shadow
}
var isSnowing = false;
function stopSnow()
{
$(document).snowfall('clear');
element.find('.js-button-text').text('Start snowfall');
element.find('.js-button-icon').removeClass('glyphicons-pause');
element.find('.js-button-icon').addClass('glyphicons-play');
isSnowing = false;
}
function startSnow()
{
stopSnow();
$(document).snowfall(snowOptions);
element.find('.js-button-text').text('Pause snowfall');
element.find('.js-button-icon').removeClass('glyphicons-play');
element.find('.js-button-icon').addClass('glyphicons-pause');
isSnowing = true;
}
element.closest('.js-tile').on('pages-tile-delete', function()
{
stopSnow();
})
element.find('.js-toggle').on('click', function()
{
if (isSnowing)
stopSnow();
else
startSnow();
});
setTimeout(function () {
startSnow();
}, 500);