├── 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 |
2 |
3 |

4 |
5 |
6 |
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 |
2 |
3 |
{{ msg }}
4 |
5 | For a guide and recipes on how to configure / customize this project,
6 | check out the
7 | vue-cli documentation.
8 |
9 |
Click me
10 |
Color stuff
11 |
12 |
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":""}
--------------------------------------------------------------------------------