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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{timestamp}}
+
+
+
+
+
+
+
+ {{timestamp}}
+
+
+
+
+
+
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
+}