Skip to content

Commit 2d98369

Browse files
authored
fix(region): highlight info of clicked region (DEV-724) (#703)
* fix(region): highlight info of clicked region (DEV-724) * refactor(resource): format code * feat(region): highlight region and display comment with label * refactor(still-image): remove console.log * feat(region): edit label, comment and color updates tooltip * test(region): fix tests and refactor code * chore(text-value): add comment for DEV-797 * refactor(still-image): organize imports * refactor(resource): remove console.log * docs(still-image): add comment
1 parent 374b5a8 commit 2d98369

8 files changed

Lines changed: 121 additions & 59 deletions

File tree

‎src/app/workspace/resource/properties/properties.component.ts‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
114114
*/
115115
@Output() referredResourceHovered: EventEmitter<ReadLinkValue> = new EventEmitter<ReadLinkValue>();
116116

117-
@Output() regionColorChanged: EventEmitter<ReadColorValue> = new EventEmitter<ReadColorValue>();
117+
@Output() regionChanged: EventEmitter<ReadValue> = new EventEmitter<ReadValue>();
118118

119119
lastModificationDate: string;
120120

@@ -352,6 +352,10 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
352352
(response: UpdateResourceMetadataResponse) => {
353353
this.resource.res.label = payload.label;
354354
this.lastModificationDate = response.lastModificationDate;
355+
// if annotations tab is active; a label of a region has been changed --> update regions
356+
if (this.isAnnotation) {
357+
this.regionChanged.emit();
358+
}
355359
},
356360
(error: ApiResponseError) => {
357361
this._errorHandler.showMessage(error);
@@ -456,8 +460,9 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
456460
if (updatedValue instanceof ReadTextValueAsXml) {
457461
this._updateStandoffLinkValue();
458462
}
459-
if (updatedValue instanceof ReadColorValue) {
460-
this.regionColorChanged.emit();
463+
// if annotations tab is active;
464+
if (this.isAnnotation) {
465+
this.regionChanged.emit();
461466
}
462467
} else {
463468
console.warn('No properties exist for this resource');

‎src/app/workspace/resource/representation/still-image/still-image.component.scss‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1+
@import "../../../../../assets/style/config";
12
// sizes
23
$max-width: 800px;
34
$panelSize: 40px;
45

56
$osd-height: 460px;
67

7-
// colors
8-
$dark: #000;
9-
$bright: #ccc;
10-
118
:host {
129
// display: inline-flex;
1310
width: 100%;
@@ -57,6 +54,26 @@ $bright: #ccc;
5754
}
5855
}
5956

57+
.annotation-tooltip {
58+
display: none;
59+
position: fixed;
60+
background-color: $black-60-opacity;
61+
color: $bright;
62+
padding: 8px;
63+
border-radius: $border-radius;
64+
min-height: 24px;
65+
max-height: 258px;
66+
max-width: 256px;
67+
box-sizing: border-box;
68+
transition: 0.1s;
69+
transform: translate(16px, 16px);
70+
font-size: small;
71+
white-space: nowrap;
72+
overflow: hidden;
73+
text-overflow: ellipsis;
74+
z-index: 1000;
75+
}
76+
6077
/*
6178
Overlay styling
6279
*/

‎src/app/workspace/resource/representation/still-image/still-image.component.spec.ts‎

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
55
import { HttpClientModule } from '@angular/common/http';
66
import { Component, OnInit, ViewChild } from '@angular/core';
77
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
8-
import { MatButtonHarness } from '@angular/material/button/testing';
98
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
10-
import { MatDialogHarness } from '@angular/material/dialog/testing';
119
import { MatIconModule } from '@angular/material/icon';
1210
import { MatSnackBarModule } from '@angular/material/snack-bar';
1311
import { MatToolbarModule } from '@angular/material/toolbar';
@@ -107,13 +105,12 @@ class TestHostComponent implements OnInit {
107105
this.readResource = res;
108106
});
109107

110-
this.stillImageFileRepresentations
111-
= [
112-
new FileRepresentation(stillImageFileValue,
113-
[
114-
new Region(makeRegion([rectangleGeom], 'first'))
115-
])
116-
];
108+
this.stillImageFileRepresentations = [
109+
new FileRepresentation(stillImageFileValue,
110+
[new Region(makeRegion([rectangleGeom], 'first'))]
111+
)
112+
];
113+
117114
}
118115

119116
regHovered(regIri: string) {

‎src/app/workspace/resource/representation/still-image/still-image.component.ts‎

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import {
22
Component,
33
ElementRef,
4-
EventEmitter,
5-
Inject,
4+
EventEmitter, Inject,
65
Input,
76
OnChanges,
8-
OnDestroy,
9-
OnInit,
10-
Output,
7+
OnDestroy, Output,
8+
Renderer2,
119
SimpleChanges
1210
} from '@angular/core';
1311
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
@@ -17,7 +15,6 @@ import {
1715
ApiResponseError,
1816
Constants,
1917
CreateColorValue,
20-
CreateFileValue,
2118
CreateGeomValue,
2219
CreateLinkValue,
2320
CreateResource,
@@ -104,7 +101,7 @@ export class GeometryForRegion {
104101
*/
105102
interface PolygonsForRegion {
106103

107-
[key: string]: HTMLDivElement[];
104+
[key: string]: HTMLElement[];
108105

109106
}
110107

@@ -143,6 +140,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
143140
private _errorHandler: ErrorHandlerService,
144141
private _matIconRegistry: MatIconRegistry,
145142
private _notification: NotificationService,
143+
private _renderer: Renderer2,
146144
private _valueOperationEventService: ValueOperationEventService
147145
) {
148146
OpenSeadragon.setString('Tooltips.Home', '');
@@ -185,17 +183,15 @@ export class StillImageComponent implements OnChanges, OnDestroy {
185183
this._unhighlightAllRegions();
186184
// tODO: check if this is necessary or could be handled below
187185
// (remove the 'else' before the 'if', so changes['activateRegion'] is always checked for)
188-
if (this.activateRegion !== undefined) {
189-
this._highlightRegion(this.activateRegion);
190-
}
191-
if (this.currentTab === 'annotations') {
192-
this.renderRegions();
193-
}
194-
} else if (changes['activateRegion']) {
186+
}
187+
if (this.activateRegion !== undefined) {
188+
this._highlightRegion(this.activateRegion);
189+
}
190+
if (this.currentTab === 'annotations') {
191+
this.renderRegions();
192+
}
193+
if (changes['activateRegion']) {
195194
this._unhighlightAllRegions();
196-
if (this.activateRegion !== undefined) {
197-
this._highlightRegion(this.activateRegion);
198-
}
199195
}
200196
}
201197

@@ -310,11 +306,13 @@ export class StillImageComponent implements OnChanges, OnDestroy {
310306
geometry.lineColor = colorValues[0].color;
311307
}
312308

313-
this._createSVGOverlay(geom.region.id, geometry, aspectRatio, imageXOffset, geom.region.label);
309+
const commentValue = (geom.region.properties[Constants.HasComment] ? geom.region.properties[Constants.HasComment][0].strval : '');
314310

311+
this._createSVGOverlay(geom.region.id, geometry, aspectRatio, imageXOffset, geom.region.label, commentValue);
312+
313+
imageXOffset++;
315314
}
316315

317-
imageXOffset++;
318316
}
319317

320318
}
@@ -326,7 +324,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
326324
for (const reg in this._regions) {
327325
if (this._regions.hasOwnProperty(reg)) {
328326
for (const pol of this._regions[reg]) {
329-
if (pol instanceof HTMLDivElement) {
327+
if (pol instanceof HTMLElement) {
330328
pol.remove();
331329
}
332330
}
@@ -345,7 +343,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
345343
this._notification.openSnackBar(message);
346344
}
347345

348-
openReplaceFileDialog(){
346+
openReplaceFileDialog() {
349347
const propId = this.parentResource.properties[Constants.HasStillImageFileValue][0].id;
350348

351349
const dialogConfig: MatDialogConfig = {
@@ -354,7 +352,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
354352
position: {
355353
top: '112px'
356354
},
357-
data: { mode: 'replaceFile', title: '2D Image (Still Image)', subtitle: 'Update image of the resource' , representation: 'stillImage', id: propId },
355+
data: { mode: 'replaceFile', title: '2D Image (Still Image)', subtitle: 'Update image of the resource', representation: 'stillImage', id: propId },
358356
disableClose: true
359357
};
360358
const dialogRef = this._dialog.open(
@@ -476,7 +474,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
476474
if (!this.regionDrawMode) {
477475
return;
478476
}
479-
const overlayElement = document.createElement('div');
477+
const overlayElement = this._renderer.createElement('div');
480478
overlayElement.style.background = 'rgba(255,0,0,0.3)';
481479
const viewportPos = this._viewer.viewport.pointFromPixel((event as OpenSeadragon.ViewerEvent).position);
482480
this._viewer.addOverlay(overlayElement, new OpenSeadragon.Rect(viewportPos.x, viewportPos.y, 0, 0));
@@ -523,7 +521,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
523521
*/
524522
private _highlightRegion(regionIri) {
525523

526-
const activeRegion: HTMLDivElement[] = this._regions[regionIri];
524+
const activeRegion: HTMLElement[] = this._regions[regionIri];
527525

528526
if (activeRegion !== undefined) {
529527
for (const pol of activeRegion) {
@@ -658,19 +656,14 @@ export class StillImageComponent implements OnChanges, OnDestroy {
658656
* @param xOffset - the x-offset in Openseadragon viewport coordinates of the image on which the geometry should be placed
659657
* @param toolTip - the tooltip which should be displayed on mousehover of the svg element
660658
*/
661-
private _createSVGOverlay(regionIri: string, geometry: RegionGeometry, aspectRatio: number, xOffset: number, toolTip: string): void {
659+
private _createSVGOverlay(regionIri: string, geometry: RegionGeometry, aspectRatio: number, xOffset: number, regionLabel: string, regionComment: string): void {
662660
const lineColor = geometry.lineColor;
663661
const lineWidth = geometry.lineWidth;
664662

665-
const elt = document.createElement('div');
666-
elt.id = 'region-overlay-' + Math.random() * 10000;
667-
elt.className = 'region';
668-
elt.title = toolTip;
669-
elt.setAttribute('style', 'outline: solid ' + lineColor + ' ' + lineWidth + 'px;');
670-
671-
elt.addEventListener('click', (event: MouseEvent) => {
672-
this.regionClicked.emit(regionIri);
673-
}, false);
663+
const regEle: HTMLElement = this._renderer.createElement('div');
664+
regEle.id = 'region-overlay-' + Math.random() * 10000;
665+
regEle.className = 'region';
666+
regEle.setAttribute('style', 'outline: solid ' + lineColor + ' ' + lineWidth + 'px;');
674667

675668
const diffX = geometry.points[1].x - geometry.points[0].x;
676669
const diffY = geometry.points[1].y - geometry.points[0].y;
@@ -684,11 +677,36 @@ export class StillImageComponent implements OnChanges, OnDestroy {
684677
loc.y = loc.y * aspectRatio;
685678

686679
this._viewer.addOverlay({
687-
element: elt,
688-
location: loc
680+
element: regEle,
681+
location: loc,
682+
});
683+
684+
// mouse tracker has to be activated on the open seadragon viewer
685+
// solution from: https://github.com/openseadragon/openseadragon/issues/1419#issuecomment-371564878
686+
const tracker = new OpenSeadragon.MouseTracker({
687+
element: regEle,
688+
clickHandler: function (event) { }
689+
});
690+
691+
this._regions[regionIri].push(regEle);
692+
693+
const comEle: HTMLElement = this._renderer.createElement('div');
694+
comEle.className = 'annotation-tooltip';
695+
comEle.innerHTML = `<strong>${regionLabel}</strong><br>${regionComment}`;
696+
regEle.append(comEle);
697+
698+
regEle.addEventListener('mousemove', (event: MouseEvent) => {
699+
comEle.setAttribute('style', 'display: block; left: ' + event.clientX + 'px; top: ' + event.clientY + 'px');
700+
});
701+
regEle.addEventListener('mouseleave', (event: MouseEvent) => {
702+
comEle.setAttribute('style', 'display: none');
703+
});
704+
regEle.addEventListener('click', (event: MouseEvent) => {
705+
this.regionClicked.emit(regionIri);
689706
});
690707

691-
this._regions[regionIri].push(elt);
692708
}
693709

710+
711+
694712
}

‎src/app/workspace/resource/resource.component.html‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,14 @@
6363
</ng-template>
6464
<div class="region-property" *ngFor="let annotation of annotationResources" [id]="annotation.res.id"
6565
[class.active]="annotation.res.id === selectedRegion">
66-
<app-properties [resource]="annotation" [displayProjectInfo]="false" [isAnnotation]="true"
67-
[adminPermissions]="adminPermissions" [editPermissions]="editPermissions" [valueUuidToHighlight]="valueUuid" (regionColorChanged)="updateRegionColor()">
66+
<app-properties
67+
[resource]="annotation"
68+
[displayProjectInfo]="false"
69+
[isAnnotation]="true"
70+
[adminPermissions]="adminPermissions"
71+
[editPermissions]="editPermissions"
72+
[valueUuidToHighlight]="valueUuid"
73+
(regionChanged)="updateRegion()">
6874
</app-properties>
6975
</div>
7076

‎src/app/workspace/resource/resource.component.ts‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {
238238

239239
} else {
240240
// if there is no incomingResource and the resource has a still image property, assign the iiiUrl to be passed as an input to the still-image component
241-
if (!this.incomingResource && this.resource.res.properties[Constants.HasStillImageFileValue]){
241+
if (!this.incomingResource && this.resource.res.properties[Constants.HasStillImageFileValue]) {
242242
this.iiifUrl = (this.resource.res.properties[Constants.HasStillImageFileValue][0] as ReadStillImageFileValue).fileUrl;
243243
}
244244

@@ -304,7 +304,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {
304304
this.incomingResource = res;
305305

306306
// if the resource is a still image, assign the iiiUrl to be passed as an input to the still-image component
307-
if (this.incomingResource.res.properties[Constants.HasStillImageFileValue]){
307+
if (this.incomingResource.res.properties[Constants.HasStillImageFileValue]) {
308308
this.iiifUrl = (this.incomingResource.res.properties[Constants.HasStillImageFileValue][0] as ReadStillImageFileValue).fileUrl;
309309
}
310310

@@ -430,6 +430,13 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {
430430
representations.push(stillImage);
431431

432432
this.annotationResources = annotations;
433+
434+
// developer feature: this keeps the annotations tab open, if you add "/annotations" to the end of the URL
435+
// e.g. http://0.0.0.0:4200/resource/[project-shortcode]/[resource-iri]/annotations
436+
if (this.valueUuid === 'annotations') {
437+
this.selectedTab = (this.incomingResource ? 2 : 1);
438+
this.selectedTabLabel = 'annotations';
439+
}
433440
}
434441

435442
} else if (resource.res.properties[Constants.HasDocumentFileValue]) {
@@ -584,7 +591,10 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {
584591
// and scroll to region with this id
585592
const region = document.getElementById(iri);
586593
if (region) {
587-
region.scrollIntoView();
594+
region.scrollIntoView({
595+
behavior: 'smooth',
596+
block: 'center'
597+
});
588598
}
589599

590600
}
@@ -599,7 +609,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {
599609
this.openRegion(iri);
600610
}
601611

602-
updateRegionColor(){
612+
updateRegion() {
603613
if (this.stillImageComponent !== undefined) {
604614
this.stillImageComponent.updateRegions();
605615
}

‎src/app/workspace/resource/values/text-value/text-value-as-string/text-value-as-string.component.html‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<span *ngIf="mode === 'read'; else showForm" class="read-mode-view">
2+
<!-- TODO in DEV-797: appLinkify pipe should only be used if property's gui-element is not richt-text; I had the same issue in regions -->
23
<span class="rm-value text-value" [innerHtml]="valueFormControl.value | appLinkify" style="white-space: pre-wrap;"></span>
34
<span class="rm-comment" *ngIf="shouldShowComment">{{commentFormControl.value}}</span>
45
</span>

‎src/assets/style/_elements.scss‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,14 @@ $gc-small: $form-width - $gc-large - 4;
761761
font-size: 11px;
762762
}
763763

764+
.annotation-tooltip {
765+
766+
p {
767+
white-space: normal;
768+
text-overflow: ellipsis;
769+
}
770+
}
771+
764772
// --------------------------------------
765773

766774
//

0 commit comments

Comments
 (0)