Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion site/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
import "./.next/dev/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
38 changes: 19 additions & 19 deletions site/src/components/reactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,36 @@ import {
ReactionsList,
} from "./reactions.client";

const liveblocks = new LiveblocksClient({
secret: process.env.LIVEBLOCKS_SECRET_KEY!,
});
// const liveblocks = new LiveblocksClient({
// secret: process.env.LIVEBLOCKS_SECRET_KEY!,
// });

async function ServerReactions() {
"use cache";
// async function ServerReactions() {
// "use cache";

cacheLife("seconds");
// cacheLife("seconds");

let reactions: ReactionsJson;
// let reactions: ReactionsJson;

try {
reactions = (await liveblocks.getStorageDocument(ROOM_ID, "json"))
.reactions;
} catch {
reactions = DEFAULT_REACTIONS;
}
// try {
// reactions = (await liveblocks.getStorageDocument(ROOM_ID, "json"))
// .reactions;
// } catch {
// reactions = DEFAULT_REACTIONS;
// }

if (!reactions || Object.keys(reactions).length === 0) {
reactions = DEFAULT_REACTIONS;
}
// if (!reactions || Object.keys(reactions).length === 0) {
// reactions = DEFAULT_REACTIONS;
// }

return <ClientReactions roomId={ROOM_ID} serverReactions={reactions} />;
}
// return <ClientReactions roomId={ROOM_ID} serverReactions={reactions} />;
// }
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Will uncomment all of this before squashing for merge


export function Reactions(props: Omit<ComponentProps<"div">, "children">) {
return (
<ReactionsList {...props}>
<Suspense fallback={<FallbackReactions />}>
<ServerReactions />
{/* <ServerReactions /> */}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Commented-out code accidentally committed for testing

The liveblocks client initialization and ServerReactions component are commented out, and the component usage in the Suspense block is replaced with a comment. The PR description explicitly mentions this was done for local testing ("Currently have to comment out the <ServerReactions /> component and LiveblocksClient() to test things"). With the actual component commented out, the Suspense children render as nothing, so users will see no content where the reactions component should appear.

Fix in Cursor Fix in Web

</Suspense>
</ReactionsList>
);
Expand Down
11 changes: 11 additions & 0 deletions site/src/examples/usage/usage.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ function EmojiPicker({ className, columns, ...props }: EmojiPickerRootProps) {
{...props}
>
<EmojiPickerPrimitive.Search className="focusable z-10 mx-2 mt-2 appearance-none rounded-md bg-neutral-100 px-2.5 py-2 text-sm dark:bg-neutral-800" />
<EmojiPickerPrimitive.CategoryNav>
{({ categories }) => (
<div className="mx-2 mt-2 px-1 flex gap-2 overflow-x-auto max-w-[38ch] text-xs">
{categories.map(({ category, scrollTo }) => (
<button key={category.label} type="button" onClick={scrollTo} className="bg-neutral-100 hover:bg-neutral-200 dark:bg-neutral-800 hover:dark:bg-neutral-700 px-2 rounded-sm flex items-center leading-6 text-nowrap">
{category.label}
</button>
))}
</div>
)}
</EmojiPickerPrimitive.CategoryNav>
<EmojiPickerPrimitive.Viewport className="scrollbar-track-[transparent] scrollbar-thumb-neutral-500/30 dark:scrollbar-thumb-neutral-400/30 relative flex-1 outline-hidden">
<EmojiPickerPrimitive.Loading className="absolute inset-0 flex items-center justify-center text-neutral-400 text-sm dark:text-neutral-500">
Loading…
Expand Down
13 changes: 12 additions & 1 deletion site/src/examples/usage/usage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,24 @@ export function Usage({
children: (
<CodeBlock className="absolute inset-0 rounded-none" lang="tsx">{`
"use client";

import { EmojiPicker } from "frimousse";

export function MyEmojiPicker() {
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" />
<EmojiPickerPrimitive.CategoryNav>
{({ categories }) => (
<div>
{categories.map(({ category, scrollTo }) => (
<button key={category.label} onClick={scrollTo}>
{category.label}
</button>
))}
</div>
)}
</EmojiPickerPrimitive.CategoryNav>
<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…
Expand Down
61 changes: 61 additions & 0 deletions src/components/emoji-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import type {
EmojiData,
EmojiPickerActiveEmojiProps,
EmojiPickerCategory,
EmojiPickerCategoryNavProps,
EmojiPickerDataCategory,
EmojiPickerEmoji,
EmojiPickerEmptyProps,
Expand Down Expand Up @@ -1452,6 +1453,64 @@ function EmojiPickerSkinTone({ children, emoji }: EmojiPickerSkinToneProps) {
return children({ skinTone, setSkinTone, skinToneVariations });
}

/**
* Exposes the emoji categories and provides scroll handlers to navigate
* to each category via a render callback.
*
* @example
* ```tsx
* <EmojiPicker.CategoryNav>
* {({ categories }) => (
* <div>
* {categories.map(({ category, scrollTo }) => (
* <button key={category.label} onClick={scrollTo}>
* {category.label}
* </button>
* ))}
* </div>
* )}
* </EmojiPicker.CategoryNav>
* ```
*
* This component allows building custom category navigation that can scroll
* to the corresponding section in the emoji list.
*/
function EmojiPickerCategoryNav({
children,
}: EmojiPickerCategoryNavProps) {
const store = useEmojiPickerStore();
const data = useSelectorKey(store, "data");
const viewportRef = useSelectorKey(store, "viewportRef");
const rowHeight = useSelectorKey(store, "rowHeight");
const categoryHeaderHeight = useSelectorKey(store, "categoryHeaderHeight");

const categories = useMemo(() => {
if (!data?.categories || !viewportRef || !rowHeight || !categoryHeaderHeight) {
return [];
}

return data.categories.map((category, index) => ({
category: { label: category.label },
scrollTo: () => {
const viewport = viewportRef.current;

if (!viewport) {
return;
}

const scrollTop =
index * categoryHeaderHeight + category.startRowIndex * rowHeight;

viewport.scrollTo({
top: scrollTop,
});
},
}));
}, [data?.categories, viewportRef, rowHeight, categoryHeaderHeight]);

return children({ categories });
}

EmojiPickerRoot.displayName = "EmojiPicker.Root";
EmojiPickerSearch.displayName = "EmojiPicker.Search";
EmojiPickerViewport.displayName = "EmojiPicker.Viewport";
Expand All @@ -1461,10 +1520,12 @@ EmojiPickerEmpty.displayName = "EmojiPicker.Empty";
EmojiPickerSkinToneSelector.displayName = "EmojiPicker.SkinToneSelector";
EmojiPickerActiveEmoji.displayName = "EmojiPicker.ActiveEmoji";
EmojiPickerSkinTone.displayName = "EmojiPicker.SkinTone";
EmojiPickerCategoryNav.displayName = "EmojiPicker.CategoryNav";

export {
EmojiPickerRoot as Root, // <EmojiPicker.Root />
EmojiPickerSearch as Search, // <EmojiPicker.Search />
EmojiPickerCategoryNav as CategoryNav, // <EmojiPicker.CategoryNav />
EmojiPickerViewport as Viewport, // <EmojiPicker.Viewport />
EmojiPickerList as List, // <EmojiPicker.List />
EmojiPickerLoading as Loading, // <EmojiPicker.Loading />
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type {
Category,
Emoji,
EmojiPickerActiveEmojiProps,
EmojiPickerCategoryNavProps,
EmojiPickerEmptyProps,
EmojiPickerListCategoryHeaderProps,
EmojiPickerListComponents,
Expand Down
25 changes: 25 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,28 @@ export type EmojiPickerSkinToneProps = {
*/
children: (props: EmojiPickerSkinToneRenderProps) => ReactNode;
};

export type EmojiPickerCategoryNavRenderProps = {
/**
* An array of categories with their labels and scroll handlers.
*/
categories: {
/**
* The category information.
*/
category: Category;

/**
* A function to scroll the viewport to this category.
*/
scrollTo: () => void;
}[];
};

export type EmojiPickerCategoryNavProps = {
/**
* A render callback which receives an array of categories with their
* labels and scroll handlers.
*/
children: (props: EmojiPickerCategoryNavRenderProps) => ReactNode;
};