chore: replace all cn comments of fe to en version by volc api (#320)

This commit is contained in:
tecvan
2025-07-31 10:32:15 +08:00
committed by GitHub
parent 716ec0cba8
commit 71f6245a01
2960 changed files with 15545 additions and 15545 deletions

View File

@@ -77,10 +77,10 @@ describe('SelectSpaceModal', () => {
wrapper.getByDisplayValue('mockBot(duplicate_rename_copy)'),
).toBeInTheDocument();
// 检查表单是否存在
// Check if the form exists
expect(wrapper.getByRole('form')).toBeInTheDocument();
// 检查确定和取消按钮
// Check OK and Cancel buttons
expect(
wrapper.getByRole('button', { name: 'confirm' }),
).toBeInTheDocument();

View File

@@ -28,23 +28,23 @@ import { CarouselItem } from './carousel-item';
import styles from './index.module.less';
export interface CarouselProps {
/** 元素布局行数默认为1 */
/** The number of rows in the element layout defaults to 1. */
rows?: number;
/** 元素布局列数,默认为均分数组 */
/** Number of element layout columns, the default is to divide the array equally */
column?: number;
/** 每次点击箭头滚动的百分比,0~1. 默认值为0.5 */
/** Percentage of scrolling per click of arrow, 0~ 1. Default value is 0.5 */
scrollStep?: number;
/** 滚动回调 */
/** scrolling callback */
onScroll?: () => void;
/** 箭头是否显示边框 */
/** Whether the arrow shows the border */
enableArrowBorder?: boolean;
/** 箭头是否显示阴影渐变 */
/** Whether the arrows show gradual changes in shadows */
enableArrowShalldow?: boolean;
/** 子元素样式 */
/** child style */
itemClassName?: string;
/** 左箭头样式 */
/** Left Arrow Style */
leftArrowClassName?: string;
/** 右箭头样式 */
/** Right Arrow Style */
rightArrowClassName?: string;
children: React.ReactNode;
}
@@ -156,7 +156,7 @@ export const Carousel: React.FC<CarouselProps> = ({
itemsContainerRef?.current?.scrollLeft !== undefined &&
itemsContainerRef?.current?.clientWidth
) {
// 部分浏览器不支持 scrollTo 方法
// Some browsers do not support the scrollTo method
itemsContainerRef.current.scrollTo?.({
left: Math.max(
itemsContainerRef.current.scrollLeft -
@@ -193,7 +193,7 @@ export const Carousel: React.FC<CarouselProps> = ({
scrollLeft;
const shouldShowArrowLeft = scrollLeft > 0;
// 极端场景下存在 1px 偏差
// There is a 1px bias in extreme scenarios
const shouldShowArrowRight = Math.abs(scrollRight) > 2;
setLeftArrowVisible(shouldShowArrowLeft);
@@ -204,7 +204,7 @@ export const Carousel: React.FC<CarouselProps> = ({
updateArrowVisible();
};
// 初始化时判读一次是否显示箭头
// Determine whether to display arrows once during initialization
updateArrowVisible();
itemsContainerRef?.current?.addEventListener('scroll', scrollEvent);
window?.addEventListener('resize', updateArrowVisible);

View File

@@ -39,7 +39,7 @@ interface CollapsibleIconButtonContextValue {
setItems: Dispatch<SetStateAction<ContextItems | undefined>>;
}
// eslint-disable-next-line @typescript-eslint/naming-convention -- 这是 context
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is context
export const CollapsibleIconButtonContext =
createContext<CollapsibleIconButtonContextValue>({
showText: true,
@@ -56,7 +56,7 @@ export const useItem = (key: symbol, ref: RefObject<HTMLElement>) => {
}));
}, [size?.width]);
// 组件销毁后移除
// Component destruction and removal
useEffect(
() => () => {
setItems(items => omit(items, key));

View File

@@ -32,7 +32,7 @@ import {
import { CollapsibleIconButtonContext, useWrapper, useItem } from './context';
/** 能让 Group 内的所有 CollapsibleIconButton 根据空余宽度自动展开(露出文案)收起(隐藏文案只剩图标) */
/** Can make all the CollapsibleIconButton in the Group automatically expand according to the free width (expose the copy) and put away (hide the copy and only the icon is left) */
export const CollapsibleIconButtonGroup: FC<
PropsWithChildren<{
/** @default 12 */
@@ -67,12 +67,12 @@ export const CollapsibleIconButton = forwardRef<
return (
<span ref={ref}>
{/* 不可见时渲染到屏幕外侧,用于获取宽度 */}
{/* Render to the outside of the screen when not visible for width */}
<div className={showText ? '' : 'fixed left-[-999px]'} ref={contentRef}>
<Button
size="default"
color="secondary"
// 不可见时不附带 testid避免对 E2E 产生影响
// Invisible without testid to avoid impact on E2E
{...(showText ? rest : omit(rest, 'data-testid'))}
>
{text}
@@ -87,7 +87,7 @@ export const CollapsibleIconButton = forwardRef<
);
});
/** 更为通用的版本 */
/** A more general version */
export const Collapsible = forwardRef<
HTMLSpanElement,
{
@@ -102,7 +102,7 @@ export const Collapsible = forwardRef<
return (
<span ref={ref}>
{/* 不可见时渲染到屏幕外侧,用于获取宽度 */}
{/* Render to the outside of the screen when not visible for width */}
<div className={showFull ? '' : 'fixed left-[-999px]'} ref={contentRef}>
{fullContent}
</div>
@@ -118,7 +118,7 @@ export const Collapsible = forwardRef<
);
});
/** 不会折叠,但参与宽度计算的元素 */
/** Elements that do not collapse, but participate in width calculations */
export function PlaceholderContainer({
itemKey,
children,

View File

@@ -57,7 +57,7 @@ interface DuplicateBotProps {
buttonSize?: Size;
enableCozeDesign?: boolean;
/**
* cozeDesign 的情况下才生效
* CozeDesign only takes effect in the case of
*/
isBlock?: boolean;
eventCallbacks?: Partial<{
@@ -143,17 +143,17 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
});
}
//复制完成,关闭空间弹窗
//Copy complete, close the space pop-up window
setShowSpaceModal(false);
} else if (pageFromFromStore === BotPageFromEnum.Explore) {
//explore时可以复制到某个空间下
//When exploring, it can be copied to a certain space
resp = await DeveloperApi.DuplicateBotToSpace({
draft_bot_id: botIdFromStore,
target_space_id: targetSpaceId || '',
name,
});
//复制完成,关闭空间弹窗
//Copy complete, close the space pop-up window
setShowSpaceModal(false);
} else {
resp = await SpaceApi.DuplicateDraftBot({
@@ -260,14 +260,14 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
from: 'explore_card',
source: 'explore_bot_detailpage',
});
//探索页面来源team>1时选择copy 空间否则copy到个人空间
//Explore the page Source: Select copy space when team > 1, otherwise copy to personal space
if (list.length === 1) {
openNewWindow(() => copyAndOpenBot(list?.[0].id));
} else {
setShowSpaceModal(true);
}
} else if (pageFrom === BotPageFromEnum.Template) {
//探索页面来源team>1时选择copy 空间否则copy到个人空间
//Explore the page Source: Select copy space when team > 1, otherwise copy to personal space
if (list.length === 1) {
openNewWindow(() => copyAndOpenBot(list?.[0].id));
} else {
@@ -281,7 +281,7 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
source: 'bots_detailpage',
from: 'bots_card',
});
// bot页面来源若有操作权限直接copy到当前空间下
// Bot page source: If there is operation permission, directly copy it to the current space
if (hide_operation) {
Toast.warning('Bot in public space cannot duplicate');
return;
@@ -316,7 +316,7 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
</UIButton>
)}
{/* 选择空间弹窗 */}
{/* Select space pop-up */}
<SelectSpaceModal
botName={botName ?? botNameFromStore}
visible={showSpaceModal}

View File

@@ -93,7 +93,7 @@ export const GenerateButton: React.FC<GenerateButtonProps> = ({
(scene === 'gif' && gifCount >= 10) ||
(scene === 'static_image' && staticImageCount >= 20)
) {
// 达到上限,禁用按钮
// Limit reached, disable button
setExceedImageGenCountLimit(true);
}
}
@@ -103,7 +103,7 @@ export const GenerateButton: React.FC<GenerateButtonProps> = ({
}
};
useEffect(() => {
// 获取图片限制,每天限制10gif20个静态图根据scene来判断是否达到上限
// Get the picture limit, limit 10 gifs and 20 static pictures per day, and judge whether the upper limit is reached according to the scene.
if (!loading) {
getGenPicTimes();
}

View File

@@ -86,7 +86,7 @@ export default function ImagePicker(props: ImagePickerProps) {
maxSize={2 * 1024}
onSizeError={() => {
Toast.error({
// starling 切换
// Starling toggle
content: withSlardarIdButton(
I18n.t(
'dataset_upload_image_warning',

View File

@@ -35,11 +35,11 @@ import s from './index.module.less';
interface GenerateGifProps {
scene: 'background' | 'avatar';
image: ImageItem; // 默认图片
text: string; // 默认文本
loading: boolean; // 生成时socket全局监听服务端响应因此loading需要受控
imageList?: ImageItem[]; // 图片候选列表(只包含静态图)
generatingTaskId: string; // 生成中的任务id
image: ImageItem; // default image
text: string; // default text
loading: boolean; // When generated, sockets listen globally for server level responses, so loading needs to be controlled
imageList?: ImageItem[]; // Picture Candidate List (contains only static images)
generatingTaskId: string; // Generating task id
exceedMaxImageCount?: boolean;
className?: string;
style?: CSSProperties;
@@ -106,7 +106,7 @@ export const GenerateGif: React.FC<GenerateGifProps> = ({
setLoading?.(true);
try {
avatarBackgroundWebSocket.createConnection();
// 只负责发送请求,响应在WebSocket中接收
// Only responsible for sending requests, responses are received in WebSocket
const { data } = await PlaygroundApi.GeneratePic({
gen_prompt: {
ori_prompt: text.trim(),

View File

@@ -31,15 +31,15 @@ import { type TabItem } from './type';
import s from './index.module.less';
export interface GenerateImageTabProps {
// tab列表
// tab list
tabs: TabItem[];
// 是否可折叠
// Is it foldable?
enableCollapsible?: boolean;
// 当前激活的tab
// Currently active tab
activeKey?: string;
// 当前激活的tab变化回调
// Currently active tab change callback
onTabChange?: (tabKey: string) => void;
// 是否展示wait文案
// Whether to show the waiting copy
showWaitTip?: boolean;
disabled?: boolean;
}
@@ -62,7 +62,7 @@ export const GenerateImageTab: React.FC<GenerateImageTabProps> = ({
setOpen(!isOpen);
};
// tabPane 不卸载
// tabPane does not uninstall
const component = (
<div>
{tabs.map(item => (
@@ -124,7 +124,7 @@ export const GenerateImageTab: React.FC<GenerateImageTabProps> = ({
) : null}
</div>
{enableCollapsible ? (
// keepDOM 异常失效使用collapseHeight 不销毁dom保留状态
// keepDOM exception fails using collapseHeight does not destroy dom keep state
<Collapsible isOpen={isOpen} keepDOM collapseHeight={1}>
<div> {component} </div>
</Collapsible>

View File

@@ -28,8 +28,8 @@ import s from './index.module.less';
export type ImageItem = PicTask;
export interface ImageListProps {
selectedKey?: string; // 选中的key
data: ImageItem[]; // 列表数据
selectedKey?: string; // Selected key
data: ImageItem[]; // list data
className?: string;
imageItemClassName?: string;
showDeleteIcon?: boolean;
@@ -39,18 +39,18 @@ export interface ImageListProps {
index?: number;
item?: ImageItem;
data: ImageItem[];
}) => void; // 删除图片data是此次删除之后的数据
}) => void; // Delete the picture, data is the data after this deletion.
onSelect?: (params: {
index?: number;
item: ImageItem;
data: ImageItem[];
selected: boolean;
}) => void; // 选中图片其中item和data都是此次选中之前的数据selected表示在本次选中之前此图片是否已是选中状态
}) => void; // Select the picture, where item and data are the data before this selection. Selected indicates whether the picture was selected before this selection
onClick?: (params: {
index: number;
item: ImageItem;
data: ImageItem[];
}) => void; // 点击图片
}) => void; // Click on the picture.
}
export const ImageList: React.FC<ImageListProps> = ({

View File

@@ -56,7 +56,7 @@ export { UploadGenerateButton } from './upload-generate-button';
export { usePluginLimitModal, transPricingRules } from './plugin-limit-info';
// 曝光埋点上报组件,进入视图上报
// Exposure event tracking report component, enter the view report
export { TeaExposure } from './tea-exposure';
export { Sticky } from './sticky';
@@ -67,6 +67,6 @@ export {
appendCopySuffix,
} from './project-duplicate-modal';
export { SpaceFormSelect } from './space-form-select';
// !Notice 以下模块只允许导出类型,避免首屏加载 react-dnd,@blueprintjs/core 等相关代码
// ! Notice that the following modules only allow export types, avoid loading react-dnd, @blueprintjs/core and other related codes on the first screen
export { type TItemRender, type ITemRenderProps } from './sortable-list';
export { type ConnectDnd, type OnMove } from './sortable-list/hooks';

View File

@@ -109,7 +109,7 @@ export const InputSlider: React.FC<InputSliderProps> = ({
const onNumberChange = (numberValue: number) => {
updateInputNumber();
// 防止 -0
// Prevent -0
if (numberValue === 0) {
onChange?.(0);
return;
@@ -124,7 +124,7 @@ export const InputSlider: React.FC<InputSliderProps> = ({
onChange?.(expectedFormattedValue);
};
// 防止 -0 导致 InputNumber 无限循环更新
// Prevent -0 from causing InputNumber to update indefinitely
const fixedValue = Object.is(value, -0) ? 0 : value;
useEffect(() => {

View File

@@ -36,7 +36,7 @@ const LimitCount: React.FC<LimitCountProps> = ({ maxLen, len }) => (
);
export interface InputWithCountProps extends InputProps {
// 设置字数限制并显示字数统计
// Set word limits and display word count
getValueLength?: (value?: InputProps['value'] | string) => number;
}

View File

@@ -37,7 +37,7 @@ export const ListTab: React.FC<PropsWithChildren<BotListHeaderProps>> = ({
{...props}
tabPaneMotion={false}
type="button"
// eslint-disable-next-line @typescript-eslint/naming-convention -- react 组件
// eslint-disable-next-line @typescript-eslint/naming-convention -- react component
renderTabBar={(innerProps, Node) => (
<div className={cs(s.header, containerClass)}>
<Node {...innerProps} />

View File

@@ -22,7 +22,7 @@ import { type ButtonProps } from '@coze-arch/bot-semi/Button';
import { UIButton, Toast, Spin } from '@coze-arch/bot-semi';
export type LoadingButtonProps = ButtonProps & {
/** 加载中的 toast 文案 */
/** Loading toast copy */
loadingToast?: string | Omit<ToastReactProps, 'type'>;
};

View File

@@ -156,7 +156,7 @@ export const ActionBar: React.FC<ActionBarProps> = ({
</span>
</InsertLinkPopover>
{/* 暂时禁用,后续放开 */}
{/* Temporarily disabled, later released */}
{SHOW_ADD_NAME_BTN && (
<Tooltip content={I18n.t('add_nickname')}>
<UIButton

View File

@@ -26,7 +26,7 @@ export interface InsertLinkPopoverProps
}
/**
* 全受控
* fully controlled
*/
export const InsertLinkPopover: React.FC<
PropsWithChildren<InsertLinkPopoverProps>

View File

@@ -29,7 +29,7 @@ export const getSyncInsertText = (action: SyncAction): string => {
}
/**
* 不应该走到这里
* Shouldn't have come here
*/
primitiveExhaustiveCheck(type);
return '';

View File

@@ -78,7 +78,7 @@ export const useMarkdownEditor = ({
},
);
// 判断使用内置上传方法 or 自定义
// Determine whether to use the built-in upload method or customize
const selectUploadMethod = () => {
if (customUpload) {
return customUpload({
@@ -143,7 +143,7 @@ export const useMarkdownEditor = ({
ref.current.focus();
const { selectionEnd } = ref.current;
/**
* 选中文字时点击 action bar, 将内容插入到文字的末尾
* When the text is selected, click the action bar to insert the content at the end of the text
*/
console.log('handleInsertText', { value, insertText, selectionEnd });

View File

@@ -25,7 +25,7 @@ import { type UploadState } from '../type';
import { UploadController } from '../service/upload-controller';
/**
* 暂时没有场景,所以这里将多实例、一次行上传多文件的能力屏蔽了
* There is no scene for the time being, so the ability to upload multiple files with multiple instances and one line is blocked here.
*/
export const useUpload = ({
getUserId,

View File

@@ -37,7 +37,7 @@ export interface MarkdownEditorProps {
}
/**
* 全受控组件
* fully controlled component
*/
export const MarkdownEditor: React.FC<MarkdownEditorProps> = ({
value = '',

View File

@@ -50,13 +50,13 @@ export interface UploadState {
fileName: string;
}
// 自定义上传方法入参
// Custom upload method imported parameters
export interface CustomUploadParams {
onUploadAllSuccess: (param: { url: string; fileName: string }) => void;
}
// 自定义上传方法出参
// Custom upload method exported parameter
export interface CustomUploadRes {
uploadFileList: (fileList: File[]) => void;
//null表示已完成
//Null means completed
uploadState: UploadState | null;
}

View File

@@ -31,11 +31,11 @@ export const matchAllTemplateRanges = (
text: string,
template: string,
): { start: number; end: number }[] => {
// 正则表达式,用于匹配双花括号内的内容
// Regular expressions to match the contents of double curly braces
const templateRegex = new RegExp(getFixedVariableTemplate(template), 'g');
const matches: { start: number; end: number }[] = [];
// 循环查找所有匹配项
// Loop through all matches
while (true) {
const match = templateRegex.exec(text);

View File

@@ -53,12 +53,12 @@ export interface OptionItemProps {
avatar: string | undefined;
name: string | undefined;
searchWords?: string[];
endPointName?: string; // 接入点名称(专业版有)
endPointName?: string; // Access point name (available in the professional version)
showEndPointName?: boolean;
className?: string;
/**
* @deprecated
* 原先只会有「限额」标签M-5395720900 后会有大量新标签,避免兼容问题产品同意先简单隐藏掉标签展示
* Originally, there would only be "limit" labels. After M-5395720900, there will be a large number of new labels to avoid compatibility problems. The product agrees to simply hide the label display first.
*/
tags?: OptionItemTag[];
}
@@ -77,12 +77,12 @@ export const ModelOptionItem: React.FC<OptionItemProps> = ({
const tags: OptionItemTag[] = [];
const shouldShowEndPoint = showEndPointName && endPointName;
// 即将支持,敬请期待
// Support soon, so stay tuned.
const displayName = FLAGS['bot.studio.model_select_switch_end_point_name_pos']
? endPointName || name
: name;
// 即将支持,敬请期待
// Support soon, so stay tuned.
const displayEndPointName = FLAGS[
'bot.studio.model_select_switch_end_point_name_pos'
]

View File

@@ -23,11 +23,11 @@ import { MonetizeDescription } from '../monetize-description';
import { MonetizeCreditRefreshCycle } from '../monetize-credit-refresh-cycle';
export interface MonetizeConfigValue {
/** 是否开启付费 */
/** Whether to start payment */
isOn: boolean;
/** 开启付费后,用户免费体验的次数 */
/** The number of free user experiences after starting payment */
freeCount: number;
/** 刷新周期 */
/** refresh cycle */
refreshCycle: BotMonetizationRefreshPeriod;
}
@@ -36,8 +36,8 @@ interface MonetizeConfigPanelProps {
value: MonetizeConfigValue;
onChange: (value: MonetizeConfigValue) => void;
/**
* 内置防抖后的 onChange 事件,业务侧可选择性使用,正常只传 onChange 即可
* (由于该组件是完全受控组件,因此不能只传 onDebouncedChange必须传 onChange 实时更新视图)
* The onChange event after built-in anti-shake can be selectively used by the business side. Normally, only onChange can be transmitted.
* (Since this component is a fully controlled component, you cannot just pass onDebouncedChange, you must pass onChange to update the view in real time)
*/
onDebouncedChange?: (value: MonetizeConfigValue) => void;
}

View File

@@ -32,23 +32,23 @@ import s from './index.module.less';
interface MenuItem {
/**
* 如果是string需传入starling key并且会由div包一层
* 如果是function则自定义label的实现,active表示是否是选中态
* If it is a string, you need to pass in the starling key, and it will be wrapped by the div layer.
* If it is a function, the implementation of the custom label, active indicates whether it is selected
*/
label: string | ((active: boolean) => React.ReactNode);
/** label 外的 badge未来再扩展配置项 */
/** The badge outside the label, and the configuration item will be expanded in the future */
badge?: string;
app: SpaceAppEnum;
/**
* Q:为什么不叫 visibleFG 要取反,filter() 也要取反,很麻烦
* A:为了兼容旧配置,缺省时认定为 visible。避免合码时无冲突 导致忽略掉新增配置的问题。
* Q: Why is it not called visible? FG should be reversed, and filter () should also be reversed, which is very troublesome.
* A: In order to be compatible with the old configuration, it is recognized as visible by default. To avoid no conflicts when combining codes, the problem of new configurations is ignored.
*/
invisible?: boolean;
/** 目前24.05.21)没发现用处,怀疑是以前的功能迭代掉了,@huangjian 说先留着 */
/** At present (24.05.21) no use is found, it is suspected that the previous function iteration is lost, @huangjian said to keep it first */
icon?: ReactNode;
/** 目前24.05.21)没发现用处,怀疑是以前的功能迭代掉了,@huangjian 说先留着 */
/** At present (24.05.21) no use is found, it is suspected that the previous function iteration is lost, @huangjian said to keep it first */
selectedIcon?: ReactNode;
/** 自动化打标 */
/** Automatic marking */
e2e?: string;
}

View File

@@ -25,7 +25,7 @@ import React, {
import classNames from 'classnames';
import s from './index.module.less';
// react-markdown 20ms 左右的 longtask
// React-markdown longtask around 20ms
const LazyReactMarkdown = lazy(() => import('react-markdown'));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const ReactMarkdown = (props: any) => (

View File

@@ -94,10 +94,10 @@ export type ProjectTemplateCopySuccessCallback = (param: {
export const useProjectTemplateCopyModal = (props: {
modalTitle: string;
/** 是否需要选择 space */
/** Do you need to choose space? */
isSelectSpace: boolean;
onSuccess?: ProjectTemplateCopySuccessCallback;
/** 埋点参数 - 当前页面/来源 */
/** Event tracking parameters - current page/source */
source: NonNullable<
ParamsTypeDefine[EVENT_NAMES.template_action_front]['source']
>;
@@ -167,7 +167,7 @@ export const useProjectTemplateCopyModal = (props: {
formProps={{
initValues,
onValueChange: val => {
// 当用户删除 input 中所有字符时val.name 字段会消失,而不是空字符串,神秘
// When the user removes all characters in input, val.name field disappears instead of empty string
setIsFormValid(!!val.name?.trim());
},
getFormApi: api => {
@@ -181,7 +181,7 @@ export const useProjectTemplateCopyModal = (props: {
sourceProduct: inputSourceProduct,
}: {
initValue: ProjectTemplateCopyValue;
/** 用于提取埋点参数 */
/** Used to extract event tracking parameters */
sourceProduct: ProductInfo;
}) => {
setInitValues({

View File

@@ -26,7 +26,7 @@ import classnames from 'classnames';
import s from './handle.module.less';
// 目前只支持水平方向,按需扩展吧
// Currently only supports horizontal direction, expand it on demand.
export interface ResizableLayoutHandleProps {
className?: string;
hotZoneClassName?: string;
@@ -94,7 +94,7 @@ export const ResizableLayoutHandle: FC<ResizableLayoutHandleProps> = ({
const offEvents = () => {
window.removeEventListener('pointermove', move, false);
// 适配移动端出现多点触控的情况
// Adapt to the situation of multi-touch on the mobile end
window.removeEventListener('pointerdown', moveEnd, false);
window.removeEventListener('pointerup', moveEnd, false);
window.removeEventListener('pointercancel', moveEnd, false);
@@ -108,12 +108,12 @@ export const ResizableLayoutHandle: FC<ResizableLayoutHandleProps> = ({
setMoving(true);
callbackRef.current.onMoveStart();
window.addEventListener('pointermove', move, false);
// 适配移动端出现多点触控的情况
// Adapt to the situation of multi-touch on the mobile end
window.addEventListener('pointerdown', moveEnd, false);
window.addEventListener('pointerup', moveEnd, false);
window.addEventListener('pointercancel', moveEnd, false);
};
// TODO hover 样式 & 热区宽度需要和 UI 对齐
// TODO hover style & hotzone width needs to be aligned with UI
return (
<div
className={classnames(hotZoneStyle, hotZoneClassName)}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
Children,
type PropsWithChildren,
@@ -61,7 +61,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
return;
}
const totalSize = sum(state.itemWidth);
// 排除还没有进行过拖拽的情况,此时本地 state 中没有记录上次分配的宽度
// Exclude the case where no drag has been performed, and the last allocated width is not recorded in the local state at this time
if (totalSize <= 0) {
return;
}
@@ -107,7 +107,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
);
}
}
// @ts-expect-error -- 跳过类型体操
// @ts-expect-error -- skip type gymnastics
const { ref } = child;
if (typeof ref === 'function') {
ref(target);
@@ -144,7 +144,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
),
});
}}
// 相对于初始位置的偏移量
// Offset from the initial position
onMove={offset => {
const pre = index - 1;
childRef.current[pre].style.width = `${
@@ -156,7 +156,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
}}
onMoveEnd={() => {
setState({
// 拖拽结束后,记录真实宽度
// After dragging, record the true width
itemWidth: childRef.current.map(
item => item.clientWidth ?? 0,
),

View File

@@ -24,7 +24,7 @@ import styles from './index.module.less';
export interface SearchProps {
defaultValue?: string;
/** 当此值变化时,更新内部的搜索内容 */
/** When this value changes, update the internal search content */
refreshValue?: string;
onSearch?: (value?: string) => void;
placeholder?: string;

View File

@@ -106,7 +106,7 @@ export const SelectSpaceModal: React.FC<
const params = form.current?.formApi.getValues();
await onConfirm?.(params?.spaceId ?? '', params?.name);
} catch {
// 审核不通过会走到这个逻辑,调用接口处会上报自定义异常
// If the review fails, you will go to this logic, and a custom exception will be reported at the calling interface.
setLoading(false);
}
}}

View File

@@ -21,7 +21,7 @@ export type OnMove<TId = string | number> = (
souceId: TId,
targetId: TId,
isBefore: boolean,
) => void; // 因为没有顺序信息,所以需要指明是在前面还是后面
) => void; // Since there is no order information, it is necessary to specify whether it is before or after
export interface UseDndSortableParams<TId = string | number> {
id: TId;
type: symbol;
@@ -61,7 +61,7 @@ export const useDnDSortableItem = <TId = string | number>({
if (!itemDomRef.current || item.id === id) {
return;
}
// 当前被拖拽元素的 X Y 坐标
// The X Y coordinates of the currently dragged element
const draggingClient = monitor.getClientOffset();
const dropTargetClient = itemDomRef.current?.getBoundingClientRect();
let isBefore = false;

View File

@@ -87,7 +87,7 @@ export const SortableList = <TData extends object>({
enabled={enabled}
direction={direction}
/**
* 原始数组 [1, 2, target, 4, 5, source, 7, 8]
* Raw array [1, 2, target, 4, 5, source, 7, 8]
* before = true j==> [1,2, source, target, 4, 5, 7, 8]
* before = false ==> [1,2, target, source, 4, 5, 7, 8]
**/
@@ -101,7 +101,7 @@ export const SortableList = <TData extends object>({
newList.findIndex(target => getId(target) === targetId) +
(before ? 0 : 1);
if (sourceIndex === targetIndex) {
// 前后 index 相同的情况不触发 onChange 避免频繁 rerender
// Do not trigger onChange if the index is the same before and after to avoid frequent rerender
return;
}
sourceItem && newList.splice(targetIndex, 0, sourceItem);

View File

@@ -58,7 +58,7 @@ export const BaseSpaceFormSelect = withField(
return;
}
// 需要触发表单 onChange 事件 否则上层响应不到数据变化的事件
// The form onChange event needs to be triggered, otherwise the upper layer will not respond to the data change event.
props.onChange?.(fixedInitValue);
}, [fixedInitValue]);

View File

@@ -20,24 +20,24 @@ import cls from 'classnames';
import { useScroll } from 'ahooks';
interface StickyProps {
/** 滚动容器,也就是那个 scrollHeight 大于 viewportHeightoverflow-y auto/scroll 的容器 */
/** Scroll container, that is, the container with scrollHeight greater than viewportHeight and overflow-y auto/scroll */
scrollContainerRef: () => Element;
/**
* 作用同 css sticky 时的 top 属性
* The top property when used as a css sticky
* @default 0
*/
top?: number;
/**
* 触发 sticky 后,底部(为了美观)额外的滚动距离
* After triggering sticky, the bottom (for aesthetics) has an extra scroll distance
* @default 0
*/
bottom?: number;
}
/**
* sticky 容器组件,用于解决 sticky 元素高于视窗时无法全部露出的问题
* Sticky container component, used to solve the problem that sticky elements cannot be fully exposed when they are higher than the viewport
*
* 效果是触发 sticky 后,sticky 容器会跟随滚动容器的滚动而有限地上下移动
* The effect is that after triggering the sticky, the sticky container will follow the scroll of the scrolling container and move up and down limited.
*/
export function Sticky({
top: stickyTop = 0,
@@ -46,11 +46,11 @@ export function Sticky({
children,
}: PropsWithChildren<StickyProps>) {
const stickyContainerRef = useRef<HTMLDivElement>(null);
/** 一个不可见的元素,用于通过 IntersectionObserver 检测是否已经 sticky */
/** An invisible element used to detect whether it is sticky by IntersectionObserver */
const stickyDetectRef = useRef<HTMLDivElement>(null);
const [isSticky, setIsSticky] = useState(false);
const prevScrollTop = useRef(scrollContainerRef()?.scrollTop || 0);
// sticky 容器模拟向上滚动的距离
// Sticky container simulates the distance of scrolling up
const [simulateScrollDistance, setSimulateScrollDistance] = useState(0);
useEffect(() => {
@@ -58,7 +58,7 @@ export function Sticky({
return;
}
/** IntersectionObserver 监听是否触发 sticky */
/** IntersectionObserver monitor if sticky is triggered */
const intersectionObserver = new IntersectionObserver(
([entry]) => {
const { isIntersecting } = entry;
@@ -73,34 +73,34 @@ export function Sticky({
};
}, []);
// 已测试该方法能监听 `scrollTo` 等方法产生的 scroll无论模式是 smooth 还是 instant
// It has been tested that this method can listen to scrolls generated by methods such as'scrollTo ', regardless of whether the mode is smooth or instant.
useScroll(scrollContainerRef, scrollEvent => {
/** 页面整体向上scrollbar 向下移动)滚动的距离,为负则代表页面向下滚动 */
/** The distance where the page scrolls up as a whole (scrollbar moves down). If it is negative, it means the page scrolls down. */
const scrollUpDistance = scrollEvent.top - prevScrollTop.current;
prevScrollTop.current = scrollEvent.top;
if (!stickyContainerRef.current || !isSticky) {
// return false 避免 useScroll 产生 rerender,下同
// (回调内的其他 setState 会正常触发 rerender
// Return false to avoid useScroll rerender
// (Other setStates within the callback will trigger rerender normally)
return false;
}
const viewportHeight = window.innerHeight;
const stickyContainerHeight = stickyContainerRef.current?.scrollHeight || 0;
/** 触发 sticky 后,模拟滚动的容器高度 */
/** After triggering sticky, simulate the height of the rolling container */
const simulateStickyContainerHeight =
stickyContainerHeight + stickyTop + stickyBottom;
// 判断高度是否小于 viewport是的话始终能正常显示在视图内不用后面乱七八糟一堆计算了
// Determine whether the height is less than the viewport. If so, it can always be displayed in the view normally, and there is no need to make a mess of calculations later.
if (simulateStickyContainerHeight < viewportHeight) {
return false;
}
/** 模拟滚动容器比视窗高出的部分,也即模拟滚动的上限 */
/** The part of the simulated scrolling container higher than the viewport, that is, the upper limit of the simulated scrolling */
const simulateMaxScrollDistance =
simulateStickyContainerHeight - viewportHeight;
if (scrollUpDistance > 0) {
// #region 处理向上滚动
// #region processing scroll up
const stickyReachedBottom =
simulateScrollDistance >= simulateMaxScrollDistance;
if (stickyReachedBottom) {
@@ -116,7 +116,7 @@ export function Sticky({
return false;
// #endregion
} else {
// #region 处理向下滚动
// #region processing scroll down
const stickyReachedTop = simulateScrollDistance <= 0;
if (stickyReachedTop) {
setSimulateScrollDistance(0);

View File

@@ -56,7 +56,7 @@ export const TableSelectAllPopover: FC<
>
{I18n.t('publish_permission_control_page_remove_choose_all')}
</Checkbox>
{/* 确保全选和右侧区域有一个最小间隔 */}
{/* Make sure there is a minimum interval between Select All and the right area */}
<div className="flex-1 min-w-[40px]" />
{renderCount ? (
<div>

View File

@@ -28,12 +28,12 @@ import {
type ParamsTypeDefine,
} from '@coze-arch/bot-tea';
/** 后续考虑通用化封装,增加诸如延迟时间、露出比例等参数 */
/** Subsequent consideration of generalized packaging, adding parameters such as delay time and exposure ratio */
type TeaExposureProps<TEventName extends EVENT_NAMES> = {
/**
* 是否只上报一次
* Is it only reported once?
* @default false
* @todo 有余力再考虑兼容虚拟滚动
* @Todo has the spare time to reconsider compatibility with virtual scrolling
*/
once?: boolean;
teaEvent: {
@@ -44,13 +44,13 @@ type TeaExposureProps<TEventName extends EVENT_NAMES> = {
HTMLAttributes<HTMLDivElement>;
/**
* 曝光埋点上报组件
* 可以当成普通的 div 来用(比如配置样式)
* Exposure event tracking report component
* It can be used as a normal div (e.g. configuration style).
*
* 封装的意义:避免组件 rerender
* The meaning of encapsulation: avoid component rerendering
*
* useInViewport 会造成组件频繁 rerender哪怕你并未使用其返回值只要调用了这个 hook就会随着其内部的 setState 触发 rerender。
* 而我们都知道对于下面这段代码A rerender 会触发 B 和 C 的rerender而 B rerender 不会触发 A 和 C 的 rerender。
* useInViewport causes the component to rerender frequently, even if you don't use its return value. As long as this hook is called, rerender will be triggered with its internal setState.
* And we all know that for the following code, A rerender will trigger the rerender of B and C, while B rerender will not trigger the rerender of A and C.
* ```
* const A = () => <B><C /></B>
* ```

View File

@@ -23,7 +23,7 @@ vi.stubGlobal('SAMI_APP_KEY', vi.fn());
vi.stubGlobal('IS_DEV_MODE', false);
vi.stubGlobal('IS_OVERSEA', false);
// Mock Canvas API 完整版本
// Mock Canvas API full version
const createMockCanvas = () => ({
getContext: vi.fn(() => ({
fillRect: vi.fn(),
@@ -82,14 +82,14 @@ if (global.document) {
});
}
// CSS 文件 mock
// CSS file mock
vi.mock('*.css', () => ({}));
vi.mock('*.scss', () => ({}));
vi.mock('*.sass', () => ({}));
vi.mock('*.less', () => ({}));
vi.mock('*.styl', () => ({}));
// Mock lottie-web 完整版本
// Mock lottie-web full version
vi.mock('lottie-web', () => ({
default: {
loadAnimation: vi.fn(() => ({