[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" class="adf-breadcrumb-container"
role="navigation" role="navigation"
[attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate" [attr.aria-label]="'BREADCRUMB.ARIA-LABEL.BREADCRUMB' | translate"
cdkDropListGroup
> >
@if (hasPreviousNodes()) { @if (hasPreviousNodes()) {
<button <button
@@ -47,6 +48,9 @@
[attr.data-automation-id]="'breadcrumb_' + item.name" [attr.data-automation-id]="'breadcrumb_' + item.name"
class="adf-breadcrumb-item-anchor" class="adf-breadcrumb-item-anchor"
(click)="onRoutePathClick(item, $event)" (click)="onRoutePathClick(item, $event)"
cdkDropList
(cdkDropListDropped)="onDrop($event)"
[id]="item.id"
> >
{{ item.name ?? '' | translate }} {{ item.name ?? '' | translate }}
</a> </a>
@@ -1,3 +1,5 @@
@use '../mat-selectors.scss' as ms;
.adf-breadcrumb { .adf-breadcrumb {
display: flex; display: flex;
flex: 1; flex: 1;
@@ -101,6 +103,11 @@
outline: 1px solid var(--mat-sys-outline-variant); outline: 1px solid var(--mat-sys-outline-variant);
outline-offset: -1px; outline-offset: -1px;
} }
&#{ms.$cdk-drop-list-dragging}:hover {
text-decoration: underline;
text-underline-offset: 4px;
}
} }
&-current { &-current {
@@ -35,10 +35,11 @@ import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { IconModule } from '@alfresco/adf-core'; import { IconModule } from '@alfresco/adf-core';
import { LiveAnnouncer } from '@angular/cdk/a11y'; import { LiveAnnouncer } from '@angular/cdk/a11y';
import { CdkDrag, CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop';
@Component({ @Component({
selector: 'adf-breadcrumb', selector: 'adf-breadcrumb',
imports: [IconModule, TranslatePipe, MatSelectModule], imports: [IconModule, TranslatePipe, MatSelectModule, CdkDropList],
templateUrl: './breadcrumb.component.html', templateUrl: './breadcrumb.component.html',
styleUrls: ['./breadcrumb.component.scss'], styleUrls: ['./breadcrumb.component.scss'],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
@@ -112,6 +113,9 @@ export class BreadcrumbComponent implements OnInit, OnChanges {
@Output() @Output()
navigate = new EventEmitter<PathElement>(); navigate = new EventEmitter<PathElement>();
@Output()
breadcrumbPathElementsChange = new EventEmitter<string[]>();
ngOnInit() { ngOnInit() {
this.transform = this.transform ? this.transform : null; this.transform = this.transform ? this.transform : null;
@@ -145,6 +149,11 @@ export class BreadcrumbComponent implements OnInit, OnChanges {
this.lastNodes = this.route; this.lastNodes = this.route;
this.previousNodes = null; this.previousNodes = null;
} }
this.breadcrumbPathElementsChange.emit(
this.lastNodes
.filter((_pathElement, i) => this.breadcrumbItemIsAnchor(i === this.lastNodes.length - 1))
.map((pathElement) => pathElement.id)
);
} }
open(): void { 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-select)="onNodeSelect($any($event).detail)"
(row-unselect)="onNodeUnselect($any($event).detail)" (row-unselect)="onNodeUnselect($any($event).detail)"
(sorting-changed)="onSortingChanged($any($event))" (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"> <div *ngIf="headerFilters">
<adf-filter-header <adf-filter-header
@@ -382,6 +382,20 @@ export class DocumentListComponent extends DataTableSchema implements OnInit, On
@Input() @Input()
displayDragAndDropHint = true; 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. * Indicates if the data is provided externally.
* If true the component won't fetch data itself * 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-select-arrow: '.mat-mdc-select-arrow';
$mat-evolution-chip-set: '.mdc-evolution-chip-set'; $mat-evolution-chip-set: '.mdc-evolution-chip-set';
$cdk-overlay-pane: '.cdk-overlay-pane'; $cdk-overlay-pane: '.cdk-overlay-pane';
$cdk-drop-list-dragging: '.cdk-drop-list-dragging';
$mat-list-item-disabled: '.mdc-list-item--disabled'; $mat-list-item-disabled: '.mdc-list-item--disabled';
$mat-switch: '.mdc-switch'; $mat-switch: '.mdc-switch';
@@ -239,6 +239,7 @@
'adf-datatable-body__dragging': isDraggingRow 'adf-datatable-body__dragging': isDraggingRow
}" }"
cdkDropList cdkDropList
[cdkDropListConnectedTo]="dropListConnectedTo"
[cdkDropListDisabled]="!enableDragRows" [cdkDropListDisabled]="!enableDragRows"
role="rowgroup" role="rowgroup"
> >
@@ -250,7 +251,7 @@
(cdkDragDropped)="onDragDrop($event)" (cdkDragDropped)="onDragDrop($event)"
(cdkDragStarted)="onDragStart()" (cdkDragStarted)="onDragStart()"
(cdkDragEnded)="onDragEnd()" (cdkDragEnded)="onDragEnd()"
[cdkDragBoundary]="'.adf-datatable-body'" [cdkDragBoundary]="dragBoundary"
[row]="row" [row]="row"
(select)="onEnterKeyPressed(row, $event)" (select)="onEnterKeyPressed(row, $event)"
(keyup)="onRowKeyUp(row, $event)" (keyup)="onRowKeyUp(row, $event)"
@@ -592,6 +593,77 @@
} }
</div> </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> </adf-datatable-row>
@if (isEmpty()) { @if (isEmpty()) {
<div role="row" class="adf-datatable-row"> <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%; 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 { DataCellEvent } from '../data-cell.event';
import { DataRowActionEvent } from '../data-row-action.event'; import { DataRowActionEvent } from '../data-row-action.event';
import { buffer, debounceTime, filter, map, share } from 'rxjs/operators'; 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 { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser'; import { DomSanitizer } from '@angular/platform-browser';
import { ResizeEvent } from '../../directives/resizable/types'; import { ResizeEvent } from '../../directives/resizable/types';
@@ -121,7 +121,8 @@ export type ShowHeaderMode = (typeof ShowHeaderMode)[keyof typeof ShowHeaderMode
AmountCellComponent, AmountCellComponent,
NumberCellComponent, NumberCellComponent,
MatTooltipModule, MatTooltipModule,
CdkDragPlaceholder CdkDragPlaceholder,
CdkDragPreview
], ],
templateUrl: './datatable.component.html', templateUrl: './datatable.component.html',
styleUrls: ['./datatable.component.scss'], styleUrls: ['./datatable.component.scss'],
@@ -227,6 +228,14 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
@Input() @Input()
stickyHeader: boolean = false; 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. */ /** Emitted when the user clicks a row. */
@Output() @Output()
rowClick = new EventEmitter<DataRowEvent>(); rowClick = new EventEmitter<DataRowEvent>();
@@ -125,6 +125,9 @@ export class DataColumnComponent implements OnInit {
@Input() @Input()
dateConfig?: DateConfig; dateConfig?: DateConfig;
@Input()
visibleInDragPreview?: boolean;
/** Data column header template */ /** Data column header template */
header?: TemplateRef<any>; header?: TemplateRef<any>;
@@ -45,6 +45,7 @@ export interface DataColumn<T = unknown> {
decimalConfig?: DecimalConfig; decimalConfig?: DecimalConfig;
dateConfig?: DateConfig; dateConfig?: DateConfig;
maxTextLength?: number; maxTextLength?: number;
visibleInDragPreview?: boolean;
} }
export interface LocaleConfig { export interface LocaleConfig {
@@ -50,4 +50,5 @@ export interface DocumentListPresetRef extends ExtensionElement {
draggable?: boolean; draggable?: boolean;
resizable?: boolean; resizable?: boolean;
maxTextLength?: number; maxTextLength?: number;
visibleInDragPreview?: boolean;
} }