Demo Apps Like Smartsheet – SmartGrid

While developing Webix, we’ve made several demo projects, in order to check the usability of our library. One of these projects is “Smart Sheet demo”. It is focused on the DataTable, one of the core Webix components. Smart Sheet demo can be used to show tabular or hierarchical data.

Smart Grid

You can download the full demo or check the demo online.

This mini application resembles the interface of Smartsheet, one of the best web-based spreadsheets. It presents the hierarchy of tasks and allows users to edit it. (We’ve implemented only the client-side interface. There’s no real back end code for this demo).

Let’s look from the inside.

index.html

The HTML file is compact:

<!DOCTYPE html>
<html>
<head>
  <title>SmartSheet</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <link rel="stylesheet" type="text/css" href="//cdn.webix.io/edge/webix.css">
  <script type="text/javascript" src="//cdn.webix.io/edge/webix.js"></script>

  <link rel="stylesheet" type="text/css" href="./styles.css">
  <script type="text/javascript" src="ui.js"></script>
</head>
<body>

<script type="text/javascript">

webix.ready(function(){
    webix.i18n.parseFormatDate = webix.Date.strToDate("%m/%d/%Y");
    webix.ui(ui_scheme);
});

</script>
</body>
</html>

It starts with HTML5 doctype, then includes the Webix library together with js and css files of the demo app. The document itself doesn’t enclose any content, except for the html placeholder for the app, and webix.ui call, which initializes user interface of the app (ui_scheme is defined in ui.js ).

There are two key moments:

  • initialization code is placed inside of the “webix.ready” block, which guarantees that the code will be executed only after full page’s loading. It is a good habit to place initialization code into the webix.ready block
  • as we’re going to work with dates, we should set the date format for data parsing, which can be done with the help of webix.i18n.parseFormatDate

ui.js – Interface and Logic

Firstly, pay attention to ui_scheme structure that was used in index.html to create interface:

var ui_scheme = {
    container: "app",
    cols: [{}, {
        rows: [header, grid, footer],
        width: 800
    }, {}]
};

It’s a usual Webix markup: a horizontal layout, consisting of three columns, with a vertical layout inside. The vertical layout has a header, a grid and a footer. The first and the last elements of the horizontal layout don’t contain anything. We define them in order to place the middle element in the centre of the page. The internal vertical layout doesn’t contain any detailed description of the components. It just contains links to variables. It’s another good habit – to place the detailed description of each component into a separate variable. This way, the code is easier to read.

The header and the footer aren’t very complicated. The first one is a usual template, which type is set as “header” to make it look like a header (it is rather self explaining):

var header = { template:"SmartSheet like demo", type:"header" };

The footer is a horizontal layout that has buttons inside. Each button has “type” property with “icon” value, which defines their appearance – transparent buttons with icons:

var footer = {
    view: "toolbar",
    height: 25,
    elements: [{
        view: "button",
        type: "icon",
        icon: "users",
        label: "Sharing (26)"
    }, {
        view: "button",
        type: "icon",
        icon: "flash",
        label: "Alerts (5)"
    }, {
        view: "button",
        type: "icon",
        icon: "attach",
        label: "Attachments (112)"
    }, {
        view: "button",
        type: "icon",
        icon: "vcard",
        label: "Forms"
    }]
};

The grid settings look more complicated:

var grid = {
    view: "treetable",
    editable: true,
    autoheight: true,
    leftSplit: 5,
    columns: [
        // [columns configuration]
    ],
    url: "data.json?v=4",
    onClick: {
        "webix_icon": function(ev, id, trg) {
            // [icon action]
        }
    },
    on: {
        "onbeforeeditstart": function(cell) {
            return !this.getItem(cell.row).notedit;
        }
    }
};

There are two major blocks: columns settings and event handling logic.

Columns Settings

For each column we can define the way it should render itself. It can be an automatic template like the column “num” has:

{id:"num", header:"", width:40, css:"shade"}

or a more complex string template like for dates and categories columns:

{
    id: "category",
    header: "Category",
    width: 250,
    template: "{common.space()} {common.icon()}  #value#",
    editor: "text",
    sort: "string"
}, {
    id: "start",
    header: "Start date",
    map: "(date)#start#",
    editor: "date",
    sort: "int"
},

or, if the above is not enough, we may use a function as a template. In SmartSheet demo it is used for columns with icons.

{
    id: "attach",
    header: "",
    width: 40,
    css: "myicon",
    template: function(obj) {
        if (!obj.attach) return "";
        return "";
    }
},

Event Handling Logic

The demo app has several columns with icons: attachments, comments, and the status icon. All of these elements contain CSS class “webix_icon”, which allows us to write a common event handler for all of them. It looks like this:

onClick: {
    "webix_icon": function(ev, id, trg) {
        if (id.column == "attach")
            webix.message("Attach call for row: " + this.getItem(id.row).num);
    }
}

The above defines the logic, according to which the code that we’ve specified will be called after a click on the element with CSS class – webix_icon. The second block of event handling specifies the logic of edit operations in the datatable. It consists in blocking edit operations for the rows marked by “notedit” attribute:

on:{
  "onbeforeeditstart":function(cell){
    return !this.getItem(cell.row).notedit;
    }
}

In total

As you can see, it is possible to create a rather complex UI with a minimum of code. DataTable, as well as other Webix components, attempts to use auto configuration whenever it’s possible. At the same time it accepts the detailed configuration when you need to implement more complex behavior.