├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .prettierrc ├── README.md ├── example ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── App.js │ ├── App.test.js │ ├── index.css │ └── index.js └── yarn.lock ├── package.json ├── src ├── __tests__ │ └── index.test.tsx ├── index.ts └── react-app-env.d.ts ├── tsconfig.json ├── tsconfig.test.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | node_modules/ 4 | .snapshots/ 5 | *.min.js -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "standard-react", 6 | "plugin:prettier/recommended", 7 | "prettier/standard", 8 | "prettier/react" 9 | ], 10 | "env": { 11 | "node": true 12 | }, 13 | "parserOptions": { 14 | "ecmaVersion": 2020, 15 | "ecmaFeatures": { 16 | "legacyDecorators": true, 17 | "jsx": true 18 | } 19 | }, 20 | "settings": { 21 | "react": { 22 | "version": "16" 23 | } 24 | }, 25 | "rules": { 26 | "space-before-function-paren": 0, 27 | "react/prop-types": 0, 28 | "react/jsx-handler-names": 0, 29 | "react/jsx-fragments": 0, 30 | "react/no-unused-prop-types": 0, 31 | "import/export": 0 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # See https://help.github.com/ignore-files/ for more about ignoring files. 3 | 4 | # dependencies 5 | node_modules 6 | 7 | # builds 8 | build 9 | dist 10 | .rpt2_cache 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | 24 | coverage 25 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-use-ripple 2 | 3 | > A react hook to implement [Material Design ripple overlays](https://www.google.com) 4 | 5 | [![NPM](https://img.shields.io/npm/v/react-use-ripple.svg)](https://www.npmjs.com/package/react-use-ripple) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) 6 | 7 | ## Demo 8 | 9 | [![Edit react-use-ripple demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/heuristic-currying-kp08n?fontsize=14&hidenavigation=1&theme=dark) 10 | 11 | ## Install 12 | 13 | ```bash 14 | npm install --save react-use-ripple 15 | ``` 16 | 17 | ## Usage 18 | 19 | `useRipple` only requires the ref of the DOM element that the ripple should be applied too. 20 | 21 | ```tsx 22 | import React from 'react'; 23 | import { useRipple } from 'react-use-ripple'; 24 | import { useRef } from 'react'; 25 | 26 | const App = () => { 27 | const ref = useRef(); 28 | useRipple(ref); 29 | 30 | return ( 31 | 34 | ); 35 | }; 36 | 37 | export default App; 38 | ``` 39 | 40 | ## Arguments 41 | 42 | `useRipple(ref [, options])` 43 | 44 | - ref `>` 45 | - options `` 46 | - disabled `` **Default** `false` 47 | - rippleColor\* `` **Default** `rgba(0, 0, 0, 0.3)` 48 | - animationLength** `` **Default\*\* `700` 49 | - rippleSize**\* `` **Default\*\* `100` 50 | - excludedRefs**\*\* `>[]` **Default\*\* `[]` 51 | 52 | > \* `rippleColor` can be any valid css color that is supported by the targeted browsers 53 | > 54 | > \*\* `animationLength` will always be in milliseconds 55 | > 56 | > \*\*\* `rippleSize` will use the smallest value of the length, width, or specified size. if specifying a size, the unit will be in pixels. 57 | > 58 | > \*\*\*\* `excludedRefs` a list of child refs that should not cause a ripple when clicked 59 | 60 | ## FAQ 61 | 62 | **Q.** Can `useRipple` be used more than once in a component? 63 | **A.** `useRipple` can be used as many times as neccessary inside a component. 64 | 65 | **Q.** Does `useRipple` support SSR? 66 | **A.** Yup! Thanks to a recent PR, `useRipple` now supports SSR (as of version 1.4.0) 67 | 68 | **Q.** Can the `useRipple` hook be conditionally rendered? 69 | **A.** No. Keep the rules-of-hooks in mind when using use `useRipple` and do not conditionally render it. 70 | 71 | **Q.** What happens when the JSX with the ref is conditionally rendered? Will the ripple still work when the component mounts? 72 | **A.** `useRipple` can handle null references, so if an element using a the ripple effect is conditionally rendered, the effect will applied when the component mounts. 73 | 74 | **Q.** How does adding a ripple to an element affect its css? 75 | **A.** `useRipple` does two things to an element's css: 76 | 77 | - If an element's position is not set (i.e. its position is `initial` or `static`) than `position: relative` is added to the element. 78 | - `useRipple` sets the overflow of the element to `hidden`, to prevent the ripple from extending outside the element. 79 | 80 | **NOTE:** If your using child or descendant selectors to style `span` elements, it may affect the css of the ripple. To avoid this, use specificity to target span elements that you wish to style. 81 | 82 | ## License 83 | 84 | MIT © [Charles Badger](https://github.com/cbadger85) 85 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | This example was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | It is linked to the use-ripple package in the parent directory for development purposes. 4 | 5 | You can run `yarn install` and then `yarn start` to test your package. 6 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-use-ripple-example", 3 | "homepage": "https://github.com/cbadger85/react-use-ripple", 4 | "version": "0.0.0", 5 | "private": true, 6 | "dependencies": { 7 | "react": "link:../node_modules/react", 8 | "react-dom": "link:../node_modules/react-dom", 9 | "react-scripts": "link:../node_modules/react-scripts", 10 | "react-use-ripple": "link:.." 11 | }, 12 | "scripts": { 13 | "start": "node ../node_modules/react-scripts/bin/react-scripts.js start", 14 | "build": "node ../node_modules/react-scripts/bin/react-scripts.js build", 15 | "test": "node ../node_modules/react-scripts/bin/react-scripts.js test", 16 | "eject": "node ../node_modules/react-scripts/bin/react-scripts.js eject" 17 | }, 18 | "eslintConfig": { 19 | "extends": "react-app" 20 | }, 21 | "browserslist": [ 22 | ">0.2%", 23 | "not dead", 24 | "not ie <= 11", 25 | "not op_mini all" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbadger85/react-use-ripple/8dbaf898e6327e93cb87476b94459006069b5940/example/public/favicon.ico -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 16 | 17 | 18 | 27 | use-ripple 28 | 29 | 30 | 31 | 34 | 35 |
36 | 37 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /example/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "react-use-ripple", 3 | "name": "react-use-ripple", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /example/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState } from 'react'; 2 | import { useRipple } from 'react-use-ripple'; 3 | 4 | const App = () => { 5 | const ref = useRef(); 6 | const excludedRef = useRef(); 7 | const [isShown, setIsShown] = useState(false); 8 | useRipple(ref, { excludedRefs: [excludedRef] }); 9 | 10 | return ( 11 | 16 | ); 17 | }; 18 | 19 | export default App; 20 | -------------------------------------------------------------------------------- /example/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './App' 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div') 7 | ReactDOM.render(, div) 8 | ReactDOM.unmountComponentAtNode(div) 9 | }) 10 | -------------------------------------------------------------------------------- /example/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 5 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | } 15 | 16 | .btn { 17 | margin-top: 100px; 18 | margin-left: 100px; 19 | display: inline-block; 20 | background-color: lightblue; 21 | border: none; 22 | text-align: center; 23 | box-sizing: border-box; 24 | padding: 20px 40px; 25 | min-width: 10vw; 26 | font-size: 20px; 27 | font-family: sans-serif; 28 | color: black; 29 | } 30 | -------------------------------------------------------------------------------- /example/src/index.js: -------------------------------------------------------------------------------- 1 | import './index.css' 2 | 3 | import React from 'react' 4 | import ReactDOM from 'react-dom' 5 | import App from './App' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-use-ripple", 3 | "version": "1.5.2", 4 | "description": "A react hook to create material design ripples for components", 5 | "author": "cbadger85", 6 | "keywords": [ 7 | "react", 8 | "material design", 9 | "react-hooks", 10 | "ripple" 11 | ], 12 | "license": "MIT", 13 | "repository": "cbadger85/react-use-ripple", 14 | "main": "dist/index.js", 15 | "module": "dist/index.modern.js", 16 | "source": "src/index.ts", 17 | "engines": { 18 | "node": ">=10" 19 | }, 20 | "scripts": { 21 | "build": "microbundle-crl --no-compress --format modern,cjs", 22 | "start": "microbundle-crl watch --no-compress --format modern,cjs", 23 | "prepublish": "run-s build", 24 | "test": "run-s test:unit test:lint test:build", 25 | "test:build": "run-s build", 26 | "test:lint": "eslint .", 27 | "test:unit": "cross-env CI=1 react-scripts test --env=jsdom --coverage", 28 | "test:watch": "react-scripts test --env=jsdom" 29 | }, 30 | "peerDependencies": { 31 | "react": "^16.8.0" 32 | }, 33 | "devDependencies": { 34 | "@testing-library/react": "^10.0.2", 35 | "@types/jest": "^25.2.1", 36 | "@types/react": "^16.9.27", 37 | "babel-eslint": "^10.0.3", 38 | "cross-env": "^7.0.2", 39 | "eslint": "^6.8.0", 40 | "eslint-config-prettier": "^6.7.0", 41 | "eslint-config-standard": "^14.1.0", 42 | "eslint-config-standard-react": "^9.2.0", 43 | "eslint-plugin-import": "^2.18.2", 44 | "eslint-plugin-node": "^11.0.0", 45 | "eslint-plugin-prettier": "^3.1.1", 46 | "eslint-plugin-promise": "^4.2.1", 47 | "eslint-plugin-react": "^7.17.0", 48 | "eslint-plugin-standard": "^4.0.1", 49 | "gh-pages": "^2.2.0", 50 | "microbundle-crl": "^0.13.8", 51 | "npm-run-all": "^4.1.5", 52 | "prettier": "^1.19.1", 53 | "react": "^16.13.1", 54 | "react-dom": "^16.13.1", 55 | "react-scripts": "^3.4.1" 56 | }, 57 | "files": [ 58 | "dist" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /src/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | import { fireEvent, render, screen } from '@testing-library/react'; 2 | import React, { CSSProperties, useRef } from 'react'; 3 | import { RippleOptions, useRipple } from '..'; 4 | 5 | const TestComponent: React.FC = ({ 6 | style, 7 | disabled, 8 | animationLength, 9 | rippleColor, 10 | rippleSize, 11 | excludeRef, 12 | }) => { 13 | const ref = useRef(null); 14 | const excludedRef = useRef(null); 15 | useRipple(ref, { 16 | disabled, 17 | animationLength, 18 | rippleColor, 19 | rippleSize, 20 | excludedRefs: excludeRef ? [excludedRef] : undefined, 21 | }); 22 | 23 | return ( 24 | 32 | ); 33 | }; 34 | 35 | interface Props extends RippleOptions { 36 | style?: CSSProperties; 37 | excludeRef?: boolean; 38 | } 39 | 40 | const NullComponent = () => { 41 | const ref = useRef(null); 42 | useRipple(ref); 43 | 44 | return
null
; 45 | }; 46 | 47 | beforeAll(jest.useFakeTimers); 48 | 49 | describe('useRipple', () => { 50 | it('should not crash if the ref is null', async () => { 51 | render(); 52 | 53 | const element = screen.getByText('null'); 54 | 55 | expect(element).toBeTruthy(); 56 | }); 57 | 58 | it('should not have the ripple created or keyframes created on mount', async () => { 59 | const { container } = render(); 60 | 61 | const ripple = container.querySelector('span'); 62 | 63 | expect(ripple).toBeFalsy(); 64 | }); 65 | 66 | it('should create the ripple and keyframes on mouse down', async () => { 67 | const { container } = render(); 68 | 69 | fireEvent.mouseDown(screen.getByText('Button')); 70 | 71 | const ripple = container.querySelector('span'); 72 | 73 | expect(ripple).toBeTruthy(); 74 | }); 75 | 76 | it('should not ripple if an excluded element clicked and the ref is added to the excludedRefs list', async () => { 77 | const { container } = render(); 78 | 79 | fireEvent.mouseDown(screen.getByText('test')); 80 | 81 | const ripple = container.querySelector('span'); 82 | 83 | expect(ripple).toBeFalsy(); 84 | }); 85 | 86 | it('should not create the ripple and keyframes on mouse down if the hook is disabled', async () => { 87 | const { container } = render(); 88 | 89 | fireEvent.mouseDown(screen.getByText('Button')); 90 | 91 | const ripple = container.querySelector('span'); 92 | 93 | expect(ripple).toBeFalsy(); 94 | }); 95 | 96 | it('should show the ripple at the point of mouse down', () => { 97 | const { container } = render(); 98 | 99 | fireEvent.mouseDown(screen.getByText('Button'), { clientX: 5, clientY: 5 }); 100 | 101 | const ripple = container.querySelector('span'); 102 | 103 | expect(ripple?.style.top).toBe('5px'); 104 | expect(ripple?.style.left).toBe('5px'); 105 | }); 106 | 107 | it('should show the ripple in the middle of the element if the event was fired from an enter press', () => { 108 | const { container } = render(); 109 | 110 | fireEvent.keyDown(screen.getByText('Button'), { key: 'Enter' }); 111 | 112 | const ripple = container.querySelector('span'); 113 | 114 | expect(ripple?.style.top).toBe('0px'); 115 | expect(ripple?.style.left).toBe('0px'); 116 | }); 117 | 118 | it('should show the ripple in the middle of the element if the event was fired from an spacebar press', () => { 119 | const { container } = render(); 120 | 121 | fireEvent.keyDown(screen.getByText('Button'), { key: ' ' }); 122 | 123 | const ripple = container.querySelector('span'); 124 | 125 | expect(ripple?.style.top).toBe('0px'); 126 | expect(ripple?.style.left).toBe('0px'); 127 | }); 128 | 129 | it('should not show the ripple in the middle of the element if the event was fired from a keydown but not an enter press', () => { 130 | const { container } = render(); 131 | 132 | fireEvent.keyDown(screen.getByText('Button'), { key: 'Tab' }); 133 | 134 | const ripple = container.querySelector('span'); 135 | 136 | expect(ripple).toBeFalsy(); 137 | }); 138 | 139 | it('should remove the ripple and keyframe after the animation', async () => { 140 | const { container } = render(); 141 | 142 | fireEvent.mouseDown(screen.getByText('Button')); 143 | 144 | fireEvent.animationEnd(container.querySelector('span') as HTMLElement); 145 | 146 | const finalRipple = container.querySelector('span'); 147 | expect(finalRipple).toBeFalsy(); 148 | }); 149 | 150 | it('should create a style tag', () => { 151 | const styleTag = document.querySelector('style'); 152 | 153 | expect(styleTag?.innerHTML).toBeTruthy(); 154 | }); 155 | 156 | it(`should change the element's position to relative`, () => { 157 | const { container } = render(); 158 | 159 | const button = container.querySelector('#btn') as HTMLElement; 160 | 161 | const position = button.style.position; 162 | 163 | expect(position).toBe('relative'); 164 | }); 165 | 166 | it(`should not overwrite position style if element is already positioned`, () => { 167 | const { container } = render( 168 | , 169 | ); 170 | 171 | const button = container.querySelector('#btn') as HTMLElement; 172 | 173 | const position = button.style.position; 174 | 175 | expect(position).toBe('fixed'); 176 | }); 177 | 178 | it('should change the ripple color if specified', () => { 179 | const { container } = render(); 180 | 181 | fireEvent.mouseDown(screen.getByText('Button'), { clientX: 5, clientY: 5 }); 182 | 183 | const ripple = container.querySelector('span'); 184 | 185 | expect(ripple?.style.backgroundColor).toBe('blue'); 186 | }); 187 | 188 | it('should change the ripple animation length if specified', () => { 189 | const { container } = render(); 190 | 191 | fireEvent.mouseDown(screen.getByText('Button'), { clientX: 5, clientY: 5 }); 192 | 193 | const ripple = container.querySelector('span'); 194 | 195 | expect(ripple?.style.animation).toBe('use-ripple-animation 3000ms ease-in'); 196 | }); 197 | }); 198 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { RefObject, useEffect } from 'react'; 2 | 3 | const ANIMATION_LENGTH = 700; 4 | const RIPPLE_SIZE = 100; 5 | const RIPPLE_COLOR = 'rgba(0, 0, 0, 0.3)'; 6 | 7 | if (typeof document !== 'undefined') { 8 | const style = document.createElement('style'); 9 | 10 | const keyframes = ` 11 | @keyframes use-ripple-animation { 12 | from { 13 | opacity: 1; 14 | transform: scale(0); 15 | } 16 | to { 17 | opacity: 0; 18 | transform: scale(10); 19 | } 20 | } 21 | `; 22 | 23 | style.innerHTML = keyframes; 24 | 25 | document.querySelector('head')?.appendChild(style); 26 | } 27 | 28 | export interface RippleOptions { 29 | disabled?: boolean; 30 | rippleColor?: string; 31 | animationLength?: number; 32 | rippleSize?: number; 33 | excludedRefs?: RefObject[]; 34 | } 35 | 36 | interface RippleEvent { 37 | clientX?: number; 38 | clientY?: number; 39 | target: EventTarget | null; 40 | } 41 | 42 | const defaultEvent: Required = { 43 | clientX: 0, 44 | clientY: 0, 45 | target: null, 46 | }; 47 | 48 | const createRipple = (element: HTMLElement, options?: RippleOptions) => ( 49 | e?: RippleEvent, 50 | ) => { 51 | const isExcluded = (options?.excludedRefs || []).some( 52 | ref => 53 | (!!ref.current && ref.current.contains(e?.target as Node)) || 54 | ref.current?.isSameNode(e?.target as Node), 55 | ); 56 | 57 | if (isExcluded) { 58 | return; 59 | } 60 | 61 | const clientX = e?.clientX || defaultEvent.clientX; 62 | const clientY = e?.clientY || defaultEvent.clientY; 63 | 64 | const { height, width, top, left } = element.getBoundingClientRect(); 65 | const x = clientX - left; 66 | const y = clientY - top; 67 | 68 | const rippleSize = Math.min( 69 | height, 70 | width, 71 | options?.rippleSize || RIPPLE_SIZE, 72 | ); 73 | 74 | const positionTop = clientX 75 | ? y - rippleSize / 2 76 | : rippleSize / 2 - height / 2; 77 | const positionLeft = clientY 78 | ? x - rippleSize / 2 79 | : width / 2 - rippleSize / 2; 80 | 81 | const span = document.createElement('span'); 82 | 83 | span.style.cssText = ` 84 | top: ${positionTop}px; 85 | left: ${positionLeft}px; 86 | position: absolute; 87 | border-radius: 50%; 88 | background-color: ${options?.rippleColor || RIPPLE_COLOR}; 89 | pointer-events: none; 90 | width: ${rippleSize}px; 91 | height: ${rippleSize}px; 92 | animation: use-ripple-animation ${options?.animationLength || 93 | ANIMATION_LENGTH}ms ease-in; 94 | `; 95 | 96 | element.appendChild(span); 97 | 98 | span.addEventListener('animationend', () => { 99 | element.removeChild(span); 100 | }); 101 | }; 102 | 103 | export const useRipple = ( 104 | ref: RefObject, 105 | options?: RippleOptions, 106 | ) => { 107 | useEffect(() => { 108 | if (options?.disabled || !ref?.current) { 109 | return; 110 | } 111 | 112 | const element = ref.current; 113 | const elementPosition = getComputedStyle(element).getPropertyValue( 114 | 'position', 115 | ); 116 | 117 | element.style.position = 118 | elementPosition === 'static' || !elementPosition 119 | ? 'relative' 120 | : elementPosition; 121 | element.style.overflow = 'hidden'; 122 | 123 | const ripple = createRipple(element, options); 124 | 125 | const keyboardRipple = (e: KeyboardEvent) => { 126 | if (e.key === 'Enter' || e.key === ' ') { 127 | ripple(); 128 | } 129 | }; 130 | 131 | element.addEventListener('mousedown', ripple); 132 | element.addEventListener('keydown', keyboardRipple); 133 | 134 | return () => { 135 | element.removeEventListener('mousedown', ripple); 136 | element.removeEventListener('keydown', keyboardRipple); 137 | }; 138 | }); 139 | }; 140 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "module": "esnext", 5 | "lib": [ 6 | "dom", 7 | "esnext" 8 | ], 9 | "moduleResolution": "node", 10 | "jsx": "react", 11 | "sourceMap": true, 12 | "declaration": true, 13 | "esModuleInterop": true, 14 | "noImplicitReturns": true, 15 | "noImplicitThis": true, 16 | "noImplicitAny": true, 17 | "strictNullChecks": true, 18 | "suppressImplicitAnyIndexErrors": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "allowSyntheticDefaultImports": true, 22 | "target": "es5", 23 | "allowJs": true, 24 | "skipLibCheck": true, 25 | "strict": true, 26 | "forceConsistentCasingInFileNames": true, 27 | "resolveJsonModule": true, 28 | "isolatedModules": true, 29 | "noEmit": true 30 | }, 31 | "include": [ 32 | "src" 33 | ], 34 | "exclude": [ 35 | "node_modules", 36 | "dist", 37 | "example" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } --------------------------------------------------------------------------------