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

Improved ontology tree performance #1257

Merged
merged 2 commits into from
May 16, 2024
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
6 changes: 2 additions & 4 deletions projects/ccf-eui/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { AppRootOverlayContainer } from './core/services/app-root-overlay/app-ro
import { ThemingService } from './core/services/theming/theming.service';
import { actionAsFn } from './core/store/action-as-fn';
import { DataStateSelectors } from './core/store/data/data.selectors';
import { DataQueryState, DataState } from './core/store/data/data.state';
import { DataState } from './core/store/data/data.state';
import { ListResultsState } from './core/store/list-results/list-results.state';
import { SceneState } from './core/store/scene/scene.state';
import {
Expand Down Expand Up @@ -123,9 +123,7 @@ export class AppComponent implements OnInit {
}

/** Emits true whenever the overlay spinner should activate. */
readonly spinnerActive$ = this.data.queryStatus$.pipe(
map(state => state === DataQueryState.Running)
);
readonly spinnerActive$ = this.data.state$.pipe(map((state) => state?.status !== 'Ready'));

readonly loadingMessage$ = this.data.state$.pipe(map(x => x?.statusMessage));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,15 @@

<!-- Templates with common structures for inner and leaf nodes -->
<ng-template #selectableRegion let-node="node">
<div class="text" [class.hidden]="node.label === highlightedNode?.label"
<div class="text"
[class.filtered-out]="!occurenceData[node.original.id] && !!termData[node.original.id]"
[class.unavailable]="!termData[node.original.id]" [class.selected]="isSelected(node)"
(click)="select($event.ctrlKey, node, true, !isSelected(node))">{{ getNodeLabel(node.label) }}</div>
</ng-template>

<!-- Leaf node template -->
<mat-tree-node *matTreeNodeDef="let node" class="node leaf-node block" matTreeNodePadding
[matTreeNodePaddingIndent]="indent" (mouseleave)="mouseOut(); slider1.reset()">

<div [style.display]="node.opacity !== -1 ? 'none' : 'block'">
<mat-icon class="icon opacity" [style.left]="getLeftIndent(node.level)" [class.fade]="node.visible !== true"
(mouseenter)="mouseOver(node)" svgIcon="app:opacity"></mat-icon>
<div class="slider" [style.left]="getLeftIndent(node.level)"
[class.hidden]="node.label !== highlightedNode?.label">
<ccf-opacity-slider #slider1 [visible]="node.visible" [opacity]="node.opacity"
(opacityChange)="updateOpacity(node, $event)" (opacityReset)="resetNode(node)"
(visibilityToggle)="toggleVisibility(node)">
</ccf-opacity-slider>
</div>
</div>

[matTreeNodePaddingIndent]="indent">
<!-- Disabled button used to add equal amount of space as an inner node's button -->
<div class="non-expandable"></div>
<div class="node-container">
Expand All @@ -40,21 +27,9 @@

<!-- Inner node template -->
<mat-tree-node *matTreeNodeDef="let node; when: isInnerNode" class="node inner-node block" matTreeNodePadding
[matTreeNodePaddingIndent]="indent" (mouseleave)="mouseOut(); slider2.reset()">

<div [style.display]="node.opacity !== -1 ? 'none' : 'block'">
<mat-icon class="icon opacity" [style.left]="getLeftIndent(node.level)" [class.fade]="node.visible !== true"
(mouseenter)="mouseOver(node)" svgIcon="app:opacity"></mat-icon>
<div class="slider" [style.left]="getLeftIndent(node.level)"
[class.hidden]="node.label !== highlightedNode?.label">
<ccf-opacity-slider #slider2 [visible]="node.visible" [opacity]="node.opacity"
(opacityChange)="updateOpacity(node, $event)" (opacityReset)="resetNode(node)"
(visibilityToggle)="toggleVisibility(node)">
</ccf-opacity-slider>
</div>
</div>
[matTreeNodePaddingIndent]="indent">
<div class="node-container">
<button class="toggle" [class.hidden]="node.label === highlightedNode?.label" mat-icon-button matTreeNodeToggle
<button class="toggle" mat-icon-button matTreeNodeToggle
attr.aria-label="Toggle {{ node.label }}">
<mat-icon class="icon font-icon">
{{ control.isExpanded(node) ? 'expand_less' : 'expand_more' }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ describe('OntologyTreeComponent', () => {
expect(instance.nodes).toEqual([node1, node2]);
});

it('should return left indent', async () => {
const { instance } = await shallow.render();
expect(instance.getLeftIndent(1)).toEqual('-1.5rem');
});

it('should set children', async () => {
const { instance } = await shallow.render();
instance.getChildren = ()=>[];
Expand Down Expand Up @@ -157,24 +152,6 @@ describe('OntologyTreeComponent', () => {
expect(instance.isInnerNode(1, flatNode2)).toBeFalse();
});

it('should change highlightedNode when moused over', async () => {
const { instance } = await shallow.render();
instance.mouseOver(flatNode1);
expect(instance.highlightedNode).toEqual(flatNode1);
});

it('should remove highlightedNode when moused out', async () => {
const { instance } = await shallow.render();
instance.mouseOut();
expect(instance.highlightedNode).toBeUndefined();
});

it('should reset the node', async () => {
const { instance, outputs } = await shallow.render();
instance.resetNode(flatNode1);
expect(outputs.nodeChanged.emit).toHaveBeenCalled();
});

it('should return number of children when getNumResults is called', async () => {
const { instance } = await shallow.render();
expect(instance.getCountLabel(flatNode1)).toEqual('');
Expand All @@ -188,18 +165,6 @@ describe('OntologyTreeComponent', () => {
expect(instance.getNodeLabel('test')).toEqual('test');
});

it('should update the opacity', async () => {
const { instance } = await shallow.render();
instance.updateOpacity(flatNode1, 50);
expect(flatNode1.opacity).toEqual(50);
});

it('should toggle the visibility', async () => {
const { instance } = await shallow.render();
instance.toggleVisibility(flatNode1);
expect(flatNode1.visible).toBeFalse();
});

it('should re-run the gradient display logic on a scroll event', async () => {
const { instance, find } = await shallow.render();
const list = find('.ccf-ontology-tree');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,6 @@ export class OntologyTreeComponent implements OnInit, OnChanges {
*/
selectedNodes: FlatNode[] = [];

highlightedNode: FlatNode | undefined;



/**
* Expand the body node when the component is initialized.
*/
Expand Down Expand Up @@ -373,67 +369,6 @@ export class OntologyTreeComponent implements OnInit, OnChanges {
}
}

/**
* Sets the current highlighted node to the moused over node (reveals opacity slider)
*
* @param node
*/
mouseOver(node: FlatNode): void {
this.highlightedNode = node;
}

/**
* Deselects the highlighted node on mouse out
*/
mouseOut(): void {
this.highlightedNode = undefined;
}

/**
* Sets the opacity of a node
*
* @param node The node to be updated
* @param value Opacity value
*/
updateOpacity(node: FlatNode, value: number | undefined): void {
node.opacity = value;
this.ga.event('opacity_update', 'ontology_tree', node.label, value);
this.nodeChanged.emit(node);
}

/**
* Resets node to default opacity and visibility
*
* @param node The node to be reset
*/
resetNode(node: FlatNode): void {
node.opacity = 20;
node.visible = true;
this.ga.event('node_reset', 'ontology_tree', node.label);
this.nodeChanged.emit(node);
}

/**
* Toggles visibility of a node
*
* @param node The node to be toggled
*/
toggleVisibility(node: FlatNode): void {
node.visible = node.visible === true ? false : true;
this.ga.event('visibility_update', 'ontology_tree', node.label, +node.visible);
this.nodeChanged.emit(node);
}

/**
* Used to properly set the position of the slider popup on the ontology tree
*
* @param level Current level of a node in the ontology tree
* @returns left indent value
*/
getLeftIndent(level: number): string {
return `${level * -1.5}rem`;
}

/**
* Handles the scroll event to detect when scroll is at the bottom.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { MatTreeModule } from '@angular/material/tree';

import { OntologyTreeComponent } from './ontology-tree.component';
import { OpacitySliderModule } from 'ccf-shared';
import { ButtonToggleModule } from '../../../shared/components/button-toggle/button-toggle.module';

@NgModule({
Expand All @@ -16,7 +15,6 @@ import { ButtonToggleModule } from '../../../shared/components/button-toggle/but
MatIconModule,
MatTooltipModule,
MatTreeModule,
OpacitySliderModule,
ButtonToggleModule
],
declarations: [OntologyTreeComponent],
Expand Down
Loading