You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2018/06/25 15:38:40 UTC

[incubator-echarts] 03/03: Support event query on components, series, data items. Support event on element of custom series.

This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git

commit fd064123626c97b36cbd6da1b5fc73385c280abd
Author: sushuang <su...@gmail.com>
AuthorDate: Mon Jun 25 23:38:00 2018 +0800

    Support event query on components, series, data items.
    Support event on element of custom series.
---
 src/chart/custom.js      |  22 +++++-
 src/echarts.js           | 129 +++++++++++++++++++++++++++++-
 src/view/Chart.js        |  11 ++-
 src/view/Component.js    |  11 ++-
 test/custom-feature.html | 129 ++++++++++++++++++++++++++++++
 test/ec-event.html       | 202 +++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 499 insertions(+), 5 deletions(-)

diff --git a/src/chart/custom.js b/src/chart/custom.js
index ae15147..056ae8a 100644
--- a/src/chart/custom.js
+++ b/src/chart/custom.js
@@ -169,7 +169,27 @@ echarts.extendChartView({
     /**
      * @override
      */
-    dispose: zrUtil.noop
+    dispose: zrUtil.noop,
+
+    /**
+     * @override
+     */
+    filterForExposedEvent: function (eventType, query, targetEl, packedEvent) {
+        var targetName = query.target;
+        if (targetName == null || targetEl.name === targetName) {
+            return true;
+        }
+
+        // Enable to give a name on a group made by `renderItem`, and listen
+        // events that triggerd by its descendents.
+        while ((targetEl = targetEl.parent) && targetEl !== this.group) {
+            if (targetEl.name === targetName) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 });
 
 
diff --git a/src/echarts.js b/src/echarts.js
index dec7148..8283d52 100644
--- a/src/echarts.js
+++ b/src/echarts.js
@@ -222,7 +222,7 @@ function ECharts(dom, theme, opts) {
      */
     this._scheduler = new Scheduler(this, api, dataProcessorFuncs, visualFuncs);
 
-    Eventful.call(this);
+    Eventful.call(this, this._ecEventProcessor = makeEventProcessor(this));
 
     /**
      * @type {module:echarts~MessageCenter}
@@ -1510,9 +1510,10 @@ echartsProto._initEvents = function () {
             var ecModel = this.getModel();
             var el = e.target;
             var params;
+            var isGlobalOut = eveName === 'globalout';
 
             // no e.target when 'globalout'.
-            if (eveName === 'globalout') {
+            if (isGlobalOut) {
                 params = {};
             }
             else if (el && el.dataIndex != null) {
@@ -1525,8 +1526,30 @@ echartsProto._initEvents = function () {
             }
 
             if (params) {
+                var componentType = params.componentType;
+                var componentIndex = params[componentType + 'Index'];
+                var model = componentType && componentIndex != null
+                    && ecModel.getComponent(componentType, componentIndex);
+                var view = model && this[
+                    model.mainType === 'series' ? '_chartsMap' : '_componentsMap'
+                ][model.__viewId];
+
+                if (__DEV__) {
+                    // `event.componentType` and `event[componentTpype + 'Index']` must not
+                    // be missed, otherwise there is no way to distinguish source component.
+                    // See `dataFormat.getDataParams`.
+                    zrUtil.assert(isGlobalOut || (model && view));
+                }
+
                 params.event = e;
                 params.type = eveName;
+
+                var ecEventProcessor = this._ecEventProcessor;
+                ecEventProcessor.targetEl = el;
+                ecEventProcessor.packedEvent = params;
+                ecEventProcessor.model = model;
+                ecEventProcessor.view = view;
+
                 this.trigger(eveName, params);
             }
 
@@ -1668,6 +1691,108 @@ function createExtensionAPI(ecInstance) {
 }
 
 /**
+ * Usage of query:
+ * `chart.on('click', query, handler);`
+ * The `query` can be:
+ * + The component type query string, only `mainType` or `mainType.subType`,
+ *   like: 'xAxis', 'series', 'xAxis.category' or 'series.line'.
+ * + The component query object, like:
+ *   `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`,
+ *   `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`.
+ * + The element query object, like:
+ *   `{targetName: 'some'}` (only available in custom series).
+ *
+ * Caveat: If a prop in the `query` object is `null/undefined`, it is the
+ * same as there is no such prop in the `query` object.
+ */
+function makeEventProcessor(ecIns) {
+    return {
+
+        normalizeQuery: function (query) {
+            var cptQuery = {};
+            var dataQuery = {};
+            var otherQuery = {};
+
+            // `query` is `mainType` or `mainType.subType` of component.
+            if (zrUtil.isString(query)) {
+                var condCptType = parseClassType(query);
+                // `.main` and `.sub` may be ''.
+                cptQuery.mainType = condCptType.main || null;
+                cptQuery.subType = condCptType.sub || null;
+            }
+            // `query` is an object, convert to {mainType, index, name, id}.
+            else {
+                // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved,
+                // can not be used in `compomentModel.filterForExposedEvent`.
+                var suffixes = ['Index', 'Name', 'Id'];
+                var dataKeys = {name: 1, dataIndex: 1, dataType: 1};
+                zrUtil.each(query, function (val, key) {
+                    var reserved;
+                    for (var i = 0; i < suffixes.length; i++) {
+                        var propSuffix = suffixes[i];
+                        var suffixPos = key.lastIndexOf(propSuffix);
+                        if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {
+                            var mainType = key.slice(0, suffixPos);
+                            // Consider `dataIndex`.
+                            if (mainType !== 'data') {
+                                cptQuery.mainType = mainType;
+                                cptQuery[propSuffix.toLowerCase()] = val;
+                                reserved = true;
+                            }
+                        }
+                    }
+                    if (dataKeys.hasOwnProperty(key)) {
+                        dataQuery[key] = val;
+                        reserved = true;
+                    }
+                    if (!reserved) {
+                        otherQuery[key] = val;
+                    }
+                });
+            }
+
+            return {
+                cptQuery: cptQuery,
+                dataQuery: dataQuery,
+                otherQuery: otherQuery
+            };
+        },
+
+        filter: function (eventType, query, args) {
+            // They should be assigned before each trigger call.
+            var targetEl = this.targetEl;
+            var packedEvent = this.packedEvent;
+            var model = this.model;
+            var view = this.view;
+
+            // For event like 'globalout'.
+            if (!model || !view) {
+                return true;
+            }
+
+            var cptQuery = query.cptQuery;
+            var dataQuery = query.dataQuery;
+
+            return check(cptQuery, model, 'mainType')
+                && check(cptQuery, model, 'subType')
+                && check(cptQuery, model, 'index', 'componentIndex')
+                && check(cptQuery, model, 'name')
+                && check(cptQuery, model, 'id')
+                && check(dataQuery, packedEvent, 'name')
+                && check(dataQuery, packedEvent, 'dataIndex')
+                && check(dataQuery, packedEvent, 'dataType')
+                && (!view.filterForExposedEvent || view.filterForExposedEvent(
+                    eventType, query.otherQuery, targetEl, packedEvent
+                ));
+        }
+    };
+
+    function check(query, host, prop, propOnHost) {
+        return query[prop] == null || host[propOnHost || prop] === query[prop];
+    }
+}
+
+/**
  * @type {Object} key: actionType.
  * @inner
  */
diff --git a/src/view/Chart.js b/src/view/Chart.js
index 67a1c37..47485d8 100644
--- a/src/view/Chart.js
+++ b/src/view/Chart.js
@@ -134,7 +134,7 @@ Chart.prototype = {
      * @param  {Object} payload
      * @return {Object} {update: true}
      */
-    updateTransform: null
+    updateTransform: null,
 
     /**
      * The view contains the given point.
@@ -144,6 +144,15 @@ Chart.prototype = {
      */
     // containPoint: function () {}
 
+    /**
+     * @param {string} eventType
+     * @param {Object} query
+     * @param {module:zrender/Element} targetEl
+     * @param {Object} packedEvent
+     * @return {boolen} Pass only when return `true`.
+     */
+    filterForExposedEvent: null
+
 };
 
 var chartProto = Chart.prototype;
diff --git a/src/view/Component.js b/src/view/Component.js
index cd09fd5..180c502 100644
--- a/src/view/Component.js
+++ b/src/view/Component.js
@@ -43,7 +43,16 @@ Component.prototype = {
 
     render: function (componentModel, ecModel, api, payload) {},
 
-    dispose: function () {}
+    dispose: function () {},
+
+    /**
+     * @param {string} eventType
+     * @param {Object} query
+     * @param {module:zrender/Element} targetEl
+     * @param {Object} packedEvent
+     * @return {boolen} Pass only when return `true`.
+     */
+    filterForExposedEvent: null
 
 };
 
diff --git a/test/custom-feature.html b/test/custom-feature.html
new file mode 100644
index 0000000..fe979db
--- /dev/null
+++ b/test/custom-feature.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+
+
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1" />
+        <script src="lib/esl.js"></script>
+        <script src="lib/config.js"></script>
+        <script src="lib/jquery.min.js"></script>
+        <script src="lib/facePrint.js"></script>
+        <script src="lib/testHelper.js"></script>
+        <link rel="stylesheet" href="lib/reset.css" />
+    </head>
+    <body>
+        <style>
+            .test-title {
+                background: #146402;
+                color: #fff;
+            }
+        </style>
+
+
+        <div id="main0"></div>
+
+
+        <script>
+
+            require([
+                'echarts'/*, 'map/js/china' */
+            ], function (echarts) {
+
+                var option = {
+                    xAxis: {},
+                    yAxis: {},
+                    dataZoom: [{}, {type: 'inside'}],
+                    series: [{
+                        type: 'custom',
+                        renderItem: function (params, api) {
+                            return {
+                                type: 'group',
+                                position: api.coord([api.value(0), api.value(1)]),
+                                children: [{
+                                    type: 'rect',
+                                    shape: {
+                                        x: -100,
+                                        y: -100,
+                                        width: 200,
+                                        height: 200,
+                                        r: 10
+                                    },
+                                    style: {
+                                        fill: 'rgba(102,241,98,0.4)'
+                                    },
+                                    silent: true,
+                                    styleEmphasis: false
+                                }, {
+                                    type: 'group',
+                                    name: 'innerGroup',
+                                    children: [{
+                                        type: 'circle',
+                                        shape: {
+                                            r: 30,
+                                            cx: 0,
+                                            cy: 0
+                                        },
+                                        style: {
+                                            fill: 'red',
+                                            text: 'dataIndex: ' + params.dataIndex,
+                                            textStroke: '#fff',
+                                            textStrokeWidth: 1
+                                        }
+                                    }, {
+                                        type: 'rect',
+                                        shape: {
+                                            x: 50,
+                                            y: 60,
+                                            width: 80,
+                                            height: 80
+                                        },
+                                        style: {
+                                            fill: '#ff3391'
+                                        }
+                                    }]
+                                }]
+                            };
+                        },
+                        data: [[121, 333], [29, 212]]
+                    }]
+                };
+
+                var chart = testHelper.create(echarts, 'main0', {
+                    title: [
+                        'Eventful: ',
+                        '+ red shapes trigger events, but green shapes not. ',
+                        '+ Only dataIndex: 1 trigger event',
+                        'events (click, mousedown, mousemove, mouseup) are printed in console log.'
+                    ],
+                    option: option
+                });
+
+                echarts.util.each(['click', 'mousedown', 'mousemove', 'mouseup'], function (eventName) {
+                    chart.on(eventName, {dataIndex: 1, target: 'innerGroup'}, function (e) {
+                        console.log(e.type, e);
+                    });
+                });
+            });
+
+        </script>
+    </body>
+</html>
\ No newline at end of file
diff --git a/test/ec-event.html b/test/ec-event.html
new file mode 100644
index 0000000..a80844c
--- /dev/null
+++ b/test/ec-event.html
@@ -0,0 +1,202 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+
+
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1" />
+        <script src="lib/esl.js"></script>
+        <script src="lib/config.js"></script>
+        <script src="lib/jquery.min.js"></script>
+        <script src="lib/facePrint.js"></script>
+        <script src="lib/testHelper.js"></script>
+        <link rel="stylesheet" href="lib/reset.css" />
+    </head>
+    <body>
+        <style>
+            .test-title {
+                background: #146402;
+                color: #fff;
+            }
+        </style>
+
+
+        <div id="main0"></div>
+        <div id="main1"></div>
+        <div id="main2"></div>
+        <h2><a href="./custom-feature.html">custom element event case.</a></h2>
+        <div id="main3"></div>
+
+
+        <script>
+
+            require([
+                'echarts'/*, 'map/js/china' */
+            ], function (echarts) {
+
+                var option = {
+                    xAxis: {
+                        triggerEvent: true,
+                        data: ['aaaa', 'bbbb', 'cccc', 'dddd', 'eeee']
+                    },
+                    yAxis: [{
+                        triggerEvent: true
+                    }, {
+                        triggerEvent: true,
+                        type: 'category'
+                    }],
+                    series: [{
+                        type: 'line',
+                        data: [121, 442, 55],
+                    }, {
+                        type: 'scatter',
+                        yAxisIndex: 1,
+                        data: ['asdf', 'zxcv', 'qwer']
+                    }, {
+                        type: 'line',
+                        data: [234, 213, 255],
+                    }]
+                };
+
+                var chart = testHelper.create(echarts, 'main0', {
+                    title: [
+                        'Check event log on console:',
+                        'click | xAxis click | yAxis.category click | series click | series scatter click',
+                    ],
+                    option: option
+                });
+
+                chart.on('click', function () {
+                    console.log('click');
+                });
+                chart.on('click', 'xAxis', function () {
+                    console.log('click xAxis');
+                });
+                chart.on('click', 'yAxis.category', function () {
+                    console.log('click yAxis.category');
+                });
+                chart.on('click', 'series', function () {
+                    console.log('click series');
+                });
+                chart.on('click', 'series.scatter', function () {
+                    console.log('click series.scatter');
+                });
+
+            });
+
+        </script>
+
+
+
+
+
+
+
+        <script>
+
+            require([
+                'echarts'/*, 'map/js/china' */
+            ], function (echarts) {
+
+                var option = {
+                    xAxis: {
+                        triggerEvent: true,
+                        data: ['aaaa', 'bbbb', 'cccc', 'dddd', 'eeee']
+                    },
+                    yAxis: [{
+                        id: 'left',
+                        triggerEvent: true
+                    }, {
+                        triggerEvent: true,
+                        type: 'category'
+                    }],
+                    series: [{
+                        type: 'line',
+                        name: 'line-all',
+                        symbolSize: 20,
+                        data: [121, 442, 55],
+                    }, {
+                        type: 'scatter',
+                        yAxisIndex: 1,
+                        label: {
+                            show: true,
+                            formatter: 'name: {b}'
+                        },
+                        symbolSize: 20,
+                        data: [
+                            'asdf',
+                            {name: 'ff', value: 'zxcv'},
+                            'qwer'
+                        ]
+                    }, {
+                        type: 'line',
+                        name: 'line-all',
+                        symbolSize: 20,
+                        label: {
+                            show: true,
+                            formatter: 'name: {b}'
+                        },
+                        data: [
+                            {name: 'ee', value: 234},
+                            {name: 'ff', value: 113},
+                            {name: 'gg', value: 255}
+                        ]
+                    }]
+                };
+
+                var chart = testHelper.create(echarts, 'main1', {
+                    title: [
+                        'Check event log on console:',
+                        'click | click yAxisIndex: 1 | mouseover yAxisId: "left" | click seriesName: "line-all"',
+                        'click data name "ff" | mouseup seriesName: "line-all", dataIndex: 1'
+                    ],
+                    option: option
+                });
+
+                chart.on('click', function () {
+                    console.log('click');
+                });
+                chart.on('click', {yAxisIndex: 1}, function () {
+                    console.log('click yAxisIndex: 1');
+                });
+                chart.on('mouseover', {yAxisId: 'left'}, function () {
+                    console.log('mouseover yAxisId: "x"');
+                });
+                chart.on('click', {seriesName: 'line-all'}, function () {
+                    console.log('click seriesName: "line-all"');
+                });
+                chart.on('click', {name: 'ff'}, function () {
+                    console.log('click {name: "ff"}');
+                });
+                chart.on('mouseup', {seriesName: 'line-all', dataIndex: 1}, function () {
+                    console.log('mouseup {seriesName: "line-all", dataIndex: 1}');
+                });
+            });
+
+        </script>
+
+
+
+
+
+
+    </body>
+</html>
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org