add layout component with header, content, sider and footer

This commit is contained in:
zhigang.li 2017-12-18 18:25:16 +08:00
parent e6508e277f
commit a2eb028782
17 changed files with 509 additions and 4 deletions

View file

@ -0,0 +1,28 @@
<template>
<div :class="wrapClasses"><slot></slot></div>
</template>
<script>
const prefixCls = 'ivu-layout';
export default {
name: 'Content',
props: {
className: {
type: String,
default: ''
}
},
data () {
return {
prefixCls: prefixCls
};
},
computed: {
wrapClasses () {
return [
`${prefixCls}-content`,
this.className
];
}
}
};
</script>

View file

@ -0,0 +1,28 @@
<template>
<div :class="wrapClasses"><slot></slot></div>
</template>
<script>
const prefixCls = 'ivu-layout';
export default {
name: 'Footer',
props: {
className: {
type: String,
default: ''
}
},
data () {
return {
prefixCls: prefixCls
};
},
computed: {
wrapClasses () {
return [
`${prefixCls}-footer`,
this.className
];
}
}
};
</script>

View file

@ -0,0 +1,28 @@
<template>
<div :class="wrapClasses"><slot></slot></div>
</template>
<script>
const prefixCls = 'ivu-layout';
export default {
name: 'Header',
props: {
className: {
type: String,
default: ''
}
},
data () {
return {
prefixCls: prefixCls
};
},
computed: {
wrapClasses () {
return [
`${prefixCls}-header`,
this.className
];
}
}
};
</script>

View file

@ -0,0 +1,12 @@
import Layout from './layout.vue';
import Header from './header.vue';
import Sider from './sider.vue';
import Content from './content.vue';
import Footer from './footer.vue';
Layout.Header = Header;
Layout.Sider = Sider;
Layout.Content = Content;
Layout.Footer = Footer;
export default Layout;

View file

@ -0,0 +1,43 @@
<template>
<div :class="wrapClasses"><slot></slot></div>
</template>
<script>
const prefixCls = 'ivu-layout';
export default {
name: 'Layout',
props: {
className: {
type: String,
default: ''
}
},
data () {
return {
prefixCls: prefixCls,
hasSider: false
};
},
computed: {
wrapClasses () {
return [
`${prefixCls}`,
this.className,
{
[`${prefixCls}-has-sider`]: this.hasSider
}
];
}
},
methods: {
findSider () {
return this.$children.some(child => {
return child.$options._componentTag === 'Sider';
});
}
},
mounted () {
this.hasSider = this.findSider();
}
};
</script>

View file

@ -0,0 +1,150 @@
<template>
<div
:class="wrapClasses"
:style="{width: siderWidth + 'px', minWidth: siderWidth + 'px', maxWidth: siderWidth + 'px', flex: '0 0 ' + siderWidth + 'px'}">
<span v-show="showZeroTrigger" @click="toggleCollapse" :class="zeroWidthTriggerClasses">
<i class="ivu-icon ivu-icon-navicon-round"></i>
</span>
<div :class="`${prefixCls}-children`">
<slot></slot>
</div>
<div v-show="!mediaMatched && !hideTrigger" :class="triggerClasses" @click="toggleCollapse" :style="{width: siderWidth + 'px'}">
<i :class="triggerIconClasses"></i>
</div>
</div>
</template>
<script>
import { on, off } from '../../utils/dom';
import { oneOf } from '../../utils/assist';
const prefixCls = 'ivu-layout-sider';
const dimensionMap = {
xs: '480px',
sm: '768px',
md: '992px',
lg: '1200px',
xl: '1600px',
};
if (typeof window !== 'undefined') {
const matchMediaPolyfill = mediaQuery => {
return {
media: mediaQuery,
matches: false,
on() {
},
off() {
},
};
};
window.matchMedia = window.matchMedia || matchMediaPolyfill;
}
export default {
name: 'Sider',
props: {
value: { // if it's collpased now
type: Boolean,
default: false
},
className: {
type: String,
default: ''
},
width: {
type: [Number, String],
default: 200
},
collapsedWidth: {
type: [Number, String],
default: 64
},
hideTrigger: {
type: Boolean,
default: false
},
breakpoint: {
type: String,
default: '',
validator (val) {
return oneOf(val, ['xs', 'sm', 'md', 'lg', 'xl']);
}
},
defaultCollapsed: {
type: Boolean,
default: false
},
reverseArrow: {
type: Boolean,
default: false
}
},
data () {
return {
prefixCls: prefixCls,
mediaMatched: false
};
},
computed: {
wrapClasses () {
return [
`${prefixCls}`,
this.className,
this.siderWidth ? '' : `${prefixCls}-zero-width`,
this.value ? `${prefixCls}-collapsed` : ''
];
},
triggerClasses () {
return [
`${prefixCls}-trigger`,
this.value ? `${prefixCls}-trigger-collapsed` : '',
];
},
zeroWidthTriggerClasses () {
return [
`${prefixCls}-zero-width-trigger`,
this.reverseArrow ? `${prefixCls}-zero-width-trigger-left` : ''
];
},
triggerIconClasses () {
return [
'ivu-icon',
`ivu-icon-chevron-${this.reverseArrow ? 'right' : 'left'}`,
`${prefixCls}-trigger-icon`,
];
},
siderWidth () {
return this.value ? (this.mediaMatched ? 0 : parseInt(this.collapsedWidth)) : parseInt(this.width);
},
showZeroTrigger () {
return this.mediaMatched && !this.hideTrigger || (parseInt(this.collapsedWidth) === 0) && this.value && !this.hideTrigger;
}
},
methods: {
toggleCollapse () {
this.$emit('input', !this.value);
this.$emit('on-collapse', !this.value);
},
matchMedia () {
let matchMedia;
if (window.matchMedia) {
matchMedia = window.matchMedia;
}
let mediaMatched = this.mediaMatched;
this.mediaMatched = matchMedia(`(max-width: ${dimensionMap[this.breakpoint]})`).matches;
if (this.mediaMatched !== mediaMatched) {
this.$emit('input', this.mediaMatched);
this.$emit('on-collapse', this.mediaMatched);
}
},
onWindowResize () {
this.matchMedia();
}
},
mounted () {
on(window, 'resize', this.onWindowResize);
this.matchMedia();
this.$emit('input', this.defaultCollapsed);
},
destroyed () {
off(window, 'resize', this.onWindowResize);
}
};
</script>