mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
Merge branch 'baidu:master' into fix-affixHeader-table-hide-nested-table-header
This commit is contained in:
commit
522c393ae9
2
LICENSE
2
LICENSE
@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Copyright 2023 Baidu
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -3149,7 +3149,7 @@ itemAction 里的 onClick 还能通过 `data` 参数拿到当前行的数据,
|
||||
| -------------- | ----------------------------------------------------------------------- | -------------------- |
|
||||
| selectedChange | `selectedItems: item[]` 已选择行<br/>`unSelectedItems: item[]` 未选择行 | 手动选择表格项时触发 |
|
||||
| columnSort | `orderBy: string` 列排序列名<br/>`orderDir: string` 列排序值 | 点击列排序时触发 |
|
||||
| columnFilter | `filterName: string` 列筛选列名<br/>`filterValue: string` 列筛选值 | 点击列筛选时触发 |
|
||||
| columnFilter | `filterName: string` 列筛选列名<br/>`filterValue: string \| undefined` 列筛选值 | 点击列筛选时触发,点击重置后事件参数`filterValue`为`undefined` |
|
||||
| columnSearch | `searchName: string` 列搜索列名<br/>`searchValue: object` 列搜索数据 | 点击列搜索时触发 |
|
||||
| orderChange | `movedItems: item[]` 已排序数据 | 手动拖拽行排序时触发 |
|
||||
| columnToggled | `columns: item[]` 当前显示的列配置数据 | 点击自定义列时触发 |
|
||||
|
@ -156,6 +156,102 @@ IconSchema 配置
|
||||
| -------- | ------------------------------------ | ---------------- |
|
||||
| change | `[name]: string \| boolean` 组件的值 | 开关值变化时触发 |
|
||||
|
||||
### change
|
||||
|
||||
switch 值更新时弹出确认提示,确认后发送请求。
|
||||
|
||||
```schema: scope="body"
|
||||
{
|
||||
"type": "crud",
|
||||
"syncLocation": false,
|
||||
"api": "/api/mock2/sample",
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"label": "ID",
|
||||
"id": "u:daa79afa2e53"
|
||||
},
|
||||
{
|
||||
"name": "engine",
|
||||
"label": "Rendering engine",
|
||||
"id": "u:3343cf518656"
|
||||
},
|
||||
{
|
||||
"name": "browser",
|
||||
"label": "Browser",
|
||||
"id": "u:fbdc85e45e2f"
|
||||
},
|
||||
{
|
||||
"name": "platform",
|
||||
"label": "Platform(s)",
|
||||
"id": "u:ccdb48cc1804"
|
||||
},
|
||||
{
|
||||
"name": "switch",
|
||||
"label": "开关",
|
||||
"id": "u:30a36768acce",
|
||||
"type": "switch",
|
||||
"inline": true,
|
||||
"onEvent": {
|
||||
"change": {
|
||||
"weight": 0,
|
||||
"actions": [
|
||||
{
|
||||
"actionType": "confirmDialog",
|
||||
"dialog": {
|
||||
"type": "dialog",
|
||||
"title": "弹框标题",
|
||||
"body": [
|
||||
{
|
||||
"type": "tpl",
|
||||
"tpl": "确定要修改${id}吗?",
|
||||
"wrapperComponent": "",
|
||||
"inline": false,
|
||||
"id": "u:1965506c7599"
|
||||
}
|
||||
],
|
||||
"showCloseButton": true,
|
||||
"showErrorMsg": true,
|
||||
"showLoading": true,
|
||||
"className": "app-popover",
|
||||
"id": "u:d9783223df98",
|
||||
"actions": [
|
||||
{
|
||||
"type": "button",
|
||||
"actionType": "cancel",
|
||||
"label": "取消",
|
||||
"id": "u:302efee8613b"
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"actionType": "confirm",
|
||||
"label": "确定",
|
||||
"primary": true,
|
||||
"id": "u:4a4d63cf35e1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"actionType": "ajax",
|
||||
"outputVar": "responseResult",
|
||||
"options": {
|
||||
},
|
||||
"api": {
|
||||
"method": "get",
|
||||
"url": "/api/mock2/form/saveForm"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"id": "u:6c781a765f97"
|
||||
}
|
||||
```
|
||||
|
||||
## 动作表
|
||||
|
||||
当前组件对外暴露以下特性动作,其他组件可以通过指定`actionType: 动作名称`、`componentId: 该组件id`来触发这些动作,动作配置可以通过`args: {动作配置项名称: xxx}`来配置具体的参数,详细请查看[事件动作](../../docs/concepts/event-action#触发其他组件的动作)。
|
||||
@ -163,3 +259,52 @@ IconSchema 配置
|
||||
| 动作名称 | 动作配置 | 说明 |
|
||||
| -------- | ------------------------------------- | -------- |
|
||||
| setValue | `value: string \| boolean` 更新的数据 | 更新数据 |
|
||||
|
||||
### setValue
|
||||
|
||||
```schema: scope="body"
|
||||
[
|
||||
{
|
||||
"type": "button",
|
||||
"label": "修改开关的值",
|
||||
"className": "mb-2",
|
||||
"onEvent": {
|
||||
"click": {
|
||||
"actions": [
|
||||
{
|
||||
"componentId": "u:6613bfa3a18e",
|
||||
"actionType": "setValue",
|
||||
"args": {
|
||||
"value": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "u:9d7d695145bb"
|
||||
},
|
||||
{
|
||||
"type": "form",
|
||||
"title": "表单",
|
||||
"debug": true,
|
||||
"body": [
|
||||
{
|
||||
"label": "开启",
|
||||
"type": "switch",
|
||||
"name": "switch",
|
||||
"id": "u:6613bfa3a18e",
|
||||
"value": false,
|
||||
"mode": "inline"
|
||||
}
|
||||
],
|
||||
"id": "u:82d44e407eb0",
|
||||
"actions": [
|
||||
{
|
||||
"type": "submit",
|
||||
"label": "提交",
|
||||
"primary": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
@ -175,6 +175,9 @@ let amisScoped = amis.embed(
|
||||
// 用来判断是否目标地址当前地址。
|
||||
// isCurrentUrl: url => {},
|
||||
//
|
||||
// 用来配置弹窗等组件的挂载位置
|
||||
// getModalContainer: () => document.getElementsByTagName('body')[0],
|
||||
//
|
||||
// 用来实现复制到剪切板
|
||||
// copy: content => {},
|
||||
//
|
||||
@ -533,6 +536,10 @@ class MyComponent extends React.Component<any, any> {
|
||||
// // 地址替换,跟 jumpTo 类似
|
||||
// },
|
||||
|
||||
// getModalContainer: () => {
|
||||
// // 弹窗挂载的 DOM 节点
|
||||
// },
|
||||
|
||||
// isCurrentUrl: (
|
||||
// url: string /*url地址*/,
|
||||
// ) => {
|
||||
|
@ -318,6 +318,7 @@ export const runAction = async (
|
||||
// 二次确认弹窗如果取消,则终止后续动作
|
||||
if (action?.actionType === 'confirmDialog' && !actionResult) {
|
||||
stopped = true;
|
||||
preventDefault = true; // 这种对表单项change比较有意义,例如switch切换时弹确认弹窗,如果取消后不能把switch修改了
|
||||
}
|
||||
|
||||
let stopPropagation = false;
|
||||
|
@ -847,7 +847,9 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
this.hooks['validate'] || [],
|
||||
forceValidate,
|
||||
throwErrors,
|
||||
__(filter(messages && messages.validateFailed, store.data))
|
||||
typeof messages?.validateFailed === 'string'
|
||||
? __(filter(messages.validateFailed, store.data))
|
||||
: undefined
|
||||
)
|
||||
.then((result: boolean) => {
|
||||
if (result) {
|
||||
@ -892,7 +894,9 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
return store.submit(
|
||||
fn,
|
||||
this.hooks['validate'] || [],
|
||||
__(filter(messages && messages.validateFailed, store.data)),
|
||||
typeof messages?.validateFailed === 'string'
|
||||
? __(filter(messages.validateFailed, store.data))
|
||||
: undefined,
|
||||
validateErrCb,
|
||||
throwErrors
|
||||
);
|
||||
@ -1172,8 +1176,14 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
|
||||
return store
|
||||
.saveRemote(action.api || (api as Api), values, {
|
||||
successMessage: filter(saveSuccess, store.data),
|
||||
errorMessage: filter(saveFailed, store.data),
|
||||
successMessage:
|
||||
typeof saveSuccess === 'string'
|
||||
? filter(saveSuccess, store.data)
|
||||
: undefined,
|
||||
errorMessage:
|
||||
typeof saveFailed === 'string'
|
||||
? filter(saveFailed, store.data)
|
||||
: undefined,
|
||||
onSuccess: async (result: Payload) => {
|
||||
// result为提交接口返回的内容
|
||||
const dispatcher = await dispatchEvent(
|
||||
@ -1303,20 +1313,21 @@ export default class Form extends React.Component<FormProps, object> {
|
||||
if (!isEffectiveApi(action.api)) {
|
||||
return env.alert(__(`当 actionType 为 ajax 时,请设置 api 属性`));
|
||||
}
|
||||
let successMsg =
|
||||
(action.messages && action.messages.success) || saveSuccess;
|
||||
let failMsg = (action.messages && action.messages.failed) || saveFailed;
|
||||
|
||||
return store
|
||||
.saveRemote(action.api as Api, data, {
|
||||
successMessage: __(
|
||||
filter(
|
||||
(action.messages && action.messages.success) || saveSuccess,
|
||||
store.data
|
||||
)
|
||||
typeof successMsg === 'string'
|
||||
? filter(successMsg, store.data)
|
||||
: undefined
|
||||
),
|
||||
errorMessage: __(
|
||||
filter(
|
||||
(action.messages && action.messages.failed) || saveFailed,
|
||||
store.data
|
||||
)
|
||||
typeof failMsg === 'string'
|
||||
? filter(failMsg, store.data)
|
||||
: undefined
|
||||
)
|
||||
})
|
||||
.then(async response => {
|
||||
|
@ -56,6 +56,7 @@
|
||||
font-size: 12px;
|
||||
color: var(--text--muted-color);
|
||||
pointer-events: none;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
|
@ -2,164 +2,7 @@ import 'amis';
|
||||
import './locale/index';
|
||||
export * from 'amis-editor-core';
|
||||
import './tpl/index';
|
||||
|
||||
// 布局容器
|
||||
import './plugin/Flex'; // flex布局
|
||||
import './plugin/Grid'; // 分栏
|
||||
import './plugin/Container'; // 容器
|
||||
import './plugin/Layout/Layout_free_container'; // 自由容器
|
||||
import './plugin/Layout/Layout_sorption_container'; // 吸附容器
|
||||
import './plugin/Layout/Layout_fixed'; // 悬浮容器
|
||||
// import './plugin/Layout/Layout1_2_v4';
|
||||
import './plugin/CollapseGroup'; // 折叠面板
|
||||
import './plugin/Panel'; // 面板
|
||||
import './plugin/Tabs'; // 选项卡
|
||||
|
||||
// 数据容器
|
||||
import './plugin/CRUD'; // 增删改查
|
||||
import './plugin/CRUD2';
|
||||
import './plugin/Form/Form'; // 表单
|
||||
import './plugin/Service'; // 服务service
|
||||
|
||||
// 表单项
|
||||
import './plugin/Form/InputText'; // 文本框
|
||||
import './plugin/Form/Textarea'; // 多行文本框
|
||||
import './plugin/Form/InputNumber'; // 数字框
|
||||
import './plugin/Form/Select'; // 下拉框
|
||||
import './plugin/Form/NestedSelect'; // 级联选择器
|
||||
import './plugin/Form/ChainedSelect'; // 链式下拉框
|
||||
import './plugin/DropDownButton'; // 下拉按钮
|
||||
import './plugin/Form/Checkboxes'; // 复选框
|
||||
import './plugin/Form/Radios'; // 单选框
|
||||
import './plugin/Form/Checkbox'; // 勾选框
|
||||
import './plugin/Form/InputDate'; // 日期
|
||||
import './plugin/Form/InputDateRange'; // 日期范围
|
||||
import './plugin/Form/InputFile'; // 文件上传
|
||||
import './plugin/Form/InputImage'; // 图片上传
|
||||
import './plugin/Form/InputExcel'; // 上传 Excel
|
||||
import './plugin/Form/InputTree'; // 树选择器
|
||||
import './plugin/Form/InputTag'; // 标签选择器
|
||||
import './plugin/Form/ListSelect'; // 列表选择
|
||||
import './plugin/Form/ButtonGroupSelect'; // 按钮点选
|
||||
import './plugin/Form/ButtonToolbar'; // 按钮工具栏
|
||||
import './plugin/Form/Picker'; // 列表选取
|
||||
import './plugin/Form/Switch'; // 开关
|
||||
import './plugin/Form/InputRange'; // 滑块
|
||||
import './plugin/Form/InputRating'; // 评分
|
||||
import './plugin/Form/InputCity'; // 城市选择
|
||||
import './plugin/Form/Transfer'; // 穿梭器
|
||||
import './plugin/Form/TabsTransfer'; // 组合穿梭器
|
||||
import './plugin/Form/InputColor'; // 颜色框
|
||||
import './plugin/Form/ConditionBuilder'; // 条件组合
|
||||
import './plugin/Form/FieldSet'; // 字段集
|
||||
import './plugin/Form/Combo'; // 组合输入
|
||||
import './plugin/Form/InputGroup'; // 输入组合
|
||||
import './plugin/Form/InputTable'; // 表格编辑器
|
||||
import './plugin/Form/MatrixCheckboxes'; // 矩阵开关
|
||||
import './plugin/Form/InputRichText'; // 富文本编辑器
|
||||
import './plugin/Form/DiffEditor'; // diff编辑器
|
||||
import './plugin/Form/CodeEditor'; // 代码编辑器
|
||||
import './plugin/SearchBox'; // 搜索框
|
||||
import './plugin/Form/InputKV'; // KV键值对
|
||||
import './plugin/Form/InputRepeat'; // 重复周期
|
||||
import './plugin/Form/UUID'; // UUID
|
||||
import './plugin/Form/LocationPicker'; // 地理位置
|
||||
import './plugin/Form/InputSubForm'; // 子表单项
|
||||
import './plugin/Form/Hidden'; // 隐藏域
|
||||
|
||||
// 功能
|
||||
import './plugin/Button'; // 按钮
|
||||
import './plugin/ButtonGroup'; // 按钮组
|
||||
import './plugin/Nav'; // 导航
|
||||
import './plugin/AnchorNav'; // 锚点导航
|
||||
import './plugin/TooltipWrapper'; // 文字提示
|
||||
import './plugin/Alert'; // 提示
|
||||
import './plugin/Wizard'; // 向导
|
||||
import './plugin/TableView'; // 表格视图
|
||||
import './plugin/WebComponent';
|
||||
import './plugin/Audio'; // 音频
|
||||
import './plugin/Video'; // 视频
|
||||
import './plugin/Custom'; // 自定义代码
|
||||
import './plugin/Tasks'; // 异步任务
|
||||
import './plugin/Each'; // 循环
|
||||
import './plugin/Property'; // 属性表
|
||||
import './plugin/IFrame';
|
||||
import './plugin/QRCode'; // 二维码
|
||||
|
||||
// 展示
|
||||
import './plugin/Tpl'; // 文字
|
||||
import './plugin/Icon'; // 图标
|
||||
import './plugin/Link'; // 链接
|
||||
import './plugin/List'; // 列表
|
||||
import './plugin/Mapping'; // 映射
|
||||
import './plugin/Avatar'; // 头像
|
||||
import './plugin/Card'; // 卡片
|
||||
import './plugin/Card2';
|
||||
import './plugin/Cards'; // 卡片列表
|
||||
import './plugin/Table'; // 表格
|
||||
import './plugin/Table2';
|
||||
import './plugin/TableCell2'; // 列配置
|
||||
import './plugin/Chart'; // 图表
|
||||
import './plugin/Sparkline'; // 走势图
|
||||
import './plugin/Carousel'; // 轮播图
|
||||
import './plugin/Image'; // 图片展示
|
||||
import './plugin/Images'; // 图片集
|
||||
import './plugin/Time'; // 时间展示
|
||||
import './plugin/Date'; // 日期展示
|
||||
import './plugin/Datetime'; // 日期时间展示
|
||||
import './plugin/Tag'; // 标签
|
||||
import './plugin/Json'; // JSON展示
|
||||
import './plugin/Progress'; // 进度展示
|
||||
import './plugin/Status'; // 状态展示
|
||||
import './plugin/Steps'; // 步骤条
|
||||
import './plugin/Timeline'; // 时间轴
|
||||
import './plugin/Divider'; // 分隔线
|
||||
import './plugin/CodeView'; // 代码高亮
|
||||
import './plugin/Markdown';
|
||||
import './plugin/Collapse'; // 折叠器
|
||||
import './plugin/OfficeViewer'; // 文档预览
|
||||
import './plugin/Log'; // 日志
|
||||
|
||||
// 其他
|
||||
import './plugin/Others/Action';
|
||||
import './plugin/Others/TableCell';
|
||||
import './plugin/Form/InputArray';
|
||||
import './plugin/Form/ConditionBuilder';
|
||||
import './plugin/Form/Control';
|
||||
import './plugin/Form/InputDateTime';
|
||||
import './plugin/Form/InputDateTimeRange';
|
||||
import './plugin/Form/InputEmail';
|
||||
import './plugin/Form/Formula';
|
||||
import './plugin/Form/Group';
|
||||
import './plugin/Form/Item';
|
||||
import './plugin/Form/InputMonth';
|
||||
import './plugin/Form/InputMonthRange';
|
||||
import './plugin/Form/InputPassword';
|
||||
import './plugin/Form/InputQuarter';
|
||||
import './plugin/Form/InputQuarterRange';
|
||||
import './plugin/Form/Static';
|
||||
import './plugin/Form/InputTime';
|
||||
import './plugin/Form/InputTimeRange';
|
||||
import './plugin/Form/TreeSelect';
|
||||
import './plugin/Form/InputURL';
|
||||
import './plugin/Form/InputYear';
|
||||
import './plugin/Form/InputYearRange';
|
||||
import './plugin/Breadcrumb';
|
||||
import './plugin/CustomRegion';
|
||||
import './plugin/Dialog';
|
||||
import './plugin/Drawer';
|
||||
import './plugin/HBox';
|
||||
import './plugin/ListItem';
|
||||
import './plugin/Operation';
|
||||
import './plugin/Page';
|
||||
import './plugin/Pagination';
|
||||
import './plugin/Plain';
|
||||
import './plugin/Reset';
|
||||
import './plugin/Submit';
|
||||
import './plugin/Wrapper';
|
||||
import './plugin/ColumnToggler';
|
||||
|
||||
import {GridPlugin} from './plugin/Grid';
|
||||
export * from './plugin';
|
||||
|
||||
import './renderer/OptionControl';
|
||||
import './renderer/NavSourceControl';
|
||||
@ -210,5 +53,3 @@ import 'amis-theme-editor/lib/renderers.css';
|
||||
|
||||
export * from './component/BaseControl';
|
||||
export * from './icons/index';
|
||||
|
||||
export {GridPlugin};
|
||||
|
@ -160,6 +160,7 @@ export class CarouselPlugin extends BasePlugin {
|
||||
(item: {
|
||||
html?: string;
|
||||
image?: string;
|
||||
href?: string;
|
||||
title?: string;
|
||||
titleClassName?: string;
|
||||
description?: string;
|
||||
@ -174,6 +175,7 @@ export class CarouselPlugin extends BasePlugin {
|
||||
type: 'image',
|
||||
content: item.image,
|
||||
title: item.title,
|
||||
href: item.href,
|
||||
titleClassName: item.titleClassName,
|
||||
description: item.description,
|
||||
descriptionClassName: item.descriptionClassName
|
||||
@ -187,6 +189,7 @@ export class CarouselPlugin extends BasePlugin {
|
||||
(item: {
|
||||
type: string;
|
||||
content: string;
|
||||
href?: string;
|
||||
title?: string;
|
||||
titleClassName?: string;
|
||||
description?: string;
|
||||
@ -198,6 +201,7 @@ export class CarouselPlugin extends BasePlugin {
|
||||
}
|
||||
: {
|
||||
image: item.content,
|
||||
href: item.href,
|
||||
title: item.title,
|
||||
titleClassName: item.titleClassName,
|
||||
description: item.description,
|
||||
|
@ -249,6 +249,25 @@ export class CheckboxesControlPlugin extends BasePlugin {
|
||||
originalValue: node.schema?.value // 记录原始值,循环引用检测需要
|
||||
};
|
||||
|
||||
if (node.schema?.joinValues === false) {
|
||||
dataSchema = {
|
||||
...dataSchema,
|
||||
type: 'object',
|
||||
title: node.schema?.label || node.schema?.name,
|
||||
properties: {
|
||||
label: {
|
||||
type: 'string',
|
||||
title: '文本'
|
||||
},
|
||||
value: {
|
||||
type,
|
||||
title: '值'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (node.schema?.multiple) {
|
||||
if (node.schema?.extractValue) {
|
||||
dataSchema = {
|
||||
type: 'array',
|
||||
@ -261,20 +280,12 @@ export class CheckboxesControlPlugin extends BasePlugin {
|
||||
items: {
|
||||
type: 'object',
|
||||
title: '成员',
|
||||
properties: {
|
||||
label: {
|
||||
type: 'string',
|
||||
title: '文本'
|
||||
},
|
||||
value: {
|
||||
type,
|
||||
title: '值'
|
||||
}
|
||||
}
|
||||
properties: dataSchema.properties
|
||||
},
|
||||
originalValue: dataSchema.originalValue
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return dataSchema;
|
||||
}
|
||||
|
160
packages/amis-editor/src/plugin/index.ts
Normal file
160
packages/amis-editor/src/plugin/index.ts
Normal file
@ -0,0 +1,160 @@
|
||||
// 布局容器
|
||||
export * from './Flex'; // flex布局
|
||||
export * from './Grid'; // 分栏
|
||||
export * from './Container'; // 容器
|
||||
export * from './Layout/Layout_free_container'; // 自由容器
|
||||
export * from './Layout/Layout_sorption_container'; // 吸附容器
|
||||
export * from './Layout/Layout_fixed'; // 悬浮容器
|
||||
// export * from './Layout/Layout1_2_v4';
|
||||
export * from './CollapseGroup'; // 折叠面板
|
||||
export * from './Panel'; // 面板
|
||||
export * from './Tabs'; // 选项卡
|
||||
|
||||
// 数据容器
|
||||
export * from './CRUD'; // 增删改查
|
||||
export {
|
||||
TableCRUDPlugin,
|
||||
ListCRUDPlugin,
|
||||
CardsCRUDPlugin,
|
||||
CRUDPlugin as CRUD2Plugin
|
||||
} from './CRUD2';
|
||||
export * from './Form/Form'; // 表单
|
||||
export * from './Service'; // 服务service
|
||||
|
||||
// 表单项
|
||||
export * from './Form/InputText'; // 文本框
|
||||
export * from './Form/Textarea'; // 多行文本框
|
||||
export * from './Form/InputNumber'; // 数字框
|
||||
export * from './Form/Select'; // 下拉框
|
||||
export * from './Form/NestedSelect'; // 级联选择器
|
||||
export * from './Form/ChainedSelect'; // 链式下拉框
|
||||
export * from './DropDownButton'; // 下拉按钮
|
||||
export * from './Form/Checkboxes'; // 复选框
|
||||
export * from './Form/Radios'; // 单选框
|
||||
export * from './Form/Checkbox'; // 勾选框
|
||||
export * from './Form/InputDate'; // 日期
|
||||
export * from './Form/InputDateRange'; // 日期范围
|
||||
export * from './Form/InputFile'; // 文件上传
|
||||
export * from './Form/InputImage'; // 图片上传
|
||||
export * from './Form/InputExcel'; // 上传 Excel
|
||||
export * from './Form/InputTree'; // 树选择器
|
||||
export * from './Form/InputTag'; // 标签选择器
|
||||
export * from './Form/ListSelect'; // 列表选择
|
||||
export * from './Form/ButtonGroupSelect'; // 按钮点选
|
||||
export * from './Form/ButtonToolbar'; // 按钮工具栏
|
||||
export * from './Form/Picker'; // 列表选取
|
||||
export * from './Form/Switch'; // 开关
|
||||
export * from './Form/InputRange'; // 滑块
|
||||
export * from './Form/InputRating'; // 评分
|
||||
export * from './Form/InputCity'; // 城市选择
|
||||
export * from './Form/Transfer'; // 穿梭器
|
||||
export * from './Form/TabsTransfer'; // 组合穿梭器
|
||||
export * from './Form/InputColor'; // 颜色框
|
||||
export * from './Form/ConditionBuilder'; // 条件组合
|
||||
export * from './Form/FieldSet'; // 字段集
|
||||
export * from './Form/Combo'; // 组合输入
|
||||
export * from './Form/InputGroup'; // 输入组合
|
||||
export * from './Form/InputTable'; // 表格编辑器
|
||||
export * from './Form/MatrixCheckboxes'; // 矩阵开关
|
||||
export * from './Form/InputRichText'; // 富文本编辑器
|
||||
export * from './Form/DiffEditor'; // diff编辑器
|
||||
export * from './Form/CodeEditor'; // 代码编辑器
|
||||
export * from './SearchBox'; // 搜索框
|
||||
export * from './Form/InputKV'; // KV键值对
|
||||
export * from './Form/InputRepeat'; // 重复周期
|
||||
export * from './Form/UUID'; // UUID
|
||||
export * from './Form/LocationPicker'; // 地理位置
|
||||
export * from './Form/InputSubForm'; // 子表单项
|
||||
export * from './Form/Hidden'; // 隐藏域
|
||||
|
||||
// 功能
|
||||
export * from './Button'; // 按钮
|
||||
export * from './ButtonGroup'; // 按钮组
|
||||
export * from './Nav'; // 导航
|
||||
export * from './AnchorNav'; // 锚点导航
|
||||
export * from './TooltipWrapper'; // 文字提示
|
||||
export * from './Alert'; // 提示
|
||||
export * from './Wizard'; // 向导
|
||||
export * from './TableView'; // 表格视图
|
||||
export * from './WebComponent';
|
||||
export * from './Audio'; // 音频
|
||||
export * from './Video'; // 视频
|
||||
export * from './Custom'; // 自定义代码
|
||||
export * from './Tasks'; // 异步任务
|
||||
export * from './Each'; // 循环
|
||||
export * from './Property'; // 属性表
|
||||
export * from './IFrame';
|
||||
export * from './QRCode'; // 二维码
|
||||
|
||||
// 展示
|
||||
export * from './Tpl'; // 文字
|
||||
export * from './Icon'; // 图标
|
||||
export * from './Link'; // 链接
|
||||
export * from './List'; // 列表
|
||||
export * from './Mapping'; // 映射
|
||||
export * from './Avatar'; // 头像
|
||||
export * from './Card'; // 卡片
|
||||
export * from './Card2';
|
||||
export * from './Cards'; // 卡片列表
|
||||
export * from './Table'; // 表格
|
||||
export * from './Table2';
|
||||
export * from './TableCell2'; // 列配置
|
||||
export * from './Chart'; // 图表
|
||||
export * from './Sparkline'; // 走势图
|
||||
export * from './Carousel'; // 轮播图
|
||||
export * from './Image'; // 图片展示
|
||||
export * from './Images'; // 图片集
|
||||
export * from './Time'; // 时间展示
|
||||
export * from './Date'; // 日期展示
|
||||
export * from './Datetime'; // 日期时间展示
|
||||
export * from './Tag'; // 标签
|
||||
export * from './Json'; // JSON展示
|
||||
export * from './Progress'; // 进度展示
|
||||
export * from './Status'; // 状态展示
|
||||
export * from './Steps'; // 步骤条
|
||||
export * from './Timeline'; // 时间轴
|
||||
export * from './Divider'; // 分隔线
|
||||
export * from './CodeView'; // 代码高亮
|
||||
export * from './Markdown';
|
||||
export * from './Collapse'; // 折叠器
|
||||
export * from './OfficeViewer'; // 文档预览
|
||||
export * from './Log'; // 日志
|
||||
|
||||
// 其他
|
||||
export * from './Others/Action';
|
||||
export * from './Others/TableCell';
|
||||
export * from './Form/InputArray';
|
||||
export * from './Form/ConditionBuilder';
|
||||
export * from './Form/Control';
|
||||
export * from './Form/InputDateTime';
|
||||
export * from './Form/InputDateTimeRange';
|
||||
export * from './Form/InputEmail';
|
||||
export * from './Form/Formula';
|
||||
export * from './Form/Group';
|
||||
export * from './Form/Item';
|
||||
export * from './Form/InputMonth';
|
||||
export * from './Form/InputMonthRange';
|
||||
export * from './Form/InputPassword';
|
||||
export * from './Form/InputQuarter';
|
||||
export * from './Form/InputQuarterRange';
|
||||
export * from './Form/Static';
|
||||
export * from './Form/InputTime';
|
||||
export * from './Form/InputTimeRange';
|
||||
export * from './Form/TreeSelect';
|
||||
export * from './Form/InputURL';
|
||||
export * from './Form/InputYear';
|
||||
export * from './Form/InputYearRange';
|
||||
export * from './Breadcrumb';
|
||||
export {CustomPlugin as CustomRegionPlugin} from './CustomRegion';
|
||||
export * from './Dialog';
|
||||
export * from './Drawer';
|
||||
export * from './HBox';
|
||||
export * from './ListItem';
|
||||
export * from './Operation';
|
||||
export * from './Page';
|
||||
export * from './Pagination';
|
||||
export * from './Plain';
|
||||
export * from './Reset';
|
||||
export * from './Submit';
|
||||
export * from './Wrapper';
|
||||
export * from './ColumnToggler';
|
@ -29,7 +29,7 @@ export interface TimelineItemProps extends FormControlProps {
|
||||
|
||||
export interface TimelineItemState {
|
||||
items: Array<Partial<TimelineItem>>;
|
||||
source: 'custom' | 'api';
|
||||
source: 'custom' | 'api' | 'variable';
|
||||
api: SchemaApi;
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ export default class TimelineItemControl extends React.Component<
|
||||
* 切换选项类型
|
||||
*/
|
||||
@autobind
|
||||
handleSourceChange(source: 'custom' | 'api') {
|
||||
handleSourceChange(source: 'custom' | 'api' | 'variable') {
|
||||
this.setState({source: source}, this.onChange);
|
||||
}
|
||||
|
||||
@ -80,6 +80,11 @@ export default class TimelineItemControl extends React.Component<
|
||||
data.items = items.map(item => ({...item}));
|
||||
data.source = api;
|
||||
}
|
||||
if (source === 'variable') {
|
||||
const {items, api} = this.state;
|
||||
data.items = items.map(item => ({...item}));
|
||||
data.source = api;
|
||||
}
|
||||
onBulkChange && onBulkChange(data);
|
||||
}
|
||||
|
||||
@ -339,10 +344,14 @@ export default class TimelineItemControl extends React.Component<
|
||||
{
|
||||
label: '接口获取',
|
||||
value: 'api'
|
||||
},
|
||||
{
|
||||
label: '上下文变量',
|
||||
value: 'variable'
|
||||
}
|
||||
] as Array<{
|
||||
label: string;
|
||||
value: 'custom' | 'api';
|
||||
value: 'custom' | 'api' | 'variable';
|
||||
}>
|
||||
).map(item => ({
|
||||
...item,
|
||||
@ -562,6 +571,19 @@ export default class TimelineItemControl extends React.Component<
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{source === 'variable'
|
||||
? render(
|
||||
'variable',
|
||||
getSchemaTpl('sourceBindControl', {
|
||||
label: false,
|
||||
className: 'ae-ExtendMore'
|
||||
}),
|
||||
{
|
||||
onChange: this.handleAPIChange
|
||||
}
|
||||
)
|
||||
: null}
|
||||
{this.renderApiPanel()}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,6 +1,4 @@
|
||||
.#{$ns}Chart {
|
||||
min-width: 300px;
|
||||
min-height: 300px;
|
||||
position: relative;
|
||||
|
||||
&-placeholder {
|
||||
|
@ -31,6 +31,7 @@ class InputInner extends React.Component<InputProps, InputState> {
|
||||
@autobind
|
||||
handleComposition(e: React.CompositionEvent<HTMLInputElement>) {
|
||||
this.isOnComposition = e.type !== 'compositionend';
|
||||
|
||||
if (!this.isOnComposition) {
|
||||
this.handleChange(e as any);
|
||||
}
|
||||
@ -47,6 +48,17 @@ class InputInner extends React.Component<InputProps, InputState> {
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
handleKeyDown(e: React.KeyboardEvent<any>) {
|
||||
const {onKeyDown} = this.props;
|
||||
|
||||
if (this.isOnComposition) {
|
||||
return;
|
||||
}
|
||||
|
||||
onKeyDown?.(e);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {forwardedRef, ...rest} = this.props;
|
||||
|
||||
@ -57,6 +69,7 @@ class InputInner extends React.Component<InputProps, InputState> {
|
||||
value={this.state.value}
|
||||
ref={forwardedRef}
|
||||
onChange={this.handleChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onCompositionStart={this.handleComposition}
|
||||
onCompositionUpdate={this.handleComposition}
|
||||
onCompositionEnd={this.handleComposition}
|
||||
|
@ -8,6 +8,7 @@ import {uncontrollable} from 'amis-core';
|
||||
import {autobind, isMobile} from 'amis-core';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
import chain from 'lodash/chain';
|
||||
import Input from './Input';
|
||||
|
||||
export interface HistoryRecord {
|
||||
/** 历史记录值 */
|
||||
@ -306,17 +307,17 @@ export class SearchBox extends React.Component<SearchBoxProps, SearchBoxState> {
|
||||
)}
|
||||
style={style}
|
||||
>
|
||||
<input
|
||||
<Input
|
||||
name={name}
|
||||
ref={this.inputRef}
|
||||
disabled={disabled}
|
||||
placeholder={__(placeholder || 'placeholder.enter')}
|
||||
value={inputValue ?? ''}
|
||||
autoComplete="off"
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
onChange={this.handleChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
value={inputValue ?? ''}
|
||||
disabled={disabled}
|
||||
placeholder={__(placeholder || 'placeholder.enter')}
|
||||
autoComplete="off"
|
||||
/>
|
||||
|
||||
{!mini && clearable && inputValue && !disabled ? (
|
||||
|
@ -0,0 +1,102 @@
|
||||
import {fireEvent, render, waitFor} from '@testing-library/react';
|
||||
import '../../../../src';
|
||||
import {render as amisRender} from '../../../../src';
|
||||
import {makeEnv, wait} from '../../../helper';
|
||||
|
||||
test('EventAction:switch', async () => {
|
||||
const fetcher = jest.fn().mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
status: 0,
|
||||
msg: 'ok',
|
||||
data: {
|
||||
id: 1
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const {container, getByText} = render(
|
||||
amisRender(
|
||||
{
|
||||
type: 'page',
|
||||
body: [
|
||||
{
|
||||
type: 'button',
|
||||
label: '修改开关的值',
|
||||
className: 'mb-2',
|
||||
onEvent: {
|
||||
click: {
|
||||
actions: [
|
||||
{
|
||||
componentId: 'u:6613bfa3a18e',
|
||||
actionType: 'setValue',
|
||||
args: {
|
||||
value: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
id: 'u:9d7d695145bb'
|
||||
},
|
||||
{
|
||||
type: 'form',
|
||||
title: '表单',
|
||||
debug: true,
|
||||
body: [
|
||||
{
|
||||
label: '开启',
|
||||
type: 'switch',
|
||||
name: 'switch',
|
||||
id: 'u:6613bfa3a18e',
|
||||
value: false,
|
||||
mode: 'inline',
|
||||
onEvent: {
|
||||
change: {
|
||||
actions: [
|
||||
{
|
||||
actionType: 'ajax',
|
||||
api: '/api/mock2/form/saveForm?switch=${switch}'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
id: 'u:82d44e407eb0',
|
||||
actions: [
|
||||
{
|
||||
type: 'submit',
|
||||
label: '提交',
|
||||
primary: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{},
|
||||
makeEnv({
|
||||
fetcher
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText('修改开关的值')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.click(getByText(/修改开关的值/));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector('.is-checked')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.click(container.querySelector('.is-checked')!);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(fetcher).toHaveBeenCalled();
|
||||
expect(fetcher.mock.calls[0][0].url).toEqual(
|
||||
'/api/mock2/form/saveForm?switch=false'
|
||||
);
|
||||
});
|
||||
});
|
@ -118,7 +118,7 @@ test('Renderer:Carousel with name & option config', async () => {
|
||||
imageClassName: 'thisisimageClassName',
|
||||
title: '这是标题',
|
||||
titleClassName: 'thiisistitleClassName',
|
||||
description: '描述', // description 属性没用
|
||||
description: '这是描述',
|
||||
href: 'https://www.baidu.com'
|
||||
},
|
||||
{
|
||||
@ -146,6 +146,7 @@ test('Renderer:Carousel with name & option config', async () => {
|
||||
'https://www.baidu.com'
|
||||
);
|
||||
expect(item).toHaveTextContent('这是标题');
|
||||
expect(item).toHaveTextContent('这是描述');
|
||||
|
||||
fireEvent.click(container.querySelector('.cxd-Carousel-dot:nth-child(2)')!);
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
* 7. 默认展开 defaultIsOpened
|
||||
* 8. 对齐方式 align
|
||||
* 9. block & size
|
||||
* 10. buttons level & className
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
@ -396,3 +397,36 @@ test('Renderer:dropdown-button with block & size', async () => {
|
||||
);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Renderer:dropdown-button buttons with className & level', async () => {
|
||||
const {container} = render(
|
||||
amisRender({
|
||||
type: "dropdown-button",
|
||||
level: "success",
|
||||
label: "下拉菜单",
|
||||
buttons: [
|
||||
{
|
||||
type: "button",
|
||||
label: "按钮1",
|
||||
level: "success",
|
||||
className: "custom-button-class"
|
||||
},
|
||||
{
|
||||
type: "button",
|
||||
label: "按钮2"
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
|
||||
const dropdownButton = document.querySelector('button.cxd-Button');
|
||||
fireEvent.click(dropdownButton as HTMLDivElement);
|
||||
|
||||
expect(container.querySelectorAll('.cxd-DropDown-menu-root .cxd-DropDown-button')[0]!).toHaveClass(
|
||||
'cxd-Button--success'
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll('.cxd-DropDown-menu-root .cxd-DropDown-button')[0]!).toHaveClass(
|
||||
'custom-button-class'
|
||||
);
|
||||
});
|
||||
|
@ -6,10 +6,11 @@
|
||||
* 3. 可清除 clearable
|
||||
* 4. mini 模式
|
||||
* 5. 立即搜索 searchImediately、样式 className
|
||||
* 6. Composition触发
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {fireEvent, render} from '@testing-library/react';
|
||||
import {fireEvent, render, screen} from '@testing-library/react';
|
||||
import '../../src';
|
||||
import {render as amisRender} from '../../src';
|
||||
import {makeEnv, wait} from '../helper';
|
||||
@ -36,7 +37,7 @@ test('Renderer:Searchbox', async () => {
|
||||
)
|
||||
);
|
||||
|
||||
await wait(200);
|
||||
await wait(500);
|
||||
|
||||
expect(fetcher).toHaveBeenCalledTimes(1);
|
||||
expect(fetcher.mock.calls[0][0].query).toEqual({
|
||||
@ -212,3 +213,34 @@ test('Renderer:Searchbox with searchImediately & className', async () => {
|
||||
keywords: 'aabb'
|
||||
});
|
||||
});
|
||||
|
||||
test('6. Renderer: Searchbox is not supposed to be triggered with composition input', async () => {
|
||||
const onQuery = jest.fn();
|
||||
const {container} = render(amisRender({
|
||||
type: "search-box",
|
||||
name: "keywords",
|
||||
}, {
|
||||
onQuery
|
||||
}))
|
||||
|
||||
const inputEl = container.querySelector('.cxd-SearchBox input')!;
|
||||
expect(inputEl).toBeInTheDocument();
|
||||
|
||||
/** 第一次输入 Enter 后,文本填入 */
|
||||
|
||||
fireEvent.compositionStart(inputEl);
|
||||
fireEvent.keyDown(inputEl, { key: 'Enter', keyCode: 13 });
|
||||
await wait(200);
|
||||
expect(onQuery).not.toHaveBeenCalled();
|
||||
|
||||
/** 退出输入法,触发搜索 */
|
||||
fireEvent.compositionEnd(inputEl);
|
||||
fireEvent.change(inputEl, {target: {value: 'test'}});
|
||||
fireEvent.keyDown(inputEl, { key: 'Enter', keyCode: 13 });
|
||||
await wait(200);
|
||||
|
||||
expect(onQuery).toHaveBeenCalledTimes(1);
|
||||
expect(onQuery.mock.calls[0][0]).toEqual({
|
||||
keywords: 'test'
|
||||
});
|
||||
})
|
||||
|
@ -32,6 +32,12 @@ exports[`Renderer:Carousel 1`] = `
|
||||
>
|
||||
标题
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Image-caption"
|
||||
title="描述"
|
||||
>
|
||||
描述
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -22,6 +22,7 @@ exports[`Renderer:Searchbox 1`] = `
|
||||
autocomplete="off"
|
||||
name="keywords"
|
||||
placeholder="请输入"
|
||||
type="text"
|
||||
value="searchkey"
|
||||
/>
|
||||
<a
|
||||
@ -49,6 +50,7 @@ exports[`Renderer:Searchbox with clearable 1`] = `
|
||||
autocomplete="off"
|
||||
name="keywords"
|
||||
placeholder="请输入"
|
||||
type="text"
|
||||
value="tmpvalue"
|
||||
/>
|
||||
<div
|
||||
@ -93,6 +95,7 @@ exports[`Renderer:Searchbox with enhance 1`] = `
|
||||
autocomplete="off"
|
||||
name="keywords"
|
||||
placeholder="请输入"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<a
|
||||
@ -120,6 +123,7 @@ exports[`Renderer:Searchbox with mini 1`] = `
|
||||
autocomplete="off"
|
||||
name="keywords"
|
||||
placeholder="请输入"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<a
|
||||
@ -143,6 +147,7 @@ exports[`Renderer:Searchbox with mini 2`] = `
|
||||
autocomplete="off"
|
||||
name="keywords"
|
||||
placeholder="请输入"
|
||||
type="text"
|
||||
value="what?"
|
||||
/>
|
||||
<a
|
||||
|
@ -146,7 +146,7 @@ const defaultSchema = {
|
||||
href={data.href}
|
||||
blank={data.blank}
|
||||
htmlTarget={data.htmlTarget}
|
||||
imageCaption={data.description}
|
||||
caption={data.description}
|
||||
thumbMode={data.thumbMode ?? thumbMode ?? 'contain'}
|
||||
imageMode="original"
|
||||
className={cx('Carousel-image')}
|
||||
|
@ -596,9 +596,8 @@ export class Chart extends React.Component<ChartProps> {
|
||||
data
|
||||
} = this.props;
|
||||
let style = this.props.style || {};
|
||||
|
||||
width && (style.width = width);
|
||||
height && (style.height = height);
|
||||
style.width = style.width || width || '100%';
|
||||
style.height = style.width || height || '300px';
|
||||
const styleVar = buildStyle(style, data);
|
||||
|
||||
return (
|
||||
|
@ -279,15 +279,25 @@ export default class DropDownButton extends React.Component<
|
||||
return (
|
||||
<li
|
||||
key={index}
|
||||
className={cx('DropDown-button', {
|
||||
className={cx(
|
||||
'DropDown-button',
|
||||
{
|
||||
['is-disabled']: isDisabled(button, data)
|
||||
})}
|
||||
},
|
||||
typeof button.level === 'undefined'
|
||||
? ''
|
||||
: button.level
|
||||
? `Button--${button.level}`
|
||||
: '',
|
||||
button.className
|
||||
)}
|
||||
>
|
||||
{render(
|
||||
`button/${index}`,
|
||||
{
|
||||
type: 'button',
|
||||
...(button as any)
|
||||
...(button as any),
|
||||
className: ''
|
||||
},
|
||||
{
|
||||
isMenuItem: true,
|
||||
|
@ -255,8 +255,21 @@ export class HeadCellFilterDropDown extends React.Component<
|
||||
});
|
||||
}
|
||||
|
||||
handleReset() {
|
||||
const {name, onQuery} = this.props;
|
||||
async handleReset() {
|
||||
const {name, dispatchEvent, data, onQuery} = this.props;
|
||||
|
||||
const rendererEvent = await dispatchEvent(
|
||||
'columnFilter',
|
||||
createObject(data, {
|
||||
filterName: name,
|
||||
filterValue: undefined
|
||||
})
|
||||
);
|
||||
|
||||
if (rendererEvent?.prevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
onQuery(
|
||||
{
|
||||
[name]: undefined
|
||||
|
@ -16,7 +16,6 @@ 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'> {
|
||||
/**
|
||||
@ -149,7 +148,7 @@ export function TimelineCmpt(props: TimelineProps) {
|
||||
typeof val === 'string' ? filter(val, data) : val && render(region, val);
|
||||
|
||||
// 处理源数据
|
||||
const resolveTimelineItems: Array<TimelineItemProps> = (items || []).map(
|
||||
const resolveTimelineItems = (items || []).map(
|
||||
(timelineItem: TimelineItemSchema, index: number) => {
|
||||
const {
|
||||
icon,
|
||||
|
Loading…
Reference in New Issue
Block a user