UNPKG

6.04 kBJavaScriptView Raw
1import classnames from 'classnames';
2import React, { cloneElement } from 'react';
3
4import { Collapse } from 'bee-transition';
5import PropTypes from 'prop-types';
6
7
8
9const propTypes = {
10 //是否添加折叠
11 collapsible: PropTypes.bool,
12 onSelect: PropTypes.func,
13 //头部组件
14 header: PropTypes.node,
15 headerStyle: PropTypes.object,
16 id: PropTypes.oneOfType([
17 PropTypes.string, PropTypes.number,
18 ]),
19 headerContent: PropTypes.bool,
20 //footer组件
21 footer: PropTypes.node,
22 footerStyle: PropTypes.object,
23 //默认是否打开
24 defaultExpanded: PropTypes.bool,
25 //是否打开
26 expanded: PropTypes.bool,
27 //每个panel的标记
28 eventKey: PropTypes.any,
29 headerRole: PropTypes.string,
30 panelRole: PropTypes.string,
31 //颜色
32 colors: PropTypes.oneOf(['primary', 'accent', 'success', 'info', 'warning', 'danger','default','bordered']),
33
34 // From Collapse.的扩展动画
35 onEnter: PropTypes.func,
36 onEntering: PropTypes.func,
37 onEntered: PropTypes.func,
38 onExit: PropTypes.func,
39 onExiting: PropTypes.func,
40 onExited: PropTypes.func,
41};
42
43const defaultProps = {
44 defaultExpanded: false,
45 clsPrefix: "u-panel",
46 colors: "default"
47};
48
49class Panel extends React.Component {
50 constructor(props, context) {
51 super(props, context);
52
53 this.handleClickTitle = this.handleClickTitle.bind(this);
54
55 this.state = {
56 expanded: this.props.defaultExpanded,
57 };
58 }
59
60 //头部点击事件
61 handleClickTitle(e) {
62 // 不让事件进入事件池
63 e.persist();
64 e.selected = true;
65
66 if (this.props.onSelect) {
67 this.props.onSelect(this.props.eventKey, e);
68 } else {
69 e.preventDefault();
70 }
71
72 if (e.selected) {
73 this.setState({ expanded: !this.state.expanded });
74 }
75 }
76
77//渲染panelheader
78 renderHeader(collapsible, header, id, role, expanded, clsPrefix) {
79 const titleClassName = `${clsPrefix}-title`;
80
81 if (!collapsible) {
82 if (!React.isValidElement(header)) {
83 return header;
84 }
85
86 return cloneElement(header, {
87 className: classnames(header.props.className, titleClassName),
88 });
89 }
90
91 if (!React.isValidElement(header)) {
92 return (
93 <h4 role="presentation" className={titleClassName}>
94 {this.renderAnchor(header, id, role, expanded)}
95 </h4>
96 );
97 }
98 if(this.props.headerContent){
99 return cloneElement(header, {
100 className: classnames(header.props.className, titleClassName),
101 });
102 }
103
104 return cloneElement(header, {
105 className: classnames(header.props.className, titleClassName),
106 children: this.renderAnchor(header.props.children, id, role, expanded),
107 });
108 }
109
110//如果使用链接,渲染为a标签
111 renderAnchor(header, id, role, expanded) {
112 return (
113 <a
114 role={role}
115 href={id && `#${id}`}
116 aria-controls={id}
117 aria-expanded={expanded}
118 aria-selected={expanded}
119 className={expanded ? null : 'collapsed' }
120 >
121 {header}
122 </a>
123 );
124 }
125
126 //如果有折叠动画,渲染折叠动画
127 renderCollapsibleBody(
128 id, expanded, role, children, clsPrefix, animationHooks
129 ) {
130 return (
131 <Collapse in={expanded} {...animationHooks}>
132 <div
133 id={id}
134 role={role}
135 className={`${clsPrefix}-collapse`}
136 aria-hidden={!expanded}
137 >
138 {this.renderBody(children, clsPrefix)}
139 </div>
140 </Collapse>
141 );
142 }
143
144 //渲染panelbody
145 renderBody(rawChildren, clsPrefix) {
146 const children = [];
147 let bodyChildren = [];
148
149 const bodyClassName = `${clsPrefix}-body`;
150
151 //添加到body的children中
152 function maybeAddBody() {
153 if (!bodyChildren.length) {
154 return;
155 }
156
157 // 给子组件添加key,为了之后触发事件时使用
158 children.push(
159 <div key={children.length} className={bodyClassName}>
160 {bodyChildren}
161 </div>
162 );
163
164 bodyChildren = [];
165 }
166
167 //转换为数组,方便复用
168 React.Children.toArray(rawChildren).forEach(child => {
169 if (React.isValidElement(child) && child.props.fill) {
170 maybeAddBody();
171
172 //将标示fill设置为undefined
173 children.push(cloneElement(child, { fill: undefined }));
174
175 return;
176 }
177
178 bodyChildren.push(child);
179 });
180
181 maybeAddBody();
182
183 return children;
184 }
185
186 render() {
187 const {
188 collapsible,
189 header,
190 id,
191 footer,
192 expanded: propsExpanded,
193 footerStyle,
194 headerStyle,
195 headerRole,
196 panelRole,
197 className,
198 colors,
199 children,
200 onEnter,
201 onEntering,
202 onEntered,
203 clsPrefix,
204 onExit,
205 headerContent,
206 onExiting,
207 onExited,
208 defaultExpanded,
209 eventKey,
210 onSelect,
211 ...props
212 } = this.props;
213
214
215 const expanded = propsExpanded != null ?
216 propsExpanded : this.state.expanded;
217
218 const classes ={};
219 classes[`${clsPrefix}`] = true;
220 classes[`${clsPrefix}-${colors}`] = true;
221
222 const headerClass = {
223 [`${clsPrefix}-heading`] : true
224 }
225
226 return (
227 <div
228 {...props}
229 className={classnames(className, classes)}
230 id={collapsible ? null : id}
231 >
232 {header && (
233 <div className={classnames(headerClass)} style={headerStyle} onClick={ this.handleClickTitle }>
234 {this.renderHeader(
235 collapsible, header, id, headerRole, expanded, clsPrefix
236 )}
237 </div>
238 )}
239
240 {collapsible ?
241 this.renderCollapsibleBody(
242 id, expanded, panelRole, children, clsPrefix,
243 { onEnter, onEntering, onEntered, onExit, onExiting, onExited }
244 ) :
245 this.renderBody(children, clsPrefix)
246 }
247
248 {footer && (
249 <div className={`${clsPrefix}-footer`} style={footerStyle}>
250 {footer}
251 </div>
252 )}
253 </div>
254 );
255 }
256}
257
258Panel.propTypes = propTypes;
259Panel.defaultProps = defaultProps;
260
261export default Panel;