diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html index 101dee255f87120673c45d7ec4a8a3df4c20ad32..d55c3ff8ca39fe9dc353e7c04f797171d2fcbfb8 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html +++ b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.html @@ -8,8 +8,9 @@ <div fxLayout="column"> <mat-form-field *ngIf="combinedProperties.hasOwnProperty('id')"> <mat-label>ID</mat-label> - <input matInput type="text" [value]="combinedProperties.id" - (input)="updateModel('id', $any($event.target).value)"> + <input matInput type="text" *ngIf="selectedElements.length === 1" [value]="combinedProperties.id" + (input)="updateModel('id', $any($event.target).value, $event)"> + <input matInput type="text" disabled *ngIf="selectedElements.length > 1" [value]="'Muss eindeutig sein'"> </mat-form-field> <mat-form-field *ngIf="combinedProperties.hasOwnProperty('label')"> <mat-label>Label</mat-label> diff --git a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts index 611cb31cbe494136b3a5f01b6c89c70e3fd2069b..c1849dd830f7a8b70a14fbf6b9e4123bac0f4969 100644 --- a/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts +++ b/projects/editor/src/app/components/unit-view/page-view/properties/properties.component.ts @@ -52,8 +52,14 @@ export class PropertiesComponent { } } - updateModel(property: string, value: string | number | boolean): void { - this.unitService.updateSelectedElementProperty(property, value); + // event as optional parameter in case the input is invalid and the old value needs + // to be restored. This is for now only relevant for IDs. Might need rework for other properties. + updateModel(property: string, value: string | number | boolean, event?: Event): void { + if (!this.unitService.updateSelectedElementProperty(property, value)) { + if (event) { + (event.target as HTMLInputElement).value = <string> this.combinedProperties[property]; + } + } } deleteElement(): void { diff --git a/projects/editor/src/app/id.service.ts b/projects/editor/src/app/id.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..da521b21de638491d42d0827076a1bce284af253 --- /dev/null +++ b/projects/editor/src/app/id.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@angular/core'; +import { + Unit, UnitPage, UnitPageSection, UnitUIElement +} from '../../../common/unit'; + +@Injectable({ + providedIn: 'root' +}) +export class IdService { + private givenIDs: string[] = []; + private idCounter: Record<string, number> = { + label: 0, + button: 0, + 'text-field': 0, + checkbox: 0, + dropdown: 0, + radio: 0, + image: 0, + audio: 0, + video: 0, + correction: 0 + }; + + getNewID(type: string): string { + do { + this.idCounter[type] += 1; + } + while (!this.isIdAvailable(`${type}_${this.idCounter[type]}`)); + this.givenIDs.push(`${type}_${this.idCounter[type]}`); + return `${type}_${this.idCounter[type]}`; + } + + readExistingIDs(unit: Unit): void { + unit.pages.forEach((page: UnitPage) => { + page.sections.forEach((section: UnitPageSection) => { + section.elements.forEach((element: UnitUIElement) => { + this.givenIDs.push(element.id); + }); + }); + }); + } + + isIdAvailable(value: string): boolean { + return !this.givenIDs.includes(value); + } + + addId(id: string): void { + this.givenIDs.push(id); + } + + removeId(id: string): void { + const index = this.givenIDs.indexOf(id, 0); + if (index > -1) { + this.givenIDs.splice(index, 1); + } + } +} diff --git a/projects/editor/src/app/model/UnitFactory.ts b/projects/editor/src/app/model/UnitFactory.ts index d3d8c73bfdf785efb400cea2c457848345d73a52..332e68b1709275776c589a61c5ef48d95d5203e6 100644 --- a/projects/editor/src/app/model/UnitFactory.ts +++ b/projects/editor/src/app/model/UnitFactory.ts @@ -6,23 +6,6 @@ import { VideoElement } from '../../../../common/unit'; -const idCounter: Record<string, number> = { - label: 0, - button: 0, - 'text-field': 0, - checkbox: 0, - dropdown: 0, - radio: 0, - image: 0, - audio: 0, - video: 0, - correction: 0 -}; - -function getNewID(type: string): string { - idCounter[type] += 1; - return `${type}_${idCounter[type]}`; -} export function createUnit(): Unit { return { @@ -51,7 +34,7 @@ export function createUnitPageSection(): UnitPageSection { export function createUnitUIElement(type: string): UnitUIElement { return { type, - id: getNewID(type), + id: 'id_placeholder', xPosition: 0, yPosition: 0, width: 180, diff --git a/projects/editor/src/app/unit.service.ts b/projects/editor/src/app/unit.service.ts index 42a851b945bcd820b2ad945348c6b74ad51fa9e4..e4f467504c69460c42b17941f1c9684adb83f88e 100644 --- a/projects/editor/src/app/unit.service.ts +++ b/projects/editor/src/app/unit.service.ts @@ -3,11 +3,12 @@ import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { - Unit, UnitPage, UnitUIElement + Unit, UnitPage, UnitPageSection, UnitUIElement } from '../../../common/unit'; import { FileService } from '../../../common/file.service'; import * as UnitFactory from './model/UnitFactory'; import { MessageService } from '../../../common/message.service'; +import { IdService } from './id.service'; @Injectable({ providedIn: 'root' @@ -22,7 +23,7 @@ export class UnitService { pageSwitch = new Subject(); elementUpdated = new Subject(); - constructor(private messageService: MessageService) { + constructor(private messageService: MessageService, private idService: IdService) { this._unit = new BehaviorSubject(UnitFactory.createUnit()); const initialPage = UnitFactory.createUnitPage(); const initialSection = UnitFactory.createUnitPageSection(); @@ -133,8 +134,10 @@ export class UnitService { case 'correction': newElement = UnitFactory.createCorrectionElement(); break; - // no default + default: + throw new Error(`ElementType ${elementType} not found!`); } + newElement.id = this.idService.getNewID(elementType); this._unit.value.pages[this._selectedPageIndex.value] .sections[this._selectedPageSectionIndex.value].elements.push(newElement!); @@ -159,15 +162,24 @@ export class UnitService { this.elementUpdated.next(); } - updateSelectedElementProperty(property: string, value: string | number | boolean): void { - this._selectedElements.value.forEach((element: UnitUIElement) => { + updateSelectedElementProperty(property: string, value: string | number | boolean): boolean { + for (const element of this._selectedElements.value) { if (['string', 'number', 'boolean'].indexOf(typeof element[property]) > -1) { + if (property === 'id') { + if (!this.idService.isIdAvailable((value as string))) { // prohibit existing IDs + this.messageService.showError('ID ist bereits vergeben'); + return false; + } + this.idService.removeId(element[property]); + this.idService.addId(<string>value); + } element[property] = value; } else if (Array.isArray(element[property])) { (element[property] as string[]).push(value as string); } - }); + } this.elementUpdated.next(); + return true; } deleteSelectedElements(): void { @@ -197,6 +209,8 @@ export class UnitService { this._unit.value.pages.forEach((page: UnitPage) => { this._pages.push(new BehaviorSubject(page)); }); + + this.idService.readExistingIDs(this._unit.value); } selectPageSection(index: number): void {