minimal-shared 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +3 -0
- package/dist/hooks/index.d.ts +20 -0
- package/dist/hooks/index.js +19 -0
- package/dist/hooks/use-back-to-top/index.d.ts +2 -0
- package/dist/hooks/use-back-to-top/index.js +1 -0
- package/dist/hooks/use-back-to-top/use-back-to-top.d.ts +33 -0
- package/dist/hooks/use-back-to-top/use-back-to-top.js +64 -0
- package/dist/hooks/use-boolean/index.d.ts +2 -0
- package/dist/hooks/use-boolean/index.js +1 -0
- package/dist/hooks/use-boolean/use-boolean.d.ts +29 -0
- package/dist/hooks/use-boolean/use-boolean.js +24 -0
- package/dist/hooks/use-client-rect/index.d.ts +2 -0
- package/dist/hooks/use-client-rect/index.js +1 -0
- package/dist/hooks/use-client-rect/use-client-rect.d.ts +29 -0
- package/dist/hooks/use-client-rect/use-client-rect.js +47 -0
- package/dist/hooks/use-cookies/index.d.ts +1 -0
- package/dist/hooks/use-cookies/index.js +1 -0
- package/dist/hooks/use-cookies/use-cookies.d.ts +39 -0
- package/dist/hooks/use-cookies/use-cookies.js +110 -0
- package/dist/hooks/use-copy-to-clipboard/index.d.ts +1 -0
- package/dist/hooks/use-copy-to-clipboard/index.js +1 -0
- package/dist/hooks/use-copy-to-clipboard/use-copy-to-clipboard.d.ts +26 -0
- package/dist/hooks/use-copy-to-clipboard/use-copy-to-clipboard.js +27 -0
- package/dist/hooks/use-countdown-date/index.d.ts +1 -0
- package/dist/hooks/use-countdown-date/index.js +1 -0
- package/dist/hooks/use-countdown-date/use-countdown-date.d.ts +29 -0
- package/dist/hooks/use-countdown-date/use-countdown-date.js +41 -0
- package/dist/hooks/use-countdown-seconds/index.d.ts +2 -0
- package/dist/hooks/use-countdown-seconds/index.js +1 -0
- package/dist/hooks/use-countdown-seconds/use-countdown-seconds.d.ts +35 -0
- package/dist/hooks/use-countdown-seconds/use-countdown-seconds.js +36 -0
- package/dist/hooks/use-debounce/index.d.ts +1 -0
- package/dist/hooks/use-debounce/index.js +1 -0
- package/dist/hooks/use-debounce/use-debounce.d.ts +21 -0
- package/dist/hooks/use-debounce/use-debounce.js +17 -0
- package/dist/hooks/use-double-click/index.d.ts +2 -0
- package/dist/hooks/use-double-click/index.js +1 -0
- package/dist/hooks/use-double-click/use-double-click.d.ts +28 -0
- package/dist/hooks/use-double-click/use-double-click.js +33 -0
- package/dist/hooks/use-event-listener/index.d.ts +2 -0
- package/dist/hooks/use-event-listener/index.js +1 -0
- package/dist/hooks/use-event-listener/use-event-listener.d.ts +7 -0
- package/dist/hooks/use-event-listener/use-event-listener.js +23 -0
- package/dist/hooks/use-is-client/index.d.ts +1 -0
- package/dist/hooks/use-is-client/index.js +1 -0
- package/dist/hooks/use-is-client/use-is-client.d.ts +18 -0
- package/dist/hooks/use-is-client/use-is-client.js +12 -0
- package/dist/hooks/use-local-storage/index.d.ts +1 -0
- package/dist/hooks/use-local-storage/index.js +1 -0
- package/dist/hooks/use-local-storage/use-local-storage.d.ts +37 -0
- package/dist/hooks/use-local-storage/use-local-storage.js +113 -0
- package/dist/hooks/use-multi-select/index.d.ts +2 -0
- package/dist/hooks/use-multi-select/index.js +1 -0
- package/dist/hooks/use-multi-select/use-multi-select.d.ts +55 -0
- package/dist/hooks/use-multi-select/use-multi-select.js +36 -0
- package/dist/hooks/use-popover/index.d.ts +2 -0
- package/dist/hooks/use-popover/index.js +1 -0
- package/dist/hooks/use-popover/use-popover.d.ts +35 -0
- package/dist/hooks/use-popover/use-popover.js +21 -0
- package/dist/hooks/use-popover-hover/index.d.ts +2 -0
- package/dist/hooks/use-popover-hover/index.js +1 -0
- package/dist/hooks/use-popover-hover/use-popover-hover.d.ts +41 -0
- package/dist/hooks/use-popover-hover/use-popover-hover.js +24 -0
- package/dist/hooks/use-scroll-offset-top/index.d.ts +2 -0
- package/dist/hooks/use-scroll-offset-top/index.js +1 -0
- package/dist/hooks/use-scroll-offset-top/use-scroll-offset-top.d.ts +28 -0
- package/dist/hooks/use-scroll-offset-top/use-scroll-offset-top.js +29 -0
- package/dist/hooks/use-set-state/index.d.ts +1 -0
- package/dist/hooks/use-set-state/index.js +1 -0
- package/dist/hooks/use-set-state/use-set-state.d.ts +32 -0
- package/dist/hooks/use-set-state/use-set-state.js +26 -0
- package/dist/hooks/use-tabs/index.d.ts +2 -0
- package/dist/hooks/use-tabs/index.js +1 -0
- package/dist/hooks/use-tabs/use-tabs.d.ts +30 -0
- package/dist/hooks/use-tabs/use-tabs.js +16 -0
- package/dist/hooks/use-text-input/index.d.ts +2 -0
- package/dist/hooks/use-text-input/index.js +1 -0
- package/dist/hooks/use-text-input/use-text-input.d.ts +16 -0
- package/dist/hooks/use-text-input/use-text-input.js +16 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +2 -0
- package/dist/utils/active-link/active-link.d.ts +16 -0
- package/dist/utils/active-link/active-link.js +43 -0
- package/dist/utils/active-link/index.d.ts +1 -0
- package/dist/utils/active-link/index.js +1 -0
- package/dist/utils/classes/classes.d.ts +25 -0
- package/dist/utils/classes/classes.js +14 -0
- package/dist/utils/classes/index.d.ts +1 -0
- package/dist/utils/classes/index.js +1 -0
- package/dist/utils/color/color.d.ts +67 -0
- package/dist/utils/color/color.js +47 -0
- package/dist/utils/color/index.d.ts +1 -0
- package/dist/utils/color/index.js +1 -0
- package/dist/utils/cookies/cookies.d.ts +34 -0
- package/dist/utils/cookies/cookies.js +46 -0
- package/dist/utils/cookies/index.d.ts +1 -0
- package/dist/utils/cookies/index.js +1 -0
- package/dist/utils/font/font.d.ts +37 -0
- package/dist/utils/font/font.js +20 -0
- package/dist/utils/font/index.d.ts +1 -0
- package/dist/utils/font/index.js +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.js +17 -0
- package/dist/utils/local-storage/index.d.ts +1 -0
- package/dist/utils/local-storage/index.js +1 -0
- package/dist/utils/local-storage/local-storage.d.ts +55 -0
- package/dist/utils/local-storage/local-storage.js +51 -0
- package/dist/utils/object/index.d.ts +1 -0
- package/dist/utils/object/index.js +1 -0
- package/dist/utils/object/object.d.ts +26 -0
- package/dist/utils/object/object.js +10 -0
- package/dist/utils/url/index.d.ts +1 -0
- package/dist/utils/url/index.js +1 -0
- package/dist/utils/url/url.d.ts +46 -0
- package/dist/utils/url/url.js +28 -0
- package/dist/utils/uuidv4/index.d.ts +1 -0
- package/dist/utils/uuidv4/index.js +1 -0
- package/dist/utils/uuidv4/uuidv4.d.ts +12 -0
- package/dist/utils/uuidv4/uuidv4.js +11 -0
- package/package.json +87 -0
package/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
export { UseTabsReturn, useTabs } from './use-tabs/use-tabs.js';
|
2
|
+
export { UseBooleanReturn, useBoolean } from './use-boolean/use-boolean.js';
|
3
|
+
export { UsePopoverReturn, usePopover } from './use-popover/use-popover.js';
|
4
|
+
export { UseCookiesReturn, useCookies } from './use-cookies/use-cookies.js';
|
5
|
+
export { UseDebounceReturn, useDebounce } from './use-debounce/use-debounce.js';
|
6
|
+
export { UseSetStateReturn, useSetState } from './use-set-state/use-set-state.js';
|
7
|
+
export { UseIsClientReturn, useIsClient } from './use-is-client/use-is-client.js';
|
8
|
+
export { UseTextInputReturn, useTextInput } from './use-text-input/use-text-input.js';
|
9
|
+
export { UseBackToTopReturn, useBackToTop } from './use-back-to-top/use-back-to-top.js';
|
10
|
+
export { UseClientRectReturn, useClientRect } from './use-client-rect/use-client-rect.js';
|
11
|
+
export { UseMultiSelectReturn, updateSelectedItems, useMultiSelect } from './use-multi-select/use-multi-select.js';
|
12
|
+
export { UseDoubleClickReturn, useDoubleClick } from './use-double-click/use-double-click.js';
|
13
|
+
export { UseLocalStorageReturn, useLocalStorage } from './use-local-storage/use-local-storage.js';
|
14
|
+
export { usePopoverHover } from './use-popover-hover/use-popover-hover.js';
|
15
|
+
export { UseCountdownDateReturn, useCountdownDate } from './use-countdown-date/use-countdown-date.js';
|
16
|
+
export { useEventListener } from './use-event-listener/use-event-listener.js';
|
17
|
+
export { UseScrollOffsetTopReturn, useScrollOffsetTop } from './use-scroll-offset-top/use-scroll-offset-top.js';
|
18
|
+
export { UseCountdownSecondsReturn, useCountdownSeconds } from './use-countdown-seconds/use-countdown-seconds.js';
|
19
|
+
export { CopiedValue, CopyFn, UseCopyToClipboardReturn, useCopyToClipboard } from './use-copy-to-clipboard/use-copy-to-clipboard.js';
|
20
|
+
import 'react';
|
@@ -0,0 +1,19 @@
|
|
1
|
+
export * from './use-tabs';
|
2
|
+
export * from './use-boolean';
|
3
|
+
export * from './use-popover';
|
4
|
+
export * from './use-cookies';
|
5
|
+
export * from './use-debounce';
|
6
|
+
export * from './use-set-state';
|
7
|
+
export * from './use-is-client';
|
8
|
+
export * from './use-text-input';
|
9
|
+
export * from './use-back-to-top';
|
10
|
+
export * from './use-client-rect';
|
11
|
+
export * from './use-multi-select';
|
12
|
+
export * from './use-double-click';
|
13
|
+
export * from './use-local-storage';
|
14
|
+
export * from './use-popover-hover';
|
15
|
+
export * from './use-countdown-date';
|
16
|
+
export * from './use-event-listener';
|
17
|
+
export * from './use-scroll-offset-top';
|
18
|
+
export * from './use-countdown-seconds';
|
19
|
+
export * from './use-copy-to-clipboard';
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './use-back-to-top';
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Custom hook to manage the visibility of a "Back to Top" button based on scroll position.
|
5
|
+
*
|
6
|
+
* @param {string | number} defaultValue - The scroll progress percentage (e.g., '90%') or distance in pixels (e.g., 80) at which the button becomes visible.
|
7
|
+
* - If `defaultValue` is a percentage string (e.g., '90%'), the button becomes visible when the scroll distance is that percentage from the top.
|
8
|
+
* - If `defaultValue` is a number (e.g., 80), the button becomes visible when the scroll distance is that many pixels from the bottom.
|
9
|
+
* @param {boolean} [isDebounce=false] - Whether to debounce the scroll handler to improve performance.
|
10
|
+
*
|
11
|
+
* @returns {UseBackToTopReturn} - An object containing:
|
12
|
+
* - `isVisible`: A boolean indicating whether the "Back to Top" button should be visible.
|
13
|
+
* - `onBackToTop`: A function to scroll the window back to the top smoothly.
|
14
|
+
* - `setIsVisible`: A function to manually set the visibility of the "Back to Top" button.
|
15
|
+
*
|
16
|
+
* @example
|
17
|
+
* const { isVisible, onBackToTop } = useBackToTop('90%');
|
18
|
+
* const { isVisible, onBackToTop } = useBackToTop(80);
|
19
|
+
*
|
20
|
+
* return (
|
21
|
+
* <button onClick={onBackToTop} style={{ display: isVisible ? 'block' : 'none' }}>
|
22
|
+
* Back to Top
|
23
|
+
* </button>
|
24
|
+
* );
|
25
|
+
*/
|
26
|
+
type UseBackToTopReturn = {
|
27
|
+
isVisible: boolean;
|
28
|
+
onBackToTop: () => void;
|
29
|
+
setIsVisible: Dispatch<SetStateAction<boolean>>;
|
30
|
+
};
|
31
|
+
declare function useBackToTop(defaultValue: string | number, isDebounce?: boolean): UseBackToTopReturn;
|
32
|
+
|
33
|
+
export { type UseBackToTopReturn, useBackToTop };
|
@@ -0,0 +1,64 @@
|
|
1
|
+
// src/hooks/use-back-to-top/use-back-to-top.ts
|
2
|
+
import { useMemo, useState, useEffect, useCallback } from "react";
|
3
|
+
function useBackToTop(defaultValue, isDebounce) {
|
4
|
+
const [isVisible, setIsVisible] = useState(false);
|
5
|
+
const parseValue = parseValueInput(defaultValue);
|
6
|
+
const handleScroll = useCallback(() => {
|
7
|
+
const windowInnerHeight = window.innerHeight;
|
8
|
+
const windowScrollY = Math.round(window.scrollY);
|
9
|
+
const documentOffsetHeight = document.body.offsetHeight;
|
10
|
+
const scrollProgress = Math.round(
|
11
|
+
windowScrollY / (documentOffsetHeight - windowInnerHeight) * 100
|
12
|
+
);
|
13
|
+
if (parseValue.type === "percentage") {
|
14
|
+
setIsVisible(scrollProgress >= parseValue.value);
|
15
|
+
} else {
|
16
|
+
const distanceValue = documentOffsetHeight - windowInnerHeight - windowScrollY;
|
17
|
+
setIsVisible(parseValue.value >= distanceValue);
|
18
|
+
}
|
19
|
+
}, [parseValue.type, parseValue.value]);
|
20
|
+
const debouncedHandleScroll = useMemo(() => {
|
21
|
+
let timeoutId;
|
22
|
+
return () => {
|
23
|
+
clearTimeout(timeoutId);
|
24
|
+
timeoutId = setTimeout(handleScroll, 100);
|
25
|
+
};
|
26
|
+
}, [handleScroll]);
|
27
|
+
useEffect(() => {
|
28
|
+
const scrollHandler = isDebounce ? debouncedHandleScroll : handleScroll;
|
29
|
+
window.addEventListener("scroll", scrollHandler);
|
30
|
+
return () => {
|
31
|
+
window.removeEventListener("scroll", scrollHandler);
|
32
|
+
};
|
33
|
+
}, [debouncedHandleScroll, handleScroll, isDebounce]);
|
34
|
+
const onBackToTop = () => {
|
35
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
36
|
+
};
|
37
|
+
return {
|
38
|
+
isVisible,
|
39
|
+
onBackToTop,
|
40
|
+
setIsVisible
|
41
|
+
};
|
42
|
+
}
|
43
|
+
function parseValueInput(inputValue) {
|
44
|
+
let value;
|
45
|
+
let type;
|
46
|
+
if (typeof inputValue === "string") {
|
47
|
+
if (inputValue.endsWith("%")) {
|
48
|
+
value = Number(inputValue.slice(0, -1));
|
49
|
+
if (isNaN(value)) {
|
50
|
+
throw new Error("Invalid percentage value");
|
51
|
+
}
|
52
|
+
type = "percentage";
|
53
|
+
} else {
|
54
|
+
throw new Error("String input must end with %");
|
55
|
+
}
|
56
|
+
} else {
|
57
|
+
value = inputValue;
|
58
|
+
type = "number";
|
59
|
+
}
|
60
|
+
return { value, type };
|
61
|
+
}
|
62
|
+
export {
|
63
|
+
useBackToTop
|
64
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './use-boolean';
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Custom hook to manage a boolean state with utility functions to set it to true, false, or toggle its value.
|
5
|
+
*
|
6
|
+
* @param {boolean} [defaultValue=false] - The initial value of the boolean state.
|
7
|
+
*
|
8
|
+
* @example
|
9
|
+
* const { value, onTrue, onFalse, onToggle } = useBoolean(false);
|
10
|
+
*
|
11
|
+
* return (
|
12
|
+
* <div>
|
13
|
+
* <p>Value: {value.toString()}</p>
|
14
|
+
* <button onClick={onTrue}>Set True</button>
|
15
|
+
* <button onClick={onFalse}>Set False</button>
|
16
|
+
* <button onClick={onToggle}>Toggle</button>
|
17
|
+
* </div>
|
18
|
+
* );
|
19
|
+
*/
|
20
|
+
type UseBooleanReturn = {
|
21
|
+
value: boolean;
|
22
|
+
onTrue: () => void;
|
23
|
+
onFalse: () => void;
|
24
|
+
onToggle: () => void;
|
25
|
+
setValue: Dispatch<SetStateAction<boolean>>;
|
26
|
+
};
|
27
|
+
declare function useBoolean(defaultValue?: boolean): UseBooleanReturn;
|
28
|
+
|
29
|
+
export { type UseBooleanReturn, useBoolean };
|
@@ -0,0 +1,24 @@
|
|
1
|
+
// src/hooks/use-boolean/use-boolean.ts
|
2
|
+
import { useState, useCallback } from "react";
|
3
|
+
function useBoolean(defaultValue = false) {
|
4
|
+
const [value, setValue] = useState(defaultValue);
|
5
|
+
const onTrue = useCallback(() => {
|
6
|
+
setValue(true);
|
7
|
+
}, []);
|
8
|
+
const onFalse = useCallback(() => {
|
9
|
+
setValue(false);
|
10
|
+
}, []);
|
11
|
+
const onToggle = useCallback(() => {
|
12
|
+
setValue((prev) => !prev);
|
13
|
+
}, []);
|
14
|
+
return {
|
15
|
+
value,
|
16
|
+
onTrue,
|
17
|
+
onFalse,
|
18
|
+
onToggle,
|
19
|
+
setValue
|
20
|
+
};
|
21
|
+
}
|
22
|
+
export {
|
23
|
+
useBoolean
|
24
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './use-client-rect';
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { RefObject } from 'react';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Custom hook to get the bounding client rect and scroll dimensions of a DOM element.
|
5
|
+
*
|
6
|
+
* @param {RefObject<HTMLDivElement>} [inputRef] - Optional ref object to the target element.
|
7
|
+
* @param {string} [eventType] - Optional event type to trigger updates (e.g., 'scroll', 'resize').
|
8
|
+
* @returns {UseClientRectReturn} - Object containing the bounding rect, scroll dimensions, and ref to the element.
|
9
|
+
*/
|
10
|
+
type ScrollElValue = {
|
11
|
+
scrollWidth: number;
|
12
|
+
scrollHeight: number;
|
13
|
+
};
|
14
|
+
type DOMRectValue = {
|
15
|
+
top: number;
|
16
|
+
right: number;
|
17
|
+
bottom: number;
|
18
|
+
left: number;
|
19
|
+
x: number;
|
20
|
+
y: number;
|
21
|
+
width: number;
|
22
|
+
height: number;
|
23
|
+
};
|
24
|
+
type UseClientRectReturn = DOMRectValue & ScrollElValue & {
|
25
|
+
elementRef: RefObject<HTMLDivElement>;
|
26
|
+
};
|
27
|
+
declare function useClientRect(inputRef?: RefObject<HTMLDivElement>, eventType?: string): UseClientRectReturn;
|
28
|
+
|
29
|
+
export { type UseClientRectReturn, useClientRect };
|
@@ -0,0 +1,47 @@
|
|
1
|
+
// src/hooks/use-client-rect/use-client-rect.ts
|
2
|
+
import { useRef, useMemo, useState, useEffect, useCallback, useLayoutEffect } from "react";
|
3
|
+
function useClientRect(inputRef, eventType) {
|
4
|
+
const initialRef = useRef(null);
|
5
|
+
const elementRef = inputRef || initialRef;
|
6
|
+
const [rect, setRect] = useState(void 0);
|
7
|
+
const [scroll, setScroll] = useState(void 0);
|
8
|
+
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
9
|
+
const handleBoundingClientRect = useCallback(() => {
|
10
|
+
if (elementRef.current) {
|
11
|
+
const clientRect = elementRef.current.getBoundingClientRect();
|
12
|
+
setRect(clientRect);
|
13
|
+
setScroll({
|
14
|
+
scrollWidth: elementRef.current.scrollWidth,
|
15
|
+
scrollHeight: elementRef.current.scrollHeight
|
16
|
+
});
|
17
|
+
}
|
18
|
+
}, [elementRef]);
|
19
|
+
useIsomorphicLayoutEffect(() => {
|
20
|
+
handleBoundingClientRect();
|
21
|
+
}, [handleBoundingClientRect]);
|
22
|
+
useEffect(() => {
|
23
|
+
const event = eventType || "resize";
|
24
|
+
window.addEventListener(event, handleBoundingClientRect);
|
25
|
+
return () => {
|
26
|
+
window.removeEventListener(event, handleBoundingClientRect);
|
27
|
+
};
|
28
|
+
}, [eventType, handleBoundingClientRect]);
|
29
|
+
const memoizedRectValue = useMemo(() => rect, [rect]);
|
30
|
+
const memoizedScrollValue = useMemo(() => scroll, [scroll]);
|
31
|
+
return {
|
32
|
+
elementRef,
|
33
|
+
top: memoizedRectValue?.top ?? 0,
|
34
|
+
right: memoizedRectValue?.right ?? 0,
|
35
|
+
bottom: memoizedRectValue?.bottom ?? 0,
|
36
|
+
left: memoizedRectValue?.left ?? 0,
|
37
|
+
x: memoizedRectValue?.x ?? 0,
|
38
|
+
y: memoizedRectValue?.y ?? 0,
|
39
|
+
width: memoizedRectValue?.width ?? 0,
|
40
|
+
height: memoizedRectValue?.height ?? 0,
|
41
|
+
scrollWidth: memoizedScrollValue?.scrollWidth ?? 0,
|
42
|
+
scrollHeight: memoizedScrollValue?.scrollHeight ?? 0
|
43
|
+
};
|
44
|
+
}
|
45
|
+
export {
|
46
|
+
useClientRect
|
47
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export { UseCookiesReturn, useCookies } from './use-cookies.js';
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './use-cookies';
|
@@ -0,0 +1,39 @@
|
|
1
|
+
/**
|
2
|
+
* Custom hook to manage state with cookies.
|
3
|
+
*
|
4
|
+
* @param {string} key - The key for the cookie.
|
5
|
+
* @param {T} initialState - The initial state value.
|
6
|
+
* @param {T} defaultValues - The default values to reset to.
|
7
|
+
* @param {Object} [options] - Optional settings.
|
8
|
+
* @param {number} [options.daysUntilExpiration] - Number of days until the cookie expires.
|
9
|
+
*
|
10
|
+
* @returns {UseCookiesReturn<T>} - An object containing:
|
11
|
+
* - `state`: The current state.
|
12
|
+
* - `resetState`: A function to reset the state to default values.
|
13
|
+
* - `setState`: A function to update the state.
|
14
|
+
* - `setField`: A function to update a specific field in the state.
|
15
|
+
*
|
16
|
+
* @example
|
17
|
+
* const { state, setState, setField, resetState } = useCookies('user', { name: '', age: 0 }, { name: '', age: 0 });
|
18
|
+
*
|
19
|
+
* return (
|
20
|
+
* <div>
|
21
|
+
* <p>Name: {state.name}</p>
|
22
|
+
* <p>Age: {state.age}</p>
|
23
|
+
* <button onClick={() => setField('name', 'John')}>Set Name</button>
|
24
|
+
* <button onClick={resetState}>Reset</button>
|
25
|
+
* </div>
|
26
|
+
* );
|
27
|
+
*/
|
28
|
+
type UseCookiesReturn<T> = {
|
29
|
+
state: T;
|
30
|
+
resetState: (defaultState?: T) => void;
|
31
|
+
setState: (updateState: T | Partial<T>) => void;
|
32
|
+
setField: (name: keyof T, updateValue: T[keyof T]) => void;
|
33
|
+
};
|
34
|
+
declare function useCookies<T>(key: string, initialState?: T, options?: {
|
35
|
+
initOnLoad?: boolean;
|
36
|
+
daysUntilExpiration?: number;
|
37
|
+
}): UseCookiesReturn<T>;
|
38
|
+
|
39
|
+
export { type UseCookiesReturn, useCookies };
|
@@ -0,0 +1,110 @@
|
|
1
|
+
// src/hooks/use-cookies/use-cookies.ts
|
2
|
+
import { useMemo, useState, useEffect, useCallback } from "react";
|
3
|
+
|
4
|
+
// src/utils/cookies/cookies.ts
|
5
|
+
function getCookie(key) {
|
6
|
+
try {
|
7
|
+
const keyName = `${key}=`;
|
8
|
+
const cDecoded = decodeURIComponent(document.cookie);
|
9
|
+
const cArr = cDecoded.split("; ");
|
10
|
+
for (const val of cArr) {
|
11
|
+
if (val.startsWith(keyName)) {
|
12
|
+
const cookieValue = val.substring(keyName.length);
|
13
|
+
try {
|
14
|
+
return JSON.parse(cookieValue);
|
15
|
+
} catch {
|
16
|
+
return cookieValue;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
} catch {
|
21
|
+
return null;
|
22
|
+
}
|
23
|
+
return null;
|
24
|
+
}
|
25
|
+
function setCookie(key, value, daysUntilExpiration = 0) {
|
26
|
+
try {
|
27
|
+
const serializedValue = encodeURIComponent(JSON.stringify(value));
|
28
|
+
let cookieOptions = `${key}=${serializedValue}; path=/`;
|
29
|
+
if (daysUntilExpiration > 0) {
|
30
|
+
const expirationDate = new Date(Date.now() + daysUntilExpiration * 24 * 60 * 60 * 1e3);
|
31
|
+
cookieOptions += `; expires=${expirationDate.toUTCString()}`;
|
32
|
+
}
|
33
|
+
document.cookie = cookieOptions;
|
34
|
+
} catch (error) {
|
35
|
+
console.error("Error while setting cookie:", error);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
function removeCookie(key) {
|
39
|
+
try {
|
40
|
+
document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
|
41
|
+
} catch (error) {
|
42
|
+
console.error("Error while removing cookie:", error);
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
// src/hooks/use-cookies/use-cookies.ts
|
47
|
+
function useCookies(key, initialState, options) {
|
48
|
+
const isValuePresent = !!getCookie(key);
|
49
|
+
const storedValue = getCookie(key) ?? initialState;
|
50
|
+
const [state, set] = useState(storedValue);
|
51
|
+
const { initOnLoad = true, daysUntilExpiration } = options ?? {};
|
52
|
+
const hasMultipleValues = state && typeof state === "object";
|
53
|
+
useEffect(() => {
|
54
|
+
if (storedValue) {
|
55
|
+
if (hasMultipleValues) {
|
56
|
+
set((prevValue) => ({ ...prevValue, ...storedValue }));
|
57
|
+
} else {
|
58
|
+
set(storedValue);
|
59
|
+
}
|
60
|
+
if (!isValuePresent && initOnLoad) {
|
61
|
+
setCookie(key, storedValue, daysUntilExpiration);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}, []);
|
65
|
+
const setState = useCallback(
|
66
|
+
(updateState) => {
|
67
|
+
if (hasMultipleValues) {
|
68
|
+
set((prevValue) => {
|
69
|
+
const updatedState = { ...prevValue, ...updateState };
|
70
|
+
setCookie(key, updatedState, daysUntilExpiration);
|
71
|
+
return updatedState;
|
72
|
+
});
|
73
|
+
} else {
|
74
|
+
setCookie(key, updateState, daysUntilExpiration);
|
75
|
+
set(updateState);
|
76
|
+
}
|
77
|
+
},
|
78
|
+
[hasMultipleValues, key, daysUntilExpiration]
|
79
|
+
);
|
80
|
+
const setField = useCallback(
|
81
|
+
(name, updateValue) => {
|
82
|
+
if (hasMultipleValues) {
|
83
|
+
setState({ [name]: updateValue });
|
84
|
+
}
|
85
|
+
},
|
86
|
+
[hasMultipleValues, setState]
|
87
|
+
);
|
88
|
+
const resetState = useCallback(
|
89
|
+
(defaultState) => {
|
90
|
+
if (defaultState) {
|
91
|
+
set(defaultState);
|
92
|
+
}
|
93
|
+
removeCookie(key);
|
94
|
+
},
|
95
|
+
[key]
|
96
|
+
);
|
97
|
+
const memoizedValue = useMemo(
|
98
|
+
() => ({
|
99
|
+
state,
|
100
|
+
setState,
|
101
|
+
setField,
|
102
|
+
resetState
|
103
|
+
}),
|
104
|
+
[resetState, setField, setState, state]
|
105
|
+
);
|
106
|
+
return memoizedValue;
|
107
|
+
}
|
108
|
+
export {
|
109
|
+
useCookies
|
110
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export { CopiedValue, CopyFn, UseCopyToClipboardReturn, useCopyToClipboard } from './use-copy-to-clipboard.js';
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './use-copy-to-clipboard';
|
@@ -0,0 +1,26 @@
|
|
1
|
+
/**
|
2
|
+
* Custom hook to copy text to the clipboard.
|
3
|
+
*
|
4
|
+
* @returns {UseCopyToClipboardReturn} - An object containing:
|
5
|
+
* - `copy`: A function to copy text to the clipboard.
|
6
|
+
* - `copiedText`: The last copied text or null if nothing has been copied.
|
7
|
+
*
|
8
|
+
* @example
|
9
|
+
* const { copy, copiedText } = useCopyToClipboard();
|
10
|
+
*
|
11
|
+
* return (
|
12
|
+
* <div>
|
13
|
+
* <button onClick={() => copy('Hello, World!')}>Copy Text</button>
|
14
|
+
* {copiedText && <p>Copied: {copiedText}</p>}
|
15
|
+
* </div>
|
16
|
+
* );
|
17
|
+
*/
|
18
|
+
type UseCopyToClipboardReturn = {
|
19
|
+
copy: CopyFn;
|
20
|
+
copiedText: CopiedValue;
|
21
|
+
};
|
22
|
+
type CopiedValue = string | null;
|
23
|
+
type CopyFn = (text: string) => Promise<boolean>;
|
24
|
+
declare function useCopyToClipboard(): UseCopyToClipboardReturn;
|
25
|
+
|
26
|
+
export { type CopiedValue, type CopyFn, type UseCopyToClipboardReturn, useCopyToClipboard };
|
@@ -0,0 +1,27 @@
|
|
1
|
+
// src/hooks/use-copy-to-clipboard/use-copy-to-clipboard.ts
|
2
|
+
import { useState, useCallback } from "react";
|
3
|
+
function useCopyToClipboard() {
|
4
|
+
const [copiedText, setCopiedText] = useState(null);
|
5
|
+
const copy = useCallback(
|
6
|
+
async (text) => {
|
7
|
+
if (!navigator?.clipboard) {
|
8
|
+
console.warn("Clipboard not supported");
|
9
|
+
return false;
|
10
|
+
}
|
11
|
+
try {
|
12
|
+
await navigator.clipboard.writeText(text);
|
13
|
+
setCopiedText(text);
|
14
|
+
return true;
|
15
|
+
} catch (error) {
|
16
|
+
console.warn("Copy failed", error);
|
17
|
+
setCopiedText(null);
|
18
|
+
return false;
|
19
|
+
}
|
20
|
+
},
|
21
|
+
[setCopiedText]
|
22
|
+
);
|
23
|
+
return { copy, copiedText };
|
24
|
+
}
|
25
|
+
export {
|
26
|
+
useCopyToClipboard
|
27
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export { UseCountdownDateReturn, useCountdownDate } from './use-countdown-date.js';
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './use-countdown-date';
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/**
|
2
|
+
* Custom hook to create a countdown timer to a target date.
|
3
|
+
*
|
4
|
+
* @param {Date} targetDate - The target date to count down to.
|
5
|
+
* @param {string} [placeholder='- -'] - The placeholder value to display before the countdown starts.
|
6
|
+
*
|
7
|
+
* @returns {UseCountdownDateReturn} - An object containing the current countdown values in days, hours, minutes, and seconds.
|
8
|
+
*
|
9
|
+
* @example
|
10
|
+
* const { days, hours, minutes, seconds } = useCountdownDate(new Date('2023-12-31T23:59:59'));
|
11
|
+
*
|
12
|
+
* return (
|
13
|
+
* <div>
|
14
|
+
* <p>Days: {days}</p>
|
15
|
+
* <p>Hours: {hours}</p>
|
16
|
+
* <p>Minutes: {minutes}</p>
|
17
|
+
* <p>Seconds: {seconds}</p>
|
18
|
+
* </div>
|
19
|
+
* );
|
20
|
+
*/
|
21
|
+
type UseCountdownDateReturn = {
|
22
|
+
days: string;
|
23
|
+
hours: string;
|
24
|
+
minutes: string;
|
25
|
+
seconds: string;
|
26
|
+
};
|
27
|
+
declare function useCountdownDate(targetDate: Date, placeholder?: string): UseCountdownDateReturn;
|
28
|
+
|
29
|
+
export { type UseCountdownDateReturn, useCountdownDate };
|
@@ -0,0 +1,41 @@
|
|
1
|
+
// src/hooks/use-countdown-date/use-countdown-date.ts
|
2
|
+
import { useState, useEffect, useCallback } from "react";
|
3
|
+
function useCountdownDate(targetDate, placeholder = "- -") {
|
4
|
+
const [value, setValue] = useState({
|
5
|
+
days: placeholder,
|
6
|
+
hours: placeholder,
|
7
|
+
minutes: placeholder,
|
8
|
+
seconds: placeholder
|
9
|
+
});
|
10
|
+
const handleUpdate = useCallback(() => {
|
11
|
+
const now = /* @__PURE__ */ new Date();
|
12
|
+
const { days, hours, minutes, seconds } = calculateTimeDifference(targetDate, now);
|
13
|
+
setValue({
|
14
|
+
days: formatTime(days),
|
15
|
+
hours: formatTime(hours),
|
16
|
+
minutes: formatTime(minutes),
|
17
|
+
seconds: formatTime(seconds)
|
18
|
+
});
|
19
|
+
}, [targetDate]);
|
20
|
+
useEffect(() => {
|
21
|
+
handleUpdate();
|
22
|
+
const interval = setInterval(handleUpdate, 1e3);
|
23
|
+
return () => clearInterval(interval);
|
24
|
+
}, []);
|
25
|
+
return value;
|
26
|
+
}
|
27
|
+
function formatTime(value) {
|
28
|
+
return String(value).length === 1 ? `0${value}` : `${value}`;
|
29
|
+
}
|
30
|
+
function calculateTimeDifference(futureDate, currentDate) {
|
31
|
+
const distance = futureDate.getTime() - currentDate.getTime();
|
32
|
+
return {
|
33
|
+
days: Math.floor(distance / (1e3 * 60 * 60 * 24)),
|
34
|
+
hours: Math.floor(distance % (1e3 * 60 * 60 * 24) / (1e3 * 60 * 60)),
|
35
|
+
minutes: Math.floor(distance % (1e3 * 60 * 60) / (1e3 * 60)),
|
36
|
+
seconds: Math.floor(distance % (1e3 * 60) / 1e3)
|
37
|
+
};
|
38
|
+
}
|
39
|
+
export {
|
40
|
+
useCountdownDate
|
41
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './use-countdown-seconds';
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Custom hook to create a countdown timer in seconds.
|
5
|
+
*
|
6
|
+
* @param {number} defaultValue - The initial countdown value in seconds.
|
7
|
+
*
|
8
|
+
* @returns {UseCountdownSecondsReturn} - An object containing:
|
9
|
+
* - `value`: The current countdown value in seconds.
|
10
|
+
* - `start`: A function to start the countdown.
|
11
|
+
* - `reset`: A function to reset the countdown to the initial value.
|
12
|
+
* - `isCounting`: A boolean indicating whether the countdown is currently active.
|
13
|
+
* - `setValue`: A function to manually set the countdown value.
|
14
|
+
*
|
15
|
+
* @example
|
16
|
+
* const { value, start, reset, isCounting } = useCountdownSeconds(30);
|
17
|
+
*
|
18
|
+
* return (
|
19
|
+
* <div>
|
20
|
+
* <p>Countdown: {value} seconds</p>
|
21
|
+
* <button onClick={start} disabled={isCounting}>Start</button>
|
22
|
+
* <button onClick={reset}>Reset</button>
|
23
|
+
* </div>
|
24
|
+
* );
|
25
|
+
*/
|
26
|
+
type UseCountdownSecondsReturn = {
|
27
|
+
value: number;
|
28
|
+
start: () => void;
|
29
|
+
reset: () => void;
|
30
|
+
isCounting: boolean;
|
31
|
+
setValue: Dispatch<SetStateAction<number>>;
|
32
|
+
};
|
33
|
+
declare function useCountdownSeconds(defaultValue: number): UseCountdownSecondsReturn;
|
34
|
+
|
35
|
+
export { type UseCountdownSecondsReturn, useCountdownSeconds };
|