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