Skip to content

stuck on loading for a long time #13

@uriva

Description

@uriva

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>
  );
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions