fix(css): remove min-width from grid cards to fix layout
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/* Grid View Styles */
|
/* Grid View Styles */
|
||||||
.entity-list.grid-view {
|
.entity-list.grid-view {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,9 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100%;
|
height: auto;
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entity-card.grid-card:hover {
|
.entity-card.grid-card:hover {
|
||||||
|
|||||||
@@ -92,22 +92,10 @@ function LocationView({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { playSfx } = useAudio()
|
const { playSfx } = useAudio()
|
||||||
|
|
||||||
// View Mode State
|
|
||||||
const [viewMode, setViewMode] = useState<'list' | 'grid'>(() => {
|
|
||||||
return (localStorage.getItem('locationViewMode') as 'list' | 'grid') || 'list'
|
|
||||||
})
|
|
||||||
|
|
||||||
// Dropdown State
|
// Dropdown State
|
||||||
const [activeDropdown, setActiveDropdown] = useState<string | null>(null)
|
const [activeDropdown, setActiveDropdown] = useState<string | null>(null)
|
||||||
const [dropdownPos, setDropdownPos] = useState({ x: 0, y: 0 })
|
const [dropdownPos, setDropdownPos] = useState({ x: 0, y: 0 })
|
||||||
|
|
||||||
const toggleViewMode = () => {
|
|
||||||
const newMode = viewMode === 'list' ? 'grid' : 'list'
|
|
||||||
setViewMode(newMode)
|
|
||||||
localStorage.setItem('locationViewMode', newMode)
|
|
||||||
playSfx('/audio/sfx/click.wav')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle dropdown toggle
|
// Handle dropdown toggle
|
||||||
const handleDropdownClick = (e: React.MouseEvent, id: string) => {
|
const handleDropdownClick = (e: React.MouseEvent, id: string) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
@@ -115,9 +103,10 @@ function LocationView({
|
|||||||
setActiveDropdown(null)
|
setActiveDropdown(null)
|
||||||
} else {
|
} else {
|
||||||
const rect = e.currentTarget.getBoundingClientRect()
|
const rect = e.currentTarget.getBoundingClientRect()
|
||||||
|
// Fix: GameDropdown uses fixed positioning, so we don't add scroll offsets
|
||||||
setDropdownPos({
|
setDropdownPos({
|
||||||
x: rect.left + window.scrollX,
|
x: rect.left,
|
||||||
y: rect.bottom + window.scrollY + 5
|
y: rect.bottom + 5
|
||||||
})
|
})
|
||||||
setActiveDropdown(id)
|
setActiveDropdown(id)
|
||||||
}
|
}
|
||||||
@@ -147,15 +136,6 @@ function LocationView({
|
|||||||
</span>
|
</span>
|
||||||
</GameTooltip>
|
</GameTooltip>
|
||||||
)}
|
)}
|
||||||
<GameButton
|
|
||||||
variant="secondary"
|
|
||||||
size="sm"
|
|
||||||
onClick={(e) => { e.stopPropagation(); toggleViewMode(); }}
|
|
||||||
className="view-toggle-btn"
|
|
||||||
style={{ marginLeft: 'auto' }}
|
|
||||||
>
|
|
||||||
{viewMode === 'list' ? '📋' : '🔲'}
|
|
||||||
</GameButton>
|
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{location.tags && location.tags.length > 0 && (
|
{location.tags && location.tags.length > 0 && (
|
||||||
@@ -235,12 +215,12 @@ function LocationView({
|
|||||||
{location.npcs.filter((npc: any) => npc.type === 'enemy').length > 0 && (
|
{location.npcs.filter((npc: any) => npc.type === 'enemy').length > 0 && (
|
||||||
<div className="entity-section enemies-section">
|
<div className="entity-section enemies-section">
|
||||||
<h3>{t('location.enemies')}</h3>
|
<h3>{t('location.enemies')}</h3>
|
||||||
<div className={`entity-list ${viewMode === 'grid' ? 'grid-view' : ''}`}>
|
<div className="entity-list grid-view">
|
||||||
{location.npcs.filter((npc: any) => npc.type === 'enemy').map((enemy: any, i: number) => {
|
{location.npcs.filter((npc: any) => npc.type === 'enemy').map((enemy: any, i: number) => {
|
||||||
const id = `enemy-${enemy.id || i}`;
|
const id = `enemy-${enemy.id || i}`;
|
||||||
return (
|
return (
|
||||||
<div key={i} className={`entity-card enemy-card ${viewMode === 'grid' ? 'grid-card' : ''}`}
|
<div key={i} className="entity-card enemy-card grid-card"
|
||||||
onClick={viewMode === 'grid' ? (e) => handleDropdownClick(e, id) : undefined}>
|
onClick={(e) => handleDropdownClick(e, id)}>
|
||||||
{enemy.id && (
|
{enemy.id && (
|
||||||
<div className="entity-image">
|
<div className="entity-image">
|
||||||
<img
|
<img
|
||||||
@@ -251,37 +231,18 @@ function LocationView({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{viewMode === 'list' && (
|
<GameTooltip content={
|
||||||
<>
|
<div>
|
||||||
<div className="entity-info">
|
<div className="tooltip-title">{getTranslatedText(enemy.name)}</div>
|
||||||
<div className="entity-name enemy-name">{getTranslatedText(enemy.name)}</div>
|
<div>{t('location.level')} {enemy.level}</div>
|
||||||
{enemy.level && <div className="entity-level">{t('location.level')} {enemy.level}</div>}
|
<div style={{ color: '#f56565', fontSize: '0.8rem' }}>Click for actions</div>
|
||||||
</div>
|
</div>
|
||||||
<GameButton
|
}>
|
||||||
variant="danger"
|
<div className="grid-overlay"></div>
|
||||||
size="sm"
|
</GameTooltip>
|
||||||
className="entity-action-btn combat-btn"
|
|
||||||
onClick={() => onInitiateCombat(enemy.id)}
|
|
||||||
>
|
|
||||||
{t('common.fight')}
|
|
||||||
</GameButton>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{viewMode === 'grid' && (
|
|
||||||
<GameTooltip content={
|
|
||||||
<div>
|
|
||||||
<div className="tooltip-title">{getTranslatedText(enemy.name)}</div>
|
|
||||||
<div>{t('location.level')} {enemy.level}</div>
|
|
||||||
<div style={{ color: '#f56565', fontSize: '0.8rem' }}>Click for actions</div>
|
|
||||||
</div>
|
|
||||||
}>
|
|
||||||
<div className="grid-overlay"></div>
|
|
||||||
</GameTooltip>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Dropdown for Grid View */}
|
{/* Dropdown for Grid View */}
|
||||||
{viewMode === 'grid' && activeDropdown === id && (
|
{activeDropdown === id && (
|
||||||
<GameDropdown
|
<GameDropdown
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={() => setActiveDropdown(null)}
|
onClose={() => setActiveDropdown(null)}
|
||||||
@@ -314,46 +275,25 @@ function LocationView({
|
|||||||
{location.corpses && location.corpses.length > 0 && (
|
{location.corpses && location.corpses.length > 0 && (
|
||||||
<div className="entity-section corpses-section">
|
<div className="entity-section corpses-section">
|
||||||
<h3>{t('location.corpses')}</h3>
|
<h3>{t('location.corpses')}</h3>
|
||||||
<div className="entity-list">
|
<div className="entity-list grid-view">
|
||||||
{location.corpses.map((corpse: any) => (
|
{location.corpses.map((corpse: any) => (
|
||||||
<div key={corpse.id} className="corpse-container">
|
<div key={corpse.id} className="corpse-container">
|
||||||
<div className={`entity-card corpse-card ${viewMode === 'grid' ? 'grid-card' : ''}`}
|
<div className="entity-card corpse-card grid-card"
|
||||||
onClick={viewMode === 'grid' ? (e) => handleDropdownClick(e, `corpse-${corpse.id}`) : undefined}
|
onClick={(e) => handleDropdownClick(e, `corpse-${corpse.id}`)}
|
||||||
>
|
>
|
||||||
{viewMode === 'list' ? (
|
<GameTooltip content={
|
||||||
<>
|
<div>
|
||||||
<div className="entity-info">
|
<div className="tooltip-title">{corpse.emoji} {getTranslatedText(corpse.name)}</div>
|
||||||
<div className="entity-name">{corpse.emoji} {getTranslatedText(corpse.name)}</div>
|
<div>{corpse.loot_count} {t('location.items')}</div>
|
||||||
<div className="corpse-loot-count">{corpse.loot_count} {t('location.items')}</div>
|
</div>
|
||||||
</div>
|
}>
|
||||||
<GameButton
|
<div className="grid-corpse-content">
|
||||||
variant="secondary"
|
<div style={{ fontSize: '2rem' }}>{corpse.emoji}</div>
|
||||||
size="sm"
|
<div style={{ fontSize: '0.8rem', color: '#ce93d8' }}>{corpse.loot_count} items</div>
|
||||||
className="entity-action-btn loot-btn"
|
</div>
|
||||||
onClick={() => {
|
</GameTooltip>
|
||||||
playSfx('/audio/sfx/interact.wav')
|
|
||||||
onLootCorpse(String(corpse.id))
|
|
||||||
}}
|
|
||||||
disabled={corpse.loot_count === 0}
|
|
||||||
>
|
|
||||||
🔍 {t('common.examine')}
|
|
||||||
</GameButton>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<GameTooltip content={
|
|
||||||
<div>
|
|
||||||
<div className="tooltip-title">{corpse.emoji} {getTranslatedText(corpse.name)}</div>
|
|
||||||
<div>{corpse.loot_count} {t('location.items')}</div>
|
|
||||||
</div>
|
|
||||||
}>
|
|
||||||
<div className="grid-corpse-content">
|
|
||||||
<div style={{ fontSize: '2rem' }}>{corpse.emoji}</div>
|
|
||||||
<div style={{ fontSize: '0.8rem', color: '#ce93d8' }}>{corpse.loot_count} items</div>
|
|
||||||
</div>
|
|
||||||
</GameTooltip>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{viewMode === 'grid' && activeDropdown === `corpse-${corpse.id}` && (
|
{activeDropdown === `corpse-${corpse.id}` && (
|
||||||
<GameDropdown
|
<GameDropdown
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={() => setActiveDropdown(null)}
|
onClose={() => setActiveDropdown(null)}
|
||||||
@@ -437,33 +377,23 @@ function LocationView({
|
|||||||
{location.npcs.filter((npc: any) => npc.type !== 'enemy').length > 0 && (
|
{location.npcs.filter((npc: any) => npc.type !== 'enemy').length > 0 && (
|
||||||
<div className="entity-section npcs-section">
|
<div className="entity-section npcs-section">
|
||||||
<h3>{t('location.npcs')}</h3>
|
<h3>{t('location.npcs')}</h3>
|
||||||
<div className="entity-list">
|
<div className="entity-list grid-view">
|
||||||
{location.npcs.filter((npc: any) => npc.type !== 'enemy').map((npc: any, i: number) => (
|
{location.npcs.filter((npc: any) => npc.type !== 'enemy').map((npc: any, i: number) => (
|
||||||
<div key={i} className={`entity-card npc-card ${viewMode === 'grid' ? 'grid-card' : ''}`}
|
<div key={i} className="entity-card npc-card grid-card"
|
||||||
onClick={viewMode === 'grid' ? (e) => handleDropdownClick(e, `npc-${i}`) : undefined}
|
onClick={(e) => handleDropdownClick(e, `npc-${i}`)}
|
||||||
>
|
>
|
||||||
<span className="entity-icon" style={viewMode === 'grid' ? { fontSize: '2.5rem' } : {}}>🧑</span>
|
<span className="entity-icon" style={{ fontSize: '2.5rem' }}>🧑</span>
|
||||||
|
|
||||||
{viewMode === 'list' ? (
|
<GameTooltip content={
|
||||||
<>
|
<div>
|
||||||
<div className="entity-info">
|
<div className="tooltip-title">{getTranslatedText(npc.name)}</div>
|
||||||
<div className="entity-name">{getTranslatedText(npc.name)}</div>
|
<div>{t('location.level')} {npc.level}</div>
|
||||||
{npc.level && <div className="entity-level">{t('location.level')} {npc.level}</div>}
|
</div>
|
||||||
</div>
|
}>
|
||||||
<GameButton variant="primary" size="sm" className="entity-action-btn">{t('common.talk')}</GameButton>
|
<div className="grid-overlay"></div>
|
||||||
</>
|
</GameTooltip>
|
||||||
) : (
|
|
||||||
<GameTooltip content={
|
|
||||||
<div>
|
|
||||||
<div className="tooltip-title">{getTranslatedText(npc.name)}</div>
|
|
||||||
<div>{t('location.level')} {npc.level}</div>
|
|
||||||
</div>
|
|
||||||
}>
|
|
||||||
<div className="grid-overlay"></div>
|
|
||||||
</GameTooltip>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{viewMode === 'grid' && activeDropdown === `npc-${i}` && (
|
{activeDropdown === `npc-${i}` && (
|
||||||
<GameDropdown
|
<GameDropdown
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={() => setActiveDropdown(null)}
|
onClose={() => setActiveDropdown(null)}
|
||||||
@@ -485,14 +415,14 @@ function LocationView({
|
|||||||
{location.items.length > 0 && (
|
{location.items.length > 0 && (
|
||||||
<div className="entity-section items-section">
|
<div className="entity-section items-section">
|
||||||
<h3>{t('location.itemsOnGround')}</h3>
|
<h3>{t('location.itemsOnGround')}</h3>
|
||||||
<div className="entity-list">
|
<div className="entity-list grid-view">
|
||||||
{location.items.map((item: any, i: number) => {
|
{location.items.map((item: any, i: number) => {
|
||||||
// Use loose equality to handle potential string/number mismatches
|
// Use loose equality to handle potential string/number mismatches
|
||||||
const isShaking = failedActionItemId == item.id;
|
const isShaking = failedActionItemId == item.id;
|
||||||
const itemId = `item-${item.id}-${i}`;
|
const itemId = `item-${item.id}-${i}`;
|
||||||
return (
|
return (
|
||||||
<div key={i} className={`entity-card item-card ${isShaking ? 'shake' : ''} ${viewMode === 'grid' ? 'grid-card' : ''}`}
|
<div key={i} className={`entity-card item-card ${isShaking ? 'shake' : ''} grid-card`}
|
||||||
onClick={viewMode === 'grid' ? (e) => handleDropdownClick(e, itemId) : undefined}
|
onClick={(e) => handleDropdownClick(e, itemId)}
|
||||||
>
|
>
|
||||||
<GameTooltip content={
|
<GameTooltip content={
|
||||||
<div className="item-info-tooltip-content">
|
<div className="item-info-tooltip-content">
|
||||||
@@ -528,13 +458,13 @@ function LocationView({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}>
|
}>
|
||||||
<div className={`entity-content-wrapper ${viewMode === 'grid' ? 'grid-content' : ''}`} style={viewMode === 'list' ? { display: 'flex', alignItems: 'center', gap: '1rem', flex: 1, cursor: 'help' } : {}}>
|
<div className="entity-content-wrapper grid-content">
|
||||||
{item.image_path ? (
|
{item.image_path ? (
|
||||||
<img
|
<img
|
||||||
src={getAssetPath(item.image_path)}
|
src={getAssetPath(item.image_path)}
|
||||||
alt={getTranslatedText(item.name)}
|
alt={getTranslatedText(item.name)}
|
||||||
className="entity-icon"
|
className="entity-icon"
|
||||||
style={viewMode === 'grid' ? { width: '100%', height: '100%', objectFit: 'contain' } : {}}
|
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
(e.target as HTMLImageElement).style.display = 'none';
|
(e.target as HTMLImageElement).style.display = 'none';
|
||||||
const icon = (e.target as HTMLImageElement).nextElementSibling;
|
const icon = (e.target as HTMLImageElement).nextElementSibling;
|
||||||
@@ -542,71 +472,15 @@ function LocationView({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<span className={`entity-icon ${item.image_path ? 'hidden' : ''}`} style={viewMode === 'grid' && !item.image_path ? { fontSize: '2rem' } : {}}>{item.emoji || '📦'}</span>
|
<span className={`entity-icon ${item.image_path ? 'hidden' : ''}`} style={!item.image_path ? { fontSize: '2rem' } : {}}>{item.emoji || '📦'}</span>
|
||||||
|
|
||||||
{viewMode === 'list' && (
|
{item.quantity > 1 && (
|
||||||
<div className="entity-info">
|
|
||||||
<div className={`entity-name ${item.tier ? `tier-${item.tier}` : ''}`}>
|
|
||||||
{getTranslatedText(item.name) || 'Unknown Item'}
|
|
||||||
</div>
|
|
||||||
{item.quantity > 1 && <div className="entity-quantity">×{item.quantity}</div>}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{viewMode === 'grid' && item.quantity > 1 && (
|
|
||||||
<div className="grid-quantity">x{item.quantity}</div>
|
<div className="grid-quantity">x{item.quantity}</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</GameTooltip>
|
</GameTooltip>
|
||||||
|
|
||||||
{viewMode === 'list' && (
|
{activeDropdown === itemId && (
|
||||||
<div className="item-actions-row">
|
|
||||||
{item.quantity === 1 ? (
|
|
||||||
<GameButton
|
|
||||||
variant="success"
|
|
||||||
size="sm"
|
|
||||||
className="action-btn pickup single"
|
|
||||||
onClick={() => {
|
|
||||||
// Sound handled in useGameEngine
|
|
||||||
onPickup(item.id, 1)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t('common.pickUp')}
|
|
||||||
</GameButton>
|
|
||||||
) : (
|
|
||||||
<div className="pickup-actions-group">
|
|
||||||
<GameButton variant="success" size="sm" className="action-btn pickup" onClick={() => {
|
|
||||||
onPickup(item.id, 1)
|
|
||||||
}}>
|
|
||||||
x1
|
|
||||||
</GameButton>
|
|
||||||
|
|
||||||
{item.quantity >= 5 && (
|
|
||||||
<GameButton variant="success" size="sm" className="action-btn pickup" onClick={() => {
|
|
||||||
onPickup(item.id, 5)
|
|
||||||
}}>
|
|
||||||
x5
|
|
||||||
</GameButton>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{item.quantity >= 10 && (
|
|
||||||
<GameButton variant="success" size="sm" className="action-btn pickup" onClick={() => {
|
|
||||||
onPickup(item.id, 10)
|
|
||||||
}}>
|
|
||||||
x10
|
|
||||||
</GameButton>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<GameButton variant="success" size="sm" className="action-btn pickup" onClick={() => {
|
|
||||||
onPickup(item.id, item.quantity)
|
|
||||||
}}>
|
|
||||||
{t('common.all') || 'All'}
|
|
||||||
</GameButton>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{viewMode === 'grid' && activeDropdown === itemId && (
|
|
||||||
<GameDropdown
|
<GameDropdown
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
onClose={() => setActiveDropdown(null)}
|
onClose={() => setActiveDropdown(null)}
|
||||||
|
|||||||
Reference in New Issue
Block a user