Skip to content

Commit

Permalink
fix(Admin UI): draggable tabs in form builder interfaces (#2305)
Browse files Browse the repository at this point in the history
see #2269

Co-authored-by: Sebastian <sebastian@aam-digital.com>
  • Loading branch information
Abhinegi2 and sleidig authored Mar 26, 2024
1 parent 60b31d8 commit b454f5c
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
<mat-tab-group [preserveContent]="true" #matTabGroup>
<mat-tab *ngFor="let tab of tabs; index as tabIndex">
<ng-template mat-tab-label>
@if (tabIndex === matTabGroup.selectedIndex) {
<app-admin-section-header
[(title)]="tab['title'] ?? tab['name']"
(remove)="
tabs.splice(tabIndex, 1); matTabGroup.selectedIndex = tabIndex - 1
"
></app-admin-section-header>
} @else {
<!-- only current tab can be renamed -->
{{ tab["title"] ?? tab["name"] }}
}
<div
class="drop-list flex-row"
[id]="'tabs-' + tabIndex"
cdkDropList
cdkDropListOrientation="horizontal"
(cdkDropListDropped)="drop($event)"
[cdkDropListConnectedTo]="getAllTabs(tabIndex)"
>
<div
cdkDrag
cdkDragLockAxis="x"
class="flex-row align-center drop-item gap-small"
>
<fa-icon icon="grip-vertical" size="xl" class="drag-handle"></fa-icon>
@if (tabIndex === matTabGroup.selectedIndex) {
<app-admin-section-header
[(title)]="tab['title'] ?? tab['name']"
(remove)="
tabs.splice(tabIndex, 1);
matTabGroup.selectedIndex = tabIndex - 1
"
></app-admin-section-header>
} @else {
<!-- only current tab can be renamed -->
{{ tab["title"] ?? tab["name"] }}
}
</div>
</div>
</ng-template>

<ng-template matTabContent>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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",
Expand All @@ -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<string[]>) {
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
}
}

0 comments on commit b454f5c

Please sign in to comment.