fix: emoji picker

This commit is contained in:
Tristan Yang
2024-03-19 11:21:16 +08:00
parent 86bc9b8c29
commit fafd367bfb
7 changed files with 488 additions and 490 deletions
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "vocechat-web",
"version": "0.7.3",
"version": "0.7.4",
"homepage": "https://voce.chat",
"dependencies": {
"@metamask/onboarding": "^1.0.1",
@@ -75,7 +75,7 @@
"slate-react": "0.102.0",
"source-map-loader": "^5.0.0",
"style-loader": "^3.3.4",
"tailwind-merge": "2.2.1",
"tailwind-merge": "2.2.2",
"terser-webpack-plugin": "^5.3.10",
"tippy.js": "^6.3.7",
"typescript": "^5.4.2",
@@ -116,15 +116,15 @@
},
"devDependencies": {
"@babel/core": "^7.24.0",
"@ianvs/prettier-plugin-sort-imports": "^4.2.0",
"@ianvs/prettier-plugin-sort-imports": "^4.2.1",
"@types/masonry-layout": "^4.2.7",
"@types/node": "^20.11.28",
"@types/react": "18.2.66",
"@types/node": "^20.11.29",
"@types/react": "18.2.67",
"@types/react-dom": "^18.2.22",
"@types/react-helmet": "^6.1.11",
"@types/react-syntax-highlighter": "^15.5.11",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"autoprefixer": "^10.4.18",
"babel-loader": "^9.1.3",
"babel-plugin-named-asset-import": "^0.3.8",
@@ -137,7 +137,7 @@
"gh-pages": "^6.1.1",
"md5-file": "^5.0.0",
"patch-package": "^8.0.0",
"postcss": "^8.4.35",
"postcss": "^8.4.36",
"postcss-loader": "^8.1.1",
"postinstall-postinstall": "^2.1.0",
"prettier": "^3.2.5",
+381 -376
View File
File diff suppressed because it is too large Load Diff
@@ -1,24 +1,19 @@
import React, { memo, useCallback } from 'react';
import {
Emoji,
EmojiSettings,
GridRow,
UseEmojiPickerType,
} from '@udecode/plate-emoji';
import { memo, useCallback } from "react";
import { Emoji, EmojiSettings, GridRow, UseEmojiPickerType } from "@udecode/plate-emoji";
import { cn } from '@/utils';
import { cn } from "@/utils";
export type EmojiPickerContentProps = Pick<
UseEmojiPickerType,
| 'i18n'
| 'onMouseOver'
| 'onSelectEmoji'
| 'emojiLibrary'
| 'isSearching'
| 'searchResult'
| 'visibleCategories'
| 'refs'
| 'settings'
| "i18n"
| "onMouseOver"
| "onSelectEmoji"
| "emojiLibrary"
| "isSearching"
| "searchResult"
| "visibleCategories"
| "refs"
| "settings"
>;
export type EmojiButtonProps = {
@@ -30,36 +25,34 @@ export type EmojiButtonProps = {
export type RowOfButtonsProps = Pick<
UseEmojiPickerType,
'onMouseOver' | 'onSelectEmoji' | 'emojiLibrary'
"onMouseOver" | "onSelectEmoji" | "emojiLibrary"
> & {
row: GridRow;
};
const Button = memo(
({ index, emoji, onSelect, onMouseOver }: EmojiButtonProps) => {
return (
<button
type="button"
aria-label={emoji.skins[0].native}
tabIndex={-1}
data-index={index}
onClick={() => onSelect(emoji)}
onMouseEnter={() => onMouseOver(emoji)}
onMouseLeave={() => onMouseOver()}
className="group relative flex h-[36px] w-[36px] cursor-pointer items-center justify-center border-none bg-transparent text-2xl leading-none"
>
<div
aria-hidden="true"
className="absolute inset-0 rounded-full bg-[rgba(0,0,0,0.05)] opacity-0 group-hover:opacity-100"
/>
<span data-emoji-set="native" style={{ position: 'relative' }}>
{emoji.skins[0].native}
</span>
</button>
);
}
);
Button.displayName = 'Button';
const Button = memo(({ index, emoji, onSelect, onMouseOver }: EmojiButtonProps) => {
return (
<button
type="button"
aria-label={emoji.skins[0].native}
tabIndex={-1}
data-index={index}
onClick={() => onSelect(emoji)}
onMouseEnter={() => onMouseOver(emoji)}
onMouseLeave={() => onMouseOver()}
className="group relative flex h-[36px] w-[36px] cursor-pointer items-center justify-center border-none bg-transparent text-2xl leading-none"
>
<div
aria-hidden="true"
className="absolute inset-0 rounded-full bg-[rgba(0,0,0,0.05)] opacity-0 group-hover:opacity-100"
/>
<span data-emoji-set="native" style={{ position: "relative" }}>
{emoji.skins[0].native}
</span>
</button>
);
});
Button.displayName = "Button";
const RowOfButtons = memo(
({ row, emojiLibrary, onSelectEmoji, onMouseOver }: RowOfButtonsProps) => (
@@ -76,7 +69,7 @@ const RowOfButtons = memo(
</div>
)
);
RowOfButtons.displayName = 'RowOfButtons';
RowOfButtons.displayName = "RowOfButtons";
export function EmojiPickerContent({
i18n,
@@ -87,15 +80,13 @@ export function EmojiPickerContent({
searchResult,
visibleCategories,
refs,
settings = EmojiSettings,
settings = EmojiSettings
}: EmojiPickerContentProps) {
const getRowWidth = settings.perLine.value * settings.buttonSize.value;
const isCategoryVisible = useCallback(
(categoryId: any) => {
return visibleCategories.has(categoryId)
? visibleCategories.get(categoryId)
: false;
return visibleCategories.has(categoryId) ? visibleCategories.get(categoryId) : false;
},
[visibleCategories]
);
@@ -145,7 +136,7 @@ export function EmojiPickerContent({
isCategoryVisible,
onSelectEmoji,
onMouseOver,
settings,
settings
]);
const SearchList = useCallback(() => {
@@ -167,24 +158,17 @@ export function EmojiPickerContent({
</div>
</div>
);
}, [
emojiLibrary,
getRowWidth,
i18n.searchResult,
searchResult,
onSelectEmoji,
onMouseOver,
]);
}, [emojiLibrary, getRowWidth, i18n.searchResult, searchResult, onSelectEmoji, onMouseOver]);
return (
<div
className={cn(
'h-full min-h-[50%] overflow-y-auto overflow-x-hidden px-3',
'[&::-webkit-scrollbar]:w-4',
'[&::-webkit-scrollbar-button]:hidden [&::-webkit-scrollbar-button]:h-0 [&::-webkit-scrollbar-button]:w-0',
':hover:[&::-webkit-scrollbar-thumb]:bg-[#f3f4f6]',
'[&::-webkit-scrollbar-thumb]:min-h-[65px] [&::-webkit-scrollbar-thumb]:rounded-2xl [&::-webkit-scrollbar-thumb]:border-4 [&::-webkit-scrollbar-thumb]:border-white',
'[&::-webkit-scrollbar-track]:border-0'
"h-full min-h-[50%] overflow-y-auto overflow-x-hidden px-3",
"[&::-webkit-scrollbar]:w-4",
"[&::-webkit-scrollbar-button]:hidden [&::-webkit-scrollbar-button]:h-0 [&::-webkit-scrollbar-button]:w-0",
":hover:[&::-webkit-scrollbar-thumb]:bg-[#f3f4f6]",
"[&::-webkit-scrollbar-thumb]:min-h-[65px] [&::-webkit-scrollbar-thumb]:rounded-2xl [&::-webkit-scrollbar-thumb]:border-4 [&::-webkit-scrollbar-thumb]:border-white",
"[&::-webkit-scrollbar-track]:border-0"
)}
data-id="scroll"
ref={refs.current.contentRoot}
@@ -1,26 +1,22 @@
import React from 'react';
import { UseEmojiPickerType } from '@udecode/plate-emoji';
import IconSearch from '@/assets/icons/search.svg'
import IconClose from '@/assets/icons/close.circle.svg'
import { cn } from '@/utils';
import React from "react";
import { UseEmojiPickerType } from "@udecode/plate-emoji";
import IconSearch from "@/assets/icons/search.svg";
import IconClose from "@/assets/icons/close.circle.svg";
import { cn } from "@/utils";
export type EmojiPickerSearchAndClearProps = Pick<
UseEmojiPickerType,
'i18n' | 'searchValue' | 'clearSearch'
"i18n" | "searchValue" | "clearSearch"
>;
export function EmojiPickerSearchAndClear({
i18n,
searchValue,
clearSearch,
clearSearch
}: EmojiPickerSearchAndClearProps) {
return (
<>
<span
className={cn(
'absolute left-2 top-1/2 z-10 flex h-5 w-5 -translate-y-1/2'
)}
>
<span className={cn("absolute left-2 top-1/2 z-10 flex h-5 w-5 -translate-y-1/2")}>
<IconSearch className="w-full h-full" />
</span>
{searchValue && (
@@ -29,7 +25,7 @@ export function EmojiPickerSearchAndClear({
aria-label="Clear"
type="button"
className={cn(
'absolute right-0 top-1/2 flex h-8 w-8 -translate-y-1/2 cursor-pointer border-none bg-transparent justify-center items-center'
"absolute right-0 top-1/2 flex h-8 w-8 -translate-y-1/2 cursor-pointer border-none bg-transparent justify-center items-center"
)}
onClick={clearSearch}
>
@@ -1,5 +1,5 @@
import React, { ReactNode } from 'react';
import * as Popover from '@radix-ui/react-popover';
import { ReactNode } from "react";
import * as Popover from "@radix-ui/react-popover";
type EmojiPopupProps = {
control: ReactNode;
@@ -8,16 +8,10 @@ type EmojiPopupProps = {
children: ReactNode;
};
export function EmojiPopup({
control,
isOpen,
setIsOpen,
children,
}: EmojiPopupProps) {
export function EmojiPopup({ control, isOpen, setIsOpen, children }: EmojiPopupProps) {
return (
<Popover.Root open={isOpen} onOpenChange={setIsOpen}>
<Popover.Trigger asChild>{control}</Popover.Trigger>
<Popover.Portal>
<Popover.Content
align="start"
@@ -4,40 +4,55 @@ import { EmojiPopup } from "./emoji-popup";
import { emojiCategoryIcons, emojiSearchIcons } from "./emoji-icons";
import { EmojiPicker } from "./emoji-picker";
import { Plate } from "@udecode/plate-common";
type EmojiDropdownMenuProps = {
context?: "markdown" | "plate";
options?: EmojiDropdownMenuOptions;
} & {
onSelectEmoji?: (emoji: Emoji) => void;
};
export function EmojiInputPicker({ options, onSelectEmoji, ...props }: EmojiDropdownMenuProps) {
const PickerWrapper = (props: EmojiDropdownMenuProps) => {
const { options, onSelectEmoji, ...others } = props;
const { isOpen, setIsOpen, emojiPickerState } = useEmojiDropdownMenuState(options);
const handleClick = () => {
setIsOpen(!isOpen);
};
const rest = onSelectEmoji ? { onSelectEmoji } : {};
return (
<EmojiPopup
control={
<button className="relative h-6 w-6" onClick={handleClick} {...props}>
<IconSmile className="w-full h-full" />
</button>
}
isOpen={isOpen}
setIsOpen={setIsOpen}
>
<EmojiPicker
{...emojiPickerState}
<Plate key={"just_for_emoji_picker"}>
<EmojiPopup
control={
<button className="relative h-6 w-6" onClick={handleClick} {...others}>
<IconSmile className="w-full h-full" />
</button>
}
isOpen={isOpen}
setIsOpen={setIsOpen}
icons={{
categories: emojiCategoryIcons,
search: emojiSearchIcons
}}
settings={options?.settings}
{...rest}
/>
</EmojiPopup>
>
<EmojiPicker
{...emojiPickerState}
isOpen={isOpen}
setIsOpen={setIsOpen}
icons={{
categories: emojiCategoryIcons,
search: emojiSearchIcons
}}
settings={options?.settings}
{...rest}
/>
</EmojiPopup>
</Plate>
);
};
export function EmojiInputPicker({ context, ...props }: EmojiDropdownMenuProps) {
if (context === "markdown")
return (
<Plate key={"just_for_emoji_picker"}>
<PickerWrapper {...props} />
</Plate>
);
return <PickerWrapper {...props} />;
}
+5 -1
View File
@@ -176,7 +176,11 @@ const Send: FC<IProps> = ({
)}
>
{mode == Modes.markdown && (
<EmojiInputPicker options={{ closeOnSelect: false }} onSelectEmoji={insertEmoji} />
<EmojiInputPicker
context="markdown"
options={{ closeOnSelect: false }}
onSelectEmoji={insertEmoji}
/>
)}
{mode == Modes.text && (
<MessageInput