-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathindex.js
More file actions
executable file
·157 lines (131 loc) · 3.39 KB
/
index.js
File metadata and controls
executable file
·157 lines (131 loc) · 3.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import {Component} from 'react'
import PropTypes from 'prop-types'
import StaggerTiming from './StaggerTiming'
const globalTiming = new StaggerTiming()
class Stagger extends Component {
static childContextTypes = {
stagger: PropTypes.object.isRequired,
}
static contextTypes = {
stagger: PropTypes.object,
}
static defaultProps = {
delay: 100,
in: true,
appear: true,
}
static propTypes = {
timing: PropTypes.instanceOf(StaggerTiming),
delay: PropTypes.oneOfType([
PropTypes.number,
PropTypes.arrayOf(PropTypes.number),
]),
in: PropTypes.bool,
appear: PropTypes.bool,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
}
staggerContext = {
subscribe: this.subscribe.bind(this),
value: false,
timing: this.props.timing || this.context.timing || globalTiming,
}
selfValue = this.props.in
subscribers = []
unsubscribe = null
state = {
value: false,
delay: 0,
}
componentWillMount() {
if (this.context.stagger) {
this.unsubscribe = this.context.stagger.subscribe(() =>
this.checkUpdate(),
)
}
if (!this.props.appear) {
this.checkUpdate(true)
}
}
componentDidMount() {
if (this.props.appear) {
this.checkUpdate()
}
}
UNSAFE_componentWillReceiveProps(newProps) {
this.selfValue = newProps.in
this.checkUpdate()
}
componentWillUnmount() {
if (this.unsubscribe) {
this.unsubscribe()
}
}
checkUpdate(forceInstant) {
// Only stagger if self and all parents are active.
const parentValue = this.context.stagger ? this.context.stagger.value : true
const value = this.selfValue && parentValue
// Only continue if the value has changed.
if (value === this.staggerContext.value) {
return
}
this.staggerContext.value = value
const delay = this.calculateDelay(value, forceInstant)
this.setState({
value,
delay,
})
}
calculateDelay(value, forceInstant) {
// Get delay for self. Note, the actual delay is max of all stagger
// delays since last leaf stagger.
// stagger(300) - 0
// stagger(100) - 0
// stagger(100) - 100
// stagger(100) - 200
// stagger(200) - 400
// stagger(100) - 400
// stagger(500) - 900
if (forceInstant) {
return 0
}
const timing = this.staggerContext.timing
const isLeaf = !this.subscribers.length
const [beforeDelay, afterDelay] = this.getOwnDelay()
// Add delay for self and get total delay since last leaf.
const totalDelay = value ? timing.getDelay(beforeDelay, isLeaf) : 0
if (!isLeaf) {
// Notify children of change.
this.subscribers.forEach(subscriber => {
subscriber()
})
}
// Add delay after children.
if (value) {
timing.getDelay(afterDelay, false)
}
return totalDelay
}
getOwnDelay() {
const {delay} = this.props
return Array.isArray(delay) ? delay : [delay, delay]
}
getChildContext() {
return {
stagger: this.staggerContext,
}
}
subscribe(handler) {
this.subscribers.push(handler)
return () => {
this.subscribers = this.subscribers.filter(h => h !== handler)
}
}
render() {
const {children} = this.props
if (typeof children === 'function') {
return children(this.state)
}
return children || null
}
}
export default Stagger