Skip to content

Commit 4adc7a8

Browse files
committed
add layout
1 parent 8482e28 commit 4adc7a8

File tree

14 files changed

+861
-16
lines changed

14 files changed

+861
-16
lines changed

components/index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import { default as Input } from './input'
5858

5959
import { default as InputNumber } from './input-number'
6060

61-
// import { default as Layout } from './layout'
61+
import { default as Layout } from './layout'
6262

6363
// import { default as List } from './list'
6464

@@ -154,6 +154,11 @@ const components = [
154154
Input.Search,
155155
Input.TextArea,
156156
InputNumber,
157+
Layout,
158+
Layout.Header,
159+
Layout.Footer,
160+
Layout.Sider,
161+
Layout.Content,
157162
LocaleProvider,
158163
Menu,
159164
Menu.Item,
@@ -235,6 +240,7 @@ export {
235240
Icon,
236241
Input,
237242
InputNumber,
243+
Layout,
238244
LocaleProvider,
239245
Menu,
240246
Modal,

components/layout/Sider.jsx

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
// matchMedia polyfill for
2+
// https://github.com/WickyNilliams/enquire.js/issues/82
3+
if (typeof window !== 'undefined') {
4+
const matchMediaPolyfill = (mediaQuery) => {
5+
return {
6+
media: mediaQuery,
7+
matches: false,
8+
addListener () {
9+
},
10+
removeListener () {
11+
},
12+
}
13+
}
14+
window.matchMedia = window.matchMedia || matchMediaPolyfill
15+
}
16+
17+
import classNames from 'classnames'
18+
import PropTypes from '../_util/vue-types'
19+
import Icon from '../icon'
20+
import { initDefaultProps, getOptionProps, hasProp, getComponentFromProp } from '../_util/props-util'
21+
import BaseMixin from '../_util/BaseMixin'
22+
23+
const dimensionMap = {
24+
xs: '480px',
25+
sm: '576px',
26+
md: '768px',
27+
lg: '992px',
28+
xl: '1200px',
29+
xxl: '1600px',
30+
}
31+
32+
// export type CollapseType = 'clickTrigger' | 'responsive';
33+
34+
export const SiderProps = {
35+
prefixCls: PropTypes.string,
36+
collapsible: PropTypes.bool,
37+
collapsed: PropTypes.bool,
38+
defaultCollapsed: PropTypes.bool,
39+
reverseArrow: PropTypes.bool,
40+
// onCollapse?: (collapsed: boolean, type: CollapseType) => void;
41+
trigger: PropTypes.any,
42+
width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
43+
collapsedWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
44+
breakpoint: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl', 'xxl']),
45+
}
46+
47+
// export interface SiderState {
48+
// collapsed?: boolean;
49+
// below: boolean;
50+
// belowShow?: boolean;
51+
// }
52+
53+
// export interface SiderContext {
54+
// siderCollapsed: boolean;
55+
// }
56+
57+
const generateId = (() => {
58+
let i = 0
59+
return (prefix = '') => {
60+
i += 1
61+
return `${prefix}${i}`
62+
}
63+
})()
64+
65+
export default {
66+
name: 'ALayoutSider',
67+
__ANT_LAYOUT_SIDER: true,
68+
mixins: [BaseMixin],
69+
props: initDefaultProps(SiderProps, {
70+
prefixCls: 'ant-layout-sider',
71+
collapsible: false,
72+
defaultCollapsed: false,
73+
reverseArrow: false,
74+
width: 200,
75+
collapsedWidth: 80,
76+
}),
77+
78+
// static childContextTypes = {
79+
// siderCollapsed: PropTypes.bool,
80+
// collapsedWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
81+
// };
82+
83+
// static contextTypes = {
84+
// siderHook: PropTypes.object,
85+
// };
86+
87+
// private mql: MediaQueryList;
88+
// private uniqueId: string;
89+
90+
data () {
91+
this.uniqueId = generateId('ant-sider-')
92+
let matchMedia
93+
if (typeof window !== 'undefined') {
94+
matchMedia = window.matchMedia
95+
}
96+
const props = getOptionProps(this)
97+
if (matchMedia && props.breakpoint && props.breakpoint in dimensionMap) {
98+
this.mql = matchMedia(`(max-width: ${dimensionMap[props.breakpoint]})`)
99+
}
100+
let sCollapsed
101+
if ('collapsed' in props) {
102+
sCollapsed = props.collapsed
103+
} else {
104+
sCollapsed = props.defaultCollapsed
105+
}
106+
return {
107+
sCollapsed,
108+
below: false,
109+
belowShow: false,
110+
}
111+
},
112+
provide () {
113+
return {
114+
layoutSiderContext: this, // menu组件中使用
115+
}
116+
},
117+
inject: {
118+
siderHook: { default: {}},
119+
},
120+
// getChildContext() {
121+
// return {
122+
// siderCollapsed: this.state.collapsed,
123+
// collapsedWidth: this.props.collapsedWidth,
124+
// };
125+
// }
126+
watch: {
127+
collapsed (val) {
128+
this.setState({
129+
sCollapsed: val,
130+
})
131+
},
132+
},
133+
134+
mounted () {
135+
this.$nextTick(() => {
136+
if (this.mql) {
137+
this.mql.addListener(this.responsiveHandler)
138+
this.responsiveHandler(this.mql)
139+
}
140+
141+
if (this.siderHook.addSider) {
142+
this.siderHook.addSider(this.uniqueId)
143+
}
144+
})
145+
},
146+
147+
beforeDestroy () {
148+
if (this.mql) {
149+
this.mql.removeListener(this.responsiveHandler)
150+
}
151+
152+
if (this.siderHook.removeSider) {
153+
this.siderHook.removeSider(this.uniqueId)
154+
}
155+
},
156+
model: {
157+
prop: 'collapsed',
158+
event: 'collapse',
159+
},
160+
methods: {
161+
responsiveHandler (mql) {
162+
this.setState({ below: mql.matches })
163+
if (this.sCollapsed !== mql.matches) {
164+
this.setCollapsed(mql.matches, 'responsive')
165+
}
166+
},
167+
168+
setCollapsed (collapsed, type) {
169+
if (!hasProp(this, 'collapsed')) {
170+
this.setState({
171+
sCollapsed: collapsed,
172+
})
173+
}
174+
this.$emit('collapse', collapsed, type)
175+
},
176+
177+
toggle () {
178+
const collapsed = !this.sCollapsed
179+
this.setCollapsed(collapsed, 'clickTrigger')
180+
},
181+
182+
belowShowChange () {
183+
this.setState({ belowShow: !this.belowShow })
184+
},
185+
},
186+
187+
render () {
188+
const { prefixCls,
189+
collapsible, reverseArrow, width, collapsedWidth,
190+
} = getOptionProps(this)
191+
const trigger = getComponentFromProp(this, 'trigger')
192+
let siderWidth = this.sCollapsed ? collapsedWidth : width
193+
siderWidth = typeof siderWidth === 'string' ? siderWidth.replace('px', '') : siderWidth
194+
// special trigger when collapsedWidth == 0
195+
const zeroWidthTrigger = collapsedWidth === 0 || collapsedWidth === '0' || collapsedWidth === '0px' ? (
196+
<span onClick={this.toggle} class={`${prefixCls}-zero-width-trigger`}>
197+
<Icon type='bars' />
198+
</span>
199+
) : null
200+
const iconObj = {
201+
'expanded': reverseArrow ? <Icon type='right' /> : <Icon type='left' />,
202+
'collapsed': reverseArrow ? <Icon type='left' /> : <Icon type='right' />,
203+
}
204+
const status = this.sCollapsed ? 'collapsed' : 'expanded'
205+
const defaultTrigger = iconObj[status]
206+
const triggerDom = (
207+
trigger !== null
208+
? zeroWidthTrigger || (
209+
<div className={`${prefixCls}-trigger`} onClick={this.toggle} style={{ width: `${siderWidth}px` }}>
210+
{trigger || defaultTrigger}
211+
</div>
212+
) : null
213+
)
214+
const divStyle = {
215+
// ...style,
216+
flex: `0 0 ${siderWidth}px`,
217+
maxWidth: `${siderWidth}px`, // Fix width transition bug in IE11
218+
minWidth: `${siderWidth}px`, // https://github.com/ant-design/ant-design/issues/6349
219+
width: `${siderWidth}px`,
220+
}
221+
const siderCls = classNames(prefixCls, {
222+
[`${prefixCls}-collapsed`]: !!this.sCollapsed,
223+
[`${prefixCls}-has-trigger`]: collapsible && trigger !== null && !zeroWidthTrigger,
224+
[`${prefixCls}-below`]: !!this.below,
225+
[`${prefixCls}-zero-width`]: siderWidth === 0 || siderWidth === '0' || siderWidth === '0px',
226+
})
227+
const divProps = {
228+
on: this.$listeners,
229+
class: siderCls,
230+
style: divStyle,
231+
}
232+
return (
233+
<div {...divProps}>
234+
<div class={`${prefixCls}-children`}>{this.$slots.default}</div>
235+
{collapsible || (this.below && zeroWidthTrigger) ? triggerDom : null}
236+
</div>
237+
)
238+
},
239+
}

components/layout/demo/basic.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<cn>
2+
#### 基本结构
3+
典型的页面布局。
4+
</cn>
5+
6+
<us>
7+
#### Basic Structure
8+
Classic page layouts.
9+
</us>
10+
11+
```html
12+
<template>
13+
<div id="components-layout-demo-basic">
14+
<a-layout>
15+
<a-layout-header>Header</a-layout-header>
16+
<a-layout-content>Content</a-layout-content>
17+
<a-layout-footer>Footer</a-layout-footer>
18+
</a-layout>
19+
20+
<a-layout>
21+
<a-layout-header>Header</a-layout-header>
22+
<a-layout>
23+
<a-layout-sider>Sider</a-layout-sider>
24+
<a-layout-content>Content</a-layout-content>
25+
</a-layout>
26+
<a-layout-footer>Footer</a-layout-footer>
27+
</a-layout>
28+
29+
<a-layout>
30+
<a-layout-header>Header</a-layout-header>
31+
<a-layout>
32+
<a-layout-content>Content</a-layout-content>
33+
<a-layout-sider>Sider</a-layout-sider>
34+
</a-layout>
35+
<a-layout-footer>Footer</a-layout-footer>
36+
</a-layout>
37+
38+
<a-layout>
39+
<a-layout-sider>Sider</a-layout-sider>
40+
<a-layout>
41+
<a-layout-header>Header</a-layout-header>
42+
<a-layout-content>Content</a-layout-content>
43+
<a-layout-footer>Footer</a-layout-footer>
44+
</a-layout>
45+
</a-layout>
46+
</div>
47+
</template>
48+
49+
<style>
50+
#components-layout-demo-basic {
51+
text-align: center;
52+
}
53+
#components-layout-demo-basic .ant-layout-header,
54+
#components-layout-demo-basic .ant-layout-footer {
55+
background: #7dbcea;
56+
color: #fff;
57+
}
58+
#components-layout-demo-basic .ant-layout-footer {
59+
line-height: 1.5;
60+
}
61+
#components-layout-demo-basic .ant-layout-sider {
62+
background: #3ba0e9;
63+
color: #fff;
64+
line-height: 120px;
65+
}
66+
#components-layout-demo-basic .ant-layout-content {
67+
background: rgba(16, 142, 233, 1);
68+
color: #fff;
69+
min-height: 120px;
70+
line-height: 120px;
71+
}
72+
#components-layout-demo-basic > .ant-layout {
73+
margin-bottom: 48px;
74+
}
75+
#components-layout-demo-basic > .ant-layout:last-child {
76+
margin: 0;
77+
}
78+
</style>
79+
```

0 commit comments

Comments
 (0)