feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
140
frontend/packages/workflow/setters/src/array/array.tsx
Normal file
140
frontend/packages/workflow/setters/src/array/array.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { IconCozPlus, IconCozMinus } from '@coze-arch/coze-design/icons';
|
||||
import { IconButton } from '@coze-arch/coze-design';
|
||||
|
||||
import type { Setter } from '../types';
|
||||
import type { Field } from './types';
|
||||
import { ColumnTitles } from './column-titles';
|
||||
import { ArraySetterItemContextProvider } from './array-context';
|
||||
|
||||
import styles from './array.module.less';
|
||||
|
||||
export interface ArrayOptions {
|
||||
disableAdd?: boolean;
|
||||
getDefaultAppendValue?: () => any;
|
||||
fields?: Field[];
|
||||
|
||||
/** 入参最大数量,若没有提供,默认为整数最大值 */
|
||||
maxItems?: number;
|
||||
|
||||
/** 入参最小数量,若没有提供,默认为 0 */
|
||||
minItems?: number;
|
||||
|
||||
/** 单条是否可删除 */
|
||||
disableDeleteItem?: ((value: unknown, index: number) => boolean) | boolean;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
export const Array: Setter<Array<any>, ArrayOptions> = ({
|
||||
value = [],
|
||||
readonly = false,
|
||||
children,
|
||||
onChange,
|
||||
context,
|
||||
disableAdd = false,
|
||||
getDefaultAppendValue,
|
||||
fields = [],
|
||||
maxItems = Number.MAX_SAFE_INTEGER,
|
||||
minItems = 0,
|
||||
disableDeleteItem = () => false,
|
||||
}) => {
|
||||
const [currentAddIndex, setCurrentAddIndex] = useState<number | undefined>();
|
||||
const { node, meta } = context || {};
|
||||
|
||||
// 后端返回的 value 可能为 null,此时不会赋值给 [],这里重新兜底下
|
||||
const originValue = value || [];
|
||||
|
||||
const add = () => {
|
||||
const defaultValue = getDefaultAppendValue?.() || {};
|
||||
setCurrentAddIndex(originValue.length);
|
||||
onChange?.([...originValue, defaultValue]);
|
||||
};
|
||||
|
||||
const remove = (index: number) => {
|
||||
const newValue = [...originValue];
|
||||
newValue.splice(index, 1);
|
||||
onChange?.(newValue);
|
||||
};
|
||||
|
||||
const showAddButton =
|
||||
!disableAdd && !readonly && originValue?.length < maxItems;
|
||||
|
||||
const calcShowDeleteButton = (item: unknown, index: number) => {
|
||||
const globalEnableDelete = !readonly && originValue?.length > minItems;
|
||||
|
||||
if (typeof disableDeleteItem === 'undefined') {
|
||||
return globalEnableDelete;
|
||||
}
|
||||
|
||||
if (typeof disableDeleteItem === 'boolean') {
|
||||
return globalEnableDelete && !disableDeleteItem;
|
||||
}
|
||||
return globalEnableDelete && !disableDeleteItem(item, index);
|
||||
};
|
||||
|
||||
const columns = [...fields, ...(readonly ? [] : [{ label: '', width: 24 }])];
|
||||
|
||||
return (
|
||||
<div className={styles.array}>
|
||||
<div className={styles.content}>
|
||||
{fields.length > 0 && <ColumnTitles columns={columns} />}
|
||||
{React.Children.toArray(children).map((child, index) => {
|
||||
const showDeleteButton = calcShowDeleteButton(
|
||||
originValue[index],
|
||||
index,
|
||||
);
|
||||
return (
|
||||
<ArraySetterItemContextProvider
|
||||
value={{
|
||||
currentAddIndex,
|
||||
currentIndex: index,
|
||||
}}
|
||||
>
|
||||
<div className={styles['array-item']}>
|
||||
<div className={styles.child}>{child}</div>
|
||||
{showDeleteButton ? (
|
||||
<IconButton
|
||||
className="!block ml-1"
|
||||
icon={<IconCozMinus className="text-sm" />}
|
||||
size="small"
|
||||
color="secondary"
|
||||
onClick={() => remove(index)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</ArraySetterItemContextProvider>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{showAddButton ? (
|
||||
<IconButton
|
||||
color="highlight"
|
||||
size="small"
|
||||
className="absolute -top-8 right-0"
|
||||
icon={<IconCozPlus />}
|
||||
onClick={() => add()}
|
||||
data-testid={`playground.node.${node?.id}.${meta?.name}.addbutton`}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user