Skip to content

Commit aa6f113

Browse files
Copilotrenemadsen
andcommitted
Add cdkDropListEnterPredicate and dynamic drop list connections for nested drag and drop
Co-authored-by: renemadsen <[email protected]>
1 parent fa6c8a4 commit aa6f113

File tree

4 files changed

+71
-7
lines changed

4 files changed

+71
-7
lines changed

eform-client/src/app/common/services/navigation-menu/navigation-menu-drag-drop.service.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,31 @@ describe('NavigationMenuDragDropService', () => {
8282
service.dragMoved(mockEvent as CdkDragMove<any>);
8383
expect(service.currentHoverDropListId).toBe('parent-drop-list');
8484
});
85+
86+
it('should allow drop when currentHoverDropListId is null', () => {
87+
service.currentHoverDropListId = undefined;
88+
const mockDrag: any = {};
89+
const mockDrop: any = { id: 'test-drop-list' };
90+
91+
const result = service.isDropAllowed(mockDrag, mockDrop);
92+
expect(result).toBe(true);
93+
});
94+
95+
it('should allow drop when drop list id matches currentHoverDropListId', () => {
96+
service.currentHoverDropListId = 'test-drop-list';
97+
const mockDrag: any = {};
98+
const mockDrop: any = { id: 'test-drop-list' };
99+
100+
const result = service.isDropAllowed(mockDrag, mockDrop);
101+
expect(result).toBe(true);
102+
});
103+
104+
it('should not allow drop when drop list id does not match currentHoverDropListId', () => {
105+
service.currentHoverDropListId = 'other-drop-list';
106+
const mockDrag: any = {};
107+
const mockDrop: any = { id: 'test-drop-list' };
108+
109+
const result = service.isDropAllowed(mockDrag, mockDrop);
110+
expect(result).toBe(false);
111+
});
85112
});

eform-client/src/app/common/services/navigation-menu/navigation-menu-drag-drop.service.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Injectable, Inject } from '@angular/core';
22
import { DOCUMENT } from '@angular/common';
3-
import { CdkDropList, CdkDragMove, CdkDragRelease } from '@angular/cdk/drag-drop';
3+
import { CdkDropList, CdkDragMove, CdkDragRelease, CdkDrag } from '@angular/cdk/drag-drop';
44

55
/**
66
* Service to handle nested drag and drop operations in navigation menu.
@@ -56,4 +56,14 @@ export class NavigationMenuDragDropService {
5656
dragReleased(event: CdkDragRelease) {
5757
this.currentHoverDropListId = undefined;
5858
}
59+
60+
/**
61+
* Determine if dropping is allowed based on the current hover state
62+
*/
63+
isDropAllowed(drag: CdkDrag, drop: CdkDropList): boolean {
64+
if (this.currentHoverDropListId == null) {
65+
return true;
66+
}
67+
return drop.id === this.currentHoverDropListId;
68+
}
5969
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
class="dragula-template"
3434
cdkDropList
3535
[cdkDropListData]="menuTemplate.items"
36-
[cdkDropListConnectedTo]="['actualMenuList'].concat(connectedDropdownIds)"
36+
[cdkDropListConnectedTo]="connectedLists"
37+
[cdkDropListEnterPredicate]="allowDropPredicate"
3738
id="mainMenu"
3839
>
3940
<app-navigation-menu-template-item
@@ -63,7 +64,8 @@
6364
cdkDropList
6465
id="actualMenuList"
6566
[cdkDropListData]="navigationMenuModel.actualMenu"
66-
[cdkDropListConnectedTo]="['mainMenu']"
67+
[cdkDropListConnectedTo]="connectedLists"
68+
[cdkDropListEnterPredicate]="allowDropPredicate"
6769
(cdkDropListDropped)="dropMenuItem($event)"
6870
class="dragula-item">
6971
<mat-expansion-panel *ngFor="
@@ -104,7 +106,8 @@
104106
cdkDropList
105107
[id]="getDropdownId(firstLevelIndex)"
106108
[cdkDropListData]="menuItem.children"
107-
[cdkDropListConnectedTo]="['mainMenu']"
109+
[cdkDropListConnectedTo]="connectedLists"
110+
[cdkDropListEnterPredicate]="allowDropPredicate"
108111
(cdkDropListDropped)="dropMenuItemChild($event, firstLevelIndex)"
109112
*ngIf="menuItem.type === menuItemTypes.Dropdown"
110113
style="min-height: 50px;"

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
2-
import { CdkDragDrop, moveItemInArray, copyArrayItem, transferArrayItem, CdkDragMove, CdkDragRelease } from '@angular/cdk/drag-drop';
1+
import { Component, OnDestroy, OnInit, ViewChild, inject, AfterViewInit, ViewChildren, QueryList } from '@angular/core';
2+
import { CdkDragDrop, moveItemInArray, copyArrayItem, transferArrayItem, CdkDragMove, CdkDragRelease, CdkDropList, CdkDrag } from '@angular/cdk/drag-drop';
33
import {
44
NavigationMenuItemIndexedModel,
55
NavigationMenuItemModel,
@@ -35,7 +35,7 @@ import {loadAppMenu, selectCurrentUserLocale} from 'src/app/state';
3535
styleUrls: ['./navigation-menu-page.component.scss'],
3636
standalone: false
3737
})
38-
export class NavigationMenuPageComponent implements OnInit, OnDestroy {
38+
export class NavigationMenuPageComponent implements OnInit, OnDestroy, AfterViewInit {
3939
private authStore = inject(Store);
4040
private navigationMenuService = inject(NavigationMenuService);
4141
private securityGroupsService = inject(SecurityGroupsService);
@@ -49,6 +49,7 @@ export class NavigationMenuPageComponent implements OnInit, OnDestroy {
4949

5050
@ViewChild('resetMenuModal')
5151
resetMenuModal: NavigationMenuResetComponent;
52+
@ViewChildren(CdkDropList) dropLists?: QueryList<CdkDropList>;
5253
securityGroups: CommonDictionaryModel[] = [];
5354
navigationMenuModel: NavigationMenuModel = new NavigationMenuModel();
5455

@@ -75,6 +76,20 @@ export class NavigationMenuPageComponent implements OnInit, OnDestroy {
7576
.filter(id => id !== null) as string[];
7677
}
7778

79+
/**
80+
* Predicate function to determine if dropping is allowed
81+
*/
82+
allowDropPredicate = (drag: CdkDrag, drop: CdkDropList): boolean => {
83+
return this.dragDropService.isDropAllowed(drag, drop);
84+
};
85+
86+
/**
87+
* Get all connected drop lists from the service
88+
*/
89+
get connectedLists(): CdkDropList[] {
90+
return this.dragDropService.dropLists;
91+
}
92+
7893
constructor() {
7994
}
8095

@@ -83,6 +98,15 @@ export class NavigationMenuPageComponent implements OnInit, OnDestroy {
8398
this.getSecurityGroups();
8499
}
85100

101+
ngAfterViewInit(): void {
102+
// Register all drop lists with the service after view initialization
103+
if (this.dropLists) {
104+
this.dropLists.forEach(dropList => {
105+
this.dragDropService.register(dropList);
106+
});
107+
}
108+
}
109+
86110
getSecurityGroups() {
87111
this.securityGroupsSub$ = this.securityGroupsService
88112
.getSecurityGroupsDictionary()

0 commit comments

Comments
 (0)