Add player rail, dice overlay, and compact card support

This update introduces a player rail to the right of the board, visually representing active and inactive players, and adds a dice overlay beneath the board. Compact variants for placeholder cards were added, with corresponding adjustments to layouts and styles for better responsiveness and a cleaner interface.
This commit is contained in:
2026-02-07 11:38:38 +01:00
parent e035a99179
commit e5f8f00832
2 changed files with 205 additions and 72 deletions

View File

@@ -34,7 +34,6 @@ export default function App() {
const [chipOpen, setChipOpen] = useState(false); const [chipOpen, setChipOpen] = useState(false);
const [chipEntry, setChipEntry] = useState(null); const [chipEntry, setChipEntry] = useState(null);
// Live refresh (optional)
const aliveRef = useRef(true); const aliveRef = useRef(true);
const load = async () => { const load = async () => {
@@ -229,7 +228,7 @@ export default function App() {
] ]
: []; : [];
const PlaceholderCard = ({ title, hint }) => ( const PlaceholderCard = ({ title, hint, compact = false }) => (
<div <div
style={{ style={{
borderRadius: 18, borderRadius: 18,
@@ -237,20 +236,23 @@ export default function App() {
background: stylesTokens.panelBg, background: stylesTokens.panelBg,
boxShadow: "0 12px 30px rgba(0,0,0,0.35)", boxShadow: "0 12px 30px rgba(0,0,0,0.35)",
backdropFilter: "blur(8px)", backdropFilter: "blur(8px)",
padding: 12, padding: compact ? 10 : 12,
overflow: "hidden", overflow: "hidden",
minWidth: 0,
}} }}
> >
<div style={{ fontWeight: 900, color: stylesTokens.textMain, fontSize: 13 }}> <div style={{ fontWeight: 900, color: stylesTokens.textMain, fontSize: 13 }}>
{title} {title}
</div> </div>
{hint ? (
<div style={{ marginTop: 6, color: stylesTokens.textDim, fontSize: 12, opacity: 0.95 }}> <div style={{ marginTop: 6, color: stylesTokens.textDim, fontSize: 12, opacity: 0.95 }}>
{hint} {hint}
</div> </div>
) : null}
<div <div
style={{ style={{
marginTop: 10, marginTop: compact ? 8 : 10,
height: 64, height: compact ? 46 : 64,
borderRadius: 14, borderRadius: 14,
border: `1px dashed ${stylesTokens.panelBorder}`, border: `1px dashed ${stylesTokens.panelBorder}`,
opacity: 0.8, opacity: 0.8,
@@ -259,23 +261,63 @@ export default function App() {
</div> </div>
); );
// Player rail placeholder (rechts vom Board, vor Notizen)
const players = [
{ id: "p1", label: "A", active: true },
{ id: "p2", label: "B" },
{ id: "p3", label: "C" },
{ id: "p4", label: "D" },
{ id: "p5", label: "E" },
];
const PlayerIcon = ({ label, active }) => (
<div
style={{
width: active ? 46 : 36,
height: active ? 46 : 36,
borderRadius: 999,
border: `1px solid ${stylesTokens.panelBorder}`,
background: stylesTokens.panelBg,
boxShadow: active ? "0 16px 40px rgba(0,0,0,0.55)" : "0 10px 28px rgba(0,0,0,0.35)",
display: "grid",
placeItems: "center",
color: active ? stylesTokens.textGold : stylesTokens.textMain,
fontWeight: 900,
fontSize: active ? 14 : 12,
transition: "transform 120ms ease, width 120ms ease, height 120ms ease",
}}
title={active ? "Aktiver Spieler" : "Spieler"}
>
{label}
</div>
);
return ( return (
<div style={styles.page}> <div style={styles.page}>
<div style={styles.bgFixed} aria-hidden="true"> <div style={styles.bgFixed} aria-hidden="true">
<div style={styles.bgMap} /> <div style={styles.bgMap} />
</div> </div>
{/* Layout: Links Game/Board (fix), Rechts Notizen (scroll) */}
<div className="appRoot"> <div className="appRoot">
{/* LEFT: Game Area */} {/* LEFT: Game Area */}
<section className="leftPane"> <section className="leftPane">
{/* Top bar placeholders (User + Settings) */} {/* Top: User + Settings adjacent */}
<div className="topBarRow"> <div className="topBarRow">
<PlaceholderCard title="User Dropdown" hint="(placeholder)" /> <PlaceholderCard title="User Dropdown" hint="(placeholder)" compact />
<PlaceholderCard title="Einstellungen" hint="(placeholder)" /> <PlaceholderCard title="Einstellungen" hint="(placeholder)" compact />
</div> </div>
{/* Center Board */} {/* Main: Tools | Board | Player Rail */}
<div className="mainRow">
{/* Left of board: Hilfskarten + Dunkles Deck side-by-side */}
<div className="leftTools">
<div className="leftToolsRow">
<PlaceholderCard title="Hilfskarten" hint="(placeholder)" />
<PlaceholderCard title="Dunkles Deck" hint="(placeholder)" />
</div>
</div>
{/* Board: big */}
<div <div
className="boardWrap" className="boardWrap"
style={{ style={{
@@ -292,10 +334,11 @@ export default function App() {
position: "absolute", position: "absolute",
inset: 0, inset: 0,
background: `linear-gradient(90deg, transparent, ${stylesTokens.goldLine}, transparent)`, background: `linear-gradient(90deg, transparent, ${stylesTokens.goldLine}, transparent)`,
opacity: 0.25, opacity: 0.22,
pointerEvents: "none", pointerEvents: "none",
}} }}
/> />
<div style={{ position: "relative", height: "100%", display: "flex", flexDirection: "column" }}> <div style={{ position: "relative", height: "100%", display: "flex", flexDirection: "column" }}>
<div style={{ fontWeight: 900, color: stylesTokens.textMain, fontSize: 14 }}> <div style={{ fontWeight: 900, color: stylesTokens.textMain, fontSize: 14 }}>
3D Board / Game View 3D Board / Game View
@@ -315,23 +358,33 @@ export default function App() {
}} }}
/> />
</div> </div>
{/* Dice: under the board slightly right (overlay) */}
<div className="diceOverlay">
<PlaceholderCard title="Würfel" hint="(placeholder)" compact />
</div>
</div> </div>
{/* Bottom HUD */} {/* Right of board: player rail, directly before notes */}
<div className="bottomHud"> <div className="playerRail">
<div className="playerRailTitle">Spieler</div>
<div className="playerRailList">
{players.map((p) => (
<PlayerIcon key={p.id} label={p.label} active={!!p.active} />
))}
</div>
</div>
</div>
{/* Bottom: Player HUD (left user card, mid secret, right points) */}
<div className="playerHud">
<PlaceholderCard title="Spielerkarte (User)" hint="(placeholder)" />
<PlaceholderCard title="Meine Geheimkarten" hint="(placeholder)" /> <PlaceholderCard title="Meine Geheimkarten" hint="(placeholder)" />
<PlaceholderCard title="Würfel + Hogwarts Points" hint="(placeholder)" /> <PlaceholderCard title="Hogwarts Points" hint="(placeholder)" />
<PlaceholderCard title="Spielerkarte / Turn" hint="(placeholder)" />
</div>
{/* Extra: Hilfskarten / Deck */}
<div className="extraRow">
<PlaceholderCard title="Dunkles Deck" hint="(placeholder)" />
<PlaceholderCard title="Hilfskarten" hint="(placeholder)" />
</div> </div>
</section> </section>
{/* RIGHT: Notes Panel */} {/* RIGHT: Notes Panel (scroll only here) */}
<aside <aside
className="notesPane" className="notesPane"
style={{ style={{

View File

@@ -1,60 +1,129 @@
/* Desktop-only layout: left fixed (no scroll), right notes scroll only */ /* Desktop-only layout:
- Left: fixed, no scroll
- Right: notes scroll only
*/
html, body, #root { html, body, #root {
height: 100%; height: 100%;
} }
body { body {
overflow: hidden; /* no page scroll, only notes panel scrolls */ overflow: hidden; /* prevent global scroll */
} }
.appRoot { .appRoot {
height: 100vh; height: 100vh;
display: grid; display: grid;
grid-template-columns: minmax(720px, 1fr) clamp(380px, 32vw, 520px); grid-template-columns: minmax(860px, 1fr) clamp(380px, 32vw, 520px);
gap: 14px; gap: 14px;
padding: 14px; padding: 14px;
box-sizing: border-box; box-sizing: border-box;
overflow: hidden; overflow: hidden;
} }
/* LEFT COLUMN */
.leftPane { .leftPane {
overflow: hidden; overflow: hidden;
min-width: 0; min-width: 0;
display: grid; display: grid;
grid-template-rows: 72px minmax(340px, 1fr) 160px 110px; /* top / board / hud / extra */ grid-template-rows: 72px minmax(340px, 1fr) 160px; /* top | main | player HUD */
gap: 14px; gap: 14px;
} }
.topBarRow { .topBarRow {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr; /* User | Settings adjacent */
gap: 14px; gap: 14px;
min-width: 0; min-width: 0;
} }
/* MIDDLE: Tools | Board | Player rail */
.mainRow {
min-height: 0;
overflow: hidden;
display: grid;
grid-template-columns: minmax(260px, 360px) minmax(520px, 1fr) 92px;
gap: 14px;
min-width: 0;
}
/* Left of board: two cards next to each other */
.leftTools {
min-width: 0;
overflow: hidden;
display: grid;
align-content: start;
}
.leftToolsRow {
display: grid;
grid-template-columns: 1fr 1fr; /* Hilfskarten | Dunkles Deck */
gap: 14px;
min-width: 0;
}
/* Board */
.boardWrap { .boardWrap {
min-height: 0; min-height: 0;
overflow: hidden; overflow: hidden;
border-radius: 22px; border-radius: 22px;
min-width: 0;
position: relative;
} }
.bottomHud { /* Dice overlay: under board slightly right */
.diceOverlay {
position: absolute;
bottom: 14px;
right: 18px; /* "leicht rechts" */
width: 220px;
pointer-events: none; /* placeholder only */
opacity: 0.95;
}
/* Player rail: right of board, before notes */
.playerRail {
min-height: 0;
overflow: hidden;
border-radius: 22px;
border: 1px solid rgba(255,255,255,0.08);
background: rgba(0,0,0,0.18);
backdrop-filter: blur(10px);
box-shadow: 0 16px 50px rgba(0,0,0,0.35);
padding: 10px 8px;
display: grid;
grid-template-rows: auto 1fr;
justify-items: center;
gap: 10px;
}
.playerRailTitle {
font-weight: 900;
font-size: 12px;
opacity: 0.9;
}
.playerRailList {
min-height: 0; min-height: 0;
overflow: hidden; overflow: hidden;
display: grid; display: grid;
grid-template-columns: 1.15fr 0.9fr 1.15fr; gap: 10px;
gap: 14px; align-content: start;
justify-items: center;
padding-top: 4px;
} }
.extraRow { /* Bottom: Player HUD (User | Secret | Points) */
.playerHud {
min-height: 0; min-height: 0;
overflow: hidden; overflow: hidden;
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1.1fr 1.4fr 1.1fr;
gap: 14px; gap: 14px;
min-width: 0;
} }
/* RIGHT COLUMN */
.notesPane { .notesPane {
overflow: hidden; overflow: hidden;
min-width: 0; min-width: 0;
@@ -69,21 +138,32 @@ body {
padding-right: 4px; padding-right: 4px;
} }
/* Make it shrink gracefully on smaller viewports */ /* graceful shrink */
@media (max-height: 860px) { @media (max-height: 860px) {
.leftPane { .leftPane {
grid-template-rows: 64px minmax(280px, 1fr) 140px 96px; grid-template-rows: 64px minmax(280px, 1fr) 140px;
}
.diceOverlay {
bottom: 10px;
right: 12px;
width: 200px;
} }
} }
@media (max-width: 1280px) { @media (max-width: 1400px) {
.appRoot { .appRoot {
grid-template-columns: minmax(620px, 1fr) clamp(340px, 34vw, 480px); grid-template-columns: minmax(760px, 1fr) clamp(360px, 34vw, 480px);
}
.mainRow {
grid-template-columns: minmax(240px, 320px) minmax(480px, 1fr) 88px;
} }
} }
@media (max-width: 1120px) { @media (max-width: 1220px) {
.appRoot { .appRoot {
grid-template-columns: 1fr clamp(320px, 36vw, 440px); grid-template-columns: 1fr clamp(340px, 36vw, 460px);
}
.mainRow {
grid-template-columns: minmax(220px, 300px) minmax(420px, 1fr) 84px;
} }
} }