UI for editing customizable dashboard views

This commit is contained in:
shamoon
2024-04-17 00:23:51 -07:00
parent 3abadf0516
commit abfdfafced
10 changed files with 281 additions and 31 deletions

View File

@@ -0,0 +1,24 @@
<div class="d-flex flex-row mt-2 align-items-center">
<ng-container i18n>Selected</ng-container>:
<div class="ms-2 d-flex flex-row gap-2 w-100"
cdkDropList #selectedList="cdkDropList"
cdkDropListOrientation="horizontal"
(cdkDropListDropped)="drop($event)"
[cdkDropListConnectedTo]="[unselectedList]">
@for (item of selectedItems; track item.id) {
<div class="badge bg-primary" cdkDrag>{{item.name}}</div>
}
</div>
</div>
<div class="d-flex flex-row mt-2 align-items-center bg-light p-2">
<ng-container i18n></ng-container>
<div class="d-flex flex-row gap-2 w-100"
cdkDropList #unselectedList="cdkDropList"
cdkDropListOrientation="horizontal"
(cdkDropListDropped)="drop($event)"
[cdkDropListConnectedTo]="[selectedList]">
@for (item of unselectedItems; track item.id) {
<div class="badge bg-secondary opacity-50" cdkDrag>{{item.name}}</div>
}
</div>
</div>

View File

@@ -0,0 +1,3 @@
.badge {
cursor: move;
}

View File

@@ -0,0 +1,99 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DragDropModule } from '@angular/cdk/drag-drop'
import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'
import { DragDropSelectComponent } from './drag-drop-select.component'
describe('DragDropSelectComponent', () => {
let component: DragDropSelectComponent
let fixture: ComponentFixture<DragDropSelectComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DragDropModule, FormsModule],
declarations: [DragDropSelectComponent],
}).compileComponents()
fixture = TestBed.createComponent(DragDropSelectComponent)
component = fixture.componentInstance
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
fixture.detectChanges()
})
it('should update selectedItems when writeValue is called', () => {
const newValue = ['1', '2', '3']
component.items = [
{ id: '1', name: 'Item 1' },
{ id: '2', name: 'Item 2' },
{ id: '3', name: 'Item 3' },
]
component.writeValue(newValue)
expect(component.selectedItems).toEqual([
{ id: '1', name: 'Item 1' },
{ id: '2', name: 'Item 2' },
{ id: '3', name: 'Item 3' },
])
})
it('should update selectedItems when an item is dropped within selectedList', () => {
component.items = [
{ id: '1', name: 'Item 1' },
{ id: '2', name: 'Item 2' },
{ id: '3', name: 'Item 3' },
{ id: '4', name: 'Item 4' },
]
component.writeValue(['1', '2', '3'])
const event = {
previousContainer: component.selectedList,
container: component.selectedList,
previousIndex: 1,
currentIndex: 2,
}
component.drop(event as any)
expect(component.selectedItems).toEqual([
{ id: '1', name: 'Item 1' },
{ id: '3', name: 'Item 3' },
{ id: '2', name: 'Item 2' },
])
})
it('should update selectedItems when an item is dropped from unselectedList to selectedList', () => {
component.items = [
{ id: '1', name: 'Item 1' },
{ id: '2', name: 'Item 2' },
{ id: '3', name: 'Item 3' },
]
component.writeValue(['1', '2'])
const event = {
previousContainer: component.unselectedList,
container: component.selectedList,
previousIndex: 0,
currentIndex: 2,
}
component.drop(event as any)
expect(component.selectedItems).toEqual([
{ id: '1', name: 'Item 1' },
{ id: '2', name: 'Item 2' },
{ id: '3', name: 'Item 3' },
])
})
it('should update selectedItems when an item is dropped from selectedList to unselectedList', () => {
component.items = [
{ id: '1', name: 'Item 1' },
{ id: '2', name: 'Item 2' },
{ id: '3', name: 'Item 3' },
]
component.writeValue(['1', '2', '3'])
const event = {
previousContainer: component.selectedList,
container: component.unselectedList,
previousIndex: 1,
currentIndex: 0,
}
component.drop(event as any)
expect(component.selectedItems).toEqual([
{ id: '1', name: 'Item 1' },
{ id: '3', name: 'Item 3' },
])
})
})

View File

@@ -0,0 +1,61 @@
import { Component, Input, ViewChild, forwardRef } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { AbstractInputComponent } from '../abstract-input'
import {
CdkDragDrop,
CdkDropList,
moveItemInArray,
} from '@angular/cdk/drag-drop'
@Component({
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DragDropSelectComponent),
multi: true,
},
],
selector: 'pngx-input-drag-drop-select',
templateUrl: './drag-drop-select.component.html',
styleUrl: './drag-drop-select.component.scss',
})
export class DragDropSelectComponent extends AbstractInputComponent<string[]> {
@Input() items: { id: string; name: string }[] = []
public selectedItems: { id: string; name: string }[] = []
@ViewChild('selectedList') selectedList: CdkDropList
@ViewChild('unselectedList') unselectedList: CdkDropList
get unselectedItems(): { id: string; name: string }[] {
return this.items.filter((i) => !this.selectedItems.includes(i))
}
writeValue(newValue: string[]): void {
super.writeValue(newValue)
this.selectedItems = newValue.map((id) =>
this.items.find((i) => i.id === id)
)
}
public drop(event: CdkDragDrop<string[]>) {
if (
event.previousContainer === event.container &&
event.container === this.selectedList
) {
moveItemInArray(
this.selectedItems,
event.previousIndex,
event.currentIndex
)
} else if (event.container === this.selectedList) {
this.selectedItems.splice(
event.currentIndex,
0,
this.unselectedItems[event.previousIndex]
)
} else {
this.selectedItems.splice(event.previousIndex, 1)
}
this.onChange(this.selectedItems.map((i) => i.id))
}
}