-
Notifications
You must be signed in to change notification settings - Fork 2.4k
[WIKI-402] feat: emoji support for all editors #7275
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Pull Request Linked with Plane Work Items
Comment Automatically Generated by Plane |
Caution Review failedThe pull request is closed. WalkthroughThe changes introduce emoji support to the editor by integrating the Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Editor
participant EmojiExtension
participant EmojiSuggestion
participant EmojiList
User->>Editor: Types ":"
Editor->>EmojiExtension: Detects emoji trigger
EmojiExtension->>EmojiSuggestion: Provides suggestion context
EmojiSuggestion->>EmojiList: Renders emoji options in popup
User->>EmojiList: Navigates/selects emoji
EmojiList->>Editor: Inserts selected emoji
Suggested labels
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (3)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (5)
packages/editor/src/core/extensions/utility.ts (1)
15-15
: Fix formatting in type union definition.Missing space after the pipe operator makes the type definition less readable.
-type TActiveDropbarExtensions = CORE_EXTENSIONS.MENTION | CORE_EXTENSIONS.EMOJI |TActiveDropbarExtensionsCE; +type TActiveDropbarExtensions = CORE_EXTENSIONS.MENTION | CORE_EXTENSIONS.EMOJI | TActiveDropbarExtensionsCE;packages/editor/src/core/extensions/slash-commands/command-items-list.tsx (1)
193-203
: Consider improving the emoji command implementation.The command logic inserts
<p>:</p>
which seems like a workaround to trigger emoji suggestion. This approach may create unwanted paragraph elements.Consider using a more direct approach:
-command: ({ editor, range }) => { - editor.chain().focus().insertContentAt(range, "<p>:</p>").run(); -}, +command: ({ editor, range }) => { + editor.chain().focus().deleteRange(range).insertContent(":").run(); +},This would insert just the colon trigger without wrapping it in paragraph tags.
packages/editor/src/core/components/menus/emoji/suggestion.ts (1)
12-24
: Consider optimizing the default emoji filtering logic.The current implementation performs a linear search through all emojis for each default emoji name, which could be inefficient with large emoji datasets.
Consider creating a lookup map for better performance:
+const createEmojiLookup = (emojis: EmojiItem[]) => { + const lookup = new Map<string, EmojiItem>(); + emojis.forEach(emoji => { + emoji.shortcodes.forEach(shortcode => lookup.set(shortcode, emoji)); + lookup.set(emoji.name, emoji); + }); + return lookup; +}; const emojiSuggestion = { items: ({ editor, query }: { editor: Editor; query: string }): EmojiItem[] => { if (query.trim() === "") { + const lookup = createEmojiLookup(editor.storage.emoji.emojis); const defaultEmojis = DEFAULT_EMOJIS - .map((name) => - editor.storage.emoji.emojis.find((emoji: EmojiItem) => - emoji.shortcodes.includes(name) || emoji.name === name - ) - ) + .map((name) => lookup.get(name)) .filter(Boolean) .slice(0, 5);packages/editor/src/core/components/menus/emoji/emoji-list.tsx (2)
120-126
: Good fallback image handling with potential accessibility improvement.The fallback image implementation is solid, but the image could benefit from better alt text.
Consider using more descriptive alt text for the fallback image:
{item.fallbackImage ? ( - <img src={item.fallbackImage} alt={item.name} className="size-4 object-contain" /> + <img src={item.fallbackImage} alt={`${item.name} emoji`} className="size-4 object-contain" /> ) : ( item.emoji )}
107-108
: Consider using stable keys for better React performance.Using array index as key can cause unnecessary re-renders when the items array changes.
If emoji items have unique identifiers, use them as keys:
<button - key={index} + key={item.name} id={`emoji-item-${index}`}Note: Only apply this change if
item.name
is guaranteed to be unique within the filtered results.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (13)
packages/editor/package.json
(1 hunks)packages/editor/src/ce/types/storage.ts
(0 hunks)packages/editor/src/ce/types/utils.ts
(1 hunks)packages/editor/src/core/components/menus/emoji/emoji-list.tsx
(1 hunks)packages/editor/src/core/components/menus/emoji/suggestion.ts
(1 hunks)packages/editor/src/core/constants/extension.ts
(1 hunks)packages/editor/src/core/extensions/enter-key.ts
(1 hunks)packages/editor/src/core/extensions/extensions.ts
(2 hunks)packages/editor/src/core/extensions/mentions/extension-config.ts
(1 hunks)packages/editor/src/core/extensions/mentions/utils.ts
(3 hunks)packages/editor/src/core/extensions/slash-commands/command-items-list.tsx
(2 hunks)packages/editor/src/core/extensions/utility.ts
(3 hunks)packages/editor/src/core/types/editor.ts
(1 hunks)
💤 Files with no reviewable changes (1)
- packages/editor/src/ce/types/storage.ts
🧰 Additional context used
🧬 Code Graph Analysis (3)
packages/editor/src/core/extensions/mentions/utils.ts (1)
packages/editor/src/core/helpers/get-extension-storage.ts (1)
getExtensionStorage
(5-8)
packages/editor/src/core/extensions/enter-key.ts (1)
packages/editor/src/core/helpers/get-extension-storage.ts (1)
getExtensionStorage
(5-8)
packages/editor/src/core/extensions/utility.ts (1)
packages/editor/src/ce/types/utils.ts (1)
TActiveDropbarExtensionsCE
(1-1)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Analyze (javascript)
🔇 Additional comments (19)
packages/editor/package.json (1)
46-46
: ```shell
#!/bin/bashDescription: Extract existing TipTap extension versions from the targeted package.json
echo "TipTap extensions in packages/editor/package.json (dependencies):"
cat packages/editor/package.json | jq -r '
.dependencies
| to_entries
| map(select(.key | startswith("@tiptap/")))
| .[]
| "(.key): (.value)"
'echo -e "\nTipTap extensions in packages/editor/package.json (devDependencies):"
cat packages/editor/package.json | jq -r '
.devDependencies // {}
| to_entries
| map(select(.key | startswith("@tiptap/")))
| .[]
| "(.key): (.value)"
'</details> <details> <summary>packages/editor/src/ce/types/utils.ts (1)</summary> `1-1`: **LGTM! Appropriate use of `never` type for CE placeholder.** The `never` type correctly represents that the Community Edition has no additional active dropbar extensions beyond the core ones. This design allows for extensibility in other editions while maintaining type safety. </details> <details> <summary>packages/editor/src/core/constants/extension.ts (1)</summary> `44-44`: **LGTM! Consistent enum extension for emoji support.** The `EMOJI` constant follows the established naming and value conventions in the enum, properly integrating with the existing extension system. </details> <details> <summary>packages/editor/src/core/types/editor.ts (1)</summary> `50-50`: **LGTM! Emoji command integration looks correct.** The addition of `"emoji"` to the `TEditorCommands` union properly extends the command system to support emoji functionality. The emoji command doesn't require extra props in `TCommandExtraProps`, which is appropriate for this use case. </details> <details> <summary>packages/editor/src/core/extensions/utility.ts (2)</summary> `25-29`: **Excellent centralization of dropbar extension state management.** The new `activeDropbarExtensions` array provides a clean, scalable approach to track multiple active dropbar extensions (mentions, emojis, etc.) in a centralized location, replacing the previous mention-specific boolean flag approach. --- `64-70`: **Proper storage initialization for active dropbar extensions.** The empty array initialization in `addStorage()` correctly establishes the initial state for tracking active dropbar extensions. </details> <details> <summary>packages/editor/src/core/extensions/extensions.ts (2)</summary> `42-43`: **LGTM! Clean emoji extension integration.** The emoji extension import and suggestion handler integration look correct and follow TipTap patterns. --- `102-106`: ```shell #!/bin/bash # Locate and inspect the Emoji extension configuration in extensions.ts grep -n "Emoji.configure" -n packages/editor/src/core/extensions/extensions.ts sed -n '1,200p' packages/editor/src/core/extensions/extensions.ts
packages/editor/src/core/extensions/slash-commands/command-items-list.tsx (1)
17-17
: LGTM! Appropriate icon import for emoji command.The Smile icon from lucide-react is a good choice for the emoji slash command.
packages/editor/src/core/extensions/enter-key.ts (1)
14-20
: LGTM! Improved centralized dropdown state management.The refactor from checking
mentionsOpen
boolean to using the centralizedactiveDropbarExtensions
array is a good architectural improvement. This allows multiple dropdown extensions (mentions, emoji) to be tracked consistently.The logic correctly prevents the Enter key callback when any dropdown is active.
packages/editor/src/core/extensions/mentions/utils.ts (3)
11-12
: LGTM! Appropriate imports for centralized state management.Adding the necessary imports for the refactored dropdown state management.
33-33
: Good: Centralized dropdown state tracking on start.Correctly adds the mention extension to the active dropbar extensions array when the dropdown opens.
69-74
: Good: Proper cleanup of dropdown state on exit.The cleanup logic correctly removes the mention extension from the active array and updates the storage. The filtering approach is safe and maintains array integrity.
packages/editor/src/core/extensions/mentions/extension-config.ts (2)
15-15
: LGTM! Cleaned up extension configuration.Removing the
MentionExtensionStorage
generic parameter is consistent with the refactor to centralized dropdown state management.
50-59
: Good: Simplified storage to core functionality.The storage now focuses only on markdown serialization, which is appropriate after moving dropdown state management to the utility extension.
packages/editor/src/core/components/menus/emoji/suggestion.ts (1)
59-75
: Excellent popup configuration with comprehensive fallback strategy.The popup configuration properly handles multiple possible container targets with appropriate fallbacks, ensuring the emoji list displays correctly in various editor contexts.
packages/editor/src/core/components/menus/emoji/emoji-list.tsx (3)
5-11
: Well-defined TypeScript interfaces with proper fallback support.The interfaces are comprehensive and the
fallbackImage
property provides good fallback handling for emoji rendering.
37-43
: Excellent circular navigation implementation.The modulo arithmetic correctly handles wrapping at both ends of the list, providing smooth keyboard navigation experience.
52-67
: Robust scroll-into-view implementation with performance optimization.The logic properly checks if the item is already in view before scrolling, preventing unnecessary scroll operations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
packages/editor/src/core/extensions/emoji/suggestion.ts (2)
15-20
: Consider more robust emoji filtering with better error handling.The current implementation uses
find()
which returnsundefined
for missing emojis, but thefilter(Boolean)
should handle this. However, consider making the logic more explicit for better maintainability.- const defaultEmojis = DEFAULT_EMOJIS.map((name) => - emojis.find((emoji: EmojiItem) => emoji.shortcodes.includes(name) || emoji.name === name) - ) - .filter(Boolean) - .slice(0, 5); + const defaultEmojis = DEFAULT_EMOJIS + .map((name) => emojis.find((emoji: EmojiItem) => + emoji.shortcodes.includes(name) || emoji.name === name + )) + .filter((emoji): emoji is EmojiItem => emoji !== undefined) + .slice(0, 5);
92-100
: Proper escape key handling but consider consistency.The escape key handling correctly hides the popup and destroys the component, but the order of operations differs from the
onExit
method. Consider using a shared cleanup function.+ const cleanup = () => { + if (popup) { + popup[0]?.hide(); + popup[0]?.destroy(); + } + if (component) { + component.destroy(); + } + }; + onKeyDown: (props: SuggestionKeyDownProps): boolean => { if (props.event.key === "Escape") { - if (popup) { - popup[0]?.hide(); - } - if (component) { - component.destroy(); - } + cleanup(); return true; }packages/editor/src/core/extensions/emoji/emoji-list.tsx (1)
39-52
: Keyboard navigation logic is correct but enterHandler could be simplified.The arrow key handlers use modulo arithmetic for proper wrapping, which is excellent. However, the
enterHandler
implementation is unnecessarily complex.const enterHandler = useCallback(() => { - setSelectedIndex((prevIndex) => { - selectItem(prevIndex); - return prevIndex; - }); + selectItem(selectedIndex); }, [selectItem, selectedIndex]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/editor/src/core/extensions/emoji/emoji-list.tsx
(1 hunks)packages/editor/src/core/extensions/emoji/index.ts
(1 hunks)packages/editor/src/core/extensions/emoji/suggestion.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/editor/src/core/extensions/emoji/index.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Analyze (javascript)
🔇 Additional comments (7)
packages/editor/src/core/extensions/emoji/suggestion.ts (2)
56-72
: Excellent popup configuration with comprehensive fallback strategy.The Tippy.js configuration properly handles different editor containers with a robust fallback chain, and the popup settings are well-configured for user interaction.
105-118
: Proper cleanup implementation with state management.The cleanup logic correctly removes the extension from the active extensions array and destroys both popup and component instances. The array manipulation using
splice
is appropriate here.packages/editor/src/core/extensions/emoji/emoji-list.tsx (5)
5-21
: Well-defined TypeScript interfaces with comprehensive typing.The interfaces properly define the expected data structure and component API. The
EmojiItem
interface includes all necessary properties including the optionalfallbackImage
for graceful degradation.
57-72
: Sophisticated scroll management with proper viewport detection.The
useLayoutEffect
implementation correctly calculates whether the selected item is in view and scrolls appropriately. The use ofuseLayoutEffect
prevents visual flicker during updates.
102-147
: Excellent accessibility implementation with proper ARIA attributes.The component uses appropriate roles (
listbox
,option
), ARIA attributes (aria-label
,aria-selected
), and semantic HTML. The fallback image handling and keyboard/mouse interaction support are well implemented.
130-136
: Robust emoji rendering with fallback image support.The conditional rendering handles both native emoji display and fallback images gracefully, ensuring emojis are visible across different platforms and environments.
88-94
: ```shell
#!/bin/bashSearch for other Enter key handlers in the editor codebase, including .ts and .tsx files
rg -A5 -B5 'event.key.Enter|keyCode.13' -g '.ts' -g '.tsx'
</details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
packages/editor/src/core/extensions/emoji/components/emojis-list.tsx (3)
16-16
: Remove unused editor prop.The
editor
prop is defined in the interface but never used within the component. Consider removing it to keep the interface clean.export interface EmojiListProps { items: EmojiItem[]; command: (item: { name: string }) => void; - editor: Editor; }
47-52
: Simplify enterHandler logic.The
enterHandler
unnecessarily wrapsselectItem
insetSelectedIndex
. SinceselectItem
doesn't modify the selected index, this wrapper adds no value.const enterHandler = useCallback(() => { - setSelectedIndex((prevIndex) => { - selectItem(prevIndex); - return prevIndex; - }); + selectItem(selectedIndex); }, [selectItem]);
111-111
: Consider key uniqueness for emoji items.Using
item.shortcodes.join(" - ")
as the key might not guarantee uniqueness if multiple emojis share identical shortcodes. Consider using the emoji name or a combination of name and emoji.- const emojiKey = item.shortcodes.join(" - "); + const emojiKey = `${item.name}-${item.emoji}`;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
packages/editor/src/core/extensions/core-without-props.ts
(2 hunks)packages/editor/src/core/extensions/emoji/components/emojis-list.tsx
(1 hunks)packages/editor/src/core/extensions/emoji/extension.ts
(1 hunks)packages/editor/src/core/extensions/emoji/suggestion.ts
(1 hunks)packages/editor/src/core/extensions/extensions.ts
(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/editor/src/core/extensions/emoji/extension.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/editor/src/core/extensions/core-without-props.ts
- packages/editor/src/core/extensions/extensions.ts
- packages/editor/src/core/extensions/emoji/suggestion.ts
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
packages/editor/src/core/extensions/emoji/components/emojis-list.tsx (3)
57-72
: Excellent scrolling implementation!The scroll-into-view logic properly handles viewport bounds checking and uses
scrollIntoView
withblock: "nearest"
for smooth navigation. The implementation correctly ensures selected items remain visible during keyboard navigation.
74-100
: Well-implemented keyboard navigation.The imperative handle correctly processes keyboard events with proper event handling (preventDefault/stopPropagation for Enter) and returns boolean values to indicate event consumption. The arrow key navigation provides intuitive UX.
102-146
: Strong accessibility and interaction support.The component implements proper ARIA roles (
listbox
/option
), labels, and selection states. The mouse and keyboard interactions work harmoniously, and the empty state is handled gracefully.
0290c78
to
f797f62
Compare
Description
Adds emoji extension with dropdown suggestions and slash command integration. Users can now input emojis using : prefix or via slash commands.
Type of Change
Screenshots and Media (if applicable)
Screen.Recording.2025-06-26.at.6.32.56.PM.mov
Test Scenarios
References
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Other