├── src ├── index.js ├── index.ts ├── directives │ ├── touch │ │ ├── index.js │ │ └── index.ts │ └── ripple │ │ ├── _variables.scss │ │ ├── VRipple.scss │ │ ├── __tests__ │ │ ├── ripple.spec.ts │ │ └── ripple.spec.js │ │ └── index.ts └── global.d.ts ├── .npmignore ├── .gitignore ├── examples └── simple │ ├── babel.config.js │ ├── vue.config.js │ ├── public │ ├── favicon.ico │ └── index.html │ ├── src │ ├── assets │ │ └── logo.png │ ├── main.js │ ├── App.vue │ └── components │ │ └── HelloWorld.vue │ ├── .gitignore │ ├── .eslintrc.js │ ├── README.md │ └── package.json ├── postcss.config.js ├── README.md ├── dist ├── vue-material-design-ripple.css ├── vue-material-design-ripple.css.map ├── vue-material-design-ripple.js └── vue-material-design-ripple.js.map ├── tslint.json ├── package.json ├── babel.config.js ├── webpack.config.js └── tsconfig.json /src/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /src/directives/touch/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | -------------------------------------------------------------------------------- /examples/simple/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /examples/simple/vue.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | chainWebpack: config => config.resolve.symlinks(false) 4 | } 5 | -------------------------------------------------------------------------------- /examples/simple/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/partykamdev/vue-material-design-ripple/HEAD/examples/simple/public/favicon.ico -------------------------------------------------------------------------------- /examples/simple/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/partykamdev/vue-material-design-ripple/HEAD/examples/simple/src/assets/logo.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const autoprefixer = require('autoprefixer') 2 | 3 | module.exports = ctx => ({ 4 | plugins: [ 5 | autoprefixer({ 6 | remove: false 7 | }) 8 | ] 9 | }) 10 | -------------------------------------------------------------------------------- /src/directives/touch/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface TouchStoredHandlers { 3 | touchstart: (e: TouchEvent) => void 4 | touchend: (e: TouchEvent) => void 5 | touchmove: (e: TouchEvent) => void 6 | } 7 | -------------------------------------------------------------------------------- /examples/simple/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /examples/simple/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Ripple from 'vue-material-design-ripple'; 3 | import 'vue-material-design-ripple/dist/vue-material-design-ripple.css'; 4 | import App from './App.vue' 5 | 6 | Vue.directive('ripple', Ripple); 7 | 8 | Vue.config.productionTip = false 9 | 10 | new Vue({ 11 | render: h => h(App), 12 | }).$mount('#app') 13 | -------------------------------------------------------------------------------- /examples/simple/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | 'eslint:recommended' 9 | ], 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 13 | }, 14 | parserOptions: { 15 | parser: 'babel-eslint' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/simple/README.md: -------------------------------------------------------------------------------- 1 | # simple 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | npm run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | npm run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-material-design-ripple 2 | 3 | Add material design ripple effect to your Vue project. 4 | 5 | ## Why another ripple plugin? 6 | 1. Most of current plugins don't work exactly as google implementation of ripple does 7 | 2. Some require weird setup or dependencies 8 | 3. None of existing solutions worked for me 9 | 10 | ## About this package 11 | 1. You can set ripple color 12 | 2. It doesn't have performance problems 13 | 3. It doesn't require any dependencies 14 | 4. It just works. Without headache or +100kb to your js build 15 | ## Demo 16 | https://codesandbox.io/s/vue-template-2f18v?fontsize=14 17 | -------------------------------------------------------------------------------- /examples/simple/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | simple 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/simple/src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 29 | -------------------------------------------------------------------------------- /src/directives/ripple/_variables.scss: -------------------------------------------------------------------------------- 1 | $transition: ( 2 | 'fast-out-slow-in': cubic-bezier(0.4, 0, 0.2, 1), 3 | 'linear-out-slow-in': cubic-bezier(0, 0, 0.2, 1), 4 | 'fast-out-linear-in': cubic-bezier(0.4, 0, 1, 1), 5 | 'ease-in-out': cubic-bezier(0.4, 0, 0.6, 1), 6 | 'fast-in-fast-out': cubic-bezier(0.25, 0.8, 0.25, 1), 7 | 'swing': cubic-bezier(0.25, 0.8, 0.5, 1) 8 | ) !default; 9 | $ripple-animation-transition-in: transform .25s cubic-bezier(0.4, 0, 0.2, 1), 10 | opacity .1s cubic-bezier(0.4, 0, 0.2, 1) !default; 11 | $ripple-animation-transition-out: opacity .3s cubic-bezier(0.4, 0, 0.2, 1) !default; 12 | $ripple-animation-visible-opacity: .15 !default; 13 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | import {TouchStoredHandlers} from "./directives/touch/index"; 2 | 3 | declare global { 4 | interface HTMLElement { 5 | _clickOutside?: EventListenerOrEventListenerObject 6 | _onResize?: { 7 | callback: () => void 8 | options?: boolean | AddEventListenerOptions 9 | } 10 | _ripple?: { 11 | enabled?: boolean 12 | centered?: boolean 13 | class?: string 14 | circle?: boolean 15 | touched?: boolean 16 | isTouch?: boolean 17 | } 18 | _onScroll?: { 19 | callback: EventListenerOrEventListenerObject 20 | options: boolean | AddEventListenerOptions 21 | target: EventTarget 22 | } 23 | _touchHandlers?: { 24 | [_uid: number]: TouchStoredHandlers 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/directives/ripple/VRipple.scss: -------------------------------------------------------------------------------- 1 | @import './_variables.scss'; 2 | 3 | .v-ripple { 4 | &__container { 5 | color: inherit; 6 | 7 | border-radius: inherit; 8 | position: absolute; 9 | width: 100%; 10 | height: 100%; 11 | left: 0; 12 | top: 0; 13 | overflow: hidden; 14 | z-index: 0; 15 | pointer-events: none; 16 | contain: strict; 17 | } 18 | 19 | &__animation { 20 | color: inherit; 21 | 22 | position: absolute; 23 | top: 0; 24 | left: 0; 25 | border-radius: 50%; 26 | background: currentColor; 27 | opacity: 0; 28 | pointer-events: none; 29 | overflow: hidden; 30 | will-change: transform, opacity; 31 | 32 | &--enter { 33 | transition: none; 34 | } 35 | 36 | &--in { 37 | transition: $ripple-animation-transition-in; 38 | } 39 | 40 | &--out { 41 | transition: $ripple-animation-transition-out; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /dist/vue-material-design-ripple.css: -------------------------------------------------------------------------------- 1 | .v-ripple__container { 2 | color: inherit; 3 | border-radius: inherit; 4 | position: absolute; 5 | width: 100%; 6 | height: 100%; 7 | left: 0; 8 | top: 0; 9 | overflow: hidden; 10 | z-index: 0; 11 | pointer-events: none; 12 | contain: strict; 13 | } 14 | .v-ripple__animation { 15 | color: inherit; 16 | position: absolute; 17 | top: 0; 18 | left: 0; 19 | border-radius: 50%; 20 | background: currentColor; 21 | opacity: 0; 22 | pointer-events: none; 23 | overflow: hidden; 24 | will-change: transform, opacity; 25 | } 26 | .v-ripple__animation--enter { 27 | transition: none; 28 | } 29 | .v-ripple__animation--in { 30 | transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.1s cubic-bezier(0.4, 0, 0.2, 1); 31 | } 32 | .v-ripple__animation--out { 33 | transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1); 34 | } 35 | 36 | /*# sourceMappingURL=vue-material-design-ripple.css.map*/ -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": "typestrict", 4 | "linterOptions": { 5 | "exclude": [ 6 | "**/*.spec.js", 7 | "**/node_modules/**/*" 8 | ] 9 | }, 10 | "jsRules": {}, 11 | "rules": { 12 | "array-type": [true, "array"], 13 | "use-type-alias": true, 14 | "no-inferrable-types": true, 15 | "unified-signatures": true, 16 | "no-undefined-argument": true, 17 | 18 | // TODO: fails with "TypeError: Cannot read property 'default' of undefined" 19 | "use-default-type-parameter": false, 20 | 21 | /* typestrict overrides */ 22 | 23 | // Handled by tsc 24 | "no-unused-variable": false, 25 | 26 | // Useless with vue 27 | "no-invalid-this": false, 28 | 29 | // Retarded 30 | "restrict-plus-operands": false, 31 | 32 | // Fails with no-unused-variable for some reason 33 | "no-useless-cast": false, 34 | 35 | "no-floating-promises": false 36 | }, 37 | "rulesDirectory": [] 38 | } 39 | -------------------------------------------------------------------------------- /dist/vue-material-design-ripple.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://MyLib/./src/directives/ripple/VRipple.scss"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"vue-material-design-ripple.css","sourcesContent":[".v-ripple__container {\n color: inherit;\n border-radius: inherit;\n position: absolute;\n width: 100%;\n height: 100%;\n left: 0;\n top: 0;\n overflow: hidden;\n z-index: 0;\n pointer-events: none;\n contain: strict;\n}\n.v-ripple__animation {\n color: inherit;\n position: absolute;\n top: 0;\n left: 0;\n border-radius: 50%;\n background: currentColor;\n opacity: 0;\n pointer-events: none;\n overflow: hidden;\n will-change: transform, opacity;\n}\n.v-ripple__animation--enter {\n transition: none;\n}\n.v-ripple__animation--in {\n transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.1s cubic-bezier(0.4, 0, 0.2, 1);\n}\n.v-ripple__animation--out {\n transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}"],"sourceRoot":""} -------------------------------------------------------------------------------- /examples/simple/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^2.6.5", 12 | "vue": "^2.6.10", 13 | "vue-material-design-ripple": "1.0.0" 14 | }, 15 | "devDependencies": { 16 | "@vue/cli-plugin-babel": "^3.10.0", 17 | "@vue/cli-plugin-eslint": "^3.10.0", 18 | "@vue/cli-service": "^3.10.0", 19 | "babel-eslint": "^10.0.1", 20 | "eslint": "^5.16.0", 21 | "eslint-plugin-vue": "^5.0.0", 22 | "vue-template-compiler": "^2.6.10" 23 | }, 24 | "eslintConfig": { 25 | "root": true, 26 | "env": { 27 | "node": true 28 | }, 29 | "extends": [ 30 | "plugin:vue/essential", 31 | "eslint:recommended" 32 | ], 33 | "rules": {}, 34 | "parserOptions": { 35 | "parser": "babel-eslint" 36 | } 37 | }, 38 | "postcss": { 39 | "plugins": { 40 | "autoprefixer": {} 41 | } 42 | }, 43 | "browserslist": [ 44 | "> 1%", 45 | "last 2 versions" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-material-design-ripple", 3 | "version": "1.0.0", 4 | "description": "Ripple directive extracted from Vuetify. Can be used without any dependencies", 5 | "main": "dist/vue-material-design-ripple.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "clean": "shx rm -rf dist", 9 | "build": "npm run clean && cross-env NODE_ENV=production webpack" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "awesome-typescript-loader": "^5.2.1", 15 | "typescript": "^3.5.3", 16 | "vue": "^2.5.0" 17 | }, 18 | "devDependencies": { 19 | "@babel/core": "^7.5.5", 20 | "@babel/plugin-proposal-object-rest-spread": "^7.5.5", 21 | "@babel/preset-env": "^7.5.5", 22 | "@types/node": "^12.7.1", 23 | "autoprefixer": "^9.6.1", 24 | "babel-loader": "^8.0.6", 25 | "css-loader": "^3.2.0", 26 | "fibers": "^4.0.1", 27 | "happypack": "^5.0.1", 28 | "mini-css-extract-plugin": "^0.8.0", 29 | "postcss-loader": "^3.0.0", 30 | "sass": "^1.22.9", 31 | "sass-loader": "^7.2.0", 32 | "shx": "^0.3.2", 33 | "style-loader": "^1.0.0", 34 | "ts-loader": "^6.0.4", 35 | "webpack": "^4.39.2", 36 | "webpack-cli": "^3.3.6" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/simple/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 22 | 23 | 24 | 40 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const vuetifyPackage = require('./package.json') 2 | 3 | const env = process.env.NODE_ENV 4 | 5 | module.exports = { 6 | presets: [ 7 | ['@babel/preset-env', { 8 | modules: false 9 | }] 10 | ], 11 | plugins: [ 12 | // ['transform-define', { 13 | // __VUETIFY_VERSION__: vuetifyPackage.version, 14 | // __REQUIRED_VUE__: vuetifyPackage.peerDependencies.vue 15 | // }] 16 | ], 17 | env: { 18 | test: { 19 | presets: [ 20 | ['@babel/preset-env', { 21 | targets: { node: true } 22 | }] 23 | ], 24 | plugins: [ 25 | ['module-resolver', { 26 | root: ['./src'], 27 | alias: { 28 | // '~components': 'components', 29 | '~directives': 'directives', 30 | // '~mixins': 'mixins', 31 | // '~scss': 'scss', 32 | // '~util': 'util' 33 | } 34 | }] 35 | ] 36 | }, 37 | es5: { 38 | presets: ['@babel/preset-env'] 39 | }, 40 | lib: { 41 | presets: [ 42 | ['@babel/preset-env', { 43 | targets: 'last 1 chrome version', 44 | modules: false 45 | }] 46 | ] 47 | } 48 | } 49 | } 50 | 51 | if (['lib', 'es5'].includes(env)) { 52 | module.exports.plugins.push('./build/babel-transform-sass-paths.js') 53 | } 54 | 55 | if (env !== 'lib') { 56 | module.exports.plugins.push('@babel/plugin-proposal-object-rest-spread') 57 | } 58 | -------------------------------------------------------------------------------- /src/directives/ripple/__tests__/ripple.spec.ts: -------------------------------------------------------------------------------- 1 | // Libraries 2 | import Vue from 'vue' 3 | 4 | // Directives 5 | import Ripple from '../' 6 | 7 | // Utilities 8 | import { 9 | mount, 10 | } from '@vue/test-utils' 11 | 12 | describe('ripple.ts', () => { 13 | it('Ripple with no value should render element with ripple enabled', () => { 14 | const testComponent = Vue.component('test', { 15 | directives: { 16 | Ripple, 17 | }, 18 | render (h) { 19 | const data = { 20 | directives: [{ 21 | name: 'ripple', 22 | }], 23 | } 24 | return h('div', data) 25 | }, 26 | }) 27 | 28 | const wrapper = mount(testComponent) 29 | 30 | const div = wrapper.find('div') 31 | expect(div.element['_ripple'].enabled).toBe(true) 32 | }) 33 | 34 | it('Ripple should update element property reactively', () => { 35 | const testComponent = Vue.component('test', { 36 | directives: { 37 | Ripple, 38 | }, 39 | props: { 40 | ripple: Boolean, 41 | default: () => false, 42 | }, 43 | render (h) { 44 | const data = { 45 | directives: [{ 46 | name: 'ripple', 47 | value: this.ripple, 48 | }], 49 | } 50 | return h('div', data) 51 | }, 52 | }) 53 | 54 | const wrapper = mount(testComponent, { 55 | propsData: { 56 | ripple: true, 57 | }, 58 | }) 59 | 60 | const div = wrapper.find('div') 61 | expect(div.element['_ripple'].enabled).toBe(true) 62 | 63 | wrapper.setProps({ ripple: false }) 64 | expect(div.element['_ripple'].enabled).toBe(false) 65 | 66 | wrapper.setProps({ ripple: true }) 67 | expect(div.element['_ripple'].enabled).toBe(true) 68 | }) 69 | }) 70 | -------------------------------------------------------------------------------- /src/directives/ripple/__tests__/ripple.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | // Libraries 4 | var vue_1 = require("vue"); 5 | // Directives 6 | var __1 = require("../"); 7 | // Utilities 8 | var test_utils_1 = require("@vue/test-utils"); 9 | describe('ripple.ts', function () { 10 | it('Ripple with no value should render element with ripple enabled', function () { 11 | var testComponent = vue_1["default"].component('test', { 12 | directives: { 13 | Ripple: __1["default"] 14 | }, 15 | render: function (h) { 16 | var data = { 17 | directives: [{ 18 | name: 'ripple' 19 | }] 20 | }; 21 | return h('div', data); 22 | } 23 | }); 24 | var wrapper = test_utils_1.mount(testComponent); 25 | var div = wrapper.find('div'); 26 | expect(div.element['_ripple'].enabled).toBe(true); 27 | }); 28 | it('Ripple should update element property reactively', function () { 29 | var testComponent = vue_1["default"].component('test', { 30 | directives: { 31 | Ripple: __1["default"] 32 | }, 33 | props: { 34 | ripple: Boolean, 35 | "default": function () { return false; } 36 | }, 37 | render: function (h) { 38 | var data = { 39 | directives: [{ 40 | name: 'ripple', 41 | value: this.ripple 42 | }] 43 | }; 44 | return h('div', data); 45 | } 46 | }); 47 | var wrapper = test_utils_1.mount(testComponent, { 48 | propsData: { 49 | ripple: true 50 | } 51 | }); 52 | var div = wrapper.find('div'); 53 | expect(div.element['_ripple'].enabled).toBe(true); 54 | wrapper.setProps({ ripple: false }); 55 | expect(div.element['_ripple'].enabled).toBe(false); 56 | wrapper.setProps({ ripple: true }); 57 | expect(div.element['_ripple'].enabled).toBe(true); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const os = require('os'); 4 | const HappyPack = require('happypack'); 5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 6 | const happyThreadPool = HappyPack.ThreadPool({ 7 | size: Math.min(os.cpus().length, 4) 8 | }); 9 | const isProd = process.env.NODE_ENV === 'production' 10 | const extractCSS = isProd || process.env.TARGET === 'development' 11 | 12 | const cssLoaders = [ 13 | // https://github.com/webpack-contrib/mini-css-extract-plugin#user-content-advanced-configuration-example 14 | // TODO: remove style-loader: https://github.com/webpack-contrib/mini-css-extract-plugin/issues/34 15 | extractCSS ? MiniCssExtractPlugin.loader : 'style-loader', 16 | { loader: 'css-loader', options: { sourceMap: !isProd } }, 17 | { loader: 'postcss-loader', options: { sourceMap: !isProd } } 18 | ] 19 | const scssLoaders = [ 20 | ...cssLoaders, 21 | { loader: 'sass-loader', options: { 22 | implementation: require('sass'), 23 | fiber: require('fibers'), 24 | indentedSyntax: false 25 | } } 26 | ] 27 | module.exports = { 28 | entry: { 29 | 'vue-material-design-ripple': './src/directives/ripple/index.ts', 30 | }, 31 | output: { 32 | path: path.resolve(__dirname, 'dist'), 33 | filename: '[name].js', 34 | libraryTarget: 'umd', 35 | library: 'MyLib', 36 | umdNamedDefine: true 37 | }, 38 | resolve: { 39 | extensions: ['.ts', '.tsx', '.js'] 40 | }, 41 | devtool: 'source-map', 42 | plugins: [ 43 | 44 | new HappyPack({ 45 | id: 'scripts', 46 | threadPool: happyThreadPool, 47 | loaders: [ 48 | 'babel-loader', 49 | { 50 | loader: 'ts-loader', 51 | options: {happyPackMode: true} 52 | } 53 | ] 54 | }), 55 | new MiniCssExtractPlugin({ 56 | filename: 'vue-material-design-ripple.css' 57 | }) 58 | ], 59 | module: { 60 | rules: [ 61 | { 62 | test: /\.[jt]s$/, 63 | use: 'happypack/loader?id=scripts', 64 | exclude: /node_modules/ 65 | }, 66 | { 67 | test: /\.scss$/, 68 | use: scssLoaders 69 | } 70 | ] 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /dist/vue-material-design-ripple.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("MyLib",[],t):"object"==typeof exports?exports.MyLib=t():e.MyLib=t()}(window,function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(i,r,function(t){return e[t]}.bind(null,r));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t),n.d(t,"Ripple",function(){return d});n(1);function i(e,t){e.style.transform=t,e.style.webkitTransform=t}function r(e,t){e.style.opacity=t.toString()}function o(e){return"TouchEvent"===e.constructor.name}var a={show:function(e,t,n){if(void 0===n&&(n={}),t._ripple&&t._ripple.enabled){var a=document.createElement("span"),s=document.createElement("span");a.appendChild(s),a.className="v-ripple__container",n.class&&(a.className+=" "+n.class);var l=function(e,t,n){void 0===n&&(n={});var i=t.getBoundingClientRect(),r=o(e)?e.touches[e.touches.length-1]:e,a=r.clientX-i.left,s=r.clientY-i.top,l=0,c=.3;t._ripple&&t._ripple.circle?(c=.15,l=t.clientWidth/2,l=n.center?l:l+Math.sqrt(Math.pow(a-l,2)+Math.pow(s-l,2))/4):l=Math.sqrt(Math.pow(t.clientWidth,2)+Math.pow(t.clientHeight,2))/2;var p=(t.clientWidth-2*l)/2+"px",u=(t.clientHeight-2*l)/2+"px";return{radius:l,scale:c,x:n.center?p:a-l+"px",y:n.center?u:s-l+"px",centerX:p,centerY:u}}(e,t,n),c=l.radius,p=l.scale,u=l.x,d=l.y,v=l.centerX,f=l.centerY,m=2*c+"px";s.className="v-ripple__animation",s.style.width=m,s.style.height=m,t.appendChild(a);var _=window.getComputedStyle(t);_&&"static"===_.position&&(t.style.position="relative",t.dataset.previousPosition="static"),s.classList.add("v-ripple__animation--enter"),s.classList.add("v-ripple__animation--visible"),i(s,"translate("+u+", "+d+") scale3d("+p+","+p+","+p+")"),r(s,0),s.dataset.activated=String(performance.now()),setTimeout(function(){s.classList.remove("v-ripple__animation--enter"),s.classList.add("v-ripple__animation--in"),i(s,"translate("+v+", "+f+") scale3d(1,1,1)"),r(s,.25)},0)}},hide:function(e){if(e&&e._ripple&&e._ripple.enabled){var t=e.getElementsByClassName("v-ripple__animation");if(0!==t.length){var n=t[t.length-1];if(!n.dataset.isHiding){n.dataset.isHiding="true";var i=performance.now()-Number(n.dataset.activated),o=Math.max(250-i,0);setTimeout(function(){n.classList.remove("v-ripple__animation--in"),n.classList.add("v-ripple__animation--out"),r(n,0),setTimeout(function(){1===e.getElementsByClassName("v-ripple__animation").length&&e.dataset.previousPosition&&(e.style.position=e.dataset.previousPosition,delete e.dataset.previousPosition),n.parentNode&&e.removeChild(n.parentNode)},300)},o)}}}}};function s(e){return void 0===e||!!e}function l(e){var t={},n=e.currentTarget;if(n&&n._ripple&&!n._ripple.touched){if(o(e))n._ripple.touched=!0,n._ripple.isTouch=!0;else if(n._ripple.isTouch)return;t.center=n._ripple.centered,n._ripple.class&&(t.class=n._ripple.class),a.show(e,n,t)}}function c(e){var t=e.currentTarget;t&&(window.setTimeout(function(){t._ripple&&(t._ripple.touched=!1)}),a.hide(t))}function p(e,t,n){var i=s(t.value);i||a.hide(e),e._ripple=e._ripple||{},e._ripple.enabled=i;var r=t.value||{};r.center&&(e._ripple.centered=!0),r.class&&(e._ripple.class=t.value.class),r.circle&&(e._ripple.circle=r.circle),i&&!n?(e.addEventListener("touchstart",l,{passive:!0}),e.addEventListener("touchend",c,{passive:!0}),e.addEventListener("touchcancel",c),e.addEventListener("mousedown",l),e.addEventListener("mouseup",c),e.addEventListener("mouseleave",c),e.addEventListener("dragstart",c,{passive:!0})):!i&&n&&u(e)}function u(e){e.removeEventListener("mousedown",l),e.removeEventListener("touchstart",c),e.removeEventListener("touchend",c),e.removeEventListener("touchcancel",c),e.removeEventListener("mouseup",c),e.removeEventListener("mouseleave",c),e.removeEventListener("dragstart",c)}var d={bind:function(e,t,n){p(e,t,!1)},unbind:function(e){delete e._ripple,u(e)},update:function(e,t){t.value!==t.oldValue&&p(e,t,s(t.oldValue))}};t.default=d},function(e,t,n){}])}); 2 | //# sourceMappingURL=vue-material-design-ripple.js.map -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ 5 | "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "lib": [ /* Specify library files to be included in the compilation */ 7 | "es2015", 8 | "es2016", 9 | "es2017", 10 | "es2018", 11 | "dom", 12 | "dom.iterable" 13 | ], 14 | "allowJs": true, /* Allow javascript files to be compiled. */ 15 | // "checkJs": true, /* Report errors in .js files. */ 16 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 17 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 18 | "sourceMap": true, /* Generates corresponding '.map' file. */ 19 | // "outFile": "./", /* Concatenate and emit output to single file. */ 20 | "outDir": "./dist", /* Redirect output structure to the directory. */ 21 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 22 | // "removeComments": true, /* Do not emit comments to output. */ 23 | // "noEmit": true, /* Do not emit outputs. */ 24 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 25 | "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 26 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 27 | 28 | /* Strict Type-Checking Options */ 29 | "strict": true, /* Enable all strict type-checking options. */ 30 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 31 | // "strictNullChecks": true, /* Enable strict null checks. */ 32 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | "noUnusedLocals": true, /* Report errors on unused locals. */ 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | 43 | /* Module Resolution Options */ 44 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 45 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 46 | // "paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 47 | // }, 48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 49 | // "typeRoots": [], /* List of folders to include type definitions from. */ 50 | // "types": [], /* Type declaration files to be included in compilation. */ 51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 52 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 54 | 55 | /* Source Map Options */ 56 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 57 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 59 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 60 | 61 | /* Experimental Options */ 62 | "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 63 | "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */ 64 | }, 65 | "include": [ 66 | "src/global.d.ts", 67 | "src/directives/ripple/index.ts" 68 | ], 69 | "exclude": [ 70 | "**/*.spec.ts", 71 | "**/*.spec.js", 72 | "node_modules", 73 | "dist" 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /src/directives/ripple/index.ts: -------------------------------------------------------------------------------- 1 | // Styles 2 | import './VRipple.scss' 3 | 4 | import { VNode, VNodeDirective } from 'vue' 5 | // import { consoleWarn } from '../../util/console' 6 | 7 | function transform (el: HTMLElement, value: string) { 8 | el.style['transform'] = value 9 | el.style['webkitTransform'] = value 10 | } 11 | 12 | function opacity (el: HTMLElement, value: number) { 13 | el.style['opacity'] = value.toString() 14 | } 15 | 16 | export interface RippleOptions { 17 | class?: string 18 | center?: boolean 19 | circle?: boolean 20 | } 21 | 22 | function isTouchEvent (e: MouseEvent | TouchEvent): e is TouchEvent { 23 | return e.constructor.name === 'TouchEvent' 24 | } 25 | 26 | const calculate = (e: MouseEvent | TouchEvent, el: HTMLElement, value: RippleOptions = {}) => { 27 | const offset = el.getBoundingClientRect() 28 | const target = isTouchEvent(e) ? e.touches[e.touches.length - 1] : e 29 | const localX = target.clientX - offset.left 30 | const localY = target.clientY - offset.top 31 | 32 | let radius = 0 33 | let scale = 0.3 34 | if (el._ripple && el._ripple.circle) { 35 | scale = 0.15 36 | radius = el.clientWidth / 2 37 | radius = value.center ? radius : radius + Math.sqrt((localX - radius) ** 2 + (localY - radius) ** 2) / 4 38 | } else { 39 | radius = Math.sqrt(el.clientWidth ** 2 + el.clientHeight ** 2) / 2 40 | } 41 | 42 | const centerX = `${(el.clientWidth - (radius * 2)) / 2}px` 43 | const centerY = `${(el.clientHeight - (radius * 2)) / 2}px` 44 | 45 | const x = value.center ? centerX : `${localX - radius}px` 46 | const y = value.center ? centerY : `${localY - radius}px` 47 | 48 | return { radius, scale, x, y, centerX, centerY } 49 | } 50 | 51 | const ripples = { 52 | /* eslint-disable max-statements */ 53 | show (e: MouseEvent | TouchEvent, el: HTMLElement, value: RippleOptions = {}) { 54 | if (!el._ripple || !el._ripple.enabled) { 55 | return 56 | } 57 | 58 | const container = document.createElement('span') 59 | const animation = document.createElement('span') 60 | 61 | container.appendChild(animation) 62 | container.className = 'v-ripple__container' 63 | 64 | if (value.class) { 65 | container.className += ` ${value.class}` 66 | } 67 | 68 | const { radius, scale, x, y, centerX, centerY } = calculate(e, el, value) 69 | 70 | const size = `${radius * 2}px` 71 | animation.className = 'v-ripple__animation' 72 | animation.style.width = size 73 | animation.style.height = size 74 | 75 | el.appendChild(container) 76 | 77 | const computed = window.getComputedStyle(el) 78 | if (computed && computed.position === 'static') { 79 | el.style.position = 'relative' 80 | el.dataset.previousPosition = 'static' 81 | } 82 | 83 | animation.classList.add('v-ripple__animation--enter') 84 | animation.classList.add('v-ripple__animation--visible') 85 | transform(animation, `translate(${x}, ${y}) scale3d(${scale},${scale},${scale})`) 86 | opacity(animation, 0) 87 | animation.dataset.activated = String(performance.now()) 88 | 89 | setTimeout(() => { 90 | animation.classList.remove('v-ripple__animation--enter') 91 | animation.classList.add('v-ripple__animation--in') 92 | transform(animation, `translate(${centerX}, ${centerY}) scale3d(1,1,1)`) 93 | opacity(animation, 0.25) 94 | }, 0) 95 | }, 96 | 97 | hide (el: HTMLElement | null) { 98 | if (!el || !el._ripple || !el._ripple.enabled) return 99 | 100 | const ripples = el.getElementsByClassName('v-ripple__animation') 101 | 102 | if (ripples.length === 0) return 103 | const animation = ripples[ripples.length - 1] 104 | 105 | if (animation.dataset.isHiding) return 106 | else animation.dataset.isHiding = 'true' 107 | 108 | const diff = performance.now() - Number(animation.dataset.activated) 109 | const delay = Math.max(250 - diff, 0) 110 | 111 | setTimeout(() => { 112 | animation.classList.remove('v-ripple__animation--in') 113 | animation.classList.add('v-ripple__animation--out') 114 | opacity(animation, 0) 115 | 116 | setTimeout(() => { 117 | const ripples = el.getElementsByClassName('v-ripple__animation') 118 | if (ripples.length === 1 && el.dataset.previousPosition) { 119 | el.style.position = el.dataset.previousPosition 120 | delete el.dataset.previousPosition 121 | } 122 | 123 | animation.parentNode && el.removeChild(animation.parentNode) 124 | }, 300) 125 | }, delay) 126 | }, 127 | } 128 | 129 | function isRippleEnabled (value: any): value is true { 130 | return typeof value === 'undefined' || !!value 131 | } 132 | 133 | function rippleShow (e: MouseEvent | TouchEvent) { 134 | const value: RippleOptions = {} 135 | const element = e.currentTarget as HTMLElement 136 | if (!element || !element._ripple || element._ripple.touched) return 137 | if (isTouchEvent(e)) { 138 | element._ripple.touched = true 139 | element._ripple.isTouch = true 140 | } else { 141 | // It's possible for touch events to fire 142 | // as mouse events on Android/iOS, this 143 | // will skip the event call if it has 144 | // already been registered as touch 145 | if (element._ripple.isTouch) return 146 | } 147 | value.center = element._ripple.centered 148 | if (element._ripple.class) { 149 | value.class = element._ripple.class 150 | } 151 | ripples.show(e, element, value) 152 | } 153 | 154 | function rippleHide (e: Event) { 155 | const element = e.currentTarget as HTMLElement | null 156 | if (!element) return 157 | 158 | window.setTimeout(() => { 159 | if (element._ripple) { 160 | element._ripple.touched = false 161 | } 162 | }) 163 | ripples.hide(element) 164 | } 165 | 166 | function updateRipple (el: HTMLElement, binding: VNodeDirective, wasEnabled: boolean) { 167 | const enabled = isRippleEnabled(binding.value) 168 | if (!enabled) { 169 | ripples.hide(el) 170 | } 171 | el._ripple = el._ripple || {} 172 | el._ripple.enabled = enabled 173 | const value = binding.value || {} 174 | if (value.center) { 175 | el._ripple.centered = true 176 | } 177 | if (value.class) { 178 | el._ripple.class = binding.value.class 179 | } 180 | if (value.circle) { 181 | el._ripple.circle = value.circle 182 | } 183 | if (enabled && !wasEnabled) { 184 | el.addEventListener('touchstart', rippleShow, { passive: true }) 185 | el.addEventListener('touchend', rippleHide, { passive: true }) 186 | el.addEventListener('touchcancel', rippleHide) 187 | 188 | el.addEventListener('mousedown', rippleShow) 189 | el.addEventListener('mouseup', rippleHide) 190 | el.addEventListener('mouseleave', rippleHide) 191 | // Anchor tags can be dragged, causes other hides to fail - #1537 192 | el.addEventListener('dragstart', rippleHide, { passive: true }) 193 | } else if (!enabled && wasEnabled) { 194 | removeListeners(el) 195 | } 196 | } 197 | 198 | function removeListeners (el: HTMLElement) { 199 | el.removeEventListener('mousedown', rippleShow) 200 | el.removeEventListener('touchstart', rippleHide) 201 | el.removeEventListener('touchend', rippleHide) 202 | el.removeEventListener('touchcancel', rippleHide) 203 | el.removeEventListener('mouseup', rippleHide) 204 | el.removeEventListener('mouseleave', rippleHide) 205 | el.removeEventListener('dragstart', rippleHide) 206 | } 207 | 208 | function directive (el: HTMLElement, binding: VNodeDirective, node: VNode) { 209 | updateRipple(el, binding, false) 210 | 211 | if (process.env.NODE_ENV === 'development') { 212 | // warn if an inline element is used, waiting for el to be in the DOM first 213 | node.context && node.context.$nextTick(() => { 214 | const computed = window.getComputedStyle(el) 215 | if (computed && computed.display === 'inline') { 216 | // const context = (node as any).fnOptions ? [(node as any).fnOptions, node.context] : [node.componentInstance] 217 | // consoleWarn('v-ripple can only be used on block-level elements', ...context) 218 | } 219 | }) 220 | } 221 | } 222 | 223 | function unbind (el: HTMLElement) { 224 | delete el._ripple 225 | removeListeners(el) 226 | } 227 | 228 | function update (el: HTMLElement, binding: VNodeDirective) { 229 | if (binding.value === binding.oldValue) { 230 | return 231 | } 232 | 233 | const wasEnabled = isRippleEnabled(binding.oldValue) 234 | updateRipple(el, binding, wasEnabled) 235 | } 236 | 237 | export const Ripple = { 238 | bind: directive, 239 | unbind, 240 | update, 241 | } 242 | 243 | export default Ripple 244 | -------------------------------------------------------------------------------- /dist/vue-material-design-ripple.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://MyLib/webpack/universalModuleDefinition","webpack://MyLib/webpack/bootstrap","webpack://MyLib/./src/directives/ripple/index.ts"],"names":["root","factory","exports","module","define","amd","window","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","transform","el","style","opacity","toString","isTouchEvent","e","constructor","ripples","show","_ripple","enabled","container","document","createElement","animation","appendChild","className","offset","getBoundingClientRect","target","touches","length","localX","clientX","left","localY","clientY","top","radius","scale","circle","clientWidth","center","Math","sqrt","clientHeight","centerX","centerY","x","y","size","width","height","computed","getComputedStyle","position","dataset","previousPosition","classList","add","activated","String","performance","now","setTimeout","remove","hide","getElementsByClassName","isHiding","diff","Number","delay","max","parentNode","removeChild","isRippleEnabled","rippleShow","element","currentTarget","touched","isTouch","centered","rippleHide","updateRipple","binding","wasEnabled","addEventListener","passive","removeListeners","removeEventListener","Ripple","node","unbind","update","oldValue"],"mappings":"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","file":"vue-material-design-ripple.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"MyLib\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"MyLib\"] = factory();\n\telse\n\t\troot[\"MyLib\"] = factory();\n})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","// Styles\nimport './VRipple.scss'\n\nimport { VNode, VNodeDirective } from 'vue'\n// import { consoleWarn } from '../../util/console'\n\nfunction transform (el: HTMLElement, value: string) {\n el.style['transform'] = value\n el.style['webkitTransform'] = value\n}\n\nfunction opacity (el: HTMLElement, value: number) {\n el.style['opacity'] = value.toString()\n}\n\nexport interface RippleOptions {\n class?: string\n center?: boolean\n circle?: boolean\n}\n\nfunction isTouchEvent (e: MouseEvent | TouchEvent): e is TouchEvent {\n return e.constructor.name === 'TouchEvent'\n}\n\nconst calculate = (e: MouseEvent | TouchEvent, el: HTMLElement, value: RippleOptions = {}) => {\n const offset = el.getBoundingClientRect()\n const target = isTouchEvent(e) ? e.touches[e.touches.length - 1] : e\n const localX = target.clientX - offset.left\n const localY = target.clientY - offset.top\n\n let radius = 0\n let scale = 0.3\n if (el._ripple && el._ripple.circle) {\n scale = 0.15\n radius = el.clientWidth / 2\n radius = value.center ? radius : radius + Math.sqrt((localX - radius) ** 2 + (localY - radius) ** 2) / 4\n } else {\n radius = Math.sqrt(el.clientWidth ** 2 + el.clientHeight ** 2) / 2\n }\n\n const centerX = `${(el.clientWidth - (radius * 2)) / 2}px`\n const centerY = `${(el.clientHeight - (radius * 2)) / 2}px`\n\n const x = value.center ? centerX : `${localX - radius}px`\n const y = value.center ? centerY : `${localY - radius}px`\n\n return { radius, scale, x, y, centerX, centerY }\n}\n\nconst ripples = {\n /* eslint-disable max-statements */\n show (e: MouseEvent | TouchEvent, el: HTMLElement, value: RippleOptions = {}) {\n if (!el._ripple || !el._ripple.enabled) {\n return\n }\n\n const container = document.createElement('span')\n const animation = document.createElement('span')\n\n container.appendChild(animation)\n container.className = 'v-ripple__container'\n\n if (value.class) {\n container.className += ` ${value.class}`\n }\n\n const { radius, scale, x, y, centerX, centerY } = calculate(e, el, value)\n\n const size = `${radius * 2}px`\n animation.className = 'v-ripple__animation'\n animation.style.width = size\n animation.style.height = size\n\n el.appendChild(container)\n\n const computed = window.getComputedStyle(el)\n if (computed && computed.position === 'static') {\n el.style.position = 'relative'\n el.dataset.previousPosition = 'static'\n }\n\n animation.classList.add('v-ripple__animation--enter')\n animation.classList.add('v-ripple__animation--visible')\n transform(animation, `translate(${x}, ${y}) scale3d(${scale},${scale},${scale})`)\n opacity(animation, 0)\n animation.dataset.activated = String(performance.now())\n\n setTimeout(() => {\n animation.classList.remove('v-ripple__animation--enter')\n animation.classList.add('v-ripple__animation--in')\n transform(animation, `translate(${centerX}, ${centerY}) scale3d(1,1,1)`)\n opacity(animation, 0.25)\n }, 0)\n },\n\n hide (el: HTMLElement | null) {\n if (!el || !el._ripple || !el._ripple.enabled) return\n\n const ripples = el.getElementsByClassName('v-ripple__animation')\n\n if (ripples.length === 0) return\n const animation = ripples[ripples.length - 1]\n\n if (animation.dataset.isHiding) return\n else animation.dataset.isHiding = 'true'\n\n const diff = performance.now() - Number(animation.dataset.activated)\n const delay = Math.max(250 - diff, 0)\n\n setTimeout(() => {\n animation.classList.remove('v-ripple__animation--in')\n animation.classList.add('v-ripple__animation--out')\n opacity(animation, 0)\n\n setTimeout(() => {\n const ripples = el.getElementsByClassName('v-ripple__animation')\n if (ripples.length === 1 && el.dataset.previousPosition) {\n el.style.position = el.dataset.previousPosition\n delete el.dataset.previousPosition\n }\n\n animation.parentNode && el.removeChild(animation.parentNode)\n }, 300)\n }, delay)\n },\n}\n\nfunction isRippleEnabled (value: any): value is true {\n return typeof value === 'undefined' || !!value\n}\n\nfunction rippleShow (e: MouseEvent | TouchEvent) {\n const value: RippleOptions = {}\n const element = e.currentTarget as HTMLElement\n if (!element || !element._ripple || element._ripple.touched) return\n if (isTouchEvent(e)) {\n element._ripple.touched = true\n element._ripple.isTouch = true\n } else {\n // It's possible for touch events to fire\n // as mouse events on Android/iOS, this\n // will skip the event call if it has\n // already been registered as touch\n if (element._ripple.isTouch) return\n }\n value.center = element._ripple.centered\n if (element._ripple.class) {\n value.class = element._ripple.class\n }\n ripples.show(e, element, value)\n}\n\nfunction rippleHide (e: Event) {\n const element = e.currentTarget as HTMLElement | null\n if (!element) return\n\n window.setTimeout(() => {\n if (element._ripple) {\n element._ripple.touched = false\n }\n })\n ripples.hide(element)\n}\n\nfunction updateRipple (el: HTMLElement, binding: VNodeDirective, wasEnabled: boolean) {\n const enabled = isRippleEnabled(binding.value)\n if (!enabled) {\n ripples.hide(el)\n }\n el._ripple = el._ripple || {}\n el._ripple.enabled = enabled\n const value = binding.value || {}\n if (value.center) {\n el._ripple.centered = true\n }\n if (value.class) {\n el._ripple.class = binding.value.class\n }\n if (value.circle) {\n el._ripple.circle = value.circle\n }\n if (enabled && !wasEnabled) {\n el.addEventListener('touchstart', rippleShow, { passive: true })\n el.addEventListener('touchend', rippleHide, { passive: true })\n el.addEventListener('touchcancel', rippleHide)\n\n el.addEventListener('mousedown', rippleShow)\n el.addEventListener('mouseup', rippleHide)\n el.addEventListener('mouseleave', rippleHide)\n // Anchor tags can be dragged, causes other hides to fail - #1537\n el.addEventListener('dragstart', rippleHide, { passive: true })\n } else if (!enabled && wasEnabled) {\n removeListeners(el)\n }\n}\n\nfunction removeListeners (el: HTMLElement) {\n el.removeEventListener('mousedown', rippleShow)\n el.removeEventListener('touchstart', rippleHide)\n el.removeEventListener('touchend', rippleHide)\n el.removeEventListener('touchcancel', rippleHide)\n el.removeEventListener('mouseup', rippleHide)\n el.removeEventListener('mouseleave', rippleHide)\n el.removeEventListener('dragstart', rippleHide)\n}\n\nfunction directive (el: HTMLElement, binding: VNodeDirective, node: VNode) {\n updateRipple(el, binding, false)\n\n if (process.env.NODE_ENV === 'development') {\n // warn if an inline element is used, waiting for el to be in the DOM first\n node.context && node.context.$nextTick(() => {\n const computed = window.getComputedStyle(el)\n if (computed && computed.display === 'inline') {\n // const context = (node as any).fnOptions ? [(node as any).fnOptions, node.context] : [node.componentInstance]\n // consoleWarn('v-ripple can only be used on block-level elements', ...context)\n }\n })\n }\n}\n\nfunction unbind (el: HTMLElement) {\n delete el._ripple\n removeListeners(el)\n}\n\nfunction update (el: HTMLElement, binding: VNodeDirective) {\n if (binding.value === binding.oldValue) {\n return\n }\n\n const wasEnabled = isRippleEnabled(binding.oldValue)\n updateRipple(el, binding, wasEnabled)\n}\n\nexport const Ripple = {\n bind: directive,\n unbind,\n update,\n}\n\nexport default Ripple\n"],"sourceRoot":""} --------------------------------------------------------------------------------