├── CHANGELOG.md ├── demos ├── assets │ ├── codemirror-5.27.4 │ │ ├── .travis.yml │ │ ├── .gitattributes │ │ ├── .npmignore │ │ ├── addon │ │ │ ├── fold │ │ │ │ ├── foldgutter.css │ │ │ │ ├── comment-fold.js │ │ │ │ ├── brace-fold.js │ │ │ │ ├── foldgutter.js │ │ │ │ ├── foldcode.js │ │ │ │ └── xml-fold.js │ │ │ └── hint │ │ │ │ ├── show-hint.css │ │ │ │ ├── anyword-hint.js │ │ │ │ ├── css-hint.js │ │ │ │ ├── xml-hint.js │ │ │ │ ├── javascript-hint.js │ │ │ │ └── html-hint.js │ │ ├── mode │ │ │ ├── htmlmixed │ │ │ │ └── htmlmixed.js │ │ │ └── xml │ │ │ │ └── xml.js │ │ └── lib │ │ │ └── codemirror.css │ ├── common.css │ ├── lazyload-2.0.0-beta.2.min.js │ ├── index.css │ ├── routie-0.3.2.min.js │ ├── index.js │ ├── codemirror-5.29.0 │ │ └── codemirror-merged.min.css │ ├── jquery.resizable-0.20.0.js │ ├── popper.js-1.12.5 │ │ └── popper-utils.min.js │ └── clipboard-1.7.1.min.js ├── interval.html ├── filter-shape.html ├── line.html ├── index.njk ├── ds-state.html ├── polygon.html ├── app.js ├── data │ ├── sp500.json │ └── iris.json └── highlight.html ├── .eslintignore ├── index.js ├── bin ├── mkdir-dist.js ├── win-dev.js ├── gh-pages.js └── screenshot.js ├── webpack-dev.config.js ├── .torch.compile.opts.js ├── .travis.yml ├── .editorconfig ├── .eslintrc ├── test └── unit │ └── index-spec.js ├── webpack.config.js ├── LICENSE ├── .gitignore ├── .npmignore ├── src ├── util.js └── brush.js ├── package.json ├── README.md ├── docs └── brush.md ├── CONTRIBUTING.zh-CN.md └── CONTRIBUTING.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demos/assets/codemirror-5.27.4/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | sudo: false 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | coverage/ 3 | dist/ 4 | mocks/ 5 | node_modules/ 6 | demos/assets/ 7 | demos/index.html 8 | -------------------------------------------------------------------------------- /demos/assets/common.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | overflow: hidden; 3 | } 4 | ::-webkit-scrollbar { 5 | display: none; 6 | } 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * g2-brush 3 | * @author sima.zhang1990@gmail.com 4 | */ 5 | const Brush = require('./src/brush'); 6 | module.exports = Brush; 7 | -------------------------------------------------------------------------------- /demos/assets/codemirror-5.27.4/.gitattributes: -------------------------------------------------------------------------------- 1 | *.txt text 2 | *.js text 3 | *.html text 4 | *.md text 5 | *.json text 6 | *.yml text 7 | *.css text 8 | *.svg text 9 | -------------------------------------------------------------------------------- /demos/assets/codemirror-5.27.4/.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /demo 3 | /doc 4 | /test 5 | /test*.html 6 | /index.html 7 | /mode/*/*test.js 8 | /mode/*/*.html 9 | /mode/index.html 10 | .* 11 | bin 12 | -------------------------------------------------------------------------------- /bin/mkdir-dist.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const path = require('path'); 3 | const shelljs = require('shelljs'); 4 | 5 | const pathname = path.join(process.cwd(), './dist'); 6 | shelljs.rm('-rf', pathname); 7 | shelljs.mkdir('-p', pathname); 8 | 9 | -------------------------------------------------------------------------------- /webpack-dev.config.js: -------------------------------------------------------------------------------- 1 | const webpackConfig = require('./webpack.config'); 2 | const _ = require('lodash'); 3 | 4 | module.exports = _.merge({ 5 | devtool: 'cheap-source-map', 6 | watch: true, 7 | watchOptions: { 8 | aggregateTimeout: 300, 9 | poll: 1000 10 | } 11 | }, webpackConfig); 12 | -------------------------------------------------------------------------------- /.torch.compile.opts.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | babelrc: { 3 | presets: [ 4 | 'es2015', 5 | 'stage-0' 6 | ], 7 | sourceMaps: 'inline' 8 | }, 9 | extensions: ['.js'], 10 | include: [ 11 | 'node_modules/**/src/gl-matrix/**/*.js ' 12 | ], 13 | exclude: [ 14 | 'bower_components/**/*.js', 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /bin/win-dev.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const shelljs = require('shelljs'); 3 | const exec = shelljs.exec; 4 | 5 | const childWatch = exec('npm run watch', { 6 | async: true 7 | }); 8 | childWatch.stdout.on('data', data => { 9 | if (data.indexOf('Hash') === 0) { 10 | exec('npm run demos-web', { 11 | async: true 12 | }); 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "8" 5 | 6 | env: 7 | matrix: 8 | - TEST_TYPE=ci 9 | 10 | addons: 11 | apt: 12 | packages: 13 | - xvfb 14 | 15 | install: 16 | - export DISPLAY=':99.0' 17 | - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 18 | - npm install 19 | 20 | script: 21 | - | 22 | if [ "$TEST_TYPE" = ci ]; then 23 | npm run ci 24 | fi 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [Makefile] 16 | indent_style = tab 17 | indent_size = 1 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /demos/assets/codemirror-5.27.4/addon/fold/foldgutter.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-foldmarker { 2 | color: blue; 3 | text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px; 4 | font-family: arial; 5 | line-height: .3; 6 | cursor: pointer; 7 | } 8 | .CodeMirror-foldgutter { 9 | width: .7em; 10 | } 11 | .CodeMirror-foldgutter-open, 12 | .CodeMirror-foldgutter-folded { 13 | cursor: pointer; 14 | } 15 | .CodeMirror-foldgutter-open:after { 16 | content: "\25BE"; 17 | } 18 | .CodeMirror-foldgutter-folded:after { 19 | content: "\25B8"; 20 | } 21 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "egg" 4 | ], 5 | "globals": { 6 | "$": true, 7 | "DataSet": true, 8 | "G2": true, 9 | "Brush": true, 10 | "_": true 11 | }, 12 | "parser": "babel-eslint", 13 | "parserOptions": { 14 | "sourceType": "module" 15 | }, 16 | "plugins": [ 17 | "html" 18 | ], 19 | "rules": { 20 | "no-bitwise": [ 21 | 0 22 | ], 23 | "experimentalDecorators": [ 24 | 0 25 | ], 26 | "comma-dangle": [ 27 | "error", 28 | "never" 29 | ], 30 | "linebreak-style": [ 31 | 0 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/unit/index-spec.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const isRenderer = require('is-electron-renderer'); 3 | const brush = require('../../build/g2-brush'); 4 | 5 | describe('sample', () => { 6 | it('test', () => { 7 | expect('test').to.be.a('string'); 8 | expect(brush).to.be.an('function'); 9 | }); 10 | }); 11 | 12 | after(() => { 13 | if (isRenderer && window.__coverage__) { 14 | const { remote } = require('electron'); 15 | const fs = remote.require('fs'); 16 | const path = remote.require('path'); 17 | fs.writeFileSync(path.resolve(process.cwd(), './test/coverage/coverage.json'), JSON.stringify(window.__coverage__)); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /demos/assets/codemirror-5.27.4/addon/hint/show-hint.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-hints { 2 | position: absolute; 3 | z-index: 10; 4 | overflow: hidden; 5 | list-style: none; 6 | 7 | margin: 0; 8 | padding: 2px; 9 | 10 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 11 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 12 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 13 | border-radius: 3px; 14 | border: 1px solid silver; 15 | 16 | background: white; 17 | font-size: 90%; 18 | font-family: monospace; 19 | 20 | max-height: 20em; 21 | overflow-y: auto; 22 | } 23 | 24 | .CodeMirror-hint { 25 | margin: 0; 26 | padding: 0 4px; 27 | border-radius: 2px; 28 | white-space: pre; 29 | color: black; 30 | cursor: pointer; 31 | } 32 | 33 | li.CodeMirror-hint-active { 34 | background: #08f; 35 | color: white; 36 | } 37 | -------------------------------------------------------------------------------- /bin/gh-pages.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | process.env.DEBUG = 'app:*'; 3 | const debug = require('debug')('app:pages'); 4 | const fs = require('fs'); 5 | const ghpages = require('gh-pages'); 6 | const path = require('path'); 7 | const shelljs = require('shelljs'); 8 | 9 | const pathnames = [ 10 | './build', 11 | './README.md', 12 | './dist', 13 | './demos' 14 | ]; 15 | 16 | const tempDir = path.join(process.cwd(), './_home'); 17 | shelljs.rm('-rf', tempDir); 18 | shelljs.mkdir('-p', tempDir); 19 | fs.writeFileSync(path.join(tempDir, '.gitignore'), ' ', 'utf8'); 20 | pathnames.forEach(pathname => { 21 | shelljs.cp('-r', path.join(process.cwd(), pathname), tempDir); 22 | }); 23 | 24 | ghpages.publish(tempDir, { 25 | add: true, 26 | dotfiles: true 27 | }, err => { 28 | debug(err); 29 | shelljs.rm('-rf', tempDir); 30 | }); 31 | 32 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const resolve = require('path').resolve; 3 | 4 | module.exports = { 5 | entry: { 6 | 'g2-brush': './index.js' 7 | }, 8 | output: { 9 | filename: '[name].js', 10 | library: 'Brush', 11 | libraryTarget: 'umd', 12 | path: resolve(__dirname, 'build/') 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | // exclude: /(node_modules|bower_components)/, 19 | use: { 20 | loader: 'babel-loader', 21 | options: { 22 | babelrc: false, 23 | plugins: [ 24 | 'transform-remove-strict-mode' 25 | ], 26 | presets: [ 27 | [ 28 | 'es2015', { 29 | loose: true 30 | // modules: false 31 | } 32 | ], 33 | 'stage-0' 34 | ] 35 | } 36 | } 37 | } 38 | ] 39 | }, 40 | plugins: [ 41 | new webpack.NoEmitOnErrorsPlugin(), 42 | new webpack.optimize.AggressiveMergingPlugin() 43 | ] 44 | }; 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alipay.inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # lock 9 | package-lock.json 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | build 64 | dist 65 | temp 66 | .DS_Store 67 | .idea 68 | demos/assets/screenshots 69 | demos/index.html 70 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # lock 9 | package-lock.json 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | .DS_Store 64 | 65 | # npmignore - content above this line is automatically generated and modifications may be omitted 66 | # see npmjs.com/npmignore for more details. 67 | test 68 | -------------------------------------------------------------------------------- /demos/assets/codemirror-5.27.4/addon/hint/anyword-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var WORD = /[\w$]+/, RANGE = 500; 15 | 16 | CodeMirror.registerHelper("hint", "anyword", function(editor, options) { 17 | var word = options && options.word || WORD; 18 | var range = options && options.range || RANGE; 19 | var cur = editor.getCursor(), curLine = editor.getLine(cur.line); 20 | var end = cur.ch, start = end; 21 | while (start && word.test(curLine.charAt(start - 1))) --start; 22 | var curWord = start != end && curLine.slice(start, end); 23 | 24 | var list = options && options.list || [], seen = {}; 25 | var re = new RegExp(word.source, "g"); 26 | for (var dir = -1; dir <= 1; dir += 2) { 27 | var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; 28 | for (; line != endLine; line += dir) { 29 | var text = editor.getLine(line), m; 30 | while (m = re.exec(text)) { 31 | if (line == cur.line && m[0] === curWord) continue; 32 | if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) { 33 | seen[m[0]] = true; 34 | list.push(m[0]); 35 | } 36 | } 37 | } 38 | } 39 | return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /demos/interval.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Interval Chart(X) 11 | 12 | 13 | 14 |
15 |
16 |
17 | 18 | 19 | 20 | 21 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | function _mix(dist, obj) { 2 | for (const k in obj) { 3 | if (obj.hasOwnProperty(k) && k !== 'constructor' && obj[k] !== undefined) { 4 | dist[k] = obj[k]; 5 | } 6 | } 7 | } 8 | 9 | const Util = { 10 | mix(dist, obj1, obj2, obj3) { 11 | if (obj1) { 12 | _mix(dist, obj1); 13 | } 14 | 15 | if (obj2) { 16 | _mix(dist, obj2); 17 | } 18 | 19 | if (obj3) { 20 | _mix(dist, obj3); 21 | } 22 | return dist; 23 | }, 24 | /** 25 | * 添加事件监听器 26 | * @param {Object} target DOM对象 27 | * @param {String} eventType 事件名 28 | * @param {Funtion} callback 回调函数 29 | * @return {Object} 返回对象 30 | */ 31 | addEventListener(target, eventType, callback) { 32 | if (target.addEventListener) { 33 | target.addEventListener(eventType, callback, false); 34 | return { 35 | remove() { 36 | target.removeEventListener(eventType, callback, false); 37 | } 38 | }; 39 | } else if (target.attachEvent) { 40 | target.attachEvent('on' + eventType, callback); 41 | return { 42 | remove() { 43 | target.detachEvent('on' + eventType, callback); 44 | } 45 | }; 46 | } 47 | }, 48 | /** 49 | * 封装事件,便于使用上下文this,和便于解除事件时使用 50 | * @protected 51 | * @param {Object} obj 对象 52 | * @param {String} action 事件名称 53 | * @return {Function} 返回事件处理函数 54 | */ 55 | wrapBehavior(obj, action) { 56 | if (obj['_wrap_' + action]) { 57 | return obj['_wrap_' + action]; 58 | } 59 | const method = e => { 60 | obj[action](e); 61 | }; 62 | obj['_wrap_' + action] = method; 63 | return method; 64 | }, 65 | /** 66 | * 获取封装的事件 67 | * @protected 68 | * @param {Object} obj 对象 69 | * @param {String} action 事件名称 70 | * @return {Function} 返回事件处理函数 71 | */ 72 | getWrapBehavior(obj, action) { 73 | return obj['_wrap_' + action]; 74 | } 75 | }; 76 | 77 | module.exports = Util; 78 | -------------------------------------------------------------------------------- /demos/assets/lazyload-2.0.0-beta.2.min.js: -------------------------------------------------------------------------------- 1 | /*! Lazy Load 2.0.0-beta.2 - MIT license - Copyright 2007-2017 Mika Tuupola */ 2 | !function(t,e){"function"==typeof define&&define.amd?define([],e(t)):"object"==typeof exports?module.exports=e(t):t.LazyLoad=e(t)}("undefined"!=typeof global?global:this.window||this.global,function(t){"use strict";function e(t,e){this.settings=r(s,e||{}),this.images=t||document.querySelectorAll(this.settings.selector),this.observer=null,this.init()}const s={src:"data-src",srcset:"data-srcset",selector:".lazyload"},r=function(){let t={},e=!1,s=0,o=arguments.length;"[object Boolean]"===Object.prototype.toString.call(arguments[0])&&(e=arguments[0],s++);for(;s0){e.observer.unobserve(t.target);let s=t.target.getAttribute(e.settings.src),r=t.target.getAttribute(e.settings.srcset);"img"===t.target.tagName.toLowerCase()?(s&&(t.target.src=s),r&&(t.target.srcset=r)):t.target.style.backgroundImage="url("+s+")"}})},s),this.images.forEach(function(t){e.observer.observe(t)})},loadAndDestroy:function(){this.settings&&(this.loadImages(),this.destroy())},loadImages:function(){if(!this.settings)return;let t=this;this.images.forEach(function(e){let s=e.getAttribute(t.settings.src),r=e.getAttribute(t.settings.srcset);"img"===e.tagName.toLowerCase()?(s&&(e.src=s),r&&(e.srcset=r)):e.style.backgroundImage="url("+s+")"})},destroy:function(){this.settings&&(this.observer.disconnect(),this.settings=null)}},t.lazyload=function(t,s){return new e(t,s)},window.jQuery){const t=window.jQuery;t.fn.lazyload=function(s){return s=s||{},s.attribute=s.attribute||"data-src",new e(t.makeArray(this),s),this}}return e}); 3 | -------------------------------------------------------------------------------- /demos/assets/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | /*overflow: hidden;*/ 3 | } 4 | ::-webkit-scrollbar { 5 | display: none; 6 | } 7 | 8 | .filter { 9 | padding: 15px; 10 | } 11 | .demo-thumbnails { 12 | padding: 15px; 13 | } 14 | .demo-thumbnail { 15 | margin-bottom: 30px; 16 | } 17 | .thumbnail-container { 18 | overflow: hidden; 19 | max-height: 202px; 20 | } 21 | .thumbnail-container img { 22 | width: 100%; 23 | } 24 | .card-body { 25 | padding: 8px; 26 | } 27 | iframe { 28 | border: none; 29 | } 30 | .scaled-frame { 31 | margin: 0 auto; 32 | width: 800px; 33 | height: 450px; 34 | -webkit-transform: scale(0.45); 35 | -webkit-transform-origin: 0 0; 36 | } 37 | #doc-container { 38 | display: -webkit-box; 39 | display: -webkit-flex; 40 | display: -ms-flexbox; 41 | display: flex; 42 | -webkit-box-flex: 1; 43 | -webkit-flex: 1 1 auto; 44 | -ms-flex: 1 1 auto; 45 | flex: 1 1 auto; 46 | position: fixed; 47 | top: 0; 48 | left: 0; 49 | height: 100%; 50 | width: 100%; 51 | background: white; 52 | } 53 | .code-panel { 54 | -webkit-overflow-scrolling: touch; 55 | -webkit-transform: translateZ(0); 56 | background-color: #fff; 57 | display: block; 58 | width: 400px; 59 | height: 100%; 60 | overflow: hidden; 61 | padding: 0 1rem 0 0; 62 | position: relative; 63 | right: 0; 64 | top: 0; 65 | border-right: 0.1rem solid #e8e8e8; 66 | transform: translateZ(0); 67 | white-space: nowrap; 68 | will-change: scroll-position; 69 | z-index: 1; 70 | } 71 | 72 | .code-banner { 73 | padding: .5rem 0; 74 | } 75 | 76 | .code-panel, .code-editor .CodeMirror { 77 | height: 100%; 78 | } 79 | 80 | .code-editor { 81 | height: calc(100% - 3.375rem); 82 | } 83 | 84 | 85 | #chart-panel { 86 | flex: 1; 87 | overflow: hidden; 88 | padding: 1rem; 89 | -webkit-transform: translateZ(0); 90 | transform: translateZ(0); 91 | will-change: scroll-position; 92 | -webkit-overflow-scrolling: touch; 93 | } 94 | #resize-handler { 95 | //background: #DEDEEB; 96 | border-right: 2px solid #DEDEEB; 97 | cursor: col-resize; 98 | } 99 | .chart-frame { 100 | height: 100%; 101 | width: 100%; 102 | } 103 | 104 | .btn { 105 | cursor: pointer; 106 | } 107 | -------------------------------------------------------------------------------- /demos/filter-shape.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Filter Shape 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /demos/assets/codemirror-5.27.4/addon/fold/comment-fold.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { 15 | return mode.blockCommentStart && mode.blockCommentEnd; 16 | }, function(cm, start) { 17 | var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd; 18 | if (!startToken || !endToken) return; 19 | var line = start.line, lineText = cm.getLine(line); 20 | 21 | var startCh; 22 | for (var at = start.ch, pass = 0;;) { 23 | var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1); 24 | if (found == -1) { 25 | if (pass == 1) return; 26 | pass = 1; 27 | at = lineText.length; 28 | continue; 29 | } 30 | if (pass == 1 && found < start.ch) return; 31 | if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) && 32 | (found == 0 || lineText.slice(found - endToken.length, found) == endToken || 33 | !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) { 34 | startCh = found + startToken.length; 35 | break; 36 | } 37 | at = found - 1; 38 | } 39 | 40 | var depth = 1, lastLine = cm.lastLine(), end, endCh; 41 | outer: for (var i = line; i <= lastLine; ++i) { 42 | var text = cm.getLine(i), pos = i == line ? startCh : 0; 43 | for (;;) { 44 | var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos); 45 | if (nextOpen < 0) nextOpen = text.length; 46 | if (nextClose < 0) nextClose = text.length; 47 | pos = Math.min(nextOpen, nextClose); 48 | if (pos == text.length) break; 49 | if (pos == nextOpen) ++depth; 50 | else if (!--depth) { end = i; endCh = pos; break outer; } 51 | ++pos; 52 | } 53 | } 54 | if (end == null || line == end && endCh == startCh) return; 55 | return {from: CodeMirror.Pos(line, startCh), 56 | to: CodeMirror.Pos(end, endCh)}; 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /demos/assets/codemirror-5.27.4/addon/hint/css-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("../../mode/css/css")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "../../mode/css/css"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1, 15 | "first-letter": 1, "first-line": 1, "first-child": 1, 16 | before: 1, after: 1, lang: 1}; 17 | 18 | CodeMirror.registerHelper("hint", "css", function(cm) { 19 | var cur = cm.getCursor(), token = cm.getTokenAt(cur); 20 | var inner = CodeMirror.innerMode(cm.getMode(), token.state); 21 | if (inner.mode.name != "css") return; 22 | 23 | if (token.type == "keyword" && "!important".indexOf(token.string) == 0) 24 | return {list: ["!important"], from: CodeMirror.Pos(cur.line, token.start), 25 | to: CodeMirror.Pos(cur.line, token.end)}; 26 | 27 | var start = token.start, end = cur.ch, word = token.string.slice(0, end - start); 28 | if (/[^\w$_-]/.test(word)) { 29 | word = ""; start = end = cur.ch; 30 | } 31 | 32 | var spec = CodeMirror.resolveMode("text/css"); 33 | 34 | var result = []; 35 | function add(keywords) { 36 | for (var name in keywords) 37 | if (!word || name.lastIndexOf(word, 0) == 0) 38 | result.push(name); 39 | } 40 | 41 | var st = inner.state.state; 42 | if (st == "pseudo" || token.type == "variable-3") { 43 | add(pseudoClasses); 44 | } else if (st == "block" || st == "maybeprop") { 45 | add(spec.propertyKeywords); 46 | } else if (st == "prop" || st == "parens" || st == "at" || st == "params") { 47 | add(spec.valueKeywords); 48 | add(spec.colorKeywords); 49 | } else if (st == "media" || st == "media_parens") { 50 | add(spec.mediaTypes); 51 | add(spec.mediaFeatures); 52 | } 53 | 54 | if (result.length) return { 55 | list: result, 56 | from: CodeMirror.Pos(cur.line, start), 57 | to: CodeMirror.Pos(cur.line, end) 58 | }; 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /demos/line.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Line Chart(XY) 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /demos/index.njk: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Demos 12 | 13 | 14 | 19 |
20 |
21 | 22 |
23 |
24 | {% for file in demoFiles %} 25 |
26 |
27 | 28 | 29 | 30 |
31 |
{{ file.basename }}
32 |
33 |
34 |
35 | {% endfor %} 36 |
37 | 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /demos/assets/routie-0.3.2.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * routie - a tiny hash router 3 | * v0.3.2 4 | * http://projects.jga.me/routie 5 | * copyright Greg Allen 2016 6 | * MIT License 7 | */ 8 | var Routie=function(a,b){var c=[],d={},e="routie",f=a[e],g=function(a,b){this.name=b,this.path=a,this.keys=[],this.fns=[],this.params={},this.regex=h(this.path,this.keys,!1,!1)};g.prototype.addHandler=function(a){this.fns.push(a)},g.prototype.removeHandler=function(a){for(var b=0,c=this.fns.length;c>b;b++){var d=this.fns[b];if(a==d)return void this.fns.splice(b,1)}},g.prototype.run=function(a){for(var b=0,c=this.fns.length;c>b;b++)this.fns[b].apply(this,a)},g.prototype.match=function(a,b){var c=this.regex.exec(a);if(!c)return!1;for(var d=1,e=c.length;e>d;++d){var f=this.keys[d-1],g="string"==typeof c[d]?decodeURIComponent(c[d]):c[d];f&&(this.params[f.name]=g),b.push(g)}return!0},g.prototype.toURL=function(a){var b=this.path;for(var c in a)b=b.replace("/:"+c,"/"+a[c]);if(b=b.replace(/\/:.*\?/g,"/").replace(/\?/g,""),-1!=b.indexOf(":"))throw new Error("missing parameters for url: "+b);return b};var h=function(a,b,c,d){return a instanceof RegExp?a:(a instanceof Array&&(a="("+a.join("|")+")"),a=a.concat(d?"":"/?").replace(/\/\(/g,"(?:/").replace(/\+/g,"__plus__").replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g,function(a,c,d,e,f,g){return b.push({name:e,optional:!!g}),c=c||"",""+(g?"":c)+"(?:"+(g?c:"")+(d||"")+(f||d&&"([^/.]+?)"||"([^/]+?)")+")"+(g||"")}).replace(/([\/.])/g,"\\$1").replace(/__plus__/g,"(.+)").replace(/\*/g,"(.*)"),new RegExp("^"+a+"$",c?"":"i"))},i=function(a,b){var e=a.split(" "),f=2==e.length?e[0]:null;a=2==e.length?e[1]:e[0],d[a]||(d[a]=new g(a,f),c.push(d[a])),d[a].addHandler(b)},j=function(a,b){if("function"==typeof b)i(a,b),j.reload();else if("object"==typeof a){for(var c in a)i(c,a[c]);j.reload()}else"undefined"==typeof b&&j.navigate(a)};j.lookup=function(a,b){for(var d=0,e=c.length;e>d;d++){var f=c[d];if(f.name==a)return f.toURL(b)}},j.remove=function(a,b){var c=d[a];c&&c.removeHandler(b)},j.removeAll=function(){d={},c=[]},j.navigate=function(a,b){b=b||{};var c=b.silent||!1;c&&o(),setTimeout(function(){window.location.hash=a,c&&setTimeout(function(){n()},1)},1)},j.noConflict=function(){return a[e]=f,j};var k=function(){return window.location.hash.substring(1)},l=function(a,b){var c=[];return b.match(a,c)?(b.run(c),!0):!1},m=j.reload=function(){for(var a=k(),b=0,d=c.length;d>b;b++){var e=c[b];if(l(a,e))return}},n=function(){a.addEventListener?a.addEventListener("hashchange",m,!1):a.attachEvent("onhashchange",m)},o=function(){a.removeEventListener?a.removeEventListener("hashchange",m):a.detachEvent("onhashchange",m)};return n(),b?j:void(a[e]=j)};"undefined"==typeof module?Routie(window):module.exports=Routie(window,!0); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@antv/g2-brush", 3 | "version": "0.0.2", 4 | "description": "Select a one-, two-dimensional or irregular region using the mouse.", 5 | "main": "build/g2-brush.js", 6 | "browser": "build/g2-brush.js", 7 | "module": "index.js", 8 | "repository": { 9 | "type": "git", 10 | "url": "git@github.com:antvis/g2-brush.git" 11 | }, 12 | "keywords": [ 13 | "antv", 14 | "g2-brush", 15 | "g2" 16 | ], 17 | "author": "sima.zhang1990@gmail.com", 18 | "license": "MIT", 19 | "devDependencies": { 20 | "@lite-js/torch": "~0.2.6", 21 | "babel-core": "~6.25.0", 22 | "babel-eslint": "~7.2.3", 23 | "babel-loader": "~7.1.1", 24 | "babel-plugin-transform-remove-strict-mode": "~0.0.2", 25 | "babel-preset-es2015": "~6.24.1", 26 | "babel-preset-stage-0": "~6.24.1", 27 | "chai": "~4.0.1", 28 | "commander": "~2.9.0", 29 | "connect": "~3.6.3", 30 | "d3-queue": "~3.0.7", 31 | "debug": "~3.1.0", 32 | "electron": "~1.6.11", 33 | "eslint": "^3.19.0", 34 | "eslint-config-airbnb": "~15.0.1", 35 | "eslint-config-egg": "~4.2.0", 36 | "eslint-plugin-html": "~3.1.1", 37 | "get-port": "~3.1.0", 38 | "gh-pages": "~1.1.0", 39 | "nightmare": "~2.10.0", 40 | "nunjucks": "~3.0.1", 41 | "open": "~0.0.5", 42 | "parseurl": "~1.3.1", 43 | "pre-commit": "~1.2.2", 44 | "serve-static": "~1.12.4", 45 | "shelljs": "~0.7.8", 46 | "uglify-js": "~3.0.15", 47 | "webpack": "~3.3.0" 48 | }, 49 | "scripts": { 50 | "build": "webpack", 51 | "ci": "npm run lint && npm run test", 52 | "compress": "uglifyjs -c -m -o dist/g2-brush.min.js -- build/g2-brush.js", 53 | "coverage": "npm run coverage-generator && npm run coverage-viewer", 54 | "coverage-generator": "torch --compile --coverage --renderer --recursive test/unit", 55 | "coverage-viewer": "torch-coverage", 56 | "demos": "electron ./demos/app.js", 57 | "demos-web": "node ./demos/app.js --web --port 2046", 58 | "dev": "npm run watch & npm run demos-web", 59 | "dist": "npm run mkdir-dist && npm run build && npm run compress", 60 | "lint": "eslint --ext .html,.js ./", 61 | "lint-fix": "eslint --ext .html,.js --fix ./", 62 | "mkdir-dist": "node ./bin/mkdir-dist.js", 63 | "prepublishOnly": "npm run dist", 64 | "screenshot": "node ./bin/screenshot.js", 65 | "test": "torch --compile --renderer --recursive ./test/unit", 66 | "test-live": "torch --compile --interactive --watch --recursive ./test/unit", 67 | "watch": "webpack --config webpack-dev.config.js", 68 | "win-dev": "node ./bin/win-dev.js" 69 | }, 70 | "pre-commit": { 71 | "run": [ 72 | "lint", 73 | "build", 74 | "test" 75 | ], 76 | "silent": false 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /bin/screenshot.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | process.env.DEBUG = 'app:*'; 3 | const debug = require('debug')('app:screenshot'); 4 | const MAX_POOL_SIZE = require('os').cpus().length; 5 | const Nightmare = require('nightmare'); 6 | const _ = require('lodash'); 7 | const commander = require('commander'); 8 | const connect = require('connect'); 9 | const getPort = require('get-port'); 10 | const http = require('http'); 11 | const path = require('path'); 12 | const basename = path.basename; 13 | const extname = path.extname; 14 | const join = path.join; 15 | const queue = require('d3-queue').queue; 16 | const serveStatic = require('serve-static'); 17 | const shelljs = require('shelljs'); 18 | const ls = shelljs.ls; 19 | const mkdir = shelljs.mkdir; 20 | const pkg = require('../package.json'); 21 | 22 | commander 23 | .version(pkg.version) 24 | .option('-p, --port ', 'specify a port number to run on', parseInt) 25 | .option('-n, --name ', 'specify the name for demos') 26 | .parse(process.argv); 27 | 28 | // assets 29 | const src = join(process.cwd(), './demos'); 30 | const dest = join(process.cwd(), './demos/assets/screenshots'); 31 | mkdir('-p', dest); 32 | 33 | const app = connect(); 34 | app.use('/', serveStatic(process.cwd())); 35 | 36 | const DELAY = 6000; 37 | 38 | getPort().then(port => { 39 | http.createServer(app).listen(port); 40 | const url = 'http://127.0.0.1:' + port; 41 | debug('server is ready on port ' + port + '! url: ' + url); 42 | 43 | const q = queue(MAX_POOL_SIZE > 2 ? MAX_POOL_SIZE - 1 : MAX_POOL_SIZE); 44 | const files = ls(src).filter(filename => (extname(filename) === '.html')); 45 | files.forEach(filename => { 46 | const name = basename(filename, '.html'); 47 | if (_.isString(commander.name) && filename.indexOf(commander.name) === -1) { 48 | debug(`>>>>>>>>> skipping because filename not matched: ${name}`); 49 | return; 50 | } 51 | q.defer(callback => { 52 | const t0 = Date.now(); 53 | const nightmare = Nightmare({ 54 | gotoTimeout: 600000, 55 | show: false 56 | }); 57 | const url = `http://127.0.0.1:${port}/demos/${name}.html`; 58 | const target = join(dest, `./${name}.png`); 59 | nightmare.viewport(800, 450) // 16 x 9 60 | .goto(url) 61 | .wait(DELAY) 62 | .screenshot(target, () => { 63 | debug(name + ' took ' + (Date.now() - t0) + ' to take a screenshot.'); 64 | callback(null); 65 | }) 66 | .end() 67 | .catch(e => { 68 | debug(url); 69 | debug(target); 70 | debug(name + ' failed to take a screenshot: ' + e); 71 | }); 72 | }); 73 | }); 74 | q.awaitAll(error => { 75 | if (error) { 76 | debug(error); 77 | process.exit(1); 78 | } 79 | debug('screenshots are all captured!'); 80 | process.exit(); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /demos/ds-state.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | DataSet State 11 | 12 | 13 | 14 |
15 |
16 | 17 | 18 | 19 | 20 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /demos/assets/index.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | // filtering 3 | const $query = $('#query'); 4 | function filter() { 5 | const str = $query.val(); 6 | if (!str) { 7 | $('.demo-thumbnail').show(); 8 | } else { 9 | $('.demo-thumbnail').each(function () { 10 | const $thumbnail = $(this); 11 | const basename = $thumbnail.data('basename'); 12 | if (basename.indexOf(str) === -1) { 13 | $thumbnail.hide(); 14 | } else { 15 | $thumbnail.show(); 16 | } 17 | }); 18 | } 19 | } 20 | $query.on('input', _.debounce(filter)); 21 | 22 | // router 23 | let currentId; 24 | const $code = $('#code'); 25 | const htmlEditor = CodeMirror.fromTextArea($code[0], { 26 | mode: "text/html", 27 | extraKeys: { 28 | 'Ctrl-Space': 'autocomplete' 29 | }, 30 | foldGutter: true, 31 | gutters: [ 32 | 'CodeMirror-linenumbers', 33 | 'CodeMirror-foldgutter' 34 | ], 35 | lineNumbers: true, 36 | lineWrapping: false 37 | }); 38 | 39 | const $docContainer = $('#doc-container'); 40 | const $chartPanel = $('#chart-panel'); 41 | const $codePanel = $('#code-panel'); 42 | 43 | function syncCode(code) { 44 | $chartPanel.html(''); 45 | $chartPanel.find('iframe')[0].contentWindow.document.write(code); 46 | htmlEditor.getDoc().setValue(code); 47 | } 48 | 49 | routie({ 50 | '/:id': id => { 51 | $docContainer.show(); 52 | const $htmlCode = $(`#code-${id}`); 53 | const code = $htmlCode.text(); 54 | syncCode(code) 55 | }, 56 | '': () => { 57 | $docContainer.hide(); 58 | } 59 | }); 60 | 61 | // resizable 62 | $codePanel.resizable({ 63 | handleSelector: '#resize-handler', 64 | resizeWidthFrom: 'right', 65 | resizeHeight: false, 66 | onDragStart() { 67 | $docContainer.css('pointer-events', 'none'); 68 | $docContainer.css('cursor', 'col-resize'); 69 | $codePanel.find('.CodeMirror-gutter-elt').css('cursor', 'col-resize'); 70 | }, 71 | onDragEnd() { 72 | $docContainer.css('pointer-events', 'auto'); 73 | $docContainer.css('cursor', 'default'); 74 | $codePanel.find('.CodeMirror-gutter-elt').css('cursor', 'default'); 75 | }, 76 | }); 77 | 78 | // copy code 79 | const BTN_COPY_SELECTOR = '#copy-code'; 80 | const clipboard = new Clipboard(BTN_COPY_SELECTOR, { 81 | text: () => htmlEditor.getValue(), 82 | }); 83 | let timer; 84 | clipboard.on('success', e => { 85 | e.clearSelection(); 86 | $(BTN_COPY_SELECTOR).text('Succeed!'); 87 | clearTimeout(timer); 88 | timer = setTimeout(() => { 89 | $(BTN_COPY_SELECTOR).text('Copy'); 90 | }, 2000); 91 | }); 92 | clipboard.on('error', e => { 93 | e.clearSelection(); 94 | $(BTN_COPY_SELECTOR).text('Failed!'); 95 | clearTimeout(timer); 96 | timer = setTimeout(() => { 97 | $(BTN_COPY_SELECTOR).text('Copy'); 98 | }, 2000); 99 | }); 100 | 101 | // run code 102 | $('#execute').on('click', () => { 103 | syncCode(htmlEditor.getValue()); 104 | }); 105 | })(); 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # g2-brush 2 | 3 | ![](https://img.shields.io/badge/language-javascript-red.svg) 4 | ![](https://img.shields.io/badge/license-MIT-000000.svg) 5 | 6 | [![npm package](https://img.shields.io/npm/v/@antv/g2-brush.svg)](https://www.npmjs.com/package/@antv/g2-brush) 7 | [![NPM downloads](http://img.shields.io/npm/dm/@antv/g2-brush.svg)](https://npmjs.org/package/@antv/g2-brush) 8 | [![Percentage of issues still open](http://isitmaintained.com/badge/open/antvis/g2-brush.svg)](http://isitmaintained.com/project/antvis/g2-brush "Percentage of issues still open") 9 | 10 | Chart's interaction enhancement tool for [G2](https://github.com/antvis/g2)(Please use a version greater than 3.0.1). 11 | 12 | ## Install 13 | 14 | ```bash 15 | $ npm install @antv/g2-brush 16 | ``` 17 | 18 | or use cdn: 19 | 20 | ```html 21 | 22 | ``` 23 | 24 | ## Usage 25 | 26 | First of all, the brush instance must be created after the chart be rendered. 27 | 28 | ```js 29 | import Brush from '@antv/g2-brush'; 30 | // ... 31 | chart.render(); 32 | 33 | new Brush({ 34 | canvas: chart.get('canvas'), // must be set 35 | chart, // if you want to filter data by default, please set the chart 36 | type: 'X', // set the brush type, default value is 'XY' 37 | }); 38 | ``` 39 | 40 | ### Example 41 | 42 | online demos: [https://antvis.github.io/g2-brush/demos/#](https://antvis.github.io/g2-brush/demos/#) 43 | 44 | ![](https://gw.alipayobjects.com/zos/rmsportal/HRkzmAbHDcJYweUxDAlC.gif) 45 | 46 | ```js 47 | $.getJSON('./data/top2000.json', data => { 48 | const ds = new DataSet(); 49 | const dv = ds.createView('test') 50 | .source(data) 51 | .transform({ 52 | as: [ 'count' ], 53 | groupBy: [ 'release' ], 54 | operations: [ 'count' ], 55 | type: 'aggregate' 56 | }); 57 | 58 | const chart = new G2.Chart({ 59 | container: 'canvas', 60 | forceFit: true, 61 | height: window.innerHeight 62 | }); 63 | chart.source(dv); 64 | chart.scale({ 65 | count: { 66 | alias: 'top2000 唱片总量' 67 | }, 68 | release: { 69 | tickInterval: 5, 70 | alias: '唱片发行年份' 71 | } 72 | }); 73 | chart.interval() 74 | .position('release*count') 75 | .color('#e50000'); 76 | 77 | chart.render(); 78 | 79 | new Brush({ 80 | canvas: chart.get('canvas'), 81 | chart, 82 | type: 'X', 83 | onBrushstart() { 84 | chart.hideTooltip(); 85 | }, 86 | onBrushmove() { 87 | chart.hideTooltip(); 88 | } 89 | }); 90 | chart.on('plotdblclick', () => { 91 | chart.get('options').filters = {}; 92 | chart.repaint(); 93 | }); 94 | }); 95 | ``` 96 | 97 | ## API 98 | 99 | [API DOCS](https://github.com/antvis/g2-brush/blob/master/docs/brush.md) 100 | 101 | ## Development 102 | 103 | ```bash 104 | $ npm install 105 | 106 | $ npm run dev 107 | ``` 108 | 109 | ## How to Contribute 110 | 111 | Please let us know how can we help. Do check out [issues](https://github.com/antvis/g2-brush/issues) for bug reports or suggestions first. 112 | 113 | To become a contributor, please follow our [contributing guide](https://github.com/antvis/g2-brush/blob/master/CONTRIBUTING.md). 114 | -------------------------------------------------------------------------------- /docs/brush.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | First of all, the brush instance must be created after the chart be rendered. 4 | 5 | 6 | ```js 7 | import Brush from '@antv/brush'; 8 | 9 | const brush = new Brush({ 10 | // properties 11 | }); 12 | ``` 13 | 14 | ## Properties 15 | 16 | - `canvas` 17 | 18 | An instance of G.Canvas. **Must be set**. If use it for G2 chart, just get it by `chart.get('canvas')`. 19 | 20 | ```js 21 | canvas: chart.get('canvas') 22 | ``` 23 | 24 | - `chart` 25 | 26 | An instance of G2.Chart. Set the brush target, then after brush action end, 27 | data will be automatically filtered when the `filter` property is true. 28 | 29 | - `type` 30 | 31 | String. Set the brush type, default is **'XY'**, also you can set **'X'**, **'Y'**, **'POLYGON'**. Case insensitive. 32 | 33 | - `style` 34 | 35 | Object. It's for setting the brush style. Default value is: 36 | 37 | ```js 38 | { 39 | fill: '#C5D4EB', 40 | opacity: 0.3, 41 | lineWidth: 1, 42 | stroke: '#82A6DD' 43 | } 44 | ``` 45 | 46 | - `inPlot` 47 | 48 | Boolean. Decide if the selection range is limited to the drawing area. Default value is true. 49 | 50 | - `filter` 51 | 52 | Boolean. Decide if automatically filter the data, default value is decided by `dragable` value,!dragable. But still set by the user. 53 | 54 | - `dragable` 55 | 56 | Boolean. Set whether the shape can be dragged, default is `false`, then `filter` is true. 57 | 58 | - `xField` 59 | 60 | String. Set the selected x-axis field name, used to get the scale instance of the x-axis. Optional. 61 | 62 | - `yField` 63 | 64 | String. Set the selected y-axis field name, used to get the scale instance of the y-axis. Optional. 65 | 66 | - `onBrushstart` 67 | 68 | Function. [examples](https://antvis.github.io/g2-brush/demos/#/highlight) 69 | 70 | ```js 71 | onBrushstart(ev) { 72 | // at the begining of the selection, you can define some properties, like chart xScale etc. 73 | const me = this; // On behalf of the object itself 74 | me.chart = chart; 75 | } 76 | ``` 77 | 78 | - `onBrushmove` 79 | 80 | Function. [examples](https://antvis.github.io/g2-brush/demos/#/highlight) 81 | 82 | ```js 83 | onBrushmove(ev) { 84 | // during selection 85 | const { data, shapes, x, y, ...others } = ev; 86 | } 87 | ``` 88 | 89 | - `onBrushend` 90 | 91 | Function. [examples]() 92 | 93 | ```js 94 | onBrushend(ev) { 95 | // selection stoped 96 | const { data, shapes, x, y, ...others } = ev; 97 | } 98 | ``` 99 | 100 | - `onDragstart` 101 | 102 | Function. Only `dragable` is true. 103 | 104 | ```js 105 | onDragstart(ev) { 106 | // at the begining of the drag, you can define some properties, like chart xScale etc. 107 | } 108 | ``` 109 | 110 | - `onDragmove` 111 | 112 | Function. Only `dragable` is true. 113 | 114 | [examples](https://antvis.github.io/g2-brush/demos/#/highlight) 115 | 116 | ```js 117 | onDragmove(ev) { 118 | // during drag 119 | const { data, shapes, x, y, ...others } = ev; 120 | } 121 | ``` 122 | 123 | - `onDragend` 124 | 125 | Function. Only `dragable` is true. 126 | 127 | [examples](https://antvis.github.io/g2-brush/demos/#/highlight) 128 | 129 | ```js 130 | onDragend(ev) { 131 | // drag stoped 132 | const { data, shapes, x, y, ...others } = ev; 133 | } 134 | ``` 135 | 136 | ## Function 137 | 138 | - `setType(type)` 139 | 140 | used for change brush type, see [examples](https://antvis.github.io/g2-brush/demos/#/highlight). 141 | -------------------------------------------------------------------------------- /demos/polygon.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Polygon brush 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /CONTRIBUTING.zh-CN.md: -------------------------------------------------------------------------------- 1 | # 代码贡献规范 2 | 3 | 有任何疑问,欢迎提交 [issue](https://github.com/antvis/g2-brush/issues), 4 | 或者直接修改提交 [PR](https://github.com/antvis/g2-brush/pulls)! 5 | 6 | ## 提交 issue 7 | 8 | - 请确定 issue 的类型。 9 | - 请避免提交重复的 issue,在提交之前搜索现有的 issue。 10 | - 在标签(分类参考**标签分类**), 标题 或者内容中体现明确的意图。 11 | 12 | 随后 AntV 负责人会确认 issue 意图,更新合适的标签,关联 milestone,指派开发者。 13 | 14 | ## 提交代码 15 | 16 | ### 提交 Pull Request 17 | 18 | 如果你有仓库的开发者权限,而且希望贡献代码,那么你可以创建分支修改代码提交 PR,AntV 开发团队会 review 代码合并到主干。 19 | 20 | ```bash 21 | # 先创建开发分支开发,分支名应该有含义,避免使用 update、tmp 之类的 22 | $ git checkout -b branch-name 23 | 24 | # 开发完成后跑下测试是否通过,必要时需要新增或修改测试用例 25 | $ npm test 26 | 27 | # 测试通过后,提交代码,message 见下面的规范 28 | 29 | $ git add . # git add -u 删除文件 30 | $ git commit -m "fix(role): role.use must xxx" 31 | $ git push origin branch-name 32 | ``` 33 | 34 | 提交后就可以在 [g2-brush](https://github.com/antvis/g2-brush/pulls) 创建 Pull Request 了。 35 | 36 | 由于谁也无法保证过了多久之后还记得多少,为了后期回溯历史的方便,请在提交 MR 时确保提供了以下信息。 37 | 38 | 1. 需求点(一般关联 issue 或者注释都算) 39 | 2. 升级原因(不同于 issue,可以简要描述下为什么要处理) 40 | 3. 框架测试点(可以关联到测试文件,不用详细描述,关键点即可) 41 | 4. 关注点(针对用户而言,可以没有,一般是不兼容更新等,需要额外提示) 42 | 43 | ### 代码风格 44 | 45 | 你的代码风格必须通过 eslint,你可以运行 `$ npm run lint` 本地测试。 46 | 47 | ### Commit 提交规范 48 | 49 | 根据 [angular 规范](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format)提交 commit, 50 | 这样 history 看起来更加清晰,还可以自动生成 changelog。 51 | 52 | ```xml 53 | (): 54 | 55 | 56 | 57 |