UNPKG

3.76 kBJSXView Raw
1/**
2 * Toast component
3 * @class ApToast
4 */
5
6'use strict'
7
8import React, {PropTypes as types} from 'react'
9import {ApPureMixin} from 'apeman-react-mixin-pure'
10import classnames from 'classnames'
11import arrayfilter from 'arrayfilter'
12import ApToastItem from './ap_toast_item'
13
14/** @lends ApToast */
15const ApToast = React.createClass({
16
17 // --------------------
18 // Specs
19 // --------------------
20
21 propTypes: {
22 message: types.string,
23 duration: types.number,
24 icon: types.string,
25 name: types.string,
26 onDismiss: types.func
27 },
28
29 mixins: [
30 ApPureMixin
31 ],
32
33 statics: {
34 itemJoiner: '____'
35 },
36
37 getInitialState () {
38 return {
39 items: ''
40 }
41 },
42
43 getDefaultProps () {
44 return {
45 message: null,
46 duration: 2000,
47 icon: null,
48 name: null,
49 onDismiss: null
50 }
51 },
52
53 render () {
54 const s = this
55 let { state, props } = s
56
57 let valid = state.items.length
58 if (!valid) {
59 return null
60 }
61 return (
62 <div className={ classnames('ap-toast', props.className, {}) }
63 style={ Object.assign({}, props.style) }>
64 <div className="ap-toast-inner">
65 {s._renderToastItem()}
66 </div>
67 </div>
68 )
69 },
70
71 // --------------------
72 // Lifecycle
73 // --------------------
74
75 componentWillMount () {
76 const s = this
77 },
78
79 componentDidMount () {
80 const s = this
81 let { props } = s
82 s.startTicking()
83 s.pushToastItem(props.message)
84 },
85
86 componentWillReceiveProps (nextProps) {
87 const s = this
88 s.pushToastItem(nextProps.message)
89 },
90
91 componentWillUnmount () {
92 const s = this
93 s.stopTicking()
94 },
95
96 // ------------------
97 // Custom
98 // ------------------
99
100 startTicking () {
101 const s = this
102 clearTimeout(s._tickTimer)
103 s._ticking = true
104 s.doTick()
105 },
106
107 stopTicking () {
108 const s = this
109 clearTimeout(s._tickTimer)
110 s._ticking = false
111 },
112
113 doTick () {
114 const s = this
115 let { props } = s
116 if (!s._ticking) {
117 return
118 }
119 s._tickTimer = setTimeout(() => {
120 s.shiftToastItem()
121 s.doTick()
122 }, props.duration)
123 },
124
125 pushToastItem (message) {
126 const s = this
127 if (!message) {
128 return
129 }
130 let items = (s.state.items || '').split(ApToast.itemJoiner)
131 let duplicate = items[ items.length - 1 ] === message
132 if (duplicate) {
133 return
134 }
135 s.setState({
136 items: items.concat(message).join(ApToast.itemJoiner)
137 })
138 },
139
140 shiftToastItem () {
141 const s = this
142 let { props } = s
143 let items = (s.state.items || '').split(ApToast.itemJoiner)
144 if (!items.length) {
145 s.setState({ items: null })
146 return
147 }
148 s.dismissToastItem(items[ 0 ])
149 },
150
151 dismissToastItem (message) {
152 const s = this
153 let { props } = s
154 let items = (s.state.items || '').split(ApToast.itemJoiner)
155 s.setState({
156 items: items.filter((filtering) => filtering !== message)
157 .join(ApToast.itemJoiner)
158 })
159 if (props.onDismiss) {
160 props.onDismiss({ dismissed: message, name: props.name })
161 }
162 },
163
164 handleToastItemTap (e) {
165 const s = this
166 let text = e.target.dataset[ 'text' ]
167 s.dismissToastItem(text)
168 },
169
170 // ------------------
171 // Private
172 // ------------------
173 _ticking: false,
174 _tickTimer: null,
175 _renderToastItem () {
176 const s = this
177 let { state, props } = s
178 return (state.items || '').split(ApToast.itemJoiner)
179 .filter(arrayfilter.emptyReject())
180 .map((text, i) => (
181 <ApToastItem key={`toast-${text}-${i}`}
182 text={text}
183 icon={props.icon}
184 onTap={s.handleToastItemTap}
185 >
186 </ApToastItem>
187 )
188 )
189 }
190})
191
192export default ApToast