Skip to content

Commit 566b7a5

Browse files
Copilotrenemadsen
andcommitted
Add DragDropService and update navigation menu to support nested drag and drop
Co-authored-by: renemadsen <[email protected]>
1 parent 7c62dd2 commit 566b7a5

File tree

5 files changed

+88
-2
lines changed

5 files changed

+88
-2
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './navigation-menu.service';
2+
export * from './navigation-menu-drag-drop.service';
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Injectable, Inject } from '@angular/core';
2+
import { DOCUMENT } from '@angular/common';
3+
import { CdkDropList, CdkDragMove, CdkDragRelease } from '@angular/cdk/drag-drop';
4+
5+
/**
6+
* Service to handle nested drag and drop operations in navigation menu.
7+
* Based on: https://stackoverflow.com/a/67337935/2144807
8+
*
9+
* This service dynamically tracks which drop list is currently being hovered
10+
* during drag operations, enabling nested drag and drop functionality that
11+
* cdkDropListGroup doesn't support out of the box.
12+
*/
13+
@Injectable()
14+
export class NavigationMenuDragDropService {
15+
dropLists: CdkDropList[] = [];
16+
currentHoverDropListId?: string;
17+
18+
constructor(@Inject(DOCUMENT) private document: Document) {}
19+
20+
/**
21+
* Register a drop list with the service
22+
*/
23+
public register(dropList: CdkDropList) {
24+
this.dropLists.push(dropList);
25+
}
26+
27+
/**
28+
* Track which drop list is currently under the pointer during drag
29+
*/
30+
dragMoved(event: CdkDragMove<any>) {
31+
const elementFromPoint = this.document.elementFromPoint(
32+
event.pointerPosition.x,
33+
event.pointerPosition.y
34+
);
35+
36+
if (!elementFromPoint) {
37+
this.currentHoverDropListId = undefined;
38+
return;
39+
}
40+
41+
const dropList = elementFromPoint.classList.contains('cdk-drop-list')
42+
? elementFromPoint
43+
: elementFromPoint.closest('.cdk-drop-list');
44+
45+
if (!dropList) {
46+
this.currentHoverDropListId = undefined;
47+
return;
48+
}
49+
50+
this.currentHoverDropListId = dropList.id;
51+
}
52+
53+
/**
54+
* Clear hover state when drag is released
55+
*/
56+
dragReleased(event: CdkDragRelease) {
57+
this.currentHoverDropListId = undefined;
58+
}
59+
}

eform-client/src/app/modules/advanced/modules/navigation-menu/components/navigation-menu-page/navigation-menu-page.component.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
</div>
2020
</eform-new-subheader>
2121

22-
<div class="d-flex flex-row align-items-start">
22+
<div class="d-flex flex-row align-items-start" cdkDropListGroup>
2323
<mat-accordion multi="true" displayMode="flat" class="mr-2">
2424
<mat-expansion-panel *ngFor="
2525
let menuTemplate of navigationMenuModel.menuTemplates;
@@ -42,6 +42,8 @@
4242
let itemIndex = index
4343
"
4444
cdkDrag
45+
(cdkDragMoved)="onDragMoved($event)"
46+
(cdkDragReleased)="onDragReleased($event)"
4547
[item]="menuTemplateItem"
4648
[itemIndex]="itemIndex"
4749
[templateIndex]="templateIndex"
@@ -69,6 +71,8 @@
6971
let firstLevelIndex = index"
7072
[hideToggle]="menuItem.type !== menuItemTypes.Dropdown"
7173
cdkDrag
74+
(cdkDragMoved)="onDragMoved($event)"
75+
(cdkDragReleased)="onDragReleased($event)"
7276
class="menu_item"
7377
>
7478
<mat-expansion-panel-header>
@@ -111,6 +115,8 @@
111115
>
112116
<app-navigation-menu-item
113117
cdkDrag
118+
(cdkDragMoved)="onDragMoved($event)"
119+
(cdkDragReleased)="onDragReleased($event)"
114120
(itemDelete)="onItemDelete($event, firstLevelIndex, secondLevelIndex)"
115121
(itemEdit)="onItemEdit($event, firstLevelIndex, secondLevelIndex)"
116122
[item]="menuItemChild"

eform-client/src/app/modules/advanced/modules/navigation-menu/components/navigation-menu-page/navigation-menu-page.component.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
2-
import { CdkDragDrop, moveItemInArray, copyArrayItem, transferArrayItem } from '@angular/cdk/drag-drop';
2+
import { CdkDragDrop, moveItemInArray, copyArrayItem, transferArrayItem, CdkDragMove, CdkDragRelease } from '@angular/cdk/drag-drop';
33
import {
44
NavigationMenuItemIndexedModel,
55
NavigationMenuItemModel,
@@ -10,6 +10,7 @@ import {
1010
import {
1111
NavigationMenuService,
1212
SecurityGroupsService,
13+
NavigationMenuDragDropService,
1314
} from 'src/app/common/services';
1415
import {Subscription, take} from 'rxjs';
1516
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe';
@@ -43,6 +44,7 @@ export class NavigationMenuPageComponent implements OnInit, OnDestroy {
4344
private dialog = inject(MatDialog);
4445
private overlay = inject(Overlay);
4546
private store = inject(Store);
47+
private dragDropService = inject(NavigationMenuDragDropService);
4648

4749

4850
@ViewChild('resetMenuModal')
@@ -246,4 +248,18 @@ export class NavigationMenuPageComponent implements OnInit, OnDestroy {
246248
}).filter(name => name !== '');
247249
}
248250

251+
/**
252+
* Handle drag move events to track hover state
253+
*/
254+
onDragMoved(event: CdkDragMove<any>) {
255+
this.dragDropService.dragMoved(event);
256+
}
257+
258+
/**
259+
* Handle drag release events to clear hover state
260+
*/
261+
onDragReleased(event: CdkDragRelease) {
262+
this.dragDropService.dragReleased(event);
263+
}
264+
249265
}

eform-client/src/app/modules/advanced/modules/navigation-menu/navigation-menu.module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {MatDialogModule} from '@angular/material/dialog';
2727
import {MatFormFieldModule} from '@angular/material/form-field';
2828
import {MtxSelectModule} from '@ng-matero/extensions/select';
2929
import {MatInputModule} from '@angular/material/input';
30+
import { NavigationMenuDragDropService } from 'src/app/common/services';
3031

3132
@NgModule({
3233
declarations: [
@@ -60,5 +61,8 @@ import {MatInputModule} from '@angular/material/input';
6061
MtxSelectModule,
6162
MatInputModule,
6263
],
64+
providers: [
65+
NavigationMenuDragDropService
66+
],
6367
})
6468
export class NavigationMenuModule {}

0 commit comments

Comments
 (0)