feat(frontend): let the board render at any width
Board components size themselves from a --board-unit CSS variable instead of vw. By default it is 1vw, so the game page looks the same; with the new width prop the same board renders small, e.g. in previews.
This commit is contained in:
parent
85e3fb60f6
commit
9278ed7447
@ -14,12 +14,19 @@ const BoardView: FunctionComponent<{
|
|||||||
boardViewModel: BoardViewModel;
|
boardViewModel: BoardViewModel;
|
||||||
revert: boolean;
|
revert: boolean;
|
||||||
onPitSelect: (index: number, pit: Pit) => void;
|
onPitSelect: (index: number, pit: Pit) => void;
|
||||||
}> = ({ game, context, boardId, boardViewModel, revert, onPitSelect }) => {
|
// Width in px, e.g. for previews; by default the board is as wide as the viewport.
|
||||||
|
width?: number;
|
||||||
|
}> = ({ game, context, boardId, boardViewModel, revert, onPitSelect, width }) => {
|
||||||
const mancalaGame = game?.mancalaGame;
|
const mancalaGame = game?.mancalaGame;
|
||||||
const theme = context.themeManager.theme;
|
const theme = context.themeManager.theme;
|
||||||
|
|
||||||
// Compute pit width based on viewport (each column is 11vw)
|
// The board is 92 units wide (8 columns of 11 plus padding); all board parts are sized in units.
|
||||||
const pitWidth = typeof window !== "undefined" ? window.innerWidth * 0.11 : 100;
|
const unit = width !== undefined ? width / 92 : (typeof window !== "undefined" ? window.innerWidth / 100 : 10);
|
||||||
|
const pitWidth = unit * 11;
|
||||||
|
const sizeVariables = {
|
||||||
|
"--board-unit": width !== undefined ? `${unit}px` : "1vw",
|
||||||
|
"--board-spacing": width !== undefined ? `${unit * 0.3}px` : "5px",
|
||||||
|
} as React.CSSProperties;
|
||||||
|
|
||||||
const createPitView = (key: any, pit: Pit, pitViewModel: PitViewModel) => (
|
const createPitView = (key: any, pit: Pit, pitViewModel: PitViewModel) => (
|
||||||
<PitView
|
<PitView
|
||||||
@ -42,7 +49,7 @@ const BoardView: FunctionComponent<{
|
|||||||
const player2BankViewModel = boardViewModel.pits[player2BankIndex];
|
const player2BankViewModel = boardViewModel.pits[player2BankIndex];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="board" style={{ background: theme.boardColor }}>
|
<div className="board" style={{ background: theme.boardColor, ...sizeVariables }}>
|
||||||
<StoreView
|
<StoreView
|
||||||
context={context}
|
context={context}
|
||||||
pitViewModel={revert ? player2BankViewModel : player1BankViewModel}
|
pitViewModel={revert ? player2BankViewModel : player1BankViewModel}
|
||||||
@ -61,11 +68,11 @@ const BoardView: FunctionComponent<{
|
|||||||
{revert ? player2Pits : player1Pits}
|
{revert ? player2Pits : player1Pits}
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.board {
|
.board {
|
||||||
padding: 2vw;
|
padding: calc(2 * var(--board-unit));
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(8, 11vw);
|
grid-template-columns: repeat(8, calc(11 * var(--board-unit)));
|
||||||
grid-template-rows: repeat(2, 11vw);
|
grid-template-rows: repeat(2, calc(11 * var(--board-unit)));
|
||||||
border-radius: 3vw;
|
border-radius: calc(3 * var(--board-unit));
|
||||||
transition: background-color 0.5s;
|
transition: background-color 0.5s;
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
|
|||||||
@ -53,9 +53,9 @@ const PitView: FunctionComponent<PitViewProps> = ({ pitViewModel, context, onCli
|
|||||||
)}
|
)}
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.pit {
|
.pit {
|
||||||
margin: 5px;
|
margin: var(--board-spacing);
|
||||||
padding: 5px;
|
padding: var(--board-spacing);
|
||||||
border-radius: 10vw;
|
border-radius: calc(10 * var(--board-unit));
|
||||||
transition: background-color 0.5s;
|
transition: background-color 0.5s;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -67,11 +67,11 @@ const PitView: FunctionComponent<PitViewProps> = ({ pitViewModel, context, onCli
|
|||||||
}
|
}
|
||||||
.stone-count {
|
.stone-count {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 1.0vw;
|
bottom: calc(1.0 * var(--board-unit));
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 1.5vw;
|
font-size: calc(1.5 * var(--board-unit));
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,9 +7,9 @@ const StoneView: FunctionComponent<{ color: string }> = ({ color }) => {
|
|||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.stone {
|
.stone {
|
||||||
margin: 1px;
|
margin: 1px;
|
||||||
width: 1vw;
|
width: calc(1 * var(--board-unit));
|
||||||
height: 1vw;
|
height: calc(1 * var(--board-unit));
|
||||||
border-radius: 10vw;
|
border-radius: calc(10 * var(--board-unit));
|
||||||
transition: background-color 0.5s;
|
transition: background-color 0.5s;
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
|
|||||||
@ -34,8 +34,8 @@ const StoreView: FunctionComponent<{
|
|||||||
</span>
|
</span>
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.store {
|
.store {
|
||||||
margin: 5px;
|
margin: var(--board-spacing);
|
||||||
border-radius: 10vw;
|
border-radius: calc(10 * var(--board-unit));
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -46,10 +46,10 @@ const StoreView: FunctionComponent<{
|
|||||||
}
|
}
|
||||||
.store-stone-count-text {
|
.store-stone-count-text {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 2vw;
|
bottom: calc(2 * var(--board-unit));
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 2vw;
|
font-size: calc(2 * var(--board-unit));
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -166,7 +166,7 @@ not spectators (R4.5).
|
|||||||
|
|
||||||
### Board size
|
### Board size
|
||||||
`BoardView` gets an optional `width` (px). All board components size themselves from a CSS variable
|
`BoardView` gets an optional `width` (px). All board components size themselves from a CSS variable
|
||||||
`--board-unit` (default `1vw`, else `width / 100` px) instead of `vw`, so the same board renders at full width in a
|
`--board-unit` (default `1vw`, else `width / 92` px, the board being 92 units wide) instead of `vw`, so the same board renders at full width in a
|
||||||
game and small in previews.
|
game and small in previews.
|
||||||
|
|
||||||
### Home page (`routes/Home.tsx`)
|
### Home page (`routes/Home.tsx`)
|
||||||
|
|||||||
@ -17,7 +17,7 @@ Work through the tasks in order; each ends with its tests passing and a commit.
|
|||||||
unit tests.
|
unit tests.
|
||||||
- [x] 7. **API**: public ids, Bearer auth middleware, `/games/me/active`, `/games/me`, `/games/popular`,
|
- [x] 7. **API**: public ids, Bearer auth middleware, `/games/me/active`, `/games/me`, `/games/popular`,
|
||||||
`/game/:id` for finished games, live games channel with throttled push; tests.
|
`/game/:id` for finished games, live games channel with throttled push; tests.
|
||||||
- [ ] 8. **Board size**: `--board-unit` in `BoardView`, `PitView`, `StoreView`, `StoneView`; `width` prop.
|
- [x] 8. **Board size**: `--board-unit` in `BoardView`, `PitView`, `StoreView`, `StoneView`; `width` prop.
|
||||||
- [ ] 9. **Home page**: `ActiveGameBanner`, `LiveGamesSection` with live updates, `RecentGamesSection`; lobby
|
- [ ] 9. **Home page**: `ActiveGameBanner`, `LiveGamesSection` with live updates, `RecentGamesSection`; lobby
|
||||||
rejection message.
|
rejection message.
|
||||||
- [ ] 10. **Live game history**: `MoveList`, review mode with `ReviewBanner` and keyboard, timeout countdown.
|
- [ ] 10. **Live game history**: `MoveList`, review mode with `ReviewBanner` and keyboard, timeout countdown.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user