2025-04-11 00:33:51 +03:00
|
|
|
import {
|
|
|
|
|
Drawer,
|
|
|
|
|
DrawerClose,
|
|
|
|
|
DrawerContent,
|
|
|
|
|
DrawerDescription,
|
|
|
|
|
DrawerHeader,
|
|
|
|
|
DrawerTitle,
|
|
|
|
|
DrawerTrigger,
|
|
|
|
|
} from "@/components/ui/drawer";
|
|
|
|
|
import {
|
|
|
|
|
DropdownMenu,
|
|
|
|
|
DropdownMenuContent,
|
|
|
|
|
DropdownMenuItem,
|
|
|
|
|
DropdownMenuLabel,
|
2025-11-17 17:12:05 +03:00
|
|
|
DropdownMenuSeparator,
|
2025-04-11 00:33:51 +03:00
|
|
|
DropdownMenuTrigger,
|
|
|
|
|
} from "@/components/ui/dropdown-menu";
|
|
|
|
|
import {
|
|
|
|
|
Tooltip,
|
|
|
|
|
TooltipContent,
|
|
|
|
|
TooltipTrigger,
|
|
|
|
|
} from "@/components/ui/tooltip";
|
|
|
|
|
import { isDesktop, isMobile } from "react-device-detect";
|
|
|
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
|
import React, { ReactNode, useMemo, useState } from "react";
|
|
|
|
|
import TextEntryDialog from "./dialog/TextEntryDialog";
|
|
|
|
|
import { Button } from "../ui/button";
|
|
|
|
|
|
|
|
|
|
type FaceSelectionDialogProps = {
|
|
|
|
|
className?: string;
|
|
|
|
|
faceNames: string[];
|
|
|
|
|
onTrainAttempt: (name: string) => void;
|
|
|
|
|
children: ReactNode;
|
|
|
|
|
};
|
|
|
|
|
export default function FaceSelectionDialog({
|
|
|
|
|
className,
|
|
|
|
|
faceNames,
|
|
|
|
|
onTrainAttempt,
|
|
|
|
|
children,
|
|
|
|
|
}: FaceSelectionDialogProps) {
|
|
|
|
|
const { t } = useTranslation(["views/faceLibrary"]);
|
|
|
|
|
|
|
|
|
|
const isChildButton = useMemo(
|
|
|
|
|
() => React.isValidElement(children) && children.type === Button,
|
|
|
|
|
[children],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// control
|
|
|
|
|
const [newFace, setNewFace] = useState(false);
|
|
|
|
|
|
|
|
|
|
// components
|
|
|
|
|
const Selector = isDesktop ? DropdownMenu : Drawer;
|
|
|
|
|
const SelectorTrigger = isDesktop ? DropdownMenuTrigger : DrawerTrigger;
|
|
|
|
|
const SelectorContent = isDesktop ? DropdownMenuContent : DrawerContent;
|
2025-05-22 18:38:14 +03:00
|
|
|
const SelectorItem = isDesktop
|
|
|
|
|
? DropdownMenuItem
|
|
|
|
|
: (props: React.HTMLAttributes<HTMLDivElement>) => (
|
|
|
|
|
<DrawerClose asChild>
|
|
|
|
|
<div {...props} className={cn(props.className, "my-2")} />
|
|
|
|
|
</DrawerClose>
|
|
|
|
|
);
|
2025-04-11 00:33:51 +03:00
|
|
|
|
|
|
|
|
return (
|
2025-10-22 16:36:09 +03:00
|
|
|
<div className={className ?? "flex"}>
|
2025-04-11 00:33:51 +03:00
|
|
|
{newFace && (
|
|
|
|
|
<TextEntryDialog
|
|
|
|
|
open={true}
|
|
|
|
|
setOpen={setNewFace}
|
|
|
|
|
title={t("createFaceLibrary.new")}
|
|
|
|
|
onSave={(newName) => onTrainAttempt(newName)}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<Selector>
|
|
|
|
|
<SelectorTrigger asChild>
|
|
|
|
|
<TooltipTrigger asChild={isChildButton}>{children}</TooltipTrigger>
|
|
|
|
|
</SelectorTrigger>
|
|
|
|
|
<SelectorContent
|
2025-05-22 18:38:14 +03:00
|
|
|
className={cn("", isMobile && "mx-1 gap-2 rounded-t-2xl px-4")}
|
2025-04-11 00:33:51 +03:00
|
|
|
>
|
|
|
|
|
{isMobile && (
|
|
|
|
|
<DrawerHeader className="sr-only">
|
2025-05-20 00:45:02 +03:00
|
|
|
<DrawerTitle>Details</DrawerTitle>
|
|
|
|
|
<DrawerDescription>Details</DrawerDescription>
|
2025-04-11 00:33:51 +03:00
|
|
|
</DrawerHeader>
|
|
|
|
|
)}
|
|
|
|
|
<DropdownMenuLabel>{t("trainFaceAs")}</DropdownMenuLabel>
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
2025-11-17 17:12:05 +03:00
|
|
|
"flex max-h-[40dvh] flex-col overflow-y-auto overflow-x-hidden",
|
2025-05-22 18:38:14 +03:00
|
|
|
isMobile && "gap-2 pb-4",
|
2025-04-11 00:33:51 +03:00
|
|
|
)}
|
|
|
|
|
>
|
2025-05-22 18:38:14 +03:00
|
|
|
{faceNames.sort().map((faceName) => (
|
2025-04-11 00:33:51 +03:00
|
|
|
<SelectorItem
|
|
|
|
|
key={faceName}
|
2025-04-23 01:21:09 +03:00
|
|
|
className="flex cursor-pointer gap-2 smart-capitalize"
|
2025-04-11 00:33:51 +03:00
|
|
|
onClick={() => onTrainAttempt(faceName)}
|
|
|
|
|
>
|
|
|
|
|
{faceName}
|
|
|
|
|
</SelectorItem>
|
|
|
|
|
))}
|
2025-11-17 17:12:05 +03:00
|
|
|
<DropdownMenuSeparator />
|
|
|
|
|
<SelectorItem
|
|
|
|
|
className="flex cursor-pointer gap-2 smart-capitalize"
|
|
|
|
|
onClick={() => setNewFace(true)}
|
|
|
|
|
>
|
|
|
|
|
{t("createFaceLibrary.new")}
|
|
|
|
|
</SelectorItem>
|
2025-04-11 00:33:51 +03:00
|
|
|
</div>
|
|
|
|
|
</SelectorContent>
|
|
|
|
|
</Selector>
|
|
|
|
|
<TooltipContent>{t("trainFace")}</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|