feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
85
frontend/packages/workflow/test-run-next/form/README.md
Normal file
85
frontend/packages/workflow/test-run-next/form/README.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# @coze-workflow/test-run-form
|
||||
|
||||
Workflow TestRun Form
|
||||
|
||||
## Overview
|
||||
|
||||
This package is part of the Coze Studio monorepo and provides workflow functionality. It includes component, store.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
Add this package to your `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"@coze-workflow/test-run-form": "workspace:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
rush update
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
import { /* exported functions/components */ } from '@coze-workflow/test-run-form';
|
||||
|
||||
// Example usage
|
||||
// TODO: Add specific usage examples
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Component
|
||||
- Store
|
||||
|
||||
## API Reference
|
||||
|
||||
### Exports
|
||||
|
||||
- `createSchemaField,
|
||||
useFormSchema,
|
||||
useForm,
|
||||
useCurrentFieldState,
|
||||
FormSchema,
|
||||
type FormModel,
|
||||
type IFormSchema,`
|
||||
- `TestRunForm`
|
||||
- `InputJson as FormBaseInputJson,
|
||||
GroupCollapse as FormBaseGroupCollapse,
|
||||
FieldItem as FormBaseFieldItem,`
|
||||
- `TestRunFormProvider,
|
||||
useTestRunFormStore,
|
||||
type TestRunFormState,`
|
||||
- `generateField,
|
||||
generateFieldValidator,
|
||||
isFormSchemaPropertyEmpty,
|
||||
stringifyFormValuesFromBacked,`
|
||||
- `TestFormFieldName`
|
||||
|
||||
|
||||
For detailed API documentation, please refer to the TypeScript definitions.
|
||||
|
||||
## Development
|
||||
|
||||
This package is built with:
|
||||
|
||||
- TypeScript
|
||||
- Modern JavaScript
|
||||
- Vitest for testing
|
||||
- ESLint for code quality
|
||||
|
||||
## Contributing
|
||||
|
||||
This package is part of the Coze Studio monorepo. Please follow the monorepo contribution guidelines.
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { FieldItem } from '../../../src/components/base-form-materials/field-item';
|
||||
|
||||
describe('FieldItem', () => {
|
||||
// 测试基本渲染
|
||||
it('should render title', () => {
|
||||
const title = 'Test Title';
|
||||
render(<FieldItem title={title} />);
|
||||
const titleElement = screen.getByText(title);
|
||||
expect(titleElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试必填标记渲染
|
||||
it('should render required marker', () => {
|
||||
render(<FieldItem title="Test" required />);
|
||||
const requiredMarker = screen.getByText('*');
|
||||
expect(requiredMarker).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试提示信息渲染
|
||||
it('should render tooltip', () => {
|
||||
const tooltipText = 'This is a tooltip';
|
||||
const el = render(<FieldItem title="Test" tooltip={tooltipText} />);
|
||||
const tooltipIcon = el.container.querySelector(
|
||||
'[data-content="This is a tooltip"]',
|
||||
);
|
||||
expect(tooltipIcon).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试标签渲染
|
||||
it('should render tag', () => {
|
||||
const tagText = 'New';
|
||||
render(<FieldItem title="Test" tag={tagText} />);
|
||||
const tagElement = screen.getByText(tagText);
|
||||
expect(tagElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试描述信息渲染
|
||||
it('should render description', () => {
|
||||
const descriptionText = 'This is a description';
|
||||
render(<FieldItem title="Test" description={descriptionText} />);
|
||||
const descriptionElement = screen.getByText(descriptionText);
|
||||
expect(descriptionElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试反馈信息渲染
|
||||
it('should render feedback', () => {
|
||||
const feedbackText = 'This is a feedback';
|
||||
render(<FieldItem title="Test" feedback={feedbackText} />);
|
||||
const feedbackElement = screen.getByText(feedbackText);
|
||||
expect(feedbackElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试子元素渲染
|
||||
it('should render children', () => {
|
||||
const childText = 'Child Content';
|
||||
render(<FieldItem title="Test">{childText}</FieldItem>);
|
||||
const childElement = screen.getByText(childText);
|
||||
expect(childElement).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
|
||||
import { GroupCollapse } from '../../../src/components/base-form-materials/group-collapse/collapse';
|
||||
|
||||
describe('GroupCollapse', () => {
|
||||
// 测试基本渲染
|
||||
it('should render label and initial content', () => {
|
||||
const label = 'Test Label';
|
||||
const childContent = 'Child Content';
|
||||
render(<GroupCollapse label={label}>{childContent}</GroupCollapse>);
|
||||
|
||||
const labelElement = screen.getByText(label);
|
||||
const childElement = screen.getByText(childContent);
|
||||
|
||||
expect(labelElement).toBeInTheDocument();
|
||||
expect(childElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试提示信息渲染
|
||||
it('should render tooltip', () => {
|
||||
const tooltipText = 'This is a tooltip';
|
||||
const el = render(
|
||||
<GroupCollapse label="Test" tooltip={tooltipText}>
|
||||
Child
|
||||
</GroupCollapse>,
|
||||
);
|
||||
|
||||
const tooltipIcon = el.container.querySelector(
|
||||
'[data-content="This is a tooltip"]',
|
||||
);
|
||||
expect(tooltipIcon).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试额外内容渲染
|
||||
it('should render extra content', () => {
|
||||
const extraText = 'Extra Content';
|
||||
render(
|
||||
<GroupCollapse label="Test" extra={extraText}>
|
||||
Child
|
||||
</GroupCollapse>,
|
||||
);
|
||||
|
||||
const extraElement = screen.getByText(extraText);
|
||||
expect(extraElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// 测试点击标题展开/折叠功能
|
||||
it('should toggle content when clicking the title', () => {
|
||||
const childContent = 'Child Content';
|
||||
const { getByText, queryByText } = render(
|
||||
<GroupCollapse label="Test">{childContent}</GroupCollapse>,
|
||||
);
|
||||
|
||||
// 初始状态下内容应该可见
|
||||
expect(queryByText(childContent)).toBeInTheDocument();
|
||||
|
||||
// 点击标题
|
||||
act(() => {
|
||||
fireEvent.click(getByText('Test'));
|
||||
});
|
||||
|
||||
// 点击后内容应该隐藏
|
||||
expect(queryByText(childContent)).toBeNull();
|
||||
});
|
||||
|
||||
// 测试粘性状态样式
|
||||
it('should apply sticky class when out of viewport or closed', async () => {
|
||||
const { useInViewport } = await vi.importMock('ahooks');
|
||||
(useInViewport as any).mockReturnValue([false]);
|
||||
|
||||
const { container } = render(
|
||||
<GroupCollapse label="Test">Child</GroupCollapse>,
|
||||
);
|
||||
|
||||
const titleElement = container.querySelector('svg');
|
||||
expect(titleElement).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
vi.mock('@coze-arch/i18n', () => ({
|
||||
I18n: {
|
||||
t: vi.fn(),
|
||||
},
|
||||
}));
|
||||
vi.mock('@coze-arch/coze-design', () => ({
|
||||
Typography: {
|
||||
Text: vi.fn(props => <span>{props.children}</span>),
|
||||
},
|
||||
Tag: vi.fn(props => <span>{props.children}</span>),
|
||||
Tooltip: vi.fn(props => (
|
||||
<span data-content={props.content}>{props.children}</span>
|
||||
)),
|
||||
Collapsible: vi.fn(props =>
|
||||
props.isOpen ? <span>{props.children}</span> : null,
|
||||
),
|
||||
}));
|
||||
vi.mock('ahooks', () => ({
|
||||
useInViewport: vi.fn(() => [true]),
|
||||
}));
|
||||
|
||||
vi.stubGlobal('IS_DEV_MODE', true);
|
||||
vi.stubGlobal('IS_OVERSEA', false);
|
||||
vi.stubGlobal('IS_BOE', false);
|
||||
vi.stubGlobal('REGION', 'cn');
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import { isFormSchemaPropertyEmpty } from '../../src/utils/is-property-empty';
|
||||
|
||||
describe('isFormSchemaPropertyEmpty', () => {
|
||||
// 测试空对象
|
||||
it('should return true for an empty object', () => {
|
||||
const emptyObject = {};
|
||||
expect(isFormSchemaPropertyEmpty(emptyObject)).toBe(true);
|
||||
});
|
||||
|
||||
// 测试非空对象
|
||||
it('should return false for a non-empty object', () => {
|
||||
const nonEmptyObject = { key: 'value' };
|
||||
expect(isFormSchemaPropertyEmpty(nonEmptyObject)).toBe(false);
|
||||
});
|
||||
|
||||
// 测试非对象值
|
||||
it('should return true for non-object values', () => {
|
||||
const values = [null, undefined, 123, 'string', true, false, []];
|
||||
values.forEach(value => {
|
||||
expect(isFormSchemaPropertyEmpty(value)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import { stringifyFormValuesFromBacked } from '../../src/utils/stringify-form-values-from-backed';
|
||||
|
||||
describe('stringifyFormValuesFromBacked', () => {
|
||||
// 测试输入为空的情况
|
||||
it('should return undefined when input is null or undefined', () => {
|
||||
expect(stringifyFormValuesFromBacked(null as any)).toBeUndefined();
|
||||
expect(stringifyFormValuesFromBacked(undefined as any)).toBeUndefined();
|
||||
});
|
||||
|
||||
// 测试输入包含字符串和布尔值的情况
|
||||
it('should return the same string and boolean values', () => {
|
||||
const input = {
|
||||
str: 'hello',
|
||||
bool: true,
|
||||
};
|
||||
const result = stringifyFormValuesFromBacked(input);
|
||||
expect(result).toEqual({
|
||||
str: 'hello',
|
||||
bool: true,
|
||||
});
|
||||
});
|
||||
|
||||
// 测试输入包含对象和数组的情况
|
||||
it('should stringify objects and arrays', () => {
|
||||
const input = {
|
||||
obj: { key: 'value' },
|
||||
arr: [1, 2, 3],
|
||||
};
|
||||
const result = stringifyFormValuesFromBacked(input);
|
||||
expect(result).toEqual({
|
||||
obj: '{"key":"value"}',
|
||||
arr: '[1,2,3]',
|
||||
});
|
||||
});
|
||||
|
||||
// 测试输入包含 null 和 undefined 的情况
|
||||
it('should set null and undefined values to undefined in the result', () => {
|
||||
const input = {
|
||||
nullValue: null,
|
||||
undefinedValue: undefined,
|
||||
};
|
||||
const result = stringifyFormValuesFromBacked(input);
|
||||
expect(result).toEqual({
|
||||
nullValue: undefined,
|
||||
undefinedValue: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"operationSettings": [
|
||||
{
|
||||
"operationName": "test:cov",
|
||||
"outputFolderNames": ["coverage"]
|
||||
},
|
||||
{
|
||||
"operationName": "ts-check",
|
||||
"outputFolderNames": ["./dist"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
const { defineConfig } = require('@coze-arch/eslint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
packageRoot: __dirname,
|
||||
preset: 'web',
|
||||
rules: {
|
||||
'@coze-arch/max-line-per-function': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@coze-arch/zustand/prefer-shallow': 'off',
|
||||
'@coze-arch/no-deep-relative-import': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
},
|
||||
});
|
||||
48
frontend/packages/workflow/test-run-next/form/package.json
Normal file
48
frontend/packages/workflow/test-run-next/form/package.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "@coze-workflow/test-run-form",
|
||||
"version": "0.0.1",
|
||||
"description": "Workflow TestRun Form",
|
||||
"author": "jiangxujin@bytedance.com",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
"build": "exit 0",
|
||||
"lint": "eslint ./ --cache",
|
||||
"test": "vitest run --passWithNoTests",
|
||||
"test:cov": "vitest run --passWithNoTests --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coze-arch/coze-design": "0.0.6-alpha.346d77",
|
||||
"@coze-arch/i18n": "workspace:*",
|
||||
"@coze-workflow/base": "workspace:*",
|
||||
"@coze-workflow/components": "workspace:*",
|
||||
"@coze-workflow/test-run-shared": "workspace:*",
|
||||
"@flowgram-adapter/common": "workspace:*",
|
||||
"@flowgram-adapter/free-layout-editor": "workspace:*",
|
||||
"ahooks": "^3.7.8",
|
||||
"ajv": "~8.12.0",
|
||||
"bignumber.js": "~9.0.0",
|
||||
"clsx": "^1.2.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"zustand": "^4.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@coze-arch/bot-typings": "workspace:*",
|
||||
"@coze-arch/eslint-config": "workspace:*",
|
||||
"@coze-arch/ts-config": "workspace:*",
|
||||
"@coze-arch/vitest-config": "workspace:*",
|
||||
"@testing-library/jest-dom": "^6.1.5",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@types/lodash-es": "^4.17.10",
|
||||
"@types/react": "18.2.37",
|
||||
"@types/react-dom": "18.2.15",
|
||||
"@vitest/coverage-v8": "~3.0.5",
|
||||
"react": "~18.2.0",
|
||||
"react-dom": "~18.2.0",
|
||||
"typescript": "~5.8.2",
|
||||
"vitest": "~3.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { FieldItem, type FieldItemProps } from './item';
|
||||
@@ -0,0 +1,46 @@
|
||||
.field-item {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
position: relative;
|
||||
margin-bottom: 4px;
|
||||
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
.item-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.item-tag {
|
||||
flex-shrink: 0;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.tooltip-icon {
|
||||
margin-left: 4px;
|
||||
color: var(--coz-fg-dim);
|
||||
}
|
||||
}
|
||||
|
||||
.title-text {
|
||||
color: var(--coz-fg-primary);
|
||||
}
|
||||
|
||||
.title-required {
|
||||
color: var(--coz-fg-hglt-red);
|
||||
}
|
||||
.item-description {
|
||||
color: var(--coz-fg-secondary);
|
||||
}
|
||||
|
||||
.item-feedback {
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-hglt-red);
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { IconCozInfoCircle } from '@coze-arch/coze-design/icons';
|
||||
import { Tag, Tooltip, Typography } from '@coze-arch/coze-design';
|
||||
|
||||
import css from './item.module.less';
|
||||
|
||||
export interface FieldItemProps {
|
||||
title?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
tag?: React.ReactNode;
|
||||
tooltip?: React.ReactNode;
|
||||
feedback?: string;
|
||||
required?: boolean;
|
||||
['data-testid']?: string;
|
||||
}
|
||||
|
||||
export const FieldItem: React.FC<React.PropsWithChildren<FieldItemProps>> = ({
|
||||
title,
|
||||
required,
|
||||
tooltip,
|
||||
tag,
|
||||
description,
|
||||
children,
|
||||
feedback,
|
||||
...props
|
||||
}) => (
|
||||
<div className={css['field-item']} data-testid={props['data-testid']}>
|
||||
{/* title */}
|
||||
<div className={css['item-title']}>
|
||||
<div className={css['item-label']}>
|
||||
<Typography.Text className={css['title-text']} strong size="small">
|
||||
{title}
|
||||
</Typography.Text>
|
||||
{required ? (
|
||||
<Typography.Text className={css['title-required']}>*</Typography.Text>
|
||||
) : null}
|
||||
{tooltip ? (
|
||||
<Tooltip content={tooltip}>
|
||||
<IconCozInfoCircle className={css['tooltip-icon']} />
|
||||
</Tooltip>
|
||||
) : null}
|
||||
|
||||
{tag ? (
|
||||
<Tag className={css['item-tag']} size="mini" color="primary">
|
||||
{tag}
|
||||
</Tag>
|
||||
) : null}
|
||||
</div>
|
||||
{description ? (
|
||||
<Typography.Text
|
||||
ellipsis={{
|
||||
showTooltip: {
|
||||
opts: {
|
||||
position: 'left',
|
||||
style: {
|
||||
maxWidth: 500,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
className={css['item-description']}
|
||||
size="small"
|
||||
>
|
||||
{description}
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
</div>
|
||||
{/* children */}
|
||||
<div>{children}</div>
|
||||
{/* feedback */}
|
||||
{feedback ? <div className={css['item-feedback']}>{feedback}</div> : null}
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,53 @@
|
||||
.collapse-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 40px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: var(--coz-bg-plus);
|
||||
border-bottom: 1px solid var(--coz-bg-plus);
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
&:hover .collapse-icon{
|
||||
opacity: 1;
|
||||
}
|
||||
&.is-sticky {
|
||||
border-color: var(--coz-stroke-primary);
|
||||
}
|
||||
}
|
||||
.collapse-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 20px;
|
||||
color: var(--coz-fg-primary);
|
||||
}
|
||||
.collapse-label-tooltip {
|
||||
font-size: 14px;
|
||||
margin-left: 4px;
|
||||
color: var(--coz-fg-dim);
|
||||
}
|
||||
.collapse-icon {
|
||||
transition: transform 0.3s ease-in-out, opacity 0.2s ease-in-out;
|
||||
opacity: 0;
|
||||
color: var(--coz-fg-dim);
|
||||
font-size: 14px;
|
||||
margin: 0 1px;
|
||||
&.is-show {
|
||||
opacity: 1;
|
||||
}
|
||||
&.is-close {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.collapse-content {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.collapse-extra {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
padding-right: 16px;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React, { useState, useRef } from 'react';
|
||||
|
||||
import { clsx } from 'clsx';
|
||||
import { useInViewport } from 'ahooks';
|
||||
import {
|
||||
IconCozArrowDownFill,
|
||||
IconCozInfoCircle,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import { Collapsible, Tooltip } from '@coze-arch/coze-design';
|
||||
|
||||
import css from './collapse.module.less';
|
||||
|
||||
interface CollapseProps {
|
||||
label: React.ReactNode;
|
||||
tooltip?: React.ReactNode;
|
||||
extra?: React.ReactNode;
|
||||
fade?: boolean;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
export const GroupCollapse: React.FC<
|
||||
React.PropsWithChildren<CollapseProps>
|
||||
> = ({ label, tooltip, extra, children }) => {
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const ref = useRef(null);
|
||||
/**
|
||||
* 探测标题是否处于 sticky 状态
|
||||
*/
|
||||
const [inViewport] = useInViewport(ref);
|
||||
return (
|
||||
<div>
|
||||
{/* 探测元素 */}
|
||||
<div ref={ref} />
|
||||
{/* header */}
|
||||
<div
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className={clsx(
|
||||
css['collapse-title'],
|
||||
(!inViewport || !isOpen) && css['is-sticky'],
|
||||
)}
|
||||
>
|
||||
<IconCozArrowDownFill
|
||||
className={clsx(css['collapse-icon'], !isOpen && css['is-close'])}
|
||||
/>
|
||||
<span className={css['collapse-label']}>{label}</span>
|
||||
{tooltip ? (
|
||||
<Tooltip content={tooltip}>
|
||||
<IconCozInfoCircle className={css['collapse-label-tooltip']} />
|
||||
</Tooltip>
|
||||
) : null}
|
||||
{extra ? (
|
||||
<div
|
||||
className={css['collapse-extra']}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
{extra}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{/* children */}
|
||||
<Collapsible isOpen={isOpen} keepDOM fade duration={300}>
|
||||
<div className={css['collapse-content']}>{children}</div>
|
||||
</Collapsible>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { GroupCollapse } from './collapse';
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 基础表单物料
|
||||
*/
|
||||
export { InputNumber, InputNumberProps } from './input-number';
|
||||
export { InputString, type InputStringProps } from './input-string';
|
||||
export { InputTime, type InputTimeProps } from './input-time';
|
||||
export { InputJson, type InputJsonProps } from './input-json';
|
||||
export { SelectBoolean } from './select-boolean';
|
||||
export { SelectVoice } from './select-voice';
|
||||
export { FieldItem, type FieldItemProps } from './field-item';
|
||||
export { GroupCollapse } from './group-collapse';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { InputJson, type InputJsonProps } from './json';
|
||||
@@ -0,0 +1,33 @@
|
||||
.input-json-wrap {
|
||||
border: 1px solid var(--coz-stroke-plus);
|
||||
border-radius: 8px;
|
||||
&.disabled {
|
||||
background-color: rgba(var(--coze-bg-5), var(--coze-bg-5-alpha));
|
||||
cursor: not-allowed;
|
||||
}
|
||||
&.error {
|
||||
border: 1px solid var(--coz-stroke-hglt-red);
|
||||
}
|
||||
}
|
||||
|
||||
.json-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
height: 28px;
|
||||
padding: 0 4px;
|
||||
|
||||
border-bottom: 1px solid var(--coz-stroke-plus);
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
.json-label {
|
||||
font-size: 12px;
|
||||
color: var(--coz-fg-secondary);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
.json-editor {
|
||||
overflow: hidden;
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { clsx } from 'clsx';
|
||||
import {
|
||||
JsonEditor,
|
||||
safeFormatJsonString,
|
||||
} from '@coze-workflow/test-run-shared';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozBroom } from '@coze-arch/coze-design/icons';
|
||||
import { Tooltip, IconButton } from '@coze-arch/coze-design';
|
||||
|
||||
import css from './json.module.less';
|
||||
|
||||
export interface InputJsonProps {
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
extensions?: any;
|
||||
jsonSchema?: any;
|
||||
height?: string;
|
||||
validateStatus?: 'error';
|
||||
['data-testid']?: string;
|
||||
onChange?: (v?: string) => void;
|
||||
didMount?: (editor: any) => void;
|
||||
}
|
||||
|
||||
export const InputJson: React.FC<InputJsonProps> = ({
|
||||
value,
|
||||
disabled,
|
||||
validateStatus,
|
||||
onChange,
|
||||
...props
|
||||
}) => {
|
||||
const handleFormat = () => {
|
||||
const next = safeFormatJsonString(value);
|
||||
if (next !== value) {
|
||||
onChange?.(next);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
css['input-json-wrap'],
|
||||
disabled && css.disabled,
|
||||
validateStatus === 'error' && css.error,
|
||||
)}
|
||||
data-testid={props['data-testid']}
|
||||
>
|
||||
<div className={css['json-header']}>
|
||||
<div className={css['json-label']}>JSON</div>
|
||||
<div>
|
||||
<Tooltip content={I18n.t('workflow_exception_ignore_format')}>
|
||||
<IconButton
|
||||
icon={<IconCozBroom />}
|
||||
disabled={disabled}
|
||||
size="small"
|
||||
color="secondary"
|
||||
onMouseDown={e => e.preventDefault()}
|
||||
onClick={handleFormat}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className={css['json-editor']}>
|
||||
<JsonEditor
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
onChange={onChange}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { InputNumber, type InputNumberProps } from './input-number';
|
||||
@@ -0,0 +1,27 @@
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 20px;
|
||||
position: relative;
|
||||
right: -7px;
|
||||
}
|
||||
|
||||
.button {
|
||||
font-size: 10px;
|
||||
color: var(--coz-fg-primary);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: rgba(var(--coze-bg-5), var(--coze-bg-5-alpha));
|
||||
}
|
||||
&.up {
|
||||
border-top-left-radius: var(--coze-5);
|
||||
border-top-right-radius: var(--coze-5);
|
||||
}
|
||||
&.down {
|
||||
border-bottom-left-radius: var(--coze-5);
|
||||
border-bottom-right-radius: var(--coze-5);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { useRef, useEffect } from 'react';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import BigNumber, { type BigNumber as IBigNumber } from 'bignumber.js';
|
||||
import {
|
||||
IconCozArrowDownFill,
|
||||
IconCozArrowUpFill,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import { Input, type InputProps } from '@coze-arch/coze-design';
|
||||
|
||||
import css from './input-number.module.less';
|
||||
|
||||
export interface InputNumberProps {
|
||||
value?: string;
|
||||
style?: React.CSSProperties;
|
||||
placeholder?: string;
|
||||
validateStatus?: InputProps['validateStatus'];
|
||||
disabled?: boolean;
|
||||
onChange: (v?: string) => void;
|
||||
onBlur: () => void;
|
||||
onFocus: () => void;
|
||||
/** 整型 */
|
||||
int?: boolean;
|
||||
}
|
||||
|
||||
/** 是否是合法的数字字符串 */
|
||||
function isValidNumber(str: string) {
|
||||
try {
|
||||
const value = new BigNumber(str);
|
||||
return !value.isNaN();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeNumber(str?: string) {
|
||||
if (!str || !isValidNumber(str)) {
|
||||
return;
|
||||
}
|
||||
return new BigNumber(str);
|
||||
}
|
||||
|
||||
export const InputNumber: React.FC<InputNumberProps> = ({
|
||||
int,
|
||||
onChange,
|
||||
onBlur,
|
||||
...props
|
||||
}) => {
|
||||
const verifiedRef = useRef<undefined | IBigNumber>(
|
||||
normalizeNumber(props.value),
|
||||
);
|
||||
|
||||
const fixed = (num: IBigNumber, innerInt?: boolean) =>
|
||||
innerInt ? num.toFixed(0, BigNumber.ROUND_DOWN) : num.toFixed();
|
||||
|
||||
const handleBlur = () => {
|
||||
if (props.value === '' || props.value === undefined) {
|
||||
/** 失焦时若值为空,则同时清空验证值 */
|
||||
verifiedRef.current = undefined;
|
||||
if (props.value === '') {
|
||||
onChange(undefined);
|
||||
}
|
||||
} else {
|
||||
/** 失焦时若值不为空,则需要验证值的合法性 */
|
||||
/**
|
||||
* 1. 若值本身合法,则对值做格式化
|
||||
* 2. 若值不合法,则采纳最近一次的合法值
|
||||
* 3. 若都没有,则返回 undefined
|
||||
*/
|
||||
let next: undefined | string;
|
||||
const nextBig = normalizeNumber(props.value) || verifiedRef.current;
|
||||
if (nextBig) {
|
||||
next = fixed(nextBig, int);
|
||||
}
|
||||
if (next !== props.value) {
|
||||
onChange(next);
|
||||
}
|
||||
}
|
||||
onBlur();
|
||||
};
|
||||
|
||||
const handlePlus = () => {
|
||||
let next = '1';
|
||||
if (verifiedRef.current) {
|
||||
const nextNum = verifiedRef.current.plus('1');
|
||||
next = fixed(nextNum, int);
|
||||
}
|
||||
onChange(next);
|
||||
};
|
||||
|
||||
const handleMinus = () => {
|
||||
let next = '0';
|
||||
if (verifiedRef.current) {
|
||||
const nextNum = verifiedRef.current.minus('1');
|
||||
next = fixed(nextNum, int);
|
||||
}
|
||||
onChange(next);
|
||||
};
|
||||
|
||||
/** 当值发生变化,需要把值同步到合法数字 */
|
||||
useEffect(() => {
|
||||
if (props.value === '' || props.value === undefined) {
|
||||
verifiedRef.current = undefined;
|
||||
}
|
||||
const next = normalizeNumber(props.value);
|
||||
if (next) {
|
||||
verifiedRef.current = normalizeNumber(props.value);
|
||||
}
|
||||
}, [props.value]);
|
||||
|
||||
return (
|
||||
<Input
|
||||
onChange={onChange}
|
||||
onBlur={handleBlur}
|
||||
size="small"
|
||||
suffix={
|
||||
props.disabled ? null : (
|
||||
<div className={css.buttons}>
|
||||
<div className={clsx(css.button, css.up)} onClick={handlePlus}>
|
||||
<IconCozArrowUpFill />
|
||||
</div>
|
||||
<div className={clsx(css.button, css.down)} onClick={handleMinus}>
|
||||
<IconCozArrowDownFill />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { InputString, type InputStringProps } from './input';
|
||||
@@ -0,0 +1,14 @@
|
||||
// semi 和 coze 都不支持小尺寸的 textarea 需要业务实现
|
||||
.input-string.small {
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
padding: 1px 3px;
|
||||
border-radius: 6px;
|
||||
:global textarea {
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
:global .semi-input-clearbtn {
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { TextArea } from '@coze-arch/coze-design';
|
||||
|
||||
import css from './input.module.less';
|
||||
|
||||
export interface InputStringProps {
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export const InputString: React.FC<InputStringProps> = props => (
|
||||
<TextArea
|
||||
className={clsx(css['input-string'], css.small)}
|
||||
autosize={{ minRows: 1, maxRows: 5 }}
|
||||
rows={1}
|
||||
showClear
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { InputTime, type InputTimeProps } from './time';
|
||||
@@ -0,0 +1,6 @@
|
||||
.input-time {
|
||||
width: 100%;
|
||||
:global .semi-select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { clsx } from 'clsx';
|
||||
import { DatePicker } from '@coze-arch/coze-design';
|
||||
|
||||
import css from './time.module.less';
|
||||
|
||||
export interface InputTimeProps {
|
||||
className?: string;
|
||||
value?: string;
|
||||
onChange?: (v?: string) => void;
|
||||
}
|
||||
|
||||
export const InputTime: React.FC<InputTimeProps> = ({
|
||||
className,
|
||||
value,
|
||||
onChange,
|
||||
...props
|
||||
}) => (
|
||||
<DatePicker
|
||||
className={clsx(css['input-time'], className)}
|
||||
type="dateTime"
|
||||
size="small"
|
||||
showClear={false}
|
||||
showSuffix={false}
|
||||
value={value}
|
||||
onChange={(_date, dateString) => {
|
||||
if (typeof dateString === 'string' || dateString === undefined) {
|
||||
onChange?.(dateString);
|
||||
}
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { SelectBoolean } from './select';
|
||||
@@ -0,0 +1,3 @@
|
||||
.select-boolean {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import { clsx } from 'clsx';
|
||||
import { Select, type SelectProps } from '@coze-arch/coze-design';
|
||||
|
||||
import css from './select.module.less';
|
||||
|
||||
interface SelectBooleanProps {
|
||||
className?: string;
|
||||
value?: boolean;
|
||||
onChange?: (v?: boolean) => void;
|
||||
}
|
||||
|
||||
export const SelectBoolean: React.FC<SelectBooleanProps> = ({
|
||||
className,
|
||||
value,
|
||||
onChange,
|
||||
...props
|
||||
}) => {
|
||||
const formattedValue = useMemo(
|
||||
() => (value === undefined ? undefined : Number(value)),
|
||||
[value],
|
||||
);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(v?: SelectProps['value']) => {
|
||||
const next = v === undefined ? v : Boolean(v);
|
||||
onChange?.(next);
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
return (
|
||||
<Select
|
||||
className={clsx(css['select-boolean'], className)}
|
||||
size="small"
|
||||
value={formattedValue}
|
||||
onChange={handleChange}
|
||||
{...props}
|
||||
>
|
||||
<Select.Option value={1}>True</Select.Option>
|
||||
<Select.Option value={0}>False</Select.Option>
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { VoiceSelect } from '@coze-workflow/components';
|
||||
|
||||
interface SelectVoiceProps {
|
||||
value?: string;
|
||||
onChange?: (v?: string) => void;
|
||||
onBlur?: () => void;
|
||||
}
|
||||
|
||||
export const SelectVoice: React.FC<SelectVoiceProps> = ({
|
||||
onChange,
|
||||
onBlur,
|
||||
...props
|
||||
}) => {
|
||||
const handleChange = (v?: string) => {
|
||||
onChange?.(v);
|
||||
onBlur?.();
|
||||
};
|
||||
|
||||
return <VoiceSelect onChange={handleChange} {...props} />;
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
import {
|
||||
FieldItem as BaseFieldItem,
|
||||
type FieldItemProps,
|
||||
} from '../../base-form-materials';
|
||||
import { useFieldSchema } from '../../../form-engine';
|
||||
import { TestFormFieldName } from '../../../constants';
|
||||
|
||||
export const FieldItem: React.FC<React.PropsWithChildren<FieldItemProps>> = ({
|
||||
tag,
|
||||
...props
|
||||
}) => {
|
||||
const schema = useFieldSchema();
|
||||
|
||||
const isBatchField = schema.path.includes(TestFormFieldName.Batch);
|
||||
/** 批处理变量 tag 增加额外描述 */
|
||||
const currentTag =
|
||||
tag && isBatchField
|
||||
? `${tag} - ${I18n.t('workflow_detail_node_batch')}`
|
||||
: tag;
|
||||
|
||||
return (
|
||||
<BaseFieldItem
|
||||
title={schema.title}
|
||||
description={schema.description}
|
||||
required={schema.required}
|
||||
tag={currentTag}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 表单物料
|
||||
*/
|
||||
export { InputString } from './input-string';
|
||||
export { FieldItem } from './field-item';
|
||||
export { InputNumber } from './input-number';
|
||||
export { InputInteger } from './input-integer';
|
||||
export { InputTime } from './input-time';
|
||||
export { InputJson } from './input-json';
|
||||
export { SelectBoolean } from './select-boolean';
|
||||
export { SelectVoice } from './select-voice';
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { InputNumber as BaseInputNumber } from '../../base-form-materials';
|
||||
|
||||
export const InputInteger = props => <BaseInputNumber int {...props} />;
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { InputJson } from '../../base-form-materials';
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { InputNumber as BaseInputNumber } from '../../base-form-materials';
|
||||
|
||||
export const InputNumber = props => <BaseInputNumber {...props} />;
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { InputString as BaseInputString } from '../../base-form-materials';
|
||||
|
||||
export const InputString = BaseInputString;
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { InputTime } from '../../base-form-materials';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { SelectBoolean } from '../../base-form-materials';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { SelectVoice } from '../../base-form-materials';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { TestRunForm } from './test-run-form';
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { Form, type FormModel } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import {
|
||||
InputString,
|
||||
InputNumber,
|
||||
InputInteger,
|
||||
InputJson,
|
||||
SelectBoolean,
|
||||
SelectVoice,
|
||||
InputTime,
|
||||
FieldItem,
|
||||
} from '../form-materials';
|
||||
import {
|
||||
createSchemaField,
|
||||
type FormSchema,
|
||||
useCreateForm,
|
||||
type IFormSchema,
|
||||
type FormSchemaReactComponents,
|
||||
} from '../../form-engine';
|
||||
|
||||
const SchemaField = createSchemaField({
|
||||
components: {
|
||||
InputString,
|
||||
InputNumber,
|
||||
InputInteger,
|
||||
InputTime,
|
||||
InputJson,
|
||||
SelectBoolean,
|
||||
SelectVoice,
|
||||
FieldItem,
|
||||
},
|
||||
});
|
||||
|
||||
interface TestRunFormProps {
|
||||
schema: IFormSchema;
|
||||
components?: FormSchemaReactComponents;
|
||||
onFormValuesChange?: (payload: any) => void;
|
||||
onMounted?: (formModel: FormModel, schema: FormSchema) => void;
|
||||
}
|
||||
|
||||
export const TestRunForm: React.FC<TestRunFormProps> = ({
|
||||
schema,
|
||||
components,
|
||||
onFormValuesChange,
|
||||
onMounted,
|
||||
}) => {
|
||||
const { control, formSchema } = useCreateForm(schema, {
|
||||
onFormValuesChange,
|
||||
onMounted,
|
||||
});
|
||||
return (
|
||||
<Form control={control}>
|
||||
<SchemaField schema={formSchema} components={components} />
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 固定的内部 field name
|
||||
*/
|
||||
export enum TestFormFieldName {
|
||||
Node = '_node',
|
||||
Batch = '_batch',
|
||||
Input = '_input',
|
||||
Setting = '_setting',
|
||||
JSON = '_json',
|
||||
/** 关联内容 */
|
||||
Related = '_related',
|
||||
Bot = '_bot',
|
||||
Conversation = '_conversation',
|
||||
TestsetSelect = '_testset_select',
|
||||
TestsetSave = '_testset_save',
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { createContext, useContext, useRef } from 'react';
|
||||
|
||||
import {
|
||||
createWithEqualityFn,
|
||||
type UseBoundStoreWithEqualityFn,
|
||||
} from 'zustand/traditional';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { type StoreApi } from 'zustand';
|
||||
|
||||
import { type IFormSchema } from '../form-engine';
|
||||
|
||||
/**
|
||||
* 单一表单内的全局性质状态集中管理
|
||||
*/
|
||||
export interface TestRunFormState {
|
||||
schema: IFormSchema | null;
|
||||
mode: 'form' | 'json';
|
||||
patch: (next: Partial<TestRunFormState>) => void;
|
||||
getSchema: () => TestRunFormState['schema'];
|
||||
}
|
||||
|
||||
const createStore = () =>
|
||||
createWithEqualityFn<TestRunFormState>(
|
||||
(set, get) => ({
|
||||
schema: null,
|
||||
mode: 'form',
|
||||
patch: next => set(() => next),
|
||||
getSchema: () => get().schema,
|
||||
}),
|
||||
shallow,
|
||||
);
|
||||
|
||||
type FormStore = UseBoundStoreWithEqualityFn<StoreApi<TestRunFormState>>;
|
||||
|
||||
const FormContext = createContext<FormStore>({} as unknown as FormStore);
|
||||
|
||||
export const TestRunFormProvider: React.FC<React.PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
const ref = useRef(createStore());
|
||||
return (
|
||||
<FormContext.Provider value={ref.current}>{children}</FormContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useTestRunFormStore = <T,>(
|
||||
selector: (s: TestRunFormState) => T,
|
||||
) => {
|
||||
const store = useContext(FormContext);
|
||||
|
||||
return store(selector);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export {
|
||||
TestRunFormProvider,
|
||||
useTestRunFormStore,
|
||||
type TestRunFormState,
|
||||
} from './form';
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { type FormSchemaReactComponents } from '../types';
|
||||
import { SchemaField, type SchemaFieldProps } from './schema-field';
|
||||
|
||||
interface CreateSchemaFieldOptions {
|
||||
components: FormSchemaReactComponents;
|
||||
}
|
||||
|
||||
type InnerSchemaField = React.FC<
|
||||
Omit<SchemaFieldProps, 'components'> &
|
||||
Pick<Partial<SchemaFieldProps>, 'components'>
|
||||
>;
|
||||
|
||||
export const createSchemaField = (options: CreateSchemaFieldOptions) => {
|
||||
const InnerSchemaField: InnerSchemaField = ({ components, ...props }) => (
|
||||
<SchemaField
|
||||
components={{
|
||||
...options.components,
|
||||
...components,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
return InnerSchemaField;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Field } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { SchemaContext, type FormSchema } from '../shared';
|
||||
import { useFieldUIState } from '../hooks';
|
||||
import { ReactiveField } from './reactive-field';
|
||||
|
||||
interface FieldProps {
|
||||
name: string;
|
||||
schema: FormSchema;
|
||||
}
|
||||
|
||||
export const GeneralField: React.FC<React.PropsWithChildren<FieldProps>> = ({
|
||||
schema,
|
||||
}) => {
|
||||
const parentUIState = useFieldUIState();
|
||||
return (
|
||||
<SchemaContext.Provider value={schema}>
|
||||
<Field name={schema.path.join('.')} defaultValue={schema.defaultValue}>
|
||||
<ReactiveField parentUIState={parentUIState} />
|
||||
</Field>
|
||||
</SchemaContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { createSchemaField } from './create-schema-field';
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { SchemaContext, type FormSchema } from '../shared';
|
||||
import { useComponents } from '../hooks';
|
||||
|
||||
interface ObjectFieldProps {
|
||||
schema: FormSchema;
|
||||
}
|
||||
|
||||
export const ObjectField: React.FC<
|
||||
React.PropsWithChildren<ObjectFieldProps>
|
||||
> = ({ schema, children }) => {
|
||||
const components = useComponents();
|
||||
const renderDecorator = () => {
|
||||
if (!schema.decoratorType || !components[schema.decoratorType]) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
return React.createElement(
|
||||
components[schema.decoratorType],
|
||||
schema.decoratorProps,
|
||||
children,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<SchemaContext.Provider value={schema}>
|
||||
{renderDecorator()}
|
||||
</SchemaContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { useCurrentField, useCurrentFieldState } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { type FormSchemaUIState } from '../types';
|
||||
import {
|
||||
useFieldUIState,
|
||||
useFieldSchema,
|
||||
useComponents,
|
||||
useFormUIState,
|
||||
} from '../hooks';
|
||||
|
||||
interface ReactiveFieldProps {
|
||||
parentUIState?: FormSchemaUIState;
|
||||
}
|
||||
|
||||
/**
|
||||
* 接入响应式的 Field
|
||||
*/
|
||||
const ReactiveField: React.FC<ReactiveFieldProps> = ({ parentUIState }) => {
|
||||
const components = useComponents();
|
||||
const schema = useFieldSchema();
|
||||
const field = useCurrentField();
|
||||
const uiState = useFieldUIState();
|
||||
const formUIState = useFormUIState();
|
||||
const fieldState = useCurrentFieldState();
|
||||
/**
|
||||
* 自生的 disabled 态由父亲和自身一起控制
|
||||
*/
|
||||
const disabled =
|
||||
parentUIState?.disabled || uiState.disabled || formUIState.disabled;
|
||||
const validateStatus = fieldState.errors?.length ? 'error' : undefined;
|
||||
|
||||
const renderComponent = () => {
|
||||
if (!schema.componentType || !components[schema.componentType]) {
|
||||
return null;
|
||||
}
|
||||
return React.createElement(components[schema.componentType], {
|
||||
disabled,
|
||||
validateStatus,
|
||||
value: field.value,
|
||||
onChange: field.onChange,
|
||||
onFocus: field.onFocus,
|
||||
onBlur: field.onBlur,
|
||||
['data-testid']: ['workflow', 'testrun', 'form', 'component']
|
||||
.concat(schema.path)
|
||||
.join('.'),
|
||||
...schema.componentProps,
|
||||
});
|
||||
};
|
||||
|
||||
const renderDecorator = (children: React.ReactNode) => {
|
||||
if (!schema.decoratorType || !components[schema.decoratorType]) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
return React.createElement(
|
||||
components[schema.decoratorType],
|
||||
{
|
||||
...schema.decoratorProps,
|
||||
['data-testid']: ['workflow', 'testrun', 'form', 'decorator']
|
||||
.concat(schema.path)
|
||||
.join('.'),
|
||||
},
|
||||
children,
|
||||
);
|
||||
};
|
||||
|
||||
return renderDecorator(renderComponent());
|
||||
};
|
||||
|
||||
export { ReactiveField, ReactiveFieldProps };
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { FormSchema } from '../shared';
|
||||
import { ObjectField } from './object-field';
|
||||
import { GeneralField } from './general-field';
|
||||
|
||||
interface RecursionFieldProps {
|
||||
schema: FormSchema;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
const computePath = (path?: string[], name?: string) =>
|
||||
[...(path || []), name].filter((i): i is string => Boolean(i));
|
||||
|
||||
/**
|
||||
* 递归 Field
|
||||
*/
|
||||
const RecursionField: React.FC<RecursionFieldProps> = ({ name, schema }) => {
|
||||
const renderProperties = () => {
|
||||
const properties = FormSchema.getProperties(schema);
|
||||
if (!properties.length) {
|
||||
return null;
|
||||
}
|
||||
const { path } = schema;
|
||||
return (
|
||||
<ObjectField schema={schema}>
|
||||
{properties.map((item, index) => (
|
||||
<RecursionField
|
||||
name={item.key}
|
||||
schema={new FormSchema(item.schema, computePath(path, item.key))}
|
||||
key={`${index}-${item.key}`}
|
||||
/>
|
||||
))}
|
||||
</ObjectField>
|
||||
);
|
||||
};
|
||||
|
||||
if (!name) {
|
||||
return renderProperties();
|
||||
}
|
||||
if (schema.type === 'object') {
|
||||
return renderProperties();
|
||||
}
|
||||
|
||||
return <GeneralField name={name} schema={schema} />;
|
||||
};
|
||||
|
||||
export { RecursionField, type RecursionFieldProps };
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import type { FormSchemaReactComponents } from '../types';
|
||||
import {
|
||||
ComponentsContext,
|
||||
FormSchemaContext,
|
||||
type FormSchema,
|
||||
} from '../shared';
|
||||
import { RecursionField } from './recursion-field';
|
||||
|
||||
export interface SchemaFieldProps {
|
||||
schema: FormSchema;
|
||||
components: FormSchemaReactComponents;
|
||||
}
|
||||
|
||||
export const SchemaField: React.FC<SchemaFieldProps> = props => (
|
||||
<ComponentsContext.Provider value={props.components}>
|
||||
<FormSchemaContext.Provider value={props.schema}>
|
||||
<RecursionField schema={props.schema} />
|
||||
</FormSchemaContext.Provider>
|
||||
</ComponentsContext.Provider>
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { useCreateForm } from './use-create-form';
|
||||
export { useFieldSchema } from './use-field-schema';
|
||||
export { useFieldUIState } from './use-field-ui-state';
|
||||
export { useComponents } from './use-components';
|
||||
export { useFormSchema } from './use-form-schema';
|
||||
export { useFormUIState } from './use-form-ui-state';
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { ComponentsContext } from '../shared';
|
||||
|
||||
export const useComponents = () => useContext(ComponentsContext);
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
import { createForm, ValidateTrigger } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import type { IFormSchema, IFormSchemaValidate } from '../types';
|
||||
import { FormSchema } from '../shared';
|
||||
|
||||
type Rules = Record<string, IFormSchemaValidate>;
|
||||
|
||||
const getFieldPath = (...args: (string | undefined)[]) =>
|
||||
args.filter(path => path).join('.');
|
||||
|
||||
export function validateResolver(schema: IFormSchema): Rules {
|
||||
const rules = {};
|
||||
|
||||
visit(schema);
|
||||
|
||||
return rules;
|
||||
|
||||
function visit(current: IFormSchema, name?: string) {
|
||||
if (name && current['x-validator']) {
|
||||
rules[name] = current['x-validator'];
|
||||
}
|
||||
if (current.type === 'object' && current.properties) {
|
||||
Object.entries(current.properties).forEach(([key, value]) => {
|
||||
visit(value, getFieldPath(name, key));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const useCreateForm = (schema: IFormSchema, options: any = {}) => {
|
||||
const { validate } = options;
|
||||
const innerValidate = useMemo(
|
||||
() => ({
|
||||
...validateResolver(schema),
|
||||
...validate,
|
||||
}),
|
||||
[schema],
|
||||
);
|
||||
const { form, control } = useMemo(
|
||||
() =>
|
||||
createForm({
|
||||
validate: innerValidate,
|
||||
validateTrigger: ValidateTrigger.onBlur,
|
||||
...options,
|
||||
}),
|
||||
[schema, innerValidate],
|
||||
);
|
||||
const formSchema = useMemo(
|
||||
() => new FormSchema({ type: 'object', ...schema }),
|
||||
[schema],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (options.onMounted) {
|
||||
options.onMounted(control._formModel, formSchema);
|
||||
}
|
||||
const disposable = control._formModel.onFormValuesUpdated(payload => {
|
||||
if (options?.onFormValuesChange) {
|
||||
options.onFormValuesChange(payload);
|
||||
}
|
||||
});
|
||||
return () => disposable.dispose();
|
||||
}, [control]);
|
||||
|
||||
return {
|
||||
form,
|
||||
control,
|
||||
model: control._formModel,
|
||||
formSchema,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { SchemaContext } from '../shared';
|
||||
|
||||
export const useFieldSchema = () => useContext(SchemaContext);
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { useObserve } from '@flowgram-adapter/common';
|
||||
|
||||
import { useFieldSchema } from './use-field-schema';
|
||||
|
||||
export const useFieldUIState = () => {
|
||||
const schema = useFieldSchema();
|
||||
return useObserve(schema?.uiState?.value);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { FormSchemaContext } from '../shared';
|
||||
|
||||
export const useFormSchema = () => useContext(FormSchemaContext);
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { useObserve } from '@flowgram-adapter/common';
|
||||
|
||||
import { useFormSchema } from './use-form-schema';
|
||||
|
||||
export const useFormUIState = () => {
|
||||
const schema = useFormSchema();
|
||||
return useObserve(schema?.uiState?.value);
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 表单引擎
|
||||
*/
|
||||
export { createSchemaField } from './fields';
|
||||
export { FormSchema } from './shared';
|
||||
export { useCreateForm, useFieldSchema, useFormSchema } from './hooks';
|
||||
export type {
|
||||
IFormSchema,
|
||||
IFormSchemaValidate,
|
||||
FormSchemaReactComponents,
|
||||
} from './types';
|
||||
|
||||
export {
|
||||
useForm,
|
||||
useCurrentFieldState,
|
||||
type FormModel,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { createContext } from 'react';
|
||||
|
||||
import type { FormSchemaReactComponents } from '../types';
|
||||
|
||||
export const ComponentsContext = createContext<FormSchemaReactComponents>({});
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import { ReactiveState } from '@flowgram-adapter/common';
|
||||
|
||||
import type { IFormSchema, FormSchemaUIState } from '../types';
|
||||
|
||||
interface PropertyWithKey {
|
||||
key: string;
|
||||
schema: IFormSchema;
|
||||
}
|
||||
|
||||
export class FormSchema implements IFormSchema {
|
||||
/** IFormSchema 透传属性 */
|
||||
type?: string | undefined;
|
||||
title?: ReactNode;
|
||||
description?: ReactNode;
|
||||
required?: boolean;
|
||||
properties?: Record<string, IFormSchema>;
|
||||
defaultValue?: any;
|
||||
|
||||
/** 模型属性 */
|
||||
uiState = new ReactiveState<FormSchemaUIState>({ disabled: false });
|
||||
path: string[] = [];
|
||||
|
||||
constructor(json: IFormSchema, path: string[] = []) {
|
||||
this.fromJSON(json);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
get componentType() {
|
||||
return this['x-component'];
|
||||
}
|
||||
get componentProps() {
|
||||
return this['x-component-props'];
|
||||
}
|
||||
get decoratorType() {
|
||||
return this['x-decorator'];
|
||||
}
|
||||
get decoratorProps() {
|
||||
return this['x-decorator-props'];
|
||||
}
|
||||
|
||||
fromJSON(json: IFormSchema) {
|
||||
Object.entries(json).forEach(([key, value]) => {
|
||||
this[key] = value;
|
||||
});
|
||||
this.uiState.value.disabled = json['x-disabled'] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得有序的 properties
|
||||
*/
|
||||
static getProperties(schema: FormSchema | IFormSchema) {
|
||||
const orderProperties: PropertyWithKey[] = [];
|
||||
const unOrderProperties: PropertyWithKey[] = [];
|
||||
Object.entries(schema.properties || {}).forEach(([key, item]) => {
|
||||
const index = item['x-index'];
|
||||
if (index !== undefined && !isNaN(index)) {
|
||||
orderProperties[index] = { schema: item, key };
|
||||
} else {
|
||||
unOrderProperties.push({ schema: item, key });
|
||||
}
|
||||
});
|
||||
return orderProperties.concat(unOrderProperties).filter(item => !!item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { ComponentsContext } from './components-context';
|
||||
export { FormSchema } from './form-schema';
|
||||
export { SchemaContext, FormSchemaContext } from './schema-context';
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { createContext } from 'react';
|
||||
|
||||
import type { FormSchema } from './form-schema';
|
||||
|
||||
export const SchemaContext = createContext<FormSchema>({} as any);
|
||||
|
||||
export const FormSchemaContext = createContext<FormSchema>({} as any);
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
type JSXComponent =
|
||||
| keyof JSX.IntrinsicElements
|
||||
| React.JSXElementConstructor<any>;
|
||||
|
||||
export type FormSchemaReactComponents = Record<string, JSXComponent>;
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type {
|
||||
IFormSchema,
|
||||
IFormSchemaValidate,
|
||||
FormSchemaUIState,
|
||||
} from './schema';
|
||||
export type { FormSchemaReactComponents } from './context';
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import type { Validate } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
export type FormSchemaTypes =
|
||||
| 'string'
|
||||
| 'object'
|
||||
| 'array'
|
||||
| 'number'
|
||||
| 'boolean'
|
||||
| 'void'
|
||||
| string;
|
||||
|
||||
export type IFormSchemaValidate = Validate;
|
||||
|
||||
export interface FormSchemaUIState {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
export interface IFormSchema<FrameworkComponent = React.ReactNode> {
|
||||
/*******************************************************
|
||||
* 核心属性
|
||||
*/
|
||||
version?: string;
|
||||
name?: string;
|
||||
type?: FormSchemaTypes;
|
||||
/** 默认值,“default” 是 jsonSchema 标准字段,但其为 js 关键字,遂使用 defaultValue */
|
||||
defaultValue?: any;
|
||||
|
||||
/*******************************************************
|
||||
* 下钻属性
|
||||
*/
|
||||
properties?: Record<string, IFormSchema<FrameworkComponent>>;
|
||||
items?: IFormSchema<FrameworkComponent>[];
|
||||
|
||||
/*******************************************************
|
||||
* ui 属性
|
||||
*/
|
||||
title?: FrameworkComponent | string;
|
||||
description?: FrameworkComponent | string;
|
||||
/** 顺序 */
|
||||
['x-index']?: number;
|
||||
['x-visible']?: boolean;
|
||||
['x-hidden']?: boolean;
|
||||
['x-disabled']?: boolean;
|
||||
/** 渲染的组件 */
|
||||
['x-component']?: string;
|
||||
['x-component-props']?: Record<string, unknown>;
|
||||
/** 装饰器 */
|
||||
['x-decorator']?: string;
|
||||
['x-decorator-props']?: Record<string, unknown>;
|
||||
|
||||
/*******************************************************
|
||||
* 合法性属性
|
||||
*/
|
||||
required?: boolean;
|
||||
['x-validator']?: IFormSchemaValidate;
|
||||
|
||||
/*******************************************************
|
||||
* 不常用或实现成本较高
|
||||
*/
|
||||
['x-reactions']?: any;
|
||||
['x-content']?: FrameworkComponent;
|
||||
/** 通配符字段 */
|
||||
patternProperties?: Record<string, IFormSchema<FrameworkComponent>>;
|
||||
/** 定义之外的字段 */
|
||||
additionalProperties?: IFormSchema<FrameworkComponent>;
|
||||
/** 定义之外的项 */
|
||||
additionalItems?: IFormSchema<FrameworkComponent>;
|
||||
|
||||
/*******************************************************
|
||||
* 业务自定义字段
|
||||
*/
|
||||
/** 节点 id */
|
||||
['x-node-id']?: string;
|
||||
/** 节点类型 */
|
||||
['x-node-type']?: string;
|
||||
/** 表单模式 */
|
||||
['x-form-mode']?: 'form' | 'json';
|
||||
/** 字段对应变量原始类型 */
|
||||
['x-origin-type']?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
26
frontend/packages/workflow/test-run-next/form/src/global.d.ts
vendored
Normal file
26
frontend/packages/workflow/test-run-next/form/src/global.d.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/// <reference types='@coze-arch/bot-typings' />
|
||||
declare module '*.otf' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.ttf' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
55
frontend/packages/workflow/test-run-next/form/src/index.ts
Normal file
55
frontend/packages/workflow/test-run-next/form/src/index.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TestRun Form
|
||||
*/
|
||||
/** Form Engine */
|
||||
export {
|
||||
createSchemaField,
|
||||
useFormSchema,
|
||||
useForm,
|
||||
useCurrentFieldState,
|
||||
FormSchema,
|
||||
type FormModel,
|
||||
type IFormSchema,
|
||||
} from './form-engine';
|
||||
|
||||
/** components */
|
||||
export { TestRunForm } from './components/test-run-form';
|
||||
export {
|
||||
InputJson as FormBaseInputJson,
|
||||
GroupCollapse as FormBaseGroupCollapse,
|
||||
FieldItem as FormBaseFieldItem,
|
||||
} from './components/base-form-materials';
|
||||
|
||||
/** context */
|
||||
export {
|
||||
TestRunFormProvider,
|
||||
useTestRunFormStore,
|
||||
type TestRunFormState,
|
||||
} from './context';
|
||||
|
||||
/** utils */
|
||||
export {
|
||||
generateField,
|
||||
generateFieldValidator,
|
||||
isFormSchemaPropertyEmpty,
|
||||
stringifyFormValuesFromBacked,
|
||||
} from './utils';
|
||||
|
||||
/** constants */
|
||||
export { TestFormFieldName } from './constants';
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { ViewVariableType, getFileAccept } from '@coze-workflow/base';
|
||||
|
||||
interface GenerateFieldComponentOptions {
|
||||
type: ViewVariableType;
|
||||
validateJsonSchema?: any;
|
||||
}
|
||||
|
||||
export const generateFieldComponent = (
|
||||
options: GenerateFieldComponentOptions,
|
||||
) => {
|
||||
const { type, validateJsonSchema } = options;
|
||||
/** 音色类型 */
|
||||
if (ViewVariableType.Voice === type) {
|
||||
return {
|
||||
['x-component']: 'SelectVoice',
|
||||
};
|
||||
}
|
||||
/** 文件类型 */
|
||||
if (ViewVariableType.isFileType(type)) {
|
||||
const fileType = [
|
||||
ViewVariableType.Image,
|
||||
ViewVariableType.ArrayImage,
|
||||
].includes(type)
|
||||
? 'image'
|
||||
: 'object';
|
||||
return {
|
||||
['x-component']: 'TypedFileInput',
|
||||
['x-component-props']: {
|
||||
// 如果是数组类型,则表明是多选的文件选择器
|
||||
multiple: ViewVariableType.isArrayType(type),
|
||||
accept: getFileAccept(type),
|
||||
fileType,
|
||||
},
|
||||
};
|
||||
}
|
||||
/** 排除文件类型的对象类型、数组类型 */
|
||||
if (ViewVariableType.isArrayType(type) || ViewVariableType.Object === type) {
|
||||
return {
|
||||
['x-component']: 'InputJson',
|
||||
['x-component-props']: {
|
||||
jsonSchema: validateJsonSchema,
|
||||
},
|
||||
defaultValue: ViewVariableType.Object === type ? '{}' : '[]',
|
||||
};
|
||||
}
|
||||
if (type === ViewVariableType.Integer) {
|
||||
return {
|
||||
['x-component']: 'InputInteger',
|
||||
};
|
||||
}
|
||||
if (type === ViewVariableType.Number) {
|
||||
return {
|
||||
['x-component']: 'InputNumber',
|
||||
};
|
||||
}
|
||||
if (type === ViewVariableType.Boolean) {
|
||||
return {
|
||||
['x-component']: 'SelectBoolean',
|
||||
defaultValue: true,
|
||||
};
|
||||
}
|
||||
if (type === ViewVariableType.Time) {
|
||||
return {
|
||||
['x-component']: 'InputTime',
|
||||
};
|
||||
}
|
||||
/** string 类型和其它未知类型都渲染普通输入框 */
|
||||
return {
|
||||
['x-component']: 'InputString',
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import Ajv from 'ajv';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
import { type IFormSchemaValidate } from '../form-engine';
|
||||
|
||||
const isEmptyValue = (v: unknown) => v === undefined || v === null || v === '';
|
||||
|
||||
interface GenerateFieldValidatorOptions {
|
||||
name: string;
|
||||
title?: string;
|
||||
required?: boolean;
|
||||
validateJsonSchema?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* ajv 实例缓存
|
||||
* 无需导入创建或者多次创建,优化内存开销
|
||||
*/
|
||||
let ajvCache: undefined | Ajv;
|
||||
|
||||
export const generateFieldValidator = (
|
||||
options: GenerateFieldValidatorOptions,
|
||||
) => {
|
||||
const { required, title, name, validateJsonSchema } = options;
|
||||
|
||||
const validator: IFormSchemaValidate = ({ value }) => {
|
||||
if (required && isEmptyValue(value)) {
|
||||
return I18n.t('workflow_testset_required_tip', {
|
||||
param_name: title || name,
|
||||
});
|
||||
}
|
||||
// 如果有结构化描述,还需要对值进行反序列化校验
|
||||
if (validateJsonSchema && value !== undefined) {
|
||||
if (!ajvCache) {
|
||||
ajvCache = new Ajv();
|
||||
}
|
||||
try {
|
||||
const valueObject = JSON.parse(value);
|
||||
const validate = ajvCache.compile(validateJsonSchema);
|
||||
const valid = validate(valueObject);
|
||||
return valid ? undefined : I18n.t('workflow_debug_wrong_json');
|
||||
} catch {
|
||||
/**
|
||||
* 报错有多种可能,预期结果都是校验不通过
|
||||
* 1. 值反序列化失败
|
||||
* 2. 反序列化的值不合法
|
||||
*/
|
||||
return I18n.t('workflow_debug_wrong_json');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
['x-validator']: validator,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { ViewVariableType } from '@coze-workflow/base';
|
||||
|
||||
import { type IFormSchema } from '../form-engine';
|
||||
import { generateFieldValidator } from './generate-field-validator';
|
||||
import { generateFieldComponent } from './generate-field-component';
|
||||
|
||||
interface GenerateFieldOptions {
|
||||
type: ViewVariableType;
|
||||
name: string;
|
||||
title?: string;
|
||||
required?: boolean;
|
||||
description?: string;
|
||||
defaultValue?: string;
|
||||
validateJsonSchema?: any;
|
||||
extra?: IFormSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单 Field Schema 计算
|
||||
*/
|
||||
export const generateField = (options: GenerateFieldOptions): IFormSchema => {
|
||||
const {
|
||||
type,
|
||||
name,
|
||||
title,
|
||||
required = true,
|
||||
description,
|
||||
defaultValue,
|
||||
validateJsonSchema,
|
||||
extra,
|
||||
} = options;
|
||||
|
||||
return {
|
||||
name,
|
||||
title,
|
||||
description,
|
||||
required,
|
||||
['x-decorator']: 'FieldItem',
|
||||
['x-decorator-props']: {
|
||||
tag: ViewVariableType.LabelMap[type],
|
||||
},
|
||||
['x-origin-type']: type as unknown as string,
|
||||
...generateFieldValidator(options),
|
||||
// 渲染组件相关
|
||||
...generateFieldComponent({ type, validateJsonSchema }),
|
||||
// component 也自带默认值,入参的默认值优先级更高
|
||||
defaultValue,
|
||||
...extra,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { generateField } from './generate-field';
|
||||
export { generateFieldValidator } from './generate-field-validator';
|
||||
export { isFormSchemaPropertyEmpty } from './is-property-empty';
|
||||
export { stringifyFormValuesFromBacked } from './stringify-form-values-from-backed';
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { isObject } from 'lodash-es';
|
||||
|
||||
/**
|
||||
* 是否是空的 properties
|
||||
*/
|
||||
export const isFormSchemaPropertyEmpty = (properties: unknown) =>
|
||||
isObject(properties) ? !Object.keys(properties).length : true;
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { isBoolean } from 'lodash-es';
|
||||
|
||||
export const stringifyFormValuesFromBacked = (value: object) => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
return Object.keys(value).reduce((acc, key) => {
|
||||
const val = value[key];
|
||||
if (val === null || val === undefined) {
|
||||
acc[key] = undefined;
|
||||
} else if (typeof val === 'string' || isBoolean(val)) {
|
||||
acc[key] = val;
|
||||
} else {
|
||||
acc[key] = JSON.stringify(value[key]);
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"compilerOptions": {
|
||||
"types": [],
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib-ts",
|
||||
"tsBuildInfoFile": "./lib-ts/tsconfig.build.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src", "./src/**/*.json"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../arch/bot-typings/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../arch/i18n/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../base/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../common/flowgram-adapter/common/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../common/flowgram-adapter/free-layout-editor/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../components/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/eslint-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/ts-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/vitest-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../shared/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
15
frontend/packages/workflow/test-run-next/form/tsconfig.json
Normal file
15
frontend/packages/workflow/test-run-next/form/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"exclude": ["**/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.misc.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"include": ["__tests__", "vitest.config.ts"],
|
||||
"exclude": ["dist"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"strictNullChecks": true,
|
||||
"rootDir": "./",
|
||||
"outDir": "./dist",
|
||||
"types": ["vitest/globals"],
|
||||
"noImplicitReturns": false,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"strictPropertyInitialization": false,
|
||||
"module": "ESNext",
|
||||
"baseUrl": "."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { defineConfig } from '@coze-arch/vitest-config';
|
||||
|
||||
export default defineConfig(
|
||||
{
|
||||
dirname: __dirname,
|
||||
preset: 'web',
|
||||
test: {
|
||||
setupFiles: ['./__tests__/setup.tsx'],
|
||||
},
|
||||
},
|
||||
{
|
||||
fixSemi: true,
|
||||
},
|
||||
);
|
||||
94
frontend/packages/workflow/test-run-next/main/README.md
Normal file
94
frontend/packages/workflow/test-run-next/main/README.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# @coze-workflow/test-run-next
|
||||
|
||||
Workflow TestRun 入口包
|
||||
|
||||
## Overview
|
||||
|
||||
This package is part of the Coze Studio monorepo and provides workflow functionality. It includes component, hook, store.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
Add this package to your `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"@coze-workflow/test-run-next": "workspace:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
rush update
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
import { /* exported functions/components */ } from '@coze-workflow/test-run-next';
|
||||
|
||||
// Example usage
|
||||
// TODO: Add specific usage examples
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Component
|
||||
- Hook
|
||||
- Store
|
||||
|
||||
## API Reference
|
||||
|
||||
### Exports
|
||||
|
||||
- `/** components */
|
||||
TestRunForm,
|
||||
FormBaseFieldItem,
|
||||
FormBaseInputJson,
|
||||
FormBaseGroupCollapse,
|
||||
TestRunFormProvider,
|
||||
/** hooks */
|
||||
useForm,
|
||||
useTestRunFormStore,
|
||||
useFormSchema,
|
||||
useCurrentFieldState,
|
||||
/** functions */
|
||||
createSchemaField,
|
||||
generateField,
|
||||
generateFieldValidator,
|
||||
isFormSchemaPropertyEmpty,
|
||||
stringifyFormValuesFromBacked,
|
||||
FormSchema,
|
||||
/** constants */
|
||||
TestFormFieldName,
|
||||
/** types */
|
||||
type FormModel,
|
||||
type TestRunFormState,
|
||||
type IFormSchema,`
|
||||
- `safeJsonParse`
|
||||
- `TraceListPanel,
|
||||
TraceDetailPanel,`
|
||||
|
||||
|
||||
For detailed API documentation, please refer to the TypeScript definitions.
|
||||
|
||||
## Development
|
||||
|
||||
This package is built with:
|
||||
|
||||
- TypeScript
|
||||
- Modern JavaScript
|
||||
- Vitest for testing
|
||||
- ESLint for code quality
|
||||
|
||||
## Contributing
|
||||
|
||||
This package is part of the Coze Studio monorepo. Please follow the monorepo contribution guidelines.
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"operationSettings": [
|
||||
{
|
||||
"operationName": "ts-check",
|
||||
"outputFolderNames": ["./dist"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
const { defineConfig } = require('@coze-arch/eslint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
packageRoot: __dirname,
|
||||
preset: 'web',
|
||||
rules: {
|
||||
'@coze-arch/max-line-per-function': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@coze-arch/zustand/prefer-shallow': 'off',
|
||||
'@coze-arch/no-deep-relative-import': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
},
|
||||
});
|
||||
28
frontend/packages/workflow/test-run-next/main/package.json
Normal file
28
frontend/packages/workflow/test-run-next/main/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@coze-workflow/test-run-next",
|
||||
"version": "0.0.1",
|
||||
"description": "Workflow TestRun 入口包",
|
||||
"author": "jiangxujin@bytedance.com",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
"build": "exit 0",
|
||||
"lint": "eslint ./ --cache",
|
||||
"test": "exit 0",
|
||||
"test:cov": "exit 0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coze-workflow/test-run-form": "workspace:*",
|
||||
"@coze-workflow/test-run-shared": "workspace:*",
|
||||
"@coze-workflow/test-run-trace": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@coze-arch/bot-typings": "workspace:*",
|
||||
"@coze-arch/eslint-config": "workspace:*",
|
||||
"@coze-arch/ts-config": "workspace:*",
|
||||
"typescript": "~5.8.2"
|
||||
}
|
||||
}
|
||||
|
||||
26
frontend/packages/workflow/test-run-next/main/src/global.d.ts
vendored
Normal file
26
frontend/packages/workflow/test-run-next/main/src/global.d.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/// <reference types='@coze-arch/bot-typings' />
|
||||
declare module '*.otf' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.ttf' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
61
frontend/packages/workflow/test-run-next/main/src/index.ts
Normal file
61
frontend/packages/workflow/test-run-next/main/src/index.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TestRun Main
|
||||
*/
|
||||
|
||||
/*******************************************************************************
|
||||
* TestRun Form
|
||||
*/
|
||||
export {
|
||||
/** components */
|
||||
TestRunForm,
|
||||
FormBaseFieldItem,
|
||||
FormBaseInputJson,
|
||||
FormBaseGroupCollapse,
|
||||
TestRunFormProvider,
|
||||
/** hooks */
|
||||
useForm,
|
||||
useTestRunFormStore,
|
||||
useFormSchema,
|
||||
useCurrentFieldState,
|
||||
/** functions */
|
||||
createSchemaField,
|
||||
generateField,
|
||||
generateFieldValidator,
|
||||
isFormSchemaPropertyEmpty,
|
||||
stringifyFormValuesFromBacked,
|
||||
FormSchema,
|
||||
/** constants */
|
||||
TestFormFieldName,
|
||||
/** types */
|
||||
type FormModel,
|
||||
type TestRunFormState,
|
||||
type IFormSchema,
|
||||
} from '@coze-workflow/test-run-form';
|
||||
|
||||
/*******************************************************************************
|
||||
* TestRun Shared
|
||||
*/
|
||||
export { safeJsonParse } from '@coze-workflow/test-run-shared';
|
||||
/**
|
||||
* TestRun Trace
|
||||
*/
|
||||
export {
|
||||
TraceListPanel,
|
||||
TraceDetailPanel,
|
||||
} from '@coze-workflow/test-run-trace';
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@coze-arch/ts-config/tsconfig.web.json",
|
||||
"compilerOptions": {
|
||||
"types": [],
|
||||
"rootDir": "./src",
|
||||
"outDir": "./lib-ts",
|
||||
"tsBuildInfoFile": "./lib-ts/tsconfig.build.tsbuildinfo"
|
||||
},
|
||||
"include": ["./src", "./src/**/*.json"],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../arch/bot-typings/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/eslint-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../../../../config/ts-config/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../form/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../shared/tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "../trace/tsconfig.build.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
15
frontend/packages/workflow/test-run-next/main/tsconfig.json
Normal file
15
frontend/packages/workflow/test-run-next/main/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"exclude": ["**/*"],
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.build.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.misc.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
69
frontend/packages/workflow/test-run-next/shared/README.md
Normal file
69
frontend/packages/workflow/test-run-next/shared/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# @coze-workflow/test-run-shared
|
||||
|
||||
Workflow TestRun 公共包
|
||||
|
||||
## Overview
|
||||
|
||||
This package is part of the Coze Studio monorepo and provides workflow functionality. It includes component, editor.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Installation
|
||||
|
||||
Add this package to your `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"@coze-workflow/test-run-shared": "workspace:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```bash
|
||||
rush update
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```typescript
|
||||
import { /* exported functions/components */ } from '@coze-workflow/test-run-shared';
|
||||
|
||||
// Example usage
|
||||
// TODO: Add specific usage examples
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Component
|
||||
- Editor
|
||||
|
||||
## API Reference
|
||||
|
||||
### Exports
|
||||
|
||||
- `JsonEditor`
|
||||
- `safeFormatJsonString, safeJsonParse, gotoDebugFlow`
|
||||
- `BottomPanel`
|
||||
|
||||
|
||||
For detailed API documentation, please refer to the TypeScript definitions.
|
||||
|
||||
## Development
|
||||
|
||||
This package is built with:
|
||||
|
||||
- TypeScript
|
||||
- Modern JavaScript
|
||||
- Vitest for testing
|
||||
- ESLint for code quality
|
||||
|
||||
## Contributing
|
||||
|
||||
This package is part of the Coze Studio monorepo. Please follow the monorepo contribution guidelines.
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import { safeFormatJsonString } from '../../src/utils/safe-format-json-string';
|
||||
|
||||
describe('utils-safe-format-json-string', () => {
|
||||
it('value is not string', () => {
|
||||
const value = true;
|
||||
expect(safeFormatJsonString(value)).toBe(value);
|
||||
});
|
||||
it('value is not json string', () => {
|
||||
const value = 'string';
|
||||
expect(safeFormatJsonString(value)).toBe(value);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import { safeJsonParse } from '../../src/utils/safe-json-parse';
|
||||
|
||||
describe('utils-safe-json-parse', () => {
|
||||
// 测试正常解析 JSON 字符串
|
||||
it('should parse valid JSON string', () => {
|
||||
const jsonString = '{"key": "value"}';
|
||||
const result = safeJsonParse(jsonString);
|
||||
expect(result).toEqual({ key: 'value' });
|
||||
});
|
||||
|
||||
// 测试解析无效 JSON 字符串
|
||||
it('should return undefined when parsing invalid JSON string', () => {
|
||||
const invalidJsonString = '{key: "value"}';
|
||||
const result = safeJsonParse(invalidJsonString);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
// 测试空字符串输入
|
||||
it('should return emptyValue when input is an empty string', () => {
|
||||
const emptyString = '';
|
||||
const emptyValue = {};
|
||||
const result = safeJsonParse(emptyString, { emptyValue });
|
||||
expect(result).toBe(emptyValue);
|
||||
});
|
||||
|
||||
it('should return object when input is an empty object', () => {
|
||||
const value = {};
|
||||
expect(safeJsonParse(value)).toBe(value);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"operationSettings": [
|
||||
{
|
||||
"operationName": "test:cov",
|
||||
"outputFolderNames": ["coverage"]
|
||||
},
|
||||
{
|
||||
"operationName": "ts-check",
|
||||
"outputFolderNames": ["./dist"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
const { defineConfig } = require('@coze-arch/eslint-config');
|
||||
|
||||
module.exports = defineConfig({
|
||||
packageRoot: __dirname,
|
||||
preset: 'web',
|
||||
rules: {
|
||||
'@coze-arch/max-line-per-function': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@coze-arch/zustand/prefer-shallow': 'off',
|
||||
'@coze-arch/no-deep-relative-import': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
},
|
||||
});
|
||||
41
frontend/packages/workflow/test-run-next/shared/package.json
Normal file
41
frontend/packages/workflow/test-run-next/shared/package.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "@coze-workflow/test-run-shared",
|
||||
"version": "0.0.1",
|
||||
"description": "Workflow TestRun 公共包",
|
||||
"author": "jiangxujin@bytedance.com",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
"build": "exit 0",
|
||||
"lint": "eslint ./ --cache",
|
||||
"test": "vitest run --passWithNoTests",
|
||||
"test:cov": "vitest run --passWithNoTests --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.10.1",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@codemirror/view": "^6.34.1",
|
||||
"@coze-editor/editor": "0.1.0-alpha.d92d50",
|
||||
"@coze-arch/coze-design": "0.0.6-alpha.346d77",
|
||||
"@lezer/common": "^1.2.2",
|
||||
"ahooks": "^3.7.8",
|
||||
"clsx": "^1.2.1",
|
||||
"lodash-es": "^4.17.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@coze-arch/bot-typings": "workspace:*",
|
||||
"@coze-arch/eslint-config": "workspace:*",
|
||||
"@coze-arch/ts-config": "workspace:*",
|
||||
"@coze-arch/vitest-config": "workspace:*",
|
||||
"@types/lodash-es": "^4.17.10",
|
||||
"@types/react": "18.2.37",
|
||||
"@vitest/coverage-v8": "~3.0.5",
|
||||
"react": "~18.2.0",
|
||||
"react-dom": "~18.2.0",
|
||||
"typescript": "~5.8.2",
|
||||
"vitest": "~3.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { BottomPanel } from './panel';
|
||||
@@ -0,0 +1,48 @@
|
||||
.base-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
border: 1px solid var(--coz-stroke-primary);
|
||||
border-radius: 8px;
|
||||
background-color: var(--coz-bg-plus);
|
||||
position: relative;
|
||||
min-width: 540px;
|
||||
}
|
||||
|
||||
.dragging {
|
||||
cursor: row-resize;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
height: 48px;
|
||||
border-bottom: 1px solid var(--coz-stroke-primary);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
padding: 0 12px;
|
||||
flex-shrink: 0;
|
||||
column-gap: 8px;
|
||||
}
|
||||
.panel-content {
|
||||
height: 100%;
|
||||
flex-shrink: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.panel-footer {
|
||||
border-top: 1px solid var(--coz-stroke-primary);;
|
||||
}
|
||||
|
||||
.resize-bar {
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
cursor: row-resize;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { isObject } from 'lodash-es';
|
||||
import { clsx } from 'clsx';
|
||||
import { IconCozCross } from '@coze-arch/coze-design/icons';
|
||||
import { IconButton } from '@coze-arch/coze-design';
|
||||
|
||||
import { useResize } from './use-resize';
|
||||
|
||||
import css from './panel.module.less';
|
||||
|
||||
interface BasePanelProps {
|
||||
className?: string;
|
||||
/**
|
||||
* 面板头,不传不渲染
|
||||
*/
|
||||
header?: React.ReactNode;
|
||||
/**
|
||||
* 面板脚,不传不渲染
|
||||
*/
|
||||
footer?: React.ReactNode;
|
||||
/**
|
||||
* 默认初始高度,不支持响应式
|
||||
*/
|
||||
height?: number;
|
||||
/**
|
||||
* 是否可拖拽改变高度
|
||||
*/
|
||||
resizable?:
|
||||
| boolean
|
||||
| {
|
||||
min?: number;
|
||||
max?: number;
|
||||
};
|
||||
/**
|
||||
* 点击关闭事件,仅当渲染面板头时可能触发
|
||||
*/
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
export const BottomPanel: React.FC<React.PropsWithChildren<BasePanelProps>> = ({
|
||||
className,
|
||||
header,
|
||||
footer,
|
||||
height,
|
||||
resizable,
|
||||
onClose,
|
||||
children,
|
||||
}) => {
|
||||
const {
|
||||
height: innerHeight,
|
||||
bind,
|
||||
ref,
|
||||
dragging,
|
||||
} = useResize({
|
||||
default: height,
|
||||
...(isObject(resizable) ? resizable : {}),
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(css['base-panel'], className, dragging && css.dragging)}
|
||||
style={{ height: innerHeight }}
|
||||
ref={ref}
|
||||
>
|
||||
{resizable ? (
|
||||
<div className={css['resize-bar']} onMouseDown={bind} />
|
||||
) : null}
|
||||
{header ? (
|
||||
<div className={css['panel-header']}>
|
||||
{header}
|
||||
<IconButton
|
||||
icon={<IconCozCross className={'text-[18px]'} />}
|
||||
color="secondary"
|
||||
onClick={onClose}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div className={css['panel-content']}>{children}</div>
|
||||
{footer ? <div className={css['panel-footer']}>{footer}</div> : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { useState, useRef, useCallback } from 'react';
|
||||
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
interface Config {
|
||||
default?: number;
|
||||
min?: number;
|
||||
max?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 目前仅支持高度可变
|
||||
*/
|
||||
export const useResize = (config: Config) => {
|
||||
const [dragging, setDragging] = useState(false);
|
||||
const [height, setHeight] = useState(config.default);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
/**
|
||||
* 拖拽过程中
|
||||
*/
|
||||
const resizing = useRef(false);
|
||||
/**
|
||||
* y 轴变化
|
||||
*/
|
||||
const startY = useRef(0);
|
||||
/** 开始位置 */
|
||||
const start = useRef(0);
|
||||
|
||||
const handleMouseMove = useMemoizedFn(e => {
|
||||
if (resizing.current) {
|
||||
const newHeight = start.current - (e.clientY - startY.current); // 计算新的高度
|
||||
if (config.max && newHeight > config.max) {
|
||||
setHeight(config.max);
|
||||
} else if (config.min && newHeight < config.min) {
|
||||
setHeight(config.min);
|
||||
} else {
|
||||
setHeight(newHeight);
|
||||
}
|
||||
}
|
||||
});
|
||||
const handleMouseUp = useCallback(() => {
|
||||
resizing.current = false;
|
||||
setDragging(false);
|
||||
document.removeEventListener('mousemove', handleMouseMove); // 取消监听
|
||||
document.removeEventListener('mouseup', handleMouseUp); // 取消监听
|
||||
}, [handleMouseMove]);
|
||||
|
||||
const handleMouseDown = useMemoizedFn(e => {
|
||||
resizing.current = true;
|
||||
setDragging(true);
|
||||
startY.current = e.clientY; // 记录鼠标开始拖拽时的 Y 轴坐标
|
||||
start.current = ref.current?.offsetHeight || 0;
|
||||
document.addEventListener('mousemove', handleMouseMove); // 监听鼠标移动事件
|
||||
document.addEventListener('mouseup', handleMouseUp); // 监听鼠标抬起事件
|
||||
});
|
||||
|
||||
return {
|
||||
height,
|
||||
bind: handleMouseDown,
|
||||
ref,
|
||||
dragging,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { EditorProvider, createRenderer } from '@coze-editor/editor/react';
|
||||
import preset, {
|
||||
languages,
|
||||
createTheme,
|
||||
tags,
|
||||
} from '@coze-editor/editor/preset-code';
|
||||
import { json } from '@coze-editor/editor/language-json';
|
||||
import { EditorView, tooltips } from '@codemirror/view';
|
||||
|
||||
const colors = {
|
||||
background: '#F7F7FC',
|
||||
// syntax
|
||||
comment: '#000A298A',
|
||||
key: '#00818C',
|
||||
string: '#D1009D',
|
||||
number: '#C74200',
|
||||
boolean: '#2B57D9',
|
||||
null: '#2B57D9',
|
||||
separator: '#0F1529D1',
|
||||
};
|
||||
|
||||
languages.register('json', json);
|
||||
|
||||
const JSONEditor: any = createRenderer(preset, [
|
||||
EditorView.theme({
|
||||
'&': {
|
||||
borderRadius: '8px',
|
||||
},
|
||||
'.cm-scroller': {
|
||||
transition: 'height .3s ease',
|
||||
},
|
||||
'.cm-content': {
|
||||
paddingTop: '6px',
|
||||
paddingBottom: '6px',
|
||||
},
|
||||
'.cm-completionIcon-property': {
|
||||
backgroundImage:
|
||||
'url("' +
|
||||
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMWVtIiBoZWlnaHQ9IjFlbSIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xMi4zNTc2IDguMTAzNTVDMTIuMTYyMyA3LjkwODI5IDExLjg0NTcgNy45MDgyOSAxMS42NTA1IDguMTAzNTVMOC4xMDM1NSAxMS42NTA1QzcuOTA4MjkgMTEuODQ1NyA3LjkwODI5IDEyLjE2MjMgOC4xMDM1NSAxMi4zNTc2TDExLjY1MDUgMTUuOTA0NUMxMS44NDU3IDE2LjA5OTggMTIuMTYyMyAxNi4wOTk4IDEyLjM1NzYgMTUuOTA0NUwxNS45MDQ1IDEyLjM1NzZDMTYuMDk5OCAxMi4xNjIzIDE2LjA5OTggMTEuODQ1NyAxNS45MDQ1IDExLjY1MDVMMTIuMzU3NiA4LjEwMzU1WiIgZmlsbD0iIzA2MDcwOUNDIi8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xMS4wMDI2IDEuNDU1NDVDMTEuNjIxNCAxLjA5ODE4IDEyLjM4MzggMS4wOTgxOCAxMy4wMDI2IDEuNDU1NDVMMjAuNjM4IDUuODYzNzRDMjEuMjU2OCA2LjIyMSAyMS42MzggNi44ODEyNiAyMS42MzggNy41OTU3OVYxNi40MTI0QzIxLjYzOCAxNy4xMjY5IDIxLjI1NjggMTcuNzg3MiAyMC42MzggMTguMTQ0NEwxMy4wMDI2IDIyLjU1MjdDMTIuMzgzOCAyMi45MSAxMS42MjE0IDIyLjkxIDExLjAwMjYgMjIuNTUyN0wzLjM2NzE5IDE4LjE0NDRDMi43NDgzOSAxNy43ODcyIDIuMzY3MTkgMTcuMTI2OSAyLjM2NzE5IDE2LjQxMjRWNy41OTU3OUMyLjM2NzE5IDYuODgxMjYgMi43NDgzOSA2LjIyMTAxIDMuMzY3MTkgNS44NjM3NEwxMS4wMDI2IDEuNDU1NDVaTTEyLjAwMjYgMy4xODc1TDE5LjYzOCA3LjU5NTc5VjE2LjQxMjRMMTIuMDAyNiAyMC44MjA3TDQuMzY3MTkgMTYuNDEyNEw0LjM2NzE5IDcuNTk1NzlMMTIuMDAyNiAzLjE4NzVaIiBmaWxsPSIjMDYwNzA5Q0MiLz48L3N2Zz4=' +
|
||||
'")',
|
||||
backgroundSize: '11px 11px',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
width: '11px',
|
||||
height: '11px',
|
||||
},
|
||||
'.cm-completionIcon-property::after': {
|
||||
content: '""',
|
||||
},
|
||||
'.cm-selectionBackground': {
|
||||
borderRadius: '4px',
|
||||
},
|
||||
'.cm-activeLineGutter': {
|
||||
borderRadius: '4px 0 0 4px',
|
||||
},
|
||||
'.cm-activeLine': {
|
||||
borderRadius: '0 4px 4px 0',
|
||||
},
|
||||
'&.cm-focused': {
|
||||
outline: 'none',
|
||||
},
|
||||
'& *': {
|
||||
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
|
||||
},
|
||||
'.cm-tooltip': {
|
||||
wordBreak: 'break-all',
|
||||
},
|
||||
'.cm-lineNumbers .cm-gutterElement': {
|
||||
transform: 'translate(0, 1px)',
|
||||
},
|
||||
'.cm-foldGutter .cm-gutterElement > div': {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
'.cm-completionIcon': {
|
||||
fontSize: '11px',
|
||||
},
|
||||
}),
|
||||
createTheme({
|
||||
variant: 'light',
|
||||
settings: {
|
||||
background: colors.background,
|
||||
foreground: '#4D4D4C',
|
||||
caret: '#AEAFAD',
|
||||
selection: '#52649A21',
|
||||
gutterBackground: colors.background,
|
||||
gutterForeground: '#000A298A',
|
||||
gutterBorderColor: 'transparent',
|
||||
gutterBorderWidth: 0,
|
||||
lineHighlight: '#efefef78',
|
||||
bracketColors: ['#E4D129', '#AC05FF', '#2B57D9'],
|
||||
tooltip: {
|
||||
backgroundColor: 'var(--coz-bg-max)',
|
||||
color: 'var(--coz-fg-primary)',
|
||||
border: 'solid 1px var(--coz-stroke-plus)',
|
||||
boxShadow: 'var(--coz-shadow-default)',
|
||||
borderRadius: '8px',
|
||||
},
|
||||
tooltipCompletion: {
|
||||
backgroundColor: '#FFFFFF',
|
||||
color: '#060709CC',
|
||||
},
|
||||
completionItemHover: {
|
||||
backgroundColor: '#5768A114',
|
||||
},
|
||||
completionItemSelected: {
|
||||
backgroundColor: '#52649A21',
|
||||
},
|
||||
completionItemIcon: {
|
||||
color: '#060709CC',
|
||||
},
|
||||
completionItemLabel: {
|
||||
color: '#060709CC',
|
||||
},
|
||||
completionItemDetail: {
|
||||
color: '#2029459E',
|
||||
},
|
||||
},
|
||||
styles: [
|
||||
// JSON
|
||||
{
|
||||
tag: tags.comment,
|
||||
color: colors.comment,
|
||||
},
|
||||
{
|
||||
tag: [tags.propertyName],
|
||||
color: colors.key,
|
||||
},
|
||||
{
|
||||
tag: [tags.string],
|
||||
color: colors.string,
|
||||
},
|
||||
{
|
||||
tag: [tags.number],
|
||||
color: colors.number,
|
||||
},
|
||||
{
|
||||
tag: [tags.bool],
|
||||
color: colors.boolean,
|
||||
},
|
||||
{
|
||||
tag: [tags.null],
|
||||
color: colors.null,
|
||||
},
|
||||
{
|
||||
tag: [tags.separator],
|
||||
color: colors.separator,
|
||||
},
|
||||
],
|
||||
}),
|
||||
tooltips({
|
||||
parent: document.body,
|
||||
tooltipSpace() {
|
||||
return {
|
||||
left: 16,
|
||||
top: 16,
|
||||
right: window.innerWidth - 16,
|
||||
bottom: window.innerHeight - 16,
|
||||
};
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
export { JSONEditor, EditorProvider };
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { JsonEditor } from './json-editor';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user