Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<h2>{{ 'Reposition annotation' | translate }}</h2>

@if (hasChanged()) {
<p>
{{
'You have moved the annotation. Choose whether you want to apply the new position below, or reset to the original position.'
| translate
}}
</p>

<p>
{{
'Applying the position will also generate a new preview image using the updated position.'
| translate
}}
</p>
} @else {
<p>
{{
'Move the annotation by clicking and dragging the annotation on the desired axis.' | translate
}}
</p>
}
<k-button-row gap="4" justify="end">
<k-button type="outlined-secondary" (click)="close('reset')">{{ 'Reset' | translate }}</k-button>
<k-button type="solid-primary" (click)="close('apply')" [disabled]="!hasChanged()">{{
'Apply' | translate
}}</k-button>
</k-button-row>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
:host {
display: flex;
flex-direction: column;
gap: 12px;
padding: 32px;
max-width: 480px;

color: white;
h1 {
font-size: var(--font-size-large);
font-weight: var(--font-weight-bold);
}

h1,
p,
ul {
margin: 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Component, computed, inject, signal } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
import { ButtonComponent, ButtonRowComponent } from '@kompakkt/komponents';
import { TranslatePipe } from '../../../pipes/translate.pipe';
import { Vector3 } from '@babylonjs/core';

export type RepositionAnnotationDialogChoice = 'apply' | 'reset';

type ChangedPositions = {
prev: Vector3;
curr: Vector3;
};

@Component({
selector: 'app-dialog-reposition-annotation',
imports: [ButtonComponent, ButtonRowComponent, TranslatePipe],
templateUrl: './dialog-reposition-annotation.component.html',
styleUrl: './dialog-reposition-annotation.component.scss',
})
export class DialogRepositionAnnotationComponent {
#ref = inject(MatDialogRef<DialogRepositionAnnotationComponent>);
#positions = signal<ChangedPositions | undefined>(undefined);
hasChanged = computed(() => {
const positions = this.#positions();
if (!positions) return false;
const distance = Vector3.Distance(positions.prev, positions.curr);
return distance > 0;
});

public close(choice: RepositionAnnotationDialogChoice) {
this.#ref.close(choice);
}

public updatePositions(positions: ChangedPositions) {
this.#positions.set(positions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<div class="header" (click)="toggleVisibility()">
<span class="ranking">{{ annotation.ranking }}</span>
<span class="title">{{ annotation.body.content.title || ('No title' | translate) }}</span>
<small>{{ (annotation.validated ? 'validated' : 'not validated') | translate }}</small>
<small>{{ (isAnnotationUnsaved() ? 'not saved' : 'saved') | translate }}</small>
</div>
<app-markdown-preview
id="annotation-content"
Expand All @@ -21,6 +21,13 @@
(click)="toggleFullscreen('edit')"
><mat-icon>edit</mat-icon></k-button
>
<k-button
icon-button
color="transparent"
[tooltip]="'Reposition annotation' | translate"
(click)="toggleRepositionMode()"
><mat-icon>open_with</mat-icon></k-button
>
}
@if (canUserReorder$ | async) {
<k-button icon-button color="transparent" class="reorder-button">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ <h2>{{ annotation.body.content.title || ('No title' | translate) }}</h2>
(click)="deleteAnnotation()"
><mat-icon>delete</mat-icon></k-button
>
<k-button
icon-button
color="transparent"
[tooltip]="'Reposition annotation' | translate"
(click)="toggleRepositionMode()"
><mat-icon>open_with</mat-icon></k-button
>
<k-button
icon-button
color="transparent"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, ElementRef, HostBinding, Input, inject, output } from '@angular/core';
import { Component, ElementRef, HostBinding, Input, computed, inject, output } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Matrix, Vector3 } from '@babylonjs/core';
import { BehaviorSubject, ReplaySubject, combineLatest, firstValueFrom, interval, map } from 'rxjs';
Expand All @@ -17,6 +17,7 @@ import { MarkdownPreviewComponent } from '../../markdown-preview/markdown-previe
import { ExtenderSlotDirective } from '@kompakkt/plugins/extender';
import DeepClone from 'rfdc';
import deepEqual from 'fast-deep-equal';
import { toSignal } from '@angular/core/rxjs-interop';
const deepClone = DeepClone({ circles: true });

export type ReorderMovement = 'one-up' | 'one-down' | 'first' | 'last';
Expand Down Expand Up @@ -52,6 +53,7 @@ export class AnnotationComponent {
}
public annotation$ = new ReplaySubject<IAnnotation>(1);
public annotation$$ = this.annotation$.asObservable();
public annotationSignal = toSignal(this.annotation$$);

public positionTop = 0;
public positionLeft = 0;
Expand Down Expand Up @@ -115,6 +117,19 @@ export class AnnotationComponent {
map(([annotation, hiddenAnnotations]) => hiddenAnnotations.includes(annotation._id.toString())),
);

public isAnnotationUnsaved = computed(() => {
const unsaved = this.annotationService.unsavedAnnotations();
const annotation = this.annotationSignal();
if (!annotation) return false;
return unsaved.has(annotation._id.toString());
});

public async toggleRepositionMode() {
const annotation = await firstValueFrom(this.annotation$);
if (!annotation) return;
this.annotationService.initializeRepositionMode(annotation);
}

constructor() {
combineLatest([interval(15), this.annotation$])
.pipe(map(([_, annotation]) => annotation))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AsyncPipe } from '@angular/common';
import { Component, HostBinding, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { toObservable, toSignal } from '@angular/core/rxjs-interop';
import { MatIcon } from '@angular/material/icon';
import { ButtonComponent, TooltipDirective } from '@kompakkt/komponents';
import { BehaviorSubject, combineLatest, firstValueFrom, map } from 'rxjs';
Expand All @@ -15,6 +15,7 @@ import { AnnotationService } from '../../../services/annotation/annotation.servi
})
export class AnnotationwalkthroughComponent {
public annotationService = inject(AnnotationService);
#isRepositioning = toObservable(this.annotationService.isRepositioning);

public ranking$ = new BehaviorSubject(-1);
public selectedAnnotation$ = combineLatest([
Expand All @@ -27,11 +28,14 @@ export class AnnotationwalkthroughComponent {
);

public title$ = this.selectedAnnotation$.pipe(
map(annotation => annotation?.body.content.title ?? 'Annotation Walkthrough'),
);
public showWalkthrough$ = this.annotationService.currentAnnotations$.pipe(
map(annotations => annotations.length > 1),
map(annotation =>
annotation ? annotation.body.content.title || 'No title' : 'Annotation Walkthrough',
),
);
public showWalkthrough$ = combineLatest([
this.annotationService.currentAnnotations$,
this.#isRepositioning,
]).pipe(map(([annotations, isRepositioning]) => annotations.length > 1 && !isRepositioning));

public showWalkthrough = toSignal(this.showWalkthrough$);

Expand Down
5 changes: 5 additions & 0 deletions src/app/components/sidenav-menu/sidenav-menu.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
left: var(--sidebar-width);
}

&.disabled {
pointer-events: none;
opacity: 0.5;
}

&:not(.is-open) div.button-close {
transition: var(--transition-duration);
transform: translateX(-100%);
Expand Down
15 changes: 10 additions & 5 deletions src/app/components/sidenav-menu/sidenav-menu.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,30 @@ import { TranslatePipe } from '../../pipes/translate.pipe';
import { OverlayService } from '../../services/overlay/overlay.service';
import { ProcessingService } from '../../services/processing/processing.service';
import { toSignal } from '@angular/core/rxjs-interop';
import { AnnotationService } from 'src/app/services/annotation/annotation.service';

@Component({
selector: 'app-sidenav-menu',
templateUrl: './sidenav-menu.component.html',
styleUrls: ['./sidenav-menu.component.scss'],
imports: [MatIcon, AsyncPipe, TranslatePipe, TooltipDirective, ButtonComponent],
host: {
'[class.is-open]': 'isOpen()',
'[class.disabled]': 'isDisabled()',
},
})
export class SidenavMenuComponent {
constructor(
public overlay: OverlayService,
public processing: ProcessingService,
public annotation: AnnotationService,
) {}

private sidenav = toSignal(this.overlay.sidenav$);
public mode = computed(() => this.sidenav()?.mode);
public isOpen = computed(() => this.sidenav()?.open && this.mode() !== '');

@HostBinding('class.is-open')
get isSidenavOpen() {
return this.isOpen();
}
public isDisabled = computed(() => {
const isRepositioning = this.annotation.isRepositioning();
return isRepositioning;
});
}
Loading
Loading