jqx-element
v1.3.27
Published
JQX Custom Element Base
Downloads
388
Readme
jqx-element
You must be a member of the ni-kismet organization on NPM to access this package. Email Mark Black you username to get access.
The repository jqx-element contains a set of classes for creating custom HTML elements similar to the standard DOM Elements. Each of these custom elements can be instantiated using document.createElement and configured using attributes or properties.
Introduction
A basic element definition looks like this:
JQX('jqx-button', class Button extends JQX.ContentElement {
// Button's properties.
static get properties() {
return {
'value': {
type: 'string'
},
'name': {
type: 'string'
},
'type': {
type: 'string'
},
'clickMode': {
allowedValues: ['hover', 'press', 'release'],
type: 'string',
value: 'release'
}
};
}
/** Button's template. */
template() {
return '<button class=\'jqx-button\' inner-h-t-m-l=\'[[innerHTML]]\' id=\'button\' type=\'[[type]]\' name=\'[[name]]\' value=\'[[value]]\' disabled=\'[[disabled]]\' role=\'button\'></button>';
}
static get listeners() {
return {
'button.mousedown': '_mouseDownHandler',
'button.mouseenter': '_mouseEnterHandler',
'button.click': '_clickHandler'
};
}
_clickHandler(event) {
const that = this;
if (that.clickMode !== 'release') {
event.preventDefault();
event.stopPropagation();
}
}
_mouseDownHandler() {
const that = this;
if (that.clickMode === 'press') {
that.$.fireEvent('click');
}
}
_mouseEnterHandler() {
const that = this;
if (that.clickMode === 'hover') {
that.$.fireEvent('click');
}
}
});
The base custom element class is called BaseElement
and is accessible through JQX.BaseElement
. Most elements derive from JQX.BaseElement
. JQX.ContentElement
extends the JQX.BaseElement
by adding content
and innerHTML
properties to it. It is useful when you need to append a child element by setting a single property.
Register a Custom Element
To register a custom element, use the JQX
function and pass in the element's tag name and class. By specification, the custom element's name must contain a dash (-). The library internally checks whether Custom Elements v1 is supported and uses its lifecycle callbacks and customElements.define. Otherwise, it uses document.registerElement and the v0 lifecycle callbacks. To use custom elements, you will need a browser which natively supports Custom Elements or you will need to load polyfills such as webcomponentsjs
.
Resources:
- https://developer.mozilla.org/en-US/docs/Web/Web_Components
- https://developers.google.com/web/fundamentals/getting-started/primers/customelements
- http://webcomponents.org/
Lifecycle callbacks
- created - Called when the element has been created, but before property values are set and local DOM is initialized. Use for one-time set-up before property values are set.
- attached - Called after the element is attached to the document. Can be called multiple times during the lifetime of an element.
- ready - Called when the element is ready. Use for one-time configuration of your element.
- detached - Called after the element is detached from the document. Can be called multiple times during the lifetime of an element.
Properties
To add properties on your custom element, you can use the properties
object. All properties part of the properties
object are automatically serialized and deserialized by the element and can also be set through attributes by using the dash(-) syntax in the HTML markup. Each property can have the following members:
- reflectToAttribute - Type: Boolean. Set to
true
to cause the corresponding attribute to be set on the host node when the property value changes. If the property value isBoolean
, the attribute is created as a standard HTML boolean attribute (set if true, not set if false). For other property types, the attribute value is a string representation of the property value. The default value of this member istrue
. - defaultReflectToAttribute - Type: Boolean. Set to
true
when we want a default attribute value to be set on the host node. - readOnly - Type: Boolean. Determines whether the property is readyonly. if
true
the property can't be set by the user. - type - Type: String. Used for deserializing from an attribute.
- any - allows assigning any value to a property.
- string - allows assigning a
String
to a property. - string? - allows assigning a 'String' or null to a property.
- boolean or bool - allows assigning a
Boolean
to a property. - boolean? or bool? - allows assigning a 'Boolean' or null to a property.
- number or float - allows assigning a 'Number' to a property.
- number? or float? - allows assigning a 'Number' or null to a property.
- int or integer - allows assigning an 'Integer' to a property.
- int? or integer? - allows assigning an 'Integer' or null to a property.
- date - allows assigning a 'Date' to a property.
- date? - allows assigning a 'Date' or null to a property.
- array - allows assigning an 'Array' to a property.
- object - allows assigning an 'Object' to a property.
- allowedValues - Type: Array. Used for defining a set of values which are allowed to be set. For other values, an exception is thrown.
- notify - Type: Boolean. Determines whether an event is raised when a property is changed. The event name is: property's attribute name + - 'changed'. Example: Property's name is 'clickMode', the event's name will be 'click-mode-changed'. Example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="./../../source/styles/jqx.base.css" type="text/css" />
<link rel="stylesheet" href="../styles/demos.css" type="text/css" />
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/0.7.22/webcomponents-lite.min.js"></script>
<script type="text/javascript" src="./../../source/jqxelement.js"></script>
<script type="text/javascript" src="../../source/jqxbutton.js"></script>
<script type="text/javascript" src="../../source/jqxscrollbar.js"></script>
<script type="text/javascript" src="./../../source/jqxlistbox.js"></script>
<script type="text/javascript" src="./../../source/jqxcheckbox.js"></script>
<script type="text/javascript" src="./../../source/jqxradiobutton.js"></script>
<script>
window.onload = function () {
var list = document.getElementById('list');
var data = [
{
label: "Andrew",
value: 1,
group: "A"
},
{
label: "Natalia",
value: 5,
group: "B"
},
{
label: "Michael",
value: 4,
group: "B"
},
{
label: "Angel",
value: 2,
group: "A"
},
{
label: "Hristo",
value: 6,
group: "C"
},
{
label: "Peter",
value: 3,
group: "A"
},
{
label: "Albert",
value: 4,
group: "A"
},
{
label: "Boyko",
value: 8,
group: "A"
},
{
label: "Dimitar",
value: 9,
group: "B"
},
{
label: "George",
value: 10,
group: "C"
}
];
list.dataSource = data;
list.addEventListener('disabled-changed', function (event) {
if (event.target === list) {
alert('disabled changed');
}
});
document.getElementById("disabled").onclick = function () {
list.disabled = !list.disabled;
}
list.properties['disabled'].notify = true;
}
</script>
</head>
<body>
<jqx-list-box style="float:left;" selection-mode="checkBox" id="list"></jqx-list-box>
<div style="float: left; margin-left:100px;">
<jqx-button style="width:auto;" id="disabled">Enable/Disable</jqx-button>
</div>
</body>
</html>
- value - Default value for the property.
- observer - Type: String. A name of a function called within the Element when the property is changed. The arguments passed to your observer are the property's
oldValue
andnewValue
. - validator - Type: String. A name of a function called within the Element when the property is changing. The arguments passed to your validator are the property's
oldValue
andnewValue
. The functionreturns
the updated value. If itreturns undefined
, the newValue remains unchanged.
propertyChangedHandler(propertyName, oldValue, newValue)
method is called when a property is changed by the user. This method is useful for updating the element when the user makes some changes.
The user may watch for property changes by using the element's instance. watch(propertiesArray, propertyChangedCallback)
. The arguments passed to the propertyChangedCallback
function are propertyName, oldValue, newValue
.
Template
The template
object determines the internal HTML structure of the Element. Within that structure you can data bind properties by using two-way or one-way data binding.
template() {
return '<button class=\'jqx-button\' inner-h-t-m-l=\'[[innerHTML]]\' id=\'button\' type=\'[[type]]\' name=\'[[name]]\' value=\'[[value]]\' disabled=\'[[disabled]]\' role=\'button\'></button>';
}
Text surrounded by double curly bracket ({{ }}) or double square bracket ([[ ]]) delimiters. Identifies the host element's property being bound.
- Double-curly brackets (}) is used for two-way data flow.
- Double square brackets ([[ ]]) is used for one-way downward from host element to target element data flow.
Two-way binding to a Native HTML element.
nativeElementProperty="{{hostElementProperty::nativeElementEvent}}"
JQX('my-element', class MyElement extends JQX.BaseElement {
static get properties() {
return {
'check': {
type: 'boolean'
}
};
}
template() {
return '<div><input type="checkbox" checked="{{check::change}}" /></div>';
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="../../source/styles/jqx.base.css" type="text/css" />
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/0.7.22/webcomponents-lite.min.js"></script>
<script type="text/javascript" src="../../source/jqxelement.js"></script>
<script type="text/javascript" src="../../source/myelement.js"></script>
<script>
window.onload = function () {
var myElement = document.querySelector('my-element');
myElement.onchange = function () {
console.log(myElement.check);
}
}
</script>
</head>
<body>
<my-element></my-element>
</body>
</html>
Content insertion point determines where the HTML elements which are within the custom element's body during initialization go during the initialization. By default that is the Custom Element itself, but you can specify a custom content insertion point, you can define a content
tag within the template's structure as in the below example:
template() {
return `<div>
<svg width="100%" height="100%" viewPort="0 0 100 100" viewBox="0 0 100 100">
<circle id="value" class ="jqx-value" r="50" cx="50" cy="50" transform="rotate(270 50 50)"></circle>
</svg>
<div class="jqx-label-container"><content></content><div id="label" class="jqx-label"></div></div>
</div>`;
}
After the template is parsed, each element of the HTML Structure is accessible via its id
and the $
symbol. Note the checkboxInput
element in the below example:
/**
* CheckBox custom element.
*/
JQX('jqx-checkbox', class CheckBox extends JQX.ToggleButton {
// CheckBox's properties.
static get properties() {
return {
'enableContainerClick': {
value: true,
type: 'boolean'
}
};
}
/** Checkbox's Html template. */
template() {
return `<div id='container' class='jqx-container'>
<div id='checkboxAnimation' class ='jqx-animation'></div>
<span id='checkboxInput' class ='jqx-input'></span>
<span id='checkboxLabel' inner-h-t-m-l='[[innerHTML]]' class ='jqx-label'><content></content></span>
</div>`;
}
static get listeners() {
return {
'click': '_clickHandler'
};
}
/** Called when the element is ready. Used for one-time configuration of the Checkbox. */
ready() {
}
/** Changes the check state wneh widget container is clicked. */
_clickHandler(event) {
const that = this;
if (that.disabled) {
return;
}
const isInputClicked = event.target === that.$.checkboxInput;
if ((that.enableContainerClick === true && !isInputClicked) || isInputClicked) {
that._changeCheckState('pointer');
that.focus();
}
}
});
A set of utility functions is accessible throught the $
symbol. The syntax is element.$
.The utilify functions are:
- addClass(className) - adds a class or classes to the element separated by space.
- removeClass(className) - removes a class or classes separated by space.
- isNativeElement - returns
true
if the element is native HTML Element. Otherwise returnsfalse
. - fireEvent(eventType, detail, options) - fires a Custom Event.
- eventType - String. Determines the event's type.
- detail - Object. Determines custom event object passed to the user.
- options - Object. Determines the event's options like
cancelable
orbubbles
. Read more on: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent
- listen(eventType, handler) - adds an event listener to the element. A set of Mobile-friendly events are supported by default. By passing any of these event types:
down, up, move, tap, taphold, swipeleft, swiperight, swipetop, swipebottom
, you will be notified when the user taps, swipes or touches with finger or mouse the element. If you listen to theresize
event, you will be notified whenever the element's boundaries are changed. - unlisten(eventType) - removes event listener by type.
- getAttributeValue(attributeName, type) - gets the attribute's typed value.
- setAttributeValue(attributeName, value, type) - sets the attribute's value by using a typed value.
By invoking JQX.Utilities.Extend(element)
you can extend any element with the above utility functions.
In order to add a custom utility class, you can use JQX.Utilities.Assign(classDefinition)
.
JQX.Utilities.Assign('BaseNumericProcessor', class BaseNumericProcessor {
}
To access that class, you can use JQX.Utilities.BaseNumericProcessor
.
Events
The listeners
object allows you to add and map events to event handlers.
In the below example, the listeners
object defines that a method called _clickHandler
is called when the element is clicked. To listen to an event of an element from the template, you can use nodeId.eventName
like checkboxInput.click:_clickHandler
.
/**
* CheckBox custom element.
*/
JQX('jqx-checkbox', class CheckBox extends JQX.ToggleButton {
// CheckBox's properties.
static get properties() {
return {
'enableContainerClick': {
value: true,
type: 'boolean'
}
};
}
/** Checkbox's Html template. */
template() {
return `<div id='container' class='jqx-container'>
<div id='checkboxAnimation' class ='jqx-animation'></div>
<span id='checkboxInput' class ='jqx-input'></span>
<span id='checkboxLabel' inner-h-t-m-l='[[innerHTML]]' class ='jqx-label'><content></content></span>
</div>`;
}
static get listeners() {
return {
'click': '_clickHandler'
};
}
/** Called when the element is ready. Used for one-time configuration of the Checkbox. */
ready() {
}
/** Changes the check state wneh widget container is clicked. */
_clickHandler(event) {
const that = this;
if (that.disabled) {
return;
}
const isInputClicked = event.target === that.$.checkboxInput;
if ((that.enableContainerClick === true && !isInputClicked) || isInputClicked) {
that._changeCheckState('pointer');
that.focus();
}
}
});
Binding to events within the Element's template.
JQX('my-element', class MyElement extends JQX.BaseElement {
static get properties() {
return {
'check': {
type: 'boolean'
}
};
}
template() {
return '<div><input id="checkbox" (change)="_changeHandler" type="checkbox" checked="{{check::change}}" /></div>';
}
_changeHandler() {
alert('Checkbox State Changed');
}
});
By using the utility functions described in the previous section, you can dynamically add and remove event listeners.
Modules
To add a missing feature or override a feature of a Custom Element, you can define a Module. The module represents a javascript class. By defining its properties
object, you can add new properties or override existing properties of the custom element. Methods defined within that class also extend or override custom element methods. The lifecycle callback functions usage is the same as in the element. To add a module to a custom element, you can use the addModule
function. The owner element is accessible through a property called ownerElement
.
window.JQX.Elements.whenRegistered('jqx-button', function (proto) {
proto.addModule(ColorModule);
});
Custom Module which adds a new color
property to the jqx-button
custom element.
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="../../source/styles/jqx.base.css" type="text/css" />
<link rel="stylesheet" href="../styles/demos.css" type="text/css" />
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/0.7.22/webcomponents-lite.min.js"></script>
<script type="text/javascript" src="../../source/jqxelement.js"></script>
<script type="text/javascript" src="../../source/jqxbutton.js"></script>
<script>
class ColorModule {
static get properties() {
const properties =
{
'color': {
value: 'red',
type: 'string',
observer: 'setColor'
}
}
return properties;
}
attached() {
}
detached() {
}
created() {
}
ready() {
this.ownerElement.$.button.style.color = this.color;
}
setColor(oldColor, color) {
this.ownerElement.$.button.style.color = this.color;
}
}
window.JQX.Elements.whenRegistered('jqx-button', function (proto) {
proto.addModule(ColorModule);
});
</script>
<script>
function clickMe(event) {
let button = document.getElementById("button");
button.color = 'green';
}
</script>
</head>
<body>
<jqx-button id="button" onclick="clickMe(event)">Click Me</jqx-button>
</body>
</html>
Inheritance
You can create a new Custom Element which extends an existing one. When you call the JQX
function, pass a class as a second argument which determines which element should be extended. All elements are registered within the JQX
global namespace and are accessible through their class name.
The below example demonstrates how to create a new element called jqx-repeat-button
which extends the jqx-button
element.
/**
* Repeat Button.
*/
JQX('jqx-repeat-button', class RepeatButton extends JQX.Button {
// button's properties.
static get properties() {
return {
'delay': {
value: 50,
type: 'number'
},
'initialDelay': {
value: 150,
type: 'number'
}
};
}
static get listeners() {
return {
'button.mousedown': '_startRepeat',
'button.mouseenter': '_updateInBoundsFlag',
'button.mouseleave': '_updateInBoundsFlag',
'document.mouseup': '_stopRepeat'
};
}
_updateInBoundsFlag(event) {
const that = this;
that._isPointerInBounds = true;
if (event.type === 'mouseleave') {
that._isPointerInBounds = false;
}
}
_startRepeat(event) {
const that = this;
if (!that._initialTimer) {
that._initialTimer = setTimeout(function () {
that._repeatTimer = setInterval(() => {
if (that._isPointerInBounds) {
const buttons = ('buttons' in event) ? event.buttons : event.which;
that.$.fireEvent('click', { buttons: buttons });
}
}, that.delay);
that._initialTimer = null;
}, that.initialDelay);
}
}
_stopRepeat() {
const that = this;
if (that._repeatTimer) {
clearInterval(that._repeatTimer);
that._repeatTimer = null;
}
if (that._initialTimer) {
clearTimeout(that._initialTimer);
that._initialTimer = null;
}
}
});
Installation
npm login
npm install
npm run test
Dependency Management and CI
- Declare
"@ni-kismet/jqx-element": "<version>"
dependency in package.json- It is highly discouraged to declare dependency on prerelease versions of the package.
- Dependent packages must include the following in the repository's .npmrc file:
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
NPM TOKEN
must be declared as an environmental variable in the Settings for the repository in Travis CI.- A token for this environmental variable can be obtained via npm.
Version and Deployment
- This package is version according to semantic versioning.
- The version must be bumped using the
npm version
command (https://docs.npmjs.com/cli/version). This increments the version in package.json, creates a tag with the same version name, and commit both to the local repository. - Push the commit and tag using
git push --follow-tags
. With a passing CI this will automatically trigger a deployment to NPM. - Command Line Example
npm version patch
--increments the patch number, e.g. 1.0.x+1git push --follow-tags
Version and Deployment of dev packages
- Developers can produce NPM packages from their feature branch only.
- Versions must follow the format:
major.minor.patch-branch-name.prerelease-version number
- The major.minor.patch version must match the version of the master branch.
- The same npm and git commands are used to set/increment the prerelease version, commit the version change and tag, and trigger a deployment.
- Command Line Examples
- Creating new prerelease version
npm version 1.0.2-feature-branch.1
--sets up a prerelease versiongit push --follow tags
- Increment an existing branch with a prerelease version
npm version patch
--increments the patch version. Starting from the version in the previous example we would now have 1.0.2-feature-branch.2git push follow tags
- Creating new prerelease version
Author website
http://www.jqwidgets.com/