1
1
/* eslint-disable jsx-a11y/accessible-emoji */
2
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
- ` ;
3
+ import { useCollapse } from 'react-collapsed' ;
4
+ import styled from 'styled-components' ;
9
5
10
6
const Banner = styled . div `
11
7
font-family: 'Space Grotesk', sans-serif;
12
8
background: white;
13
9
font-size: 1.125rem;
14
10
position: relative;
15
- transition: opacity 0.5s ease, transform 0.5s ease;
16
- animation: ${ ( { hidden } ) => ( hidden ? fadeOut : 'none' ) } 0.5s forwards;
11
+ overflow: hidden;
17
12
` ;
18
13
19
14
const Container = styled . div `
@@ -24,41 +19,47 @@ const Container = styled.div`
24
19
` ;
25
20
26
21
const DarkSideBar = styled . div `
27
- width: 20px ;
22
+ width: 30px ;
28
23
background: #aa1d47;
29
24
` ;
30
25
31
26
const CallToAction = styled . div `
32
27
flex: 2;
33
- padding: 2rem 2.5rem ;
28
+ padding: 1rem 3rem ;
34
29
display: flex;
35
30
flex-direction: column;
36
31
z-index: 1;
37
32
position: relative;
38
33
34
+ h1 {
35
+ font-size: 2.5rem;
36
+ }
37
+
39
38
p {
40
- margin-bottom: 1rem;
39
+ margin: 0.5rem 0;
40
+ font-size: 1.3rem;
41
41
}
42
42
43
43
ul {
44
- margin-top: 1rem;
44
+ margin: 0.5rem;
45
+ font-size: 1.3rem;
45
46
}
46
47
` ;
47
48
48
49
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%);
50
+ width: 90px ;
51
+ background: linear-gradient(to bottom, #ed225d 0%, #aa1d47 100%);
52
+ clip-path: polygon(0 0, 100% 0, 30 % 50%, 100% 100%, 0 100%);
52
53
z-index: 0;
53
- transform: rotateY(3.142rad);
54
+ transform: rotateY(3.142rad); /* keep flip as intended */
54
55
@media (max-width: 850px) {
55
56
display: none;
56
57
}
57
58
` ;
58
59
59
60
const Action = styled . div `
60
61
flex: 1;
61
- padding: 2rem ;
62
+ padding: 4rem ;
62
63
display: flex;
63
64
flex-direction: column;
64
65
gap: 1rem;
@@ -70,6 +71,8 @@ const IntervalSelect = styled.div`
70
71
background: #f1f5f9;
71
72
border-radius: 6px;
72
73
padding: 0.5rem;
74
+ margin: 0 3rem;
75
+ font-size: 1.2rem;
73
76
` ;
74
77
75
78
const Interval = styled . div `
@@ -81,7 +84,6 @@ const Interval = styled.div`
81
84
82
85
&.active {
83
86
background: white;
84
- font-weight: bold;
85
87
}
86
88
87
89
&:hover {
@@ -96,12 +98,13 @@ const AmountSelect = styled.div`
96
98
` ;
97
99
98
100
const Amount = styled . div `
99
- padding: 0.5rem 1rem;
101
+ padding: 1rem;
100
102
text-align: center;
101
103
border-radius: 6px;
102
104
border: 1px solid #94a3b8;
103
105
cursor: pointer;
104
106
background: ${ ( { active } ) => ( active ? '#f1f5f9' : 'white' ) } ;
107
+ font-weight: ${ ( { active } ) => ( active ? 'bold' : 'normal' ) } ;
105
108
transition: background 0.2s, transform 0.1s;
106
109
107
110
&:hover {
@@ -135,7 +138,7 @@ const DonateButton = styled.button`
135
138
` ;
136
139
137
140
const MoreInfo = styled . div `
138
- font-size: 0.9rem ;
141
+ font-size: 1.3rem ;
139
142
text-align: center;
140
143
color: #64748b;
141
144
` ;
@@ -161,8 +164,10 @@ const CloseButton = styled.button`
161
164
const SkipContainer = styled . button `
162
165
align-self: flex-end;
163
166
margin-top: auto;
167
+ margin-bottom: 1rem;
164
168
color: #64748b;
165
- font-size: 0.9rem;
169
+ font-size: 1.1rem;
170
+ font-weight: 500;
166
171
background: none;
167
172
border: none;
168
173
cursor: pointer;
@@ -175,87 +180,98 @@ const SkipContainer = styled.button`
175
180
176
181
export default function BannerMockup ( ) {
177
182
const [ visible , setVisible ] = useState ( true ) ;
178
- const [ hidden , setHidden ] = useState ( false ) ;
183
+ const [ isExpanded , setExpanded ] = useState ( true ) ;
184
+ const { getCollapseProps } = useCollapse ( {
185
+ isExpanded,
186
+ duration : 1500 ,
187
+ onCollapseEnd : ( ) => setVisible ( false )
188
+ } ) ;
179
189
const [ selectedInterval , setSelectedInterval ] = useState ( 'monthly' ) ;
180
190
const [ selectedAmount , setSelectedAmount ] = useState ( 10 ) ;
181
191
182
192
const amounts = [ 5 , 10 , 25 , 55 ] ;
183
193
184
- const closeBanner = ( ) => {
185
- setHidden ( true ) ;
186
- setTimeout ( ( ) => setVisible ( false ) , 500 ) ;
187
- } ;
194
+ const closeBanner = ( ) => setExpanded ( false ) ;
188
195
189
196
if ( ! visible ) return null ;
190
197
191
198
return (
192
- < Banner hidden = { hidden } >
193
- < CloseButton onClick = { closeBanner } > ×</ 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 ) }
199
+ < div { ...getCollapseProps ( ) } >
200
+ < Banner >
201
+ < CloseButton onClick = { closeBanner } > ×</ CloseButton >
202
+ < Container >
203
+ < DarkSideBar />
204
+ < CallToAction >
205
+ < h1 > 👋 😄 Keep p5.js Awesome (and Private)</ h1 >
206
+ < p >
207
+ < strong > Private sketches are coming to p5.js!</ strong > Soon you’ll
208
+ be able to keep your work-in-progress just that: private. Whether
209
+ you’re a student trying things out, a teacher setting up lessons,
210
+ or an artist prototyping your next big idea, you’ll be able to
211
+ sketch on your own terms before sharing with the world.
212
+ </ p >
213
+ < p >
214
+ But features like this don’t build themselves. If every caring
215
+ p5.js creator chips in the cost of a coffee each month, we’ll be
216
+ able to keep building:
217
+ </ p >
218
+ < ul >
219
+ < li > ✏️ Student and teacher-friendly features</ li >
220
+ < li > ♿ Accessibility tools for all users</ li >
221
+ < li > ⚡ Faster, smoother performance</ li >
222
+ < li >
223
+ 💖 A creative coding platform that’s free and open forever
224
+ </ li >
225
+ </ ul >
226
+ < p >
227
+ p5.js cares about your data privacy. We don’t collect personal
228
+ info like gender or birthdate. Learn more about our{ ' ' }
229
+ < a href = "/privacy-policy" > Privacy Policy</ a > .
230
+ </ p >
231
+ < SkipContainer onClick = { closeBanner } >
232
+ ✔️ I already donated
233
+ </ SkipContainer >
234
+ </ CallToAction >
235
+
236
+ < ChevronWrapper />
237
+
238
+ < Action >
239
+ < IntervalSelect >
240
+ < Interval
241
+ className = { selectedInterval === 'onetime' ? 'active' : '' }
242
+ onClick = { ( ) => setSelectedInterval ( 'onetime' ) }
243
+ >
244
+ One-time
245
+ </ Interval >
246
+ < Interval
247
+ className = { selectedInterval === 'monthly' ? 'active' : '' }
248
+ onClick = { ( ) => setSelectedInterval ( 'monthly' ) }
245
249
>
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 >
250
+ ⭐ Monthly
251
+ </ Interval >
252
+ </ IntervalSelect >
253
+
254
+ < AmountSelect >
255
+ { amounts . map ( ( amt ) => (
256
+ < Amount
257
+ key = { amt }
258
+ active = { selectedAmount === amt }
259
+ onClick = { ( ) => setSelectedAmount ( amt ) }
260
+ >
261
+ ${ amt }
262
+ </ Amount >
263
+ ) ) }
264
+ < CustomAmount placeholder = "Other amount" />
265
+ </ AmountSelect >
266
+
267
+ < DonateButton > Donate Now – it takes 30 seconds!</ DonateButton >
268
+ < MoreInfo >
269
+ $5 makes a difference. $55 makes our day. < br />
270
+ Let’s keep creative coding open to all.
271
+ </ MoreInfo >
272
+ </ Action >
273
+ </ Container >
274
+ </ Banner >
275
+ </ div >
260
276
) ;
261
277
}
0 commit comments