Skip to content

Commit 354dbc4

Browse files
committed
app(codename)
1 parent 282cbc1 commit 354dbc4

File tree

4 files changed

+153
-2
lines changed

4 files changed

+153
-2
lines changed

public/posts/picker-wheel-deep-dive.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
In this post, we'll take a deep dive into the implementation of the Picker Wheel app, a fun and interactive way to pick a random winner from a list of entries. We'll explore how it's built using React, the Canvas API, and Tailwind CSS, and we'll cover the key concepts and techniques used in its development.
44

5+
You can play with it here [apps::pw](/#/apps::pw)
6+
57
## The Canvas Wheel
68

79
The heart of the Picker Wheel is the wheel itself, which is drawn using the HTML5 Canvas API. The canvas provides a powerful and flexible way to draw graphics and animations, and it's perfect for creating the dynamic and interactive wheel we need.
@@ -209,4 +211,3 @@ The app uses a flexbox layout to arrange the wheel and the entry list side-by-si
209211
## Conclusion
210212

211213
The Picker Wheel app is a fun and interactive way to pick a random winner from a list of entries. It's built using a combination of React, the Canvas API, and Tailwind CSS, and it demonstrates a variety of concepts and techniques used in modern web development.
212-

src/components/AnimatedRoutes.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import CssUnitConverterPage from '../pages/apps/CssUnitConverterPage';
3131
import FantasyNameGeneratorPage from '../pages/apps/FantasyNameGeneratorPage';
3232
import DiceRollerPage from '../pages/apps/DiceRollerPage';
3333
import PickerWheelPage from '../pages/apps/PickerWheelPage';
34+
import CodenameGeneratorPage from '../pages/apps/CodenameGeneratorPage';
3435

3536
import UsefulLinksPage from '../pages/UsefulLinksPage';
3637

@@ -314,6 +315,7 @@ function AnimatedRoutes() {
314315
<Route path="/apps::fng" element={<Navigate to="/apps/fantasy-name-generator" replace />} />
315316
<Route path="/apps::dice" element={<Navigate to="/apps/dice-roller" replace />} />
316317
<Route path="/apps::pw" element={<Navigate to="/apps/picker-wheel" replace />} />
318+
<Route path="/apps::cg" element={<Navigate to="/apps/codename-generator" replace />} />
317319
{/* End of hardcoded redirects */}
318320
<Route
319321
path="/apps/ip"
@@ -512,6 +514,20 @@ function AnimatedRoutes() {
512514
</motion.div>
513515
}
514516
/>
517+
<Route
518+
path="/apps/codename-generator"
519+
element={
520+
<motion.div
521+
initial="initial"
522+
animate="in"
523+
exit="out"
524+
variants={pageVariants}
525+
transition={pageTransition}
526+
>
527+
<CodenameGeneratorPage />
528+
</motion.div>
529+
}
530+
/>
515531
{/* D&D specific 404 page */}
516532
<Route
517533
path="/dnd/*"

src/pages/AppPage.js

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

77
const apps = [
8+
{
9+
to: '/apps/codename-generator',
10+
title: 'Codename Generator',
11+
description: 'Generate cool, spy-ish project names.',
12+
icon: ShieldCheck,
13+
},
814
{
915
to: '/apps/picker-wheel',
1016
title: 'Picker Wheel',
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { Link } from 'react-router-dom';
3+
import { ArrowLeftIcon, Sparkle, Copy } from '@phosphor-icons/react';
4+
import usePageTitle from '../../utils/usePageTitle';
5+
import colors from '../../config/colors';
6+
import { useToast } from '../../hooks/useToast';
7+
8+
const prefixes = [
9+
'Operation', 'Project', 'Protocol', 'Directive', 'Initiative', 'Task Force', 'Unit', 'Division', 'Cell', 'Asset',
10+
'Red', 'Blue', 'Green', 'Black', 'White', 'Gold', 'Silver', 'Bronze',
11+
];
12+
13+
const nouns = [
14+
'Viper', 'Phoenix', 'Shadow', 'Ghost', 'Spectre', 'Wraith', 'Raven', 'Crow', 'Jackal', 'Cobra', 'Hydra', 'Cerberus',
15+
'Aegis', 'Shield', 'Sword', 'Dagger', 'Arrow', 'Spear', 'Javelin', 'Trident', 'Hammer', 'Anvil', 'Crucible',
16+
'Avalanche', 'Blizzard', 'Cyclone', 'Hurricane', 'Tornado', 'Typhoon', 'Earthquake', 'Volcano', 'Tsunami',
17+
'Genesis', 'Exodus', 'Leviathan', 'Behemoth', 'Goliath', 'Titan', 'Colossus', 'Gargantua', 'Prometheus',
18+
'Icarus', 'Daedalus', 'Orpheus', 'Morpheus', 'Nyx', 'Erebus', 'Hades', 'Styx', 'Charon', 'Thanatos',
19+
'Valkyrie', 'Ragnarok', 'Valhalla', 'Asgard', 'Midgard', 'Jotunheim', 'Fenrir', 'Jormungandr', 'Sleipnir',
20+
'Paperclip', 'Viking', 'Survival', 'Overlord', 'Neptune', 'Barbarossa', 'Stalingrad', 'Kursk', 'Midway',
21+
'Normandy', 'Market Garden', 'Watchtower', 'Downfall', 'Unthinkable', 'Crossbow', 'Mincemeat', 'Fortitude',
22+
'Dragon', 'Wyvern', 'Griffin', 'Chimera', 'Manticore', 'Basilisk', 'Kraken', 'Scylla', 'Charybdis',
23+
'Sun', 'Moon', 'Star', 'Comet', 'Nebula', 'Galaxy', 'Quasar', 'Pulsar', 'Supernova',
24+
'Storm', 'Tempest', 'Maelstrom', 'Vortex', 'Singularity', 'Event Horizon', 'Black Hole',
25+
'Nomad', 'Wanderer', 'Ronin', 'Samurai', 'Ninja', 'Shinobi', 'Kunoichi',
26+
'Gladiator', 'Centurion', 'Legionnaire', 'Spartan', 'Hoplite', 'Phalanx',
27+
'Crusader', 'Paladin', 'Templar', 'Inquisitor', 'Exorcist',
28+
'Warlock', 'Sorcerer', 'Mage', 'Wizard', 'Archmage', 'Necromancer',
29+
'Druid', 'Shaman', 'Witch Doctor', 'Oracle', 'Seer', 'Prophet',
30+
'Bard', 'Skald', 'Troubadour', 'Minstrel',
31+
'Thief', 'Rogue', 'Assassin', 'Cutthroat', 'Bandit', 'Outlaw',
32+
'Pirate', 'Corsair', 'Privateer', 'Buccaneer', 'Freebooter',
33+
'Rebel', 'Insurgent', 'Guerrilla', 'Freedom Fighter', 'Revolutionary',
34+
'Anarchist', 'Nihilist', 'Terrorist', 'Extremist',
35+
'Cyborg', 'Android', 'Robot', 'Mech', 'Drone',
36+
'Mutant', 'Chimera', 'Hybrid', 'Abomination',
37+
'Vampire', 'Werewolf', 'Lich', 'Ghoul', 'Zombie',
38+
'Angel', 'Demon', 'Devil', 'Archon', 'Seraph', 'Nephilim',
39+
'God', 'Goddess', 'Deity', 'Avatar', 'Primordial',
40+
'Serpent', 'Owl', 'Hawk', 'Viper', 'Hunter', 'Orchid', 'Fox', 'Mamba', 'Phoenix', 'Wind'
41+
];
42+
43+
const CodenameGeneratorPage = () => {
44+
usePageTitle('Codename Generator');
45+
const [codename, setCodename] = useState('');
46+
const { addToast } = useToast();
47+
48+
useEffect(() => {
49+
generateCodename();
50+
}, []);
51+
52+
const generateCodename = () => {
53+
const prefix = prefixes[Math.floor(Math.random() * prefixes.length)];
54+
const noun = nouns[Math.floor(Math.random() * nouns.length)];
55+
setCodename(`${prefix} ${noun}`);
56+
};
57+
58+
const handleCopy = () => {
59+
navigator.clipboard.writeText(codename);
60+
addToast('Copied to clipboard!', 'success');
61+
};
62+
63+
const cardStyle = {
64+
backgroundColor: colors['app-alpha-10'],
65+
borderColor: colors['app-alpha-50'],
66+
color: colors.app,
67+
};
68+
69+
return (
70+
<div className="py-16 sm:py-24">
71+
<div className="mx-auto max-w-7xl px-6 lg:px-8 text-gray-300">
72+
<Link
73+
to="/apps"
74+
className="text-article hover:underline flex items-center justify-center gap-2 text-lg mb-4"
75+
>
76+
<ArrowLeftIcon size={24} /> Back to Apps
77+
</Link>
78+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center">
79+
<span className="codex-color">fc</span>
80+
<span className="separator-color">::</span>
81+
<span className="apps-color">apps</span>
82+
<span className="separator-color">::</span>
83+
<span className="single-app-color">codename-generator</span>
84+
</h1>
85+
<hr className="border-gray-700" />
86+
<div className="flex justify-center items-center mt-16">
87+
<div
88+
className="group bg-transparent border rounded-lg shadow-2xl p-6 flex flex-col justify-between relative transform transition-all duration-300 ease-in-out scale-105 overflow-hidden h-full w-full max-w-4xl"
89+
style={cardStyle}
90+
>
91+
<div
92+
className="absolute top-0 left-0 w-full h-full opacity-10"
93+
style={{
94+
backgroundImage:
95+
'radial-gradient(circle, white 1px, transparent 1px)',
96+
backgroundSize: '10px 10px',
97+
}}
98+
></div>
99+
<div className="relative z-10 p-1">
100+
<h1 className="text-3xl font-arvo font-normal mb-4 text-app"> Codename Generator </h1>
101+
<hr className="border-gray-700 mb-4" />
102+
<div className="flex flex-col items-center gap-8">
103+
<div className="text-4xl text-app font-arvo font-bold text-center h-16 mt-12">{codename}</div>
104+
<div className="flex gap-4">
105+
<button
106+
onClick={generateCodename}
107+
className="flex items-center gap-2 text-lg font-arvo font-normal px-4 py-2 rounded-md border transition-colors duration-300 ease-in-out bg-app/50 text-white hover:bg-app/70"
108+
>
109+
<Sparkle size={20} /> Generate
110+
</button>
111+
<button
112+
onClick={handleCopy}
113+
className="flex items-center gap-2 text-lg font-arvo font-normal px-4 py-2 rounded-md border transition-colors duration-300 ease-in-out bg-app/50 text-white hover:bg-app/70"
114+
disabled={!codename}
115+
>
116+
<Copy size={20} /> Copy
117+
</button>
118+
</div>
119+
</div>
120+
</div>
121+
</div>
122+
</div>
123+
</div>
124+
</div>
125+
);
126+
};
127+
128+
export default CodenameGeneratorPage;

0 commit comments

Comments
 (0)