feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View 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>
);
};