Define a Custom Element

In this post, we will show you how to define a Custom Element with Smart Framework. We will create a Material Color Picker custom element.

1. Define the Custom Element. To define a new custom element with Smart Framework, we need to call the Smart function with two parameters - the tag name and the Class with the logic.

Smart('smart-colors', class SmartColors extends Smart.BaseElement {  
}


2. Smart Framework gives us useful things like Templates, Data Binding, Typed Properties, Lifecycle callbacks, Event Handling. Read more about this https://www.htmlelements.com/docs/base-element/. In our Custom Element, we will use some of these features. We will use the 'ready' lifecycle callback. It is invoked once, when the element is attached for first time. We use this function to create and initialize the custom element. The 'properties' definition includes a property called 'color'. This property has default value - '#fff' and its type is 'string'. This means that when you try to set a 'bool', 'date', 'numeric' value, the element will throw an error with 'Invalid property type'. To raise the 'change' event, we use the '$.fireEvent' method.

3. The full code of our custom element is below
  // Define Custom Element
Smart('smart-colors', class SmartColors extends Smart.BaseElement {  
  // Declare properties
  static get properties() {  
    return {
      'color': 
        {
           value: '#fff',
           type: 'string'
        }
    };
  }
   
  // Ready is called when the element is in the DOM.
  ready () {
    const that = this;
    
    that._renderGrid();
    that._addHandlers();
  }
   
  // Renders the Color Panel.
  _renderGrid() {
    const that = this;
    const labelsAndPaletteContainer = document.createElement('div');

    that._renderShades();
    that._renderColorPalette();
    that._renderColorLabels();

    labelsAndPaletteContainer.classList = 'smart-labels-and-palette'
    labelsAndPaletteContainer.appendChild(that._colorLabelsContainer);
    labelsAndPaletteContainer.appendChild(that._paletteContainer);
    that.appendChild(labelsAndPaletteContainer);
  }
 
  // Rneders all colors.
  _renderColorPalette() {
    const that = this;
    const colorsArray = [
      ['#ffebee', '#ffcdd2', '#ef9a9a', '#e57373', '#ef5350', '#f44336', '#e53935', '#d32f2f', '#c62828', '#b71c1c', '#ff8a80', '#ff5252', '#ff1744', '#d50000'],
      ['#fce4ec', '#f8bbd0', '#f48fb1', '#f06292', '#ec407a', '#e91e63', '#d81b60', '#c2185b', '#ad1457', '#880e4f', '#ff80ab', '#ff4081', '#f50057', '#c51162'],
      ['#f3e5f5', '#e1bee7', '#ce93d8', '#ba68c8', '#ab47bc', '#9c27b0', '#8e24aa', '#7b1fa2', '#6a1b9a', '#4a148c', '#ea80fc', '#e040fb', '#d500f9', '#aa00ff'],
      ['#ede7f6', '#d1c4e9', '#b39ddb', '#9575cd', '#7e57c2', '#673ab7', '#5e35b1', '#512da8', '#4527a0', '#311b92', '#b388ff', '#7c4dff', '#651fff', '#6200ea'],
      ['#e8eaf6', '#c5cae9', '#9fa8da', '#7986cb', '#5c6bc0', '#3f51b5', '#3949ab', '#303f9f', '#283593', '#1a237e', '#8c9eff', '#536dfe', '#3d5afe', '#304ffe'],
      ['#e3f2fd', '#bbdefb', '#90caf9', '#64b5f6', '#42a5f5', '#2196f3', '#1e88e5', '#1976d2', '#1565c0', '#0d47a1', '#82b1ff', '#448aff', '#2979ff', '#2962ff'],
      ['#e1f5fe', '#b3e5fc', '#81d4fa', '#4fc3f7', '#29b6f6', '#03a9f4', '#039be5', '#0288d1', '#0277bd', '#01579b', '#80d8ff', '#40c4ff', '#00b0ff', '#0091ea'],
      ['#e0f7fa', '#b2ebf2', '#80deea', '#4dd0e1', '#26c6da', '#00bcd4', '#00acc1', '#0097a7', '#00838f', '#006064', '#84ffff', '#18ffff', '#00e5ff', '#00b8d4'],
      ['#e0f2f1', '#b2dfdb', '#80cbc4', '#4db6ac', '#26a69a', '#009688', '#00897b', '#00796b', '#00695c', '#004d40', '#a7ffeb', '#64ffda', '#1de9b6', '#00bfa5'],
      ['#e8f5e9', '#c8e6c9', '#a5d6a7', '#81c784', '#66bb6a', '#4caf50', '#43a047', '#388e3c', '#2e7d32', '#1b5e20', '#b9f6ca', '#69f0ae', '#00e676', '#00c853'],
      ['#f1f8e9', '#dcedc8', '#c5e1a5', '#aed581', '#9ccc65', '#8bc34a', '#7cb342', '#689f38', '#558b2f', '#33691e', '#ccff90', '#b2ff59', '#76ff03', '#64dd17'],
      ['#f9fbe7', '#f0f4c3', '#e6ee9c', '#dce775', '#d4e157', '#cddc39', '#c0ca33', '#afb42b', '#9e9d24', '#827717', '#f4ff81', '#eeff41', '#c6ff00', '#aeea00'],
      ['#fffde7', '#fff9c4', '#fff59d', '#fff176', '#ffee58', '#ffeb3b', '#fdd835', '#fbc02d', '#f9a825', '#f57f17', '#ffff8d', '#ffff00', '#ffea00', '#ffd600'],
      ['#fff8e1', '#ffecb3', '#ffe082', '#ffd54f', '#ffca28', '#ffc107', '#ffb300', '#ffa000', '#ff8f00', '#ff6f00', '#ffe57f', '#ffd740', '#ffc400', '#ffab00'],
      ['#fff3e0', '#ffe0b2', '#ffcc80', '#ffb74d', '#ffa726', '#ff9800', '#fb8c00', '#f57c00', '#ef6c00', '#e65100', '#ffd180', '#ffab40', '#ff9100', '#ff6d00'],
      ['#fbe9e7', '#ffccbc', '#ffab91', '#ff8a65', '#ff7043', '#ff5722', '#f4511e', '#e64a19', '#d84315', '#bf360c', '#ff9e80', '#ff6e40', '#ff3d00', '#dd2c00'],
      ['#efebe9', '#d7ccc8', '#bcaaa4', '#a1887f', '#8d6e63', '#795548', '#6d4c41', '#5d4037', '#4e342e', '#3e2723'],
      ['#fafafa', '#f5f5f5', '#eeeeee', '#e0e0e0', '#bdbdbd', '#9e9e9e', '#757575', '#616161', '#424242', '#212121'],
      ['#eceff1', '#cfd8dc', '#b0bec5', '#90a4ae', '#78909c', '#607d8b', '#546e7a', '#455a64', '#37474f', '#263238'],
    ]
    const paletteContainer = document.createElement('div');

    for (let index = 0, length = colorsArray.length; index < length; index++) {
      const currentRow = colorsArray[index];
      const currentUl = that._renderRow(currentRow, 'smart-color-cell', false);

      paletteContainer.appendChild(currentUl);
    }

    paletteContainer.className = 'smart-palette';
    that._paletteContainer = paletteContainer;
  }
  
  // Renders all shades.
  _renderShades() {
    const that = this;
    const shadesContainer = document.createElement('div');
    const shadesArray = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 'A 100', 'A 200', 'A 400', 'A 700'];
    const shadesRow = that._renderRow(shadesArray, 'smart-shade-cell', true);

    shadesContainer.className = 'smart-shades';
    shadesContainer.appendChild(shadesRow);
    that.appendChild(shadesContainer);
  }

  _renderColorLabels() {
    const that = this;
    const colorLabelsContainer = document.createElement('div');
    const colorLabelsArray = ['Red', 'Pink', 'Purple', 'Deep Purple', 'Indigo', 'Blue', 'Light Blue', 'Cyan', 'Teal', 'Green', 'Light Green', 'Lime', 'Yellow', 'Amber', 'Orange', 'Deep Orange', 'Brown', 'Grey', 'Blue Grey'];
    const colorLabelsColumn = that._renderRow(colorLabelsArray, 'smart-color-label', true);

    colorLabelsContainer.className = 'smart-color-labels';
    colorLabelsContainer.appendChild(colorLabelsColumn);
    that._colorLabelsContainer = colorLabelsContainer;
  }
  
  // Renders a single row of colors.
  _renderRow(array, cellClass, addInnerHtml) {
    const ul = document.createElement('ul');

    for (let index = 0, length = array.length; index < length; index++) {
      const currentElement = array[index];
      const li = document.createElement('li');

      if (addInnerHtml) {
        li.innerHTML = currentElement;
      }
      else {
        li.style.background = currentElement;
        li.setAttribute('data-color', currentElement);
      }

      li.className = cellClass;

      ul.appendChild(li);
    }

    return ul;
  }
  
  // Fires a 'change' event when a color is selected.
  _addHandlers() {
    const that = this;

    const cells = that.querySelectorAll('.smart-color-cell');

    for(let i = 0; i < cells.length; i++) {
      const  cell = cells[i];

      cell.addEventListener('click', function() {
        that._currentColorHex = event.target.getAttribute('data-color');
        that._currentColorRgb = event.target.style.background;
        that.color = that.getColor().hex.toString();
        that.$.fireEvent('change');
      });
    }
  }
 
  // gets the selected color.
  getColor() {
    const that = this;
    const rgb = that._currentColorRgb.match(/\d+/g);

    return {
      hex: that._currentColorHex.substring(1),
      r: parseInt(rgb[0]),
      g: parseInt(rgb[1]),
      b: parseInt(rgb[2])
    };
  }
});

See the Pen SMART HTML ELEMENTS by Boyko Markov (@boyko-markov) on CodePen.

HTML Elements, Javascript, Web Components


Leave a comment

TreeGrid and Grouping

Tree Grid, Grouping, Context Menus




The latest version of Smart HTML Elements brings important new features to our Smart Grid Web Component. It is now possible to display data hierarchies in the web component. The Grid allows you to display data as a combination of Tree and Grid. The Tree Grid mode has full support for data sorting, data filtering and data editing. The Grouping features allows you to group data by columns. The context menu feature allows you to quickly add a context menu functionality to the Grid.


Tree Grid

Tree Grid mode allows you to display hierarchical data in the Grid and make the component work like a hybrid between Tree and Grid.


grid custom element column template
Tree Grid

Virtual Tree Grid

In Virtual Mode, new rows are loaded on demand when you expand a row. This process repeats until the "leaf" property of the row is set to true.


Virtual Tree Grid

Virtual Tree Grid Pagination

With Pagination enabled, Root rows are loaded on demand when the current page is changed. Sub Rows are loaded when a treegrid row is expanded.

grid custom element column template

Virtual Tree Grid Pagination

Grid Grouping

Grouping enables you to group the component by one or multiple columns. The grouping styling is customizable through CSS Variables.


Grid GroupBy Grouping

Grid Grouping Grouping Styling

Grid Context Menu



Context Menu Grid Context Menu
HTML Elements, Javascript, Smart Grid
, , ,

Leave a comment

New Grid Features

Newest features in the Grid




HTML Elements, Smart Grid


Leave a comment

Lazy Initialization of Web Components

This post shows how to create a Grid Web component and initialize it from a DIV tag

The next version ver3.1.0 of Smart HTML Elements will introduce an alternative way to create a Web Component on demand from an existing HTML Element.

Let's look at the sample below. In the 'index.htm' web page code, we see a HTMLDIVElement(DIV tag) with id="grid". We will use that HTML element to create a new Grid Web Component instance.

<!DOCTYPE html>  
 <html xmlns="http://www.w3.org/1999/xhtml">  
 <head>  
   <title>Grid Lazy Load Demo</title>  
   <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
   <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />  
   <link rel="stylesheet" type="text/css" href="../../../source/styles/smart.default.css" />  
   <link rel="stylesheet" type="text/css" href="../../../styles/demos.css" />  
   <link rel="stylesheet" type="text/css" href="../../../styles/common.css" />  
   <link rel="stylesheet" type="text/css" href="styles.css" />  
   <script type="text/javascript" src="../../../scripts/common.js"></script>  
   <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.2.0/webcomponents-lite.js"></script>  
   <script type="module" src="index.js">  
   </script>  
 </head>  
 <body>  
   <div id="grid"></div>  
 </body>  
 </html>  


The 'index.js' file is defined as a Javascript Module. In that module our grid web component is created on window.onload, within a function called 'init()'. That is achieved by calling the Smart.Grid function and passing as a first argument the 'id' of the DIV tag and as a second argument the initialization object with the Grid web component's dataSource, columns and other configuration options. As a result, our DIV tag is replaced with a 'smart-grid' tag and a Grid is displayed.

That approach is useful, when we want to update an existing application with minimum changes or to create a Smart Web Component on demand with ES6 code.

import "../../../source/smart.elements.js";  
import "../../../scripts/data.js";  
 
document.readyState === 'complete' ? init() : window.onload = init;  
 function init() {  
   const grid = new Smart.Grid('#grid', {  
     dataSource: new Smart.DataAdapter(  
     {  
       dataSource: Data,  
       dataFields:  
       [  
         'id: number',  
         'firstName: string',  
         'lastName: string',  
         'productName: string',  
         'available: bool',  
         'quantity: number',  
         'price: number',  
         'total: number'  
       ]  
     }),      
     columns: [  
     {  
       label: 'First Name', dataField: 'firstName'  
     },  
     { label: 'Last Name', dataField: 'lastName' },  
     { label: 'Product', dataField: 'productName' },  
     { label: 'Available', dataField: 'available', template: 'checkBox', editor: 'checkBox' },  
     { label: 'Quantity', dataField: 'quantity', editor: 'numberInput' },  
     { label: 'Unit Price', dataField: 'price', editor: 'numberInput', cellsFormat: 'c2' }  
     ]  
   });  
 }  
HTML Elements, Javascript, Web Components
, , ,

Leave a comment

Chart Custom Element Released

Best Chart Custom Element has just arrived

The latest version of Smart HTML Elements, brings a full featured Chart Custom Element with more than 30 different Data Visualization options. The Chart Types supported are listed: Chart Types.

Best Chart Custom Element

Our Chart Custom Element is written in JavaScript and CSS. It does not depend on any other third-party scripts. The rendering is achieved with SVG and HTML5 canvas. The Chart works well on mobile browsers and is the most feature-complete chart available in the web components world.
Data displayed on the Chart can be formatted in multiple different ways. For example, the charting element has a property called 'formatSettings' with the following data formatting options.

  • decimalSeparator - character used as a decimal separator. If not specified, the default separator is '.'
  • thousandsSeparator - character used as thousands separator. Default value is ','
  • decimalPlaces - number of digits after the decimal separator. Default value is 2 for floating point numbers.
  • negativeWithBrackets - boolean which specifies whether to display negative numbers in brackets. Default value is false.
  • prefix - any string which will be added as a prefix. Default value is empty.
  • sufix - any string which will be added as a sufix. Default value is empty.
  • dateFormat - optional date format string. This property is applicable only when displaying Date objects.




The image below illustrates the Axes supported by the custom element. Smart.Chart has two main types of axes - valueAxis and xAxis. Typically the valueAxis represents the vertical axis in the chart although you may choose to rotate the axes and in this case the valueAxis will be displayed horizontally.Chart Custom Element Axes

Smart.Chart also ships with 32 built-in color schemes which are used to automatically set colors for different series. You can change the color scheme by setting the colorScheme property of the chart. The available value are from 'scheme01' to 'scheme32'. By setting its 'theme' property, users can Data Visualize their graphics in 'Light' and 'Dark' mode. Smart.Chart is based on: jqxChart.

We invite you to look at our Chart Custom Element demos: Chart overview
HTML Elements, Javascript, Web Components


Leave a comment

Binding to Row, Cell and Column Events in Grid Web Component

This post shows how to bind to the Grid Web Component events. The code below initializes the Grid instance and binds to the 'click' event. Within the event handler, we can get details about which part of the Grid was clicked - Cell, Row or Column. This is achieved by utilizing the 'event.path' array. It is also important to note that each Data Grid Column has 'data-field' attribute and each Row has 'data-id' attribute. The data source, which is used in the example is available in the 'data.js' file from the download package.

 Smart('#grid', class {  
      get properties() {  
           return {  
                dataSource: new Smart.DataAdapter(  
                {  
                     dataSource: getCountriesData(),  
                     dataFields:  
                     [  
                          'ID: number',  
                          'Country: string',  
                          'Area: number',  
                          'Population_Urban: number',  
                          'Population_Rural: number',  
                          'Population_Total: number',  
                          'GDP_Agriculture: number',  
                          'GDP_Industry: number',  
                          'GDP_Services: number',  
                          'GDP_Total: number'  
                     ]  
                }),  
                columns: [  
                     'Country',  
                     'Area',  
                     'Population_Rural',  
                     'Population_Total',  
                     'GDP_Total'  
                ]  
           }  
      }  
 });  
 window.onload = function() {  
  grid.addEventListener('click', function(event){  
     const path = event.path;  
     let cell = null;  
     let row = null;  
     let column = null;  
     for(let i = 0; i < path.length; i++) {  
       const node = path[i];  
       if (node.nodeName === 'SMART-GRID-CELL') {  
         cell = node;  
       }  
       if (node.nodeName === 'SMART-GRID-ROW') {  
         row = node;  
       }  
       if (node.nodeName === 'SMART-GRID-COLUMN')      {  
         column = node;  
       }  
     }  
     if (row) {  
       alert(row.getAttribute('data-id'));  
     }  
     if (column) {  
       alert(column.getAttribute('data-field'));  
     }  
     if (cell) {  
       alert(cell.innerText);  
     }  
  });  
 }  
Smart Grid, Web Components
,

Leave a comment

AutoComplete Input Custom Element also known as Typeahead

The newest version of our framework marks the availability of a new AutoComplete Input tag. We needed such lightweight component for the purposes of our Grid web component.
There, it is used as an inline cell editor. You can check it out here: grid-editing-cell-auto-complete.

Here is how the web component looks like when you add it to your web page:

Auto Complete Input

All you need to do is to add a reference to smart.elements.js, smart.default.css and type the following:

 <smart-input data-source="[South America, North America, Asia, Africa, Europe]"></smart-input>  
Uncategorized
, , , , , , , , , ,

Leave a comment

Extend Elements with Behaviors

Our Web Components library has a feature, which allows you to dynamically extend a Custom Element with additional behaviors. We call them Modules. To add a new module, you have to call the 'addModule' method. In this blog, we will show you how to create a new module, which adds a 'color' property to a Custom Element. We will call it 'ColorModule'.

class ColorModule  {
	 static get properties() {
		 const properties =
		 {
			 'color': {
				 value: 'red',
				 type: 'string',
				 observer: 'setColor'
			 }
		 }

		 return properties;
	 }

	ready() {
		this.ownerElement.$.root.style.color = this.color;
	}

	setColor(oldColor, color) {
		this.ownerElement.$.root.style.color = this.color;
	}
}

The 'ColorModule' defines a 'color' property. The default property value is 'red'. Whenever the 'color' property is changed, the 'setColor' method is invoked which applies the Color to the element.
The below code adds the Module to the 'smart-toggle'button' Custom Element.

window.Smart.Elements.whenRegistered('smart-toggle-button', function (proto) {
    proto.addModule(ColorModule, true);
});


Note that the second parameter is 'true'. This means that our 'ColorModule' will be added to all sub-classes of the 'smart-toggle-button' i.e 'smart-radio-button', 'smart-check-box' and 'smart-switch-button'. All these custom elements will have the 'color' property.
Usage example:

window.onload = function() {
    document.querySelector('smart-radio-button').color = "blue";		
}
HTML Elements, Javascript
, , , , , ,

Leave a comment

Get Caller JavaScript File Location

If you need to get the Path to the current Javascript file being executed, you can use this small function:
 const location = (function () {
                if (document.currentScript) {
                    let link = document.currentScript.src;
                    let lastIndex = link.lastIndexOf('/');

                    link = link.substring(0, lastIndex);

                    return link;
                }
 })();

In our code base, we use it to dynamically load Component dependencies.
HTML Elements, Web Components
, ,

Leave a comment

Grid with Very Large Data Set

One of the new additions to our Javascript Grid Web Component is the capability to load very large data sets. Example: Grid Large Data Set. In the example, we demonstrate how to load 50,000 rows and 1,000 columns. This can be achieved due to the built in Fast Data and User Interface virtualization. We create HTML Elements only for the View and update them while user scrolls. The Grid is also in unbound mode and the demo is created by using this code:

 Smart('#grid', class {
get properties() {
return {
appearance: {
showRowNumber: true
},
columnWidth: 100,
dataSource: new Smart.DataAdapter(
{
dataSource: 50000,
dataFields: 1000
}),
onCellRender(cell) {
cell.value = cell.row.index + '.' + cell.column.index;
}
}
}
}); 
 

To create it in unbound mode, we need to set the dataSource and dataFields properties to Numeric values which determine the Rows and Columns. The "onCellRender" callback function allows us to render cell values on demand when the cell is in the view. The Data in the example is Client Side data. If you have an application scenario, where you need to use larger data sets, our Grid offers a solution for that due through the Virtual Data Source. Check out: Grid Demos.
Smart Grid
, , , , , , , , ,

Leave a comment