151 lines
3.7 KiB
TypeScript
151 lines
3.7 KiB
TypeScript
'use client';
|
||
|
||
import shared from '../../styles/shared.module.css';
|
||
import styles from '../../styles/Customizations.module.css';
|
||
import { CustomizationItem, Tab, getCosmeticIconUrl } from './types';
|
||
|
||
type Props = {
|
||
tab: Tab;
|
||
allFilteredItems: CustomizationItem[];
|
||
pagedItems: CustomizationItem[];
|
||
page: number;
|
||
totalPages: number;
|
||
search: string;
|
||
unlockedSet: Set<string>;
|
||
characterMap: Map<number, string>;
|
||
onSearchChange: (s: string) => void;
|
||
onUnlockAll: () => void;
|
||
onLockAll: () => void;
|
||
onUnlockPage: () => void;
|
||
onLockPage: () => void;
|
||
onToggle: (id: string) => void;
|
||
onPageChange: (p: number) => void;
|
||
};
|
||
|
||
export default function FlatCategory({
|
||
tab,
|
||
allFilteredItems,
|
||
pagedItems,
|
||
page,
|
||
totalPages,
|
||
search,
|
||
unlockedSet,
|
||
characterMap,
|
||
onSearchChange,
|
||
onUnlockAll,
|
||
onLockAll,
|
||
onUnlockPage,
|
||
onLockPage,
|
||
onToggle,
|
||
onPageChange
|
||
}: Props) {
|
||
const buildPageNumbers = () => {
|
||
const pages: number[] = [];
|
||
const start = Math.max(1, Math.min(page - 2, totalPages - 4));
|
||
const end = Math.min(totalPages, start + 4);
|
||
for (let i = start; i <= end; i++) pages.push(i);
|
||
return pages;
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<div className={shared.toolbar}>
|
||
<input
|
||
className={shared.searchInput}
|
||
type='text'
|
||
placeholder={`Search ${tab}...`}
|
||
value={search}
|
||
onChange={(e) => onSearchChange(e.target.value)}
|
||
/>
|
||
<span className={shared.spacer} />
|
||
<span className={shared.resultCount}>
|
||
{allFilteredItems.length} items
|
||
</span>
|
||
<button className={shared.unlockAllBtn} onClick={onUnlockAll}>
|
||
Unlock all ({allFilteredItems.length})
|
||
</button>
|
||
<button className={shared.lockAllBtn} onClick={onLockAll}>
|
||
Lock all
|
||
</button>
|
||
<button className={styles.pageActionBtn} onClick={onUnlockPage}>
|
||
Unlock visible
|
||
</button>
|
||
<button className={styles.pageActionBtn} onClick={onLockPage}>
|
||
Lock visible
|
||
</button>
|
||
</div>
|
||
|
||
{allFilteredItems.length === 0 ? (
|
||
<div className={shared.empty}>No items found</div>
|
||
) : (
|
||
<>
|
||
<div className={styles.grid}>
|
||
{pagedItems.map((item) => {
|
||
const unlocked = unlockedSet.has(item.id);
|
||
return (
|
||
<div
|
||
key={item.id}
|
||
className={`${shared.card} ${unlocked ? shared.cardUnlocked : ''}`}
|
||
onClick={() => onToggle(item.id)}
|
||
>
|
||
<img
|
||
className={shared.cardIcon}
|
||
src={getCosmeticIconUrl(item, characterMap)}
|
||
alt={item.name}
|
||
loading='lazy'
|
||
/>
|
||
<span className={shared.cardName}>{item.name}</span>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{totalPages > 1 && (
|
||
<div className={shared.pagination}>
|
||
<button
|
||
className={`${shared.pageBtn} ${page === 1 ? shared.pageBtnDisabled : ''}`}
|
||
onClick={() => onPageChange(1)}
|
||
>
|
||
«
|
||
</button>
|
||
<button
|
||
className={`${shared.pageBtn} ${page === 1 ? shared.pageBtnDisabled : ''}`}
|
||
onClick={() => onPageChange(page - 1)}
|
||
>
|
||
‹
|
||
</button>
|
||
|
||
{buildPageNumbers().map((n) => (
|
||
<button
|
||
key={n}
|
||
className={`${shared.pageBtn} ${n === page ? shared.pageBtnActive : ''}`}
|
||
onClick={() => onPageChange(n)}
|
||
>
|
||
{n}
|
||
</button>
|
||
))}
|
||
|
||
<button
|
||
className={`${shared.pageBtn} ${page === totalPages ? shared.pageBtnDisabled : ''}`}
|
||
onClick={() => onPageChange(page + 1)}
|
||
>
|
||
›
|
||
</button>
|
||
<button
|
||
className={`${shared.pageBtn} ${page === totalPages ? shared.pageBtnDisabled : ''}`}
|
||
onClick={() => onPageChange(totalPages)}
|
||
>
|
||
»
|
||
</button>
|
||
|
||
<span className={shared.pageInfo}>
|
||
{page} / {totalPages}
|
||
</span>
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
</>
|
||
);
|
||
}
|