categories,18popup,clear.TextEditorSwap,GameCardHover #177
@ -9,44 +9,48 @@ import styles from './CategoryFilterPopup.module.scss'
|
||||
interface CategoryFilterPopupProps {
|
||||
categories: string[]
|
||||
setCategories: React.Dispatch<React.SetStateAction<string[]>>
|
||||
heirarchies: string[]
|
||||
setHeirarchies: React.Dispatch<React.SetStateAction<string[]>>
|
||||
hierarchies: string[]
|
||||
setHierarchies: React.Dispatch<React.SetStateAction<string[]>>
|
||||
handleClose: () => void
|
||||
handleApply: () => void
|
||||
}
|
||||
|
||||
export const CategoryFilterPopup = ({
|
||||
categories,
|
||||
setCategories,
|
||||
heirarchies,
|
||||
setHeirarchies,
|
||||
handleClose,
|
||||
handleApply
|
||||
hierarchies,
|
||||
setHierarchies,
|
||||
handleClose
|
||||
}: CategoryFilterPopupProps) => {
|
||||
const [filterCategories, setFilterCategories] = useState(categories)
|
||||
const [filterHierarchies, setFilterHierarchies] = useState(hierarchies)
|
||||
const handleApply = () => {
|
||||
setCategories(filterCategories)
|
||||
setHierarchies(filterHierarchies)
|
||||
}
|
||||
const [inputValue, setInputValue] = useState<string>('')
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setInputValue(e.target.value)
|
||||
}
|
||||
const handleSingleSelection = (category: string, isSelected: boolean) => {
|
||||
let updatedCategories = [...categories]
|
||||
let updatedCategories = [...filterCategories]
|
||||
if (isSelected) {
|
||||
updatedCategories.push(category)
|
||||
} else {
|
||||
updatedCategories = updatedCategories.filter((item) => item !== category)
|
||||
}
|
||||
setCategories(updatedCategories)
|
||||
setFilterCategories(updatedCategories)
|
||||
}
|
||||
const handleCombinationSelection = (path: string[], isSelected: boolean) => {
|
||||
const pathString = path.join(':')
|
||||
let updatedHeirarchies = [...heirarchies]
|
||||
let updatedHierarchies = [...filterHierarchies]
|
||||
if (isSelected) {
|
||||
updatedHeirarchies.push(pathString)
|
||||
updatedHierarchies.push(pathString)
|
||||
} else {
|
||||
updatedHeirarchies = updatedHeirarchies.filter(
|
||||
updatedHierarchies = updatedHierarchies.filter(
|
||||
(item) => item !== pathString
|
||||
)
|
||||
}
|
||||
setHeirarchies(updatedHeirarchies)
|
||||
setFilterHierarchies(updatedHierarchies)
|
||||
}
|
||||
const handleAddNew = () => {
|
||||
if (inputValue) {
|
||||
@ -55,9 +59,9 @@ export const CategoryFilterPopup = ({
|
||||
.split('>')
|
||||
.map((s) => s.trim())
|
||||
if (values.length > 1) {
|
||||
setHeirarchies([...categories, values.join(':')])
|
||||
setFilterHierarchies([...filterHierarchies, values.join(':')])
|
||||
} else {
|
||||
setCategories([...categories, values[0]])
|
||||
setFilterCategories([...filterCategories, values[0]])
|
||||
}
|
||||
setInputValue('')
|
||||
}
|
||||
@ -157,8 +161,8 @@ export const CategoryFilterPopup = ({
|
||||
path={[category.name]}
|
||||
handleSingleSelection={handleSingleSelection}
|
||||
handleCombinationSelection={handleCombinationSelection}
|
||||
selectedSingles={categories}
|
||||
selectedCombinations={heirarchies}
|
||||
selectedSingles={filterCategories}
|
||||
selectedCombinations={filterHierarchies}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -181,8 +185,8 @@ export const CategoryFilterPopup = ({
|
||||
className='btn btnMain btnMainPopup'
|
||||
type='button'
|
||||
onPointerDown={() => {
|
||||
setCategories([])
|
||||
setHeirarchies([])
|
||||
setFilterCategories([])
|
||||
setFilterHierarchies([])
|
||||
}}
|
||||
>
|
||||
Reset
|
||||
|
@ -235,7 +235,7 @@ export const ModForm = ({ existingModData }: ModFormProps) => {
|
||||
}
|
||||
|
||||
// Prepend com.degmods to avoid leaking categories to 3rd party client's search
|
||||
// Add heirarchical namespaces labels
|
||||
// Add hierarchical namespaces labels
|
||||
if (formState.LTags.length > 0) {
|
||||
for (let i = 0; i < formState.LTags.length; i++) {
|
||||
tags.push(['L', `com.degmods:${formState.LTags[i]}`])
|
||||
@ -946,12 +946,12 @@ export const CategoryAutocomplete = ({
|
||||
const concatenatedValue = Array.from(uniqueValues)
|
||||
return concatenatedValue
|
||||
}
|
||||
const getSelectedHeirarchy = (cats: Categories[]) => {
|
||||
const heirarchies = cats.reduce<string[]>(
|
||||
const getSelectedhierarchy = (cats: Categories[]) => {
|
||||
const hierarchies = cats.reduce<string[]>(
|
||||
(prev, cat) => [...prev, cat.hierarchy.replace(/ > /g, ':')],
|
||||
[]
|
||||
)
|
||||
const concatenatedValue = Array.from(heirarchies)
|
||||
const concatenatedValue = Array.from(hierarchies)
|
||||
return concatenatedValue
|
||||
}
|
||||
const handleReset = () => {
|
||||
@ -989,7 +989,7 @@ export const CategoryAutocomplete = ({
|
||||
setFormState((prevState) => ({
|
||||
...prevState,
|
||||
['lTags']: getSelectedCategories(selectedCategories),
|
||||
['LTags']: getSelectedHeirarchy(selectedCategories)
|
||||
['LTags']: getSelectedhierarchy(selectedCategories)
|
||||
}))
|
||||
}, [selectedCategories, setFormState])
|
||||
|
||||
@ -1134,16 +1134,20 @@ export const CategoryAutocomplete = ({
|
||||
{LTags.length > 0 && (
|
||||
<div className='IBMSMSMBSSCategories'>
|
||||
{LTags.map((hierarchy) => {
|
||||
const heirarchicalCategories = hierarchy.split(`:`)
|
||||
const categories = heirarchicalCategories
|
||||
.map<React.ReactNode>((c, i) =>
|
||||
game ? (
|
||||
const hierarchicalCategories = hierarchy.split(`:`)
|
||||
const categories = hierarchicalCategories
|
||||
.map<React.ReactNode>((c, i) => {
|
||||
const partialHierarchy = hierarchicalCategories
|
||||
.slice(0, i + 1)
|
||||
.join(':')
|
||||
|
||||
return game ? (
|
||||
<Link
|
||||
key={`category-${i}`}
|
||||
target='_blank'
|
||||
to={{
|
||||
pathname: getGamePageRoute(game),
|
||||
search: `l=${c}`
|
||||
search: `h=${partialHierarchy}`
|
||||
}}
|
||||
className='IBMSMSMBSSCategoriesBoxItem'
|
||||
>
|
||||
@ -1154,7 +1158,7 @@ export const CategoryAutocomplete = ({
|
||||
{capitalizeEachWord(c)}
|
||||
</p>
|
||||
)
|
||||
)
|
||||
})
|
||||
.reduce((prev, curr, i) => [
|
||||
prev,
|
||||
<div
|
||||
|
@ -14,7 +14,8 @@ import {
|
||||
useLocalStorage,
|
||||
useMuteLists,
|
||||
useNDKContext,
|
||||
useNSFWList
|
||||
useNSFWList,
|
||||
useSessionStorage
|
||||
} from 'hooks'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useParams, useSearchParams } from 'react-router-dom'
|
||||
@ -54,9 +55,11 @@ export const GamePage = () => {
|
||||
const [searchTerm, setSearchTerm] = useState(searchParams.get('q') || '')
|
||||
|
||||
// Categories filter
|
||||
const [categories, setCategories] = useState(searchParams.getAll('l') || [])
|
||||
const [heirarchies, setHeirarchies] = useState(searchParams.getAll('h') || [])
|
||||
const [categories, setCategories] = useSessionStorage<string[]>('l', [])
|
||||
const [hierarchies, setHierarchies] = useSessionStorage<string[]>('h', [])
|
||||
const [showCategoryPopup, setShowCategoryPopup] = useState(false)
|
||||
const linkedHierarchy = searchParams.get('h')
|
||||
const isCategoryFilterActive = categories.length + hierarchies.length > 0
|
||||
|
||||
const handleSearch = () => {
|
||||
const value = searchTermRef.current?.value || '' // Access the input value from the ref
|
||||
@ -134,12 +137,17 @@ export const GamePage = () => {
|
||||
'#t': [T_TAG_VALUE]
|
||||
}
|
||||
|
||||
if (categories.length) {
|
||||
filter['#l'] = categories.map((l) => `com.degmods:${l}`)
|
||||
}
|
||||
// Linked category will override the filter
|
||||
if (linkedHierarchy && linkedHierarchy !== '') {
|
||||
filter['#L'] = [`com.degmods:${linkedHierarchy}`]
|
||||
} else {
|
||||
if (categories.length) {
|
||||
filter['#l'] = categories.map((l) => `com.degmods:${l}`)
|
||||
}
|
||||
|
||||
if (heirarchies.length) {
|
||||
filter['#L'] = heirarchies.map((L) => `com.degmods:${L}`)
|
||||
if (hierarchies.length) {
|
||||
filter['#L'] = hierarchies.map((L) => `com.degmods:${L}`)
|
||||
}
|
||||
}
|
||||
|
||||
const subscription = ndk.subscribe(filter, {
|
||||
@ -165,7 +173,7 @@ export const GamePage = () => {
|
||||
return () => {
|
||||
subscription.stop()
|
||||
}
|
||||
}, [gameName, ndk, categories, heirarchies])
|
||||
}, [gameName, ndk, linkedHierarchy, categories, hierarchies])
|
||||
|
||||
if (!gameName) return null
|
||||
|
||||
@ -203,26 +211,73 @@ export const GamePage = () => {
|
||||
</div>
|
||||
</div>
|
||||
<ModFilter>
|
||||
<div className='FiltersMainElement'>
|
||||
<button
|
||||
className='btn btnMain btnMainDropdown'
|
||||
type='button'
|
||||
{linkedHierarchy && linkedHierarchy !== '' ? (
|
||||
<span
|
||||
className='IBMSMSMBSSTagsTag'
|
||||
style={{
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
onClick={() => {
|
||||
setShowCategoryPopup(true)
|
||||
searchParams.delete('h')
|
||||
setSearchParams(searchParams)
|
||||
}}
|
||||
>
|
||||
Categories
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 512 512'
|
||||
viewBox='0 0 576 512'
|
||||
width='1em'
|
||||
height='1em'
|
||||
fill='currentColor'
|
||||
>
|
||||
<path d='M3.9 54.9C10.5 40.9 24.5 32 40 32l432 0c15.5 0 29.5 8.9 36.1 22.9s4.6 30.5-5.2 42.5L320 320.9 320 448c0 12.1-6.8 23.2-17.7 28.6s-23.8 4.3-33.5-3l-64-48c-8.1-6-12.8-15.5-12.8-25.6l0-79.1L9 97.3C-.7 85.4-2.8 68.8 3.9 54.9z' />
|
||||
<path d='M 3.9,22.9 C 10.5,8.9 24.5,0 40,0 h 432 c 15.5,0 29.5,8.9 36.1,22.9 6.6,14 4.6,30.5 -5.2,42.5 L 396.4,195.6 C 316.2,212.1 256,283 256,368 c 0,27.4 6.3,53.4 17.5,76.5 -1.6,-0.8 -3.2,-1.8 -4.7,-2.9 l -64,-48 C 196.7,387.6 192,378.1 192,368 V 288.9 L 9,65.3 C -0.7,53.4 -2.8,36.8 3.9,22.9 Z M 432,224 c 79.52906,0 143.99994,64.471 143.99994,144 0,79.529 -64.47088,144 -143.99994,144 -79.52906,0 -143.99994,-64.471 -143.99994,-144 0,-79.529 64.47088,-144 143.99994,-144 z' />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{linkedHierarchy.replace(/:/g, ' > ')}
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 512 512'
|
||||
width='0.8em'
|
||||
height='0.8em'
|
||||
fill='currentColor'
|
||||
>
|
||||
<path d='M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z' />
|
||||
</svg>
|
||||
</span>
|
||||
) : (
|
||||
<div className='FiltersMainElement'>
|
||||
<button
|
||||
className='btn btnMain btnMainDropdown'
|
||||
type='button'
|
||||
onClick={() => {
|
||||
setShowCategoryPopup(true)
|
||||
}}
|
||||
>
|
||||
Categories
|
||||
{isCategoryFilterActive ? (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 576 512'
|
||||
width='1em'
|
||||
height='1em'
|
||||
fill='currentColor'
|
||||
>
|
||||
<path d='M 3.9,22.9 C 10.5,8.9 24.5,0 40,0 h 432 c 15.5,0 29.5,8.9 36.1,22.9 6.6,14 4.6,30.5 -5.2,42.5 L 396.4,195.6 C 316.2,212.1 256,283 256,368 c 0,27.4 6.3,53.4 17.5,76.5 -1.6,-0.8 -3.2,-1.8 -4.7,-2.9 l -64,-48 C 196.7,387.6 192,378.1 192,368 V 288.9 L 9,65.3 C -0.7,53.4 -2.8,36.8 3.9,22.9 Z M 432,224 c 79.52906,0 143.99994,64.471 143.99994,144 0,79.529 -64.47088,144 -143.99994,144 -79.52906,0 -143.99994,-64.471 -143.99994,-144 0,-79.529 64.47088,-144 143.99994,-144 z' />
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 512 512'
|
||||
width='1em'
|
||||
height='1em'
|
||||
fill='currentColor'
|
||||
>
|
||||
<path d='M3.9 54.9C10.5 40.9 24.5 32 40 32l432 0c15.5 0 29.5 8.9 36.1 22.9s4.6 30.5-5.2 42.5L320 320.9 320 448c0 12.1-6.8 23.2-17.7 28.6s-23.8 4.3-33.5-3l-64-48c-8.1-6-12.8-15.5-12.8-25.6l0-79.1L9 97.3C-.7 85.4-2.8 68.8 3.9 54.9z' />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</ModFilter>
|
||||
|
||||
<div className='IBMSecMain IBMSMListWrapper'>
|
||||
@ -244,29 +299,11 @@ export const GamePage = () => {
|
||||
<CategoryFilterPopup
|
||||
categories={categories}
|
||||
setCategories={setCategories}
|
||||
heirarchies={heirarchies}
|
||||
setHeirarchies={setHeirarchies}
|
||||
hierarchies={hierarchies}
|
||||
setHierarchies={setHierarchies}
|
||||
handleClose={() => {
|
||||
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
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@ -543,20 +543,26 @@ const Body = ({
|
||||
{LTags.length > 0 && (
|
||||
<div className='IBMSMSMBSSCategories'>
|
||||
{LTags.map((hierarchy) => {
|
||||
const heirarchicalCategories = hierarchy.split(`:`)
|
||||
const categories = heirarchicalCategories
|
||||
.map<React.ReactNode>((c: string) => (
|
||||
<ReactRouterLink
|
||||
className='IBMSMSMBSSCategoriesBoxItem'
|
||||
target='_blank'
|
||||
to={{
|
||||
pathname: getGamePageRoute(game),
|
||||
search: `l=${c}`
|
||||
}}
|
||||
>
|
||||
<p>{capitalizeEachWord(c)}</p>
|
||||
</ReactRouterLink>
|
||||
))
|
||||
const hierarchicalCategories = hierarchy.split(`:`)
|
||||
const categories = hierarchicalCategories
|
||||
.map<React.ReactNode>((c, i) => {
|
||||
const partialHierarchy = hierarchicalCategories
|
||||
.slice(0, i + 1)
|
||||
.join(':')
|
||||
|
||||
return (
|
||||
<ReactRouterLink
|
||||
className='IBMSMSMBSSCategoriesBoxItem'
|
||||
target='_blank'
|
||||
to={{
|
||||
pathname: getGamePageRoute(game),
|
||||
search: `h=${partialHierarchy}`
|
||||
}}
|
||||
>
|
||||
<p>{capitalizeEachWord(c)}</p>
|
||||
</ReactRouterLink>
|
||||
)
|
||||
})
|
||||
.reduce((prev, curr) => [
|
||||
prev,
|
||||
<div className='IBMSMSMBSSCategoriesBoxSeparator'>
|
||||
|
Loading…
x
Reference in New Issue
Block a user