feat: timeline组件支持detailClaaName/titleClassName/timeClassName & 关联上下文& 可视化配置 (#7654)

Co-authored-by: yanglu19 <yanglu19@baidu.com>
This commit is contained in:
Dora 2023-08-08 10:04:04 +08:00 committed by GitHub
parent f6a35036e0
commit 34403e5b8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 271 additions and 37 deletions

View File

@ -329,7 +329,9 @@ order: 73
}
```
## 远程数据
## 动态数据
### 远程数据
```schema
{
@ -391,25 +393,85 @@ order: 73
}
```
### 数据域变量配置
> 3.4.0 及以上版本
```schema
{
"type": "page",
"data": {
"items": [
{
"title": "First",
"time": "this is subTitle",
"detail": "this is description"
},
{
"title": "Second"
},
{
"title": "Last"
}
]
},
"body": [
{
"type": "timeline",
"source": "${items}"
}
]
}
```
```schema
{
"type": "page",
"data": {
"items":
{
"First": "this is subTitle",
"detail": "this is description",
"title": "Second"
}
}
,
"body": [
{
"type": "timeline",
"source": "${items}"
}
]
}
```
## 属性表
| 属性名 | 类型 | 默认值 | 说明 |
| --------- | ------------------------------------- | ---------- | ----------------------------------------------------------- |
| 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` | 根据时间倒序显示 |
| 属性名 | 类型 | 默认值 | 说明 |
| --------------- | --------------------------------------------------------------------------------- | ---------- | ----------------------------------------------------------- |
| type | `string` | | `"timeline"` 指定为 时间轴 渲染器 |
| items | Array<[timelineItem](#timeline.item)> | [] | 配置节点数据 |
| source | [API](../../../docs/types/api) 或 [数据映射](../../../docs/concepts/data-mapping) | | 数据源,可通过数据映射获取当前数据域变量、或者配置 API 对象 |
| mode | `left` \| `right` \| `alternate` | `right` | 指定文字相对于时间轴的位置,仅 direction=vertical 时支持 |
| direction | `vertical` \| `horizontal` | `vertical` | 时间轴方向 |
| reverse | `boolean` | `false` | 根据时间倒序显示 |
| iconClassName | `string` | | 统一配置的节点图标 CSS 类3.4.0 版本支持)名 |
| timeClassName | `string` | | 统一配置的节点时间 CSS 类3.4.0 版本支持)名 |
| titleClassName | `string` | | 统一配置的节点标题 CSS 类3.4.0 版本支持)名 |
| detailClassName | `string` | | 统一配置的节点详情 CSS 类3.4.0 版本支持)名 |
### 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 |
| 属性名 | 类型 | 默认值 | 说明 |
| ------------------- | ------------------------------------------------------- | --------- | ------------------------------------------------------------------------------- |
| 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 |
| iconClassName | `string` | | 节点图标的 CSS 类名(优先级高于统一配置的 iconClassName 3.4.0 版本支持)) |
| timeClassName | `string` | | 节点时间的 CSS 类名(优先级高于统一配置的 timeClassName3.4.0 版本支持)) |
| titleClassName | `string` | | 节点标题的 CSS 类名(优先级高于统一配置的 titleClassName3.4.0 版本支持)) |
| detailClassName | `string` | | 节点详情的 CSS 类名(优先级高于统一配置的 detailClassName3.4.0 版本支持)) |

View File

@ -136,7 +136,23 @@ export class TimelinePlugin extends BasePlugin {
title: '外观',
body: getSchemaTpl('collapseGroup', [
getSchemaTpl('style:classNames', {
isFormItem: false
isFormItem: false,
schema: [
getSchemaTpl('className', {
name: 'timeClassName',
label: '时间区'
}),
getSchemaTpl('className', {
name: 'titleClassName',
label: '标题区'
}),
getSchemaTpl('className', {
name: 'detailClassName',
label: '详情区'
})
]
})
])
}

View File

@ -77,6 +77,7 @@
.#{$ns}TimelineItem-title {
display: flex;
word-break: break-word;
color: var(--TimelineItem--text-primary-color);
font-size: var(--Timeline-title-fontSize);
font-weight: var(--Timeline-title-fontWeight);
@ -112,8 +113,8 @@
.#{$ns}TimelineItem-detail-visible {
display: block;
word-break: break-word;
border-radius: var(--Timeline-visible-border-radius);
max-width: var(--TimelineItem-detail-visible-max-width);
font-size: var(--Timeline-detail-content-fontSize);
font-weight: var(--Timeline-detail-content-fontWeight);
color: var(--Timeline-detail-content-color);
@ -131,6 +132,15 @@
&.#{$ns}Timeline-left {
.#{$ns}TimelineItem {
flex-direction: row-reverse;
.#{$ns}TimelineItem-title {
text-align: right;
}
.#{$ns}TimelineItem-content,
.#{$ns}TimelineItem-detail {
display: flex;
flex-direction: column;
align-items: flex-end;
}
}
}
@ -138,6 +148,15 @@
.#{$ns}TimelineItem:nth-child(odd) {
flex-direction: row-reverse;
max-width: 50%;
.#{$ns}TimelineItem-title {
text-align: right;
}
.#{$ns}TimelineItem-content,
.#{$ns}TimelineItem-detail {
display: flex;
flex-direction: column;
align-items: flex-end;
}
}
.#{$ns}TimelineItem:nth-child(even) {
@ -253,8 +272,8 @@
.#{$ns}TimelineItem-detail-visible {
display: block;
word-break: break-word;
border-radius: var(--Timeline-visible-border-radius);
max-width: var(--TimelineItem-detail-visible-max-width);
font-size: var(--Timeline-detail-content-fontSize);
font-weight: var(--Timeline-detail-content-fontWeight);
color: var(--Timeline-detail-content-color);

View File

@ -7,6 +7,10 @@ export interface TimelineProps extends ThemeProps {
direction?: 'vertical' | 'horizontal';
reverse?: boolean;
mode?: 'left' | 'right' | 'alternate';
iconClassName?: string;
timeClassName?: string;
titleClassName?: string;
detailClassName?: string;
}
export function Timeline(props: TimelineProps) {
@ -14,6 +18,11 @@ export function Timeline(props: TimelineProps) {
items,
style,
classnames: cx,
className,
iconClassName,
timeClassName,
titleClassName,
detailClassName,
direction = 'vertical',
reverse = false,
mode = 'right'
@ -25,11 +34,23 @@ export function Timeline(props: TimelineProps) {
return (
<div
className={cx('Timeline', `Timeline-${direction}`, `Timeline-${mode}`)}
className={cx(
'Timeline',
`Timeline-${direction}`,
`Timeline-${mode}`,
className
)}
style={style}
>
{timelineDatasource?.map((item: TimelineItemProps, index: number) => (
<TimelineItem {...item} key={`TimelineItem-${index}`} />
<TimelineItem
{...item}
key={`TimelineItem-${index}`}
iconClassName={item.iconClassName || iconClassName}
timeClassName={item.timeClassName || timeClassName}
titleClassName={item.titleClassName || titleClassName}
detailClassName={item.detailClassName || detailClassName}
/>
))}
</div>
);

View File

@ -43,6 +43,18 @@ export interface TimelineItemProps {
/** ICON的CSS类名 */
iconClassName?: string;
/**
* CSS类名 titleClassName
*/
timeClassName?: string;
/**
* CSS类名titleClassName
*/
titleClassName?: string;
/**
* CSS类名detailClassName
*/
detailClassName?: string;
}
export interface TimelineItem
@ -62,6 +74,9 @@ export function TimelineItem(props: TimelineItem) {
color,
icon,
iconClassName,
timeClassName,
titleClassName,
detailClassName,
classnames: cx,
translate: __,
classPrefix,
@ -97,7 +112,8 @@ export function TimelineItem(props: TimelineItem) {
detailVisible
? 'TimelineItem-detail-visible'
: 'TimelineItem-detail-invisible'
}`
}`,
detailClassName
)}
>
{detail}
@ -135,8 +151,8 @@ export function TimelineItem(props: TimelineItem) {
)}
</div>
<div className={cx('TimelineItem-content')}>
<div className={cx('TimelineItem-time')}>{time}</div>
<div className={cx('TimelineItem-title')}>{title}</div>
<div className={cx('TimelineItem-time', timeClassName)}>{time}</div>
<div className={cx('TimelineItem-title', titleClassName)}>{title}</div>
{detail && (
<div className={cx('TimelineItem-detail')}>
{renderDetail(detail, detailCollapsedText, detailExpandedText)}

View File

@ -229,9 +229,9 @@ test('Renderer:timeline itemTitleSchema', async () => {
type: 'timeline',
itemTitleSchema: [
{
type: "tpl",
type: 'tpl',
tpl: '<div class="itemSchemaClassName">${title}</div>'
},
}
],
items: [
{
@ -267,3 +267,51 @@ test('Renderer:timeline itemTitleSchema', async () => {
expect(container).toMatchSnapshot();
expect(container.querySelector('.itemSchemaClassName')).toBeInTheDocument();
});
test('Renderer:timeline detailClassName timeClassName', async () => {
const {container, getByText} = render(
amisRender(
{
type: 'timeline',
detailClassName: 'auto-detail-class',
items: [
{
time: '2019-02-07',
title: '节点数据',
detail: '#ffb200',
detailCollapsedText: 'detailCollapsedText',
detailExpandedText: 'detailExpandedText',
icon: 'close'
},
{
time: '2019-02-08',
title: '节点数据',
titleClassName: 'auto-item-title-class',
detail: '#4F86F4'
},
{
time: '2019-02-09',
title: '节点数据',
detail: 'success'
},
{
time: '2019-02-09',
title: '节点数据',
detail: 'warning'
}
]
},
{},
makeEnv()
)
);
fireEvent.click(getByText('detailExpandedText'));
const timelineDetail = () =>
container.querySelector('.cxd-TimelineItem-detail-visible')!;
expect(timelineDetail()).toHaveClass('auto-detail-class');
const timelineTitles = () =>
container.querySelectorAll('.cxd-TimelineItem-title')!;
expect(timelineTitles()[1]).toHaveClass('auto-item-title-class');
});

View File

@ -16,6 +16,7 @@ import type {
SchemaTokenizeableString
} from '../Schema';
import type {IconCheckedSchema} from 'amis-ui';
import {TimelineItemProps} from 'packages/amis-ui/src/components/TimelineItem';
export interface TimelineItemSchema extends Omit<BaseSchema, 'type'> {
/**
@ -57,6 +58,18 @@ export interface TimelineItemSchema extends Omit<BaseSchema, 'type'> {
* CSS类名
*/
iconClassName?: string;
/**
* CSS类名timeClassName
*/
timeClassName?: string;
/**
* CSS类名titleClassName
*/
titleClassName?: string;
/**
* CSS类名detailClassName
*/
detailClassName?: string;
}
export interface TimelineSchema extends BaseSchema {
@ -93,6 +106,22 @@ export interface TimelineSchema extends BaseSchema {
* title自定一展示模板
*/
itemTitleSchema?: SchemaCollection;
/**
* CSS类名
*/
iconClassName?: string;
/**
* CSS类名
*/
timeClassName?: string;
/**
* CSS类名
*/
titleClassName?: string;
/**
* CSS类名
*/
detailClassName?: string;
}
export interface TimelineProps
@ -107,27 +136,36 @@ export function TimelineCmpt(props: TimelineProps) {
direction,
reverse,
data,
config,
source,
itemTitleSchema,
className,
timeClassName,
titleClassName,
detailClassName,
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(
const resolveTimelineItems: Array<TimelineItemProps> = (items || []).map(
(timelineItem: TimelineItemSchema, index: number) => {
const {icon, iconClassName, title} = timelineItem;
const {
icon,
iconClassName,
title,
timeClassName,
titleClassName,
detailClassName
} = timelineItem;
return {
...timelineItem,
iconClassName,
timeClassName,
titleClassName,
detailClassName,
icon: isPureVariable(icon)
? resolveVariableAndFilter(icon, data, '| raw')
: icon,
@ -147,6 +185,10 @@ export function TimelineCmpt(props: TimelineProps) {
reverse={reverse}
mode={mode}
style={style}
className={className}
timeClassName={timeClassName}
titleClassName={titleClassName}
detailClassName={detailClassName}
/>
);
}
@ -158,8 +200,18 @@ const TimelineWithRemoteConfig = withRemoteConfig({
RemoteOptionsProps & React.ComponentProps<typeof TimelineCmpt>
> {
render() {
const {config, deferLoad, loading, updateConfig, ...rest} = this.props;
return <TimelineCmpt config={config} {...rest} />;
const {config, items, deferLoad, loading, updateConfig, ...rest} =
this.props;
let sourceItems: Array<TimelineItemSchema> = config
? Array.isArray(config)
? config
: Object.keys(config).map(key => ({
time: key,
title: config[key]
}))
: items || [];
return <TimelineCmpt items={sourceItems} {...rest} />;
}
}
);