From 61d694bd34a650f9a2883531a6fa05cb0df6930c Mon Sep 17 00:00:00 2001 From: Peter Hegman Date: Tue, 5 Nov 2019 20:27:42 -0800 Subject: [PATCH 1/3] Rewrite in TypeScript and improve build/deployment --- .babelrc | 3 - .browserslistrc | 4 + .eslintrc.js | 23 +- .gitignore | 50 +- .prettierrc | 5 + .travis.yml | 29 +- CNAME | 1 - dist/main.js | 433 -- dist/v-scroll-lock.js | 136 - index.html | 17 - package.json | 100 +- readme.md | 69 +- rollup.config.js | 33 + src/App.vue | 46 - src/Demo.vue | 46 + src/assets/close-icon.svg | 3 + src/classes/VScrollLock.js | 51 - src/components/Modal.vue | 278 +- src/demo.ts | 9 + src/index.html | 19 + src/index.ts | 57 + src/interfaces/plugin-options.interface.ts | 6 + src/interfaces/v-scroll-lock.interface.ts | 12 + src/lorem-ipsum.ts | 4 + src/main.js | 9 - src/noop.ts | 4 + src/shims-vue.d.ts | 4 + src/v-scroll-lock.js | 4 - tsconfig.json | 18 + webpack.config.js | 75 +- yarn.lock | 7531 ++++++++++++-------- 31 files changed, 5039 insertions(+), 4040 deletions(-) delete mode 100755 .babelrc create mode 100644 .browserslistrc mode change 100755 => 100644 .eslintrc.js create mode 100644 .prettierrc delete mode 100644 CNAME delete mode 100755 dist/main.js delete mode 100755 dist/v-scroll-lock.js delete mode 100755 index.html mode change 100755 => 100644 package.json create mode 100644 rollup.config.js delete mode 100755 src/App.vue create mode 100644 src/Demo.vue create mode 100644 src/assets/close-icon.svg delete mode 100755 src/classes/VScrollLock.js mode change 100755 => 100644 src/components/Modal.vue create mode 100644 src/demo.ts create mode 100644 src/index.html create mode 100644 src/index.ts create mode 100644 src/interfaces/plugin-options.interface.ts create mode 100644 src/interfaces/v-scroll-lock.interface.ts create mode 100644 src/lorem-ipsum.ts delete mode 100755 src/main.js create mode 100644 src/noop.ts create mode 100644 src/shims-vue.d.ts delete mode 100755 src/v-scroll-lock.js create mode 100644 tsconfig.json mode change 100755 => 100644 webpack.config.js diff --git a/.babelrc b/.babelrc deleted file mode 100755 index 2f01e1d..0000000 --- a/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["env"] -} \ No newline at end of file diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000..069fca5 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,4 @@ +> 0.5% +last 2 versions +Firefox ESR +not dead \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js old mode 100755 new mode 100644 index 0367139..3600543 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,15 +1,14 @@ module.exports = { - "env": { - "browser": true, - "es6": true + root: true, + env: { + node: true, }, - "extends": ["plugin:vue/strongly-recommended", "standard"], - "parserOptions": { - "sourceType": "module" + extends: ['plugin:vue/essential', '@vue/prettier', '@vue/typescript'], + rules: { + 'vue/max-attributes-per-line': 'off', + 'vue/html-self-closing': 'off', }, - "globals": { - "beforeEach": false, - "test": false, - "expect": false - } -}; + parserOptions: { + parser: '@typescript-eslint/parser', + }, +} diff --git a/.gitignore b/.gitignore index 1c53f7d..a900e44 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,32 @@ -# General -.DS_Store -.AppleDouble -.LSOverride +# Logs +logs +*.log +yarn-debug.log* +yarn-error.log* -# Icon must end with two \r -Icon +# Dependency directories +node_modules/ +# TypeScript cache +*.tsbuildinfo -# Thumbnails -._* +# Optional npm cache directory +.npm -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent +# Optional eslint cache +.eslintcache + +# Yarn Integrity file +.yarn-integrity -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk +# Build artifacts +dist -#node_modules -/node_modules +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..36301bc --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": false, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/.travis.yml b/.travis.yml index 71e6f8d..c24d918 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,30 @@ language: node_js node_js: - - "9" + - '10' script: - - yarn run lint \ No newline at end of file + - yarn lint + - yarn build +deploy: + - provider: npm + email: $NPM_EMAIL + api_key: $NPM_TOKEN + skip_cleanup: true + on: + branch: master + tags: true + - provider: releases + api_key: $GITHUB_OAUTH_TOKEN + file_glob: true + file: dist/* + skip_cleanup: true + on: + branch: master + tags: true + - provider: pages + skip_cleanup: true + github_token: $GITHUB_PAGES_TOKEN + local_dir: dist + fqdn: v-scroll-lock.peterhegman.com + on: + branch: master + tags: true diff --git a/CNAME b/CNAME deleted file mode 100644 index a5e9c3a..0000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -v-scroll-lock.peterhegman.com \ No newline at end of file diff --git a/dist/main.js b/dist/main.js deleted file mode 100755 index 1014766..0000000 --- a/dist/main.js +++ /dev/null @@ -1,433 +0,0 @@ -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; -/******/ -/******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); -/******/ return ns; -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = "./src/main.js"); -/******/ }) -/************************************************************************/ -/******/ ({ - -/***/ "./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js?!./src/App.vue?vue&type=script&lang=js&": -/*!********************************************************************************************************************************!*\ - !*** ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js& ***! - \********************************************************************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _Modal = __webpack_require__(/*! ./components/Modal.vue */ \"./src/components/Modal.vue\");\n\nvar _Modal2 = _interopRequireDefault(_Modal);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.default = {\n name: 'App',\n components: {\n Modal: _Modal2.default\n }\n}; //\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n//# sourceURL=webpack:///./src/App.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options"); - -/***/ }), - -/***/ "./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/index.js?!./src/components/Modal.vue?vue&type=script&lang=js&": -/*!*********************************************************************************************************************************************!*\ - !*** ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Modal.vue?vue&type=script&lang=js& ***! - \*********************************************************************************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\nexports.default = {\n name: 'Modal',\n data: function data() {\n return {\n open: false\n };\n },\n\n methods: {\n openModal: function openModal() {\n this.open = true;\n },\n closeModal: function closeModal() {\n this.open = false;\n }\n },\n mounted: function mounted() {\n var _this = this;\n\n document.addEventListener('keydown', function (e) {\n if (_this.open && e.keyCode === 27) {\n _this.closeModal();\n }\n });\n }\n};\n\n//# sourceURL=webpack:///./src/components/Modal.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options"); - -/***/ }), - -/***/ "./node_modules/body-scroll-lock/lib/bodyScrollLock.js": -/*!*************************************************************!*\ - !*** ./node_modules/body-scroll-lock/lib/bodyScrollLock.js ***! - \*************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nfunction _toConsumableArray(arr) {\n if (Array.isArray(arr)) {\n for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {\n arr2[i] = arr[i];\n }return arr2;\n } else {\n return Array.from(arr);\n }\n}\n\nvar isIosDevice = typeof window !== 'undefined' && window.navigator && window.navigator.platform && /iPad|iPhone|iPod|(iPad Simulator)|(iPhone Simulator)|(iPod Simulator)/.test(window.navigator.platform);\n// Adopted and modified solution from Bohdan Didukh (2017)\n// https://stackoverflow.com/questions/41594997/ios-10-safari-prevent-scrolling-behind-a-fixed-overlay-and-maintain-scroll-posi\n\nvar firstTargetElement = null;\nvar allTargetElements = [];\nvar initialClientY = -1;\nvar previousBodyOverflowSetting = void 0;\nvar previousBodyPaddingRight = void 0;\n\nvar preventDefault = function preventDefault(rawEvent) {\n var e = rawEvent || window.event;\n if (e.preventDefault) e.preventDefault();\n\n return false;\n};\n\nvar setOverflowHidden = function setOverflowHidden(options) {\n // Setting overflow on body/documentElement synchronously in Desktop Safari slows down\n // the responsiveness for some reason. Setting within a setTimeout fixes this.\n setTimeout(function () {\n // If previousBodyPaddingRight is already set, don't set it again.\n if (previousBodyPaddingRight === undefined) {\n var _reserveScrollBarGap = !!options && options.reserveScrollBarGap === true;\n var scrollBarGap = window.innerWidth - document.documentElement.clientWidth;\n\n if (_reserveScrollBarGap && scrollBarGap > 0) {\n previousBodyPaddingRight = document.body.style.paddingRight;\n document.body.style.paddingRight = scrollBarGap + 'px';\n }\n }\n\n // If previousBodyOverflowSetting is already set, don't set it again.\n if (previousBodyOverflowSetting === undefined) {\n previousBodyOverflowSetting = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n }\n });\n};\n\nvar restoreOverflowSetting = function restoreOverflowSetting() {\n // Setting overflow on body/documentElement synchronously in Desktop Safari slows down\n // the responsiveness for some reason. Setting within a setTimeout fixes this.\n setTimeout(function () {\n if (previousBodyPaddingRight !== undefined) {\n document.body.style.paddingRight = previousBodyPaddingRight;\n\n // Restore previousBodyPaddingRight to undefined so setOverflowHidden knows it\n // can be set again.\n previousBodyPaddingRight = undefined;\n }\n\n if (previousBodyOverflowSetting !== undefined) {\n document.body.style.overflow = previousBodyOverflowSetting;\n\n // Restore previousBodyOverflowSetting to undefined\n // so setOverflowHidden knows it can be set again.\n previousBodyOverflowSetting = undefined;\n }\n });\n};\n\n// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight#Problems_and_solutions\nvar isTargetElementTotallyScrolled = function isTargetElementTotallyScrolled(targetElement) {\n return targetElement ? targetElement.scrollHeight - targetElement.scrollTop <= targetElement.clientHeight : false;\n};\n\nvar handleScroll = function handleScroll(event, targetElement) {\n var clientY = event.targetTouches[0].clientY - initialClientY;\n\n if (targetElement && targetElement.scrollTop === 0 && clientY > 0) {\n // element is at the top of its scroll\n return preventDefault(event);\n }\n\n if (isTargetElementTotallyScrolled(targetElement) && clientY < 0) {\n // element is at the top of its scroll\n return preventDefault(event);\n }\n\n return true;\n};\n\nvar disableBodyScroll = exports.disableBodyScroll = function disableBodyScroll(targetElement, options) {\n if (isIosDevice) {\n // targetElement must be provided, and disableBodyScroll must not have been\n // called on this targetElement before.\n if (targetElement && !allTargetElements.includes(targetElement)) {\n allTargetElements = [].concat(_toConsumableArray(allTargetElements), [targetElement]);\n\n targetElement.ontouchstart = function (event) {\n if (event.targetTouches.length === 1) {\n // detect single touch\n initialClientY = event.targetTouches[0].clientY;\n }\n };\n targetElement.ontouchmove = function (event) {\n if (event.targetTouches.length === 1) {\n // detect single touch\n handleScroll(event, targetElement);\n }\n };\n }\n } else {\n setOverflowHidden(options);\n\n if (!firstTargetElement) firstTargetElement = targetElement;\n }\n};\n\nvar clearAllBodyScrollLocks = exports.clearAllBodyScrollLocks = function clearAllBodyScrollLocks() {\n if (isIosDevice) {\n // Clear all allTargetElements ontouchstart/ontouchmove handlers, and the references\n allTargetElements.forEach(function (targetElement) {\n targetElement.ontouchstart = null;\n targetElement.ontouchmove = null;\n });\n\n allTargetElements = [];\n\n // Reset initial clientY\n initialClientY = -1;\n } else {\n restoreOverflowSetting();\n\n firstTargetElement = null;\n }\n};\n\nvar enableBodyScroll = exports.enableBodyScroll = function enableBodyScroll(targetElement) {\n if (isIosDevice) {\n targetElement.ontouchstart = null;\n targetElement.ontouchmove = null;\n\n allTargetElements = allTargetElements.filter(function (element) {\n return element !== targetElement;\n });\n } else if (firstTargetElement === targetElement) {\n restoreOverflowSetting();\n\n firstTargetElement = null;\n }\n};\n\n//# sourceURL=webpack:///./node_modules/body-scroll-lock/lib/bodyScrollLock.js?"); - -/***/ }), - -/***/ "./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/sass-loader/lib/loader.js!./node_modules/vue-loader/lib/index.js?!./src/App.vue?vue&type=style&index=0&lang=scss&": -/*!*************************************************************************************************************************************************************************************************************************************!*\ - !*** ./node_modules/css-loader!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/sass-loader/lib/loader.js!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=style&index=0&lang=scss& ***! - \*************************************************************************************************************************************************************************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("exports = module.exports = __webpack_require__(/*! ../node_modules/css-loader/lib/css-base.js */ \"./node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\\nhtml {\\n box-sizing: border-box;\\n -ms-overflow-style: scrollbar;\\n}\\n*,\\n*::before,\\n*::after {\\n box-sizing: inherit;\\n}\\nbody {\\n background-color: #f4f7fd;\\n padding: 0;\\n margin: 0;\\n}\\n.app {\\n max-width: 1000px;\\n padding: 20px;\\n margin: 0 auto;\\n}\\n\", \"\"]);\n\n// exports\n\n\n//# sourceURL=webpack:///./src/App.vue?./node_modules/css-loader!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/sass-loader/lib/loader.js!./node_modules/vue-loader/lib??vue-loader-options"); - -/***/ }), - -/***/ "./node_modules/css-loader/index.js!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/sass-loader/lib/loader.js!./node_modules/vue-loader/lib/index.js?!./src/components/Modal.vue?vue&type=style&index=0&id=701ac82d&lang=scss&scoped=true&": -/*!**************************************************************************************************************************************************************************************************************************************************************************!*\ - !*** ./node_modules/css-loader!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/sass-loader/lib/loader.js!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Modal.vue?vue&type=style&index=0&id=701ac82d&lang=scss&scoped=true& ***! - \**************************************************************************************************************************************************************************************************************************************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -eval("exports = module.exports = __webpack_require__(/*! ../../node_modules/css-loader/lib/css-base.js */ \"./node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\\n.modal[data-v-701ac82d] {\\n background-color: #fff;\\n border-radius: 4px;\\n bottom: 15px;\\n box-shadow: 0 0 15px rgba(30, 47, 67, 0.15);\\n left: 15px;\\n padding: 15px;\\n position: fixed;\\n right: 15px;\\n top: 15px;\\n}\\n@media screen and (min-width: 992px) {\\n.modal[data-v-701ac82d] {\\n bottom: auto;\\n height: 500px;\\n left: 50%;\\n max-height: 100%;\\n padding: 30px;\\n right: auto;\\n top: 50%;\\n transform: translate(-50%, -50%);\\n width: 700px;\\n}\\n}\\n.close-modal[data-v-701ac82d] {\\n background-color: transparent;\\n border: 0;\\n cursor: pointer;\\n padding: 10px;\\n position: absolute;\\n right: 10px;\\n top: 10px;\\n border-radius: 50%;\\n background-color: #00a3ff;\\n color: #fff;\\n}\\n@media screen and (min-width: 992px) {\\n.close-modal[data-v-701ac82d] {\\n right: 20px;\\n top: 20px;\\n}\\n}\\n.close-modal svg[data-v-701ac82d] {\\n display: block;\\n height: 20px;\\n width: 20px;\\n}\\n.content-wrap[data-v-701ac82d] {\\n -webkit-overflow-scrolling: touch;\\n height: 100%;\\n overflow: auto;\\n width: 100%;\\n}\\n.fade-enter-active[data-v-701ac82d],\\n.fade-leave-active[data-v-701ac82d] {\\n transition: opacity 0.3s;\\n}\\n.fade-enter[data-v-701ac82d],\\n.fade-leave-to[data-v-701ac82d] {\\n opacity: 0;\\n}\\n.fade-enter-to[data-v-701ac82d],\\n.fade-leave[data-v-701ac82d] {\\n opacity: 1;\\n}\\n.open-modal[data-v-701ac82d] {\\n background-color: #00a3ff;\\n border-radius: 40px;\\n border: 0;\\n color: #fff;\\n cursor: pointer;\\n display: inline-block;\\n font-family: sans-serif;\\n font-size: 16px;\\n margin-top: 15px;\\n padding: 15px 30px;\\n text-align: center;\\n text-decoration: none;\\n transition: background-color 0.3s, box-shadow 0.3s, color 0.3s;\\n}\\n.open-modal[data-v-701ac82d]:hover {\\n background-color: #0082cc;\\n}\\n\", \"\"]);\n\n// exports\n\n\n//# sourceURL=webpack:///./src/components/Modal.vue?./node_modules/css-loader!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/sass-loader/lib/loader.js!./node_modules/vue-loader/lib??vue-loader-options"); - -/***/ }), - -/***/ "./node_modules/css-loader/lib/css-base.js": -/*!*************************************************!*\ - !*** ./node_modules/css-loader/lib/css-base.js ***! - \*************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n/*\n\tMIT License http://www.opensource.org/licenses/mit-license.php\n\tAuthor Tobias Koppers @sokra\n*/\n// css base code, injected by the css-loader\nmodule.exports = function (useSourceMap) {\n\tvar list = [];\n\n\t// return the list of modules as css string\n\tlist.toString = function toString() {\n\t\treturn this.map(function (item) {\n\t\t\tvar content = cssWithMappingToString(item, useSourceMap);\n\t\t\tif (item[2]) {\n\t\t\t\treturn \"@media \" + item[2] + \"{\" + content + \"}\";\n\t\t\t} else {\n\t\t\t\treturn content;\n\t\t\t}\n\t\t}).join(\"\");\n\t};\n\n\t// import a list of modules into the list\n\tlist.i = function (modules, mediaQuery) {\n\t\tif (typeof modules === \"string\") modules = [[null, modules, \"\"]];\n\t\tvar alreadyImportedModules = {};\n\t\tfor (var i = 0; i < this.length; i++) {\n\t\t\tvar id = this[i][0];\n\t\t\tif (typeof id === \"number\") alreadyImportedModules[id] = true;\n\t\t}\n\t\tfor (i = 0; i < modules.length; i++) {\n\t\t\tvar item = modules[i];\n\t\t\t// skip already imported module\n\t\t\t// this implementation is not 100% perfect for weird media query combinations\n\t\t\t// when a module is imported multiple times with different media queries.\n\t\t\t// I hope this will never occur (Hey this way we have smaller bundles)\n\t\t\tif (typeof item[0] !== \"number\" || !alreadyImportedModules[item[0]]) {\n\t\t\t\tif (mediaQuery && !item[2]) {\n\t\t\t\t\titem[2] = mediaQuery;\n\t\t\t\t} else if (mediaQuery) {\n\t\t\t\t\titem[2] = \"(\" + item[2] + \") and (\" + mediaQuery + \")\";\n\t\t\t\t}\n\t\t\t\tlist.push(item);\n\t\t\t}\n\t\t}\n\t};\n\treturn list;\n};\n\nfunction cssWithMappingToString(item, useSourceMap) {\n\tvar content = item[1] || '';\n\tvar cssMapping = item[3];\n\tif (!cssMapping) {\n\t\treturn content;\n\t}\n\n\tif (useSourceMap && typeof btoa === 'function') {\n\t\tvar sourceMapping = toComment(cssMapping);\n\t\tvar sourceURLs = cssMapping.sources.map(function (source) {\n\t\t\treturn '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */';\n\t\t});\n\n\t\treturn [content].concat(sourceURLs).concat([sourceMapping]).join('\\n');\n\t}\n\n\treturn [content].join('\\n');\n}\n\n// Adapted from convert-source-map (MIT)\nfunction toComment(sourceMap) {\n\t// eslint-disable-next-line no-undef\n\tvar base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));\n\tvar data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;\n\n\treturn '/*# ' + data + ' */';\n}\n\n//# sourceURL=webpack:///./node_modules/css-loader/lib/css-base.js?"); - -/***/ }), - -/***/ "./node_modules/process/browser.js": -/*!*****************************************!*\ - !*** ./node_modules/process/browser.js ***! - \*****************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("\n\n// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things. But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals. It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout() {\n throw new Error('clearTimeout has not been defined');\n}\n(function () {\n try {\n if (typeof setTimeout === 'function') {\n cachedSetTimeout = setTimeout;\n } else {\n cachedSetTimeout = defaultSetTimout;\n }\n } catch (e) {\n cachedSetTimeout = defaultSetTimout;\n }\n try {\n if (typeof clearTimeout === 'function') {\n cachedClearTimeout = clearTimeout;\n } else {\n cachedClearTimeout = defaultClearTimeout;\n }\n } catch (e) {\n cachedClearTimeout = defaultClearTimeout;\n }\n})();\nfunction runTimeout(fun) {\n if (cachedSetTimeout === setTimeout) {\n //normal enviroments in sane situations\n return setTimeout(fun, 0);\n }\n // if setTimeout wasn't available but was latter defined\n if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n cachedSetTimeout = setTimeout;\n return setTimeout(fun, 0);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedSetTimeout(fun, 0);\n } catch (e) {\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedSetTimeout.call(null, fun, 0);\n } catch (e) {\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n return cachedSetTimeout.call(this, fun, 0);\n }\n }\n}\nfunction runClearTimeout(marker) {\n if (cachedClearTimeout === clearTimeout) {\n //normal enviroments in sane situations\n return clearTimeout(marker);\n }\n // if clearTimeout wasn't available but was latter defined\n if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n cachedClearTimeout = clearTimeout;\n return clearTimeout(marker);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedClearTimeout(marker);\n } catch (e) {\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedClearTimeout.call(null, marker);\n } catch (e) {\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n return cachedClearTimeout.call(this, marker);\n }\n }\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n if (!draining || !currentQueue) {\n return;\n }\n draining = false;\n if (currentQueue.length) {\n queue = currentQueue.concat(queue);\n } else {\n queueIndex = -1;\n }\n if (queue.length) {\n drainQueue();\n }\n}\n\nfunction drainQueue() {\n if (draining) {\n return;\n }\n var timeout = runTimeout(cleanUpNextTick);\n draining = true;\n\n var len = queue.length;\n while (len) {\n currentQueue = queue;\n queue = [];\n while (++queueIndex < len) {\n if (currentQueue) {\n currentQueue[queueIndex].run();\n }\n }\n queueIndex = -1;\n len = queue.length;\n }\n currentQueue = null;\n draining = false;\n runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n var args = new Array(arguments.length - 1);\n if (arguments.length > 1) {\n for (var i = 1; i < arguments.length; i++) {\n args[i - 1] = arguments[i];\n }\n }\n queue.push(new Item(fun, args));\n if (queue.length === 1 && !draining) {\n runTimeout(drainQueue);\n }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n this.fun = fun;\n this.array = array;\n}\nItem.prototype.run = function () {\n this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess.versions = {};\n\nfunction noop() {}\n\nprocess.on = noop;\nprocess.addListener = noop;\nprocess.once = noop;\nprocess.off = noop;\nprocess.removeListener = noop;\nprocess.removeAllListeners = noop;\nprocess.emit = noop;\nprocess.prependListener = noop;\nprocess.prependOnceListener = noop;\n\nprocess.listeners = function (name) {\n return [];\n};\n\nprocess.binding = function (name) {\n throw new Error('process.binding is not supported');\n};\n\nprocess.cwd = function () {\n return '/';\n};\nprocess.chdir = function (dir) {\n throw new Error('process.chdir is not supported');\n};\nprocess.umask = function () {\n return 0;\n};\n\n//# sourceURL=webpack:///./node_modules/process/browser.js?"); - -/***/ }), - -/***/ "./node_modules/setimmediate/setImmediate.js": -/*!***************************************************!*\ - !*** ./node_modules/setimmediate/setImmediate.js ***! - \***************************************************/ -/*! no static exports found */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -eval("/* WEBPACK VAR INJECTION */(function(global, process) {\n\n(function (global, undefined) {\n \"use strict\";\n\n if (global.setImmediate) {\n return;\n }\n\n var nextHandle = 1; // Spec says greater than zero\n var tasksByHandle = {};\n var currentlyRunningATask = false;\n var doc = global.document;\n var registerImmediate;\n\n function setImmediate(callback) {\n // Callback can either be a function or a string\n if (typeof callback !== \"function\") {\n callback = new Function(\"\" + callback);\n }\n // Copy function arguments\n var args = new Array(arguments.length - 1);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i + 1];\n }\n // Store and register the task\n var task = { callback: callback, args: args };\n tasksByHandle[nextHandle] = task;\n registerImmediate(nextHandle);\n return nextHandle++;\n }\n\n function clearImmediate(handle) {\n delete tasksByHandle[handle];\n }\n\n function run(task) {\n var callback = task.callback;\n var args = task.args;\n switch (args.length) {\n case 0:\n callback();\n break;\n case 1:\n callback(args[0]);\n break;\n case 2:\n callback(args[0], args[1]);\n break;\n case 3:\n callback(args[0], args[1], args[2]);\n break;\n default:\n callback.apply(undefined, args);\n break;\n }\n }\n\n function runIfPresent(handle) {\n // From the spec: \"Wait until any invocations of this algorithm started before this one have completed.\"\n // So if we're currently running a task, we'll need to delay this invocation.\n if (currentlyRunningATask) {\n // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a\n // \"too much recursion\" error.\n setTimeout(runIfPresent, 0, handle);\n } else {\n var task = tasksByHandle[handle];\n if (task) {\n currentlyRunningATask = true;\n try {\n run(task);\n } finally {\n clearImmediate(handle);\n currentlyRunningATask = false;\n }\n }\n }\n }\n\n function installNextTickImplementation() {\n registerImmediate = function registerImmediate(handle) {\n process.nextTick(function () {\n runIfPresent(handle);\n });\n };\n }\n\n function canUsePostMessage() {\n // The test against `importScripts` prevents this implementation from being installed inside a web worker,\n // where `global.postMessage` means something completely different and can't be used for this purpose.\n if (global.postMessage && !global.importScripts) {\n var postMessageIsAsynchronous = true;\n var oldOnMessage = global.onmessage;\n global.onmessage = function () {\n postMessageIsAsynchronous = false;\n };\n global.postMessage(\"\", \"*\");\n global.onmessage = oldOnMessage;\n return postMessageIsAsynchronous;\n }\n }\n\n function installPostMessageImplementation() {\n // Installs an event handler on `global` for the `message` event: see\n // * https://developer.mozilla.org/en/DOM/window.postMessage\n // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages\n\n var messagePrefix = \"setImmediate$\" + Math.random() + \"$\";\n var onGlobalMessage = function onGlobalMessage(event) {\n if (event.source === global && typeof event.data === \"string\" && event.data.indexOf(messagePrefix) === 0) {\n runIfPresent(+event.data.slice(messagePrefix.length));\n }\n };\n\n if (global.addEventListener) {\n global.addEventListener(\"message\", onGlobalMessage, false);\n } else {\n global.attachEvent(\"onmessage\", onGlobalMessage);\n }\n\n registerImmediate = function registerImmediate(handle) {\n global.postMessage(messagePrefix + handle, \"*\");\n };\n }\n\n function installMessageChannelImplementation() {\n var channel = new MessageChannel();\n channel.port1.onmessage = function (event) {\n var handle = event.data;\n runIfPresent(handle);\n };\n\n registerImmediate = function registerImmediate(handle) {\n channel.port2.postMessage(handle);\n };\n }\n\n function installReadyStateChangeImplementation() {\n var html = doc.documentElement;\n registerImmediate = function registerImmediate(handle) {\n // Create a - - diff --git a/package.json b/package.json old mode 100755 new mode 100644 index dada573..3ca649d --- a/package.json +++ b/package.json @@ -1,42 +1,82 @@ { "name": "v-scroll-lock", - "version": "1.0.1", + "version": "1.1.0", "description": "A Vue.js directive for scroll locking", - "main": "dist/v-scroll-lock.js", + "main": "dist/v-scroll-lock.umd.js", + "module": "dist/v-scroll-lock.esm.js", + "unpkg": "dist/v-scroll-lock.min.js", "author": "Peter Hegman", "license": "GPL-3.0", + "keywords": [ + "Vue.js", + "Vue", + "scroll lock", + "prevent scrolling", + "iOS", + "body scrolling", + "directive" + ], + "repository": { + "type": "git", + "url": "https://github.com/phegman/v-scroll-lock.git" + }, "dependencies": { - "body-scroll-lock": "^2.4.6", - "vue": "^2.5.16" + "body-scroll-lock": "^2.6.4" }, "devDependencies": { - "babel-cli": "^6.26.0", - "babel-core": "^6.26.3", - "babel-jest": "^23.0.1", - "babel-loader": "^7.1.4", - "babel-preset-env": "^1.7.0", - "css-loader": "^1.0.0", - "eslint": "^4.19.1", - "eslint-config-standard": "^11.0.0", - "eslint-plugin-import": "^2.12.0", - "eslint-plugin-node": "^6.0.1", - "eslint-plugin-promise": "^3.8.0", - "eslint-plugin-standard": "^3.1.0", - "eslint-plugin-vue": "^4.5.0", - "jest": "^23.1.0", - "node-sass": "^4.9.0", - "regenerator-runtime": "^0.11.1", - "sass-loader": "^7.0.3", - "vue-loader": "^15.2.4", - "vue-template-compiler": "^2.5.17", - "webpack": "^4.11.0", - "webpack-cli": "^3.0.2", - "webpack-merge": "^4.1.2" + "@babel/core": "^7.6.4", + "@babel/preset-env": "^7.6.3", + "@rollup/plugin-replace": "^2.2.0", + "@types/body-scroll-lock": "^2.6.1", + "@types/node": "^12.12.5", + "@typescript-eslint/parser": "^2.5.0", + "@vue/eslint-config-prettier": "^5.0.0", + "@vue/eslint-config-typescript": "^4.0.0", + "css-loader": "^3.2.0", + "eslint": "^6.5.1", + "eslint-plugin-prettier": "^3.1.1", + "eslint-plugin-vue": "^5.2.3", + "file-loader": "^4.2.0", + "focus-trap": "^5.0.2", + "html-webpack-plugin": "^3.2.0", + "husky": "^3.0.9", + "node-sass": "^4.12.0", + "prettier": "^1.18.2", + "pretty-quick": "^2.0.0", + "rimraf": "^3.0.0", + "rollup": "^1.25.1", + "rollup-plugin-alias": "^2.2.0", + "rollup-plugin-babel": "^4.3.3", + "rollup-plugin-commonjs": "^10.1.0", + "rollup-plugin-node-resolve": "^5.2.0", + "rollup-plugin-terser": "^5.1.2", + "rollup-plugin-typescript2": "^0.24.3", + "sass-loader": "^8.0.0", + "ts-loader": "^6.2.0", + "typescript": "^3.6.4", + "vue": "^2.6.10", + "vue-class-component": "^7.1.0", + "vue-loader": "^15.7.1", + "vue-property-decorator": "^8.3.0", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.6.10", + "webpack": "^4.41.2", + "webpack-cli": "^3.3.9", + "webpack-dev-server": "^3.9.0" }, "scripts": { - "build": "node_modules/.bin/webpack", - "prepublishOnly": "yarn run build", - "test": "jest", - "lint": "node_modules/.bin/eslint src --ext .vue" + "build": "rimraf dist & yarn build:umd & yarn build:esm & yarn build:esm-no-dep & yarn build:iife & yarn build:demo", + "build:umd": "rollup --format umd --file dist/v-scroll-lock.umd.js --config", + "build:esm": "rollup --format esm --file dist/v-scroll-lock.esm.js --config", + "build:iife": "rollup --format iife --file dist/v-scroll-lock.min.js --config", + "build:esm-no-dep": "rollup --format esm --file dist/v-scroll-lock-no-dep.esm.js --config --environment NO_DEP:true", + "build:demo": "webpack --mode production", + "lint": "./node_modules/.bin/eslint src/**/*.{vue,ts}", + "dev": "webpack-dev-server --mode development" + }, + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged" + } } } diff --git a/readme.md b/readme.md index e4927d4..f930352 100644 --- a/readme.md +++ b/readme.md @@ -2,6 +2,7 @@ [![Build Status](https://travis-ci.org/phegman/v-scroll-lock.svg?branch=master)](https://travis-ci.org/phegman/v-scroll-lock) # v-scroll-lock + A Vue.js directive for body scroll locking (for iOS Mobile and Tablet, Android, desktop Safari/Chrome/Firefox) without breaking scrolling of a target element (eg. modal/lightbox/flyouts/nav-menus). Built on top of [https://github.com/willmcpo/body-scroll-lock](https://github.com/willmcpo/body-scroll-lock) ## Table of Contents @@ -14,31 +15,47 @@ A Vue.js directive for body scroll locking (for iOS Mobile and Tablet, Android, - [Contributing](#contributing) ## Overview -Preventing the body from scrolling when you have a modal/lightbox/flyout/nav-menu open on all devices can be a huge pain. This package wraps the awesome library [https://github.com/willmcpo/body-scroll-lock](https://github.com/willmcpo/body-scroll-lock) into an easy to use Vue.js directive. + +Preventing the body from scrolling when you have a modal/lightbox/flyout/nav-menu open on all devices can be a huge pain. This package wraps [https://github.com/willmcpo/body-scroll-lock](https://github.com/willmcpo/body-scroll-lock) into an easy to use Vue.js directive. ## Demo + Demo can be viewed here: [http://v-scroll-lock.peterhegman.com/](https://v-scroll-lock.peterhegman.com/) -Source code for demo can be viewed in `src/App.vue` +Source code for demo can be viewed in `src/Demo.vue` ## Installation -### Yarn +### Module Bundler (Webpack, Rollup, etc) + +#### Yarn + +```bash +yarn add v-scroll-lock +``` + +#### NPM -`yarn add v-scroll-lock` +```bash +npm install v-scroll-lock --save +``` -### NPM +#### Install the Directive -`npm install v-scroll-lock --save` +```js +import VScrollLock from 'v-scroll-lock' -### Install the Vue plugin +Vue.use(VScrollLock) +``` -In your main JS file first import this plugin +### [Vue CDN](https://vuejs.org/v2/guide/#Getting-Started) -`import VScrollLock from 'v-scroll-lock'` +Download latest `v-scroll-lock.min.js` from [https://github.com/phegman/v-scroll-lock/releases](https://github.com/phegman/v-scroll-lock/releases) -Install the plugin +Include using a ` +``` ## Usage @@ -46,7 +63,7 @@ Once the plugin is installed the `v-scroll-lock` directive can be used in any of Here is an example of how you may implement it in a basic modal. See `src/components/Modal.vue` for a more in depth example. -```html +```vue -``` -```js ``` +### Providing Your Own Version of [body-scroll-lock](https://github.com/willmcpo/body-scroll-lock) + +To make using this directive easier [body-scroll-lock](https://github.com/willmcpo/body-scroll-lock) is included in the package. In the case that you would like to use a version different than the packaged version this can be specified in the plugin options. Also note that `v-scroll-lock-no-dep.esm.js` should be imported to prevent duplicate code in your bundle. See example below: + +```js +import VScrollLock from 'v-scroll-lock/dist/v-scroll-lock-no-dep.esm' +import { enableBodyScroll, disableBodyScroll } from 'body-scroll-lock' + +Vue.use(VScrollLock, { + enableBodyScroll, + disableBodyScroll, +}) +``` + ## Support Please [open an issue](https://github.com/phegman/v-scroll-lock/issues/new/) for support. diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..9f4d564 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,33 @@ +import * as path from 'path' +import alias from 'rollup-plugin-alias' +import babel from 'rollup-plugin-babel' +import { terser } from 'rollup-plugin-terser' +import resolve from 'rollup-plugin-node-resolve' +import typescript from 'rollup-plugin-typescript2' + +export default { + input: 'src/index.ts', + output: { + name: 'VShowSlide', + exports: 'named', + }, + plugins: [ + alias({ + resolve: ['.js', '.ts'], + entries: { + 'body-scroll-lock': path.join( + __dirname, + process.env.NO_DEP + ? '/src/noop' + : '/node_modules/body-scroll-lock/lib/bodyScrollLock.es6' + ), + }, + }), + resolve(), + typescript({ clean: true }), + babel({ + presets: ['@babel/preset-env'], + }), + terser(), + ], +} diff --git a/src/App.vue b/src/App.vue deleted file mode 100755 index 19074cc..0000000 --- a/src/App.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - - - diff --git a/src/Demo.vue b/src/Demo.vue new file mode 100644 index 0000000..04f2dbf --- /dev/null +++ b/src/Demo.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/src/assets/close-icon.svg b/src/assets/close-icon.svg new file mode 100644 index 0000000..f61b78a --- /dev/null +++ b/src/assets/close-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/classes/VScrollLock.js b/src/classes/VScrollLock.js deleted file mode 100755 index 2fd2194..0000000 --- a/src/classes/VScrollLock.js +++ /dev/null @@ -1,51 +0,0 @@ -export default class VScrollLock { - constructor (disableBodyScroll, enableBodyScroll) { - this.disableBodyScroll = disableBodyScroll - this.enableBodyScroll = enableBodyScroll - } - - /** - * Called when plugin is initialized - * @param {Object} Vue The Vue instance - * @param {Object} options Options passed to plugin - */ - install (Vue, options) { - Vue.directive('scroll-lock', { - inserted: this.inserted.bind(this), - componentUpdated: this.componentUpdated.bind(this), - unbind: this.unbind.bind(this) - }) - } - - /** - * Inserted directive hook. Called when the bound element has been inserted into its parent node - * @param {Node} el Element directive is bound to - * @param {Object} binding Binding options - */ - inserted (el, binding) { - if (binding.value) { - this.disableBodyScroll(el) - } - } - - /** - * Update directive hook. called after the containing component’s VNode and the VNodes of its children have updated - * @param {Node} el Element directive is bound to - * @param {Object} binding Binding options - */ - componentUpdated (el, binding) { - if (binding.value) { - this.disableBodyScroll(el) - } else { - this.enableBodyScroll(el) - } - } - - /** - * Unbind directive hook - * @param {Node} el Element directive is bound to - */ - unbind (el) { - this.enableBodyScroll(el) - } -} diff --git a/src/components/Modal.vue b/src/components/Modal.vue old mode 100755 new mode 100644 index 87f3a46..8b2db80 --- a/src/components/Modal.vue +++ b/src/components/Modal.vue @@ -1,153 +1,181 @@ - - diff --git a/src/demo.ts b/src/demo.ts new file mode 100644 index 0000000..8accce1 --- /dev/null +++ b/src/demo.ts @@ -0,0 +1,9 @@ +import Vue from 'vue' +import Demo from './Demo.vue' +import VScrollLock from './index' + +Vue.use(VScrollLock) + +new Vue({ + render: h => h(Demo), +}).$mount('#app') diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..67e5e60 --- /dev/null +++ b/src/index.html @@ -0,0 +1,19 @@ + + + + + + + v-scroll-lock Demo + + + +
+ + + diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..0057785 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,57 @@ +import { VueConstructor } from 'vue' +import { enableBodyScroll, disableBodyScroll } from 'body-scroll-lock' +import VScrollLockInterface from './interfaces/v-scroll-lock.interface' + +declare global { + interface Window { + Vue?: VueConstructor + } +} + +const VScrollLock: VScrollLockInterface = { + enableBodyScroll, + disableBodyScroll, + install(Vue, options) { + if (options) { + const { + enableBodyScroll: enableBodyScrollOption, + disableBodyScroll: disableBodyScrollOption, + } = options + + if (enableBodyScrollOption) { + this.enableBodyScroll = enableBodyScrollOption + } + + if (disableBodyScrollOption) { + this.disableBodyScroll = disableBodyScrollOption + } + } + Vue.directive('scroll-lock', { + inserted: this.inserted.bind(this), + componentUpdated: this.componentUpdated.bind(this), + unbind: this.unbind.bind(this), + }) + }, + inserted(el, binding) { + if (binding.value) { + this.disableBodyScroll(el) + } + }, + componentUpdated(el, binding) { + if (binding.value) { + this.disableBodyScroll(el) + } else { + this.enableBodyScroll(el) + } + }, + unbind(el) { + this.enableBodyScroll(el) + }, +} + +// Auto-install when vue is found (eg. in browser via