├── static └── .gitkeep ├── npm-debug.log.3836475044 ├── .eslintignore ├── config ├── prod.env.js ├── dev.env.js └── index.js ├── .gitignore ├── src ├── assets │ └── logo.png ├── helpers │ └── event-bus.js ├── main.js ├── mixins │ ├── Requests.js │ ├── Pagination.js │ └── Validator.js ├── App.vue └── components │ └── VueEditortable.vue ├── .editorconfig ├── .postcssrc.js ├── index.html ├── .babelrc ├── .eslintrc.js ├── LICENSE ├── package.json └── README.md /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /npm-debug.log.3836475044: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | json/ 6 | notes.txt 7 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/el-jacko/vue-editortable/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/helpers/event-bus.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | const VetEventBus = new Vue(); 4 | 5 | export default VetEventBus; 6 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserlist" field in package.json 6 | "autoprefixer": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-editortable 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { "modules": false }], 4 | "stage-2" 5 | ], 6 | "plugins": ["transform-runtime"], 7 | "comments": false, 8 | "env": { 9 | "test": { 10 | "presets": ["env", "stage-2"], 11 | "plugins": [ "istanbul" ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue'; 4 | import Vuetify from 'vuetify'; 5 | import App from './App'; 6 | 7 | Vue.use(Vuetify); 8 | 9 | /* eslint-disable no-new */ 10 | new Vue({ 11 | el: '#app', 12 | template: '', 13 | components: { App }, 14 | }); 15 | -------------------------------------------------------------------------------- /src/mixins/Requests.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export default { 4 | methods: { 5 | getData(url, cb, errorCb) { 6 | axios.get(url).then(cb).catch(errorCb); 7 | }, 8 | postData(url, data, cb, errorCb) { 9 | axios.post(url, data).then(cb).catch(errorCb); 10 | }, 11 | putData(url, data, cb, errorCb) { 12 | axios.put(url, data).then(cb).catch(errorCb); 13 | }, 14 | patchData(url, data, cb, errorCb) { 15 | axios.patch(url, data).then(cb).catch(errorCb); 16 | }, 17 | deleteData(url, cb, errorCb) { 18 | axios.delete(url).then(cb).catch(errorCb); 19 | }, 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // http://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | sourceType: 'module' 8 | }, 9 | env: { 10 | browser: true, 11 | }, 12 | extends: 'airbnb-base', 13 | // required to lint *.vue files 14 | plugins: [ 15 | 'html' 16 | ], 17 | // check if imports actually resolve 18 | 'settings': { 19 | 'import/resolver': { 20 | 'webpack': { 21 | 'config': 'build/webpack.base.conf.js' 22 | } 23 | } 24 | }, 25 | // add your custom rules here 26 | 'rules': { 27 | 'no-console': 0, 28 | // don't require .vue extension when importing 29 | 'import/extensions': ['error', 'always', { 30 | 'js': 'never', 31 | 'vue': 'never' 32 | }], 33 | // allow optionalDependencies 34 | 'import/no-extraneous-dependencies': ['error', { 35 | 'optionalDependencies': ['test/unit/index.js'] 36 | }], 37 | // allow debugger during development 38 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jacques Mannon 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 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/', 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'], 18 | // Run the build command with an extra argument to 19 | // View the bundle analyzer report after build finishes: 20 | // `npm run build --report` 21 | // Set to `true` or `false` to always turn it on or off 22 | bundleAnalyzerReport: process.env.npm_config_report 23 | }, 24 | dev: { 25 | env: require('./dev.env'), 26 | port: 8080, 27 | autoOpenBrowser: true, 28 | assetsSubDirectory: 'static', 29 | assetsPublicPath: '/', 30 | proxyTable: {}, 31 | // CSS Sourcemaps off by default because relative paths are "buggy" 32 | // with this option, according to the CSS-Loader README 33 | // (https://github.com/webpack/css-loader#sourcemaps) 34 | // In our experience, they generally work as expected, 35 | // just be aware of this issue when enabling this option. 36 | cssSourceMap: false 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/mixins/Pagination.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | currentPage: 0, 5 | maxNumber: 0, 6 | resultCount: 0, 7 | }; 8 | }, 9 | methods: { 10 | setPage(pageNumber) { 11 | this.currentPage = pageNumber - 1; 12 | }, 13 | resetCurrentPage() { 14 | this.currentPage = 0; 15 | }, 16 | showPrev() { 17 | if (this.isStartPage) return; 18 | this.currentPage -= 1; 19 | }, 20 | showNext() { 21 | if (this.isEndPage) return; 22 | this.currentPage += 1; 23 | }, 24 | paginate(data) { 25 | this.resultCount = data.length; 26 | this.maxNumber = this.totalPages - 1; 27 | const index = this.currentPage * this.opt.pagination.itemsPerPage; 28 | return data.slice(index, index + this.opt.pagination.itemsPerPage); 29 | }, 30 | isInPaginationRange(pageNumber) { 31 | let show; 32 | if ( 33 | this.maxNumber < 7 || 34 | pageNumber === 1 || 35 | pageNumber >= this.maxNumber + 1 || 36 | (pageNumber <= 5 && this.currentPage < 3) || 37 | (pageNumber >= this.maxNumber - 3 && this.currentPage > this.maxNumber - 4) || 38 | (pageNumber >= this.currentPage && pageNumber <= (this.currentPage + 2)) 39 | ) { 40 | show = true; 41 | } 42 | return show; 43 | }, 44 | }, 45 | computed: { 46 | totalPages() { 47 | return Math.ceil(this.resultCount / this.opt.pagination.itemsPerPage); 48 | }, 49 | isStartPage() { 50 | return (this.currentPage === 0); 51 | }, 52 | isEndPage() { 53 | return (this.currentPage + 1) * 54 | this.opt.pagination.itemsPerPage >= this.resultCount; 55 | }, 56 | }, 57 | }; 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-editortable", 3 | "version": "1.1.5", 4 | "description": "A Vue.js editable table component", 5 | "author": "Jacques Mannon", 6 | "private": false, 7 | "main": "./src/components/VueEditortable.vue", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/el-jacko/vue-editortable.git" 11 | }, 12 | "license": "MIT", 13 | "scripts": { 14 | "dev": "node build/dev-server.js", 15 | "build": "node build/build.js", 16 | "lint": "eslint --ext .js,.vue src" 17 | }, 18 | "dependencies": { 19 | "axios": "^0.16.1", 20 | "validator": "^7.0.0", 21 | "vue": "^2.2.2", 22 | "vue-awesome": "^2.1.0", 23 | "vue-resource": "^1.2.1" 24 | }, 25 | "devDependencies": { 26 | "autoprefixer": "^6.7.2", 27 | "babel-core": "^6.22.1", 28 | "babel-eslint": "^7.1.1", 29 | "babel-loader": "^6.2.10", 30 | "babel-plugin-transform-runtime": "^6.22.0", 31 | "babel-preset-env": "^1.2.1", 32 | "babel-preset-stage-2": "^6.22.0", 33 | "babel-register": "^6.22.0", 34 | "chalk": "^1.1.3", 35 | "connect-history-api-fallback": "^1.3.0", 36 | "copy-webpack-plugin": "^4.0.1", 37 | "css-loader": "^0.26.1", 38 | "eslint": "^3.14.1", 39 | "eslint-config-airbnb-base": "^11.0.1", 40 | "eslint-friendly-formatter": "^2.0.7", 41 | "eslint-import-resolver-webpack": "^0.8.1", 42 | "eslint-loader": "^1.6.1", 43 | "eslint-plugin-html": "^2.0.0", 44 | "eslint-plugin-import": "^2.2.0", 45 | "eventsource-polyfill": "^0.9.6", 46 | "express": "^4.14.1", 47 | "extract-text-webpack-plugin": "^2.0.0", 48 | "file-loader": "^0.10.0", 49 | "friendly-errors-webpack-plugin": "^1.1.3", 50 | "function-bind": "^1.1.0", 51 | "html-webpack-plugin": "^2.28.0", 52 | "http-proxy-middleware": "^0.17.3", 53 | "opn": "^4.0.2", 54 | "optimize-css-assets-webpack-plugin": "^1.3.0", 55 | "ora": "^1.1.0", 56 | "rimraf": "^2.6.0", 57 | "semver": "^5.3.0", 58 | "url-loader": "^0.5.7", 59 | "vue-loader": "^11.1.4", 60 | "vue-style-loader": "^2.0.0", 61 | "vue-template-compiler": "^2.2.1", 62 | "vuetify": "^0.12.7", 63 | "webpack": "^2.2.1", 64 | "webpack-bundle-analyzer": "^2.2.1", 65 | "webpack-dev-middleware": "^1.10.0", 66 | "webpack-hot-middleware": "^2.16.1", 67 | "webpack-merge": "^2.6.1" 68 | }, 69 | "engines": { 70 | "node": ">= 4.0.0", 71 | "npm": ">= 3.0.0" 72 | }, 73 | "browserslist": [ 74 | "> 1%", 75 | "last 2 versions", 76 | "not ie <= 8" 77 | ] 78 | } 79 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 115 | 116 | 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Important 2 | Due to lack of time this project is on hold. 3 | 4 | ## Support on Beerpay 5 | Hey dude! Help me out for a couple of :beers:! 6 | 7 | [![Beerpay](https://beerpay.io/el-jacko/vue-editortable/badge.svg?style=beer-square)](https://beerpay.io/el-jacko/vue-editortable) [![Beerpay](https://beerpay.io/el-jacko/vue-editortable/make-wish.svg?style=flat-square)](https://beerpay.io/el-jacko/vue-editortable?focus=wish) 8 | 9 | # vue-editortable 10 | 11 | > A Vue.js editable table component 12 | > * Load/Save Data from/to a database 13 | > * Create, Edit, Save, and Delete Data 14 | > * Show/Hide columns 15 | > * Keyboard Navigation & Shortcuts 16 | > * SWIPE Design for wide tables 17 | > * Simple Responsive Design with data attributes 18 | > * Configurable 19 | > * Multisorting 20 | > * Searchfilter 21 | > * dynamic Pagination 22 | > * Validation 23 | 24 | --- 25 | ## Demo 26 | * Demo Pages are temporarely offline 27 | 28 | --- 29 | ## Dependencies 30 | 31 | Only Vue.js, no other frameworks/libraries 32 | * Vue.js >= 2.0 (tested with 2.2.2) 33 | * Vue-awesome Icons 34 | * Axios 35 | * validator 36 | 37 | --- 38 | ## Browser compatibility 39 | 40 | Only tested browser by now is Chrome. 41 | Feel free to test and share your results. 42 | 43 | --- 44 | ## Installation 45 | 46 | #### If you use Webpack/Browserify 47 | 48 | ### npm 49 | ``` sh 50 | npm install --save vue-editortable 51 | ``` 52 | Import globally in an App: 53 | 54 | ``` javascript 55 | import VueEditortable from "vue-editortable" 56 | Vue.component('vue-editortable', VueEditortable) 57 | ``` 58 | Import locally in a component: 59 | 60 | ``` javascript 61 | import VueEditortable from "vue-editortable" 62 | // ... 63 | components: { 64 | VueEditortable, 65 | } 66 | // ... 67 | ``` 68 | 69 | ## Usage 70 | 71 | ``` html 72 | 73 | ``` 74 | ``` javascript 75 | // ... 76 | data() { 77 | return { 78 | columns: [ 79 | { 80 | title: 'Id', 81 | name: 'id', 82 | editable: false, 83 | }, 84 | { 85 | title: 'Firstname', 86 | name: 'firstname', 87 | editable: true, 88 | }, 89 | { 90 | title: 'Lastname', 91 | name: 'lastname', 92 | editable: true, 93 | }, 94 | { 95 | title: 'Email', 96 | name: 'email', 97 | editable: true, 98 | }, 99 | ], 100 | options: { 101 | showSearchFilter: true, 102 | requests: { 103 | allUrl: 'http://api.dev/api/users', 104 | }, 105 | }, 106 | }; 107 | }, 108 | // ... 109 | ``` 110 | 111 | --- 112 | ## Documentation 113 | 114 | You will find a complete documentation [here](https://github.com/el-jacko/vue-editortable/wiki). 115 | 116 | --- 117 | ## Build Setup 118 | 119 | ``` sh 120 | # install dependencies 121 | npm install 122 | 123 | # serve with hot reload at localhost:8080 124 | npm run dev 125 | 126 | # build for production with minification 127 | npm run build 128 | 129 | # build for production and view the bundle analyzer report 130 | npm run build --report 131 | ``` 132 | 133 | For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 134 | 135 | --- 136 | ## TODOs 137 | 138 | Some major TODOs: 139 | * error messages 140 | * implementing Datatypes 141 | * filter per columns 142 | * optional Modal before deleting 143 | * compatibility with CSS Frameworks (Bootstrap, Semantic, Foundation) 144 | * search & replace 145 | * edit multiple fields 146 | * statistics 147 | 148 | --- 149 | ## Contributions 150 | 151 | All contributions are welcome! 152 | 153 | --- 154 | ## License 155 | 156 | [MIT](http://opensource.org/licenses/MIT) 157 | -------------------------------------------------------------------------------- /src/mixins/Validator.js: -------------------------------------------------------------------------------- 1 | import validator from 'validator'; 2 | 3 | export default { 4 | methods: { 5 | validate(rules, messages, value) { 6 | const res = []; 7 | rules.forEach((rule, key) => { 8 | if (!this.makeValidation(rule, value)) { 9 | res.push(messages[key]); 10 | } 11 | }); 12 | return res; 13 | }, 14 | makeValidation(str, value) { 15 | let res = false; 16 | const arr = this.checkIfParameter(str); 17 | const l = arr.length; 18 | const string = arr[0]; 19 | let paraOne; 20 | let paraTwo; 21 | if (l > 0) paraOne = arr[1]; 22 | if (l > 1) paraTwo = arr[2]; 23 | switch (string) { 24 | case 'required': 25 | res = !validator.isEmpty(value); 26 | break; 27 | case 'email': 28 | res = validator.isEmail(value); 29 | break; 30 | case 'contains': 31 | res = validator.contains(value, paraOne); 32 | break; 33 | case 'int': 34 | res = validator.isInt(value); 35 | break; 36 | case 'float': 37 | res = validator.isFloat(value); 38 | break; 39 | case 'min': 40 | if (validator.isInt(value)) { 41 | res = validator.isInt(value, { min: parseInt(paraOne, 10) }); 42 | } else if (validator.isFloat(value)) { 43 | res = validator.isFloat(value, { min: parseFloat(paraOne, 10) }); 44 | } 45 | break; 46 | case 'max': 47 | if (validator.isInt(value)) { 48 | res = validator.isInt(value, { max: parseInt(paraOne, 10) }); 49 | } else if (validator.isFloat(value)) { 50 | res = validator.isFloat(value, { max: parseFloat(paraOne, 10) }); 51 | } 52 | break; 53 | case 'between': 54 | if (validator.isInt(value)) { 55 | res = validator.isInt(value, { min: parseInt(paraOne, 10), 56 | max: parseInt(paraTwo, 10) }); 57 | } else if (validator.isFloat(value)) { 58 | res = validator.isFloat(value, { min: parseFloat(paraOne, 10), 59 | max: parseFloat(paraTwo, 10) }); 60 | } 61 | break; 62 | case 'gt': 63 | if (validator.isInt(value)) { 64 | res = validator.isInt(value, { gt: parseInt(paraOne, 10) }); 65 | } else if (validator.isFloat(value)) { 66 | res = validator.isFloat(value, { gt: parseFloat(paraOne, 10) }); 67 | } 68 | break; 69 | case 'lt': 70 | if (validator.isInt(value)) { 71 | res = validator.isInt(value, { lt: parseInt(paraOne, 10) }); 72 | } else if (validator.isFloat(value)) { 73 | res = validator.isFloat(value, { lt: parseFloat(paraOne, 10) }); 74 | } 75 | break; 76 | case 'alpha': 77 | res = validator.isAlpha(value, paraOne); 78 | break; 79 | case 'alphaNum': 80 | res = validator.isAlphanumeric(value, paraOne); 81 | break; 82 | case 'bool': 83 | res = validator.isBoolean(value); 84 | break; 85 | case 'url': 86 | res = validator.isURL(value); 87 | break; 88 | case 'fqdn': 89 | res = validator.isFQDN(value); 90 | break; 91 | case 'hexColor': 92 | res = validator.isHexColor(value); 93 | break; 94 | case 'ip4': 95 | res = validator.isIP(value, 4); 96 | break; 97 | case 'ip6': 98 | res = validator.isIP(value, 6); 99 | break; 100 | default: 101 | res = false; 102 | } 103 | return res; 104 | }, 105 | checkIfParameter(str) { 106 | let i = 0; 107 | let res; 108 | i = str.indexOf(':'); 109 | if (i > -1) { 110 | res = str.split(':'); 111 | } else { 112 | res = [str]; 113 | } 114 | return res; 115 | }, 116 | }, 117 | }; 118 | -------------------------------------------------------------------------------- /src/components/VueEditortable.vue: -------------------------------------------------------------------------------- 1 | 210 | 211 | 1256 | 1257 | 1595 | 1596 | --------------------------------------------------------------------------------