-
Notifications
You must be signed in to change notification settings - Fork 36
Open
Description
the picker is stuck on loading for a long time, i.e. >1 minute
eventually works
my code
import { EmojiPicker } from "frimousse";
import { createPortal } from "preact/compat";
import { useEffect, useRef, useState } from "preact/hooks";
import { Button } from "./button.tsx";
const MyEmojiPicker = (
{ onEmojiSelect }: { onEmojiSelect: (emoji: string) => void },
) => {
return (
<EmojiPicker.Root className="isolate flex h-[368px] w-fit flex-col bg-white dark:bg-neutral-900">
<EmojiPicker.Search className="z-10 mx-2 mt-2 appearance-none rounded-md bg-neutral-100 px-2.5 py-2 text-sm dark:bg-neutral-800" />
<EmojiPicker.Viewport className="relative flex-1 outline-hidden">
<EmojiPicker.Loading className="absolute inset-0 flex items-center justify-center text-neutral-400 text-sm dark:text-neutral-500">
Loading…
</EmojiPicker.Loading>
<EmojiPicker.Empty className="absolute inset-0 flex items-center justify-center text-neutral-400 text-sm dark:text-neutral-500">
No emoji found.
</EmojiPicker.Empty>
<EmojiPicker.List
className="select-none pb-1.5"
components={{
// @ts-ignore typing
CategoryHeader: ({ category, ...props }) => (
<div
className="bg-white px-3 pt-3 pb-1.5 font-medium text-neutral-600 text-xs dark:bg-neutral-900 dark:text-neutral-400"
{...props}
>
{category.label}
</div>
),
// @ts-ignore typing
Row: ({ children, ...props }) => (
<div className="scroll-my-1.5 px-1.5" {...props}>
{children}
</div>
),
// @ts-ignore typing
Emoji: ({ emoji, ...props }) => (
<button
className="flex size-8 items-center justify-center rounded-md text-lg data-[active]:bg-neutral-100 dark:data-[active]:bg-neutral-800"
{...props}
onClick={() => onEmojiSelect(emoji.emoji)}
>
{emoji.emoji}
</button>
),
}}
/>
</EmojiPicker.Viewport>
</EmojiPicker.Root>
);
};
export const EmojiPickerButton = (
{ textareaRef }: { textareaRef: React.RefObject<HTMLTextAreaElement> },
) => {
const [open, setOpen] = useState(false);
const pickerRef = useRef<HTMLDivElement>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [pickerPos, setPickerPos] = useState<
{ left: number; top: number } | null
>(null);
useEffect(() => {
if (!open) return;
if (buttonRef.current) {
const rect = buttonRef.current.getBoundingClientRect();
setPickerPos({
left: rect.left + globalThis.scrollX,
top: rect.top + globalThis.scrollY - 368,
});
}
const handler = (e: MouseEvent) => {
if (
pickerRef.current &&
!pickerRef.current.contains(e.target as Node) &&
buttonRef.current &&
!buttonRef.current.contains(e.target as Node)
) {
setOpen(false);
}
};
document.addEventListener("mousedown", handler);
return () => document.removeEventListener("mousedown", handler);
}, [open]);
// Helper to insert emoji at cursor position
function handleEmojiSelect(emoji: string) {
const textarea = textareaRef.current;
if (!textarea) return;
const start = textarea.selectionStart ?? 0;
const end = textarea.selectionEnd ?? 0;
const value = textarea.value;
const newValue = value.slice(0, start) + emoji + value.slice(end);
textarea.value = newValue;
// Move cursor after inserted emoji
const cursor = start + emoji.length;
textarea.selectionStart = textarea.selectionEnd = cursor;
// Focus textarea
textarea.focus();
setOpen(false);
// Optionally, trigger input event if parent needs to know
const event = new Event("input", { bubbles: true });
textarea.dispatchEvent(event);
}
return (
<div
style={{
position: "relative",
display: "flex",
alignItems: "center",
marginLeft: 0,
marginRight: 0,
height: 44,
}}
>
<Button
ref={buttonRef}
aria-label="Pick emoji"
style={{
height: 44,
width: 44,
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 28,
lineHeight: 1,
background: "var(--surface-alt)",
border: "1.5px solid var(--border)",
borderRadius: 0,
cursor: "pointer",
marginRight: 0,
marginLeft: 0,
padding: 0,
boxSizing: "border-box",
}}
onClick={() => {
setOpen((v) => !v);
}}
>
<span style={{ display: "block", lineHeight: 1 }}>😊</span>
</Button>
{open && pickerPos && typeof window !== "undefined" && createPortal(
<div
ref={pickerRef}
style={{
position: "absolute",
left: pickerPos.left,
top: pickerPos.top,
zIndex: 1000,
}}
>
<MyEmojiPicker onEmojiSelect={handleEmojiSelect} />
</div>,
globalThis.document.body,
)}
</div>
);
};Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels