feat: categories and popups #171
@ -1,6 +1,7 @@
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { Event, kinds, nip19, UnsignedEvent } from 'nostr-tools'
|
import { Event, kinds, nip19, UnsignedEvent } from 'nostr-tools'
|
||||||
import React, {
|
import React, {
|
||||||
|
CSSProperties,
|
||||||
Fragment,
|
Fragment,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
@ -10,7 +11,7 @@ import React, {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { FixedSizeList as List } from 'react-window'
|
import { VariableSizeList, FixedSizeList } from 'react-window'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { T_TAG_VALUE } from '../constants'
|
import { T_TAG_VALUE } from '../constants'
|
||||||
import { useAppSelector, useGames, useNDKContext } from '../hooks'
|
import { useAppSelector, useGames, useNDKContext } from '../hooks'
|
||||||
@ -865,7 +866,7 @@ const GameDropdown = ({
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div className='dropdown-menu dropdownMainMenu dropdownMainMenuAlt'>
|
<div className='dropdown-menu dropdownMainMenu dropdownMainMenuAlt'>
|
||||||
<List
|
<FixedSizeList
|
||||||
height={500}
|
height={500}
|
||||||
width={'100%'}
|
width={'100%'}
|
||||||
itemCount={filteredOptions.length}
|
itemCount={filteredOptions.length}
|
||||||
@ -887,7 +888,7 @@ const GameDropdown = ({
|
|||||||
{filteredOptions[index].label}
|
{filteredOptions[index].label}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</List>
|
</FixedSizeList>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -992,6 +993,70 @@ export const CategoryAutocomplete = ({
|
|||||||
}))
|
}))
|
||||||
}, [selectedCategories, setFormState])
|
}, [selectedCategories, setFormState])
|
||||||
|
|
||||||
|
const listRef = useRef<VariableSizeList>(null)
|
||||||
|
const rowHeights = useRef<{ [index: number]: number }>({})
|
||||||
|
const setRowHeight = (index: number, size: number) => {
|
||||||
|
rowHeights.current = { ...rowHeights.current, [index]: size }
|
||||||
|
if (listRef.current) {
|
||||||
|
listRef.current.resetAfterIndex(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const getRowHeight = (index: number) => {
|
||||||
|
return (rowHeights.current[index] || 35) + 8
|
||||||
|
}
|
||||||
|
|
||||||
|
const Row = ({ index, style }: { index: number; style: CSSProperties }) => {
|
||||||
|
const rowRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const rowElement = rowRef.current
|
||||||
|
if (!rowElement) return
|
||||||
|
const updateHeight = () => {
|
||||||
|
const height = Math.max(rowElement.scrollHeight, 35)
|
||||||
|
setRowHeight(index, height)
|
||||||
|
}
|
||||||
|
const observer = new ResizeObserver(() => {
|
||||||
|
updateHeight()
|
||||||
|
})
|
||||||
|
observer.observe(rowElement)
|
||||||
|
updateHeight()
|
||||||
|
return () => {
|
||||||
|
observer.disconnect()
|
||||||
|
}
|
||||||
|
}, [index])
|
||||||
|
|
||||||
|
if (!filteredOptions) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={rowRef}
|
||||||
|
style={style}
|
||||||
|
className='dropdown-item dropdownMainMenuItem'
|
||||||
|
onClick={() => handleSelect(filteredOptions[index])}
|
||||||
|
>
|
||||||
|
{capitalizeEachWord(filteredOptions[index].hierarchy)}
|
||||||
|
{selectedCategories.some(
|
||||||
|
(cat) => cat.hierarchy === filteredOptions[index].hierarchy
|
||||||
|
) && (
|
||||||
|
<button
|
||||||
|
className='btn btnMain btnMainInsideField btnMainRemove'
|
||||||
|
onClick={() => handleRemove(filteredOptions[index])}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
viewBox='-32 0 512 512'
|
||||||
|
width='1em'
|
||||||
|
height='1em'
|
||||||
|
fill='currentColor'
|
||||||
|
>
|
||||||
|
<path d='M323.3 32.01H188.7C172.3 32.01 160 44.31 160 60.73V96.01H32C14.33 96.01 0 110.3 0 128S14.33 160 32 160H480c17.67 0 32-14.33 32-32.01S497.7 96.01 480 96.01H352v-35.28C352 44.31 339.7 32.01 323.3 32.01zM64.9 477.5C66.5 492.3 79.31 504 94.72 504H417.3c15.41 0 28.22-11.72 29.81-26.5L480 192.2H32L64.9 477.5z'></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='inputLabelWrapperMain'>
|
<div className='inputLabelWrapperMain'>
|
||||||
<label className='form-label labelMain'>Categories</label>
|
<label className='form-label labelMain'>Categories</label>
|
||||||
@ -1025,43 +1090,22 @@ export const CategoryAutocomplete = ({
|
|||||||
|
|
||||||
<div className='dropdown-menu dropdownMainMenu dropdownMainMenuAlt'>
|
<div className='dropdown-menu dropdownMainMenu dropdownMainMenuAlt'>
|
||||||
{filteredOptions && filteredOptions.length > 0 ? (
|
{filteredOptions && filteredOptions.length > 0 ? (
|
||||||
<List
|
<VariableSizeList
|
||||||
|
ref={listRef}
|
||||||
height={500}
|
height={500}
|
||||||
width={'100%'}
|
width={'100%'}
|
||||||
itemCount={filteredOptions.length}
|
itemCount={filteredOptions.length}
|
||||||
|
itemSize={getRowHeight}
|
||||||
|
>
|
||||||
|
{Row}
|
||||||
|
</VariableSizeList>
|
||||||
|
) : (
|
||||||
|
<FixedSizeList
|
||||||
|
height={500}
|
||||||
|
width={'100%'}
|
||||||
|
itemCount={1}
|
||||||
itemSize={35}
|
itemSize={35}
|
||||||
>
|
>
|
||||||
{({ index, style }) => (
|
|
||||||
<div
|
|
||||||
style={style}
|
|
||||||
className='dropdown-item dropdownMainMenuItem'
|
|
||||||
onClick={() => handleSelect(filteredOptions[index])}
|
|
||||||
>
|
|
||||||
{capitalizeEachWord(filteredOptions[index].hierarchy)}
|
|
||||||
{selectedCategories.some(
|
|
||||||
(cat) =>
|
|
||||||
cat.hierarchy === filteredOptions[index].hierarchy
|
|
||||||
) && (
|
|
||||||
<button
|
|
||||||
className='btn btnMain btnMainInsideField btnMainRemove'
|
|
||||||
onClick={() => handleRemove(filteredOptions[index])}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns='http://www.w3.org/2000/svg'
|
|
||||||
viewBox='-32 0 512 512'
|
|
||||||
width='1em'
|
|
||||||
height='1em'
|
|
||||||
fill='currentColor'
|
|
||||||
>
|
|
||||||
<path d='M323.3 32.01H188.7C172.3 32.01 160 44.31 160 60.73V96.01H32C14.33 96.01 0 110.3 0 128S14.33 160 32 160H480c17.67 0 32-14.33 32-32.01S497.7 96.01 480 96.01H352v-35.28C352 44.31 339.7 32.01 323.3 32.01zM64.9 477.5C66.5 492.3 79.31 504 94.72 504H417.3c15.41 0 28.22-11.72 29.81-26.5L480 192.2H32L64.9 477.5z'></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</List>
|
|
||||||
) : (
|
|
||||||
<List height={500} width={'100%'} itemCount={1} itemSize={35}>
|
|
||||||
{({ index, style }) => (
|
{({ index, style }) => (
|
||||||
<div
|
<div
|
||||||
style={style}
|
style={style}
|
||||||
@ -1093,7 +1137,7 @@ export const CategoryAutocomplete = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</List>
|
</FixedSizeList>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1103,8 +1147,9 @@ export const CategoryAutocomplete = ({
|
|||||||
{LTags.map((hierarchy) => {
|
{LTags.map((hierarchy) => {
|
||||||
const heirarchicalCategories = hierarchy.split(`:`)
|
const heirarchicalCategories = hierarchy.split(`:`)
|
||||||
const categories = heirarchicalCategories
|
const categories = heirarchicalCategories
|
||||||
.map<React.ReactNode>((c: string) => (
|
.map<React.ReactNode>((c, i) => (
|
||||||
<Link
|
<Link
|
||||||
|
key={`category-${i}`}
|
||||||
to={{
|
to={{
|
||||||
pathname: getGamePageRoute(game),
|
pathname: getGamePageRoute(game),
|
||||||
search: `c=${c}`
|
search: `c=${c}`
|
||||||
@ -1114,9 +1159,12 @@ export const CategoryAutocomplete = ({
|
|||||||
<p>{capitalizeEachWord(c)}</p>
|
<p>{capitalizeEachWord(c)}</p>
|
||||||
</Link>
|
</Link>
|
||||||
))
|
))
|
||||||
.reduce((prev, curr) => [
|
.reduce((prev, curr, i) => [
|
||||||
prev,
|
prev,
|
||||||
<div className='IBMSMSMBSSCategoriesBoxSeparator'>
|
<div
|
||||||
|
key={`separator-${i}`}
|
||||||
|
className='IBMSMSMBSSCategoriesBoxSeparator'
|
||||||
|
>
|
||||||
<p>></p>
|
<p>></p>
|
||||||
</div>,
|
</div>,
|
||||||
curr
|
curr
|
||||||
|
@ -298,7 +298,7 @@ h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dropdownMainMenuItem {
|
.dropdownMainMenuItem {
|
||||||
transition: ease 0.4s;
|
transition: background ease 0.4s, color ease 0.4s;
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
rgba(255, 255, 255, 0.03),
|
rgba(255, 255, 255, 0.03),
|
||||||
rgba(255, 255, 255, 0.03)
|
rgba(255, 255, 255, 0.03)
|
||||||
@ -319,7 +319,7 @@ h6 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dropdownMainMenuItem:hover {
|
.dropdownMainMenuItem:hover {
|
||||||
transition: ease 0.4s;
|
transition: background ease 0.4s, color ease 0.4s;
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
rgba(255, 255, 255, 0.05),
|
rgba(255, 255, 255, 0.05),
|
||||||
rgba(255, 255, 255, 0.05)
|
rgba(255, 255, 255, 0.05)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user