UNPKG

25.3 kBJavaScriptView Raw
1import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
2import _extends from 'babel-runtime/helpers/extends';
3import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
4import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
5import _inherits from 'babel-runtime/helpers/inherits';
6import React, { Component } from 'react';
7import classnames from 'classnames';
8import PropTypes from 'prop-types';
9import { polyfill } from 'react-lifecycles-compat';
10import ConfigProvider from '../config-provider';
11import Affix from '../affix';
12import Icon from '../icon';
13import { KEYCODE, dom, env } from '../util';
14
15import { isBoolean, getCollapseMap } from './util';
16/**
17 * Shell
18 */
19export default function ShellBase(props) {
20 var _class, _temp, _initialiseProps;
21
22 var componentName = props.componentName;
23 var Shell = (_temp = _class = function (_Component) {
24 _inherits(Shell, _Component);
25
26 function Shell(props) {
27 _classCallCheck(this, Shell);
28
29 var _this = _possibleConstructorReturn(this, _Component.call(this, props));
30
31 _initialiseProps.call(_this);
32
33 var deviceMap = getCollapseMap(props.device);
34 _this.layout = {};
35
36 _this.state = {
37 controll: false,
38 collapseMap: deviceMap,
39 device: props.device
40 };
41 return _this;
42 }
43
44 Shell.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) {
45 var device = prevState.device;
46
47
48 if (nextProps.device !== device) {
49 var deviceMap = getCollapseMap(nextProps.device);
50 return {
51 controll: false,
52 collapseMap: deviceMap,
53 device: nextProps.device
54 };
55 }
56
57 return {};
58 };
59
60 Shell.prototype.componentDidMount = function componentDidMount() {
61 this.checkAsideFixed();
62 };
63
64 Shell.prototype.componentDidUpdate = function componentDidUpdate(prevProps) {
65 var _this2 = this;
66
67 if (prevProps.device !== this.props.device) {
68 var deviceMapBefore = getCollapseMap(prevProps.device);
69 var deviceMapAfter = getCollapseMap(this.props.device);
70
71 Object.keys(deviceMapAfter).forEach(function (block) {
72 var _ref = _this2.layout[block] || {},
73 props = _ref.props;
74
75 if (deviceMapBefore[block] !== deviceMapAfter[block]) {
76 if (props && typeof props.onCollapseChange === 'function') {
77 props.onCollapseChange(deviceMapAfter[block]);
78 }
79 }
80 });
81 }
82
83 setTimeout(function () {
84 // 如果左侧边栏固定
85 _this2.checkAsideFixed();
86 }, 201);
87 };
88
89 Shell.prototype.render = function render() {
90 return this.renderShell(this.props);
91 };
92
93 return Shell;
94 }(Component), _class.displayName = componentName, _class._typeMark = componentName, _class.propTypes = _extends({}, ConfigProvider.propTypes, {
95 prefix: PropTypes.string,
96 /**
97 * 设备类型
98 * @enumdesc 手机, 平板, PC电脑
99 */
100 device: PropTypes.oneOf(['phone', 'tablet', 'desktop']),
101 /**
102 * 设备类型
103 * @enumdesc 浅色, 深色, 主题色
104 */
105 type: PropTypes.oneOf(['light', 'dark', 'brand']),
106 /**
107 * 是否固定 header, 用sticky实现,IE下降级为Affix
108 */
109 fixedHeader: PropTypes.bool
110 }), _class.defaultProps = {
111 prefix: 'next-',
112 device: 'desktop',
113 type: 'light',
114 fixedHeader: false
115 }, _initialiseProps = function _initialiseProps() {
116 var _this3 = this;
117
118 this.checkAsideFixed = function () {
119 var fixedHeader = _this3.props.fixedHeader;
120
121
122 if (!fixedHeader) {
123 return;
124 }
125
126 var headerHeight = void 0;
127 if (_this3.headerRef && (_this3.navigationFixed || _this3.toolDockFixed)) {
128 headerHeight = dom.getStyle(_this3.headerRef, 'height');
129 }
130
131 if (_this3.navigationFixed) {
132 var style = {};
133 style.marginLeft = dom.getStyle(_this3.navRef, 'width');
134 dom.addClass(_this3.navRef, 'fixed');
135 headerHeight && dom.setStyle(_this3.navRef, { top: headerHeight });
136 dom.setStyle(_this3.localNavRef || _this3.submainRef, style);
137 }
138
139 if (_this3.toolDockFixed) {
140 var _style = {};
141 _style.marginRight = dom.getStyle(_this3.toolDockRef, 'width');
142 dom.addClass(_this3.toolDockRef, 'fixed');
143 headerHeight && dom.setStyle(_this3.toolDockRef, { top: headerHeight });
144 dom.setStyle(_this3.localNavRef || _this3.submainRef, _style);
145 }
146 };
147
148 this.setChildCollapse = function (child, mark) {
149 var _state = _this3.state,
150 device = _state.device,
151 collapseMap = _state.collapseMap,
152 controll = _state.controll;
153 var collapse = child.props.collapse;
154
155 var deviceMap = getCollapseMap(device);
156 var props = {};
157
158 // 非受控模式
159 if (isBoolean(collapse) === false) {
160 props.collapse = controll ? collapseMap[mark] : deviceMap[mark];
161 // props.collapse = collapseMap[mark];
162 }
163
164 if (device !== 'phone' && mark === 'Navigation') {
165 props.miniable = true;
166 }
167
168 return React.cloneElement(child, props);
169 };
170
171 this.toggleAside = function (mark, props, e) {
172 var _extends2;
173
174 var _state2 = _this3.state,
175 device = _state2.device,
176 collapseMap = _state2.collapseMap;
177
178 var deviceMap = getCollapseMap(device);
179 var current = props.collapse;
180
181 var newCollapseMap = _extends({}, deviceMap, collapseMap, (_extends2 = {}, _extends2[mark] = !current, _extends2));
182 _this3.setState({
183 controll: true,
184 collapseMap: newCollapseMap
185 });
186
187 if (props && typeof props.onCollapseChange === 'function') {
188 props.onCollapseChange(newCollapseMap[mark]);
189 }
190
191 var children = _this3.props.children;
192
193 var com = void 0;
194 if (mark === 'Navigation') {
195 com = children.filter(function (child) {
196 return child && child.type._typeMark.replace('Shell_', '') === mark && child.props.direction !== 'hoz';
197 }).pop();
198 } else {
199 com = children.filter(function (child) {
200 return child && child.type._typeMark.replace('Shell_', '') === mark;
201 }).pop();
202 }
203
204 var _com$props$triggerPro = com.props.triggerProps,
205 triggerProps = _com$props$triggerPro === undefined ? {} : _com$props$triggerPro;
206
207
208 if (typeof triggerProps.onClick === 'function') {
209 triggerProps.onClick(e, _this3.state.collapseMap[mark]);
210 }
211 };
212
213 this.toggleNavigation = function (e) {
214 var mark = 'Navigation';
215 var props = _this3.layout[mark].props;
216
217
218 if ('keyCode' in e && e.keyCode !== KEYCODE.ENTER) {
219 return;
220 }
221
222 _this3.toggleAside(mark, props, e);
223 };
224
225 this.toggleLocalNavigation = function (e) {
226 var mark = 'LocalNavigation';
227 var props = _this3.layout[mark].props;
228
229
230 if ('keyCode' in e && e.keyCode !== KEYCODE.ENTER) {
231 return;
232 }
233
234 _this3.toggleAside(mark, props, e);
235 };
236
237 this.toggleAncillary = function (e) {
238 var mark = 'Ancillary';
239 var props = _this3.layout[mark].props;
240
241
242 if ('keyCode' in e && e.keyCode !== KEYCODE.ENTER) {
243 return;
244 }
245
246 _this3.toggleAside(mark, props, e);
247 };
248
249 this.toggleToolDock = function (e) {
250 var mark = 'ToolDock';
251 var props = _this3.layout[mark].props;
252
253
254 if ('keyCode' in e && e.keyCode !== KEYCODE.ENTER) {
255 return;
256 }
257
258 _this3.toggleAside(mark, props, e);
259 };
260
261 this.saveHeaderRef = function (ref) {
262 _this3.headerRef = ref;
263 };
264
265 this.saveLocalNavRef = function (ref) {
266 _this3.localNavRef = ref;
267 };
268
269 this.saveNavRef = function (ref) {
270 _this3.navRef = ref;
271 };
272
273 this.saveSubmainRef = function (ref) {
274 _this3.submainRef = ref;
275 };
276
277 this.saveToolDockRef = function (ref) {
278 _this3.toolDockRef = ref;
279 };
280
281 this.renderShell = function (props) {
282 var _classnames, _classnames2, _classnames3, _classnames4, _classnames5, _classnames6, _classnames7, _classnames11;
283
284 var prefix = props.prefix,
285 children = props.children,
286 className = props.className,
287 type = props.type,
288 fixedHeader = props.fixedHeader,
289 others = _objectWithoutProperties(props, ['prefix', 'children', 'className', 'type', 'fixedHeader']);
290
291 var device = _this3.state.device;
292
293
294 var layout = {};
295 layout.header = {};
296 var hasToolDock = false,
297 needNavigationTrigger = false,
298 needDockTrigger = false;
299
300 React.Children.map(children, function (child) {
301 if (child && typeof child.type === 'function') {
302 var mark = child.type._typeMark.replace('Shell_', '');
303 switch (mark) {
304 case 'Branding':
305 case 'Action':
306 layout.header[mark] = child;
307 break;
308 case 'MultiTask':
309 layout.taskHeader = child;
310 break;
311 case 'LocalNavigation':
312 if (!layout[mark]) {
313 layout[mark] = [];
314 }
315 layout[mark] = _this3.setChildCollapse(child, mark);
316 break;
317 case 'Ancillary':
318 if (!layout[mark]) {
319 layout[mark] = [];
320 }
321
322 layout[mark] = _this3.setChildCollapse(child, mark);
323 break;
324 case 'ToolDock':
325 hasToolDock = true;
326
327 if (!layout[mark]) {
328 layout[mark] = [];
329 }
330
331 _this3.toolDockFixed = child.props.fixed;
332 var childT = _this3.setChildCollapse(child, mark);
333 layout[mark] = childT;
334
335 break;
336 case 'AppBar':
337 case 'Content':
338 case 'Footer':
339 layout.content || (layout.content = []);
340 layout.content.push(child);
341 break;
342 case 'Page':
343 layout.page || (layout.page = []);
344 layout.page = child;
345 break;
346 case 'Navigation':
347 if (child.props.direction === 'hoz') {
348 layout.header[mark] = child;
349 } else {
350 if (!layout[mark]) {
351 layout[mark] = [];
352 }
353
354 needNavigationTrigger = true;
355 _this3.navigationFixed = child.props.fixed;
356 var childN = _this3.setChildCollapse(child, mark);
357 layout[mark] = childN;
358 }
359 break;
360 default:
361 break;
362 }
363 }
364 });
365
366 var headerCls = classnames((_classnames = {}, _classnames[prefix + 'shell-header'] = true, _classnames[prefix + 'shell-fixed-header'] = fixedHeader, _classnames));
367
368 var mainCls = classnames((_classnames2 = {}, _classnames2[prefix + 'shell-main'] = true, _classnames2));
369
370 var pageCls = classnames((_classnames3 = {}, _classnames3[prefix + 'shell-page'] = true, _classnames3));
371
372 var submainCls = classnames((_classnames4 = {}, _classnames4[prefix + 'shell-sub-main'] = true, _classnames4));
373
374 var asideCls = classnames((_classnames5 = {}, _classnames5[prefix + 'shell-aside'] = true, _classnames5));
375
376 var toolDockCls = classnames((_classnames6 = {}, _classnames6[prefix + 'aside-tooldock'] = true, _classnames6));
377
378 var navigationCls = classnames((_classnames7 = {}, _classnames7[prefix + 'aside-navigation'] = true, _classnames7[prefix + 'shell-collapse'] = layout.Navigation && layout.Navigation.props.collapse, _classnames7));
379
380 if (hasToolDock) {
381 if (device === 'phone') {
382 needDockTrigger = true;
383 }
384 }
385
386 // 如果存在垂直模式的 Navigation, 则需要在 Branding 上出现 trigger
387 if (needNavigationTrigger) {
388 var branding = layout.header.Branding;
389 var _layout$Navigation$pr = layout.Navigation.props,
390 trigger = _layout$Navigation$pr.trigger,
391 collapse = _layout$Navigation$pr.collapse;
392
393
394 if ('trigger' in layout.Navigation.props) {
395 trigger = trigger && React.cloneElement(trigger, {
396 onClick: _this3.toggleNavigation,
397 'aria-expanded': !collapse
398 }) || trigger;
399 } else {
400 trigger = React.createElement(
401 'div',
402 {
403 key: 'nav-trigger',
404 role: 'button',
405 tabIndex: 0,
406 'aria-expanded': !collapse,
407 'aria-label': 'toggle',
408 className: 'nav-trigger',
409 onClick: _this3.toggleNavigation,
410 onKeyDown: _this3.toggleNavigation
411 },
412 collapse ? React.createElement(Icon, { size: 'small', type: 'toggle-right' }) : React.createElement(Icon, { size: 'small', type: 'toggle-left' })
413 );
414 }
415
416 if (!branding) {
417 trigger && (layout.header.Branding = trigger);
418 } else {
419 layout.header.Branding = React.cloneElement(branding, {}, [trigger, branding.props.children]);
420 }
421 }
422
423 // 如果存在 toolDock, 则需要在 Action 上出现 trigger
424 if (needDockTrigger) {
425 var action = layout.header.Action;
426 var _layout$ToolDock$prop = layout.ToolDock.props,
427 _trigger = _layout$ToolDock$prop.trigger,
428 _collapse = _layout$ToolDock$prop.collapse;
429
430
431 if ('trigger' in layout.ToolDock.props) {
432 _trigger = _trigger && React.cloneElement(_trigger, {
433 onClick: _this3.toggleToolDock,
434 'aria-expanded': !_collapse
435 }) || _trigger;
436 } else {
437 _trigger = React.createElement(
438 'div',
439 {
440 key: 'dock-trigger',
441 tabIndex: 0,
442 role: 'button',
443 'aria-expanded': !_collapse,
444 'aria-label': 'toggle',
445 className: 'dock-trigger',
446 onClick: _this3.toggleToolDock,
447 onKeyDown: _this3.toggleToolDock
448 },
449 React.createElement(Icon, { size: 'small', type: 'add' })
450 );
451 }
452
453 if (!action) {
454 layout.header.Action = _trigger;
455 } else {
456 layout.header.Action = React.cloneElement(action, {}, [action.props.children, _trigger]);
457 }
458 }
459
460 var headerDom = [],
461 contentArr = [],
462 innerArr = [],
463 taskHeaderDom = null;
464
465 if (layout.taskHeader) {
466 var _classnames8;
467
468 var taskHeaderCls = classnames((_classnames8 = {}, _classnames8[prefix + 'shell-task-header'] = true, _classnames8));
469
470 taskHeaderDom = React.createElement(
471 'section',
472 { key: 'task-header', className: taskHeaderCls },
473 layout.taskHeader
474 );
475 }
476
477 // 按照dom结构,innerArr 包括 LocalNavigation content Ancillary
478 if (layout.LocalNavigation) {
479 var _classnames9;
480
481 var _layout$LocalNavigati = layout.LocalNavigation.props,
482 _trigger2 = _layout$LocalNavigati.trigger,
483 _collapse2 = _layout$LocalNavigati.collapse;
484
485
486 if ('trigger' in layout.LocalNavigation.props) {
487 _trigger2 = _trigger2 && React.cloneElement(_trigger2, {
488 onClick: _this3.toggleLocalNavigation,
489 'aria-expanded': !_collapse2
490 }) || _trigger2;
491 } else {
492 _trigger2 = React.createElement(
493 'div',
494 {
495 key: 'local-nav-trigger',
496 role: 'button',
497 tabIndex: 0,
498 'aria-expanded': !_collapse2,
499 'aria-label': 'toggle',
500 className: 'local-nav-trigger aside-trigger',
501 onClick: _this3.toggleLocalNavigation,
502 onKeyDown: _this3.toggleLocalNavigation
503 },
504 _collapse2 ? React.createElement(Icon, { size: 'small', type: 'arrow-right' }) : React.createElement(Icon, { size: 'small', type: 'arrow-left' })
505 );
506 }
507
508 var localNavCls = classnames(asideCls, (_classnames9 = {}, _classnames9[prefix + 'aside-localnavigation'] = true, _classnames9));
509
510 innerArr.push(React.createElement(
511 'aside',
512 { key: 'localnavigation', className: localNavCls, ref: _this3.saveLocalNavRef },
513 React.cloneElement(layout.LocalNavigation, {}, [React.createElement(
514 'div',
515 { key: 'wrapper', className: prefix + 'shell-content-wrapper' },
516 layout.LocalNavigation.props.children
517 ), _trigger2])
518 ));
519 }
520
521 if (layout.content) {
522 innerArr.push(React.createElement(
523 'section',
524 { key: 'submain', className: submainCls, ref: _this3.saveSubmainRef },
525 layout.content
526 ));
527 }
528
529 if (layout.Ancillary) {
530 var _classnames10;
531
532 var _layout$Ancillary$pro = layout.Ancillary.props,
533 _trigger3 = _layout$Ancillary$pro.trigger,
534 _collapse3 = _layout$Ancillary$pro.collapse;
535
536
537 if ('trigger' in layout.Ancillary.props) {
538 _trigger3 = _trigger3 && React.cloneElement(_trigger3, {
539 onClick: _this3.toggleAncillary,
540 'aria-expanded': !_collapse3
541 }) || _trigger3;
542 } else {
543 _trigger3 = React.createElement(
544 'div',
545 {
546 key: 'ancillary-trigger',
547 role: 'button',
548 tabIndex: 0,
549 'aria-expanded': !_collapse3,
550 'aria-label': 'toggle',
551 className: 'ancillary-trigger aside-trigger',
552 onClick: _this3.toggleAncillary,
553 onKeyDown: _this3.toggleAncillary
554 },
555 _collapse3 ? React.createElement(Icon, { size: 'small', type: 'arrow-left' }) : React.createElement(Icon, { size: 'small', type: 'arrow-right' })
556 );
557 }
558
559 var ancillaryCls = classnames(asideCls, (_classnames10 = {}, _classnames10[prefix + 'aside-ancillary'] = true, _classnames10));
560
561 innerArr.push(React.createElement(
562 'aside',
563 { key: 'ancillary', className: ancillaryCls },
564 React.cloneElement(layout.Ancillary, {}, [React.createElement(
565 'div',
566 { key: 'wrapper', className: prefix + 'shell-content-wrapper' },
567 layout.Ancillary.props.children
568 ), _trigger3])
569 ));
570 }
571
572 // 按照dom结构, arr 包括 header Navigation ToolDock 和 innerArr
573 if (Object.keys(layout.header).length > 0) {
574 var _dom = React.createElement(
575 'header',
576 { key: 'header', className: headerCls, ref: _this3.saveHeaderRef },
577 layout.header.Branding,
578 layout.header.Navigation,
579 layout.header.Action
580 );
581 if (fixedHeader && env.ieVersion) {
582 headerDom = React.createElement(
583 Affix,
584 { style: { zIndex: 9 } },
585 _dom
586 );
587 } else {
588 headerDom = _dom;
589 }
590 }
591
592 layout.Navigation && contentArr.push(React.createElement(
593 'aside',
594 { key: 'navigation', className: navigationCls, ref: _this3.saveNavRef },
595 React.cloneElement(layout.Navigation, {
596 className: classnames(asideCls, layout.Navigation.props.className)
597 })
598 ));
599
600 // const contentArea = innerArr.length > 0
601 // ? <section key="main" className={mainCls}>{innerArr}</section>
602 // : layout.page;
603
604 // contentArr.push(contentArea);
605 contentArr = contentArr.concat(innerArr.length > 0 ? innerArr : [React.createElement(
606 'section',
607 { key: 'page', ref: _this3.saveSubmainRef, className: submainCls },
608 layout.page
609 )]);
610
611 layout.ToolDock && contentArr.push(React.createElement(
612 'aside',
613 { key: 'tooldock', className: toolDockCls, ref: _this3.saveToolDockRef },
614 React.cloneElement(layout.ToolDock, {
615 className: classnames(asideCls, layout.ToolDock.props.className),
616 key: 'tooldock'
617 })
618 ));
619
620 var cls = classnames((_classnames11 = {}, _classnames11[prefix + 'shell'] = true, _classnames11[prefix + 'shell-' + device] = true, _classnames11[prefix + 'shell-' + type] = true, _classnames11[className] = !!className, _classnames11));
621
622 if (componentName === 'Page') {
623 return React.createElement(
624 'section',
625 { className: pageCls },
626 children
627 );
628 }
629
630 _this3.layout = layout;
631
632 return React.createElement(
633 'section',
634 _extends({ className: cls }, others),
635 headerDom,
636 taskHeaderDom,
637 React.createElement(
638 'section',
639 { className: mainCls },
640 contentArr
641 )
642 );
643 };
644 }, _temp);
645 Shell.displayName = 'Shell';
646
647
648 return polyfill(Shell);
649}
\No newline at end of file