Spaces:
Sleeping
Sleeping
| 'use client' | |
| import { useState, useMemo } from 'react' | |
| import { useUIStore } from '@/stores/uiStore' | |
| import { usePlayerStore } from '@/stores/playerStore' | |
| import { BLOCK_DEFS, CREATIVE_BLOCKS, BLOCK_CATEGORIES } from '@/engine/constants' | |
| const CATEGORY_TABS = [ | |
| { key: 'all', label: 'All' }, | |
| { key: 'Natural', label: 'Natural' }, | |
| { key: 'Wood', label: 'Wood' }, | |
| { key: 'Ores', label: 'Ores' }, | |
| { key: 'Wool', label: 'Wool' }, | |
| { key: 'Concrete', label: 'Concrete' }, | |
| { key: 'Glass', label: 'Glass' }, | |
| { key: 'Bricks', label: 'Bricks' }, | |
| { key: 'Functional', label: 'Functional' }, | |
| { key: 'Redstone', label: 'Redstone' }, | |
| { key: 'Nether', label: 'Nether' }, | |
| { key: 'End', label: 'End' }, | |
| { key: 'Plants', label: 'Plants' }, | |
| { key: 'Lights', label: 'Lights' }, | |
| { key: 'Mineral Blocks', label: 'Minerals' }, | |
| { key: 'Copper', label: 'Copper' }, | |
| { key: 'Terracotta', label: 'Terracotta' }, | |
| ] | |
| export function CreativeInventory() { | |
| const { showInventory, setShowInventory } = useUIStore() | |
| const { player } = usePlayerStore() | |
| const [activeTab, setActiveTab] = useState('all') | |
| const [searchQuery, setSearchQuery] = useState('') | |
| if (!showInventory || !player) return null | |
| const handleSelect = (blockId: number) => { | |
| const { hotbarSlot } = useUIStore.getState() | |
| const newHotbar = [...player.inventory.hotbar] | |
| newHotbar[hotbarSlot] = { id: blockId, count: 64, damage: 0 } | |
| usePlayerStore.setState({ | |
| player: { ...player, inventory: { ...player.inventory, hotbar: newHotbar } } | |
| }) | |
| } | |
| // Get blocks for current tab | |
| const displayBlocks = useMemo(() => { | |
| let blocks: number[] | |
| if (activeTab === 'all') { | |
| blocks = CREATIVE_BLOCKS | |
| } else { | |
| blocks = BLOCK_CATEGORIES[activeTab] || [] | |
| } | |
| // Apply search filter | |
| if (searchQuery) { | |
| const query = searchQuery.toLowerCase() | |
| blocks = blocks.filter(id => { | |
| const def = BLOCK_DEFS[id] | |
| return def && def.name.toLowerCase().includes(query) | |
| }) | |
| } | |
| return blocks | |
| }, [activeTab, searchQuery]) | |
| return ( | |
| <div className="absolute inset-0 bg-black/70 flex items-center justify-center z-50" | |
| onClick={(e) => { if (e.target === e.currentTarget) setShowInventory(false) }}> | |
| <div className="bg-[#1a1a2e]/95 border border-gray-600/50 rounded-lg w-full max-w-2xl mx-4 max-h-[85vh] flex flex-col backdrop-blur-sm shadow-2xl"> | |
| {/* Header */} | |
| <div className="flex items-center justify-between p-3 border-b border-gray-700/50"> | |
| <h3 className="text-white text-lg font-bold">Creative Inventory</h3> | |
| <button className="text-gray-400 hover:text-white text-xl px-2" | |
| onClick={() => setShowInventory(false)}>✕</button> | |
| </div> | |
| {/* Search */} | |
| <div className="px-3 pt-2"> | |
| <input | |
| type="text" | |
| value={searchQuery} | |
| onChange={(e) => setSearchQuery(e.target.value)} | |
| placeholder="Search blocks..." | |
| className="w-full px-3 py-1.5 bg-gray-800/80 border border-gray-600/50 rounded text-white text-sm placeholder-gray-500 focus:outline-none focus:border-blue-500" | |
| /> | |
| </div> | |
| {/* Category tabs */} | |
| <div className="flex flex-wrap gap-1 px-3 py-2 overflow-x-auto"> | |
| {CATEGORY_TABS.map(tab => ( | |
| <button | |
| key={tab.key} | |
| className={`px-2 py-0.5 text-xs rounded transition-colors ${ | |
| activeTab === tab.key | |
| ? 'bg-blue-600 text-white' | |
| : 'bg-gray-700/60 text-gray-300 hover:bg-gray-600/60' | |
| }`} | |
| onClick={() => setActiveTab(tab.key)} | |
| > | |
| {tab.label} | |
| </button> | |
| ))} | |
| </div> | |
| {/* Block grid */} | |
| <div className="flex-1 overflow-y-auto p-3"> | |
| <div className="grid grid-cols-9 gap-1"> | |
| {displayBlocks.map((blockId) => { | |
| const def = BLOCK_DEFS[blockId] | |
| if (!def) return null | |
| return ( | |
| <div | |
| key={blockId} | |
| className="w-9 h-9 sm:w-10 sm:h-10 rounded cursor-pointer border border-gray-700/50 hover:border-white/80 flex items-center justify-center transition-all duration-75 hover:scale-110 relative group" | |
| style={{ backgroundColor: def.color + '44' }} | |
| onClick={() => handleSelect(blockId)} | |
| title={def.name} | |
| > | |
| <div className="w-6 h-6 rounded-sm shadow-inner" style={{ backgroundColor: def.color }} /> | |
| <div className="hidden group-hover:block absolute -top-8 left-1/2 -translate-x-1/2 bg-black/90 text-white text-xs px-2 py-0.5 rounded whitespace-nowrap z-10 border border-gray-600/50"> | |
| {def.name} | |
| </div> | |
| </div> | |
| ) | |
| })} | |
| </div> | |
| {displayBlocks.length === 0 && ( | |
| <div className="text-gray-500 text-center py-8">No blocks found</div> | |
| )} | |
| </div> | |
| {/* Footer */} | |
| <div className="p-2 border-t border-gray-700/50 flex items-center justify-between"> | |
| <p className="text-gray-500 text-xs">{displayBlocks.length} blocks</p> | |
| <p className="text-gray-600 text-xs">Click a block to add to hotbar</p> | |
| </div> | |
| </div> | |
| </div> | |
| ) | |
| } | |