mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 09:17:23 +00:00
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:
parent
7cb5ff554e
commit
9a81b1b8ee
@ -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;
|
Loading…
Reference in New Issue
Block a user