├── .eslintignore ├── .browserslistrc ├── .firebaserc ├── .npmrc ├── postcss.config.js ├── babel.config.js ├── tests └── unit │ ├── .eslintrc.js │ ├── TableServer.spec.js │ ├── Pagination.spec.js │ └── Client.spec.js ├── dist ├── demo.html ├── EnaTableClient │ ├── demo.html │ └── EnaTableClient.css ├── EnaTableServer │ ├── demo.html │ ├── EnaTableServer.css │ └── EnaTableServer.umd.min.js └── EnaTable.css ├── proj.code-workspace ├── proj.sublime-project ├── src ├── main.js ├── components │ ├── mixins │ │ ├── filters.js │ │ ├── ActionsCell.vue │ │ ├── methods.js │ │ ├── default-props.js │ │ └── Pagination.vue │ ├── entry.js │ ├── Server.vue │ └── Client.vue └── App.vue ├── .editorconfig ├── .gitignore ├── firebase.json ├── public └── index.html ├── functions └── countries.js ├── LICENSE ├── vue.config.js ├── .circleci └── config.yml ├── CONTRIBUTING.md ├── .eslintrc.js ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | public/* 3 | dist/* 4 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 10 4 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "vue-myena-table" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # install as dev by default 2 | save-dev = true 3 | registry=https://registry.npmjs.org 4 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | ], 5 | }; 6 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | mocha: true, 4 | }, 5 | rules: { 6 | 'no-unused-expressions': 'off', 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /dist/demo.html: -------------------------------------------------------------------------------- 1 | 2 | EnaTable demo 3 | 4 | 5 | 6 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /proj.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | }, 6 | { 7 | "path": "C:\\Users\\andyg\\hobby\\myena-advanced-select" 8 | } 9 | ], 10 | "settings": { 11 | "npm-scripts.showStartNotification": false 12 | } 13 | } -------------------------------------------------------------------------------- /proj.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": "." 6 | } 7 | ], 8 | "settings": { 9 | "SublimeLinter.linters.eslint.disable": false, 10 | "SublimeLinter.linters.eslint.selector": "text.html.vue, source.js - meta.attribute-with-value" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import 'bootstrap/dist/css/bootstrap.css'; 4 | import 'font-awesome/css/font-awesome.min.css'; 5 | 6 | import App from './App.vue'; 7 | 8 | Vue.config.productionTip = false; 9 | 10 | new Vue({ 11 | render: h => h(App), 12 | }).$mount('#app'); 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | indent_style = tab 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | *.sw* 21 | 22 | /proj.sublime-workspace 23 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "example", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | }, 16 | "functions": { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/components/mixins/filters.js: -------------------------------------------------------------------------------- 1 | export default { 2 | filters: { 3 | heading(key, headings) { 4 | if (undefined !== headings[key]) { 5 | return headings[key]; 6 | } 7 | const firstUpper = w => w.charAt(0).toUpperCase() + w.slice(1); 8 | return key.split('_').map(firstUpper).join(' '); 9 | }, 10 | format: (str, ...args) => [...args].reduce((s, a) => s.replace(/%s/, a), str), 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /dist/EnaTableClient/demo.html: -------------------------------------------------------------------------------- 1 | 2 | EnaTableClient demo 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 | 13 | 20 | -------------------------------------------------------------------------------- /dist/EnaTableServer/demo.html: -------------------------------------------------------------------------------- 1 | 2 | EnaTableServer demo 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 | 13 | 20 | -------------------------------------------------------------------------------- /src/components/entry.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Server from './Server.vue'; 3 | import Client from './Client.vue'; 4 | 5 | const components = { 6 | Server, 7 | Client, 8 | }; 9 | 10 | // global register components 11 | function register() { 12 | Object.keys(components).forEach(name => Vue.component( 13 | `${LIBNAME}${name}`, 14 | components[name], 15 | { 16 | name: `${LIBNAME}${name}`, 17 | } 18 | )); 19 | } 20 | 21 | export { 22 | Server, 23 | Client, 24 | }; 25 | 26 | export default register; 27 | -------------------------------------------------------------------------------- /tests/unit/TableServer.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { shallowMount } from '@vue/test-utils'; 3 | import Server from '@/components/Server.vue'; 4 | 5 | describe('Server.vue', () => { 6 | it('renders a table with 1 row', () => { 7 | const wrapper = shallowMount(Server, { 8 | propsData: { 9 | }, 10 | methods: { 11 | fetch() { 12 | return { 13 | data: [{ first_name: '1', id: 1 }], 14 | total: 1, 15 | }; 16 | }, 17 | }, 18 | }); 19 | expect(wrapper.contains('table tbody tr')).to.be.true; 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%=LIBNAME%> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /functions/countries.js: -------------------------------------------------------------------------------- 1 | const countries = require('./countries.json'); 2 | 3 | const collator = new Intl.Collator('en', { sensitivity: 'base', numeric: true }); 4 | 5 | exports.handler = (event, context, callback) => { 6 | // your server-side functionality 7 | const { page = 1, per_page: perPage = 10, sort_by: sortBy = 'name', sort_dir: sortDir = 1, filter = {} } = event.queryStringParameters; 8 | // filter 9 | const filtered = countries.filter((c) => !filter.name 10 | || (filter.name && c.name.toLowerCase().indexOf(filter.name.toLowerCase()) !== -1)); 11 | // sort 12 | filtered.sort((a, b) => sortDir * collator.compare(a[sortBy], b[sortBy])); 13 | // slice & send 14 | const start = (page - 1) * perPage; 15 | const end = start + parseInt(perPage, 10); 16 | callback(null, { 17 | statusCode: 200, 18 | body: JSON.stringify({ 19 | list: filtered.slice(start, end), 20 | total: filtered.length, 21 | }), 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /src/components/mixins/ActionsCell.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Education Networks of America 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. -------------------------------------------------------------------------------- /src/components/mixins/methods.js: -------------------------------------------------------------------------------- 1 | export default { 2 | computed: { 3 | allColumns() { 4 | const allColumns = this.columns.slice(); 5 | if (!this.columns.includes('actions') && this.opts.detailsRow) { 6 | allColumns.push('actions'); 7 | } 8 | return allColumns; 9 | }, 10 | computedRowClasses() { 11 | return Object.values(this.pageData).map((rowGroup) => { 12 | return rowGroup.map((row) => { 13 | const classes = {}; 14 | Object.keys(this.opts.rowClasses).forEach((prop) => { 15 | if (row[prop]) { 16 | classes[this.opts.rowClasses[prop]] = true; 17 | } 18 | }); 19 | return classes; 20 | }); 21 | }); 22 | }, 23 | colspan() { 24 | return this.allColumns.length; 25 | }, 26 | }, 27 | methods: { 28 | isShown(key) { 29 | return typeof this.shown[key] === 'undefined' || this.shown[key]; 30 | }, 31 | toggleRow(id) { 32 | this.expandedRows[id] = !this.expandedRows[id]; 33 | this.expandedRows = Object.assign({}, this.expandedRows); 34 | this.$emit('toggleRow', id, this.expandedRows); 35 | }, 36 | isRowExpanded(id) { 37 | return this.expandedRows[id]; 38 | }, 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const pkg = require('./package.json'); 3 | 4 | module.exports = { 5 | css: { 6 | loaderOptions: { 7 | css: { 8 | // localIdentName: `${pkg.libname}[name]_[local]_[hash:base64:5]`, 9 | }, 10 | }, 11 | }, 12 | lintOnSave: false, 13 | configureWebpack: (/* config */) => { 14 | const customConfig = { 15 | plugins: [ 16 | new webpack.ProvidePlugin({ 17 | $: 'jquery', 18 | jquery: 'jquery', 19 | jQuery: 'jquery', 20 | 'window.jquery': 'jquery', 21 | 'window.jQuery': 'jquery', 22 | 'window.$': 'jquery', 23 | }), 24 | new webpack.DefinePlugin({ 25 | LIBNAME: JSON.stringify(pkg.libname), 26 | }), 27 | ], 28 | }; 29 | // config for lib 30 | if (process.env.VUE_CLI_BUILD_TARGET === 'lib') { 31 | // set external modules so they won't get bundled with the lib 32 | customConfig.externals = [ 33 | 'bootbox', 34 | 'bootstrap', 35 | 'font-awesome', 36 | 'jquery', 37 | 'vue', 38 | ]; 39 | } 40 | // config parameter can be mutated 41 | // or a new object (to be used with webpack-merge) returned 42 | return customConfig; 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | install: 8 | docker: 9 | - image: circleci/node:10.13.0 10 | 11 | working_directory: ~/repo 12 | 13 | steps: 14 | - checkout 15 | 16 | # Download and cache dependencies 17 | - restore_cache: 18 | keys: 19 | - v1-dependencies-{{ checksum "package-lock.json" }} 20 | # fallback to using the latest cache if no exact match is found 21 | - v1-dependencies- 22 | 23 | - run: npm ci 24 | 25 | - save_cache: 26 | paths: 27 | - node_modules 28 | key: v1-dependencies-{{ checksum "package-lock.json" }} 29 | 30 | - save_cache: 31 | key: v1-repo-{{ .Environment.CIRCLE_SHA1 }} 32 | paths: 33 | - ~/repo 34 | test: 35 | docker: 36 | - image: circleci/node:10.13.0 37 | 38 | working_directory: ~/repo 39 | 40 | steps: 41 | - restore_cache: 42 | key: v1-repo-{{ .Environment.CIRCLE_SHA1 }} 43 | # run tests! 44 | - run: npm run test:unit 45 | 46 | workflows: 47 | version: 2 48 | 49 | install-and-test: 50 | jobs: 51 | - install 52 | - test: 53 | requires: 54 | - install 55 | -------------------------------------------------------------------------------- /dist/EnaTableClient/EnaTableClient.css: -------------------------------------------------------------------------------- 1 | .Pagination_pagination_1xsiO{margin:0}.Pagination_pagination_1xsiO>li>span,.Pagination_pagination_1xsiO>li>span:focus,.Pagination_pagination_1xsiO>li>span:hover{border-top:1px solid transparent;border-bottom:1px solid transparent}.Pagination_pagination_1xsiO>li>span:focus,.Pagination_pagination_1xsiO>li>span:hover{background-color:#fff}.Pagination_info_2cUbo{float:right}.Pagination_info_2cUbo .Pagination_perPageSelector_CN8cp{margin-left:10px;margin-right:10px}.Client_table_30YmA{border-bottom:none}.Client_table_30YmA>thead:first-child>tr:first-child>th{border-top:1px solid #333;border-bottom:1px solid #333}.Client_table_30YmA>thead:first-child>tr:first-child>th a{cursor:default;color:inherit;text-decoration:none;display:block}.Client_table_30YmA>thead:first-child>tr:first-child>th.Client_sortable_pJAaM a{cursor:pointer}.Client_table_30YmA>thead:first-child>tr:first-child>th.Client_sortable_pJAaM a:focus{text-decoration:underline}.Client_table_30YmA>thead:first-child>tr:first-child>th.Client_sortable_pJAaM i{margin-top:5px;margin-left:5px}.Client_table_30YmA tbody>tr:first-child>th{background-color:#f2f2f2}.Client_table_30YmA tbody>tr:first-child>th a+span{margin-left:5px}.Client_table_30YmA tbody>tr:first-child>th a{color:#333;font-size:16px;display:inline-block;width:20px;text-align:center}.Client_table_30YmA .Client_checkbox_2krmy{margin:0}.Client_table_30YmA .Client_checkbox_2krmy label{min-height:18px}td.Client_selectable_3NYta:hover{cursor:pointer} -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | :+1::tada: Thanks for taking the time to contribute! :tada::+1: 4 | 5 | ## Development guidelines 6 | 7 | * Checkout the repo, install dependencies and serve the example code with `npm run serve` 8 | 9 | * You can work on the components code under `src/components`. 10 | 11 | * You can work on the example code that uses the components is in `App.vue`. This code is what is served on the Demo page 12 | 13 | * Make sure to have ESLint enabled and fix all lint errors before pushing the code. 14 | 15 | #### **Did you find a bug or have a suggestion for a feature?** 16 | 17 | * **Ensure the bug/feature was not already reported** by searching on GitHub under [Issues](https://github.com/myENA/vue-table/issues). 18 | 19 | * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/myENA/vue-table/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, optionally a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. 20 | 21 | #### **Did you write a patch that fixes a bug or adds a feature?** 22 | 23 | * Ensure the existing unit tests pass, with `npm run test:unit`. Write new or adapt the existing ones to cover the change. 24 | 25 | * Open a new GitHub pull request with the patch. 26 | 27 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 28 | 29 | * Make sure to adapt the existing examples, as applicable. 30 | 31 | -------------------------------------------------------------------------------- /dist/EnaTableServer/EnaTableServer.css: -------------------------------------------------------------------------------- 1 | .Pagination_pagination_1xsiO{margin:0}.Pagination_pagination_1xsiO>li>span,.Pagination_pagination_1xsiO>li>span:focus,.Pagination_pagination_1xsiO>li>span:hover{border-top:1px solid transparent;border-bottom:1px solid transparent}.Pagination_pagination_1xsiO>li>span:focus,.Pagination_pagination_1xsiO>li>span:hover{background-color:#fff}.Pagination_info_2cUbo{float:right}.Pagination_info_2cUbo .Pagination_perPageSelector_CN8cp{margin-left:10px;margin-right:10px}.Server_table_1-Jbh{border-bottom:none}.Server_table_1-Jbh>thead:first-child>tr:first-child>th{border-top:1px solid #333;border-bottom:1px solid #333}.Server_table_1-Jbh>thead:first-child>tr:first-child>th a{cursor:default;color:inherit;text-decoration:none;display:block}.Server_table_1-Jbh>thead:first-child>tr:first-child>th.Server_sortable_351RP a{cursor:pointer}.Server_table_1-Jbh>thead:first-child>tr:first-child>th.Server_sortable_351RP a:focus{text-decoration:underline}.Server_table_1-Jbh>thead:first-child>tr:first-child>th.Server_sortable_351RP i{margin-top:5px;margin-left:5px}.Server_table_1-Jbh tbody>tr:first-child>th{background-color:#f2f2f2}.Server_table_1-Jbh tbody>tr:first-child>th a+span{margin-left:5px}.Server_table_1-Jbh tbody>tr:first-child>th a{color:#333;font-size:16px;display:inline-block;width:20px;text-align:center}.Server_table_1-Jbh .Server_checkbox_1lyNb{margin:0}.Server_table_1-Jbh .Server_checkbox_1lyNb label{min-height:18px}.Server_pagination_3erRE{margin:0}.Server_info_2gbZL{float:right}.Server_info_2gbZL .Server_perPageSelector_3GVN9{margin-left:10px;margin-right:10px} -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:vue/essential', 8 | '@vue/airbnb', 9 | ], 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 13 | 'comma-dangle': ['error', { 14 | arrays: 'always-multiline', 15 | objects: 'always-multiline', 16 | imports: 'always-multiline', 17 | exports: 'always-multiline', 18 | functions: 'never', 19 | }], 20 | 'import/no-extraneous-dependencies': ['error', { 21 | devDependencies: true, 22 | optionalDependencies: false, 23 | peerDependencies: false, 24 | }], 25 | 'linebreak-style': ['off'], 26 | 'no-mixed-operators': ['error', { 27 | groups: [ 28 | ['&', '|', '^', '~', '<<', '>>', '>>>'], 29 | ['==', '!=', '===', '!==', '>', '>=', '<', '<='], 30 | ['&&', '||'], 31 | ['in', 'instanceof'], 32 | ], 33 | allowSamePrecedence: true, 34 | }], 35 | 'no-param-reassign': ['error', { 36 | props: true, 37 | ignorePropertyModificationsFor: [ 38 | 'state', 39 | 'acc', 40 | ], 41 | }], 42 | 'object-curly-newline': ['error', { 43 | ObjectExpression: { consistent: true }, 44 | ObjectPattern: { multiline: true }, 45 | }], 46 | 'template-curly-spacing': 'off', 47 | indent: [ 48 | 'error', 2, 49 | { ignoredNodes: ['TemplateLiteral'] } 50 | ], 51 | }, 52 | parserOptions: { 53 | parser: 'babel-eslint', 54 | }, 55 | globals: { 56 | LIBNAME: true, 57 | }, 58 | }; 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@myena/vue-table", 3 | "version": "0.16.15", 4 | "description": "Vue Table components (client/server) used in ENA projects", 5 | "author": "Andy Ghiuta ", 6 | "scripts": { 7 | "serve": "vue-cli-service serve --host localhost", 8 | "build": "node build.js", 9 | "test:unit": "vue-cli-service test:unit", 10 | "lint": "vue-cli-service lint", 11 | "build:example": "vue-cli-service build --dest example", 12 | "postversion": "git push && git push --tags && echo Run: \"npm publish\"", 13 | "preversion": "npm run test:unit && npm run build && git add -A dist", 14 | "test:unitw": "vue-cli-service test:unit --watch" 15 | }, 16 | "main": "dist/index.js", 17 | "files": [ 18 | "dist/*", 19 | "src/components/*" 20 | ], 21 | "dependencies": { 22 | "core-js": "^3.6.4", 23 | "ramda": "^0.28.0" 24 | }, 25 | "devDependencies": { 26 | "@vue/cli-plugin-babel": "^4.3.1", 27 | "@vue/cli-plugin-eslint": "^4.3.1", 28 | "@vue/cli-plugin-unit-mocha": "^4.3.1", 29 | "@vue/cli-service": "^4.3.1", 30 | "@vue/eslint-config-airbnb": "^5.0.2", 31 | "@vue/test-utils": "^1.0.0-beta.29", 32 | "axios": "^0.26.1", 33 | "babel-eslint": "^10.1.0", 34 | "bootstrap": "^3.4.1", 35 | "chai": "^4.1.2", 36 | "eslint": "^6.7.2", 37 | "eslint-plugin-import": "^2.20.2", 38 | "eslint-plugin-vue": "^6.2.2", 39 | "firebase-tools": "^6.10.0", 40 | "font-awesome": "^4.7.0", 41 | "glob": "^7.1.3", 42 | "jquery": "^3.4.1", 43 | "less": "^3.0.4", 44 | "less-loader": "^4.1.0", 45 | "qs": "^6.6.0", 46 | "vue": "^2.6.11", 47 | "vue-template-compiler": "^2.6.11" 48 | }, 49 | "peerDependencies": { 50 | "axios": "^0.26.1", 51 | "bootstrap": "^3.3.7", 52 | "font-awesome": "^4.7.0", 53 | "jquery": "^3.3.1", 54 | "vue": "^2.6.10" 55 | }, 56 | "keywords": [ 57 | "vue", 58 | "table" 59 | ], 60 | "libname": "EnaTable", 61 | "license": "MIT", 62 | "repository": "github:myENA/vue-table" 63 | } 64 | -------------------------------------------------------------------------------- /tests/unit/Pagination.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { shallowMount } from '@vue/test-utils'; 3 | import Pagination from '@/components/mixins/Pagination.vue'; 4 | 5 | const propsData = { 6 | text: { 7 | prev: '', 8 | next: '', 9 | info: { 10 | showing: 'Showing %s to %s of %s rows.', 11 | records: 'records per page', 12 | noRows: 'No rows to display', 13 | }, 14 | }, 15 | pageInterval: 7, 16 | perPage: 1, 17 | perPageValues: [1], 18 | currentPage: 1, 19 | totalRows: 1, 20 | }; 21 | describe('Pagination', () => { 22 | it('is rendered', () => { 23 | const wrapper = shallowMount(Pagination, { 24 | propsData, 25 | }); 26 | expect(wrapper.contains('div')).to.be.true; 27 | }); 28 | it('Shows correct pages', () => { 29 | const wrapper = shallowMount(Pagination, { 30 | propsData, 31 | }); 32 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([]); 33 | wrapper.setProps({ totalRows: 7, currentPage: 4 }); 34 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([2, 3, 4, 5, 6]); 35 | wrapper.setProps({ totalRows: 10, currentPage: 1 }); 36 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([2, 3, 4, 5, 6, 7, 8]); 37 | wrapper.setProps({ currentPage: 2 }); 38 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([2, 3, 4, 5, 6, 7, 8]); 39 | wrapper.setProps({ currentPage: 3 }); 40 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([2, 3, 4, 5, 6, 7, 8]); 41 | wrapper.setProps({ currentPage: 4 }); 42 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([2, 3, 4, 5, 6, 7, 8]); 43 | wrapper.setProps({ currentPage: 5 }); 44 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([2, 3, 4, 5, 6, 7, 8]); 45 | wrapper.setProps({ currentPage: 6 }); 46 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([3, 4, 5, 6, 7, 8, 9]); 47 | wrapper.setProps({ currentPage: 7 }); 48 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([3, 4, 5, 6, 7, 8, 9]); 49 | wrapper.setProps({ currentPage: 8 }); 50 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([3, 4, 5, 6, 7, 8, 9]); 51 | wrapper.setProps({ currentPage: 9 }); 52 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([3, 4, 5, 6, 7, 8, 9]); 53 | wrapper.setProps({ currentPage: 10 }); 54 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([3, 4, 5, 6, 7, 8, 9]); 55 | wrapper.setProps({ totalRows: 14, currentPage: 9 }); 56 | expect(wrapper.vm.pagesToShow).to.be.deep.equal([6, 7, 8, 9, 10, 11, 12]); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /dist/EnaTable.css: -------------------------------------------------------------------------------- 1 | .Pagination_pagination_1xsiO{margin:0}.Pagination_pagination_1xsiO>li>span,.Pagination_pagination_1xsiO>li>span:focus,.Pagination_pagination_1xsiO>li>span:hover{border-top:1px solid transparent;border-bottom:1px solid transparent}.Pagination_pagination_1xsiO>li>span:focus,.Pagination_pagination_1xsiO>li>span:hover{background-color:#fff}.Pagination_info_2cUbo{float:right}.Pagination_info_2cUbo .Pagination_perPageSelector_CN8cp{margin-left:10px;margin-right:10px}.Server_table_1-Jbh{border-bottom:none}.Server_table_1-Jbh>thead:first-child>tr:first-child>th{border-top:1px solid #333;border-bottom:1px solid #333}.Server_table_1-Jbh>thead:first-child>tr:first-child>th a{cursor:default;color:inherit;text-decoration:none;display:block}.Server_table_1-Jbh>thead:first-child>tr:first-child>th.Server_sortable_351RP a{cursor:pointer}.Server_table_1-Jbh>thead:first-child>tr:first-child>th.Server_sortable_351RP a:focus{text-decoration:underline}.Server_table_1-Jbh>thead:first-child>tr:first-child>th.Server_sortable_351RP i{margin-top:5px;margin-left:5px}.Server_table_1-Jbh tbody>tr:first-child>th{background-color:#f2f2f2}.Server_table_1-Jbh tbody>tr:first-child>th a+span{margin-left:5px}.Server_table_1-Jbh tbody>tr:first-child>th a{color:#333;font-size:16px;display:inline-block;width:20px;text-align:center}.Server_table_1-Jbh .Server_checkbox_1lyNb{margin:0}.Server_table_1-Jbh .Server_checkbox_1lyNb label{min-height:18px}.Server_pagination_3erRE{margin:0}.Server_info_2gbZL{float:right}.Server_info_2gbZL .Server_perPageSelector_3GVN9{margin-left:10px;margin-right:10px}.Client_table_30YmA{border-bottom:none}.Client_table_30YmA>thead:first-child>tr:first-child>th{border-top:1px solid #333;border-bottom:1px solid #333}.Client_table_30YmA>thead:first-child>tr:first-child>th a{cursor:default;color:inherit;text-decoration:none;display:block}.Client_table_30YmA>thead:first-child>tr:first-child>th.Client_sortable_pJAaM a{cursor:pointer}.Client_table_30YmA>thead:first-child>tr:first-child>th.Client_sortable_pJAaM a:focus{text-decoration:underline}.Client_table_30YmA>thead:first-child>tr:first-child>th.Client_sortable_pJAaM i{margin-top:5px;margin-left:5px}.Client_table_30YmA tbody>tr:first-child>th{background-color:#f2f2f2}.Client_table_30YmA tbody>tr:first-child>th a+span{margin-left:5px}.Client_table_30YmA tbody>tr:first-child>th a{color:#333;font-size:16px;display:inline-block;width:20px;text-align:center}.Client_table_30YmA .Client_checkbox_2krmy{margin:0}.Client_table_30YmA .Client_checkbox_2krmy label{min-height:18px}td.Client_selectable_3NYta:hover{cursor:pointer} -------------------------------------------------------------------------------- /src/components/mixins/default-props.js: -------------------------------------------------------------------------------- 1 | export default { 2 | /** 3 | * Key-value pairs with the headings to overwrite (label to display) 4 | * can also be overwritten with slot: "heading_colname" 5 | * 6 | * @inner 7 | * @type {Object} 8 | */ 9 | headings: {}, 10 | /** 11 | * Key-value pairs with templates (components) for the column value 12 | * 13 | * @type {Object} 14 | */ 15 | templates: {}, 16 | /** 17 | * empty object to disable sorting for all, 18 | * or define what columns are sortable; defaults to all sortable 19 | * 20 | * @default 21 | * @type {true|Object} 22 | */ 23 | sortable: true, 24 | /** 25 | * Object (key, order) to sort table by on first load (on created) 26 | * @type {Object} 27 | */ 28 | sortBy: { 29 | column: null, 30 | order: null, 31 | }, 32 | /** 33 | * Required, unique identifier 34 | * 35 | * @default 36 | * @type {String} 37 | */ 38 | uniqueKey: 'id', 39 | /** 40 | * show extra row for each row with details 41 | * 42 | * @default 43 | * @type {Boolean} 44 | */ 45 | detailsRow: false, 46 | /** 47 | * number of items per page 48 | * 49 | * @default 50 | * @type {Number} 51 | */ 52 | perPage: 10, 53 | /** 54 | * How many pages to show in the paginator. Odd number 55 | * 56 | * @default 57 | * @type {Number} 58 | */ 59 | pageInterval: 7, 60 | /** 61 | * values to show in the selector of items per page 62 | * 63 | * @default 64 | * @type {Array} 65 | */ 66 | perPageValues: [1, 2, 5, 10, 20, 50], 67 | /** 68 | * Classes to use on various elements 69 | * 70 | * @inner 71 | * @type {Object} 72 | */ 73 | classes: { 74 | wrapper: 'table-responsive', 75 | table: 'table', 76 | sort: { 77 | none: 'fa fa-sort', 78 | ascending: 'fa fa-sort-asc', 79 | descending: 'fa fa-sort-desc', 80 | }, 81 | checkbox: 'checkbox', 82 | pagination: { 83 | wrapper: 'pagination', 84 | formControl: 'form-control', 85 | info: 'info form-inline', 86 | first: 'fa fa-angle-double-left', 87 | prev: 'fa fa-angle-left', 88 | next: 'fa fa-angle-right', 89 | last: 'fa fa-angle-double-right', 90 | }, 91 | group: { 92 | show: 'fa fa-chevron-right', 93 | hide: 'fa fa-chevron-down', 94 | }, 95 | }, 96 | /** 97 | * Texts 98 | * 99 | * @type Object 100 | */ 101 | text: { 102 | /** 103 | * Text to show when row can be expanded 104 | * @type {String} 105 | */ 106 | expand: 'Show details', 107 | /** 108 | * Text to show when row can be collapsed 109 | * @type {String} 110 | */ 111 | collapse: 'Hide details', 112 | /** 113 | * Message to show when there is no data 114 | * @type {String} 115 | */ 116 | noData: 'No data to show', 117 | /** 118 | * Message to show when no results are found for the search 119 | * @type {String} 120 | */ 121 | emptyResults: 'No results for this filter', 122 | /** 123 | * Message to show when no results are found for the search 124 | * @type {String} 125 | */ 126 | loading: 'Loading ...', 127 | /** 128 | * Text to show for pagination helper buttons 129 | * @type {Object} 130 | */ 131 | pagination: { 132 | prev: '', 133 | next: '', 134 | info: { 135 | showing: 'Showing %s to %s of %s rows.', 136 | records: 'records per page', 137 | noRows: 'No rows to display', 138 | }, 139 | }, 140 | }, 141 | /* 142 | * Key-value options for column and class 143 | */ 144 | columnsClasses: {}, 145 | /* 146 | * If to do inital fetch on create 147 | */ 148 | initialFetch: true, 149 | /* 150 | * Key-value options for column and class 151 | */ 152 | rowClasses: {}, 153 | /** 154 | * Show the actions cell (with expand) last or first 155 | */ 156 | actionsCellLast: true, 157 | }; 158 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 157 | 158 | 167 | -------------------------------------------------------------------------------- /tests/unit/Client.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { shallowMount } from '@vue/test-utils'; 3 | import Client from '@/components/Client.vue'; 4 | 5 | describe('Client.vue', () => { 6 | it('Renders a table with 1 row', () => { 7 | const wrapper = shallowMount(Client, { 8 | propsData: { 9 | data: [{ first_name: '1', id: 1 }], 10 | }, 11 | }); 12 | expect(wrapper.contains('table tbody tr')).to.be.true; 13 | 14 | const rows = wrapper.findAll('table tbody tr'); 15 | expect(rows).to.have.lengthOf(1); 16 | }); 17 | 18 | it('Sort by columns', async () => { 19 | const wrapper = shallowMount(Client, { 20 | propsData: { 21 | data: [{ 22 | first_name: '1', 23 | id: 1, 24 | eng: 10, 25 | }, { 26 | first_name: '2', 27 | id: 2, 28 | eng: 5, 29 | }, { 30 | first_name: '3', 31 | id: 3, 32 | eng: 15, 33 | }], 34 | columns: ['first_name', 'id'], 35 | }, 36 | }); 37 | expect(wrapper.contains('table tbody tr')).to.be.true; 38 | const rows = wrapper.findAll('table tbody tr'); 39 | expect(rows).to.have.lengthOf(3); 40 | 41 | wrapper.vm.sortBy({ 42 | key: 'id', 43 | order: 'descending', 44 | }); 45 | 46 | expect(wrapper.vm.pageData).to.be.deep.equal({ 47 | all: [{ 48 | first_name: '3', 49 | id: 3, 50 | eng: 15, 51 | }, { 52 | first_name: '2', 53 | id: 2, 54 | eng: 5, 55 | }, { 56 | first_name: '1', 57 | id: 1, 58 | eng: 10, 59 | }], 60 | }); 61 | 62 | 63 | wrapper.setProps({ 64 | columns: ['first_name', 'id', 'eng'], 65 | }); 66 | wrapper.vm.sortBy({ 67 | key: 'eng', 68 | order: 'descending', 69 | }); 70 | await wrapper.vm.$nextTick(); 71 | 72 | expect(wrapper.vm.pageData).to.be.deep.equal({ 73 | all: [{ 74 | first_name: '3', 75 | id: 3, 76 | eng: 15, 77 | }, { 78 | first_name: '1', 79 | id: 1, 80 | eng: 10, 81 | }, { 82 | first_name: '2', 83 | id: 2, 84 | eng: 5, 85 | }], 86 | }); 87 | }); 88 | 89 | it('Adds row classes on ungrouped tables', () => { 90 | const wrapper = shallowMount(Client, { 91 | propsData: { 92 | data: [{ 93 | first_name: '1', 94 | id: 1, 95 | testClass: true, 96 | }, { 97 | first_name: '2', 98 | id: 2, 99 | testClass: true, 100 | }, { 101 | first_name: '3', 102 | id: 3, 103 | testClass: false, 104 | }], 105 | options: { 106 | rowClasses: { 107 | testClass: 'testClass', 108 | }, 109 | }, 110 | }, 111 | }); 112 | expect(wrapper.contains('table tbody tr')).to.be.true; 113 | 114 | const rows = wrapper.findAll('table tbody tr.testClass'); 115 | expect(rows).to.have.lengthOf(2); 116 | }); 117 | 118 | it('Adds row classes on grouped tables', () => { 119 | const wrapper = shallowMount(Client, { 120 | propsData: { 121 | data: [{ 122 | first_name: '1', 123 | id: 1, 124 | testClass: true, 125 | }, { 126 | first_name: '2', 127 | id: 2, 128 | testClass: false, 129 | }, { 130 | first_name: '2', 131 | id: 3, 132 | testClass: false, 133 | }, { 134 | first_name: '3', 135 | id: 4, 136 | testClass: true, 137 | }, { 138 | first_name: '3', 139 | id: 5, 140 | testClass: true, 141 | }], 142 | options: { 143 | groupBy: 'first_name', 144 | rowClasses: { 145 | testClass: 'testClass', 146 | }, 147 | }, 148 | }, 149 | }); 150 | expect(wrapper.contains('table tbody tr')).to.be.true; 151 | 152 | const rows = wrapper.findAll('table tbody tr.testClass'); 153 | expect(rows).to.have.lengthOf(3); 154 | }); 155 | }); 156 | 157 | describe('Client.vue methods', () => { 158 | const wrapper = shallowMount(Client, {}); 159 | it('prepareValueForCsv escapes quotes', () => { 160 | expect(wrapper.vm.prepareValueForCsv('test with quote "')).to.equal('"test with quote \\""'); 161 | }); 162 | it('prepareValueForCsv returns an empty string when input is null', () => { 163 | expect(wrapper.vm.prepareValueForCsv(null)).to.equal(''); 164 | }); 165 | it('prepareValueForCsv returns an empty string when input is undefined', () => { 166 | expect(wrapper.vm.prepareValueForCsv(undefined)).to.equal(''); 167 | }); 168 | it('prepareValueForCsv returns "0" when input is 0', () => { 169 | expect(wrapper.vm.prepareValueForCsv(0)).to.equal('"0"'); 170 | }); 171 | it('prepareValueForCsv removes line feeds', () => { 172 | expect(wrapper.vm.prepareValueForCsv('test with line feed \n')).to.equal('"test with line feed "'); 173 | }); 174 | it('prepareValueForCsv removes carriage returns', () => { 175 | expect(wrapper.vm.prepareValueForCsv('test with carriage return \r')).to.equal('"test with carriage return "'); 176 | }); 177 | it('getFileNameWithExtension returns a filename with .csv extension when input does not have it included', () => { 178 | expect(wrapper.vm.getFileNameWithExtension('filename')).to.equal('filename.csv'); 179 | }); 180 | }); 181 | -------------------------------------------------------------------------------- /src/components/mixins/Pagination.vue: -------------------------------------------------------------------------------- 1 | 105 | 106 | 128 | 129 | 248 | -------------------------------------------------------------------------------- /src/components/Server.vue: -------------------------------------------------------------------------------- 1 | 116 | 117 | 173 | 174 | 403 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-table 2 | 3 | [![CircleCI (all branches)](https://img.shields.io/circleci/project/github/myENA/vue-table.svg)](https://circleci.com/gh/myENA/vue-table) 4 | [![npm (scoped)](https://img.shields.io/npm/v/@myena/vue-table.svg)](https://www.npmjs.com/package/@myena/vue-table) 5 | ![](https://img.shields.io/npm/dt/@myena/vue-table.svg) 6 | [![NpmLicense](https://img.shields.io/npm/l/@myena/vue-table.svg)](https://www.npmjs.com/package/@myena/vue-table) 7 | ![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/@myena/vue-table.svg) 8 | ![David](https://img.shields.io/david/peer/myena/vue-table.svg) 9 | ![David](https://img.shields.io/david/dev/myena/vue-table.svg) 10 | 11 | 12 | ## What's this 13 | Components to render a table using client or remote data 14 | 15 | ## Install 16 | ``` 17 | npm install @myena/vue-table 18 | ``` 19 | 20 | # Dependencies 21 | 22 | - Vue 2 23 | - Bootstrap 3 24 | - FontAwesome 4 25 | 26 | # Demo 27 | https://myena-vue-table.netlify.app/ 28 | 29 | # Vue Client Table 30 | 31 | Vue component for rendering a client side table with pagination, grouping, sorting, filtering, details row. 32 | Entire table data should be given to the table and will be paginated client side. Data can come from a Vuex store. 33 | 34 | ## Usage: 35 | 36 | ```html 37 | 38 | 39 | 40 | (Total in group: {{data.total}}) 41 | 42 | 45 | 48 | 51 | 52 | ``` 53 | 54 | ### As plugin (or in Browser) 55 | 56 | ```javascript 57 | // this will create the global (window) object "EnaTableClient" 58 | import '@myena/vue-table/dist/EnaTableClient'; 59 | // or include as script in html : 60 | // registers the component globally 61 | 62 | // in the view that contains the table 63 | const MyView = new Vue({ 64 | data() { 65 | // Define properties 66 | return { 67 | /** 68 | * List of objects to present in the table 69 | * 70 | * @member 71 | * @type {Array} 72 | */ 73 | data: Array, 74 | /** 75 | * List of keys to use from each object (table columns) 76 | * 77 | * @type {Array} 78 | */ 79 | columns: Array, 80 | /** 81 | * The filter object. If updated will filter the results by the value 82 | * 83 | * @type {Object} 84 | */ 85 | filter: { 86 | type: Object, 87 | default: () => ({ 88 | // The search query string. If updated will filter the results by the value 89 | keyword: '', 90 | }), 91 | }, 92 | /** 93 | * Loading indicator. If true, will display the `loadingMsg` instead of the body 94 | * @type {Boolean} 95 | */ 96 | loading: { 97 | type: Boolean, 98 | default: false, 99 | }, 100 | /** 101 | * Options for the table 102 | * 103 | * @inner 104 | * @type {Object} 105 | */ 106 | options: { 107 | /** 108 | * Classes to use on various elements 109 | * 110 | * @inner 111 | * @type {Object} 112 | */ 113 | classes: { 114 | wrapper: 'table-responsive', 115 | table: 'table', 116 | formControl: 'form-control', 117 | sort: { 118 | none: 'fa fa-sort', 119 | ascending: 'fa fa-sort-asc', 120 | descending: 'fa fa-sort-desc', 121 | }, 122 | pagination: { 123 | wrapper: 'pagination', 124 | info: 'info form-inline', 125 | first: 'fa fa-angle-double-left', 126 | prev: 'fa fa-angle-left', 127 | next: 'fa fa-angle-right', 128 | last: 'fa fa-angle-double-right', 129 | }, 130 | group: { 131 | show: 'fa fa-chevron-right', 132 | hide: 'fa fa-chevron-down', 133 | }, 134 | }, 135 | /** 136 | * Key-value pairs with the headings to overwrite (label to display) 137 | * can also be overwritten with slot: "heading_colname" 138 | * 139 | * @inner 140 | * @type {Object} 141 | */ 142 | headings: {}, 143 | /** 144 | * Key-value pairs with templates (components) for the column value 145 | * 146 | * @type {Object} 147 | */ 148 | templates: {}, 149 | /** 150 | * Key-value pairs with custom search function per column, 151 | * or false to disable search for that column 152 | * 153 | * @type {Object} 154 | */ 155 | search: {}, 156 | /** 157 | * Field to group by - key name 158 | * 159 | * @default 160 | * @type {Boolean|String} 161 | */ 162 | groupBy: false, 163 | /** 164 | * Expand/collapse groups 165 | * 166 | * @default 167 | * @type {Boolean} 168 | */ 169 | toggleGroups: false, 170 | /** 171 | * Object of data to use for each group "header" (key is the group value) 172 | * 173 | * @type {Object} 174 | */ 175 | groupMeta: {}, 176 | /** 177 | * Required, unique identifier 178 | * 179 | * @default 180 | * @type {String} 181 | */ 182 | uniqueKey: 'id', 183 | /** 184 | * show extra row for each row with details 185 | * 186 | * @default 187 | * @type {Boolean} 188 | */ 189 | detailsRow: false, 190 | /** 191 | * Texts 192 | * 193 | * @type Object 194 | */ 195 | text: { 196 | /** 197 | * Text to show when row can be expanded 198 | * @type {String} 199 | */ 200 | expand: 'Show details', 201 | /** 202 | * Text to show when row can be collapsed 203 | * @type {String} 204 | */ 205 | collapse: 'Hide details', 206 | /** 207 | * Message to show when there is no data 208 | * @type {String} 209 | */ 210 | noData: 'No data to show', 211 | /** 212 | * Message to show when no results are found for the search 213 | * @type {String} 214 | */ 215 | emptyResults: 'No results for this filter', 216 | /** 217 | * Message to show when no results are found for the search 218 | * @type {String} 219 | */ 220 | loading: 'Loading ...', 221 | /** 222 | * Text to show for pagination helper buttons 223 | * @type {Object} 224 | */ 225 | pagination: { 226 | next: '', 227 | last: '', 228 | info: { 229 | showing: 'Showing %s to %s of %s rows.', 230 | records: 'records per page', 231 | noRows: 'No rows to display', 232 | }, 233 | }, 234 | }, 235 | /** 236 | * empty object to disable sorting for all, 237 | * or define what columns are sortable; defaults to all sortable 238 | * 239 | * @default 240 | * @type {true|Object} 241 | */ 242 | sortable: true, 243 | /** 244 | * false, to disable pagination - show all; defaults to true 245 | * 246 | * @default 247 | * @type {Boolean} 248 | */ 249 | pagination: true, 250 | /** 251 | * number of items per page 252 | * 253 | * @default 254 | * @type {Number} 255 | */ 256 | perPage: 10, 257 | /** 258 | * How many pages to show in the paginator. Odd number 259 | * 260 | * @default 261 | * @type {Number} 262 | */ 263 | pageInterval: 7, 264 | /** 265 | * values to show in the selector of items per page 266 | * 267 | * @default 268 | * @type {Array} 269 | */ 270 | perPageValues: [1, 2, 5, 10, 20, 50], 271 | /** 272 | * Is the table editable (eg: can select value) 273 | * @type {Boolean} 274 | */ 275 | editable: false, 276 | /** 277 | * List of columns that should be disabled for click to select/deselect 278 | * @type {Array} 279 | */ 280 | nonSelectableColumns: [], 281 | /** 282 | * Object (key, order) to sort table by on first load (on created) 283 | * @type {Object} 284 | */ 285 | sortBy: { 286 | column: null, 287 | order: null, 288 | }, 289 | /** 290 | * The collator used for sorting 291 | * @type {Intl.Collator} 292 | */ 293 | sortCollator: new Intl.Collator('en', { 294 | numeric: true, 295 | sensitivity: 'base', 296 | }), 297 | }, 298 | }; 299 | }, 300 | // OR use computed properties instead 301 | computed: { 302 | data() { 303 | return this.$store.state.myListData; // or any other source that has all data 304 | }, 305 | }, 306 | methods: { 307 | search(filter) { 308 | this.searchQuery = filter.keyword; 309 | }, 310 | }, 311 | watch: { 312 | data(data) { 313 | // set the group metas when data changes 314 | // groupColumn is the same as the one used for 'groupBy' option 315 | this.options.groupMeta = data.reduce((groupMeta, r) => { 316 | if (!groupMeta[r.groupColumn]) { 317 | groupMeta[r.groupColumn] = { 318 | label: r.groupColumn, 319 | total: 0, 320 | }; 321 | } 322 | groupMeta[r.groupColumn].total += 1; 323 | return groupMeta; 324 | }, {}); 325 | }, 326 | }, 327 | }); 328 | ``` 329 | 330 | ### As module/local component 331 | 332 | ```javascript 333 | import { Client } from '@myena/vue-table'; 334 | 335 | // in the view that contains the table 336 | const MyView = new Vue({ 337 | components: { 338 | ClientTable: Client, 339 | }, 340 | data() { 341 | return { 342 | columns: ['column1', 'column2'], 343 | options: { 344 | }, 345 | }; 346 | }, 347 | computed: { 348 | data() { 349 | return this.$store.state.myListData; // or any other source that has all data 350 | }, 351 | }, 352 | }); 353 | ``` 354 | 355 | # Vue Server Table 356 | 357 | Vue component for rendering a table that loads data from the server, with pagination, sorting, filtering, details row. 358 | It doesn't support grouping. 359 | 360 | ## Usage: 361 | 362 | ```html 363 | 364 |
365 | 366 | 367 |
368 | 371 | 374 | 377 |
378 | ``` 379 | 380 | ```javascript 381 | import axios from 'axios'; 382 | import Qs from 'qs'; 383 | import { Server: ServerTable } from '@myena/vue-table'; 384 | 385 | const myServerTable = { 386 | extends: ServerTable, 387 | methods: { 388 | /** 389 | * Override the fetch method 390 | */ 391 | async fetch(params) { 392 | const { data } = await axios.get(this.url, { 393 | params: Object.assign({}, params, { 394 | filter: this.options.filter, 395 | }), 396 | paramsSerializer(p) { 397 | return Qs.stringify(p, { arrayFormat: 'brackets' }); 398 | }, 399 | }); 400 | return data; 401 | }, 402 | /** 403 | * Override the parse method to return `data` and `total` fields 404 | */ 405 | parse({ list, total }) { 406 | return { 407 | data: list, 408 | total, 409 | }; 410 | }, 411 | }, 412 | }; 413 | 414 | export default { 415 | name: 'app', 416 | components: { 417 | ServerTable: myServerTable, 418 | }, 419 | data: () => ({ 420 | columns: ['name', 'capital', 'population'], 421 | url: 'https://us-central1-vue-myena-table.cloudfunctions.net/countries', 422 | options: { 423 | perPage: 5, 424 | uniqueKey: 'alpha3Code', 425 | // not handled by the component 426 | // added here for easier access in the overridden fetch function 427 | filter: { 428 | name: null, 429 | }, 430 | }, 431 | }), 432 | methods: { 433 | filter() { 434 | // call component loadData, which sets some params then calls fetch (above) 435 | this.$refs.serverTable.loadData(); 436 | }, 437 | }, 438 | }; 439 | ``` 440 | -------------------------------------------------------------------------------- /src/components/Client.vue: -------------------------------------------------------------------------------- 1 | 204 | 205 | 255 | 256 | 782 | -------------------------------------------------------------------------------- /dist/EnaTableServer/EnaTableServer.umd.min.js: -------------------------------------------------------------------------------- 1 | (function(t,e){"object"===typeof exports&&"object"===typeof module?module.exports=e():"function"===typeof define&&define.amd?define([],e):"object"===typeof exports?exports["EnaTableServer"]=e():t["EnaTableServer"]=e()})("undefined"!==typeof self?self:this,(function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"===typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t["default"]}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s="fb15")}({"00ee":function(t,e,n){var r=n("b622"),o=r("toStringTag"),i={};i[o]="z",t.exports="[object z]"===String(i)},"0366":function(t,e,n){var r=n("1c0b");t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 0:return function(){return t.call(e)};case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},"057f":function(t,e,n){var r=n("fc6a"),o=n("241c").f,i={}.toString,a="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],c=function(t){try{return o(t)}catch(e){return a.slice()}};t.exports.f=function(t){return a&&"[object Window]"==i.call(t)?c(t):o(r(t))}},"06cf":function(t,e,n){var r=n("83ab"),o=n("d1e7"),i=n("5c6c"),a=n("fc6a"),c=n("c04e"),s=n("5135"),u=n("0cfb"),f=Object.getOwnPropertyDescriptor;e.f=r?f:function(t,e){if(t=a(t),e=c(e,!0),u)try{return f(t,e)}catch(n){}if(s(t,e))return i(!o.f.call(t,e),t[e])}},"07ac":function(t,e,n){var r=n("23e7"),o=n("6f53").values;r({target:"Object",stat:!0},{values:function(t){return o(t)}})},"0a06":function(t,e,n){"use strict";var r=n("c532"),o=n("30b5"),i=n("f6b4"),a=n("5270"),c=n("4a7b"),s=n("848b"),u=s.validators;function f(t){this.defaults=t,this.interceptors={request:new i,response:new i}}f.prototype.request=function(t,e){"string"===typeof t?(e=e||{},e.url=t):e=t||{},e=c(this.defaults,e),e.method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var n=e.transitional;void 0!==n&&s.assertOptions(n,{silentJSONParsing:u.transitional(u.boolean),forcedJSONParsing:u.transitional(u.boolean),clarifyTimeoutError:u.transitional(u.boolean)},!1);var r=[],o=!0;this.interceptors.request.forEach((function(t){"function"===typeof t.runWhen&&!1===t.runWhen(e)||(o=o&&t.synchronous,r.unshift(t.fulfilled,t.rejected))}));var i,f=[];if(this.interceptors.response.forEach((function(t){f.push(t.fulfilled,t.rejected)})),!o){var l=[a,void 0];Array.prototype.unshift.apply(l,r),l=l.concat(f),i=Promise.resolve(e);while(l.length)i=i.then(l.shift(),l.shift());return i}var p=e;while(r.length){var d=r.shift(),h=r.shift();try{p=d(p)}catch(v){h(v);break}}try{i=a(p)}catch(v){return Promise.reject(v)}while(f.length)i=i.then(f.shift(),f.shift());return i},f.prototype.getUri=function(t){return t=c(this.defaults,t),o(t.url,t.params,t.paramsSerializer).replace(/^\?/,"")},r.forEach(["delete","get","head","options"],(function(t){f.prototype[t]=function(e,n){return this.request(c(n||{},{method:t,url:e,data:(n||{}).data}))}})),r.forEach(["post","put","patch"],(function(t){f.prototype[t]=function(e,n,r){return this.request(c(r||{},{method:t,url:e,data:n}))}})),t.exports=f},"0cfb":function(t,e,n){var r=n("83ab"),o=n("d039"),i=n("cc12");t.exports=!r&&!o((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},"0df6":function(t,e,n){"use strict";t.exports=function(t){return function(e){return t.apply(null,e)}}},1276:function(t,e,n){"use strict";var r=n("d784"),o=n("44e7"),i=n("825a"),a=n("1d80"),c=n("4840"),s=n("8aa5"),u=n("50c4"),f=n("14c3"),l=n("9263"),p=n("d039"),d=[].push,h=Math.min,v=4294967295,g=!p((function(){return!RegExp(v,"y")}));r("split",2,(function(t,e,n){var r;return r="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(t,n){var r=String(a(this)),i=void 0===n?v:n>>>0;if(0===i)return[];if(void 0===t)return[r];if(!o(t))return e.call(r,t,i);var c,s,u,f=[],p=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.unicode?"u":"")+(t.sticky?"y":""),h=0,g=new RegExp(t.source,p+"g");while(c=l.call(g,r)){if(s=g.lastIndex,s>h&&(f.push(r.slice(h,c.index)),c.length>1&&c.index=i))break;g.lastIndex===c.index&&g.lastIndex++}return h===r.length?!u&&g.test("")||f.push(""):f.push(r.slice(h)),f.length>i?f.slice(0,i):f}:"0".split(void 0,0).length?function(t,n){return void 0===t&&0===n?[]:e.call(this,t,n)}:e,[function(e,n){var o=a(this),i=void 0==e?void 0:e[t];return void 0!==i?i.call(e,o,n):r.call(String(o),e,n)},function(t,o){var a=n(r,t,this,o,r!==e);if(a.done)return a.value;var l=i(t),p=String(this),d=c(l,RegExp),y=l.unicode,b=(l.ignoreCase?"i":"")+(l.multiline?"m":"")+(l.unicode?"u":"")+(g?"y":"g"),m=new d(g?l:"^(?:"+l.source+")",b),w=void 0===o?v:o>>>0;if(0===w)return[];if(0===p.length)return null===f(m,p)?[p]:[];var x=0,P=0,S=[];while(P1?arguments[1]:void 0)}})},"14c3":function(t,e,n){var r=n("c6b6"),o=n("9263");t.exports=function(t,e){var n=t.exec;if("function"===typeof n){var i=n.call(t,e);if("object"!==typeof i)throw TypeError("RegExp exec method returned something other than an Object or null");return i}if("RegExp"!==r(t))throw TypeError("RegExp#exec called on incompatible receiver");return o.call(t,e)}},"159b":function(t,e,n){var r=n("da84"),o=n("fdbc"),i=n("17c2"),a=n("9112");for(var c in o){var s=r[c],u=s&&s.prototype;if(u&&u.forEach!==i)try{a(u,"forEach",i)}catch(f){u.forEach=i}}},"17c2":function(t,e,n){"use strict";var r=n("b727").forEach,o=n("a640"),i=n("ae40"),a=o("forEach"),c=i("forEach");t.exports=a&&c?[].forEach:function(t){return r(this,t,arguments.length>1?arguments[1]:void 0)}},"19aa":function(t,e){t.exports=function(t,e,n){if(!(t instanceof e))throw TypeError("Incorrect "+(n?n+" ":"")+"invocation");return t}},"1be4":function(t,e,n){var r=n("d066");t.exports=r("document","documentElement")},"1c0b":function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t}},"1c7e":function(t,e,n){var r=n("b622"),o=r("iterator"),i=!1;try{var a=0,c={next:function(){return{done:!!a++}},return:function(){i=!0}};c[o]=function(){return this},Array.from(c,(function(){throw 2}))}catch(s){}t.exports=function(t,e){if(!e&&!i)return!1;var n=!1;try{var r={};r[o]=function(){return{next:function(){return{done:n=!0}}}},t(r)}catch(s){}return n}},"1cdc":function(t,e,n){var r=n("342f");t.exports=/(iphone|ipod|ipad).*applewebkit/i.test(r)},"1d2b":function(t,e,n){"use strict";t.exports=function(t,e){return function(){for(var n=new Array(arguments.length),r=0;r=51||!r((function(){var e=[],n=e.constructor={};return n[a]=function(){return{foo:1}},1!==e[t](Boolean).foo}))}},2266:function(t,e,n){var r=n("825a"),o=n("e95a"),i=n("50c4"),a=n("0366"),c=n("35a1"),s=n("9bdd"),u=function(t,e){this.stopped=t,this.result=e},f=t.exports=function(t,e,n,f,l){var p,d,h,v,g,y,b,m=a(e,n,f?2:1);if(l)p=t;else{if(d=c(t),"function"!=typeof d)throw TypeError("Target is not iterable");if(o(d)){for(h=0,v=i(t.length);v>h;h++)if(g=f?m(r(b=t[h])[0],b[1]):m(t[h]),g&&g instanceof u)return g;return new u(!1)}p=d.call(t)}y=p.next;while(!(b=y.call(p)).done)if(g=s(p,m,b.value,f),"object"==typeof g&&g&&g instanceof u)return g;return new u(!1)};f.stop=function(t){return new u(!0,t)}},"23cb":function(t,e,n){var r=n("a691"),o=Math.max,i=Math.min;t.exports=function(t,e){var n=r(t);return n<0?o(n+e,0):i(n,e)}},"23e7":function(t,e,n){var r=n("da84"),o=n("06cf").f,i=n("9112"),a=n("6eeb"),c=n("ce4e"),s=n("e893"),u=n("94ca");t.exports=function(t,e){var n,f,l,p,d,h,v=t.target,g=t.global,y=t.stat;if(f=g?r:y?r[v]||c(v,{}):(r[v]||{}).prototype,f)for(l in e){if(d=e[l],t.noTargetGet?(h=o(f,l),p=h&&h.value):p=f[l],n=u(g?l:v+(y?".":"#")+l,t.forced),!n&&void 0!==p){if(typeof d===typeof p)continue;s(d,p)}(t.sham||p&&p.sham)&&i(d,"sham",!0),a(f,l,d,t)}}},"241c":function(t,e,n){var r=n("ca84"),o=n("7839"),i=o.concat("length","prototype");e.f=Object.getOwnPropertyNames||function(t){return r(t,i)}},2532:function(t,e,n){"use strict";var r=n("23e7"),o=n("5a34"),i=n("1d80"),a=n("ab13");r({target:"String",proto:!0,forced:!a("includes")},{includes:function(t){return!!~String(i(this)).indexOf(o(t),arguments.length>1?arguments[1]:void 0)}})},2626:function(t,e,n){"use strict";var r=n("d066"),o=n("9bf2"),i=n("b622"),a=n("83ab"),c=i("species");t.exports=function(t){var e=r(t),n=o.f;a&&e&&!e[c]&&n(e,c,{configurable:!0,get:function(){return this}})}},"2cf4":function(t,e,n){var r,o,i,a=n("da84"),c=n("d039"),s=n("c6b6"),u=n("0366"),f=n("1be4"),l=n("cc12"),p=n("1cdc"),d=a.location,h=a.setImmediate,v=a.clearImmediate,g=a.process,y=a.MessageChannel,b=a.Dispatch,m=0,w={},x="onreadystatechange",P=function(t){if(w.hasOwnProperty(t)){var e=w[t];delete w[t],e()}},S=function(t){return function(){P(t)}},E=function(t){P(t.data)},O=function(t){a.postMessage(t+"",d.protocol+"//"+d.host)};h&&v||(h=function(t){var e=[],n=1;while(arguments.length>n)e.push(arguments[n++]);return w[++m]=function(){("function"==typeof t?t:Function(t)).apply(void 0,e)},r(m),m},v=function(t){delete w[t]},"process"==s(g)?r=function(t){g.nextTick(S(t))}:b&&b.now?r=function(t){b.now(S(t))}:y&&!p?(o=new y,i=o.port2,o.port1.onmessage=E,r=u(i.postMessage,i,1)):!a.addEventListener||"function"!=typeof postMessage||a.importScripts||c(O)||"file:"===d.protocol?r=x in l("script")?function(t){f.appendChild(l("script"))[x]=function(){f.removeChild(this),P(t)}}:function(t){setTimeout(S(t),0)}:(r=O,a.addEventListener("message",E,!1))),t.exports={set:h,clear:v}},"2d00":function(t,e,n){var r,o,i=n("da84"),a=n("342f"),c=i.process,s=c&&c.versions,u=s&&s.v8;u?(r=u.split("."),o=r[0]+r[1]):a&&(r=a.match(/Edge\/(\d+)/),(!r||r[1]>=74)&&(r=a.match(/Chrome\/(\d+)/),r&&(o=r[1]))),t.exports=o&&+o},"2d83":function(t,e,n){"use strict";var r=n("387f");t.exports=function(t,e,n,o,i){var a=new Error(t);return r(a,e,n,o,i)}},"2e67":function(t,e,n){"use strict";t.exports=function(t){return!(!t||!t.__CANCEL__)}},"2f27":function(t,e,n){"use strict";var r=n("378f"),o=n.n(r);e["default"]=o.a},"30b5":function(t,e,n){"use strict";var r=n("c532");function o(t){return encodeURIComponent(t).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}t.exports=function(t,e,n){if(!e)return t;var i;if(n)i=n(e);else if(r.isURLSearchParams(e))i=e.toString();else{var a=[];r.forEach(e,(function(t,e){null!==t&&"undefined"!==typeof t&&(r.isArray(t)?e+="[]":t=[t],r.forEach(t,(function(t){r.isDate(t)?t=t.toISOString():r.isObject(t)&&(t=JSON.stringify(t)),a.push(o(e)+"="+o(t))})))})),i=a.join("&")}if(i){var c=t.indexOf("#");-1!==c&&(t=t.slice(0,c)),t+=(-1===t.indexOf("?")?"?":"&")+i}return t}},"342f":function(t,e,n){var r=n("d066");t.exports=r("navigator","userAgent")||""},"35a1":function(t,e,n){var r=n("f5df"),o=n("3f8c"),i=n("b622"),a=i("iterator");t.exports=function(t){if(void 0!=t)return t[a]||t["@@iterator"]||o[r(t)]}},"378f":function(t,e,n){t.exports={pagination:"Pagination_pagination_1xsiO",info:"Pagination_info_2cUbo",perPageSelector:"Pagination_perPageSelector_CN8cp"}},"37e8":function(t,e,n){var r=n("83ab"),o=n("9bf2"),i=n("825a"),a=n("df75");t.exports=r?Object.defineProperties:function(t,e){i(t);var n,r=a(e),c=r.length,s=0;while(c>s)o.f(t,n=r[s++],e[n]);return t}},"387f":function(t,e,n){"use strict";t.exports=function(t,e,n,r,o){return t.config=e,n&&(t.code=n),t.request=r,t.response=o,t.isAxiosError=!0,t.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code,status:this.response&&this.response.status?this.response.status:null}},t}},3934:function(t,e,n){"use strict";var r=n("c532");t.exports=r.isStandardBrowserEnv()?function(){var t,e=/(msie|trident)/i.test(navigator.userAgent),n=document.createElement("a");function o(t){var r=t;return e&&(n.setAttribute("href",r),r=n.href),n.setAttribute("href",r),{href:n.href,protocol:n.protocol?n.protocol.replace(/:$/,""):"",host:n.host,search:n.search?n.search.replace(/^\?/,""):"",hash:n.hash?n.hash.replace(/^#/,""):"",hostname:n.hostname,port:n.port,pathname:"/"===n.pathname.charAt(0)?n.pathname:"/"+n.pathname}}return t=o(window.location.href),function(e){var n=r.isString(e)?o(e):e;return n.protocol===t.protocol&&n.host===t.host}}():function(){return function(){return!0}}()},"3bbe":function(t,e,n){var r=n("861d");t.exports=function(t){if(!r(t)&&null!==t)throw TypeError("Can't set "+String(t)+" as a prototype");return t}},"3f8c":function(t,e){t.exports={}},4160:function(t,e,n){"use strict";var r=n("23e7"),o=n("17c2");r({target:"Array",proto:!0,forced:[].forEach!=o},{forEach:o})},"428f":function(t,e,n){var r=n("da84");t.exports=r},4362:function(t,e,n){e.nextTick=function(t){setTimeout(t,0)},e.platform=e.arch=e.execPath=e.title="browser",e.pid=1,e.browser=!0,e.env={},e.argv=[],e.binding=function(t){throw new Error("No such module. (Possibly not yet loaded)")},function(){var t,r="/";e.cwd=function(){return r},e.chdir=function(e){t||(t=n("df7c")),r=t.resolve(e,r)}}(),e.exit=e.kill=e.umask=e.dlopen=e.uptime=e.memoryUsage=e.uvCounters=function(){},e.features={}},"44ad":function(t,e,n){var r=n("d039"),o=n("c6b6"),i="".split;t.exports=r((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==o(t)?i.call(t,""):Object(t)}:Object},"44d2":function(t,e,n){var r=n("b622"),o=n("7c73"),i=n("9bf2"),a=r("unscopables"),c=Array.prototype;void 0==c[a]&&i.f(c,a,{configurable:!0,value:o(null)}),t.exports=function(t){c[a][t]=!0}},"44de":function(t,e,n){var r=n("da84");t.exports=function(t,e){var n=r.console;n&&n.error&&(1===arguments.length?n.error(t):n.error(t,e))}},"44e7":function(t,e,n){var r=n("861d"),o=n("c6b6"),i=n("b622"),a=i("match");t.exports=function(t){var e;return r(t)&&(void 0!==(e=t[a])?!!e:"RegExp"==o(t))}},"467f":function(t,e,n){"use strict";var r=n("2d83");t.exports=function(t,e,n){var o=n.config.validateStatus;n.status&&o&&!o(n.status)?e(r("Request failed with status code "+n.status,n.config,null,n.request,n)):t(n)}},4840:function(t,e,n){var r=n("825a"),o=n("1c0b"),i=n("b622"),a=i("species");t.exports=function(t,e){var n,i=r(t).constructor;return void 0===i||void 0==(n=r(i)[a])?e:o(n)}},4930:function(t,e,n){var r=n("d039");t.exports=!!Object.getOwnPropertySymbols&&!r((function(){return!String(Symbol())}))},"4a7b":function(t,e,n){"use strict";var r=n("c532");t.exports=function(t,e){e=e||{};var n={};function o(t,e){return r.isPlainObject(t)&&r.isPlainObject(e)?r.merge(t,e):r.isPlainObject(e)?r.merge({},e):r.isArray(e)?e.slice():e}function i(n){return r.isUndefined(e[n])?r.isUndefined(t[n])?void 0:o(void 0,t[n]):o(t[n],e[n])}function a(t){if(!r.isUndefined(e[t]))return o(void 0,e[t])}function c(n){return r.isUndefined(e[n])?r.isUndefined(t[n])?void 0:o(void 0,t[n]):o(void 0,e[n])}function s(n){return n in e?o(t[n],e[n]):n in t?o(void 0,t[n]):void 0}var u={url:a,method:a,data:a,baseURL:c,transformRequest:c,transformResponse:c,paramsSerializer:c,timeout:c,timeoutMessage:c,withCredentials:c,adapter:c,responseType:c,xsrfCookieName:c,xsrfHeaderName:c,onUploadProgress:c,onDownloadProgress:c,decompress:c,maxContentLength:c,maxBodyLength:c,transport:c,httpAgent:c,httpsAgent:c,cancelToken:c,socketPath:c,responseEncoding:c,validateStatus:s};return r.forEach(Object.keys(t).concat(Object.keys(e)),(function(t){var e=u[t]||i,o=e(t);r.isUndefined(o)&&e!==s||(n[t]=o)})),n}},"4c3d":function(t,e,n){"use strict";(function(e){var r=n("c532"),o=n("c8af"),i=n("387f"),a=n("cafa"),c={"Content-Type":"application/x-www-form-urlencoded"};function s(t,e){!r.isUndefined(t)&&r.isUndefined(t["Content-Type"])&&(t["Content-Type"]=e)}function u(){var t;return("undefined"!==typeof XMLHttpRequest||"undefined"!==typeof e&&"[object process]"===Object.prototype.toString.call(e))&&(t=n("b50d")),t}function f(t,e,n){if(r.isString(t))try{return(e||JSON.parse)(t),r.trim(t)}catch(o){if("SyntaxError"!==o.name)throw o}return(n||JSON.stringify)(t)}var l={transitional:a,adapter:u(),transformRequest:[function(t,e){return o(e,"Accept"),o(e,"Content-Type"),r.isFormData(t)||r.isArrayBuffer(t)||r.isBuffer(t)||r.isStream(t)||r.isFile(t)||r.isBlob(t)?t:r.isArrayBufferView(t)?t.buffer:r.isURLSearchParams(t)?(s(e,"application/x-www-form-urlencoded;charset=utf-8"),t.toString()):r.isObject(t)||e&&"application/json"===e["Content-Type"]?(s(e,"application/json"),f(t)):t}],transformResponse:[function(t){var e=this.transitional||l.transitional,n=e&&e.silentJSONParsing,o=e&&e.forcedJSONParsing,a=!n&&"json"===this.responseType;if(a||o&&r.isString(t)&&t.length)try{return JSON.parse(t)}catch(c){if(a){if("SyntaxError"===c.name)throw i(c,this,"E_JSON_PARSE");throw c}}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,validateStatus:function(t){return t>=200&&t<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};r.forEach(["delete","get","head"],(function(t){l.headers[t]={}})),r.forEach(["post","put","patch"],(function(t){l.headers[t]=r.merge(c)})),t.exports=l}).call(this,n("4362"))},"4c67":function(t,e,n){"use strict";var r=n("dd1a"),o=n.n(r);e["default"]=o.a},"4d64":function(t,e,n){var r=n("fc6a"),o=n("50c4"),i=n("23cb"),a=function(t){return function(e,n,a){var c,s=r(e),u=o(s.length),f=i(a,u);if(t&&n!=n){while(u>f)if(c=s[f++],c!=c)return!0}else for(;u>f;f++)if((t||f in s)&&s[f]===n)return t||f||0;return!t&&-1}};t.exports={includes:a(!0),indexOf:a(!1)}},"4de4":function(t,e,n){"use strict";var r=n("23e7"),o=n("b727").filter,i=n("1dde"),a=n("ae40"),c=i("filter"),s=a("filter");r({target:"Array",proto:!0,forced:!c||!s},{filter:function(t){return o(this,t,arguments.length>1?arguments[1]:void 0)}})},"50c4":function(t,e,n){var r=n("a691"),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},5135:function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},5270:function(t,e,n){"use strict";var r=n("c532"),o=n("c401"),i=n("2e67"),a=n("4c3d"),c=n("7a77");function s(t){if(t.cancelToken&&t.cancelToken.throwIfRequested(),t.signal&&t.signal.aborted)throw new c("canceled")}t.exports=function(t){s(t),t.headers=t.headers||{},t.data=o.call(t,t.data,t.headers,t.transformRequest),t.headers=r.merge(t.headers.common||{},t.headers[t.method]||{},t.headers),r.forEach(["delete","get","head","post","put","patch","common"],(function(e){delete t.headers[e]}));var e=t.adapter||a.adapter;return e(t).then((function(e){return s(t),e.data=o.call(t,e.data,e.headers,t.transformResponse),e}),(function(e){return i(e)||(s(t),e&&e.response&&(e.response.data=o.call(t,e.response.data,e.response.headers,t.transformResponse))),Promise.reject(e)}))}},5319:function(t,e,n){"use strict";var r=n("d784"),o=n("825a"),i=n("7b0b"),a=n("50c4"),c=n("a691"),s=n("1d80"),u=n("8aa5"),f=n("14c3"),l=Math.max,p=Math.min,d=Math.floor,h=/\$([$&'`]|\d\d?|<[^>]*>)/g,v=/\$([$&'`]|\d\d?)/g,g=function(t){return void 0===t?t:String(t)};r("replace",2,(function(t,e,n,r){var y=r.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE,b=r.REPLACE_KEEPS_$0,m=y?"$":"$0";return[function(n,r){var o=s(this),i=void 0==n?void 0:n[t];return void 0!==i?i.call(n,o,r):e.call(String(o),n,r)},function(t,r){if(!y&&b||"string"===typeof r&&-1===r.indexOf(m)){var i=n(e,t,this,r);if(i.done)return i.value}var s=o(t),d=String(this),h="function"===typeof r;h||(r=String(r));var v=s.global;if(v){var x=s.unicode;s.lastIndex=0}var P=[];while(1){var S=f(s,d);if(null===S)break;if(P.push(S),!v)break;var E=String(S[0]);""===E&&(s.lastIndex=u(d,a(s.lastIndex),x))}for(var O="",_=0,j=0;j=_&&(O+=d.slice(_,R)+N,_=R+T.length)}return O+d.slice(_)}];function w(t,n,r,o,a,c){var s=r+t.length,u=o.length,f=v;return void 0!==a&&(a=i(a),f=h),e.call(c,f,(function(e,i){var c;switch(i.charAt(0)){case"$":return"$";case"&":return t;case"`":return n.slice(0,r);case"'":return n.slice(s);case"<":c=a[i.slice(1,-1)];break;default:var f=+i;if(0===f)return e;if(f>u){var l=d(f/10);return 0===l?e:l<=u?void 0===o[l-1]?i.charAt(1):o[l-1]+i.charAt(1):e}c=o[f-1]}return void 0===c?"":c}))}}))},5692:function(t,e,n){var r=n("c430"),o=n("c6cd");(t.exports=function(t,e){return o[t]||(o[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.6.5",mode:r?"pure":"global",copyright:"© 2020 Denis Pushkarev (zloirock.ru)"})},"56ef":function(t,e,n){var r=n("d066"),o=n("241c"),i=n("7418"),a=n("825a");t.exports=r("Reflect","ownKeys")||function(t){var e=o.f(a(t)),n=i.f;return n?e.concat(n(t)):e}},5899:function(t,e){t.exports="\t\n\v\f\r                 \u2028\u2029\ufeff"},"58a8":function(t,e,n){var r=n("1d80"),o=n("5899"),i="["+o+"]",a=RegExp("^"+i+i+"*"),c=RegExp(i+i+"*$"),s=function(t){return function(e){var n=String(r(e));return 1&t&&(n=n.replace(a,"")),2&t&&(n=n.replace(c,"")),n}};t.exports={start:s(1),end:s(2),trim:s(3)}},"5a34":function(t,e,n){var r=n("44e7");t.exports=function(t){if(r(t))throw TypeError("The method doesn't accept regular expressions");return t}},"5c6c":function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},"5cce":function(t,e){t.exports={version:"0.26.1"}},"5f02":function(t,e,n){"use strict";var r=n("c532");t.exports=function(t){return r.isObject(t)&&!0===t.isAxiosError}},"60da":function(t,e,n){"use strict";var r=n("83ab"),o=n("d039"),i=n("df75"),a=n("7418"),c=n("d1e7"),s=n("7b0b"),u=n("44ad"),f=Object.assign,l=Object.defineProperty;t.exports=!f||o((function(){if(r&&1!==f({b:1},f(l({},"a",{enumerable:!0,get:function(){l(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},n=Symbol(),o="abcdefghijklmnopqrst";return t[n]=7,o.split("").forEach((function(t){e[t]=t})),7!=f({},t)[n]||i(f({},e)).join("")!=o}))?function(t,e){var n=s(t),o=arguments.length,f=1,l=a.f,p=c.f;while(o>f){var d,h=u(arguments[f++]),v=l?i(h).concat(l(h)):i(h),g=v.length,y=0;while(g>y)d=v[y++],r&&!p.call(h,d)||(n[d]=h[d])}return n}:f},6547:function(t,e,n){var r=n("a691"),o=n("1d80"),i=function(t){return function(e,n){var i,a,c=String(o(e)),s=r(n),u=c.length;return s<0||s>=u?t?"":void 0:(i=c.charCodeAt(s),i<55296||i>56319||s+1===u||(a=c.charCodeAt(s+1))<56320||a>57343?t?c.charAt(s):i:t?c.slice(s,s+2):a-56320+(i-55296<<10)+65536)}};t.exports={codeAt:i(!1),charAt:i(!0)}},"65f0":function(t,e,n){var r=n("861d"),o=n("e8b5"),i=n("b622"),a=i("species");t.exports=function(t,e){var n;return o(t)&&(n=t.constructor,"function"!=typeof n||n!==Array&&!o(n.prototype)?r(n)&&(n=n[a],null===n&&(n=void 0)):n=void 0),new(void 0===n?Array:n)(0===e?0:e)}},"69f3":function(t,e,n){var r,o,i,a=n("7f9a"),c=n("da84"),s=n("861d"),u=n("9112"),f=n("5135"),l=n("f772"),p=n("d012"),d=c.WeakMap,h=function(t){return i(t)?o(t):r(t,{})},v=function(t){return function(e){var n;if(!s(e)||(n=o(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return n}};if(a){var g=new d,y=g.get,b=g.has,m=g.set;r=function(t,e){return m.call(g,t,e),e},o=function(t){return y.call(g,t)||{}},i=function(t){return b.call(g,t)}}else{var w=l("state");p[w]=!0,r=function(t,e){return u(t,w,e),e},o=function(t){return f(t,w)?t[w]:{}},i=function(t){return f(t,w)}}t.exports={set:r,get:o,has:i,enforce:h,getterFor:v}},"6eeb":function(t,e,n){var r=n("da84"),o=n("9112"),i=n("5135"),a=n("ce4e"),c=n("8925"),s=n("69f3"),u=s.get,f=s.enforce,l=String(String).split("String");(t.exports=function(t,e,n,c){var s=!!c&&!!c.unsafe,u=!!c&&!!c.enumerable,p=!!c&&!!c.noTargetGet;"function"==typeof n&&("string"!=typeof e||i(n,"name")||o(n,"name",e),f(n).source=l.join("string"==typeof e?e:"")),t!==r?(s?!p&&t[e]&&(u=!0):delete t[e],u?t[e]=n:o(t,e,n)):u?t[e]=n:a(e,n)})(Function.prototype,"toString",(function(){return"function"==typeof this&&u(this).source||c(this)}))},"6f53":function(t,e,n){var r=n("83ab"),o=n("df75"),i=n("fc6a"),a=n("d1e7").f,c=function(t){return function(e){var n,c=i(e),s=o(c),u=s.length,f=0,l=[];while(u>f)n=s[f++],r&&!a.call(c,n)||l.push(t?[n,c[n]]:c[n]);return l}};t.exports={entries:c(!0),values:c(!1)}},7156:function(t,e,n){var r=n("861d"),o=n("d2bb");t.exports=function(t,e,n){var i,a;return o&&"function"==typeof(i=e.constructor)&&i!==n&&r(a=i.prototype)&&a!==n.prototype&&o(t,a),t}},7418:function(t,e){e.f=Object.getOwnPropertySymbols},"746f":function(t,e,n){var r=n("428f"),o=n("5135"),i=n("e538"),a=n("9bf2").f;t.exports=function(t){var e=r.Symbol||(r.Symbol={});o(e,t)||a(e,t,{value:i.f(t)})}},7839:function(t,e){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},"7a77":function(t,e,n){"use strict";function r(t){this.message=t}r.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},r.prototype.__CANCEL__=!0,t.exports=r},"7aac":function(t,e,n){"use strict";var r=n("c532");t.exports=r.isStandardBrowserEnv()?function(){return{write:function(t,e,n,o,i,a){var c=[];c.push(t+"="+encodeURIComponent(e)),r.isNumber(n)&&c.push("expires="+new Date(n).toGMTString()),r.isString(o)&&c.push("path="+o),r.isString(i)&&c.push("domain="+i),!0===a&&c.push("secure"),document.cookie=c.join("; ")},read:function(t){var e=document.cookie.match(new RegExp("(^|;\\s*)("+t+")=([^;]*)"));return e?decodeURIComponent(e[3]):null},remove:function(t){this.write(t,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},"7b0b":function(t,e,n){var r=n("1d80");t.exports=function(t){return Object(r(t))}},"7c73":function(t,e,n){var r,o=n("825a"),i=n("37e8"),a=n("7839"),c=n("d012"),s=n("1be4"),u=n("cc12"),f=n("f772"),l=">",p="<",d="prototype",h="script",v=f("IE_PROTO"),g=function(){},y=function(t){return p+h+l+t+p+"/"+h+l},b=function(t){t.write(y("")),t.close();var e=t.parentWindow.Object;return t=null,e},m=function(){var t,e=u("iframe"),n="java"+h+":";return e.style.display="none",s.appendChild(e),e.src=String(n),t=e.contentWindow.document,t.open(),t.write(y("document.F=Object")),t.close(),t.F},w=function(){try{r=document.domain&&new ActiveXObject("htmlfile")}catch(e){}w=r?b(r):m();var t=a.length;while(t--)delete w[d][a[t]];return w()};c[v]=!0,t.exports=Object.create||function(t,e){var n;return null!==t?(g[d]=o(t),n=new g,g[d]=null,n[v]=t):n=w(),void 0===e?n:i(n,e)}},"7f9a":function(t,e,n){var r=n("da84"),o=n("8925"),i=r.WeakMap;t.exports="function"===typeof i&&/native code/.test(o(i))},"825a":function(t,e,n){var r=n("861d");t.exports=function(t){if(!r(t))throw TypeError(String(t)+" is not an object");return t}},"83ab":function(t,e,n){var r=n("d039");t.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},"83b9":function(t,e,n){"use strict";var r=n("d925"),o=n("e683");t.exports=function(t,e){return t&&!r(e)?o(t,e):e}},8418:function(t,e,n){"use strict";var r=n("c04e"),o=n("9bf2"),i=n("5c6c");t.exports=function(t,e,n){var a=r(e);a in t?o.f(t,a,i(0,n)):t[a]=n}},"848b":function(t,e,n){"use strict";var r=n("5cce").version,o={};["object","boolean","number","function","string","symbol"].forEach((function(t,e){o[t]=function(n){return typeof n===t||"a"+(e<1?"n ":" ")+t}}));var i={};function a(t,e,n){if("object"!==typeof t)throw new TypeError("options must be an object");var r=Object.keys(t),o=r.length;while(o-- >0){var i=r[o],a=e[i];if(a){var c=t[i],s=void 0===c||a(c,i,t);if(!0!==s)throw new TypeError("option "+i+" must be "+s)}else if(!0!==n)throw Error("Unknown option "+i)}}o.transitional=function(t,e,n){function o(t,e){return"[Axios v"+r+"] Transitional option '"+t+"'"+e+(n?". "+n:"")}return function(n,r,a){if(!1===t)throw new Error(o(r," has been removed"+(e?" in "+e:"")));return e&&!i[r]&&(i[r]=!0,console.warn(o(r," has been deprecated since v"+e+" and will be removed in the near future"))),!t||t(n,r,a)}},t.exports={assertOptions:a,validators:o}},"861d":function(t,e){t.exports=function(t){return"object"===typeof t?null!==t:"function"===typeof t}},8875:function(t,e,n){var r,o,i;(function(n,a){o=[],r=a,i="function"===typeof r?r.apply(e,o):r,void 0===i||(t.exports=i)})("undefined"!==typeof self&&self,(function(){function t(){if(document.currentScript)return document.currentScript;try{throw new Error}catch(l){var t,e,n,r=/.*at [^(]*\((.*):(.+):(.+)\)$/gi,o=/@([^@]*):(\d+):(\d+)\s*$/gi,i=r.exec(l.stack)||o.exec(l.stack),a=i&&i[1]||!1,c=i&&i[2]||!1,s=document.location.href.replace(document.location.hash,""),u=document.getElementsByTagName("script");a===s&&(t=document.documentElement.outerHTML,e=new RegExp("(?:[^\\n]+?\\n){0,"+(c-2)+"}[^<]*