mirror of
https://gitee.com/element-plus/element-plus.git
synced 2024-11-30 02:08:12 +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 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)
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
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