mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-27 08:39:59 +08:00
support expression editor in object values
This commit is contained in:
parent
8a375861e6
commit
46fa447432
@ -3,10 +3,9 @@ import * as acorn from 'acorn';
|
||||
import * as acornLoose from 'acorn-loose';
|
||||
import { simple as simpleWalk } from 'acorn-walk';
|
||||
import { flattenDeep, isArray, isObject } from 'lodash-es';
|
||||
import { isExpression } from '../validator/utils';
|
||||
import { ComponentId, IFieldModel, ModuleId } from './IAppModel';
|
||||
|
||||
const regExp = /.*{{.*}}.*/;
|
||||
|
||||
export class FieldModel implements IFieldModel {
|
||||
isDynamic = false;
|
||||
refs: Record<ComponentId | ModuleId, string[]> = {};
|
||||
@ -62,13 +61,13 @@ export class FieldModel implements IFieldModel {
|
||||
(isArrayValue
|
||||
? []
|
||||
: shouldExtendValues && isOldValueObject
|
||||
? this.value
|
||||
: {}) as Record<string, IFieldModel>
|
||||
? this.value
|
||||
: {}) as Record<string, IFieldModel>
|
||||
);
|
||||
} else {
|
||||
this.value = value;
|
||||
}
|
||||
this.isDynamic = typeof value === 'string' && regExp.test(value);
|
||||
this.isDynamic = isExpression(value);
|
||||
this.parseReferences();
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
} from '@chakra-ui/react';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { AnyKind, UnknownKind } from '@sinclair/typebox';
|
||||
import { isExpression as _isExpression } from '../../../validator/utils';
|
||||
import { FieldProps, getCodeMode, getDisplayLabel } from './fields';
|
||||
import { widgets } from './widgets/widgets';
|
||||
import StringField from './StringField';
|
||||
@ -89,7 +90,7 @@ const SchemaField: React.FC<Props> = props => {
|
||||
const { schema, label, formData, onChange, registry, stateManager } = props;
|
||||
const [isExpression, setIsExpression] = useState(
|
||||
// FIXME: regexp copied from FieldModel.ts, is this a stable way to check expression?
|
||||
() => typeof formData === 'string' && /.*{{.*}}.*/.test(formData)
|
||||
() => _isExpression(formData)
|
||||
);
|
||||
|
||||
if (isEmpty(schema)) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
toNumber,
|
||||
isString,
|
||||
@ -11,6 +11,7 @@ import {
|
||||
} from 'lodash-es';
|
||||
import { FieldProps } from '../fields';
|
||||
import { ExpressionEditor } from '../../../CodeEditor';
|
||||
import { isExpression } from '../../../../validator/utils';
|
||||
|
||||
type Props = FieldProps;
|
||||
|
||||
@ -89,27 +90,52 @@ const customTreeTypeDefCreator = (dataTree: Record<string, Record<string, unknow
|
||||
return { ...def };
|
||||
};
|
||||
|
||||
const getDefaultCode = (value: unknown): { defaultCode: string; type: string } => {
|
||||
const type = typeof value;
|
||||
if (type === 'object' || type === 'boolean') {
|
||||
value = JSON.stringify(value, null, 2);
|
||||
} else {
|
||||
value = String(value);
|
||||
}
|
||||
return {
|
||||
defaultCode: value as string,
|
||||
type,
|
||||
};
|
||||
};
|
||||
|
||||
const getParsedValue = (raw: string, type: string) => {
|
||||
if (isExpression(raw)) {
|
||||
return raw;
|
||||
}
|
||||
if (type === 'object' || type === 'boolean') {
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (error) {
|
||||
// TODO: handle error
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (type === 'number') {
|
||||
return toNumber(raw);
|
||||
}
|
||||
return raw;
|
||||
};
|
||||
|
||||
export const ExpressionWidget: React.FC<Props> = props => {
|
||||
const { formData, onChange, stateManager } = props;
|
||||
const [defs, setDefs] = useState<any>();
|
||||
useEffect(() => {
|
||||
setDefs([customTreeTypeDefCreator(stateManager.store)]);
|
||||
}, [stateManager]);
|
||||
const { defaultCode, type } = useMemo(() => {
|
||||
return getDefaultCode(formData);
|
||||
}, [formData]);
|
||||
|
||||
return (
|
||||
<ExpressionEditor
|
||||
// TODO: better serialization
|
||||
defaultCode={String(formData)}
|
||||
defaultCode={defaultCode}
|
||||
onBlur={_v => {
|
||||
// TODO: move into expression editor?
|
||||
let v: string | number | boolean = _v;
|
||||
if (isNumeric(v)) {
|
||||
v = toNumber(v);
|
||||
} else if (v === 'true') {
|
||||
v = true;
|
||||
} else if (v === 'false') {
|
||||
v = false;
|
||||
}
|
||||
const v = getParsedValue(_v, type);
|
||||
onChange(v);
|
||||
}}
|
||||
defs={defs}
|
||||
|
@ -1,4 +1,3 @@
|
||||
export function isExpression (str: unknown) {
|
||||
const regExp = new RegExp('.*{{.*}}.*');
|
||||
return typeof str === 'string' && regExp.test(str)
|
||||
export function isExpression(str: unknown) {
|
||||
return typeof str === 'string' && /[\s\S]*{{[\s\S]*}}[\s\S]*/m.test(str);
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ export class StateManager {
|
||||
|
||||
clear = () => {
|
||||
this.store = reactive<Record<string, any>>({});
|
||||
}
|
||||
};
|
||||
|
||||
evalExp = (expChunk: ExpChunk, scopeObject = {}): unknown => {
|
||||
if (typeof expChunk === 'string') {
|
||||
@ -178,8 +178,8 @@ export const parseExpression = (exp: string, parseListItem = false): ExpChunk[]
|
||||
let item;
|
||||
|
||||
while ((item = tokens.shift())) {
|
||||
if (item == '}}') return result;
|
||||
result.push(item == '{{' ? build(tokens) : item);
|
||||
if (item === '}}') return result;
|
||||
result.push(item === '{{' ? build(tokens) : item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user