aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/web/components/dashboard/tags/AllTagsView.tsx27
-rw-r--r--apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx6
-rw-r--r--apps/web/components/dashboard/tags/TagPill.tsx66
3 files changed, 70 insertions, 29 deletions
diff --git a/apps/web/components/dashboard/tags/AllTagsView.tsx b/apps/web/components/dashboard/tags/AllTagsView.tsx
index 7b81ed72..f6a42ef2 100644
--- a/apps/web/components/dashboard/tags/AllTagsView.tsx
+++ b/apps/web/components/dashboard/tags/AllTagsView.tsx
@@ -19,6 +19,7 @@ import { ArrowDownAZ, Combine } from "lucide-react";
import type { ZGetTagResponse } from "@hoarder/shared/types/tags";
import { useDeleteUnusedTags } from "@hoarder/shared-react/hooks/tags";
+import DeleteTagConfirmationDialog from "./DeleteTagConfirmationDialog";
import { TagPill } from "./TagPill";
function DeleteAllUnusedTags({ numUnusedTags }: { numUnusedTags: number }) {
@@ -71,9 +72,22 @@ export default function AllTagsView({
}: {
initialData: ZGetTagResponse[];
}) {
+ interface Tag {
+ id: string;
+ name: string;
+ }
+
const [draggingEnabled, setDraggingEnabled] = React.useState(false);
const [sortByName, setSortByName] = React.useState(false);
+ const [isDialogOpen, setIsDialogOpen] = React.useState(false);
+ const [selectedTag, setSelectedTag] = React.useState<Tag | null>(null);
+
+ const handleOpenDialog = (tag: Tag) => {
+ setSelectedTag(tag);
+ setIsDialogOpen(true);
+ };
+
function toggleSortByName(): void {
setSortByName(!sortByName);
}
@@ -104,6 +118,7 @@ export default function AllTagsView({
name={t.name}
count={t.count}
isDraggable={draggingEnabled}
+ onOpenDialog={handleOpenDialog}
/>
))}
</div>
@@ -115,6 +130,18 @@ export default function AllTagsView({
};
return (
<>
+ {selectedTag && (
+ <DeleteTagConfirmationDialog
+ tag={selectedTag}
+ open={isDialogOpen}
+ setOpen={(o) => {
+ if (!o) {
+ setSelectedTag(null);
+ }
+ setIsDialogOpen(o);
+ }}
+ />
+ )}
<div className="flex justify-end gap-x-2">
<Toggle
variant="outline"
diff --git a/apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx b/apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx
index 7021b715..998ac9b8 100644
--- a/apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx
+++ b/apps/web/components/dashboard/tags/DeleteTagConfirmationDialog.tsx
@@ -7,12 +7,10 @@ import { useDeleteTag } from "@hoarder/shared-react/hooks/tags";
export default function DeleteTagConfirmationDialog({
tag,
- children,
open,
setOpen,
}: {
tag: { id: string; name: string };
- children?: React.ReactNode;
open?: boolean;
setOpen?: (v: boolean) => void;
}) {
@@ -55,8 +53,6 @@ export default function DeleteTagConfirmationDialog({
Delete
</ActionButton>
)}
- >
- {children}
- </ActionConfirmingDialog>
+ />
);
}
diff --git a/apps/web/components/dashboard/tags/TagPill.tsx b/apps/web/components/dashboard/tags/TagPill.tsx
index 88b88b52..ff17f224 100644
--- a/apps/web/components/dashboard/tags/TagPill.tsx
+++ b/apps/web/components/dashboard/tags/TagPill.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useState } from "react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
@@ -9,19 +9,24 @@ import Draggable from "react-draggable";
import { useMergeTag } from "@hoarder/shared-react/hooks/tags";
-import DeleteTagConfirmationDialog from "./DeleteTagConfirmationDialog";
-
export function TagPill({
id,
name,
count,
isDraggable,
+ onOpenDialog,
}: {
id: string;
name: string;
count: number;
isDraggable: boolean;
+ onOpenDialog: (tag: { id: string; name: string }) => void;
}) {
+ const [isHovered, setIsHovered] = useState(false);
+
+ const handleMouseOver = () => setIsHovered(true);
+ const handleMouseOut = () => setIsHovered(false);
+
const { mutate: mergeTag } = useMergeTag({
onSuccess: () => {
toast({
@@ -62,6 +67,39 @@ export function TagPill({
},
);
+ const pill = (
+ <div
+ className="group relative flex"
+ onMouseOver={handleMouseOver}
+ onFocus={handleMouseOver}
+ onMouseOut={handleMouseOut}
+ onBlur={handleMouseOut}
+ >
+ <Link
+ className={
+ "flex gap-2 rounded-md border border-border bg-background px-2 py-1 text-foreground hover:bg-foreground hover:text-background"
+ }
+ href={`/dashboard/tags/${id}`}
+ data-id={id}
+ >
+ {name} <Separator orientation="vertical" /> {count}
+ </Link>
+
+ {isHovered && !isDraggable && (
+ <Button
+ size="none"
+ variant="secondary"
+ className="-translate-1/2 absolute -right-1 -top-1 hidden rounded-full group-hover:block"
+ onClick={() => onOpenDialog({ id, name })}
+ >
+ <X className="size-3" />
+ </Button>
+ )}
+ </div>
+ );
+ if (!isDraggable) {
+ return pill;
+ }
return (
<Draggable
key={id}
@@ -72,27 +110,7 @@ export function TagPill({
defaultClassNameDragging={"position-relative z-10 pointer-events-none"}
position={{ x: 0, y: 0 }}
>
- <div className="group relative flex">
- <Link
- className={
- "flex gap-2 rounded-md border border-border bg-background px-2 py-1 text-foreground hover:bg-foreground hover:text-background"
- }
- href={`/dashboard/tags/${id}`}
- data-id={id}
- >
- {name} <Separator orientation="vertical" /> {count}
- </Link>
-
- <DeleteTagConfirmationDialog tag={{ name, id }}>
- <Button
- size="none"
- variant="secondary"
- className="-translate-1/2 absolute -right-1 -top-1 hidden rounded-full group-hover:block"
- >
- <X className="size-3" />
- </Button>
- </DeleteTagConfirmationDialog>
- </div>
+ {pill}
</Draggable>
);
}