├── .circleci └── config.yml ├── .gitignore ├── .npmignore ├── babel.config.js ├── dist ├── demo.html ├── vue-modal.common.js ├── vue-modal.common.js.map ├── vue-modal.umd.js ├── vue-modal.umd.js.map ├── vue-modal.umd.min.js └── vue-modal.umd.min.js.map ├── jest.config.js ├── package-lock.json ├── package.json ├── readme.md ├── src └── components │ ├── ExampleComponent.vue │ ├── Modal.vue │ └── index.js ├── test └── unit │ ├── .eslintrc.js │ └── Modal.spec.js └── vue.config.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.0 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/node:latest 6 | steps: 7 | - checkout 8 | - run: 9 | name: Install npm 10 | command: npm i 11 | - run: 12 | name: Unit testing 13 | command: npm run test:unit 14 | - run: 15 | name: Semantic realease 16 | command: npx semantic-release 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | /public 5 | /src/assets 6 | /src/App.vue 7 | /src/main.js 8 | 9 | 10 | # local env files 11 | .env.local 12 | .env.*.local 13 | 14 | # Log files 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | # Editor directories and files 20 | .idea 21 | .vscode 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw* 27 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/App.vue 2 | dist/demo.html -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /dist/demo.html: -------------------------------------------------------------------------------- 1 | 2 | vue-modal demo 3 | 4 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /dist/vue-modal.common.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | /******/ (function(modules) { // webpackBootstrap 3 | /******/ // The module cache 4 | /******/ var installedModules = {}; 5 | /******/ 6 | /******/ // The require function 7 | /******/ function __webpack_require__(moduleId) { 8 | /******/ 9 | /******/ // Check if module is in cache 10 | /******/ if(installedModules[moduleId]) { 11 | /******/ return installedModules[moduleId].exports; 12 | /******/ } 13 | /******/ // Create a new module (and put it into the cache) 14 | /******/ var module = installedModules[moduleId] = { 15 | /******/ i: moduleId, 16 | /******/ l: false, 17 | /******/ exports: {} 18 | /******/ }; 19 | /******/ 20 | /******/ // Execute the module function 21 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 22 | /******/ 23 | /******/ // Flag the module as loaded 24 | /******/ module.l = true; 25 | /******/ 26 | /******/ // Return the exports of the module 27 | /******/ return module.exports; 28 | /******/ } 29 | /******/ 30 | /******/ 31 | /******/ // expose the modules object (__webpack_modules__) 32 | /******/ __webpack_require__.m = modules; 33 | /******/ 34 | /******/ // expose the module cache 35 | /******/ __webpack_require__.c = installedModules; 36 | /******/ 37 | /******/ // define getter function for harmony exports 38 | /******/ __webpack_require__.d = function(exports, name, getter) { 39 | /******/ if(!__webpack_require__.o(exports, name)) { 40 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 41 | /******/ } 42 | /******/ }; 43 | /******/ 44 | /******/ // define __esModule on exports 45 | /******/ __webpack_require__.r = function(exports) { 46 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 47 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 48 | /******/ } 49 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 50 | /******/ }; 51 | /******/ 52 | /******/ // create a fake namespace object 53 | /******/ // mode & 1: value is a module id, require it 54 | /******/ // mode & 2: merge all properties of value into the ns 55 | /******/ // mode & 4: return value when already ns object 56 | /******/ // mode & 8|1: behave like require 57 | /******/ __webpack_require__.t = function(value, mode) { 58 | /******/ if(mode & 1) value = __webpack_require__(value); 59 | /******/ if(mode & 8) return value; 60 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 61 | /******/ var ns = Object.create(null); 62 | /******/ __webpack_require__.r(ns); 63 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 64 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 65 | /******/ return ns; 66 | /******/ }; 67 | /******/ 68 | /******/ // getDefaultExport function for compatibility with non-harmony modules 69 | /******/ __webpack_require__.n = function(module) { 70 | /******/ var getter = module && module.__esModule ? 71 | /******/ function getDefault() { return module['default']; } : 72 | /******/ function getModuleExports() { return module; }; 73 | /******/ __webpack_require__.d(getter, 'a', getter); 74 | /******/ return getter; 75 | /******/ }; 76 | /******/ 77 | /******/ // Object.prototype.hasOwnProperty.call 78 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 79 | /******/ 80 | /******/ // __webpack_public_path__ 81 | /******/ __webpack_require__.p = ""; 82 | /******/ 83 | /******/ 84 | /******/ // Load entry module and return exports 85 | /******/ return __webpack_require__(__webpack_require__.s = "fb15"); 86 | /******/ }) 87 | /************************************************************************/ 88 | /******/ ({ 89 | 90 | /***/ "07b6": 91 | /***/ (function(module, exports, __webpack_require__) { 92 | 93 | exports = module.exports = __webpack_require__("2350")(false); 94 | // imports 95 | 96 | 97 | // module 98 | exports.push([module.i, ".overlay[data-v-03833336]{background-color:rgba(0,0,0,.8);height:100%;width:100%;position:fixed;top:0;bottom:0;right:0;left:0;z-index:9999}.fade-enter-active[data-v-03833336],.fade-leave-active[data-v-03833336]{-webkit-transition:opacity .5s;transition:opacity .5s}.fade-enter[data-v-03833336],.fade-leave-to[data-v-03833336]{opacity:0}", ""]); 99 | 100 | // exports 101 | 102 | 103 | /***/ }), 104 | 105 | /***/ "230e": 106 | /***/ (function(module, exports, __webpack_require__) { 107 | 108 | var isObject = __webpack_require__("d3f4"); 109 | var document = __webpack_require__("7726").document; 110 | // typeof document.createElement is 'object' in old IE 111 | var is = isObject(document) && isObject(document.createElement); 112 | module.exports = function (it) { 113 | return is ? document.createElement(it) : {}; 114 | }; 115 | 116 | 117 | /***/ }), 118 | 119 | /***/ "2350": 120 | /***/ (function(module, exports) { 121 | 122 | /* 123 | MIT License http://www.opensource.org/licenses/mit-license.php 124 | Author Tobias Koppers @sokra 125 | */ 126 | // css base code, injected by the css-loader 127 | module.exports = function(useSourceMap) { 128 | var list = []; 129 | 130 | // return the list of modules as css string 131 | list.toString = function toString() { 132 | return this.map(function (item) { 133 | var content = cssWithMappingToString(item, useSourceMap); 134 | if(item[2]) { 135 | return "@media " + item[2] + "{" + content + "}"; 136 | } else { 137 | return content; 138 | } 139 | }).join(""); 140 | }; 141 | 142 | // import a list of modules into the list 143 | list.i = function(modules, mediaQuery) { 144 | if(typeof modules === "string") 145 | modules = [[null, modules, ""]]; 146 | var alreadyImportedModules = {}; 147 | for(var i = 0; i < this.length; i++) { 148 | var id = this[i][0]; 149 | if(typeof id === "number") 150 | alreadyImportedModules[id] = true; 151 | } 152 | for(i = 0; i < modules.length; i++) { 153 | var item = modules[i]; 154 | // skip already imported module 155 | // this implementation is not 100% perfect for weird media query combinations 156 | // when a module is imported multiple times with different media queries. 157 | // I hope this will never occur (Hey this way we have smaller bundles) 158 | if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { 159 | if(mediaQuery && !item[2]) { 160 | item[2] = mediaQuery; 161 | } else if(mediaQuery) { 162 | item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; 163 | } 164 | list.push(item); 165 | } 166 | } 167 | }; 168 | return list; 169 | }; 170 | 171 | function cssWithMappingToString(item, useSourceMap) { 172 | var content = item[1] || ''; 173 | var cssMapping = item[3]; 174 | if (!cssMapping) { 175 | return content; 176 | } 177 | 178 | if (useSourceMap && typeof btoa === 'function') { 179 | var sourceMapping = toComment(cssMapping); 180 | var sourceURLs = cssMapping.sources.map(function (source) { 181 | return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */' 182 | }); 183 | 184 | return [content].concat(sourceURLs).concat([sourceMapping]).join('\n'); 185 | } 186 | 187 | return [content].join('\n'); 188 | } 189 | 190 | // Adapted from convert-source-map (MIT) 191 | function toComment(sourceMap) { 192 | // eslint-disable-next-line no-undef 193 | var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))); 194 | var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64; 195 | 196 | return '/*# ' + data + ' */'; 197 | } 198 | 199 | 200 | /***/ }), 201 | 202 | /***/ "499e": 203 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 204 | 205 | "use strict"; 206 | __webpack_require__.r(__webpack_exports__); 207 | 208 | // CONCATENATED MODULE: ./node_modules/vue-style-loader/lib/listToStyles.js 209 | /** 210 | * Translates the list format produced by css-loader into something 211 | * easier to manipulate. 212 | */ 213 | function listToStyles (parentId, list) { 214 | var styles = [] 215 | var newStyles = {} 216 | for (var i = 0; i < list.length; i++) { 217 | var item = list[i] 218 | var id = item[0] 219 | var css = item[1] 220 | var media = item[2] 221 | var sourceMap = item[3] 222 | var part = { 223 | id: parentId + ':' + i, 224 | css: css, 225 | media: media, 226 | sourceMap: sourceMap 227 | } 228 | if (!newStyles[id]) { 229 | styles.push(newStyles[id] = { id: id, parts: [part] }) 230 | } else { 231 | newStyles[id].parts.push(part) 232 | } 233 | } 234 | return styles 235 | } 236 | 237 | // CONCATENATED MODULE: ./node_modules/vue-style-loader/lib/addStylesClient.js 238 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return addStylesClient; }); 239 | /* 240 | MIT License http://www.opensource.org/licenses/mit-license.php 241 | Author Tobias Koppers @sokra 242 | Modified by Evan You @yyx990803 243 | */ 244 | 245 | 246 | 247 | var hasDocument = typeof document !== 'undefined' 248 | 249 | if (typeof DEBUG !== 'undefined' && DEBUG) { 250 | if (!hasDocument) { 251 | throw new Error( 252 | 'vue-style-loader cannot be used in a non-browser environment. ' + 253 | "Use { target: 'node' } in your Webpack config to indicate a server-rendering environment." 254 | ) } 255 | } 256 | 257 | /* 258 | type StyleObject = { 259 | id: number; 260 | parts: Array 261 | } 262 | 263 | type StyleObjectPart = { 264 | css: string; 265 | media: string; 266 | sourceMap: ?string 267 | } 268 | */ 269 | 270 | var stylesInDom = {/* 271 | [id: number]: { 272 | id: number, 273 | refs: number, 274 | parts: Array<(obj?: StyleObjectPart) => void> 275 | } 276 | */} 277 | 278 | var head = hasDocument && (document.head || document.getElementsByTagName('head')[0]) 279 | var singletonElement = null 280 | var singletonCounter = 0 281 | var isProduction = false 282 | var noop = function () {} 283 | var options = null 284 | var ssrIdKey = 'data-vue-ssr-id' 285 | 286 | // Force single-tag solution on IE6-9, which has a hard limit on the # of \n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Modal.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Modal.vue?vue&type=script&lang=js&\"","/* globals __VUE_SSR_CONTEXT__ */\n\n// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).\n// This module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle.\n\nexport default function normalizeComponent (\n scriptExports,\n render,\n staticRenderFns,\n functionalTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier, /* server only */\n shadowMode /* vue-cli only */\n) {\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (render) {\n options.render = render\n options.staticRenderFns = staticRenderFns\n options._compiled = true\n }\n\n // functional template\n if (functionalTemplate) {\n options.functional = true\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = 'data-v-' + scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = shadowMode\n ? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }\n : injectStyles\n }\n\n if (hook) {\n if (options.functional) {\n // for template-only hot-reload because in that case the render fn doesn't\n // go through the normalizer\n options._injectStyles = hook\n // register for functioal component in vue file\n var originalRender = options.render\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return originalRender(h, context)\n }\n } else {\n // inject component registration as beforeCreate hook\n var existing = options.beforeCreate\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n }\n }\n\n return {\n exports: scriptExports,\n options: options\n }\n}\n","import { render, staticRenderFns } from \"./Modal.vue?vue&type=template&id=03833336&scoped=true&\"\nimport script from \"./Modal.vue?vue&type=script&lang=js&\"\nexport * from \"./Modal.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Modal.vue?vue&type=style&index=0&id=03833336&scoped=true&lang=scss&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"03833336\",\n null\n \n)\n\nexport default component.exports","import Vue from \"vue\"\nimport Modal from './Modal.vue'\n\nVue.component(Modal.name, Modal)\n\nexport default Modal","import './setPublicPath'\nimport mod from '~entry'\nexport default mod\nexport * from '~entry'\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /dist/vue-modal.umd.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("vue")); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["vue-modal"] = factory(require("vue")); 8 | else 9 | root["vue-modal"] = factory(root["Vue"]); 10 | })((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__8bbf__) { 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 | /******/ i: moduleId, 25 | /******/ l: false, 26 | /******/ exports: {} 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.l = 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 | /******/ // define getter function for harmony exports 47 | /******/ __webpack_require__.d = function(exports, name, getter) { 48 | /******/ if(!__webpack_require__.o(exports, name)) { 49 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 50 | /******/ } 51 | /******/ }; 52 | /******/ 53 | /******/ // define __esModule on exports 54 | /******/ __webpack_require__.r = function(exports) { 55 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 56 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 57 | /******/ } 58 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 59 | /******/ }; 60 | /******/ 61 | /******/ // create a fake namespace object 62 | /******/ // mode & 1: value is a module id, require it 63 | /******/ // mode & 2: merge all properties of value into the ns 64 | /******/ // mode & 4: return value when already ns object 65 | /******/ // mode & 8|1: behave like require 66 | /******/ __webpack_require__.t = function(value, mode) { 67 | /******/ if(mode & 1) value = __webpack_require__(value); 68 | /******/ if(mode & 8) return value; 69 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 70 | /******/ var ns = Object.create(null); 71 | /******/ __webpack_require__.r(ns); 72 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 73 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 74 | /******/ return ns; 75 | /******/ }; 76 | /******/ 77 | /******/ // getDefaultExport function for compatibility with non-harmony modules 78 | /******/ __webpack_require__.n = function(module) { 79 | /******/ var getter = module && module.__esModule ? 80 | /******/ function getDefault() { return module['default']; } : 81 | /******/ function getModuleExports() { return module; }; 82 | /******/ __webpack_require__.d(getter, 'a', getter); 83 | /******/ return getter; 84 | /******/ }; 85 | /******/ 86 | /******/ // Object.prototype.hasOwnProperty.call 87 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 88 | /******/ 89 | /******/ // __webpack_public_path__ 90 | /******/ __webpack_require__.p = ""; 91 | /******/ 92 | /******/ 93 | /******/ // Load entry module and return exports 94 | /******/ return __webpack_require__(__webpack_require__.s = "fb15"); 95 | /******/ }) 96 | /************************************************************************/ 97 | /******/ ({ 98 | 99 | /***/ "07b6": 100 | /***/ (function(module, exports, __webpack_require__) { 101 | 102 | exports = module.exports = __webpack_require__("2350")(false); 103 | // imports 104 | 105 | 106 | // module 107 | exports.push([module.i, ".overlay[data-v-03833336]{background-color:rgba(0,0,0,.8);height:100%;width:100%;position:fixed;top:0;bottom:0;right:0;left:0;z-index:9999}.fade-enter-active[data-v-03833336],.fade-leave-active[data-v-03833336]{-webkit-transition:opacity .5s;transition:opacity .5s}.fade-enter[data-v-03833336],.fade-leave-to[data-v-03833336]{opacity:0}", ""]); 108 | 109 | // exports 110 | 111 | 112 | /***/ }), 113 | 114 | /***/ "230e": 115 | /***/ (function(module, exports, __webpack_require__) { 116 | 117 | var isObject = __webpack_require__("d3f4"); 118 | var document = __webpack_require__("7726").document; 119 | // typeof document.createElement is 'object' in old IE 120 | var is = isObject(document) && isObject(document.createElement); 121 | module.exports = function (it) { 122 | return is ? document.createElement(it) : {}; 123 | }; 124 | 125 | 126 | /***/ }), 127 | 128 | /***/ "2350": 129 | /***/ (function(module, exports) { 130 | 131 | /* 132 | MIT License http://www.opensource.org/licenses/mit-license.php 133 | Author Tobias Koppers @sokra 134 | */ 135 | // css base code, injected by the css-loader 136 | module.exports = function(useSourceMap) { 137 | var list = []; 138 | 139 | // return the list of modules as css string 140 | list.toString = function toString() { 141 | return this.map(function (item) { 142 | var content = cssWithMappingToString(item, useSourceMap); 143 | if(item[2]) { 144 | return "@media " + item[2] + "{" + content + "}"; 145 | } else { 146 | return content; 147 | } 148 | }).join(""); 149 | }; 150 | 151 | // import a list of modules into the list 152 | list.i = function(modules, mediaQuery) { 153 | if(typeof modules === "string") 154 | modules = [[null, modules, ""]]; 155 | var alreadyImportedModules = {}; 156 | for(var i = 0; i < this.length; i++) { 157 | var id = this[i][0]; 158 | if(typeof id === "number") 159 | alreadyImportedModules[id] = true; 160 | } 161 | for(i = 0; i < modules.length; i++) { 162 | var item = modules[i]; 163 | // skip already imported module 164 | // this implementation is not 100% perfect for weird media query combinations 165 | // when a module is imported multiple times with different media queries. 166 | // I hope this will never occur (Hey this way we have smaller bundles) 167 | if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { 168 | if(mediaQuery && !item[2]) { 169 | item[2] = mediaQuery; 170 | } else if(mediaQuery) { 171 | item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; 172 | } 173 | list.push(item); 174 | } 175 | } 176 | }; 177 | return list; 178 | }; 179 | 180 | function cssWithMappingToString(item, useSourceMap) { 181 | var content = item[1] || ''; 182 | var cssMapping = item[3]; 183 | if (!cssMapping) { 184 | return content; 185 | } 186 | 187 | if (useSourceMap && typeof btoa === 'function') { 188 | var sourceMapping = toComment(cssMapping); 189 | var sourceURLs = cssMapping.sources.map(function (source) { 190 | return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */' 191 | }); 192 | 193 | return [content].concat(sourceURLs).concat([sourceMapping]).join('\n'); 194 | } 195 | 196 | return [content].join('\n'); 197 | } 198 | 199 | // Adapted from convert-source-map (MIT) 200 | function toComment(sourceMap) { 201 | // eslint-disable-next-line no-undef 202 | var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))); 203 | var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64; 204 | 205 | return '/*# ' + data + ' */'; 206 | } 207 | 208 | 209 | /***/ }), 210 | 211 | /***/ "499e": 212 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 213 | 214 | "use strict"; 215 | __webpack_require__.r(__webpack_exports__); 216 | 217 | // CONCATENATED MODULE: ./node_modules/vue-style-loader/lib/listToStyles.js 218 | /** 219 | * Translates the list format produced by css-loader into something 220 | * easier to manipulate. 221 | */ 222 | function listToStyles (parentId, list) { 223 | var styles = [] 224 | var newStyles = {} 225 | for (var i = 0; i < list.length; i++) { 226 | var item = list[i] 227 | var id = item[0] 228 | var css = item[1] 229 | var media = item[2] 230 | var sourceMap = item[3] 231 | var part = { 232 | id: parentId + ':' + i, 233 | css: css, 234 | media: media, 235 | sourceMap: sourceMap 236 | } 237 | if (!newStyles[id]) { 238 | styles.push(newStyles[id] = { id: id, parts: [part] }) 239 | } else { 240 | newStyles[id].parts.push(part) 241 | } 242 | } 243 | return styles 244 | } 245 | 246 | // CONCATENATED MODULE: ./node_modules/vue-style-loader/lib/addStylesClient.js 247 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return addStylesClient; }); 248 | /* 249 | MIT License http://www.opensource.org/licenses/mit-license.php 250 | Author Tobias Koppers @sokra 251 | Modified by Evan You @yyx990803 252 | */ 253 | 254 | 255 | 256 | var hasDocument = typeof document !== 'undefined' 257 | 258 | if (typeof DEBUG !== 'undefined' && DEBUG) { 259 | if (!hasDocument) { 260 | throw new Error( 261 | 'vue-style-loader cannot be used in a non-browser environment. ' + 262 | "Use { target: 'node' } in your Webpack config to indicate a server-rendering environment." 263 | ) } 264 | } 265 | 266 | /* 267 | type StyleObject = { 268 | id: number; 269 | parts: Array 270 | } 271 | 272 | type StyleObjectPart = { 273 | css: string; 274 | media: string; 275 | sourceMap: ?string 276 | } 277 | */ 278 | 279 | var stylesInDom = {/* 280 | [id: number]: { 281 | id: number, 282 | refs: number, 283 | parts: Array<(obj?: StyleObjectPart) => void> 284 | } 285 | */} 286 | 287 | var head = hasDocument && (document.head || document.getElementsByTagName('head')[0]) 288 | var singletonElement = null 289 | var singletonCounter = 0 290 | var isProduction = false 291 | var noop = function () {} 292 | var options = null 293 | var ssrIdKey = 'data-vue-ssr-id' 294 | 295 | // Force single-tag solution on IE6-9, which has a hard limit on the # of \n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Modal.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Modal.vue?vue&type=script&lang=js&\"","/* globals __VUE_SSR_CONTEXT__ */\n\n// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).\n// This module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle.\n\nexport default function normalizeComponent (\n scriptExports,\n render,\n staticRenderFns,\n functionalTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier, /* server only */\n shadowMode /* vue-cli only */\n) {\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (render) {\n options.render = render\n options.staticRenderFns = staticRenderFns\n options._compiled = true\n }\n\n // functional template\n if (functionalTemplate) {\n options.functional = true\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = 'data-v-' + scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = shadowMode\n ? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }\n : injectStyles\n }\n\n if (hook) {\n if (options.functional) {\n // for template-only hot-reload because in that case the render fn doesn't\n // go through the normalizer\n options._injectStyles = hook\n // register for functioal component in vue file\n var originalRender = options.render\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return originalRender(h, context)\n }\n } else {\n // inject component registration as beforeCreate hook\n var existing = options.beforeCreate\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n }\n }\n\n return {\n exports: scriptExports,\n options: options\n }\n}\n","import { render, staticRenderFns } from \"./Modal.vue?vue&type=template&id=03833336&scoped=true&\"\nimport script from \"./Modal.vue?vue&type=script&lang=js&\"\nexport * from \"./Modal.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Modal.vue?vue&type=style&index=0&id=03833336&scoped=true&lang=scss&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"03833336\",\n null\n \n)\n\nexport default component.exports","import Vue from \"vue\"\nimport Modal from './Modal.vue'\n\nVue.component(Modal.name, Modal)\n\nexport default Modal","import './setPublicPath'\nimport mod from '~entry'\nexport default mod\nexport * from '~entry'\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /dist/vue-modal.umd.min.js: -------------------------------------------------------------------------------- 1 | (function(t,e){"object"===typeof exports&&"object"===typeof module?module.exports=e(require("vue")):"function"===typeof define&&define.amd?define([],e):"object"===typeof exports?exports["vue-modal"]=e(require("vue")):t["vue-modal"]=e(t["Vue"])})("undefined"!==typeof self?self:this,function(t){return function(t){var e={};function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},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 o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(o,r,function(e){return t[e]}.bind(null,r));return o},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")}({"07b6":function(t,e,n){e=t.exports=n("2350")(!1),e.push([t.i,".overlay[data-v-03833336]{background-color:rgba(0,0,0,.8);height:100%;width:100%;position:fixed;top:0;bottom:0;right:0;left:0;z-index:9999}.fade-enter-active[data-v-03833336],.fade-leave-active[data-v-03833336]{-webkit-transition:opacity .5s;transition:opacity .5s}.fade-enter[data-v-03833336],.fade-leave-to[data-v-03833336]{opacity:0}",""])},"230e":function(t,e,n){var o=n("d3f4"),r=n("7726").document,i=o(r)&&o(r.createElement);t.exports=function(t){return i?r.createElement(t):{}}},2350:function(t,e){function n(t,e){var n=t[1]||"",r=t[3];if(!r)return n;if(e&&"function"===typeof btoa){var i=o(r),a=r.sources.map(function(t){return"/*# sourceURL="+r.sourceRoot+t+" */"});return[n].concat(a).concat([i]).join("\n")}return[n].join("\n")}function o(t){var e=btoa(unescape(encodeURIComponent(JSON.stringify(t)))),n="sourceMappingURL=data:application/json;charset=utf-8;base64,"+e;return"/*# "+n+" */"}t.exports=function(t){var e=[];return e.toString=function(){return this.map(function(e){var o=n(e,t);return e[2]?"@media "+e[2]+"{"+o+"}":o}).join("")},e.i=function(t,n){"string"===typeof t&&(t=[[null,t,""]]);for(var o={},r=0;rn.parts.length&&(o.parts.length=n.parts.length)}else{var a=[];for(r=0;r"}},methods:{beforeOpen:function(){this.$emit("before-open")},beforeClose:function(){this.$emit("before-close")},toggleModal:function(t){this.modalVisible=!this.modalVisible,this.clickedBtn=t},switchContent:function(t){this.clickedBtn=t},prevModal:function(){this.clickedBtn>0&&(this.clickedBtn-=1)},nextModal:function(){this.clickedBtn\n}\n\ntype StyleObjectPart = {\n css: string;\n media: string;\n sourceMap: ?string\n}\n*/\n\nvar stylesInDom = {/*\n [id: number]: {\n id: number,\n refs: number,\n parts: Array<(obj?: StyleObjectPart) => void>\n }\n*/}\n\nvar head = hasDocument && (document.head || document.getElementsByTagName('head')[0])\nvar singletonElement = null\nvar singletonCounter = 0\nvar isProduction = false\nvar noop = function () {}\nvar options = null\nvar ssrIdKey = 'data-vue-ssr-id'\n\n// Force single-tag solution on IE6-9, which has a hard limit on the # of \n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Modal.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Modal.vue?vue&type=script&lang=js&\"","/* globals __VUE_SSR_CONTEXT__ */\n\n// IMPORTANT: Do NOT use ES2015 features in this file (except for modules).\n// This module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle.\n\nexport default function normalizeComponent (\n scriptExports,\n render,\n staticRenderFns,\n functionalTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier, /* server only */\n shadowMode /* vue-cli only */\n) {\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (render) {\n options.render = render\n options.staticRenderFns = staticRenderFns\n options._compiled = true\n }\n\n // functional template\n if (functionalTemplate) {\n options.functional = true\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = 'data-v-' + scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = shadowMode\n ? function () { injectStyles.call(this, this.$root.$options.shadowRoot) }\n : injectStyles\n }\n\n if (hook) {\n if (options.functional) {\n // for template-only hot-reload because in that case the render fn doesn't\n // go through the normalizer\n options._injectStyles = hook\n // register for functioal component in vue file\n var originalRender = options.render\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return originalRender(h, context)\n }\n } else {\n // inject component registration as beforeCreate hook\n var existing = options.beforeCreate\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n }\n }\n\n return {\n exports: scriptExports,\n options: options\n }\n}\n","import { render, staticRenderFns } from \"./Modal.vue?vue&type=template&id=03833336&scoped=true&\"\nimport script from \"./Modal.vue?vue&type=script&lang=js&\"\nexport * from \"./Modal.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Modal.vue?vue&type=style&index=0&id=03833336&scoped=true&lang=scss&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"03833336\",\n null\n \n)\n\nexport default component.exports","import Vue from \"vue\"\nimport Modal from './Modal.vue'\n\nVue.component(Modal.name, Modal)\n\nexport default Modal","import './setPublicPath'\nimport mod from '~entry'\nexport default mod\nexport * from '~entry'\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], 3 | transform: { 4 | '^.+\\.vue$': 'vue-jest', 5 | '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', 6 | '^.+\\.jsx?$': 'babel-jest', 7 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub' 8 | }, 9 | moduleNameMapper: { 10 | '^@/(.*)$': '/src/$1' 11 | }, 12 | snapshotSerializers: ['jest-serializer-vue'], 13 | testMatch: [ 14 | '**/test/unit/**/*.spec.(js|jsx|ts|tsx)|**/__test__/*.(js|jsx|ts|tsx)' 15 | ], 16 | testURL: 'http://localhost/', 17 | transformIgnorePatterns: ['/node_modules(?![\\\\/]vue-awesome[\\\\/])/'] 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@melmacaluso/vue-modal", 3 | "version": "0.0.0-development", 4 | "private": false, 5 | "author": { 6 | "name": "Mel Macaluso" 7 | }, 8 | "scripts": { 9 | "commit": "git-cz", 10 | "serve": "vue-cli-service serve", 11 | "build": "vue-cli-service build", 12 | "lint": "vue-cli-service lint", 13 | "build-lib": "vue-cli-service build --target lib --name vue-modal ./src/components/index.js", 14 | "test:unit": "vue-cli-service test:unit", 15 | "test:unit-watch": "vue-cli-service test:unit --watch", 16 | "semantic-release": "semantic-release" 17 | }, 18 | "dependencies": { 19 | "vue": "^2.6.6" 20 | }, 21 | "devDependencies": { 22 | "@vue/cli-plugin-babel": "^3.4.0", 23 | "@vue/cli-plugin-eslint": "^3.4.0", 24 | "@vue/cli-plugin-unit-jest": "^3.4.1", 25 | "@vue/cli-service": "^3.4.0", 26 | "@vue/test-utils": "^1.0.0-beta.29", 27 | "babel-core": "7.0.0-bridge.0", 28 | "babel-eslint": "^10.0.1", 29 | "babel-jest": "^23.6.0", 30 | "commitizen": "^3.0.7", 31 | "cz-conventional-changelog": "^2.1.0", 32 | "eslint": "^5.8.0", 33 | "eslint-plugin-vue": "^5.0.0", 34 | "ghooks": "^2.0.4", 35 | "node-sass": "^4.11.0", 36 | "sass-loader": "^7.1.0", 37 | "semantic-release": "^15.13.3", 38 | "vue-template-compiler": "^2.5.21" 39 | }, 40 | "eslintConfig": { 41 | "root": true, 42 | "env": { 43 | "node": true 44 | }, 45 | "extends": [ 46 | "plugin:vue/essential", 47 | "eslint:recommended" 48 | ], 49 | "rules": {}, 50 | "parserOptions": { 51 | "parser": "babel-eslint" 52 | } 53 | }, 54 | "postcss": { 55 | "plugins": { 56 | "autoprefixer": {} 57 | } 58 | }, 59 | "config": { 60 | "commitizen": { 61 | "path": "node_modules/cz-conventional-changelog" 62 | }, 63 | "ghooks": { 64 | "pre-commit": "npm run test:unit" 65 | } 66 | }, 67 | "browserslist": [ 68 | "> 1%", 69 | "last 2 versions", 70 | "not ie <= 8" 71 | ], 72 | "files": [ 73 | "src/*", 74 | "dist/*", 75 | "*.json", 76 | "*.js" 77 | ], 78 | "keywords": [ 79 | "front-end", 80 | "web", 81 | "vue", 82 | "vuejs", 83 | "dialog", 84 | "alert", 85 | "modal", 86 | "vue-modal", 87 | "component", 88 | "popup", 89 | "vue-modal-component" 90 | ], 91 | "license": "MPL 2.0", 92 | "main": "dist/vue-modal.umd.min.js", 93 | "module": "dist/vue-modal-mhm.esm.js", 94 | "repository": { 95 | "type": "git", 96 | "url": "https://github.com/MelMacaluso/vue-modal.git" 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # Vue Modal 🖼 3 | [![npm](https://img.shields.io/npm/dw/@melmacaluso/vue-modal.svg?label=Downloads)](https://www.npmjs.com/package/@melmacaluso/vue-modal) 4 | [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) 5 | ![CircleCI (all branches)](https://img.shields.io/circleci/project/github/MelMacaluso/vue-modal.svg) 6 | [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) 7 | 8 |
9 | 10 | ![Reusable Modal component, supports own custom HTML, text and classes and/or passing a component. Featuring multiple modal content / buttons.](https://user-images.githubusercontent.com/24974216/55087239-b2b9f980-50a1-11e9-8924-499b6cbfe965.png) 11 | 12 |
13 | 14 | ## Intro 15 | 16 | Reusable Modal component, supports own custom HTML, text and classes and/or passing a component. Featuring multiple modal content / buttons. 17 | 18 | ## What this ISN'T 19 | 20 | This component is not meant to be a bootstrap-ish already-styled-modal-replacer for Vue. 21 | 22 | ## What this IS 23 | 24 | Instead: it wants to take it a step further: it gives you a skeleton base structure where you are free to apply your own css styling according to your requirements/website and gives you freedom of formatting the content/arrows/buttons/events as you wish with little to no effort. 25 | 26 | ## Features 27 | 28 | - Animated modal transition 29 | - Overlay on modal background 30 | - Custom event triggering on `before-close` and `before-open` 31 | - Conditional: Next/prev arrows, close button, paging 32 | - Next and prev arrow for switching between modal contents 33 | - Modal contents navigation with custom paging 34 | - CSS/HTML customisation of: prev/next arrows, modal content, modal 35 | navigation, modal trigger button/s 36 | 37 | ## Demo 38 |
39 | 40 | ![Vue modal animated demo](https://media.giphy.com/media/7zoNLc5G8zJjWlyR3T/giphy.gif) 41 | 42 |
43 | 44 | - Code editor - [codesandobx](https://codesandbox.io/s/rmj2y345xo) 45 | - Preview - [codesandbox](https://rmj2y345xo.codesandbox.io/) 46 | 47 | ## Installation 48 | 49 | ```shell 50 | npm i @melmacaluso/vue-modal 51 | ``` 52 | 53 | ## Usage 54 | 55 | Simply import it in your desired vue component as follows: 56 | 57 | ```javascript 58 | import Modal from "@melmacaluso/vue-modal"; 59 | ``` 60 | 61 | ## Props 62 | 63 | | **Prop** | **Type** | **Comment** | 64 | | ------------------ | -------- | -------------------------------------------------------------------------------------------------------- | 65 | | `btnText` | String | Text label for modal button | 66 | | `modalContent` | String | Pass here your html for the modal main modal | 67 | | `closeBtn` | Boolean | Conditionally add a close button | 68 | | `closeBtn-content` | String | Pass here your html for the close button | 69 | | `multiple` | Boolean | Allow multiple buttons/content within the modal | 70 | | `modals` | Array | Pass here an array of objects, they retain the same props within the array's scope ie. `.btnText` | 71 | | `showNav` | Boolean | Conditionally show a navigation with each modal's `btnText` | 72 | | `showArrows` | Boolean | Conditionally show an arrow based navigation | 73 | | `showArrowsCloseBtn` | Boolean | Conditionally show an the close button between the prev/next arrows, it inherits `closeBtn-content` | 74 | | `arrowNextContent` | String | Pass here your html for the next arrow | 75 | | `arrowPrevContent` | String | Pass here your html for the previous arrow | 76 | | `@before-open` | Function | Attach here your custom function, it will be invoked before the modal opens | 77 | | `@before-close` | Function | Attach here your custom function, it will be invoked before the modal closes | 78 | 79 | ## Examples 80 | 81 | ### Inline HTML: 82 | 83 | ```vue 84 | 98 | ``` 99 | 100 | ### Passing component: 101 | 102 | ```vue 103 | 108 | 109 | 110 | ``` 111 | 112 | ### Multiple buttons & modal content + custom functions: 113 | 114 | ```vue 115 | 136 | ``` 137 | 138 | ### From Api/Json feed + Prev/Next Arrows: 139 | 140 | ```vue 141 | 146 | 147 | export default { 148 | data: () => { 149 | return { 150 | users: [] 151 | } 152 | }, 153 | mounted(){ 154 | fetch('https://jsonplaceholder.typicode.com/users') 155 | .then(res => res.json()) 156 | .then(res => this.users = res) 157 | .catch(err => console.log(err)) 158 | }, 159 | computed: { 160 | formattedUsers: function() { 161 | return this.users.map(user => { 162 | return { 163 | btnText: `${user.name}`, 164 | modalContent: ` 165 |

Email:${user.email}

166 |

Phone:${user.phone}

167 | ` 168 | }; 169 | }); 170 | } 171 | } 172 | } 173 | ``` 174 | 175 | -------------------------------------------------------------------------------- /src/components/ExampleComponent.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/components/Modal.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 102 | 103 | 126 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue" 2 | import Modal from './Modal.vue' 3 | 4 | Vue.component(Modal.name, Modal) 5 | 6 | export default Modal -------------------------------------------------------------------------------- /test/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } -------------------------------------------------------------------------------- /test/unit/Modal.spec.js: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | import Modal from '@/components/Modal' 3 | 4 | describe('Multiple Modals', ()=> { 5 | let wrapper 6 | beforeEach(() => { 7 | wrapper = mount(Modal, { 8 | propsData: { 9 | multiple: true, 10 | modals: [ 11 | { 12 | btnText: 'Press me 1', 13 | modalContent: 'This is the content 1' 14 | }, 15 | { 16 | btnText: 'Press me 2', 17 | modalContent: 18 | '' 19 | }, 20 | { 21 | btnText: 'Press me 3', 22 | modalContent: 'This is the

content 3

' 23 | } 24 | ], 25 | closeBtn: true, 26 | closeBtnContent: "x", 27 | showNav: true, 28 | showArrows: true 29 | } 30 | }) 31 | // Open the modal 32 | wrapper.setData({ modalVisible: true }) 33 | }) 34 | 35 | describe('Contents rendered', ()=> { 36 | it('Trigger-modal Buttons', () => { 37 | wrapper.props().modals.forEach(btn => { 38 | expect(wrapper.html()).toContain(``) 39 | }) 40 | }) 41 | it('Modal Cotents', () => { 42 | wrapper.props().modals.forEach((btn,i) => { 43 | wrapper.setData({ clickedBtn: i }) 44 | expect(wrapper.html()).toContain(btn.modalContent) 45 | }) 46 | }) 47 | }) 48 | 49 | describe('Interactions', ()=> { 50 | it('Open Modal', () => { 51 | wrapper.setData({ modalVisible: false }) 52 | const btn = wrapper.find('.btns-wrapper button') 53 | btn.trigger('click') 54 | expect(wrapper.vm.modalVisible).toBeTruthy() 55 | expect(wrapper.contains('.modals')).toBeTruthy() 56 | expect(wrapper.contains('.overlay')).toBeTruthy() 57 | }) 58 | it('Close Modal from closeBtn', () => { 59 | const btn = wrapper.find('.close') 60 | btn.trigger('click') 61 | expect(wrapper.vm.modalVisible).toBe(false) 62 | expect(wrapper.contains('.modals')).toBe(false) 63 | expect(wrapper.contains('.overlay')).toBe(false) 64 | }) 65 | it('Close Modal from overlay', () => { 66 | const overlay = wrapper.find('.overlay') 67 | overlay.trigger('click') 68 | expect(wrapper.vm.modalVisible).toBe(false) 69 | expect(wrapper.contains('.modals')).toBe(false) 70 | expect(wrapper.contains('.overlay')).toBe(false) 71 | }) 72 | it('Next Content', () => { 73 | const btn = wrapper.find('.navigation-arrows .next-arrow') 74 | btn.trigger('click') 75 | expect(wrapper.vm.clickedBtn).toBe(1) 76 | }) 77 | it('Previous Content', () => { 78 | wrapper.setData({ clickedBtn: 1 }) 79 | const btn = wrapper.find('.navigation-arrows .prev-arrow') 80 | btn.trigger('click') 81 | expect(wrapper.vm.clickedBtn).toBe(0) 82 | }) 83 | }) 84 | }) 85 | 86 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | css: { 3 | extract: false 4 | } 5 | }; --------------------------------------------------------------------------------