IGNITE-9105 Web Console: Added support of canvas based charts.

Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/4d9f4504
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/4d9f4504
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/4d9f4504

Branch: refs/heads/ignite-8446
Commit: 4d9f45041d8cb1ece1e2612d265fa3b792246857
Parents: 1b5d275
Author: Alexander Kalinin <verba...@yandex.ru>
Authored: Tue Jul 31 10:24:32 2018 +0700
Committer: Alexey Kuznetsov <akuznet...@apache.org>
Committed: Tue Jul 31 10:24:32 2018 +0700

----------------------------------------------------------------------
 modules/web-console/frontend/app/app.js         |   4 +
 .../ignite-chart-series-selector/component.js   |  28 ++
 .../ignite-chart-series-selector/controller.js  |  62 ++++
 .../ignite-chart-series-selector/index.js       |  24 ++
 .../ignite-chart-series-selector/template.pug   |  29 ++
 .../app/components/ignite-chart/controller.js   | 320 +++++++++++++++++++
 .../app/components/ignite-chart/index.js        |  38 +++
 .../app/components/ignite-chart/style.scss      |  69 ++++
 .../app/components/ignite-chart/template.pug    |  36 +++
 .../frontend/app/primitives/btn/index.scss      |   9 +
 modules/web-console/frontend/package.json       |   2 +
 11 files changed, 621 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/app/app.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/app.js 
b/modules/web-console/frontend/app/app.js
index baf06dd..e807319 100644
--- a/modules/web-console/frontend/app/app.js
+++ b/modules/web-console/frontend/app/app.js
@@ -142,6 +142,8 @@ import pageLanding from './components/page-landing';
 import passwordVisibility from './components/password-visibility';
 import progressLine from './components/progress-line';
 import formField from './components/form-field';
+import igniteChart from './components/ignite-chart';
+import igniteChartSelector from './components/ignite-chart-series-selector';
 
 import pageProfile from './components/page-profile';
 import pagePasswordChanged from './components/page-password-changed';
@@ -249,6 +251,8 @@ export default angular.module('ignite-console', [
     uiAceSpring.name,
     breadcrumbs.name,
     passwordVisibility.name,
+    igniteChart.name,
+    igniteChartSelector.name,
     progressLine.name,
     formField.name
 ])

http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/app/components/ignite-chart-series-selector/component.js
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/components/ignite-chart-series-selector/component.js
 
b/modules/web-console/frontend/app/components/ignite-chart-series-selector/component.js
new file mode 100644
index 0000000..41549fc
--- /dev/null
+++ 
b/modules/web-console/frontend/app/components/ignite-chart-series-selector/component.js
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import template from './template.pug';
+import controller from './controller.js';
+
+export default {
+    template,
+    controller,
+    transclude: true,
+    bindings: {
+        chartApi: '<'
+    }
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/app/components/ignite-chart-series-selector/controller.js
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/components/ignite-chart-series-selector/controller.js
 
b/modules/web-console/frontend/app/components/ignite-chart-series-selector/controller.js
new file mode 100644
index 0000000..00f57ca
--- /dev/null
+++ 
b/modules/web-console/frontend/app/components/ignite-chart-series-selector/controller.js
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export default class IgniteChartSeriesSelectorController {
+    static $inject = [];
+
+    constructor() {
+        this.charts = [];
+        this.selectedCharts = [];
+    }
+
+    $onChanges(changes) {
+        if (changes && 'chartApi' in changes && changes.chartApi.currentValue)
+            this.applyValues();
+    }
+
+    applyValues() {
+        this.charts = this._makeMenu();
+        this.selectedCharts = this.charts.map(({ key }) => key);
+    }
+
+    setSelectedCharts() {
+        const selectedDataset = ({ label }) => 
this.selectedCharts.includes(label);
+
+        this.chartApi.config.data.datasets
+            .forEach((dataset) => {
+                dataset.hidden = true;
+
+                if (!selectedDataset(dataset))
+                    return;
+
+                dataset.hidden = false;
+            });
+
+        this.chartApi.update();
+    }
+
+    _makeMenu() {
+        const labels = this.chartApi.config.datasetLegendMapping;
+
+        return 
Object.keys(this.chartApi.config.datasetLegendMapping).map((key) => {
+            return {
+                key,
+                label: labels[key]
+            };
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/app/components/ignite-chart-series-selector/index.js
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/components/ignite-chart-series-selector/index.js
 
b/modules/web-console/frontend/app/components/ignite-chart-series-selector/index.js
new file mode 100644
index 0000000..7ad3da0
--- /dev/null
+++ 
b/modules/web-console/frontend/app/components/ignite-chart-series-selector/index.js
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import angular from 'angular';
+
+import component from './component';
+
+export default angular
+    .module('ignite-console.ignite-chart-series-selector', [])
+    .component('igniteChartSeriesSelector', component);

http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/app/components/ignite-chart-series-selector/template.pug
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/components/ignite-chart-series-selector/template.pug
 
b/modules/web-console/frontend/app/components/ignite-chart-series-selector/template.pug
new file mode 100644
index 0000000..fec0d9a
--- /dev/null
+++ 
b/modules/web-console/frontend/app/components/ignite-chart-series-selector/template.pug
@@ -0,0 +1,29 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+button.btn-ignite.btn-ignite--link-dashed-secondary(
+    protect-from-bs-select-render
+    bs-select
+    ng-model='$ctrl.selectedCharts'
+    ng-change='$ctrl.setSelectedCharts()'
+    ng-model-options='{debounce: {default: 5}}',
+    bs-options='chart.key as chart.label for chart in $ctrl.charts'
+    bs-on-before-show='$ctrl.onShow'
+    data-multiple='true'
+    ng-transclude
+    ng-show='$ctrl.charts.length'
+)
+    svg(ignite-icon='gear').icon

http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/app/components/ignite-chart/controller.js
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/components/ignite-chart/controller.js 
b/modules/web-console/frontend/app/components/ignite-chart/controller.js
new file mode 100644
index 0000000..df11050
--- /dev/null
+++ b/modules/web-console/frontend/app/components/ignite-chart/controller.js
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import _ from 'lodash';
+
+/**
+ * @typedef {{x: number, y: {[key: string]: number}}} IgniteChartDataPoint
+ */
+
+const RANGE_RATE_PRESET = [{
+    label: '1 min',
+    value: 1
+}, {
+    label: '5 min',
+    value: 5
+}, {
+    label: '10 min',
+    value: 10
+}, {
+    label: '15 min',
+    value: 15
+}, {
+    label: '30 min',
+    value: 30
+}];
+
+export class IgniteChartController {
+    /** @type {import('chart.js').ChartConfiguration} */
+    chartOptions;
+    /** @type {string} */
+    chartTitle;
+    /** @type {IgniteChartDataPoint} */
+    chartDataPoint;
+    /** @type {Array<IgniteChartDataPoint>} */
+    chartHistory;
+    newPoints = [];
+
+    static $inject = ['$element', 'IgniteChartColors', '$filter'];
+
+    /**
+     * @param {JQLite} $element
+     * @param {ng.IScope} $scope
+     * @param {ng.IFilterService} $filter
+     */
+    constructor($element, IgniteChartColors, $filter) {
+        this.$element = $element;
+        this.IgniteChartColors = IgniteChartColors;
+
+        this.datePipe = $filter('date');
+        this.ranges = RANGE_RATE_PRESET;
+        this.currentRange = this.ranges[0];
+        this.maxRangeInMilliseconds = 
RANGE_RATE_PRESET[RANGE_RATE_PRESET.length - 1].value * 60 * 1000;
+        this.ctx = this.$element.find('canvas')[0].getContext('2d');
+
+        this.localHistory = [];
+        this.updateIsBusy = false;
+    }
+
+    $onDestroy() {
+        if (this.chart) this.chart.destroy();
+        this.$element = this.ctx = this.chart = null;
+    }
+
+    $onInit() {
+        this.chartColors = _.get(this.chartOptions, 'chartColors', 
this.IgniteChartColors);
+    }
+
+    /**
+     * @param {{chartOptions: 
ng.IChangesObject<import('chart.js').ChartConfiguration>, chartTitle: 
ng.IChangesObject<string>, chartDataPoint: 
ng.IChangesObject<IgniteChartDataPoint>, chartHistory: 
ng.IChangesObject<Array<IgniteChartDataPoint>>}} changes
+     */
+    $onChanges(changes) {
+        if (this.chart && _.get(changes, 'refreshRate.currentValue'))
+            this.onRefreshRateChanged(_.get(changes, 
'refreshRate.currentValue'));
+
+        // TODO: Investigate other signaling for resetting component state.
+        if (changes.chartDataPoint && 
_.isNil(changes.chartDataPoint.currentValue)) {
+            this.clearDatasets();
+            return;
+        }
+
+        if (changes.chartHistory && changes.chartHistory.currentValue && 
changes.chartHistory.currentValue.length !== 
changes.chartHistory.previousValue.length) {
+            if (!this.chart)
+                this.initChart();
+
+            this.clearDatasets();
+            this.localHistory = [...changes.chartHistory.currentValue];
+
+            this.newPoints.splice(0, this.newPoints.length, 
...changes.chartHistory.currentValue);
+
+            this.onRefresh();
+            this.rerenderChart();
+            return;
+        }
+
+        if (this.chartDataPoint && changes.chartDataPoint) {
+            if (!this.chart)
+                this.initChart();
+
+            this.newPoints.push(this.chartDataPoint);
+            this.localHistory.push(this.chartDataPoint);
+        }
+    }
+
+    async initChart() {
+        /** @type {import('chart.js').ChartConfiguration} */
+        this.config = {
+            type: 'line',
+            data: {
+                datasets: []
+            },
+            options: {
+                elements: {
+                    line: {
+                        tension: 0
+                    },
+                    point: {
+                        radius: 2,
+                        pointStyle: 'rectRounded'
+                    }
+                },
+                animation: {
+                    duration: 0 // general animation time
+                },
+                hover: {
+                    animationDuration: 0 // duration of animations when 
hovering an item
+                },
+                responsiveAnimationDuration: 0, // animation duration after a 
resize
+                maintainAspectRatio: false,
+                responsive: true,
+                legend: {
+                    display: false
+                },
+                scales: {
+                    xAxes: [{
+                        type: 'realtime',
+                        display: true,
+                        time: {
+                            displayFormats: {
+                                second: 'HH:mm:ss',
+                                minute: 'HH:mm:ss',
+                                hour: 'HH:mm:ss'
+                            }
+                        },
+                        ticks: {
+                            maxRotation: 0,
+                            minRotation: 0
+                        }
+                    }],
+                    yAxes: [{
+                        type: 'linear',
+                        display: true,
+                        ticks: {
+                            min: 0,
+                            beginAtZero: true,
+                            maxTicksLimit: 4,
+                            callback: (value, index, labels) => {
+                                if (value === 0)
+                                    return 0;
+
+                                if (_.max(labels) <= 4000 && value <= 4000)
+                                    return value;
+
+                                if (_.max(labels) <= 1000000 && value <= 
1000000)
+                                    return `${value / 1000}K`;
+
+                                if ((_.max(labels) <= 4000000 && value >= 
500000) || (_.max(labels) > 4000000))
+                                    return `${value / 1000000}M`;
+
+                                return value;
+                            }
+                        }
+                    }]
+                },
+                tooltips: {
+                    mode: 'index',
+                    position: 'nearest',
+                    intersect: false,
+                    xPadding: 20,
+                    yPadding: 20,
+                    bodyFontSize: 13,
+                    callbacks: {
+                        title: (tooltipItem) => {
+                            return tooltipItem[0].xLabel.slice(0, -7);
+                        },
+                        label: (tooltipItem, data) => {
+                            const label = 
data.datasets[tooltipItem.datasetIndex].label || '';
+
+                            return `${_.startCase(label)}: 
${tooltipItem.yLabel} per sec`;
+                        },
+                        labelColor: (tooltipItem) => {
+                            return {
+                                borderColor: 'rgba(255,255,255,0.5)',
+                                borderWidth: 0,
+                                boxShadow: 'none',
+                                backgroundColor: 
this.chartColors[tooltipItem.datasetIndex]
+                            };
+                        }
+                    }
+                },
+                plugins: {
+                    streaming: {
+                        duration: this.currentRange.value * 1000 * 60,
+                        frameRate: 1000 / this.refreshRate || 1 / 3,
+                        refresh: this.refreshRate || 3000,
+                        onRefresh: () => {
+                            this.onRefresh();
+                        }
+                    }
+                }
+            }
+        };
+
+        this.config = _.merge(this.config, this.chartOptions);
+
+        const chartModule = await import('chart.js');
+        const Chart = chartModule.default;
+
+        await import('chartjs-plugin-streaming');
+
+        this.chart = new Chart(this.ctx, this.config);
+        this.changeXRange(this.currentRange);
+    }
+
+    onRefresh() {
+        this.newPoints.forEach((point) => {
+            this.appendChartPoint(point);
+        });
+
+        this.newPoints.splice(0, this.newPoints.length);
+    }
+
+    /**
+     * @param {IgniteChartDataPoint} dataPoint
+     */
+    appendChartPoint(dataPoint) {
+        Object.keys(dataPoint.y).forEach((key) => {
+            if (this.checkDatasetCanBeAdded(key)) {
+                let datasetIndex = this.findDatasetIndex(key);
+
+                if (datasetIndex < 0) {
+                    datasetIndex = this.config.data.datasets.length;
+                    this.addDataset(key);
+                }
+
+                // Prune excessive data points.
+                if (this.maxPointsNumber && 
this.config.data.datasets[datasetIndex].length - this.maxPointsNumber > 0)
+                    this.config.data.datasets[datasetIndex].data.splice(0, 
this.config.data.datasets[datasetIndex].length - this.maxPointsNumber);
+
+                this.config.data.datasets[datasetIndex].data.push({x: 
dataPoint.x, y: dataPoint.y[key]});
+                this.config.data.datasets[datasetIndex].borderColor = 
this.chartOptions.chartColors[datasetIndex];
+                this.config.data.datasets[datasetIndex].borderWidth = 2;
+                this.config.data.datasets[datasetIndex].fill = false;
+            }
+        });
+    }
+
+    /**
+     * Checks if a key of dataset can be added to chart or should be ignored.
+     * @param dataPointKey {String}
+     * @return {Boolean}
+     */
+    checkDatasetCanBeAdded(dataPointKey) {
+        // If datasetLegendMapping is empty all keys are allowed.
+        if (!this.config.datasetLegendMapping)
+            return true;
+
+        return 
Object.keys(this.config.datasetLegendMapping).includes(dataPointKey);
+    }
+
+    clearDatasets() {
+        if (!_.isNil(this.config))
+            this.config.data.datasets.forEach((dataset) => dataset.data = []);
+    }
+
+    addDataset(datasetName) {
+        if (this.findDatasetIndex(datasetName) >= 0)
+            throw new Error(`Dataset with name ${datasetName} is already in 
chart`);
+        else
+            this.config.data.datasets.push({ label: datasetName, data: [] });
+    }
+
+    findDatasetIndex(searchedDatasetLabel) {
+        return this.config.data.datasets.findIndex((dataset) => dataset.label 
=== searchedDatasetLabel);
+    }
+
+    changeXRange(range) {
+        const deltaInMilliSeconds = range.value * 60 * 1000;
+        this.chart.config.options.plugins.streaming.duration = 
deltaInMilliSeconds;
+
+        this.clearDatasets();
+        this.newPoints.splice(0, this.newPoints.length, ...this.localHistory);
+
+        this.onRefresh();
+        this.rerenderChart();
+    }
+
+    onRefreshRateChanged(refreshRate) {
+        this.chart.config.options.plugins.streaming.frameRate = 1000 / 
refreshRate;
+        this.chart.config.options.plugins.streaming.refresh = refreshRate;
+        this.rerenderChart();
+    }
+
+    rerenderChart() {
+        this.chart.update();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/app/components/ignite-chart/index.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/ignite-chart/index.js 
b/modules/web-console/frontend/app/components/ignite-chart/index.js
new file mode 100644
index 0000000..337ba36
--- /dev/null
+++ b/modules/web-console/frontend/app/components/ignite-chart/index.js
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import angular from 'angular';
+
+import { IgniteChartController } from './controller';
+import template from './template.pug';
+import './style.scss';
+
+export default angular
+    .module('ignite-console.ignite-chart', [])
+    .component('igniteChart', {
+        controller: IgniteChartController,
+        template,
+        bindings: {
+            chartOptions: '<',
+            chartDataPoint: '<',
+            chartHistory: '<',
+            chartTitle: '<',
+            chartColors: '<',
+            maxPointsNumber: '<',
+            refreshRate: '<'
+        }
+    });

http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/app/components/ignite-chart/style.scss
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/components/ignite-chart/style.scss 
b/modules/web-console/frontend/app/components/ignite-chart/style.scss
new file mode 100644
index 0000000..be0fb6d
--- /dev/null
+++ b/modules/web-console/frontend/app/components/ignite-chart/style.scss
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ignite-chart {
+  display: flex;
+  flex-direction: column;
+
+  height: 100%;
+
+  header {
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    box-sizing: content-box;
+
+    height: 36px;
+    min-height: 36px;
+    padding: 7px 21px;
+
+    border-bottom: 1px solid #d4d4d4;
+
+    h5 {
+      margin: 0;
+
+      font-size: 16px;
+      font-weight: normal;
+      line-height: 36px;
+    }
+
+    > div {
+      display: flex;
+      align-items: center;
+    }
+  }
+
+  .ignite-chart-placeholder {
+    display: block;
+    height: calc(100% - 71px);
+    margin-top: 20px;
+  }
+
+  .no-data {
+    position: absolute;
+    top: 50%;
+
+    width: 100%;
+    padding: 0 20px;
+
+    border-radius: 0 0 4px 4px;
+
+    font-style: italic;
+    line-height: 16px;
+    text-align: center;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/app/components/ignite-chart/template.pug
----------------------------------------------------------------------
diff --git 
a/modules/web-console/frontend/app/components/ignite-chart/template.pug 
b/modules/web-console/frontend/app/components/ignite-chart/template.pug
new file mode 100644
index 0000000..076d8d4
--- /dev/null
+++ b/modules/web-console/frontend/app/components/ignite-chart/template.pug
@@ -0,0 +1,36 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+header
+    div
+        h5 {{ $ctrl.chartTitle }}
+        ignite-chart-series-selector(chart-api='$ctrl.chart')
+
+    div
+        span Range:
+            button.btn-ignite.btn-ignite--link-success.link-primary(
+                ng-model='$ctrl.currentRange'
+                ng-change='$ctrl.changeXRange($ctrl.currentRange)'
+                bs-options='item as item.label for item in $ctrl.ranges'
+                bs-select
+            )
+
+.ignite-chart-placeholder
+    canvas
+
+.no-data(ng-if='!$ctrl.config.data.datasets')
+    | No Data #[br]
+    | Make sure you are connected to the right grid.

http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/app/primitives/btn/index.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/primitives/btn/index.scss 
b/modules/web-console/frontend/app/primitives/btn/index.scss
index 2d9e9c4..277b1ae 100644
--- a/modules/web-console/frontend/app/primitives/btn/index.scss
+++ b/modules/web-console/frontend/app/primitives/btn/index.scss
@@ -319,10 +319,19 @@ $btn-content-padding-with-border: 9px 11px;
         $line-color: $ignite-brand-success;
         border-right-color: change-color($line-color, $saturation: 63%, 
$lightness: 33%);
     }
+
+    &[disabled] .btn-ignite.btn-ignite--primary {
+        border-right-color: change-color($ignite-brand-primary, $lightness: 
83%);
+    }
+
+    &[disabled] .btn-ignite.btn-ignite--success {
+        border-right-color: change-color($ignite-brand-success, $lightness: 
83%);
+    }
 }
 
 @mixin ignite-link($color, $color-hover) {
     color: $color;
+    text-decoration: none;
 
     &:hover, &.hover,
     &:focus, &.focus {

http://git-wip-us.apache.org/repos/asf/ignite/blob/4d9f4504/modules/web-console/frontend/package.json
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/package.json 
b/modules/web-console/frontend/package.json
index 02acbe0..6a6b97f 100644
--- a/modules/web-console/frontend/package.json
+++ b/modules/web-console/frontend/package.json
@@ -54,6 +54,8 @@
     "brace": "0.10.0",
     "browser-update": "3.1.13",
     "bson-objectid": "1.1.5",
+    "chart.js": "2.7.2",
+    "chartjs-plugin-streaming": "1.5.0",
     "file-saver": "1.3.3",
     "font-awesome": "4.7.0",
     "jquery": "3.2.1",

Reply via email to