From e60e6ea36be6a9d3fe03592d071e3e91b55f454b Mon Sep 17 00:00:00 2001 From: waynelwz <100347187+waynelwz@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:55:23 +0800 Subject: [PATCH] feat(console): model argument schema support array/enum etc (#3132) --- .../templates/ArrayFieldItemTemplate.tsx | 90 +++++++++++++++++ .../Argument/templates/ArrayFieldTemplate.tsx | 89 +++++++++++++++++ .../Argument/templates/IconButton.tsx | 97 +++++++++++++++++++ .../src/RJSFForm/Argument/templates/index.ts | 36 +++++++ .../Argument/widgets/CheckboxWidget.tsx | 94 ++++++++++++++++++ .../src/RJSFForm/Argument/widgets/index.ts | 27 ++++++ .../src/RJSFForm/ArgumentForm.tsx | 35 +++++++ .../templates/FieldTemplate/FieldTemplate.tsx | 49 ++++++++++ .../templates/FieldTemplate/Label.tsx | 27 ++++++ .../RJSFForm/templates/FieldTemplate/index.ts | 3 + .../src/RJSFForm/templates/index.ts | 7 +- .../domain/job/components/FormFieldModel.tsx | 24 +++-- console/src/domain/job/utils.ts | 9 +- 13 files changed, 572 insertions(+), 15 deletions(-) create mode 100644 console/packages/starwhale-ui/src/RJSFForm/Argument/templates/ArrayFieldItemTemplate.tsx create mode 100644 console/packages/starwhale-ui/src/RJSFForm/Argument/templates/ArrayFieldTemplate.tsx create mode 100644 console/packages/starwhale-ui/src/RJSFForm/Argument/templates/IconButton.tsx create mode 100644 console/packages/starwhale-ui/src/RJSFForm/Argument/templates/index.ts create mode 100644 console/packages/starwhale-ui/src/RJSFForm/Argument/widgets/CheckboxWidget.tsx create mode 100644 console/packages/starwhale-ui/src/RJSFForm/Argument/widgets/index.ts create mode 100644 console/packages/starwhale-ui/src/RJSFForm/ArgumentForm.tsx create mode 100644 console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/FieldTemplate.tsx create mode 100644 console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/Label.tsx create mode 100644 console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/index.ts diff --git a/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/ArrayFieldItemTemplate.tsx b/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/ArrayFieldItemTemplate.tsx new file mode 100644 index 0000000000..c951e66660 --- /dev/null +++ b/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/ArrayFieldItemTemplate.tsx @@ -0,0 +1,90 @@ +import { CSSProperties } from 'react' +import { ArrayFieldTemplateItemType, FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils' + +/** The `ArrayFieldItemTemplate` component is the template used to render an items of an array. + * + * @param props - The `ArrayFieldTemplateItemType` props for the component + */ +export default function ArrayFieldItemTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: ArrayFieldTemplateItemType) { + const { + children, + className, + disabled, + hasToolbar, + hasMoveDown, + hasMoveUp, + hasRemove, + hasCopy, + index, + onCopyIndexClick, + onDropIndexClick, + onReorderClick, + readonly, + registry, + uiSchema, + } = props + const { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } = registry.templates.ButtonTemplates + const btnStyle: CSSProperties = { + flex: 1, + paddingLeft: 6, + paddingRight: 6, + fontWeight: 'bold', + } + return ( +
+
{children}
+ {hasToolbar && ( +
+
+ {(hasMoveUp || hasMoveDown) && ( + + )} + {(hasMoveUp || hasMoveDown) && ( + + )} + {hasCopy && ( + + )} + {hasRemove && ( + + )} +
+
+ )} +
+ ) +} diff --git a/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/ArrayFieldTemplate.tsx b/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/ArrayFieldTemplate.tsx new file mode 100644 index 0000000000..8bb4382c86 --- /dev/null +++ b/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/ArrayFieldTemplate.tsx @@ -0,0 +1,89 @@ +import { + getTemplate, + getUiOptions, + ArrayFieldTemplateProps, + ArrayFieldTemplateItemType, + FormContextType, + RJSFSchema, + StrictRJSFSchema, +} from '@rjsf/utils' + +/** The `ArrayFieldTemplate` component is the template used to render all items in an array. + * + * @param props - The `ArrayFieldTemplateItemType` props for the component + */ +export default function ArrayFieldTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: ArrayFieldTemplateProps) { + const { + canAdd, + className, + disabled, + idSchema, + uiSchema, + items, + onAddClick, + readonly, + registry, + // required, + schema, + // title, + } = props + const uiOptions = getUiOptions(uiSchema) + const ArrayFieldDescriptionTemplate = getTemplate<'ArrayFieldDescriptionTemplate', T, S, F>( + 'ArrayFieldDescriptionTemplate', + registry, + uiOptions + ) + const ArrayFieldItemTemplate = getTemplate<'ArrayFieldItemTemplate', T, S, F>( + 'ArrayFieldItemTemplate', + registry, + uiOptions + ) + // const ArrayFieldTitleTemplate = getTemplate<'ArrayFieldTitleTemplate', T, S, F>( + // 'ArrayFieldTitleTemplate', + // registry, + // uiOptions + // ) + // Button templates are not overridden in the uiSchema + const { + ButtonTemplates: { AddButton }, + } = registry.templates + + return ( +
+ {/* */} + +
+ {items && + items.map(({ key, ...itemProps }: ArrayFieldTemplateItemType) => ( + + ))} +
+ {canAdd && ( + + )} +
+ ) +} diff --git a/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/IconButton.tsx b/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/IconButton.tsx new file mode 100644 index 0000000000..5d00a8c950 --- /dev/null +++ b/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/IconButton.tsx @@ -0,0 +1,97 @@ +import { FormContextType, IconButtonProps, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils' +import { ExtendButton } from '@starwhale/ui/Button' + +export default function IconButton( + props: IconButtonProps +) { + const { icon, ...otherProps } = props + return ( + // + // + // @ts-ignore + + ) +} + +export function AddButton( + props: IconButtonProps +) { + const { + registry: { translateString }, + } = props + return ( + + ) +} + +export function CopyButton( + props: IconButtonProps +) { + const { + registry: { translateString }, + } = props + return ( + + ) +} + +export function MoveDownButton( + props: IconButtonProps +) { + const { + registry: { translateString }, + } = props + return ( + + ) +} + +export function MoveUpButton( + props: IconButtonProps +) { + const { + registry: { translateString }, + } = props + return ( + + ) +} + +export function RemoveButton( + props: IconButtonProps +) { + const { + registry: { translateString }, + } = props + return ( + + ) +} diff --git a/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/index.ts b/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/index.ts new file mode 100644 index 0000000000..a531abe30c --- /dev/null +++ b/console/packages/starwhale-ui/src/RJSFForm/Argument/templates/index.ts @@ -0,0 +1,36 @@ +import ArrayFieldItemTemplate from './ArrayFieldItemTemplate' +import ArrayFieldTemplate from './ArrayFieldTemplate' +// import BaseInputTemplate from "./BaseInputTemplate"; +// import DescriptionField from "./DescriptionField"; +// import ErrorList from "./ErrorList"; +import { AddButton, MoveDownButton, MoveUpButton, RemoveButton } from './IconButton' +// import FieldErrorTemplate from "./FieldErrorTemplate"; +// import FieldTemplate from './FieldTemplate' +// import SubmitButton from "./SubmitButton"; +// import TitleField from "./TitleField"; +// import WrapIfAdditionalTemplate from "./WrapIfAdditionalTemplate"; +import { TemplatesType } from '@rjsf/utils' + +const Index: Partial = { + ArrayFieldItemTemplate, + ArrayFieldTemplate: ArrayFieldTemplate as TemplatesType['ArrayFieldTemplate'], + // BaseInputTemplate, + // @ts-ignore + ButtonTemplates: { + AddButton, + MoveDownButton, + MoveUpButton, + RemoveButton, + // SubmitButton, + }, + // DescriptionFieldTemplate: DescriptionField, + // ErrorListTemplate: ErrorList, + // FieldErrorTemplate, + // FieldTemplate, + // ObjectFieldTemplate, + // ObjectFieldTemplate as TemplatesType["ObjectFieldTemplate"], + // TitleFieldTemplate: TitleField as TemplatesType["TitleFieldTemplate"], + // WrapIfAdditionalTemplate, +} + +export default Index diff --git a/console/packages/starwhale-ui/src/RJSFForm/Argument/widgets/CheckboxWidget.tsx b/console/packages/starwhale-ui/src/RJSFForm/Argument/widgets/CheckboxWidget.tsx new file mode 100644 index 0000000000..ac76ba70f1 --- /dev/null +++ b/console/packages/starwhale-ui/src/RJSFForm/Argument/widgets/CheckboxWidget.tsx @@ -0,0 +1,94 @@ +import { ChangeEvent, FocusEvent, useCallback } from 'react' +import { + ariaDescribedByIds, + descriptionId, + getTemplate, + schemaRequiresTrueValue, + FormContextType, + RJSFSchema, + StrictRJSFSchema, + WidgetProps, +} from '@rjsf/utils' + +/** The `CheckBoxWidget` is a widget for rendering boolean properties. + * It is typically used to represent a boolean. + * + * @param props - The `WidgetProps` for this component + */ +function CheckboxWidget({ + schema, + uiSchema, + options, + id, + value, + disabled, + readonly, + hideLabel, + autofocus = false, + onBlur, + onFocus, + onChange, + registry, +}: WidgetProps) { + const DescriptionFieldTemplate = getTemplate<'DescriptionFieldTemplate', T, S, F>( + 'DescriptionFieldTemplate', + registry, + options + ) + // Because an unchecked checkbox will cause html5 validation to fail, only add + // the "required" attribute if the field value must be "true", due to the + // "const" or "enum" keywords + const required = schemaRequiresTrueValue(schema) + + const handleChange = useCallback( + (event: ChangeEvent) => onChange(event.target.checked), + [onChange] + ) + + const handleBlur = useCallback( + (event: FocusEvent) => onBlur(id, event.target.checked), + [onBlur, id] + ) + + const handleFocus = useCallback( + (event: FocusEvent) => onFocus(id, event.target.checked), + [onFocus, id] + ) + const description = options.description ?? schema.description + + return ( +
+
+
+ (id)} + /> +
+
+ {!hideLabel && !!description && ( + (id)} + description={description} + schema={schema} + uiSchema={uiSchema} + registry={registry} + /> + )} +
+
+
+ ) +} + +export default CheckboxWidget diff --git a/console/packages/starwhale-ui/src/RJSFForm/Argument/widgets/index.ts b/console/packages/starwhale-ui/src/RJSFForm/Argument/widgets/index.ts new file mode 100644 index 0000000000..4f22800ace --- /dev/null +++ b/console/packages/starwhale-ui/src/RJSFForm/Argument/widgets/index.ts @@ -0,0 +1,27 @@ +// import AltDateTimeWidget from "./AltDateTimeWidget"; +// import AltDateWidget from "./AltDateWidget"; +// import CheckboxesWidget from "./CheckboxesWidget"; +import CheckboxWidget from './CheckboxWidget' +// import DateTimeWidget from "./DateTimeWidget"; +// import DateWidget from "./DateWidget"; +// import PasswordWidget from "./PasswordWidget"; +// import RadioWidget from "./RadioWidget"; +// import RangeWidget from "./RangeWidget"; +// import SelectWidget from './SelectWidget' +// import TextWidget from './TextWidget' + +const Widgets = { + // AltDateTimeWidget, + // AltDateWidget, + // CheckboxesWidget, + CheckboxWidget, + // DateTimeWidget, + // DateWidget, + // PasswordWidget, + // RadioWidget, + // RangeWidget, + // SelectWidget, + // TextWidget, +} + +export default Widgets diff --git a/console/packages/starwhale-ui/src/RJSFForm/ArgumentForm.tsx b/console/packages/starwhale-ui/src/RJSFForm/ArgumentForm.tsx new file mode 100644 index 0000000000..0150fb53b4 --- /dev/null +++ b/console/packages/starwhale-ui/src/RJSFForm/ArgumentForm.tsx @@ -0,0 +1,35 @@ +import Widgets from '@starwhale/ui/RJSFForm/widgets' +import Templates from '@starwhale/ui/RJSFForm/templates' +import Form from '@rjsf/core' +import validator from '@rjsf/validator-ajv8' +import React from 'react' +import ExtraWidgets from './Argument/widgets' +import ExtraTemplates from './Argument/templates' + +// @ts-ignore +function ArgumentForm({ formData, onChange, onSubmit, form }: any, ref?: any) { + const { schema, uiSchema } = form.schemas + return ( +
{ + if (ref) + // eslint-disable-next-line no-param-reassign + ref.current = f + }} + onChange={(e) => { + onChange?.(e.formData) + }} + /> + ) +} + +export default React.forwardRef(ArgumentForm) diff --git a/console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/FieldTemplate.tsx b/console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/FieldTemplate.tsx new file mode 100644 index 0000000000..c5ba834a25 --- /dev/null +++ b/console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/FieldTemplate.tsx @@ -0,0 +1,49 @@ +import { + FieldTemplateProps, + FormContextType, + RJSFSchema, + StrictRJSFSchema, + getTemplate, + getUiOptions, +} from '@rjsf/utils' + +import Label from './Label' + +/** The `FieldTemplate` component is the template used by `SchemaField` to render any field. It renders the field + * content, (label, description, children, errors and help) inside of a `WrapIfAdditional` component. + * + * @param props - The `FieldTemplateProps` for this component + */ +export default function FieldTemplate< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any +>(props: FieldTemplateProps) { + const { id, label, children, errors, help, description, hidden, required, displayLabel, registry, uiSchema } = props + const uiOptions = getUiOptions(uiSchema) + const WrapIfAdditionalTemplate = getTemplate<'WrapIfAdditionalTemplate', T, S, F>( + 'WrapIfAdditionalTemplate', + registry, + uiOptions + ) + + if (hidden) { + return
{children}
+ } + + return ( + +

+

+
+ {children} +
+ {displayLabel && description ? description : null} + {errors} + {help} +
+
+
+ ) +} diff --git a/console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/Label.tsx b/console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/Label.tsx new file mode 100644 index 0000000000..29199e61c0 --- /dev/null +++ b/console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/Label.tsx @@ -0,0 +1,27 @@ +const REQUIRED_FIELD_SYMBOL = '*' + +export type LabelProps = { + /** The label for the field */ + label?: string + /** A boolean value stating if the field is required */ + required?: boolean + /** The id of the input field being labeled */ + id?: string +} + +/** Renders a label for a field + * + * @param props - The `LabelProps` for this component + */ +export default function Label(props: LabelProps) { + const { label, required, id } = props + if (!label) { + return null + } + return ( + + ) +} diff --git a/console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/index.ts b/console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/index.ts new file mode 100644 index 0000000000..1f3c0b2be3 --- /dev/null +++ b/console/packages/starwhale-ui/src/RJSFForm/templates/FieldTemplate/index.ts @@ -0,0 +1,3 @@ +import FieldTemplate from './FieldTemplate' + +export default FieldTemplate diff --git a/console/packages/starwhale-ui/src/RJSFForm/templates/index.ts b/console/packages/starwhale-ui/src/RJSFForm/templates/index.ts index 44c7e3b83d..2f57af70bc 100644 --- a/console/packages/starwhale-ui/src/RJSFForm/templates/index.ts +++ b/console/packages/starwhale-ui/src/RJSFForm/templates/index.ts @@ -10,8 +10,7 @@ // RemoveButton, // } from "./IconButton"; // import FieldErrorTemplate from "./FieldErrorTemplate"; -// import FieldTemplate from "./FieldTemplate"; -// import ObjectFieldTemplate from "./ObjectFieldTemplate"; +import FieldTemplate from './FieldTemplate' // import SubmitButton from "./SubmitButton"; // import TitleField from "./TitleField"; // import WrapIfAdditionalTemplate from "./WrapIfAdditionalTemplate"; @@ -31,8 +30,8 @@ const Index: Partial = { // DescriptionFieldTemplate: DescriptionField, // ErrorListTemplate: ErrorList, // FieldErrorTemplate, - // FieldTemplate, - // ObjectFieldTemplate: + FieldTemplate, + // ObjectFieldTemplate, // ObjectFieldTemplate as TemplatesType["ObjectFieldTemplate"], // TitleFieldTemplate: TitleField as TemplatesType["TitleFieldTemplate"], // WrapIfAdditionalTemplate, diff --git a/console/src/domain/job/components/FormFieldModel.tsx b/console/src/domain/job/components/FormFieldModel.tsx index 6d05f64638..bf97cfbe33 100644 --- a/console/src/domain/job/components/FormFieldModel.tsx +++ b/console/src/domain/job/components/FormFieldModel.tsx @@ -11,11 +11,11 @@ import { createUseStyles } from 'react-jss' import yaml from 'js-yaml' import { toaster } from 'baseui/toast' import { IStepSpec } from '@/api' -import { WidgetForm } from '@starwhale/core/form' import { convertToRJSF } from '../utils' import { Button } from '@starwhale/ui' import { getReadableStorageQuantityStr } from '@/utils' import { useSelections, useSetState } from 'ahooks' +import ArgumentForm from '@starwhale/ui/RJSFForm/ArgumentForm' const useStyles = createUseStyles({ modelField: { @@ -25,13 +25,19 @@ const useStyles = createUseStyles({ gridTemplateRows: 'minmax(0px, max-content)', }, rjsfForm: { - '& .control-label': { - flexBasis: '170px !important', - width: '170px !important', - display: '-webkit-box', - WebkitLineClamp: 2, - WebkitBoxOrient: 'vertical', - overflow: 'hidden', + // '& .control-label': { + // flexBasis: '170px !important', + // width: '170px !important', + // display: '-webkit-box', + // WebkitLineClamp: 2, + // WebkitBoxOrient: 'vertical', + // overflow: 'hidden', + // }, + '& .rjsf fieldset .form-group > div': { + width: '400px !important', + }, + '& .rjsf .array-item .field > p': { + display: 'none', }, }, }) @@ -248,7 +254,7 @@ function FormFieldModel({
{t('Parameters')}
- 0) { @@ -68,6 +68,11 @@ function convertToRJSF(sourceJson) { if (typeof field.type.name === 'object' && field.type.name !== null) { traverse(field.type.name, currentKey, field.multiple) } + + uiSchema[currentKey] = { + 'ui:help': field.help, + // 'ui:field': FieldTemplate, + } } }) }