Skip to content

Commit 4199f2d

Browse files
committed
add animated bulb + cleanups
1 parent 25cd4c4 commit 4199f2d

File tree

7 files changed

+150
-21
lines changed

7 files changed

+150
-21
lines changed

showcase/src/App.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const StyledContainer = styled.div`
2020

2121
const App = () => {
2222
const isMediumOrLarger = useMediaQuery({ minWidth: SCREEN_SIZES.md })
23+
24+
// toggle sidebar display
2325
const [showSidebar, setShowSidebar] = useState(false)
2426
const isSidebarShown = isMediumOrLarger || (!isMediumOrLarger && showSidebar)
2527

showcase/src/Body.js

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react'
1+
import React, { useState, useCallback } from 'react'
22
import styled from 'styled-components'
33
import { Router } from '@reach/router'
44
import { HEADER_ALLOWANCE } from './utils/constants'
@@ -7,14 +7,21 @@ import {
77
media,
88
StyledFloatingBtn,
99
StyledContentContainer,
10+
StyledInfoContainer,
11+
Header as StyledHeader,
1012
H1,
1113
Columns,
1214
Column,
1315
Box,
1416
CTAContainer
1517
} from './content/StyledContent'
16-
import GithubLogo from './assets/github_logo.svg'
1718
import Button from './Button'
19+
import { NOTES } from './constants/displayBoxNotes'
20+
import { INFO } from './constants/patternInfo'
21+
22+
import GithubLogo from './assets/github_logo.svg'
23+
import InfoIllustration from './assets/light_bulb.svg'
24+
import { useAnimatedBulb } from './content/AnimatedBulb'
1825

1926
const StyledAppBody = styled.div`
2027
background: #191921;
@@ -37,51 +44,85 @@ const patterns = [
3744
'state-reducers'
3845
]
3946

40-
const notes = {
41-
'1': 'Animated via an HOC',
42-
'2': 'Animated via a hook 💪'
43-
}
44-
4547
const PR_ROOT =
4648
'https://github.com/ohansemmanuel/advanced-react-patterns-ultrasimplified/pull'
4749
const PR_IDs = [1, 16, 17, 19, 6, 7, 8, 9, 10, 12]
4850
const PRs = PR_IDs.map(id => `${PR_ROOT}/${id}`)
4951

50-
const RouteComponent = ({ pattern, index, isMediumOrLarger }) => {
52+
const Header = ({ title, patternNumber }) => {
53+
// animated el
54+
const [lightBulbEl, setlightBulbEl] = useState(null)
55+
56+
const setLightBulbElRef = useCallback(node => {
57+
if (node !== null) {
58+
setlightBulbEl(node)
59+
}
60+
}, [])
61+
62+
const animatedBulbTimeline = useAnimatedBulb({
63+
el: lightBulbEl
64+
})
65+
66+
// toggle info display
67+
const [isInfoShown, setInfoShown] = useState(false)
68+
69+
const toggleInfo = () => {
70+
setInfoShown(isInfoShown => !isInfoShown)
71+
animatedBulbTimeline.replay()
72+
}
73+
74+
return (
75+
<StyledHeader>
76+
{isInfoShown && (
77+
<StyledInfoContainer>{INFO[patternNumber]}</StyledInfoContainer>
78+
)}
79+
<H1>{title}</H1>
80+
<div ref={setLightBulbElRef}>
81+
<InfoIllustration
82+
style={{ width: '30px', marginLeft: '5px', cursor: 'pointer' }}
83+
onClick={toggleInfo}
84+
/>
85+
</div>
86+
</StyledHeader>
87+
)
88+
}
89+
90+
const RouteComponent = ({ pattern, patternNumber, isMediumOrLarger }) => {
5191
const firstLetterCap = str => str.slice(0, 1).toUpperCase() + str.slice(1)
5292
const title = pattern
5393
.split('-')
5494
.map(firstLetterCap)
5595
.join(' ')
5696

57-
const transformIndex = i => (i < 10 ? `0${i}` : i)
58-
const indexes = [index, index + 1].map(transformIndex)
97+
const indexToTwoDigits = i => (i < 10 ? `0${i}` : i)
98+
const beforeAndAfterPatternNumbers = [patternNumber, patternNumber + 1].map(
99+
indexToTwoDigits
100+
)
59101

60102
// Demo to be shown in Display Boxes
61103
let Demo1, Demo2
62104
try {
63-
Demo1 = require(`./patterns/${indexes[0]}`).default
105+
Demo1 = require(`./patterns/${beforeAndAfterPatternNumbers[0]}`).default
64106
} catch (error) {
65107
Demo1 = () => null
66108
}
67109
try {
68-
Demo2 = require(`./patterns/${indexes[1]}`).default
110+
Demo2 = require(`./patterns/${beforeAndAfterPatternNumbers[1]}`).default
69111
} catch (error) {
70112
Demo2 = () => null
71113
}
72114

73115
const goToCodeImplementatiomn = () => {
74-
const newWindow = window.open(PRs[index], 'blank')
116+
const newWindow = window.open(PRs[patternNumber], 'blank')
75117
newWindow.opener = null
76118
}
77119

78120
return (
79121
<StyledContentContainer>
80-
<H1>{title}</H1>
81-
122+
<Header title={title} patternNumber={patternNumber} />
82123
<Columns>
83124
<Column>
84-
<Box isPrimary note={notes[index]}>
125+
<Box isPrimary note={NOTES[patternNumber]}>
85126
<Demo1 />
86127
</Box>
87128
{isMediumOrLarger && (
@@ -91,7 +132,10 @@ const RouteComponent = ({ pattern, index, isMediumOrLarger }) => {
91132
)}
92133
</Column>
93134
<Column leftGap>
94-
<Box note={notes[index + 1]} m={!isMediumOrLarger && '15px 0 0 0'}>
135+
<Box
136+
note={NOTES[patternNumber + 1]}
137+
m={!isMediumOrLarger && '15px 0 0 0'}
138+
>
95139
<Demo2 />
96140
</Box>
97141
{!isMediumOrLarger && (
@@ -111,7 +155,6 @@ const RouteComponent = ({ pattern, index, isMediumOrLarger }) => {
111155
</StyledContentContainer>
112156
)
113157
}
114-
115158
const Body = ({ setShowSidebar, isMediumOrLarger }) => {
116159
const toggleSidebar = () => setShowSidebar(val => !val)
117160

@@ -121,10 +164,10 @@ const Body = ({ setShowSidebar, isMediumOrLarger }) => {
121164
<Home path='/' />
122165
{patterns.map((pattern, index) => (
123166
<RouteComponent
124-
pattern={pattern}
125-
index={index + 1}
126167
path={pattern}
127168
key={pattern}
169+
pattern={pattern}
170+
patternNumber={index + 1}
128171
isMediumOrLarger={isMediumOrLarger}
129172
/>
130173
))}

showcase/src/assets/light_bulb.svg

Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const NOTES = {
2+
'1': 'Animated via an HOC',
3+
'2': 'Animated via a hook 💪'
4+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const THE_MEDIUM_CLAP =
2+
"The Medium clap is such a great component to build. It's practical and poses real world problems. Note how the 'before' and 'after' demos behave in the same way but are powered by different mechanisms: an higher order component, and a custom hook!"
3+
4+
export const INFO = {
5+
'1': THE_MEDIUM_CLAP,
6+
'2': 'Hola hola!!!'
7+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React, { useState, useLayoutEffect } from 'react'
2+
import mojs from 'mo-js'
3+
4+
export const useAnimatedBulb = ({ el }) => {
5+
const [animationTimeline, setAnimationTimeline] = useState(
6+
new mojs.Timeline()
7+
)
8+
9+
useLayoutEffect(() => {
10+
if (!el) {
11+
return
12+
}
13+
14+
const OPTS = {
15+
parent: el,
16+
fill: 'none',
17+
radius: 10,
18+
strokeWidth: { 20: 0 },
19+
scale: { 0: 1 },
20+
angle: { 'rand(-35, -70)': 0 },
21+
duration: 500,
22+
left: '50%',
23+
top: '50%',
24+
easing: 'cubic.out',
25+
className: 'no-pointer',
26+
isShowEnd: false
27+
}
28+
const circle1 = new mojs.Shape({
29+
...OPTS,
30+
stroke: 'cyan'
31+
})
32+
33+
const circle2 = new mojs.Shape({
34+
...OPTS,
35+
radius: { 0: 15 },
36+
strokeWidth: { 15: 0 },
37+
stroke: 'magenta',
38+
delay: 'rand(75, 150)',
39+
isShowEnd: false
40+
})
41+
42+
const animateBulb = new mojs.Html({
43+
el,
44+
isShowEnd: true,
45+
y: { 0: -15 },
46+
duration: 100
47+
}).then({
48+
y: { 20: 0 },
49+
delay: 100 / 2
50+
})
51+
52+
const updatedAnimationTimeline = animationTimeline.add([
53+
circle1,
54+
circle2,
55+
animateBulb
56+
])
57+
setAnimationTimeline(updatedAnimationTimeline)
58+
}, [el, animationTimeline])
59+
60+
return animationTimeline
61+
}

showcase/src/content/StyledContent.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ export const StyledFloatingBtn = styled.button`
4040
color: #fff;
4141
`
4242

43+
export const Header = styled.div`
44+
display: flex;
45+
align-items: center;
46+
margin-bottom: ${() => `${SIDEBAR_LEFT_PADDING}vw`};
47+
`
48+
49+
export const StyledInfoContainer = styled.aside`
50+
position: absolute;
51+
top: 0;
52+
background: red;
53+
`
54+
4355
// "0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)"
4456

4557
export const media = Object.keys(SCREEN_SIZES).reduce((accumulator, label) => {
@@ -67,7 +79,6 @@ export const H1 = styled.h1`
6779
color: ${() => `${ASH}`};
6880
padding: 0;
6981
margin: 0;
70-
margin-bottom: ${() => `${SIDEBAR_LEFT_PADDING}vw`};
7182
`
7283

7384
export const Columns = styled.div`

0 commit comments

Comments
 (0)