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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions