mirror of
https://gitee.com/element-plus/element-plus.git
synced 2024-11-29 17:58:08 +08:00
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:
parent
4553871ae4
commit
5fcd416cfb
@ -3,11 +3,18 @@ import ElButton from '@element-plus/button'
|
||||
import ElBadge from '@element-plus/badge'
|
||||
import ElCard from '@element-plus/card'
|
||||
import ElTag from '@element-plus/tag'
|
||||
import ElLayout from '@element-plus/layout'
|
||||
import ElDivider from '@element-plus/divider'
|
||||
import ElTimeLine from '@element-plus/time-line'
|
||||
|
||||
export {
|
||||
ElButton, ElBadge, ElCard, ElDivider, ElTag, ElTimeLine,
|
||||
ElLayout,
|
||||
ElButton,
|
||||
ElBadge,
|
||||
ElCard,
|
||||
ElDivider,
|
||||
ElTag,
|
||||
ElTimeLine,
|
||||
}
|
||||
|
||||
export default function install(app: App): void {
|
||||
@ -15,6 +22,7 @@ export default function install(app: App): void {
|
||||
ElBadge(app)
|
||||
ElCard(app)
|
||||
ElTag(app)
|
||||
ElLayout(app)
|
||||
ElDivider(app)
|
||||
ElTimeLine(app)
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
"dependencies": {
|
||||
"@element-plus/badge": "^0.0.0",
|
||||
"@element-plus/button": "^0.0.0",
|
||||
"@element-plus/layout": "^0.0.0",
|
||||
"@element-plus/card": "^0.0.0",
|
||||
"@element-plus/tag": "^0.0.0",
|
||||
"@element-plus/time-line": "^0.0.0",
|
||||
|
102
packages/layout/__tests__/layout.spec.ts
Normal file
102
packages/layout/__tests__/layout.spec.ts
Normal 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')
|
||||
})
|
||||
})
|
||||
|
59
packages/layout/doc/basic.vue
Normal file
59
packages/layout/doc/basic.vue
Normal 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>
|
||||
|
6
packages/layout/doc/index.stories.ts
Normal file
6
packages/layout/doc/index.stories.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { default as BasicUsage } from './basic.vue'
|
||||
|
||||
export default {
|
||||
title: 'Layout',
|
||||
}
|
||||
|
7
packages/layout/index.ts
Normal file
7
packages/layout/index.ts
Normal 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)
|
||||
}
|
12
packages/layout/package.json
Normal file
12
packages/layout/package.json
Normal 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"
|
||||
}
|
||||
}
|
67
packages/layout/src/col.ts
Normal file
67
packages/layout/src/col.ts
Normal 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
|
47
packages/layout/src/row.ts
Normal file
47
packages/layout/src/row.ts
Normal 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?.(),
|
||||
)
|
||||
},
|
||||
})
|
@ -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
|
Loading…
Reference in New Issue
Block a user