Files
frontend/apps/cs/src/pages/menu/ui/menu-node.tsx
T

115 lines
3.1 KiB
TypeScript

import { useEffect, useState } from "react";
import { useQueryClient } from "@tanstack/react-query";
import type { NodeModel, TreeMethods } from "@minoru/react-dnd-treeview";
import { Button, cn } from "@common/components";
import { TreeMenuApi, type TreeMenu } from "@/entities/tree-menu";
import { useMutationWrapper } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { EditableNode } from "./editable-node";
type MenuNodeProps = {
node: NodeModel<TreeMenu>;
depth: number;
isOpen: boolean;
isSelected: boolean;
isAdditionalParent: boolean;
treeMethods: TreeMethods | null;
onToggle: (id: NodeModel["id"]) => void;
onSelect: (node: NodeModel<TreeMenu>) => void;
};
function MenuNode({
node,
depth,
isOpen,
isSelected,
isAdditionalParent,
treeMethods,
onToggle,
onSelect,
}: MenuNodeProps) {
const { id, text, data } = node;
const indent = depth * 10 + 5;
const queryClient = useQueryClient();
const { mutateAsync: updateMenuName } = useMutationWrapper({
mutationFn: (nodeName: string) => {
const encodedName = encodeURIComponent(nodeName);
return TreeMenuApi.updateMenuName(id as number, encodedName);
},
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.MENUS],
});
},
});
const [editMode, setEditMode] = useState(false);
const [nodeName, setNodeName] = useState(text);
const handleToggle = () => onToggle(id);
const handleSelect = () => onSelect(node);
const handleSave = async () => {
await updateMenuName(nodeName);
setEditMode(false);
};
const handleCancel = () => {
setEditMode(false);
setNodeName(text);
};
useEffect(() => {
if (isAdditionalParent) treeMethods?.open(id);
}, [isAdditionalParent]);
return (
<div
style={{ paddingInlineStart: indent }}
className={cn(
"group flex cursor-pointer items-center justify-between gap-2 rounded-sm p-2 hover:bg-accent",
isSelected && "!bg-[rgba(32,148,250,0.2)]",
)}
>
{editMode ? (
<EditableNode
value={nodeName}
onChange={(e) => setNodeName(e.target.value)}
onSave={handleSave}
onCancel={handleCancel}
/>
) : (
<>
<div className="flex items-center" onClick={handleSelect}>
{data?.children?.length && (
<i
className={cn(
"diveicon di-arrow-forward mr-2 text-base text-label",
isOpen && "rotate-90",
)}
onClick={(e) => {
e.stopPropagation();
handleToggle();
}}
/>
)}
<span className="text-[1.4rem] font-medium text-label">{text}</span>
</div>
<Button
variant="ghost"
size="icon"
className="invisible !bg-transparent group-hover:visible"
onClick={() => setEditMode(true)}
>
<i className="diveicon di-pencil" />
</Button>
</>
)}
</div>
);
}
export { MenuNode };