Skip to content

Commit 15a0067

Browse files
daniel-monroeclaude
andcommitted
Fix play-page layout shifts and drag-piece offset at non-100% zoom
- Pin the play page's left sidebar to a constant size (h-[75vh] w-[22rem]) with overflow-hidden, so neither its width nor height varies as content updates - Allow the FEN/PGN containers in ExportGame to shrink below their content width (min-w-0 throughout the flex chain + min-w-0 on the text element), so the long unwrappable FEN string can no longer push the sidebar wider and shift the board horizontally - Pin the PGN bar to a constant 160 px tall with internal scroll - Replace scrollIntoView in MovesContainer with a manual scrollTo on the moves panel only, so the move-into-view scroll never propagates up to the document body Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 3ba7d91 commit 15a0067

3 files changed

Lines changed: 37 additions & 12 deletions

File tree

src/components/Board/GameplayInterface.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export const GameplayInterface: React.FC<React.PropsWithChildren<Props>> = (
161161
<>
162162
<div className="flex h-full flex-1 flex-col justify-center gap-1 py-2 md:py-4">
163163
<div className="mx-auto mt-2 flex w-[90%] flex-row items-start justify-between gap-3">
164-
<div className="flex h-[75vh] min-w-[16rem] max-w-[22rem] flex-shrink-0 flex-col">
164+
<div className="flex h-[75vh] max-h-[75vh] min-h-[75vh] w-[22rem] min-w-[22rem] max-w-[22rem] flex-shrink-0 flex-col overflow-hidden">
165165
<div className="flex h-full w-full flex-col overflow-hidden rounded-md border border-glass-border bg-glass backdrop-blur-md">
166166
<GameInfo
167167
icon="swords"
@@ -219,7 +219,7 @@ export const GameplayInterface: React.FC<React.PropsWithChildren<Props>> = (
219219
/>
220220
) : null}
221221
</div>
222-
<div className="flex h-[75vh] min-w-[18rem] flex-grow flex-col gap-2">
222+
<div className="flex h-[75vh] min-w-0 flex-grow basis-[18rem] flex-col gap-2 overflow-hidden">
223223
{timeControl != 'unlimited' ? (
224224
<GameClock
225225
player={orientation == 'white' ? 'black' : 'white'}

src/components/Board/MovesContainer.tsx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,36 @@ export const MovesContainer: React.FC<
162162
])
163163

164164
useEffect(() => {
165-
if (currentMoveRef.current && containerRef.current) {
166-
currentMoveRef.current.scrollIntoView({
165+
const move = currentMoveRef.current
166+
const container = containerRef.current
167+
if (!move || !container) return
168+
169+
// scrollIntoView walks every scrollable ancestor up to the document body,
170+
// so it can shift the whole page if the moves panel sits near a viewport
171+
// edge. Scroll the moves panel directly instead, leaving everything above
172+
// it alone.
173+
const moveRect = move.getBoundingClientRect()
174+
const containerRect = container.getBoundingClientRect()
175+
176+
let topDelta = 0
177+
if (moveRect.top < containerRect.top) {
178+
topDelta = moveRect.top - containerRect.top
179+
} else if (moveRect.bottom > containerRect.bottom) {
180+
topDelta = moveRect.bottom - containerRect.bottom
181+
}
182+
183+
let leftDelta = 0
184+
if (moveRect.left < containerRect.left) {
185+
leftDelta = moveRect.left - containerRect.left
186+
} else if (moveRect.right > containerRect.right) {
187+
leftDelta = moveRect.right - containerRect.right
188+
}
189+
190+
if (topDelta !== 0 || leftDelta !== 0) {
191+
container.scrollTo({
192+
top: container.scrollTop + topDelta,
193+
left: container.scrollLeft + leftDelta,
167194
behavior: 'smooth',
168-
block: 'nearest',
169-
inline: 'nearest',
170195
})
171196
}
172197
}, [controller.currentNode])

src/components/Common/ExportGame.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ export const ExportGame: React.FC<Props> = (props) => {
9898
}
9999

100100
return (
101-
<div className="flex w-full flex-col gap-2">
102-
<div className="flex w-full flex-col gap-0.5">
101+
<div className="flex w-full min-w-0 flex-col gap-2 overflow-hidden">
102+
<div className="flex w-full min-w-0 flex-col gap-0.5">
103103
<div className="flex w-full items-center justify-between">
104104
<p className="select-none text-xs font-semibold tracking-wider text-secondary">
105105
FEN
@@ -117,14 +117,14 @@ export const ExportGame: React.FC<Props> = (props) => {
117117
role="button"
118118
tabIndex={0}
119119
onClick={() => copy(fen)}
120-
className="border-1 group flex w-full cursor-pointer overflow-x-hidden rounded border border-white/5 p-1"
120+
className="border-1 group flex w-full min-w-0 cursor-pointer overflow-x-hidden rounded border border-white/5 p-1"
121121
>
122-
<p className="whitespace-nowrap text-xxs text-secondary group-hover:text-secondary/80">
122+
<p className="min-w-0 overflow-hidden whitespace-nowrap text-xxs text-secondary group-hover:text-secondary/80">
123123
{fen}
124124
</p>
125125
</div>
126126
</div>
127-
<div className="flex w-full flex-col gap-0.5">
127+
<div className="flex w-full min-w-0 flex-col gap-0.5">
128128
<div className="flex w-full items-center justify-between">
129129
<div className="flex w-full items-center">
130130
<p className="select-none text-xs font-semibold tracking-wider text-secondary">
@@ -144,7 +144,7 @@ export const ExportGame: React.FC<Props> = (props) => {
144144
role="button"
145145
tabIndex={0}
146146
onClick={() => copy(pgn)}
147-
className="group flex w-full cursor-pointer overflow-x-hidden overflow-y-scroll rounded border border-white/5 p-1"
147+
className="group flex h-40 max-h-40 min-h-40 w-full min-w-0 cursor-pointer overflow-x-hidden overflow-y-scroll rounded border border-white/5 p-1"
148148
>
149149
<p className="whitespace-pre-wrap text-xxs text-secondary group-hover:text-secondary/80">
150150
{pgn}

0 commit comments

Comments
 (0)