mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
feat: 添加 Combo InputTable ui 组件 (#5690)
* feat: 添加 Combo InputTable ui 组件 * feat: 添加 Combo InputTable ui 组件 * 改成用 field.id * 顺便把版本设置改成一个不处理也不报错的写法
This commit is contained in:
parent
4f45647423
commit
88ff7c8912
@ -166,10 +166,8 @@ function getPlugins(format = 'esm') {
|
||||
main: true
|
||||
}),
|
||||
replace({
|
||||
'preventAssignment': true,
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
'__buildDate__': () => JSON.stringify(new Date()),
|
||||
'__buildVersion': JSON.stringify(version)
|
||||
preventAssignment: true,
|
||||
__buildVersion: version
|
||||
}),
|
||||
typescript(typeScriptOptions),
|
||||
commonjs({
|
||||
|
@ -93,7 +93,7 @@ import type {FormHorizontal} from './renderers/Form';
|
||||
import {enableDebug, promisify, replaceText, wrapFetcher} from './utils/index';
|
||||
|
||||
// @ts-ignore
|
||||
export const version = __buildVersion;
|
||||
export const version = '__buildVersion';
|
||||
|
||||
export {
|
||||
clearStoresCache,
|
||||
|
@ -9,6 +9,10 @@
|
||||
white-space: nowrap;
|
||||
min-width: auto;
|
||||
|
||||
> svg.icon {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&--primary {
|
||||
@include button-variant(
|
||||
var(--button-primary-default-bg-color),
|
||||
|
@ -1042,7 +1042,7 @@
|
||||
justify-content: space-between;
|
||||
|
||||
.#{$ns}Button {
|
||||
border: none;
|
||||
border-color: transparent;
|
||||
color: var(--Button--link-color);
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ export class ArrayInput extends React.Component<ArrayInputProps> {
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon icon="plus" className="icon" />
|
||||
<span>{__('Combo.add')}</span>
|
||||
<span>{__('add')}</span>
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
|
276
packages/amis-ui/src/components/Combo.tsx
Normal file
276
packages/amis-ui/src/components/Combo.tsx
Normal file
@ -0,0 +1,276 @@
|
||||
/**
|
||||
* 纯组件版本的 combo 实现
|
||||
*
|
||||
* 只能用在 <Form> 里面。
|
||||
*
|
||||
* 示例:
|
||||
*
|
||||
* <Form
|
||||
* defaultValues={{a: 1, b: 2, arr: [{a: 1, b: 2}]}}
|
||||
* onSubmit={values => console.log(values)}
|
||||
* >
|
||||
* {({control, getValues, setValue, handleSubmit}) => (
|
||||
* <>
|
||||
* <Combo
|
||||
* name="arr"
|
||||
* control={control}
|
||||
* label="Combo"
|
||||
* mode="horizontal"
|
||||
* itemRender={({
|
||||
* control,
|
||||
* getValues,
|
||||
* setValue,
|
||||
* handleSubmit
|
||||
* }) => (
|
||||
* <>
|
||||
* <Controller
|
||||
* name="a"
|
||||
* label="A"
|
||||
* mode="horizontal"
|
||||
* horizontal={{justify: true}}
|
||||
* control={control}
|
||||
* rules={{maxLength: 20}}
|
||||
* render={({field, fieldState}) => (
|
||||
* <InputBox
|
||||
* {...field}
|
||||
* hasError={fieldState.error}
|
||||
* disabled={false}
|
||||
* />
|
||||
* )}
|
||||
* />
|
||||
*
|
||||
* <Controller
|
||||
* name="b"
|
||||
* control={control}
|
||||
* rules={{maxLength: 20}}
|
||||
* render={({field, fieldState}) => (
|
||||
* <InputBox
|
||||
* {...field}
|
||||
* hasError={fieldState.error}
|
||||
* disabled={false}
|
||||
* />
|
||||
* )}
|
||||
* />
|
||||
* </>
|
||||
* )}
|
||||
* />
|
||||
* </>
|
||||
* )}
|
||||
* </Form>
|
||||
*/
|
||||
|
||||
import {
|
||||
ClassNamesFn,
|
||||
localeable,
|
||||
LocaleProps,
|
||||
themeable,
|
||||
ThemeProps,
|
||||
TranslateFn
|
||||
} from 'amis-core';
|
||||
import React from 'react';
|
||||
import {
|
||||
Control,
|
||||
RegisterOptions,
|
||||
useFieldArray,
|
||||
UseFieldArrayProps,
|
||||
UseFormReturn
|
||||
} from 'react-hook-form';
|
||||
import useSubForm from '../hooks/use-sub-form';
|
||||
import Button from './Button';
|
||||
import FormField, {FormFieldProps} from './FormField';
|
||||
import {Icon} from './icons';
|
||||
|
||||
export interface ComboProps<T = any>
|
||||
extends ThemeProps,
|
||||
LocaleProps,
|
||||
Omit<FormFieldProps, 'children' | 'errors' | 'hasError' | 'className'>,
|
||||
UseFieldArrayProps {
|
||||
itemRender: (methods: UseFormReturn, index: number) => JSX.Element | null;
|
||||
control: Control<any>;
|
||||
fieldClassName?: string;
|
||||
|
||||
rules?: Omit<
|
||||
RegisterOptions,
|
||||
'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'
|
||||
> & {
|
||||
[propName: string]: any;
|
||||
};
|
||||
|
||||
/**
|
||||
* 要不要包裹 label 之类的
|
||||
*/
|
||||
wrap?: boolean;
|
||||
|
||||
/**
|
||||
* 是否多行模式
|
||||
*/
|
||||
multiLine?: boolean;
|
||||
|
||||
itemsWrapperClassName?: string;
|
||||
itemClassName?: string;
|
||||
|
||||
scaffold?: Record<string, any>;
|
||||
addButtonClassName?: string;
|
||||
addButtonText?: string;
|
||||
addable?: boolean;
|
||||
// draggable?: boolean;
|
||||
// draggableTip?: string;
|
||||
maxLength?: number;
|
||||
minLength?: number;
|
||||
removable?: boolean;
|
||||
}
|
||||
|
||||
export function Combo({
|
||||
control,
|
||||
name,
|
||||
wrap,
|
||||
mode,
|
||||
label,
|
||||
labelAlign,
|
||||
labelClassName,
|
||||
description,
|
||||
fieldClassName,
|
||||
className,
|
||||
multiLine,
|
||||
itemsWrapperClassName,
|
||||
itemClassName,
|
||||
addButtonClassName,
|
||||
itemRender,
|
||||
translate: __,
|
||||
classnames: cx,
|
||||
addable,
|
||||
scaffold,
|
||||
addButtonText,
|
||||
removable,
|
||||
rules,
|
||||
isRequired,
|
||||
minLength,
|
||||
maxLength
|
||||
}: ComboProps) {
|
||||
// 看文档是支持的,但是传入报错,后面看看
|
||||
// let rules2: any = {...rules};
|
||||
|
||||
// if (isRequired) {
|
||||
// rules2.required = true;
|
||||
// }
|
||||
const {fields, append, update, remove} = useFieldArray({
|
||||
control,
|
||||
name: name,
|
||||
shouldUnregister: true
|
||||
// rules: rules2
|
||||
});
|
||||
|
||||
function renderBody() {
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
`Combo Combo--multi`,
|
||||
className,
|
||||
multiLine ? `Combo--ver` : `Combo--hor`
|
||||
)}
|
||||
>
|
||||
<div className={cx(`Combo-items`, itemsWrapperClassName)}>
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className={cx(`Combo-item`, itemClassName)}>
|
||||
<ComboItem
|
||||
control={control}
|
||||
update={update}
|
||||
index={index}
|
||||
value={field}
|
||||
itemRender={itemRender}
|
||||
translate={__}
|
||||
classnames={cx}
|
||||
/>
|
||||
<a
|
||||
onClick={() => remove(index)}
|
||||
key="delete"
|
||||
className={cx(
|
||||
`Combo-delBtn ${
|
||||
removable === false ||
|
||||
(minLength && fields.length <= minLength)
|
||||
? 'is-disabled'
|
||||
: ''
|
||||
}`
|
||||
)}
|
||||
data-tooltip={__('delete')}
|
||||
data-position="bottom"
|
||||
>
|
||||
<Icon icon="status-close" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{addable !== false && (!maxLength || fields.length < maxLength) ? (
|
||||
<div className={cx(`Combo-toolbar`)}>
|
||||
<Button
|
||||
className={cx(`Combo-addBtn`, addButtonClassName)}
|
||||
onClick={() => append({...scaffold})}
|
||||
>
|
||||
<Icon icon="plus" className="icon" />
|
||||
<span>{__(addButtonText || 'add')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return wrap === false ? (
|
||||
renderBody()
|
||||
) : (
|
||||
<FormField
|
||||
className={fieldClassName}
|
||||
label={label}
|
||||
labelAlign={labelAlign}
|
||||
labelClassName={labelClassName}
|
||||
description={description}
|
||||
mode={mode}
|
||||
isRequired={isRequired}
|
||||
hasError={false /*目前看来不支持,后续研究一下 */}
|
||||
errors={undefined /*目前看来不支持,后续研究一下 */}
|
||||
>
|
||||
{renderBody()}
|
||||
</FormField>
|
||||
);
|
||||
}
|
||||
|
||||
export interface ComboItemProps {
|
||||
value: any;
|
||||
control: Control<any>;
|
||||
itemRender: (methods: UseFormReturn, index: number) => JSX.Element | null;
|
||||
update: (index: number, data: Record<string, any>) => void;
|
||||
index: number;
|
||||
translate: TranslateFn;
|
||||
classnames: ClassNamesFn;
|
||||
}
|
||||
|
||||
export function ComboItem({
|
||||
value,
|
||||
itemRender,
|
||||
index,
|
||||
translate,
|
||||
update,
|
||||
classnames: cx
|
||||
}: ComboItemProps) {
|
||||
const methods = useSubForm(value, translate, data => update(index, data));
|
||||
|
||||
let child: any = itemRender(methods, index);
|
||||
if (child?.type === React.Fragment) {
|
||||
child = child.props.children;
|
||||
}
|
||||
if (Array.isArray(child)) {
|
||||
child = (
|
||||
<div className={cx('Form-row')}>
|
||||
{child.map((child, index) => (
|
||||
<div className={cx('Form-col')} key={child.key || index}>
|
||||
{child}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className={cx('Combo-itemInner')}>{child}</div>;
|
||||
}
|
||||
|
||||
export default themeable(localeable(Combo));
|
@ -19,10 +19,11 @@ export interface FormProps extends ThemeProps, LocaleProps {
|
||||
onSubmit: (value: any) => void;
|
||||
forwardRef?: FormRef;
|
||||
children?: (methods: UseFormReturn) => JSX.Element | null;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Form(props: FormProps) {
|
||||
const {classnames: cx} = props;
|
||||
const {classnames: cx, className} = props;
|
||||
const methods = useForm({
|
||||
defaultValues: props.defaultValues,
|
||||
resolver: useValidationResolver(props.translate)
|
||||
@ -51,7 +52,7 @@ export function Form(props: FormProps) {
|
||||
|
||||
return (
|
||||
<form
|
||||
className={cx('Form')}
|
||||
className={cx('Form', className)}
|
||||
onSubmit={methods.handleSubmit(props.onSubmit)}
|
||||
noValidate
|
||||
>
|
||||
|
@ -18,7 +18,9 @@ export interface FormFieldProps extends LocaleProps, ThemeProps {
|
||||
leftFixed?: boolean | number | 'xs' | 'sm' | 'md' | 'lg';
|
||||
justify?: boolean; // 两端对齐
|
||||
};
|
||||
label?: string;
|
||||
label?: string | boolean;
|
||||
labelAlign?: 'left' | 'right';
|
||||
labelClassName?: string;
|
||||
description?: string;
|
||||
isRequired?: boolean;
|
||||
hasError?: boolean;
|
||||
@ -35,6 +37,8 @@ function FormField(props: FormFieldProps) {
|
||||
hasError,
|
||||
isRequired,
|
||||
label,
|
||||
labelAlign,
|
||||
labelClassName,
|
||||
description
|
||||
} = props;
|
||||
|
||||
@ -45,6 +49,68 @@ function FormField(props: FormFieldProps) {
|
||||
: [];
|
||||
|
||||
if (mode === 'horizontal') {
|
||||
const horizontal = props.horizontal || {
|
||||
leftFixed: true
|
||||
};
|
||||
return (
|
||||
<div
|
||||
data-role="form-item"
|
||||
className={cx(`Form-item Form-item--horizontal`, className, {
|
||||
'is-error': hasError,
|
||||
[`is-required`]: isRequired,
|
||||
'Form-item--horizontal-justify': horizontal.justify
|
||||
})}
|
||||
>
|
||||
{label !== false ? (
|
||||
<label
|
||||
className={cx(
|
||||
`Form-label`,
|
||||
{
|
||||
[`Form-itemColumn--${
|
||||
typeof horizontal.leftFixed === 'string'
|
||||
? horizontal.leftFixed
|
||||
: 'normal'
|
||||
}`]: horizontal.leftFixed,
|
||||
[`Form-itemColumn--${horizontal.left}`]: !horizontal.leftFixed,
|
||||
'Form-label--left': labelAlign === 'left'
|
||||
},
|
||||
labelClassName
|
||||
)}
|
||||
>
|
||||
<span>
|
||||
{label}
|
||||
{isRequired && label ? (
|
||||
<span className={cx(`Form-star`)}>*</span>
|
||||
) : null}
|
||||
</span>
|
||||
</label>
|
||||
) : null}
|
||||
|
||||
<div
|
||||
className={cx(`Form-value`, {
|
||||
// [`Form-itemColumn--offset${getWidthRate(horizontal.offset)}`]: !label && label !== false,
|
||||
[`Form-itemColumn--${horizontal.right}`]:
|
||||
!horizontal.leftFixed &&
|
||||
!!horizontal.right &&
|
||||
horizontal.right !== 12 - horizontal.left!
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
|
||||
{hasError && errors.length ? (
|
||||
<ul className={cx(`Form-feedback`)}>
|
||||
{errors.map((msg: string, key: number) => (
|
||||
<li key={key}>{msg}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
|
||||
{description ? (
|
||||
<div className={cx(`Form-description`)}>{description}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
@ -56,7 +122,7 @@ function FormField(props: FormFieldProps) {
|
||||
})}
|
||||
>
|
||||
{label ? (
|
||||
<label className={cx(`Form-label`)}>
|
||||
<label className={cx(`Form-label`, labelClassName)}>
|
||||
<span>
|
||||
{label}
|
||||
{isRequired && label ? (
|
||||
@ -96,9 +162,14 @@ export interface ControllerProps
|
||||
> & {
|
||||
[propName: string]: any;
|
||||
};
|
||||
|
||||
/**
|
||||
* 配置成 false 则不包裹
|
||||
*/
|
||||
wrap?: boolean;
|
||||
}
|
||||
export function Controller(props: ControllerProps) {
|
||||
const {render, name, shouldUnregister, defaultValue, control, ...rest} =
|
||||
const {render, name, shouldUnregister, defaultValue, control, wrap, ...rest} =
|
||||
props;
|
||||
let rules = {...props.rules};
|
||||
|
||||
@ -108,20 +179,24 @@ export function Controller(props: ControllerProps) {
|
||||
|
||||
return (
|
||||
<ReactHookFormController
|
||||
name={name}
|
||||
name={name || ''}
|
||||
rules={rules}
|
||||
shouldUnregister={shouldUnregister}
|
||||
defaultValue={defaultValue}
|
||||
control={control}
|
||||
render={methods => (
|
||||
<ThemedFormField
|
||||
{...rest}
|
||||
hasError={!!methods.fieldState.error}
|
||||
errors={methods.fieldState.error?.message}
|
||||
>
|
||||
{render(methods)}
|
||||
</ThemedFormField>
|
||||
)}
|
||||
render={methods =>
|
||||
wrap === false ? (
|
||||
render(methods)
|
||||
) : (
|
||||
<ThemedFormField
|
||||
{...rest}
|
||||
hasError={!!methods.fieldState.error}
|
||||
errors={methods.fieldState.error?.message}
|
||||
>
|
||||
{render(methods)}
|
||||
</ThemedFormField>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
215
packages/amis-ui/src/components/InputTable.tsx
Normal file
215
packages/amis-ui/src/components/InputTable.tsx
Normal file
@ -0,0 +1,215 @@
|
||||
import {
|
||||
ClassNamesFn,
|
||||
localeable,
|
||||
LocaleProps,
|
||||
themeable,
|
||||
ThemeProps,
|
||||
TranslateFn
|
||||
} from 'amis-core';
|
||||
import React from 'react';
|
||||
import {
|
||||
Control,
|
||||
useFieldArray,
|
||||
UseFieldArrayProps,
|
||||
UseFormReturn
|
||||
} from 'react-hook-form';
|
||||
import useSubForm from '../hooks/use-sub-form';
|
||||
import Button from './Button';
|
||||
import FormField, {FormFieldProps} from './FormField';
|
||||
import {Icon} from './icons';
|
||||
|
||||
export interface InputTabbleProps<T = any>
|
||||
extends ThemeProps,
|
||||
LocaleProps,
|
||||
Omit<
|
||||
FormFieldProps,
|
||||
'children' | 'errors' | 'hasError' | 'isRequired' | 'className'
|
||||
>,
|
||||
UseFieldArrayProps {
|
||||
control: Control<any>;
|
||||
fieldClassName?: string;
|
||||
|
||||
columns: Array<{
|
||||
title?: string;
|
||||
className?: string;
|
||||
thRender?: () => JSX.Element;
|
||||
tdRender: (methods: UseFormReturn, index: number) => JSX.Element | null;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* 要不要包裹 label 之类的
|
||||
*/
|
||||
wrap?: boolean;
|
||||
scaffold?: any;
|
||||
|
||||
addable?: boolean;
|
||||
addButtonClassName?: string;
|
||||
addButtonText?: string;
|
||||
|
||||
maxLength?: number;
|
||||
minLength?: number;
|
||||
removable?: boolean;
|
||||
}
|
||||
|
||||
export function InputTable({
|
||||
control,
|
||||
name,
|
||||
wrap,
|
||||
mode,
|
||||
label,
|
||||
labelAlign,
|
||||
labelClassName,
|
||||
description,
|
||||
fieldClassName,
|
||||
className,
|
||||
translate: __,
|
||||
classnames: cx,
|
||||
removable,
|
||||
columns,
|
||||
addable,
|
||||
addButtonText,
|
||||
addButtonClassName,
|
||||
scaffold,
|
||||
minLength,
|
||||
maxLength
|
||||
}: InputTabbleProps) {
|
||||
const {fields, append, update, remove} = useFieldArray({
|
||||
control,
|
||||
name: name
|
||||
});
|
||||
|
||||
if (!Array.isArray(columns)) {
|
||||
columns = [];
|
||||
}
|
||||
|
||||
function renderBody() {
|
||||
return (
|
||||
<div className={cx(`Table`, className)}>
|
||||
<div className={cx(`Table-contentWrap`)}>
|
||||
<table className={cx(`Table-table`)}>
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map((item, index) => (
|
||||
<th key={index} className={item.className}>
|
||||
{item.thRender ? item.thRender() : item.title}
|
||||
</th>
|
||||
))}
|
||||
<th key="operation">{__('Table.operation')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fields.length ? (
|
||||
fields.map((field, index) => (
|
||||
<tr key={field.id}>
|
||||
<InputTableRow
|
||||
key="columns"
|
||||
control={control}
|
||||
update={update}
|
||||
index={index}
|
||||
value={field}
|
||||
columns={columns}
|
||||
translate={__}
|
||||
classnames={cx}
|
||||
/>
|
||||
<td key="operation">
|
||||
<Button
|
||||
level="link"
|
||||
key="delete"
|
||||
className={cx(
|
||||
`Table-delBtn ${
|
||||
removable === false ||
|
||||
(minLength && fields.length <= minLength)
|
||||
? 'is-disabled'
|
||||
: ''
|
||||
}`
|
||||
)}
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
{__('delete')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={columns.length + 1}>
|
||||
<Icon
|
||||
icon="desk-empty"
|
||||
className={cx('Table-placeholder-empty-icon', 'icon')}
|
||||
/>
|
||||
{__('placeholder.noData')}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{addable !== false && (!maxLength || fields.length < maxLength) ? (
|
||||
<div className={cx(`InputTable-toolbar`)}>
|
||||
<Button
|
||||
className={cx(addButtonClassName)}
|
||||
onClick={() => append({...scaffold})}
|
||||
size="sm"
|
||||
>
|
||||
<Icon icon="plus" className="icon" />
|
||||
<span>{__(addButtonText || 'add')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return wrap === false ? (
|
||||
renderBody()
|
||||
) : (
|
||||
<FormField
|
||||
className={fieldClassName}
|
||||
label={label}
|
||||
labelAlign={labelAlign}
|
||||
labelClassName={labelClassName}
|
||||
description={description}
|
||||
mode={mode}
|
||||
hasError={false /*目前看来不支持,后续研究一下 */}
|
||||
errors={undefined /*目前看来不支持,后续研究一下 */}
|
||||
>
|
||||
{renderBody()}
|
||||
</FormField>
|
||||
);
|
||||
}
|
||||
|
||||
export interface InputTableRowProps {
|
||||
value: any;
|
||||
control: Control<any>;
|
||||
columns: Array<{
|
||||
tdRender: (methods: UseFormReturn, index: number) => JSX.Element | null;
|
||||
className?: string;
|
||||
}>;
|
||||
update: (index: number, data: Record<string, any>) => void;
|
||||
index: number;
|
||||
translate: TranslateFn;
|
||||
classnames: ClassNamesFn;
|
||||
}
|
||||
|
||||
export function InputTableRow({
|
||||
value,
|
||||
columns,
|
||||
index,
|
||||
translate,
|
||||
update,
|
||||
classnames: cx
|
||||
}: InputTableRowProps) {
|
||||
const methods = useSubForm(value, translate, data => update(index, data));
|
||||
|
||||
return (
|
||||
<>
|
||||
{columns.map((item, index) => (
|
||||
<td key={index} className={item.className}>
|
||||
{item.tdRender(methods, index)}
|
||||
</td>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default themeable(localeable(InputTable));
|
@ -113,6 +113,10 @@ import Timeline from './Timeline';
|
||||
import ImageGallery from './ImageGallery';
|
||||
import BaiduMapPicker from './BaiduMapPicker';
|
||||
import MultilineText from './MultilineText';
|
||||
import Form from './Form';
|
||||
import FormField, {Controller} from './FormField';
|
||||
import Combo from './Combo';
|
||||
import InputTable from './InputTable';
|
||||
|
||||
export {
|
||||
NotFound,
|
||||
@ -227,5 +231,10 @@ export {
|
||||
Timeline,
|
||||
ImageGallery,
|
||||
BaiduMapPicker,
|
||||
MultilineText
|
||||
MultilineText,
|
||||
Form,
|
||||
FormField,
|
||||
Controller,
|
||||
Combo,
|
||||
InputTable
|
||||
};
|
||||
|
45
packages/amis-ui/src/hooks/use-sub-form.ts
Normal file
45
packages/amis-ui/src/hooks/use-sub-form.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import {useCallback, useState} from 'react';
|
||||
import isFunction from 'lodash/isFunction';
|
||||
import {TranslateFn} from 'amis-core';
|
||||
import {useForm, UseFormReturn} from 'react-hook-form';
|
||||
import debounce from 'lodash/debounce';
|
||||
import React from 'react';
|
||||
import useValidationResolver from './use-validation-resolver';
|
||||
|
||||
const useSubForm = (
|
||||
defaultValue: any,
|
||||
translate: TranslateFn,
|
||||
onUpdate: (data: any) => void
|
||||
): UseFormReturn => {
|
||||
const methods = useForm({
|
||||
defaultValues: defaultValue,
|
||||
mode: 'onChange', // 每次修改都验证
|
||||
shouldUnregister: true,
|
||||
resolver: useValidationResolver(translate)
|
||||
});
|
||||
|
||||
// 数据修改后,自动提交更新到上层
|
||||
const lazySubmit = React.useRef(
|
||||
debounce(methods.handleSubmit(onUpdate), 250, {
|
||||
leading: false,
|
||||
trailing: true
|
||||
})
|
||||
);
|
||||
|
||||
// 销毁的时候要 cancel
|
||||
React.useEffect(() => {
|
||||
return () => lazySubmit.current.cancel();
|
||||
}, []);
|
||||
|
||||
// 监控数值变化,自动同步到上层
|
||||
React.useEffect(() => {
|
||||
const unsubscribe = methods.watch(() => {
|
||||
lazySubmit.current();
|
||||
});
|
||||
return () => unsubscribe.unsubscribe();
|
||||
}, [methods.watch]);
|
||||
|
||||
return methods;
|
||||
};
|
||||
|
||||
export default useSubForm;
|
@ -1339,7 +1339,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
{
|
||||
type: 'dropdown-button',
|
||||
icon: addIcon ? <Icon icon="plus" className="icon" /> : '',
|
||||
label: __(addButtonText || 'Combo.add'),
|
||||
label: __(addButtonText || 'add'),
|
||||
level: 'info',
|
||||
size: 'sm',
|
||||
closeOnClick: true
|
||||
@ -1357,7 +1357,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
) : tabsMode ? (
|
||||
<a onClick={this.addItem}>
|
||||
{addIcon ? <Icon icon="plus" className="icon" /> : null}
|
||||
<span>{__(addButtonText || 'Combo.add')}</span>
|
||||
<span>{__(addButtonText || 'add')}</span>
|
||||
</a>
|
||||
) : isObject(addBtn) ? (
|
||||
render('add-button', {
|
||||
@ -1371,7 +1371,7 @@ export default class ComboControl extends React.Component<ComboProps> {
|
||||
onClick={this.addItem}
|
||||
>
|
||||
{addIcon ? <Icon icon="plus" className="icon" /> : null}
|
||||
<span>{__(addButtonText || 'Combo.add')}</span>
|
||||
<span>{__(addButtonText || 'add')}</span>
|
||||
</Button>
|
||||
))}
|
||||
</>
|
||||
|
@ -30,9 +30,9 @@
|
||||
"types": ["typePatches"],
|
||||
"references": [],
|
||||
"include": [
|
||||
"**.ts",
|
||||
"**.tsx",
|
||||
"**.jsx",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.jsx",
|
||||
"scripts/fis3plugin.ts",
|
||||
"scripts/markdownPlugin.ts",
|
||||
"scripts/mockApiPlugin.ts",
|
||||
|
@ -32,12 +32,7 @@ export default defineConfig({
|
||||
dimensions: false
|
||||
}
|
||||
}),
|
||||
monacoEditorPlugin({}),
|
||||
replace({
|
||||
preventAssignment: true,
|
||||
__buildDate__: () => JSON.stringify(new Date()),
|
||||
__buildVersion: JSON.stringify('dev')
|
||||
})
|
||||
monacoEditorPlugin({})
|
||||
],
|
||||
optimizeDeps: {
|
||||
include: ['amis-formula/lib/doc'],
|
||||
|
Loading…
Reference in New Issue
Block a user