atlassian / pragmatic-drag-and-drop Goto Github PK
View Code? Open in Web Editor NEWFast drag and drop for any experience on any tech stack
Home Page: https://atlassian.design/components/pragmatic-drag-and-drop
License: Other
Fast drag and drop for any experience on any tech stack
Home Page: https://atlassian.design/components/pragmatic-drag-and-drop
License: Other
Hi.
In the process of upgrading Next.js from 12 -> 14 we need to switch from react-dnd. We chose Pragmatic since it seems really nice! We have, however, run into an issue:
const ref = useRef<HTMLDivElement | null>(null);
const [isDragging, setIsDragging] = useState(false);
useEffect(() => {
const element = ref.current;
invariant(element);
return draggable({
element,
onDragStart: () => {
setIsDragging(true);
},
onDrop: () => setIsDragging(false),
getInitialData(): { index: number } {
return { index };
},
});
});
return (
<div ref={ref}>
<Link href={"https://www.svt.se"}>{index}</Link>
</div>
);
The above code gives us a link element where the draggable
doesn't seem to be registered. Is this working as intended? Have we missed anything? We have checked the documentation but alas cannot find anything that would help us. Creating a horrible hack sophisticated solution by using onMouseUp
and onMouseDown
and saving pointer state seems to defeat the whole point of using a drag and drop library.
I've noticed that there seems to be an inconsistent opacity in the drag preview. I cannot figure out why it occurs but I noticed it when making my own lists. You can also see it in the examples on the example page.
Is there a way to ensure an opacity of 100% for the drag preview? Is there a reason it would sometimes have a ~50% opacity?
Edit: I notice that the drag previews fade in. I think this may be a timing bug where sometimes they don't get faded in all the way.
Hello,
Thanks, hugs, love ya, etc. "What a library" - me.
I'm running into broken links pretty frequently in the documentation. Some examples:
once
utility function" link at https://atlassian.design/components/pragmatic-drag-and-drop/core-package/drop-targets/#handling-ondrop-for-nested-drop-targetsUsually I'm able to find the new location based on the broken href
but it's sort of annoying. Thanks for considering a fix!
Hi,
I wanted to play around with the library, so I created a fresh Vite project npm create vite
with React & TypeScript and started with such import
import { draggable } from "@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter";
Unfortunately it was not resolved. I dug into the package.json and from what I saw only the main entry points are exposed (which contain nothing, since as per documentation we should import individual files).
Adding proper "exports" field to package.json with all files in dist/(esm|cjs|types) fixed the issue for me, but I'm now relying on patch-package lib and a git diff to keep it working.
This is the diff I applied:
@atlaskit+pragmatic-drag-and-drop+1.1.3.patch
and a preview of the file:
diff --git a/node_modules/@atlaskit/pragmatic-drag-and-drop/package.json b/node_modules/@atlaskit/pragmatic-drag-and-drop/package.json
index b270896..9454cc2 100644
--- a/node_modules/@atlaskit/pragmatic-drag-and-drop/package.json
+++ b/node_modules/@atlaskit/pragmatic-drag-and-drop/package.json
@@ -17,6 +17,340 @@
]
}
},
+ "type": "module",
+ "exports": {
+ "./package.json": "./package.json",
+ "./": {
+ "default": "./dist/esm/index.js",
+ "require": "./dist/cjs/index.js",
+ "types": "./dist/types/index.d.ts"
+ },
+ "./internal-types": {
+ "default": "./dist/esm/internal-types.js",
+ "require": "./dist/cjs/internal-types.js",
+ "types": "./dist/types/internal-types.d.ts"
+ },
+ "./adapter/element-adapter-native-data-key": {
+ "default": "./dist/esm/adapter/element-adapter-native-data-key.js",
+ "require": "./dist/cjs/adapter/element-adapter-native-data-key.js",
+ "types": "./dist/types/adapter/element-adapter-native-data-key.d.ts"
+ },
+ "./adapter/element-adapter": {
+ "default": "./dist/esm/adapter/element-adapter.js",
+ "require": "./dist/cjs/adapter/element-adapter.js",
+ "types": "./dist/types/adapter/element-adapter.d.ts"
+ },
+ "./adapter/external-adapter": {
+ "default": "./dist/esm/adapter/external-adapter.js",
+ "require": "./dist/cjs/adapter/external-adapter.js",
+ "types": "./dist/types/adapter/external-adapter.d.ts"
+ },
+ "./adapter/text-selection-adapter": {
+ "default": "./dist/esm/adapter/text-selection-adapter.js",
+ "require": "./dist/cjs/adapter/text-selection-adapter.js",
+ "types": "./dist/types/adapter/text-selection-adapter.d.ts"
+ },
+ "./entry-point/cancel-unhandled": {
+ "default": "./dist/esm/entry-point/cancel-unhandled.js",
+ "require": "./dist/cjs/entry-point/cancel-unhandled.js",
+ "types": "./dist/types/entry-point/cancel-unhandled.d.ts"
+ },
+ "./entry-point/combine": {
+ "default": "./dist/esm/entry-point/combine.js",
+ "require": "./dist/cjs/entry-point/combine.js",
+ "types": "./dist/types/entry-point/combine.d.ts"
+ },
+ "./entry-point/once": {
+ "default": "./dist/esm/entry-point/once.js",
+ "require": "./dist/cjs/entry-point/once.js",
+ "types": "./dist/types/entry-point/once.d.ts"
+ },
+ "./entry-point/prevent-unhandled": {
+ "default": "./dist/esm/entry-point/prevent-unhandled.js",
+ "require": "./dist/cjs/entry-point/prevent-unhandled.js",
+ "types": "./dist/types/entry-point/prevent-unhandled.d.ts"
+ },
...
iOS lets you drag multiple elements at once, the library doesn’t handle that correctly. Only the first element is actually moved after dropping.
Here’s a video that shows the problem.
Not shown: cards already being dragged can be added multiple times to the stack. Any card being dragged should be made inert.
Looking at the code in "element-adapter.js":
var target = event.target;
// see if the thing being dragged is owned by us
var entry = draggableRegistry.get(target);
If the dragged element is contained within the shadow root of another web component then 'target' above won't be the element being dragged but will instead be the web component due to event re-targetting. The following code sandbox shows the issue:
https://codesandbox.io/p/sandbox/pdnd-web-components-forked-l8f6tv
Whenever I try to launch the Board example ( https://atlassian.design/components/pragmatic-drag-and-drop/examples#board ) or the table example ( https://atlassian.design/components/pragmatic-drag-and-drop/examples#table ), it gives an error that
"Could not find/install babel plugin 'proposal-decorators': Cannot find plugin 'proposal-decorators' or 'babel-plugin-proposal-decorators'"
We would love to see what you create with Pragmatic drag and drop. Please feel free to post a screen recording of your experience in this issue ✨
Would it be a good idea to use logical positioning values for the hitbox detection? Would be nice to have this handled by Pragmatic drag and drop, otherwise we would have to flip the left
and right
value ourselves for RTL layout.
Running into a case where handling drop events in the capture phase seems ideal, but though this is possible with native DnD API, it's not covered in PDnD.
Is that by design or just the current state of the library?
Example 1 --
Consider a board showing todos, workers and teams. Workers belong to teams. Todos must belong to teams and may belong to a worker on that team — perhaps for 'reasons' a todo assignment to a worker must be prefaced (however immediately) by assignment to that worker's team.
An admin drags a todo from the Worker 1 on the Team 1 panel to Worker 1 on Team 2.
It seems preferable if the parent team-element's handler was narrowly and solely responsible for triggering team assignment during capture phase and, afterwards, the child worker element's drop handler was narrowly and solely responsible for worker assignment.
Handling the events in bubbling order means mixing team and worker assignment somewhere at some point or making under the hood changes to assignment logic, which smells funny.
Example 2 --
Consider a Risk-like board game with dudes on a map but with a deep nesting of countries, provinces, cities etc, and where moving dudes from one country's province's city to another country's province's city would require handling higher level boundary changes before the lower ones as they might interrupt the move. If the game is set in stone a master handler is probably fine, but if its life involves more churn ('interplanetary travel dlc incoming!'), more narrowly scoped top-down handling could be useful.
Hi,
Will it be possible to make animations for drag and drop within simple lists similar to react-beautiful-dnd, when dragging an item moves the other items around using css transforms ?
I acknowledge that this might not be the recommended approach due to potential user experience and performance concerns. Nonetheless, I'd like to know if there are any inherent limitations that might hinder someone from replicating an experience similar to what was offered by react-beautiful-dnd.
Hello there!
I was wondering the choice for the drag preview to use ReactDom.render()
instead of the ReactDom.createRoot().render()
got a warning in react that it'll run in react 17 instead of react 18
// in the docs
setCustomNativeDragPreview({
render({ container }) {
ReactDOM.render(<Preview item={item} />, container);
return function cleanup() {
ReactDOM.unmountComponentAtNode(container);
};
},
nativeSetDragImage,
});
vs
setCustomNativeDragPreview({
render: ({ container }) => {
const root = ReactDOM.createRoot(container);
root.render(<Preview item={item} />);
return () => root.unmount();
},
nativeSetDragImage,
});
thank you again for making a great tool!
I am trying to implement a 'tree' style drag and drop interface which will have different types of elements. For logical reasons, certain element types can only exist as children of other element types.
I have tried to implement a basic example using the 'section' and 'field' types. sections can be children of the root or of other sections, fields can only be children of sections.
I have added the dropTargetForElements adaptor to the root element, and configured it's 'canDrop' to only return true for 'section' type items, but 'field' type items can still be successfully dropped there.
I have slightly adjusted the tree example to include the type on the TreeItem, and to only return canDrop: true on the root element if the item.type === 'section'
.
If you check the console.logs, the 'section' items return canDrop: true
, and the field items return canDrop: false
, yet the field items can still be dropped.
https://codesandbox.io/p/sandbox/tree-forked-83sfc7?file=%2Fexample.tsx%3A251%2C26
I assume this is a bug, as this behaviour does not match the typedoc for canDrop:
Used to conditionally block dropping. By default a drop target can be dropped on.
Return false if you want to block a drop.
If this is not a bug and is the intended behaviour of canDrop on the root elements, is there another way to achieve my goal?
Unfortunately, drag and drop is currently not supported on Firefox on Android. The global usage Firefox on Android is 0.29%
(at 2nd April 2024). I'll raise an issue with Firefox to see what their plans are regarding Android support.
Mitigation (not ideal)
If you wire up alternative flows as per our accessibility guide, users on Firefox will be able to achieve the same outcomes as drag and drop operations without drag and drop
Not sure if this is a bug or as designed.
In the tutorial, the draggable ref is to an img
but on iOS dragging requires press-and-hold which when performed on an img
, can activate the OS's image action state if a move does not occur soon enough. On an actual device, when the finger obscures the visual draggable-state change, it's easy to hold 'too long'.
Question:
Should draggable
be expected to produce consistent cross-platform behavior absent side effects like these by default or is it up to the developer to be aware of and handle these kinds of situations?
In desktop Safari 17.4.1 a drag preview created by rendering an element into the provided container via react's createRoot can result in other elements in the top left being visually included in the preview image.
Repro:
https://codesandbox.io/p/sandbox/safari-pdnd-preview-issue-react-2cl6k4?file=%2Fsrc%2FDragMe.jsx
Works fine in Chrome 124 and FF 125.
I'm running into an issue that has been a challenge to debug. Basically, the drop target isn't being detected until the mouse moves off the window and then back onto the window.
We are using a the "tree" component and when reordering items in the tree everything works. However, we also want to support dragging a new item (from outside the tree but still in same window) into the tree and creating it. In that case, the drop targets are only found when the user moves cursor off the window and back on. Very strange.
System:
OS: macOS 14.5
CPU: (12) arm64 Apple M3 Pro
Memory: 204.63 MB / 36.00 GB
Shell: 5.9 - /bin/zsh
Browsers:
Chrome: 124.0.6367.61
Safari: 17.5
I tried to implement drag and drop in an iframe, but seems no event is firing. I tried with your example. I cannot find any iframe specific settings in it. but it clone the whole react app into iframe somehow, how and why this is happened, I have no idea(i did not find the code) 😞
Here is my implementation
Works fine outside of iframe, but not working inside iframe.
In react-dnd I was doing this:
import {useContext, useEffect} from 'react';
import {DndContext} from 'react-dnd';
import {FrameContext} from 'react-frame-component';
const DndProviderWrapper = ({children}) => {
const {dragDropManager} = useContext(DndContext);
const {window} = useContext(FrameContext);
useEffect(() => {
// @ts-ignore
dragDropManager.getBackend().addEventListeners(window);
});
return children;
};
export default DndProviderWrapper;
But I cannot figure out how to do this in pragmatic
Assume the link should go here : https://atlassian.design/components/pragmatic-drag-and-drop/core-package/adapters/element/about
When trying to experiment with pragmatic-drag-and-drop I stumbled upon this (what seems to me very basic) problem.
Environment: Node v21.7.3, NPM 10.5.2
To reproduce the issue I started a new vite project using the react-swc-ts
template:
npm create vite@latest dnd-example -- --template react-swc-ts
cd dnd-example
npm install
The created project uses TypeScript ^5.2.2 and vite ^5.2.0.
Then I installed the pragmatic-drag-and-drop dependency:
npm install @atlaskit/pragmatic-drag-and-drop
and added the following import to the generated src/App.tsx
file:
import {draggable} from "@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter"
Running
npm run build
then prints this error message:
src/App.tsx:5:25 - error TS2307: Cannot find module '@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter' or its corresponding type declarations.
5 import {draggable} from "@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter"
Do I need to separately install some types for pragmatic-drag-and-drop?
When dragging a component that is out of view (see left cards in image below) the dragged card is cropped and only displays what was shown in the view before. My board roughly follows the example board.
The board in the examples does not have this issue, so I am missing something. Since my board doesn't follow the example strictly (I don't use Atlassian dependencies and persist the board in a DB) it is difficult to troubleshoot though. I've looked through the docs but can't find what is responsible to track the component UI when it is outside of the view.
A few folks have linked me about @formkit/drag-and-drop
as it is another recent drag and drop library, and so I thought I would create this issue containing my thoughts to link people to if they ask me about it.
I generally try to stay away from detailed public solution comparison for the things that I build, so I will be keeping my thoughts pretty high level. I want to encourage and celebrate anybody who puts something out there for people to use. The reality is that in open source software, that all projects are being inspired from the ones around them.
While not related to the code itself, the effort Formkit put into their branding and their website should be celebrated. It's really incredible!
@formkit/drag-and-drop
appears to be a library that enables the quick setting up of single level sortable experiences, and a way to connect those sortable experiences together. It looks to be targeting a similar problem domain to Sortable
Pragmatic drag and drop is a lower level abstraction, and can be used to power any experience, including: sortable lists, connected sortable lists, boards, trees, tables, site builders, resizing, drawing, file dropping, text selection dragging, dragging into and out of iframes - and anything else that is possible on the web platform. Pragmatic drag and drop is a low level abstraction layer on the web platform itself. I suspect that (most of) the API and behaviour of @formkit/drag-and-drop
could be built on top of Pragmatic drag and drop.
I hope this is helpful,
Cheers
I've implemented a tree drag and drop and for business logic reasons certain items cannot be dropped onto other items.
I have implemented a 'make-child' block, but when dragging items over these blocked items the indicators are very janky, they flash in and out as the mouse moves. This is because the instruction is being set to 'instruction blocked' when the mouse is near the center of the items.
It would be nice for new users to be able to configure the zones via arguments, instead of having to clone the hitbox code and customise it yourself (which is straightforward enough once you're used to pragmatic-drag-and-drop).
I'm thinking it would be great to pass in some enabled/disabled flags along with percentage values for the different zones. This would allow users to remove zones for instructions which are blocked completely, i.e. only allow make child, only allow reorder-above/below, only have blocked, etc.
I'm interested in using Pragmatic Drag-and-Drop within an iframe component like react-frame-component. Is this supported?
I've seen some discussions online suggesting it might be possible with limitations, but I couldn't find any official documentation on this.
Trying out the examples in the documentation on a surface device with latest windows and edge, none of them seem to work with touch - pressing and holding does not start the drag and drop process.
I've tried to follow the instructions here: https://atlassian.design/components/pragmatic-drag-and-drop/optional-packages/react-beautiful-dnd-migration/code
But I can't "select the codemod based on which version of react-beautiful-dnd
you are migrating from" as there are no adoption codemods visible in the list when I run the command.
I see this:
On your webpage you have examples listed:
Examples webpage
These examples run with react 16 which was released in 2019.
After upgrading to the latest react version you have a lot of error to fix to get it running:
Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node
. Still no idea how to fix this.tiny-invariant
. You can only install this package with the force
npm attribute// eslint-disable-next-line @atlaskit/design-system/no-banned-imports
is prohibiting your code to runPortal
necessary? Portal is a performance killer, but I never worked with portals so maybe it is still needed.ReactDOM.createPortal
ist not supported in react18, use createPortal
insteadReactDOM.render
ist not supported in react18 use createRoot
(thanks buzzie-bee)Maybe you can update your code to a more recent version. Thx
I was checking out some of the optional packages that were suggested and got an error when I tried to install @atlaskit/pragmatic-drag-and-drop-react-accessibility
, as it had React version ^16.8.0 as a peer dependency. This seems like it may be an error, though I noticed most of codesandbox examples are using React 16. Are newer versions of React supported?
Edit: It seemed to work fine in React 18. Also, I noticed the same peer dependency issue in several other packages (ex: @atlaskit/tokens).
Hi, congratulations on the release!
Im trying to make a simple example to work in vue 3 with video.
Im trying to copy the Grid example from the docs, and just making a simple swap work in a grid or list.
https://codesandbox.io/p/devbox/pragmatic-dnd-forked-nmlnsj
My issue is that it will replace the actual item, but the video does not seem to want to come along.
If I seek to 50% in the video from the "A" container, then drag it to "B" and drop, it will swap the items but the video that i was hoping would move to "B" is still left in "A" on 50%.
What am I doing wrong?
Currently for drag to start, you need to press and hold (this was not very obvious so I initially thought drag doesn't work in mobile).
The problem is when what you are trying to drag is an image - in that case iOS does this.
Is it possible to just drag instead of having to press and hold? Dndkit / formkit dnd etc works that way
Hello, it would be great if there was a built-in way for making vertical trees
So that the hitboxes for "reorder above" would be on the left and hitboxes for "reorder below" -- on the right.
https://atlassian.design/components/pragmatic-drag-and-drop/optional-packages/hitbox/about#tree-item
Or another solution would be to add something like maxDetectionDistance
parameter to attachClosestEdge()
function to allow for detecting the hitbox of the container
EDIT:
For my use case I just patched the hitbox calculating function with pnpm patch
(the code bellow also in /es2019
and /cjs
directories):
diff --git a/dist/esm/tree-item.js b/dist/esm/tree-item.js
index 47356cf1b195747f52f47b73ee8c0dd8b7155f82..7ca74ff683bcfc12f33c41c87cddea2c9320a376 100644
--- a/dist/esm/tree-item.js
+++ b/dist/esm/tree-item.js
@@ -14,16 +14,16 @@ function getCenter(rect) {
function standardHitbox(_ref) {
var client = _ref.client,
borderBox = _ref.borderBox;
- var quarterOfHeight = borderBox.height / 4;
+ var quarterOfHeight = borderBox.width / 4;
// In the top 1/4: reorder-above
// On the line = in the top 1/4 to give this zone a bit more space
- if (client.y <= borderBox.top + quarterOfHeight) {
+ if (client.x <= borderBox.left + quarterOfHeight) {
return 'reorder-above';
}
// In the bottom 1/4: reorder-below
// On the line = in the bottom 1/4 to give this zone a bit more space
- if (client.y >= borderBox.bottom - quarterOfHeight) {
+ if (client.x >= borderBox.right - quarterOfHeight) {
return 'reorder-below';
}
return 'make-child';
And then the drop indicator is hacked like this:
import { DropIndicator as TreeItemDropIndicator } from "@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/tree-item";
import { DropIndicator as BoxDropIndicator } from "@atlaskit/pragmatic-drag-and-drop-react-drop-indicator/box";
export type DropIndicatorProps = {
instruction: Instruction;
gap?: string;
};
export const DropIndicator = ({ instruction, gap }: DropIndicatorProps) => {
if (instruction.type === "reorder-above") {
return <BoxDropIndicator edge={"left"} gap={gap} />;
}
if (instruction.type === "reorder-below") {
return <BoxDropIndicator edge={"right"} gap={gap} />;
}
return <TreeItemDropIndicator instruction={instruction} />;
};
I just don't know how to explain what is happening. Please refer to video attached.
Below is my typescript code for dragging on each task card.
useEffect(() => {
const el = dndRef.current
return draggable({
element: el!,
})
}, [])
At the moment all commit messages are "Synchronise latest changes".
I'm super curious to try out this lib, but since it's also in an early stage, seeing what's changing / fixed is important. Seeing only opaque commit messages is a bit scary, as we don't know what's happening
I think Seeing the commit changes would be super helpful :)
Would it be possible to have the bot make fast forward merges when syncing?
SUPER curious to try this out. It looks like a lot of care and engineering effort was done here!
I'm excited to dig more into this library and so far I am loving the approach of a small core and small utility libraries around it. One thing that I think might get in the way for adoption is the dependency on @emotion/react
for the drop indicator package.
Emotion is a great library that I have used for many years but it is quite large and it also doesn't work (well) with modern react features like streaming rendering or RSC.
Looking at the usage, it seems possible to provide an additional package that just uses a pre-compiled css file instead of emotion. Would you be open to that?
This is a suggestion to implement some simpler examples without Atlassian dependencies.
The documentation for this project is very good, but the examples are all quite large with heavy dependencies on Atlassian components and tools.
The List Example is a great starting point for what I'd like to build, but it's overwhelming to try and recreate locally by copy pasting the example code.
It might be possible for prospective users to dig into the Atlassian code base to figure out how to replicate for example the Box component with plain jsx and their own styling, but with how many dependencies even the Box component is a major effort to try to understand what's going on.
It would very helpful to adopters of this package if we could have some versions of the examples using plain JSX elements and either plain css files or tailwind so it's easier to understand how to recreate the examples locally.
Ideally the examples should only have imports from React and pragmatic-drag-and-drop, and if you copy pasted them into a project with those dependencies installed it should just work.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.