├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── dist ├── vuejs-paginator.js ├── vuejs-paginator.js.map └── vuejs-paginator.min.js ├── package.json ├── src ├── VPaginator.vue ├── index.js └── utils.js ├── test └── unit │ ├── .eslintrc │ ├── index.js │ ├── karma.conf.js │ └── specs │ ├── VPaginator.spec.js │ ├── data.js │ └── utils.spec.js ├── webpack.build.js ├── webpack.build.min.js └── webpack.config.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "eslint-plugin-html" // https://github.com/BenoitZugmeyer/eslint-plugin-html 4 | ], 5 | "parser": "babel-eslint", 6 | "env": { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments 7 | "browser": true, // browser global variables 8 | "node": true // Node.js global variables and Node.js-specific rules 9 | }, 10 | "ecmaFeatures": { 11 | "arrowFunctions": true, 12 | "blockBindings": true, 13 | "classes": true, 14 | "defaultParams": true, 15 | "destructuring": true, 16 | "forOf": true, 17 | "generators": false, 18 | "modules": true, 19 | "objectLiteralComputedProperties": true, 20 | "objectLiteralDuplicateProperties": false, 21 | "objectLiteralShorthandMethods": true, 22 | "objectLiteralShorthandProperties": true, 23 | "spread": true, 24 | "superInFunctions": true, 25 | "templateStrings": true, 26 | "jsx": false 27 | }, 28 | "rules": { 29 | /** 30 | * Strict mode 31 | */ 32 | // babel inserts "use strict"; for us 33 | "strict": [2, "never"], // http://eslint.org/docs/rules/strict 34 | 35 | /** 36 | * ES6 37 | */ 38 | "no-var": 1, // http://eslint.org/docs/rules/no-var 39 | "prefer-const": 1, // http://eslint.org/docs/rules/prefer-const 40 | 41 | /** 42 | * Variables 43 | */ 44 | "no-shadow": 2, // http://eslint.org/docs/rules/no-shadow 45 | "no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names 46 | "no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars 47 | "vars": "local", 48 | "args": "after-used" 49 | }], 50 | "no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define 51 | 52 | /** 53 | * Possible errors 54 | */ 55 | "comma-dangle": 0, 56 | "no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign 57 | "no-console": 1, // http://eslint.org/docs/rules/no-console 58 | "no-debugger": 1, // http://eslint.org/docs/rules/no-debugger 59 | "no-alert": 1, // http://eslint.org/docs/rules/no-alert 60 | "no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition 61 | "no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys 62 | "no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case 63 | "no-empty": 2, // http://eslint.org/docs/rules/no-empty 64 | "no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign 65 | "no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast 66 | "no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi 67 | "no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign 68 | "no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations 69 | "no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp 70 | "no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace 71 | "no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls 72 | "no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays 73 | "no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable 74 | "use-isnan": 2, // http://eslint.org/docs/rules/use-isnan 75 | "block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var 76 | 77 | /** 78 | * Best practices 79 | */ 80 | "consistent-return": 2, // http://eslint.org/docs/rules/consistent-return 81 | "curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly 82 | "default-case": 2, // http://eslint.org/docs/rules/default-case 83 | "dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation 84 | "allowKeywords": true 85 | }], 86 | "eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq 87 | "guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in 88 | "no-caller": 2, // http://eslint.org/docs/rules/no-caller 89 | "no-else-return": 2, // http://eslint.org/docs/rules/no-else-return 90 | "no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null 91 | "no-eval": 2, // http://eslint.org/docs/rules/no-eval 92 | "no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native 93 | "no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind 94 | "no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough 95 | "no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal 96 | "no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval 97 | "no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks 98 | "no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func 99 | "no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str 100 | "no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign 101 | "no-new": 0, 102 | "no-new-func": 2, // http://eslint.org/docs/rules/no-new-func 103 | "no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers 104 | "no-octal": 2, // http://eslint.org/docs/rules/no-octal 105 | "no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape 106 | "no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign 107 | "no-proto": 2, // http://eslint.org/docs/rules/no-proto 108 | "no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare 109 | "no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign 110 | "no-script-url": 2, // http://eslint.org/docs/rules/no-script-url 111 | "no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare 112 | "no-sequences": 2, // http://eslint.org/docs/rules/no-sequences 113 | "no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal 114 | "no-with": 2, // http://eslint.org/docs/rules/no-with 115 | "radix": 2, // http://eslint.org/docs/rules/radix 116 | "vars-on-top": 2, // http://eslint.org/docs/rules/vars-on-top 117 | "wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife 118 | "yoda": 2, // http://eslint.org/docs/rules/yoda 119 | 120 | /** 121 | * Style 122 | */ 123 | "brace-style": [2, // http://eslint.org/docs/rules/brace-style 124 | "1tbs", { 125 | "allowSingleLine": true 126 | }], 127 | "quotes": [ 128 | 2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes 129 | ], 130 | "camelcase": [2, { // http://eslint.org/docs/rules/camelcase 131 | "properties": "never" 132 | }], 133 | "comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing 134 | "before": false, 135 | "after": true 136 | }], 137 | "comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style 138 | "eol-last": 2, // http://eslint.org/docs/rules/eol-last 139 | "func-names": 1, // http://eslint.org/docs/rules/func-names 140 | "key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing 141 | "beforeColon": false, 142 | "afterColon": true 143 | }], 144 | "new-cap": [2, { // http://eslint.org/docs/rules/new-cap 145 | "newIsCap": true 146 | }], 147 | "no-multiple-empty-lines": 0, 148 | "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary 149 | "no-new-object": 2, // http://eslint.org/docs/rules/no-new-object 150 | "no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func 151 | "no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces 152 | "no-extra-parens": [2, "functions"], // http://eslint.org/docs/rules/no-extra-parens 153 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle 154 | "one-var": [2, "never"], // http://eslint.org/docs/rules/one-var 155 | "padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks 156 | "semi": [2, "never"], // http://eslint.org/docs/rules/semi 157 | "semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing 158 | "before": false, 159 | "after": true 160 | }], 161 | "space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords 162 | "space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks 163 | "space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren 164 | "space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops 165 | "space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case 166 | "spaced-comment": [2, "always", {// http://eslint.org/docs/rules/spaced-comment 167 | "exceptions": ["-", "+"], 168 | "markers": ["=", "!"] // space here to support sprockets directives 169 | }] 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | npm-debug.log 4 | static 5 | build 6 | .idea 7 | yarn.lock 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Alex Kyriakidis 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue.js Paginator [![CircleCI](https://circleci.com/gh/hootlex/vuejs-paginator.svg?style=shield&circle-token=:circle-ci-badge-token)](https://circleci.com/gh/hootlex/vuejs-paginator) [![npm downloads](https://img.shields.io/npm/dt/vuejs-paginator.svg)](https://www.npmjs.com/package/vuejs-paginator) Version [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](LICENSE) 2 | > A Vue.js plugin to easily integrate pagination in your projects. 3 | 4 | VueJs Paginator is a simple but powerful plugin since it gives you access on how to render the data, instead of using a predefined table. 5 | 6 | ![vue paginator preview](http://i.imgur.com/2jah1qt.gif) 7 | 8 | The way you use it is similar to Laravel's paginator. 9 | 10 | ## Installation 11 | > For Vue 1.* use [v1.0.15](https://github.com/hootlex/vuejs-paginator/tree/v1.0.15). 12 | 13 | ### Through npm 14 | ``` bash 15 | npm install vuejs-paginator --save 16 | ``` 17 | 18 | ### From a cdn 19 | ```HTML 20 | 21 | 22 | 23 | ``` 24 | 25 | ## Usage 26 | Use VPaginator in the HTML. 27 | ```html 28 | 29 | ``` 30 | 31 | Prepare the Vue instance. 32 | ```js 33 | // if you are not using the cdn version you have to import VuePaginator. 34 | // import VuePaginator from 'vuejs-paginator' 35 | new Vue({ 36 | data () { 37 | return { 38 | animals: [] 39 | } 40 | }, 41 | components: { 42 | VPaginator: VuePaginator 43 | }, 44 | methods: { 45 | updateResource(data){ 46 | this.animals = data 47 | } 48 | } 49 | ... 50 | }); 51 | ``` 52 | 53 | ### Thats it 54 | 55 | Every time a page is changed or fetched, resource variable will contain the returned data. 56 | 57 | ```html 58 | 63 | ``` 64 | 65 | ### Documentation 66 | [Here you can find the detailed Documentation](http://hootlex.github.io/vuejs-paginator/) 67 | 68 | ## Build Setup 69 | 70 | ``` bash 71 | # install dependencies 72 | npm install 73 | 74 | # build for production with minification 75 | npm run build 76 | 77 | # run unit tests 78 | npm run unit 79 | # run all tests 80 | npm test 81 | ``` 82 | -------------------------------------------------------------------------------- /dist/vuejs-paginator.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["VuePaginator"] = factory(); 8 | else 9 | root["VuePaginator"] = factory(); 10 | })(this, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | /******/ 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | /******/ 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | /******/ 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | /******/ 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | /******/ 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | /******/ 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | /******/ 39 | /******/ 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | /******/ 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | /******/ 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = ""; 48 | /******/ 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ (function(module, exports, __webpack_require__) { 56 | 57 | 'use strict'; 58 | 59 | var _VPaginator = __webpack_require__(2); 60 | 61 | var _VPaginator2 = _interopRequireDefault(_VPaginator); 62 | 63 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 64 | 65 | module.exports = _VPaginator2.default; 66 | 67 | /***/ }), 68 | /* 1 */, 69 | /* 2 */ 70 | /***/ (function(module, exports, __webpack_require__) { 71 | 72 | module.exports = __webpack_require__(3) 73 | 74 | if (module.exports.__esModule) module.exports = module.exports.default 75 | ;(typeof module.exports === "function" ? module.exports.options : module.exports).template = __webpack_require__(5) 76 | if (false) { 77 | (function () { 78 | var hotAPI = require("vue-hot-reload-api") 79 | hotAPI.install(require("vue")) 80 | if (!hotAPI.compatible) return 81 | var id = "-!babel!../node_modules/vue-loader/lib/selector.js?type=script&index=0!./VPaginator.vue" 82 | hotAPI.createRecord(id, module.exports) 83 | module.hot.accept(["-!babel!../node_modules/vue-loader/lib/selector.js?type=script&index=0!./VPaginator.vue","-!vue-html-loader!../node_modules/vue-loader/lib/selector.js?type=template&index=0!./VPaginator.vue"], function () { 84 | var newOptions = require("-!babel!../node_modules/vue-loader/lib/selector.js?type=script&index=0!./VPaginator.vue") 85 | if (newOptions && newOptions.__esModule) newOptions = newOptions.default 86 | var newTemplate = require("-!vue-html-loader!../node_modules/vue-loader/lib/selector.js?type=template&index=0!./VPaginator.vue") 87 | hotAPI.update(id, newOptions, newTemplate) 88 | }) 89 | })() 90 | } 91 | 92 | /***/ }), 93 | /* 3 */ 94 | /***/ (function(module, exports, __webpack_require__) { 95 | 96 | 'use strict'; 97 | 98 | Object.defineProperty(exports, "__esModule", { 99 | value: true 100 | }); 101 | 102 | var _utils = __webpack_require__(4); 103 | 104 | exports.default = { 105 | props: { 106 | resource_url: { 107 | type: String, 108 | required: true 109 | }, 110 | custom_template: '', 111 | options: { 112 | type: Object, 113 | required: false, 114 | default: function _default() { 115 | return {}; 116 | } 117 | } 118 | }, 119 | data: function data() { 120 | return { 121 | current_page: '', 122 | last_page: '', 123 | next_page_url: '', 124 | prev_page_url: '', 125 | config: { 126 | remote_data: 'data', 127 | remote_current_page: 'current_page', 128 | remote_last_page: 'last_page', 129 | remote_next_page_url: 'next_page_url', 130 | remote_prev_page_url: 'prev_page_url', 131 | previous_button_text: 'Previous', 132 | next_button_text: 'Next', 133 | classes_prev: 'btn btn-default', 134 | classes_next: 'btn btn-default' 135 | } 136 | }; 137 | }, 138 | 139 | methods: { 140 | fetchData: function fetchData(pageUrl) { 141 | this.$emit("request_start"); 142 | pageUrl = pageUrl || this.resource_url; 143 | var self = this; 144 | var config = {}; 145 | if (this.config.headers) { 146 | config.headers = this.config.headers; 147 | } 148 | this.$http.get(pageUrl, config).then(function (response) { 149 | self.$emit("request_finish", response); 150 | self.handleResponseData(response.data); 151 | }).catch(function (response) { 152 | self.$emit("request_error", response); 153 | console.log('Fetching data failed.', response); 154 | }); 155 | }, 156 | handleResponseData: function handleResponseData(response) { 157 | this.makePagination(response); 158 | var data = _utils.utils.getNestedValue(response, this.config.remote_data); 159 | this.$emit('update', data); 160 | }, 161 | makePagination: function makePagination(data) { 162 | this.current_page = _utils.utils.getNestedValue(data, this.config.remote_current_page); 163 | this.last_page = _utils.utils.getNestedValue(data, this.config.remote_last_page); 164 | this.next_page_url = this.current_page === this.last_page ? null : _utils.utils.getNestedValue(data, this.config.remote_next_page_url); 165 | this.prev_page_url = this.current_page === 1 ? null : _utils.utils.getNestedValue(data, this.config.remote_prev_page_url); 166 | }, 167 | initConfig: function initConfig() { 168 | this.config = _utils.utils.merge_objects(this.config, this.options); 169 | } 170 | }, 171 | watch: { 172 | resource_url: function resource_url() { 173 | this.fetchData(); 174 | } 175 | }, 176 | created: function created() { 177 | this.initConfig(); 178 | this.fetchData(); 179 | } 180 | }; 181 | // 182 | // 193 | 194 | // \n\n\n\n// WEBPACK FOOTER //\n// ./src/VPaginator.vue","const merge_objects = (obj1, obj2) => {\n let obj3 = {};\n for (let attrname in obj1) { obj3[attrname] = obj1[attrname]; }\n for (let attrname in obj2) { obj3[attrname] = obj2[attrname]; }\n return obj3;\n}\n\nconst getNestedValue = (obj, path) => {\n let originalPath = path\n path = path.split('.')\n let res = obj\n for (let i = 0; i < path.length; i++) {\n res = res[path[i]]\n }\n if(typeof res == 'undefined') console.log(`[VuePaginator] Response doesn't contain key ${originalPath}!`)\n return res\n}\n\nexport const utils = { merge_objects, getNestedValue }\n\n\n\n// WEBPACK FOOTER //\n// ./src/utils.js","module.exports = \"
\\n \\n Page {{current_page}} of {{last_page}}\\n \\n
\";\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/vue-html-loader!./~/vue-loader/lib/selector.js?type=template&index=0!./src/VPaginator.vue\n// module id = 5\n// module chunks = 0"],"sourceRoot":""} -------------------------------------------------------------------------------- /dist/vuejs-paginator.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.VuePaginator=t():e.VuePaginator=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}var o=n(2),a=r(o);e.exports=a.default},,function(e,t,n){e.exports=n(3),e.exports.__esModule&&(e.exports=e.exports.default),("function"==typeof e.exports?e.exports.options:e.exports).template=n(5)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(4);t.default={props:{resource_url:{type:String,required:!0},custom_template:"",options:{type:Object,required:!1,default:function(){return{}}}},data:function(){return{current_page:"",last_page:"",next_page_url:"",prev_page_url:"",config:{remote_data:"data",remote_current_page:"current_page",remote_last_page:"last_page",remote_next_page_url:"next_page_url",remote_prev_page_url:"prev_page_url",previous_button_text:"Previous",next_button_text:"Next",classes_prev:"btn btn-default",classes_next:"btn btn-default"}}},methods:{fetchData:function(e){this.$emit("request_start"),e=e||this.resource_url;var t=this,n={};this.config.headers&&(n.headers=this.config.headers),this.$http.get(e,n).then(function(e){t.$emit("request_finish",e),t.handleResponseData(e.data)}).catch(function(e){t.$emit("request_error",e),console.log("Fetching data failed.",e)})},handleResponseData:function(e){this.makePagination(e);var t=r.utils.getNestedValue(e,this.config.remote_data);this.$emit("update",t)},makePagination:function(e){this.current_page=r.utils.getNestedValue(e,this.config.remote_current_page),this.last_page=r.utils.getNestedValue(e,this.config.remote_last_page),this.next_page_url=this.current_page===this.last_page?null:r.utils.getNestedValue(e,this.config.remote_next_page_url),this.prev_page_url=1===this.current_page?null:r.utils.getNestedValue(e,this.config.remote_prev_page_url)},initConfig:function(){this.config=r.utils.merge_objects(this.config,this.options)}},watch:{resource_url:function(){this.fetchData()}},created:function(){this.initConfig(),this.fetchData()}}},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e,t){var n={};for(var r in e)n[r]=e[r];for(var o in t)n[o]=t[o];return n},r=function(e,t){var n=t;t=t.split(".");for(var r=e,o=0;o Page {{current_page}} of {{last_page}} "}])}); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuejs-paginator", 3 | "version": "2.0.2", 4 | "description": "A Vue.js plugin to easily integrate pagination.", 5 | "main": "dist/vuejs-paginator.js", 6 | "author": "Alex Kyriakidis ", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "hootlex/vuejs-paginator" 11 | }, 12 | "homepage": "", 13 | "directories": { 14 | "src": "src/" 15 | }, 16 | "dependencies": { 17 | "vue": "^2.0.0", 18 | "vue-resource": "^1.0.3" 19 | }, 20 | "files": [ 21 | "LICENSE", 22 | "README.md", 23 | "src", 24 | "dist" 25 | ], 26 | "keywords": [ 27 | "pagination", 28 | "vuejs-pagination", 29 | "vuejs-laravel", 30 | "vuejs" 31 | ], 32 | "devDependencies": { 33 | "babel-core": "^6.1.21", 34 | "babel-loader": "^6.1.0", 35 | "babel-plugin-transform-runtime": "^6.1.18", 36 | "babel-preset-es2015": "^6.1.18", 37 | "babel-runtime": "^6.3.19", 38 | "chai": "^3.5.0", 39 | "css-loader": "^0.23.1", 40 | "function-bind": "^1.1.0", 41 | "isparta-loader": "^2.0.0", 42 | "karma": "^1.3.0", 43 | "karma-coverage": "^0.5.5", 44 | "karma-mocha": "^0.2.2", 45 | "karma-phantomjs-launcher": "^1.0.0", 46 | "karma-sinon-chai": "^1.2.0", 47 | "karma-sourcemap-loader": "^0.3.7", 48 | "karma-spec-reporter": "0.0.24", 49 | "karma-webpack": "^1.8.0", 50 | "lolex": "^1.5.0", 51 | "mocha": "^2.5.3", 52 | "node-sass": "^3.7.0", 53 | "ora": "^0.2.0", 54 | "phantomjs-prebuilt": "^2.1.7", 55 | "prismjs": "^1.3.0", 56 | "sass-loader": "^3.2.0", 57 | "sinon": "^1.17.4", 58 | "sinon-chai": "^2.8.0", 59 | "style-loader": "^0.13.1", 60 | "vue-hot-reload-api": "^1.2.0", 61 | "vue-html-loader": "^1.0.0", 62 | "vue-loader": "7.1.7", 63 | "vue-style-loader": "^1.0.0", 64 | "webpack": "^1.12.2", 65 | "webpack-dev-server": "^1.12.0", 66 | "webpack-merge": "^0.13.0" 67 | }, 68 | "scripts": { 69 | "build": "webpack --progress --hide-modules --config webpack.build.min.js && webpack --progress --hide-modules --config webpack.build.js", 70 | "unit": "karma start test/unit/karma.conf.js --single-run", 71 | "test": "npm run build && npm run unit" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/VPaginator.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 93 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import VPaginator from './VPaginator.vue' 2 | 3 | module.exports = VPaginator 4 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const merge_objects = (obj1, obj2) => { 2 | let obj3 = {}; 3 | for (let attrname in obj1) { obj3[attrname] = obj1[attrname]; } 4 | for (let attrname in obj2) { obj3[attrname] = obj2[attrname]; } 5 | return obj3; 6 | } 7 | 8 | const getNestedValue = (obj, path) => { 9 | let originalPath = path 10 | path = path.split('.') 11 | let res = obj 12 | for (let i = 0; i < path.length; i++) { 13 | res = res[path[i]] 14 | } 15 | if(typeof res == 'undefined') console.log(`[VuePaginator] Response doesn't contain key ${originalPath}!`) 16 | return res 17 | } 18 | 19 | export const utils = { merge_objects, getNestedValue } 20 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | // Polyfill fn.bind() for PhantomJS 2 | /* eslint-disable no-extend-native */ 3 | Function.prototype.bind = require('function-bind') 4 | 5 | // require all test files (files that ends with .spec.js) 6 | var testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except main.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | var srcContext = require.context('../../src', true, /^\.\/(?!idex(\.js)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // This is a karma config file. For more details see 2 | // http://karma-runner.github.io/0.13/config/configuration-file.html 3 | // we are also using it with karma-webpack 4 | // https://github.com/webpack/karma-webpack 5 | 6 | var path = require('path') 7 | var merge = require('webpack-merge') 8 | var baseConfig = require('../../webpack.build') 9 | var webpack = require('webpack') 10 | var projectRoot = path.resolve(__dirname, '../../') 11 | 12 | var webpackConfig = merge(baseConfig, { 13 | // use inline sourcemap for karma-sourcemap-loader 14 | devtool: '#inline-source-map', 15 | vue: { 16 | loaders: { 17 | js: 'isparta' 18 | } 19 | }, 20 | plugins: [ 21 | new webpack.DefinePlugin({ 22 | 'process.env': {NODE_ENV: '"testing"'} 23 | }) 24 | ] 25 | }) 26 | 27 | // no need for app entry during tests 28 | delete webpackConfig.entry 29 | 30 | // make sure isparta loader is applied before eslint 31 | webpackConfig.module.preLoaders = webpackConfig.module.preLoaders || [] 32 | webpackConfig.module.preLoaders.unshift({ 33 | test: /\.js$/, 34 | loader: 'isparta', 35 | include: path.resolve(projectRoot, 'src') 36 | }) 37 | 38 | // only apply babel for test files when using isparta 39 | webpackConfig.module.loaders.some(function (loader, i) { 40 | if (loader.loader === 'babel') { 41 | loader.include = path.resolve(projectRoot, 'test/unit') 42 | return true 43 | } 44 | }) 45 | 46 | module.exports = function (config) { 47 | config.set({ 48 | // to run in additional browsers: 49 | // 1. install corresponding karma launcher 50 | // http://karma-runner.github.io/0.13/config/browsers.html 51 | // 2. add it to the `browsers` array below. 52 | browsers: ['PhantomJS'], 53 | frameworks: ['mocha', 'sinon-chai'], 54 | reporters: ['spec'], 55 | files: ['./index.js'], 56 | preprocessors: { 57 | './index.js': ['webpack', 'sourcemap'] 58 | }, 59 | webpack: webpackConfig, 60 | webpackMiddleware: { 61 | noInfo: true 62 | } 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /test/unit/specs/VPaginator.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue/dist/vue.js' 2 | import VueResource from 'vue-resource' 3 | import VPaginator from 'dist/vuejs-paginator' 4 | import {mockedResponse, options} from './data.js' 5 | Vue.use(VueResource) 6 | 7 | describe('VPaginator.vue', () => { 8 | it('should render the paginator', () => { 9 | const vm = new Vue({ 10 | data: { dummies: [] }, 11 | template: '
', 12 | components: { VPaginator } 13 | }).$mount() 14 | expect(vm.$el.querySelector('.v-paginator').textContent).to.contain('Previous') 15 | }) 16 | it('should render the paginator with custom button texts', () => { 17 | const options = { previous_button_text: 'Go back' } 18 | const vm = new Vue({ 19 | data: { dummies: [], options: options }, 20 | template: '
', 21 | components: { VPaginator } 22 | }).$mount() 23 | expect(vm.$el.querySelector('.v-paginator').textContent).to.contain('Go back') 24 | }) 25 | it('should render the paginator with custom classes', () => { 26 | const options = { classes_next: 'btn btn-primary' } 27 | const vm = new Vue({ 28 | data: { dummies: [], options: options }, 29 | template: '
', 30 | components: { VPaginator } 31 | }).$mount() 32 | expect(vm.$el.querySelector('.btn-primary').textContent).to.contain('Next') 33 | }) 34 | it('should set pagination data correctly', () => { 35 | const vm = new Vue({ 36 | data: { dummies: [], options: options }, 37 | template: '
', 38 | components: { VPaginator } 39 | }).$mount() 40 | vm.$children[0].handleResponseData(mockedResponse) 41 | // expect that pagination data have been set correctly 42 | expect(vm.$children[0].next_page_url).to.equal(mockedResponse.nested.next_page_url) 43 | expect(vm.$children[0].next_page_url).to.not.equal('something else') 44 | expect(vm.$children[0].prev_page_url).to.equal(mockedResponse.nested.prev_page_url) 45 | expect(vm.$children[0].current_page).to.equal(mockedResponse.nested.current_page) 46 | expect(vm.$children[0].last_page).to.equal(mockedResponse.nested.last_page) 47 | }) 48 | it('should emit update after fetching data', () => { 49 | var resource = [] 50 | const vm = new Vue({ 51 | data: { dummies: [], options: options }, 52 | template: '
', 53 | components: { VPaginator }, 54 | methods: { 55 | updateResource (data) { 56 | resource = data 57 | } 58 | } 59 | }).$mount() 60 | vm.$children[0].handleResponseData(mockedResponse) 61 | // check that response data have been reflected to current instance 62 | expect(resource).to.have.length(5) 63 | }) 64 | it('should merge options with default config', () => { 65 | const options = { previous_button_text: 'Go back' } 66 | const vm = new Vue({ 67 | data: { dummies: [], options: options }, 68 | template: '
', 69 | components: { VPaginator } 70 | }).$mount() 71 | vm.$children[0].initConfig() 72 | // check that response data have been reflected to current instance 73 | expect(vm.$children[0].config.previous_button_text).to.equal('Go back') 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /test/unit/specs/data.js: -------------------------------------------------------------------------------- 1 | export const mockedResponse = { 2 | 'nested': { 3 | 'current_page': 1, 4 | 'last_page': 3, 5 | 'next_page_url': '/responses/dummy2.json', 6 | 'prev_page_url': null, 7 | 'data': [ 8 | { 9 | 'id': 1, 10 | 'plot': 'Anakin, you\'re breaking my heart! And you\'re going down a path I cannot follow!', 11 | 'upvotes': '490', 12 | 'writer': 'Padme' 13 | }, 14 | { 15 | 'id': 2, 16 | 'plot': 'One thing\'s for sure, we\'re all going to be a lot thinner.', 17 | 'upvotes': '752', 18 | 'writer': 'Han Solo' 19 | }, 20 | { 21 | 'id': 3, 22 | 'plot': 'At last we will reveal ourselves to the Jedi. At last we will have revenge.', 23 | 'upvotes': '193', 24 | 'writer': 'Darth Maul' 25 | }, 26 | { 27 | 'id': 4, 28 | 'plot': 'The Dark Side of the Force is the pathway to many abilities some consider to be… Unnatural.', 29 | 'upvotes': '578', 30 | 'writer': 'Senator Palpatine' 31 | }, 32 | { 33 | 'id': 5, 34 | 'plot': 'You can’t win, Darth. Strike me down, and I will become more powerful than you could possibly imagine.', 35 | 'upvotes': '256', 36 | 'writer': 'Obi Wan Kenobi' 37 | } 38 | ] 39 | } 40 | } 41 | 42 | export const options = { 43 | remote_data: 'nested.data', 44 | remote_current_page: 'nested.current_page', 45 | remote_last_page: 'nested.last_page', 46 | remote_next_page_url: 'nested.next_page_url', 47 | remote_prev_page_url: 'nested.prev_page_url' 48 | } 49 | -------------------------------------------------------------------------------- /test/unit/specs/utils.spec.js: -------------------------------------------------------------------------------- 1 | import {utils} from 'src/utils.js' 2 | 3 | describe('utils', () => { 4 | it('should export nested value from object', () => { 5 | const object = { nested: { inside: { object: { value: 'foo' } } } } 6 | const value = utils.getNestedValue(object, 'nested.inside.object.value') 7 | expect(value).to.equal('foo') 8 | }) 9 | it('should merge two objects', () => { 10 | const alex = { name: 'alex', years:23 } 11 | const kostas = { name: 'kostas', kilos:60 } 12 | const merged = utils.merge_objects(alex, kostas) 13 | const expected = { name: 'kostas', years:23, kilos:60 } 14 | expect(merged.name).to.equal(expected.name) 15 | expect(merged.years).to.equal(expected.years) 16 | expect(merged.kilos).to.equal(expected.kilos) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /webpack.build.js: -------------------------------------------------------------------------------- 1 | var config = require('./webpack.config.js') 2 | 3 | config.entry = { 4 | 'vuejs-paginator': './src/index.js', 5 | } 6 | 7 | config.output = { 8 | filename: './dist/[name].js', 9 | library: 'VuePaginator', 10 | libraryTarget: 'umd' 11 | } 12 | 13 | 14 | module.exports = config 15 | -------------------------------------------------------------------------------- /webpack.build.min.js: -------------------------------------------------------------------------------- 1 | var config = require('./webpack.build.js') 2 | var webpack = require('webpack') 3 | 4 | 5 | config.output.filename = config.output.filename.replace(/\.js$/, '.min.js') 6 | 7 | delete config.devtool 8 | 9 | config.plugins = [ 10 | new webpack.optimize.UglifyJsPlugin({ 11 | sourceMap: false, 12 | drop_console: true, 13 | compress: { 14 | warnings: false 15 | } 16 | }) 17 | ] 18 | 19 | module.exports = config 20 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | var path = require('path') 3 | 4 | module.exports = { 5 | entry: './docs/index.js', 6 | output: { 7 | path: './build', 8 | publicPath: '/build/', 9 | filename: 'build-docs.js' 10 | }, 11 | resolve: { 12 | root: path.resolve('./') 13 | }, 14 | module: { 15 | loaders: [ 16 | {test: /\.vue$/, loader: 'vue' }, 17 | { 18 | test: /\.js$/, 19 | exclude: /node_modules|vue\/src|vue-router\/|vue-loader\/|vue-hot-reload-api\//, 20 | loader: 'babel' 21 | }, 22 | { test: /\.css$/, loader: "style-loader!css-loader?root=./docs/" }, 23 | {test: /\.scss$/, loader: "style!css!sass"}, 24 | {test: /\.less$/, loader: "style-loader!css-loader!less-loader"}, 25 | ] 26 | }, 27 | babel: { 28 | presets: ['es2015'], 29 | plugins: ['transform-runtime'] 30 | }, 31 | devtool: 'source-map' 32 | }; 33 | 34 | 35 | if (process.env.NODE_ENV === 'production') { 36 | delete module.exports.devtool; 37 | module.exports.plugins = [ 38 | new webpack.DefinePlugin({ 39 | 'process.env': { 40 | NODE_ENV: '"production"' 41 | } 42 | }), 43 | new webpack.optimize.UglifyJsPlugin({ 44 | compress: { 45 | warnings: false 46 | } 47 | }) 48 | ]; 49 | } --------------------------------------------------------------------------------