Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(region): highlight info of clicked region (DEV-724) #703

Merged
merged 14 commits into from Apr 13, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/app/workspace/resource/properties/properties.component.ts
Expand Up @@ -114,7 +114,7 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
*/
@Output() referredResourceHovered: EventEmitter<ReadLinkValue> = new EventEmitter<ReadLinkValue>();

@Output() regionColorChanged: EventEmitter<ReadColorValue> = new EventEmitter<ReadColorValue>();
@Output() regionChanged: EventEmitter<ReadValue> = new EventEmitter<ReadValue>();

lastModificationDate: string;

Expand Down Expand Up @@ -352,6 +352,10 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
(response: UpdateResourceMetadataResponse) => {
this.resource.res.label = payload.label;
this.lastModificationDate = response.lastModificationDate;
// if annotations tab is active; a label of a region has been changed --> update regions
if (this.isAnnotation) {
this.regionChanged.emit();
}
},
(error: ApiResponseError) => {
this._errorHandler.showMessage(error);
Expand Down Expand Up @@ -456,8 +460,9 @@ export class PropertiesComponent implements OnInit, OnChanges, OnDestroy {
if (updatedValue instanceof ReadTextValueAsXml) {
this._updateStandoffLinkValue();
}
if (updatedValue instanceof ReadColorValue) {
this.regionColorChanged.emit();
// if annotations tab is active;
if (this.isAnnotation) {
this.regionChanged.emit();
}
} else {
console.warn('No properties exist for this resource');
Expand Down
@@ -1,13 +1,10 @@
@import "../../../../../assets/style/config";
// sizes
$max-width: 800px;
$panelSize: 40px;

$osd-height: 460px;

// colors
$dark: #000;
$bright: #ccc;

:host {
// display: inline-flex;
width: 100%;
Expand Down Expand Up @@ -57,6 +54,26 @@ $bright: #ccc;
}
}

.annotation-tooltip {
display: none;
position: fixed;
background-color: $black-60-opacity;
color: $bright;
padding: 8px;
border-radius: $border-radius;
min-height: 24px;
max-height: 258px;
max-width: 256px;
box-sizing: border-box;
transition: 0.1s;
transform: translate(16px, 16px);
font-size: small;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
z-index: 1000;
}

/*
Overlay styling
*/
Expand Down
Expand Up @@ -5,9 +5,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { HttpClientModule } from '@angular/common/http';
import { Component, OnInit, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { MatButtonHarness } from '@angular/material/button/testing';
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatDialogHarness } from '@angular/material/dialog/testing';
import { MatIconModule } from '@angular/material/icon';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatToolbarModule } from '@angular/material/toolbar';
Expand Down Expand Up @@ -107,13 +105,12 @@ class TestHostComponent implements OnInit {
this.readResource = res;
});

this.stillImageFileRepresentations
= [
new FileRepresentation(stillImageFileValue,
[
new Region(makeRegion([rectangleGeom], 'first'))
])
];
this.stillImageFileRepresentations = [
new FileRepresentation(stillImageFileValue,
[new Region(makeRegion([rectangleGeom], 'first'))]
)
];

}

regHovered(regIri: string) {
Expand Down
@@ -1,13 +1,11 @@
import {
Component,
ElementRef,
EventEmitter,
Inject,
EventEmitter, Inject,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
OnDestroy, Output,
Renderer2,
SimpleChanges
} from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
Expand All @@ -17,7 +15,6 @@ import {
ApiResponseError,
Constants,
CreateColorValue,
CreateFileValue,
CreateGeomValue,
CreateLinkValue,
CreateResource,
Expand Down Expand Up @@ -104,7 +101,7 @@ export class GeometryForRegion {
*/
interface PolygonsForRegion {

[key: string]: HTMLDivElement[];
[key: string]: HTMLElement[];

}

Expand Down Expand Up @@ -143,6 +140,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
private _errorHandler: ErrorHandlerService,
private _matIconRegistry: MatIconRegistry,
private _notification: NotificationService,
private _renderer: Renderer2,
private _valueOperationEventService: ValueOperationEventService
) {
OpenSeadragon.setString('Tooltips.Home', '');
Expand Down Expand Up @@ -185,17 +183,15 @@ export class StillImageComponent implements OnChanges, OnDestroy {
this._unhighlightAllRegions();
// tODO: check if this is necessary or could be handled below
// (remove the 'else' before the 'if', so changes['activateRegion'] is always checked for)
if (this.activateRegion !== undefined) {
this._highlightRegion(this.activateRegion);
}
if (this.currentTab === 'annotations') {
this.renderRegions();
}
} else if (changes['activateRegion']) {
}
if (this.activateRegion !== undefined) {
this._highlightRegion(this.activateRegion);
}
if (this.currentTab === 'annotations') {
this.renderRegions();
}
if (changes['activateRegion']) {
this._unhighlightAllRegions();
if (this.activateRegion !== undefined) {
this._highlightRegion(this.activateRegion);
}
}
}

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

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

this._createSVGOverlay(geom.region.id, geometry, aspectRatio, imageXOffset, geom.region.label, commentValue);

imageXOffset++;
}

imageXOffset++;
}

}
Expand All @@ -326,7 +324,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
for (const reg in this._regions) {
if (this._regions.hasOwnProperty(reg)) {
for (const pol of this._regions[reg]) {
if (pol instanceof HTMLDivElement) {
if (pol instanceof HTMLElement) {
pol.remove();
}
}
Expand All @@ -345,7 +343,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
this._notification.openSnackBar(message);
}

openReplaceFileDialog(){
openReplaceFileDialog() {
const propId = this.parentResource.properties[Constants.HasStillImageFileValue][0].id;

const dialogConfig: MatDialogConfig = {
Expand All @@ -354,7 +352,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
position: {
top: '112px'
},
data: { mode: 'replaceFile', title: '2D Image (Still Image)', subtitle: 'Update image of the resource' , representation: 'stillImage', id: propId },
data: { mode: 'replaceFile', title: '2D Image (Still Image)', subtitle: 'Update image of the resource', representation: 'stillImage', id: propId },
disableClose: true
};
const dialogRef = this._dialog.open(
Expand Down Expand Up @@ -476,7 +474,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
if (!this.regionDrawMode) {
return;
}
const overlayElement = document.createElement('div');
const overlayElement = this._renderer.createElement('div');
overlayElement.style.background = 'rgba(255,0,0,0.3)';
const viewportPos = this._viewer.viewport.pointFromPixel((event as OpenSeadragon.ViewerEvent).position);
this._viewer.addOverlay(overlayElement, new OpenSeadragon.Rect(viewportPos.x, viewportPos.y, 0, 0));
Expand Down Expand Up @@ -523,7 +521,7 @@ export class StillImageComponent implements OnChanges, OnDestroy {
*/
private _highlightRegion(regionIri) {

const activeRegion: HTMLDivElement[] = this._regions[regionIri];
const activeRegion: HTMLElement[] = this._regions[regionIri];

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

const elt = document.createElement('div');
elt.id = 'region-overlay-' + Math.random() * 10000;
elt.className = 'region';
elt.title = toolTip;
elt.setAttribute('style', 'outline: solid ' + lineColor + ' ' + lineWidth + 'px;');

elt.addEventListener('click', (event: MouseEvent) => {
this.regionClicked.emit(regionIri);
}, false);
const regEle: HTMLElement = this._renderer.createElement('div');
regEle.id = 'region-overlay-' + Math.random() * 10000;
regEle.className = 'region';
regEle.setAttribute('style', 'outline: solid ' + lineColor + ' ' + lineWidth + 'px;');

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

this._viewer.addOverlay({
element: elt,
location: loc
element: regEle,
location: loc,
});

// mouse tracker has to be activated on the open seadragon viewer
// solution from: https://github.com/openseadragon/openseadragon/issues/1419#issuecomment-371564878
const tracker = new OpenSeadragon.MouseTracker({
kilchenmann marked this conversation as resolved.
Show resolved Hide resolved
element: regEle,
clickHandler: function (event) { }
});

this._regions[regionIri].push(regEle);

const comEle: HTMLElement = this._renderer.createElement('div');
comEle.className = 'annotation-tooltip';
comEle.innerHTML = `<strong>${regionLabel}</strong><br>${regionComment}`;
regEle.append(comEle);

regEle.addEventListener('mousemove', (event: MouseEvent) => {
comEle.setAttribute('style', 'display: block; left: ' + event.clientX + 'px; top: ' + event.clientY + 'px');
});
regEle.addEventListener('mouseleave', (event: MouseEvent) => {
comEle.setAttribute('style', 'display: none');
});
regEle.addEventListener('click', (event: MouseEvent) => {
this.regionClicked.emit(regionIri);
});

this._regions[regionIri].push(elt);
}



}
10 changes: 8 additions & 2 deletions src/app/workspace/resource/resource.component.html
Expand Up @@ -63,8 +63,14 @@
</ng-template>
<div class="region-property" *ngFor="let annotation of annotationResources" [id]="annotation.res.id"
[class.active]="annotation.res.id === selectedRegion">
<app-properties [resource]="annotation" [displayProjectInfo]="false" [isAnnotation]="true"
[adminPermissions]="adminPermissions" [editPermissions]="editPermissions" [valueUuidToHighlight]="valueUuid" (regionColorChanged)="updateRegionColor()">
<app-properties
[resource]="annotation"
[displayProjectInfo]="false"
[isAnnotation]="true"
[adminPermissions]="adminPermissions"
[editPermissions]="editPermissions"
[valueUuidToHighlight]="valueUuid"
(regionChanged)="updateRegion()">
</app-properties>
</div>

Expand Down
18 changes: 14 additions & 4 deletions src/app/workspace/resource/resource.component.ts
Expand Up @@ -238,7 +238,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {

} else {
// 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
if (!this.incomingResource && this.resource.res.properties[Constants.HasStillImageFileValue]){
if (!this.incomingResource && this.resource.res.properties[Constants.HasStillImageFileValue]) {
this.iiifUrl = (this.resource.res.properties[Constants.HasStillImageFileValue][0] as ReadStillImageFileValue).fileUrl;
}

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

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

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

this.annotationResources = annotations;

// developer feature: this keeps the annotations tab open, if you add "/annotations" to the end of the URL
// e.g. http://0.0.0.0:4200/resource/[project-shortcode]/[resource-iri]/annotations
if (this.valueUuid === 'annotations') {
this.selectedTab = (this.incomingResource ? 2 : 1);
this.selectedTabLabel = 'annotations';
}
}

} else if (resource.res.properties[Constants.HasDocumentFileValue]) {
Expand Down Expand Up @@ -584,7 +591,10 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {
// and scroll to region with this id
const region = document.getElementById(iri);
if (region) {
region.scrollIntoView();
region.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}

}
Expand All @@ -599,7 +609,7 @@ export class ResourceComponent implements OnInit, OnChanges, OnDestroy {
this.openRegion(iri);
}

updateRegionColor(){
updateRegion() {
if (this.stillImageComponent !== undefined) {
this.stillImageComponent.updateRegions();
}
Expand Down
@@ -1,4 +1,5 @@
<span *ngIf="mode === 'read'; else showForm" class="read-mode-view">
<!-- 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 -->
<span class="rm-value text-value" [innerHtml]="valueFormControl.value | appLinkify" style="white-space: pre-wrap;"></span>
<span class="rm-comment" *ngIf="shouldShowComment">{{commentFormControl.value}}</span>
</span>
Expand Down
8 changes: 8 additions & 0 deletions src/assets/style/_elements.scss
Expand Up @@ -761,6 +761,14 @@ $gc-small: $form-width - $gc-large - 4;
font-size: 11px;
}

.annotation-tooltip {

p {
white-space: normal;
text-overflow: ellipsis;
}
}

// --------------------------------------

//
Expand Down