Skip to content

Commit

Permalink
Highlight visible sections in menu on gene pages
Browse files Browse the repository at this point in the history
Refs #2271
Refs #1940
  • Loading branch information
kimrutherford committed Dec 10, 2024
1 parent 37e9287 commit 0e3266c
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 6 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"bootstrap": "^4",
"file-saver": "^2.0.2",
"ngx-bootstrap": "^18",
"ngx-intersection-observer": "^1.0.13",
"ngx-page-scroll": "^13",
"ngx-page-scroll-core": "^13",
"ngx-toastr": "^19",
Expand Down
4 changes: 4 additions & 0 deletions src/app/annotation-table-intersection-event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class AnnotationTableIntersection {
constructor(public annotationTypeName: string,
public isIntersecting: boolean) {}
}
8 changes: 6 additions & 2 deletions src/app/annotation-table/annotation-table.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
</div>
</div>

<div *ngIf="split_by_parents.length == 0">
<div *ngIf="split_by_parents.length == 0"
intersectionObserver
(intersection)="intersectHandler(annotationTypeName, $event.intersect)">
<app-annotation-sub-table
[annotationTypeName]="annotationTypeName"
[scope]="scope"
Expand All @@ -53,7 +55,9 @@
</div>
<div *ngIf="split_by_parents.length != 0">
<div *ngFor="let conf of split_by_parents; let i = index">
<div *ngIf="splitDataList[conf.config_name] && splitDataList[conf.config_name].length > 0">
<div *ngIf="splitDataList[conf.config_name] && splitDataList[conf.config_name].length > 0"
intersectionObserver
(intersection)="intersectHandler(annotationTypeName + '-' + conf.config_name, $event.intersect)">
<div *ngIf="conf.display_name" [attr.id]="annotationTypeName + '-' + conf.config_name"
class="annotation-sub-table-name">{{capitalize(conf.display_name)}}</div>
<app-annotation-sub-table
Expand Down
8 changes: 7 additions & 1 deletion src/app/annotation-table/annotation-table.component.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Component, Input, OnInit, OnChanges } from '@angular/core';
import { Component, Input, OnInit, OnChanges, Output, EventEmitter } from '@angular/core';
import { TermAnnotation, GeneDetails } from '../pombase-api.service';

import { getAnnotationTableConfig, AnnotationTableConfig, AnnotationType,
SplitByParentsConfig } from '../config';
import { DeployConfigService } from '../deploy-config.service';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { Util } from '../shared/util';
import { AnnotationTableIntersection } from '../annotation-table-intersection-event';

@Component({
selector: 'app-annotation-table',
Expand All @@ -21,6 +22,7 @@ export class AnnotationTableComponent implements OnInit, OnChanges {
@Input() annotationTable: Array<TermAnnotation>;
@Input() geneDetails?: GeneDetails;
@Input() scope: string; // "gene", "term", "reference" ...
@Output() annotationTableIntersection = new EventEmitter<AnnotationTableIntersection>();

config: AnnotationTableConfig = getAnnotationTableConfig();
typeConfig: AnnotationType;
Expand Down Expand Up @@ -89,6 +91,10 @@ export class AnnotationTableComponent implements OnInit, OnChanges {
ngOnInit() {
}

intersectHandler(subTableName: string, isIntersecting: boolean) {
this.annotationTableIntersection.emit(new AnnotationTableIntersection(subTableName, isIntersecting));
}

ngOnChanges() {
this.typeConfig = this.config.getAnnotationType(this.annotationTypeName);

Expand Down
12 changes: 11 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ import { GeneticInteractionTableTypeFilterComponent } from './genetic-interactio
import { RnaStructureComponent } from './rna-structure/rna-structure.component';
import { ProteinFeatureTableComponent } from './protein-feature-table/protein-feature-table.component';

import { IntersectionObserverModule } from 'ngx-intersection-observer';
import { IntersectionObserverConfig } from 'ngx-intersection-observer/lib/intersection-observer-config.model';

@Pipe({
name: 'safeUrl',
standalone: false
Expand Down Expand Up @@ -319,7 +322,14 @@ export class PomBaseUrlSerializer extends DefaultUrlSerializer {
ProteinFeatureTableComponent,
],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [BrowserModule,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [
IntersectionObserverModule.forRoot({
debounce: 50,
threshold: 1,
autoRemove: true
} as IntersectionObserverConfig),
BrowserModule,
CommonModule,
BrowserAnimationsModule,
FormsModule,
Expand Down
33 changes: 32 additions & 1 deletion src/app/gene-details/gene-details.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
<div id="gene-details-menu">
<app-details-page-menu *ngIf="geneDetails"
[title]="(geneDetails.name || geneDetails.uniquename) + ' summary'"
[onScreenItems]="onScreenItems"
[menuItems]="menuItems"></app-details-page-menu>
</div>

Expand Down Expand Up @@ -133,14 +134,20 @@
<div *ngFor="let annotationTypeName of annotationTypeOrderNames">

<div *ngIf="geneDetails.cv_annotations[annotationTypeName]">
<app-quant-gene-ex-table id="{{annotationTypeName}}" *ngIf="annotationTypeName == 'quantitative_gene_expression'"
<app-quant-gene-ex-table id="{{annotationTypeName}}"
intersectionObserver
(intersection)="intersect(annotationTypeName, $event.intersect)"
*ngIf="annotationTypeName == 'quantitative_gene_expression'"
[scope]="'gene'"
[geneDetails]="geneDetails"
[hideColumns]="['gene']"
[annotationTable]="geneDetails.cv_annotations[annotationTypeName]"></app-quant-gene-ex-table>

<app-annotation-table id="{{annotationTypeName}}"
*ngIf="annotationTypeName != 'quantitative_gene_expression'"
intersectionObserver
(intersection)="intersect(annotationTypeName, $event.intersect)"
(annotationTableIntersection)="subTableIntersect($event)"
[scope]="'gene'"
[annotationTypeName]="annotationTypeName"
[geneDetails]="geneDetails"
Expand All @@ -150,57 +157,81 @@

<app-misc-annotation-table id="{{annotationTypeName}}"
*ngIf="annotationTypeName == 'miscellaneous' && hasMiscAnnotations()"
intersectionObserver
(intersection)="intersect(annotationTypeName, $event.intersect)"
[annotationTypeNames]="miscAnnotationTypeNames"
[geneDetails]="geneDetails"></app-misc-annotation-table>

<app-protein-features id="{{annotationTypeName}}"
*ngIf="annotationTypeName == 'protein_domains_and_properties' && showProteinFeatures"
intersectionObserver
(intersection)="intersect(annotationTypeName, $event.intersect)"
[geneDetails]="geneDetails"></app-protein-features>


<app-interaction-annotation-table *ngIf="annotationTypeName == 'physical_interactions'"
id="{{annotationTypeName}}"
intersectionObserver
(intersection)="intersect(annotationTypeName, $event.intersect)"
[annotationTypeName]="annotationTypeName"
[currentGene]="geneDetails"
[annotationTable]="geneDetails.physical_interactions"></app-interaction-annotation-table>

<app-genetic-interaction-annotation-table
*ngIf="annotationTypeName == 'genetic_interactions'"
id="{{annotationTypeName}}"
intersectionObserver
(intersection)="intersect(annotationTypeName, $event.intersect)"
[annotationTypeName]="annotationTypeName"
[currentGene]="geneDetails"
[annotationTable]="geneDetails.genetic_interactions">
</app-genetic-interaction-annotation-table>

<app-ortholog-annotation-table *ngIf="annotationTypeName == 'orthologs' && showOrthologsSection()"
id="{{annotationTypeName}}"
intersectionObserver
(intersection)="intersect(annotationTypeName, $event.intersect)"
[currentGene]="geneDetails"
[annotationTable]="geneDetails.ortholog_annotations"></app-ortholog-annotation-table>
<app-paralog-annotation-table *ngIf="annotationTypeName == 'paralogs'"
id="{{annotationTypeName}}"
intersectionObserver
(intersection)="intersect(annotationTypeName, $event.intersect)"
[annotationTable]="geneDetails.paralog_annotations"></app-paralog-annotation-table>
<app-target-of-annotation-table *ngIf="annotationTypeName == 'target_of'"
id="{{annotationTypeName}}"
intersectionObserver
(intersection)="intersect(annotationTypeName, $event.intersect)"
[geneDetails]="geneDetails"
[annotationTable]="geneDetails.target_of_annotations"></app-target-of-annotation-table>
</div>

<app-transcript-view id="transcript_view"
*ngIf="geneDetails!.transcripts.length > 0"
intersectionObserver
(intersection)="intersect('transcript_view', $event.intersect)"
[geneDetails]="geneDetails"></app-transcript-view>

<app-transcript-sequence-select *ngIf="isConfiguredOrganism" id="transcript-sequence"
intersectionObserver
(intersection)="intersect('transcript-sequence', $event.intersect)"
[geneDetails]="geneDetails"></app-transcript-sequence-select>

<app-gene-history-table id="gene-history"
*ngIf="geneDetails.gene_history.length"
intersectionObserver
(intersection)="intersect('gene-history', $event.intersect)"
[geneDetails]="geneDetails">
</app-gene-history-table>

<app-gene-external-references *ngIf="isConfiguredOrganism" id="external-refs"
intersectionObserver
(intersection)="intersect('external-refs', $event.intersect)"
[geneDetails]="geneDetails"></app-gene-external-references>

<app-gene-references-table id="literature"
intersectionObserver
(intersection)="intersect('literature', $event.intersect)"
[geneDetails]="geneDetails"
[references]="geneDetails.references"></app-gene-references-table>
</div>
Expand Down
15 changes: 15 additions & 0 deletions src/app/gene-details/gene-details.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { DeployConfigService } from '../deploy-config.service';
import { Util } from '../shared/util';
import { MenuItem } from '../types';

import { AnnotationTableIntersection } from '../annotation-table-intersection-event';

@Component({
selector: 'app-gene-details',
templateUrl: './gene-details.component.html',
Expand All @@ -32,6 +34,7 @@ export class GeneDetailsComponent implements OnInit {
annotationTypeOrder: Array<TypeOrderConfigItem> = [];
annotationTypeOrderNames: Array<string> = [];
menuItems: Array<MenuItem> = [];
onScreenItems: Set<string> = new Set();
config: AnnotationTableConfig = getAnnotationTableConfig();
appConfig: AppConfig = getAppConfig();
noGeneNameRoute = this.appConfig.no_gene_name_route;
Expand Down Expand Up @@ -423,6 +426,18 @@ export class GeneDetailsComponent implements OnInit {
!!this.geneDetails.characterisation_status;
}

intersect(annotationTypeName: string, isIntersecting: boolean): void {
if (isIntersecting) {
this.onScreenItems.add(annotationTypeName);
} else {
this.onScreenItems.delete(annotationTypeName);
}
}

subTableIntersect(event: AnnotationTableIntersection) {
this.intersect(event.annotationTypeName, event.isIntersecting);
}

ngOnInit(): void {
this.route.params.forEach((params: Params) => {
if (params['uniquename'] !== undefined) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
opacity: 1;
transition: opacity 0.2s;
}

.is-on-screen {
border-right: 3px solid #667;
background-color: #e8e8dfdd;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
<div *ngFor="let menuItem of displayMenuItems">
<div id="menu-item-{{menuItem.id}}" class="left-menu-part left-menu-item"
(click)="clicked(menuItem)">
<span [ngClass]="{'is-on-screen': isOnScreen(menuItem, menuItem.subItemsVisible)}">
<a pageScroll href="#{{menuItem.id}}">{{menuItem.displayName}}</a>
</span>
</div>

<div *ngIf="menuItem.subItems && menuItem.subItemsVisible">
<div class="left-menu-part left-sub-menu-item" *ngFor="let subMenuItem of menuItem.subItems">
<span [ngClass]="{'is-on-screen': isOnScreen(subMenuItem, false)}">
<a pageScroll href="#{{subMenuItem.id}}">{{subMenuItem.displayName}}</a>
</span>
</div>
</div>
</div>
Expand Down
31 changes: 30 additions & 1 deletion src/app/shared/details-page-menu/details-page-menu.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface DisplayMenuItem extends MenuItem {
export class DetailsPageMenuComponent implements OnInit, OnChanges {
@Input() title: string;
@Input() menuItems: Array<MenuItem> = [];
@Input() onScreenItems = new Set<string>();

displayMenuItems: Array<DisplayMenuItem> = [];

Expand Down Expand Up @@ -52,21 +53,49 @@ export class DetailsPageMenuComponent implements OnInit, OnChanges {
}

clicked(menuItem: DisplayMenuItem): void {
if (!menuItem.subItems) {
menuItem.subItemsVisible = false;
return;
}
const currentItemValue = menuItem.subItemsVisible;
this.displayMenuItems.map(item => {
item.subItemsVisible = false;
});
menuItem.subItemsVisible = !currentItemValue;
}

isOnScreen(menuItem: MenuItem, subItemsVisible: boolean): boolean {
if (!this.menuPositionFixed) {
// don't highlight menu item before we've scrolled
return false;
}
if (subItemsVisible) {
return false;
}
if (this.onScreenItems.has(menuItem.id)) {
return true;
}
if (menuItem.subItems) {
for (const subMenuItem of menuItem.subItems) {
if (this.onScreenItems.has(subMenuItem.id)) {
return true;
}
}
}

return false;
}

ngOnInit() {
}

ngOnChanges() {
this.displayMenuItems =
this.menuItems.map(item => {
let displayItem = { ...item, subItemsVisible: false };

if (displayItem.subItems && displayItem.subItems.length == 0) {
displayItem.subItems = undefined;
}
return displayItem;
});
}
Expand Down
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5245,6 +5245,13 @@ ngx-bootstrap@^18:
dependencies:
tslib "^2.3.0"

ngx-intersection-observer@^1.0.13:
version "1.0.13"
resolved "https://registry.yarnpkg.com/ngx-intersection-observer/-/ngx-intersection-observer-1.0.13.tgz#da40026f79a5883d9845f19d3af5b54e40f7feef"
integrity sha512-QQsJInaH6OM/ANxiMBmKm9GJo8UfCo6VNf+5D1XTKV3x7ETwKIoQB+K11AV2LD/5GYPjoigl7TrtJKkR/GAkdg==
dependencies:
tslib "^2.3.0"

ngx-page-scroll-core@^13:
version "13.0.0"
resolved "https://registry.yarnpkg.com/ngx-page-scroll-core/-/ngx-page-scroll-core-13.0.0.tgz#d898bdbf4d0620998e04aa30c612a6a588b37064"
Expand Down

0 comments on commit 0e3266c

Please sign in to comment.