158 lines
5.2 KiB
JavaScript
158 lines
5.2 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
|
import GameDataManager from "../../../services/GameDataManager.js";
|
|
import "./styles/ItemListTab.css";
|
|
import { config } from "../../../config/api.js";
|
|
|
|
const ItemListTab = () => {
|
|
const [allItems, setAllItems] = useState([]);
|
|
const [filteredItems, setFilteredItems] = useState([]);
|
|
const [searchQuery, setSearchQuery] = useState("");
|
|
const [selectedCategory, setSelectedCategory] = useState("all");
|
|
const [selectedItem, setSelectedItem] = useState(null);
|
|
const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
|
|
|
|
const ASSET_BASE_URL = `${config.serverUrl}/static/`;
|
|
|
|
useEffect(() => {
|
|
const handleResize = () => setIsMobile(window.innerWidth <= 768);
|
|
window.addEventListener("resize", handleResize);
|
|
|
|
const itemsArray = Array.from(GameDataManager.items.keys()).map((id) =>
|
|
GameDataManager.getItem(id),
|
|
);
|
|
setAllItems(itemsArray);
|
|
setFilteredItems(itemsArray);
|
|
|
|
return () => window.removeEventListener("resize", handleResize);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
let result = allItems;
|
|
if (selectedCategory !== "all") {
|
|
result = result.filter(
|
|
(item) => item.meta?.category === selectedCategory,
|
|
);
|
|
}
|
|
if (searchQuery) {
|
|
const q = searchQuery.toLowerCase();
|
|
result = result.filter(
|
|
(item) =>
|
|
item.displayName.toLowerCase().includes(q) ||
|
|
item.id.toLowerCase().includes(q),
|
|
);
|
|
}
|
|
setFilteredItems(result);
|
|
}, [searchQuery, selectedCategory, allItems]);
|
|
|
|
const getFullTextureUrl = (path) => {
|
|
if (!path) return "/assets/no-image.png";
|
|
return path.startsWith("http") ? path : `${ASSET_BASE_URL}${path}`;
|
|
};
|
|
|
|
const categories = [
|
|
"all",
|
|
...new Set(allItems.map((i) => i.meta?.category).filter(Boolean)),
|
|
];
|
|
|
|
const renderInspector = () => (
|
|
<div
|
|
className={`item-inspector ${isMobile && selectedItem ? "mobile-modal" : ""}`}
|
|
onClick={() => isMobile && setSelectedItem(null)}
|
|
>
|
|
{selectedItem && (
|
|
<div className="inspector-content" onClick={(e) => e.stopPropagation()}>
|
|
{isMobile && (
|
|
<button
|
|
className="close-inspector"
|
|
onClick={() => setSelectedItem(null)}
|
|
>
|
|
×
|
|
</button>
|
|
)}
|
|
|
|
<div className={`inspector-header ${selectedItem.meta?.rarity}`}>
|
|
<div className="header-visual">
|
|
<img src={getFullTextureUrl(selectedItem.texture)} alt="" />
|
|
</div>
|
|
<div className="header-info">
|
|
<div className="id-tag">{selectedItem.id}</div>
|
|
<h1>{selectedItem.displayName}</h1>
|
|
<span className="rarity-label">
|
|
{selectedItem.meta?.rarity?.toUpperCase()}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="inspector-grid">
|
|
<div className="inspector-section">
|
|
<div className="section-title">DATA_DESCRIPTION</div>
|
|
<p>{selectedItem.description}</p>
|
|
</div>
|
|
|
|
{selectedItem.stats &&
|
|
Object.keys(selectedItem.stats).length > 0 && (
|
|
<div className="inspector-section">
|
|
<div className="section-title">PARAMETER_READOUT</div>
|
|
<div className="stat-pills">
|
|
{Object.entries(selectedItem.stats).map(([k, v]) => (
|
|
<div key={k} className="stat-pill">
|
|
<span>{GameDataManager.getStatName(k)}</span>
|
|
<span>+{v}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div className="item-list-container">
|
|
<div className="item-list-sidebar">
|
|
<div className="search-box">
|
|
<input
|
|
type="text"
|
|
placeholder="SEARCH_ID_OR_NAME..."
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div className="category-filters">
|
|
{categories.map((cat) => (
|
|
<button
|
|
key={cat}
|
|
className={`filter-btn ${selectedCategory === cat ? "active" : ""}`}
|
|
onClick={() => setSelectedCategory(cat)}
|
|
>
|
|
{cat.toUpperCase()}
|
|
</button>
|
|
))}
|
|
</div>
|
|
<div className="items-list-scroll">
|
|
{filteredItems.map((item) => (
|
|
<div
|
|
key={item.id}
|
|
className={`item-row ${selectedItem?.id === item.id ? "selected" : ""} ${item.meta?.rarity}`}
|
|
onClick={() => setSelectedItem(item)}
|
|
>
|
|
<div className="item-row-icon">
|
|
<img src={getFullTextureUrl(item.texture)} alt="" />
|
|
</div>
|
|
<div className="item-row-content">
|
|
<div className="item-row-title">{item.displayName}</div>
|
|
<div className="item-row-subtitle">{item.id}</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
{(!isMobile || (isMobile && selectedItem)) && renderInspector()}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ItemListTab;
|