From bcab86f3e8c2ec021c4d6564b3e6f2c49da946f8 Mon Sep 17 00:00:00 2001 From: VipinDevelops Date: Fri, 4 Jul 2025 16:23:44 +0530 Subject: [PATCH 1/4] fix: emoji modal scroll --- .../emoji/components/emojis-list.tsx | 7 +- .../emoji/components/floating-emoji-list.tsx | 122 ++++++++++++++++++ .../src/core/extensions/emoji/suggestion.ts | 84 ++++++------ 3 files changed, 169 insertions(+), 44 deletions(-) create mode 100644 packages/editor/src/core/extensions/emoji/components/floating-emoji-list.tsx diff --git a/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx b/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx index 86443cc9d40..8e49280a96b 100644 --- a/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx +++ b/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx @@ -77,20 +77,23 @@ export const EmojiList = forwardRef((props, ref) = () => ({ onKeyDown: ({ event }: { event: KeyboardEvent }): boolean => { if (event.key === "ArrowUp") { + event.preventDefault(); + event.stopPropagation(); upHandler(); return true; } if (event.key === "ArrowDown") { + event.preventDefault(); + event.stopPropagation(); downHandler(); return true; } if (event.key === "Enter") { - enterHandler(); event.preventDefault(); event.stopPropagation(); - + enterHandler(); return true; } diff --git a/packages/editor/src/core/extensions/emoji/components/floating-emoji-list.tsx b/packages/editor/src/core/extensions/emoji/components/floating-emoji-list.tsx new file mode 100644 index 00000000000..a3efdf286e6 --- /dev/null +++ b/packages/editor/src/core/extensions/emoji/components/floating-emoji-list.tsx @@ -0,0 +1,122 @@ +import { computePosition, flip, shift } from "@floating-ui/dom"; +import { posToDOMRect } from "@tiptap/react"; +import { useEffect, useRef, useState, forwardRef, useImperativeHandle } from "react"; +import { SuggestionKeyDownProps } from "@tiptap/suggestion"; +// local imports +import { EmojiList, EmojiListRef, EmojiListProps } from "./emojis-list"; + +export interface FloatingEmojiListProps extends EmojiListProps { + isOpen: boolean; + onOpenChange: (open: boolean) => void; +} + +export interface FloatingEmojiListRef { + onKeyDown: (props: SuggestionKeyDownProps) => boolean; +} + +const updatePosition = (editor: any, element: HTMLElement) => { + const virtualElement = { + getBoundingClientRect: () => posToDOMRect(editor.view, editor.state.selection.from, editor.state.selection.to), + }; + + computePosition(virtualElement, element, { + placement: "bottom-start", + strategy: "absolute", + middleware: [shift(), flip()], + }).then(({ x, y, strategy }) => { + Object.assign(element.style, { + width: "max-content", + position: strategy, + left: `${x}px`, + top: `${y}px`, + }); + }); +}; + +export const FloatingEmojiList = forwardRef((props, ref) => { + const { isOpen, onOpenChange, ...emojiListProps } = props; + const [isAnimatedIn, setIsAnimatedIn] = useState(false); + const emojiListRef = useRef(null); + const containerRef = useRef(null); + + // Expose onKeyDown method via forwardRef + useImperativeHandle( + ref, + () => ({ + onKeyDown: (keyProps: SuggestionKeyDownProps): boolean => { + if (keyProps.event.key === "Escape") { + onOpenChange(false); + return true; + } + + // Delegate to EmojiList + if (emojiListRef.current) { + return emojiListRef.current.onKeyDown({ event: keyProps.event }); + } + + return false; + }, + }), + [onOpenChange] + ); + + // Animation effect + useEffect(() => { + if (isOpen) { + setIsAnimatedIn(false); + // Add a small delay before starting the animation + const timeout = setTimeout(() => { + requestAnimationFrame(() => { + setIsAnimatedIn(true); + }); + }, 50); + + return () => clearTimeout(timeout); + } else { + setIsAnimatedIn(false); + } + }, [isOpen]); + + // Update position when component mounts or editor changes + useEffect(() => { + if (isOpen && containerRef.current && emojiListProps.editor) { + updatePosition(emojiListProps.editor, containerRef.current); + } + }, [isOpen, emojiListProps.editor, emojiListProps.items]); + + // Handle scroll events to update position + useEffect(() => { + const handleScroll = () => { + if (isOpen && containerRef.current && emojiListProps.editor) { + updatePosition(emojiListProps.editor, containerRef.current); + } + }; + + document.addEventListener("scroll", handleScroll, true); + + return () => { + document.removeEventListener("scroll", handleScroll, true); + }; + }, [isOpen, emojiListProps.editor]); + + if (!isOpen) { + return null; + } + + return ( +
+ +
+ ); +}); + +FloatingEmojiList.displayName = "FloatingEmojiList"; diff --git a/packages/editor/src/core/extensions/emoji/suggestion.ts b/packages/editor/src/core/extensions/emoji/suggestion.ts index 459d605a5de..ea37e63f643 100644 --- a/packages/editor/src/core/extensions/emoji/suggestion.ts +++ b/packages/editor/src/core/extensions/emoji/suggestion.ts @@ -1,13 +1,13 @@ import type { EmojiOptions } from "@tiptap/extension-emoji"; import { ReactRenderer, Editor } from "@tiptap/react"; import { SuggestionProps, SuggestionKeyDownProps } from "@tiptap/suggestion"; -import tippy, { Instance as TippyInstance } from "tippy.js"; // constants import { CORE_EXTENSIONS } from "@/constants/extension"; // helpers import { getExtensionStorage } from "@/helpers/get-extension-storage"; // local imports -import { EmojiItem, EmojiList, EmojiListRef, EmojiListProps } from "./components/emojis-list"; +import { EmojiItem } from "./components/emojis-list"; +import { FloatingEmojiList, FloatingEmojiListProps, FloatingEmojiListRef } from "./components/floating-emoji-list"; const DEFAULT_EMOJIS = ["+1", "-1", "smile", "orange_heart", "eyes"]; @@ -36,73 +36,75 @@ const emojiSuggestion: EmojiOptions["suggestion"] = { allowSpaces: false, render: () => { - let component: ReactRenderer; - let popup: TippyInstance[] | null = null; + let component: ReactRenderer; + let isOpen = false; return { onStart: (props: SuggestionProps): void => { - const emojiListProps: EmojiListProps = { + if (!props.clientRect) return; + + isOpen = true; + + const floatingEmojiListProps: FloatingEmojiListProps = { items: props.items, command: props.command, editor: props.editor, + isOpen, + onOpenChange: (open: boolean) => { + isOpen = open; + if (!open) { + // Handle close from floating UI + const utilityStorage = getExtensionStorage(props.editor, CORE_EXTENSIONS.UTILITY); + const index = utilityStorage.activeDropbarExtensions.indexOf(CORE_EXTENSIONS.EMOJI); + if (index > -1) { + utilityStorage.activeDropbarExtensions.splice(index, 1); + } + } + }, }; getExtensionStorage(props.editor, CORE_EXTENSIONS.UTILITY).activeDropbarExtensions.push(CORE_EXTENSIONS.EMOJI); - component = new ReactRenderer(EmojiList, { - props: emojiListProps, + component = new ReactRenderer(FloatingEmojiList, { + props: floatingEmojiListProps, editor: props.editor, }); - if (!props.clientRect) return; + // Append to the active editor or editor container + const targetElement = (props.editor.options.element || document.body) as HTMLElement; - popup = tippy("body", { - getReferenceClientRect: props.clientRect as () => DOMRect, - appendTo: () => - document.querySelector(".active-editor") ?? - document.querySelector('[id^="editor-container"]') ?? - document.body, - content: component.element, - showOnCreate: true, - interactive: true, - trigger: "manual", - placement: "bottom-start", - hideOnClick: false, - sticky: "reference", - animation: false, - duration: 0, - offset: [0, 8], - }); + targetElement.appendChild(component.element); }, onUpdate: (props: SuggestionProps): void => { - const emojiListProps: EmojiListProps = { + if (!component || !component.element) return; + + component.updateProps({ items: props.items, command: props.command, editor: props.editor, - }; - - component.updateProps(emojiListProps); - - if (popup && props.clientRect) { - popup[0]?.setProps({ - getReferenceClientRect: props.clientRect as () => DOMRect, - }); - } + isOpen, + onOpenChange: (open: boolean) => { + isOpen = open; + }, + }); }, onKeyDown: (props: SuggestionKeyDownProps): boolean => { if (props.event.key === "Escape") { - if (popup) { - popup[0]?.hide(); - } + isOpen = false; if (component) { component.destroy(); } return true; } - return component.ref?.onKeyDown(props) || false; + // Delegate keyboard events to the FloatingEmojiList component + if (component && component.ref) { + return component.ref.onKeyDown(props) || false; + } + + return false; }, onExit: (props: SuggestionProps): void => { @@ -112,9 +114,7 @@ const emojiSuggestion: EmojiOptions["suggestion"] = { utilityStorage.activeDropbarExtensions.splice(index, 1); } - if (popup) { - popup[0]?.destroy(); - } + isOpen = false; if (component) { component.destroy(); } From b139fca0c4b71efc0856b931fac5993d2ecb438b Mon Sep 17 00:00:00 2001 From: VipinDevelops Date: Fri, 4 Jul 2025 16:30:42 +0530 Subject: [PATCH 2/4] refactor: emoji list --- .../emoji/components/emojis-list.tsx | 215 ++++++++++-------- .../emoji/components/floating-emoji-list.tsx | 122 ---------- .../src/core/extensions/emoji/suggestion.ts | 74 ++---- 3 files changed, 150 insertions(+), 261 deletions(-) delete mode 100644 packages/editor/src/core/extensions/emoji/components/floating-emoji-list.tsx diff --git a/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx b/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx index 8e49280a96b..a1050d7cb71 100644 --- a/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx +++ b/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx @@ -1,5 +1,7 @@ -import { Editor } from "@tiptap/react"; -import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react"; +import { Editor, posToDOMRect } from "@tiptap/react"; +import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react"; +import { computePosition, flip, shift } from "@floating-ui/dom"; +import { SuggestionKeyDownProps } from "@tiptap/suggestion"; // plane imports import { cn } from "@plane/utils"; @@ -18,14 +20,33 @@ export interface EmojiListProps { } export interface EmojiListRef { - onKeyDown: (props: { event: KeyboardEvent }) => boolean; + onKeyDown: (props: SuggestionKeyDownProps) => boolean; } +const updatePosition = (editor: any, element: HTMLElement) => { + const virtualElement = { + getBoundingClientRect: () => posToDOMRect(editor.view, editor.state.selection.from, editor.state.selection.to), + }; + + computePosition(virtualElement, element, { + placement: "bottom-start", + strategy: "absolute", + middleware: [shift(), flip()], + }).then(({ x, y, strategy }) => { + Object.assign(element.style, { + width: "max-content", + position: strategy, + left: `${x}px`, + top: `${y}px`, + }); + }); +}; + export const EmojiList = forwardRef((props, ref) => { - const { items, command } = props; + const { items, command, editor } = props; const [selectedIndex, setSelectedIndex] = useState(0); - // refs - const emojiListContainer = useRef(null); + const [isVisible, setIsVisible] = useState(false); + const containerRef = useRef(null); const selectItem = useCallback( (index: number): void => { @@ -37,26 +58,63 @@ export const EmojiList = forwardRef((props, ref) = [command, items] ); - const upHandler = useCallback(() => { - setSelectedIndex((prevIndex) => (prevIndex + items.length - 1) % items.length); - }, [items.length]); + const handleKeyDown = useCallback( + (event: KeyboardEvent): boolean => { + if (event.key === "ArrowUp") { + event.preventDefault(); + setSelectedIndex((prev) => (prev + items.length - 1) % items.length); + return true; + } - const downHandler = useCallback(() => { - setSelectedIndex((prevIndex) => (prevIndex + 1) % items.length); - }, [items.length]); + if (event.key === "ArrowDown") { + event.preventDefault(); + setSelectedIndex((prev) => (prev + 1) % items.length); + return true; + } - const enterHandler = useCallback(() => { - setSelectedIndex((prevIndex) => { - selectItem(prevIndex); - return prevIndex; - }); - }, [selectItem]); + if (event.key === "Enter") { + event.preventDefault(); + selectItem(selectedIndex); + return true; + } + + return false; + }, + [items.length, selectedIndex, selectItem] + ); + // Update position when items change + useEffect(() => { + if (containerRef.current && editor) { + updatePosition(editor, containerRef.current); + } + }, [items, editor]); + + // Handle scroll events + useEffect(() => { + const handleScroll = () => { + if (containerRef.current && editor) { + updatePosition(editor, containerRef.current); + } + }; + + document.addEventListener("scroll", handleScroll, true); + return () => document.removeEventListener("scroll", handleScroll, true); + }, [editor]); + + // Show animation + useEffect(() => { + setIsVisible(false); + const timeout = setTimeout(() => setIsVisible(true), 50); + return () => clearTimeout(timeout); + }, []); + + // Reset selection when items change useEffect(() => setSelectedIndex(0), [items]); - // scroll to the dropdown item when navigating via keyboard - useLayoutEffect(() => { - const container = emojiListContainer?.current; + // Scroll selected item into view + useEffect(() => { + const container = containerRef.current; if (!container) return; const item = container.querySelector(`#emoji-item-${selectedIndex}`) as HTMLElement; @@ -64,9 +122,7 @@ export const EmojiList = forwardRef((props, ref) = const containerRect = container.getBoundingClientRect(); const itemRect = item.getBoundingClientRect(); - const isItemInView = itemRect.top >= containerRect.top && itemRect.bottom <= containerRect.bottom; - - if (!isItemInView) { + if (itemRect.top < containerRect.top || itemRect.bottom > containerRect.bottom) { item.scrollIntoView({ block: "nearest" }); } } @@ -75,78 +131,57 @@ export const EmojiList = forwardRef((props, ref) = useImperativeHandle( ref, () => ({ - onKeyDown: ({ event }: { event: KeyboardEvent }): boolean => { - if (event.key === "ArrowUp") { - event.preventDefault(); - event.stopPropagation(); - upHandler(); - return true; - } - - if (event.key === "ArrowDown") { - event.preventDefault(); - event.stopPropagation(); - downHandler(); - return true; - } - - if (event.key === "Enter") { - event.preventDefault(); - event.stopPropagation(); - enterHandler(); - return true; - } - - return false; - }, + onKeyDown: ({ event }: SuggestionKeyDownProps): boolean => handleKeyDown(event), }), - [upHandler, downHandler, enterHandler] + [handleKeyDown] ); + return (
- {items.length ? ( - items.map((item, index) => { - const isSelected = index === selectedIndex; - const emojiKey = item.shortcodes.join(" - "); - - return ( - - ); - }) - ) : ( -
No emojis found
- )} + onClick={() => selectItem(index)} + onMouseEnter={() => setSelectedIndex(index)} + > + + {item.fallbackImage ? ( + {item.name} + ) : ( + item.emoji + )} + + + :{item.name}: + + + ); + }) + ) : ( +
No emojis found
+ )} +
); }); diff --git a/packages/editor/src/core/extensions/emoji/components/floating-emoji-list.tsx b/packages/editor/src/core/extensions/emoji/components/floating-emoji-list.tsx deleted file mode 100644 index a3efdf286e6..00000000000 --- a/packages/editor/src/core/extensions/emoji/components/floating-emoji-list.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { computePosition, flip, shift } from "@floating-ui/dom"; -import { posToDOMRect } from "@tiptap/react"; -import { useEffect, useRef, useState, forwardRef, useImperativeHandle } from "react"; -import { SuggestionKeyDownProps } from "@tiptap/suggestion"; -// local imports -import { EmojiList, EmojiListRef, EmojiListProps } from "./emojis-list"; - -export interface FloatingEmojiListProps extends EmojiListProps { - isOpen: boolean; - onOpenChange: (open: boolean) => void; -} - -export interface FloatingEmojiListRef { - onKeyDown: (props: SuggestionKeyDownProps) => boolean; -} - -const updatePosition = (editor: any, element: HTMLElement) => { - const virtualElement = { - getBoundingClientRect: () => posToDOMRect(editor.view, editor.state.selection.from, editor.state.selection.to), - }; - - computePosition(virtualElement, element, { - placement: "bottom-start", - strategy: "absolute", - middleware: [shift(), flip()], - }).then(({ x, y, strategy }) => { - Object.assign(element.style, { - width: "max-content", - position: strategy, - left: `${x}px`, - top: `${y}px`, - }); - }); -}; - -export const FloatingEmojiList = forwardRef((props, ref) => { - const { isOpen, onOpenChange, ...emojiListProps } = props; - const [isAnimatedIn, setIsAnimatedIn] = useState(false); - const emojiListRef = useRef(null); - const containerRef = useRef(null); - - // Expose onKeyDown method via forwardRef - useImperativeHandle( - ref, - () => ({ - onKeyDown: (keyProps: SuggestionKeyDownProps): boolean => { - if (keyProps.event.key === "Escape") { - onOpenChange(false); - return true; - } - - // Delegate to EmojiList - if (emojiListRef.current) { - return emojiListRef.current.onKeyDown({ event: keyProps.event }); - } - - return false; - }, - }), - [onOpenChange] - ); - - // Animation effect - useEffect(() => { - if (isOpen) { - setIsAnimatedIn(false); - // Add a small delay before starting the animation - const timeout = setTimeout(() => { - requestAnimationFrame(() => { - setIsAnimatedIn(true); - }); - }, 50); - - return () => clearTimeout(timeout); - } else { - setIsAnimatedIn(false); - } - }, [isOpen]); - - // Update position when component mounts or editor changes - useEffect(() => { - if (isOpen && containerRef.current && emojiListProps.editor) { - updatePosition(emojiListProps.editor, containerRef.current); - } - }, [isOpen, emojiListProps.editor, emojiListProps.items]); - - // Handle scroll events to update position - useEffect(() => { - const handleScroll = () => { - if (isOpen && containerRef.current && emojiListProps.editor) { - updatePosition(emojiListProps.editor, containerRef.current); - } - }; - - document.addEventListener("scroll", handleScroll, true); - - return () => { - document.removeEventListener("scroll", handleScroll, true); - }; - }, [isOpen, emojiListProps.editor]); - - if (!isOpen) { - return null; - } - - return ( -
- -
- ); -}); - -FloatingEmojiList.displayName = "FloatingEmojiList"; diff --git a/packages/editor/src/core/extensions/emoji/suggestion.ts b/packages/editor/src/core/extensions/emoji/suggestion.ts index ea37e63f643..5bf2187466b 100644 --- a/packages/editor/src/core/extensions/emoji/suggestion.ts +++ b/packages/editor/src/core/extensions/emoji/suggestion.ts @@ -6,8 +6,7 @@ import { CORE_EXTENSIONS } from "@/constants/extension"; // helpers import { getExtensionStorage } from "@/helpers/get-extension-storage"; // local imports -import { EmojiItem } from "./components/emojis-list"; -import { FloatingEmojiList, FloatingEmojiListProps, FloatingEmojiListRef } from "./components/floating-emoji-list"; +import { EmojiItem, EmojiList, EmojiListRef } from "./components/emojis-list"; const DEFAULT_EMOJIS = ["+1", "-1", "smile", "orange_heart", "eyes"]; @@ -36,85 +35,62 @@ const emojiSuggestion: EmojiOptions["suggestion"] = { allowSpaces: false, render: () => { - let component: ReactRenderer; - let isOpen = false; + let component: ReactRenderer; + let editor: Editor; return { onStart: (props: SuggestionProps): void => { if (!props.clientRect) return; - isOpen = true; + editor = props.editor; - const floatingEmojiListProps: FloatingEmojiListProps = { - items: props.items, - command: props.command, - editor: props.editor, - isOpen, - onOpenChange: (open: boolean) => { - isOpen = open; - if (!open) { - // Handle close from floating UI - const utilityStorage = getExtensionStorage(props.editor, CORE_EXTENSIONS.UTILITY); - const index = utilityStorage.activeDropbarExtensions.indexOf(CORE_EXTENSIONS.EMOJI); - if (index > -1) { - utilityStorage.activeDropbarExtensions.splice(index, 1); - } - } - }, - }; - - getExtensionStorage(props.editor, CORE_EXTENSIONS.UTILITY).activeDropbarExtensions.push(CORE_EXTENSIONS.EMOJI); + // Track active dropdown + getExtensionStorage(editor, CORE_EXTENSIONS.UTILITY).activeDropbarExtensions.push(CORE_EXTENSIONS.EMOJI); - component = new ReactRenderer(FloatingEmojiList, { - props: floatingEmojiListProps, + component = new ReactRenderer(EmojiList, { + props: { + items: props.items, + command: props.command, + editor: props.editor, + }, editor: props.editor, }); - // Append to the active editor or editor container + // Append to editor container const targetElement = (props.editor.options.element || document.body) as HTMLElement; - targetElement.appendChild(component.element); }, onUpdate: (props: SuggestionProps): void => { - if (!component || !component.element) return; + if (!component) return; component.updateProps({ items: props.items, command: props.command, editor: props.editor, - isOpen, - onOpenChange: (open: boolean) => { - isOpen = open; - }, }); }, onKeyDown: (props: SuggestionKeyDownProps): boolean => { if (props.event.key === "Escape") { - isOpen = false; - if (component) { - component.destroy(); - } return true; } - // Delegate keyboard events to the FloatingEmojiList component - if (component && component.ref) { - return component.ref.onKeyDown(props) || false; - } - - return false; + // Delegate to EmojiList + return component?.ref?.onKeyDown(props) || false; }, - onExit: (props: SuggestionProps): void => { - const utilityStorage = getExtensionStorage(props.editor, CORE_EXTENSIONS.UTILITY); - const index = utilityStorage.activeDropbarExtensions.indexOf(CORE_EXTENSIONS.EMOJI); - if (index > -1) { - utilityStorage.activeDropbarExtensions.splice(index, 1); + onExit: (): void => { + // Remove from active dropdowns + if (editor) { + const utilityStorage = getExtensionStorage(editor, CORE_EXTENSIONS.UTILITY); + const index = utilityStorage.activeDropbarExtensions.indexOf(CORE_EXTENSIONS.EMOJI); + if (index > -1) { + utilityStorage.activeDropbarExtensions.splice(index, 1); + } } - isOpen = false; + // Cleanup if (component) { component.destroy(); } From e2be8147dfc6bc446cfccc4a3aa9ceb44dd7b9ba Mon Sep 17 00:00:00 2001 From: VipinDevelops Date: Fri, 4 Jul 2025 16:39:03 +0530 Subject: [PATCH 3/4] fix: escape behavior --- .../src/core/extensions/emoji/components/emojis-list.tsx | 5 +++++ packages/editor/src/core/extensions/emoji/suggestion.ts | 3 +++ 2 files changed, 8 insertions(+) diff --git a/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx b/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx index a1050d7cb71..cbe5c5f2943 100644 --- a/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx +++ b/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx @@ -60,6 +60,11 @@ export const EmojiList = forwardRef((props, ref) = const handleKeyDown = useCallback( (event: KeyboardEvent): boolean => { + if (event.key === "Escape") { + event.preventDefault(); + return true; + } + if (event.key === "ArrowUp") { event.preventDefault(); setSelectedIndex((prev) => (prev + items.length - 1) % items.length); diff --git a/packages/editor/src/core/extensions/emoji/suggestion.ts b/packages/editor/src/core/extensions/emoji/suggestion.ts index 5bf2187466b..d9feec21bec 100644 --- a/packages/editor/src/core/extensions/emoji/suggestion.ts +++ b/packages/editor/src/core/extensions/emoji/suggestion.ts @@ -73,6 +73,9 @@ const emojiSuggestion: EmojiOptions["suggestion"] = { onKeyDown: (props: SuggestionKeyDownProps): boolean => { if (props.event.key === "Escape") { + if (component) { + component.destroy(); + } return true; } From cbd97fdebe68efca8bb1f61da823ec50906e0065 Mon Sep 17 00:00:00 2001 From: VipinDevelops Date: Fri, 4 Jul 2025 17:02:31 +0530 Subject: [PATCH 4/4] fix: minor type fixes --- .../src/core/extensions/emoji/components/emojis-list.tsx | 6 +++--- packages/editor/src/core/extensions/emoji/suggestion.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx b/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx index cbe5c5f2943..706dbc685a2 100644 --- a/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx +++ b/packages/editor/src/core/extensions/emoji/components/emojis-list.tsx @@ -1,7 +1,7 @@ -import { Editor, posToDOMRect } from "@tiptap/react"; -import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react"; import { computePosition, flip, shift } from "@floating-ui/dom"; +import { Editor, posToDOMRect } from "@tiptap/react"; import { SuggestionKeyDownProps } from "@tiptap/suggestion"; +import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react"; // plane imports import { cn } from "@plane/utils"; @@ -23,7 +23,7 @@ export interface EmojiListRef { onKeyDown: (props: SuggestionKeyDownProps) => boolean; } -const updatePosition = (editor: any, element: HTMLElement) => { +const updatePosition = (editor: Editor, element: HTMLElement) => { const virtualElement = { getBoundingClientRect: () => posToDOMRect(editor.view, editor.state.selection.from, editor.state.selection.to), }; diff --git a/packages/editor/src/core/extensions/emoji/suggestion.ts b/packages/editor/src/core/extensions/emoji/suggestion.ts index d9feec21bec..a75e93fb031 100644 --- a/packages/editor/src/core/extensions/emoji/suggestion.ts +++ b/packages/editor/src/core/extensions/emoji/suggestion.ts @@ -57,7 +57,8 @@ const emojiSuggestion: EmojiOptions["suggestion"] = { }); // Append to editor container - const targetElement = (props.editor.options.element || document.body) as HTMLElement; + const targetElement = + (props.editor.options.element as HTMLElement) || props.editor.view.dom.parentElement || document.body; targetElement.appendChild(component.element); },