mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
feat: timeline3.0 (#3072)
Co-authored-by: yanglu19 <yanglu19@baidu.com> Co-authored-by: liaoxuezhi <liaoxuezhi@baidu.com>
This commit is contained in:
parent
3e8c73225f
commit
13846fb4e2
46
__tests__/renderers/Timeline.test.tsx
Normal file
46
__tests__/renderers/Timeline.test.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React = require('react');
|
||||
import {render, cleanup} from '@testing-library/react';
|
||||
import '../../src/themes/default';
|
||||
import {render as amisRender} from '../../src/index';
|
||||
import {makeEnv} from '../helper';
|
||||
import {clearStoresCache} from '../../src/factory';
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
clearStoresCache();
|
||||
});
|
||||
|
||||
test('Renderer:timeline', async () => {
|
||||
const {container} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'timeline',
|
||||
items: [
|
||||
{
|
||||
time: "2019-02-07",
|
||||
title: "节点数据",
|
||||
color: "#ffb200",
|
||||
},
|
||||
{
|
||||
time: "2019-02-08",
|
||||
title: "节点数据",
|
||||
color: "#4F86F4",
|
||||
},
|
||||
{
|
||||
time: "2019-02-09",
|
||||
title: "节点数据",
|
||||
color: "success",
|
||||
},
|
||||
{
|
||||
time: "2019-02-09",
|
||||
title: "节点数据",
|
||||
color: "warning",
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv()
|
||||
)
|
||||
);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
390
docs/zh-CN/components/timeline.md
Normal file
390
docs/zh-CN/components/timeline.md
Normal file
@ -0,0 +1,390 @@
|
||||
---
|
||||
title: Timeline 时间轴
|
||||
description:
|
||||
type: 0
|
||||
group: ⚙ 组件
|
||||
menuName: Timeline
|
||||
icon:
|
||||
order: 73
|
||||
---
|
||||
|
||||
时间轴组件
|
||||
|
||||
## 基本用法
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
"type": "timeline",
|
||||
"items": [
|
||||
{
|
||||
"time": "2019-02-07",
|
||||
"title": "节点数据",
|
||||
"detail": "error",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-08",
|
||||
"title": "节点数据",
|
||||
"detail": "success",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-09",
|
||||
"title": "节点数据",
|
||||
"detail": "error",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 时间轴节点颜色设置
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
"type": "timeline",
|
||||
"items": [
|
||||
{
|
||||
"time": "2019-02-07",
|
||||
"title": "节点数据",
|
||||
"color": "#ffb200",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-08",
|
||||
"title": "节点数据",
|
||||
"color": "#4F86F4",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-09",
|
||||
"title": "节点数据",
|
||||
"color": "success",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-09",
|
||||
"title": "节点数据",
|
||||
"color": "warning",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 时间轴节点图标设置
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
"type": "timeline",
|
||||
"items": [
|
||||
{
|
||||
"time": "2019-02-07",
|
||||
"title": "节点数据error",
|
||||
"detail": "error",
|
||||
"icon": "status-fail"
|
||||
},
|
||||
{
|
||||
"time": "2019-02-08",
|
||||
"title": "节点数据success",
|
||||
"detail": "success",
|
||||
"icon": "status-success"
|
||||
},
|
||||
{
|
||||
"time": "2019-02-09",
|
||||
"title": "节点数据warning",
|
||||
"detail": "warning",
|
||||
"icon": "status-warning"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 节点标题自定义
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
"type": "timeline",
|
||||
"items": [
|
||||
{
|
||||
"time": "2019-02-07",
|
||||
"title": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "2019年02月7日"
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"label": "查看",
|
||||
"actionType": "dialog",
|
||||
"level": "link",
|
||||
"dialog": {
|
||||
"title": "查看详情",
|
||||
"body": "这是详细内容。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"label": "删除",
|
||||
"level": "link",
|
||||
"actionType": "dialog",
|
||||
"dialog": {
|
||||
"title": "删除",
|
||||
"body": "确认删除吗?"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"time": "2019-02-10",
|
||||
"title": [
|
||||
{
|
||||
"type": "text",
|
||||
"value": "2019年02月10日"
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"label": "查看",
|
||||
"actionType": "dialog",
|
||||
"level": "link",
|
||||
"dialog": {
|
||||
"title": "查看详情",
|
||||
"body": "这是详细内容。"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"label": "删除",
|
||||
"level": "link",
|
||||
"actionType": "dialog",
|
||||
"dialog": {
|
||||
"title": "删除",
|
||||
"body": "确认删除吗?"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 设置节点数据倒序
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
"type": "timeline",
|
||||
direction: "vertical",
|
||||
reverse: true,
|
||||
"items": [
|
||||
{
|
||||
"time": "2019-02-07",
|
||||
"title": "节点数据",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-08",
|
||||
"title": "节点数据",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-09",
|
||||
"title": "节点数据",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-10",
|
||||
"title": "节点数据",
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 设置时间轴方向
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
"type": "timeline",
|
||||
direction: "horizontal",
|
||||
"items": [
|
||||
{
|
||||
"time": "2019-02-07",
|
||||
"title": "节点数据",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-08",
|
||||
"title": "节点数据",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-09",
|
||||
"title": "节点数据",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-10",
|
||||
"title": "节点数据",
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 设置文字相对时间轴方向(时间轴横向时不支持)
|
||||
### 文字位于时间轴左侧
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
"type": "timeline",
|
||||
"mode": "left",
|
||||
"items": [
|
||||
{
|
||||
"time": "2019-02-07",
|
||||
"title": "节点数据",
|
||||
"detail": "error",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-08",
|
||||
"title": "节点数据",
|
||||
"detail": "success",
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 文字交替位于时间轴两侧
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
"type": "timeline",
|
||||
"mode": "alternate",
|
||||
"items": [
|
||||
{
|
||||
"time": "2019-02-07",
|
||||
"title": "节点数据",
|
||||
"detail": "error",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-08",
|
||||
"title": "节点数据",
|
||||
"detail": "success",
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 文字位于时间轴右侧
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
"type": "timeline",
|
||||
"mode": "right",
|
||||
"items": [
|
||||
{
|
||||
"time": "2019-02-07",
|
||||
"title": "节点数据",
|
||||
"detail": "error",
|
||||
},
|
||||
{
|
||||
"time": "2019-02-08",
|
||||
"title": "节点数据",
|
||||
"detail": "success",
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 远程数据
|
||||
|
||||
```schema
|
||||
{
|
||||
"type": "page",
|
||||
"body": [
|
||||
{
|
||||
"type": "timeline",
|
||||
"source": {
|
||||
"method": "get",
|
||||
"url": "/api/mock2/timeline/timelineItems"
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
"source": "/api/mock2/timeline/timelineItems",
|
||||
|
||||
远程拉取接口时,返回的数据结构除了需要满足 amis 接口要求的基本数据结构 以外,必须用"items"作为时间轴数据的 key 值,如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"items": [
|
||||
{"time": "2019-02-07", "title": "数据开发", "detail": "2019-02-07detail", "color":"#ffb200", "icon": "close"},
|
||||
{"time": "2019-02-08", "title": "管理中心", "detail": "2019-02-08detail" },
|
||||
{"time": "2019-02-09", "title": "SQL语句", "detail": "2019-02-09detail", "color":"warning"},
|
||||
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-10detail", "icon": "compress-alt"},
|
||||
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-11detail"},
|
||||
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-12detail", "icon": "close"},
|
||||
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-13detail"}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 属性表
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| --------- | --------------------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------- |
|
||||
| type | `string` | | `"timeline"` 指定为 时间轴 渲染器 |
|
||||
| items | Array<[timelineItem](#timeline.item)> | [] | 配置节点数据 |
|
||||
| source | [API](../../../docs/types/api) | | 数据源,可通过数据映射获取当前数据域变量、或者配置 API 对象 |
|
||||
| mode | `left` \| `right` \| `alternate` | `right` | 指定文字相对于时间轴的位置,仅 direction=vertical时支持 |
|
||||
| direction | `vertical` \| `horizontal` | `vertical` | 时间轴方向 | |
|
||||
| reverse | `boolean` | `false` | 根据时间倒序显示
|
||||
|
||||
### timeline.item
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| ----------- | ----------------------------------------------------- | ------ | --------------------------------------- |
|
||||
| time | `string ` | | 节点时间 |
|
||||
| title | `string \| [SchemaNode](../../docs/types/schemanode)` | | 节点标题 |
|
||||
| detail | `string` | | 节点详细描述(折叠) |
|
||||
| detailCollapsedText | `string` | `展开` | 详细内容折叠时按钮文案 |
|
||||
| detailExpandedText | `string` | `折叠` | 详细内容展开时按钮文案 |
|
||||
| color | `string \| level样式(info、success、warning、danger)` | `#DADBDD` | 时间轴节点颜色 |
|
||||
| icon | `string` | | icon 名,支持 fontawesome v4 或使用 url(优先级高于color) |
|
@ -999,7 +999,15 @@ export const components = [
|
||||
import('../../docs/zh-CN/components/video.md').then(
|
||||
makeMarkdownRenderer
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Timeline 时间轴',
|
||||
path: '/zh-CN/components/timeline',
|
||||
getComponent: () =>
|
||||
import('../../docs/zh-CN/components/timeline.md').then(
|
||||
makeMarkdownRenderer
|
||||
)
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
|
15
mock/cfc/mock/timeline/timelineItems.json
Normal file
15
mock/cfc/mock/timeline/timelineItems.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"status": 0,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"items": [
|
||||
{"time": "2019-02-07", "title": "数据开发", "detail": "2019-02-07detail", "color":"#ffb200", "icon": "close"},
|
||||
{"time": "2019-02-08", "title": "管理中心", "detail": "2019-02-08detail" },
|
||||
{"time": "2019-02-09", "title": "SQL语句", "detail": "2019-02-09detail", "color":"warning"},
|
||||
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-10detail", "icon": "compress-alt"},
|
||||
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-11detail"},
|
||||
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-12detail", "icon": "close"},
|
||||
{"time": "2019-02-10", "title": "一键部署", "detail": "2019-02-13detail"}
|
||||
]
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"coverage": "jest --coverage",
|
||||
"serve": "fis3 server start --www ./public --port 8888 --no-daemon --no-browse",
|
||||
"serve": "fis3 server start --www ./public --port 8000 --no-daemon --no-browse",
|
||||
"start": "concurrently --restart-tries -1 npm:serve npm:dev",
|
||||
"update-snapshot": "jest --updateSnapshot",
|
||||
"stop": "fis3 server stop",
|
||||
|
198
scss/components/_timeline.scss
Normal file
198
scss/components/_timeline.scss
Normal file
@ -0,0 +1,198 @@
|
||||
.#{$ns}Timeline-vertical {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
||||
.#{$ns}TimelineItem {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
|
||||
&:last-of-type {
|
||||
.#{$ns}TimelineItem-axle .#{$ns}TimelineItem-line {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-axle {
|
||||
position: relative;
|
||||
flex: var(--TimelineItem--axle-flex);
|
||||
|
||||
.#{$ns}TimelineItem-line {
|
||||
position: absolute;
|
||||
height: calc(100% - var(--TimelineItem--left-line-top));
|
||||
width: var(--TimelineItem--left-line-width);
|
||||
left: var(--TimelineItem--left-line-left);
|
||||
top: var(--TimelineItem--left-line-top);
|
||||
background-color: var(--TimelineItem--line-bg);
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem-round {
|
||||
position: absolute;
|
||||
width: var(--TimelineItem--round-width);
|
||||
height: var(--TimelineItem--round-height);
|
||||
left: var(--TimelineItem--round-left);
|
||||
top: var(--TimelineItem--round-top);
|
||||
background: var(--TimelineItem-round-bg);
|
||||
border-radius: var(--TimelineItem--round-radius);
|
||||
|
||||
&--danger {
|
||||
background: var(--Timeline--danger-bg);
|
||||
}
|
||||
|
||||
&--info {
|
||||
background: var(--Timeline--info-bg);
|
||||
}
|
||||
|
||||
&--success {
|
||||
background: var(--Timeline--success-bg);
|
||||
}
|
||||
|
||||
&--warning {
|
||||
background: var(--Timeline--warning-bg);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem-icon {
|
||||
position: absolute;
|
||||
width: var(--TimelineItem--icon-width);
|
||||
height: var(--TimelineItem--icon-height);
|
||||
left: var(--TimelineItem--icon-left);
|
||||
border-radius: var(--TimelineItem--icon-radius);
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
padding-bottom: var(--TimelineItem--content-padding-bottom);
|
||||
margin-left: var(--TimelineItem--content-margin-left);
|
||||
|
||||
.#{$ns}TimelineItem-time {
|
||||
color: var(--TimelineItem--text-secondary-color);
|
||||
font-size: var(--Timeline--font-size);
|
||||
margin-bottom: var(--TimelineItem--content-time-margin-bottom);
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem-title {
|
||||
color: var(--TimelineItem--text-primary-color);
|
||||
font-size: var(--Timeline--font-size);
|
||||
margin-bottom: var(--TimelineItem--content-title-margin-bottom);
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem-detail {
|
||||
.#{$ns}TimelineItem-detail-button {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
font-size: var(--Timeline--font-size);
|
||||
color: var(--TimelineItem--detail-button-color);
|
||||
margin-bottom: var(--TimelineItem--detail-button-margin-bottom);
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem-detail-arrow {
|
||||
width: var(--TimelineItem-detail-arrow-width);
|
||||
height: var(--TimelineItem-detail-arrow-width);
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem-detail-arrow-top {
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem-detail-visible {
|
||||
display: block;
|
||||
max-width: var(--TimelineItem-detail-visible-max-width);
|
||||
font-size: var(--Timeline--font-size);
|
||||
padding: var(--TimelineItem-detail-visible-padding);
|
||||
box-shadow: var(--TimelineItem-detail-visible-shadow);
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem-detail-invisible {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Timeline-left {
|
||||
.#{$ns}TimelineItem {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$ns}Timeline-alternate {
|
||||
.#{$ns}TimelineItem:nth-child(odd) {
|
||||
flex-direction: row-reverse;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem:nth-child(even) {
|
||||
margin-left: calc(50% - var(--Timeline-alternate-margin-left));
|
||||
max-width: calc(50% + var(--Timeline-alternate-margin-left));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}Timeline-horizontal {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
.#{$ns}TimelineItem {
|
||||
display: flex;
|
||||
width: -webkit-fill-available;
|
||||
flex-flow: column;
|
||||
|
||||
&:last-of-type {
|
||||
.#{$ns}TimelineItem-axle .#{$ns}TimelineItem-line {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&-axle {
|
||||
position: relative;
|
||||
flex: var(--TimelineItem--axle-flex);
|
||||
|
||||
.#{$ns}TimelineItem-line {
|
||||
position: absolute;
|
||||
height: var(--TimelineItem--left-line-width);
|
||||
width: calc(100% - var(--TimelineItem--left-line-left));
|
||||
left: var(--TimelineItem--left-line-top);
|
||||
top: var(--TimelineItem--left-line-left);
|
||||
background-color: var(--TimelineItem--line-bg);
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem-round {
|
||||
position: absolute;
|
||||
width: var(--TimelineItem--round-width);
|
||||
height: var(--TimelineItem--round-height);
|
||||
left: var(--TimelineItem--round-top);
|
||||
top: var(--TimelineItem--round-left);
|
||||
background: var(--TimelineItem-round-bg);
|
||||
border-radius: var(--TimelineItem--round-radius);
|
||||
|
||||
&--danger {
|
||||
background: var(--Timeline--danger-bg);
|
||||
}
|
||||
|
||||
&--info {
|
||||
background: var(--Timeline--info-bg);
|
||||
}
|
||||
|
||||
&--success {
|
||||
background: var(--Timeline--success-bg);
|
||||
}
|
||||
|
||||
&--warning {
|
||||
background: var(--Timeline--warning-bg);
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}TimelineItem-icon {
|
||||
position: absolute;
|
||||
width: var(--TimelineItem--icon-width);
|
||||
height: var(--TimelineItem--icon-height);
|
||||
left: var(--TimelineItem--icon-left);
|
||||
border-radius: var(--TimelineItem--icon-radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -166,4 +166,46 @@ $link-color: $info;
|
||||
--Table-onChecked-borderColor: var(--Table-borderColor);
|
||||
|
||||
--Switch-bgColor: #bfbfbf;
|
||||
|
||||
// timeline
|
||||
--TimelineItem--axle-flex: 0 0 #{px2rem(24px)};
|
||||
--TimelineItem--left-line-width: #{px2rem(2px)};
|
||||
--TimelineItem--left-line-left: #{px2rem(13px)};
|
||||
--TimelineItem--left-line-top: #{px2rem(20px)};
|
||||
--TimelineItem--round-width: #{px2rem(8px)};
|
||||
--TimelineItem--round-height: #{px2rem(8px)};
|
||||
--TimelineItem--round-left: #{px2rem(10px)};
|
||||
--TimelineItem--round-top: #{px2rem(8px)};
|
||||
--TimelineItem--icon-width: #{px2rem(16px)};
|
||||
--TimelineItem--icon-height: #{px2rem(16px)};
|
||||
--TimelineItem--icon-left: #{px2rem(6px)};
|
||||
--TimelineItem--content-padding-bottom: #{px2rem(16px)};
|
||||
--TimelineItem--content-margin-left: #{px2rem(8px)};
|
||||
--TimelineItem--content-time-margin-bottom: #{px2rem(4px)};
|
||||
--TimelineItem--content-title-margin-bottom: #{px2rem(4px)};
|
||||
--TimelineItem--detail-button-margin-bottom: #{px2rem(8px)};
|
||||
--TimelineItem-detail-arrow-width: #{px2rem(16px)};
|
||||
--TimelineItem-detail-visible-padding: #{px2rem(10px)};
|
||||
--TimelineItem-detail-visible-max-width: #{px2rem(300px)};
|
||||
--Timeline-alternate-margin-left: #{px2rem(24px)};
|
||||
|
||||
--TimelineItem--icon-radius: 50%;
|
||||
--TimelineItem--round-radius: 50%;
|
||||
--TimelineItem--content-radius: #{px2rem(2px)};
|
||||
|
||||
--TimelineItem-detail-visible-shadow: 0 #{px2rem(1px)} #{px2rem(10px)} 0 rgba(0 0 0 / 10%);
|
||||
|
||||
--TimelineItem--font-size: #{px2rem(12px)};
|
||||
|
||||
--TimelineItem--text-primary-color: #151a26;
|
||||
--TimelineItem--text-secondary-color: #83868c;
|
||||
--TimelineItem--detail-button-color: var(--primary);
|
||||
--TimelineItem--line-bg: #e6e6e8;
|
||||
--TimelineItem--content-bg: #f2f2f4;
|
||||
--TimelineItem-round-bg: #dadbdd;
|
||||
|
||||
--Timeline--success-bg: var(--success);
|
||||
--Timeline--info-bg: var(--info);
|
||||
--Timeline--warning-bg: var(--warning);
|
||||
--Timeline--danger-bg: var(--danger);
|
||||
}
|
||||
|
@ -119,5 +119,6 @@
|
||||
@import '../components/link';
|
||||
@import '../components/mapping';
|
||||
@import '../components/formula';
|
||||
@import '../components/timeline';
|
||||
|
||||
@import '../utilities';
|
||||
|
@ -613,6 +613,49 @@ $L1: 0px 4px 6px 0px rgba(8, 14, 26, 0.06),
|
||||
|
||||
--Rating-inactive-color: #{$G9};
|
||||
|
||||
// timeline
|
||||
--TimelineItem--axle-flex: 0 0 #{px2rem(24px)};
|
||||
--TimelineItem--left-line-width: #{px2rem(2px)};
|
||||
--TimelineItem--left-line-left: #{px2rem(13px)};
|
||||
--TimelineItem--left-line-top: #{px2rem(20px)};
|
||||
--TimelineItem--round-width: #{px2rem(8px)};
|
||||
--TimelineItem--round-height: #{px2rem(8px)};
|
||||
--TimelineItem--round-left: #{px2rem(10px)};
|
||||
--TimelineItem--round-top: #{px2rem(8px)};
|
||||
--TimelineItem--icon-width: #{px2rem(16px)};
|
||||
--TimelineItem--icon-height: #{px2rem(16px)};
|
||||
--TimelineItem--icon-left: #{px2rem(6px)};
|
||||
--TimelineItem--content-padding-bottom: #{px2rem(16px)};
|
||||
--TimelineItem--content-margin-left: #{px2rem(8px)};
|
||||
--TimelineItem--content-time-margin-bottom: #{px2rem(4px)};
|
||||
--TimelineItem--content-title-margin-bottom: #{px2rem(4px)};
|
||||
--TimelineItem--detail-button-margin-bottom: #{px2rem(8px)};
|
||||
--TimelineItem-detail-arrow-width: #{px2rem(16px)};
|
||||
--TimelineItem-detail-visible-padding: #{px2rem(10px)};
|
||||
--TimelineItem-detail-visible-max-width: #{px2rem(300px)};
|
||||
--Timeline-alternate-margin-left: #{px2rem(24px)};
|
||||
|
||||
--TimelineItem--icon-radius: #{$R8};
|
||||
--TimelineItem--round-radius: #{$R8};
|
||||
--TimelineItem--content-radius: #{$R2};
|
||||
|
||||
--TimelineItem-detail-visible-shadow: 0 #{px2rem(1px)} #{px2rem(10px)} 0 rgba(0 0 0 / 10%);
|
||||
|
||||
--TimelineItem--font-size: #{$T2};
|
||||
|
||||
--TimelineItem--text-primary-color: #{$text-color};
|
||||
--TimelineItem--text-secondary-color: #{$G5};
|
||||
--TimelineItem--detail-button-color: var(--primary);
|
||||
--TimelineItem--line-bg: #{$G9};
|
||||
--TimelineItem--content-bg: #{$G10};
|
||||
--TimelineItem-round-bg: #{$G8};
|
||||
|
||||
--Timeline--success-bg: var(--success);
|
||||
--Timeline--info-bg: var(--info);
|
||||
--Timeline--warning-bg: var(--warning);
|
||||
--Timeline--danger-bg: var(--danger);
|
||||
|
||||
|
||||
// Formula
|
||||
--Formula-header-bgColor: #{$G10};
|
||||
--Formula-funcItem-bgColor-onActive: #{$light};
|
||||
|
@ -56,6 +56,7 @@ import {PaginationSchema} from './renderers/Pagination';
|
||||
import {AnchorNavSchema} from './renderers/AnchorNav';
|
||||
import {AvatarSchema} from './renderers/Avatar';
|
||||
import {StepsSchema} from './renderers/Steps';
|
||||
import {TimelineSchema} from './renderers/Timeline';
|
||||
import {ArrayControlSchema} from './renderers/Form/InputArray';
|
||||
import {ButtonGroupControlSchema} from './renderers/Form/ButtonGroupSelect';
|
||||
import {ChainedSelectControlSchema} from './renderers/Form/ChainedSelect';
|
||||
@ -201,6 +202,7 @@ export type SchemaType =
|
||||
| 'web-component'
|
||||
| 'anchor-nav'
|
||||
| 'steps'
|
||||
| 'timeline'
|
||||
| 'control'
|
||||
| 'input-array'
|
||||
| 'button'
|
||||
@ -387,6 +389,7 @@ export type SchemaObject =
|
||||
| AnchorNavSchema
|
||||
| StepsSchema
|
||||
| PortletSchema
|
||||
| TimelineSchema
|
||||
|
||||
// 表单项
|
||||
| FormControlSchema
|
||||
|
31
src/components/Timeline.tsx
Normal file
31
src/components/Timeline.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import {themeable, ThemeProps} from '../theme';
|
||||
import TimelineItem, {TimelineItemProps} from './TimelineItem';
|
||||
|
||||
export interface TimelineProps extends ThemeProps {
|
||||
items: Array<TimelineItemProps>;
|
||||
direction?: 'vertical' | 'horizontal';
|
||||
reverse?: boolean;
|
||||
mode?: 'left' | 'right' | 'alternate';
|
||||
}
|
||||
|
||||
export function Timeline(props: TimelineProps) {
|
||||
const {
|
||||
items,
|
||||
classnames: cx,
|
||||
direction = 'vertical',
|
||||
reverse = false,
|
||||
mode = 'right',
|
||||
} = props;
|
||||
|
||||
const timelineDatasource = items?.slice();
|
||||
|
||||
reverse && timelineDatasource?.reverse();
|
||||
|
||||
return (
|
||||
<div className={cx('Timeline', `Timeline-${direction}`, `Timeline-${mode}`)}>
|
||||
{timelineDatasource?.map((item: TimelineItemProps) => <TimelineItem {...item} />)}
|
||||
</div>)
|
||||
}
|
||||
|
||||
export default themeable(Timeline);
|
107
src/components/TimelineItem.tsx
Normal file
107
src/components/TimelineItem.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import React, {ReactNode, useState} from 'react';
|
||||
import {localeable, LocaleProps} from '../locale';
|
||||
import {SchemaExpression} from '../Schema';
|
||||
import {themeable, ThemeProps} from '../theme';
|
||||
import {Icon} from './icons';
|
||||
|
||||
export interface TimelineItemProps {
|
||||
/**
|
||||
* 时间点
|
||||
*/
|
||||
time: string;
|
||||
|
||||
/**
|
||||
* 事件名称
|
||||
*/
|
||||
title?: string| ReactNode;
|
||||
|
||||
/**
|
||||
* 详细内容
|
||||
*/
|
||||
detail?: string;
|
||||
|
||||
/**
|
||||
* detail折叠时文案
|
||||
*/
|
||||
detailCollapsedText?: string;
|
||||
|
||||
/**
|
||||
* detail展开时文案
|
||||
*/
|
||||
detailExpandedText?: string;
|
||||
|
||||
/**
|
||||
* 时间点圆圈颜色,可传入英文/颜色值/level样式(info、success、warning、danger)
|
||||
*/
|
||||
color?: SchemaExpression;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
icon?: string | ReactNode;
|
||||
}
|
||||
|
||||
export interface TimelineItem extends ThemeProps, LocaleProps, TimelineItemProps {}
|
||||
|
||||
export function TimelineItem(props: TimelineItem) {
|
||||
const {
|
||||
time,
|
||||
title,
|
||||
detail,
|
||||
detailCollapsedText,
|
||||
detailExpandedText,
|
||||
color,
|
||||
icon,
|
||||
classnames: cx,
|
||||
translate: __,
|
||||
} = props;
|
||||
|
||||
const [detailVisible, setDetailVisible] = useState<boolean>(false);
|
||||
|
||||
const renderDetail = (detail: string, detailCollapsedText: string = __('Timeline.collapseText'), detailExpandedText: string = __('Timeline.expandText')) : ReactNode => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={cx('TimelineItem-detail-button')} onClick={() => setDetailVisible(!detailVisible)}>
|
||||
{detailVisible ? detailExpandedText : detailCollapsedText}
|
||||
<div className={cx('TimelineItem-detail-arrow', `${detailVisible && 'TimelineItem-detail-arrow-top'}`)}>
|
||||
<Icon icon="tree-down"/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={cx(`${detailVisible ? 'TimelineItem-detail-visible' : 'TimelineItem-detail-invisible'}`)}>
|
||||
{detail}
|
||||
</div>
|
||||
</>);
|
||||
}
|
||||
|
||||
// 判断是否为颜色值
|
||||
const isColorVal = color && /^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/.test(color);
|
||||
|
||||
// 取level级颜色
|
||||
const levelColor = !isColorVal && color;
|
||||
|
||||
return (
|
||||
<div className={cx('TimelineItem')}>
|
||||
<div className={cx('TimelineItem-axle')}>
|
||||
<div className={cx('TimelineItem-line')}></div>
|
||||
{icon
|
||||
? <div className={cx('TimelineItem-icon')}>
|
||||
<Icon icon={icon} className="icon"/>
|
||||
</div>
|
||||
: <div
|
||||
className={cx('TimelineItem-round',
|
||||
levelColor && `TimelineItem-round--${levelColor}`)}
|
||||
style={isColorVal ? {backgroundColor: color} : undefined}
|
||||
></div>
|
||||
}
|
||||
</div>
|
||||
<div className={cx('TimelineItem-content')}>
|
||||
<div className={cx('TimelineItem-time')}>{time}</div>
|
||||
<div className={cx('TimelineItem-title')}>{title}</div>
|
||||
{detail
|
||||
&& <div className={cx('TimelineItem-detail')}>{renderDetail(detail, detailCollapsedText, detailExpandedText)}</div>}
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
|
||||
export default themeable(localeable(TimelineItem));
|
@ -43,6 +43,7 @@ import ExchangeIcon from '../icons/exchange.svg';
|
||||
import ColmunsIcon from '../icons/columns.svg';
|
||||
import CalendarIcon from '../icons/calendar.svg';
|
||||
import ClockIcon from '../icons/clock.svg';
|
||||
import TreeDownIcon from '../icons/tree-down.svg';
|
||||
|
||||
import CopyIcon from '../icons/copy.svg';
|
||||
import FilterIcon from '../icons/filter.svg';
|
||||
@ -176,6 +177,7 @@ registerIcon('alert-success', AlertSuccess);
|
||||
registerIcon('alert-info', AlertInfo);
|
||||
registerIcon('alert-warning', AlertWarning);
|
||||
registerIcon('alert-danger', AlertDanger);
|
||||
registerIcon('tree-down', TreeDownIcon);
|
||||
|
||||
export function Icon({
|
||||
icon,
|
||||
|
5
src/icons/tree-down.svg
Normal file
5
src/icons/tree-down.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1024 1024" width="30"
|
||||
fill="currentColor">
|
||||
<path d="M512 704L256 384h512z"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 155 B |
@ -165,6 +165,7 @@ import './renderers/Icon';
|
||||
import './renderers/Carousel';
|
||||
import './renderers/AnchorNav';
|
||||
import './renderers/Steps';
|
||||
import './renderers/Timeline';
|
||||
import './renderers/Markdown';
|
||||
import './renderers/TableView';
|
||||
import './renderers/Code';
|
||||
|
@ -254,5 +254,7 @@ register('de-DE', {
|
||||
'Condition.cond_placeholder': 'Bedingung auswählen',
|
||||
'Condition.field_placeholder': 'Feld auswählen',
|
||||
'Condition.blank': 'leer',
|
||||
'InputTable.uniqueError': 'Column `{{label}}` unique validate failed'
|
||||
'InputTable.uniqueError': 'Column `{{label}}` unique validate failed',
|
||||
'Timeline.collapseText': 'Entfalten',
|
||||
'Timeline.expandText': 'Falten',
|
||||
});
|
||||
|
@ -256,5 +256,7 @@ register('en-US', {
|
||||
'Condition.cond_placeholder': 'select condition',
|
||||
'Condition.field_placeholder': 'select field',
|
||||
'Condition.blank': 'blank',
|
||||
'InputTable.uniqueError': 'Column `{{label}}` unique validate failed'
|
||||
'InputTable.uniqueError': 'Column `{{label}}` unique validate failed',
|
||||
'Timeline.collapseText': 'Unfold',
|
||||
'Timeline.expandText': 'Fold',
|
||||
});
|
||||
|
@ -260,5 +260,7 @@ register('zh-CN', {
|
||||
'Condition.cond_placeholder': '请选择操作',
|
||||
'Condition.field_placeholder': '请选择字段',
|
||||
'Condition.blank': '空',
|
||||
'InputTable.uniqueError': '列`{{label}}`没有通过唯一验证'
|
||||
'InputTable.uniqueError': '列`{{label}}`没有通过唯一验证',
|
||||
'Timeline.collapseText': '展开',
|
||||
'Timeline.expandText': '折叠',
|
||||
});
|
||||
|
141
src/renderers/Timeline.tsx
Normal file
141
src/renderers/Timeline.tsx
Normal file
@ -0,0 +1,141 @@
|
||||
import React from 'react';
|
||||
import {Renderer, RendererProps} from '../factory';
|
||||
import {BaseSchema, SchemaApi, SchemaCollection, SchemaTokenizeableString} from '../Schema';
|
||||
import {resolveVariable} from '../utils/tpl-builtin';
|
||||
import Timeline from '../components/Timeline';
|
||||
import {filter} from '../utils/tpl';
|
||||
import { RemoteOptionsProps, withRemoteConfig } from '../components/WithRemoteConfig';
|
||||
|
||||
export interface TimelineItemSchema extends Omit<BaseSchema, 'type'> {
|
||||
/**
|
||||
* 时间点
|
||||
*/
|
||||
time: string;
|
||||
|
||||
/**
|
||||
* 时间节点标题
|
||||
*/
|
||||
title?: SchemaCollection;
|
||||
|
||||
/**
|
||||
* 详细内容
|
||||
*/
|
||||
detail?: string;
|
||||
|
||||
/**
|
||||
* detail折叠时文案
|
||||
*/
|
||||
detailCollapsedText?: string;
|
||||
|
||||
/**
|
||||
* detail展开时文案
|
||||
*/
|
||||
detailExpandedText?: string;
|
||||
|
||||
/**
|
||||
* 时间点圆圈颜色
|
||||
*/
|
||||
color?: string;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
icon?: SchemaCollection;
|
||||
}
|
||||
|
||||
export interface TimelineSchema extends BaseSchema {
|
||||
/**
|
||||
* 指定为 Timeline 时间轴渲染器
|
||||
*/
|
||||
type: 'timeline';
|
||||
|
||||
/**
|
||||
* 节点数据
|
||||
*/
|
||||
items?: Array<TimelineItemSchema>;
|
||||
|
||||
/**
|
||||
* API 或 数据映射
|
||||
*/
|
||||
source?: SchemaApi | SchemaTokenizeableString;
|
||||
|
||||
/**
|
||||
* 文字相对于时间轴展示方向
|
||||
*/
|
||||
mode?: 'left' | 'right' | 'alternate';
|
||||
|
||||
/**
|
||||
* 展示方向
|
||||
*/
|
||||
direction?: 'horizontal' | 'vertical'
|
||||
|
||||
/**
|
||||
* 节点倒序
|
||||
*/
|
||||
reverse?: boolean,
|
||||
}
|
||||
|
||||
export interface TimelineProps
|
||||
extends RendererProps,
|
||||
Omit<TimelineSchema, 'className'> {}
|
||||
|
||||
export function TimelineCmpt(props: TimelineProps) {
|
||||
const {
|
||||
items,
|
||||
mode,
|
||||
direction,
|
||||
reverse,
|
||||
data,
|
||||
config,
|
||||
source,
|
||||
render
|
||||
} = props;
|
||||
|
||||
// 获取源数据
|
||||
const timelineItemsRow: Array<TimelineItemSchema> = config || items || [];
|
||||
|
||||
// 渲染内容
|
||||
const resolveRender = (region: string, val?: SchemaCollection) => typeof val === 'string'
|
||||
? filter(val, data)
|
||||
: (val && render(region, val));
|
||||
|
||||
// 处理源数据
|
||||
const resolveTimelineItems =
|
||||
timelineItemsRow?.map((timelineItem: TimelineItemSchema) => {
|
||||
return {
|
||||
...timelineItem,
|
||||
icon: resolveRender('icon', timelineItem.icon),
|
||||
title: resolveRender('title', timelineItem.title),
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return (<Timeline
|
||||
items = {resolveTimelineItems}
|
||||
direction = {direction}
|
||||
reverse = {reverse}
|
||||
mode = {mode}
|
||||
/>)
|
||||
}
|
||||
|
||||
const TimelineWithRemoteConfig = withRemoteConfig({
|
||||
adaptor: data => data.items || data
|
||||
})(
|
||||
class extends React.Component<
|
||||
RemoteOptionsProps & React.ComponentProps<typeof TimelineCmpt>
|
||||
> {
|
||||
render() {
|
||||
const {config, deferLoad, loading, updateConfig, ...rest} = this.props;
|
||||
return <TimelineCmpt config={config} {...rest} />;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@Renderer({
|
||||
type: 'timeline'
|
||||
})
|
||||
export class TimelineRenderer extends React.Component<TimelineProps> {
|
||||
render() {
|
||||
return <TimelineWithRemoteConfig {...this.props} />;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user