Command
Do you think a new command would be helpful for your restaurant? You can follow these guidelines to create your own command.
Within this documentation we will treat how to create a command to change the background of the clicked tables.
Download from HERE the complete example file.
Interface
A command is represented by an abstract class named UICommand
, which needs the implementation of a few methods.
UICommand
extends the methods provided by UIShortcut
class because also a command can be initialised, destroyed, activated and triggered using a shortcut.
class UICommand extends UIShortcut {
init(canvas) {
// inherit in children classes
}
destroy() {
// inherit in children classes
}
activate(canvas) {
// inherit in children classes
}
deactivate(canvas) {
// inherit in children classes
}
getTitle() {
// inherit in children classes
}
getShortcut() {
// inherit in children classes
}
getToolbar() {
// inherit in children classes
}
}
The name of the class we need to extend must start with UICommand
and should continue with a descriptive name. In our case, the class will be named UICommandBackground
.
/**
* Command Background class.
*/
class UICommandBackground extends UICommand {
/**
* @todo methods implementation goes here
*/
}
Initialise
Every time a command is registered within the canvas, the init()
method is invoked in order to prepare the command.
The init()
method receives the current instance of UICanvas
as parameter, the object used to handle the drawing area.
The init()
method doesn't need to return any values.
This method is invoked only once.
When it is needed to use this method, it is mandatory to invoke the parent method first.
In our case, we don't need to implement this method. Anyhow here's an example of implementation.
init(canvas) {
super.init(canvas);
console.log('[UICommandBackground] initialized on: ' + new Date());
}
Destroy
Every time a command is deregistered from the canvas, the destroy()
method is invoked in order to clean some junk data.
By default, the system never deregisters any commands. They can be deregistered by using the UICanvas::unregisterCommand(UICommand command)
method. This method is available for customizations that require to unset other commands. For example, it is possible to temporarily unregister a specific command at runtime.
The destroy()
method doesn't receive any parameters. In case the UICanvas
argument is needed, it is possible to assign it to an internal property while initialising this object.
The destroy()
method doesn't need to return any values.
This method is invoked only once.
In our case, we don't need to implement this method. Anyhow here's an example of implementation.
destroy() {
console.log('[UICommandBackground] destroyed on: ' + new Date());
}
Activation
The activate()
method is invoked after selecting the command or in case the command shortcut is triggered. The shortcut combination can be defined through the getShortcut()
method.
The activate()
method receives the current instance of UICanvas
as parameter, the object used to handle the drawing area.
The activate()
method doesn't need to return any values.
When it is needed to use this method, it is mandatory to invoke the parent method first.
Here's what our command should do when it is activated.
activate(canvas) {
super.activate(canvas);
// keep a reference to the canvas
this.canvas = canvas;
}
Deactivation
The deactivate()
method is invoked when a different command is activated.
The deactivate()
method receives the current instance of UICanvas
as parameter, the object used to handle the drawing area.
The deactivate()
method doesn't need to return any values.
When it is needed to use this method, it is mandatory to invoke the parent method first.
In our case, we don't need to implement this method. Anyhow here's an example of implementation.
deactivate(canvas) {
super.deactivate(canvas);
// remove canvas reference as it is no more needed
delete this.canvas;
}
Title
Here's possible to return a string that will be placed within the title attribute of the command. By hovering the mouse above the command, a tooltip containing this string will be displayed.
In case the command can be triggered using a shortcut, those keys will be automatically included within the title.
The getTitle()
method must return a string. In case the title is not needed, the string to return must be empty.
getTitle() {
return 'Change background';
}
Shortcut
It is possible to trigger the command by using a specific shortcut too. This can be done by implementing the getShortcut()
method. This method must return an array of keys built as follows:
[MODIFIER..., CHARACTER]
The allowed modifiers are meta
, ctrl
, alt
and shift
. The shortcut can support none, one or multiple modifiers. The array must specify only one character (e.g. a letter, a number or a symbol) and must be specified as last element. Here's an example list:
- ⌘⇧F -
['meta', 'shift', 'F']
- ⌃⌥P -
['ctrl', 'alt', 'P']
- ⌃, -
['meta', ',']
- X -
['X']
The getShortcut()
method doesn't receive any parameters.
The getShortcut()
method must return an array containing the modifiers (if any) and a character.
In our case, we may use (B) to trigger the activation of the command.
getShortcut() {
return ['B'];
}
Toolbar
The getToolbar()
method can be used to create a form with some fields. Those fields could be used to enhance the functionalities of the command.
The getToolbar()
method doesn't receive any parameters.
The getToolbar()
method must return a simple object containing the form fields. The keys of the object are the names of the fields. The keys are assigned to other objects that contain the rules to create the fields.
Every field object must specify the type
property, which is needed to recognize what is the input to render. The list below mentions all the types that should be used within the toolbar.
- checkbox - field used to collect YES/NO information.
- color - field used to select a color from the colopicker popup.
- hidden - hidden field that can be used for internal purposes.
- list - a dropdown that can be used to select one or more values.
- medialist - a dropdown containing all the available images.
- number - a spinner that can be used to insert a number.
- radio - used to provide a few selectable options.
- text - default input that can be used to insert some texts.
In our case, we need to provide a colorpicker to select the background color of the shape.
getToolbar() {
// get last shape to fetch the default color
var lastKey = Object.keys(this.canvas.shapes).pop();
var bgColor = 'd3d3d3';
if (lastKey) {
// get shape config
var config = this.canvas.shapes[lastKey].getConfig();
// make sure the shape supports background
if (config.bgColor) {
bgColor = config.bgColor;
}
}
// create form fieldset
return {
// background color
color: {
label: 'Background',
type: 'color',
default: bgColor,
},
};
}
Events
This class supports also dynamic methods that will be invoked for specific events. By default, every command is attached to these events:
- mousedown
- click
- mouseup
Every time the canvas gets clicked, the system will try to invoke the related methods of the active command, if any. The method to invoke must have the same name of the triggered event.
The invoked method receives 2 arguments as parameters:
Event
event - the object containing the properties of the triggered event.object
element - the instance of the element related to the triggered event.
The invoked method can return FALSE in order to stop the propagation of the event. For example, in case a shape gets clicked, the system will try to dispatch the related events (mousedown, click, mouseup) for the shape and the canvas (by following the natural hierarchy), unless the shape stops the event propagation.
mousedown(event, element) {
if (!this.active) {
// ignore if inactive
return;
}
// make sure the element is a shape
if (!(element instanceof UIShape)) {
// go ahead
return;
}
// get current element configuration
var data = element.getConfig();
// inject new background within the config
data.bgColor = this.config.color;
// save element using the updated configuration
element.save(data);
// always stop propagating the event
return false;
}
Within the initialization of the command, it is possible to register custom events at runtime (e.g. mousemove) by using this method:
UICanvas::bindEvent(object element, mixed source, mixed events)
It is possible to detach some events from a specific element by using the method below:
UICanvas::unbindEvent(mixed source, mixed events)
DOM Registration
Once the file is ready, we need to load it within the DOM and attach it to the canvas instance.
Before all, the file should be uploaded within a folder of the server, such as this one:
/administrator/components/com_vikrestaurants/assets/js/ui-svg/addons/commands/
In case the command folder doesn't exist, it is possible to create it. Obviously the file can be uploaded wherever you want.
After uploading the file, it is needed to create an override for the view used to manage the map. These are the steps to follow:
- open the Joomla! back-end
- visit the Extensions > Templates > Templates page
- select the Administrator option from the dropdown next to the search bar
- edit the active template (probably Isis)
- access the Create Overrides tab
- pick the Components > com_vikrestaurants > managemap view
This will create an override for the managemap view, which can be edited without altering the core files of the software. The file can be accessed directly from the back-end or via FTP. Here's the file path:
/administrator/templates/[TEMPLATE]/html/com_vikrestaurants/managemap/default.php
We can include here the javascript file for the command, as explained in the example below. This example assumes that the file is called background.js.
<?php
/**
* @package VikRestaurants
* @subpackage com_vikrestaurants
* @author Matteo Galletti - e4j
* @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All Rights Reserved.
* @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
* @link https://extensionsforjoomla.com
*/
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
JHtml::_('behavior.keepalive');
JHtml::_('formbehavior.chosen');
// add JS file here
$backgroundPath = 'administrator/components/com_vikrestaurants/assets/js/ui-svg/addons/commands/background.js';
VikApplication::getInstance()->addScript(JUri::root() . $backgroundPath);
After loading the javascript file, we need to place the HTML of the button within the commands panel. The box containing the commands owns a class named ui-commands-panel
.
The button must provide a unique id attribute and it should start with cmd-, such as id="cmd-background"
.
Within the body of the button, it is possible to use a FontAwesome icon to display a specific image. The system is able to display all the icons included within the FontAwesome 4.7.0 version.
<!-- clone tool -->
<button type="button" class="cmd-btn" id="cmd-clone"><i class="fa fa-clone"></i></button>
<!-- add here our BACKGROUND tool -->
<button type="button" class="cmd-btn" id="cmd-background"><i class="fa fa-paint-brush"></i></button>
<!-- search tool -->
<button type="button" class="cmd-btn" id="cmd-search"><i class="fa fa-search"></i></button>
Finally, we need to instantiate the class created previously and register it within the canvas. Here's an example.
// visible commands
canvas.registerCommand(new UICommandSelect('cmd-select'));
canvas.registerCommand(new UICommandShape('cmd-draw', cmdOptions['cmd-draw']));
canvas.registerCommand(new UICommandRubber('cmd-rubber'));
canvas.registerCommand(new UICommandClone('cmd-clone'));
// register here our JS file
canvas.registerCommand(new UICommandBackground('cmd-background'));
canvas.registerCommand(new UICommandSearch('cmd-search'));
canvas.registerCommand(new UICommandHelp('cmd-help'));
// hidden commands
canvas.registerCommand(new UICommandShortcut('cmd-shortcut'));