function FloatingLinkEditor()

in libs/designer-ui/src/lib/editor/base/plugins/FloatingLinkEditor.tsx [34:278]


function FloatingLinkEditor({
  editor,
  isLink,
  setIsLink,
  anchorElem,
  isMainEditorFocused,
}: {
  editor: LexicalEditor;
  isLink: boolean;
  setIsLink: Dispatch<boolean>;
  anchorElem: HTMLElement;
  isMainEditorFocused: boolean;
}): JSX.Element {
  const { isInverted } = useTheme();
  const editorRef = useRef<HTMLDivElement | null>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const [linkUrl, setLinkUrl] = useState('');
  const [editedLinkUrl, setEditedLinkUrl] = useState('');
  const [isEditMode, setEditMode] = useState(false);
  const [lastSelection, setLastSelection] = useState<BaseSelection | null>(null);
  const [showFloatingLink, setShowFloatingLink] = useState(isMainEditorFocused);

  useEffect(() => {
    if (isMainEditorFocused) {
      setShowFloatingLink(isMainEditorFocused);
    }
  }, [isMainEditorFocused]);

  useOutsideClick([editorRef], () => {
    if (!isMainEditorFocused && showFloatingLink) {
      setShowFloatingLink(false);
      setEditMode(false);
    }
  });

  useEffect(() => {
    editor.registerCommand<boolean>(
      TOGGLE_LINK_COMMAND,
      (_) => {
        setShowFloatingLink(true);
        return false;
      },
      COMMAND_PRIORITY_CRITICAL
    );
  }, [editor, showFloatingLink]);

  const updateLinkEditor = useCallback(() => {
    const selection = $getSelection();
    if ($isRangeSelection(selection)) {
      const node = getSelectedNode(selection);
      const parent = node.getParent();
      if ($isLinkNode(parent)) {
        setLinkUrl(parent.getURL());
      } else if ($isLinkNode(node)) {
        setLinkUrl(node.getURL());
      } else {
        setLinkUrl('');
      }
    }
    const editorElem = editorRef.current;
    const nativeSelection = window.getSelection();
    const activeElement = document.activeElement;

    if (editorElem === null) {
      return;
    }

    const rootElement = editor.getRootElement();

    if (
      selection !== null &&
      nativeSelection !== null &&
      rootElement !== null &&
      rootElement.contains(nativeSelection.anchorNode) &&
      editor.isEditable()
    ) {
      const domRect: DOMRect | undefined = nativeSelection.focusNode?.parentElement?.getBoundingClientRect();
      if (domRect) {
        domRect.y += 40;
        setFloatingElemPositionForLinkEditor(domRect, editorElem, anchorElem);
      }
      setLastSelection(selection);
    } else if (!activeElement || activeElement.className !== 'link-input') {
      if (rootElement !== null) {
        setFloatingElemPositionForLinkEditor(null, editorElem, anchorElem);
      }
      setLastSelection(null);
      setEditMode(false);
      setLinkUrl('');
    }

    return true;
  }, [anchorElem, editor]);

  useEffect(() => {
    const scrollerElem = anchorElem.parentElement;

    const update = () => {
      editor.getEditorState().read(() => {
        updateLinkEditor();
      });
    };

    window.addEventListener('resize', update);

    if (scrollerElem) {
      scrollerElem.addEventListener('scroll', update);
    }

    return () => {
      window.removeEventListener('resize', update);

      if (scrollerElem) {
        scrollerElem.removeEventListener('scroll', update);
      }
    };
  }, [anchorElem.parentElement, editor, updateLinkEditor]);

  useEffect(() => {
    return mergeRegister(
      editor.registerUpdateListener(({ editorState }) => {
        editorState.read(() => {
          updateLinkEditor();
        });
      }),

      editor.registerCommand(
        SELECTION_CHANGE_COMMAND,
        () => {
          updateLinkEditor();
          return true;
        },
        COMMAND_PRIORITY_LOW
      ),
      editor.registerCommand(
        KEY_ESCAPE_COMMAND,
        () => {
          if (isLink) {
            setIsLink(false);
            return true;
          }
          return false;
        },
        COMMAND_PRIORITY_HIGH
      )
    );
  }, [editor, updateLinkEditor, setIsLink, isLink]);

  useEffect(() => {
    editor.getEditorState().read(() => {
      updateLinkEditor();
    });
  }, [editor, updateLinkEditor]);

  useEffect(() => {
    if (isEditMode && inputRef.current) {
      inputRef.current.focus();
    }
  }, [isEditMode]);

  const monitorInputInteraction = (event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {
    if (event.key === 'Enter') {
      event.preventDefault();
      handleLinkSubmission();
    } else if (event.key === 'Escape') {
      event.preventDefault();
      setEditMode(false);
    }
  };

  const handleLinkSubmission = () => {
    if (lastSelection !== null) {
      if (linkUrl !== '') {
        editor.dispatchCommand(TOGGLE_LINK_COMMAND, sanitizeUrl(editedLinkUrl));
      }
      setEditMode(false);
    }
  };

  return (
    <div ref={editorRef} className={css('msla-html-link-editor', isInverted && 'inverted')}>
      {!showFloatingLink || !isLink ? null : isEditMode ? (
        <div className="msla-html-link-view">
          <TextField
            styles={{ root: { padding: '4px', width: '80%' } }}
            elementRef={inputRef}
            className="link-input"
            value={editedLinkUrl}
            onChange={(_, value) => {
              setEditedLinkUrl(value ?? '');
            }}
            onKeyDown={(event) => {
              monitorInputInteraction(event);
            }}
          />
          <div>
            <IconButton
              tabIndex={0}
              onMouseDown={(event) => event.preventDefault()}
              iconProps={{ iconName: 'Cancel' }}
              styles={buttonStyles}
              onClick={() => {
                setEditMode(false);
              }}
            />
            <IconButton
              tabIndex={0}
              onMouseDown={(event) => event.preventDefault()}
              iconProps={{ iconName: 'CheckMark' }}
              styles={buttonStyles}
              onClick={handleLinkSubmission}
            />
          </div>
        </div>
      ) : (
        <div className="msla-html-link-view">
          <a href={sanitizeUrl(linkUrl)} target="_blank" rel="noopener noreferrer">
            {linkUrl}
          </a>
          <div>
            <IconButton
              tabIndex={0}
              onMouseDown={(event) => event.preventDefault()}
              iconProps={{ iconName: 'Edit' }}
              styles={buttonStyles}
              onClick={() => {
                setEditedLinkUrl(linkUrl);
                setEditMode(true);
              }}
            />
            <IconButton
              tabIndex={0}
              onMouseDown={(event) => event.preventDefault()}
              iconProps={{ iconName: 'Delete' }}
              styles={buttonStyles}
              onClick={() => {
                editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
              }}
            />
          </div>
        </div>
      )}
    </div>
  );
}