├── .gitignore ├── .npmignore ├── LICENSE ├── build └── rollup.config.js ├── dist ├── vue-virtual-table.esm.css ├── vue-virtual-table.esm.js ├── vue-virtual-table.min.js └── vue-virtual-table.umd.js ├── example ├── complexTable.vue ├── mockData.js └── simpleTable.vue ├── package.json ├── readme.md └── src ├── components ├── base-button.vue ├── base-checkgroup.vue ├── base-icon.vue ├── base-input.vue ├── base-popover.vue ├── base-select.vue ├── base-tooltip.vue ├── icons │ ├── arrowCarrotDown.vue │ ├── arrowCarrotRight.vue │ ├── boxChecked.vue │ ├── boxEmpty.vue │ ├── closeAlt2.vue │ ├── cloudDownloadAlt.vue │ └── documentsAlt.vue └── virtual-scroller.vue ├── entry.js ├── utils └── index.js └── vue-virtual-table.vue /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | yarn.lock -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | example/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 waningflow 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. -------------------------------------------------------------------------------- /build/rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import vue from 'rollup-plugin-vue' 3 | import babel from 'rollup-plugin-babel' 4 | import commonjs from 'rollup-plugin-commonjs' 5 | import replace from 'rollup-plugin-replace' 6 | import { terser } from 'rollup-plugin-terser' 7 | import minimist from 'minimist' 8 | import resolve from '@rollup/plugin-node-resolve'; 9 | import css from 'rollup-plugin-css-only' 10 | 11 | const argv = minimist(process.argv.slice(2)) 12 | 13 | const baseConfig = { 14 | input: 'src/entry.js', 15 | plugins: [ 16 | replace({ 17 | 'process.env.NODE_ENV': JSON.stringify('production') 18 | }), 19 | resolve(), 20 | commonjs(), 21 | vue({ 22 | css: true, 23 | compileTemplate: true, 24 | template: { 25 | isProduction: true 26 | } 27 | }), 28 | css(), 29 | babel() 30 | ] 31 | } 32 | 33 | // UMD/IIFE shared settings: externals and output.globals 34 | // Refer to https://rollupjs.org/guide/en#output-globals for details 35 | const external = [ 36 | // list external dependencies, exactly the way it is written in the import statement. 37 | // eg. 'jquery' 38 | ] 39 | const globals = { 40 | // Provide global variable names to replace your external imports 41 | // eg. jquery: '$' 42 | } 43 | 44 | // Customize configs for individual targets 45 | const buildFormats = [] 46 | if (!argv.format || argv.format === 'es') { 47 | const esConfig = { 48 | ...baseConfig, 49 | output: { 50 | file: 'dist/vue-virtual-table.esm.js', 51 | format: 'esm', 52 | exports: 'named' 53 | }, 54 | plugins: [ 55 | ...baseConfig.plugins, 56 | terser({ 57 | output: { 58 | ecma: 6 59 | } 60 | }) 61 | ] 62 | } 63 | buildFormats.push(esConfig) 64 | } 65 | 66 | if (!argv.format || argv.format === 'umd') { 67 | const umdConfig = { 68 | ...baseConfig, 69 | external, 70 | output: { 71 | compact: true, 72 | file: 'dist/vue-virtual-table.umd.js', 73 | format: 'umd', 74 | name: 'VueVirtualTable', 75 | exports: 'named', 76 | globals 77 | }, 78 | plugins: [ 79 | ...baseConfig.plugins, 80 | terser({ 81 | output: { 82 | ecma: 6 83 | } 84 | }) 85 | ] 86 | } 87 | buildFormats.push(umdConfig) 88 | } 89 | 90 | if (!argv.format || argv.format === 'iife') { 91 | const unpkgConfig = { 92 | ...baseConfig, 93 | external, 94 | output: { 95 | compact: true, 96 | file: 'dist/vue-virtual-table.min.js', 97 | format: 'iife', 98 | name: 'VueVirtualTable', 99 | exports: 'named', 100 | globals 101 | }, 102 | plugins: [ 103 | ...baseConfig.plugins, 104 | terser({ 105 | output: { 106 | ecma: 5 107 | } 108 | }) 109 | ] 110 | } 111 | buildFormats.push(unpkgConfig) 112 | } 113 | 114 | // Export config 115 | export default buildFormats 116 | -------------------------------------------------------------------------------- /dist/vue-virtual-table.esm.css: -------------------------------------------------------------------------------- 1 | .resize-observer[data-v-b329ee4c]{position:absolute;top:0;left:0;z-index:-1;width:100%;height:100%;border:none;background-color:transparent;pointer-events:none;display:block;overflow:hidden;opacity:0}.resize-observer[data-v-b329ee4c] object{display:block;position:absolute;top:0;left:0;height:100%;width:100%;overflow:hidden;pointer-events:none;z-index:-1} -------------------------------------------------------------------------------- /example/complexTable.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 178 | 179 | 190 | -------------------------------------------------------------------------------- /example/mockData.js: -------------------------------------------------------------------------------- 1 | export default Array(200).fill({ 2 | id: '83nbd9282130jd03e', 3 | name: 4 | 'XXXXX-XXXXXX-YYYYYYY-NNNNNNN-MMMMMM-SSSSSSS-LLLLLL-SSSSSS-PPPPPP-OOOOOOO-JJJJJJJJ-LLLLLLLL-AAAAAAAA', 5 | status: 'Active', 6 | budget: 15, 7 | country: 'US', 8 | impressions: 1157, 9 | reach: 1101, 10 | clicks: 6, 11 | spend: 2.04, 12 | mobile_app_install: 1, 13 | ctr: 0.54, 14 | cpc: 0.34, 15 | cpm: 1.76, 16 | cpr: 2.04, 17 | cr: 0.17 18 | }) 19 | -------------------------------------------------------------------------------- /example/simpleTable.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 155 | 156 | 167 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-virtual-table", 3 | "version": "0.2.22", 4 | "description": "vue table component with virtual dom", 5 | "author": "https://github.com/waningflow", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/waningflow/vue-virtual-table.git" 10 | }, 11 | "keywords": [ 12 | "vue", 13 | "virtual", 14 | "component", 15 | "table", 16 | "smooth", 17 | "easy" 18 | ], 19 | "main": "dist/vue-virtual-table.umd.js", 20 | "module": "dist/vue-virtual-table.esm.js", 21 | "unpkg": "dist/vue-virtual-table.min.js", 22 | "browser": { 23 | "./sfc": "src/vue-virtual-table.vue" 24 | }, 25 | "files": [ 26 | "dist/*", 27 | "src/*" 28 | ], 29 | "scripts": { 30 | "ins": "yarn --registry https://registry.yarnpkg.com", 31 | "build": "cross-env NODE_ENV=production rollup --config build/rollup.config.js", 32 | "build:umd": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format umd", 33 | "build:es": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format es", 34 | "build:unpkg": "cross-env NODE_ENV=production rollup --config build/rollup.config.js --format iife" 35 | }, 36 | "publishConfig": { 37 | "registry": "https://registry.npmjs.org" 38 | }, 39 | "dependencies": { 40 | "v-tooltip": "2.0.2", 41 | "vue-observe-visibility": "^0.4.3", 42 | "vue-resize": "^0.4.5" 43 | }, 44 | "devDependencies": { 45 | "@babel/core": "^7.4.3", 46 | "@rollup/plugin-node-resolve": "^8.1.0", 47 | "cross-env": "^5.2.0", 48 | "minimist": "^1.2.0", 49 | "node-sass": "^4.11.0", 50 | "rollup": "^1.32.1", 51 | "rollup-plugin-babel": "^4.3.2", 52 | "rollup-plugin-buble": "^0.19.6", 53 | "rollup-plugin-commonjs": "^9.2.1", 54 | "rollup-plugin-css-only": "^2.1.0", 55 | "rollup-plugin-replace": "^2.1.0", 56 | "rollup-plugin-terser": "^4.0.4", 57 | "rollup-plugin-vue": "^4.7.2", 58 | "sass-loader": "^7.1.0", 59 | "vue": "^2.6.8", 60 | "vue-template-compiler": "^2.6.8" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # vue-virtual-table 2 | 3 | [![npm version](https://badge.fury.io/js/vue-virtual-table.svg)](https://badge.fury.io/js/vue-virtual-table) 4 | [![downloads](https://img.shields.io/npm/dm/vue-virtual-table.svg)](https://www.npmjs.com/package/vue-virtual-table) 5 | 6 | Vue table component with virtual dom and easy api. 7 | 8 | - Keep smooth when the data reachs thousands of rows or even more. 9 | - Easy to use with a simple config. 10 | 11 | [live demo](https://waningflow.com/vue-virtual-table/) 12 | 13 | # Installation 14 | 15 | ``` 16 | yarn add vue-virtual-table 17 | ``` 18 | 19 | or 20 | 21 | ``` 22 | npm install --save vue-virtual-table 23 | ``` 24 | 25 | # Usage 26 | 27 | A simplest example: 28 | 29 | ```html 30 | 33 | 34 | 46 | ``` 47 | 48 | Every item of the config refers to a column. When you don't set sepcific 'name' of the table column header, it will uses the 'prop' value as default. Or you can set the tableConfig like: 49 | 50 | ```js 51 | tableConfig: [{ prop: 'user', name: 'User Name' }, { prop: 'age', name: 'Age' }] 52 | ``` 53 | 54 | And if you want to search in the 'user' column, set the tableConfig like: 55 | 56 | ```js 57 | tableConfig: [{ prop: 'user', name: 'User Name', searchable: true }, { prop: 'age', name: 'Age' }] 58 | ``` 59 | 60 | For the 'age' column which is a set of number, you'd better use 'numberFilter' to filter numbers with "<", ">", "between" etc. 61 | 62 | ```js 63 | tableConfig: [{ prop: 'user', name: 'User Name', searchable: true }, { prop: 'age', name: 'Age', numberFilter: true }] 64 | ``` 65 | 66 | There are many convenient features hard to explain one by one. 67 | Here is a complex example and you can get more info in the tables below the example: 68 | 69 | ```html 70 | 87 | 88 | 127 | ``` 128 | 129 | ### Examples 130 | 131 | [Click here](https://github.com/waningflow/vue-virtual-table/blob/master/example/simpleTable.vue) to see the examples. You can clone this repo and use `vue serve example/xxx.vue` to preview. 132 | 133 | ### Table Attributes 134 | 135 | | name | type | description | required | default | 136 | | -------------- | ------- | --------------------------------------- | -------- | ------- | 137 | | data | Array | The array of data. Every item is a row. | Yes | | 138 | | config | Array | The config of table. | Yes | | 139 | | minWidth | Number | Set the minimum width of table. | No | 1200px | 140 | | height | Number | Set the height of table. | No | 300px | 141 | | itemHeight | Number | Set the height of row. | No | 42px | 142 | | bordered | Boolean | Whether table has vertical border. | No | false | 143 | | hoverHighlight | Boolean | Whether to hightlight current row. | No | true | 144 | | selectable | Boolean | Whether row is selectable. | No | false | 145 | | enableExport | Boolean | Whether to show export-to-table button | No | false | 146 | | language | String | Language from ['en', 'cn', 'ptBR'] | No | 'cn' | 147 | 148 | ### Table Events 149 | 150 | | name | parameters | description | 151 | | --------------- | ---------- | ----------------------------- | 152 | | changeSelection | rows | When the selected rows change | 153 | | click | row, $event| When row is clicked | 154 | | contextmenu | row, $event| When row is right-clicked | 155 | 156 | ### Table Config 157 | 158 | | param | type | description | required | default | 159 | | -------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------- | 160 | | prop | String | Property name | Yes | | 161 | | key | String | Key of element with data need in filter | No | same to the property name | 162 | | name | String | Display name | No | same to the property name | 163 | | width | Number | Column width | No | auto | 164 | | sortable | Boolean | Whether this column is sortable | No | false | 165 | | searchable | Boolean | Whether this column is searchable | No | false | 166 | | filterable | Boolean | Whether this column is filterable | No | false | 167 | | numberFilter | Boolean | If it's a column of number. You can use this. | No | false | 168 | | summary | String | summary type from ['COUNT', 'SUM'] or customize(eg. `${clicks}*100/${reach}` is calculated with the summary of other two columns). | No | | 169 | | prefix | String | Display before the value | No | | 170 | | suffix | String | Display after the value | No | | 171 | | alignItems | String | Same with flex. Control the content of a cell | No | center | 172 | | justifyContent | String | Defines how the browser distributes space between and around content items. | No | center | 173 | | isHidden | Boolean | Whether this column is hidden | No | false | 174 | | eTip | Array | Tool tip of a cell to display certain props (eg. `['name']` will display the value of 'name' prop in the tool tip ) | No | | 175 | | eTipWithProp | Boolean | Whether to show the prop name in the tool tip | No | | 176 | | eClass | Object | Attach class to the cell (eg. `{redColor: '${spend}>100'}` add the 'redColor' class to the cell whose 'spend' prop is greater than 100) | No | No | 177 | 178 | ### Special Props 179 | 180 | | name | description | 181 | | -------- | ------------------------------------ | 182 | | \_index | Show the index of row | 183 | | \_action | A slot to customize the content | 184 | | \_expand | A slot to customize a popover window | 185 | 186 | 187 | ### Custom slot with formatted data and filter 188 | 189 | ```javascript 190 | {prop: '_action', key: 'price', name: 'Price', sortable: true, numberFilter: true, prefix: "R$ ", actionName: 'price-info'} 191 | ``` 192 | 193 | ```javascript 194 | {id: v.id, name: obj.name, price: obj.price, price_formatted: currency(obj.price)} 195 | ``` 196 | 197 | ```html 198 | 201 | ``` -------------------------------------------------------------------------------- /src/components/base-button.vue: -------------------------------------------------------------------------------- 1 | 6 | 20 | 94 | -------------------------------------------------------------------------------- /src/components/base-checkgroup.vue: -------------------------------------------------------------------------------- 1 | 12 | 61 | 108 | -------------------------------------------------------------------------------- /src/components/base-icon.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 55 | 56 | 63 | -------------------------------------------------------------------------------- /src/components/base-input.vue: -------------------------------------------------------------------------------- 1 | 4 | 42 | 62 | -------------------------------------------------------------------------------- /src/components/base-popover.vue: -------------------------------------------------------------------------------- 1 | 20 | 146 | 205 | -------------------------------------------------------------------------------- /src/components/base-select.vue: -------------------------------------------------------------------------------- 1 | 18 | 76 | 134 | -------------------------------------------------------------------------------- /src/components/base-tooltip.vue: -------------------------------------------------------------------------------- 1 | 19 | 117 | 185 | -------------------------------------------------------------------------------- /src/components/icons/arrowCarrotDown.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/icons/arrowCarrotRight.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/icons/boxChecked.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/icons/boxEmpty.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/icons/closeAlt2.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/icons/cloudDownloadAlt.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/icons/documentsAlt.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/components/virtual-scroller.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 454 | 455 | 477 | -------------------------------------------------------------------------------- /src/entry.js: -------------------------------------------------------------------------------- 1 | // Import vue component 2 | import component from './vue-virtual-table.vue'; 3 | 4 | // install function executed by Vue.use() 5 | function install(Vue) { 6 | if (install.installed) return; 7 | install.installed = true; 8 | Vue.component('VueVirtualTable', component); 9 | } 10 | 11 | // Create module definition for Vue.use() 12 | const plugin = { 13 | install, 14 | }; 15 | 16 | // To auto-install when vue is found 17 | /* global window global */ 18 | let GlobalVue = null; 19 | if (typeof window !== 'undefined') { 20 | GlobalVue = window.Vue; 21 | } else if (typeof global !== 'undefined') { 22 | GlobalVue = global.Vue; 23 | } 24 | if (GlobalVue) { 25 | GlobalVue.use(plugin); 26 | } 27 | 28 | // Inject install function into component - allows component 29 | // to be registered via Vue.use() as well as Vue.component() 30 | component.install = install; 31 | 32 | // Export component by default 33 | export default component; 34 | 35 | // It's possible to expose named exports when writing components that can 36 | // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; 37 | // export const RollupDemoDirective = component; 38 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | export function _uuid() { 2 | var d = Date.now() 3 | if ( 4 | typeof performance !== 'undefined' && 5 | typeof performance.now === 'function' 6 | ) { 7 | d += performance.now() //use high-precision timer if available 8 | } 9 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 10 | var r = ((d + Math.random() * 16) % 16) | 0 11 | d = Math.floor(d / 16) 12 | return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16) 13 | }) 14 | } 15 | 16 | export function exportCsv(data, columns, title) { 17 | let csv = JSONtoCSV(data, columns) 18 | 19 | let createAndDownloadFile = (fileName, content) => { 20 | let aTag = document.createElement('a') 21 | let blob = new Blob(['\uFEFF', content]) 22 | aTag.download = fileName 23 | aTag.href = URL.createObjectURL(blob) 24 | aTag.click() 25 | URL.revokeObjectURL(blob) 26 | } 27 | createAndDownloadFile(title, csv) 28 | } 29 | 30 | export function JSONtoCSV(arr, columns, delimiter = ',') { 31 | return [ 32 | ...arr.map(obj => 33 | columns.reduce( 34 | (acc, key) => 35 | `${acc}${!acc.length ? '' : delimiter}"${!obj[key] ? '' : obj[key]}"`, 36 | '' 37 | ) 38 | ) 39 | ].join('\n') 40 | } 41 | 42 | export function deepCopy(obj) { 43 | let obj_cp = JSON.parse(JSON.stringify(obj)) 44 | return obj_cp 45 | } 46 | 47 | export function debounce(fun, t = 0) { 48 | let st 49 | if (typeof fun !== 'function') { 50 | throw new TypeError('Not a function') 51 | } 52 | return function() { 53 | if (st) { 54 | clearTimeout(st) 55 | } 56 | st = setTimeout(() => { 57 | fun.apply(this, arguments) 58 | }, t) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/vue-virtual-table.vue: -------------------------------------------------------------------------------- 1 | 588 | 1430 | 1726 | --------------------------------------------------------------------------------