UNPKG

@vnetwork/richtext-editor

Version:

react richtext editor, only using for internal purpose

104 lines (93 loc) 3.97 kB
import _JSXStyle from "styled-jsx/style"; function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } import React from "react"; import { Transforms, Range } from "slate"; import { ReactEditor, useSelected, useFocused } from "slate-react"; import { useAppContext } from "../../context/app"; import Portal from "../Portal"; import { PORTAL_GAP } from "../../constants"; import MentionItem from "./Item"; export const createMentionNode = character => ({ type: "mention", character, children: [{ text: "" }] }); export const MentionModal = ({ editor, target, search, callback }) => { const ref = React.useRef(); const { employees } = useAppContext(); const mentions = employees.filter(employee => employee.email?.toString().toLowerCase().startsWith(search.toLowerCase())).slice(0, 10); const handleOnClick = character => { const { selection } = editor; const mention = createMentionNode(character); Transforms.select(editor, target); if (Range.isCollapsed(selection)) { Transforms.insertNodes(editor, mention); } callback(); }; React.useEffect(() => { // perform update mention modal position follow editor range is focused if (target && ref?.current) { const currentSlateRange = ReactEditor.toDOMRange(editor, target); const boundingClientRect = currentSlateRange.getBoundingClientRect(); const top = boundingClientRect.top + window.pageYOffset; const left = boundingClientRect.left + window.pageXOffset; ref.current.style.top = `${top + PORTAL_GAP}px`; ref.current.style.left = `${left}px`; ref.current.style.opacity = 1; } const handleOnClickOutside = event => { // console.log("components.MentionModal:event(click:out)"); return !ref.current?.contains(event.target) ? Transforms.select(editor, target) : null; }; // handle document click outside mention modal & remove event listener on unmount component document.addEventListener("click", handleOnClickOutside); return () => document.removeEventListener("click", handleOnClickOutside); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return /*#__PURE__*/React.createElement(Portal, null, /*#__PURE__*/React.createElement("div", { ref: ref, "data-cy": "richtext-mentions", className: "jsx-151523523" + " " + "richtext-portal" }, mentions.length !== 0 ? mentions.map((mention, index) => { return /*#__PURE__*/React.createElement(MentionItem, { mention: mention, key: index, callback: handleOnClick }); }) : "components.MentionModal:empty"), /*#__PURE__*/React.createElement(_JSXStyle, { id: "151523523" }, ".richtext-portal.jsx-151523523{height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;background-color:white;border-radius:0.25rem;overflow:hidden;box-shadow:rgba(9,30,66,0.31) 0px 0px 1px, rgba(9,30,66,0.25) 0px 4px 8px -2px;position:absolute;z-index:1;left:0;opacity:0;-webkit-transition:opacity linear 0.25s;transition:opacity linear 0.25s;}")); }; const Mention = ({ attributes, children, element }) => { const selected = useSelected(); const focused = useFocused(); return /*#__PURE__*/React.createElement("span", _extends({}, attributes, { contentEditable: false, "data-cy": `mention-${element.character.replace(" ", "-")}`, style: { padding: "3px 3px 2px", margin: "0 1px", verticalAlign: "baseline", display: "inline-block", borderRadius: "4px", backgroundColor: "#eee", fontSize: "0.9em", boxShadow: selected && focused ? "0 0 0 2px #B4D5FF" : "none" } }), "@", element.character, children); }; export default Mention;