From b1d2027b79a4564d85bc449b2b852a5935a0381c Mon Sep 17 00:00:00 2001 From: rhenck <richard.henck@iqb.hu-berlin.de> Date: Thu, 23 Sep 2021 11:19:58 +0200 Subject: [PATCH] [editor] Fix dropping elements in dynamic sections And having the correct grid coordinates. Also refactored the canvas sections to be different components. --- projects/editor/src/app/app.module.ts | 8 +- .../canvas/page-canvas.component.html | 12 +- .../canvas/section-dynamic.component.ts | 147 +++++++++++++++++ .../canvas/section-static.component.ts | 43 +++++ .../page-view/canvas/section.component.ts | 153 ------------------ projects/editor/src/app/unit.service.ts | 13 +- 6 files changed, 210 insertions(+), 166 deletions(-) create mode 100644 projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts create mode 100644 projects/editor/src/app/components/unit-view/page-view/canvas/section-static.component.ts delete mode 100644 projects/editor/src/app/components/unit-view/page-view/canvas/section.component.ts diff --git a/projects/editor/src/app/app.module.ts b/projects/editor/src/app/app.module.ts index 101598cd1..f08586d98 100644 --- a/projects/editor/src/app/app.module.ts +++ b/projects/editor/src/app/app.module.ts @@ -17,7 +17,6 @@ import { UiElementToolboxComponent } from './components/unit-view/page-view/ui-e import { UnitViewComponent } from './components/unit-view/unit-view.component'; import { PageViewComponent } from './components/unit-view/page-view/page-view.component'; import { PageCanvasComponent } from './components/unit-view/page-view/canvas/page-canvas.component'; -import { SectionComponent } from './components/unit-view/page-view/canvas/section.component'; import { StaticCanvasOverlayComponent } from './components/unit-view/page-view/canvas/static-canvas-overlay.component'; import { DynamicCanvasOverlayComponent } from './components/unit-view/page-view/canvas/dynamic-canvas-overlay.component'; import { SharedModule } from '../../../common/app.module'; @@ -27,6 +26,8 @@ import { import { EditorTranslateLoader } from './editor-translate-loader'; import { ElementPropertiesComponent } from './components/unit-view/page-view/properties/element-properties.component'; import { SectionMenuComponent } from './components/unit-view/page-view/canvas/section-menu.component'; +import { SectionStaticComponent } from './components/unit-view/page-view/canvas/section-static.component'; +import { SectionDynamicComponent } from './components/unit-view/page-view/canvas/section-dynamic.component'; @NgModule({ declarations: [ @@ -36,7 +37,6 @@ import { SectionMenuComponent } from './components/unit-view/page-view/canvas/se UnitViewComponent, PageViewComponent, PageCanvasComponent, - SectionComponent, StaticCanvasOverlayComponent, DynamicCanvasOverlayComponent, ConfirmationDialog, @@ -44,7 +44,9 @@ import { SectionMenuComponent } from './components/unit-view/page-view/canvas/se MultilineTextEditDialog, RichTextEditDialogTinyMCE, ElementPropertiesComponent, - SectionMenuComponent + SectionMenuComponent, + SectionStaticComponent, + SectionDynamicComponent ], imports: [ BrowserModule, diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html b/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html index 724bc0dbf..940b22b24 100644 --- a/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html +++ b/projects/editor/src/app/components/unit-view/page-view/canvas/page-canvas.component.html @@ -15,24 +15,24 @@ (mouseover)="hoveredSection = i" (mouseleave)="hoveredSection = -1"> <!-- keep menu open--> </app-section-menu> <!-- TODO split section into static and dynamic--> - <div *ngIf="!section.dynamicPositioning" - app-canvas-section class="section drop-list" id="section-{{i}}" + <app-section-static *ngIf="!section.dynamicPositioning" + class="section drop-list" id="section-{{i}}" [section]="section" cdkDropList cdkDropListSortingDisabled [cdkDropListData]="{ sectionIndex: i }" (cdkDropListDropped)="elementDropped($event)" [cdkDropListConnectedTo]="dropListList" (mouseover)="hoveredSection = i" (mouseleave)="hoveredSection = -1"> - </div> - <div *ngIf="section.dynamicPositioning" - app-canvas-section class="section drop-list" + </app-section-static> + <app-section-dynamic *ngIf="section.dynamicPositioning" + class="section drop-list" [section]="section" [sectionIndex]="i" [dropListList]="dropListList" (transferElement)="moveElementBetweenSections($event.element, $event.previousSectionIndex, $event.newSectionIndex)" (mouseover)="hoveredSection = i" (mouseleave)="hoveredSection = -1"> - </div> + </app-section-dynamic> </div> </div> </div> diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts new file mode 100644 index 000000000..ab0ba1f49 --- /dev/null +++ b/projects/editor/src/app/components/unit-view/page-view/canvas/section-dynamic.component.ts @@ -0,0 +1,147 @@ +import { + Component, Input, Output, EventEmitter +} from '@angular/core'; +import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events'; +import { DragItemData, DropListData } from './page-canvas.component'; +import { UnitPageSection, UnitUIElement } from '../../../../../../../common/unit'; +import { SelectionService } from '../../../../selection.service'; +import { UnitService } from '../../../../unit.service'; + +@Component({ + selector: 'app-section-dynamic', + template: ` + <div [style.display]="'grid'" + [style.grid-template-columns]="section.gridColumnSizes" + [style.grid-template-rows]="section.gridRowSizes" + [style.height.%]="100" + cdkDropListGroup + [style.border]="selected ? '1px solid': '1px dotted'" + [style.height.px]="section.height" + [style.background-color]="section.backgroundColor"> + + <!-- Dynamic sections have the droplists for the grid cells next to the actual elements. Elements can not + be children of the grid cells because they can span over multiple cells. --> + <ng-container *ngFor="let column of this.section.gridColumnSizes.split(' '); let x = index"> + <ng-container *ngFor="let row of this.section.gridRowSizes.split(' '); let y = index"> + <div class="grid-placeholder" + [style.grid-column-start]="x + 1" + [style.grid-column-end]="x + 1" + [style.grid-row-start]="y + 1" + [style.grid-row-end]="y + 1" + cdkDropList [cdkDropListData]="{ sectionIndex: sectionIndex, gridCoordinates: [x + 1, y + 1] }" + (cdkDropListDropped)="drop($any($event))" + id="list-{{sectionIndex}}-{{x+1}}-{{y+1}}" + (dragover)="$event.preventDefault()" (drop)="newElementDropped($event, x + 1, y + 1)"> + {{x + 1}} / {{y + 1}} + </div> + </ng-container> + </ng-container> + + <app-dynamic-canvas-overlay *ngFor="let element of section.elements" + [element]="$any(element)" + [style.min-width.px]="element.width" + [style.min-height.px]="element.height" + [style.margin-left.px]="element.marginLeft" + [style.margin-right.px]="element.marginRight" + [style.margin-top.px]="element.marginTop" + [style.margin-bottom.px]="element.marginBottom" + [style.grid-column-start]="element.gridColumnStart" + [style.grid-column-end]="element.gridColumnEnd" + [style.grid-row-start]="element.gridRowStart" + [style.grid-row-end]="element.gridRowEnd" + cdkDropList cdkDropListSortingDisabled + [cdkDropListData]="{ sectionIndex: sectionIndex }" + [cdkDropListConnectedTo]="dropListList" + (resize)="resizeOverlay($event)" + [style.pointer-events]="dragging ? 'none' : 'auto'" + [style.position]="dragging ? 'absolute' : null" + [style.width.px]="dragging ? draggingElementWidth : null" + [style.height.px]="dragging ? draggingElementHeight : null"> + </app-dynamic-canvas-overlay> + </div> + `, + styles: [ + '.grid-placeholder {border: 25px inset aliceblue; text-align: center;}' + ] +}) +export class SectionDynamicComponent { + @Input() section!: UnitPageSection; + @Input() sectionIndex!: number; + @Input() dropListList!: string[]; + @Output() transferElement = new EventEmitter<{ element: UnitUIElement, + previousSectionIndex: number, + newSectionIndex: number }>(); + + selected = true; + dragging = false; + draggingElementWidth: number | undefined = 0; + draggingElementHeight: number | undefined = 0; + + constructor(public selectionService: SelectionService, public unitService: UnitService) { } + + drop(event: CdkDragDrop<DropListData>): void { + const dragItemData: DragItemData = event.item.data; + + // Move element to other section - handled by parent (page-canvas). + if (event.previousContainer.data.sectionIndex !== event.container.data.sectionIndex) { + this.transferElement.emit({ + element: event.item.data.element, + previousSectionIndex: event.previousContainer.data.sectionIndex, + newSectionIndex: event.container.data.sectionIndex + }); + } + if (dragItemData.dragType === 'move') { + const elementWidth: number = event.item.data.element.gridColumnEnd - event.item.data.element.gridColumnStart; + const elementHeight: number = event.item.data.element.gridRowEnd - event.item.data.element.gridRowStart; + this.unitService.updateElementProperty( + [event.item.data.element], + 'gridColumnStart', + event.container.data.gridCoordinates![0] + ); + // Ensure the end value is at least the same as the start, otherwise the grid breaks. + this.unitService.updateElementProperty( + [dragItemData.element], + 'gridColumnEnd', + event.container.data.gridCoordinates![0] + elementWidth + ); + this.unitService.updateElementProperty( + [dragItemData.element], + 'gridRowStart', + event.container.data.gridCoordinates![1] + ); + this.unitService.updateElementProperty( + [dragItemData.element], + 'gridRowEnd', + event.container.data.gridCoordinates![1] + elementHeight + ); + } else if (event.item.data.dragType === 'resize') { + this.unitService.updateElementProperty( + [dragItemData.element], + 'gridColumnEnd', + event.container.data.gridCoordinates![0] + 1 + ); + this.unitService.updateElementProperty( + [dragItemData.element], + 'gridRowEnd', + event.container.data.gridCoordinates![1] + 1 + ); + } else { + throw new Error('Unknown drop event'); + } + } + + newElementDropped(event: DragEvent, gridX: number, gridY: number): void { + event.preventDefault(); + this.unitService.addElementToSection( + event.dataTransfer?.getData('elementType') as string, + this.section, + { x: gridX, y: gridY } + ); + } + + resizeOverlay(event: { dragging: boolean, elementWidth?: number, elementHeight?: number }): void { + this.dragging = event.dragging; + this.draggingElementWidth = event.elementWidth; + this.draggingElementHeight = event.elementHeight; + } +} diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/section-static.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/section-static.component.ts new file mode 100644 index 000000000..c9e15ad9c --- /dev/null +++ b/projects/editor/src/app/components/unit-view/page-view/canvas/section-static.component.ts @@ -0,0 +1,43 @@ +import { + Component, ElementRef, Input, ViewChild +} from '@angular/core'; +import { UnitPageSection } from '../../../../../../../common/unit'; +import { SelectionService } from '../../../../selection.service'; +import { UnitService } from '../../../../unit.service'; + +@Component({ + selector: 'app-section-static', + template: ` + <div #sectionElement class="section-wrapper" + [style.border]="selected ? '1px solid': '1px dotted'" + [style.height.px]="section.height" + [style.background-color]="section.backgroundColor" + (click)="selectionService.selectSection(this)" + (dragover)="$event.preventDefault()" (drop)="newElementDropped($event)"> + <app-static-canvas-overlay + *ngFor="let element of section.elements" + [element]="$any(element)"> + </app-static-canvas-overlay> + </div> + `, + styles: [ + '.section-wrapper {width: 100%}' + ] +}) +export class SectionStaticComponent { + @Input() section!: UnitPageSection; + @ViewChild('sectionElement') sectionElement!: ElementRef; + selected = true; + + constructor(public selectionService: SelectionService, public unitService: UnitService) { } + + newElementDropped(event: DragEvent): void { + event.preventDefault(); + const sectionRect = this.sectionElement.nativeElement.getBoundingClientRect(); + this.unitService.addElementToSection( + event.dataTransfer?.getData('elementType') as string, + this.section, + { x: event.clientX - Math.round(sectionRect.left), y: event.clientY - Math.round(sectionRect.top) } + ); + } +} diff --git a/projects/editor/src/app/components/unit-view/page-view/canvas/section.component.ts b/projects/editor/src/app/components/unit-view/page-view/canvas/section.component.ts deleted file mode 100644 index fbb5c42f7..000000000 --- a/projects/editor/src/app/components/unit-view/page-view/canvas/section.component.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { - Component, Input, Output, EventEmitter, ViewChild, ElementRef -} from '@angular/core'; -import { CdkDragDrop } from '@angular/cdk/drag-drop/drag-events'; -import { UnitPageSection, UnitUIElement } from '../../../../../../../common/unit'; -import { UnitService } from '../../../../unit.service'; -import { SelectionService } from '../../../../selection.service'; -import { DragItemData, DropListData } from './page-canvas.component'; - -@Component({ - selector: '[app-canvas-section]', - template: ` - <div #sectionElement class="section-wrapper" - [style.border]="selected ? '1px solid': '1px dotted'" - [style.height.px]="section.height" - [style.background-color]="section.backgroundColor" - (click)="selectionService.selectSection(this)" - (dragover)="$event.preventDefault()" (drop)="newElementDropped($event)"> - <div *ngIf="!section.dynamicPositioning"> - <app-static-canvas-overlay - *ngFor="let element of section.elements" - [element]="$any(element)"> - </app-static-canvas-overlay> - </div> - - <div *ngIf="section.dynamicPositioning" - [style.display]="'grid'" - [style.grid-template-columns]="section.gridColumnSizes" - [style.grid-template-rows]="section.gridRowSizes" - [style.height.%]="100" - cdkDropListGroup> - - <!-- Dynamic sections have the droplists for the grid cells next to the actual elements. Elements can not - be children of the grid cells because they can span over multiple cells. --> - <ng-container *ngFor="let column of this.section.gridColumnSizes.split(' '); let x = index"> - <ng-container *ngFor="let row of this.section.gridRowSizes.split(' '); let y = index"> - <div class="grid-placeholder" - [style.grid-column-start]="x + 1" - [style.grid-column-end]="x + 1" - [style.grid-row-start]="y + 1" - [style.grid-row-end]="y + 1" - cdkDropList [cdkDropListData]="{ sectionIndex: sectionIndex, gridCoordinates: [x + 1, y + 1] }" - (cdkDropListDropped)="drop($any($event))" - id="list-{{sectionIndex}}-{{x+1}}-{{y+1}}"> - {{x + 1}} / {{y + 1}} - </div> - </ng-container> - </ng-container> - - <app-dynamic-canvas-overlay *ngFor="let element of section.elements" - [element]="$any(element)" - [style.min-width.px]="element.width" - [style.min-height.px]="element.height" - [style.margin-left.px]="element.marginLeft" - [style.margin-right.px]="element.marginRight" - [style.margin-top.px]="element.marginTop" - [style.margin-bottom.px]="element.marginBottom" - [style.grid-column-start]="element.gridColumnStart" - [style.grid-column-end]="element.gridColumnEnd" - [style.grid-row-start]="element.gridRowStart" - [style.grid-row-end]="element.gridRowEnd" - cdkDropList cdkDropListSortingDisabled - [cdkDropListData]="{ sectionIndex: sectionIndex }" - [cdkDropListConnectedTo]="dropListList" - (resize)="resizeOverlay($event)" - [style.pointer-events]="dragging ? 'none' : 'auto'" - [style.position]="dragging ? 'absolute' : null" - [style.width.px]="dragging ? draggingElementWidth : null" - [style.height.px]="dragging ? draggingElementHeight : null"> - </app-dynamic-canvas-overlay> - </div> - </div> - `, - styles: [ - '.section-wrapper {width: 100%}', - '.grid-placeholder {border: 25px inset aliceblue; text-align: center;}' - ] -}) -export class SectionComponent { - @Input() section!: UnitPageSection; - @Input() sectionIndex!: number; - @Input() dropListList!: string[]; - @Output() transferElement = new EventEmitter<{ element: UnitUIElement, - previousSectionIndex: number, - newSectionIndex: number }>(); - - @ViewChild('sectionElement') sectionElement!: ElementRef; - selected = true; - dragging = false; - draggingElementWidth: number | undefined = 0; - draggingElementHeight: number | undefined = 0; - - constructor(public selectionService: SelectionService, public unitService: UnitService) { } - - drop(event: CdkDragDrop<DropListData>): void { - const dragItemData: DragItemData = event.item.data; - - // Move element to other section - handled by parent (page-canvas). - if (event.previousContainer.data.sectionIndex !== event.container.data.sectionIndex) { - this.transferElement.emit({ - element: event.item.data.element, - previousSectionIndex: event.previousContainer.data.sectionIndex, - newSectionIndex: event.container.data.sectionIndex - }); - } - if (dragItemData.dragType === 'move') { - this.unitService.updateElementProperty( - [event.item.data.element], - 'gridColumnStart', event.container.data.gridCoordinates![0] - ); - // Ensure the end value is at least the same as the start, otherwise the grid breaks. - this.unitService.updateElementProperty( - [dragItemData.element], - 'gridColumnEnd', Math.max(event.item.data.element.gridColumnEnd, event.container.data.gridCoordinates![0]) - ); - this.unitService.updateElementProperty( - [dragItemData.element], - 'gridRowStart', event.container.data.gridCoordinates![1] - ); - this.unitService.updateElementProperty( - [dragItemData.element], - 'gridRowEnd', Math.max(event.item.data.element.gridRowEnd, event.container.data.gridCoordinates![1]) - ); - } else if (event.item.data.dragType === 'resize') { - this.unitService.updateElementProperty( - [dragItemData.element], - 'gridColumnEnd', event.container.data.gridCoordinates![0] + 1 - ); - this.unitService.updateElementProperty( - [dragItemData.element], - 'gridRowEnd', event.container.data.gridCoordinates![1] + 1 - ); - } else { - throw new Error('Unknown drop event'); - } - } - - resizeOverlay(event: { dragging: boolean, elementWidth?: number, elementHeight?: number }): void { - this.dragging = event.dragging; - this.draggingElementWidth = event.elementWidth; - this.draggingElementHeight = event.elementHeight; - } - - newElementDropped(event: DragEvent): void { - event.preventDefault(); - const sectionRect = this.sectionElement.nativeElement.getBoundingClientRect(); - this.unitService.addElementToSection( - event.dataTransfer?.getData('elementType') as string, - this.section, - { x: event.clientX - Math.round(sectionRect.left), y: event.clientY - Math.round(sectionRect.top) } - ); - } -} diff --git a/projects/editor/src/app/unit.service.ts b/projects/editor/src/app/unit.service.ts index 6fdaa54ce..58742afa4 100644 --- a/projects/editor/src/app/unit.service.ts +++ b/projects/editor/src/app/unit.service.ts @@ -130,7 +130,7 @@ export class UnitService { async addElementToSection(elementType: string, section: UnitPageSection, - elementCoordinates?: Record<string, number>): Promise<void> { + coordinates?: { x: number, y: number }): Promise<void> { let newElement: UnitUIElement; switch (elementType) { case 'text': @@ -171,9 +171,14 @@ export class UnitService { } newElement.id = this.idService.getNewID(elementType); newElement.dynamicPositioning = section.dynamicPositioning; - if (elementCoordinates) { - newElement.xPosition = elementCoordinates.x; - newElement.yPosition = elementCoordinates.y; + if (coordinates && section.dynamicPositioning) { + newElement.gridColumnStart = coordinates.x; + newElement.gridColumnEnd = coordinates.x + 1; + newElement.gridRowStart = coordinates.y; + newElement.gridRowEnd = coordinates.y + 1; + } else if (coordinates && !section.dynamicPositioning) { + newElement.xPosition = coordinates.x; + newElement.yPosition = coordinates.y; } section.elements.push(newElement); this.veronaApiService.sendVoeDefinitionChangedNotification(); -- GitLab