Skip to content

Commit fedba4a

Browse files
committed
[IMPL] useDoubleClick hook
1 parent 25deca6 commit fedba4a

7 files changed

Lines changed: 113 additions & 3 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { useCallback, useState } from "react";
2+
import { useDoubleClick } from "../../../../../../packages/react-tools/src"
3+
4+
/**
5+
The component renders a tag p with __message__ state variable and a button which has an onClick handler. Its handler is __handler__, the returned function of _useDoubleClick_ hook that set __message__ value in different way by click type.
6+
*/
7+
export const UseDoubleClick = () => {
8+
const [message, setMessage] = useState("");
9+
const handler = useDoubleClick({
10+
doubleClick: useCallback(() => {
11+
setMessage("Double click executed.")
12+
}, []),
13+
singleClick: useCallback(() => {
14+
setMessage("Single click executed.")
15+
}, [])
16+
});
17+
18+
return <div>
19+
<p>Message: {message}</p>
20+
<button onClick={handler}>Click/DoubleClick</button>
21+
</div>
22+
}

apps/react-tools-demo/src/constants/components.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ export const COMPONENTS = [
6060
"useScrollIntoView",
6161
"useMouse",
6262
"useLongPress",
63-
"useBeforeUnload"
63+
"useBeforeUnload",
64+
"useDoubleClick"
6465
],
6566
//API DOM
6667
[
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# useDoubleClick
2+
Hook to handle double click event. Double clicking in react as well as with vanilla js, it is possible to manage it but it is not possible to have both managers on the same element. Thanks to this hook it is possible to do this, and it works with all events that can be associated with a user click (for example _mousedown_ but also _touchstart_).
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseDoubleClick = () => {
8+
const [message, setMessage] = useState("");
9+
const handler = useDoubleClick({
10+
doubleClick: useCallback(() => {
11+
setMessage("Double click executed.")
12+
}, []),
13+
singleClick: useCallback(() => {
14+
setMessage("Single click executed.")
15+
}, [])
16+
});
17+
18+
return <div>
19+
<p>Message: {message}</p>
20+
<button onClick={handler}>Click/DoubleClick</button>
21+
</div>
22+
}
23+
```
24+
25+
> The component renders a tag p with __message__ state variable and a button which has an onClick handler. Its handler is __handler__, the returned function of _useDoubleClick_ hook that set __message__ value in different way by click type.
26+
27+
28+
## API
29+
30+
```tsx
31+
useDoubleClick <T extends Element = Element, E extends Event = Event>(handler: ((evt: SyntheticEvent<T, E>) => Promise<void> | void) | { doubleClick: (evt: SyntheticEvent<T, E>) => Promise<void> | void, singleClick?: (evt: SyntheticEvent<T, E>) => Promise<void> | void, tolerance?: number })
32+
```
33+
34+
> ### Params
35+
>
36+
> - __handler__: _((evt: SyntheticEvent<T, E>) => Promise<void>|void)|Object_
37+
> - __handler.doubleClick__: _((evt: SyntheticEvent<T, E>) => Promise<void> | void)_
38+
callback executed on double click.
39+
> - __handler.singleClick?__: _((evt: SyntheticEvent<T, E>) => Promise<void> | void)_
40+
callback executed on single click.
41+
> - __handler.tolerance=150?__: _number_
42+
delay to execute __singleClick__ callback.
43+
>
44+
45+
> ### Returns
46+
>
47+
> __callback__
48+
> - __Union of__:
49+
> - _((evt: SyntheticEvent<T, E>) => Promise<void>_
50+
> - _void)_
51+
>

packages/react-tools/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
- [x] useScrollIntoView
6666
- [x] useMouse
6767
- [x] useLongPress
68+
- [ ] useDoubleClick
6869
- [x] useBeforeUnload
6970
- [ ] useOrientation
7071
- [ ] useKeysEvents

packages/react-tools/src/hooks/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,5 @@ export { useGeolocation } from './useGeolocation';
6565
export { useShare } from './useShare';
6666
export { useEyeDropper } from './useEyeDropper';
6767
export { useDialogBox } from './useDialogBox';
68-
export { useBeforeUnload } from './useBeforeUnload';
68+
export { useBeforeUnload } from './useBeforeUnload';
69+
export { useDoubleClick } from './useDoubleClick';
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { SyntheticEvent, useCallback, useRef } from "react";
2+
3+
/**
4+
* **`useDoubleClick`**: hook to handle double click event. Double clicking in react as well as with vanilla js, it is possible to manage it but it is not possible to have both managers on the same element. Thanks to this hook it is possible to do this, and it works with all events that can be associated with a user click (for example _mousedown_ but also _touchstart_).
5+
* @param {((evt: SyntheticEvent<T, E>) => Promise<void>|void)|Object} handler
6+
* @param {((evt: SyntheticEvent<T, E>) => Promise<void> | void)} handler.doubleClick - callback executed on double click.
7+
* @param {((evt: SyntheticEvent<T, E>) => Promise<void> | void)} [handler.singleClick] - callback executed on single click.
8+
* @param {number} [handler.tolerance=300] - delay to execute __singleClick__ callback.
9+
* @returns {((evt: SyntheticEvent<T, E>) => Promise<void> | void)} callback
10+
*/
11+
export const useDoubleClick = <T extends Element = Element, E extends Event = Event>(handler: ((evt: SyntheticEvent<T, E>) => Promise<void> | void) | { doubleClick: (evt: SyntheticEvent<T, E>) => Promise<void> | void, singleClick?: (evt: SyntheticEvent<T, E>) => Promise<void> | void, tolerance?: number }) => {
12+
const count = useRef(0);
13+
const tolerance = useRef("tolerance" in handler ? handler.tolerance : 300);
14+
const idTimeout = useRef<number>();
15+
16+
return useCallback((evt: SyntheticEvent<T, E>) => {
17+
"persist" in evt && evt.persist();
18+
!!idTimeout.current && clearTimeout(idTimeout.current);
19+
count.current++;
20+
if (count.current === 1 && "singleClick" in handler && !!handler.singleClick) {
21+
idTimeout.current = setTimeout(() => {
22+
handler.singleClick!(evt);
23+
count.current = 0;
24+
}, tolerance.current) as unknown as number;
25+
}
26+
if (count.current === 2) {
27+
"doubleClick" in handler
28+
? handler.doubleClick(evt)
29+
: handler(evt);
30+
count.current = 0;
31+
}
32+
}, [handler]);
33+
}

packages/react-tools/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ export {
8181
useShare,
8282
useEyeDropper,
8383
useDialogBox,
84-
useBeforeUnload
84+
useBeforeUnload,
85+
useDoubleClick
8586
} from './hooks'
8687

8788
export {

0 commit comments

Comments
 (0)