Skip to content

Commit 8ad256a

Browse files
committed
[IMPL] useSwipe hook
1 parent a6a96f0 commit 8ad256a

15 files changed

Lines changed: 298 additions & 37 deletions

File tree

apps/react-tools-demo/src/components/hooks/useEventDispatcher/UseEventDispatcher.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const UseEventDispatcher = () => {
2222
useEventListener({
2323
type: "demo",
2424
element: inputRef,
25-
listener: (evt) => {
25+
listener: (evt: CustomEvent) => {
2626
setState((evt as CustomEvent<string>).detail);
2727
},
2828
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useRef } from "react";
2+
import { useSwipe } from "../../../../../../packages/react-tools/src";
3+
4+
/**
5+
The component uses _useSwipe_ hook to handle swipe event on a div element.
6+
*/
7+
export const UseSwipe = () => {
8+
const divRef = useRef<HTMLDivElement>(null);
9+
useSwipe({
10+
target: divRef,
11+
onSwipe(e, direction, delta) {
12+
console.log(direction)
13+
delta.x >=0 && (divRef.current!.style.left = delta.x + "px");
14+
},
15+
onSwipeEnd(e, direction, delta) {
16+
divRef.current!.style.left = `${delta.x>330 ? "400" : "0"}px`
17+
},
18+
});
19+
const reset = () => {
20+
divRef.current!.style.left = "0";
21+
}
22+
return <div style={{ position: "relative", display: "flex", alignItems: "center", justifyContent: "center", width: 400, height: 100, border: "1px solid lightgray", overflow: "hidden", margin: "0 auto" }}>
23+
<button onClick={reset}>RESET</button>
24+
<div ref={divRef} style={{ position: "absolute", backgroundColor: "rgb(73 84 104)", zIndex: 100, inset: 0 }}><p>SWIPE</p></div>
25+
</div>
26+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export const COMPONENTS = [
7171
"usePinchZoom",
7272
"usePointerLock",
7373
"useContextMenu",
74+
"useSwipe"
7475
],
7576
//API DOM
7677
[

apps/react-tools-demo/src/markdown/useDisplayMedia.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@ Hook to capture the contents of a display.
55

66
```tsx
77
export const UseDisplayMedia = () => {
8-
const start = useDisplayMedia();
8+
const [stream, start, stop] = useDisplayMedia();
99
const ref = useRef<HTMLVideoElement>(null);
1010

1111
const init = async () => {
12-
const stream = await start();
13-
ref.current && (ref.current.srcObject = stream);
12+
await start();
1413
}
1514

16-
return <div style={{ display: "grid", gridTemplateRows: "auto auto", justifyContent: "center", gap: 50, maxHeight: 350, overflow: "auto" }}>
15+
if (ref.current) {
16+
stream
17+
? (ref.current.srcObject = stream)
18+
: (ref.current.srcObject = null);
19+
}
20+
21+
return <div style={{ display: "grid", gridTemplateRows: "auto auto auto", justifyContent: "center", gap: 20, maxHeight: 350, overflow: "auto" }}>
1722
<button onClick={init}>Start</button>
18-
<video autoPlay controls ref={ref}/>
23+
<button onClick={stop}>Stop</button>
24+
<video autoPlay width={300} height={200} ref={ref}/>
1925
</div>
2026
}
2127

@@ -27,7 +33,7 @@ export const UseDisplayMedia = () => {
2733
## API
2834

2935
```tsx
30-
useDisplayMedia()
36+
useDisplayMedia(): [MediaStream | undefined, (options?: TDisplayMediaStreamOptions) => Promise<void>, () => void]
3137
```
3238
3339
> ### Params
@@ -37,8 +43,13 @@ useDisplayMedia()
3743
3844
> ### Returns
3945
>
40-
> __start__: function to start capture.
41-
> - __Union of__:
42-
> - _(options: DisplayMediaStreamOptions_
43-
> - _undefined) => Promise<MediaStream>_
46+
> __result__: __Union of__:
47+
- __Array__:
48+
- _MediaStream|undefined_
49+
- _(options: TDisplayMediaStreamOption_
50+
- _undefined) => Promise<void>, ()=>void]_
51+
> Array containing:
52+
> - first element: the captured stream.
53+
> - second element: function that starts capture.
54+
> - third element: function that stops capture.
4455
>

apps/react-tools-demo/src/markdown/useEventDispatcher.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const UseEventDispatcher = () => {
1717
useEventListener({
1818
type: "demo",
1919
element: inputRef,
20-
listener: (evt) => {
20+
listener: (evt: CustomEvent) => {
2121
setState((evt as CustomEvent<string>).detail);
2222
},
2323
});

apps/react-tools-demo/src/markdown/useEventListener.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,17 @@ export { UseEventListener };
3535
## API
3636

3737
```tsx
38-
useEventListener<T extends Event | CustomEvent>({ type, listener, element = window, listenerOpts, effectType = "normal" }: { type: string, listener: ((evt: T) => unknown | Promise<unknown>), element?: RefObject<HTMLElement> | Window, listenerOpts?: boolean | AddEventListenerOptions, effectType?: "normal" | "layout" }): (() => void)
38+
useEventListener<T extends keyof WindowEventMap>({ type, listener, element = window, listenerOpts, effectType = "normal" }: { type: T|(T[]), listener: ((evt: WindowEventMap[T]) => unknown | Promise<unknown>), element?: RefObject<Element> | Element | Window, listenerOpts?: boolean | AddEventListenerOptions, effectType?: "normal" | "layout" }): (() => void)
3939
```
4040
4141
> ### Params
4242
>
4343
> - __options__: _Object_
44-
> - __options.type__: _string_
45-
event type.
44+
> - __options.type__: _keyof WindowEventMap|(keyof WindowEventMap)[]_
45+
event or events type.
4646
> - __options.listener__: _(evt: Event | CustomEvent) => void_
4747
listener to be executed on specified event.
48-
> - __options.element=window?__: _RefObject<HTMLElement> | Window_
48+
> - __options.element=window?__: _RefObject<Element> | Element | Window_
4949
element on which attaching eventListener.
5050
> - __options.listenerOpts?__: _boolean | AddEventListenerOptions_
5151
options for listener.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# useSwipe
2+
Hook to handle swipe gesture.
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseSwipe = () => {
8+
return <div style={{ position: "relative", width: 400, height: 100, border: "1px solid lightgray" }}>
9+
<div style={{position: "absolute", backgroundColor: "lightgray", width: 400, height: 100}}></div>
10+
</div>
11+
}
12+
```
13+
14+
> The component uses _useSwipe_ hook to handle swipe event on a div element.
15+
16+
17+
## API
18+
19+
```tsx
20+
useSwipe({ target, onSwipeStart, onSwipe, onSwipeEnd, options }: UseSwipeProps): UseSwipeResult
21+
```
22+
23+
> ### Params
24+
>
25+
> - __param__: _UseSwipeProps_
26+
object
27+
> - __param.target__: _RefObject<Element>|Element_
28+
element on which attach swipe event.
29+
> - __param.onSwipeStart?__: _(e: PointerEvent) => void_
30+
callback that will be executed when swipe starts.
31+
> - __param.onSwipe?__: _(e: PointerEvent, direction: SwipeDirection, delta: {x: number, y: number}) => void_
32+
callback that will be executed when swipe moves.
33+
> - __param.onSwipeEnd?__: _(e: PointerEvent, direction: SwipeDirection, delta: {x: number, y: number}) => void_
34+
callback that will be executed when swipe ends.
35+
> - __param.options?__: _Object_
36+
object to set option for listener.
37+
> - __param.options.passive=true?__: _boolean_
38+
if true, handler callback never calls _preventDefault_ method.
39+
> - __param.options.threshold=0?__: _threshold_
40+
a threshold value for swipe event.
41+
>
42+
43+
> ### Returns
44+
>
45+
> __- callback that stops listener.__
46+
> - _UseSwipeResult_
47+
>

packages/react-tools/README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
- [x] useMergedRef
4545
- [x] useLazyRef
4646
- [x] useId
47-
- [ ] usePromise (https://github.com/vigzmv/react-promise-suspense/blob/master/lib/index.ts)
47+
- [ ] usePromise (https://github.com/vigzmv/react-promise-suspense/blob/master/lib/index.ts)
4848

4949
- __EVENTS__
5050
- [x] useEvents
@@ -72,6 +72,7 @@
7272
- [x] usePinchZoom
7373
- [x] usePointerLock
7474
- [x] useContextMenu
75+
- [x] useSwipe
7576
- [ ] useInfiniteScroll
7677
- [ ] useDragAndDrop (check for mobile usage)
7778

@@ -118,7 +119,6 @@
118119
- [x] usePermission
119120
- [x] useMediaDevices
120121
- [x] useDisplayMedia
121-
- [ ] usePointerTouchSwipe (https://vueuse.org/core/usePointerSwipe/ https://vueuse.org/core/useSwipe/)
122122
- [ ] useWebWorker (https://vueuse.org/core/useWebWorker/)
123123
- [ ] useWebWorkerFn (https://vueuse.org/core/useWebWorkerFn/)
124124
- [ ] useIndexedDB
@@ -139,12 +139,22 @@
139139
- [ ] fetch-client (???ARTS-like)
140140
- [ ] defaultSerializer (https://github.com/vueuse/vueuse/blob/main/packages/core/useBase64/serialization.ts)
141141
- [ ] useBase64 (https://vueuse.org/core/useBase64/)
142+
- [ ] removePropertiesFromArrayObject
143+
- [ ] uniqueElements
144+
- [ ] getKeyObjectFromValue
145+
- [ ] dottedStringToObject
146+
- [ ] mergeObjects
147+
- [ ] alphanumericCompare
148+
- [ ] camelCase
149+
- [ ] pascalCase
150+
- [ ] kebabCase
151+
- [ ] snakeCase
142152

143153
- __COMPONENT__
144154
- [ ] Show component to render or not a component by a condition. Props: when, fallback, keyed. Keyed is a boolean and needs to avoid rerenders children when it is a function.
145155
- [ ] Switch and Match components with fallback and when props
146-
- [ ] For/Each: A referentially keyed loop with efficient updating of only changed items. The callback takes the current item as the first argument (ref https://javascript.plainenglish.io/react-each-and-of-pattern-b00aa4305089 )
147-
- [ ] Index: Non-keyed list iteration (rendered nodes are keyed to an array index). This is useful when there is no conceptual key, like if the data consists of primitives and it is the index that is fixed rather than the value.
156+
- [ ] For/Each: A referentially keyed loop with efficient updating of only changed items. The callback takes the current item as the first argument (ref https://javascript.plainenglish.io/react-each-and-of-pattern-b00aa4305089)
157+
- [ ] Index: Non-keyed list iteration (rendered nodes are keyed to an array index). This is useful when there is no conceptual key, like if the data consists of primitives and it is the index that is fixed rather than the value.
148158
- [ ] RestrictedRoute (maybe)
149159
- [ ] ErrorBoundary (?? error event listener)
150160
- [ ] Suspense: Suspence compontent react-like for async component

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,5 @@ export { useWebSocket } from './useWebSocket';
9595
export { useContextMenu } from './useContextMenu';
9696
export { useMediaDevices } from './useMediaDevices';
9797
export { usePermission } from './usePermission';
98-
export { useDisplayMedia } from './useDisplayMedia';
98+
export { useDisplayMedia } from './useDisplayMedia';
99+
export { useSwipe } from './useSwipe';

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

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,40 @@ import { useMemoizedFunction } from ".";
44
/**
55
* __`useEventListener`__: Hook to simplify add and remove EventListener use. It's persist during rerendering and automatically remove eventlistener on unMount component lifecycle.
66
* @param {Object} options
7-
* @param {string} options.type - event type.
7+
* @param {keyof WindowEventMap|(keyof WindowEventMap)[]} options.type - event or events type.
88
* @param {(evt: Event | CustomEvent) => void} options.listener - listener to be executed on specified event.
9-
* @param {RefObject<HTMLElement> | Window} [options.element=window] - element on which attaching eventListener.
9+
* @param {RefObject<Element> | Element | Window} [options.element=window] - element on which attaching eventListener.
1010
* @param {boolean | AddEventListenerOptions} [options.listenerOpts] - options for listener.
1111
* @param {"normal"|"layout"} [options.effectType="normal"] - option to set which hook is used to attach event listener.
1212
* @returns {()=>void} remove - used to manually remove the eventListener, otherwise is removed when component is unmounted.
1313
*/
14-
export const useEventListener = <T extends Event | CustomEvent>({ type, listener, element = window, listenerOpts, effectType = "normal" }: { type: string, listener: ((evt: T) => unknown | Promise<unknown>), element?: RefObject<HTMLElement> | Window, listenerOpts?: boolean | AddEventListenerOptions, effectType?: "normal" | "layout" }): (() => void) => {
14+
function useEventListener<T extends keyof DocumentEventMap>({ type, listener, element, listenerOpts, effectType }: { type: T | (T[]), listener: ((evt: DocumentEventMap[T]) => unknown | Promise<unknown>), element?: RefObject<Element> | Element | Window, listenerOpts?: boolean | AddEventListenerOptions, effectType?: "normal" | "layout" }): (() => void);
15+
function useEventListener<T extends keyof HTMLElementEventMap>({ type, listener, element, listenerOpts, effectType }: { type: T | (T[]), listener: ((evt: HTMLElementEventMap[T]) => unknown | Promise<unknown>), element?: RefObject<Element> | Element | Window, listenerOpts?: boolean | AddEventListenerOptions, effectType?: "normal" | "layout" }): (() => void);
16+
function useEventListener<T extends string, E extends Event|CustomEvent>({ type, listener, element, listenerOpts, effectType }: { type: T | (T[]), listener: ((evt: E) => unknown | Promise<unknown>), element?: RefObject<Element> | Element | Window, listenerOpts?: boolean | AddEventListenerOptions, effectType?: "normal" | "layout" }): (() => void);
17+
function useEventListener<T extends keyof WindowEventMap>({ type, listener, element = window, listenerOpts, effectType = "normal" }: { type: T|(T[]), listener: ((evt: WindowEventMap[T]) => unknown | Promise<unknown>), element?: RefObject<Element> | Element | Window, listenerOpts?: boolean | AddEventListenerOptions, effectType?: "normal" | "layout" }): (() => void) {
1518
const optsMemoized = useRef<typeof listenerOpts>(listenerOpts);
16-
const elementReference = useRef<HTMLElement | Window | null>();
19+
const elementReference = useRef<Element | Window | null>();
1720
const effect = effectType === "layout" ? useLayoutEffect : useEffect;
18-
21+
const types = useRef(Array.isArray(type) ? type : [type]);
1922
effect(() => {
2023
const opts = optsMemoized.current;
2124
elementReference.current = Reflect.has(element, "current")
22-
? (element as RefObject<HTMLElement>)?.current !== null
23-
? (element as RefObject<HTMLElement>).current
25+
? (element as RefObject<Element>)?.current !== null
26+
? (element as RefObject<Element>).current
2427
: null
2528
: element as Window
2629

27-
elementReference.current && (elementReference.current as HTMLElement | Window).addEventListener(type, listener as EventListenerOrEventListenerObject, opts);
30+
elementReference.current && types.current.forEach(type => (elementReference.current as Element | Window).addEventListener(type, listener as EventListenerOrEventListenerObject, opts));
2831
return () => {
29-
elementReference.current && (elementReference.current as HTMLElement | Window).removeEventListener(type, listener as EventListenerOrEventListenerObject, opts);
32+
elementReference.current && types.current.forEach(type => (elementReference.current as Element | Window).removeEventListener(type, listener as EventListenerOrEventListenerObject, opts));
3033
}
31-
}, [element, type, listener]);
34+
}, [element, listener]);
3235

3336
const remove = useMemoizedFunction(() => {
34-
elementReference.current && (elementReference.current as HTMLElement | Window).removeEventListener(type, listener as EventListenerOrEventListenerObject, optsMemoized.current);
37+
elementReference.current && types.current.forEach(type => (elementReference.current as Element | Window).removeEventListener(type, listener as EventListenerOrEventListenerObject, optsMemoized.current));
3538
});
3639

3740
return remove;
38-
}
41+
}
42+
43+
export { useEventListener };

0 commit comments

Comments
 (0)