fix(variable-input): fix style (#1761)

* fix(variable-input): fix style

* refactor: avoid lint error

* feat: add invariable

* fix: fix the default value variable of the expression is empty
This commit is contained in:
被雨水过滤的空气-Rairn 2023-04-26 09:56:25 +08:00 committed by GitHub
parent 9101bbbb1b
commit 95e4e7e7b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 126 additions and 92 deletions

View File

@ -175,4 +175,5 @@ export const linkTo: IField = {
// }, // },
], ],
}, },
invariable: true,
}; };

View File

@ -282,4 +282,5 @@ export const m2m: IField = {
// }, // },
], ],
}, },
invariable: true,
}; };

View File

@ -214,4 +214,5 @@ export const m2o: IField = {
// }, // },
], ],
}, },
invariable: true,
}; };

View File

@ -273,4 +273,5 @@ export const o2m: IField = {
// }, // },
], ],
}, },
invariable: true,
}; };

View File

@ -252,6 +252,7 @@ export const o2o: IField = {
// }, // },
], ],
}, },
invariable: true,
}; };
export const oho: IField = { export const oho: IField = {
@ -422,6 +423,7 @@ export const oho: IField = {
// }, // },
], ],
}, },
invariable: true,
}; };
export const obo: IField = { export const obo: IField = {
@ -605,4 +607,5 @@ export const obo: IField = {
// }, // },
], ],
}, },
invariable: true,
}; };

View File

@ -10,5 +10,7 @@ export interface IField extends ISchema {
default?: IDefault; default?: IDefault;
operators?: any[]; operators?: any[];
filterable?: any; filterable?: any;
/** 不支持使用变量的值进行设置 */
invariable?: boolean;
[key: string]: any; [key: string]: any;
} }

View File

@ -18,9 +18,16 @@ import { SchemaComponent } from '../../core';
import { useCompile, useDesignable, useFieldComponentOptions } from '../../hooks'; import { useCompile, useDesignable, useFieldComponentOptions } from '../../hooks';
import { BlockItem } from '../block-item'; import { BlockItem } from '../block-item';
import { HTMLEncode } from '../input/shared'; import { HTMLEncode } from '../input/shared';
import { isInvariable } from '../variable';
import { FilterFormDesigner } from './FormItem.FilterFormDesigner'; import { FilterFormDesigner } from './FormItem.FilterFormDesigner';
import { useEnsureOperatorsValid } from './SchemaSettingOptions'; import { useEnsureOperatorsValid } from './SchemaSettingOptions';
const defaultInputStyle = css`
& > .nb-form-item {
flex: 1;
}
`;
const divWrap = (schema: ISchema) => { const divWrap = (schema: ISchema) => {
return { return {
type: 'void', type: 'void',
@ -420,8 +427,7 @@ FormItem.Designer = function Designer() {
type: 'object', type: 'object',
title: t('Set default value'), title: t('Set default value'),
properties: { properties: {
// 关系字段不支持设置变量 default: isInvariable(interfaceConfig)
default: collectionField?.target
? { ? {
...(fieldSchema || {}), ...(fieldSchema || {}),
'x-decorator': 'FormItem', 'x-decorator': 'FormItem',
@ -434,6 +440,12 @@ FormItem.Designer = function Designer() {
service: { service: {
resource: collectionField?.target, resource: collectionField?.target,
}, },
// for DynamicExpression
sourceCollection: form?.values.sourceCollection,
style: {
width: '100%',
verticalAlign: 'top',
},
}, },
name: 'default', name: 'default',
title: t('Default value'), title: t('Default value'),
@ -447,6 +459,7 @@ FormItem.Designer = function Designer() {
...(fieldSchema?.['x-component-props'] || {}), ...(fieldSchema?.['x-component-props'] || {}),
collectionName: collectionField?.collectionName, collectionName: collectionField?.collectionName,
schema: collectionField?.uiSchema, schema: collectionField?.uiSchema,
className: defaultInputStyle,
renderSchemaComponent: function Com(props) { renderSchemaComponent: function Com(props) {
const s = _.cloneDeep(fieldSchema) || ({} as Schema); const s = _.cloneDeep(fieldSchema) || ({} as Schema);
s.title = ''; s.title = '';
@ -464,6 +477,7 @@ FormItem.Designer = function Designer() {
defaultValue: getFieldDefaultValue(s, collectionField), defaultValue: getFieldDefaultValue(s, collectionField),
style: { style: {
width: '100%', width: '100%',
verticalAlign: 'top',
}, },
}, },
}} }}

View File

@ -6,6 +6,7 @@ import moment from 'moment';
import React from 'react'; import React from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import classNames from 'classnames';
import { useCompile } from '../..'; import { useCompile } from '../..';
import { XButton } from './XButton'; import { XButton } from './XButton';
@ -51,7 +52,7 @@ const ConstantTypes = {
boolean: { boolean: {
label: `{{t("Boolean")}}`, label: `{{t("Boolean")}}`,
value: 'boolean', value: 'boolean',
component({ onChange, value }) { component: function Com({ onChange, value }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Select <Select
@ -88,7 +89,7 @@ const ConstantTypes = {
null: { null: {
label: `{{t("Null")}}`, label: `{{t("Null")}}`,
value: 'null', value: 'null',
component() { component: function Com() {
const { t } = useTranslation(); const { t } = useTranslation();
return <AntInput readOnly placeholder={t('Null')} className="null-value" />; return <AntInput readOnly placeholder={t('Null')} className="null-value" />;
}, },
@ -115,12 +116,12 @@ export function Input(props) {
const compile = useCompile(); const compile = useCompile();
const form = useForm(); const form = useForm();
const { value = '', scope, onChange, children, button, useTypedConstant, style } = props; const { value = '', scope, onChange, children, button, useTypedConstant, style, className } = props;
const parsed = parseValue(value); const parsed = parseValue(value);
const isConstant = typeof parsed === 'string'; const isConstant = typeof parsed === 'string';
const type = isConstant ? parsed : ''; const type = isConstant ? parsed : '';
const variable = isConstant ? null : parsed; const variable = isConstant ? null : parsed;
const varialbeOptions = typeof scope === 'function' ? scope() : scope ?? []; const variableOptions = typeof scope === 'function' ? scope() : scope ?? [];
const { component: ConstantComponent, ...constantOption }: VariableOptions & { component?: React.FC<any> } = children const { component: ConstantComponent, ...constantOption }: VariableOptions & { component?: React.FC<any> } = children
? { ? {
@ -134,7 +135,7 @@ export function Input(props) {
label: '{{t("Null")}}', label: '{{t("Null")}}',
component: ConstantTypes.null.component, component: ConstantTypes.null.component,
}; };
const options: VariableOptions[] = compile([constantOption, ...varialbeOptions]); const options: VariableOptions[] = compile([constantOption, ...variableOptions]);
function onSwitch(next) { function onSwitch(next) {
if (next[0] === '') { if (next[0] === '') {
@ -166,86 +167,88 @@ export function Input(props) {
<AntInput.Group <AntInput.Group
compact compact
style={style} style={style}
className={css` className={classNames(
width: auto; className,
display: flex !important; css`
.ant-input-disabled { width: auto;
.ant-tag { display: flex !important;
color: #bfbfbf; .ant-input-disabled {
border-color: #d9d9d9; .ant-tag {
color: #bfbfbf;
border-color: #d9d9d9;
}
} }
} .ant-input.null-value {
.ant-input.null-value { width: 4em;
width: 4em; min-width: 4em;
min-width: 4em; }
} `,
`} )}
> >
<div style={{ flex: 1 }}> {variable ? (
{variable ? ( <div
className={css`
position: relative;
line-height: 0;
&:hover {
.ant-select-clear {
opacity: 0.8;
}
}
.ant-input {
overflow: auto;
white-space: nowrap;
${disabled ? '' : 'padding-right: 28px;'}
.ant-tag {
display: inline;
line-height: 19px;
margin: 0;
padding: 2px 7px;
border-radius: 10px;
}
}
`}
>
<div <div
className={css` onInput={(e) => e.preventDefault()}
position: relative; onKeyDown={(e) => {
line-height: 0; if (e.key !== 'Backspace') {
e.preventDefault();
&:hover { return;
.ant-select-clear {
opacity: 0.8;
}
} }
onChange(null);
.ant-input { }}
overflow: auto; className={cx('ant-input', { 'ant-input-disabled': disabled })}
white-space: nowrap; contentEditable={!disabled}
${disabled ? '' : 'padding-right: 28px;'} suppressContentEditableWarning
.ant-tag {
display: inline;
line-height: 19px;
margin: 0;
padding: 2px 7px;
border-radius: 10px;
}
}
`}
> >
<div <Tag contentEditable={false} color="blue">
onInput={(e) => e.preventDefault()} {variableText}
onKeyDown={(e) => { </Tag>
if (e.key !== 'Backspace') {
e.preventDefault();
return;
}
onChange(null);
}}
className={cx('ant-input', { 'ant-input-disabled': disabled })}
contentEditable={!disabled}
suppressContentEditableWarning
>
<Tag contentEditable={false} color="blue">
{variableText}
</Tag>
</div>
{!disabled ? (
<span
className={cx(
'ant-select-clear',
css`
user-select: 'none';
`,
)}
unselectable="on"
aria-hidden
onClick={() => onChange(null)}
>
<CloseCircleFilled />
</span>
) : null}
</div> </div>
) : ( {!disabled ? (
children ?? <ConstantComponent value={value} onChange={onChange} /> <span
)} className={cx(
</div> 'ant-select-clear',
css`
user-select: 'none';
`,
)}
// eslint-disable-next-line react/no-unknown-property
unselectable="on"
aria-hidden
onClick={() => onChange(null)}
>
<CloseCircleFilled />
</span>
) : null}
</div>
) : (
children ?? <ConstantComponent value={value} onChange={onChange} />
)}
{options.length > 1 ? ( {options.length > 1 ? (
<Cascader <Cascader
options={options} options={options}

View File

@ -1,8 +1,9 @@
import { connect, mapReadPretty } from '@formily/react'; import { connect, mapReadPretty } from '@formily/react';
import { IField } from '../../../collection-manager';
import { Input } from './Input'; import { Input } from './Input';
import { TextArea } from './TextArea';
import { JSONInput } from './JSONInput'; import { JSONInput } from './JSONInput';
import { TextArea } from './TextArea';
export function Variable() { export function Variable() {
return null; return null;
@ -15,3 +16,7 @@ Variable.TextArea = connect(TextArea, mapReadPretty(TextArea.ReadPretty));
Variable.JSON = connect(JSONInput); Variable.JSON = connect(JSONInput);
export default Variable; export default Variable;
export function isInvariable(value: IField) {
return !!value?.invariable;
}

View File

@ -7,14 +7,15 @@ type Props = {
onChange: (value: any) => void; onChange: (value: any) => void;
collectionName: string; collectionName: string;
renderSchemaComponent?: (props: any) => any; renderSchemaComponent?: (props: any) => any;
style: React.CSSProperties;
schema: any; schema: any;
operator: any; operator: any;
children: any; children?: any;
className?: string;
style?: React.CSSProperties;
}; };
export const VariableInput = (props: Props) => { export const VariableInput = (props: Props) => {
const { value, onChange, renderSchemaComponent: RenderSchemaComponent, style, schema } = props; const { value, onChange, renderSchemaComponent: RenderSchemaComponent, style, schema, className } = props;
const compile = useCompile(); const compile = useCompile();
const userVariable = useUserVariable({ schema, level: 1 }); const userVariable = useUserVariable({ schema, level: 1 });
const scope = useMemo(() => { const scope = useMemo(() => {
@ -37,7 +38,7 @@ export const VariableInput = (props: Props) => {
}, []); }, []);
return ( return (
<Variable.Input value={value} onChange={onChange} scope={scope} style={style}> <Variable.Input className={className} value={value} onChange={onChange} scope={scope} style={style}>
<RenderSchemaComponent value={value} onChange={onChange} /> <RenderSchemaComponent value={value} onChange={onChange} />
</Variable.Input> </Variable.Input>
); );

View File

@ -1,7 +1,7 @@
import React, { useState, useMemo } from 'react';
import { onFieldInputValueChange, onFormInitialValuesChange } from '@formily/core'; import { onFieldInputValueChange, onFormInitialValuesChange } from '@formily/core';
import { useForm, observer, connect, mapReadPretty, useFormEffects } from '@formily/react'; import { connect, mapReadPretty, observer, useForm, useFormEffects } from '@formily/react';
import { Tag } from 'antd'; import { Tag } from 'antd';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useRecord, Variable } from '@nocobase/client'; import { useRecord, Variable } from '@nocobase/client';
@ -10,7 +10,7 @@ import { NAMESPACE } from '../locale';
import { useCollectionFieldOptions } from '../variable'; import { useCollectionFieldOptions } from '../variable';
const InternalExpression = observer((props: any) => { const InternalExpression = observer((props: any) => {
const { onChange } = props; const { onChange, sourceCollection } = props;
const { values } = useForm(); const { values } = useForm();
const [collection, setCollection] = useState(values?.sourceCollection); const [collection, setCollection] = useState(values?.sourceCollection);
@ -24,17 +24,18 @@ const InternalExpression = observer((props: any) => {
}); });
}); });
const options = useCollectionFieldOptions({ collection }); const options = useCollectionFieldOptions({ collection: sourceCollection || collection });
return <Variable.TextArea {...props} scope={options} />; return <Variable.TextArea {...props} scope={options} />;
}); });
function Result(props) { function Result(props) {
const { sourceCollection } = props;
const { t } = useTranslation(); const { t } = useTranslation();
const values = useRecord(); const values = useRecord();
const options = useMemo( const options = useMemo(
() => useCollectionFieldOptions({ collection: values.sourceCollection }), () => useCollectionFieldOptions({ collection: sourceCollection || values.sourceCollection }),
[values.sourceCollection, values.sourceCollection], [values.sourceCollection, values.sourceCollection, sourceCollection],
); );
return props.value ? ( return props.value ? (
<Variable.TextArea {...props} scope={options} /> <Variable.TextArea {...props} scope={options} />

View File

@ -21,4 +21,5 @@ export default {
properties: { properties: {
...defaultProps, ...defaultProps,
}, },
invariable: true,
} as IField; } as IField;