Skip to content

Commit 4c621cb

Browse files
Copilotrenemadsen
andcommitted
Revert navigation menu and visual editor to dragula implementation
Co-authored-by: renemadsen <[email protected]>
1 parent 33db399 commit 4c621cb

File tree

8 files changed

+80
-170
lines changed

8 files changed

+80
-170
lines changed

eform-client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
"luxon": "^3.7.2",
100100
"moment": "2.30.1",
101101
"ng-gallery": "^12.0.0",
102+
"ng2-dragula": "^6.0.0",
102103
"ng2-file-upload": "^9.0.0",
103104
"ng2-pdf-viewer": "^10.3.4",
104105
"ngx-auto-unsubscribe": "3.0.1",
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
export * from './navigation-menu.service';
2-
export * from './navigation-menu-drag-drop.service';

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

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,23 @@
2323
<mat-accordion multi="true" displayMode="flat" class="mr-2">
2424
<mat-expansion-panel *ngFor="
2525
let menuTemplate of navigationMenuModel.menuTemplates;
26-
let templateIndex = index" class="menuTemplate">
26+
let templateIndex = index" id="menuTemplate">
2727
<mat-expansion-panel-header>
2828
<mat-panel-title>
2929
{{ menuTemplate.name }}
3030
</mat-panel-title>
3131
</mat-expansion-panel-header>
3232
<div
3333
class="dragula-template"
34-
cdkDropList
35-
[cdkDropListData]="menuTemplate.items"
36-
[cdkDropListConnectedTo]="connectedLists"
37-
[cdkDropListEnterPredicate]="allowDropPredicate"
34+
dragula="MENU_ITEMS"
3835
id="mainMenu"
36+
[(dragulaModel)]="menuTemplate.items"
3937
>
4038
<app-navigation-menu-template-item
4139
*ngFor="
4240
let menuTemplateItem of menuTemplate.items;
4341
let itemIndex = index
4442
"
45-
cdkDrag
46-
(cdkDragMoved)="onDragMoved($event)"
47-
(cdkDragReleased)="onDragReleased($event)"
4843
[item]="menuTemplateItem"
4944
[itemIndex]="itemIndex"
5045
[templateIndex]="templateIndex"
@@ -61,20 +56,16 @@
6156
<mat-accordion
6257
multi="true"
6358
displayMode="flat"
64-
cdkDropList
65-
id="actualMenuList"
66-
[cdkDropListData]="navigationMenuModel.actualMenu"
67-
[cdkDropListConnectedTo]="connectedLists"
68-
[cdkDropListEnterPredicate]="allowDropPredicate"
69-
(cdkDropListDropped)="dropMenuItem($event)">
59+
dragula="MENU_ITEMS"
60+
class="dragula-item"
61+
[dragulaModel]="navigationMenuModel.actualMenu"
62+
(dragulaModelChange)="menuItemModelChange($event)">
7063
<mat-expansion-panel *ngFor="
7164
let menuItem of navigationMenuModel.actualMenu;
7265
let firstLevelIndex = index"
7366
[hideToggle]="menuItem.type !== menuItemTypes.Dropdown"
74-
cdkDrag
75-
(cdkDragMoved)="onDragMoved($event)"
76-
(cdkDragReleased)="onDragReleased($event)"
77-
class="menu_item"
67+
class="dragula-item"
68+
id="menuItems"
7869
>
7970
<mat-expansion-panel-header>
8071
<mat-panel-title>
@@ -101,24 +92,18 @@
10192
</mat-panel-title>
10293
</mat-expansion-panel-header>
10394
<div
104-
cdkDropList
105-
class="cdk-drop-list dropdownBody"
106-
[id]="getDropdownId(firstLevelIndex)"
107-
[cdkDropListData]="menuItem.children"
108-
[cdkDropListConnectedTo]="connectedLists"
109-
[cdkDropListEnterPredicate]="allowDropPredicate"
110-
(cdkDropListDropped)="dropMenuItemChild($event, firstLevelIndex)"
95+
id="dropdownBody"
96+
class="dragula-dropdown"
97+
dragula="MENU_ITEMS"
11198
*ngIf="menuItem.type === menuItemTypes.Dropdown"
99+
[(dragulaModel)]="menuItem.children"
112100
style="min-height: 50px;"
113101
>
114102
<ng-container
115103
*ngFor="let menuItemChild of menuItem.children;
116104
let secondLevelIndex = index"
117105
>
118106
<app-navigation-menu-item
119-
cdkDrag
120-
(cdkDragMoved)="onDragMoved($event)"
121-
(cdkDragReleased)="onDragReleased($event)"
122107
(itemDelete)="onItemDelete($event, firstLevelIndex, secondLevelIndex)"
123108
(itemEdit)="onItemEdit($event, firstLevelIndex, secondLevelIndex)"
124109
[item]="menuItemChild"

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

Lines changed: 35 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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';
1+
import { Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
2+
import { DragulaService } from 'ng2-dragula';
33
import {
44
NavigationMenuItemIndexedModel,
55
NavigationMenuItemModel,
@@ -10,7 +10,6 @@ import {
1010
import {
1111
NavigationMenuService,
1212
SecurityGroupsService,
13-
NavigationMenuDragDropService,
1413
} from 'src/app/common/services';
1514
import {Subscription, take} from 'rxjs';
1615
import { AutoUnsubscribe } from 'ngx-auto-unsubscribe';
@@ -35,21 +34,20 @@ import {loadAppMenu, selectCurrentUserLocale} from 'src/app/state';
3534
styleUrls: ['./navigation-menu-page.component.scss'],
3635
standalone: false
3736
})
38-
export class NavigationMenuPageComponent implements OnInit, OnDestroy, AfterViewInit {
37+
export class NavigationMenuPageComponent implements OnInit, OnDestroy {
3938
private authStore = inject(Store);
39+
private dragulaService = inject(DragulaService);
4040
private navigationMenuService = inject(NavigationMenuService);
4141
private securityGroupsService = inject(SecurityGroupsService);
4242
private authStateService = inject(AuthStateService);
4343
private appMenuStateService = inject(AppMenuStateService);
4444
private dialog = inject(MatDialog);
4545
private overlay = inject(Overlay);
4646
private store = inject(Store);
47-
private dragDropService = inject(NavigationMenuDragDropService);
4847

4948

5049
@ViewChild('resetMenuModal')
5150
resetMenuModal: NavigationMenuResetComponent;
52-
@ViewChildren(CdkDropList) dropLists?: QueryList<CdkDropList>;
5351
securityGroups: CommonDictionaryModel[] = [];
5452
navigationMenuModel: NavigationMenuModel = new NavigationMenuModel();
5553

@@ -66,60 +64,40 @@ export class NavigationMenuPageComponent implements OnInit, OnDestroy, AfterView
6664
return NavigationMenuItemTypeEnum;
6765
}
6866

69-
getDropdownId(index: number): string {
70-
return `dropdown-${index}`;
71-
}
72-
73-
get connectedDropdownIds(): string[] {
74-
return this.navigationMenuModel.actualMenu
75-
.map((item, index) => item.type === NavigationMenuItemTypeEnum.Dropdown ? this.getDropdownId(index) : null)
76-
.filter(id => id !== null) as string[];
77-
}
78-
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-
9367
constructor() {
68+
const dragulaService = this.dragulaService;
69+
70+
dragulaService.createGroup('MENU_ITEMS', {
71+
moves: (el, container, handle) => {
72+
return handle.classList.contains('dragula-handle');
73+
},
74+
copy: (el, source) => {
75+
return source.id === 'mainMenu' || source.id === 'pluginMenu';
76+
},
77+
copyItem: (data: NavigationMenuItemModel) => {
78+
return { ...data, type: NavigationMenuItemTypeEnum.Link, isInternalLink: true, };
79+
},
80+
accepts: (el, target) => {
81+
// To avoid dragging from right to left container
82+
return (
83+
(target.classList.contains('dragula-item') ||
84+
(target.classList.contains('dragula-dropdown') &&
85+
!el.classList.contains('dragula-dropdown'))) &&
86+
target.id !== 'mainMenu' &&
87+
target.id !== 'pluginMenu'
88+
);
89+
},
90+
});
91+
this.dragulaService.drop('MENU_ITEMS').subscribe(({ name }) => {
92+
this.dragulaService.find(name).drake.cancel(true);
93+
});
9494
}
9595

9696
ngOnInit(): void {
9797
this.getNavigationMenu();
9898
this.getSecurityGroups();
9999
}
100100

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-
// Subscribe to changes in drop lists (for dynamically added dropdowns)
109-
this.dropLists.changes.subscribe(() => {
110-
// Use setTimeout to defer the update to avoid ExpressionChangedAfterItHasBeenCheckedError
111-
// This ensures the update happens after the current change detection cycle
112-
setTimeout(() => {
113-
// Clear and re-register all drop lists when the list changes
114-
this.dragDropService.dropLists = [];
115-
this.dropLists?.forEach(dropList => {
116-
this.dragDropService.register(dropList);
117-
});
118-
});
119-
});
120-
}
121-
}
122-
123101
getSecurityGroups() {
124102
this.securityGroupsSub$ = this.securityGroupsService
125103
.getSecurityGroupsDictionary()
@@ -153,49 +131,10 @@ export class NavigationMenuPageComponent implements OnInit, OnDestroy, AfterView
153131
...this.navigationMenuModel.actualMenu,
154132
model,
155133
];
156-
// Trigger change detection and update drop lists after the new item is rendered
157-
// This ensures newly added dropdowns are immediately available as drop targets
158-
setTimeout(() => {
159-
if (this.dropLists) {
160-
this.dragDropService.dropLists = [];
161-
this.dropLists.forEach(dropList => {
162-
this.dragDropService.register(dropList);
163-
});
164-
}
165-
});
166-
}
167-
168-
dropMenuItem(event: CdkDragDrop<NavigationMenuItemModel[]>) {
169-
if (event.previousContainer === event.container) {
170-
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
171-
} else {
172-
// Copy from template menu
173-
const item = {
174-
...event.previousContainer.data[event.previousIndex],
175-
type: NavigationMenuItemTypeEnum.Link,
176-
isInternalLink: true
177-
};
178-
this.navigationMenuModel.actualMenu.splice(event.currentIndex, 0, item);
179-
}
180-
this.navigationMenuModel.actualMenu = [...this.navigationMenuModel.actualMenu];
181-
}
182-
183-
dropMenuItemChild(event: CdkDragDrop<NavigationMenuItemModel[]>, parentIndex: number) {
184-
if (event.previousContainer === event.container) {
185-
moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
186-
} else {
187-
// Copy from template menu
188-
const item = {
189-
...event.previousContainer.data[event.previousIndex],
190-
type: NavigationMenuItemTypeEnum.Link,
191-
isInternalLink: true
192-
};
193-
this.navigationMenuModel.actualMenu[parentIndex].children.splice(event.currentIndex, 0, item);
194-
}
195-
this.navigationMenuModel.actualMenu = [...this.navigationMenuModel.actualMenu];
196134
}
197135

198136
ngOnDestroy(): void {
137+
this.dragulaService.destroy('MENU_ITEMS');
199138
}
200139

201140
onItemDelete(
@@ -288,25 +227,15 @@ export class NavigationMenuPageComponent implements OnInit, OnDestroy, AfterView
288227
).name;
289228
}
290229

230+
menuItemModelChange($event: any[]) {
231+
this.navigationMenuModel.actualMenu = [...$event];
232+
}
233+
291234
getSecurityGroupNames(ids: number[]): string[] {
292235
return ids.map(id => {
293236
const group = this.securityGroups.find(g => g.id === id);
294237
return group ? group.name : '';
295238
}).filter(name => name !== '');
296239
}
297240

298-
/**
299-
* Handle drag move events to track hover state
300-
*/
301-
onDragMoved(event: CdkDragMove<any>) {
302-
this.dragDropService.dragMoved(event);
303-
}
304-
305-
/**
306-
* Handle drag release events to clear hover state
307-
*/
308-
onDragReleased(event: CdkDragRelease) {
309-
this.dragDropService.dragReleased(event);
310-
}
311-
312241
}

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
} from './components';
1414
import { NavigationMenuRouting } from './navigation-menu.routing';
1515
import { TranslateModule } from '@ngx-translate/core';
16-
import { DragDropModule } from '@angular/cdk/drag-drop';
16+
import { DragulaModule } from 'ng2-dragula';
1717
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
1818
import { EformSharedModule } from 'src/app/common/modules/eform-shared/eform-shared.module';
1919
import {MatCheckboxModule} from '@angular/material/checkbox';
@@ -27,7 +27,6 @@ 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';
3130

3231
@NgModule({
3332
declarations: [
@@ -45,7 +44,7 @@ import { NavigationMenuDragDropService } from 'src/app/common/services';
4544
CommonModule,
4645
NavigationMenuRouting,
4746
TranslateModule,
48-
DragDropModule,
47+
DragulaModule,
4948
FormsModule,
5049
EformSharedModule,
5150
ReactiveFormsModule,
@@ -61,8 +60,5 @@ import { NavigationMenuDragDropService } from 'src/app/common/services';
6160
MtxSelectModule,
6261
MatInputModule,
6362
],
64-
providers: [
65-
NavigationMenuDragDropService
66-
],
6763
})
6864
export class NavigationMenuModule {}

eform-client/src/app/modules/eforms/eform-visual-editor/components/eform-visual-editor-page/eform-visual-editor-container/eform-visual-editor-container.component.html

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,16 @@
7474
[ngClass]="{'collapsed' : isItemsCollapsed}"
7575
>
7676
<div
77-
cdkDropList
77+
dragula="CHECK_LISTS"
7878
id="editorChecklists"
79-
[cdkDropListData]="visualEditorTemplateModel.checkLists"
80-
(cdkDropListDropped)="dropChecklist($event)"
81-
class="cdk-drop-list"
79+
(dragulaModelChange)="dragulaPositionChecklistChanged($event)"
80+
[dragulaModel]="visualEditorTemplateModel.checkLists"
8281
>
8382
<app-visual-editor-checklist
8483
*ngFor="
8584
let checklist of visualEditorTemplateModel.checkLists;
8685
let i = index
8786
"
88-
cdkDrag
89-
[cdkDragDisabled]="!checklist.collapsed"
9087
[checklist]="checklist"
9188
[checklistRecursionIndexes]="[i]"
9289
[checklistIndex]="i"
@@ -97,7 +94,6 @@
9794
(addNewNestedField)="showFieldModal($event)"
9895
(deleteNestedField)="showDeleteFieldModal($event)"
9996
(editNestedField)="showFieldModal($event)"
100-
[ngClass]="{ 'no-drag': !checklist.collapsed }"
10197
(nestedFieldPositionChanged)="
10298
onNestedFieldPositionChanged($event)
10399
"
@@ -112,18 +108,17 @@
112108
!visualEditorTemplateModel.checkLists ||
113109
visualEditorTemplateModel.checkLists.length == 0
114110
"
115-
cdkDropList
111+
dragula="FIELDS"
116112
id="editorFields"
117-
class="editor-fields cdk-drop-list"
118-
[cdkDropListData]="visualEditorTemplateModel.fields"
119-
(cdkDropListDropped)="dropField($event)"
113+
class="editor-fields"
114+
(dragulaModelChange)="dragulaPositionFieldChanged($event)"
115+
[dragulaModel]="visualEditorTemplateModel.fields"
120116
>
121117
<app-visual-editor-field
122118
*ngFor="
123119
let field of visualEditorTemplateModel.fields;
124120
let i = index
125121
"
126-
cdkDrag
127122
[field]="field"
128123
[fieldIndex]="i"
129124
id="field_{{ i }}"

0 commit comments

Comments
 (0)