Skip to content

Commit

Permalink
feat(Shell): support fixedHeader&Navigation fixed mode (#1738)
Browse files Browse the repository at this point in the history
  • Loading branch information
youluna committed Apr 3, 2020
1 parent 43cd5a3 commit 0b674b6
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 33 deletions.
6 changes: 5 additions & 1 deletion docs/shell/index.en-us.md
Expand Up @@ -46,6 +46,9 @@ Shell is the infrastructure framework of the whole application. It embodies the
| Param | Description | Type | Default Value |
| -------------------- | ------------ | ----------------- | ------------------ |
| device | Preset screen width, tt determines whether `Navigation` `LocalNavigation` `Ancillary`take space or not<br><br>**option**:<br>'phone', 'tablet', 'desktop' | Enum | desktop |
| type | type of Shell <br><br>**可选值**:<br>'light', 'dark', 'brand' | Enum | light |
| fixedHeader | fixed header or not. Doesn't work under IE11 | Boolean | false |


### Shell.Navigation
It will tell his children whether it's collapse or not by `isCollapse` via Context.
Expand All @@ -56,7 +59,7 @@ It will tell his children whether it's collapse or not by `isCollapse` via Conte
| direction | header or asider<br><br>**option**:<br>'hoz', 'ver' | Enum | hoz |
| align | Arrangement of Navigation when direction is hoz<br><br>**option**:<br>'left', 'right', 'center' | Enum | right |
| onCollapseChange | this will be triggered when collapse changed by inner icon | Function | () => {} |

| fixed | fixed or not, only worked when Shell fixedHeader is true | Boolean | false |

### Shell.LocalNavigation
| Param | Description | Type | Default Value |
Expand All @@ -69,6 +72,7 @@ It will tell his children whether it's collapse or not by `isCollapse` via Conte
| -------------------- | ------------ | ----------------- | ------------------ |
| collapse | collapse or not | Boolean | false |
| onCollapseChange | this will be triggered when collapse changed by inner icon | Function | () => {} |
| fixed | fixed or not, only worked when Shell fixedHeader is true | Boolean | false |

### Shell.Ancillary
| Param | Description | Type | Default Value |
Expand Down
4 changes: 3 additions & 1 deletion docs/shell/index.md
Expand Up @@ -47,6 +47,7 @@ Shell 是整个应用的基础结构框架。它体现应用的结构形式和
| -------------------- | ------------ | ----------------- | ------------------ |
| device | 预设屏幕宽度,会影响`Navigation` `LocalNavigation` `Ancillary`等是否占据空间<br><br>**可选值**:<br>'phone', 'tablet', 'desktop' | Enum | desktop |
| type | 样式类型,分浅色主题、深色主题、主题色主题,用户可自定义配置<br><br>**可选值**:<br>'light', 'dark', 'brand' | Enum | light |
| fixedHeader | 是否固定Header,采用sticky布局,不支持 IE11 | Boolean | false |

### Shell.Navigation
向子组件透传 isCollapse 的Context,表示当前是否处于折叠状态
Expand All @@ -57,7 +58,7 @@ Shell 是整个应用的基础结构框架。它体现应用的结构形式和
| collapse | 是否折叠(折叠成只有icon状态) | Boolean | false |
| align | 横向模式下,导航排布的位置<br><br>**可选值**:<br>'left', 'right', 'center' | Enum | right |
| onCollapseChange | 默认按钮触发的展开收起状态 | Function | () => {} |

| fixed | 是否固定,且需要在在 Shell fixedHeader时生效 | Boolean | false |

### Shell.LocalNavigation
| 参数 | 说明 | 类型 | 默认值 |
Expand All @@ -70,6 +71,7 @@ Shell 是整个应用的基础结构框架。它体现应用的结构形式和
| -------------------- | ------------ | ----------------- | ------------------ |
| collapse | 是否折叠(完全收起) | Boolean | false |
| onCollapseChange | 默认按钮触发的展开收起状态 | Function | () => {} |
| fixed | 是否固定,且需要在在 Shell fixedHeader时生效 | Boolean | false |

### Shell.Ancillary
| 参数 | 说明 | 类型 | 默认值 |
Expand Down
14 changes: 6 additions & 8 deletions src/shell/base.jsx
Expand Up @@ -25,12 +25,17 @@ export default function Base(props) {
* @param {Boolean} collapse 弹层是否显示
*/
onCollapseChange: PropTypes.func,
/**
* 是否固定,仅对 Shell.Navigation Shell.ToolDock 生效,且需要在在 Shell fixedHeader时生效
*/
fixed: PropTypes.bool,
};

static defaultProps = {
prefix: 'next-',
component: 'div',
onCollapseChange: () => {},
fixed: false,
};

static childContextTypes = {
Expand Down Expand Up @@ -61,7 +66,7 @@ export default function Base(props) {
...others
} = this.props;

let Tag = component;
const Tag = component;

const cls = classnames({
[`${prefix}shell-${componentName.toLowerCase()}`]: true,
Expand All @@ -87,13 +92,6 @@ export default function Base(props) {
return children;
}

if (
['ToolDock'].indexOf(componentName) > -1 ||
(componentName === 'Navigation' && direction === 'ver')
) {
Tag = 'aside';
}

return (
<Tag className={cls} {...others}>
{newChildren}
Expand Down
22 changes: 22 additions & 0 deletions src/shell/main.scss
Expand Up @@ -42,6 +42,11 @@
margin-left: 10px;
}

&#{$shell-prefix}-fixed-header {
position: sticky;
top: 0;
}

#{$shell-prefix}-navigation {
flex: 1;
display: flex;
Expand Down Expand Up @@ -95,6 +100,23 @@
}
}

.#{$css-prefix}aside-navigation,
.#{$css-prefix}aside-tooldock {
display: flex;
&.fixed {
position: fixed;
top: 0;
bottom: 0;
}
}

.#{$css-prefix}aside-navigation.fixed {
left: 0;
}
.#{$css-prefix}aside-tooldock.fixed {
right: 0;
}

&-aside {
transition: all .2s $motion-ease;

Expand Down
156 changes: 134 additions & 22 deletions src/shell/shell.jsx
Expand Up @@ -3,11 +3,11 @@ import classnames from 'classnames';
import PropTypes from 'prop-types';
import { polyfill } from 'react-lifecycles-compat';
import ConfigProvider from '../config-provider';
import Affix from '../affix';
import Icon from '../icon';
import { KEYCODE } from '../util';
import { KEYCODE, dom, env } from '../util';

import { isBoolean, getCollapseMap } from './util';

/**
* Shell
*/
Expand All @@ -31,12 +31,17 @@ export default function ShellBase(props) {
* @enumdesc 浅色, 深色, 主题色
*/
type: PropTypes.oneOf(['light', 'dark', 'brand']),
/**
* 是否固定 header, 用sticky实现,IE下降级为Affix
*/
fixedHeader: PropTypes.bool,
};

static defaultProps = {
prefix: 'next-',
device: 'desktop',
type: 'light',
fixedHeader: false,
};

constructor(props) {
Expand Down Expand Up @@ -67,6 +72,10 @@ export default function ShellBase(props) {
return {};
}

componentDidMount() {
this.checkAsideFixed();
}

componentDidUpdate(prevProps) {
if (prevProps.device !== this.props.device) {
const deviceMapBefore = getCollapseMap(prevProps.device);
Expand All @@ -84,8 +93,47 @@ export default function ShellBase(props) {
}
});
}

setTimeout(() => {
// 如果左侧边栏固定
this.checkAsideFixed();
}, 201);
}

checkAsideFixed = () => {
const { fixedHeader } = this.props;

if (!fixedHeader) {
return;
}

let headerHeight;
if (
this.headerRef &&
(this.navigationFixed || this.toolDockFixed)
) {
headerHeight = dom.getStyle(this.headerRef, 'height');
}

if (this.navigationFixed) {
const style = {};
style.marginLeft = dom.getStyle(this.navRef, 'width');
dom.addClass(this.navRef, 'fixed');
headerHeight &&
dom.setStyle(this.navRef, { top: headerHeight });
dom.setStyle(this.localNavRef || this.submainRef, style);
}

if (this.toolDockFixed) {
const style = {};
style.marginRight = dom.getStyle(this.toolDockRef, 'width');
dom.addClass(this.toolDockRef, 'fixed');
headerHeight &&
dom.setStyle(this.toolDockRef, { top: headerHeight });
dom.setStyle(this.localNavRef || this.submainRef, style);
}
};

setChildCollapse = (child, mark) => {
const { device, collapseMap, controll } = this.state;
const { collapse } = child.props;
Expand Down Expand Up @@ -197,8 +245,34 @@ export default function ShellBase(props) {
this.toggleAside(mark, props, e);
};

saveHeaderRef = ref => {
this.headerRef = ref;
};
saveLocalNavRef = ref => {
this.localNavRef = ref;
};

saveNavRef = ref => {
this.navRef = ref;
};

saveSubmainRef = ref => {
this.submainRef = ref;
};

saveToolDockRef = ref => {
this.toolDockRef = ref;
};

renderShell = props => {
const { prefix, children, className, type, ...others } = props;
const {
prefix,
children,
className,
type,
fixedHeader,
...others
} = props;

const { device } = this.state;

Expand Down Expand Up @@ -239,6 +313,7 @@ export default function ShellBase(props) {
layout[mark] = [];
}

this.toolDockFixed = child.props.fixed;
const childT = this.setChildCollapse(child, mark);
layout[mark] = childT;

Expand All @@ -262,7 +337,7 @@ export default function ShellBase(props) {
}

needNavigationTrigger = true;

this.navigationFixed = child.props.fixed;
const childN = this.setChildCollapse(
child,
mark
Expand All @@ -278,6 +353,7 @@ export default function ShellBase(props) {

const headerCls = classnames({
[`${prefix}shell-header`]: true,
[`${prefix}shell-fixed-header`]: fixedHeader,
});

const mainCls = classnames({
Expand All @@ -292,6 +368,14 @@ export default function ShellBase(props) {
[`${prefix}shell-aside`]: true,
});

const toolDockCls = classnames({
[`${prefix}aside-tooldock`]: true,
});

const navigationCls = classnames({
[`${prefix}aside-navigation`]: true,
});

if (hasToolDock) {
if (device === 'phone') {
needDockTrigger = true;
Expand Down Expand Up @@ -437,7 +521,11 @@ export default function ShellBase(props) {
});

innerArr.push(
<aside key="localnavigation" className={localNavCls}>
<aside
key="localnavigation"
className={localNavCls}
ref={this.saveLocalNavRef}
>
{React.cloneElement(layout.LocalNavigation, {}, [
<div
key="wrapper"
Expand All @@ -453,7 +541,11 @@ export default function ShellBase(props) {

if (layout.content) {
innerArr.push(
<section key="submain" className={submainCls}>
<section
key="submain"
className={submainCls}
ref={this.saveSubmainRef}
>
{layout.content}
</section>
);
Expand Down Expand Up @@ -512,24 +604,38 @@ export default function ShellBase(props) {

// 按照dom结构, arr 包括 header Navigation ToolDock 和 innerArr
if (Object.keys(layout.header).length > 0) {
headerDom = (
<header key="header" className={headerCls}>
const dom = (
<header
key="header"
className={headerCls}
ref={this.saveHeaderRef}
>
{layout.header.Branding}
{layout.header.Navigation}
{layout.header.Action}
</header>
);
if (fixedHeader && env.ieVersion) {
headerDom = <Affix style={{ zIndex: 9 }}>{dom}</Affix>;
} else {
headerDom = dom;
}
}

layout.Navigation &&
contentArr.push(
React.cloneElement(layout.Navigation, {
className: classnames(
asideCls,
layout.Navigation.props.className
),
key: 'navigation',
})
<aside
key="navigation"
className={navigationCls}
ref={this.saveNavRef}
>
{React.cloneElement(layout.Navigation, {
className: classnames(
asideCls,
layout.Navigation.props.className
),
})}
</aside>
);

// const contentArea = innerArr.length > 0
Expand All @@ -543,13 +649,19 @@ export default function ShellBase(props) {

layout.ToolDock &&
contentArr.push(
React.cloneElement(layout.ToolDock, {
className: classnames(
asideCls,
layout.ToolDock.props.className
),
key: 'tooldock',
})
<aside
key="tooldock"
className={toolDockCls}
ref={this.saveToolDockRef}
>
{React.cloneElement(layout.ToolDock, {
className: classnames(
asideCls,
layout.ToolDock.props.className
),
key: 'tooldock',
})}
</aside>
);

const cls = classnames({
Expand Down

0 comments on commit 0b674b6

Please sign in to comment.