[ACS-12728] Allow drag and drop outside document list

This commit is contained in:
Aleksander Sklorz
2026-09-07 12:15:18 +02:00
parent e100d384fa
commit 9029643186
12 changed files with 144 additions and 5 deletions
@@ -4,6 +4,7 @@
class="adf-breadcrumb-container"
role="navigation"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate"
cdkDropListGroup
>
@if (hasPreviousNodes()) {
<button
@@ -47,6 +48,9 @@
[attr.data-automation-id]="'breadcrumb_' + item.name"
class="adf-breadcrumb-item-anchor"
(click)="onRoutePathClick(item, $event)"
cdkDropList
(cdkDropListDropped)="onDrop($event)"
[id]="item.id"
>
{{ item.name ?? '' | translate }}
</a>
@@ -1,3 +1,5 @@
@use '../mat-selectors.scss' as ms;
.adf-breadcrumb {
display: flex;
flex: 1;
@@ -101,6 +103,11 @@
outline: 1px solid var(--mat-sys-outline-variant);
outline-offset: -1px;
}
&#{ms.$cdk-drop-list-dragging}:hover {
text-decoration: underline;
text-underline-offset: 4px;
}
}
&-current {
@@ -35,10 +35,11 @@ import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { IconModule } from '@alfresco/adf-core';
import { LiveAnnouncer } from '@angular/cdk/a11y';
import { CdkDrag, CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop';
@Component({
selector: 'adf-breadcrumb',
imports: [IconModule, TranslatePipe, MatSelectModule],
imports: [IconModule, TranslatePipe, MatSelectModule, CdkDropList],
templateUrl: './breadcrumb.component.html',
styleUrls: ['./breadcrumb.component.scss'],
encapsulation: ViewEncapsulation.None,
@@ -112,6 +113,9 @@ export class BreadcrumbComponent implements OnInit, OnChanges {
@Output()
navigate = new EventEmitter<PathElement>();
@Output()
breadcrumbPathElementsChange = new EventEmitter<string[]>();
ngOnInit() {
this.transform = this.transform ? this.transform : null;
@@ -145,6 +149,11 @@ export class BreadcrumbComponent implements OnInit, OnChanges {
this.lastNodes = this.route;
this.previousNodes = null;
}
this.breadcrumbPathElementsChange.emit(
this.lastNodes
.filter((_pathElement, i) => this.breadcrumbItemIsAnchor(i === this.lastNodes.length - 1))
.map((pathElement) => pathElement.id)
);
}
open(): void {
@@ -218,4 +227,10 @@ export class BreadcrumbComponent implements OnInit, OnChanges {
}
}
}
onDrop(event: CdkDragDrop<CdkDrag>) {
if (!event.isPointerOverContainer) {
return;
}
}
}
@@ -29,7 +29,10 @@
(row-select)="onNodeSelect($any($event).detail)"
(row-unselect)="onNodeUnselect($any($event).detail)"
(sorting-changed)="onSortingChanged($any($event))"
[class.adf-datatable-gallery-thumbnails]="data.thumbnails">
[class.adf-datatable-gallery-thumbnails]="data.thumbnails"
[enableDragRows]="enableDragRows"
[dropListConnectedTo]="dropListConnectedTo"
[dragBoundary]="dragBoundary">
<div *ngIf="headerFilters">
<adf-filter-header
@@ -382,6 +382,20 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On
@Input()
displayDragAndDropHint = true;
/**
* Flag that enables dragging rows
*/
@Input()
enableDragRows = false;
/** The connected drop lists for drag and drop functionality. */
@Input()
dropListConnectedTo: string[] = [];
/** The boundary element for drag and drop functionality. */
@Input()
dragBoundary = '.adf-datatable-body';
/**
* Indicates if the data is provided externally.
* If true the component won't fetch data itself
@@ -40,5 +40,6 @@ $mat-evolution-chip-graphic: '.mdc-evolution-chip__graphic';
$mat-select-arrow: '.mat-mdc-select-arrow';
$mat-evolution-chip-set: '.mdc-evolution-chip-set';
$cdk-overlay-pane: '.cdk-overlay-pane';
$cdk-drop-list-dragging: '.cdk-drop-list-dragging';
$mat-list-item-disabled: '.mdc-list-item--disabled';
$mat-switch: '.mdc-switch';
@@ -239,6 +239,7 @@
'adf-datatable-body__dragging': isDraggingRow
}"
cdkDropList
[cdkDropListConnectedTo]="dropListConnectedTo"
[cdkDropListDisabled]="!enableDragRows"
role="rowgroup"
>
@@ -250,7 +251,7 @@
(cdkDragDropped)="onDragDrop($event)"
(cdkDragStarted)="onDragStart()"
(cdkDragEnded)="onDragEnd()"
[cdkDragBoundary]="'.adf-datatable-body'"
[cdkDragBoundary]="dragBoundary"
[row]="row"
(select)="onEnterKeyPressed(row, $event)"
(keyup)="onRowKeyUp(row, $event)"
@@ -592,6 +593,77 @@
}
</div>
}
<div *cdkDragPreview class="adf-datatable-row__dragging-preview">
@for (col of getVisibleColumns(); track col.key) {
@if (col.visibleInDragPreview) {
@if (!col.template) {
<div class="adf-datatable-cell-container">
@switch (data.getColumnType(row, col)) {
@case ('image') {
<div class="adf-cell-value">
@if (isIconValue(row, col)) {
<mat-icon
[attr.aria-label]="col.srTitle ? (col.srTitle | translate) : null"
[attr.aria-hidden]="!asIconValue(row, col)"
[adf-icon]="asIconValue(row, col)"
/>
} @else {
@if (row.isSelected && !multiselect) {
<mat-icon class="adf-datatable-selected" svgIcon="selected" />
} @else {
<img
class="adf-datatable-center-img-ie"
[attr.aria-label]="
(data.getValue(row, col) | fileType) === 'disable'
? ('ADF-DATATABLE.ACCESSIBILITY.ICON_DISABLED' | translate)
: ('ADF-DATATABLE.ACCESSIBILITY.ICON_TEXT'
| translate
: {
type:
'ADF-DATATABLE.FILE_TYPE.' +
(data.getValue(row, col) | fileType | uppercase)
| translate
})
"
[attr.alt]="
(data.getValue(row, col) | fileType) === 'disable'
? ('ADF-DATATABLE.ACCESSIBILITY.ICON_DISABLED' | translate)
: ('ADF-DATATABLE.ACCESSIBILITY.ICON_TEXT'
| translate
: {
type:
'ADF-DATATABLE.FILE_TYPE.' +
(data.getValue(row, col) | fileType | uppercase)
| translate
})
"
src="{{ data.getValue(row, col) }}"
(error)="onImageLoadingError($event, row)"
/>
}
}
</div>
}
}
</div>
} @else {
<div class="adf-datatable-cell-container">
<div class="adf-cell-value">
<ng-container
[ngTemplateOutlet]="col.template"
[ngTemplateOutletContext]="{
$implicit: { data: data, row: row, col: col },
value: data.getValue(row, col, resolverFn)
}"
/>
</div>
</div>
}
}
}
</div>
<ng-container *cdkDragPlaceholder />
</adf-datatable-row>
@if (isEmpty()) {
<div role="row" class="adf-datatable-row">
@@ -768,3 +768,12 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width-1 !default;
max-height: 50%;
}
}
.adf-datatable-row__dragging-preview {
height: auto;
min-height: 0;
padding: 15px;
gap: 10px;
background-color: color-mix(in srgb, var(--mat-sys-surface) 87%, transparent);
box-shadow: var(--mat-sys-level2);
}
@@ -57,7 +57,7 @@ import { ObjectDataTableAdapter } from '../../data/object-datatable-adapter';
import { DataCellEvent } from '../data-cell.event';
import { DataRowActionEvent } from '../data-row-action.event';
import { buffer, debounceTime, filter, map, share } from 'rxjs/operators';
import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDragPlaceholder, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDragPlaceholder, CdkDragPreview, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { ResizeEvent } from '../../directives/resizable/types';
@@ -121,7 +121,8 @@ export type ShowHeaderMode = (typeof ShowHeaderMode)[keyof typeof ShowHeaderMode
AmountCellComponent,
NumberCellComponent,
MatTooltipModule,
CdkDragPlaceholder
CdkDragPlaceholder,
CdkDragPreview
],
templateUrl: './datatable.component.html',
styleUrls: ['./datatable.component.scss'],
@@ -227,6 +228,14 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
@Input()
stickyHeader: boolean = false;
/** The connected drop lists for drag and drop functionality. */
@Input()
dropListConnectedTo: string[] = [];
/** The boundary element for drag and drop functionality. */
@Input()
dragBoundary = '.adf-datatable-body';
/** Emitted when the user clicks a row. */
@Output()
rowClick = new EventEmitter<DataRowEvent>();
@@ -125,6 +125,9 @@ export class DataColumnComponent implements OnInit {
@Input()
dateConfig?: DateConfig;
@Input()
visibleInDragPreview?: boolean;
/** Data column header template */
header?: TemplateRef<any>;
@@ -45,6 +45,7 @@ export interface DataColumn<T = unknown> {
decimalConfig?: DecimalConfig;
dateConfig?: DateConfig;
maxTextLength?: number;
visibleInDragPreview?: boolean;
}
export interface LocaleConfig {
@@ -50,4 +50,5 @@ export interface DocumentListPresetRef extends ExtensionElement {
draggable?: boolean;
resizable?: boolean;
maxTextLength?: number;
visibleInDragPreview?: boolean;
}