@amoa/hooks 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +2 -0
- package/package.json +15 -0
- package/src/use-click-outside.js +34 -0
- package/src/use-client.js +13 -0
- package/src/use-debounce.js +23 -0
- package/src/use-dragging.js +21 -0
- package/src/use-filters.js +20 -0
- package/src/use-hover.js +35 -0
- package/src/use-intersection.js +38 -0
- package/src/use-interval.js +31 -0
- package/src/use-lazy-loading.js +23 -0
- package/src/use-local-storage.js +53 -0
- package/src/use-resize.js +28 -0
- package/src/use-session-storage.js +66 -0
- package/src/use-skeleton.js +71 -0
- package/src/use-summary.js +36 -0
- package/src/use-timeout.js +18 -0
- package/src/use-tooltip.js +24 -0
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
export const useClickOutside = (callback) => {
|
|
4
|
+
const ref = React.useRef(null);
|
|
5
|
+
const callbackRef = React.useRef(callback);
|
|
6
|
+
|
|
7
|
+
React.useLayoutEffect(() => {
|
|
8
|
+
callbackRef.current = callback;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
React.useEffect(() => {
|
|
12
|
+
if (ref.current) {
|
|
13
|
+
const element = ref.current.parentNode;
|
|
14
|
+
|
|
15
|
+
const handler = (e) => {
|
|
16
|
+
if (element && !element.contains(e.target)) {
|
|
17
|
+
callbackRef.current(e);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
document.addEventListener("mousedown", handler);
|
|
22
|
+
document.addEventListener("touchstart", handler);
|
|
23
|
+
|
|
24
|
+
return () => {
|
|
25
|
+
document.removeEventListener("mousedown", handler);
|
|
26
|
+
document.removeEventListener("touchstart", handler);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
return ref;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default useClickOutside;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
export const useDebounce = (callback, time = 300, minimum = 3) => {
|
|
4
|
+
const [value, setValue] = React.useState("");
|
|
5
|
+
const [result, setResult] = React.useState();
|
|
6
|
+
|
|
7
|
+
const clear = () => setResult();
|
|
8
|
+
|
|
9
|
+
React.useEffect(() => {
|
|
10
|
+
// setResult();
|
|
11
|
+
if (typeof value !== "string" || value.length < minimum) return;
|
|
12
|
+
const delay = setTimeout(async () => {
|
|
13
|
+
const result = await callback(value);
|
|
14
|
+
setResult(result);
|
|
15
|
+
}, time);
|
|
16
|
+
|
|
17
|
+
return () => clearTimeout(delay);
|
|
18
|
+
}, [value]);
|
|
19
|
+
|
|
20
|
+
return [result, setValue, clear];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default useDebounce;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
export const useDragging = (callback, delay, state, layoff) => {
|
|
4
|
+
const savedCallback = React.useRef();
|
|
5
|
+
|
|
6
|
+
React.useEffect(() => {
|
|
7
|
+
savedCallback.state = callback;
|
|
8
|
+
}, [callback]);
|
|
9
|
+
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
function tick() {
|
|
12
|
+
savedCallback.state();
|
|
13
|
+
}
|
|
14
|
+
if (!layoff && delay !== null && delay !== 0) {
|
|
15
|
+
const interval = setInterval(tick, delay);
|
|
16
|
+
return () => clearInterval(interval);
|
|
17
|
+
}
|
|
18
|
+
}, [state, delay, layoff]);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default useDragging;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import * as Filters from "@amoa/forge/filters";
|
|
4
|
+
|
|
5
|
+
export const useFilters = (data, filters, sort, reference) => {
|
|
6
|
+
const [filtered, setFiltered] = React.useState(data);
|
|
7
|
+
const { search, ...other } = filters;
|
|
8
|
+
|
|
9
|
+
React.useEffect(() => {
|
|
10
|
+
if (!search && !Object.keys(other).length) setFiltered(data);
|
|
11
|
+
const searched = Filters.search(data, search);
|
|
12
|
+
const filtered = Filters.filter(searched, other, reference);
|
|
13
|
+
const sorted = Filters.sort(filtered, sort);
|
|
14
|
+
setFiltered(sorted);
|
|
15
|
+
}, [data, filters, sort]);
|
|
16
|
+
|
|
17
|
+
return filtered;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default useFilters;
|
package/src/use-hover.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
export const useHover = () => {
|
|
4
|
+
const [hovering, setHovering] = React.useState(false);
|
|
5
|
+
const previousNode = React.useRef(null);
|
|
6
|
+
|
|
7
|
+
const handleMouseEnter = React.useCallback(() => {
|
|
8
|
+
setHovering(true);
|
|
9
|
+
}, []);
|
|
10
|
+
|
|
11
|
+
const handleMouseLeave = React.useCallback(() => {
|
|
12
|
+
setHovering(false);
|
|
13
|
+
}, []);
|
|
14
|
+
|
|
15
|
+
const customRef = React.useCallback(
|
|
16
|
+
(node) => {
|
|
17
|
+
if (previousNode.current?.nodeType === Node.ELEMENT_NODE) {
|
|
18
|
+
previousNode.current.removeEventListener("mouseenter", handleMouseEnter);
|
|
19
|
+
previousNode.current.removeEventListener("mouseleave", handleMouseLeave);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (node?.nodeType === Node.ELEMENT_NODE) {
|
|
23
|
+
node.addEventListener("mouseenter", handleMouseEnter);
|
|
24
|
+
node.addEventListener("mouseleave", handleMouseLeave);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
previousNode.current = node;
|
|
28
|
+
},
|
|
29
|
+
[handleMouseEnter, handleMouseLeave]
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return [customRef, hovering];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default useHover;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
const OPTIONS = { threshold: 1, root: null, rootMargin: "0px" };
|
|
6
|
+
|
|
7
|
+
export const useIntersection = (options = {}) => {
|
|
8
|
+
const [entry, setEntry] = React.useState(null);
|
|
9
|
+
const { threshold, root, rootMargin } = { ...OPTIONS, ...options };
|
|
10
|
+
|
|
11
|
+
const reference = React.useRef(null);
|
|
12
|
+
|
|
13
|
+
const ref = React.useCallback(
|
|
14
|
+
(node) => {
|
|
15
|
+
if (reference.current) {
|
|
16
|
+
reference.current.disconnect();
|
|
17
|
+
reference.current = null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (node?.nodeType === Node.ELEMENT_NODE) {
|
|
21
|
+
const observer = new IntersectionObserver(
|
|
22
|
+
([entry]) => {
|
|
23
|
+
setEntry(entry);
|
|
24
|
+
},
|
|
25
|
+
{ threshold, root, rootMargin }
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
observer.observe(node);
|
|
29
|
+
reference.current = observer;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
[threshold, root, rootMargin]
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return [ref, entry];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default useIntersection;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
export const useInterval = (callback, delay) => {
|
|
6
|
+
const [id, setId] = React.useState();
|
|
7
|
+
const savedCallback = React.useRef();
|
|
8
|
+
|
|
9
|
+
const clear = () => {
|
|
10
|
+
clearInterval(id);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
React.useEffect(() => {
|
|
14
|
+
savedCallback.current = callback;
|
|
15
|
+
}, [callback]);
|
|
16
|
+
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
function tick() {
|
|
19
|
+
savedCallback.current();
|
|
20
|
+
}
|
|
21
|
+
if (delay !== null) {
|
|
22
|
+
const id = setInterval(tick, delay);
|
|
23
|
+
setId(id);
|
|
24
|
+
return () => clearInterval(id);
|
|
25
|
+
}
|
|
26
|
+
}, [delay]);
|
|
27
|
+
|
|
28
|
+
return [clear];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default useInterval;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import * as Functions from "@amoa/forge/functions";
|
|
4
|
+
|
|
5
|
+
const OPTIONS = {
|
|
6
|
+
rootMargin: "200px 0px",
|
|
7
|
+
threshold: 0.1
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const useLazyLoading = (ref, callback, delay = 300) => {
|
|
11
|
+
const throttle = React.useMemo(() => Functions.throttle(callback, delay), [callback, delay]);
|
|
12
|
+
|
|
13
|
+
React.useEffect(() => {
|
|
14
|
+
const node = ref.current;
|
|
15
|
+
if (!node) return;
|
|
16
|
+
|
|
17
|
+
const observer = new IntersectionObserver(([e]) => e.isIntersecting && throttle(), OPTIONS);
|
|
18
|
+
observer.observe(node);
|
|
19
|
+
return () => observer.disconnect();
|
|
20
|
+
}, [ref.current, callback]);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default useLazyLoading;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
export const useLocalStorage = (key, initialValue, session = false) => {
|
|
4
|
+
const [storedValue, setStoredValue] = useState(() => {
|
|
5
|
+
if (typeof window !== "undefined") {
|
|
6
|
+
try {
|
|
7
|
+
const item = session ? window.sessionStorage.getItem(key) : window.localStorage.getItem(key);
|
|
8
|
+
return item !== null ? JSON.parse(item) : initialValue;
|
|
9
|
+
} catch (error) {
|
|
10
|
+
console.info(error);
|
|
11
|
+
return initialValue;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const setValue = (value) => {
|
|
17
|
+
if (typeof window === "undefined") return [];
|
|
18
|
+
try {
|
|
19
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
20
|
+
if (valueToStore !== undefined) {
|
|
21
|
+
const _value = JSON.stringify(valueToStore);
|
|
22
|
+
if (session) {
|
|
23
|
+
window.sessionStorage.setItem(key, _value);
|
|
24
|
+
} else {
|
|
25
|
+
window.localStorage.setItem(key, _value);
|
|
26
|
+
}
|
|
27
|
+
} else {
|
|
28
|
+
if (session) {
|
|
29
|
+
window.sessionStorage.removeItem(key);
|
|
30
|
+
} else {
|
|
31
|
+
window.localStorage.removeItem(key);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
window.dispatchEvent(new Event(key));
|
|
35
|
+
setStoredValue(valueToStore);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.info(error);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const check = () => {
|
|
43
|
+
const value = window.localStorage.getItem(key);
|
|
44
|
+
if (storedValue !== value) setStoredValue(value);
|
|
45
|
+
};
|
|
46
|
+
window.addEventListener(key, check);
|
|
47
|
+
return () => window.removeEventListener(key, check);
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
return [storedValue, setValue];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default useLocalStorage;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
export const useResize = () => {
|
|
4
|
+
const [size, setSize] = React.useState({
|
|
5
|
+
width: null,
|
|
6
|
+
height: null
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const resize = React.useCallback(() => {
|
|
10
|
+
setSize({
|
|
11
|
+
width: window.innerWidth,
|
|
12
|
+
height: window.innerHeight
|
|
13
|
+
});
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
React.useLayoutEffect(() => {
|
|
17
|
+
resize();
|
|
18
|
+
window.addEventListener("resize", resize);
|
|
19
|
+
|
|
20
|
+
return () => {
|
|
21
|
+
window.removeEventListener("resize", resize);
|
|
22
|
+
};
|
|
23
|
+
}, [resize]);
|
|
24
|
+
|
|
25
|
+
return size;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default useResize;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const dispatchEvent = (key, newValue) => {
|
|
4
|
+
window.dispatchEvent(new StorageEvent("storage", { key, newValue }));
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const setSessionStorageItem = (key, value) => {
|
|
8
|
+
const stringifiedValue = JSON.stringify(value);
|
|
9
|
+
window.sessionStorage.setItem(key, stringifiedValue);
|
|
10
|
+
dispatchEvent(key, stringifiedValue);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const removeSessionStorageItem = (key) => {
|
|
14
|
+
window.sessionStorage.removeItem(key);
|
|
15
|
+
dispatchEvent(key, null);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const getSessionStorageItem = (key) => {
|
|
19
|
+
return window.sessionStorage.getItem(key);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const useSessionStorageSubscribe = (callback) => {
|
|
23
|
+
window.addEventListener("storage", callback);
|
|
24
|
+
return () => window.removeEventListener("storage", callback);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const getSessionStorageServerSnapshot = () => {
|
|
28
|
+
// throw Error("Le Session Storage n'est utilisable que côté client.");
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const useSessionStorage = (key, initialValue, fallback) => {
|
|
32
|
+
const getClientSnapshot = () => getSessionStorageItem(key);
|
|
33
|
+
|
|
34
|
+
const getServerSnapshot = fallback !== undefined ? () => fallback : undefined;
|
|
35
|
+
|
|
36
|
+
const getSnapshot = typeof window !== "undefined" ? getClientSnapshot : getServerSnapshot;
|
|
37
|
+
|
|
38
|
+
const store = React.useSyncExternalStore(useSessionStorageSubscribe, getSnapshot, getSessionStorageServerSnapshot);
|
|
39
|
+
|
|
40
|
+
const setState = React.useCallback(
|
|
41
|
+
(callback) => {
|
|
42
|
+
try {
|
|
43
|
+
const nextState = typeof callback === "function" ? callback(JSON.parse(store)) : callback;
|
|
44
|
+
|
|
45
|
+
if (nextState === undefined || nextState === null) {
|
|
46
|
+
removeSessionStorageItem(key);
|
|
47
|
+
} else {
|
|
48
|
+
setSessionStorageItem(key, nextState);
|
|
49
|
+
}
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.warn(e);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[key, store]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
if (getSessionStorageItem(key) === null && typeof initialValue !== "undefined") {
|
|
59
|
+
setSessionStorageItem(key, initialValue);
|
|
60
|
+
}
|
|
61
|
+
}, [key, initialValue]);
|
|
62
|
+
|
|
63
|
+
return [store ? JSON.parse(store) : initialValue, setState];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default useSessionStorage;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const OBSERVER = { childList: true, subtree: true, characterData: true };
|
|
4
|
+
|
|
5
|
+
const leaves = (node) => {
|
|
6
|
+
if (!node) return [];
|
|
7
|
+
|
|
8
|
+
const { children } = node;
|
|
9
|
+
const list = Array.from(children).filter((child) => child.tagName !== "svg" || child?.remove());
|
|
10
|
+
const { length } = list;
|
|
11
|
+
|
|
12
|
+
if (length) return list.reduce((acc, node) => [...acc, ...leaves(node)], []);
|
|
13
|
+
|
|
14
|
+
const width = 80; // (Math.ceil(Math.random() * 4) + 4) * 10;
|
|
15
|
+
const min = 50; // (Math.ceil(Math.random() * 3) + 4) * 10;
|
|
16
|
+
|
|
17
|
+
if (node.style.backgroundColor !== null) node.style.backgroundColor = "var(--light-background)";
|
|
18
|
+
if (node.style.borderRadius === "") node.style.borderRadius = "4px";
|
|
19
|
+
|
|
20
|
+
node.style.display = "flex";
|
|
21
|
+
node.style.flexDirection = "column";
|
|
22
|
+
node.style.borderColor = "transparent";
|
|
23
|
+
node.style.gap = "0.5px";
|
|
24
|
+
node.innerHTML = `<span style='min-width:${min}px;width:${width}%;height:14.5px;margin:0.5px 0;background-color:var(--light-background);border-radius:4px;' />`;
|
|
25
|
+
node.parentNode.style.gap = "1.5px";
|
|
26
|
+
|
|
27
|
+
return [node];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const excarnate = (element) => {
|
|
31
|
+
Array.from(element).forEach((child) => {
|
|
32
|
+
child.style.borderColor = "transparent";
|
|
33
|
+
child.style.filter = "unset";
|
|
34
|
+
child.style.cursor = "default";
|
|
35
|
+
child.style.pointerEvents = "none";
|
|
36
|
+
child.classList.add("skeleton");
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const useSkeleton = (ref) => {
|
|
41
|
+
React.useEffect(() => {
|
|
42
|
+
if (!ref.current) return;
|
|
43
|
+
|
|
44
|
+
excarnate(ref.current.children);
|
|
45
|
+
leaves(ref.current);
|
|
46
|
+
|
|
47
|
+
const observer = new MutationObserver((mutations) => {
|
|
48
|
+
observer.disconnect();
|
|
49
|
+
mutations.forEach(({ addedNodes = [], type, target }) => {
|
|
50
|
+
const parent = target.parentElement;
|
|
51
|
+
addedNodes.forEach?.((node) => {
|
|
52
|
+
if (node.nodeType === 1) {
|
|
53
|
+
excarnate([node]);
|
|
54
|
+
leaves(node);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
if (type === "characterData") {
|
|
58
|
+
excarnate([parent]);
|
|
59
|
+
leaves(parent);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
observer.observe(ref.current, OBSERVER);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
observer.observe(ref.current, OBSERVER);
|
|
66
|
+
|
|
67
|
+
return () => observer.disconnect();
|
|
68
|
+
}, [ref.current]);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export default useSkeleton;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
const surface = ({ width, height }) => width * height;
|
|
6
|
+
|
|
7
|
+
export const useSummary = (elements = [], options = {}, target) => {
|
|
8
|
+
const { threshold = 1, root = null, rootMargin = "0px" } = options;
|
|
9
|
+
const [visible, setVisible] = React.useState(null);
|
|
10
|
+
const reference = React.useRef(null);
|
|
11
|
+
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
if (!elements.length) return;
|
|
14
|
+
|
|
15
|
+
const observer = new IntersectionObserver(
|
|
16
|
+
(entries) => {
|
|
17
|
+
const [first] = entries.sort((a, b) => surface(b.intersectionRect) - surface(a.intersectionRect));
|
|
18
|
+
if (first?.intersectionRect.height > 0) {
|
|
19
|
+
setVisible(target(first));
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{ threshold, root, rootMargin }
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
reference.current?.disconnect();
|
|
26
|
+
|
|
27
|
+
elements.forEach((element) => observer.observe(element));
|
|
28
|
+
reference.current = observer;
|
|
29
|
+
|
|
30
|
+
return () => observer.disconnect();
|
|
31
|
+
}, [elements, threshold, root, rootMargin, target]);
|
|
32
|
+
|
|
33
|
+
return visible;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default useSummary;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
export const useTimeout = (callback, time) => {
|
|
4
|
+
const id = React.useRef(null);
|
|
5
|
+
|
|
6
|
+
const clear = React.useCallback(() => {
|
|
7
|
+
window.clearTimeout(id.current);
|
|
8
|
+
}, []);
|
|
9
|
+
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
id.current = window.setTimeout(callback, time);
|
|
12
|
+
return clear;
|
|
13
|
+
}, [time, clear]);
|
|
14
|
+
|
|
15
|
+
return clear;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default useTimeout;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const limits = {
|
|
4
|
+
top: 60,
|
|
5
|
+
bottom: 60,
|
|
6
|
+
right: 60,
|
|
7
|
+
left: 60
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const useTooltip = (item, text, position) => {
|
|
11
|
+
React.useEffect(() => {
|
|
12
|
+
if (text && item.current) {
|
|
13
|
+
const { top, left, bottom, right } = item.current.getBoundingClientRect();
|
|
14
|
+
item.current.dataset.tooltip = text;
|
|
15
|
+
if (position) item.current.dataset[position] = "";
|
|
16
|
+
if (top < limits.top) item.current.dataset.tooltipTop = "";
|
|
17
|
+
if (window.innerHeight - bottom < limits.bottom) item.current.dataset.tooltipBottom = "";
|
|
18
|
+
if (window.innerWidth - right < limits.right) item.current.dataset.tooltipRight = "";
|
|
19
|
+
if (left < limits.left) item.current.dataset.tooltipLeft = "";
|
|
20
|
+
}
|
|
21
|
+
}, [item, text]);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default useTooltip;
|