Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af85b3e481 | ||
|
|
573d0df20d | ||
|
|
d312969747 | ||
|
|
7f65a67cef |
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,3 +1,17 @@
|
||||
## [2.14.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.14.1...v2.14.2) (2025-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* correct avatar positioning to prevent fly-in animation ([573d0df](https://github.com/antialias/soroban-abacus-flashcards/commit/573d0df20dcdac41021c46feb423dbf3782728f6))
|
||||
|
||||
## [2.14.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.14.0...v2.14.1) (2025-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* prevent avatar fly-in and hide local player's own hover ([7f65a67](https://github.com/antialias/soroban-abacus-flashcards/commit/7f65a67cef3d7f0ebce1bd7417972a6138acfc46))
|
||||
|
||||
## [2.14.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.13.0...v2.14.0) (2025-10-09)
|
||||
|
||||
|
||||
|
||||
@@ -92,23 +92,36 @@ function HoverAvatar({
|
||||
playerInfo: { emoji: string; name: string; color?: string }
|
||||
cardElement: HTMLElement | null
|
||||
}) {
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 })
|
||||
const [position, setPosition] = useState<{ x: number; y: number } | null>(null)
|
||||
|
||||
// Update position when card element changes
|
||||
useEffect(() => {
|
||||
if (cardElement) {
|
||||
const rect = cardElement.getBoundingClientRect()
|
||||
// Calculate the actual position we want the avatar centered at (top-right of card)
|
||||
// Since we're using translate(-50%, -50%), we need the center point
|
||||
const avatarSize = 48
|
||||
const avatarCenterX = rect.right - 12 // 12px from right edge
|
||||
const avatarCenterY = rect.top - 12 // 12px from top edge
|
||||
|
||||
setPosition({
|
||||
x: rect.right - 12, // Position at top-right of card
|
||||
y: rect.top - 12,
|
||||
x: avatarCenterX,
|
||||
y: avatarCenterY,
|
||||
})
|
||||
}
|
||||
}, [cardElement])
|
||||
|
||||
// Smooth spring animation for position changes
|
||||
// Use 'from' to set initial position when avatar first appears
|
||||
const springProps = useSpring({
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
from: position
|
||||
? { x: position.x, y: position.y, opacity: 0 }
|
||||
: { x: 0, y: 0, opacity: 0 },
|
||||
to: {
|
||||
x: position?.x ?? 0,
|
||||
y: position?.y ?? 0,
|
||||
opacity: position ? 1 : 0,
|
||||
},
|
||||
config: {
|
||||
tension: 280,
|
||||
friction: 60,
|
||||
@@ -116,14 +129,21 @@ function HoverAvatar({
|
||||
},
|
||||
})
|
||||
|
||||
// Don't render until we have a position
|
||||
if (!position) return null
|
||||
|
||||
return (
|
||||
<animated.div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
left: springProps.x,
|
||||
top: springProps.y,
|
||||
// Don't use translate, just position directly at the calculated point
|
||||
left: springProps.x.to((x) => `${x}px`),
|
||||
top: springProps.y.to((y) => `${y}px`),
|
||||
opacity: springProps.opacity,
|
||||
width: '48px',
|
||||
height: '48px',
|
||||
marginLeft: '-24px', // Center horizontally (half of width)
|
||||
marginTop: '-24px', // Center vertically (half of height)
|
||||
borderRadius: '50%',
|
||||
background: playerInfo.color || 'linear-gradient(135deg, #667eea, #764ba2)',
|
||||
display: 'flex',
|
||||
@@ -136,7 +156,6 @@ function HoverAvatar({
|
||||
border: '3px solid white',
|
||||
zIndex: 1000,
|
||||
pointerEvents: 'none',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
filter: 'drop-shadow(0 0 8px rgba(102, 126, 234, 0.8))',
|
||||
}}
|
||||
className={css({
|
||||
@@ -363,6 +382,11 @@ export function MemoryGrid() {
|
||||
{state.playerHovers &&
|
||||
Object.entries(state.playerHovers)
|
||||
.filter(([_, cardId]) => cardId !== null) // Only show if hovering a card
|
||||
.filter(([playerId]) => {
|
||||
// Don't show your own hover avatar (only show remote players)
|
||||
const player = playerMap.get(playerId)
|
||||
return player?.isLocal !== true
|
||||
})
|
||||
.map(([playerId, cardId]) => {
|
||||
const playerInfo = getPlayerHoverInfo(playerId)
|
||||
const cardElement = cardId ? cardRefs.current.get(cardId) : null
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "soroban-monorepo",
|
||||
"version": "2.14.0",
|
||||
"version": "2.14.2",
|
||||
"private": true,
|
||||
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
|
||||
"workspaces": [
|
||||
|
||||
Reference in New Issue
Block a user