aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-05-18 12:14:55 +0100
committerMohamedBassem <me@mbassem.com>2024-05-18 12:14:55 +0100
commite5fd9eeca0d34658c9eed4f1a7d6ec25d4918488 (patch)
tree8e30298660158aa6f9bdf4fffadf6367c0b4d2ee /apps
parent1fee129c337069fc41d1c46141beaaec94033af8 (diff)
downloadkarakeep-e5fd9eeca0d34658c9eed4f1a7d6ec25d4918488.tar.zst
fix(web): Simplify the logic for tag drag and dropping
Diffstat (limited to 'apps')
-rw-r--r--apps/web/components/dashboard/tags/AllTagsView.tsx72
-rw-r--r--apps/web/components/dashboard/tags/TagPill.tsx22
-rw-r--r--apps/web/lib/drag-and-drop.ts101
3 files changed, 68 insertions, 127 deletions
diff --git a/apps/web/components/dashboard/tags/AllTagsView.tsx b/apps/web/components/dashboard/tags/AllTagsView.tsx
index 017a1e40..ac139e23 100644
--- a/apps/web/components/dashboard/tags/AllTagsView.tsx
+++ b/apps/web/components/dashboard/tags/AllTagsView.tsx
@@ -15,6 +15,7 @@ import { Toggle } from "@/components/ui/toggle";
import { toast } from "@/components/ui/use-toast";
import { useDragAndDrop } from "@/lib/drag-and-drop";
import { api } from "@/lib/trpc";
+import { ArrowDownAZ, Combine } from "lucide-react";
import Draggable from "react-draggable";
import type { ZGetTagResponse } from "@hoarder/shared/types/tags";
@@ -75,27 +76,26 @@ export default function AllTagsView({
}: {
initialData: ZGetTagResponse[];
}) {
- const [draggingEnabled, toggleDraggingEnabled] = React.useState(false);
- const [sortByName, toggleSortByName] = React.useState(false);
-
- const { dragState, handleDrag, handleDragStart, handleDragEnd } =
- useDragAndDrop(
- "data-id",
- "data-id",
- (dragSourceId: string, dragTargetId: string) => {
- mergeTag({
- fromTagIds: [dragSourceId],
- intoTagId: dragTargetId,
- });
- },
- );
+ const [draggingEnabled, setDraggingEnabled] = React.useState(false);
+ const [sortByName, setSortByName] = React.useState(false);
+
+ const { handleDragStart, handleDragEnd } = useDragAndDrop(
+ "data-id",
+ "data-id",
+ (dragSourceId: string, dragTargetId: string) => {
+ mergeTag({
+ fromTagIds: [dragSourceId],
+ intoTagId: dragTargetId,
+ });
+ },
+ );
- function handleSortByNameChange(): void {
- toggleSortByName(!sortByName);
+ function toggleSortByName(): void {
+ setSortByName(!sortByName);
}
- function handleDraggableChange(): void {
- toggleDraggingEnabled(!draggingEnabled);
+ function toggleDraggingEnabled(): void {
+ setDraggingEnabled(!draggingEnabled);
}
const { mutate: mergeTag } = useMergeTag({
@@ -131,6 +131,7 @@ export default function AllTagsView({
const { data } = api.tags.list.useQuery(undefined, {
initialData: { tags: initialData },
});
+
// Sort tags by usage desc
const allTags = data.tags.sort(sortByName ? byNameSorter : byUsageSorter);
@@ -148,28 +149,15 @@ export default function AllTagsView({
key={t.id}
axis="both"
onStart={handleDragStart}
- onDrag={handleDrag}
onStop={handleDragEnd}
disabled={!draggingEnabled}
defaultClassNameDragging={
"position-relative z-10 pointer-events-none"
}
- position={
- !dragState.dragSourceId
- ? {
- x: dragState.initialX ?? 0,
- y: dragState.initialY ?? 0,
- }
- : undefined
- }
+ position={{ x: 0, y: 0 }}
>
- <div className="group relative flex cursor-grab" data-id={t.id}>
- <TagPill
- id={t.id}
- name={t.name}
- count={t.count}
- isDraggable={draggingEnabled}
- />
+ <div className="cursor-grab" data-id={t.id}>
+ <TagPill id={t.id} name={t.name} count={t.count} />
</div>
</Draggable>
))}
@@ -182,22 +170,24 @@ export default function AllTagsView({
};
return (
<>
- <div className="float-right">
+ <div className="flex justify-end gap-x-2">
<Toggle
variant="outline"
- aria-label="Toggle bold"
pressed={draggingEnabled}
- onPressedChange={handleDraggableChange}
+ onPressedChange={toggleDraggingEnabled}
>
- Allow Merging via Drag&Drop
+ <Combine className="mr-2 size-4" />
+ Drag & Drop Merging
+ <InfoTooltip size={15} className="my-auto ml-2" variant="explain">
+ <p>Drag and drop tags on each other to merge them</p>
+ </InfoTooltip>
</Toggle>
<Toggle
variant="outline"
- aria-label="Toggle bold"
pressed={sortByName}
- onPressedChange={handleSortByNameChange}
+ onPressedChange={toggleSortByName}
>
- Sort by Name
+ <ArrowDownAZ className="mr-2 size-4" /> Sort by Name
</Toggle>
</div>
<span className="flex items-center gap-2">
diff --git a/apps/web/components/dashboard/tags/TagPill.tsx b/apps/web/components/dashboard/tags/TagPill.tsx
index 7236dc39..f1c99d70 100644
--- a/apps/web/components/dashboard/tags/TagPill.tsx
+++ b/apps/web/components/dashboard/tags/TagPill.tsx
@@ -5,32 +5,24 @@ import { X } from "lucide-react";
import DeleteTagConfirmationDialog from "./DeleteTagConfirmationDialog";
-const PILL_STYLE =
- "flex gap-2 rounded-md border border-border bg-background px-2 py-1 text-foreground hover:bg-foreground hover:text-background";
-
export function TagPill({
id,
name,
count,
- isDraggable,
}: {
id: string;
name: string;
count: number;
- isDraggable: boolean;
}) {
- // When the element is draggable, do not generate a link. Links can be dragged into e.g. the tab-bar and therefore dragging the TagPill does not work properly
- if (isDraggable) {
- return (
- <div className={PILL_STYLE} data-id={id}>
- {name} <Separator orientation="vertical" /> {count}
- </div>
- );
- }
-
return (
<div className="group relative flex">
- <Link className={PILL_STYLE} href={`/dashboard/tags/${id}`}>
+ <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>
diff --git a/apps/web/lib/drag-and-drop.ts b/apps/web/lib/drag-and-drop.ts
index 03a9e7c3..e005a6d0 100644
--- a/apps/web/lib/drag-and-drop.ts
+++ b/apps/web/lib/drag-and-drop.ts
@@ -1,69 +1,30 @@
+import React from "react";
import { DraggableData, DraggableEvent } from "react-draggable";
-export interface DragState {
- // The id of the element that is being dragged
- dragSourceId: string | null;
- // The id of the element that is currently being hovered over
- dragTargetId: string | null;
- // The position of the elements being dragged such that on drag over, we can revert the position.
- initialX: number;
- initialY: number;
-}
-
-export interface DragAndDropFunctions {
- handleDragStart: (e: DraggableEvent, data: DraggableData) => void;
- handleDrag: (e: DraggableEvent) => void;
- handleDragEnd: () => void;
- dragState: DragState;
-}
-
export function useDragAndDrop(
dragSourceIdAttribute: string,
dragTargetIdAttribute: string,
- callback: (dragSourceId: string, dragTargetId: string) => void,
-): DragAndDropFunctions {
- const initialState: DragState = {
- dragSourceId: null,
- dragTargetId: null,
- initialX: 0,
- initialY: 0,
- };
-
- let currentState: DragState = initialState;
-
- function handleDragStart(e: DraggableEvent, data: DraggableData): void {
- const { node } = data;
- const id = node.getAttribute(dragSourceIdAttribute);
-
- currentState = {
- ...initialState,
- dragSourceId: id,
- initialX: data.x,
- initialY: data.y,
- };
- }
-
- function handleDrag(e: DraggableEvent): void {
- const { dragTargetId } = currentState;
- const { target } = e;
-
- // Important according to the sample I found
- e.preventDefault();
-
- if (target) {
- const id = (target as HTMLElement).getAttribute(dragTargetIdAttribute);
-
- if (id !== dragTargetId) {
- currentState.dragTargetId = id;
- }
- }
- }
-
- function handleDragEnd(): void {
- const { dragSourceId, dragTargetId } = currentState;
-
- if (dragSourceId && dragTargetId && dragSourceId !== dragTargetId) {
- /*
+ onDragOver: (dragSourceId: string, dragTargetId: string) => void,
+) {
+ const [dragSourceId, setDragSourceId] = React.useState<string | null>(null);
+
+ const handleDragStart = React.useCallback(
+ (_e: DraggableEvent, { node }: DraggableData) => {
+ const id = node.getAttribute(dragSourceIdAttribute);
+ setDragSourceId(id);
+ },
+ [],
+ );
+
+ const handleDragEnd = React.useCallback(
+ (e: DraggableEvent) => {
+ const { target } = e;
+ const dragTargetId = (target as HTMLElement).getAttribute(
+ dragTargetIdAttribute,
+ );
+
+ if (dragSourceId && dragTargetId && dragSourceId !== dragTargetId) {
+ /*
As Draggable tries to setState when the
component is unmounted, it is needed to
push onCombine to the event loop queue.
@@ -71,19 +32,17 @@ export function useDragAndDrop(
Draggable so it would fix the issue until
they fix it on their end.
*/
- setTimeout(() => {
- console.log(dragSourceId, dragTargetId);
- callback(dragSourceId, dragTargetId);
- }, 0);
- }
-
- currentState = initialState;
- }
+ setTimeout(() => {
+ onDragOver(dragSourceId, dragTargetId);
+ }, 0);
+ }
+ setDragSourceId(null);
+ },
+ [dragSourceId, onDragOver],
+ );
return {
- dragState: currentState,
handleDragStart,
- handleDrag,
handleDragEnd,
};
}