Really it's a bit complex but let's go
The first we need take account to distribute a series of component is that we can use any way. I choose use "masonary" from bootstrap 4 (but you can make as you choose)
When we drag a cdkList and this is not distributed horizontal or vertical else a "grid" we enter in problems. so I choose create a series of cdkDropList with an unique element. But as I want not move while I'm dragging, when is dragging I put in a position absolute. So our array items will be like
[{data:1,style:null},{data:2,style:null}]
And when we are going to drag, we calcule the positions. For this, we need use ViewChildren to get the cdkDragList, but use {read:ElementRef} because we are only interested in the ElementRef
@ViewChildren(CdkDropList,{read:ElementRef}) cards:QueryList<ElementRef>
wrapperStyle={width:'',height:''}
calculePosition()
{
let width=0;
let height=0;
this.cards.forEach((x,index)=>{
const rect=x.nativeElement.getBoundingClientRect()
if (width<rect.left+rect.width)
width=rect.left+rect.width;
if (height<rect.top+rect.height)
height=rect.top+rect.height;
this.items[index].style={
position:'absolute',
top:rect.top+'px',
left:rect.left+'px',
width:rect.width+'px',
height:rect.height+'px'
}
this.onDrag=true;
})
this.wrapperStyle={width:width+'px',height:height+'px'}
}
Our .html can be like
<div [ngStyle]="onDrag?wrapperStyle:null" cdkDropListGroup
[ngClass]="onDrag?'position-relative':'card-columns'">
<ng-container *ngFor="let item of items;let index=index">
<div [ngStyle]="onDrag?item.style:null"
cdkDropList
orientation="vertical"
cdkDropListSortingDisabled="true"
[cdkDropListData]="index"
(cdkDropListDropped)="drop($event)"
>
<div class="card" cdkDrag (mousedown)="calculePosition()" >
<div #place>
</div>
<div *cdkDragPlaceholder></div>
</div>
</div>
</ng-container>
</div>
See how with the variable "onDrag" we use position absolute or not. See also that as "data" we put the "index" of the component. This allow us our function drop becomes like
drop(event: CdkDragDrop<any,any>) {
const currentIndex=event.container.data;
const previousIndex=event.previousContainer.data;
this.items=this.items.map((x,i)=>{
if (i==previousIndex)
return this.items[currentIndex]
if (i==currentIndex)
return this.items[previousIndex]
return x
})
this.onDrag=false;
}
Just only need attach to our "#place" the components (based in the "item.data")
For this, first use viewChildren with read:ViewContainerRef and use AfterViewInit
@ViewChildren('place', { read: ViewContainerRef }) places: QueryList<ViewContainerRef>
ngAfterViewInit()
{
this.places.forEach((x,index)=>{
switch (this.items[index].data)
{
case 0:
x.createComponent(HelloComponent)
break;
default:
x.createComponent(ByComponent)
break;
}
})
}
a little stackblizt
NOTE: You only need store in the localstore the array "items" to "save the configuration"