diff --git a/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.html b/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.html index 2e5b7e90cc..8917d70ce5 100644 --- a/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.html +++ b/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.html @@ -1,17 +1,34 @@ - @if (tabIndex === matTabGroup.selectedIndex) { - - } @else { - - {{ tab["title"] ?? tab["name"] }} - } +
+
+ + @if (tabIndex === matTabGroup.selectedIndex) { + + } @else { + + {{ tab["title"] ?? tab["name"] }} + } +
+
diff --git a/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.scss b/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.scss index 1c7e1539cd..440f2af370 100644 --- a/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.scss +++ b/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.scss @@ -1,3 +1,4 @@ +@use "variables/colors"; :host ::ng-deep .mat-mdc-tab { // adjust tab header height to properly display mat-form-field for editing tab label @@ -6,3 +7,28 @@ app-admin-section-header { margin-bottom: -1em; } +.drag-handle { + cursor: move; + margin: auto; + color: colors.$accent; +} + +.cdk-drag-preview { + box-sizing: border-box; + border-radius: 4px; + box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); +} + +.cdk-drag-placeholder { + opacity: 0; +} + +.cdk-drag-animating { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} + +.drop-list.cdk-drop-list-dragging .drop-item:not(.cdk-drag-placeholder) { + transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); +} diff --git a/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.ts b/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.ts index 831c3468d7..31ca719e33 100644 --- a/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.ts +++ b/src/app/core/admin/building-blocks/admin-tabs/admin-tabs.component.ts @@ -19,6 +19,11 @@ import { } from "@angular/material/tabs"; import { MatTooltip } from "@angular/material/tooltip"; import { AdminTabTemplateDirective } from "./admin-tab-template.directive"; +import { + CdkDragDrop, + DragDropModule, + moveItemInArray, +} from "@angular/cdk/drag-drop"; /** * Building block for drag&drop form builder to let an admin user manage multiple tabs. @@ -50,6 +55,7 @@ import { AdminTabTemplateDirective } from "./admin-tab-template.directive"; MatTabLabel, MatTooltip, AdminTabTemplateDirective, + DragDropModule, ], templateUrl: "./admin-tabs.component.html", styleUrl: "./admin-tabs.component.scss", @@ -76,4 +82,40 @@ export class AdminTabsComponent< this.tabGroup.focusTab(newTabIndex); }); } + + /** + * A list of tab element ids required for linking drag&drop targets + * due to the complex template of tab headers. + * @param index + */ + getAllTabs(index: number) { + const allTabs = []; + for (let i = 0; i < this.tabs?.length; i++) { + if (i != index) { + allTabs.push("tabs-" + i); + } + } + + return allTabs; + } + + drop(event: CdkDragDrop) { + const previousIndex = parseInt( + event.previousContainer.id.replace("tabs-", ""), + ); + const currentIndex = parseInt(event.container.id.replace("tabs-", "")); + + const previouslySelectedTab = this.tabs[this.tabGroup.selectedIndex]; + + moveItemInArray(this.tabs, previousIndex, currentIndex); + + // re-select the previously selected tab, even after its index shifted + let shiftedSelectedIndex = this.tabs.indexOf(previouslySelectedTab); + if (shiftedSelectedIndex !== this.tabGroup.selectedIndex) { + this.tabGroup.selectedIndex = shiftedSelectedIndex; + this.tabGroup.focusTab(shiftedSelectedIndex); + } + + this.tabs = JSON.parse(JSON.stringify(this.tabs)); // Needed to avoid Angular Ivy render bug + } }