Skip to content

Commit bf7518f

Browse files
committed
add quick mock banner
1 parent 2b41306 commit bf7518f

File tree

2 files changed

+263
-3
lines changed

2 files changed

+263
-3
lines changed

client/common/BannerMock.jsx

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
/* eslint-disable jsx-a11y/accessible-emoji */
2+
import React, { useState } from 'react';
3+
import styled, { keyframes } from 'styled-components';
4+
5+
const fadeOut = keyframes`
6+
from { opacity: 1; transform: scale(1); }
7+
to { opacity: 0; transform: scale(0.95); }
8+
`;
9+
10+
const Banner = styled.div`
11+
font-family: 'Space Grotesk', sans-serif;
12+
background: white;
13+
font-size: 1.125rem;
14+
position: relative;
15+
transition: opacity 0.5s ease, transform 0.5s ease;
16+
animation: ${({ hidden }) => (hidden ? fadeOut : 'none')} 0.5s forwards;
17+
`;
18+
19+
const Container = styled.div`
20+
display: flex;
21+
justify-content: space-between;
22+
align-items: stretch;
23+
position: relative;
24+
`;
25+
26+
const DarkSideBar = styled.div`
27+
width: 20px;
28+
background: #aa1d47;
29+
`;
30+
31+
const CallToAction = styled.div`
32+
flex: 2;
33+
padding: 2rem 2.5rem;
34+
display: flex;
35+
flex-direction: column;
36+
z-index: 1;
37+
position: relative;
38+
39+
p {
40+
margin-bottom: 1rem;
41+
}
42+
43+
ul {
44+
margin-top: 1rem;
45+
}
46+
`;
47+
48+
const ChevronWrapper = styled.div`
49+
width: 80px;
50+
background: linear-gradient(to bottom, #ed225d 0%, #d31f52 100%);
51+
clip-path: polygon(0 0, 100% 0, 70% 50%, 100% 100%, 0 100%);
52+
z-index: 0;
53+
transform: rotateY(3.142rad);
54+
@media (max-width: 850px) {
55+
display: none;
56+
}
57+
`;
58+
59+
const Action = styled.div`
60+
flex: 1;
61+
padding: 2rem;
62+
display: flex;
63+
flex-direction: column;
64+
gap: 1rem;
65+
`;
66+
67+
const IntervalSelect = styled.div`
68+
display: grid;
69+
grid-template-columns: 1fr 1fr;
70+
background: #f1f5f9;
71+
border-radius: 6px;
72+
padding: 0.5rem;
73+
`;
74+
75+
const Interval = styled.div`
76+
padding: 0.5rem;
77+
text-align: center;
78+
border-radius: 6px;
79+
cursor: pointer;
80+
transition: background 0.2s;
81+
82+
&.active {
83+
background: white;
84+
font-weight: bold;
85+
}
86+
87+
&:hover {
88+
background: #e2e8f0;
89+
}
90+
`;
91+
92+
const AmountSelect = styled.div`
93+
display: grid;
94+
grid-template-columns: repeat(3, 1fr);
95+
gap: 0.5rem;
96+
`;
97+
98+
const Amount = styled.div`
99+
padding: 0.5rem 1rem;
100+
text-align: center;
101+
border-radius: 6px;
102+
border: 1px solid #94a3b8;
103+
cursor: pointer;
104+
background: ${({ active }) => (active ? '#f1f5f9' : 'white')};
105+
transition: background 0.2s, transform 0.1s;
106+
107+
&:hover {
108+
background: #f1f5f9;
109+
transform: scale(1.03);
110+
}
111+
`;
112+
113+
const CustomAmount = styled.input`
114+
grid-column: span 2;
115+
padding: 0.5rem 1rem;
116+
font-size: 1rem;
117+
border: 1px solid #94a3b8;
118+
border-radius: 6px;
119+
`;
120+
121+
const DonateButton = styled.button`
122+
background: #ed225d;
123+
color: #fff !important;
124+
border: none;
125+
padding: 1rem;
126+
font-weight: bold;
127+
border-radius: 6px;
128+
cursor: pointer;
129+
transition: background 0.2s, box-shadow 0.2s;
130+
131+
&:hover {
132+
box-shadow: 0 0 6px rgba(237, 34, 93, 0.5);
133+
background: #d31f52;
134+
}
135+
`;
136+
137+
const MoreInfo = styled.div`
138+
font-size: 0.9rem;
139+
text-align: center;
140+
color: #64748b;
141+
`;
142+
143+
const CloseButton = styled.button`
144+
position: absolute;
145+
top: 10px;
146+
right: 10px;
147+
background: transparent;
148+
border: none;
149+
font-size: 3rem;
150+
cursor: pointer;
151+
color: #64748b;
152+
line-height: 1;
153+
z-index: 2;
154+
transition: transform 0.4s ease-in-out, opacity 0.4s ease-in-out;
155+
156+
&:hover {
157+
color: #ed225d;
158+
}
159+
`;
160+
161+
const SkipContainer = styled.button`
162+
align-self: flex-end;
163+
margin-top: auto;
164+
color: #64748b;
165+
font-size: 0.9rem;
166+
background: none;
167+
border: none;
168+
cursor: pointer;
169+
padding-top: 2rem;
170+
171+
&:hover {
172+
color: #ed225d;
173+
}
174+
`;
175+
176+
export default function BannerMockup() {
177+
const [visible, setVisible] = useState(true);
178+
const [hidden, setHidden] = useState(false);
179+
const [selectedInterval, setSelectedInterval] = useState('monthly');
180+
const [selectedAmount, setSelectedAmount] = useState(10);
181+
182+
const amounts = [5, 10, 25, 55];
183+
184+
const closeBanner = () => {
185+
setHidden(true);
186+
setTimeout(() => setVisible(false), 500);
187+
};
188+
189+
if (!visible) return null;
190+
191+
return (
192+
<Banner hidden={hidden}>
193+
<CloseButton onClick={closeBanner}>&times;</CloseButton>
194+
<Container>
195+
<DarkSideBar />
196+
<CallToAction>
197+
<h1>👋 😄 Keep p5.js Awesome (and Private)</h1>
198+
<p>
199+
<strong>Private sketches are coming to p5.js!</strong> Soon you’ll
200+
be able to keep your work-in-progress just that: private. Whether
201+
you’re a student trying things out, a teacher setting up lessons, or
202+
an artist prototyping your next big idea, you’ll be able to sketch
203+
in peace, on your own terms.
204+
</p>
205+
<p>
206+
But features like this don’t build themselves. If every caring p5.js
207+
creator like you buys us a coffee every month, we’ll be able to keep
208+
building:
209+
</p>
210+
<ul>
211+
<li>✏️ Student and teacher-friendly features</li>
212+
<li>♿ Accessibility tools for all users</li>
213+
<li>⚡ Faster, smoother performance</li>
214+
<li>💖 A creative coding platform that’s free and open forever</li>
215+
</ul>
216+
<SkipContainer onClick={closeBanner}>
217+
✔️ I already donated
218+
</SkipContainer>
219+
</CallToAction>
220+
221+
<ChevronWrapper />
222+
223+
<Action>
224+
<IntervalSelect>
225+
<Interval
226+
className={selectedInterval === 'onetime' ? 'active' : ''}
227+
onClick={() => setSelectedInterval('onetime')}
228+
>
229+
One-time
230+
</Interval>
231+
<Interval
232+
className={selectedInterval === 'monthly' ? 'active' : ''}
233+
onClick={() => setSelectedInterval('monthly')}
234+
>
235+
⭐ Monthly
236+
</Interval>
237+
</IntervalSelect>
238+
239+
<AmountSelect>
240+
{amounts.map((amt) => (
241+
<Amount
242+
key={amt}
243+
active={selectedAmount === amt}
244+
onClick={() => setSelectedAmount(amt)}
245+
>
246+
${amt}
247+
</Amount>
248+
))}
249+
<CustomAmount placeholder="Other amount" />
250+
</AmountSelect>
251+
252+
<DonateButton>Donate Now – it takes 30 seconds!</DonateButton>
253+
<MoreInfo>
254+
$5 makes a difference. $55 makes our day. <br />
255+
Let’s keep creative coding open to all.
256+
</MoreInfo>
257+
</Action>
258+
</Container>
259+
</Banner>
260+
);
261+
}

client/modules/IDE/pages/IDEView.jsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
} from '../components/Editor/MobileEditor';
2828
import IDEOverlays from '../components/IDEOverlays';
2929
import useIsMobile from '../hooks/useIsMobile';
30-
import Banner from '../components/Banner';
30+
import BannerMock from '../../../common/BannerMock';
3131
import { P5VersionProvider } from '../hooks/useP5Version';
3232

3333
function getTitle(project) {
@@ -106,7 +106,6 @@ const IDEView = () => {
106106
const [sidebarSize, setSidebarSize] = useState(160);
107107
const [isOverlayVisible, setIsOverlayVisible] = useState(false);
108108
const [MaxSize, setMaxSize] = useState(window.innerWidth);
109-
const [displayBanner, setDisplayBanner] = useState(true);
110109

111110
const cmRef = useRef({});
112111

@@ -173,7 +172,7 @@ const IDEView = () => {
173172
<Helmet>
174173
<title>{getTitle(project)}</title>
175174
</Helmet>
176-
{displayBanner && <Banner onClose={() => setDisplayBanner(false)} />}
175+
<BannerMock />
177176
<IDEKeyHandlers getContent={() => cmRef.current?.getContent()} />
178177
<WarnIfUnsavedChanges />
179178
<Toast />

0 commit comments

Comments
 (0)