import { observer } from "mobx-react-lite"; import { memo, useEffect, useRef, useState } from "react"; import ReactMarkdown from "react-markdown"; import rehypeRaw from "rehype-raw"; import remarkGfm from "remark-gfm"; import useCurrentUser from "@/hooks/useCurrentUser"; import { cn } from "@/lib/utils"; import { memoStore } from "@/store"; import { useTranslate } from "@/utils/i18n"; import { remarkPreserveType } from "@/utils/remark-plugins/remark-preserve-type"; import { remarkTag } from "@/utils/remark-plugins/remark-tag"; import { isSuperUser } from "@/utils/user"; import { CodeBlock } from "./CodeBlock"; import { createConditionalComponent, isTagNode, isTaskListItemNode } from "./ConditionalComponent"; import { MemoContentContext } from "./MemoContentContext"; import { Tag } from "./Tag"; import { TaskListItem } from "./TaskListItem"; // MAX_DISPLAY_HEIGHT is the maximum height of the memo content to display in compact mode. const MAX_DISPLAY_HEIGHT = 256; interface Props { content: string; memoName?: string; compact?: boolean; readonly?: boolean; disableFilter?: boolean; className?: string; contentClassName?: string; onClick?: (e: React.MouseEvent) => void; onDoubleClick?: (e: React.MouseEvent) => void; parentPage?: string; } type ContentCompactView = "ALL" | "SNIPPET"; const MemoContent = observer((props: Props) => { const { className, contentClassName, content, memoName, onClick, onDoubleClick } = props; const t = useTranslate(); const currentUser = useCurrentUser(); const memoContentContainerRef = useRef(null); const [showCompactMode, setShowCompactMode] = useState(undefined); const memo = memoName ? memoStore.getMemoByName(memoName) : null; const allowEdit = !props.readonly && memo && (currentUser?.name === memo.creator || isSuperUser(currentUser)); // Context for custom components const contextValue = { memoName, readonly: !allowEdit, disableFilter: props.disableFilter, parentPage: props.parentPage, containerRef: memoContentContainerRef, }; // Initial compact mode. useEffect(() => { if (!props.compact) { return; } if (!memoContentContainerRef.current) { return; } if ((memoContentContainerRef.current as HTMLDivElement).getBoundingClientRect().height > MAX_DISPLAY_HEIGHT) { setShowCompactMode("ALL"); } }, []); const onMemoContentClick = async (e: React.MouseEvent) => { // Image clicks and other handlers if (onClick) { onClick(e); } }; const onMemoContentDoubleClick = async (e: React.MouseEvent) => { if (onDoubleClick) { onDoubleClick(e); } }; const compactStates = { ALL: { text: t("memo.show-more"), nextState: "SNIPPET" }, SNIPPET: { text: t("memo.show-less"), nextState: "ALL" }, }; return (
( {children} ), }} > {content}
{showCompactMode == "ALL" && (
)} {showCompactMode != undefined && (
{ setShowCompactMode(compactStates[showCompactMode].nextState as ContentCompactView); }} > {compactStates[showCompactMode].text}
)}
); }); export default memo(MemoContent);