Skip to content

Commit 3d4bda2

Browse files
authored
feat: implemented responsiveness for mobile (#22)
2 parents a73c0f6 + 362e4fe commit 3d4bda2

7 files changed

Lines changed: 281 additions & 147 deletions

File tree

app/_components/forms-stages/form-stage-3.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export default function FormStage3({ data, updateData }: FormStage3Props) {
4343
/>
4444
</FormField>
4545

46-
<div className="grid grid-cols-2 gap-3">
46+
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
4747
<FormField label="Company / Org" optional>
4848
<TextInput
4949
type="text"
@@ -89,7 +89,7 @@ export default function FormStage3({ data, updateData }: FormStage3Props) {
8989
/>
9090
</FormField>
9191

92-
<div className="grid grid-cols-2 gap-3">
92+
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
9393
<FormField label="Institution" optional>
9494
<TextInput
9595
type="text"

app/_components/forms-stages/form-stage-5.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export default function FormStage5({ data, updateData }: FormStage5Props) {
129129
onChange={(v) => updateData({ showPinnedRepos: v })}
130130
/>
131131
{data.showPinnedRepos && (
132-
<div className="grid grid-cols-2 gap-3 pl-3">
132+
<div className="grid grid-cols-1 gap-3 pl-3 sm:grid-cols-2">
133133
<RepoSelect
134134
label="Left Repo"
135135
value={data.pinnedRepo1}
Lines changed: 158 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,62 @@
11
"use client";
22

3-
import { Clipboard, Download, RefreshCw, Check } from "lucide-react";
4-
import { useEffect, useMemo, useState } from "react";
3+
import {
4+
Clipboard,
5+
Download,
6+
RefreshCw,
7+
Check,
8+
X,
9+
PartyPopper,
10+
Terminal,
11+
Info,
12+
} from "lucide-react";
13+
import { useEffect, useState } from "react";
14+
import Confetti from "react-confetti";
15+
import { cn } from "@/lib/utils";
516

617
interface FormStageDoneProps {
718
markdown: string;
819
onReset: () => void;
20+
onClose: () => void;
921
}
1022

11-
export default function FormStageDone({ markdown, onReset }: FormStageDoneProps) {
23+
function useWindowSize() {
24+
const [windowSize, setWindowSize] = useState({
25+
width: typeof window !== "undefined" ? window.innerWidth : 0,
26+
height: typeof window !== "undefined" ? window.innerHeight : 0,
27+
});
28+
29+
useEffect(() => {
30+
function handleResize() {
31+
setWindowSize({
32+
width: window.innerWidth,
33+
height: window.innerHeight,
34+
});
35+
}
36+
37+
window.addEventListener("resize", handleResize);
38+
return () => window.removeEventListener("resize", handleResize);
39+
}, []);
40+
41+
return windowSize;
42+
}
43+
44+
export default function FormStageDone({ markdown, onReset, onClose }: FormStageDoneProps) {
1245
const [copied, setCopied] = useState(false);
1346
const [showConfetti, setShowConfetti] = useState(true);
14-
const confettiPieces = useMemo(
15-
() =>
16-
Array.from({ length: 24 }, (_, index) => ({
17-
id: index,
18-
left: `${Math.random() * 100}%`,
19-
delay: `${Math.random() * 0.6}s`,
20-
duration: `${3.2 + Math.random() * 1.4}s`,
21-
rotation: `${Math.random() * 360}deg`,
22-
color: ["#f59e0b", "#22c55e", "#3b82f6", "#ef4444", "#a855f7", "#14b8a6"][
23-
index % 6
24-
],
25-
})),
26-
[],
27-
);
47+
const [isClosing, setIsClosing] = useState(false);
48+
const { width, height } = useWindowSize();
2849

2950
useEffect(() => {
30-
const timeout = window.setTimeout(() => setShowConfetti(false), 4200);
51+
const timeout = window.setTimeout(() => setShowConfetti(false), 8000);
3152
return () => window.clearTimeout(timeout);
3253
}, []);
3354

55+
const handleClose = () => {
56+
setIsClosing(true);
57+
setTimeout(onClose, 200);
58+
};
59+
3460
const handleCopy = async () => {
3561
await navigator.clipboard.writeText(markdown);
3662
setCopied(true);
@@ -50,102 +76,127 @@ export default function FormStageDone({ markdown, onReset }: FormStageDoneProps)
5076
};
5177

5278
return (
53-
<div className="relative grid h-full place-content-center gap-6 overflow-hidden px-6 text-center">
79+
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 sm:p-6">
80+
{/* Backdrop */}
81+
<div
82+
className={cn(
83+
"absolute inset-0 bg-black/70 backdrop-blur-[2px] transition-opacity duration-300 ease-in-out",
84+
isClosing ? "opacity-0" : "animate-in fade-in opacity-100",
85+
)}
86+
onClick={handleClose}
87+
aria-hidden="true"
88+
/>
89+
90+
{/* Confetti */}
5491
{showConfetti && (
55-
<div aria-hidden="true" className="pointer-events-none absolute inset-0">
56-
{confettiPieces.map((piece) => (
57-
<span
58-
key={piece.id}
59-
className="absolute top-0 h-3 w-2 rounded-full opacity-90"
60-
style={{
61-
left: piece.left,
62-
backgroundColor: piece.color,
63-
animation: `done-confetti-fall ${piece.duration} ease-out ${piece.delay} forwards`,
64-
transform: `translateY(-12vh) rotate(${piece.rotation})`,
65-
}}
66-
/>
67-
))}
68-
</div>
92+
<Confetti
93+
width={width}
94+
height={height}
95+
recycle={false}
96+
numberOfPieces={2000}
97+
gravity={0.25}
98+
initialVelocityY={25}
99+
tweenDuration={5000}
100+
style={{ zIndex: 110 }}
101+
colors={["#238636", "#2ea043", "#3fb950", "#58a6ff", "#1f6feb", "#f0883e"]}
102+
/>
69103
)}
70104

71-
{/* Success icon */}
72-
<div className="relative z-10 flex justify-center">
73-
<div className="bg-success/10 border-success/30 flex h-20 w-20 items-center justify-center rounded-full border-2">
74-
<span className="text-4xl">🎉</span>
105+
{/* Modal */}
106+
<div
107+
className={cn(
108+
"bg-background border-border relative z-[120] w-full max-w-lg overflow-hidden rounded-xl border shadow-2xl transition-all duration-200 ease-in-out sm:rounded-lg",
109+
isClosing
110+
? "translate-y-4 scale-95 opacity-0"
111+
: "animate-in fade-in zoom-in-95 slide-in-from-bottom-4 translate-y-0 opacity-100",
112+
)}
113+
>
114+
{/* Header */}
115+
<div className="bg-background_lighter border-border flex items-center justify-between border-b px-5 py-4">
116+
<div className="flex items-center gap-2.5">
117+
<div className="bg-success/20 text-success rounded-md p-1.5">
118+
<PartyPopper size={18} />
119+
</div>
120+
<h2 className="text-foreground-50 text-xl font-bold">
121+
Your README is ready to go!
122+
</h2>
123+
</div>
124+
<button
125+
onClick={handleClose}
126+
className="text-foreground-400 hover:text-foreground-100 hover:bg-border/30 rounded-md p-1 transition-colors"
127+
>
128+
<X size={20} />
129+
</button>
75130
</div>
76-
</div>
77131

78-
<header className="relative z-10 space-y-2">
79-
<h2 className="text-2xl font-bold">Your README is Ready!</h2>
80-
<p className="text-foreground-400 m-auto max-w-sm text-sm">
81-
Your live preview stays on the right, and this final step is all about export.
82-
Copy the markdown or download a ready-to-use{" "}
83-
<span className="text-foreground-100 font-medium">README.md</span>.
84-
</p>
85-
</header>
86-
87-
<div className="relative z-10 flex flex-col gap-3">
88-
{/* Copy */}
89-
<button
90-
type="button"
91-
onClick={handleCopy}
92-
className={`flex cursor-pointer items-center justify-center gap-2 rounded-md px-4 py-2.5 text-sm font-semibold text-white transition-colors ${
93-
copied ? "bg-success" : "bg-accent hover:bg-accent-600"
94-
}`}
95-
>
96-
{copied ? (
97-
<>
98-
<Check size={16} />
99-
Copied to Clipboard!
100-
</>
101-
) : (
102-
<>
103-
<Clipboard size={16} />
104-
Copy README Markdown
105-
</>
106-
)}
107-
</button>
108-
109-
{/* Download */}
110-
<button
111-
type="button"
112-
onClick={handleDownload}
113-
className="border-border hover:bg-background flex cursor-pointer items-center justify-center gap-2 rounded-md border px-4 py-2.5 text-sm font-semibold transition-colors"
114-
>
115-
<Download size={16} />
116-
Download README.md
117-
</button>
118-
</div>
132+
{/* Content */}
133+
<div className="p-6 sm:p-8">
134+
<div className="mb-6 text-left">
135+
<p className="text-foreground-400 text-sm leading-relaxed">
136+
We&apos;ve generated your personalized GitHub Profile README.
137+
</p>
138+
</div>
119139

120-
<div className="border-border relative z-10 border-t pt-4">
121-
<p className="text-foreground-500 mb-3 text-xs">
122-
Want to make changes? Head back to any step — the preview updates live.
123-
</p>
124-
<button
125-
type="button"
126-
onClick={onReset}
127-
className="text-foreground-400 hover:text-foreground-100 flex cursor-pointer items-center justify-center gap-2 text-sm transition-colors"
128-
>
129-
<RefreshCw size={14} />
130-
Start Over
131-
</button>
132-
</div>
140+
{/* GitHub-style Info Box */}
141+
<div className="border-accent/40 bg-accent/5 mb-8 flex gap-3 rounded-lg border p-4">
142+
<Info className="text-accent mt-0.5 shrink-0" size={18} />
143+
<div className="text-foreground-200 text-sm leading-relaxed">
144+
<span className="text-foreground-400">
145+
Need to make changes? Close this modal and head back to any step to
146+
refine your details.
147+
</span>
148+
</div>
149+
</div>
133150

134-
<style jsx>{`
135-
@keyframes done-confetti-fall {
136-
0% {
137-
opacity: 0;
138-
transform: translateY(-12vh) rotate(0deg) scale(0.85);
139-
}
140-
10% {
141-
opacity: 1;
142-
}
143-
100% {
144-
opacity: 0;
145-
transform: translateY(115vh) rotate(540deg) scale(1);
146-
}
147-
}
148-
`}</style>
151+
<div className="grid gap-4">
152+
{/* Primary Action: Copy */}
153+
<button
154+
type="button"
155+
onClick={handleCopy}
156+
className={cn(
157+
"border-success flex w-full cursor-pointer items-center justify-center gap-2 rounded-md border py-2.5 text-sm font-semibold transition-all duration-200",
158+
copied
159+
? "bg-success text-white"
160+
: "bg-success hover:bg-success-500 shadow-success/20 text-white shadow-sm",
161+
)}
162+
>
163+
{copied ? (
164+
<>
165+
<Check size={16} />
166+
Copied!
167+
</>
168+
) : (
169+
<>
170+
<Clipboard size={16} />
171+
Copy Markdown
172+
</>
173+
)}
174+
</button>
175+
176+
{/* Secondary Action: Download */}
177+
<button
178+
type="button"
179+
onClick={handleDownload}
180+
className="border-border hover:bg-background_lighter hover:text-foreground-50 flex cursor-pointer items-center justify-center gap-2 rounded-md border bg-transparent px-4 py-2.5 text-sm font-semibold text-white transition-all duration-200"
181+
>
182+
<Download size={18} />
183+
Download README.md
184+
</button>
185+
</div>
186+
</div>
187+
188+
{/* Footer */}
189+
<div className="bg-background_lighter border-border flex items-center justify-end border-t px-6 py-4">
190+
<button
191+
type="button"
192+
onClick={onReset}
193+
className="text-foreground-400 hover:text-foreground-100 flex cursor-pointer items-center gap-2 text-sm font-medium transition-colors"
194+
>
195+
<RefreshCw size={14} />
196+
Start Fresh
197+
</button>
198+
</div>
199+
</div>
149200
</div>
150201
);
151202
}

0 commit comments

Comments
 (0)