fix(client/formula): set cursor focus on input (#959)

* fix(client/formula): set cursor focus on input

* fix(client/formula): when formula field name has contains the other one will case FormulaError

refactor partial implementation
This commit is contained in:
lyf-coder 2022-10-31 11:12:19 +08:00 committed by chenos
parent 7cb5ff554e
commit 9a81b1b8ee

View File

@ -1,7 +1,7 @@
import { Field, onFormSubmitValidateStart } from '@formily/core';
import { connect, mapProps, mapReadPretty, useField, useFieldSchema, useFormEffects } from '@formily/react';
import { Button, Input, Popover, Tag, Menu, Dropdown } from 'antd';
import React, { useContext, useEffect, useRef, useState } from 'react';
import { connect, mapProps, useField, useFormEffects } from '@formily/react';
import { Menu, Dropdown } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
import ContentEditable from 'react-contenteditable';
import { useTranslation } from 'react-i18next';
import * as math from 'mathjs';
@ -14,62 +14,65 @@ const AntdFormulaInput = (props) => {
const inputRef = useRef();
const [dropdownVisible, setDropdownVisible] = useState(false);
const [formula, setFormula] = useState(null);
const [html, setHtml] = useState(null);
const numColumns = new Map<string, string>();
const scope = {};
fields.filter(field => supports.includes(field.interface)).forEach(field => {
fields
.filter((field) => supports.includes(field.interface))
.forEach((field) => {
numColumns.set(field.name, field.uiSchema.title);
scope[field.name] = 1;
})
});
const keys = Array.from(numColumns.keys());
let initHtml;
if (value) {
initHtml = value;
numColumns.forEach((value, key) => {
initHtml = initHtml.replaceAll(key, `<span contentEditable="false" style="border: 1px solid #aaa; padding: 2px 5px;">${value}</span>`)
})
}
useEffect(() => {
if (onChange && formula) {
let v = formula || '';
if (value) {
let newHtml = value;
numColumns.forEach((value, key) => {
v = v.replaceAll(value, key);
})
if (v != value) {
onChange(v);
newHtml = newHtml.replaceAll(
key,
`<span contentEditable="false" ><input disabled="disabled" style="width:${
18 * value.length
}px;max-width: 120px" value="${value}"/><span hidden>${key}</span></span>`,
);
});
newHtml = `${newHtml}<span style="padding-left: 5px"></span>`; // set extra span for cursor focus on last position
setHtml(newHtml);
} else {
setHtml('');
}
}
}, [formula])
}, [value]);
const menu = (
<Menu onClick={async (args) => {
const replaceFormula = formula.replace('@', numColumns.get(args.key));
const replaceHtml = html.replace('@', `<span contentEditable="false" style="border: 1px solid #aaa; padding: 2px 5px;">${numColumns.get(args.key)}</span>`);
setFormula(replaceFormula);
setHtml(replaceHtml);
setDropdownVisible(false);
}}>
{
keys.map(key => (<Menu.Item key={key}>{numColumns.get(key)}</Menu.Item>))
<Menu
onClick={async (args) => {
const replaceFormula = field.value.replace('@', args.key);
if (onChange && replaceFormula != field.value) {
onChange(replaceFormula);
}
setDropdownVisible(false);
(inputRef.current as any).focus();
}}
>
{keys.map((key) => (
<Menu.Item key={key}>{numColumns.get(key)}</Menu.Item>
))}
</Menu>
);
const handleChange = (e) => {
const current = inputRef.current as any;
setFormula(e.currentTarget.textContent);
setHtml(current.innerHTML);
if (e.currentTarget.textContent == '' && onChange) {
if (onChange) {
if (e.currentTarget.textContent == '') {
onChange(null);
} else {
onChange(e.currentTarget.textContent);
}
}
};
const handleKeyDown = (e) => {
const {key} = e;
const { key } = e;
switch (key) {
case 'Enter':
e.preventDefault();
@ -82,22 +85,23 @@ const AntdFormulaInput = (props) => {
setDropdownVisible(false);
break;
}
}
};
useFormEffects(() => {
onFormSubmitValidateStart(() => {
try {
math.evaluate(field.value, scope);
field.feedbacks = [];
} catch {
} catch (e) {
console.error(field.value, scope, (e as Error).message);
field.setFeedback({
type: 'error',
code: 'FormulaError',
messages: [t('Formula error.')],
});
}
})
})
});
});
return (
<Dropdown overlay={menu} visible={dropdownVisible}>
@ -106,16 +110,12 @@ const AntdFormulaInput = (props) => {
className="ant-input"
onChange={handleChange}
onKeyDown={handleKeyDown}
html={html || initHtml || ''}
html={html || ''}
/>
</Dropdown>
)
}
);
};
export const FormulaInput = connect(
AntdFormulaInput,
mapProps({
}),
);
export const FormulaInput = connect(AntdFormulaInput, mapProps({}));
export default FormulaInput;