tangjinzhou
7 years ago
14 changed files with 861 additions and 16 deletions
@ -0,0 +1,239 @@
|
||||
// matchMedia polyfill for |
||||
// https://github.com/WickyNilliams/enquire.js/issues/82 |
||||
if (typeof window !== 'undefined') { |
||||
const matchMediaPolyfill = (mediaQuery) => { |
||||
return { |
||||
media: mediaQuery, |
||||
matches: false, |
||||
addListener () { |
||||
}, |
||||
removeListener () { |
||||
}, |
||||
} |
||||
} |
||||
window.matchMedia = window.matchMedia || matchMediaPolyfill |
||||
} |
||||
|
||||
import classNames from 'classnames' |
||||
import PropTypes from '../_util/vue-types' |
||||
import Icon from '../icon' |
||||
import { initDefaultProps, getOptionProps, hasProp, getComponentFromProp } from '../_util/props-util' |
||||
import BaseMixin from '../_util/BaseMixin' |
||||
|
||||
const dimensionMap = { |
||||
xs: '480px', |
||||
sm: '576px', |
||||
md: '768px', |
||||
lg: '992px', |
||||
xl: '1200px', |
||||
xxl: '1600px', |
||||
} |
||||
|
||||
// export type CollapseType = 'clickTrigger' | 'responsive'; |
||||
|
||||
export const SiderProps = { |
||||
prefixCls: PropTypes.string, |
||||
collapsible: PropTypes.bool, |
||||
collapsed: PropTypes.bool, |
||||
defaultCollapsed: PropTypes.bool, |
||||
reverseArrow: PropTypes.bool, |
||||
// onCollapse?: (collapsed: boolean, type: CollapseType) => void; |
||||
trigger: PropTypes.any, |
||||
width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), |
||||
collapsedWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), |
||||
breakpoint: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl', 'xxl']), |
||||
} |
||||
|
||||
// export interface SiderState { |
||||
// collapsed?: boolean; |
||||
// below: boolean; |
||||
// belowShow?: boolean; |
||||
// } |
||||
|
||||
// export interface SiderContext { |
||||
// siderCollapsed: boolean; |
||||
// } |
||||
|
||||
const generateId = (() => { |
||||
let i = 0 |
||||
return (prefix = '') => { |
||||
i += 1 |
||||
return `${prefix}${i}` |
||||
} |
||||
})() |
||||
|
||||
export default { |
||||
name: 'ALayoutSider', |
||||
__ANT_LAYOUT_SIDER: true, |
||||
mixins: [BaseMixin], |
||||
props: initDefaultProps(SiderProps, { |
||||
prefixCls: 'ant-layout-sider', |
||||
collapsible: false, |
||||
defaultCollapsed: false, |
||||
reverseArrow: false, |
||||
width: 200, |
||||
collapsedWidth: 80, |
||||
}), |
||||
|
||||
// static childContextTypes = { |
||||
// siderCollapsed: PropTypes.bool, |
||||
// collapsedWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), |
||||
// }; |
||||
|
||||
// static contextTypes = { |
||||
// siderHook: PropTypes.object, |
||||
// }; |
||||
|
||||
// private mql: MediaQueryList; |
||||
// private uniqueId: string; |
||||
|
||||
data () { |
||||
this.uniqueId = generateId('ant-sider-') |
||||
let matchMedia |
||||
if (typeof window !== 'undefined') { |
||||
matchMedia = window.matchMedia |
||||
} |
||||
const props = getOptionProps(this) |
||||
if (matchMedia && props.breakpoint && props.breakpoint in dimensionMap) { |
||||
this.mql = matchMedia(`(max-width: ${dimensionMap[props.breakpoint]})`) |
||||
} |
||||
let sCollapsed |
||||
if ('collapsed' in props) { |
||||
sCollapsed = props.collapsed |
||||
} else { |
||||
sCollapsed = props.defaultCollapsed |
||||
} |
||||
return { |
||||
sCollapsed, |
||||
below: false, |
||||
belowShow: false, |
||||
} |
||||
}, |
||||
provide () { |
||||
return { |
||||
layoutSiderContext: this, // menu组件中使用 |
||||
} |
||||
}, |
||||
inject: { |
||||
siderHook: { default: {}}, |
||||
}, |
||||
// getChildContext() { |
||||
// return { |
||||
// siderCollapsed: this.state.collapsed, |
||||
// collapsedWidth: this.props.collapsedWidth, |
||||
// }; |
||||
// } |
||||
watch: { |
||||
collapsed (val) { |
||||
this.setState({ |
||||
sCollapsed: val, |
||||
}) |
||||
}, |
||||
}, |
||||
|
||||
mounted () { |
||||
this.$nextTick(() => { |
||||
if (this.mql) { |
||||
this.mql.addListener(this.responsiveHandler) |
||||
this.responsiveHandler(this.mql) |
||||
} |
||||
|
||||
if (this.siderHook.addSider) { |
||||
this.siderHook.addSider(this.uniqueId) |
||||
} |
||||
}) |
||||
}, |
||||
|
||||
beforeDestroy () { |
||||
if (this.mql) { |
||||
this.mql.removeListener(this.responsiveHandler) |
||||
} |
||||
|
||||
if (this.siderHook.removeSider) { |
||||
this.siderHook.removeSider(this.uniqueId) |
||||
} |
||||
}, |
||||
model: { |
||||
prop: 'collapsed', |
||||
event: 'collapse', |
||||
}, |
||||
methods: { |
||||
responsiveHandler (mql) { |
||||
this.setState({ below: mql.matches }) |
||||
if (this.sCollapsed !== mql.matches) { |
||||
this.setCollapsed(mql.matches, 'responsive') |
||||
} |
||||
}, |
||||
|
||||
setCollapsed (collapsed, type) { |
||||
if (!hasProp(this, 'collapsed')) { |
||||
this.setState({ |
||||
sCollapsed: collapsed, |
||||
}) |
||||
} |
||||
this.$emit('collapse', collapsed, type) |
||||
}, |
||||
|
||||
toggle () { |
||||
const collapsed = !this.sCollapsed |
||||
this.setCollapsed(collapsed, 'clickTrigger') |
||||
}, |
||||
|
||||
belowShowChange () { |
||||
this.setState({ belowShow: !this.belowShow }) |
||||
}, |
||||
}, |
||||
|
||||
render () { |
||||
const { prefixCls, |
||||
collapsible, reverseArrow, width, collapsedWidth, |
||||
} = getOptionProps(this) |
||||
const trigger = getComponentFromProp(this, 'trigger') |
||||
let siderWidth = this.sCollapsed ? collapsedWidth : width |
||||
siderWidth = typeof siderWidth === 'string' ? siderWidth.replace('px', '') : siderWidth |
||||
// special trigger when collapsedWidth == 0 |
||||
const zeroWidthTrigger = collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px' ? ( |
||||
<span onClick={this.toggle} class={`${prefixCls}-zero-width-trigger`}> |
||||
<Icon type='bars' /> |
||||
</span> |
||||
) : null |
||||
const iconObj = { |
||||
'expanded': reverseArrow ? <Icon type='right' /> : <Icon type='left' />, |
||||
'collapsed': reverseArrow ? <Icon type='left' /> : <Icon type='right' />, |
||||
} |
||||
const status = this.sCollapsed ? 'collapsed' : 'expanded' |
||||
const defaultTrigger = iconObj[status] |
||||
const triggerDom = ( |
||||
trigger !== null |
||||
? zeroWidthTrigger || ( |
||||
<div className={`${prefixCls}-trigger`} onClick={this.toggle} style={{ width: `${siderWidth}px` }}> |
||||
{trigger || defaultTrigger} |
||||
</div> |
||||
) : null |
||||
) |
||||
const divStyle = { |
||||
// ...style, |
||||
flex: `0 0 ${siderWidth}px`, |
||||
maxWidth: `${siderWidth}px`, // Fix width transition bug in IE11 |
||||
minWidth: `${siderWidth}px`, // https://github.com/ant-design/ant-design/issues/6349 |
||||
width: `${siderWidth}px`, |
||||
} |
||||
const siderCls = classNames(prefixCls, { |
||||
[`${prefixCls}-collapsed`]: !!this.sCollapsed, |
||||
[`${prefixCls}-has-trigger`]: collapsible && trigger !== null && !zeroWidthTrigger, |
||||
[`${prefixCls}-below`]: !!this.below, |
||||
[`${prefixCls}-zero-width`]: siderWidth === 0 || siderWidth === '0' || siderWidth === '0px', |
||||
}) |
||||
const divProps = { |
||||
on: this.$listeners, |
||||
class: siderCls, |
||||
style: divStyle, |
||||
} |
||||
return ( |
||||
<div {...divProps}> |
||||
<div class={`${prefixCls}-children`}>{this.$slots.default}</div> |
||||
{collapsible || (this.below && zeroWidthTrigger) ? triggerDom : null} |
||||
</div> |
||||
) |
||||
}, |
||||
} |
@ -0,0 +1,79 @@
|
||||
<cn> |
||||
#### 基本结构 |
||||
典型的页面布局。 |
||||
</cn> |
||||
|
||||
<us> |
||||
#### Basic Structure |
||||
Classic page layouts. |
||||
</us> |
||||
|
||||
```html |
||||
<template> |
||||
<div id="components-layout-demo-basic"> |
||||
<a-layout> |
||||
<a-layout-header>Header</a-layout-header> |
||||
<a-layout-content>Content</a-layout-content> |
||||
<a-layout-footer>Footer</a-layout-footer> |
||||
</a-layout> |
||||
|
||||
<a-layout> |
||||
<a-layout-header>Header</a-layout-header> |
||||
<a-layout> |
||||
<a-layout-sider>Sider</a-layout-sider> |
||||
<a-layout-content>Content</a-layout-content> |
||||
</a-layout> |
||||
<a-layout-footer>Footer</a-layout-footer> |
||||
</a-layout> |
||||
|
||||
<a-layout> |
||||
<a-layout-header>Header</a-layout-header> |
||||
<a-layout> |
||||
<a-layout-content>Content</a-layout-content> |
||||
<a-layout-sider>Sider</a-layout-sider> |
||||
</a-layout> |
||||
<a-layout-footer>Footer</a-layout-footer> |
||||
</a-layout> |
||||
|
||||
<a-layout> |
||||
<a-layout-sider>Sider</a-layout-sider> |
||||
<a-layout> |
||||
<a-layout-header>Header</a-layout-header> |
||||
<a-layout-content>Content</a-layout-content> |
||||
<a-layout-footer>Footer</a-layout-footer> |
||||
</a-layout> |
||||
</a-layout> |
||||
</div> |
||||
</template> |
||||
|
||||
<style> |
||||
#components-layout-demo-basic { |
||||
text-align: center; |
||||
} |
||||
#components-layout-demo-basic .ant-layout-header, |
||||
#components-layout-demo-basic .ant-layout-footer { |
||||
background: #7dbcea; |
||||
color: #fff; |
||||
} |
||||
#components-layout-demo-basic .ant-layout-footer { |
||||
line-height: 1.5; |
||||
} |
||||
#components-layout-demo-basic .ant-layout-sider { |
||||
background: #3ba0e9; |
||||
color: #fff; |
||||
line-height: 120px; |
||||
} |
||||
#components-layout-demo-basic .ant-layout-content { |
||||
background: rgba(16, 142, 233, 1); |
||||
color: #fff; |
||||
min-height: 120px; |
||||
line-height: 120px; |
||||
} |
||||
#components-layout-demo-basic > .ant-layout { |
||||
margin-bottom: 48px; |
||||
} |
||||
#components-layout-demo-basic > .ant-layout:last-child { |
||||
margin: 0; |
||||
} |
||||
</style> |
||||
``` |
@ -0,0 +1,77 @@
|
||||
<cn> |
||||
#### 自定义触发器 |
||||
要使用自定义触发器,可以设置 `:trigger="null"` 来隐藏默认设定。 |
||||
</cn> |
||||
|
||||
<us> |
||||
#### Custom trigger |
||||
If you want to use a customized trigger, you can hide the default one by setting `:trigger="null"`. |
||||
</us> |
||||
|
||||
```html |
||||
<template> |
||||
<a-layout id="components-layout-demo-custom-trigger"> |
||||
<a-layout-sider |
||||
:trigger="null" |
||||
collapsible |
||||
v-model="collapsed" |
||||
> |
||||
<div class="logo" /> |
||||
<a-menu theme="dark" mode="inline" defaultSelectedKeys={['1']}> |
||||
<a-menu-item key="1"> |
||||
<a-icon type="user" /> |
||||
<span>nav 1</span> |
||||
</a-menu-item> |
||||
<a-menu-item key="2"> |
||||
<a-icon type="video-camera" /> |
||||
<span>nav 2</span> |
||||
</a-menu-item> |
||||
<a-menu-item key="3"> |
||||
<a-icon type="upload" /> |
||||
<span>nav 3</span> |
||||
</a-menu-item> |
||||
</a-menu> |
||||
</a-layout-sider> |
||||
<a-layout> |
||||
<a-header style="background: #fff; padding: 0"> |
||||
<a-icon |
||||
class="trigger" |
||||
:type="collapsed ? 'menu-unfold' : 'menu-fold'" |
||||
@click="()=> collapsed = !collapsed" |
||||
/> |
||||
</a-header> |
||||
<a-layout-content :style="{ margin: '24px 16px', padding: '24px', background: '#fff', minHeight: '280px' }"> |
||||
Content |
||||
</a-layout-content> |
||||
</a-layout> |
||||
</a-layout> |
||||
</template> |
||||
<script> |
||||
export default { |
||||
data(){ |
||||
return { |
||||
collapsed: false, |
||||
} |
||||
}, |
||||
} |
||||
</script> |
||||
<style> |
||||
#components-layout-demo-custom-trigger .trigger { |
||||
font-size: 18px; |
||||
line-height: 64px; |
||||
padding: 0 24px; |
||||
cursor: pointer; |
||||
transition: color .3s; |
||||
} |
||||
|
||||
#components-layout-demo-custom-trigger .trigger:hover { |
||||
color: #1890ff; |
||||
} |
||||
|
||||
#components-layout-demo-custom-trigger .logo { |
||||
height: 32px; |
||||
background: rgba(255,255,255,.2); |
||||
margin: 16px; |
||||
} |
||||
</style> |
||||
``` |
@ -0,0 +1,113 @@
|
||||
--- |
||||
category: Components |
||||
type: Layout |
||||
cols: 1 |
||||
title: Layout |
||||
--- |
||||
|
||||
Handling the overall layout of a page. |
||||
|
||||
## Specification |
||||
|
||||
### Size |
||||
|
||||
The first level navigation is inclined left near a logo, and the secondary menu is inclined right. |
||||
|
||||
- Top Navigation (almost systems): the height of the first level navigation `64px`, the second level navigation `48px`. |
||||
- Top Navigation(contents page): the height of the first level navigation `80px`, the second level navigation `56px`. |
||||
- Calculation formula of a top navigation: `48+8n`. |
||||
- Calculation formula of an aside navigation: `200+8n`. |
||||
|
||||
### Interaction rules |
||||
|
||||
- The first level navigation and the last level navigation should be distincted by visualization; |
||||
- The current item should have the highest priority of visualization; |
||||
- When the current navigation item is collapsed, the stlye of the current navigation item will be applied to its parent level; |
||||
- The left side navigation bar has support for both the accordion and expanding styles, you can choose the one that fits your case best. |
||||
|
||||
## Visualization rules |
||||
|
||||
Style of a navigation should conform to its level. |
||||
|
||||
- **Emphasis by colorblock** |
||||
|
||||
When background color is a deep color, you can use this pattern for the parent level navigation item of current page. |
||||
|
||||
- **The highlight match stick** |
||||
|
||||
When background color is a light color, you can use this pattern for the current page navigation item, we recommed using it for the last item of the navigation path. |
||||
|
||||
- **Hightlighted font** |
||||
|
||||
From the visualization aspect, hightlighted font is stronger than colorblock, this pattern is often used for the parent level of the current item. |
||||
|
||||
- **Enlarge the size of the font** |
||||
|
||||
`12px`、`14px` is a standard font size of navigations,`14px` is used for the first and the second level of the navigation. You can choose a appropriate font size in terms of the level of your navigation. |
||||
|
||||
## Component Overview |
||||
|
||||
- `Layout`: The layout wrapper, in which `Header` `Sider` `Content` `Footer` or `Layout` itself can be nested, and can be placed in any parent container. |
||||
- `Header`: The top layout with default style, in which any element can be nested, and must be placed in `Layout`. |
||||
- `Sider`: The sidebar with default style and basic functions, in which any element can be nested, and must be placed in `Layout`. |
||||
- `Content`: The content layout with default style, in which any element can be nested, and must be placed in `Layout`. |
||||
- `Footer`: The bottom layout with default style, in which any element can be nested, and must be placed in `Layout`. |
||||
|
||||
> Based on `flex layout`, please pay attention to the [compatibility](http://caniuse.com/#search=flex). |
||||
|
||||
## API |
||||
|
||||
```jsx |
||||
<Layout> |
||||
<Header>header</Header> |
||||
<Layout> |
||||
<Sider>left sidebar</Sider> |
||||
<Content>main content</Content> |
||||
<Sider>right sidebar</Sider> |
||||
</Layout> |
||||
<Footer>footer</Footer> |
||||
</Layout> |
||||
``` |
||||
|
||||
### Layout |
||||
|
||||
The wrapper. |
||||
|
||||
| Property | Description | Type | Default | |
||||
| -------- | ----------- | ---- | ------- | |
||||
| className | container className | string | - | |
||||
| style | to customize the styles | object | - | |
||||
| hasSider | whether contain Sider in children, don't have to assign it normally. Useful in ssr avoid style flickering | boolean | - | |
||||
|
||||
> APIs of `Layout.Header` `Layout.Footer` `Layout.Content` are the same as that of `Layout`. |
||||
|
||||
### Layout.Sider |
||||
|
||||
The sidebar. |
||||
|
||||
| Property | Description | Type | Default | |
||||
| -------- | ----------- | ---- | ------- | |
||||
| breakpoint | [breakpoints](/components/grid#api) of the responsive layout | Enum { 'xs', 'sm', 'md', 'lg', 'xl', 'xxl' } | - | |
||||
| className | container className | string | - | |
||||
| collapsed | to set the current status | boolean | - | |
||||
| collapsedWidth | width of the collapsed sidebar, by setting to `0` a special trigger will appear | number | 64 | |
||||
| collapsible | whether can be collapsed | boolean | false | |
||||
| defaultCollapsed | to set the initial status | boolean | false | |
||||
| reverseArrow | reverse direction of arrow, for a sider that expands from the right | boolean | false | |
||||
| style | to customize the styles | object | - | |
||||
| trigger | specify the customized trigger, set to null to hide the trigger | string\|ReactNode | - | |
||||
| width | width of the sidebar | number\|string | 200 | |
||||
| onCollapse | the callback function, executed by clicking the trigger or activating the responsive layout | (collapsed, type) => {} | - | |
||||
|
||||
#### breakpoint width |
||||
|
||||
```js |
||||
{ |
||||
xs: '480px', |
||||
sm: '576px', |
||||
md: '768px', |
||||
lg: '992px', |
||||
xl: '1200px', |
||||
xxl: '1600px', |
||||
} |
||||
``` |
@ -0,0 +1,5 @@
|
||||
import Layout from './layout' |
||||
import Sider from './Sider' |
||||
|
||||
Layout.Sider = Sider |
||||
export default Layout |
@ -0,0 +1,114 @@
|
||||
--- |
||||
category: Components |
||||
subtitle: 布局 |
||||
type: Layout |
||||
cols: 1 |
||||
title: Layout |
||||
--- |
||||
|
||||
协助进行页面级整体布局。 |
||||
|
||||
## 设计规则 |
||||
|
||||
### 尺寸 |
||||
|
||||
一级导航项偏左靠近 logo 放置,辅助菜单偏右放置。 |
||||
|
||||
- 顶部导航(大部分系统):一级导航高度 `64px`,二级导航 `48px`。 |
||||
- 顶部导航(展示类页面):一级导航高度 `80px`,二级导航 `56px`。 |
||||
- 顶部导航高度的范围计算公式为:`48+8n`。 |
||||
- 侧边导航宽度的范围计算公式:`200+8n`。 |
||||
|
||||
### 交互 |
||||
|
||||
- 一级导航和末级的导航需要在可视化的层面被强调出来; |
||||
- 当前项应该在呈现上优先级最高; |
||||
- 当导航收起的时候,当前项的样式自动赋予给它的上一个层级; |
||||
- 左侧导航栏的收放交互同时支持手风琴和全展开的样式,根据业务的要求进行适当的选择。 |
||||
|
||||
### 视觉 |
||||
|
||||
导航样式上需要根据信息层级合理的选择样式: |
||||
|
||||
- **大色块强调** |
||||
|
||||
建议用于底色为深色系时,当前页面父级的导航项。 |
||||
|
||||
- **高亮火柴棍** |
||||
|
||||
当导航栏底色为浅色系时使用,可用于当前页面对应导航项,建议尽量在导航路径的最终项使用。 |
||||
|
||||
- **字体高亮变色** |
||||
|
||||
从可视化层面,字体高亮的视觉强化力度低于大色块,通常在当前项的上一级使用。 |
||||
|
||||
- **字体放大** |
||||
|
||||
`12px`、`14px` 是导航的标准字号,14 号字体用在一、二级导航中。字号可以考虑导航项的等级做相应选择。 |
||||
|
||||
## 组件概述 |
||||
|
||||
- `Layout`:布局容器,其下可嵌套 `Header` `Sider` `Content` `Footer` 或 `Layout` 本身,可以放在任何父容器中。 |
||||
- `Header`:顶部布局,自带默认样式,其下可嵌套任何元素,只能放在 `Layout` 中。 |
||||
- `Sider`:侧边栏,自带默认样式及基本功能,其下可嵌套任何元素,只能放在 `Layout` 中。 |
||||
- `Content`:内容部分,自带默认样式,其下可嵌套任何元素,只能放在 `Layout` 中。 |
||||
- `Footer`:底部布局,自带默认样式,其下可嵌套任何元素,只能放在 `Layout` 中。 |
||||
|
||||
> 注意:采用 flex 布局实现,请注意[浏览器兼容性](http://caniuse.com/#search=flex)问题。 |
||||
|
||||
## API |
||||
|
||||
```jsx |
||||
<Layout> |
||||
<Header>header</Header> |
||||
<Layout> |
||||
<Sider>left sidebar</Sider> |
||||
<Content>main content</Content> |
||||
<Sider>right sidebar</Sider> |
||||
</Layout> |
||||
<Footer>footer</Footer> |
||||
</Layout> |
||||
``` |
||||
|
||||
### Layout |
||||
|
||||
布局容器。 |
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | |
||||
| --- | --- | --- | --- | |
||||
| class | 容器 class | string | - | |
||||
| style | 指定样式 | object | - | |
||||
| hasSider | 表示子元素里有 Sider,一般不用指定。可用于服务端渲染时避免样式闪动 | boolean | - | |
||||
|
||||
> `Layout.Header` `Layout.Footer` `Layout.Content` API 与 `Layout` 相同 |
||||
|
||||
### Layout.Sider |
||||
|
||||
侧边栏。 |
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 | |
||||
| --- | --- | --- | --- | |
||||
| breakpoint | 触发响应式布局的[断点](/components/grid#api) | Enum { 'xs', 'sm', 'md', 'lg', 'xl', 'xxl' } | - | |
||||
| class | 容器 class | string | - | |
||||
| collapsed | 当前收起状态 | boolean | - | |
||||
| collapsedWidth | 收缩宽度,设置为 0 会出现特殊 trigger | number | 64 | |
||||
| collapsible | 是否可收起 | boolean | false | |
||||
| defaultCollapsed | 是否默认收起 | boolean | false | |
||||
| reverseArrow | 翻转折叠提示箭头的方向,当 Sider 在右边时可以使用 | boolean | false | |
||||
| style | 指定样式 | object | - | |
||||
| trigger | 自定义 trigger,设置为 null 时隐藏 trigger | string\|slot | - | |
||||
| width | 宽度 | number\|string | 200 | |
||||
| onCollapse | 展开-收起时的回调函数,有点击 trigger 以及响应式反馈两种方式可以触发 | (collapsed, type) => {} | - | |
||||
|
||||
#### breakpoint width |
||||
|
||||
```js |
||||
{ |
||||
xs: '480px', |
||||
sm: '576px', |
||||
md: '768px', |
||||
lg: '992px', |
||||
xl: '1200px', |
||||
xxl: '1600px', |
||||
} |
||||
``` |
@ -0,0 +1,98 @@
|
||||
import PropTypes from '../_util/vue-types' |
||||
import classNames from 'classnames' |
||||
import { getOptionProps } from '../_util/props-util' |
||||
|
||||
export const BasicProps = { |
||||
prefixCls: PropTypes.string, |
||||
hasSider: PropTypes.boolean, |
||||
} |
||||
|
||||
function generator (props, name) { |
||||
return (BasicComponent) => { |
||||
return { |
||||
name, |
||||
props: BasicComponent.props, |
||||
render () { |
||||
const { prefixCls } = props |
||||
const basicComponentProps = { |
||||
props: { |
||||
prefixCls, |
||||
...getOptionProps(this), |
||||
}, |
||||
on: this.$listeners, |
||||
} |
||||
return <BasicComponent {...basicComponentProps}>{this.$slots.default}</BasicComponent> |
||||
}, |
||||
} |
||||
} |
||||
} |
||||
|
||||
const Basic = { |
||||
props: BasicProps, |
||||
render () { |
||||
const { prefixCls, $slots, $listeners } = this |
||||
const divProps = { |
||||
class: prefixCls, |
||||
on: $listeners, |
||||
} |
||||
return ( |
||||
<div {...divProps}>{$slots.default}</div> |
||||
) |
||||
}, |
||||
} |
||||
|
||||
const BasicLayout = { |
||||
props: BasicProps, |
||||
data () { |
||||
return { |
||||
siders: [], |
||||
} |
||||
}, |
||||
provide () { |
||||
return { |
||||
siderHook: { |
||||
addSider: (id) => { |
||||
this.siders = [...this.siders, id] |
||||
}, |
||||
removeSider: (id) => { |
||||
this.siders = this.siders.filter(currentId => currentId !== id) |
||||
}, |
||||
}, |
||||
} |
||||
}, |
||||
render () { |
||||
const { prefixCls, $slots, hasSider, $listeners } = this |
||||
const divCls = classNames(prefixCls, { |
||||
[`${prefixCls}-has-sider`]: hasSider || this.siders.length > 0, |
||||
}) |
||||
const divProps = { |
||||
class: divCls, |
||||
on: $listeners, |
||||
} |
||||
return ( |
||||
<div {...divProps}>{$slots.default}</div> |
||||
) |
||||
}, |
||||
} |
||||
|
||||
const Layout = generator({ |
||||
prefixCls: 'ant-layout', |
||||
}, 'ALayout')(BasicLayout) |
||||
|
||||
const Header = generator({ |
||||
prefixCls: 'ant-layout-header', |
||||
}, 'ALayoutHeader')(Basic) |
||||
|
||||
const Footer = generator({ |
||||
prefixCls: 'ant-layout-footer', |
||||
}, 'ALayoutFooter')(Basic) |
||||
|
||||
const Content = generator({ |
||||
prefixCls: 'ant-layout-content', |
||||
}, 'ALayoutContent')(Basic) |
||||
|
||||
Layout.Header = Header |
||||
Layout.Footer = Footer |
||||
Layout.Content = Content |
||||
|
||||
export default Layout |
@ -0,0 +1,2 @@
|
||||
import '../../style/index.less' |
||||
import './index.less' |
@ -0,0 +1,112 @@
|
||||
@import "../../style/themes/default"; |
||||
@import "../../style/mixins/index"; |
||||
|
||||
@layout-prefix-cls: ~"@{ant-prefix}-layout"; |
||||
|
||||
.@{layout-prefix-cls} { |
||||
display: flex; |
||||
flex-direction: column; |
||||
flex: auto; |
||||
background: @layout-body-background; |
||||
|
||||
&, |
||||
* { |
||||
box-sizing: border-box; |
||||
} |
||||
|
||||
&&-has-sider { |
||||
flex-direction: row; |
||||
> .@{layout-prefix-cls}, |
||||
> .@{layout-prefix-cls}-content { |
||||
overflow-x: hidden; |
||||
} |
||||
} |
||||
|
||||
&-header, |
||||
&-footer { |
||||
flex: 0 0 auto; |
||||
} |
||||
|
||||
&-header { |
||||
background: @layout-header-background; |
||||
padding: @layout-header-padding; |
||||
height: @layout-header-height; |
||||
line-height: @layout-header-height; |
||||
} |
||||
|
||||
&-footer { |
||||
background: @layout-footer-background; |
||||
padding: @layout-footer-padding; |
||||
color: @text-color; |
||||
font-size: @font-size-base; |
||||
} |
||||
|
||||
&-content { |
||||
flex: auto; |
||||
} |
||||
|
||||
&-sider { |
||||
transition: all .2s; |
||||
position: relative; |
||||
background: @layout-sider-background; |
||||
|
||||
/* fix firefox can't set width smaller than content on flex item */ |
||||
min-width: 0; |
||||
|
||||
&-children { |
||||
height: 100%; |
||||
// Hack for fixing margin collaspe bug |
||||
// https://github.com/ant-design/ant-design/issues/7967 |
||||
// solution from https://stackoverflow.com/a/33132624/3040605 |
||||
padding-top: 0.1px; |
||||
margin-top: -0.1px; |
||||
} |
||||
|
||||
&-has-trigger { |
||||
padding-bottom: @layout-trigger-height; |
||||
} |
||||
|
||||
&-right { |
||||
order: 1; |
||||
} |
||||
|
||||
&-trigger { |
||||
position: fixed; |
||||
text-align: center; |
||||
bottom: 0; |
||||
cursor: pointer; |
||||
height: @layout-trigger-height; |
||||
line-height: @layout-trigger-height; |
||||
color: @layout-trigger-color; |
||||
background: @layout-trigger-background; |
||||
z-index: 1; |
||||
transition: all .2s; |
||||
} |
||||
|
||||
&-zero-width { |
||||
& > * { |
||||
overflow: hidden; |
||||
} |
||||
|
||||
&-trigger { |
||||
position: absolute; |
||||
top: @layout-header-height; |
||||
right: -@layout-zero-trigger-width; |
||||
text-align: center; |
||||
width: @layout-zero-trigger-width; |
||||
height: @layout-zero-trigger-height; |
||||
line-height: @layout-zero-trigger-height; |
||||
background: @layout-sider-background; |
||||
color: @layout-trigger-color; |
||||
font-size: @layout-zero-trigger-width / 2; |
||||
border-radius: 0 @border-radius-base @border-radius-base 0; |
||||
cursor: pointer; |
||||
transition: background .3s ease; |
||||
|
||||
&:hover { |
||||
background: tint(@layout-sider-background, 10%); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue