revamp template
This commit is contained in:
8
src/hooks/index.ts
Normal file
8
src/hooks/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export * from './useIsMounted';
|
||||
export * from './useMatchPath';
|
||||
export * from './useMediaQuery';
|
||||
export * from './useResponsive';
|
||||
export * from './useScrollPosition';
|
||||
export * from './useViewport';
|
||||
export * from './useBodyClasses';
|
||||
export * from './useCallApi';
|
||||
2
src/hooks/types.d.ts
vendored
Normal file
2
src/hooks/types.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export type TResponsiveBreakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number;
|
||||
export type TResponsiveQuery = 'up' | 'down' | 'between';
|
||||
22
src/hooks/useBodyClasses.ts
Normal file
22
src/hooks/useBodyClasses.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const useBodyClasses = (classNames: string) => {
|
||||
useEffect(() => {
|
||||
// Split classNames by spaces, including multi-line support
|
||||
const classes = classNames.split(/\s+/).filter(Boolean); // Filter out empty strings
|
||||
|
||||
// Add each class to the body element when the component mounts
|
||||
classes.forEach((className) => {
|
||||
document.body.classList.add(className);
|
||||
});
|
||||
|
||||
// Cleanup function to remove classes when the component unmounts
|
||||
return () => {
|
||||
classes.forEach((className) => {
|
||||
document.body.classList.remove(className);
|
||||
});
|
||||
};
|
||||
}, [classNames]); // Re-run the effect if classNames changes
|
||||
};
|
||||
|
||||
export default useBodyClasses;
|
||||
91
src/hooks/useCallApi.ts
Normal file
91
src/hooks/useCallApi.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { useCallback } from 'react';
|
||||
import axios from 'axios';
|
||||
import { jsonToQueryParams } from '@/utils';
|
||||
|
||||
const useCallApi = () => {
|
||||
const GetExportData = useCallback(async (url: string, param: any, fileName: string) => {
|
||||
try {
|
||||
url = `${url}?` + jsonToQueryParams(param);
|
||||
|
||||
const currentDate = new Date().toISOString().split('T')[0];
|
||||
fileName = `${fileName}${currentDate}.xlsx`;
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
const { response } = error;
|
||||
return {
|
||||
status: false,
|
||||
message: response?.data?.error ?? response?.data?.message ?? 'Unknown error',
|
||||
data: null
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
const GetData = useCallback(async (url: string, field: any) => {
|
||||
try {
|
||||
const response = await axios.get(url, { params: field });
|
||||
const result = response.data;
|
||||
|
||||
if (result.status) {
|
||||
return { status: true, message: 'Success fetch data', data: result.data };
|
||||
}
|
||||
} catch (error: any) {
|
||||
const { response } = error;
|
||||
return { status: false, message: response.data.error ?? response.data.message, data: null };
|
||||
}
|
||||
}, []);
|
||||
|
||||
const PostData = useCallback(async (url: string, field: any) => {
|
||||
try {
|
||||
const response = await axios.post(url, field);
|
||||
const result = response.data;
|
||||
|
||||
if (result.status) {
|
||||
return { status: true, message: result.data };
|
||||
}
|
||||
} catch (error: any) {
|
||||
const { response } = error;
|
||||
return { status: false, message: response.data.error ?? response.data.message };
|
||||
}
|
||||
}, []);
|
||||
|
||||
const PutData = useCallback(async (url: string, field: any) => {
|
||||
try {
|
||||
const response = await axios.put(url, field);
|
||||
const result = response.data;
|
||||
|
||||
if (result.status) {
|
||||
return { status: true, message: result.data };
|
||||
}
|
||||
} catch (error: any) {
|
||||
const { response } = error;
|
||||
return { status: false, message: response.data.error ?? response.data.message };
|
||||
}
|
||||
}, []);
|
||||
|
||||
const DeleteData = useCallback(async (url: string, field: any) => {
|
||||
try {
|
||||
const response = await axios.delete(url, field);
|
||||
const result = response.data;
|
||||
|
||||
if (result.status) {
|
||||
return { status: true, message: result.data };
|
||||
}
|
||||
} catch (error: any) {
|
||||
const { response } = error;
|
||||
return { status: false, message: response.data.error ?? response.data.message };
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { GetData, PostData, PutData, DeleteData, GetExportData };
|
||||
};
|
||||
|
||||
export { useCallApi };
|
||||
18
src/hooks/useIsMounted.ts
Normal file
18
src/hooks/useIsMounted.ts
Normal file
@ -0,0 +1,18 @@
|
||||
// see: https://usehooks-ts.com/react-hook/use-is-mounted
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
const useIsMounted = () => {
|
||||
const isMounted = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
isMounted.current = true;
|
||||
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return useCallback(() => isMounted.current, []);
|
||||
};
|
||||
|
||||
export { useIsMounted };
|
||||
24
src/hooks/useMatchPath.ts
Normal file
24
src/hooks/useMatchPath.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { matchPath, useLocation } from 'react-router-dom';
|
||||
|
||||
interface IUseMatchPath {
|
||||
match: boolean;
|
||||
isExternal: boolean;
|
||||
}
|
||||
|
||||
const useMatchPath = (path: string, mode = 'default'): IUseMatchPath => {
|
||||
const { pathname } = useLocation();
|
||||
let match: boolean = false;
|
||||
|
||||
if (mode === 'default' && matchPath({ path, end: true }, pathname)) {
|
||||
match = true;
|
||||
} else if (mode === 'full' && matchPath({ path, end: false }, pathname)) {
|
||||
match = true;
|
||||
}
|
||||
|
||||
return {
|
||||
match,
|
||||
isExternal: path.startsWith('http') || path.startsWith('//')
|
||||
};
|
||||
};
|
||||
|
||||
export { useMatchPath };
|
||||
34
src/hooks/useMediaQuery.ts
Normal file
34
src/hooks/useMediaQuery.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const getMatches = (query: string): boolean => {
|
||||
// Prevents SSR issues
|
||||
if (typeof window !== 'undefined') {
|
||||
return window.matchMedia(query).matches;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const useMediaQuery = (query: string): boolean => {
|
||||
const [matches, setMatches] = useState<boolean>(getMatches(query));
|
||||
|
||||
useEffect(() => {
|
||||
function handleChange() {
|
||||
setMatches(getMatches(query));
|
||||
}
|
||||
|
||||
const matchMedia = window.matchMedia(query);
|
||||
|
||||
// Triggered at the first client-side load and if query changes
|
||||
handleChange();
|
||||
|
||||
matchMedia.addEventListener('change', handleChange);
|
||||
|
||||
return () => {
|
||||
matchMedia.removeEventListener('change', handleChange);
|
||||
};
|
||||
}, [query]);
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
export { useMediaQuery };
|
||||
38
src/hooks/useResponsive.ts
Normal file
38
src/hooks/useResponsive.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import tailwindConfig from 'tailwindcss/defaultConfig';
|
||||
import { useMediaQuery } from './useMediaQuery';
|
||||
|
||||
export type TResponsiveBreakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | number;
|
||||
export type TResponsiveQuery = 'up' | 'down' | 'between';
|
||||
|
||||
const breakpoints: TResponsiveBreakpoint[] = ['sm', 'md', 'lg', 'xl', '2xl'];
|
||||
|
||||
const useResponsive = (
|
||||
query: TResponsiveQuery,
|
||||
key?: TResponsiveBreakpoint,
|
||||
start?: TResponsiveBreakpoint,
|
||||
end?: TResponsiveBreakpoint
|
||||
) => {
|
||||
const screens = tailwindConfig?.theme?.screens as Record<string, TResponsiveBreakpoint>;
|
||||
|
||||
if (query === 'up' && key) {
|
||||
key = breakpoints.includes(key) && screens ? screens[key] : key;
|
||||
|
||||
return useMediaQuery(`(min-width: ${key})`);
|
||||
}
|
||||
|
||||
if (query === 'down' && key) {
|
||||
key = breakpoints.includes(key) && screens ? screens[key] : key;
|
||||
|
||||
return useMediaQuery(`(max-width: ${key})`);
|
||||
}
|
||||
|
||||
if (query === 'between' && start && end) {
|
||||
start = breakpoints.includes(start) && screens ? screens[start] : start;
|
||||
end = breakpoints.includes(end) && screens ? screens[end] : end;
|
||||
|
||||
return useMediaQuery(`(min-width: ${start}) and (max-width: ${end})`);
|
||||
}
|
||||
};
|
||||
|
||||
export { useResponsive };
|
||||
23
src/hooks/useResponsiveProp.ts
Normal file
23
src/hooks/useResponsiveProp.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { useResponsive, TResponsiveBreakpoint } from '.';
|
||||
|
||||
export default function useResponsiveProp(prop: any, defaultProp: any = null) {
|
||||
let value = prop;
|
||||
|
||||
if (prop) {
|
||||
for (const condition in prop) {
|
||||
const breakpoint = prop[condition] as TResponsiveBreakpoint;
|
||||
if (condition === 'up' && useResponsive('up', breakpoint)) {
|
||||
value = prop[condition][breakpoint];
|
||||
} else if (condition === 'down' && useResponsive('down', breakpoint)) {
|
||||
value = prop[condition][breakpoint];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = value ?? defaultProp;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export { useResponsiveProp };
|
||||
34
src/hooks/useScrollPosition.ts
Normal file
34
src/hooks/useScrollPosition.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { useEffect, useState, RefObject } from 'react';
|
||||
|
||||
interface IUseScrollPositionProps {
|
||||
targetRef?: RefObject<HTMLElement | Document | undefined>; // Ref to the scrollable element
|
||||
}
|
||||
|
||||
const useScrollPosition = ({ targetRef }: IUseScrollPositionProps = {}): number => {
|
||||
const [scrollPosition, setScrollPosition] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
// If the ref is not provided or its current value is null, fall back to document
|
||||
const target = targetRef?.current || document;
|
||||
const scrollable = target === document ? window : target;
|
||||
|
||||
const updatePosition = () => {
|
||||
// Determine if we're scrolling the document or a specific element
|
||||
const scrollY = target === document ? window.scrollY : (target as HTMLElement).scrollTop;
|
||||
setScrollPosition(scrollY);
|
||||
};
|
||||
|
||||
scrollable.addEventListener('scroll', updatePosition);
|
||||
|
||||
// Set the initial position
|
||||
updatePosition();
|
||||
|
||||
return () => {
|
||||
scrollable.removeEventListener('scroll', updatePosition);
|
||||
};
|
||||
}, [targetRef]);
|
||||
|
||||
return scrollPosition;
|
||||
};
|
||||
|
||||
export { useScrollPosition };
|
||||
26
src/hooks/useViewport.ts
Normal file
26
src/hooks/useViewport.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
type TUseViewport = [number, number];
|
||||
|
||||
const useViewport = (): TUseViewport => {
|
||||
const [dimensions, setDimensions] = useState<TUseViewport>([
|
||||
window.innerHeight,
|
||||
window.innerWidth
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = (): void => {
|
||||
setDimensions([window.innerHeight, window.innerWidth]);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return dimensions;
|
||||
};
|
||||
|
||||
export { useViewport };
|
||||
Reference in New Issue
Block a user