-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from chenz24/feat/hooks
Feat/hooks
- Loading branch information
Showing
14 changed files
with
301 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"name": "@kubed/hooks", | ||
"version": "0.0.1", | ||
"main": "cjs/index.js", | ||
"module": "esm/index.js", | ||
"browser": "lib/index.umd.js", | ||
"types": "lib/index.d.ts", | ||
"license": "MIT", | ||
"sideEffects": false, | ||
"homepage": "https://github.com/kubesphere/kube-design", | ||
"repository": { | ||
"url": "https://github.com/kubesphere/kube-design.git", | ||
"type": "git", | ||
"directory": "packages/hooks" | ||
}, | ||
"peerDependencies": { | ||
"react": ">=16.8.0" | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export { useId } from './useId'; | ||
export { useToggle, useBooleanToggle } from './useToggle'; | ||
export { useUncontrolled } from './useUncontrolled'; | ||
export { useMediaQuery } from './useMediaQuery'; | ||
export { useReducedMotion } from './useReducedMotion'; | ||
export { useWindowEvent } from './useWindowEvent'; | ||
export { useLocalStorage } from './useLocalStorage'; | ||
export { useForceUpdate } from './useForceUpdate'; | ||
export { useClipboard } from './useClipboard'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { useState } from 'react'; | ||
|
||
export function useClipboard({ timeout = 2000 } = {}) { | ||
const [error, setError] = useState<Error>(null); | ||
const [copied, setCopied] = useState(false); | ||
const [copyTimeout, setCopyTimeout] = useState(null); | ||
|
||
const handleCopyResult = (value: boolean) => { | ||
clearTimeout(copyTimeout); | ||
setCopyTimeout(setTimeout(() => setCopied(false), timeout)); | ||
setCopied(value); | ||
}; | ||
|
||
const copy = (valueToCopy: any) => { | ||
if ('clipboard' in navigator) { | ||
navigator.clipboard | ||
.writeText(valueToCopy) | ||
.then(() => handleCopyResult(true)) | ||
.catch((err) => setError(err)); | ||
} else { | ||
setError(new Error('useClipboard: navigator.clipboard is not supported')); | ||
} | ||
}; | ||
|
||
const reset = () => { | ||
setCopied(false); | ||
setError(null); | ||
clearTimeout(copyTimeout); | ||
}; | ||
|
||
return { copy, reset, error, copied }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { useEffect } from 'react'; | ||
|
||
export function useDidMount(callback: () => any): void { | ||
useEffect(() => { | ||
if (typeof callback === 'function') { | ||
callback(); | ||
} | ||
}, []); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { useReducer } from 'react'; | ||
|
||
const reducer = (value: number) => (value + 1) % 1000000; | ||
|
||
export function useForceUpdate(): () => void { | ||
const [, update] = useReducer(reducer, 0); | ||
return update; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { useRef } from 'react'; | ||
import { randomId } from '../utils'; | ||
|
||
export function useId(id?: string, generateId: () => string = randomId) { | ||
const generatedId = useRef(generateId()); | ||
return id || generatedId.current; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// https://github.com/mantinedev/mantine/blob/master/src/mantine-hooks/src/use-local-storage-value/use-local-storage-value.ts | ||
import { useState, useCallback, useEffect } from 'react'; | ||
import { useWindowEvent } from '../useWindowEvent'; | ||
|
||
export function useLocalStorage<T extends string>({ | ||
key, | ||
defaultValue = undefined, | ||
}: { | ||
key: string; | ||
defaultValue?: T; | ||
}) { | ||
const [value, setValue] = useState<T>( | ||
typeof window !== 'undefined' && 'localStorage' in window | ||
? (window.localStorage.getItem(key) as T) | ||
: ((defaultValue ?? '') as T) | ||
); | ||
|
||
const setLocalStorageValue = useCallback( | ||
(val: T | ((prevState: T) => T)) => { | ||
if (typeof val === 'function') { | ||
setValue((current) => { | ||
const result = val(current); | ||
window.localStorage.setItem(key, result); | ||
return result; | ||
}); | ||
} else { | ||
window.localStorage.setItem(key, val); | ||
setValue(val); | ||
} | ||
}, | ||
[key] | ||
); | ||
|
||
useWindowEvent('storage', (event) => { | ||
if (event.storageArea === window.localStorage && event.key === key) { | ||
setValue(event.newValue as T); | ||
} | ||
}); | ||
|
||
useEffect(() => { | ||
if (defaultValue && !value) { | ||
setLocalStorageValue(defaultValue); | ||
} | ||
}, [defaultValue, value, setLocalStorageValue]); | ||
|
||
return [value, setLocalStorageValue] as const; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { useState, useEffect, useRef } from 'react'; | ||
|
||
type MediaQueryCallback = (event: { matches: boolean; media: string }) => void; | ||
|
||
/** | ||
* Older versions of Safari (shipped withCatalina and before) do not support addEventListener on matchMedia | ||
* https://stackoverflow.com/questions/56466261/matchmedia-addlistener-marked-as-deprecated-addeventlistener-equivalent | ||
* */ | ||
function attachMediaListener(query: MediaQueryList, callback: MediaQueryCallback) { | ||
try { | ||
query.addEventListener('change', callback); | ||
return () => query.removeEventListener('change', callback); | ||
} catch (e) { | ||
query.addListener(callback); | ||
return () => query.removeListener(callback); | ||
} | ||
} | ||
|
||
function getInitialValue(query: string) { | ||
if (typeof window !== 'undefined' && 'matchMedia' in window) { | ||
return window.matchMedia(query).matches; | ||
} | ||
return false; | ||
} | ||
|
||
export function useMediaQuery(query: string) { | ||
const [matches, setMatches] = useState(getInitialValue(query)); | ||
const queryRef = useRef<MediaQueryList>(); | ||
|
||
// eslint-disable-next-line consistent-return | ||
useEffect(() => { | ||
if ('matchMedia' in window) { | ||
queryRef.current = window.matchMedia(query); | ||
setMatches(queryRef.current.matches); | ||
return attachMediaListener(queryRef.current, (event) => setMatches(event.matches)); | ||
} | ||
}, [query]); | ||
|
||
return matches; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { useMediaQuery } from '../useMediaQuery'; | ||
|
||
export function useReducedMotion() { | ||
return useMediaQuery('(prefers-reduced-motion: reduce)'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// https://github.com/mantinedev/mantine/blob/master/src/mantine-hooks/src/use-toggle/use-toggle.ts | ||
import React, { useState } from 'react'; | ||
|
||
export function useToggle<T>(initialValue: T, options: [T, T]) { | ||
const [state, setState] = useState(initialValue); | ||
|
||
const toggle = (value?: React.SetStateAction<T>) => { | ||
if (typeof value !== 'undefined') { | ||
setState(value); | ||
} else { | ||
setState((current) => { | ||
if (current === options[0]) { | ||
return options[1]; | ||
} | ||
|
||
return options[0]; | ||
}); | ||
} | ||
}; | ||
|
||
return [state, toggle] as const; | ||
} | ||
|
||
export function useBooleanToggle(initialValue = false) { | ||
return useToggle(initialValue, [true, false]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// https://github.com/mantinedev/mantine/blob/master/src/mantine-hooks/src/use-uncontrolled/use-uncontrolled.ts | ||
import { useEffect, useRef, useState } from 'react'; | ||
|
||
export type UncontrolledMode = 'initial' | 'controlled' | 'uncontrolled'; | ||
|
||
export interface UncontrolledOptions<T> { | ||
value: T | null | undefined; | ||
defaultValue: T | null | undefined; | ||
finalValue: T | null; | ||
onChange(value: T | null): void; | ||
onValueUpdate?(value: T | null): void; | ||
rule: (value: T | null | undefined) => boolean; | ||
} | ||
|
||
export function useUncontrolled<T>({ | ||
value, | ||
defaultValue, | ||
finalValue, | ||
rule, | ||
onChange, | ||
onValueUpdate, | ||
}: UncontrolledOptions<T>): readonly [T | null, (nextValue: T | null) => void, UncontrolledMode] { | ||
// determine, whether new props indicate controlled state | ||
const shouldBeControlled = rule(value); | ||
|
||
// initialize state | ||
const modeRef = useRef<UncontrolledMode>('initial'); | ||
const initialValue = rule(defaultValue) ? defaultValue : finalValue; | ||
const [uncontrolledValue, setUncontrolledValue] = useState(initialValue); | ||
|
||
// compute effective value | ||
let effectiveValue = shouldBeControlled ? value : uncontrolledValue; | ||
|
||
if (!shouldBeControlled && modeRef.current === 'controlled') { | ||
// We are transitioning from controlled to uncontrolled | ||
// this transition is special as it happens when clearing out | ||
// the input using "invalid" value (typically null or undefined). | ||
// | ||
// Since the value is invalid, doing nothing would mean just | ||
// transitioning to uncontrolled state and using whatever value | ||
// it currently holds which is likely not the bavior | ||
// user expects, so lets change the state to finalValue. | ||
// | ||
// The value will be propagated to internal state by useEffect below. | ||
|
||
effectiveValue = finalValue; | ||
} | ||
modeRef.current = shouldBeControlled ? 'controlled' : 'uncontrolled'; | ||
const mode = modeRef.current; | ||
|
||
const handleChange = (nextValue: T | null) => { | ||
typeof onChange === 'function' && onChange(nextValue); | ||
|
||
// Controlled input only triggers onChange event and expects | ||
// the controller to propagate new value back. | ||
if (mode === 'uncontrolled') { | ||
setUncontrolledValue(nextValue); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
if (mode === 'uncontrolled') { | ||
setUncontrolledValue(effectiveValue); | ||
} | ||
typeof onValueUpdate === 'function' && onValueUpdate(effectiveValue); | ||
}, [mode, effectiveValue]); | ||
|
||
return [effectiveValue, handleChange, modeRef.current] as const; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { useEffect } from 'react'; | ||
|
||
export function useWindowEvent<K extends keyof WindowEventMap>( | ||
type: K, | ||
listener: (this: Window, ev: WindowEventMap[K]) => any, | ||
options?: boolean | AddEventListenerOptions | ||
) { | ||
useEffect(() => { | ||
window.addEventListener(type, listener, options); | ||
return () => window.removeEventListener(type, listener, options); | ||
}, []); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export function randomId() { | ||
return `kubed-${Math.random().toString(36).substr(2, 9)}`; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"extends": "../../tsconfig.base.json", | ||
"include": ["./src",], | ||
"compilerOptions": { | ||
"rootDir": "src", | ||
"baseUrl": ".", | ||
"outDir": "lib", | ||
"declaration": true, | ||
"declarationMap": true, | ||
"declarationDir": "lib", | ||
"composite": true | ||
} | ||
} |