feat: add Layout component (#50)

* feat: add row component

* feat: add col

* test: update test

* refactor: rename to layout

* chore: update
This commit is contained in:
zazzaz 2020-07-31 18:20:03 +08:00 committed by GitHub
parent 4553871ae4
commit 5fcd416cfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 310 additions and 22 deletions

View File

@ -3,11 +3,18 @@ import ElButton from '@element-plus/button'
import ElBadge from '@element-plus/badge' import ElBadge from '@element-plus/badge'
import ElCard from '@element-plus/card' import ElCard from '@element-plus/card'
import ElTag from '@element-plus/tag' import ElTag from '@element-plus/tag'
import ElLayout from '@element-plus/layout'
import ElDivider from '@element-plus/divider' import ElDivider from '@element-plus/divider'
import ElTimeLine from '@element-plus/time-line' import ElTimeLine from '@element-plus/time-line'
export { export {
ElButton, ElBadge, ElCard, ElDivider, ElTag, ElTimeLine, ElLayout,
ElButton,
ElBadge,
ElCard,
ElDivider,
ElTag,
ElTimeLine,
} }
export default function install(app: App): void { export default function install(app: App): void {
@ -15,6 +22,7 @@ export default function install(app: App): void {
ElBadge(app) ElBadge(app)
ElCard(app) ElCard(app)
ElTag(app) ElTag(app)
ElLayout(app)
ElDivider(app) ElDivider(app)
ElTimeLine(app) ElTimeLine(app)
} }

View File

@ -16,6 +16,7 @@
"dependencies": { "dependencies": {
"@element-plus/badge": "^0.0.0", "@element-plus/badge": "^0.0.0",
"@element-plus/button": "^0.0.0", "@element-plus/button": "^0.0.0",
"@element-plus/layout": "^0.0.0",
"@element-plus/card": "^0.0.0", "@element-plus/card": "^0.0.0",
"@element-plus/tag": "^0.0.0", "@element-plus/tag": "^0.0.0",
"@element-plus/time-line": "^0.0.0", "@element-plus/time-line": "^0.0.0",

View File

@ -0,0 +1,102 @@
import { mount } from '@vue/test-utils'
import Col from '../src/col'
import Row from '../src/row'
describe('Col', () => {
it('create', () => {
const wrapper = mount(Col)
expect(wrapper.classes()).toContain('el-col')
})
it('span', () => {
const wrapper = mount(Col, {
props: { span: 12 },
})
expect(wrapper.classes()).toContain('el-col-12')
})
it('pull', () => {
const wrapper = mount(Col, {
props: { span: 12, pull: 3 },
})
expect(wrapper.classes()).toContain('el-col-pull-3')
})
it('push', () => {
const wrapper = mount(Col, {
props: { span: 12, push: 3 },
})
expect(wrapper.classes()).toContain('el-col-push-3')
})
it('gutter', () => {
const TestComponent = {
template: `<el-row :gutter="20">
<el-col :span="12" ref="col"></el-col>
</el-row>`,
components: {
'el-col': Col,
'el-row':Row,
},
}
const wrapper = mount(TestComponent)
const colElm = wrapper.findComponent({ ref: 'col' }).element as HTMLElement
expect(colElm.style.paddingLeft === '10px').toBe(true)
expect(colElm.style.paddingRight === '10px').toBe(true)
})
it('responsive', () => {
const TestComponent = {
template: `<el-row :gutter="20">
<el-col ref="col" :sm="{ span: 4, offset: 2 }" :md="8" :lg="{ span: 6, offset: 3 }">
</el-col>
</el-row>`,
components: {
'el-col': Col,
'el-row':Row,
},
}
const wrapper = mount(TestComponent)
const colElmClass = wrapper.findComponent({ ref: 'col' }).classes()
expect(colElmClass.includes('el-col-sm-4')).toBe(true)
expect(colElmClass.includes('el-col-sm-4')).toBe(true)
expect(colElmClass.includes('el-col-sm-offset-2')).toBe(true)
expect(colElmClass.includes('el-col-lg-6')).toBe(true)
expect(colElmClass.includes('el-col-lg-offset-3')).toBe(true)
expect(colElmClass.includes('el-col-md-8')).toBe(true)
})
})
describe('Row', () => {
test('create', () => {
const wrapper = mount(Row)
expect(wrapper.classes()).toContain('el-row')
})
test('gutter', () => {
const wrapper = mount(Row, {
props: { gutter: 20 },
})
const rowElm = wrapper.element as HTMLElement
expect(rowElm.style.marginLeft).toEqual('-10px')
expect(rowElm.style.marginRight).toEqual('-10px')
})
test('type', () => {
const wrapper = mount(Row, {
props: { type: 'flex' },
})
expect(wrapper.classes()).toContain('el-row--flex')
})
test('justify', () => {
const wrapper = mount(Row, {
props: { justify: 'end' },
})
expect(wrapper.classes()).toContain('is-justify-end')
})
test('align', () => {
const wrapper = mount(Row, {
props: { align: 'bottom' },
})
expect(wrapper.classes()).toContain('is-align-bottom')
})
})

View File

@ -0,0 +1,59 @@
<template>
<el-row>
<el-col :span="24"><div class="grid-content bg-purple-dark"></div></el-col>
</el-row>
<el-row>
<el-col :span="12"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="12"><div class="grid-content bg-purple-light"></div></el-col>
</el-row>
<el-row>
<el-col :span="8"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="8"><div class="grid-content bg-purple-light"></div></el-col>
<el-col :span="8"><div class="grid-content bg-purple"></div></el-col>
</el-row>
<el-row>
<el-col :span="6"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="6"><div class="grid-content bg-purple-light"></div></el-col>
<el-col :span="6"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="6"><div class="grid-content bg-purple-light"></div></el-col>
</el-row>
<el-row>
<el-col :span="4"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="4"><div class="grid-content bg-purple-light"></div></el-col>
<el-col :span="4"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="4"><div class="grid-content bg-purple-light"></div></el-col>
<el-col :span="4"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="4"><div class="grid-content bg-purple-light"></div></el-col>
</el-row>
</template>
<style scoped>
.el-row {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
.el-col {
border-radius: 4px;
}
.bg-purple-dark {
background: #99a9bf;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple-light {
background: #e5e9f2;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
.row-bg {
padding: 10px 0;
background-color: #f9fafc;
}
</style>

View File

@ -0,0 +1,6 @@
export { default as BasicUsage } from './basic.vue'
export default {
title: 'Layout',
}

7
packages/layout/index.ts Normal file
View File

@ -0,0 +1,7 @@
import { App } from 'vue'
import Row from './src/row'
import Col from './src/col'
export default (app: App) => {
app.component(Row.name, Row)
app.component(Col.name, Col)
}

View File

@ -0,0 +1,12 @@
{
"name": "@element-plus/layout",
"version": "0.0.0",
"main": "dist/index.js",
"license": "MIT",
"peerDependencies": {
"vue": "^3.0.0-rc.1"
},
"devDependencies": {
"@vue/test-utils": "^2.0.0-beta.0"
}
}

View File

@ -0,0 +1,67 @@
import { defineComponent, computed, inject, h } from 'vue'
const ElCol = defineComponent({
name: 'ElCol',
props: {
span: {
type: Number,
default: 24,
},
offset: Number,
pull: Number,
push: Number,
xs: [Number, Object],
sm: [Number, Object],
md: [Number, Object],
lg: [Number, Object],
xl: [Number, Object],
},
setup(props, { slots }) {
const gutter = inject('ElRow', 0)
const style = computed(() => {
if (gutter) {
return {
paddingLeft: gutter / 2 + 'px',
paddingRight: gutter / 2 + 'px',
}
}
return {}
})
const classList = computed(() => {
const ret: string[] = []
const pos: ['span', 'offset', 'pull', 'push'] = ['span', 'offset', 'pull', 'push']
pos.forEach(prop => {
const size = props[prop]
if (typeof size === 'number' && size >= 0) {
ret.push(prop !== 'span' ? `el-col-${prop}-${props[prop]}` : `el-col-${props[prop]}`)
}
})
const sizes: ['xs', 'sm', 'md', 'lg', 'xl'] = ['xs', 'sm', 'md', 'lg', 'xl']
sizes.forEach(size => {
if (typeof props[size] === 'number') {
ret.push(`el-col-${size}-${props[size]}`)
} else if (typeof props[size] === 'object') {
const sizeProps = props[size]
Object.keys(sizeProps).forEach(prop => {
ret.push(
prop !== 'span' ? `el-col-${size}-${prop}-${sizeProps[prop]}` : `el-col-${size}-${sizeProps[prop]}`,
)
})
}
})
return ret
})
return () =>h(
'div',
{
class: ['el-col', classList.value],
style: style.value,
},
slots.default?.(),
)
},
})
export default ElCol

View File

@ -0,0 +1,47 @@
import { defineComponent, computed, h, provide } from 'vue'
export default defineComponent({
name: 'ElRow',
props: {
tag: {
type: String,
default: 'div',
},
gutter: Number,
type: String,
justify: {
type: String,
default: 'start',
},
align: {
type: String,
default: 'top',
},
},
setup(props, { slots }) {
provide('ElRow', props.gutter)
const style = computed(() => {
const ret = { marginLeft: '', marginRight: '' }
if (props.gutter) {
ret.marginLeft = `-${props.gutter / 2}px`
ret.marginRight = ret.marginLeft
}
return ret
})
return () =>
h(
props.tag,
{
class: [
'el-row',
props.justify !== 'start' ? `is-justify-${props.justify}` : '',
props.align !== 'top' ? `is-align-${props.align}` : '',
props.type === 'flex' ? 'el-row--flex' : '',
],
style: style.value,
},
slots.default?.(),
)
},
})

View File

@ -1,21 +0,0 @@
import { RouteRecordRaw, createRouter, createWebHistory } from 'vue-router'
import Button from '@element-plus/button/doc/index.vue'
import Tag from '@element-plus/tag/doc/index.vue'
const routes: Array<RouteRecordRaw> = [
{
path: '/button',
component: Button,
},
{
path: '/tag',
component: Tag,
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
})
export default router