diff --git a/projects/common/models/elements/element.ts b/projects/common/models/elements/element.ts index d15258c0eafa0bee4a733316c0382ba75900e2c1..ef065c13e73cfe1965b508201b80799278ac3c4e 100644 --- a/projects/common/models/elements/element.ts +++ b/projects/common/models/elements/element.ts @@ -196,6 +196,14 @@ export abstract class InputElement extends UIElement implements InputElementProp } } +export function isInputElement(el: UIElement): el is InputElement { + return el.label !== undefined && + el.value !== undefined && + el.required !== undefined && + el.requiredWarnMessage !== undefined && + el.readOnly !== undefined; +} + export interface KeyInputElementProperties { inputAssistancePreset: InputAssistancePreset; inputAssistancePosition: 'floating' | 'right'; diff --git a/projects/editor/src/app/app.module.ts b/projects/editor/src/app/app.module.ts index 4bbd95dcb4ebc42af45a334d866b1bf1d56e2c3a..56c05ce9f869b47d8c138f5f948452ab6b9e208e 100644 --- a/projects/editor/src/app/app.module.ts +++ b/projects/editor/src/app/app.module.ts @@ -18,12 +18,8 @@ import { MatInputModule } from '@angular/material/input'; import { MatListModule } from '@angular/material/list'; import { APIService, SharedModule } from 'common/shared.module'; import { SectionInsertDialogComponent } from 'editor/src/app/components/dialogs/section-insert-dialog.component'; -import { HotspotListPanelComponent } from 'editor/src/app/components/properties-panel/hotspot-list-panel.component'; import { VeronaAPIService } from 'editor/src/app/services/verona-api.service'; import { MatRadioModule } from '@angular/material/radio'; -import { - HotspotFieldSetComponent -} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/hotspot-field-set.component'; import { HotspotEditDialogComponent } from 'editor/src/app/components/dialogs/hotspot-edit-dialog.component'; import { MathEditorModule } from 'common/math-editor.module'; import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay'; @@ -36,6 +32,12 @@ import { import { ShowStateVariablesButtonComponent } from 'editor/src/app/components/new-ui-element-panel/show-state-variables-button.component'; +import { + TextFieldElementPropertiesComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-field-element-properties.component'; +import { + ScaleAndZoomPropertiesComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/scale-and-zoom-properties.component'; import { StateVariableEditorComponent } from 'editor/src/app/components/dialogs/state-variables-dialog/state-variable-editor.component'; @@ -94,25 +96,13 @@ import { ElementPositionPropertiesComponent } from './components/properties-panel/position-properties-tab/element-position-properties.component'; import { ElementStylePropertiesComponent } from './components/properties-panel/style-properties-tab/element-style-properties.component'; -import { ElementModelPropertiesComponent } from +import { ElementModelPropertiesComponent, IsInputElementPipe } from './components/properties-panel/model-properties-tab/element-model-properties.component'; import { DynamicSectionHelperGridComponent } from './components/canvas/dynamic-section-helper-grid.component'; import { ElementGridChangeListenerDirective } from './components/canvas/element-grid-change-listener.directive'; import { OptionsFieldSetComponent } from './components/properties-panel/model-properties-tab/input-groups/options-field-set.component'; -import { TextPropertiesFieldSetComponent } from - './components/properties-panel/model-properties-tab/input-groups/text-properties-field-set.component'; -import { ButtonPropertiesComponent } from - './components/properties-panel/model-properties-tab/input-groups/button-properties.component'; -import { SliderPropertiesComponent } from - './components/properties-panel/model-properties-tab/input-groups/slider-properties.component'; -import { TextFieldElementPropertiesComponent } from - './components/properties-panel/model-properties-tab/input-groups/text-field-element-properties.component'; -import { ScaleAndZoomPropertiesComponent } from - './components/properties-panel/model-properties-tab/input-groups/scale-and-zoom-properties.component'; -import { DropListPropertiesComponent, GetValidDropListsPipe } from - './components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component'; import { RichTextEditorSimpleComponent } from './text-editor-simple/rich-text-editor-simple.component'; import { RichTextSimpleEditDialogComponent } from './components/dialogs/rich-text-simple-edit-dialog.component'; import { SelectPropertiesComponent } from @@ -121,21 +111,18 @@ import { InputElementPropertiesComponent } from './components/properties-panel/model-properties-tab/input-groups/input-element-properties.component'; import { PresetValuePropertiesComponent } from './components/properties-panel/model-properties-tab/input-groups/preset-value-properties.component'; -import { OptionListPanelComponent } from './components/properties-panel/option-list-panel.component'; import { LikertRowLabelPipe } from './components/properties-panel/likert-row-label.pipe'; import { LabelEditDialogComponent } from './components/dialogs/label-edit-dialog.component'; -import { - BorderPropertiesComponent -} from './components/properties-panel/model-properties-tab/input-groups/border-properties.component'; import { GeogebraAppDefinitionDialogComponent } from './components/dialogs/geogebra-app-definition-dialog.component'; import { SizeInputPanelComponent } from './components/util/size-input-panel.component'; import { ComboButtonComponent } from './components/util/combo-button.component'; import { DeleteReferenceDialogComponent } from './components/dialogs/delete-reference-dialog.component'; import { SanitizationDialogComponent } from './components/dialogs/sanitization-dialog.component'; import { CheckboxNodeviewComponent } from './text-editor/angular-node-views/checkbox-nodeview.component'; +import { OptionListPanelComponent } from 'editor/src/app/components/properties-panel/option-list-panel.component'; import { - MathTablePropertiesComponent -} from './components/properties-panel/model-properties-tab/input-groups/math-table-properties.component'; + EleSpecificPropsComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific-props.component'; @NgModule({ declarations: [ @@ -172,27 +159,18 @@ import { DynamicSectionHelperGridComponent, ElementGridChangeListenerDirective, OptionsFieldSetComponent, - TextPropertiesFieldSetComponent, - ButtonPropertiesComponent, ActionPropertiesComponent, - SliderPropertiesComponent, TextFieldElementPropertiesComponent, ScaleAndZoomPropertiesComponent, - DropListPropertiesComponent, RichTextEditorSimpleComponent, RichTextSimpleEditDialogComponent, SectionInsertDialogComponent, SelectPropertiesComponent, InputElementPropertiesComponent, PresetValuePropertiesComponent, - OptionListPanelComponent, - HotspotFieldSetComponent, - HotspotListPanelComponent, LikertRowLabelPipe, LabelEditDialogComponent, - BorderPropertiesComponent, GeogebraAppDefinitionDialogComponent, - GetValidDropListsPipe, GetAnchorIdsPipe, GetStateVariablePipe, ScrollPageIndexPipe, @@ -209,7 +187,6 @@ import { SanitizationDialogComponent, TooltipPropertiesDialogComponent, GetValidAudioVideoIDsPipe, - MathTablePropertiesComponent, InputAssistancePropertiesComponent ], imports: [ @@ -240,7 +217,10 @@ import { MathEditorModule, CdkConnectedOverlay, CdkOverlayOrigin, - MatBadgeModule + MatBadgeModule, + IsInputElementPipe, + OptionListPanelComponent, + EleSpecificPropsComponent ], providers: [ { provide: APIService, useExisting: VeronaAPIService } diff --git a/projects/editor/src/app/components/properties-panel/hotspot-list-panel.component.ts b/projects/editor/src/app/components/properties-panel/hotspot-list-panel.component.ts deleted file mode 100644 index 303e8c171572316ba649690bea1a8d04695736d9..0000000000000000000000000000000000000000 --- a/projects/editor/src/app/components/properties-panel/hotspot-list-panel.component.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { - Component, EventEmitter, Input, Output -} from '@angular/core'; -import { CdkDragDrop } from '@angular/cdk/drag-drop'; - - -import { Hotspot } from 'common/models/elements/input-elements/hotspot-image'; - -@Component({ - selector: 'aspect-hotspot-list-panel', - template: ` - <fieldset class="fx-column-start-stretch"> - <legend>{{title | translate }}</legend> - <button class="fx-align-self-end" mat-mini-fab matSuffix color="primary" [style.bottom.px]="3" - (click)="addListItem();"> - <mat-icon>add</mat-icon> - </button> - - <div class="drop-list" cdkDropList [cdkDropListData]="itemList" - (cdkDropListDropped)="moveListValue($event)"> - <div *ngFor="let item of itemList; let i = index" cdkDrag - class="option-draggable fx-row-start-stretch"> - <div class="fx-flex fx-align-self-center">{{'hotspot.'+item.shape | translate}}({{i + 1}})</div> - <button mat-icon-button color="primary" - (click)="editItem.emit(i)"> - <mat-icon>build</mat-icon> - </button> - <button mat-icon-button color="primary" - (click)="removeListItem(i)"> - <mat-icon>clear</mat-icon> - </button> - </div> - </div> - </fieldset> - `, - styles: [` - .fx-column-start-stretch { - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: stretch; - } - - .fx-row-start-stretch { - box-sizing: border-box; - display: flex; - flex-direction: row; - justify-content: flex-start; - align-items: stretch; - } - - .fx-align-self-end { - align-self: flex-end; - } - - .fx-align-self-center { - align-self: center; - } - - .fx-flex { - flex: 1 1 0; - box-sizing: border-box; - } - `] -}) -export class HotspotListPanelComponent { - @Input() title!: string; - @Input() textFieldLabel!: string; - @Input() itemList!: Hotspot[]; - @Output() addItem = new EventEmitter<void>(); - @Output() removeItem = new EventEmitter<number>(); - @Output() editItem = new EventEmitter<number>(); - @Output() changedItemOrder = new EventEmitter<{ previousIndex: number, currentIndex: number }>(); - - addListItem(): void { - this.addItem.emit(); - } - - removeListItem(itemIndex: number): void { - this.removeItem.emit(itemIndex); - } - - moveListValue(event: CdkDragDrop<Hotspot[]>): void { - this.changedItemOrder.emit({ previousIndex: event.previousIndex, currentIndex: event.currentIndex }); - } -} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html index 2f80f0d62dddf1992d7a078dad880e9a2fd3b60a..0eeb94b157e7a2e9f0a3cef02b9664fe7c00019d 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html @@ -7,7 +7,8 @@ <input matInput type="text" disabled *ngIf="selectedElements.length > 1" [value]="'Muss eindeutig sein'"> </mat-form-field> - <aspect-input-element-properties [combinedProperties]="combinedProperties" + <aspect-input-element-properties *ngIf="combinedProperties | isInputElement" + [combinedProperties]="combinedProperties" (updateModel)="updateModel.emit($event)"> </aspect-input-element-properties> @@ -15,14 +16,14 @@ (updateModel)="updateModel.emit($event)"> </aspect-preset-value-properties> - <mat-form-field *ngIf="combinedProperties.label !== undefined && combinedProperties.required === undefined" + <!-- non-InputElement elements that have labels, i.e. Likert, Button--> + <mat-form-field *ngIf="combinedProperties.label !== undefined && !(combinedProperties | isInputElement)" appearance="fill"> <mat-label>{{'propertiesPanel.label' | translate }}</mat-label> <textarea matInput type="text" [value]="$any(combinedProperties.label)" (input)="updateModel.emit({property: 'label', value: $any($event.target).value })"> </textarea> - </mat-form-field> <mat-form-field *ngIf="combinedProperties.label2 !== undefined" appearance="fill"> @@ -37,13 +38,17 @@ (updateModel)="updateModel.emit($event)"> </aspect-options-field-set> - <aspect-hotspot-field-set *ngIf="combinedProperties.type === 'hotspot-image'" - [combinedProperties]="combinedProperties" - (updateModel)="updateModel.emit($event)"> - </aspect-hotspot-field-set> + <button *ngIf="combinedProperties.src" + class="media-src-button fx-align-self-center" + mat-fab color="primary" + [matTooltip]="'Medienquelle ändern'" [matTooltipPosition]="'right'" + (click)="changeMediaSrc(combinedProperties.type)"> + <mat-icon>upload_file</mat-icon> + </button> - <aspect-border-properties [combinedProperties]="combinedProperties" - (updateModel)="updateModel.emit($event)"></aspect-border-properties> + <aspect-ele-specific-props [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-ele-specific-props> <button *ngIf="combinedProperties.document" class="fx-align-self-center" @@ -52,14 +57,6 @@ Text und Elemente editieren </button> - <button *ngIf="combinedProperties.src" - class="media-src-button fx-align-self-center" - mat-fab color="primary" - [matTooltip]="'Medienquelle ändern'" [matTooltipPosition]="'right'" - (click)="changeMediaSrc(combinedProperties.type)"> - <mat-icon>upload_file</mat-icon> - </button> - <mat-form-field *ngIf="combinedProperties.alt !== undefined"> <mat-label>Alternativtext</mat-label> <input matInput type="text" [value]="$any(combinedProperties.alt)" @@ -73,10 +70,6 @@ Medienoptionen anpassen </button> - <aspect-text-properties-field-set [combinedProperties]="combinedProperties" - (updateModel)="updateModel.emit($event)"> - </aspect-text-properties-field-set> - <mat-form-field *ngIf="combinedProperties.alignment" appearance="fill"> <mat-label>{{'propertiesPanel.alignment' | translate }}</mat-label> <mat-select [value]="combinedProperties.alignment" @@ -105,7 +98,6 @@ {{'propertiesPanel.hasAutoHeight' | translate }} </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.hasDynamicRowCount !== undefined" [disabled]="$any(combinedProperties.hasAutoHeight)" [checked]="$any(combinedProperties.hasDynamicRowCount)" @@ -130,14 +122,6 @@ (input)="updateModel.emit({ property: 'expectedCharactersCount', value: $any($event.target).value || 0 })"> </mat-form-field> - <aspect-math-table-properties [combinedProperties]="combinedProperties" - (updateModel)="updateModel.emit($event)"> - </aspect-math-table-properties> - - <aspect-button-properties [combinedProperties]="combinedProperties" - (updateModel)="updateModel.emit($event)"> - </aspect-button-properties> - <aspect-action-properties *ngIf="combinedProperties.action !== undefined" [actions]="combinedProperties.type === 'button' ? ['unitNav', 'pageNav', 'highlightText', 'stateVariableChange'] : ['highlightText', 'stateVariableChange']" @@ -145,10 +129,6 @@ (updateModel)="updateModel.emit($event)"> </aspect-action-properties> - <aspect-slider-properties [combinedProperties]="combinedProperties" - (updateModel)="updateModel.emit($event)"> - </aspect-slider-properties> - <ng-container *ngIf="combinedProperties.type === 'checkbox'"> {{'preset' | translate }} <mat-button-toggle-group class="fx-align-self-start" @@ -206,84 +186,7 @@ {{'propertiesPanel.verticalOrientation' | translate }} </mat-checkbox> - <aspect-drop-list-properties [combinedProperties]="combinedProperties" - (updateModel)="updateModel.emit($event)"> - </aspect-drop-list-properties> - - <mat-form-field *ngIf="combinedProperties.appDefinition != null" - matTooltip="{{'propertiesPanel.appDefinition' | translate }}" - appearance="fill"> - <mat-label>{{'propertiesPanel.appDefinition' | translate }}</mat-label> - <input matInput disabled - [value]="$any(combinedProperties.appDefinition)" - (input)="updateModel.emit({ property: 'appDefinition', value: $any($event.target).value })"> - <button mat-icon-button matSuffix (click)="showGeogebraAppDefDialog()"> - <mat-icon>edit</mat-icon> - </button> - </mat-form-field> - - <mat-checkbox *ngIf="combinedProperties.showResetIcon !== undefined" - [checked]="$any(combinedProperties.showResetIcon)" - (change)="updateModel.emit({ property: 'showResetIcon', value: $event.checked })"> - {{'propertiesPanel.showResetIcon' | translate }} - </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.enableUndoRedo !== undefined" - [checked]="$any(combinedProperties.enableUndoRedo)" - (change)="updateModel.emit({ property: 'enableUndoRedo', value: $event.checked })"> - {{'propertiesPanel.enableUndoRedo' | translate }} - </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.enableShiftDragZoom !== undefined" - [checked]="$any(combinedProperties.enableShiftDragZoom)" - (change)="updateModel.emit({ property: 'enableShiftDragZoom', value: $event.checked })"> - {{'propertiesPanel.enableShiftDragZoom' | translate }} - </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.showZoomButtons !== undefined" - [checked]="$any(combinedProperties.showZoomButtons)" - (change)="updateModel.emit({ property: 'showZoomButtons', value: $event.checked })"> - {{'propertiesPanel.showZoomButtons' | translate }} - </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.showFullscreenButton !== undefined" - [checked]="$any(combinedProperties.showFullscreenButton)" - (change)="updateModel.emit({ property: 'showFullscreenButton', value: $event.checked })"> - {{'propertiesPanel.showFullscreenButton' | translate }} - </mat-checkbox> - - <mat-checkbox *ngIf="combinedProperties.showToolbar !== undefined" - [checked]="$any(combinedProperties.showToolbar)" - (change)="updateModel.emit({ property: 'showToolbar', value: $event.checked })"> - {{'propertiesPanel.showToolbar' | translate }} - </mat-checkbox> - <mat-form-field *ngIf="combinedProperties.customToolbar != null" - matTooltip="{{'propertiesPanel.customToolbarHelp' | translate }}" - appearance="fill"> - <mat-label>{{'propertiesPanel.customToolbar' | translate }}</mat-label> - <input matInput [disabled]="!combinedProperties.showToolbar" - [value]="$any(combinedProperties.customToolbar)" - (input)="updateModel.emit({ property: 'customToolbar', value: $any($event.target).value })"> - </mat-form-field> - - <mat-form-field *ngIf="combinedProperties.trackedVariables !== undefined" - class="wide-form-field" appearance="fill"> - <mat-label>{{'propertiesPanel.trackedVariables' | translate }}</mat-label> - <mat-select multiple [ngModel]="combinedProperties.trackedVariables" - (ngModelChange)="setGeometryVariables($event)"> - <mat-select-trigger> - {{'propertiesPanel.trackedVariables' | translate }} ({{$any(combinedProperties.trackedVariables).length}}) - </mat-select-trigger> - <mat-option *ngFor="let variable of geometryObjects | async" [value]="variable"> - {{variable}} - </mat-option> - </mat-select> - </mat-form-field> - - <mat-checkbox *ngIf="combinedProperties.enableModeSwitch !== undefined" - [checked]="$any(combinedProperties.enableModeSwitch)" - (change)="updateModel.emit({ property: 'enableModeSwitch', value: $event.checked })"> - {{'propertiesPanel.enableModeSwitch' | translate }} - </mat-checkbox> - - <mat-checkbox *ngIf="combinedProperties.isRelevantForPresentationComplete !== undefined" - [style.margin-top.px]="20" + <mat-checkbox [style.margin-top.px]="20" [checked]="$any(combinedProperties.isRelevantForPresentationComplete)" (change)="updateModel .emit({ property: 'isRelevantForPresentationComplete', value: $event.checked })"> diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts index 44d3bd7871ad2c62782307fbbd1a35773b1b3253..4bfb98fae62b2122e3ad963d2b649599fcdedcdf 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts @@ -1,9 +1,12 @@ +// eslint-disable-next-line max-classes-per-file import { Component, ComponentRef, EventEmitter, - Input, OnDestroy, OnInit, Output + Input, OnDestroy, OnInit, Output, Pipe, PipeTransform } from '@angular/core'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; -import { InputElementValue, UIElement } from 'common/models/elements/element'; +import { + InputElement, InputElementValue, isInputElement, UIElement, UIElementValue +} from 'common/models/elements/element'; import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row'; import { FileService } from 'common/services/file.service'; import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; @@ -24,45 +27,22 @@ import { DialogService } from '../../../services/dialog.service'; templateUrl: './element-model-properties.component.html', styleUrls: ['./element-model-properties.component.css'] }) -export class ElementModelPropertiesComponent implements OnInit, OnDestroy { +export class ElementModelPropertiesComponent implements OnDestroy { @Input() combinedProperties!: CombinedProperties; @Input() selectedElements: UIElement[] = []; @Output() updateModel = new EventEmitter<{ property: string; - value: InputElementValue | TextImageLabel[] | LikertRowElement[] | TextLabel[] | Hotspot[] | StateVariable + // value: InputElementValue | TextImageLabel[] | LikertRowElement[] | TextLabel[] | Hotspot[] | StateVariable + value: UIElementValue isInputValid?: boolean | null }>(); - geometryObjects: BehaviorSubject<string[]> = new BehaviorSubject<string[]>([]); private ngUnsubscribe = new Subject<void>(); constructor(public unitService: UnitService, public selectionService: SelectionService, public dialogService: DialogService) { } - ngOnInit(): void { - this.initGeometryListener(); - } - - initGeometryListener(): void { - this.selectionService.selectedElements.pipe( - switchMap((selectedElements: UIElement[]) => { - if (selectedElements.length !== 1 || - selectedElements[0].type !== 'geometry') { - return of(false); - } - return (this.selectionService.selectedElementComponents[0].childComponent as ComponentRef<GeometryComponent>) - .instance.isLoaded.asObservable(); - })) - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe((isLoaded: boolean) => { - if (!isLoaded) return; - this.geometryObjects.next( - (this.selectionService.selectedElementComponents[0].childComponent as ComponentRef<GeometryComponent>) - .instance.getGeometryObjects()); - }); - } - addListValue(property: string, value: string): void { this.updateModel.emit({ property: property, @@ -104,20 +84,18 @@ export class ElementModelPropertiesComponent implements OnInit, OnDestroy { this.updateModel.emit({ property: 'src', value: mediaSrc }); } - async showGeogebraAppDefDialog() { - const appDefinition = await firstValueFrom(this.dialogService.showGeogebraAppDefinitionDialog()); - if (appDefinition) this.updateModel.emit({ property: 'appDefinition', value: appDefinition }); - } - - setGeometryVariables(variables: string[]) { - this.updateModel.emit({ - property: 'trackedVariables', - value: variables - }); - } - ngOnDestroy(): void { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); } } + +@Pipe({ + name: 'isInputElement', + standalone: true +}) +export class IsInputElementPipe implements PipeTransform { + transform(el: UIElement): el is InputElement { + return isInputElement(el); + } +} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific-props.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific-props.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3af66f405a606d37f5cc7d14f38447ac39d1fbd --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific-props.component.ts @@ -0,0 +1,99 @@ +import { + Component, EventEmitter, Input, Output +} from '@angular/core'; +import { NgIf } from '@angular/common'; +import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; +import { + MathFieldPropsComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/math-field-props.component'; +import { UIElementValue } from 'common/models/elements/element'; +import { + MathTablePropertiesComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/math-table-properties.component'; +import { + ButtonPropertiesComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/button-properties.component'; +import { + DropListPropertiesComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component'; +import { + BorderPropertiesComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/border-properties.component'; +import { + GeometryPropsComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/geometry-props.component'; +import { + HotspotPropsComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/hotspot-props.component'; +import { + SliderPropertiesComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/slider-properties.component'; +import { + TextPropsComponent +} from 'editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/text-properties-field-set.component'; + +@Component({ + selector: 'aspect-ele-specific-props', + standalone: true, + imports: [ + NgIf, + MathFieldPropsComponent, + BorderPropertiesComponent, + MathTablePropertiesComponent, + ButtonPropertiesComponent, + DropListPropertiesComponent, + BorderPropertiesComponent, + GeometryPropsComponent, + HotspotPropsComponent, + SliderPropertiesComponent, + TextPropsComponent + ], + template: ` + <aspect-math-field-props *ngIf="combinedProperties.type === 'math-field'" + [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-math-field-props> + + <aspect-border-properties *ngIf="combinedProperties.type === 'frame'" + [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-border-properties> + + <aspect-math-table-properties *ngIf="combinedProperties.type === 'math-table'" + [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-math-table-properties> + + <aspect-button-properties *ngIf="combinedProperties.type === 'button'" + [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-button-properties> + + <aspect-drop-list-properties [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-drop-list-properties> + + <aspect-geometry-props *ngIf="combinedProperties.type === 'geometry'" + [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-geometry-props> + + <aspect-hotspot-props *ngIf="combinedProperties.type === 'hotspot-image'" + [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-hotspot-props> + + <aspect-slider-properties [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-slider-properties> + + <aspect-text-props [combinedProperties]="combinedProperties" + (updateModel)="updateModel.emit($event)"> + </aspect-text-props> + ` +}) +export class EleSpecificPropsComponent { + @Input() combinedProperties!: CombinedProperties; + @Output() updateModel = + new EventEmitter<{ property: string; value: UIElementValue, isInputValid?: boolean | null }>(); +} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/border-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/border-properties.component.ts similarity index 60% rename from projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/border-properties.component.ts rename to projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/border-properties.component.ts index 9ec559c9088a18fcea45a74e44838b76d4babb89..25db4de1339a9afc103197856403c6b73f98e48e 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/border-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/border-properties.component.ts @@ -2,30 +2,35 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { UIElement } from 'common/models/elements/element'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgIf } from '@angular/common'; @Component({ selector: 'aspect-border-properties', + standalone: true, + imports: [ + MatCheckboxModule, + TranslateModule, + NgIf + ], template: ` <div class="fx-column-start-stretch"> - <mat-checkbox *ngIf="combinedProperties.hasBorderTop !== undefined" - [checked]="$any(combinedProperties).hasBorderTop" + <mat-checkbox [checked]="$any(combinedProperties).hasBorderTop" (change)="updateModel.emit({ property: 'hasBorderTop', value: $event.checked })"> - {{'propertiesPanel.hasBorderTop' | translate }} + {{ 'propertiesPanel.hasBorderTop' | translate }} </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.hasBorderBottom !== undefined" - [checked]="$any(combinedProperties).hasBorderBottom" + <mat-checkbox [checked]="$any(combinedProperties).hasBorderBottom" (change)="updateModel.emit({ property: 'hasBorderBottom', value: $event.checked })"> - {{'propertiesPanel.hasBorderBottom' | translate }} + {{ 'propertiesPanel.hasBorderBottom' | translate }} </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.hasBorderLeft !== undefined" - [checked]="$any(combinedProperties).hasBorderLeft" + <mat-checkbox [checked]="$any(combinedProperties).hasBorderLeft" (change)="updateModel.emit({ property: 'hasBorderLeft', value: $event.checked })"> - {{'propertiesPanel.hasBorderLeft' | translate }} + {{ 'propertiesPanel.hasBorderLeft' | translate }} </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.hasBorderRight !== undefined" - [checked]="$any(combinedProperties).hasBorderRight" + <mat-checkbox [checked]="$any(combinedProperties).hasBorderRight" (change)="updateModel.emit({ property: 'hasBorderRight', value: $event.checked })"> - {{'propertiesPanel.hasBorderRight' | translate }} + {{ 'propertiesPanel.hasBorderRight' | translate }} </mat-checkbox> </div> `, diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/button-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/button-properties.component.ts similarity index 76% rename from projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/button-properties.component.ts rename to projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/button-properties.component.ts index 1771bac24f7455dde2cfdf66cc8975987fd67ac8..cb4ccb26780cf8c930ad9f9112ff95771e9cc01b 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/button-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/button-properties.component.ts @@ -3,13 +3,33 @@ import { } from '@angular/core'; import { FileService } from 'common/services/file.service'; import { UIElement } from 'common/models/elements/element'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgForOf, NgIf } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatInputModule } from '@angular/material/input'; +import { FormsModule } from '@angular/forms'; @Component({ selector: 'aspect-button-properties', + standalone: true, + imports: [ + NgIf, + NgForOf, + TranslateModule, + MatButtonModule, + MatCheckboxModule, + MatFormFieldModule, + MatSelectModule, + MatInputModule, + FormsModule + ], template: ` <ng-container *ngIf="combinedProperties.asLink !== undefined"> <fieldset> - <legend>{{'propertiesPanel.presentation' | translate}}</legend> + <legend>{{ 'propertiesPanel.presentation' | translate }}</legend> <div class="fx-column-start-stretch fx-gap-20"> <div class="fx-row-space-between-stretch"> @@ -17,11 +37,13 @@ import { UIElement } from 'common/models/elements/element'; class="fx-fill" [class.checked]="!combinedProperties.imageSrc && !combinedProperties.asLink" (click)="removeImage(); updateModel.emit({ property: 'asLink', value: false });"> - {{'propertiesPanel.button' | translate}}</button> + {{ 'propertiesPanel.button' | translate }} + </button> <button mat-button class="fx-fill" [class.checked]="!!combinedProperties.imageSrc && !combinedProperties.asLink" (click)="imageUpload.click(); updateModel.emit({ property: 'asLink', value: false });"> - {{'propertiesPanel.image' | translate}}</button> + {{ 'propertiesPanel.image' | translate }} + </button> <input type="file" hidden accept="image/*" #imageUpload id="button-image-upload" (change)="loadImage($event)"> @@ -29,7 +51,8 @@ import { UIElement } from 'common/models/elements/element'; class="fx-fill" [class.checked]="!combinedProperties.imageSrc && !!combinedProperties.asLink" (click)="removeImage(); updateModel.emit({ property: 'asLink', value: true });"> - {{'propertiesPanel.link' | translate}}</button> + {{ 'propertiesPanel.link' | translate }} + </button> </div> <div *ngIf="!!combinedProperties.imageSrc"> @@ -37,12 +60,12 @@ import { UIElement } from 'common/models/elements/element'; <button mat-raised-button [disabled]="combinedProperties.asLink" (click)="imageUpload.click();"> - {{'updateImage' | translate }} + {{ 'updateImage' | translate }} </button> <button mat-raised-button class="fx-fill" (click)="removeImage()"> - {{'removeImage' | translate }} + {{ 'removeImage' | translate }} </button> </div> <img class="image-preview" @@ -54,28 +77,28 @@ import { UIElement } from 'common/models/elements/element'; [disabled]="combinedProperties.labelAlignment === 'sub'" (change)="updateModel.emit({ property: 'labelAlignment', value: $event.checked ? 'super' : 'baseline' })"> - {{'propertiesPanel.super' | translate }} + {{ 'propertiesPanel.super' | translate }} </mat-checkbox> <mat-checkbox [checked]="combinedProperties.labelAlignment === 'sub'" [disabled]="combinedProperties.labelAlignment === 'super'" (change)="updateModel.emit({ property: 'labelAlignment', value: $event.checked ? 'sub' : 'baseline' })"> - {{'propertiesPanel.sub' | translate }} + {{ 'propertiesPanel.sub' | translate }} </mat-checkbox> </div> </div> </fieldset> <fieldset> - <legend>{{'propertiesPanel.tooltip' | translate}}</legend> + <legend>{{ 'propertiesPanel.tooltip' | translate }}</legend> <div class="fx-column-start-stretch"> <mat-form-field *ngIf="combinedProperties.tooltipText !== undefined"> - <mat-label>{{'propertiesPanel.tooltipText' | translate}}</mat-label> + <mat-label>{{ 'propertiesPanel.tooltipText' | translate }}</mat-label> <input matInput [ngModel]="combinedProperties.tooltipText" (ngModelChange)="updateModel.emit({ property: 'tooltipText', value: $event })"> </mat-form-field> <mat-form-field *ngIf="combinedProperties.tooltipPosition !== undefined" appearance="fill"> - <mat-label>{{'propertiesPanel.tooltipPosition' | translate }}</mat-label> + <mat-label>{{ 'propertiesPanel.tooltipPosition' | translate }}</mat-label> <mat-select [value]="combinedProperties.tooltipPosition" (selectionChange)="updateModel.emit({ property: 'tooltipPosition', value: $event.value })"> <mat-option *ngFor="let option of ['left', 'right', 'above', 'below']" @@ -92,7 +115,8 @@ import { UIElement } from 'common/models/elements/element'; .checked { background-color: #ccc; } - .image-preview { + + .image-preview { max-width: 100%; max-height: 100%; } @@ -100,10 +124,12 @@ import { UIElement } from 'common/models/elements/element'; .fx-fill { flex: 1 1 0; } + .fx-gap-20 { gap: 20px; } - .fx-row-space-between-stretch{ + + .fx-row-space-between-stretch { box-sizing: border-box; display: flex; flex-direction: row; @@ -116,7 +142,8 @@ import { UIElement } from 'common/models/elements/element'; display: flex; flex-direction: column; justify-content: flex-start; - align-items: stretch;} + align-items: stretch; + } `] }) export class ButtonPropertiesComponent { diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts similarity index 89% rename from projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts rename to projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts index 6b7f4a1fd92ab51de8bf2df984a5af69c780fec8..9f421250987ed557fbbe2e4353009417537d32f5 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/drop-list-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts @@ -2,20 +2,54 @@ import { Component, EventEmitter, Input, Output, Pipe, PipeTransform, ViewChild } from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { moveItemInArray } from '@angular/cdk/drag-drop'; -import { MatSelect } from '@angular/material/select'; +import { MatSelect, MatSelectModule } from '@angular/material/select'; import { MatOption } from '@angular/material/core'; import { MessageService } from 'common/services/message.service'; import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; import { IDService } from 'editor/src/app/services/id.service'; import { DragNDropValueObject, TextImageLabel } from 'common/models/elements/label-interfaces'; -import { UnitService } from '../../../../services/unit.service'; -import { SelectionService } from '../../../../services/selection.service'; -import { DialogService } from '../../../../services/dialog.service'; +import { NgForOf, NgIf } from '@angular/common'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { OptionListPanelComponent } from 'editor/src/app/components/properties-panel/option-list-panel.component'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { UnitService } from 'editor/src/app/services/unit.service'; +import { DialogService } from 'editor/src/app/services/dialog.service'; + +@Pipe({ + name: 'getValidDropLists', + standalone: true +}) +export class GetValidDropListsPipe implements PipeTransform { + constructor(private unitService: UnitService) {} + + transform(idList: string[] | undefined): string[] { + if (!idList) return []; + return this.unitService.getAllDropListElementIDs() + .filter(dropListID => !idList.includes(dropListID)); + } +} @Component({ selector: 'aspect-drop-list-properties', + standalone: true, + imports: [ + NgIf, + TranslateModule, + MatInputModule, + MatSelectModule, + MatCheckboxModule, + OptionListPanelComponent, + FormsModule, + MatButtonModule, + NgForOf, + MatTooltipModule, + GetValidDropListsPipe + ], template: ` <div *ngIf="combinedProperties.type === 'drop-list'" class="fx-column-start-stretch"> @@ -123,19 +157,15 @@ export class DropListPropertiesComponent { value: string | number | boolean | string[] | DragNDropValueObject[], isInputValid?: boolean | null }>(); + @ViewChild('selectConnectedLists') selectConnected!: MatSelect; constructor(public unitService: UnitService, - private selectionService: SelectionService, private dialogService: DialogService, private idManager: IDService, private messageService: MessageService, private translateService: TranslateService) { } - notifyListChange(changedList: DragNDropValueObject[]): void { - this.updateModel.emit({ property: 'value', value: changedList }); - } - updateAllowReplacement(value: boolean) { if (value) this.updateOnlyOneItem(true); this.updateModel.emit({ property: 'allowReplacement', value }); @@ -204,16 +234,3 @@ export class DropListPropertiesComponent { this.selectConnected.options.forEach((item: MatOption) => item.select()); } } - -@Pipe({ - name: 'getValidDropLists' -}) -export class GetValidDropListsPipe implements PipeTransform { - constructor(private unitService: UnitService) {} - - transform(idList: string[] | undefined): string[] { - if (!idList) return []; - return this.unitService.getAllDropListElementIDs() - .filter(dropListID => !idList.includes(dropListID)); - } -} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/geometry-props.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/geometry-props.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..c7f6cb6f7bde897ac643d37a436abc243e05e95d --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/geometry-props.component.ts @@ -0,0 +1,142 @@ +import { Component, ComponentRef, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { TranslateModule } from '@ngx-translate/core'; +import { AsyncPipe, NgForOf, NgIf } from '@angular/common'; +import { MatInputModule } from '@angular/material/input'; +import { MatIconModule } from '@angular/material/icon'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { UIElement } from 'common/models/elements/element'; +import { MatButtonModule } from '@angular/material/button'; +import { MatSelectModule } from '@angular/material/select'; +import { FormsModule } from '@angular/forms'; +import { BehaviorSubject, firstValueFrom, of, Subject, switchMap } from 'rxjs'; +import { GeometryComponent } from 'common/components/geometry/geometry.component'; +import { takeUntil } from 'rxjs/operators'; +import { SelectionService } from 'editor/src/app/services/selection.service'; +import { DialogService } from 'editor/src/app/services/dialog.service'; + +@Component({ + selector: 'aspect-geometry-props', + standalone: true, + imports: [ + NgForOf, + MatInputModule, + MatIconModule, + MatCheckboxModule, + MatTooltipModule, + TranslateModule, + MatButtonModule, + MatSelectModule, + FormsModule, + AsyncPipe + ], + template: ` + <mat-form-field matTooltip="{{'propertiesPanel.appDefinition' | translate }}" + appearance="fill"> + <mat-label>{{ 'propertiesPanel.appDefinition' | translate }}</mat-label> + <input matInput disabled + [value]="$any(combinedProperties.appDefinition)" + (input)="updateModel.emit({ property: 'appDefinition', value: $any($event.target).value })"> + <button mat-icon-button matSuffix (click)="showGeogebraAppDefDialog()"> + <mat-icon>edit</mat-icon> + </button> + </mat-form-field> + + <mat-checkbox [checked]="$any(combinedProperties.showResetIcon)" + (change)="updateModel.emit({ property: 'showResetIcon', value: $event.checked })"> + {{ 'propertiesPanel.showResetIcon' | translate }} + </mat-checkbox> + <mat-checkbox [checked]="$any(combinedProperties.enableUndoRedo)" + (change)="updateModel.emit({ property: 'enableUndoRedo', value: $event.checked })"> + {{ 'propertiesPanel.enableUndoRedo' | translate }} + </mat-checkbox> + <mat-checkbox [checked]="$any(combinedProperties.enableShiftDragZoom)" + (change)="updateModel.emit({ property: 'enableShiftDragZoom', value: $event.checked })"> + {{ 'propertiesPanel.enableShiftDragZoom' | translate }} + </mat-checkbox> + <mat-checkbox [checked]="$any(combinedProperties.showZoomButtons)" + (change)="updateModel.emit({ property: 'showZoomButtons', value: $event.checked })"> + {{ 'propertiesPanel.showZoomButtons' | translate }} + </mat-checkbox> + <mat-checkbox [checked]="$any(combinedProperties.showFullscreenButton)" + (change)="updateModel.emit({ property: 'showFullscreenButton', value: $event.checked })"> + {{ 'propertiesPanel.showFullscreenButton' | translate }} + </mat-checkbox> + + <mat-checkbox [checked]="$any(combinedProperties.showToolbar)" + (change)="updateModel.emit({ property: 'showToolbar', value: $event.checked })"> + {{ 'propertiesPanel.showToolbar' | translate }} + </mat-checkbox> + <mat-form-field matTooltip="{{'propertiesPanel.customToolbarHelp' | translate }}" + appearance="fill"> + <mat-label>{{ 'propertiesPanel.customToolbar' | translate }}</mat-label> + <input matInput [disabled]="!combinedProperties.showToolbar" + [value]="$any(combinedProperties.customToolbar)" + (input)="updateModel.emit({ property: 'customToolbar', value: $any($event.target).value })"> + </mat-form-field> + + <mat-form-field class="wide-form-field" appearance="fill"> + <mat-label>{{ 'propertiesPanel.trackedVariables' | translate }}</mat-label> + <mat-select multiple [ngModel]="combinedProperties.trackedVariables" + (ngModelChange)="setGeometryVariables($event)"> + <mat-select-trigger> + {{ 'propertiesPanel.trackedVariables' | translate }} ({{ $any(combinedProperties.trackedVariables).length }}) + </mat-select-trigger> + <mat-option *ngFor="let variable of geometryObjects | async" [value]="variable"> + {{ variable }} + </mat-option> + </mat-select> + </mat-form-field> + ` +}) +export class GeometryPropsComponent implements OnInit, OnDestroy { + @Input() combinedProperties!: UIElement; + @Output() updateModel = + new EventEmitter<{ property: string; value: string | number | boolean | null | string[] }>(); + + geometryObjects: BehaviorSubject<string[]> = new BehaviorSubject<string[]>([]); + private ngUnsubscribe = new Subject<void>(); + + constructor(public selectionService: SelectionService, + public dialogService: DialogService) { } + + ngOnInit(): void { + this.initGeometryListener(); + } + + initGeometryListener(): void { + this.selectionService.selectedElements.pipe( + switchMap((selectedElements: UIElement[]) => { + if (selectedElements.length !== 1 || + selectedElements[0].type !== 'geometry') { + return of(false); + } + return (this.selectionService.selectedElementComponents[0].childComponent as ComponentRef<GeometryComponent>) + .instance.isLoaded.asObservable(); + })) + .pipe(takeUntil(this.ngUnsubscribe)) + .subscribe((isLoaded: boolean) => { + if (!isLoaded) return; + this.geometryObjects.next( + (this.selectionService.selectedElementComponents[0].childComponent as ComponentRef<GeometryComponent>) + .instance.getGeometryObjects()); + }); + } + + async showGeogebraAppDefDialog() { + const appDefinition = await firstValueFrom(this.dialogService.showGeogebraAppDefinitionDialog()); + if (appDefinition) this.updateModel.emit({ property: 'appDefinition', value: appDefinition }); + } + + setGeometryVariables(variables: string[]) { + this.updateModel.emit({ + property: 'trackedVariables', + value: variables + }); + } + + ngOnDestroy(): void { + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); + } +} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/hotspot-field-set.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/hotspot-props.component.ts similarity index 55% rename from projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/hotspot-field-set.component.ts rename to projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/hotspot-props.component.ts index dbd182b1f4badf943533034516d6ed9c00907713..1aed25df33727ba05f3542f5e3b9d305a4593fe8 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/hotspot-field-set.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/hotspot-props.component.ts @@ -3,25 +3,54 @@ import { } from '@angular/core'; import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; import { DialogService } from 'editor/src/app/services/dialog.service'; -import { moveItemInArray } from '@angular/cdk/drag-drop'; - +import { CdkDrag, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop'; import { Hotspot } from 'common/models/elements/input-elements/hotspot-image'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgForOf, NgIf } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; @Component({ - selector: 'aspect-hotspot-field-set', + selector: 'aspect-hotspot-props', + standalone: true, + imports: [ + NgIf, + TranslateModule, + CdkDrag, + CdkDropList, + MatButtonModule, + MatFormFieldModule, + MatIconModule, + NgForOf + ], template: ` - <aspect-hotspot-list-panel *ngIf="combinedProperties.value !== undefined" - [itemList]="$any(combinedProperties.value)" - [title]="'propertiesPanel.hotspots' | translate" - [textFieldLabel]="'propertiesPanel.newHotspot' | translate" - (changedItemOrder)="moveHotspot($event)" - (addItem)="addHotspot()" - (removeItem)="removeHotspot($event)" - (editItem)="editHotspot($event)"> - </aspect-hotspot-list-panel> + <fieldset class="fx-column-start-stretch"> + <legend>{{'propertiesPanel.hotspots' | translate }}</legend> + <button class="fx-align-self-center" mat-mini-fab matSuffix color="primary" [style.bottom.px]="3" + (click)="addHotspot()"> + <mat-icon>add</mat-icon> + </button> + + <div class="drop-list" cdkDropList [cdkDropListData]="combinedProperties.value" + (cdkDropListDropped)="moveHotspot($event)"> + <div *ngFor="let item of $any(combinedProperties.value); let i = index" cdkDrag + class="option-draggable fx-row-start-stretch"> + <div class="fx-flex fx-align-self-center">{{'hotspot.'+item.shape | translate}}</div> + <button mat-icon-button color="primary" + (click)="editHotspot(i)"> + <mat-icon>build</mat-icon> + </button> + <button mat-icon-button color="primary" + (click)="removeHotspot(i)"> + <mat-icon>clear</mat-icon> + </button> + </div> + </div> + </fieldset> ` }) -export class HotspotFieldSetComponent { +export class HotspotPropsComponent { @Input() combinedProperties!: CombinedProperties; @Output() updateModel = new EventEmitter<{ property: string; value: Hotspot[] }>(); diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/math-field-props.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/math-field-props.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..a011a3a72c75dac09324ae7d031f8aa6c4a93e8e --- /dev/null +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/math-field-props.component.ts @@ -0,0 +1,26 @@ +import { + Component, EventEmitter, Input, Output +} from '@angular/core'; +import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatCheckboxModule } from '@angular/material/checkbox'; + +@Component({ + selector: 'aspect-math-field-props', + standalone: true, + imports: [ + TranslateModule, + MatCheckboxModule + ], + template: ` + <mat-checkbox [checked]="$any(combinedProperties.enableModeSwitch)" + (change)="updateModel.emit({ property: 'enableModeSwitch', value: $event.checked })"> + {{'propertiesPanel.enableModeSwitch' | translate }} + </mat-checkbox> + ` +}) +export class MathFieldPropsComponent { + @Input() combinedProperties!: CombinedProperties; + @Output() updateModel = + new EventEmitter<{ property: string; value: boolean, isInputValid?: boolean | null }>(); +} diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/math-table-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/math-table-properties.component.ts similarity index 80% rename from projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/math-table-properties.component.ts rename to projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/math-table-properties.component.ts index bde0ee614fcab09dcfe84a114861675b6b0f5381..8eb40655f34af8bd1fb595b23334a1d4c0951d16 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/math-table-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/math-table-properties.component.ts @@ -8,24 +8,43 @@ import { ViewChildren } from '@angular/core'; import { UIElement } from 'common/models/elements/element'; +import { NgForOf, NgIf } from '@angular/common'; +import { MatInputModule } from '@angular/material/input'; +import { MatSelectModule } from '@angular/material/select'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatIconModule } from '@angular/material/icon'; +import { MatButtonModule } from '@angular/material/button'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatCheckboxModule } from '@angular/material/checkbox'; @Component({ selector: 'aspect-math-table-properties', + standalone: true, + imports: [ + NgIf, + NgForOf, + MatInputModule, + MatSelectModule, + TranslateModule, + MatIconModule, + MatButtonModule, + MatMenuModule, + MatCheckboxModule + ], template: ` - <div *ngIf="combinedProperties.terms !== undefined" - [style.display]="'flex'" [style.flex-direction]="'column'"> + <div [style.display]="'flex'" [style.flex-direction]="'column'"> <mat-form-field> <mat-label>Operation</mat-label> <mat-select [value]="combinedProperties.operation" (selectionChange)="updateModel.emit({ property: 'operation', value: $event.value })"> <mat-option *ngFor="let operation of ['addition', 'subtraction', 'multiplication', 'variable']" [value]="operation"> - {{operation | translate}} + {{ operation | translate }} </mat-option> </mat-select> </mat-form-field> - {{'termRows' | translate}} + {{ 'termRows' | translate }} <div *ngFor="let term of $any(combinedProperties).terms; let i = index;" class="term-list"> <ng-container *ngIf="combinedProperties.operation !== 'multiplication' || i < 2"> @@ -45,18 +64,19 @@ import { UIElement } from 'common/models/elements/element'; [disabled]="combinedProperties.operation === 'multiplication' && $any(combinedProperties.terms).length >= 2" (click)="addTerm()"> - <mat-icon>add</mat-icon>{{'addTermRow' | translate}} + <mat-icon>add</mat-icon> + {{ 'addTermRow' | translate }} </button> <mat-form-field (input)="updateModel.emit({ property: 'resultHelperRow', value: $any($event.target).value })"> - <mat-label>{{'resultHelperRow' | translate}}</mat-label> + <mat-label>{{ 'resultHelperRow' | translate }}</mat-label> <input matInput [disabled]="combinedProperties.operation === 'variable' && !$any(combinedProperties.variableLayoutOptions).showResultRow" [value]="combinedProperties.resultHelperRow"> </mat-form-field> <mat-form-field (input)="updateModel.emit({ property: 'result', value: $any($event.target).value })"> - <mat-label>{{'resultRow' | translate}}</mat-label> + <mat-label>{{ 'resultRow' | translate }}</mat-label> <input matInput [disabled]="combinedProperties.operation === 'variable' && !$any(combinedProperties.variableLayoutOptions).showResultRow" [value]="combinedProperties.result"> @@ -72,27 +92,27 @@ import { UIElement } from 'common/models/elements/element'; <mat-checkbox [checked]="$any(combinedProperties.variableLayoutOptions).allowArithmeticChars" (click)="$event.stopPropagation()" (change)="updateModel.emit({ property: 'allowArithmeticChars', value: $event.checked })"> - {{'propertiesPanel.allowArithmeticChars' | translate }} + {{ 'propertiesPanel.allowArithmeticChars' | translate }} </mat-checkbox> <mat-checkbox [checked]="$any(combinedProperties.variableLayoutOptions).isFirstLineUnderlined" (click)="$event.stopPropagation()" (change)="updateModel.emit({ property: 'isFirstLineUnderlined', value: $event.checked })"> - {{'propertiesPanel.isFirstLineUnderlined' | translate }} + {{ 'propertiesPanel.isFirstLineUnderlined' | translate }} </mat-checkbox> <mat-checkbox [checked]="$any(combinedProperties.variableLayoutOptions).showResultRow" (click)="$event.stopPropagation()" (change)="updateModel.emit({ property: 'showResultRow', value: $event.checked })"> - {{'propertiesPanel.showResultRowWithHelperRow' | translate }} + {{ 'propertiesPanel.showResultRowWithHelperRow' | translate }} </mat-checkbox> <mat-checkbox [checked]="$any(combinedProperties.variableLayoutOptions).showTopHelperRows" (click)="$event.stopPropagation()" (change)="updateModel.emit({ property: 'showTopHelperRows', value: $event.checked })"> - {{'propertiesPanel.showTopHelperRows' | translate }} + {{ 'propertiesPanel.showTopHelperRows' | translate }} </mat-checkbox> <mat-checkbox [checked]="$any(combinedProperties.variableLayoutOptions).allowFirstLineCrossOut" (click)="$event.stopPropagation()" (change)="updateModel.emit({ property: 'allowFirstLineCrossOut', value: $event.checked })"> - {{'propertiesPanel.allowFirstLineCrossOut' | translate }} + {{ 'propertiesPanel.allowFirstLineCrossOut' | translate }} </mat-checkbox> </mat-menu> </div> @@ -103,6 +123,7 @@ import { UIElement } from 'common/models/elements/element'; flex-direction: row; margin-left: 15px; } + .add-button { width: 60%; border-radius: 0; diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/slider-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/slider-properties.component.ts similarity index 86% rename from projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/slider-properties.component.ts rename to projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/slider-properties.component.ts index 63957c34e21c7fc01c583902883eb600869f6537..fd485b0b53549bb80524644fbac08aab53bf49b9 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/slider-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/slider-properties.component.ts @@ -1,9 +1,22 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { NgIf } from '@angular/common'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatInputModule } from '@angular/material/input'; +import { FormsModule } from '@angular/forms'; +import { MatCheckboxModule } from '@angular/material/checkbox'; @Component({ selector: 'aspect-slider-properties', + standalone: true, + imports: [ + NgIf, + TranslateModule, + MatInputModule, + FormsModule, + MatCheckboxModule + ], template: ` <mat-form-field *ngIf="combinedProperties.minValue !== undefined" appearance="fill"> <mat-label>{{'propertiesPanel.minValue' | translate }}</mat-label> diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-properties-field-set.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/text-properties-field-set.component.ts similarity index 88% rename from projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-properties-field-set.component.ts rename to projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/text-properties-field-set.component.ts index 433db79c7bf59fa99c019105f3135f145dae6454..ef0ff9893436ff8a05b6874a17b4a595a79249b5 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/text-properties-field-set.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/text-properties-field-set.component.ts @@ -1,12 +1,23 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { NgIf } from '@angular/common'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { SharedModule } from 'common/shared.module'; import { TextElement } from 'common/models/elements/text/text'; -import { DialogService } from '../../../../services/dialog.service'; -import { SelectionService } from '../../../../services/selection.service'; +import { DialogService } from '../../../../../services/dialog.service'; +import { SelectionService } from '../../../../../services/selection.service'; @Component({ - selector: 'aspect-text-properties-field-set', + selector: 'aspect-text-props', + standalone: true, + imports: [ + NgIf, + SharedModule, + MatInputModule, + MatCheckboxModule + ], template: ` <div *ngIf="combinedProperties.text" class="fx-column-start-stretch"> Text @@ -63,16 +74,9 @@ import { SelectionService } from '../../../../services/selection.service'; max-height: 200px; overflow: scroll; } - .fx-column-start-stretch { - box-sizing: border-box; - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: stretch; - } `] }) -export class TextPropertiesFieldSetComponent { +export class TextPropsComponent { @Input() combinedProperties!: any; @Output() updateModel = new EventEmitter<{ property: string; value: string | number | boolean | string[], isInputValid?: boolean | null }>(); diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/input-element-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/input-element-properties.component.ts index ad80acfa8ae9f7d2838e1e2b5afb5e7995544553..d39381c3de43d869b92aa76dd5c94818bc020090 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/input-element-properties.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/input-element-properties.component.ts @@ -6,10 +6,10 @@ import { UIElement } from 'common/models/elements/element'; @Component({ selector: 'aspect-input-element-properties', template: ` - <fieldset *ngIf="combinedProperties.required !== undefined" class="fx-column-start-stretch"> + <fieldset class="fx-column-start-stretch"> <legend>Eingabeelement</legend> - <mat-form-field *ngIf="combinedProperties.label !== undefined && - combinedProperties.type !== 'drop-list'" + <!-- DropList label is unused --> + <mat-form-field *ngIf="combinedProperties.type !== 'drop-list'" appearance="fill"> <mat-label>{{'propertiesPanel.label' | translate }}</mat-label> <textarea matInput type="text" @@ -18,14 +18,12 @@ import { UIElement } from 'common/models/elements/element'; </textarea> </mat-form-field> - <mat-checkbox *ngIf="combinedProperties.readOnly !== undefined" - [checked]="$any(combinedProperties.readOnly)" + <mat-checkbox [checked]="$any(combinedProperties.readOnly)" (change)="updateModel.emit({ property: 'readOnly', value: $event.checked })"> {{'propertiesPanel.readOnly' | translate }} </mat-checkbox> - <mat-checkbox *ngIf="combinedProperties.required !== undefined" - [checked]="$any(combinedProperties.required)" + <mat-checkbox [checked]="$any(combinedProperties.required)" (change)="updateModel.emit({ property: 'required', value: $event.checked })"> {{'propertiesPanel.requiredField' | translate }} </mat-checkbox> diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts index dc5a68f2b5c8929ea5d125f9a6537a293abb0e28..865e12ce51fa990b807ff6506ac7932ceed69862 100644 --- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts +++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts @@ -14,10 +14,11 @@ import { OptionElement } from 'common/models/elements/element'; @Component({ selector: 'aspect-options-field-set', template: ` - <!--dropdown--> + <!--dropdown, radio-button-group--> <!-- [useRichText]="combinedProperties.type === 'radio'"--> <aspect-option-list-panel *ngIf="combinedProperties.options !== undefined" - [title]="'propertiesPanel.options'" [textFieldLabel]="'Neue Option'" + [title]="'propertiesPanel.options'" + [textFieldLabel]="'Neue Option'" [itemList]="$any(combinedProperties.options)" (addItem)="addOption('options', $event)" (removeItem)="removeOption('options', $event)" @@ -30,9 +31,7 @@ import { OptionElement } from 'common/models/elements/element'; [itemList]="$any(combinedProperties).rows | LikertRowLabel" [title]="'rows'" [textFieldLabel]="'Neue Zeile'" - (changedItemOrder)="moveLikertRow($event)" - (addItem)="addLikertRow($event)" (removeItem)="removeLikertRow($event)" (editItem)="editLikertRow($event)"> diff --git a/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts b/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts index 7e24474916969debb63a17b04cb5ee9fb212c129..981141d7ae9c39d74d83e1970b11e8fca37e7d32 100644 --- a/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts +++ b/projects/editor/src/app/components/properties-panel/option-list-panel.component.ts @@ -1,13 +1,28 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { CdkDragDrop } from '@angular/cdk/drag-drop'; - - +import { CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop'; import { Label } from 'common/models/elements/label-interfaces'; +import { TranslateModule } from '@ngx-translate/core'; +import { MatInputModule } from '@angular/material/input'; +import { SharedModule } from 'common/shared.module'; +import { MatIconModule } from '@angular/material/icon'; +import { NgForOf, NgIf } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; @Component({ selector: 'aspect-option-list-panel', + standalone: true, + imports: [ + NgIf, + NgForOf, + TranslateModule, + MatInputModule, + MatIconModule, + CdkDropList, + MatButtonModule, + SharedModule // TODO make pipe standalone and remove + ], template: ` <fieldset class="fx-column-start-stretch"> <legend>{{title | translate }}</legend>