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