import { EditorView } from "prosemirror-view"; import * as React from "react"; import createAndInsertLink from "@shared/editor/commands/createAndInsertLink"; import { Dictionary } from "~/hooks/useDictionary"; import FloatingToolbar from "./FloatingToolbar"; import LinkEditor, { SearchResult } from "./LinkEditor"; type Props = { isActive: boolean; view: EditorView; dictionary: Dictionary; onCreateLink?: (title: string) => Promise; onSearchLink?: (term: string) => Promise; onClickLink: ( href: string, event: React.MouseEvent ) => void; onShowToast: (message: string) => void; onClose: () => void; }; function isActive(props: Props) { const { view } = props; const { selection } = view.state; try { const paragraph = view.domAtPos(selection.from); return props.isActive && !!paragraph.node; } catch (err) { return false; } } export default class LinkToolbar extends React.Component { menuRef = React.createRef(); state = { left: -1000, top: undefined, }; componentDidMount() { window.addEventListener("mousedown", this.handleClickOutside); } componentWillUnmount() { window.removeEventListener("mousedown", this.handleClickOutside); } handleClickOutside = (event: Event) => { if ( event.target instanceof HTMLElement && this.menuRef.current && this.menuRef.current.contains(event.target) ) { return; } this.props.onClose(); }; handleOnCreateLink = async (title: string) => { const { dictionary, onCreateLink, view, onClose, onShowToast } = this.props; onClose(); this.props.view.focus(); if (!onCreateLink) { return; } const { dispatch, state } = view; const { from, to } = state.selection; if (from !== to) { // selection must be collapsed return; } const href = `creating#${title}…`; // Insert a placeholder link dispatch( view.state.tr .insertText(title, from, to) .addMark( from, to + title.length, state.schema.marks.link.create({ href }) ) ); createAndInsertLink(view, title, href, { onCreateLink, onShowToast, dictionary, }); }; handleOnSelectLink = ({ href, title, }: { href: string; title: string; from: number; to: number; }) => { const { view, onClose } = this.props; onClose(); this.props.view.focus(); const { dispatch, state } = view; const { from, to } = state.selection; if (from !== to) { // selection must be collapsed return; } dispatch( view.state.tr .insertText(title, from, to) .addMark( from, to + title.length, state.schema.marks.link.create({ href }) ) ); }; render() { const { onCreateLink, onClose, ...rest } = this.props; const { selection } = this.props.view.state; const active = isActive(this.props); return ( {active && ( )} ); } }