Skip to content

Commit 43d063a

Browse files
committed
feat: added more functions
1 parent d2d0ec0 commit 43d063a

12 files changed

+293
-6
lines changed

README.md

+15-5
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
<a href="https://www.npmjs.com/package/@vuehooks/core" target="__blank">
1212
<img src="https://img.shields.io/npm/v/@vuehooks/core?color=1abc9c" alt="NPM version" /></a>
1313
<a href="https://www.npmjs.com/package/@vuehooks" target="__blank"><img alt="NPM Downloads" src="https://img.shields.io/npm/dm/@vuehooks/core?color=34495e"/></a>
14-
<a href="https://github.com/datatorch/vuehooks" target="__blank"><img src="https://img.shields.io/github/last-commit/datatorch/vuehooks.svg?color=9b59b6" alt="GitHub last commit" /></a>
15-
<a href="https://github.com/datatorch/vuehooks/issues" target="__blank"><img src="https://img.shields.io/github/issues/datatorch/vuehooks.svg?color=3498db" alt="GitHub issues" /></a>
16-
<a href='https://coveralls.io/github/datatorch/vuehooks?branch=master'><img src='https://coveralls.io/repos/github/datatorch/vuehooks/badge.svg?branch=master' alt='Coverage Status' /></a>
17-
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/workflow/status/datatorch/vuehooks/Tests?color=2ecc71">
14+
<a href="https://github.com/jsbroks/vuehooks" target="__blank"><img src="https://img.shields.io/github/last-commit/jsbroks/vuehooks.svg?color=9b59b6" alt="GitHub last commit" /></a>
15+
<a href="https://github.com/jsbroks/vuehooks/issues" target="__blank"><img src="https://img.shields.io/github/issues/jsbroks/vuehooks.svg?color=3498db" alt="GitHub issues" /></a>
16+
<a href='https://coveralls.io/github/jsbroks/vuehooks?branch=master'><img src='https://coveralls.io/repos/github/jsbroks/vuehooks/badge.svg?branch=master' alt='Coverage Status' /></a>
17+
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/workflow/status/jsbroks/vuehooks/Tests?color=2ecc71">
1818
</p>
1919

2020
<br />
@@ -40,5 +40,15 @@
4040
asynchronous data.
4141
- [@vuehooks/router](/packages/router) - Hooks for using VueRouter easier.
4242

43-
<br />
43+
## Alternatives
44+
45+
| Features | VueHooks | VueUse | vue-composable | vue-use-web |
46+
| :-----------: | :------: | :-------: | :------------: | :---------: |
47+
| Functions | 33 | 53 | 51 | 24 |
48+
| Packages | 3 | 3 | 2 | 1 |
49+
| Stars | 2 | 1.3k | 260 | 450 |
50+
| Test Coverage | 42% | _unknown_ | 96% | _unknown_ |
4451

52+
_last updated 12/08/2020_
53+
54+
<br />

packages/core/README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,17 @@
3232
- `useTimeout` — change variables state after a timeout.
3333
- `useTimeoutFn` — call a function after a timeout.
3434
- **Browser**
35+
- `useLocalStorage` — manages a value in localStorage.
36+
- `useSessionStorage` — manages a value in sessionStorage.
37+
- `useClipboard` — copies text to clipboard.
38+
- `useFullscreen` — display an element fullscreen.
3539
- `useEventListener` — add event listeners to dom with ease.
3640
- `useEventListenerElement` — add event listeners to element with ease.
3741
- `useMediaQuery` — track the results of a media query programmatically.
3842
- **Sensors**
43+
- `useBattery` — tracks device batter state.
44+
- `useGeolocation` — tracks geo location state of user's device.
45+
- `useHover` — track if an element is being hovered over.
3946
- `useLongPress` — track long press gestures.
4047
- `usePrint` — track if user is printing.
4148
- `useMouse` — track mouse position.
@@ -47,8 +54,8 @@
4754
- `createGlobalState` — create state accessible in any component.
4855
- `useClamp` — track state of a clamped number.
4956
- `useCounter` — track state of a number.
50-
- `useHover` — track if an element is being hovered over.
5157
- `useToggle` — track state of a boolean.
58+
- `useMounted` — track if the component is mounted.
5259
- **Emitter**
5360
- `createEmitter` — create an emitter for an event bus.
5461
- `useEventOn` — add event listeners to emitters.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { renderHook } from '../../../testing/src'
2+
import { useLocalStorage } from '../useLocalStorage'
3+
4+
describe('useLocalStorage', () => {
5+
// it('sets default value for local storage', () => {
6+
// renderHook(() => {
7+
// const defaultValue = 'default'
8+
// const { supported, data } = useLocalStorage('key', defaultValue)
9+
10+
// expect(supported).toBeTruthy()
11+
// expect(data.value).toBe(defaultValue)
12+
// })
13+
// })
14+
15+
it('sets value for local storage with custom serializer', () => {
16+
renderHook(() => {
17+
const defaultValue = 'default2'
18+
const serializer = jest.fn().mockReturnValue(defaultValue)
19+
const deserializer = jest.fn().mockReturnValue(defaultValue)
20+
21+
const { data } = useLocalStorage('key', defaultValue, {
22+
raw: false,
23+
serializer,
24+
deserializer
25+
})
26+
27+
expect(serializer).toBeCalledTimes(1)
28+
expect(data.value).toBe(defaultValue)
29+
})
30+
})
31+
})

packages/core/src/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,10 @@ export { useTimeout } from './useTimeout'
77
export { useTimeoutFn } from './useTimeoutFn'
88
export { useLongPress } from './useLongPress'
99
export { useBus } from './useBus'
10+
export { useBattery } from './useBattery'
11+
export { useClipboard } from './useClipboard'
12+
export { useMounted } from './useMounted'
13+
export { useGeolocation } from './useGeolocation'
14+
export { useFullscreen } from './useFullscreen'
1015

1116
export { Emitter } from './utils/emitter'

packages/core/src/useBattery.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { onMounted, onUnmounted, ref } from '@vue/composition-api'
2+
3+
export interface BatteryState extends EventTarget {
4+
charging: boolean
5+
chargingTime: number
6+
dischargingTime: number
7+
level: number
8+
}
9+
10+
type NavigatorWithBattery = Navigator & {
11+
getBattery: () => Promise<BatteryState>
12+
}
13+
14+
const batteryEvents = [
15+
'chargingchange',
16+
'levelchange',
17+
'chargingtimechange',
18+
'dischargingtimechange'
19+
]
20+
21+
export function useBattery() {
22+
const supported = ref('getBattery' in navigator)
23+
const charging = ref(false)
24+
const chargingTime = ref(0)
25+
const dischargingTime = ref(0)
26+
const level = ref(0)
27+
28+
let battery: BatteryState | null = null
29+
30+
const updateBatteryInfo = () => {
31+
if (!battery) return
32+
charging.value = battery.charging
33+
chargingTime.value = battery.chargingTime
34+
dischargingTime.value = battery.dischargingTime
35+
level.value = battery.level
36+
}
37+
38+
const listenAll = () =>
39+
batteryEvents.forEach(e => battery?.addEventListener(e, updateBatteryInfo))
40+
41+
const unlistenAll = () =>
42+
batteryEvents.forEach(e =>
43+
battery?.removeEventListener(e, updateBatteryInfo)
44+
)
45+
46+
onMounted(async () => {
47+
if (!supported.value) return
48+
battery = await (navigator as NavigatorWithBattery).getBattery()
49+
updateBatteryInfo()
50+
listenAll()
51+
})
52+
53+
onUnmounted(() => unlistenAll())
54+
55+
return { supported, charging, chargingTime, dischargingTime, level }
56+
}

packages/core/src/useClipboard.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { ref } from '@vue/composition-api'
2+
import { useMounted } from './useMounted'
3+
import { MaybeRef } from './utils/typings'
4+
5+
const copyText = (text: string) => {
6+
const input = document.createElement('textarea')
7+
input.innerHTML = text
8+
document.body.appendChild(input)
9+
input.select()
10+
var result = document.execCommand('copy')
11+
document.body.removeChild(input)
12+
return result
13+
}
14+
15+
export function useClipboard(text?: MaybeRef<string>) {
16+
const textRef = ref(text ?? '')
17+
const isMounted = useMounted()
18+
19+
const supported = ref('clipboard' in navigator)
20+
const copy = (text?: string) => {
21+
if (!isMounted) return
22+
if (text) textRef.value = text
23+
copyText(textRef.value)
24+
}
25+
26+
return { supported, copy, text }
27+
}

packages/core/src/useFullscreen.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Ref, ref } from '@vue/composition-api'
2+
import { useToggle } from './useToggle'
3+
4+
export function useFullscreen(target: Ref<HTMLElement> = ref(document.body)) {
5+
const { on: isFullscreen, set } = useToggle(false)
6+
const exit = () => {
7+
if (document.fullscreenElement) document.exitFullscreen()
8+
set(false)
9+
}
10+
11+
const enter = () => {
12+
exit()
13+
if (!target.value) return
14+
target.value.requestFullscreen().then(() => set(true))
15+
}
16+
17+
return { isFullscreen, enter, exit }
18+
}

packages/core/src/useGeolocation.ts

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { onMounted, onUnmounted, ref, Ref } from '@vue/composition-api'
2+
3+
export function useGeolocation(options?: PositionOptions) {
4+
const timestamp: Ref<number | null> = ref(null)
5+
const error: Ref<PositionError | null> = ref(null)
6+
const coords: Ref<Coordinates> = ref({
7+
accuracy: 0,
8+
latitude: 0,
9+
longitude: 0,
10+
altitude: null,
11+
altitudeAccuracy: null,
12+
heading: null,
13+
speed: null
14+
})
15+
16+
const update = (position: Position) => {
17+
timestamp.value = position.timestamp
18+
coords.value = position.coords
19+
error.value = null
20+
}
21+
22+
let watchNumber: number | null = null
23+
24+
onMounted(() => {
25+
if (!('geolocation' in navigator)) return
26+
watchNumber = navigator.geolocation.watchPosition(
27+
update,
28+
err => (error.value = err),
29+
options
30+
)
31+
})
32+
33+
onUnmounted(() => {
34+
if (watchNumber === null) return
35+
navigator.geolocation.clearWatch(watchNumber)
36+
})
37+
38+
return { coords, timestamp, error }
39+
}

packages/core/src/useLocalStorage.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { StorageOptions, useStorage } from './useStorage'
2+
import { hasWindow } from './utils'
3+
4+
import { MaybeRef } from './utils/typings'
5+
6+
export function useLocalStorage<T>(
7+
key: string,
8+
initialData?: MaybeRef<T>,
9+
options?: StorageOptions<T>
10+
) {
11+
const supported = hasWindow() && !!window.localStorage
12+
const storage = useStorage(localStorage, key, initialData, options)
13+
return { supported, ...storage }
14+
}

packages/core/src/useMounted.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { onMounted, onUnmounted } from '@vue/composition-api'
2+
import { useToggle } from './useToggle'
3+
4+
export function useMounted() {
5+
const { on, set } = useToggle(false)
6+
onMounted(() => set(true))
7+
onUnmounted(() => set(false))
8+
return on
9+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { StorageOptions, useStorage } from './useStorage'
2+
import { hasWindow } from './utils'
3+
4+
import { MaybeRef } from './utils/typings'
5+
6+
export function useSessionStorage<T>(
7+
key: string,
8+
initialData?: MaybeRef<T>,
9+
options?: StorageOptions<T>
10+
) {
11+
const supported = hasWindow() && !!window.sessionStorage
12+
const storage = useStorage(sessionStorage, key, initialData, options)
13+
return { supported, ...storage }
14+
}

packages/core/src/useStorage.ts

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { onMounted, Ref, ref, watch } from '@vue/composition-api'
2+
import { useWindowEvent } from './useWindowEvent'
3+
import { hasWindow, noop } from './utils'
4+
import { MaybeRef } from './utils/typings'
5+
6+
type SerializerFunction<T> = (v: T) => string
7+
type DeserializerFunction<T> = (v: string) => T
8+
9+
export type StorageOptions<T> =
10+
| { raw: true }
11+
| {
12+
raw?: false
13+
serializer: SerializerFunction<T>
14+
deserializer: DeserializerFunction<T>
15+
}
16+
17+
export function useStorage<T>(
18+
storage: Storage,
19+
key: string,
20+
initialData?: MaybeRef<T>,
21+
options?: StorageOptions<T>
22+
) {
23+
const data = ref(initialData) as Ref<T | undefined>
24+
const initData = data.value
25+
26+
const serializer: SerializerFunction<T> = options
27+
? options.raw
28+
? (noop as any)
29+
: options.serializer
30+
: JSON.stringify
31+
32+
const deserializer: DeserializerFunction<T> = options
33+
? options.raw
34+
? (noop as any)
35+
: options.deserializer
36+
: JSON.parse
37+
38+
const read = () => {
39+
const item = storage.getItem(key)
40+
if (item == null && initData !== undefined) write()
41+
else data.value = item ? deserializer(item) : initData
42+
}
43+
44+
const write = () => {
45+
const v = data.value
46+
if (v == null) storage.removeItem(key)
47+
else storage.setItem(key, serializer(v))
48+
}
49+
50+
if (hasWindow()) read()
51+
52+
onMounted(read)
53+
useWindowEvent('storage', read)
54+
watch(data, write, { deep: true })
55+
56+
return { data }
57+
}

0 commit comments

Comments
 (0)