RC-10: add new custom hooks
This commit is contained in:
@@ -1,20 +1,3 @@
|
|||||||
import { useState, useEffect } from 'react';
|
export { default as useWindowDimensions } from './useWindowDimensions';
|
||||||
|
export { default as useOnClickOutside } from './useOnClickOutside';
|
||||||
export function useWindowDimensions() {
|
export { default as usePropertiesHeader } from './usePropertiesHeader';
|
||||||
const getWindowDimensions = () => {
|
|
||||||
const { innerWidth: width, innerHeight: height } = window;
|
|
||||||
|
|
||||||
return { width, height };
|
|
||||||
};
|
|
||||||
const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const onRecize = () => setWindowDimensions(getWindowDimensions());
|
|
||||||
|
|
||||||
window.addEventListener('resize', onRecize);
|
|
||||||
|
|
||||||
return () => window.removeEventListener('resize', onRecize);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return windowDimensions;
|
|
||||||
}
|
|
||||||
|
|||||||
29
src/app/hooks/useOnClickOutside.js
Normal file
29
src/app/hooks/useOnClickOutside.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
export default function useOnClickOutside(ref, handler) {
|
||||||
|
useEffect(() => {
|
||||||
|
const onEvent = (event) => {
|
||||||
|
if (!ref.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event instanceof KeyboardEvent && event.key === 'Escape') {
|
||||||
|
handler(event);
|
||||||
|
} else if (ref.current.contains(event.target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', onEvent);
|
||||||
|
document.addEventListener('touchstart', onEvent);
|
||||||
|
document.addEventListener('keydown', onEvent);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', onEvent);
|
||||||
|
document.removeEventListener('touchstart', onEvent);
|
||||||
|
document.removeEventListener('keydown', onEvent);
|
||||||
|
};
|
||||||
|
}, [ref, handler]);
|
||||||
|
}
|
||||||
105
src/app/hooks/usePropertiesHeader.js
Normal file
105
src/app/hooks/usePropertiesHeader.js
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import { PROPERTIES_LAYOUTS } from 'app/configs/consts';
|
||||||
|
import { useState, useMemo, useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
|
export default function usePropertiesHeader(items) {
|
||||||
|
const [categories, setCategories] = useState([
|
||||||
|
{
|
||||||
|
name: 'all',
|
||||||
|
amount: 0,
|
||||||
|
active: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const [layouts, setLayouts] = useState([
|
||||||
|
{ name: PROPERTIES_LAYOUTS.list, active: true },
|
||||||
|
{ name: PROPERTIES_LAYOUTS.grid, active: false },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const activeCategory = useMemo(
|
||||||
|
() => categories.find(({ active }) => active)?.name ?? categories[0].name,
|
||||||
|
[categories]
|
||||||
|
);
|
||||||
|
const activeLayout = useMemo(
|
||||||
|
() => layouts.find(({ active }) => active)?.name ?? PROPERTIES_LAYOUTS.list,
|
||||||
|
[layouts]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onCategory = useCallback(
|
||||||
|
(value) => {
|
||||||
|
if (value === activeCategory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCategories((prevState) =>
|
||||||
|
prevState.map((category) => ({ ...category, active: category.name === value }))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[activeCategory]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLayout = useCallback(
|
||||||
|
(value) => {
|
||||||
|
if (value === activeLayout) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLayouts((prevState) => prevState.map(({ name }) => ({ name, active: name === value })));
|
||||||
|
},
|
||||||
|
[activeLayout]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onItemDelete = (itemCategory) => {
|
||||||
|
setCategories((prevState) => {
|
||||||
|
const isItemCategoryLast =
|
||||||
|
prevState.find((category) => category.name === itemCategory)?.amount === 1;
|
||||||
|
|
||||||
|
return prevState
|
||||||
|
.map((category, idx) => {
|
||||||
|
if (!idx) {
|
||||||
|
return {
|
||||||
|
name: category.name,
|
||||||
|
amount: category.amount - 1,
|
||||||
|
active: isItemCategoryLast,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category?.name === itemCategory) {
|
||||||
|
if (category.amount > 1) {
|
||||||
|
return { ...category, amount: category.amount - 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return category;
|
||||||
|
})
|
||||||
|
.filter((category) => category);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let updatedCategories = [...categories];
|
||||||
|
items.forEach((item) => {
|
||||||
|
const hasItemCategory = updatedCategories.find((category) => category.name === item.category);
|
||||||
|
updatedCategories = updatedCategories.map((category, idx) => {
|
||||||
|
if (!idx) {
|
||||||
|
category.amount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return category;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasItemCategory) {
|
||||||
|
updatedCategories = updatedCategories.map((category) => ({
|
||||||
|
...category,
|
||||||
|
amount: item.category === category.name ? category.amount + 1 : category.amount,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
updatedCategories.push({ name: item.category, amount: 1, active: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setCategories(updatedCategories);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { categories, activeCategory, onCategory, layouts, activeLayout, onLayout, onItemDelete };
|
||||||
|
}
|
||||||
20
src/app/hooks/useWindowDimensions.js
Normal file
20
src/app/hooks/useWindowDimensions.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
export default function useWindowDimensions() {
|
||||||
|
const getWindowDimensions = () => {
|
||||||
|
const { innerWidth: width, innerHeight: height } = window;
|
||||||
|
|
||||||
|
return { width, height };
|
||||||
|
};
|
||||||
|
const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onRecize = () => setWindowDimensions(getWindowDimensions());
|
||||||
|
|
||||||
|
window.addEventListener('resize', onRecize);
|
||||||
|
|
||||||
|
return () => window.removeEventListener('resize', onRecize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return windowDimensions;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user