| 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 |
24x
24x
29x
292x
24x
292x
122x
122x
18x
24x
24x
24x
24x
22x
22x
60x
60x
60x
132x
60x
146x
146x
82x
82x
82x
82x
22x
22x
10x
60x
60x
54x
64x
64x
64x
64x
64x
64x
64x
58x
58x
58x
58x
29x
| import React, { Children } from 'react'
import PropTypes from 'prop-types'
export default class ThroughProvider extends React.Component {
static childContextTypes = {
through: PropTypes.object.isRequired,
}
static propTypes = {
shouldBreadcrumbsUpdate: PropTypes.func,
children: PropTypes.element,
}
constructor(props) {
super(props)
this.areas = {}
}
getChildContext() {
return {
through: {
install: this.install,
remove: this.remove,
subscribe: this.subscribe,
}
}
}
area = (area) => {
if( !this.areas.hasOwnProperty(area) ) {
this.areas[area] = {
name: area,
listeners: [],
data: {},
}
}
return this.areas[area]
}
notify = (area, syncUpdate) => {
area = this.area(area)
area.listeners.forEach(
listener => listener(area.data, syncUpdate)
)
}
subscribe = ( area, listener ) => {
area = this.area(area)
area.listeners.push(listener)
listener(area.data)
return () => {
area.listeners = area.listeners.filter(
item => item !== listener
)
}
}
_diffAndComplicatedCounts(prevProps, props) {
const keys = Object.keys(prevProps).concat(Object.keys(props))
// reflect types in the documentation if changed
const quicks = [ 'string', 'number', 'undefined', 'boolean', 'symbol' ]
const [diff, comp] = keys.reduce( (stat,k) => {
return [
stat[0] + (prevProps[k] !== props[k] ? 1 : 0),
stat[1] + (!quicks.includes(typeof(props[k])) ? 1 : 0)
]
}, [0, 0])
return [diff, comp]
}
checkArgs(area, key, props) {
Eif (process.env.NODE_ENV !== 'production') {
Iif(
!( typeof area === 'string' || area instanceof String ) ||
!( typeof key === 'string' || key instanceof String ) ||
!( props instanceof Object && !(props instanceof Array) )
) {
throw new Error(
"type error: through.[install|remove](area:string, key:string, props:Object)"
)
}
}
}
install = (area, key, props, syncUpdate = undefined) => {
this.checkArgs(area, key, props)
area = this.area(area)
const prevProps = area.data[key] || {}
if( this.props.shouldBreadcrumbsUpdate ) {
const diff = this.props.shouldBreadcrumbsUpdate(prevProps, props)
if( !diff ) return
syncUpdate = true
} else {
const [diff, comp] = this._diffAndComplicatedCounts(prevProps, props)
if( !diff ) return
Iif( undefined === syncUpdate ) {
syncUpdate = !comp
}
}
const data = Object.assign({}, area.data)
data[key] = {...props}
area.data = data
this.notify(area.name, syncUpdate)
}
remove = (area, key, syncUpdate = undefined) => {
this.checkArgs(area, key, {})
area = this.area(area)
if( area.data.hasOwnProperty(key) ) {
const data = Object.assign({}, area.data)
delete data[key]
area.data = data
this.notify(area.name, true)
}
}
render() {
return React.Children.only(this.props.children)
}
}
|