Add interactive dice panel with custom components
This commit introduces a new DicePanel component, complete with custom dice (DieD6 and HouseDie) for enhanced interactivity. The panel is integrated into the UI with responsive styling, hover effects, and animations, providing a polished and functional user experience. Styles and layout adjustments in `AppLayout.css` support the new feature.
This commit is contained in:
@@ -541,6 +541,261 @@ export default function App() {
|
||||
);
|
||||
};
|
||||
|
||||
// App.jsx (füge diese Komponente irgendwo unter deinen anderen Komponenten ein)
|
||||
// ✅ 3 Würfel: 2x d6 + 1x Spezial (Häuser + Hilfkarte + Dunkles Deck)
|
||||
|
||||
const DicePanel = () => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
pointerEvents: "auto", // overlay ist sonst pointer-events none im CSS
|
||||
display: "grid",
|
||||
gap: 10,
|
||||
alignContent: "start",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 18,
|
||||
border: `1px solid ${stylesTokens.panelBorder}`,
|
||||
background: stylesTokens.panelBg,
|
||||
boxShadow: "0 12px 30px rgba(0,0,0,0.35)",
|
||||
backdropFilter: "blur(10px)",
|
||||
padding: 12,
|
||||
overflow: "visible",
|
||||
minWidth: 0,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontWeight: 900,
|
||||
color: stylesTokens.textMain,
|
||||
fontSize: 13,
|
||||
letterSpacing: 0.2,
|
||||
lineHeight: 1.15,
|
||||
}}
|
||||
>
|
||||
Würfel
|
||||
</div>
|
||||
<div style={{ marginTop: 6, color: stylesTokens.textDim, fontSize: 12, opacity: 0.95 }}>
|
||||
2× d6 + 1× Spezial
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginTop: 10,
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(3, 1fr)",
|
||||
gap: 10,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<DieD6 value={4} />
|
||||
<DieD6 value={2} />
|
||||
<HouseDie face="gryffindor" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DieShell = ({ children, ringColor = "rgba(255,255,255,0.10)" }) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: 62,
|
||||
height: 62,
|
||||
borderRadius: 18,
|
||||
border: `1px solid ${stylesTokens.panelBorder}`,
|
||||
background:
|
||||
"radial-gradient(120% 120% at 20% 10%, rgba(255,255,255,0.10), rgba(0,0,0,0.35) 55%, rgba(0,0,0,0.55))",
|
||||
boxShadow: "0 18px 50px rgba(0,0,0,0.55), inset 0 0 0 1px rgba(255,255,255,0.06)",
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
display: "grid",
|
||||
placeItems: "center",
|
||||
cursor: "pointer",
|
||||
transition: "transform 160ms ease, box-shadow 160ms ease",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.transform = "translateY(-3px) rotate(-1deg)";
|
||||
e.currentTarget.style.boxShadow =
|
||||
"0 26px 70px rgba(0,0,0,0.65), inset 0 0 0 1px rgba(255,255,255,0.08)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.transform = "translateY(0px) rotate(0deg)";
|
||||
e.currentTarget.style.boxShadow =
|
||||
"0 18px 50px rgba(0,0,0,0.55), inset 0 0 0 1px rgba(255,255,255,0.06)";
|
||||
}}
|
||||
title="Würfel (placeholder)"
|
||||
>
|
||||
{/* subtle inner ring */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 7,
|
||||
borderRadius: 14,
|
||||
border: `1px solid ${ringColor}`,
|
||||
opacity: 0.85,
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* gloss */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
background:
|
||||
"linear-gradient(120deg, rgba(255,255,255,0.10) 0%, transparent 38%, transparent 70%, rgba(255,255,255,0.06) 100%)",
|
||||
opacity: 0.75,
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
/>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DieD6 = ({ value = 1 }) => {
|
||||
// pip layout positions on 3x3 grid
|
||||
const P = ({ x, y }) => (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: `${x * 50}%`,
|
||||
top: `${y * 50}%`,
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 7.5,
|
||||
height: 7.5,
|
||||
borderRadius: 999,
|
||||
background: "rgba(245,245,245,0.92)",
|
||||
boxShadow: "0 3px 10px rgba(0,0,0,0.35)",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const faces = {
|
||||
1: [{ x: 1, y: 1 }],
|
||||
2: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 2, y: 2 },
|
||||
],
|
||||
3: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 1, y: 1 },
|
||||
{ x: 2, y: 2 },
|
||||
],
|
||||
4: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 2, y: 0 },
|
||||
{ x: 0, y: 2 },
|
||||
{ x: 2, y: 2 },
|
||||
],
|
||||
5: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 2, y: 0 },
|
||||
{ x: 1, y: 1 },
|
||||
{ x: 0, y: 2 },
|
||||
{ x: 2, y: 2 },
|
||||
],
|
||||
6: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 0, y: 1 },
|
||||
{ x: 0, y: 2 },
|
||||
{ x: 2, y: 0 },
|
||||
{ x: 2, y: 1 },
|
||||
{ x: 2, y: 2 },
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<DieShell ringColor="rgba(242,210,122,0.18)">
|
||||
<div style={{ width: 44, height: 44, position: "relative" }}>
|
||||
{(faces[value] || faces[1]).map((p, idx) => (
|
||||
<P key={idx} x={p.x} y={p.y} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* tiny label */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 6,
|
||||
right: 8,
|
||||
fontSize: 10,
|
||||
fontWeight: 900,
|
||||
color: "rgba(255,255,255,0.55)",
|
||||
letterSpacing: 0.6,
|
||||
}}
|
||||
>
|
||||
d6
|
||||
</div>
|
||||
</DieShell>
|
||||
);
|
||||
};
|
||||
|
||||
const HouseDie = ({ face = "gryffindor" }) => {
|
||||
// 4 Häuser + Hilfkarte + Dunkles Deck
|
||||
const faces = {
|
||||
gryffindor: { label: "G", color: "#ef4444", sub: "Gryff." },
|
||||
slytherin: { label: "S", color: "#22c55e", sub: "Slyth." },
|
||||
ravenclaw: { label: "R", color: "#3b82f6", sub: "Raven." },
|
||||
hufflepuff: { label: "H", color: "#facc15", sub: "Huff." },
|
||||
help: { label: "?", color: "#f2d27a", sub: "Hilf" },
|
||||
dark: { label: "☾", color: "rgba(255,255,255,0.70)", sub: "Dark" },
|
||||
};
|
||||
|
||||
const f = faces[face] || faces.gryffindor;
|
||||
|
||||
return (
|
||||
<DieShell ringColor={`${f.color}55`}>
|
||||
<div
|
||||
style={{
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 14,
|
||||
display: "grid",
|
||||
placeItems: "center",
|
||||
background:
|
||||
"radial-gradient(120% 120% at 30% 20%, rgba(255,255,255,0.10), rgba(0,0,0,0.35) 70%, rgba(0,0,0,0.55))",
|
||||
border: `1px solid rgba(255,255,255,0.08)`,
|
||||
boxShadow: `inset 0 0 0 1px ${f.color}22`,
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontWeight: 1000,
|
||||
fontSize: 22,
|
||||
color: f.color,
|
||||
textShadow: `0 0 16px ${f.color}55, 0 8px 24px rgba(0,0,0,0.55)`,
|
||||
letterSpacing: 0.5,
|
||||
}}
|
||||
>
|
||||
{f.label}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 6,
|
||||
left: 10,
|
||||
fontSize: 10,
|
||||
fontWeight: 900,
|
||||
color: "rgba(255,255,255,0.55)",
|
||||
letterSpacing: 0.6,
|
||||
}}
|
||||
>
|
||||
Spezial
|
||||
</div>
|
||||
</DieShell>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const PlayerIdentityCard = ({
|
||||
name = "Harry Potter",
|
||||
houseLabel = "Gryffindor",
|
||||
@@ -761,7 +1016,7 @@ export default function App() {
|
||||
</PlaceholderCard>
|
||||
|
||||
<div className="diceOverlay">
|
||||
<PlaceholderCard title="Würfel" variant="compact" />
|
||||
<DicePanel />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user