diff --git a/src/components/Filters/CategoryFilterPopup.module.scss b/src/components/Filters/CategoryFilterPopup.module.scss new file mode 100644 index 0000000..752a21e --- /dev/null +++ b/src/components/Filters/CategoryFilterPopup.module.scss @@ -0,0 +1,3 @@ +.noResult:not(:only-child) { + display: none; +} diff --git a/src/components/Filters/CategoryFilterPopup.tsx b/src/components/Filters/CategoryFilterPopup.tsx index 2257fa6..06f287e 100644 --- a/src/components/Filters/CategoryFilterPopup.tsx +++ b/src/components/Filters/CategoryFilterPopup.tsx @@ -1,3 +1,371 @@ -export const CategoryFilterPopup = () => { - return <>Popup +import React, { useEffect, useState } from 'react' +import { createPortal } from 'react-dom' +import { Category } from 'types' +import categoriesData from './../../assets/categories/categories.json' +import { capitalizeEachWord } from 'utils' + +import styles from './CategoryFilterPopup.module.scss' + +interface CategoryFilterPopupProps { + categories: string[] + setCategories: React.Dispatch> + heirarchies: string[] + setHeirarchies: React.Dispatch> + handleClose: () => void + handleApply: () => void +} + +export const CategoryFilterPopup = ({ + categories, + setCategories, + heirarchies, + setHeirarchies, + handleClose, + handleApply +}: CategoryFilterPopupProps) => { + const [inputValue, setInputValue] = useState('') + const handleInputChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value) + } + const handleSingleSelection = (category: string, isSelected: boolean) => { + let updatedCategories = [...categories] + if (isSelected) { + updatedCategories.push(category) + } else { + updatedCategories = updatedCategories.filter((item) => item !== category) + } + setCategories(updatedCategories) + } + const handleCombinationSelection = (path: string[], isSelected: boolean) => { + const pathString = path.join(':') + let updatedHeirarchies = [...heirarchies] + if (isSelected) { + updatedHeirarchies.push(pathString) + } else { + updatedHeirarchies = updatedHeirarchies.filter( + (item) => item !== pathString + ) + } + setHeirarchies(updatedHeirarchies) + } + const handleAddNew = () => { + if (inputValue) { + const values = inputValue + .trim() + .split('>') + .map((s) => s.trim()) + if (values.length > 1) { + setHeirarchies([...categories, values.join(':')]) + } else { + setCategories([...categories, values[0]]) + } + setInputValue('') + } + } + + return createPortal( +
+
+
+
+
+
+

Categories filter

+
+
+ + + +
+
+
+
+
+ +

+ This is description for an input and how to use search here +

+
+ + {true && ( +
+ +

Maybe

+
+ )} +
+
+
+
No results.
+
+
+ Search for "{inputValue}" category + +
+
+ {(categoriesData as Category[]).map((category) => ( + + ))} +
+
+
+ + + +
+
+
+
+
+
+
, + document.body + ) +} + +interface CategoryCheckboxProps { + inputValue: string + category: Category | string + path: string[] + handleSingleSelection: (category: string, isSelected: boolean) => void + handleCombinationSelection: (path: string[], isSelected: boolean) => void + selectedSingles: string[] + selectedCombinations: string[] + indentLevel?: number +} + +const CategoryCheckbox: React.FC = ({ + inputValue, + category, + path, + handleSingleSelection, + handleCombinationSelection, + selectedSingles, + selectedCombinations, + indentLevel = 0 +}) => { + const name = typeof category === 'string' ? category : category.name + const isMatching = path + .join(' > ') + .toLowerCase() + .includes(inputValue.toLowerCase()) + const [isSingleChecked, setIsSingleChecked] = useState(false) + const [isCombinationChecked, setIsCombinationChecked] = + useState(false) + const [isIndeterminate, setIsIndeterminate] = useState(false) + + useEffect(() => { + const pathString = path.join(':') + setIsSingleChecked(selectedSingles.includes(name)) + setIsCombinationChecked(selectedCombinations.includes(pathString)) + + const childPaths = + category.sub && Array.isArray(category.sub) + ? category.sub.map((sub) => + typeof sub === 'string' + ? [...path, sub].join(':') + : [...path, sub.name].join(':') + ) + : [] + const anyChildCombinationSelected = childPaths.some((childPath) => + selectedCombinations.includes(childPath) + ) + + if ( + anyChildCombinationSelected && + !selectedCombinations.includes(pathString) + ) { + setIsIndeterminate(true) + } else { + setIsIndeterminate(false) + } + }, [selectedSingles, selectedCombinations, path, name, category.sub]) + + const handleSingleChange = () => { + setIsSingleChecked(!isSingleChecked) + handleSingleSelection(name, !isSingleChecked) + } + + const handleCombinationChange = () => { + setIsCombinationChecked(!isCombinationChecked) + handleCombinationSelection(path, !isCombinationChecked) + } + + return ( + <> + {isMatching && ( +
+
+ { + if (input) { + input.indeterminate = isIndeterminate + } + }} + className='CheckboxMain' + checked={isCombinationChecked} + onChange={handleCombinationChange} + /> + + +
+
+ )} + {typeof category !== 'string' && + category.sub && + Array.isArray(category.sub) && ( + <> + {category.sub.map((subCategory) => { + if (typeof subCategory === 'string') { + return ( + + ) + } else { + return ( + + ) + } + })} + + )} + + ) } diff --git a/src/pages/game.tsx b/src/pages/game.tsx index ea81648..9c8d7d6 100644 --- a/src/pages/game.tsx +++ b/src/pages/game.tsx @@ -55,9 +55,7 @@ export const GamePage = () => { // Categories filter const [categories, setCategories] = useState(searchParams.getAll('l') || []) - const [heirarchies, setFullHeirarchies] = useState( - searchParams.getAll('h') || [] - ) + const [heirarchies, setHeirarchies] = useState(searchParams.getAll('h') || []) const [showCategoryPopup, setShowCategoryPopup] = useState(false) const handleSearch = () => { @@ -242,7 +240,35 @@ export const GamePage = () => { - {showCategoryPopup && } + {showCategoryPopup && ( + { + setShowCategoryPopup(false) + }} + handleApply={() => { + searchParams.delete('l') + searchParams.delete('h') + categories.forEach((l) => { + if (l) { + searchParams.delete('h') + searchParams.append('l', l) + } + }) + heirarchies.forEach((h) => { + if (h) { + searchParams.append('h', h) + } + }) + setSearchParams(searchParams, { + replace: true + }) + }} + /> + )} ) }