如何通过在 Typescript React 中单击按钮向 <TextInput 对象发送上下文菜单事件和 select 菜单项

How to send a <TextInput object the contextmenu event and select a menu item via a button click in Typescript React

在我们的聊天组件中,很多人不知道他们可以通过右键单击文本区域并从菜单中选择表情符号来找到表情符号菜单。我想在 UI 中放置一个可以直接打开表情符号菜单的按钮。这样,不知道右键单击和上下文菜单的人只需单击表情符号按钮即可获得内置的表情符号。

我尝试在文本输入旁边添加一个按钮(因此不是 TextInput 的子项)并像这样发送上下文菜单:

const showCtxMenu = () => {
    var element = document.getElementById("chatti");
    var ev2 = new MouseEvent("contextmenu", {
      bubbles: true,
      cancelable: false,
      view: window,
      button: 2,
      buttons: 0,
      clientX: element.getBoundingClientRect().x,
      clientY: element.getBoundingClientRect().y
    });
    element.dispatchEvent(ev2);
  }

和TextInput对象在同一个功能组件中是这样的:

return (
    <View
      style={[
        style.inputView,
        {borderColor: primaryColor, height: 40}
      ]}>
      <div id="chatti">
      <TextInput
        value={message}
        // onContentSizeChange={(event) => {
        // causes infinite react state update on ctrl+A -> delete
        // setHeight(event.nativeEvent.contentSize.height);
        // }}
        onChangeText={(text) => onChangeMessage(text)}
        style={{

单击按钮时,我将事件发送给父级 div,因此没有任何反应。 我不知道如何将事件直接发送到 TextInput,因为我不知道如何找到它。

我在 React 中设置了一个代码示例,说明如何通过单击按钮调用上下文菜单事件。您将需要 DOM 引用 TextInput 和您拥有上下文菜单的上下文菜单容器。

工作样本:https://stackblitz.com/edit/react-727dpd?file=src/App.js

在我的示例中,右键单击文本区域或按钮时会显示上下文菜单。您需要关注的一点是我如何将 ref 添加到 textarea。

import React, { useRef, useEffect } from 'react';
import './style.css';

export default function App() {
  const contextMenuRef = useRef();
  const chatBoxRef = useRef();

  useEffect(() => {
    const contextMenu = contextMenuRef.current;
    const scope = chatBoxRef.current;

    const normalizePozition = (mouseX, mouseY) => {
      // ? compute what is the mouse position relative to the container element (scope)
      let { left: scopeOffsetX, top: scopeOffsetY } =
        scope.getBoundingClientRect();

      scopeOffsetX = scopeOffsetX < 0 ? 0 : scopeOffsetX;
      scopeOffsetY = scopeOffsetY < 0 ? 0 : scopeOffsetY;

      const scopeX = mouseX - scopeOffsetX;
      const scopeY = mouseY - scopeOffsetY;

      // ? check if the element will go out of bounds
      const outOfBoundsOnX =
        scopeX + contextMenu.clientWidth > scope.clientWidth;

      const outOfBoundsOnY =
        scopeY + contextMenu.clientHeight > scope.clientHeight;

      let normalizedX = mouseX;
      let normalizedY = mouseY;

      // ? normalize on X
      if (outOfBoundsOnX) {
        normalizedX =
          scopeOffsetX + scope.clientWidth - contextMenu.clientWidth;
      }

      // ? normalize on Y
      if (outOfBoundsOnY) {
        normalizedY =
          scopeOffsetY + scope.clientHeight - contextMenu.clientHeight;
      }

      return { normalizedX, normalizedY };
    };

    scope.addEventListener('contextmenu', (event) => {
      console.log(event);
      event.preventDefault();

      const { clientX: mouseX, clientY: mouseY } = event;

      const { normalizedX, normalizedY } = normalizePozition(mouseX, mouseY);

      contextMenu.classList.remove('visible');

      contextMenu.style.top = `${normalizedY}px`;
      contextMenu.style.left = `${normalizedX}px`;

      setTimeout(() => {
        contextMenu.classList.add('visible');
      });
    });

    scope.addEventListener('click', (e) => {
      // ? close the menu if the user clicks outside of it
      if (e.target.offsetParent != contextMenu) {
        contextMenu.classList.remove('visible');
      }
    });
  }, []);

  const showCtxMenu = (e) => {
    //e.preventDefault();
    var element = chatBoxRef.current;
    var ev2 = new MouseEvent('contextmenu', {
      bubbles: true,
      cancelable: false,
      view: window,
      button: 2,
      buttons: 0,
      clientX: element.getBoundingClientRect().x,
      clientY: element.getBoundingClientRect().y,
    });
    element.dispatchEvent(ev2);
  };

  return (
    <>
      <div style={{ height: '300px' }}></div>
      <textarea ref={chatBoxRef} col={10}></textarea>
      <div ref={contextMenuRef} id="context-menu">
        <div class="item">Option 1</div>
        <div class="item">Option 2</div>
        <div class="item">Option 3</div>
        <div class="item">Option 4</div>
        <div class="item">Option 5</div>
      </div>
      <button onClick={showCtxMenu}>Press Context</button>
    </>
  );
}