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 metactrlalt and shift. The shortcut can support noneone 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:

  1. open the Joomla! back-end
  2. visit the Extensions > Templates > Templates page
  3. select the Administrator option from the dropdown next to the search bar
  4. edit the active template (probably Isis)
  5. access the Create Overrides tab
  6. 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'));