Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve merging of tags by simple drag and drop #144 #154

Merged
merged 5 commits into from
May 18, 2024

Conversation

kamtschatka
Copy link
Contributor

Added drag&drop functionality
Allowing sorting the tags by name, as this is more intuitive

Just a crude change to get some feedback. As I am no UI dev, I am thankful for any improvement ideas.

Added drag&drop functionality
Allowing sorting the tags by name, as this is more intuitive
Copy link

vercel bot commented May 15, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
hoarder-app-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 15, 2024 7:54pm
hoarder-app-landing ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 15, 2024 7:54pm

@MohamedBassem
Copy link
Collaborator

Hi, thanks a lot for working on this! I tried it locally and it's really really cool! I'll review the code now :)

Copy link
Collaborator

@MohamedBassem MohamedBassem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good to me! Thank you! Left a bunch of hopefully small comments.

x: 0,
y: 0,
};
let currentState: DragState = initialState;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a react expert but I think global mutable state are frowned upon? Is it not possible to do this with a React.useState instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not an expert either^^
I have however extracted it out now, since that functionality is pretty much the same for the drag&drop functionality for assigning bookmarks to lists (next PR ;-) ), so it makes sense to reuse it.
It is now more encapsulated, so I guess that concern should be gone, but let me know.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, after learning a lot, i found this:
useState needs to be used for the change detection to work, but the drag&drop handlers are quite slow to execute and react-draggable is not implemented that well. Due to the processing overhead, the

will drift away from the mouse over time (lagging behind) and it looks stupid.
My followup PR for also implementing drag&drop for assigning bookmarks to lists has therefore some changes:

  • The components themselves keep their state instead of the parent --> more fine grained rerender decisions possible
  • most of the state is kept outside of useState and instead in a simple closure, to make sure everything is still fast enough
  • the isDragging state is kept with useState, which makes a rerender possible, when the user starts to drag.

Comment on lines 233 to 249
<label>
<input
type="checkbox"
checked={draggingEnabled}
onChange={handleDraggableChange}
/>
Allow Merging via Drag&Drop
</label>
<br />
<label>
<input
type="checkbox"
checked={sortByName}
onChange={handleSortByNameChange}
/>
Sort by Name
</label>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think those can probably be shadcn toggles (https://ui.shadcn.com/docs/components/toggle) and can probably be aligned right in the page.
If UI design is not your expertise, I'm happy to implement it myself after merging your PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
looks like this now

apps/web/components/dashboard/tags/AllTagsView.tsx Outdated Show resolved Hide resolved
Comment on lines 209 to 213
<div
className="group relative flex cursor-grab"
data-id={t.id}
onDragOver={handleDrag}
>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we pass onDragOver to TagPill and remove that div?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

turns out the onDragOver is not actually needed, so I removed it.
If I remove the div though, it is suddenly not possible to drag it anymore. I am not sure why though...

apps/web/components/dashboard/tags/AllTagsView.tsx Outdated Show resolved Hide resolved
apps/web/components/dashboard/tags/AllTagsView.tsx Outdated Show resolved Hide resolved
minor renamings
removed some unnecessary code
extracted out the drag and drop functionality to be more encapsulated and reusable
improved the usage sorter to additionally compare by name if the usage is the same
replaced checkboxes with toggles floating on the right
Copy link

vercel bot commented May 17, 2024

Someone is attempting to deploy a commit to the Mohamed Bassem's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Collaborator

@MohamedBassem MohamedBassem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot of addressing the comments! I think there are some minor changes (e.g. adding some icons and margins to the buttons) but I'll take care of those!

Thanks again for working on this and also giving me a tool to be able to add support for drag and drops in other places. Very cool!

Comment on lines +13 to +18
export interface DragAndDropFunctions {
handleDragStart: (e: DraggableEvent, data: DraggableData) => void;
handleDrag: (e: DraggableEvent) => void;
handleDragEnd: () => void;
dragState: DragState;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This interface seems to only be used as the return type of the useDragAndDrop hook. I think you can drop it completely and typescript will be able to infer it.

Comment on lines +81 to +91
const { dragState, handleDrag, handleDragStart, handleDragEnd } =
useDragAndDrop(
"data-id",
"data-id",
(dragSourceId: string, dragTargetId: string) => {
mergeTag({
fromTagIds: [dragSourceId],
intoTagId: dragTargetId,
});
},
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very very cool!

@MohamedBassem MohamedBassem merged commit 1fee129 into hoarder-app:main May 18, 2024
3 of 5 checks passed
@MohamedBassem
Copy link
Collaborator

@kamtschatka You can see some of the simplifications I made here (e5fd9ee). Mainly:

  1. Managed to get dragging of links to work by adding data-id on the link itself on the parent div.
  2. Found a way that we won't need the initialX and initialY.
  3. handleDrag turned out to not be needed as you only care when the drag is over.
  4. Added some icons and help messages on the buttons.

Again thanks a lot for the idea and implementation!

@MohamedBassem
Copy link
Collaborator

This is what it looks like now:

Screenshot 2024-05-18 at 12 19 43 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants