Skip to content

Commit 87cc908

Browse files
committed
[IMPL] useDeviceMotion + useDeviceOrientation + useVibrate hooks
1 parent 3004d3b commit 87cc908

16 files changed

Lines changed: 406 additions & 6 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Fragment } from "react";
2+
import { useDeviceMotion } from "../../../../../../packages/react-tools/src"
3+
4+
/**
5+
The component uses _useDeviceMotion_ hook to show device motion details.
6+
*/
7+
export const UseDeviceMotion = () => {
8+
const data = useDeviceMotion();
9+
10+
return <div style={{ textAlign: "center" }}>
11+
{
12+
Object.keys(data).map(key => {
13+
const value = data[key as keyof typeof data];
14+
return value !== null && typeof value === "object" && Reflect.get(value, "x")
15+
? <Fragment key={key}>
16+
<p>{key}:</p>
17+
<ul>
18+
<li>x: {Reflect.get(data[key as keyof typeof data] as object, "x")}</li>
19+
<li>y: {Reflect.get(data[key as keyof typeof data] as object, "y")}</li>
20+
<li>z: {Reflect.get(data[key as keyof typeof data] as object, "z")}</li>
21+
</ul>
22+
</Fragment>
23+
: value !== null && typeof value === "object" && Reflect.get(value, "alpha")
24+
? <Fragment key={key}>
25+
<p>{key}:</p>
26+
<ul>
27+
<li>alpha: {Reflect.get(data[key as keyof typeof data] as object, "alpha")}</li>
28+
<li>beta: {Reflect.get(data[key as keyof typeof data] as object, "beta")}</li>
29+
<li>gamma: {Reflect.get(data[key as keyof typeof data] as object, "gamma")}</li>
30+
</ul>
31+
</Fragment>
32+
: <p key={key}>{key}: {value !== null ? value.toString(): ""}</p>
33+
})
34+
}
35+
</div>
36+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useDeviceOrientation } from "../../../../../../packages/react-tools/src"
2+
3+
/**
4+
The component uses _useDeviceOrientation_ hook to show device motion details.
5+
*/
6+
export const UseDeviceOrientation = () => {
7+
const data = useDeviceOrientation();
8+
9+
return <div style={{ textAlign: "center" }}>
10+
{
11+
Object.keys(data).map(key => {
12+
const value = data[key as keyof typeof data];
13+
return <p key={key}>{key}: {value !== null ? value.toString(): ""}</p>
14+
})
15+
}
16+
</div>
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useVibrate } from "../../../../../../packages/react-tools/src"
2+
3+
/**
4+
The component uses _useVibrate_ hook to know if vibrate is supported and to activate/deactivate vibration with two buttons.
5+
*/
6+
export const UseVibrate = () => {
7+
const { isSupported, vibrate, cancel } = useVibrate();
8+
9+
return <div>
10+
<p>Supported: {isSupported.toString()}</p>
11+
<button type="button" onClick={() => vibrate([200, 30, 200, 30, 200])}>Vibrate</button>
12+
<button type="button" onClick={cancel}>Cancel</button>
13+
</div>
14+
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ export const COMPONENTS = [
9090
"useGeolocation",
9191
"useShare",
9292
"useEyeDropper",
93-
"useDialogBox"
93+
"useDialogBox",
94+
"useDeviceMotion",
95+
"useDeviceOrientation",
96+
"useVibrate"
9497
]
9598
],
9699
//UTILS
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# useDeviceMotion
2+
Hook to handle [device motion](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicemotion_event).
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseDeviceMotion = () => {
8+
const data = useDeviceMotion();
9+
10+
return <div style={{ textAlign: "center" }}>
11+
{
12+
Object.keys(data).map(key => {
13+
const value = data[key as keyof typeof data];
14+
return value !== null && typeof value === "object" && Reflect.get(value, "x")
15+
? <Fragment key={key}>
16+
<p>{key}:</p>
17+
<ul>
18+
<li>x: {Reflect.get(data[key as keyof typeof data] as object, "x")}</li>
19+
<li>y: {Reflect.get(data[key as keyof typeof data] as object, "y")}</li>
20+
<li>z: {Reflect.get(data[key as keyof typeof data] as object, "z")}</li>
21+
</ul>
22+
</Fragment>
23+
: value !== null && typeof value === "object" && Reflect.get(value, "alpha")
24+
? <Fragment key={key}>
25+
<p>{key}:</p>
26+
<ul>
27+
<li>alpha: {Reflect.get(data[key as keyof typeof data] as object, "alpha")}</li>
28+
<li>beta: {Reflect.get(data[key as keyof typeof data] as object, "beta")}</li>
29+
<li>gamma: {Reflect.get(data[key as keyof typeof data] as object, "gamma")}</li>
30+
</ul>
31+
</Fragment>
32+
: <p key={key}>{key}: {value !== null ? value.toString(): ""}</p>
33+
})
34+
}
35+
</div>
36+
}
37+
```
38+
39+
> The component uses _useDeviceMotion_ hook to show device motion details.
40+
41+
42+
## API
43+
44+
```tsx
45+
useDeviceMotion(): DeviceMotionProps
46+
```
47+
48+
> ### Params
49+
>
50+
>
51+
>
52+
53+
> ### Returns
54+
>
55+
> __props__: device motion properties.
56+
> - _DeviceMotionProps_
57+
>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# useDeviceOrientation
2+
Hook to handle [device orientation](https://developer.mozilla.org/en-US/docs/Web/API/Window/deviceorientation_event).
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseDeviceOrientation = () => {
8+
const data = useDeviceOrientation();
9+
10+
return <div style={{ textAlign: "center" }}>
11+
{
12+
Object.keys(data).map(key => {
13+
const value = data[key as keyof typeof data];
14+
return <p key={key}>{key}: {value !== null ? value.toString(): ""}</p>
15+
})
16+
}
17+
</div>
18+
}
19+
```
20+
21+
> The component uses _useDeviceOrientation_ hook to show device motion details.
22+
23+
24+
## API
25+
26+
```tsx
27+
useDeviceOrientation(): DeviceOrientationProps
28+
```
29+
30+
> ### Params
31+
>
32+
>
33+
>
34+
35+
> ### Returns
36+
>
37+
> __props__: device orientation properties.
38+
> - _DeviceOrientationProps_
39+
>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# useVibrate
2+
Hook to use device vibration hardware.
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseVibrate = () => {
8+
const { isSupported, vibrate, cancel } = useVibrate();
9+
10+
return <div>
11+
<p>Supported: {isSupported.toString()}</p>
12+
<button type="button" onClick={() => vibrate([200, 30, 200, 30, 200])}>Vibrate</button>
13+
<button type="button" onClick={cancel}>Cancel</button>
14+
</div>
15+
}
16+
```
17+
18+
> The component uses _useVibrate_ hook to know if vibrate is supported and to activate/deactivate vibration with two buttons.
19+
20+
21+
## API
22+
23+
```tsx
24+
useVibrate():{isSupported: boolean, vibrate: ((pattern: number | number[]) => void), cancel: ()=>void}
25+
```
26+
27+
> ### Params
28+
>
29+
>
30+
>
31+
32+
> ### Returns
33+
>
34+
> __result__: __Union of__:
35+
- __Object__:
36+
- __isSupported__ : _boolean_
37+
- __vibrate__ : _((pattern: numbe_
38+
- _number[]) => void), cancel: ()=>void}_
39+
> - _isSupported_: boolean to detect if vibration is supported or not.
40+
> - _vibrate_: function to activate device vibration hardware.
41+
> - _cancel_: function to stop vibration running.
42+
>

packages/react-tools/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,9 @@
9393
- [x] useShare
9494
- [x] useEyeDropper
9595
- [x] useDialogBox
96-
- [ ] useDeviceMotion (??? mobile)
97-
- [ ] useVibrate (??? mobile)
96+
- [x] useDeviceMotion
97+
- [x] useDeviceOrientatio
98+
- [x] useVibrate (??? mobile)
9899
- [ ] useSpeech (https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance#examples)
99100
- [ ] useAudio (???)
100101
- [ ] useVideo (???)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,6 @@ export { useMergedRef } from './useMergedRef';
7272
export { useHotKeys } from './useHotKeys';
7373
export { usePinchZoom } from './usePinchZoom';
7474
export { useLogger } from './useLogger';
75+
export { useDeviceMotion } from './useDeviceMotion';
76+
export { useDeviceOrientation } from './useDeviceOrientation';
77+
export { useVibrate } from './useVibrate';
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { useCallback, useRef } from "react";
2+
import { useSyncExternalStore } from "."
3+
import { DeviceMotionProps } from "../models";
4+
5+
const listeners = new Set<(evt:DeviceMotionEvent) => void>();
6+
const handler = (evt: DeviceMotionEvent) => listeners.forEach(l => l(evt));
7+
8+
/**
9+
* **`useDeviceMotion`**: Hook to handle [device motion](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicemotion_event).
10+
* @returns {DeviceMotionProps} props - device motion properties.
11+
*/
12+
export const useDeviceMotion = (): DeviceMotionProps => {
13+
const prev = useRef<DeviceMotionProps>({
14+
isSupported: "DeviceMotionEvent" in window,
15+
acceleration: null,
16+
accelerationIncludingGravity: null,
17+
interval: null,
18+
rotationRate: null
19+
});
20+
const curr = useRef<DeviceMotionProps>();
21+
22+
23+
return useSyncExternalStore(
24+
useCallback(notif => {
25+
const listener = (evt: DeviceMotionEvent) => {
26+
const { acceleration, accelerationIncludingGravity, interval, rotationRate } = evt;
27+
curr.current = {
28+
isSupported: true,
29+
acceleration,
30+
accelerationIncludingGravity,
31+
rotationRate,
32+
interval
33+
};
34+
notif();
35+
};
36+
if ("DeviceMotionEvent" in window) {
37+
listeners.add(listener);
38+
listeners.size === 1 && addEventListener("devicemotion", handler);
39+
}
40+
return () => {
41+
if ("DeviceMotionEvent" in window) {
42+
listeners.delete(listener);
43+
listeners.size === 0 && window.removeEventListener("devicemotion", handler)
44+
}
45+
}
46+
}, []),
47+
useCallback(() => {
48+
if (
49+
curr.current &&
50+
(
51+
prev.current.isSupported !== curr.current.isSupported ||
52+
prev.current.acceleration?.x !== curr.current.acceleration?.x ||
53+
prev.current.acceleration?.y !== curr.current.acceleration?.y ||
54+
prev.current.acceleration?.z !== curr.current.acceleration?.z ||
55+
prev.current.accelerationIncludingGravity?.x !== curr.current.accelerationIncludingGravity?.x ||
56+
prev.current.accelerationIncludingGravity?.y !== curr.current.accelerationIncludingGravity?.y ||
57+
prev.current.accelerationIncludingGravity?.z !== curr.current.accelerationIncludingGravity?.z ||
58+
prev.current.rotationRate?.alpha !== curr.current.rotationRate?.alpha ||
59+
prev.current.rotationRate?.beta !== curr.current.rotationRate?.beta ||
60+
prev.current.rotationRate?.gamma !== curr.current.rotationRate?.gamma ||
61+
prev.current.interval !== curr.current.interval
62+
)
63+
) {
64+
prev.current = curr.current;
65+
}
66+
return prev.current;
67+
}, [])
68+
);
69+
}

0 commit comments

Comments
 (0)