diff --git a/components.json b/components.json index 7e8b58a93..9b9ebc795 100644 --- a/components.json +++ b/components.json @@ -67,5 +67,7 @@ "header": "./packages/header/index.js", "aside": "./packages/aside/index.js", "main": "./packages/main/index.js", - "footer": "./packages/footer/index.js" + "footer": "./packages/footer/index.js", + "timeline": "./packages/timeline/index.js", + "timeline-item": "./packages/timeline-item/index.js" } diff --git a/examples/docs/en-US/timeline.md b/examples/docs/en-US/timeline.md new file mode 100644 index 000000000..dd8a706ca --- /dev/null +++ b/examples/docs/en-US/timeline.md @@ -0,0 +1,199 @@ + + + +## Timeline + +Visually display timeline. + +### Basic usage + +Timeline can be split into multiple activities in ascending or descending. Timestamps are important features that distinguish them from other components. Note the difference with Steps. + +:::demo +```html +
+
+ Order: + + descending + ascending + +
+ + + + {{activity.content}} + + +
+ + +``` +::: + +### Custom node + +Size, color, and icons can be customized in node. + +:::demo +```html +
+ + + {{activity.content}} + + +
+ + +``` +::: + +### Custom timestamp + +Timestamp can be placed on top of content when content is too high. + +:::demo +```html +
+ + + +

Update Github template

+

Tom committed 2018/4/12 20:46

+
+
+ + +

Update Github template

+

Tom committed 2018/4/3 20:46

+
+
+ + +

Update Github template

+

Tom committed 2018/4/2 20:46

+
+
+
+
+``` +::: + +### Timeline Attributes +| Attribute | Description | Type | Accepted Values | Default | +|---------- |-------- |---------- |------------- |-------- | +| reverse | whether the node is ascending or descending, default is ascending | boolean | — | false | + +### Timeline-item Attributes +| Attribute | Description | Type | Accepted Values | Default | +|---------- |-------- |---------- |------------- |-------- | +| timestamp | timestamp content | string | - | — | +| hide-timestamp | whether to show timestamp | boolean | — | false | +| placement | position of timestamp | string | top / bottom | bottom | +| type | node type | string | primary / success / warning / danger / info | - | +| color | background color of node | string | hsl / hsv / hex / rgb | - | +| size | node size | string | normal / large | normal | +| icon | icon class name | string | — | - | + +### Timeline-Item Slot +| name | Description | +|------|--------| +| — | Custom content for timeline item | +| dot | Custom defined node | diff --git a/examples/docs/es/timeline.md b/examples/docs/es/timeline.md new file mode 100644 index 000000000..0f0afca8e --- /dev/null +++ b/examples/docs/es/timeline.md @@ -0,0 +1,199 @@ + + + +## Timeline + +Visually display timeline. + +### Basic usage + +Timeline can be split into multiple activities in ascending or descending. Timestamps are important features that distinguish them from other components. Note the difference with Steps. + +:::demo +```html +
+
+ Order: + + descending + ascending + +
+ + + + {{activity.content}} + + +
+ + +``` +::: + +### Custom node + +Size, color, and icons can be customized in node. + +:::demo +```html +
+ + + {{activity.content}} + + +
+ + +``` +::: + +### Custom timestamp + +Timestamp can be placed on top of content when content is too high. + +:::demo +```html +
+ + + +

Update Github template

+

Tom committed 2018/4/12 20:46

+
+
+ + +

Update Github template

+

Tom committed 2018/4/3 20:46

+
+
+ + +

Update Github template

+

Tom committed 2018/4/2 20:46

+
+
+
+
+``` +::: + +### Timeline Attributes +| Attribute | Description | Type | Accepted Values | Default | +|---------- |-------- |---------- |------------- |-------- | +| reverse | whether the node is ascending or descending, default is ascending | boolean | — | false | + +### Timeline-item Attributes +| Attribute | Description | Type | Accepted Values | Default | +|---------- |-------- |---------- |------------- |-------- | +| timestamp | timestamp content | string | - | — | +| hide-timestamp | whether to show timestamp | boolean | — | false | +| placement | position of timestamp | string | top / bottom | bottom | +| type | node type | string | primary / success / warning / danger / info | - | +| color | background color of node | string | hsl / hsv / hex / rgb | - | +| size | node size | string | normal / large | normal | +| icon | icon class name | string | — | - | + +### Timeline-Item Slot +| name | Description | +|------|--------| +| — | Custom content for timeline item | +| dot | Custom defined node | diff --git a/examples/docs/zh-CN/timeline.md b/examples/docs/zh-CN/timeline.md new file mode 100644 index 000000000..5f8fbde07 --- /dev/null +++ b/examples/docs/zh-CN/timeline.md @@ -0,0 +1,199 @@ + + + +## Timeline 时间线 + +可视化地呈现时间流信息。 + +### 基础用法 + +Timeline 可拆分成多个按照时间戳正序或倒序排列的 activity,时间戳是其区分于其他控件的重要特征,使⽤时注意与 Steps 步骤条等区分。 + +:::demo +```html +
+
+ 排序: + + 倒序 + 正序 + +
+ + + + {{activity.content}} + + +
+ + +``` +::: + +### ⾃定义节点样式 + +可根据实际场景⾃定义节点尺⼨、颜⾊,或直接使⽤图标。 + +:::demo +```html +
+ + + {{activity.content}} + + +
+ + +``` +::: + +### ⾃定义时间戳 + +当内容在垂直⽅向上过⾼时,可将时间戳置于内容之上。 + +:::demo +```html +
+ + + +

更新 Github 模板

+

王小虎 提交于 2018/4/12 20:46

+
+
+ + +

更新 Github 模板

+

王小虎 提交于 2018/4/3 20:46

+
+
+ + +

更新 Github 模板

+

王小虎 提交于 2018/4/2 20:46

+
+
+
+
+``` +::: + +### Timeline Attributes +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| reverse | 指定节点排序方向,默认为正序 | boolean | — | false | + +### Timeline-item Attributes +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------- |---------- |------------- |-------- | +| timestamp | 时间戳 | string | - | — | +| hide-timestamp | 是否隐藏时间戳 | boolean | — | false | +| placement | 时间戳位置 | string | top / bottom | bottom | +| type | 节点类型 | string | primary / success / warning / danger / info | - | +| color | 节点颜色 | string | hsl / hsv / hex / rgb | - | +| size | 节点尺寸 | string | normal / large | normal | +| icon | 节点图标 | string | — | - | + +### Timeline-Item Slot +| name | 说明 | +|------|--------| +| — | Timeline-Item 的内容 | +| dot | 自定义节点 | diff --git a/examples/nav.config.json b/examples/nav.config.json index 651e6910f..e7ee0e1cc 100644 --- a/examples/nav.config.json +++ b/examples/nav.config.json @@ -243,6 +243,10 @@ { "path": "/collapse", "title": "Collapse 折叠面板" + }, + { + "path": "/timeline", + "title": "Timeline 时间线" } ] } @@ -493,6 +497,10 @@ { "path": "/collapse", "title": "Collapse" + }, + { + "path": "/timeline", + "title": "Timeline" } ] } @@ -743,6 +751,10 @@ { "path": "/collapse", "title": "Collapse" + }, + { + "path": "/timeline", + "title": "Timeline" } ] } diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss index 315af8bdb..991183688 100644 --- a/packages/theme-chalk/src/common/var.scss +++ b/packages/theme-chalk/src/common/var.scss @@ -683,6 +683,12 @@ $--footer-padding: 0 20px !default; --------------------------*/ $--main-padding: 20px !default; +/* Timeline +--------------------------*/ +$--timeline-node-size-normal: 12px !default; +$--timeline-node-size-large: 14px !default; +$--timeline-node-color: $--border-color-light !default; + /* Break-point --------------------------*/ $--sm: 768px !default; diff --git a/packages/theme-chalk/src/index.scss b/packages/theme-chalk/src/index.scss index aed930721..d644e304f 100644 --- a/packages/theme-chalk/src/index.scss +++ b/packages/theme-chalk/src/index.scss @@ -65,3 +65,5 @@ @import "./aside.scss"; @import "./main.scss"; @import "./footer.scss"; +@import "./timeline.scss"; +@import "./timeline-item.scss"; diff --git a/packages/theme-chalk/src/timeline-item.scss b/packages/theme-chalk/src/timeline-item.scss new file mode 100644 index 000000000..b36ed2b3e --- /dev/null +++ b/packages/theme-chalk/src/timeline-item.scss @@ -0,0 +1,86 @@ +@import "mixins/mixins"; +@import "common/var"; + +@include b(timeline-item) { + position: relative; + padding-bottom: 20px; + + @include e(wrapper) { + position: relative; + padding-left: 28px; + top: -3px; + } + + @include e(tail) { + position: absolute; + left: 4px; + height: 100%; + border-left: 2px solid $--timeline-node-color; + } + + @include e(icon) { + color: $--color-white; + font-size: $--font-size-small; + } + + @include e(node) { + position: absolute; + background-color: $--timeline-node-color; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + + @include m(normal) { + left: -1px; + width: $--timeline-node-size-normal; + height: $--timeline-node-size-normal; + } + @include m(large) { + left: -2px; + width: $--timeline-node-size-large; + height: $--timeline-node-size-large; + } + + @include m(primary) { + background-color: $--color-primary; + } + @include m(success) { + background-color: $--color-success; + } + @include m(warning) { + background-color: $--color-warning; + } + @include m(danger) { + background-color: $--color-danger; + } + @include m(info) { + background-color: $--color-info; + } + } + + @include e(dot) { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + } + + @include e(content) { + color: $--color-text-primary; + } + + @include e(timestamp) { + color: $--color-text-secondary; + line-height: 1; + font-size: $--font-size-small; + + @include when(top) { + margin-bottom: 8px; + padding-top: 4px; + } + @include when(bottom) { + margin-top: 8px; + } + } +} diff --git a/packages/theme-chalk/src/timeline.scss b/packages/theme-chalk/src/timeline.scss new file mode 100644 index 000000000..60a779eca --- /dev/null +++ b/packages/theme-chalk/src/timeline.scss @@ -0,0 +1,14 @@ +@import "mixins/mixins"; +@import "common/var"; + +@include b(timeline) { + margin: 0; + font-size: $--font-size-base; + list-style: none; + + & .el-timeline-item:last-child { + & .el-timeline-item__tail { + display: none; + } + } +} diff --git a/packages/timeline-item/index.js b/packages/timeline-item/index.js new file mode 100644 index 000000000..a9e524596 --- /dev/null +++ b/packages/timeline-item/index.js @@ -0,0 +1,8 @@ +import ElTimelineItem from '../timeline/src/item'; + +/* istanbul ignore next */ +ElTimelineItem.install = function(Vue) { + Vue.component(ElTimelineItem.name, ElTimelineItem); +}; + +export default ElTimelineItem; diff --git a/packages/timeline/index.js b/packages/timeline/index.js new file mode 100644 index 000000000..ddfa6da00 --- /dev/null +++ b/packages/timeline/index.js @@ -0,0 +1,8 @@ +import Timeline from './src/main'; + +/* istanbul ignore next */ +Timeline.install = function(Vue) { + Vue.component(Timeline.name, Timeline); +}; + +export default Timeline; diff --git a/packages/timeline/src/item.vue b/packages/timeline/src/item.vue new file mode 100644 index 000000000..86771bbac --- /dev/null +++ b/packages/timeline/src/item.vue @@ -0,0 +1,73 @@ + + + diff --git a/packages/timeline/src/main.vue b/packages/timeline/src/main.vue new file mode 100644 index 000000000..dde498429 --- /dev/null +++ b/packages/timeline/src/main.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/index.js b/src/index.js index 0e1496d2c..e78f2acae 100644 --- a/src/index.js +++ b/src/index.js @@ -69,6 +69,8 @@ import Header from '../packages/header/index.js'; import Aside from '../packages/aside/index.js'; import Main from '../packages/main/index.js'; import Footer from '../packages/footer/index.js'; +import Timeline from '../packages/timeline/index.js'; +import TimelineItem from '../packages/timeline-item/index.js'; import locale from 'element-ui/src/locale'; import CollapseTransition from 'element-ui/src/transitions/collapse-transition'; @@ -138,6 +140,8 @@ const components = [ Aside, Main, Footer, + Timeline, + TimelineItem, CollapseTransition ]; @@ -245,5 +249,7 @@ export default { Header, Aside, Main, - Footer + Footer, + Timeline, + TimelineItem }; diff --git a/test/unit/specs/timeline.spec.js b/test/unit/specs/timeline.spec.js new file mode 100644 index 000000000..69b44469f --- /dev/null +++ b/test/unit/specs/timeline.spec.js @@ -0,0 +1,228 @@ +import { createVue, destroyVM } from '../util'; + +describe('Timeline', () => { + let vm; + afterEach(() => { + destroyVM(vm); + }); + + it('create', () => { + vm = createVue({ + template: ` + + + {{activity.content}} + + + `, + data() { + return { + activities: [{ + content: '创建成功', + timestamp: '2018-04-11' + }, { + content: '通过审核', + timestamp: '2018-04-13' + }, { + content: '活动按期开始', + timestamp: '2018-04-15' + }] + }; + } + }, true); + let contentElms = vm.$el.querySelectorAll('.el-timeline-item__content'); + contentElms.forEach((elm, index) => { + expect(elm.innerText).to.equal(vm.activities[index].content); + }); + let timestampElms = vm.$el.querySelectorAll('.el-timeline-item__timestamp'); + timestampElms.forEach((elm, index) => { + expect(elm.innerText).to.equal(vm.activities[index].timestamp); + }); + }); + + it('reverse', done => { + vm = createVue({ + template: ` + + + {{activity.content}} + + + `, + + data() { + return { + reverse: true, + activities: [{ + content: '创建成功', + timestamp: '2018-04-11' + }, { + content: '通过审核', + timestamp: '2018-04-13' + }, { + content: '活动按期开始', + timestamp: '2018-04-15' + }] + }; + } + }, true); + + const contentElms = vm.$el.querySelectorAll('.el-timeline-item__content'); + contentElms.forEach((elm, index) => { + expect(elm.innerText).to.equal(vm.activities[vm.activities.length - index - 1].content); + }); + + vm.reverse = false; + vm.$nextTick(() => { + const contentElms = vm.$el.querySelectorAll('.el-timeline-item__content'); + contentElms.forEach((elm, index) => { + expect(elm.innerText).to.equal(vm.activities[index].content); + }); + done(); + }); + }); + + it('placement', () => { + vm = createVue({ + template: ` + + + {{activity.content}} + + + `, + + data() { + return { + activities: [{ + content: '创建成功', + timestamp: '2018-04-11', + placement: 'top' + }, { + content: '通过审核', + timestamp: '2018-04-13' + }, { + content: '活动按期开始', + timestamp: '2018-04-15' + }] + }; + } + }, true); + + const timestampElm = vm.$el.querySelectorAll('.el-timeline-item__timestamp')[0]; + expect(timestampElm.classList.contains('is-top')).to.true; + }); + + it('hide-timestamp', () => { + vm = createVue({ + template: ` + + + {{activity.content}} + + + `, + + data() { + return { + activities: [{ + content: '创建成功', + timestamp: '2018-04-11', + hideTimestamp: true + }, { + content: '通过审核', + timestamp: '2018-04-13' + }, { + content: '活动按期开始', + timestamp: '2018-04-15' + }] + }; + } + }, true); + + const timestampElms = vm.$el.querySelectorAll('.el-timeline-item__timestamp'); + expect(timestampElms.length).to.equal(2); + }); + + it('color', () => { + vm = createVue({ + template: ` + + + 创建成功 + + + ` + }, true); + + const nodeElm = vm.$el.querySelector('.el-timeline-item__node'); + expect(nodeElm.style.backgroundColor).to.equal('rgb(255, 0, 0)'); + }); + + it('type', () => { + vm = createVue({ + template: ` + + + 创建成功 + + + ` + }, true); + + const nodeElm = vm.$el.querySelector('.el-timeline-item__node'); + expect(nodeElm.classList.contains('el-timeline-item__node--primary')).to.true; + }); + + it('size', () => { + vm = createVue({ + template: ` + + + 创建成功 + + + ` + }, true); + + const nodeElm = vm.$el.querySelector('.el-timeline-item__node'); + expect(nodeElm.classList.contains('el-timeline-item__node--large')).to.true; + }); + + it('icon', () => { + vm = createVue({ + template: ` + + + 创建成功 + + + ` + }, true); + + const nodeElm = vm.$el.querySelector('.el-timeline-item__icon'); + expect(nodeElm.classList.contains('el-icon-more')).to.true; + }); +}); diff --git a/types/element-ui.d.ts b/types/element-ui.d.ts index 6411cdb53..2c341a8e5 100644 --- a/types/element-ui.d.ts +++ b/types/element-ui.d.ts @@ -61,6 +61,8 @@ import { ElTableColumn } from './table-column' import { ElTag } from './tag' import { ElTabs } from './tabs' import { ElTabPane } from './tab-pane' +import { ElTimeline } from './timeline' +import { ElTimelineItem } from './timeline-item' import { ElTimePicker } from './time-picker' import { ElTimeSelect } from './time-select' import { ElTooltip } from './tooltip' @@ -275,6 +277,12 @@ export class TabPane extends ElTabPane {} /** Tag Component */ export class Tag extends ElTag {} +/** Timeline Component */ +export class Timeline extends ElTimeline {} + +/** Timeline Item Component */ +export class TimelineItem extends ElTimelineItem {} + /** TimePicker Component */ export class TimePicker extends ElTimePicker {} diff --git a/types/timeline-item.d.ts b/types/timeline-item.d.ts new file mode 100644 index 000000000..647d5550a --- /dev/null +++ b/types/timeline-item.d.ts @@ -0,0 +1,20 @@ +import { ElementUIComponent } from './component' + +export type TimelineItemPlacement = 'top' | 'bottom' +export type TimelineItemType = 'primary' | 'success' | 'warning' | 'danger' | 'info' +export type TimelineItemSize = 'normal' | 'large' + +/** TimelineItem Component */ +export declare class ElTimelineItem extends ElementUIComponent { + timestamp: string + + hideTimestamp: boolean + + placement: TimelineItemPlacement + + type: TimelineItemType + + size: TimelineItemSize + + icon: string +} diff --git a/types/timeline.d.ts b/types/timeline.d.ts new file mode 100644 index 000000000..20e033d05 --- /dev/null +++ b/types/timeline.d.ts @@ -0,0 +1,6 @@ +import { ElementUIComponent } from './component' + +/** Timeline Component */ +export declare class ElTimeline extends ElementUIComponent { + reverse: boolean +}