Skip to content

Commit 9ad35b2

Browse files
feat: prompt copied animation
1 parent d875b10 commit 9ad35b2

File tree

2 files changed

+82
-9
lines changed

2 files changed

+82
-9
lines changed

templates/src/App.css.template

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,75 @@ h2 {
9191
transition: all 0.2s;
9292
user-select: text;
9393
cursor: pointer;
94+
position: relative;
9495
}
9596

9697
.prompts-list li:hover {
9798
background: rgba(255, 255, 255, 0.1);
9899
border-left-color: rgba(99, 102, 241, 0.8);
99100
transform: translateX(4px);
100101
}
102+
103+
@keyframes wipe-in-out {
104+
0% {
105+
clip-path: polygon(
106+
0% 0%,
107+
0% 100%,
108+
0% 100%,
109+
0% 0%
110+
);
111+
}
112+
15% {
113+
clip-path: polygon(
114+
0% 0%,
115+
0% 100%,
116+
100% 100%,
117+
100% 0%
118+
);
119+
}
120+
85% {
121+
clip-path: polygon(
122+
0% 0%,
123+
0% 100%,
124+
100% 100%,
125+
100% 0%
126+
);
127+
}
128+
100% {
129+
clip-path: polygon(
130+
100% 0%,
131+
100% 100%,
132+
100% 100%,
133+
100% 0%
134+
);
135+
}
136+
}
137+
138+
.prompts-list li::after {
139+
content: '✓ copied';
140+
position: absolute;
141+
left: 0;
142+
right: 0;
143+
top: 0;
144+
bottom: 0;
145+
background-color: green;
146+
border-radius: 0.5rem;
147+
visibility: hidden;
148+
padding-top: 0.75em;
149+
padding-left: 1em;
150+
151+
/* without this it sometimes glitched at the end? */
152+
clip-path: polygon(
153+
0% 0%,
154+
0% 100%,
155+
0% 100%,
156+
0% 0%
157+
);
158+
159+
}
160+
161+
.prompts-list li.clicked::after {
162+
visibility: visible;
163+
animation-duration: 1.5s;
164+
animation-name: wipe-in-out;
165+
}

templates/src/App.tsx.template

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const hasUploadPreset = Boolean(uploadPreset);
1414

1515
function App() {
1616
const [uploadedImageId, setUploadedImageId] = useState<string | null>(null);
17+
const [clickedIds, setClickedIds] = useState(new Set());
1718

1819
const handleUploadSuccess = (result: CloudinaryUploadResult) => {
1920
console.log('Upload successful:', result);
@@ -25,9 +26,16 @@ function App() {
2526
alert(`Upload failed: ${error.message}`);
2627
};
2728

28-
const copyPrompt = (e: React.MouseEvent<HTMLLIElement>) => {
29+
const copyPrompt = (e: React.MouseEvent<HTMLLIElement>, id: number) => {
2930
const text = e.currentTarget.textContent ?? '';
30-
void navigator.clipboard.writeText(text);
31+
void navigator.clipboard.writeText(text).then(() => {
32+
setClickedIds((prev) => new Set(prev).add(id));
33+
setTimeout(() => setClickedIds( (prev) => {
34+
const next = new Set(prev);
35+
next.delete(id);
36+
return next;
37+
}), 2000);
38+
});
3139
};
3240

3341
// Display uploaded image if available, otherwise show a sample
@@ -77,16 +85,16 @@ function App() {
7785
<ul className="prompts-list">
7886
{hasUploadPreset ? (
7987
<>
80-
<li onClick={copyPrompt} title="Click to copy">Create an image gallery with lazy loading and responsive images</li>
81-
<li onClick={copyPrompt} title="Click to copy">Create a video player that plays a Cloudinary video</li>
82-
<li onClick={copyPrompt} title="Click to copy">Add image overlays with text or logos</li>
88+
<li key={0} onClick={(e) => copyPrompt(e, 0)} title="Click to copy" className={ clickedIds.has(0) ? "clicked" : '' }>Create an image gallery with lazy loading and responsive images</li>
89+
<li key={1} onClick={(e) => copyPrompt(e, 1)} title="Click to copy" className={ clickedIds.has(1) ? "clicked" : '' }>Create a video player that plays a Cloudinary video</li>
90+
<li key={2} onClick={(e) => copyPrompt(e, 2)} title="Click to copy" className={ clickedIds.has(2) ? "clicked" : '' }>Add image overlays with text or logos</li>
8391
</>
8492
) : (
8593
<>
86-
<li onClick={copyPrompt} title="Click to copy">Let&apos;s try uploading — help me add an upload preset and upload widget</li>
87-
<li onClick={copyPrompt} title="Click to copy">Create an image gallery with lazy loading and responsive images</li>
88-
<li onClick={copyPrompt} title="Click to copy">Create a video player that plays a Cloudinary video</li>
89-
<li onClick={copyPrompt} title="Click to copy">Add image overlays with text or logos</li>
94+
<li key={0} onClick={(e) => copyPrompt(e, 0)} title="Click to copy" className={ clickedIds.has(0) ? "clicked" : '' }>Let&apos;s try uploading — help me add an upload preset and upload widget</li>
95+
<li key={1} onClick={(e) => copyPrompt(e, 1)} title="Click to copy" className={ clickedIds.has(1) ? "clicked" : '' }>Create an image gallery with lazy loading and responsive images</li>
96+
<li key={2} onClick={(e) => copyPrompt(e, 2)} title="Click to copy" className={ clickedIds.has(2) ? "clicked" : '' }>Create a video player that plays a Cloudinary video</li>
97+
<li key={3} onClick={(e) => copyPrompt(e, 3)} title="Click to copy" className={ clickedIds.has(3) ? "clicked" : '' }>Add image overlays with text or logos</li>
9098
</>
9199
)}
92100
</ul>

0 commit comments

Comments
 (0)