├── packages ├── resize-observer │ ├── .tool-versions │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── src │ │ └── index.test.tsx │ ├── tsconfig.json │ ├── types │ │ └── index.d.ts │ └── LICENSE ├── copy │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── types │ │ └── index.d.ts │ ├── tsconfig.json │ ├── src │ │ ├── index.test.tsx │ │ └── index.tsx │ ├── LICENSE │ └── README.md ├── size │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── src │ │ ├── index.test.tsx │ │ └── index.tsx │ ├── tsconfig.json │ ├── types │ │ └── index.d.ts │ ├── LICENSE │ └── README.md ├── async │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── LICENSE │ └── types │ │ └── index.d.ts ├── cache │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── types │ │ └── lru.d.ts │ └── LICENSE ├── change │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── types │ │ └── index.d.ts │ ├── tsconfig.json │ ├── src │ │ ├── index.tsx │ │ └── index.test.tsx │ ├── LICENSE │ └── README.md ├── click │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── types │ │ └── index.d.ts │ ├── LICENSE │ └── src │ │ ├── index.tsx │ │ └── index.test.tsx ├── counter │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── types │ │ └── index.d.ts │ ├── LICENSE │ └── src │ │ └── index.tsx ├── debounce │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── types │ │ └── index.d.ts │ ├── LICENSE │ └── src │ │ ├── index.tsx │ │ └── index.test.tsx ├── event │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── LICENSE │ ├── types │ │ └── index.d.ts │ └── src │ │ ├── index.tsx │ │ └── index.test.tsx ├── hotkey │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── LICENSE │ └── types │ │ └── index.d.ts ├── hover │ ├── .gitignore │ ├── babel.config.js │ ├── tsconfig.json │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── types │ │ └── index.d.ts │ ├── LICENSE │ ├── src │ │ ├── index.tsx │ │ └── index.test.tsx │ └── README.md ├── latest │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── types │ │ └── index.d.ts │ ├── src │ │ ├── index.tsx │ │ └── index.test.tsx │ ├── tsconfig.json │ ├── LICENSE │ └── README.md ├── previous │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── types │ │ └── index.d.ts │ ├── tsconfig.json │ ├── src │ │ ├── index.tsx │ │ └── index.test.tsx │ ├── LICENSE │ └── README.md ├── switch │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── types │ │ └── index.d.ts │ ├── LICENSE │ └── src │ │ ├── index.tsx │ │ └── index.test.tsx ├── throttle │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── types │ │ └── index.d.ts │ ├── LICENSE │ └── src │ │ └── index.tsx ├── timeout │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── LICENSE │ ├── types │ │ └── index.d.ts │ └── src │ │ ├── index.test.tsx │ │ └── index.tsx ├── toggle │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── types │ │ └── index.d.ts │ ├── tsconfig.json │ ├── src │ │ ├── index.test.tsx │ │ └── index.tsx │ ├── LICENSE │ └── README.md ├── media-query │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── LICENSE │ └── types │ │ └── index.d.ts ├── merged-ref │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── src │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.tsx │ │ └── index.test.tsx │ ├── types │ │ └── index.d.ts │ ├── tsconfig.json │ ├── LICENSE │ └── README.md ├── mouse-position │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── src │ │ └── index.test.tsx │ ├── tsconfig.json │ ├── types │ │ └── index.d.ts │ └── LICENSE ├── window-scroll │ ├── .gitignore │ ├── babel.config.js │ ├── types │ │ └── index.d.ts │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── src │ │ ├── index.tsx │ │ └── index.test.tsx │ ├── LICENSE │ └── README.md ├── window-size │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ ├── resolve-snapshot.js │ │ └── test-utils.js │ ├── tsconfig.json │ ├── types │ │ └── index.d.ts │ ├── throttled │ │ ├── types │ │ │ └── index.d.ts │ │ ├── package.json │ │ └── src │ │ │ └── index.tsx │ ├── LICENSE │ └── src │ │ └── index.tsx ├── google-optimize │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── types │ │ └── index.d.ts │ ├── tsconfig.json │ ├── src │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.tsx │ │ └── index.test.tsx │ └── LICENSE ├── server-promises │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── src │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.test.tsx │ │ └── index.tsx │ ├── types │ │ └── index.d.ts │ └── LICENSE ├── intersection-observer │ ├── .gitignore │ ├── babel.config.js │ ├── test │ │ ├── setup.js │ │ └── resolve-snapshot.js │ ├── tsconfig.json │ ├── LICENSE │ ├── types │ │ └── index.d.ts │ └── src │ │ └── index.test.tsx └── passive-layout-effect │ ├── .gitignore │ ├── babel.config.js │ ├── test │ ├── setup.js │ └── resolve-snapshot.js │ ├── types │ └── index.d.ts │ ├── src │ ├── index.node.test.ts │ ├── index.dom.test.ts │ └── index.tsx │ ├── tsconfig.json │ ├── LICENSE │ └── README.md ├── .gitignore ├── .gitattributes ├── .travis └── build-condition.sh ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── pull_request_template.md ├── LICENSE ├── CONTRIBUTING.md ├── .travis.yml └── CODE_OF_CONDUCT.md /packages/resize-observer/.tool-versions: -------------------------------------------------------------------------------- 1 | nodejs 18.17.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | packages/useScroll.js 4 | *.bak -------------------------------------------------------------------------------- /packages/copy/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/size/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/async/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/cache/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/change/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/click/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/counter/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/debounce/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/event/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/hotkey/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/hover/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/latest/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/previous/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/switch/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/throttle/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/timeout/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/toggle/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/media-query/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/merged-ref/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/mouse-position/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/window-scroll/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/window-size/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/google-optimize/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/resize-observer/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/server-promises/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /packages/intersection-observer/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/passive-layout-effect/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | *.log -------------------------------------------------------------------------------- /packages/copy/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/size/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/async/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/cache/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/change/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/click/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/counter/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/debounce/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/event/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/hotkey/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/hover/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/latest/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/previous/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/switch/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/throttle/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/timeout/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/toggle/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/media-query/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/merged-ref/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/mouse-position/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/window-scroll/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/window-size/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/google-optimize/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/resize-observer/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/server-promises/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/intersection-observer/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/passive-layout-effect/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('lundle').babelConfig('test', {react: true}) 2 | -------------------------------------------------------------------------------- /packages/async/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/cache/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/change/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/click/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/copy/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/hotkey/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/latest/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/size/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/switch/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/toggle/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/window-scroll/types/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare const useWindowScroll: (fps?: number) => number 2 | export default useWindowScroll 3 | -------------------------------------------------------------------------------- /packages/counter/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/debounce/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/media-query/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/merged-ref/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/previous/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/throttle/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/timeout/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/window-size/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/google-optimize/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/mouse-position/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/resize-observer/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/server-promises/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/window-scroll/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/intersection-observer/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/passive-layout-effect/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | -------------------------------------------------------------------------------- /packages/merged-ref/src/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`merges object and function refs 1`] = `
`; 4 | -------------------------------------------------------------------------------- /packages/event/test/setup.js: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom' 2 | // This file is for setting up Jest test environments 3 | afterEach(() => { 4 | jest.clearAllMocks() 5 | }) 6 | -------------------------------------------------------------------------------- /packages/change/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const useChange: ( 2 | value: T, 3 | onChange: (current: T, prev: T) => any 4 | ) => void 5 | export default useChange 6 | -------------------------------------------------------------------------------- /packages/merged-ref/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | declare function useMergedRef(...refs: React.Ref[]): React.RefCallback 3 | export default useMergedRef 4 | -------------------------------------------------------------------------------- /packages/latest/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | declare const useLatest: ( 3 | current: T 4 | ) => React.MutableRefObject 5 | export default useLatest 6 | -------------------------------------------------------------------------------- /packages/previous/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare function usePrevious(value: T, initialValue: T): T 2 | declare function usePrevious(value: T): T | undefined 3 | export default usePrevious 4 | -------------------------------------------------------------------------------- /packages/copy/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare function useCopy( 2 | text: string 3 | ): { 4 | readonly copied: boolean 5 | readonly copy: () => Promise 6 | readonly reset: () => void 7 | } 8 | export default useCopy 9 | -------------------------------------------------------------------------------- /packages/toggle/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare function useToggle( 2 | off?: Off, 3 | on?: On, 4 | defaultValue?: Off | On 5 | ): readonly [Off | On, () => void] 6 | export default useToggle 7 | -------------------------------------------------------------------------------- /packages/passive-layout-effect/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | declare const usePassiveLayoutEffect: 3 | | typeof React.useLayoutEffect 4 | | typeof React.useEffect 5 | export default usePassiveLayoutEffect 6 | -------------------------------------------------------------------------------- /packages/mouse-position/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | // import {fireEvent} from '@testing-library/dom' 2 | // import {renderHook, act} from '@testing-library/react-hooks' 3 | // import useMouse from './index' 4 | 5 | it('passes', () => { 6 | expect(true).toBe(true) 7 | }) 8 | -------------------------------------------------------------------------------- /packages/passive-layout-effect/src/index.node.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment node 3 | */ 4 | import * as React from 'react' 5 | import usePassiveLayoutEffect from './index' 6 | 7 | it('is useEffect', () => { 8 | expect(usePassiveLayoutEffect).toBe(React.useEffect) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/google-optimize/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare function useGoogleOptimize( 2 | experimentId: string, 3 | variants: T[], 4 | timeout?: number 5 | ): T | null 6 | declare global { 7 | interface Window { 8 | dataLayer: any[] 9 | } 10 | } 11 | export default useGoogleOptimize 12 | -------------------------------------------------------------------------------- /packages/passive-layout-effect/src/index.dom.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | import * as React from 'react' 5 | import usePassiveLayoutEffect from './index' 6 | 7 | it('is useLayoutEffect', () => { 8 | expect(usePassiveLayoutEffect).toBe(React.useLayoutEffect) 9 | }) 10 | -------------------------------------------------------------------------------- /.travis/build-condition.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ -z $1 ]]; then 4 | echo "Commit range cannot be empty" 5 | exit 1 6 | fi 7 | 8 | if [[ -z $2 ]]; then 9 | echo "Change path cannot be empty" 10 | exit 1 11 | fi 12 | 13 | git diff --name-only $1 | sort -u | uniq | grep $2 > /dev/null -------------------------------------------------------------------------------- /packages/latest/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | const useLatest = (current: T) => { 4 | const storedValue = React.useRef(current) 5 | React.useEffect(() => { 6 | storedValue.current = current 7 | }) 8 | return storedValue 9 | } 10 | 11 | export default useLatest 12 | -------------------------------------------------------------------------------- /packages/passive-layout-effect/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const usePassiveLayoutEffect = 4 | React[ 5 | typeof document !== 'undefined' && document.createElement !== void 0 6 | ? 'useLayoutEffect' 7 | : 'useEffect' 8 | ] 9 | 10 | export default usePassiveLayoutEffect 11 | -------------------------------------------------------------------------------- /packages/size/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react' 2 | // import {renderHook} from '@testing-library/react-hooks' 3 | // import {render} from '@testing-library/react' 4 | // import userEvent from '@testing-library/user-event 5 | describe('size', () => { 6 | it('should pass', () => { 7 | expect(true).toBe(true) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/resize-observer/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react' 2 | // import {renderHook} from '@testing-library/react-hooks' 3 | // import {render} from '@testing-library/react' 4 | // import userEvent from '@testing-library/user-event 5 | describe('useResizeObserver()', () => { 6 | it('should pass', () => { 7 | expect(true).toBe(true) 8 | }) 9 | }) 10 | -------------------------------------------------------------------------------- /packages/async/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/cache/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/change/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/click/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/copy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/event/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/hotkey/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/hover/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/latest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/size/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/switch/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/toggle/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/counter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/debounce/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/media-query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/previous/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/throttle/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/timeout/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/google-optimize/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/mouse-position/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/window-scroll/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/passive-layout-effect/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/merged-ref/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "**/__snapshots__/**"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/server-promises/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "**/__snapshots__/**"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/window-size/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "throttled/src"], 3 | "exclude": ["src/**/*.test.ts", "throttled/src/**/*.test.ts"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/resize-observer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "downlevelIteration": true, 12 | "strict": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/intersection-observer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"], 4 | "compilerOptions": { 5 | "target": "es5", 6 | "lib": ["esnext", "dom", "dom.iterable"], 7 | "jsx": "react", 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "downlevelIteration": true, 12 | "strict": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/throttle/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | export declare function useThrottleCallback( 3 | callback: (...args: CallbackArguments) => void, 4 | fps?: number, 5 | leading?: boolean 6 | ): (...args: CallbackArguments) => void 7 | export declare function useThrottle( 8 | initialState: State | (() => State), 9 | fps?: number, 10 | leading?: boolean 11 | ): [State, React.Dispatch>] 12 | -------------------------------------------------------------------------------- /packages/hover/test/setup.js: -------------------------------------------------------------------------------- 1 | // This file is for setting up Jest test environments 2 | afterEach(() => { 3 | jest.clearAllMocks() 4 | }) 5 | 6 | window.matchMedia = jest.fn().mockImplementation((query) => { 7 | return { 8 | matches: false, 9 | media: query, 10 | onchange: null, 11 | addListener: jest.fn(), 12 | removeListener: jest.fn(), 13 | addEventListener: jest.fn(), 14 | removeEventListener: jest.fn(), 15 | dispatchEvent: jest.fn(), 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /packages/previous/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | function usePrevious(value: T, initialValue: T): T 4 | function usePrevious(value: T): T | undefined 5 | function usePrevious(value: T, initialValue?: T): T | undefined { 6 | const storedValue = React.useRef(initialValue) 7 | React.useEffect(() => { 8 | storedValue.current = value 9 | }, [value]) 10 | return storedValue.current 11 | } 12 | 13 | export default usePrevious 14 | -------------------------------------------------------------------------------- /packages/server-promises/src/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`useServerPromises() should render to string w/ resolved promises: 4 |
5 | Hello
world
6 |
7 | 1`] = `"
Hello
world
"`; 8 | 9 | exports[`useServerPromises() should use custom renderer: 10 |
11 | Hello
world
12 |
13 | 1`] = `"
Hello
world
"`; 14 | -------------------------------------------------------------------------------- /packages/counter/types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const useCounter: ( 2 | initialValue?: number, 3 | options?: UseCounterOptions 4 | ) => { 5 | value: number 6 | set: (value: number) => void 7 | incr: (by?: any) => void 8 | decr: (by?: any) => void 9 | } 10 | export interface UseCounterOptions { 11 | min?: number 12 | max?: number 13 | cast?: (value: number) => number 14 | step?: number 15 | onMin?: (set: (value: number) => void) => void 16 | onMax?: (set: (value: number) => void) => void 17 | } 18 | export default useCounter 19 | -------------------------------------------------------------------------------- /packages/change/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import usePrevious from '@react-hook/previous' 3 | import useLatest from '@react-hook/latest' 4 | 5 | const useChange = ( 6 | value: T, 7 | onChange: (current: T, prev: T) => any 8 | ) => { 9 | const storedOnChange = useLatest(onChange) 10 | const prevValue = usePrevious(value, value) 11 | React.useEffect(() => { 12 | if (value !== prevValue) storedOnChange.current(value, prevValue) 13 | }, [value, prevValue, storedOnChange]) 14 | } 15 | 16 | export default useChange 17 | -------------------------------------------------------------------------------- /packages/window-size/types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface DebouncedWindowSizeOptions { 2 | initialWidth?: number 3 | initialHeight?: number 4 | wait?: number 5 | leading?: boolean 6 | } 7 | export declare const useWindowSize: ( 8 | options?: DebouncedWindowSizeOptions 9 | ) => readonly [number, number] 10 | export declare const useWindowHeight: ( 11 | options?: Omit 12 | ) => number 13 | export declare const useWindowWidth: ( 14 | options?: Omit 15 | ) => number 16 | -------------------------------------------------------------------------------- /packages/window-size/throttled/types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface ThrottledWindowSizeOptions { 2 | initialWidth?: number 3 | initialHeight?: number 4 | fps?: number 5 | leading?: boolean 6 | } 7 | export declare const useWindowSize: ( 8 | options?: ThrottledWindowSizeOptions 9 | ) => readonly [number, number] 10 | export declare const useWindowHeight: ( 11 | options?: Omit 12 | ) => number 13 | export declare const useWindowWidth: ( 14 | options?: Omit 15 | ) => number 16 | -------------------------------------------------------------------------------- /packages/hover/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | declare function useHover( 3 | target: React.RefObject | T | null, 4 | options?: UseHoverOptions 5 | ): boolean 6 | export interface UseHoverOptions { 7 | enterDelay?: number 8 | leaveDelay?: number 9 | } 10 | export interface UseHoverState { 11 | status: 'idle' | 'hovering' 12 | timeout: number | undefined 13 | } 14 | export declare type UseHoverAction = { 15 | type: 'setStatus' 16 | value: 'idle' | 'hovering' 17 | force?: boolean 18 | } 19 | export default useHover 20 | -------------------------------------------------------------------------------- /packages/debounce/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | export declare const useDebounceCallback: ( 3 | callback: (...args: CallbackArgs) => void, 4 | wait?: number, 5 | leading?: boolean 6 | ) => (...args: CallbackArgs) => void 7 | export declare const useDebounce: ( 8 | initialState: State | (() => State), 9 | wait?: number | undefined, 10 | leading?: boolean | undefined 11 | ) => [ 12 | State, 13 | React.Dispatch>, 14 | React.Dispatch> 15 | ] 16 | -------------------------------------------------------------------------------- /packages/merged-ref/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | function useMergedRef(...refs: React.Ref[]): React.RefCallback { 4 | return React.useCallback( 5 | (element: T) => { 6 | for (let i = 0; i < refs.length; i++) { 7 | const ref = refs[i] 8 | if (typeof ref === 'function') ref(element) 9 | else if (ref && typeof ref === 'object') 10 | (ref as React.MutableRefObject).current = element 11 | } 12 | }, 13 | // eslint-disable-next-line react-hooks/exhaustive-deps 14 | refs 15 | ) 16 | } 17 | 18 | export default useMergedRef 19 | -------------------------------------------------------------------------------- /packages/size/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | /** 3 | * A React hook for measuring the size of HTML elements including when they change 4 | * 5 | * @param target A React ref created by `useRef()` or an HTML element 6 | * @param options Configures the initial width and initial height of the hook's state 7 | */ 8 | declare const useSize: ( 9 | target: T | React.RefObject | null, 10 | options?: UseSizeOptions | undefined 11 | ) => [number, number] 12 | export interface UseSizeOptions { 13 | initialWidth: number 14 | initialHeight: number 15 | } 16 | export default useSize 17 | -------------------------------------------------------------------------------- /packages/async/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/cache/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/click/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/click/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | declare function useClick( 3 | conditions: string | string[], 4 | callback: (...args: any[]) => any 5 | ): (e: React.MouseEvent) => void 6 | export declare const CLICK_TYPES: { 7 | readonly single: 'detail=1' 8 | readonly double: 'detail=2' 9 | readonly triple: 'detail=3' 10 | readonly left: 'button=0' 11 | readonly middle: 'button=1' 12 | readonly right: 'button=2' 13 | readonly shift: 'shiftKey' 14 | readonly control: 'ctrlKey' 15 | readonly meta: 'metaKey' 16 | readonly alt: 'altKey' 17 | } 18 | export default useClick 19 | -------------------------------------------------------------------------------- /packages/copy/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/event/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/hover/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/size/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/change/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/counter/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/debounce/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/hotkey/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/latest/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/merged-ref/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/previous/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/switch/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/throttle/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/timeout/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/toggle/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/google-optimize/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/media-query/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/mouse-position/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/resize-observer/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/server-promises/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/window-scroll/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/window-size/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/intersection-observer/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/passive-layout-effect/test/resolve-snapshot.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const snapshots = '__snapshots__' 3 | 4 | module.exports = { 5 | resolveSnapshotPath: (testPath, snapshotExtension) => 6 | path.join( 7 | testPath.split('/').slice(0, -1).join('/'), 8 | snapshots, 9 | testPath.split('/').pop() + snapshotExtension 10 | ), 11 | resolveTestPath: (snapshotFilePath, snapshotExtension) => 12 | path.join( 13 | snapshotFilePath.split('/').slice(0, -2).join('/'), 14 | snapshotFilePath.split('/').pop().slice(0, -snapshotExtension.length) 15 | ), 16 | testPathForConsistencyCheck: 'src/foo.test.js', 17 | } 18 | -------------------------------------------------------------------------------- /packages/copy/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import {renderHook, act} from '@testing-library/react-hooks' 2 | import useCopy from './index' 3 | 4 | describe('useCopy()', () => { 5 | const writeText = jest.fn(async (text) => text) 6 | // @ts-ignore 7 | window.navigator.clipboard = { 8 | writeText, 9 | } 10 | it('should work', async () => { 11 | const {result} = renderHook(() => useCopy('copy me')) 12 | expect(result.current.copied).toBe(false) 13 | await act(() => result.current.copy()) 14 | expect(writeText).toBeCalled() 15 | expect(result.current.copied).toBe(true) 16 | act(() => result.current.reset()) 17 | expect(result.current.copied).toBe(false) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /packages/window-scroll/src/index.tsx: -------------------------------------------------------------------------------- 1 | import {useThrottle} from '@react-hook/throttle' 2 | import useEvent from '@react-hook/event' 3 | 4 | const win = typeof window === 'undefined' ? null : window 5 | const getScrollY = (): number => 6 | (win as Window).scrollY !== void 0 7 | ? (win as Window).scrollY 8 | : (win as Window).pageYOffset === void 0 9 | ? 0 10 | : (win as Window).pageYOffset 11 | 12 | export const useWindowScroll = (fps = 30): number => { 13 | const state = useThrottle( 14 | typeof window === 'undefined' ? 0 : getScrollY, 15 | fps, 16 | true 17 | ) 18 | useEvent(win, 'scroll', (): void => state[1](getScrollY())) 19 | return state[0] 20 | } 21 | 22 | export default useWindowScroll 23 | -------------------------------------------------------------------------------- /packages/mouse-position/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | declare function useMouse( 3 | target: React.RefObject | T | null, 4 | options?: UseMouseOptions 5 | ): MousePosition 6 | export interface UseMouseOptions { 7 | enterDelay?: number 8 | leaveDelay?: number 9 | fps?: number 10 | } 11 | export interface MousePosition { 12 | x: number | null 13 | y: number | null 14 | pageX: number | null 15 | pageY: number | null 16 | clientX: number | null 17 | clientY: number | null 18 | screenX: number | null 19 | screenY: number | null 20 | elementWidth: number | null 21 | elementHeight: number | null 22 | isOver: boolean 23 | isDown: boolean 24 | isTouch: boolean 25 | } 26 | export default useMouse 27 | -------------------------------------------------------------------------------- /packages/switch/types/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A hook for creating controlled toggles with on, off, and toggle callbacks. 3 | * This is extremely useful for creating controlled inputs for components like Checkbox. 4 | * 5 | * @param defaultValue Sets the default value of the switch 6 | * @param controlledValue Sets the controlled value of the switch, which will override 7 | * the defaultValue 8 | * @param onChange A callback invoked whenever toggle callbacks that change state are invoked 9 | */ 10 | declare function useSwitch( 11 | defaultValue?: boolean, 12 | controlledValue?: boolean, 13 | onChange?: (value: boolean) => any 14 | ): readonly [ 15 | boolean, 16 | (() => void) & { 17 | on: () => void 18 | off: () => void 19 | } 20 | ] 21 | export default useSwitch 22 | -------------------------------------------------------------------------------- /packages/cache/types/lru.d.ts: -------------------------------------------------------------------------------- 1 | export declare const lru: ( 2 | maxSize: number 3 | ) => LRUCache 4 | export declare type LRUCache = { 5 | head: LRUNode | undefined 6 | size: number 7 | forEach(fn: (key: Key, value: Value) => void): void 8 | search(key: Key): LRUNode | undefined 9 | read(key: Key): Value | undefined 10 | write(key: Key, value: Value): Value 11 | delete(key: Key): Value | undefined 12 | pop(): Value | undefined 13 | _insertHead(node: LRUNode): void 14 | _deleteNode(node: LRUNode): void 15 | } 16 | declare type LRUNode = { 17 | next: LRUNode 18 | prev: LRUNode 19 | key: Key 20 | value: Value 21 | } 22 | export {} 23 | -------------------------------------------------------------------------------- /packages/window-size/throttled/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-hook/window-size-throttled", 3 | "version": "2.2.0", 4 | "homepage": "https://github.com/jaredLunde/react-hook/tree/master/window-size/throttled#readme", 5 | "repository": "github:jaredLunde/react-hook", 6 | "bugs": "https://github.com/jaredLunde/react-hook/issues", 7 | "license": "MIT", 8 | "main": "dist/main/index.js", 9 | "module": "dist/module/index.js", 10 | "unpkg": "dist/umd/use-window-size.js", 11 | "source": "src/index.tsx", 12 | "types": "types/index.d.ts", 13 | "files": [ 14 | "/dist", 15 | "/src", 16 | "/types" 17 | ], 18 | "sideEffects": false, 19 | "dependencies": { 20 | "@react-hook/debounce": "^3.0.0", 21 | "@react-hook/event": "^1.2.1", 22 | "@react-hook/throttle": "^2.2.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/resize-observer/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | /** 3 | * A React hook that fires a callback whenever ResizeObserver detects a change to its size 4 | * 5 | * @param target A React ref created by `useRef()` or an HTML element 6 | * @param callback Invoked with a single `ResizeObserverEntry` any time 7 | * the `target` resizes 8 | */ 9 | declare function useResizeObserver( 10 | target: React.RefObject | React.ForwardedRef | T | null, 11 | callback: UseResizeObserverCallback, 12 | options?: UseResizeObserverOptions 13 | ): ResizeObserver 14 | export type UseResizeObserverCallback = ( 15 | entry: ResizeObserverEntry, 16 | observer: ResizeObserver 17 | ) => any 18 | export type UseResizeObserverOptions = { 19 | polyfill?: any 20 | } 21 | export default useResizeObserver 22 | -------------------------------------------------------------------------------- /packages/toggle/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import {renderHook, act} from '@testing-library/react-hooks' 2 | import useToggle from './index' 3 | 4 | describe('useToggle()', () => { 5 | it('should have expected defaults', () => { 6 | const {result} = renderHook(() => useToggle()) 7 | expect(result.current[0]).toBe(false) 8 | }) 9 | 10 | it('should use custom off/on values', () => { 11 | const {result} = renderHook(() => useToggle('off', 'on')) 12 | expect(result.current[0]).toBe('off') 13 | act(() => result.current[1]()) 14 | expect(result.current[0]).toBe('on') 15 | }) 16 | 17 | it('should default to user-defined default', () => { 18 | const {result} = renderHook(() => useToggle('off', 'on', 'on')) 19 | expect(result.current[0]).toBe('on') 20 | act(() => result.current[1]()) 21 | expect(result.current[0]).toBe('off') 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /packages/toggle/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | function useToggle( 4 | // @ts-ignore 5 | off: Off = false, 6 | // @ts-ignore 7 | on: On = true, 8 | defaultValue: Off | On = off 9 | ) { 10 | /* istanbul ignore next */ 11 | if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') { 12 | if (defaultValue !== on && defaultValue !== off) { 13 | throw new Error( 14 | `'defaultValue' must be one of either the 'off' or 'on' value. ` + 15 | `'${defaultValue}' was not equal to '${off}' or '${on}'.` 16 | ) 17 | } 18 | } 19 | 20 | const [value, setValue] = React.useState(defaultValue) 21 | 22 | return [ 23 | value, 24 | React.useCallback( 25 | () => setValue((current) => (current === off ? on : off)), 26 | [on, off] 27 | ), 28 | ] as const 29 | } 30 | 31 | export default useToggle 32 | -------------------------------------------------------------------------------- /packages/server-promises/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | export declare const ServerPromisesContext: React.Context<{ 3 | promises: Promise[] 4 | push: (...args: Promise[]) => number 5 | load(): Promise 6 | }> 7 | export declare const useServerPromises: () => { 8 | promises: Promise[] 9 | push: (...args: Promise[]) => number 10 | load(): Promise 11 | } 12 | export declare function loadPromises( 13 | tree: React.ReactElement, 14 | render?: (element: React.ReactElement) => T 15 | ): Promise 16 | export interface ServerPromisesContextType { 17 | /** 18 | * An array of promises that are still pending 19 | */ 20 | promises: Promise[] 21 | /** 22 | * Adds a promise to the promises array 23 | */ 24 | push: (...args: Promise[]) => number 25 | /** 26 | * Loads all of the promises currently in the promises array 27 | */ 28 | load: () => Promise 29 | } 30 | -------------------------------------------------------------------------------- /packages/merged-ref/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | /* jest */ 2 | import * as React from 'react' 3 | import {render as renderComponent} from '@testing-library/react' 4 | import useMergedRef from './index' 5 | 6 | const render = (children: any): any => renderComponent(children) 7 | 8 | it('merges object and function refs', () => { 9 | const refValues: any = {} 10 | const RefComponent: React.FC = () => { 11 | const refA = React.useRef(null) 12 | const refB_ = React.useRef(null) 13 | const refB = (el: HTMLDivElement): void => { 14 | refB_.current = el 15 | } 16 | const ref = useMergedRef(refA, refB) 17 | React.useEffect(() => { 18 | refValues.a = refA.current 19 | refValues.b = refB_.current 20 | }) 21 | 22 | return React.createElement('div', {ref}) 23 | } 24 | render(React.createElement(RefComponent)) 25 | expect(refValues.a).toBe(refValues.b) 26 | expect(refValues.b).toMatchSnapshot() 27 | }) 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /packages/google-optimize/src/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`useOptimize should pick "3":
3
1`] = ` 4 | 5 |
6 | 3 7 |
8 |
9 | `; 10 | 11 | exports[`useOptimize should throw without dataLayer or gtag 1`] = ` 12 | Array [ 13 | "[@react-hook/google-optimize] Google Tag Manager was not found on your site. Neither \\"gtag()\\" or \\"dataLayer[]\\" could be located on the \\"window\\". If you are not using Google Tag Manager in dev you can ignore this warning. Otherwise, see the Google Tag Manager dev guide for examples: https://developers.google.com/tag-manager/devguide", 14 | ] 15 | `; 16 | 17 | exports[`useOptimize should timeout and default to "1":
1
1`] = ` 18 | 19 |
20 | 1 21 |
22 |
23 | `; 24 | 25 | exports[`useOptimize should timeout and default to "1":
null
1`] = ` 26 | 27 |
28 | null 29 |
30 |
31 | `; 32 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | > IMPORTANT: Please do not create a Pull Request without creating an issue first. 2 | > **Any change needs to be discussed before proceeding.** Failure to do so may result 3 | > in the rejection of the pull request. 4 | 5 | ## Thank you for contributing to react-hook 6 | 7 | ### Before submitting 8 | 9 | Read the [CONTRIBUTING.md](https://github.com/jaredLunde/react-hook/blob/master/CONTRIBUTING.md) file and confirm that you're following 10 | all of the guidelines 11 | 12 | ### Please provide enough information so that others can review your pull request 13 | 14 | - What does this implement/fix? Explain your changes. 15 | - Does this close any currently open issues? 16 | - Include the package name or names you're updating in your PR title, e.g. `[async] Fixes #129`, `[debounce, throttle] Updates build scripts` 17 | 18 | ### Testing 19 | 20 | Confirm that your pull request contains tests and that all existing tests pass. 21 | 22 | ### Closing issues 23 | 24 | Put closes #XXXX in your comment to auto-close the issue that your PR fixes (if such). 25 | -------------------------------------------------------------------------------- /packages/window-size/test/test-utils.js: -------------------------------------------------------------------------------- 1 | // Simulate window resize event 2 | const resizeEvent = document.createEvent('Event') 3 | resizeEvent.initEvent('resize', true, true) 4 | const orientationEvent = document.createEvent('Event') 5 | orientationEvent.initEvent('orientationchange', true, true) 6 | 7 | export const resizeTo = (width, height) => { 8 | Object.defineProperty(document.documentElement, 'clientWidth', { 9 | value: width, 10 | configurable: true, 11 | }) 12 | Object.defineProperty(document.documentElement, 'clientHeight', { 13 | value: height, 14 | configurable: true, 15 | }) 16 | window.dispatchEvent(resizeEvent) 17 | } 18 | 19 | export const changeOrientation = (width, height) => { 20 | Object.defineProperty(document.documentElement, 'clientWidth', { 21 | value: width, 22 | configurable: true, 23 | }) 24 | Object.defineProperty(document.documentElement, 'clientHeight', { 25 | value: height, 26 | configurable: true, 27 | }) 28 | window.dispatchEvent(orientationEvent) 29 | } 30 | 31 | export const resetSize = () => { 32 | resizeTo(0, 0) 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/async/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/cache/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/change/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/click/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/copy/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/counter/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/debounce/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/event/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/hotkey/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/hover/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/latest/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/previous/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/size/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/switch/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/throttle/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/timeout/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/toggle/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/media-query/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/merged-ref/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/previous/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import {renderHook, act} from '@testing-library/react-hooks' 3 | // import {render} from '@testing-library/react' 4 | // import userEvent from '@testing-library/user-event 5 | import usePrevious from './index' 6 | 7 | describe('usePrevious()', () => { 8 | it('should pass', () => { 9 | const handleChange = jest.fn() 10 | const useChanged = (onChange: typeof handleChange) => { 11 | const [status, setStatus] = React.useState('off') 12 | const prevStatus = usePrevious(status, status) 13 | 14 | React.useEffect(() => { 15 | if (status !== prevStatus) onChange(status, prevStatus) 16 | // eslint-disable-next-line react-hooks/exhaustive-deps 17 | }, [status]) 18 | 19 | return [status, setStatus] as const 20 | } 21 | 22 | const {result} = renderHook(() => useChanged(handleChange)) 23 | expect(result.current[0]).toBe('off') 24 | expect(handleChange).not.toBeCalled() 25 | act(() => result.current[1]('on')) 26 | expect(result.current[0]).toBe('on') 27 | expect(handleChange).toBeCalledWith('on', 'off') 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /packages/window-scroll/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/window-size/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/google-optimize/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/mouse-position/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/resize-observer/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/server-promises/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/intersection-observer/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/passive-layout-effect/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jared Lunde 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/event/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | declare function useEvent(target: Window | null, type: K, listener: WindowEventListener, cleanup?: (...args: any[]) => void): void; 3 | declare function useEvent(target: Document | null, type: K, listener: DocumentEventListener, cleanup?: (...args: any[]) => void): void; 4 | declare function useEvent(target: React.RefObject | T | null, type: K, listener: ElementEventListener, cleanup?: (...args: any[]) => void): void; 5 | export declare type ElementEventListener = (this: HTMLElement, ev: HTMLElementEventMap[K]) => any; 6 | export declare type DocumentEventListener = (this: Document, ev: DocumentEventMap[K]) => any; 7 | export declare type WindowEventListener = (this: Document, ev: WindowEventMap[K]) => any; 8 | export default useEvent; 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | To contribute to this project, first: 4 | 5 | 1. Fork this repo to your account 6 | 2. `git clone https://github.com/[your-username]/react-hook.git` 7 | 3. `cd react-hook` 8 | 9 | Packages must be installed individually. They must also be installed using the `yarn` packager. An example for working on `throttle`: 10 | 11 | 1. `cd packages/throttle` 12 | 2. `yarn install` 13 | 14 | ### Development scripts 15 | 16 | Each individual package includes the following scripts: 17 | 18 | - `build`: Builds cjs/esm, types, and umd 19 | - `dev`: Runs cjs and esm builds in `--watch` mode 20 | - `check-types`: Checks TypeScript types 21 | - `format`: Formats the package w/ Prettier 22 | - `lint`: Lints the package w/ eslint 23 | - `test`: Tests the package w/ Jest 24 | - `validate`: Tests, lints, and checks types for the package 25 | 26 | ### Submitting a pull request 27 | 28 | Prior to submitting a pull request please ensure that: 29 | 30 | 1. `yarn validate` passes 31 | 2. You've included a tag with the packages you updated in your commit message e.g. `[throttle] fix: setState was not called on the leading edge`. 32 | 3. You've opened an issue prior to large or breaking changes. Talking about it first will increase the likelihood of merging. 33 | -------------------------------------------------------------------------------- /packages/latest/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import {fireEvent} from '@testing-library/react' 3 | import {renderHook} from '@testing-library/react-hooks' 4 | import useLatest from './index' 5 | 6 | describe('useLatest()', () => { 7 | it('should store the latest value', () => { 8 | const fnA = () => {} 9 | const fnB = () => {} 10 | const {result, rerender} = renderHook(({fn}) => useLatest(fn), { 11 | initialProps: {fn: fnA}, 12 | }) 13 | expect(result.current.current).toBe(fnA) 14 | rerender({fn: fnB}) 15 | expect(result.current.current).toBe(fnB) 16 | }) 17 | 18 | it('should work as expected in useEffect()', () => { 19 | const fnA = jest.fn() 20 | const fnB = jest.fn() 21 | const {rerender} = renderHook( 22 | ({fn}) => { 23 | const latest = useLatest(fn) 24 | React.useEffect(() => { 25 | window.addEventListener('click', () => latest.current()) 26 | }, [latest]) 27 | }, 28 | { 29 | initialProps: {fn: fnA}, 30 | } 31 | ) 32 | 33 | fireEvent.click(window) 34 | expect(fnA).toBeCalledTimes(1) 35 | rerender({fn: fnB}) 36 | fireEvent.click(window) 37 | expect(fnA).toBeCalledTimes(1) 38 | expect(fnB).toBeCalledTimes(1) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /packages/change/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import {renderHook, act} from '@testing-library/react-hooks' 3 | // import {render} from '@testing-library/react' 4 | // import userEvent from '@testing-library/user-event 5 | import useChange from './index' 6 | 7 | describe('useChange()', () => { 8 | it('should call change handler when value changes', () => { 9 | const handleChange = jest.fn() 10 | 11 | const {result} = renderHook(() => { 12 | const [value, setValue] = React.useState('off') 13 | useChange(value, handleChange) 14 | return [value, setValue] as const 15 | }) 16 | 17 | expect(result.current[0]).toBe('off') 18 | expect(handleChange).not.toBeCalled() 19 | act(() => result.current[1]('on')) 20 | expect(result.current[0]).toBe('on') 21 | expect(handleChange).toBeCalledWith('on', 'off') 22 | }) 23 | 24 | it('should not call change handler when value does not change', () => { 25 | const handleChange = jest.fn() 26 | 27 | const {rerender} = renderHook(({value}) => useChange(value, handleChange), { 28 | initialProps: {value: 'off'}, 29 | }) 30 | 31 | expect(handleChange).not.toBeCalled() 32 | rerender({value: 'off'}) 33 | expect(handleChange).not.toBeCalled() 34 | rerender({value: 'on'}) 35 | expect(handleChange).toBeCalledWith('on', 'off') 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /packages/media-query/types/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A hook that returns a [`MediaQueryMatches`](#mediaquerymatches) object which will 3 | * tell you if specific media queries matched, all media queries matched, or 4 | * any media queries matched. Matches in this hook will always return `false` when 5 | * rendering on the server. 6 | * 7 | * @param queryMap The media queries you want to match against e.g. `{screen: "screen", width: "(min-width: 12em)"}` 8 | */ 9 | export declare function useMediaQueries( 10 | queryMap: MediaQueries 11 | ): MediaQueryMatches 12 | /** 13 | * A hook that returns `true` if the media query matched and `false` if not. This 14 | * hook will always return `false` when rendering on the server. 15 | * 16 | * @param query The media query you want to match against e.g. `"only screen and (min-width: 12em)"` 17 | */ 18 | export declare function useMediaQuery(query: string): boolean 19 | export declare type MediaQueries = { 20 | [Name in keyof T]: string 21 | } 22 | export declare type Matches = { 23 | [Name in keyof T]: boolean 24 | } 25 | export interface MediaQueryMatches { 26 | /** 27 | * Returns a map of query key/didMatch pairs 28 | */ 29 | matches: Matches 30 | /** 31 | * `true` if any of the media queries matched 32 | */ 33 | matchesAny: boolean 34 | /** 35 | * `true` if all of the media queries matched 36 | */ 37 | matchesAll: boolean 38 | } 39 | -------------------------------------------------------------------------------- /packages/async/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | export declare function useAsync< 3 | ValueType extends any = any, 4 | ErrorType extends any = Error, 5 | Args extends any[] = any[] 6 | >( 7 | asyncCallback: (...args: Args) => Promise 8 | ): readonly [ 9 | { 10 | status: AsyncStatus 11 | value: ValueType | undefined 12 | error: ErrorType | undefined 13 | cancel: () => void 14 | }, 15 | ((...args: Args) => Promise) & { 16 | cancel: () => void 17 | } 18 | ] 19 | export declare function useAsyncEffect< 20 | ValueType extends any = any, 21 | ErrorType extends any = Error 22 | >( 23 | asyncCallback: () => Promise, 24 | dependencies?: React.DependencyList 25 | ): { 26 | status: AsyncStatus 27 | value: ValueType | undefined 28 | error: ErrorType | undefined 29 | cancel: () => void 30 | } 31 | export interface AsyncReducerState { 32 | status: AsyncStatus 33 | value?: ValueType 34 | error?: ErrorType 35 | } 36 | export declare type AsyncAction = 37 | | { 38 | status: 'idle' | 'loading' | 'cancelled' 39 | } 40 | | { 41 | status: 'success' 42 | value: ValueType 43 | } 44 | | { 45 | status: 'error' 46 | error?: ErrorType 47 | } 48 | export declare type AsyncStatus = 49 | | 'idle' 50 | | 'loading' 51 | | 'success' 52 | | 'error' 53 | | 'cancelled' 54 | -------------------------------------------------------------------------------- /packages/intersection-observer/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import 'intersection-observer' 2 | import * as React from 'react' 3 | declare function useIntersectionObserver( 4 | target: React.RefObject | T | null, 5 | options?: IntersectionObserverOptions 6 | ): MockIntersectionObserverEntry | IntersectionObserverEntry 7 | export declare type UseIntersectionObserverCallback = ( 8 | entry: IntersectionObserverEntry, 9 | observer: IntersectionObserver 10 | ) => any 11 | export interface IntersectionObserverOptions { 12 | root?: HTMLElement | null 13 | pollInterval?: number | null 14 | useMutationObserver?: boolean 15 | rootMargin?: string 16 | threshold?: number | number[] 17 | initialIsIntersecting?: boolean 18 | } 19 | export interface IntersectionObserverBounds { 20 | readonly height: number 21 | readonly width: number 22 | readonly top: number 23 | readonly left: number 24 | readonly right: number 25 | readonly bottom: number 26 | } 27 | export interface MockIntersectionObserverEntry { 28 | readonly time: number | null 29 | readonly rootBounds: IntersectionObserverBounds | null 30 | readonly boundingClientRect: IntersectionObserverBounds | null 31 | readonly intersectionRect: IntersectionObserverBounds | null 32 | readonly intersectionRatio: number | null 33 | readonly target: HTMLElement | null 34 | readonly isIntersecting: boolean 35 | } 36 | export default useIntersectionObserver 37 | -------------------------------------------------------------------------------- /packages/counter/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import useLatest from '@react-hook/latest' 3 | 4 | const useCounter = (initialValue = 0, options: UseCounterOptions = {}) => { 5 | const {min, max, cast = Number, step = 1, onMin, onMax} = options 6 | const [value, _setValue] = React.useState( 7 | Math.min( 8 | Math.max(initialValue, min ?? initialValue), 9 | max ?? min ?? initialValue 10 | ) 11 | ) 12 | const storedOnMin = useLatest(onMin) 13 | const storedOnMax = useLatest(onMax) 14 | const set = React.useCallback( 15 | (value: number) => { 16 | if (min === void 0 || min === null || value >= min) { 17 | if (max === void 0 || max === null || value <= max) { 18 | _setValue(cast(value)) 19 | } else { 20 | _setValue(max) 21 | storedOnMax.current?.(set) 22 | } 23 | } else { 24 | _setValue(min) 25 | storedOnMin.current?.(set) 26 | } 27 | }, 28 | [min, max, cast, storedOnMin, storedOnMax] 29 | ) 30 | 31 | return { 32 | value, 33 | set, 34 | incr: React.useCallback((by = step) => set(value + by), [value, set, step]), 35 | decr: React.useCallback((by = step) => set(value - by), [value, set, step]), 36 | } 37 | } 38 | 39 | export interface UseCounterOptions { 40 | min?: number 41 | max?: number 42 | cast?: (value: number) => number 43 | step?: number 44 | onMin?: (set: (value: number) => void) => void 45 | onMax?: (set: (value: number) => void) => void 46 | } 47 | 48 | export default useCounter 49 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | yarn: true 5 | directories: 6 | - node_modules 7 | notifications: 8 | email: false 9 | node_js: "12" 10 | branches: 11 | only: 12 | - master 13 | matrix: 14 | include: 15 | - env: TARGET=packages/async 16 | - env: TARGET=packages/cache 17 | - env: TARGET=packages/change 18 | - env: TARGET=packages/click 19 | - env: TARGET=packages/copy 20 | - env: TARGET=packages/counter 21 | - env: TARGET=packages/debounce 22 | - env: TARGET=packages/event 23 | - env: TARGET=packages/google-optimize 24 | - env: TARGET=packages/hotkey 25 | - env: TARGET=packages/hover 26 | - env: TARGET=packages/intersection-observer 27 | - env: TARGET=packages/latest 28 | - env: TARGET=packages/media-query 29 | - env: TARGET=packages/merged-ref 30 | - env: TARGET=packages/mouse-position 31 | - env: TARGET=packages/passive-layout-effect 32 | - env: TARGET=packages/previous 33 | - env: TARGET=packages/resize-observer 34 | - env: TARGET=packages/server-promises 35 | - env: TARGET=packages/size 36 | - env: TARGET=packages/switch 37 | - env: TARGET=packages/throttle 38 | - env: TARGET=packages/timeout 39 | - env: TARGET=packages/toggle 40 | - env: TARGET=packages/window-scroll 41 | - env: TARGET=packages/window-size 42 | 43 | before_script: 44 | - chmod +x ./.travis/build-condition.sh 45 | script: if .travis/build-condition.sh $TRAVIS_COMMIT_RANGE $TARGET; then echo "$TARGET is being built" && cd $TARGET && yarn install && yarn validate; else echo "$TARGET is NOT being built"; fi 46 | -------------------------------------------------------------------------------- /packages/window-size/src/index.tsx: -------------------------------------------------------------------------------- 1 | import {useDebounce} from '@react-hook/debounce' 2 | import useEvent from '@react-hook/event' 3 | 4 | const emptyObj = {} 5 | 6 | export interface DebouncedWindowSizeOptions { 7 | initialWidth?: number 8 | initialHeight?: number 9 | wait?: number 10 | leading?: boolean 11 | } 12 | 13 | const win = typeof window === 'undefined' ? null : window 14 | const wv = 15 | win && typeof win.visualViewport !== 'undefined' ? win.visualViewport : null 16 | const getSize = () => 17 | [ 18 | document.documentElement.clientWidth, 19 | document.documentElement.clientHeight, 20 | ] as const 21 | 22 | export const useWindowSize = ( 23 | options: DebouncedWindowSizeOptions = emptyObj 24 | ): readonly [number, number] => { 25 | const {wait, leading, initialWidth = 0, initialHeight = 0} = options 26 | const [size, setDebouncedSize] = useDebounce( 27 | /* istanbul ignore next */ 28 | typeof document === 'undefined' ? [initialWidth, initialHeight] : getSize, 29 | wait, 30 | leading 31 | ) 32 | const setSize = (): void => setDebouncedSize(getSize) 33 | 34 | useEvent(win, 'resize', setSize) 35 | // @ts-expect-error 36 | useEvent(wv, 'resize', setSize) 37 | useEvent(win, 'orientationchange', setSize) 38 | 39 | return size 40 | } 41 | 42 | export const useWindowHeight = ( 43 | options?: Omit 44 | ): number => useWindowSize(options)[1] 45 | 46 | export const useWindowWidth = ( 47 | options?: Omit 48 | ): number => useWindowSize(options)[0] 49 | -------------------------------------------------------------------------------- /packages/switch/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import useLatest from '@react-hook/latest' 3 | 4 | /** 5 | * A hook for creating controlled toggles with on, off, and toggle callbacks. 6 | * This is extremely useful for creating controlled inputs for components like Checkbox. 7 | * 8 | * @param defaultValue Sets the default value of the switch 9 | * @param controlledValue Sets the controlled value of the switch, which will override 10 | * the defaultValue 11 | * @param onChange A callback invoked whenever toggle callbacks that change state are invoked 12 | */ 13 | function useSwitch( 14 | defaultValue = false, 15 | controlledValue?: boolean, 16 | onChange: (value: boolean) => any = noop 17 | ) { 18 | const [current, setCurrent] = React.useState(controlledValue ?? defaultValue) 19 | const storedOnChange = useLatest(onChange) 20 | 21 | if (typeof controlledValue === 'boolean' && controlledValue !== current) { 22 | setCurrent(controlledValue) 23 | } 24 | 25 | const toggle = React.useCallback(() => { 26 | setCurrent(!current) 27 | storedOnChange.current(!current) 28 | }, [storedOnChange, current]) 29 | 30 | return [ 31 | controlledValue ?? current, 32 | Object.assign(toggle, { 33 | on: React.useCallback(() => { 34 | setCurrent(true) 35 | if (!current) storedOnChange.current(true) 36 | }, [storedOnChange, current]), 37 | off: React.useCallback(() => { 38 | setCurrent(false) 39 | if (current) storedOnChange.current(false) 40 | }, [storedOnChange, current]), 41 | }), 42 | ] as const 43 | } 44 | 45 | function noop() {} 46 | 47 | export default useSwitch 48 | -------------------------------------------------------------------------------- /packages/hover/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import useEvent from '@react-hook/event' 3 | 4 | function useHover( 5 | target: React.RefObject | T | null, 6 | options: UseHoverOptions = {} 7 | ): boolean { 8 | const {enterDelay, leaveDelay} = options 9 | const timeout = React.useRef() 10 | const [hovering, setHovering] = React.useState(false) 11 | 12 | const toggle = (which: boolean) => { 13 | if (!canHover()) return 14 | const delay = which ? enterDelay : leaveDelay 15 | window.clearTimeout(timeout.current) 16 | 17 | if (delay) { 18 | timeout.current = window.setTimeout(() => setHovering(which), delay) 19 | } else { 20 | setHovering(which) 21 | } 22 | } 23 | 24 | useEvent(target, 'mouseenter', () => toggle(true)) 25 | useEvent(target, 'mouseleave', () => toggle(false)) 26 | 27 | // Cleans up timeout on unmount 28 | React.useEffect( 29 | () => () => { 30 | window.clearTimeout(timeout.current) 31 | }, 32 | // eslint-disable-next-line react-hooks/exhaustive-deps 33 | [] 34 | ) 35 | 36 | return hovering 37 | } 38 | 39 | const canHover = (): boolean => 40 | typeof window !== 'undefined' 41 | ? !window.matchMedia('(hover: none)').matches 42 | : false 43 | 44 | export interface UseHoverOptions { 45 | enterDelay?: number 46 | leaveDelay?: number 47 | } 48 | 49 | export interface UseHoverState { 50 | status: 'idle' | 'hovering' 51 | timeout: number | undefined 52 | } 53 | 54 | export type UseHoverAction = { 55 | type: 'setStatus' 56 | value: 'idle' | 'hovering' 57 | force?: boolean 58 | } 59 | 60 | export default useHover 61 | -------------------------------------------------------------------------------- /packages/window-size/throttled/src/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | import useEvent from '@react-hook/event' 3 | import {useThrottle} from '@react-hook/throttle' 4 | 5 | const emptyObj = {} 6 | 7 | export interface ThrottledWindowSizeOptions { 8 | initialWidth?: number 9 | initialHeight?: number 10 | fps?: number 11 | leading?: boolean 12 | } 13 | 14 | const win = typeof window === 'undefined' ? null : window 15 | const wv = 16 | win && typeof win.visualViewport !== 'undefined' ? win.visualViewport : null 17 | const getSize = () => 18 | [ 19 | document.documentElement.clientWidth, 20 | document.documentElement.clientHeight, 21 | ] as const 22 | 23 | export const useWindowSize = ( 24 | options: ThrottledWindowSizeOptions = emptyObj 25 | ): readonly [number, number] => { 26 | const {fps, leading, initialWidth = 0, initialHeight = 0} = options 27 | const [size, setThrottledSize] = useThrottle( 28 | /* istanbul ignore next */ 29 | typeof document === 'undefined' ? [initialWidth, initialHeight] : getSize, 30 | fps, 31 | leading 32 | ) 33 | const setSize = (): void => setThrottledSize(getSize) 34 | 35 | useEvent(win, 'resize', setSize) 36 | // @ts-expect-error 37 | useEvent(wv, 'resize', setSize) 38 | useEvent(win, 'orientationchange', setSize) 39 | 40 | return size 41 | } 42 | 43 | export const useWindowHeight = ( 44 | options?: Omit 45 | ): number => useWindowSize(options)[1] 46 | 47 | export const useWindowWidth = ( 48 | options?: Omit 49 | ): number => useWindowSize(options)[0] 50 | -------------------------------------------------------------------------------- /packages/size/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import useResizeObserver from '@react-hook/resize-observer' 3 | import useLayoutEffect from '@react-hook/passive-layout-effect' 4 | 5 | /** 6 | * A React hook for measuring the size of HTML elements including when they change 7 | * 8 | * @param target A React ref created by `useRef()` or an HTML element 9 | * @param options Configures the initial width and initial height of the hook's state 10 | */ 11 | const useSize = ( 12 | target: React.RefObject | T | null, 13 | options?: UseSizeOptions 14 | ): [number, number] => { 15 | const [size, setSize] = React.useState<[number, number]>(() => { 16 | const targetEl = target && 'current' in target ? target.current : target 17 | return targetEl 18 | ? [targetEl.offsetWidth, targetEl.offsetHeight] 19 | : [options?.initialWidth ?? 0, options?.initialHeight ?? 0] 20 | }) 21 | 22 | useLayoutEffect(() => { 23 | const targetEl = target && 'current' in target ? target.current : target 24 | if (!targetEl) return 25 | setSize([targetEl.offsetWidth, targetEl.offsetHeight]) 26 | }, [target]) 27 | 28 | // Where the magic happens 29 | useResizeObserver(target, (entry) => { 30 | const target = entry.target as HTMLElement 31 | setSize([target.offsetWidth, target.offsetHeight]) 32 | }) 33 | 34 | return size 35 | } 36 | 37 | export interface UseSizeOptions { 38 | // The initial width to set into state. 39 | // This is useful for SSR environments. 40 | initialWidth: number 41 | // The initial height to set into state. 42 | // This is useful for SSR environments. 43 | initialHeight: number 44 | } 45 | 46 | export default useSize 47 | -------------------------------------------------------------------------------- /packages/server-promises/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {renderHook} from '@testing-library/react-hooks' 3 | import {renderToString} from 'react-dom/server' 4 | import {useServerPromises, loadPromises} from './index' 5 | 6 | describe('useServerPromises()', () => { 7 | it('should add promises', () => { 8 | const {result} = renderHook(() => useServerPromises()) 9 | result.current.push(Promise.resolve('foo')) 10 | expect(result.current.promises.length).toBe(1) 11 | }) 12 | 13 | it('should render to string w/ resolved promises', async () => { 14 | const Component = ({value, stop = false}) => { 15 | const {push} = useServerPromises() 16 | 17 | React.useEffect(() => { 18 | push(Promise.resolve(value)) 19 | }, [push, value]) 20 | 21 | return ( 22 |
23 | {value} {!stop && } 24 |
25 | ) 26 | } 27 | 28 | const html = await loadPromises() 29 | expect(html).toMatchSnapshot(` 30 |
31 | Hello
world
32 |
33 | `) 34 | }) 35 | 36 | it('should use custom renderer', async () => { 37 | const Component = ({value, stop = false}) => { 38 | const {push} = useServerPromises() 39 | 40 | React.useEffect(() => { 41 | push(Promise.resolve(value)) 42 | }, [push, value]) 43 | 44 | return ( 45 |
46 | {value} {!stop && } 47 |
48 | ) 49 | } 50 | 51 | const html = await loadPromises(, renderToString) 52 | expect(html).toMatchSnapshot(` 53 |
54 | Hello
world
55 |
56 | `) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /packages/hover/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import {fireEvent} from '@testing-library/react' 2 | import {renderHook, act} from '@testing-library/react-hooks' 3 | import useHover from './index' 4 | 5 | it('responds to enters and leaves', () => { 6 | const element = document.createElement('div') 7 | 8 | const {result} = renderHook(() => useHover(element)) 9 | 10 | expect(result.current).toBe(false) 11 | 12 | act(() => { 13 | fireEvent.mouseEnter(element) 14 | }) 15 | expect(result.current).toBe(true) 16 | 17 | act(() => { 18 | fireEvent.mouseLeave(element) 19 | }) 20 | expect(result.current).toBe(false) 21 | }) 22 | 23 | it('responds to enter delay', () => { 24 | jest.useFakeTimers() 25 | const element = document.createElement('div') 26 | const {result} = renderHook(() => useHover(element, {enterDelay: 1000})) 27 | 28 | expect(result.current).toBe(false) 29 | 30 | act(() => { 31 | fireEvent.mouseEnter(element) 32 | }) 33 | expect(result.current).toBe(false) 34 | 35 | act(() => { 36 | jest.runAllTimers() 37 | }) 38 | 39 | expect(result.current).toBe(true) 40 | 41 | act(() => { 42 | fireEvent.mouseLeave(element) 43 | }) 44 | expect(result.current).toBe(false) 45 | }) 46 | 47 | it('responds to leave delay', () => { 48 | jest.useFakeTimers() 49 | const element = document.createElement('div') 50 | 51 | const {result} = renderHook(() => 52 | useHover(element, {enterDelay: 0, leaveDelay: 1000}) 53 | ) 54 | 55 | expect(result.current).toBe(false) 56 | 57 | act(() => { 58 | fireEvent.mouseEnter(element) 59 | }) 60 | expect(result.current).toBe(true) 61 | 62 | act(() => { 63 | fireEvent.mouseLeave(element) 64 | }) 65 | expect(result.current).toBe(true) 66 | 67 | act(() => { 68 | jest.runAllTimers() 69 | }) 70 | 71 | expect(result.current).toBe(false) 72 | }) 73 | -------------------------------------------------------------------------------- /packages/server-promises/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | export const ServerPromisesContext = React.createContext(createPromisesCache()) 3 | export const useServerPromises = () => React.useContext(ServerPromisesContext) 4 | 5 | // renders the app until there are no promises that get pushed to the cache array 6 | export function loadPromises( 7 | tree: React.ReactElement, 8 | // eslint-disable-next-line @typescript-eslint/no-var-requires 9 | render: (element: React.ReactElement) => T = require('react-dom/server') 10 | .renderToStaticMarkup 11 | ): Promise { 12 | const cache = createPromisesCache() 13 | 14 | const process = (): Promise | T => { 15 | const html = render( 16 | React.createElement(ServerPromisesContext.Provider, { 17 | value: cache, 18 | children: tree, 19 | }) 20 | ) 21 | 22 | return cache.promises.length > 0 ? cache.load().then(process) : html 23 | } 24 | 25 | return Promise.resolve().then(process) 26 | } 27 | 28 | // preloads all of the async components used in the current react tree 29 | function createPromisesCache(initial: Promise[] = []) { 30 | const cache = { 31 | // Map from Query component instances to pending promises. 32 | promises: initial, 33 | push: (...args: Promise[]) => cache.promises.push(...args), 34 | load(): Promise { 35 | return Promise.all(cache.promises).then(() => (cache.promises = [])) 36 | }, 37 | } 38 | 39 | return cache 40 | } 41 | 42 | export interface ServerPromisesContextType { 43 | /** 44 | * An array of promises that are still pending 45 | */ 46 | promises: Promise[] 47 | /** 48 | * Adds a promise to the promises array 49 | */ 50 | push: (...args: Promise[]) => number 51 | /** 52 | * Loads all of the promises currently in the promises array 53 | */ 54 | load: () => Promise 55 | } 56 | -------------------------------------------------------------------------------- /packages/timeout/types/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A hook that sets a `timedOut` boolean flag to true after `ms` have passed and the timeout has 3 | * been started. 4 | * 5 | * @param ms The amount of time to wait before setting `timedOut` to `true` 6 | * @returns The first element is a `timedOut` boolean that is `true` when 7 | * the timeout `ms` have been exceeded and `false` otherwise. The second element is a `start` method for 8 | * starting the timeout. The third element is a `reset` method for clearing the timeout. 9 | * 10 | * @example 11 | * const [timedOut, start, reset] = useTimeout(300) 12 | * // starts the timeout 13 | * start() 14 | * // clears the timeout 15 | * reset() 16 | * // logs the timedOut state 17 | * console.log(timedOut) 18 | */ 19 | export declare const useTimeout: ( 20 | ms?: number 21 | ) => [boolean, () => void, () => void] 22 | /** 23 | * A hook that executes a callback after a timeout is reached and the clock has 24 | * been started. 25 | * 26 | * @param callback The callback you want invoked when the timeout is exceeded 27 | * @param ms The amount of time to wait before firing the callback after `start()` is invoked. 28 | * When this is `0` the callback will never fire. 29 | * @param dependencies Dependencies array for your callback. Anytime your dependencies change, 30 | * the timeout will be reset to an idle state and any pending timeouts will not invoke. 31 | * @returns The first element is a `start` method for starting the timeout. The 32 | * second element is a `reset` method for clearing the timeout. 33 | * 34 | * @example 35 | * const [start, reset] = useTimeoutCallback(() => setState('copied'), 0, [text]) 36 | * // starts the timeout 37 | * start() 38 | * // clears the timeout 39 | * reset() 40 | */ 41 | export declare const useTimeoutCallback: ( 42 | callback: (...args: any[]) => any, 43 | ms?: number 44 | ) => [() => void, () => void] 45 | -------------------------------------------------------------------------------- /packages/timeout/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | /* jest */ 2 | import {renderHook, act} from '@testing-library/react-hooks' 3 | import {useTimeout} from './index' 4 | 5 | beforeAll(() => { 6 | jest.useFakeTimers() 7 | }) 8 | 9 | afterAll(() => { 10 | jest.useRealTimers() 11 | }) 12 | 13 | describe('useTimeout()', () => { 14 | it('should return timedOut = true when timeout duration has been met', () => { 15 | const {result} = renderHook(() => useTimeout(300)) 16 | expect(result.current[0]).toBe(false) 17 | act(() => result.current[1]()) 18 | act(() => jest.advanceTimersByTime(299)) 19 | expect(result.current[0]).toBe(false) 20 | act(() => jest.advanceTimersByTime(1)) 21 | expect(result.current[0]).toBe(true) 22 | }) 23 | 24 | it('should reset the current timer when reset() is invoked', () => { 25 | const {result} = renderHook(() => useTimeout(300)) 26 | expect(result.current[0]).toBe(false) 27 | act(() => result.current[1]()) 28 | act(() => jest.advanceTimersByTime(299)) 29 | expect(result.current[0]).toBe(false) 30 | act(() => result.current[2]()) 31 | act(() => jest.advanceTimersByTime(1)) 32 | expect(result.current[0]).toBe(false) 33 | }) 34 | 35 | it('should reset the current timer when the ms argument changes', () => { 36 | const {result, rerender} = renderHook( 37 | ({initialValue}) => useTimeout(initialValue), 38 | { 39 | initialProps: {initialValue: 300}, 40 | } 41 | ) 42 | 43 | expect(result.current[0]).toBe(false) 44 | act(() => result.current[1]()) 45 | act(() => jest.advanceTimersByTime(299)) 46 | expect(result.current[0]).toBe(false) 47 | rerender({initialValue: 1}) 48 | act(() => jest.advanceTimersByTime(1)) 49 | expect(result.current[0]).toBe(false) 50 | act(() => result.current[1]()) 51 | act(() => jest.advanceTimersByTime(1)) 52 | expect(result.current[0]).toBe(true) 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /packages/debounce/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import useLatest from '@react-hook/latest' 3 | 4 | export const useDebounceCallback = ( 5 | callback: (...args: CallbackArgs) => void, 6 | wait = 100, 7 | leading = false 8 | ): ((...args: CallbackArgs) => void) => { 9 | const storedCallback = useLatest(callback) 10 | const timeout = React.useRef>() 11 | const deps = [wait, leading, storedCallback] 12 | // Cleans up pending timeouts when the deps change 13 | React.useEffect( 14 | () => () => { 15 | timeout.current && clearTimeout(timeout.current) 16 | timeout.current = void 0 17 | }, 18 | deps 19 | ) 20 | 21 | return React.useCallback(function () { 22 | // eslint-disable-next-line prefer-rest-params 23 | const args = arguments 24 | const {current} = timeout 25 | // Calls on leading edge 26 | if (current === void 0 && leading) { 27 | timeout.current = setTimeout(() => { 28 | timeout.current = void 0 29 | }, wait) 30 | // eslint-disable-next-line prefer-spread 31 | return storedCallback.current.apply(null, args as any) 32 | } 33 | // Clear the timeout every call and start waiting again 34 | current && clearTimeout(current) 35 | // Waits for `wait` before invoking the callback 36 | timeout.current = setTimeout(() => { 37 | timeout.current = void 0 38 | storedCallback.current.apply(null, args as any) 39 | }, wait) 40 | }, deps) 41 | } 42 | 43 | export const useDebounce = ( 44 | initialState: State | (() => State), 45 | wait?: number, 46 | leading?: boolean 47 | ): [ 48 | State, 49 | React.Dispatch>, 50 | React.Dispatch> 51 | ] => { 52 | const state = React.useState(initialState) 53 | return [state[0], useDebounceCallback(state[1], wait, leading), state[1]] 54 | } 55 | -------------------------------------------------------------------------------- /packages/copy/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | function useCopy(text: string) { 4 | const [copied, setCopied] = React.useState(false) 5 | const reset = React.useRef(() => setCopied(false)) 6 | // Resets 'copied' if text changes 7 | React.useEffect(() => reset.current, [text]) 8 | return { 9 | copied, 10 | copy: React.useCallback( 11 | () => 12 | copyToClipboard(text) 13 | .then(() => setCopied(true)) 14 | .catch(() => setCopied((copied) => copied)), 15 | [text] 16 | ), 17 | reset: reset.current, 18 | } as const 19 | } 20 | /* istanbul ignore next */ 21 | function copyToClipboard(text: string) { 22 | // uses the Async Clipboard API when available. Requires a secure browing 23 | // context (i.e. HTTPS) 24 | if (navigator.clipboard) return navigator.clipboard.writeText(text) 25 | // puts the text to copy into a 26 | const span = document.createElement('span') 27 | span.textContent = text 28 | // preserves consecutive spaces and newlines 29 | span.style.whiteSpace = 'pre' 30 | // adds the to the page 31 | document.body.appendChild(span) 32 | // makes a selection object representing the range of text selected by the user 33 | const selection = window.getSelection() 34 | if (!selection) return Promise.reject() 35 | const range = window.document.createRange() 36 | selection.removeAllRanges() 37 | range.selectNode(span) 38 | selection.addRange(range) 39 | // copies text to the clipboard 40 | try { 41 | window.document.execCommand('copy') 42 | } catch (err) { 43 | return Promise.reject() 44 | } 45 | // cleans up the dom element and selection 46 | selection.removeAllRanges() 47 | window.document.body.removeChild(span) 48 | // the Async Clipboard API returns a promise that may reject with `undefined` 49 | // so we match that here for consistency 50 | return Promise.resolve() 51 | } 52 | 53 | export default useCopy 54 | -------------------------------------------------------------------------------- /packages/passive-layout-effect/README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | usePassiveLayoutEffect() 5 |

6 |
7 | 8 |

9 | 10 | Bundlephobia 11 | 12 | 13 | Types 14 | 15 | 16 | Build status 17 | 18 | 19 | NPM Version 20 | 21 | 22 | MIT License 23 | 24 |

25 | 26 |
npm i @react-hook/passive-layout-effect
27 |
28 | 29 | A React hook that uses `useEffect()` on the server and `useLayoutEffect()` in the browser 30 | 31 | ## Quick Start 32 | 33 | ```jsx harmony 34 | import useLayoutEffect from '@react-hook/passive-layout-effect' 35 | 36 | const Component = ({foo, bar}) => { 37 | // Used the same way useEffect() and useLayoutEffect() are 38 | useLayoutEffect(() => {}, [foo, bar]) 39 | } 40 | ``` 41 | 42 | ## Full credit to 43 | 44 | [Dan Abramov](https://gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85) 45 | 46 | ## LICENSE 47 | 48 | MIT 49 | -------------------------------------------------------------------------------- /packages/throttle/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import useLatest from '@react-hook/latest' 3 | 4 | const perf = typeof performance !== 'undefined' ? performance : Date 5 | const now = () => perf.now() 6 | 7 | export function useThrottleCallback( 8 | callback: (...args: CallbackArguments) => void, 9 | fps = 30, 10 | leading = false 11 | ): (...args: CallbackArguments) => void { 12 | const storedCallback = useLatest(callback) 13 | const ms = 1000 / fps 14 | const prev = React.useRef(0) 15 | const trailingTimeout = React.useRef>() 16 | const clearTrailing = () => 17 | trailingTimeout.current && clearTimeout(trailingTimeout.current) 18 | const deps = [fps, leading, storedCallback] 19 | 20 | // Reset any time the deps change 21 | React.useEffect( 22 | () => () => { 23 | prev.current = 0 24 | clearTrailing() 25 | }, 26 | deps 27 | ) 28 | 29 | return React.useCallback(function () { 30 | // eslint-disable-next-line prefer-rest-params 31 | const args = arguments 32 | const rightNow = now() 33 | const call = () => { 34 | prev.current = rightNow 35 | clearTrailing() 36 | storedCallback.current.apply(null, args as any) 37 | } 38 | const current = prev.current 39 | // leading 40 | if (leading && current === 0) return call() 41 | // body 42 | if (rightNow - current > ms) { 43 | if (current > 0) return call() 44 | prev.current = rightNow 45 | } 46 | // trailing 47 | clearTrailing() 48 | trailingTimeout.current = setTimeout(() => { 49 | call() 50 | prev.current = 0 51 | }, ms) 52 | }, deps) 53 | } 54 | 55 | export function useThrottle( 56 | initialState: State | (() => State), 57 | fps?: number, 58 | leading?: boolean 59 | ): [State, React.Dispatch>] { 60 | const state = React.useState(initialState) 61 | return [state[0], useThrottleCallback(state[1], fps, leading)] 62 | } 63 | -------------------------------------------------------------------------------- /packages/copy/README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | useCopy() 5 |

6 |
7 | 8 |

9 | 10 | Bundlephobia 11 | 12 | 13 | Types 14 | 15 | 16 | Build status 17 | 18 | 19 | NPM Version 20 | 21 | 22 | MIT License 23 | 24 |

25 | 26 |
npm i @react-hook/copy
27 |
28 | 29 | A React hook for copying text to the clipboard 30 | 31 | ## Quick Start 32 | 33 | ```jsx harmony 34 | import useCopy from '@react-hook/copy' 35 | 36 | const Component = (props) => { 37 | const {copied, copy, reset} = useCopy( 38 | 'This text will be copied to the clipboard' 39 | ) 40 | 41 | return {copied === false ? 'Copy' : 'Copied'} 42 | } 43 | ``` 44 | 45 | ## API 46 | 47 | ### useCopy(text: string) 48 | 49 | | Argument | Type | Required? | Description | 50 | | -------- | -------- | --------- | ----------------------------------------------------------------- | 51 | | text | `string` | Yes | The text you want to copy to the clipboard when `copy` is clicked | 52 | 53 | ### Returns `{copied: boolean, copy: () => void, reset: () => void}` 54 | 55 | ## LICENSE 56 | 57 | MIT 58 | -------------------------------------------------------------------------------- /packages/switch/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import {renderHook, act} from '@testing-library/react-hooks' 2 | import useSwitch from './index' 3 | 4 | describe('useSwitch()', () => { 5 | it('should turn on', () => { 6 | const {result} = renderHook(() => useSwitch()) 7 | expect(result.current[0]).toBe(false) 8 | act(result.current[1].on) 9 | expect(result.current[0]).toBe(true) 10 | }) 11 | 12 | it('should turn off', () => { 13 | const {result} = renderHook(() => useSwitch(true)) 14 | expect(result.current[0]).toBe(true) 15 | act(result.current[1].off) 16 | expect(result.current[0]).toBe(false) 17 | }) 18 | 19 | it('should toggle', () => { 20 | const {result} = renderHook(() => useSwitch(false)) 21 | expect(result.current[0]).toBe(false) 22 | act(result.current[1]) 23 | expect(result.current[0]).toBe(true) 24 | act(result.current[1]) 25 | expect(result.current[0]).toBe(false) 26 | }) 27 | 28 | it('should be controlled', () => { 29 | let value = true 30 | const handleChange = (nextValue: boolean) => { 31 | value = nextValue 32 | } 33 | 34 | const {result, rerender} = renderHook( 35 | ({value}) => useSwitch(false, value, handleChange), 36 | {initialProps: {value}} 37 | ) 38 | 39 | expect(result.current[0]).toBe(true) 40 | act(result.current[1]) 41 | expect(result.current[0]).toBe(true) 42 | rerender({value}) 43 | expect(result.current[0]).toBe(false) 44 | }) 45 | 46 | it('should be controlled externally', () => { 47 | const handleChange = jest.fn() 48 | const {result, rerender} = renderHook( 49 | ({value}) => useSwitch(false, value, handleChange), 50 | {initialProps: {value: true}} 51 | ) 52 | 53 | rerender({value: false}) 54 | expect(result.current[0]).toBe(false) 55 | rerender({value: true}) 56 | expect(result.current[0]).toBe(true) 57 | expect(handleChange).not.toBeCalled() 58 | rerender({value: false}) 59 | expect(result.current[0]).toBe(false) 60 | rerender({value: false}) 61 | act(result.current[1]) 62 | expect(result.current[0]).toBe(false) 63 | expect(handleChange).toBeCalledWith(true) 64 | }) 65 | }) 66 | -------------------------------------------------------------------------------- /packages/merged-ref/README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | useMergedRef() 5 |

6 |
7 | 8 |

9 | 10 | Bundlephobia 11 | 12 | 13 | Types 14 | 15 | 16 | Build status 17 | 18 | 19 | NPM Version 20 | 21 | 22 | MIT License 23 | 24 |

25 | 26 |
npm i @react-hook/merged-ref
27 |
28 | 29 | A React hook for merging multiple refs into one ref 30 | 31 | ## Quick Start 32 | 33 | ```jsx harmony 34 | import React from 'react' 35 | import useMergedRef from '@react-hook/merged-ref' 36 | 37 | const Component = React.forwardRef((props, ref) => { 38 | const otherRef = React.useRef(null) 39 | const multiRef = useMergedRef(ref, otherRef) 40 | return
41 | }) 42 | ``` 43 | 44 | ## API 45 | 46 | ### `useMergedRef(...refs)` 47 | 48 | ```ts 49 | function useMergedRef(...refs: React.Ref[]): React.RefCallback 50 | ``` 51 | 52 | | Argument | Description | 53 | | -------- | ------------------------------------------------------------------ | 54 | | refs | React callback refs or refs created with `useRef()`, `createRef()` | 55 | 56 | #### Returns `React.RefCallback` 57 | 58 | Returns a callback ref 59 | 60 | ## LICENSE 61 | 62 | MIT 63 | -------------------------------------------------------------------------------- /packages/change/README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | useChange() 5 |

6 |
7 | 8 |

9 | 10 | Bundlephobia 11 | 12 | 13 | Types 14 | 15 | 16 | Build status 17 | 18 | 19 | NPM Version 20 | 21 | 22 | MIT License 23 | 24 |

25 | 26 |
npm i @react-hook/change
27 |
28 | 29 | A React hook that invokes a callback anytime a value changes 30 | 31 | ## Quick Start 32 | 33 | ```jsx harmony 34 | import useChange from '@react-hook/change' 35 | 36 | const useChangeLog = (value) => { 37 | // Logs the value each time it changes 38 | useChange(value, console.log) 39 | } 40 | ``` 41 | 42 | ## API 43 | 44 | ### useChange(value, onChange) 45 | 46 | ```ts 47 | const useChange = ( 48 | value: T, 49 | onChange: (current: T, prev: T) => any 50 | ): void 51 | ``` 52 | 53 | | Argument | Type | Required? | Description | 54 | | -------- | ------------------------------ | --------- | ----------------------------------------------------- | 55 | | value | `T` | Yes | The value to watch for changes to | 56 | | onChange | `(current: T, prev: T) => any` | Yes | This callback is invoked any time the `value` changes | 57 | 58 | ### Returns `void` 59 | 60 | ## LICENSE 61 | 62 | MIT 63 | -------------------------------------------------------------------------------- /packages/event/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | function useEvent< 4 | T extends Window = Window, 5 | K extends keyof WindowEventMap = keyof WindowEventMap 6 | >( 7 | target: Window | null, 8 | type: K, 9 | listener: WindowEventListener, 10 | cleanup?: (...args: any[]) => void 11 | ): void 12 | function useEvent< 13 | T extends Document = Document, 14 | K extends keyof DocumentEventMap = keyof DocumentEventMap 15 | >( 16 | target: Document | null, 17 | type: K, 18 | listener: DocumentEventListener, 19 | cleanup?: (...args: any[]) => void 20 | ): void 21 | function useEvent< 22 | T extends HTMLElement = HTMLElement, 23 | K extends keyof HTMLElementEventMap = keyof HTMLElementEventMap 24 | >( 25 | target: React.RefObject | T | null, 26 | type: K, 27 | listener: ElementEventListener, 28 | cleanup?: (...args: any[]) => void 29 | ): void 30 | function useEvent(target: any, type: any, listener: any, cleanup: any): void { 31 | const storedListener = React.useRef(listener) 32 | const storedCleanup = React.useRef(cleanup) 33 | 34 | React.useEffect(() => { 35 | storedListener.current = listener 36 | storedCleanup.current = cleanup 37 | }) 38 | 39 | React.useEffect(() => { 40 | const targetEl = target && 'current' in target ? target.current : target 41 | if (!targetEl) return 42 | 43 | let didUnsubscribe = 0 44 | function listener(this: any, ...args: any[]) { 45 | if (didUnsubscribe) return 46 | storedListener.current.apply(this, args) 47 | } 48 | 49 | targetEl.addEventListener(type, listener) 50 | const cleanup = storedCleanup.current 51 | 52 | return () => { 53 | didUnsubscribe = 1 54 | targetEl.removeEventListener(type, listener) 55 | cleanup && cleanup() 56 | } 57 | // eslint-disable-next-line react-hooks/exhaustive-deps 58 | }, [target, type]) 59 | } 60 | 61 | export type ElementEventListener< 62 | K extends keyof HTMLElementEventMap = keyof HTMLElementEventMap 63 | > = (this: HTMLElement, ev: HTMLElementEventMap[K]) => any 64 | 65 | export type DocumentEventListener< 66 | K extends keyof DocumentEventMap = keyof DocumentEventMap 67 | > = (this: Document, ev: DocumentEventMap[K]) => any 68 | 69 | export type WindowEventListener< 70 | K extends keyof WindowEventMap = keyof WindowEventMap 71 | > = (this: Document, ev: WindowEventMap[K]) => any 72 | 73 | export default useEvent 74 | -------------------------------------------------------------------------------- /packages/event/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import {render, fireEvent, screen} from '@testing-library/react' 3 | import useEvent from './index' 4 | 5 | describe('useEvent()', () => { 6 | it('should add events to ref object', () => { 7 | const Component = () => { 8 | const ref = React.useRef(null) 9 | const [foo, setFoo] = React.useState('') 10 | useEvent(ref, 'click', () => setFoo('bar')) 11 | return ( 12 |
13 | {foo} 14 |
15 | ) 16 | } 17 | 18 | const {unmount} = render() 19 | expect(screen.getByTestId('foo')).toBeEmptyDOMElement() 20 | fireEvent.click(screen.getByTestId('foo')) 21 | expect(screen.getByTestId('foo').innerHTML).toBe('bar') 22 | unmount() 23 | }) 24 | 25 | it('should add events to document', () => { 26 | const Component = () => { 27 | const [foo, setFoo] = React.useState('') 28 | useEvent(document, 'click', () => setFoo('bar')) 29 | useEvent(document, 'click', () => setFoo('bar')) 30 | return
{foo}
31 | } 32 | 33 | const {unmount} = render() 34 | expect(screen.getByTestId('foo')).toBeEmptyDOMElement() 35 | fireEvent.click(document) 36 | expect(screen.getByTestId('foo').innerHTML).toBe('bar') 37 | unmount() 38 | }) 39 | 40 | it('should add events to window', () => { 41 | const Component = () => { 42 | const [foo, setFoo] = React.useState('') 43 | useEvent(window, 'click', () => setFoo('bar')) 44 | return
{foo}
45 | } 46 | 47 | const {unmount} = render() 48 | expect(screen.getByTestId('foo')).toBeEmptyDOMElement() 49 | fireEvent.click(window) 50 | expect(screen.getByTestId('foo').innerHTML).toBe('bar') 51 | unmount() 52 | }) 53 | 54 | it('should invoke cleanup fn on unmount', () => { 55 | const cleanup = jest.fn() 56 | 57 | const Component = () => { 58 | const ref = React.useRef(null) 59 | const [foo, setFoo] = React.useState('') 60 | useEvent(ref, 'click', () => setFoo('bar'), cleanup) 61 | return ( 62 |
63 | {foo} 64 |
65 | ) 66 | } 67 | 68 | const {unmount} = render() 69 | expect(cleanup).not.toBeCalled() 70 | unmount() 71 | expect(cleanup).toBeCalled() 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /packages/latest/README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | useLatest() 5 |

6 |
7 | Supported by FlexStack 8 |
9 |
10 |
11 | 12 |

13 | 14 | Bundlephobia 15 | 16 | 17 | Types 18 | 19 | 20 | Build status 21 | 22 | 23 | NPM Version 24 | 25 | 26 | MIT License 27 | 28 |

29 | 30 |
npm i @react-hook/latest
31 | 32 | A React hook that updates useRef().current with the most recent value each invocation 33 | 34 | ## Quick Start 35 | 36 | ```jsx harmony 37 | import useLatest from '@react-hook/latest' 38 | 39 | const useEvent = (element, name, listener) => { 40 | const latest = useLatest(listener) 41 | 42 | React.useEffect(() => { 43 | const listen = (e) => latest.current(e) 44 | element.addEventListener(name, listen) 45 | return () => element.removeEventListener(name, listen) 46 | }, [name]) 47 | } 48 | ``` 49 | 50 | ## API 51 | 52 | ### useLatest(value) 53 | 54 | ```ts 55 | const useLatest: (current: T) => React.MutableRefObject 56 | ``` 57 | 58 | ### Props 59 | 60 | | Prop | Type | Required? | Description | 61 | | ----- | --------------- | --------- | ------------------------- | 62 | | value | `T extends any` | Yes | Any value you want stored | 63 | 64 | ### Returns `React.MutableRefObject` 65 | 66 | ## LICENSE 67 | 68 | MIT 69 | -------------------------------------------------------------------------------- /packages/click/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import useLatest from '@react-hook/latest' 3 | 4 | function useClick( 5 | conditions: string | string[], 6 | callback: (...args: any[]) => any 7 | ) { 8 | const storedConditions = useLatest(conditions) 9 | const storedCallback = useLatest(callback) 10 | 11 | return React.useCallback( 12 | (e: React.MouseEvent) => { 13 | if ( 14 | isClickOfType( 15 | e, 16 | Array.isArray(storedConditions.current) 17 | ? storedConditions.current 18 | : [storedConditions.current] 19 | ) === true 20 | ) { 21 | const {left, top} = (e.target as HTMLElement).getBoundingClientRect() 22 | storedCallback.current(e, { 23 | x: e.clientX - Math.floor(left), 24 | y: e.clientY - Math.floor(top), 25 | count: e.detail, 26 | }) 27 | } 28 | }, 29 | [storedCallback, storedConditions] 30 | ) 31 | } 32 | 33 | function isClickOfType( 34 | e: React.MouseEvent, 35 | types: string[] 36 | ) { 37 | let i = 0 38 | let j 39 | 40 | for (; i < types.length; i++) { 41 | const type = types[i] 42 | 43 | if (type.indexOf('|') > -1) { 44 | const ors = type.split('|').map((o) => o.trim()) 45 | let orSatisfied = true 46 | 47 | for (j = 0; j < ors.length; j++) { 48 | orSatisfied = isClickOfType(e, [ors[j]]) 49 | if (orSatisfied) break 50 | } 51 | 52 | if (!orSatisfied) return false 53 | } else { 54 | const props = type.split('+') 55 | 56 | for (j = 0; j < props.length; j++) { 57 | const prop = 58 | props[j] in CLICK_TYPES 59 | ? CLICK_TYPES[props[j] as keyof typeof CLICK_TYPES] 60 | : props[j] 61 | const [propName, propValue] = prop.split('=') 62 | 63 | if (propValue !== void 0) { 64 | if (String(e[propName as keyof typeof e]) !== String(propValue)) { 65 | return false 66 | } 67 | } else if (e[propName as keyof typeof e] === false) { 68 | return false 69 | } 70 | } 71 | } 72 | } 73 | 74 | return true 75 | } 76 | 77 | export const CLICK_TYPES = { 78 | single: 'detail=1', 79 | double: 'detail=2', 80 | triple: 'detail=3', 81 | left: 'button=0', 82 | middle: 'button=1', 83 | right: 'button=2', 84 | shift: 'shiftKey', 85 | control: 'ctrlKey', 86 | meta: 'metaKey', 87 | alt: 'altKey', 88 | } as const 89 | 90 | export default useClick 91 | -------------------------------------------------------------------------------- /packages/toggle/README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | useToggle() 5 |

6 |
7 | 8 |

9 | 10 | Bundlephobia 11 | 12 | 13 | Types 14 | 15 | 16 | Build status 17 | 18 | 19 | NPM Version 20 | 21 | 22 | MIT License 23 | 24 |

25 | 26 |
npm i @react-hook/toggle
27 |
28 | 29 | A React hook for toggling between two values 30 | 31 | ## Quick Start 32 | 33 | ```jsx harmony 34 | import useToggle from '@react-hook/toggle' 35 | 36 | const Component = (props) => { 37 | const [value, toggle] = useToggle(false, true) 38 | 39 | return ( 40 | 43 | ) 44 | } 45 | ``` 46 | 47 | ## API 48 | 49 | ### useToggle(off?, on?, defaultValue?) 50 | 51 | ```ts 52 | function useToggle( 53 | off: Off, 54 | on: On, 55 | defaultValue: Off | On = off 56 | ): [Off | On, () => void] 57 | ``` 58 | 59 | | Argument | Type | Default | Required? | Description | 60 | | ------------ | ---------- | ------- | --------- | ------------------------------------------------------------------ | 61 | | off | `Off` | `false` | No | The value of the toggle in its `off` state | 62 | | on | `On` | `true` | No | The value of the toggle in its `on` state | 63 | | defaultValue | `Off \| On` | `off` | No | The default value of the toggle, either the value of `off` or `on` | 64 | 65 | ## LICENSE 66 | 67 | MIT 68 | -------------------------------------------------------------------------------- /packages/previous/README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | usePrevious() 5 |

6 |
7 | 8 |

9 | 10 | Bundlephobia 11 | 12 | 13 | Types 14 | 15 | 16 | Build status 17 | 18 | 19 | NPM Version 20 | 21 | 22 | MIT License 23 | 24 |

25 | 26 |
npm i @react-hook/previous
27 |
28 | 29 | A React hook that stores a value from the previous render. 30 | 31 | ## Quick Start 32 | 33 | ```jsx harmony 34 | import * as React from 'react' 35 | import usePrevious from '@react-hook/previous' 36 | 37 | const useChanged = (onChange) => { 38 | const [status, setStatus] = React.useState('off') 39 | const prevStatus = usePrevious(status) 40 | 41 | React.useEffect(() => { 42 | if (status !== prevStatus) onChange() 43 | }, [status]) 44 | 45 | return [status, setStatus] 46 | } 47 | ``` 48 | 49 | ## API 50 | 51 | ### usePrevious() 52 | 53 | ```ts 54 | const usePrevious: ( 55 | value: T, 56 | initialValue?: T | undefined 57 | ) => T | undefined 58 | ``` 59 | 60 | | Argument | Type | Default | Required? | Description | 61 | | ------------ | ---------------- | ----------- | --------- | ------------------------------------------------------------------------- | 62 | | value | `T` | | Yes | The current value | 63 | | initialValue | `T \| undefined` | `undefined` | No | The value returned by the hook when the current value has not yet changed | 64 | 65 | ## LICENSE 66 | 67 | MIT 68 | -------------------------------------------------------------------------------- /packages/window-scroll/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import {renderHook, act} from '@testing-library/react-hooks' 2 | import useWindowScroll from './index' 3 | 4 | const renderWindowScroll = (...args): any => 5 | renderHook(() => useWindowScroll(...args)) 6 | const scrollEvent = document.createEvent('Event') 7 | scrollEvent.initEvent('scroll', true, true) 8 | const scrollTo = (value): void => { 9 | Object.defineProperty(window, 'scrollY', {value, configurable: true}) 10 | window.dispatchEvent(scrollEvent) 11 | } 12 | const resetScroll = (): void => { 13 | scrollTo(0) 14 | } 15 | 16 | const mockPerf = () => { 17 | // @ts-ignore 18 | const original = global?.performance 19 | let ts = (typeof performance !== 'undefined' ? performance : Date).now() 20 | 21 | return { 22 | install: () => { 23 | ts = Date.now() 24 | const perfNowStub = jest 25 | .spyOn(performance, 'now') 26 | .mockImplementation(() => ts) 27 | // @ts-ignore 28 | global.performance = { 29 | now: perfNowStub, 30 | } 31 | }, 32 | advanceBy: (amt: number) => (ts += amt), 33 | advanceTo: (t: number) => (ts = t), 34 | uninstall: () => { 35 | if (original) { 36 | //@ts-ignore 37 | global.performance = original 38 | } 39 | }, 40 | } 41 | } 42 | 43 | const perf = mockPerf() 44 | jest.useFakeTimers() 45 | 46 | describe('useWindowScroll()', () => { 47 | beforeEach(() => { 48 | perf.install() 49 | resetScroll() 50 | }) 51 | 52 | afterEach(perf.uninstall) 53 | 54 | it('should update scroll positon at 30fps', () => { 55 | const {result} = renderWindowScroll() 56 | act(() => scrollTo(1)) // fires leading 57 | expect(result.current).toBe(1) 58 | 59 | let i = 0 60 | 61 | for (; i < Math.ceil(1000 / 30); i++) { 62 | perf.advanceBy(1) 63 | expect(result.current).toBe(1) 64 | act(() => scrollTo(i)) 65 | } 66 | 67 | expect(result.current).toBe(33) 68 | }) 69 | 70 | it('should update scroll positon at 60fps', () => { 71 | const {result} = renderWindowScroll(60) 72 | act(() => scrollTo(1)) // fires leading 73 | expect(result.current).toBe(1) 74 | 75 | let i = 0 76 | 77 | for (; i < Math.ceil(1000 / 60); i++) { 78 | perf.advanceBy(1) 79 | expect(result.current).toBe(1) 80 | act(() => scrollTo(i)) 81 | } 82 | 83 | expect(result.current).toBe(16) 84 | 85 | for (; i < Math.ceil(1000 / 60) * 2; i++) { 86 | perf.advanceBy(1) 87 | expect(result.current).toBe(16) 88 | act(() => scrollTo(i)) 89 | } 90 | 91 | expect(result.current).toBe(33) 92 | }) 93 | }) 94 | -------------------------------------------------------------------------------- /packages/timeout/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import useLatest from '@react-hook/latest' 3 | 4 | /** 5 | * A hook that sets a `timedOut` boolean flag to true after `ms` have passed and the timeout has 6 | * been started. 7 | * 8 | * @param ms The amount of time to wait before setting `timedOut` to `true` 9 | * @returns The first element is a `timedOut` boolean that is `true` when 10 | * the timeout `ms` have been exceeded and `false` otherwise. The second element is a `start` method for 11 | * starting the timeout. The third element is a `reset` method for clearing the timeout. 12 | * 13 | * @example 14 | * const [timedOut, start, reset] = useTimeout(300) 15 | * // starts the timeout 16 | * start() 17 | * // clears the timeout 18 | * reset() 19 | * // logs the timedOut state 20 | * console.log(timedOut) 21 | */ 22 | export const useTimeout = (ms = 0): [boolean, () => void, () => void] => { 23 | const [timedOut, setTimedOut] = React.useState(false) 24 | const [start, reset] = useTimeoutCallback(() => setTimedOut(true), ms) 25 | return [timedOut, start, reset] 26 | } 27 | 28 | /** 29 | * A hook that executes a callback after a timeout is reached and the clock has 30 | * been started. 31 | * 32 | * @param callback The callback you want invoked when the timeout is exceeded 33 | * @param ms The amount of time to wait before firing the callback after `start()` is invoked. 34 | * When this is `0` the callback will never fire. 35 | * @param dependencies Dependencies array for your callback. Anytime your dependencies change, 36 | * the timeout will be reset to an idle state and any pending timeouts will not invoke. 37 | * @returns The first element is a `start` method for starting the timeout. The 38 | * second element is a `reset` method for clearing the timeout. 39 | * 40 | * @example 41 | * const [start, reset] = useTimeoutCallback(() => setState('copied'), 0, [text]) 42 | * // starts the timeout 43 | * start() 44 | * // clears the timeout 45 | * reset() 46 | */ 47 | export const useTimeoutCallback = ( 48 | callback: (...args: any[]) => any, 49 | ms = 0 50 | ): [() => void, () => void] => { 51 | const [timeout, setTimeoutId] = React.useState< 52 | ReturnType 53 | >() 54 | const storedCallback = useLatest(callback) 55 | // Clears existing timeouts when a new one set or the hook unmounts 56 | React.useEffect( 57 | () => () => { 58 | timeout && clearTimeout(timeout) 59 | }, 60 | [timeout, ms] 61 | ) 62 | 63 | return [ 64 | React.useCallback( 65 | () => setTimeoutId(setTimeout(storedCallback.current, ms)), 66 | [ms, storedCallback] 67 | ), 68 | React.useCallback(() => setTimeoutId(undefined), []), 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /packages/click/src/index.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import {render, screen} from '@testing-library/react' 3 | import userEvent from '@testing-library/user-event' 4 | import useClick from './index' 5 | 6 | describe('click', () => { 7 | it('should capture double click', () => { 8 | const callback = jest.fn() 9 | 10 | const Component = () => { 11 | const onClick = useClick('double', callback) 12 | return