infoalloggi-monorepo/apps/infoalloggi/src/components/custom_ui/emoji-picker.tsx

235 lines
5.9 KiB
TypeScript

"use client";
// import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
import {
type EmojiPickerListCategoryHeaderProps,
type EmojiPickerListEmojiProps,
type EmojiPickerListRowProps,
EmojiPicker as EmojiPickerPrimitive,
// type SkinTone,
// useActiveEmoji,
// useSkinTone,
} from "frimousse";
import { LoaderIcon, SearchIcon } from "lucide-react";
import type * as React from "react";
// import { Button } from "~/components/ui/button";
// import {
// Popover,
// PopoverContent,
// PopoverTrigger,
// } from "~/components/ui/popover";
import { cn } from "~/lib/utils";
// from https://ui-x.junwen-k.dev/docs/components/emoji-picker
function EmojiPicker({
className,
...props
}: React.ComponentProps<typeof EmojiPickerPrimitive.Root>) {
return (
<EmojiPickerPrimitive.Root
className={cn(
"isolate flex h-full w-fit flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
className,
)}
data-slot="emoji-picker"
{...props}
/>
);
}
function EmojiPickerSearch({
className,
...props
}: React.ComponentProps<typeof EmojiPickerPrimitive.Search>) {
return (
<div
className={cn("flex h-10 items-center gap-2 border-b px-3", className)}
data-slot="emoji-picker-search-wrapper"
>
<SearchIcon className="size-4 shrink-0 opacity-50" />
<EmojiPickerPrimitive.Search
className="flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden placeholder:text-muted-foreground focus:border-accent-foreground focus:ring-0 disabled:cursor-not-allowed disabled:opacity-50"
data-slot="emoji-picker-search"
{...props}
/>
</div>
);
}
function EmojiPickerRow({ children, ...props }: EmojiPickerListRowProps) {
return (
<div className="scroll-my-1 px-1" data-slot="emoji-picker-row" {...props}>
{children}
</div>
);
}
function EmojiPickerEmoji({
emoji,
className,
...props
}: EmojiPickerListEmojiProps) {
return (
<button
className={cn(
"flex size-7 items-center justify-center rounded-xs text-base data-[active]:bg-accent",
className,
)}
data-slot="emoji-picker-emoji"
{...props}
>
{emoji.emoji}
</button>
);
}
function EmojiPickerCategoryHeader({
category,
...props
}: EmojiPickerListCategoryHeaderProps) {
return (
<div
className="bg-popover px-3 pt-3.5 pb-2 text-muted-foreground text-xs leading-none"
data-slot="emoji-picker-category-header"
{...props}
>
{category.label}
</div>
);
}
function EmojiPickerContent({
className,
...props
}: React.ComponentProps<typeof EmojiPickerPrimitive.Viewport>) {
return (
<EmojiPickerPrimitive.Viewport
className={cn("relative flex-1 outline-hidden", className)}
data-slot="emoji-picker-viewport"
{...props}
>
<EmojiPickerPrimitive.Loading
className="absolute inset-0 flex items-center justify-center text-muted-foreground"
data-slot="emoji-picker-loading"
>
<LoaderIcon className="size-4 animate-spin" />
</EmojiPickerPrimitive.Loading>
<EmojiPickerPrimitive.Empty
className="absolute inset-0 flex items-center justify-center text-muted-foreground text-sm"
data-slot="emoji-picker-empty"
>
No emoji found.
</EmojiPickerPrimitive.Empty>
<EmojiPickerPrimitive.List
className="select-none pb-1"
components={{
CategoryHeader: EmojiPickerCategoryHeader,
Emoji: EmojiPickerEmoji,
Row: EmojiPickerRow,
}}
data-slot="emoji-picker-list"
/>
</EmojiPickerPrimitive.Viewport>
);
}
/*function EmojiPickerFooter({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="emoji-picker-footer"
className={cn(
"flex w-full max-w-(--frimousse-viewport-width) min-w-0 items-center justify-between gap-1 border-t p-2",
className,
)}
{...props}
>
<EmojiPickerActiveEmojiPreview />
<EmojiPickerSkinToneSelector />
</div>
);
}
function EmojiPickerActiveEmojiPreview({
className,
...props
}: React.ComponentProps<"div">) {
const emoji = useActiveEmoji();
if (!emoji) {
return (
<div
className="text-muted-foreground ml-1.5 flex h-7 items-center truncate text-xs"
{...props}
>
Select an emoji…
</div>
);
}
return (
<div className={cn("flex items-center gap-1", className)} {...props}>
<div className="flex items-center gap-1">
<div className="flex size-7 flex-none items-center justify-center text-lg">
{emoji.emoji}
</div>
<span className="text-secondary-foreground truncate text-xs">
{emoji.label}
</span>
</div>
</div>
);
}
function EmojiPickerSkinToneSelector() {
const [skinTone, setSkinTone, skinToneVariations] = useSkinTone();
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" size="icon" className="size-7">
{
skinToneVariations.find(
(variation) => variation.skinTone === skinTone,
)?.emoji
}
</Button>
</PopoverTrigger>
<PopoverContent
side="left"
align="center"
className="w-fit overflow-hidden p-0"
>
<ToggleGroupPrimitive.Root
type="single"
value={skinTone}
onValueChange={(value) => value && setSkinTone(value as SkinTone)}
aria-label="Select skin tone"
orientation="horizontal"
>
{skinToneVariations.map((variation) => (
<ToggleGroupPrimitive.Item
key={variation.skinTone}
aria-label={`${variation.skinTone} skin tone`}
value={variation.skinTone}
className="data-[state=on]:bg-accent data-[state=on]:text-accent-foreground hover:bg-muted focus-visible:ring-ring size-7"
>
{variation.emoji}
</ToggleGroupPrimitive.Item>
))}
</ToggleGroupPrimitive.Root>
</PopoverContent>
</Popover>
);
}
*/
export {
EmojiPicker,
EmojiPickerSearch,
EmojiPickerContent,
//EmojiPickerFooter,
};