diff --git a/src/app/hooks/index.js b/src/app/hooks/index.js index 68e6a53..4e6ba13 100644 --- a/src/app/hooks/index.js +++ b/src/app/hooks/index.js @@ -1,20 +1,3 @@ -import { useState, useEffect } from 'react'; - -export 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; -} +export { default as useWindowDimensions } from './useWindowDimensions'; +export { default as useOnClickOutside } from './useOnClickOutside'; +export { default as usePropertiesHeader } from './usePropertiesHeader'; diff --git a/src/app/hooks/useOnClickOutside.js b/src/app/hooks/useOnClickOutside.js new file mode 100644 index 0000000..98cc268 --- /dev/null +++ b/src/app/hooks/useOnClickOutside.js @@ -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]); +} diff --git a/src/app/hooks/usePropertiesHeader.js b/src/app/hooks/usePropertiesHeader.js new file mode 100644 index 0000000..6e3d4d1 --- /dev/null +++ b/src/app/hooks/usePropertiesHeader.js @@ -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 }; +} diff --git a/src/app/hooks/useWindowDimensions.js b/src/app/hooks/useWindowDimensions.js new file mode 100644 index 0000000..8991ab0 --- /dev/null +++ b/src/app/hooks/useWindowDimensions.js @@ -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; +}