Working rename
This commit is contained in:
+168
-153
@@ -1,6 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
||||
import { useState } from "react";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
@@ -9,27 +11,13 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "../ui/sidebar";
|
||||
import { Collapsible, CollapsibleContent } from "../ui/collapsible";
|
||||
import { ChevronDown, MoreHorizontal } from "lucide-react";
|
||||
import { Route, Workspace, LayoutNode } from "@/lib/types";
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "../ui/dropdown-menu";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useState } from "react";
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "../ui/collapsible";
|
||||
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuContent,
|
||||
@@ -38,6 +26,22 @@ import {
|
||||
ContextMenuTrigger,
|
||||
} from "../ui/context-menu";
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
} from "../ui/dialog";
|
||||
|
||||
import { Input } from "../ui/input";
|
||||
import { Button } from "../ui/button";
|
||||
|
||||
import { Route, Workspace, LayoutNode } from "@/lib/types";
|
||||
|
||||
type DialogMode = "rename" | "add-item" | "add-group";
|
||||
|
||||
type Props = {
|
||||
node: LayoutNode;
|
||||
workspace: Workspace;
|
||||
@@ -47,101 +51,151 @@ type Props = {
|
||||
parentGroupId: string | null,
|
||||
name?: string,
|
||||
) => string | undefined;
|
||||
addGroup: (name: string | undefined, parentGroupId: string | null) => string;
|
||||
addGroup: (name: string, parentGroupId: string | null) => string;
|
||||
renameEntity: (entityId: string, newName: string) => void;
|
||||
deleteEntity: (entityId: string) => void;
|
||||
removeNode: (id: string) => void;
|
||||
renameGroup: (id: string, name: string) => void;
|
||||
};
|
||||
|
||||
export default function EntitySwitch({
|
||||
node,
|
||||
workspace,
|
||||
setRoute,
|
||||
addEntity,
|
||||
addGroup,
|
||||
renameEntity,
|
||||
deleteEntity,
|
||||
removeNode,
|
||||
renameGroup,
|
||||
}: Props) {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
export default function EntitySwitch(props: Props) {
|
||||
const {
|
||||
node,
|
||||
workspace,
|
||||
setRoute,
|
||||
addEntity,
|
||||
addGroup,
|
||||
renameEntity,
|
||||
deleteEntity,
|
||||
removeNode,
|
||||
renameGroup,
|
||||
} = props;
|
||||
|
||||
const [dialogValue, setDialogValue] = useState("");
|
||||
const [dialogMode, setDialogMode] = useState<
|
||||
"rename-group" | "rename-item" | "add-item" | "add-group"
|
||||
>("rename-item");
|
||||
const [dialogMode, setDialogMode] = useState<DialogMode | undefined>();
|
||||
|
||||
const openDialog = (mode: typeof dialogMode, initial = "") => {
|
||||
const parentId = node.type === "group" ? node.id : null;
|
||||
|
||||
const openDialog = (mode: DialogMode, initial = "") => {
|
||||
setDialogMode(mode);
|
||||
// supply default for add-item
|
||||
if (mode === "add-item") {
|
||||
setDialogValue("New list");
|
||||
} else {
|
||||
setDialogValue(initial);
|
||||
}
|
||||
setDialogOpen(true);
|
||||
setDialogValue(
|
||||
initial ||
|
||||
(mode === "add-item"
|
||||
? "New list"
|
||||
: mode === "add-group"
|
||||
? "New group"
|
||||
: ""),
|
||||
);
|
||||
};
|
||||
|
||||
const handleDialogSubmit = () => {
|
||||
if (dialogMode === "rename-group" && node.type === "group") {
|
||||
renameGroup(node.id, dialogValue);
|
||||
} else if (dialogMode === "rename-item" && node.type === "item") {
|
||||
renameEntity(node.entityId, dialogValue);
|
||||
} else if (dialogMode === "add-item") {
|
||||
const parentId = node.type === "group" ? node.id : null;
|
||||
addEntity("todo", parentId, dialogValue || "New list");
|
||||
} else if (dialogMode === "add-group") {
|
||||
const parentId = node.type === "group" ? node.id : null;
|
||||
addGroup(dialogValue || "New group", parentId);
|
||||
const closeDialog = () => setDialogMode(undefined);
|
||||
|
||||
const handleSubmit = () => {
|
||||
const value = dialogValue.trim();
|
||||
if (!value) return;
|
||||
|
||||
switch (dialogMode) {
|
||||
case "rename":
|
||||
if (node.type === "group") renameGroup(node.id, value);
|
||||
else if (node.type === "item") renameEntity(node.entityId, value);
|
||||
break;
|
||||
|
||||
case "add-item":
|
||||
addEntity("todo", parentId, value);
|
||||
break;
|
||||
|
||||
case "add-group":
|
||||
addGroup(value, parentId);
|
||||
break;
|
||||
}
|
||||
setDialogOpen(false);
|
||||
|
||||
closeDialog();
|
||||
};
|
||||
|
||||
// ---- GROUP ----
|
||||
const confirmDelete = (message: string, action: () => void) => {
|
||||
if (window.confirm(message)) action();
|
||||
};
|
||||
|
||||
const dialogTitle = dialogMode?.startsWith("rename")
|
||||
? "Rename"
|
||||
: dialogMode === "add-item"
|
||||
? "Add new list"
|
||||
: "Add new group";
|
||||
|
||||
const dialog = (
|
||||
<Dialog open={!!dialogMode} onOpenChange={(o) => !o && closeDialog()}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{dialogTitle}</DialogTitle>
|
||||
|
||||
{(dialogMode === "add-item" || dialogMode === "add-group") && (
|
||||
<DialogDescription>Enter a name below.</DialogDescription>
|
||||
)}
|
||||
</DialogHeader>
|
||||
|
||||
<Input
|
||||
autoFocus
|
||||
value={dialogValue}
|
||||
onChange={(e) => setDialogValue(e.target.value)}
|
||||
/>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={closeDialog}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleSubmit}>Save</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
/* ================= GROUP ================= */
|
||||
|
||||
if (node.type === "group") {
|
||||
return (
|
||||
<>
|
||||
<Collapsible defaultOpen className="group/collapsible">
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel asChild>
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger asChild>
|
||||
<SidebarMenuButton asChild>
|
||||
<CollapsibleTrigger className="flex w-full items-center">
|
||||
{node.name}
|
||||
<ChevronDown className="ml-auto h-4 w-4 transition-transform group-data-[state=open]/collapsible:rotate-180" />
|
||||
</CollapsibleTrigger>
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger asChild>
|
||||
<CollapsibleTrigger asChild>
|
||||
<SidebarMenuButton className="w-full">
|
||||
{node.name}
|
||||
<ChevronDown className="ml-auto h-4 w-4 transition-transform group-data-[state=open]/collapsible:rotate-180" />
|
||||
</SidebarMenuButton>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem onSelect={() => openDialog("add-item")}>
|
||||
Add list
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem onSelect={() => openDialog("add-group")}>
|
||||
Add group
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator />
|
||||
<ContextMenuItem
|
||||
onSelect={() => openDialog("rename-group", node.name)}
|
||||
>
|
||||
Rename
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
variant="destructive"
|
||||
onSelect={() => {
|
||||
if (
|
||||
window.confirm("Delete this group and its children?")
|
||||
) {
|
||||
removeNode(node.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
</div>
|
||||
</CollapsibleTrigger>
|
||||
</ContextMenuTrigger>
|
||||
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem onSelect={() => openDialog("add-item")}>
|
||||
Add list
|
||||
</ContextMenuItem>
|
||||
|
||||
<ContextMenuItem onSelect={() => openDialog("add-group")}>
|
||||
Add group
|
||||
</ContextMenuItem>
|
||||
|
||||
<ContextMenuSeparator />
|
||||
|
||||
<ContextMenuItem
|
||||
onSelect={() => openDialog("rename", node.name)}
|
||||
>
|
||||
Rename
|
||||
</ContextMenuItem>
|
||||
|
||||
<ContextMenuItem
|
||||
variant="destructive"
|
||||
onSelect={() =>
|
||||
confirmDelete("Delete this group and its children?", () =>
|
||||
removeNode(node.id),
|
||||
)
|
||||
}
|
||||
>
|
||||
Delete
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
</SidebarGroupLabel>
|
||||
|
||||
<CollapsibleContent>
|
||||
@@ -150,15 +204,8 @@ export default function EntitySwitch({
|
||||
{node.children.map((child) => (
|
||||
<EntitySwitch
|
||||
key={child.type === "group" ? child.id : child.entityId}
|
||||
{...props}
|
||||
node={child}
|
||||
workspace={workspace}
|
||||
setRoute={setRoute}
|
||||
addEntity={addEntity}
|
||||
addGroup={addGroup}
|
||||
renameEntity={renameEntity}
|
||||
deleteEntity={deleteEntity}
|
||||
removeNode={removeNode}
|
||||
renameGroup={renameGroup}
|
||||
/>
|
||||
))}
|
||||
</SidebarMenu>
|
||||
@@ -166,52 +213,19 @@ export default function EntitySwitch({
|
||||
</CollapsibleContent>
|
||||
</SidebarGroup>
|
||||
</Collapsible>
|
||||
|
||||
{/* dialog for add/rename */}
|
||||
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{dialogMode.startsWith("rename")
|
||||
? "Rename"
|
||||
: dialogMode === "add-item"
|
||||
? "Add new list"
|
||||
: "Add new group"}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{dialogMode === "add-item" &&
|
||||
"A default todo list will be created."}
|
||||
{dialogMode === "add-group" &&
|
||||
"Enter the name for the new group."}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Input
|
||||
value={dialogValue}
|
||||
onChange={(e) => setDialogValue(e.target.value)}
|
||||
className="w-full"
|
||||
/>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setDialogOpen(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleDialogSubmit}>Save</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
{dialog}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// ---- ITEM ----
|
||||
const workspaceEntity = workspace.entities[node.entityId];
|
||||
/* ================= ITEM ================= */
|
||||
|
||||
if (!workspaceEntity) {
|
||||
return null;
|
||||
}
|
||||
const entity = workspace.entities[node.entityId];
|
||||
if (!entity) return null;
|
||||
|
||||
return (
|
||||
<SidebarMenuItem>
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<>
|
||||
<SidebarMenuItem>
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger asChild>
|
||||
<SidebarMenuButton
|
||||
@@ -222,28 +236,29 @@ export default function EntitySwitch({
|
||||
})
|
||||
}
|
||||
>
|
||||
{workspaceEntity.name}
|
||||
{entity.name}
|
||||
</SidebarMenuButton>
|
||||
</ContextMenuTrigger>
|
||||
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem
|
||||
onSelect={() => openDialog("rename-item", workspaceEntity.name)}
|
||||
>
|
||||
<ContextMenuItem onSelect={() => openDialog("rename", entity.name)}>
|
||||
Rename
|
||||
</ContextMenuItem>
|
||||
|
||||
<ContextMenuItem
|
||||
variant="destructive"
|
||||
onSelect={() => {
|
||||
if (window.confirm("Delete this list?")) {
|
||||
deleteEntity(node.entityId);
|
||||
}
|
||||
}}
|
||||
onSelect={() =>
|
||||
confirmDelete("Delete this list?", () =>
|
||||
deleteEntity(node.entityId),
|
||||
)
|
||||
}
|
||||
>
|
||||
Delete
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
</div>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenuItem>
|
||||
{dialog}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user