Skip to content

Commit bdc7895

Browse files
committed
feat: lorem ipsum
1 parent 5fe16d0 commit bdc7895

File tree

4 files changed

+207
-1
lines changed

4 files changed

+207
-1
lines changed

public/apps/apps.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,13 @@
202202
"title": "Color Palette Generator",
203203
"description": "Generate random color palettes.",
204204
"icon": "PaletteIcon"
205+
},
206+
{
207+
"slug": "lorem-ipsum-generator",
208+
"to": "/apps/lorem-ipsum-generator",
209+
"title": "Lorem Ipsum Generator",
210+
"description": "Generate random placeholder text for your projects.",
211+
"icon": "ArticleIcon"
205212
}
206213
]
207214
},

src/components/AnimatedRoutes.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ import WordLadderPage from '../pages/apps/WordLadderPage'; // Import WordLadderP
5959
import LightsOutPage from '../pages/apps/LightsOutPage'; // Import LightsOutPage
6060
import NonogramPage from '../pages/apps/NonogramPage'; // Import NonogramPage
6161
import WhackABugPage from '../pages/apps/WhackABugPage';
62-
import SimonSaysPage from '../pages/apps/SimonSaysPage';
6362
import BubbleWrapPage from '../pages/apps/BubbleWrapPage';
63+
import LoremIpsumGeneratorPage from '../pages/apps/LoremIpsumGeneratorPage';
64+
import SimonSaysPage from '../pages/apps/SimonSaysPage';
6465
import SettingsPage from '../pages/SettingsPage';
6566

6667
import UsefulLinksPage from '../pages/UsefulLinksPage';
@@ -574,6 +575,10 @@ function AnimatedRoutes() {
574575
path="/apps::pop"
575576
element={<Navigate to="/apps/bubble-wrap" replace />}
576577
/>
578+
<Route
579+
path="/apps::lorem"
580+
element={<Navigate to="/apps/lorem-ipsum-generator" replace />}
581+
/>
577582
{/* End of hardcoded redirects */}
578583
<Route
579584
path="/apps/ip"
@@ -1178,6 +1183,20 @@ function AnimatedRoutes() {
11781183
</motion.div>
11791184
}
11801185
/>
1186+
<Route
1187+
path="/apps/lorem-ipsum-generator"
1188+
element={
1189+
<motion.div
1190+
initial="initial"
1191+
animate="in"
1192+
exit="out"
1193+
variants={pageVariants}
1194+
transition={pageTransition}
1195+
>
1196+
<LoremIpsumGeneratorPage />
1197+
</motion.div>
1198+
}
1199+
/>
11811200
{/* D&D specific 404 page */}
11821201
<Route
11831202
path="/stories/*"
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import React, {useState} from 'react';
2+
import {Link} from 'react-router-dom';
3+
import {ArrowLeftIcon, ArticleIcon} from '@phosphor-icons/react';
4+
import colors from '../../config/colors';
5+
import useSeo from '../../hooks/useSeo';
6+
import {useToast} from '../../hooks/useToast';
7+
8+
const LOREM_WORDS = [
9+
'lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit', 'sed', 'do',
10+
'eiusmod', 'tempor', 'incididunt', 'ut', 'labore', 'et', 'dolore', 'magna', 'aliqua', 'ut',
11+
'enim', 'ad', 'minim', 'veniam', 'quis', 'nostrud', 'exercitation', 'ullamco', 'laboris',
12+
'nisi', 'ut', 'aliquip', 'ex', 'ea', 'commodo', 'consequat', 'duis', 'aute', 'irure', 'dolor',
13+
'in', 'reprehenderit', 'in', 'voluptate', 'velit', 'esse', 'cillum', 'dolore', 'eu', 'fugiat',
14+
'nulla', 'pariatur', 'excepteur', 'sint', 'occaecat', 'cupidatat', 'non', 'proident', 'sunt',
15+
'in', 'culpa', 'qui', 'officia', 'deserunt', 'mollit', 'anim', 'id', 'est', 'laborum'
16+
];
17+
18+
const LoremIpsumGeneratorPage = () => {
19+
useSeo({
20+
title: 'Lorem Ipsum Generator | Fezcodex',
21+
description: 'Generate random placeholder text for your projects.',
22+
keywords: ['Fezcodex', 'lorem ipsum', 'text generator', 'placeholder text', 'dummy text'],
23+
ogTitle: 'Lorem Ipsum Generator | Fezcodex',
24+
ogDescription: 'Generate random placeholder text for your projects.',
25+
ogImage: 'https://fezcode.github.io/logo512.png',
26+
twitterCard: 'summary_large_image',
27+
twitterTitle: 'Lorem Ipsum Generator | Fezcodex',
28+
twitterDescription: 'Generate random placeholder text for your projects.',
29+
twitterImage: 'https://fezcode.github.io/logo512.png',
30+
});
31+
32+
const {addToast} = useToast();
33+
const [paragraphs, setParagraphs] = useState(3);
34+
const [startWithLorem, setStartWithLorem] = useState(true);
35+
const [generatedText, setGeneratedText] = useState('');
36+
37+
const generateText = () => {
38+
let text = [];
39+
for (let i = 0; i < paragraphs; i++) {
40+
const numSentences = Math.floor(Math.random() * 5) + 3; // 3 to 7 sentences
41+
let paragraph = [];
42+
43+
for (let j = 0; j < numSentences; j++) {
44+
const numWords = Math.floor(Math.random() * 10) + 5; // 5 to 14 words
45+
let sentence = [];
46+
for (let k = 0; k < numWords; k++) {
47+
const word = LOREM_WORDS[Math.floor(Math.random() * LOREM_WORDS.length)];
48+
sentence.push(word);
49+
}
50+
// Capitalize first letter
51+
sentence[0] = sentence[0].charAt(0).toUpperCase() + sentence[0].slice(1);
52+
paragraph.push(sentence.join(' ') + '.');
53+
}
54+
text.push(paragraph.join(' '));
55+
}
56+
57+
let finalResult = text.join('\n\n');
58+
59+
if (startWithLorem) {
60+
const prefix = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ";
61+
// Simple check to avoid double generating if random chance made it similar
62+
if (!finalResult.startsWith("Lorem")) {
63+
finalResult = prefix + finalResult;
64+
}
65+
}
66+
67+
setGeneratedText(finalResult);
68+
};
69+
70+
// Initial generation
71+
React.useEffect(() => {
72+
generateText();
73+
// eslint-disable-next-line react-hooks/exhaustive-deps
74+
}, []);
75+
76+
const copyToClipboard = () => {
77+
navigator.clipboard.writeText(generatedText).then(() => {
78+
addToast({title: 'Copied!', message: 'Text copied to clipboard.', duration: 2000});
79+
});
80+
};
81+
82+
const cardStyle = {
83+
backgroundColor: colors['app-alpha-10'],
84+
borderColor: colors['app-alpha-50'],
85+
color: colors.app,
86+
};
87+
88+
return (
89+
<div className="py-16 sm:py-24">
90+
<div className="mx-auto max-w-7xl px-6 lg:px-8 text-gray-300">
91+
<Link
92+
to="/apps"
93+
className="text-article hover:underline flex items-center justify-center gap-2 text-lg mb-4"
94+
>
95+
<ArrowLeftIcon size={24}/> Back to Apps
96+
</Link>
97+
<h1 className="text-4xl font-bold tracking-tight sm:text-6xl mb-4 flex items-center justify-center">
98+
<span className="codex-color">fc</span>
99+
<span className="separator-color">::</span>
100+
<span className="apps-color">apps</span>
101+
<span className="separator-color">::</span>
102+
<span className="single-app-color">lorem</span>
103+
</h1>
104+
<hr className="border-gray-700"/>
105+
<div className="flex justify-center items-center mt-16">
106+
<div
107+
className="group bg-transparent border rounded-lg shadow-2xl p-6 flex flex-col justify-between relative transform overflow-hidden h-full w-full max-w-4xl"
108+
style={cardStyle}
109+
>
110+
<div
111+
className="absolute top-0 left-0 w-full h-full opacity-10"
112+
style={{
113+
backgroundImage:
114+
'radial-gradient(circle, white 1px, transparent 1px)',
115+
backgroundSize: '10px 10px',
116+
}}
117+
></div>
118+
<div className="relative z-10 p-1">
119+
<h1 className="text-3xl font-arvo font-normal mb-4 text-app flex items-center gap-2">
120+
<ArticleIcon size={32}/> Lorem Ipsum Generator
121+
</h1>
122+
<hr className="border-gray-700 mb-6"/>
123+
124+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
125+
<div>
126+
<label className="block text-sm font-medium mb-2 opacity-80">Paragraphs</label>
127+
<input
128+
type="number"
129+
min="1"
130+
max="50"
131+
value={paragraphs}
132+
onChange={(e) => setParagraphs(parseInt(e.target.value) || 1)}
133+
className="w-full bg-black/20 border border-gray-600 rounded px-3 py-2 focus:outline-none focus:border-blue-500 transition-colors"
134+
/>
135+
</div>
136+
<div className="flex items-center">
137+
<label className="flex items-center space-x-3 cursor-pointer mt-6">
138+
<input
139+
type="checkbox"
140+
checked={startWithLorem}
141+
onChange={(e) => setStartWithLorem(e.target.checked)}
142+
className="form-checkbox h-5 w-5 text-blue-600 rounded bg-black/20 border-gray-600"
143+
/>
144+
<span>Start with "Lorem ipsum..."</span>
145+
</label>
146+
</div>
147+
</div>
148+
149+
<div className="flex justify-end gap-4 mb-6">
150+
<button
151+
onClick={generateText}
152+
className="px-6 py-2 rounded-md font-arvo font-normal border transition-colors duration-300 hover:bg-white/10"
153+
style={{borderColor: cardStyle.color, color: cardStyle.color}}
154+
>
155+
Generate
156+
</button>
157+
<button
158+
onClick={copyToClipboard}
159+
className="px-6 py-2 rounded-md font-arvo font-normal border transition-colors duration-300 text-blue-500 hover:bg-blue-500 hover:text-white border-blue-500"
160+
>
161+
Copy
162+
</button>
163+
</div>
164+
165+
<div
166+
className="bg-black/30 rounded-lg p-4 min-h-[200px] max-h-[500px] overflow-y-auto font-serif leading-relaxed whitespace-pre-wrap border border-gray-700">
167+
{generatedText}
168+
</div>
169+
170+
</div>
171+
</div>
172+
</div>
173+
</div>
174+
</div>
175+
);
176+
};
177+
178+
export default LoremIpsumGeneratorPage;

src/utils/appIcons.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
GridFourIcon,
3939
BugIcon,
4040
CirclesFourIcon,
41+
ArticleIcon,
4142
} from '@phosphor-icons/react';
4243

4344
export const appIcons = {
@@ -80,4 +81,5 @@ export const appIcons = {
8081
GridFourIcon,
8182
BugIcon,
8283
CirclesFourIcon,
84+
ArticleIcon,
8385
};

0 commit comments

Comments
 (0)