Skip to content

Commit

Permalink
Add brush selection on detailed timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
MohamedHamouGisaia committed Jul 20, 2018
1 parent 89cec5e commit abb7eab
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 33 deletions.
7 changes: 7 additions & 0 deletions src/app/components/timeline/timeline.component.css
Expand Up @@ -21,3 +21,10 @@
padding: 0 0 5px 5px;
}

.show-detailedtimeline-true {
visibility: visible;
}
.show-detailedtimeline-false {
visibility: hidden;
height: 0;
}
74 changes: 72 additions & 2 deletions src/app/components/timeline/timeline.component.html
Expand Up @@ -3,6 +3,76 @@
<arlas-timeline-shortcut [timelineComponent]="timelineComponent"></arlas-timeline-shortcut>
</div>
<div class="arlas-timeline--timelines">
<arlas-tool-widget *ngIf="detailedTimelineComponent && showDetailedTimeline" [contributorId]="detailedTimelineComponent?.contributorId" [componentParams]="detailedTimelineComponent?.input"></arlas-tool-widget>
<arlas-tool-widget #timeline *ngIf="timelineComponent" [contributorId]="timelineComponent?.contributorId" [componentParams]="timelineComponent?.input"></arlas-tool-widget>
<div *ngIf="detailedTimelineComponent" class="show-detailedtimeline-{{showDetailedTimeline.valueOf()}}">
<arlas-histogram #histogram
[data]="detailedTimelineContributor?.chartData"
[intervalSelection]="detailedTimelineIntervalSelection"
(dataPlottedEvent)="afterDetailedDataPlotted($event)"
(valuesListChangedEvent)="onDetailedIntervalBrushed($event)"
[id]="detailedTimelineComponent?.input?.id"
[displayOnlyIntervalsWithData]=true
[dataType]="detailedTimelineComponent?.input?.dataType"
[dataUnit]="detailedTimelineComponent?.input?.dataUnit"
[valuesDateFormat]="detailedTimelineComponent?.input?.valuesDateFormat"
[isHistogramSelectable]="detailedTimelineComponent?.input?.isHistogramSelectable"
[multiselectable]="detailedTimelineComponent?.input?.multiselectable"
[topOffsetRemoveInterval]="detailedTimelineComponent?.input?.topOffsetRemoveInterval"
[leftOffsetRemoveInterval]="detailedTimelineComponent?.input?.leftOffsetRemoveInterval"
[brushHandlesHeightWeight]="detailedTimelineComponent?.input?.brushHandlesHeightWeight"
[chartType]="detailedTimelineComponent?.input?.chartType"
[chartTitle]="detailedTimelineComponent?.input?.chartTitle"
[chartWidth]="detailedTimelineComponent?.input?.chartWidth"
[chartHeight]="detailedTimelineComponent?.input?.chartHeight"
[customizedCssClass]="detailedTimelineComponent?.input?.customizedCssClass"
[xAxisPosition]="detailedTimelineComponent?.input?.xAxisPosition"
[descriptionPosition]="detailedTimelineComponent?.input?.descriptionPosition"
[xTicks]="detailedTimelineComponent?.input?.xTicks"
[yTicks]="detailedTimelineComponent?.input?.yTicks"
[xLabels]="detailedTimelineComponent?.input?.xLabels"
[yLabels]="detailedTimelineComponent?.input?.yLabels"
[showXTicks]="detailedTimelineComponent?.input?.showXTicks"
[showYTicks]="detailedTimelineComponent?.input?.showYTicks"
[showXLabels]="detailedTimelineComponent?.input?.showXLabels"
[showYLabels]="detailedTimelineComponent?.input?.showYLabels"
[ticksDateFormat]="detailedTimelineComponent?.input?.ticksDateFormat"
[isSmoothedCurve]="detailedTimelineComponent?.input?.isSmoothedCurve"
[barWeight]="detailedTimelineComponent?.input?.barWeight"
[paletteColors]="detailedTimelineComponent?.input?.paletteColors"

></arlas-histogram>
</div>
<div *ngIf="timelineComponent">
<arlas-histogram #timeline [intervalListSelection]="timelineContributor?.intervalListSelection" [intervalSelection]="timelineContributor?.intervalSelection"
[data]="timelineContributor?.chartData" (valuesListChangedEvent)="onTimelineIntervalBrushed($event)"
[id]="timelineComponent?.input?.id"
[dataType]="timelineComponent?.input?.dataType"
[dataUnit]="timelineComponent?.input?.dataUnit"
[valuesDateFormat]="timelineComponent?.input?.valuesDateFormat"
[isHistogramSelectable]="timelineComponent?.input?.isHistogramSelectable"
[multiselectable]="timelineComponent?.input?.multiselectable"
[topOffsetRemoveInterval]="timelineComponent?.input?.topOffsetRemoveInterval"
[leftOffsetRemoveInterval]="timelineComponent?.input?.leftOffsetRemoveInterval"
[brushHandlesHeightWeight]="timelineComponent?.input?.brushHandlesHeightWeight"
[chartType]="timelineComponent?.input?.chartType"
[chartTitle]="timelineComponent?.input?.chartTitle"
[chartWidth]="timelineComponent?.input?.chartWidth"
[chartHeight]="timelineComponent?.input?.chartHeight"
[customizedCssClass]="timelineComponent?.input?.customizedCssClass"
[xAxisPosition]="timelineComponent?.input?.xAxisPosition"
[descriptionPosition]="timelineComponent?.input?.descriptionPosition"
[xTicks]="timelineComponent?.input?.xTicks"
[yTicks]="timelineComponent?.input?.yTicks"
[xLabels]="timelineComponent?.input?.xLabels"
[yLabels]="timelineComponent?.input?.yLabels"
[showXTicks]="timelineComponent?.input?.showXTicks"
[showYTicks]="timelineComponent?.input?.showYTicks"
[showXLabels]="timelineComponent?.input?.showXLabels"
[showYLabels]="timelineComponent?.input?.showYLabels"
[ticksDateFormat]="timelineComponent?.input?.ticksDateFormat"
[isSmoothedCurve]="timelineComponent?.input?.isSmoothedCurve"
[barWeight]="timelineComponent?.input?.barWeight"
[paletteColors]="timelineComponent?.input?.paletteColors"

></arlas-histogram>
</div>
</div>
125 changes: 96 additions & 29 deletions src/app/components/timeline/timeline.component.ts
Expand Up @@ -17,14 +17,15 @@
* under the License.
*/

import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { HistogramContributor } from 'arlas-web-contributors';
import { OperationEnum, Contributor } from 'arlas-web-core';
import { Component, Input, OnInit, ViewChild, ChangeDetectorRef } from '@angular/core';
import { HistogramContributor, DetailedHistogramContributor } from 'arlas-web-contributors';
import { OperationEnum } from 'arlas-web-core';
import { ArlasCollaborativesearchService, ArlasStartupService } from './../../services/startup/startup.service';
import { StringifiedTimeShortcut, TimeShortcut, SelectedOutputValues } from 'arlas-web-contributors/models/models';
import { StringifiedTimeShortcut, SelectedOutputValues } from 'arlas-web-contributors/models/models';
import * as d3 from 'd3';
import { WidgetComponent } from '../widget/widget.component';
import { TranslateService } from '@ngx-translate/core';
import { ChartType, DataType, Position } from 'arlas-d3';
import { HistogramComponent } from 'arlas-web-components';

@Component({
selector: 'arlas-timeline',
Expand All @@ -35,64 +36,130 @@ export class TimelineComponent implements OnInit {

@Input() public detailedTimelineComponent: any;
@Input() public timelineComponent: any;
@ViewChild('timeline') private timelineWidget: WidgetComponent;
@ViewChild('timeline') public timelineHistogramComponent: HistogramComponent;

public showDetailedTimeline = false;
private timelineIsFiltered = false;
public detailedTimelineContributor: DetailedHistogramContributor;
public timelineContributor: HistogramContributor;
public detailedTimelineIntervalSelection: SelectedOutputValues;

private isDetailedIntervalBrushed = false;
private applicationFirstLoad = false;
private timelineIsFiltered = false;

constructor(private arlasCollaborativesearchService: ArlasCollaborativesearchService, private arlasStartupService: ArlasStartupService) {
constructor(private arlasCollaborativesearchService: ArlasCollaborativesearchService, private cdr: ChangeDetectorRef,
private arlasStartupService: ArlasStartupService) {
}

public ngOnInit() {
if (this.timelineComponent && this.detailedTimelineComponent) {
this.detailedTimelineComponent.input.isHistogramSelectable = false;
this.resetHistogramsInputs(this.timelineComponent.input);
this.resetHistogramsInputs(this.detailedTimelineComponent.input);
this.detailedTimelineContributor = this.arlasStartupService.contributorRegistry.get(this.detailedTimelineComponent.contributorId);
this.timelineContributor = this.arlasStartupService.contributorRegistry.get(this.timelineComponent.contributorId);
this.showDetailedTimelineOnCollaborationEnd();
} else if (this.timelineComponent) {
this.resetHistogramsInputs(this.timelineComponent.input);
this.timelineContributor = this.arlasStartupService.contributorRegistry.get(this.timelineComponent.contributorId);
}
}

/**
* Recalculates the new data of detailed timeline and resets its own current selection.
* @param selections List containing only the current selection of detailed timeline
*/
public onDetailedIntervalBrushed(selections: SelectedOutputValues[]): void {
this.isDetailedIntervalBrushed = true;
this.detailedTimelineIntervalSelection = {startvalue: selections[0].startvalue, endvalue: selections[0].endvalue};
this.timelineContributor.valueChanged(this.timelineContributor.intervalListSelection.concat(selections));
}

/**
* Runs when the selection is brushed on timeline.
* @param selections List containing only the current selection of detailed timeline
*/
public onTimelineIntervalBrushed(selections: SelectedOutputValues[]): void {
this.timelineContributor.valueChanged(selections);
}

/**
* Runs when the detailed timeline is plotted.
* Sets current selection of detailed timeline after it is plotted
* Applies the current selection of detailed timeline on the main timeline
*/
public afterDetailedDataPlotted() {
if (this.isDetailedIntervalBrushed) { // If detailed timeline is replotted after moving its own brush.
// Reset current selection of detailed timeline after it is plotted
this.detailedTimelineIntervalSelection = { startvalue: this.detailedTimelineIntervalSelection.startvalue,
endvalue: this.detailedTimelineIntervalSelection.endvalue };
// Apply the current selection of detailed timeline on the main timeline
this.timelineContributor.intervalSelection = { startvalue: this.detailedTimelineIntervalSelection.startvalue,
endvalue: this.detailedTimelineIntervalSelection.endvalue };
} else { // If detailed timeline is replotted after moving the brush of the main timeline or when the app is loaded.
const selection = this.timelineContributor.intervalSelection;
if (selection) {
this.detailedTimelineIntervalSelection = { startvalue: selection.startvalue, endvalue: selection.endvalue };
} else {
this.applicationFirstLoad = true;
}
}
this.isDetailedIntervalBrushed = false;
this.cdr.detectChanges();
}

private showDetailedTimelineOnCollaborationEnd(): void {
this.arlasCollaborativesearchService.collaborationBus.filter(c => (c.id === this.timelineComponent.contributorId || c.all))
.subscribe(c => {
if (c.operation === OperationEnum.remove) {
this.timelineIsFiltered = false;
this.showDetailedTimeline = false;
this.timelineWidget.histogramComponent.histogram.histogramParams.chartHeight = this.timelineComponent.input.chartHeight;
this.timelineWidget.histogramComponent.resizeHistogram();
this.timelineHistogramComponent.histogram.histogramParams.chartHeight = this.timelineComponent.input.chartHeight;
this.timelineHistogramComponent.resizeHistogram();
} else if (c.operation === OperationEnum.add ) {
this.timelineIsFiltered = true;
}
});

this.arlasCollaborativesearchService.ongoingSubscribe.subscribe(nb => {
if (this.arlasCollaborativesearchService.totalSubscribe === 0 && this.timelineIsFiltered) {
const timelineRange = (<HistogramContributor>this.arlasCollaborativesearchService.registry
.get(this.timelineComponent.contributorId)).range;
const detailedTimelineRange = (<HistogramContributor>this.arlasCollaborativesearchService.registry
.get(this.detailedTimelineComponent.contributorId)).range;
const timelineRange = this.timelineContributor.range;
const detailedTimelineRange = this.detailedTimelineContributor.range;
if (timelineRange && detailedTimelineRange) {
// For timeline : calculate the range of data + selections
let min = timelineRange.min;
let max = timelineRange.max;
const timelineContributor = this.arlasStartupService.contributorRegistry.get(this.timelineComponent.contributorId);
timelineContributor.intervalListSelection.forEach( intervalSelection => {
min = (min > intervalSelection.startvalue) ? intervalSelection.startvalue : min;
max = (max < intervalSelection.endvalue) ? intervalSelection.endvalue : max;
});
min = (min > timelineContributor.intervalSelection.startvalue) ? timelineContributor.intervalSelection.startvalue : min;
max = (max < timelineContributor.intervalSelection.endvalue) ? timelineContributor.intervalSelection.endvalue : max;
// For timeline : calculate the range of data + current selection
const min = Math.min(timelineRange.min, +this.timelineContributor.intervalSelection.startvalue);
const max = Math.max(timelineRange.max, +this.timelineContributor.intervalSelection.endvalue);
this.showDetailedTimeline = ((detailedTimelineRange.max - detailedTimelineRange.min) <= 0.2 * (max - min));
this.timelineWidget.histogramComponent.histogram.histogramParams.chartHeight = (this.showDetailedTimeline) ?
this.timelineHistogramComponent.histogram.histogramParams.chartHeight = (this.showDetailedTimeline) ?
this.detailedTimelineComponent.input.chartHeight : this.timelineComponent.input.chartHeight;
this.timelineWidget.histogramComponent.resizeHistogram();
this.timelineHistogramComponent.resizeHistogram();
if (this.applicationFirstLoad) {
// Sets current selection of detailed timeline
const select = this.timelineContributor.intervalSelection;
this.detailedTimelineIntervalSelection = { startvalue: select.startvalue, endvalue: select.endvalue };
this.applicationFirstLoad = false;
}
} else {
this.showDetailedTimeline = false;
this.timelineWidget.histogramComponent.histogram.histogramParams.chartHeight = this.timelineComponent.input.chartHeight;
this.timelineWidget.histogramComponent.resizeHistogram();
this.timelineHistogramComponent.histogram.histogramParams.chartHeight = this.timelineComponent.input.chartHeight;
this.timelineHistogramComponent.resizeHistogram();
}
}
});
}

private resetHistogramsInputs(inputs: any) {
Object.keys(inputs).forEach(key => {
if (key === 'chartType') {
inputs[key] = ChartType[inputs[key]];
} else if (key === 'dataType') {
inputs[key] = DataType[inputs[key]];
} else if (key === 'xAxisPosition') {
inputs[key] = Position[inputs[key]];
} else if (key === 'descriptionPosition') {
inputs = Position[inputs[key]];
}
});
}
}

@Component({
Expand Down
7 changes: 7 additions & 0 deletions src/app/services/startup/arlasconfig.schema.json
Expand Up @@ -24,6 +24,13 @@
}
]
},
"^(detailedhistogram\\$).*$": {
"allOf": [
{
"$ref": "detailedHistogramContributorConf.schema.json#"
}
]
},
"^(powerbars\\$).*$": {
"allOf": [
{
Expand Down
9 changes: 9 additions & 0 deletions src/app/services/startup/contributorBuilder.ts
Expand Up @@ -20,6 +20,7 @@
import { DataType } from 'arlas-web-contributors/models/models';
import {
HistogramContributor,
DetailedHistogramContributor,
PowerbarsContributor,
ResultListContributor,
SwimLaneContributor
Expand All @@ -46,6 +47,14 @@ export class ContributorBuilder {
collaborativesearchService,
configService, isOneDimension);
break;
case 'detailedhistogram':
datatype = config['datatype'];
isOneDimension = config['isOneDimension'];
contributor = new DetailedHistogramContributor(identifier,
DataType[datatype],
collaborativesearchService,
configService, isOneDimension);
break;
case 'powerbars':
const title: string = config['title'];
contributor = new PowerbarsContributor(identifier,
Expand Down
4 changes: 2 additions & 2 deletions src/app/services/startup/startup.service.ts
Expand Up @@ -19,7 +19,6 @@

import { Injectable, Inject, OnInit } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/catch';
Expand All @@ -35,7 +34,7 @@ import {
SwimLaneContributor,
ChipsSearchContributor,
DonutContributor,
contributors
DetailedHistogramContributor
} from 'arlas-web-contributors';
import * as rootContributorConfSchema from 'arlas-web-contributors/jsonSchemas/rootContributorConf.schema.json';
import { ConfigService, CollaborativesearchService } from 'arlas-web-core';
Expand Down Expand Up @@ -129,6 +128,7 @@ export class ArlasStartupService {
const validateConfig = ajv()
.addSchema(rootContributorConfSchema)
.addSchema(HistogramContributor.getJsonSchema())
.addSchema(DetailedHistogramContributor.getJsonSchema())
.addSchema(SwimLaneContributor.getJsonSchema())
.addSchema(PowerbarsContributor.getJsonSchema())
.addSchema(ResultListContributor.getJsonSchema())
Expand Down

0 comments on commit abb7eab

Please sign in to comment.