Skip to content

Commit dbcee70

Browse files
committed
feat: dice
1 parent 96e5cb1 commit dbcee70

File tree

6 files changed

+359
-8
lines changed

6 files changed

+359
-8
lines changed

src/components/AnimatedRoutes.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import UuidGeneratorPage from '../pages/apps/UuidGeneratorPage';
2929
import ColorPaletteGeneratorPage from '../pages/apps/ColorPaletteGeneratorPage';
3030
import CssUnitConverterPage from '../pages/apps/CssUnitConverterPage';
3131
import FantasyNameGeneratorPage from '../pages/apps/FantasyNameGeneratorPage';
32+
import DiceRollerPage from '../pages/apps/DiceRollerPage';
3233

3334
import UsefulLinksPage from '../pages/UsefulLinksPage';
3435

@@ -465,6 +466,20 @@ function AnimatedRoutes() {
465466
</motion.div>
466467
}
467468
/>
469+
<Route
470+
path="/apps/dice-roller"
471+
element={
472+
<motion.div
473+
initial="initial"
474+
animate="in"
475+
exit="out"
476+
variants={pageVariants}
477+
transition={pageTransition}
478+
>
479+
<DiceRollerPage />
480+
</motion.div>
481+
}
482+
/>
468483
{/* D&D specific 404 page */}
469484
<Route
470485
path="/dnd/*"

src/components/AppCard.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ArrowRight } from '@phosphor-icons/react';
44
import colors from '../config/colors';
55

66
const AppCard = ({ app }) => {
7-
const { to, title, description } = app;
7+
const { to, title, description, icon: Icon } = app;
88

99
const cardStyle = {
1010
backgroundColor: colors['app-alpha-10'],
@@ -37,7 +37,10 @@ const AppCard = ({ app }) => {
3737
}}
3838
></div>
3939
<div>
40-
<h2 className="text-xl font-normal transition-colors" style={{ color: cardStyle.color }}>{title}</h2>
40+
<h2 className="text-xl font-normal transition-colors flex items-center gap-2" style={{ color: cardStyle.color }}>
41+
{Icon && <Icon size={24} />}
42+
{title}
43+
</h2>
4144
<p className="mt-2" style={{ color: detailTextColor }}>{description}</p>
4245
</div>
4346
<div className="flex justify-end items-center mt-4">

src/components/Dice.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React from 'react';
2+
import { DiceSix } from '@phosphor-icons/react';
3+
import colors from '../config/colors';
4+
5+
const Dice = ({ value, type, isRolling }) => {
6+
const diceStyle = {
7+
width: '60px',
8+
height: '60px',
9+
display: 'flex',
10+
justifyContent: 'center',
11+
alignItems: 'center',
12+
border: `2px solid ${colors['app-alpha-50']}`,
13+
borderRadius: '8px',
14+
backgroundColor: colors['app-alpha-10'],
15+
color: colors.app,
16+
fontSize: '24px',
17+
fontWeight: 'bold',
18+
position: 'relative',
19+
overflow: 'hidden',
20+
};
21+
22+
const pipStyle = {
23+
backgroundColor: colors.app,
24+
borderRadius: '50%',
25+
position: 'absolute',
26+
};
27+
28+
const renderPips = (num) => {
29+
const pips = [];
30+
const pipSize = '10px';
31+
const offset = '15px';
32+
33+
const getPipPosition = (position) => {
34+
switch (position) {
35+
case 'top-left': return { top: offset, left: offset };
36+
case 'top-center': return { top: offset, left: '50%', transform: 'translateX(-50%)' };
37+
case 'top-right': return { top: offset, right: offset };
38+
case 'middle-left': return { top: '50%', left: offset, transform: 'translateY(-50%)' };
39+
case 'middle-center': return { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' };
40+
case 'middle-right': return { top: '50%', right: offset, transform: 'translateY(-50%)' };
41+
case 'bottom-left': return { bottom: offset, left: offset };
42+
case 'bottom-center': return { bottom: offset, left: '50%', transform: 'translateX(-50%)' };
43+
case 'bottom-right': return { bottom: offset, right: offset };
44+
default: return {};
45+
}
46+
};
47+
48+
const pipLayouts = {
49+
1: ['middle-center'],
50+
2: ['top-left', 'bottom-right'],
51+
3: ['top-left', 'middle-center', 'bottom-right'],
52+
4: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
53+
5: ['top-left', 'top-right', 'middle-center', 'bottom-left', 'bottom-right'],
54+
6: ['top-left', 'top-right', 'middle-left', 'middle-right', 'bottom-left', 'bottom-right'],
55+
};
56+
57+
if (pipLayouts[num]) {
58+
pipLayouts[num].forEach((pos, index) => {
59+
pips.push(
60+
<div
61+
key={index}
62+
style={{ ...pipStyle, ...getPipPosition(pos), width: pipSize, height: pipSize }}
63+
/>
64+
);
65+
});
66+
}
67+
return pips;
68+
};
69+
70+
if (isRolling) {
71+
return (
72+
<div className="dice-face rolling" style={diceStyle}>
73+
<DiceSix size={48} color={colors.app} />
74+
</div>
75+
);
76+
} else if (type === 6) {
77+
return (
78+
<div className="dice-face" style={diceStyle}>
79+
{renderPips(value)}
80+
</div>
81+
);
82+
} else {
83+
return (
84+
<div className="dice-face" style={diceStyle}>
85+
{value}
86+
</div>
87+
);
88+
}
89+
};
90+
91+
export default Dice;

src/pages/AppPage.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import { Link } from 'react-router-dom';
3-
import { ArrowLeftIcon } from '@phosphor-icons/react';
3+
import { ArrowLeftIcon, ListNumbers, Sparkle, TextT, TextAa, Code, Link as LinkIcon, Keyboard, Fingerprint, Key, Palette, Ruler, DiceSix } from '@phosphor-icons/react';
44
import AppCard from '../components/AppCard';
55
import usePageTitle from '../utils/usePageTitle';
66

@@ -9,56 +9,73 @@ const apps = [
99
to: '/apps/tournament-bracket',
1010
title: 'Tournament Bracket',
1111
description: 'Create and manage tournament brackets.',
12+
icon: ListNumbers,
13+
},
14+
{
15+
to: '/apps/fantasy-name-generator',
16+
title: 'Fantasy Name Generator',
17+
description: 'Generate fantasy names for characters, places, and items.',
18+
icon: Sparkle,
19+
},
20+
{
21+
to: '/apps/dice-roller',
22+
title: 'Dice Roller',
23+
description: 'Roll various types of dice for your games and adventures.',
24+
icon: DiceSix,
1225
},
1326
{
1427
to: '/apps/word-counter',
1528
title: 'Word Counter',
1629
description: 'Count words, characters, lines and paragraphs in a text.',
30+
icon: TextT,
1731
},
1832
{
1933
to: '/apps/case-converter',
2034
title: 'Case Converter',
2135
description: 'Convert text to different cases (e.g., uppercase, lowercase, camelCase).',
36+
icon: TextAa,
2237
},
2338
{
2439
to: '/apps/base64-converter',
2540
title: 'Base64 Converter',
2641
description: 'Encode and decode text to and from Base64 format.',
42+
icon: Code,
2743
},
2844
{
2945
to: '/apps/url-converter',
3046
title: 'URL Encoder/Decoder',
3147
description: 'Encode and decode URL strings.',
48+
icon: LinkIcon,
3249
},
3350
{
3451
to: '/apps/ascii-converter',
3552
title: 'Text to ASCII Converter',
3653
description: 'Convert text to ASCII codes and vice-versa.',
54+
icon: Keyboard,
3755
},
3856
{
3957
to: '/apps/hash-generator',
4058
title: 'Hash Generator',
4159
description: 'Generate SHA1, SHA256, and SHA512 hashes from text.',
60+
icon: Fingerprint,
4261
},
4362
{
4463
to: '/apps/uuid-generator',
4564
title: 'UUID Generator',
4665
description: 'Generate UUID v4.',
66+
icon: Key,
4767
},
4868
{
4969
to: '/apps/color-palette-generator',
5070
title: 'Color Palette Generator',
5171
description: 'Generate random color palettes.',
72+
icon: Palette,
5273
},
5374
{
5475
to: '/apps/css-unit-converter',
5576
title: 'CSS Unit Converter',
5677
description: 'Convert between px, em, rem, vw, vh, and % units.',
57-
},
58-
{
59-
to: '/apps/fantasy-name-generator',
60-
title: 'Fantasy Name Generator',
61-
description: 'Generate fantasy names for characters, places, and items.',
78+
icon: Ruler,
6279
},
6380
];
6481

src/pages/apps/DiceRollerPage.css

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
.dice-animation-container {
2+
display: flex;
3+
flex-wrap: wrap;
4+
justify-content: center; /* Changed to center for better aesthetics when fewer than 10 dice */
5+
gap: 10px;
6+
margin-top: 20px;
7+
max-width: 710px; /* (60px width + 10px gap) * 10 dice - 10px (last gap) = 700px. Added 10px for padding/margin tolerance */
8+
margin-left: auto;
9+
margin-right: auto;
10+
}
11+
12+
.dice-face {
13+
flex-shrink: 0; /* Prevent dice from shrinking */
14+
width: 60px;
15+
height: 60px;
16+
display: flex;
17+
justify-content: center;
18+
align-items: center;
19+
border: 2px solid #ccc;
20+
border-radius: 8px;
21+
background-color: #333;
22+
color: white;
23+
font-size: 24px;
24+
font-weight: bold;
25+
}
26+
27+
.dice-face.rolling {
28+
animation: roll 1s infinite linear;
29+
}
30+
31+
@keyframes roll {
32+
0% {
33+
transform: rotate(0deg) scale(1);
34+
opacity: 1;
35+
}
36+
25% {
37+
transform: rotate(90deg) scale(1.1);
38+
opacity: 0.8;
39+
}
40+
50% {
41+
transform: rotate(180deg) scale(1);
42+
opacity: 1;
43+
}
44+
75% {
45+
transform: rotate(270deg) scale(1.1);
46+
opacity: 0.8;
47+
}
48+
100% {
49+
transform: rotate(360deg) scale(1);
50+
opacity: 1;
51+
}
52+
}
53+
54+
.roll-button:not(:disabled):hover {
55+
background-color: rgba(158, 197, 171, 0.5) !important;
56+
}

0 commit comments

Comments
 (0)