I have a problem with Angular's Drag'n'drop, DropZone to be exact.
TL;DR
DropZone in Angular CDK Drag'n'Drop works out-of-box only for non-scroll containers. If it has a scroll you have to use cdkScrollable, but this makes the column move in 4 directions. This excludes its use on sticky headers - an attempt to use drag'n'drop ends with a scroll to the very beginning of the table.
Structure:
The brown container has overflow-x and overflow-y. Height is set to 100% of the screen height, where width matches the content.
The orange has position: relative. That's all.
The blue container contains divs with table headers, has position: sticky, and hovers over the table while scrolling. I use it to simulate table headings with sticky behaviour. I had to do it this way due to the limited support for any positions (fixed, sticky, absolute) in tables.
Green is a table with data.
Problem:
I have been assigned the task of preparing a table whose columns match the contents. It was required to be able to rearrange the columns using drag'n'drop. The result of my efforts was the above structure, which does not impress, but it works. However, there was a problem - if the screen is small and the columns are many, auto-scroll is needed after grabbing the element and moving it to the edge of the screen. I marked in pink what is currently displayed in the browser.
The solution turned out to be cdkScrollable, which I added to the main container with the scroll, i.e. the brown frame. Unfortunately, there is one bug that prevents me from completing the task - cdkScrollable scrolls in 4 directions. So if I am in the middle of the table and I decide to change the order of the columns, cdkScrollable catches me in the scrolling area at the top of the screen, which brings me back to the very beginning of the table.
What i tried:
- Event listener with scrollTop that neutralizes cdkScrollable
It doesn't work because cdkScrollable introduces EventListener in ngZone. As a result, we have two listeners - one adds / subtracts scrollTop, the other returns it to its previous state. As a result, the entire table flickers
- Turn off the scroll while dragging
It's useless. Turning off the overflow only hides the scroll, but not the scrolling mechanism itself. The scrolling mechanism cannot be turned off, you can only block specific user movements (e.g. touch move), but when cdkScrollable has an event listener, this method does not work.
- CDK Drag'n'Drop Insertion Point
Even though it is in the documentation (https://material.angular.io/cdk/drag-drop/overview#drag-preview-insertion-point) I was unable to connect it with the code. There is also no example of use. From the information on the github, there were plans to add this option, but they were eventually abandoned in favor of a custom class.
- Writing your own scroller
Works, but DragZone is equal to the width of the currently displayed screen(purple). Everything inside the scroll is ignored; dragzone does not move with the screen. I suspect the placement is counted from the body element, so it's impossible to select more than the screen width. In the photo below, 1270 is the current position of the mouse relative to the entire screen. The only way to go beyond this area is to position the mouse outside the browser.
- Rewrite CDK Drag'n'drop for desired functions
I thought about a directive that binds to @Host, but the CDK code is a nightmare. Without knowing the dependencies, I find it impossible.
Do you have any idea how to solve it?