mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 08:21:53 +00:00
refactor(client): abstract RawTextArea for variable input (#2204)
This commit is contained in:
parent
6496c65fc4
commit
6646007dd7
@ -25,7 +25,7 @@ export const Json = React.forwardRef<typeof Input.TextArea, JSONTextAreaProps>(
|
||||
{...props}
|
||||
className={cx(
|
||||
css`
|
||||
font-size: 90%;
|
||||
font-size: 80%;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
`,
|
||||
props.className,
|
||||
|
@ -1,64 +1,8 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import { css } from '@emotion/css';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
import { Input } from '../input';
|
||||
import { VariableSelect } from './VariableSelect';
|
||||
|
||||
// NOTE: https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js/46012210#46012210
|
||||
function setNativeInputValue(input, value) {
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(input.constructor.prototype, 'value')?.set;
|
||||
nativeInputValueSetter?.call(input, value);
|
||||
input.dispatchEvent(
|
||||
new Event('input', {
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
import { RawTextArea } from './RawTextArea';
|
||||
|
||||
export function JSONInput(props) {
|
||||
const inputRef = useRef<any>(null);
|
||||
const { scope, changeOnSelect, ...others } = props;
|
||||
const [options, setOptions] = useState(scope ? cloneDeep(scope) : []);
|
||||
|
||||
function onInsert(selected) {
|
||||
if (!inputRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const variable = `{{${selected.join('.')}}}`;
|
||||
|
||||
const { textArea } = inputRef.current.resizableTextArea;
|
||||
const nextValue =
|
||||
textArea.value.slice(0, textArea.selectionStart) + variable + textArea.value.slice(textArea.selectionEnd);
|
||||
const nextPos = [textArea.selectionStart, textArea.selectionStart + variable.length];
|
||||
setNativeInputValue(textArea, nextValue);
|
||||
textArea.setSelectionRange(...nextPos);
|
||||
textArea.focus();
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
position: relative;
|
||||
.ant-input {
|
||||
width: 100%;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Input.JSON {...others} ref={inputRef} />
|
||||
<Button.Group
|
||||
className={css`
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
.ant-btn-sm {
|
||||
font-size: 85%;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<VariableSelect options={options} setOptions={setOptions} onInsert={onInsert} changeOnSelect={changeOnSelect} />
|
||||
</Button.Group>
|
||||
</div>
|
||||
);
|
||||
return <RawTextArea {...props} component={Input.JSON} />;
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import { Button, Input } from 'antd';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
import { VariableSelect } from './VariableSelect';
|
||||
|
||||
// NOTE: https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js/46012210#46012210
|
||||
function setNativeInputValue(input, value) {
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(input.constructor.prototype, 'value')?.set;
|
||||
nativeInputValueSetter?.call(input, value);
|
||||
input.dispatchEvent(
|
||||
new Event('input', {
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function RawTextArea(props): JSX.Element {
|
||||
const inputRef = useRef<any>(null);
|
||||
const { scope, changeOnSelect, component: Component = Input.TextArea, ...others } = props;
|
||||
const [options, setOptions] = useState(scope ? cloneDeep(scope) : []);
|
||||
|
||||
function onInsert(selected) {
|
||||
if (!inputRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const variable = `{{${selected.join('.')}}}`;
|
||||
|
||||
const { textArea } = inputRef.current.resizableTextArea;
|
||||
const nextValue =
|
||||
textArea.value.slice(0, textArea.selectionStart) + variable + textArea.value.slice(textArea.selectionEnd);
|
||||
const nextPos = [textArea.selectionStart, textArea.selectionStart + variable.length];
|
||||
setNativeInputValue(textArea, nextValue);
|
||||
textArea.setSelectionRange(...nextPos);
|
||||
textArea.focus();
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={css`
|
||||
position: relative;
|
||||
.ant-input {
|
||||
width: 100%;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Component {...others} ref={inputRef} />
|
||||
<Button.Group
|
||||
className={css`
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
.ant-btn-sm {
|
||||
font-size: 85%;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<VariableSelect options={options} setOptions={setOptions} onInsert={onInsert} changeOnSelect={changeOnSelect} />
|
||||
</Button.Group>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -3,6 +3,7 @@ import { connect, mapReadPretty } from '@formily/react';
|
||||
import { IField } from '../../../collection-manager';
|
||||
import { Input } from './Input';
|
||||
import { JSONInput } from './JSONInput';
|
||||
import { RawTextArea } from './RawTextArea';
|
||||
import { TextArea } from './TextArea';
|
||||
|
||||
export function Variable() {
|
||||
@ -13,6 +14,8 @@ Variable.Input = connect(Input);
|
||||
|
||||
Variable.TextArea = connect(TextArea, mapReadPretty(TextArea.ReadPretty));
|
||||
|
||||
Variable.RawTextArea = connect(RawTextArea);
|
||||
|
||||
Variable.JSON = connect(JSONInput);
|
||||
|
||||
export default Variable;
|
||||
|
Loading…
Reference in New Issue
Block a user