diff --git a/packages/descriptions-item/index.ts b/packages/descriptions-item/index.ts new file mode 100644 index 0000000000..7a4a3bb794 --- /dev/null +++ b/packages/descriptions-item/index.ts @@ -0,0 +1,11 @@ +import { App } from 'vue' +import type { SFCWithInstall } from '@element-plus/utils/types' +import DescriptionsItem from '../descriptions/src/description-item' + +DescriptionsItem.install = (app: App): void => { + app.component(DescriptionsItem.name, DescriptionsItem) +} + +const _DescriptionsItem: SFCWithInstall = DescriptionsItem + +export default _DescriptionsItem diff --git a/packages/descriptions-item/package.json b/packages/descriptions-item/package.json new file mode 100644 index 0000000000..1f994f8f79 --- /dev/null +++ b/packages/descriptions-item/package.json @@ -0,0 +1,12 @@ +{ + "name": "@element-plus/descriptions-item", + "version": "0.0.0", + "main": "dist/index.js", + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.9" + }, + "devDependencies": { + "@vue/test-utils": "^2.0.0-beta.3" + } +} diff --git a/packages/descriptions/__tests__/descriptions.spec.ts b/packages/descriptions/__tests__/descriptions.spec.ts new file mode 100644 index 0000000000..307377153f --- /dev/null +++ b/packages/descriptions/__tests__/descriptions.spec.ts @@ -0,0 +1,98 @@ +import { mount } from '@vue/test-utils' +import Descriptions from '../src/index.vue' +import DescriptionsItem from '../src/description-item' +import { nextTick } from 'vue' + +const _mount = (template: string, data?: () => void, methods?: any) => + mount({ + components: { + 'el-descriptions': Descriptions, + 'el-descriptions-item': DescriptionsItem, + }, + template, + data, + methods, + }) + +describe('Descriptions.vue', () => { + test('render test', () => { + const wrapper = _mount(` + + + + `) + + expect(wrapper.find('.el-descriptions__title').text()).toEqual('title') + expect(wrapper.find('.el-descriptions__extra').text()).toEqual('extra') + expect(wrapper.findAll('.el-descriptions__label').length).toEqual(4) + }) + + test('should render border props', () => { + const wrapper = _mount(` + + {{ item }} + + `) + + expect(wrapper.find('table').classes()).toContain('is-bordered') + }) + + test('should render column props', async () => { + const wrapper = _mount(` + + {{ item }} + + `, () => { + return { + border: false, + } + }) + + expect(wrapper.find('tr').element.children.length).toEqual(5) + wrapper.vm.border = true + await nextTick() + expect(wrapper.find('tr').element.children.length).toEqual(10) + }) + + test('should render direction props', async () => { + const wrapper = _mount(` + + {{ item }} + + `, () => { + return { + direction: 'horizontal', + } + }) + + expect(wrapper.find('tr').element.children.length).toEqual(10) + expect(wrapper.findAll('tr')[0].element.children[0].innerHTML).toEqual(wrapper.findAll('tr')[0].element.children[1].innerHTML) + wrapper.vm.direction = 'vertical' + await nextTick() + expect(wrapper.find('tr').element.children.length).toEqual(5) + expect(wrapper.findAll('tr')[0].element.children[0].innerHTML).toEqual(wrapper.findAll('tr')[1].element.children[0].innerHTML) + }) + + test('should render title slots', async () => { + const wrapper = _mount(` + + + {{ item }} + + `) + + expect(wrapper.find('.el-descriptions__title').text()).toEqual('title') + }) + + test('should render span props', async () => { + const wrapper = _mount(` + + 1 + 2 + 3 + + `) + + expect(wrapper.findAll('td')[1].element.getAttribute('colSpan')).toEqual('2') + }) +}) diff --git a/packages/descriptions/index.ts b/packages/descriptions/index.ts new file mode 100644 index 0000000000..9cc1178329 --- /dev/null +++ b/packages/descriptions/index.ts @@ -0,0 +1,11 @@ +import { App } from 'vue' +import type { SFCWithInstall } from '@element-plus/utils/types' +import Descriptions from './src/index.vue' + +Descriptions.install = (app: App): void => { + app.component(Descriptions.name, Descriptions) +} + +const _Descriptions: SFCWithInstall = Descriptions + +export default _Descriptions diff --git a/packages/descriptions/package.json b/packages/descriptions/package.json new file mode 100644 index 0000000000..06c32f4b54 --- /dev/null +++ b/packages/descriptions/package.json @@ -0,0 +1,12 @@ +{ + "name": "@element-plus/descriptions", + "version": "0.0.0", + "main": "dist/index.js", + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.9" + }, + "devDependencies": { + "@vue/test-utils": "^2.0.0-beta.3" + } +} diff --git a/packages/descriptions/src/description-item.ts b/packages/descriptions/src/description-item.ts new file mode 100644 index 0000000000..92981c294c --- /dev/null +++ b/packages/descriptions/src/description-item.ts @@ -0,0 +1,5 @@ +import { defineComponent } from 'vue' + +export default defineComponent({ + name: 'ElDescriptionsItem', +}) diff --git a/packages/descriptions/src/descriptions-cell.ts b/packages/descriptions/src/descriptions-cell.ts new file mode 100644 index 0000000000..027b976aa9 --- /dev/null +++ b/packages/descriptions/src/descriptions-cell.ts @@ -0,0 +1,55 @@ +import { computed, defineComponent, h, inject } from 'vue' +import { elDescriptionsKey, IDescriptionsInject } from './descriptions.type' + +export default defineComponent({ + name: 'ElDescriptionsCell', + props: { + cell: { + type: Object, + }, + tag: { + type: String, + }, + type: { + type: String, + }, + }, + setup(props) { + const descriptions = inject(elDescriptionsKey, {} as IDescriptionsInject) + + const label = computed(() => props.cell?.children?.label?.() || props.cell?.props?.label) + const content = computed(() => props.cell?.children?.default?.()) + const span = computed(() => props.cell?.props?.span || 1) + + return { + descriptions, + label: label, + content: content, + span: span, + } + }, + render() { + switch (this.type) { + case 'label': + return h(this.tag, { + class: ['el-descriptions__label', { 'is-bordered-label': this.descriptions.border }], + colSpan: this.descriptions.direction === 'vertical' ? this.span : 1, + }, this.label) + case 'content': + return h(this.tag, { + class: 'el-descriptions__content', + colSpan: this.descriptions.direction === 'vertical' ? this.span : this.span * 2 - 1, + }, this.content) + default: + return h('td', { + colSpan: this.span, + }, [ + h('span', { + class: ['el-descriptions__label', { 'is-bordered-label': this.descriptions.border }], + }, this.label), + h('span', { + class: 'el-descriptions__content', + }, this.content)]) + } + }, +}) diff --git a/packages/descriptions/src/descriptions-row.vue b/packages/descriptions/src/descriptions-row.vue new file mode 100644 index 0000000000..b3e5d97d4e --- /dev/null +++ b/packages/descriptions/src/descriptions-row.vue @@ -0,0 +1,53 @@ + + + diff --git a/packages/descriptions/src/descriptions.type.ts b/packages/descriptions/src/descriptions.type.ts new file mode 100644 index 0000000000..53368d19c6 --- /dev/null +++ b/packages/descriptions/src/descriptions.type.ts @@ -0,0 +1,17 @@ +import { InjectionKey, PropType } from 'vue' + +interface IDescriptionsInject { + border: boolean + column: number + direction: PropType<'horizontal' | 'vertical'> + size: PropType + title: string + extra: string +} + +const elDescriptionsKey: InjectionKey = 'elDescriptions' as any + +export { + IDescriptionsInject, + elDescriptionsKey, +} diff --git a/packages/descriptions/src/index.vue b/packages/descriptions/src/index.vue new file mode 100644 index 0000000000..f10ee07d80 --- /dev/null +++ b/packages/descriptions/src/index.vue @@ -0,0 +1,134 @@ + + + diff --git a/packages/element-plus/index.ts b/packages/element-plus/index.ts index 479086f877..29979abe79 100644 --- a/packages/element-plus/index.ts +++ b/packages/element-plus/index.ts @@ -89,6 +89,8 @@ import ElSpace from '@element-plus/space' import ElSkeleton from '@element-plus/skeleton' import ElSkeletonItem from '@element-plus/skeleton-item' import ElCheckTag from '@element-plus/check-tag' +import ElDescriptions from '@element-plus/descriptions' +import ElDescriptionsItem from '@element-plus/descriptions-item' import ElResult from '@element-plus/result' import { use, i18n } from '@element-plus/locale' @@ -208,6 +210,8 @@ const components = [ ElSpace, ElSkeleton, ElSkeletonItem, + ElDescriptions, + ElDescriptionsItem, ElResult, ] @@ -328,6 +332,8 @@ export { ElSpace, ElSkeleton, ElSkeletonItem, + ElDescriptions, + ElDescriptionsItem, ElResult, version, install, diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss index 564d32f0b1..67c2c2f69c 100644 --- a/packages/theme-chalk/src/common/var.scss +++ b/packages/theme-chalk/src/common/var.scss @@ -984,6 +984,13 @@ $--empty-image-width: 160px !default; $--empty-description-margin-top: 20px !default; $--empty-bottom-margin-top: 20px !default; +/* Descriptions +-------------------------- */ +$--descriptions-header-margin-bottom: 20px !default; +$--descriptions-title-font-size: 16px !default; +$--descriptions-table-border: 1px solid $--border-color-lighter !default; +$--descriptions-item-bordered-label-background: #fafafa !default; + /* Result -------------------------- */ $--result-padding: 40px 30px !default; diff --git a/packages/theme-chalk/src/descriptions-item.scss b/packages/theme-chalk/src/descriptions-item.scss new file mode 100644 index 0000000000..007c1d5a99 --- /dev/null +++ b/packages/theme-chalk/src/descriptions-item.scss @@ -0,0 +1,20 @@ +@import 'mixins/mixins'; +@import 'common/var'; + +@include b(descriptions) { + @include e(label) { + &.is-bordered-label { + font-weight: bold; + color: $--color-text-secondary; + background: $--descriptions-item-bordered-label-background; + } + + &:not(.is-bordered-label) { + margin-right: 10px; + } + } + + @include e(content) { + + } +} diff --git a/packages/theme-chalk/src/descriptions.scss b/packages/theme-chalk/src/descriptions.scss new file mode 100644 index 0000000000..c5ec15b2a9 --- /dev/null +++ b/packages/theme-chalk/src/descriptions.scss @@ -0,0 +1,96 @@ +@import 'mixins/mixins'; +@import 'common/var'; +@import 'descriptions-item'; + +@include b(descriptions) { + box-sizing: border-box; + font-size: $--font-size-base; + color: $--color-text-primary; + + @include e(header) { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: $--descriptions-header-margin-bottom; + + @include e(title) { + font-size: $--descriptions-title-font-size; + font-weight: bold; + } + } + + @include e(body) { + color: $--color-text-regular; + background-color: $--color-white; + + table { + border-collapse: collapse; + width: 100%; + + th, td { + text-align: left; + font-weight: normal; + line-height: 1.5; + } + } + } + + .is-bordered { + th, td { + border: $--descriptions-table-border; + padding: 12px 10px; + } + } + + :not(.is-bordered) { + th, td { + padding-bottom: 12px; + } + } + + @include m(medium) { + &.is-bordered { + th, td { + padding: 10px; + } + } + + &:not(.is-bordered) { + th, td { + padding-bottom: 10px; + } + } + } + + @include m(small) { + font-size: 12px; + + &.is-bordered { + th, td { + padding: 8px 10px; + } + } + + &:not(.is-bordered) { + th, td { + padding-bottom: 8px; + } + } + } + + @include m(mini) { + font-size: 12px; + + &.is-bordered { + th, td { + padding: 6px 10px; + } + } + + &:not(.is-bordered) { + th, td { + padding-bottom: 6px; + } + } + } +} diff --git a/packages/theme-chalk/src/index.scss b/packages/theme-chalk/src/index.scss index b4b4a23678..b428a4ff23 100644 --- a/packages/theme-chalk/src/index.scss +++ b/packages/theme-chalk/src/index.scss @@ -87,4 +87,6 @@ @import "./empty.scss"; @import "./affix.scss"; @import "./check-tag.scss"; +@import "./descriptions"; +@import "./descriptions-item"; @import "./result.scss"; diff --git a/website/demo-styles/descriptions.scss b/website/demo-styles/descriptions.scss new file mode 100644 index 0000000000..cbe9eebe82 --- /dev/null +++ b/website/demo-styles/descriptions.scss @@ -0,0 +1,5 @@ +.demo-block.demo-descriptions { + .margin-top { + margin-top: 20px; + } +} diff --git a/website/demo-styles/index.scss b/website/demo-styles/index.scss index ffa131c66e..e8327b97bc 100644 --- a/website/demo-styles/index.scss +++ b/website/demo-styles/index.scss @@ -49,3 +49,4 @@ @import "./drawer.scss"; @import "./skeleton.scss"; @import "./space.scss"; +@import "./descriptions.scss"; diff --git a/website/docs/en-US/descriptions.md b/website/docs/en-US/descriptions.md new file mode 100644 index 0000000000..2c4a8403e7 --- /dev/null +++ b/website/docs/en-US/descriptions.md @@ -0,0 +1,156 @@ +## Descriptions + +Display multiple fields in list form. + +### Basic usage + +:::demo + +```html + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + +``` +::: + +### Sizes + +:::demo + +```html + + + +``` +::: + +### Vertical List + +:::demo + +```html + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + + + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + +``` +::: + +### Descriptions Attributes +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| border | with or without border | boolean | — | false | +| column | numbers of `Descriptions Item` in one line | number | — | 3 | +| direction | direction of list | string | vertical / horizontal | horizontal | +| size | size of list | string | medium / small / mini | — | +| title | title text, display on the top left | string | — | — | +| extra | extra text, display on the top right | string | — | — | + +### Descriptions Slots + +| Name | Description | +|------|--------| +| title | custom title, display on the top left | +| extra | custom extra area, display on the top right | + +### Descriptions Item Attributes +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| label | label text | string | — | — | +| span | colspan of column | number | — | 1 | + +### Descriptions Item Slots + +| Name | Description | +|------|--------| +| label | custom label | diff --git a/website/docs/es/descriptions.md b/website/docs/es/descriptions.md new file mode 100644 index 0000000000..2c4a8403e7 --- /dev/null +++ b/website/docs/es/descriptions.md @@ -0,0 +1,156 @@ +## Descriptions + +Display multiple fields in list form. + +### Basic usage + +:::demo + +```html + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + +``` +::: + +### Sizes + +:::demo + +```html + + + +``` +::: + +### Vertical List + +:::demo + +```html + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + + + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + +``` +::: + +### Descriptions Attributes +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| border | with or without border | boolean | — | false | +| column | numbers of `Descriptions Item` in one line | number | — | 3 | +| direction | direction of list | string | vertical / horizontal | horizontal | +| size | size of list | string | medium / small / mini | — | +| title | title text, display on the top left | string | — | — | +| extra | extra text, display on the top right | string | — | — | + +### Descriptions Slots + +| Name | Description | +|------|--------| +| title | custom title, display on the top left | +| extra | custom extra area, display on the top right | + +### Descriptions Item Attributes +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| label | label text | string | — | — | +| span | colspan of column | number | — | 1 | + +### Descriptions Item Slots + +| Name | Description | +|------|--------| +| label | custom label | diff --git a/website/docs/fr-FR/descriptions.md b/website/docs/fr-FR/descriptions.md new file mode 100644 index 0000000000..2c4a8403e7 --- /dev/null +++ b/website/docs/fr-FR/descriptions.md @@ -0,0 +1,156 @@ +## Descriptions + +Display multiple fields in list form. + +### Basic usage + +:::demo + +```html + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + +``` +::: + +### Sizes + +:::demo + +```html + + + +``` +::: + +### Vertical List + +:::demo + +```html + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + + + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + +``` +::: + +### Descriptions Attributes +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| border | with or without border | boolean | — | false | +| column | numbers of `Descriptions Item` in one line | number | — | 3 | +| direction | direction of list | string | vertical / horizontal | horizontal | +| size | size of list | string | medium / small / mini | — | +| title | title text, display on the top left | string | — | — | +| extra | extra text, display on the top right | string | — | — | + +### Descriptions Slots + +| Name | Description | +|------|--------| +| title | custom title, display on the top left | +| extra | custom extra area, display on the top right | + +### Descriptions Item Attributes +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| label | label text | string | — | — | +| span | colspan of column | number | — | 1 | + +### Descriptions Item Slots + +| Name | Description | +|------|--------| +| label | custom label | diff --git a/website/docs/jp/descriptions.md b/website/docs/jp/descriptions.md new file mode 100644 index 0000000000..2c4a8403e7 --- /dev/null +++ b/website/docs/jp/descriptions.md @@ -0,0 +1,156 @@ +## Descriptions + +Display multiple fields in list form. + +### Basic usage + +:::demo + +```html + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + +``` +::: + +### Sizes + +:::demo + +```html + + + +``` +::: + +### Vertical List + +:::demo + +```html + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + + + + kooriookami + 18100000000 + Suzhou + + School + + No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province + +``` +::: + +### Descriptions Attributes +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| border | with or without border | boolean | — | false | +| column | numbers of `Descriptions Item` in one line | number | — | 3 | +| direction | direction of list | string | vertical / horizontal | horizontal | +| size | size of list | string | medium / small / mini | — | +| title | title text, display on the top left | string | — | — | +| extra | extra text, display on the top right | string | — | — | + +### Descriptions Slots + +| Name | Description | +|------|--------| +| title | custom title, display on the top left | +| extra | custom extra area, display on the top right | + +### Descriptions Item Attributes +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| label | label text | string | — | — | +| span | colspan of column | number | — | 1 | + +### Descriptions Item Slots + +| Name | Description | +|------|--------| +| label | custom label | diff --git a/website/docs/zh-CN/descriptions.md b/website/docs/zh-CN/descriptions.md new file mode 100644 index 0000000000..961942c376 --- /dev/null +++ b/website/docs/zh-CN/descriptions.md @@ -0,0 +1,156 @@ +## Descriptions 描述列表 + +列表形式展示多个字段。 + +### 基础用法 + +:::demo + +```html + + kooriookami + 18100000000 + 苏州市 + + 学校 + + 江苏省苏州市吴中区吴中大道 1188 号 + +``` +::: + +### 不同尺寸 + +:::demo + +```html + + + +``` +::: + +### 垂直列表 + +:::demo + +```html + + kooriookami + 18100000000 + 苏州市 + + 学校 + + 江苏省苏州市吴中区吴中大道 1188 号 + + + + kooriookami + 18100000000 + 苏州市 + + 学校 + + 江苏省苏州市吴中区吴中大道 1188 号 + +``` +::: + +### Descriptions Attributes +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|------------- |---------------- |---------------- |---------------------- |-------- | +| border | 是否带有边框 | boolean | — | false | +| column | 一行 `Descriptions Item` 的数量 | number | — | 3 | +| direction | 排列的方向 | string | vertical / horizontal | horizontal | +| size | 列表的尺寸 | string | medium / small / mini | — | +| title | 标题文本,显示在左上方 | string | — | — | +| extra | 操作区文本,显示在右上方 | string | — | — | + +### Descriptions Slots + +| Name | 说明 | +|------|--------| +| title | 自定义标题,显示在左上方 | +| extra | 自定义操作区,显示在右上方 | + +### Descriptions Item Attributes +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|------------- |---------------- |---------------- |---------------------- |-------- | +| label | 标签文本 | string | — | — | +| span | 列的数量 | number | — | 1 | + +### Descriptions Item Slots + +| Name | 说明 | +|------|--------| +| label | 自定义标签文本 | diff --git a/website/nav.config.json b/website/nav.config.json index fa0a545871..baaa40cfcc 100644 --- a/website/nav.config.json +++ b/website/nav.config.json @@ -197,6 +197,10 @@ "path": "/empty", "title": "Empty 空状态" }, + { + "path": "/descriptions", + "title": "Descriptions 描述列表" + }, { "path": "/result", "title": "Result 结果" @@ -519,6 +523,10 @@ "path": "/empty", "title": "Empty" }, + { + "path": "/descriptions", + "title": "Descriptions" + }, { "path": "/result", "title": "Result" @@ -845,6 +853,10 @@ "path": "/empty", "title": "Empty" }, + { + "path": "/descriptions", + "title": "Descriptions" + }, { "path": "/result", "title": "Result" @@ -1171,6 +1183,10 @@ "path": "/empty", "title": "Empty" }, + { + "path": "/descriptions", + "title": "Descriptions" + }, { "path": "/result", "title": "Result" @@ -1497,6 +1513,10 @@ "path": "/empty", "title": "Empty" }, + { + "path": "/descriptions", + "title": "Descriptions" + }, { "path": "/result", "title": "Result"