forked from lineupjs/lineupjs
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added OverviewDetail column, for Caleydo/lineup_app#3 Caleydo/lineup_…
- Loading branch information
Showing
13 changed files
with
597 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import {Category, SupportType, toolbar} from './annotations'; | ||
import {IDataRow, IGroup} from './interfaces'; | ||
import ValueColumn, {IValueColumnDesc} from './ValueColumn'; | ||
|
||
/** | ||
* factory for creating a description creating a rank column | ||
* @param label | ||
* @returns {{type: string, label: string}} | ||
*/ | ||
export function createDetailDesc(label: string = 'Detail Checkboxes') { | ||
return {type: 'detail', label, fixed: true}; | ||
} | ||
|
||
export interface IDetailColumnDesc extends IValueColumnDesc<boolean> { | ||
/** | ||
* setter for selecting/deselecting the given row | ||
*/ | ||
setter(row: IDataRow, value: boolean): void; | ||
|
||
/** | ||
* setter for selecting/deselecting the given row | ||
*/ | ||
setterAll(rows: IDataRow[], value: boolean): void; | ||
} | ||
|
||
/** | ||
* a checkbox column for selections | ||
*/ | ||
@SupportType() | ||
@toolbar('sort', 'stratify') | ||
@Category('support') | ||
export default class OverviewDetailColumn extends ValueColumn<boolean> { | ||
private static DETAILED_GROUP: IGroup = { | ||
name: 'Detailed', | ||
color: 'blue' | ||
}; | ||
private static NOT_DETAILED_GROUP: IGroup = { | ||
name: 'Undetailed', | ||
color: 'gray' | ||
}; | ||
static readonly EVENT_DETAIL = 'detail'; | ||
|
||
constructor(id: string, desc: Readonly<IDetailColumnDesc>) { | ||
super(id, desc); | ||
this.setDefaultWidth(20); | ||
} | ||
|
||
get frozen() { | ||
return this.desc.frozen !== false; | ||
} | ||
|
||
protected createEventList() { | ||
return super.createEventList().concat([OverviewDetailColumn.EVENT_DETAIL]); | ||
} | ||
|
||
setValue(row: IDataRow, value: boolean) { | ||
const old = this.getValue(row); | ||
if (old === value) { | ||
return true; | ||
} | ||
return this.setImpl(row, value); | ||
} | ||
|
||
setValues(rows: IDataRow[], value: boolean) { | ||
if (rows.length === 0) { | ||
return; | ||
} | ||
if ((<IDetailColumnDesc>this.desc).setterAll) { | ||
(<IDetailColumnDesc>this.desc).setterAll(rows, value); | ||
} | ||
this.fire(OverviewDetailColumn.EVENT_DETAIL, rows[0], value, rows); | ||
return true; | ||
} | ||
|
||
private setImpl(row: IDataRow, value: boolean) { | ||
if ((<IDetailColumnDesc>this.desc).setter) { | ||
(<IDetailColumnDesc>this.desc).setter(row, value); | ||
} | ||
this.fire(OverviewDetailColumn.EVENT_DETAIL, row, value); | ||
return true; | ||
} | ||
|
||
toggleValue(row: IDataRow) { | ||
const old = this.getValue(row); | ||
this.setImpl(row, !old); | ||
return !old; | ||
} | ||
|
||
compare(a: IDataRow, b: IDataRow) { | ||
const va = this.getValue(a) === true; | ||
const vb = this.getValue(b) === true; | ||
return va === vb ? 0 : (va < vb ? -1 : +1); | ||
} | ||
|
||
group(row: IDataRow) { | ||
const isSelected = this.getValue(row); | ||
return isSelected ? OverviewDetailColumn.DETAILED_GROUP : OverviewDetailColumn.NOT_DETAILED_GROUP; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ import Ranking from '../model/Ranking'; | |
import StackColumn from '../model/StackColumn'; | ||
import {exportRanking, IExportOptions} from './utils'; | ||
import {isSupportType} from '../model/annotations'; | ||
import {createDetailDesc} from '../model/OverviewDetailColumn'; | ||
|
||
export {IExportOptions} from './utils'; | ||
|
||
|
@@ -50,6 +51,16 @@ export interface IDataProvider extends AEventDispatcher { | |
|
||
isSelected(i: number): boolean; | ||
|
||
detailAllOf(ranking: Ranking): void; | ||
|
||
getDetail(): number[]; | ||
|
||
setDetail(dataIndices: number[]): void; | ||
|
||
toggleDetail(i: number, additional?: boolean): boolean; | ||
|
||
isDetail(i: number): boolean; | ||
|
||
removeRanking(ranking: Ranking): void; | ||
|
||
ensureOneRanking(): void; | ||
|
@@ -85,6 +96,7 @@ export interface IDataProvider extends AEventDispatcher { | |
*/ | ||
abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | ||
static readonly EVENT_SELECTION_CHANGED = 'selectionChanged'; | ||
static readonly EVENT_DETAIL_CHANGED = 'detailChanged'; | ||
static readonly EVENT_ADD_COLUMN = Ranking.EVENT_ADD_COLUMN; | ||
static readonly EVENT_REMOVE_COLUMN = Ranking.EVENT_REMOVE_COLUMN; | ||
static readonly EVENT_ADD_RANKING = 'addRanking'; | ||
|
@@ -110,6 +122,8 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
*/ | ||
private readonly selection = new OrderedSet<number>(); | ||
|
||
private readonly detail = new OrderedSet<number>(); | ||
|
||
//[email protected] | ||
private aggregations = new Set<string>(); | ||
|
||
|
@@ -141,7 +155,7 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
ADataProvider.EVENT_ADD_COLUMN, ADataProvider.EVENT_REMOVE_COLUMN, | ||
ADataProvider.EVENT_ADD_RANKING, ADataProvider.EVENT_REMOVE_RANKING, | ||
ADataProvider.EVENT_DIRTY, ADataProvider.EVENT_DIRTY_HEADER, ADataProvider.EVENT_DIRTY_VALUES, | ||
ADataProvider.EVENT_ORDER_CHANGED, ADataProvider.EVENT_SELECTION_CHANGED, | ||
ADataProvider.EVENT_ORDER_CHANGED, ADataProvider.EVENT_SELECTION_CHANGED, ADataProvider.EVENT_DETAIL_CHANGED, | ||
ADataProvider.EVENT_ADD_DESC, ADataProvider.EVENT_CLEAR_DESC, | ||
ADataProvider.EVENT_JUMP_TO_NEAREST, ADataProvider.EVENT_GROUP_AGGREGATION_CHANGED]); | ||
} | ||
|
@@ -335,6 +349,10 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
(<ISelectionColumnDesc>desc).accessor = (row: IDataRow) => this.isSelected(row.i); | ||
(<ISelectionColumnDesc>desc).setter = (row: IDataRow, value: boolean) => value ? this.select(row.i) : this.deselect(row.i); | ||
(<ISelectionColumnDesc>desc).setterAll = (rows: IDataRow[], value: boolean) => value ? this.selectAll(rows.map((d) => d.i)) : this.deselectAll(rows.map((d) => d.i)); | ||
} else if (desc.type === 'detail') { | ||
(<ISelectionColumnDesc>desc).accessor = (row: IDataRow) => this.isDetail(row.i); | ||
(<ISelectionColumnDesc>desc).setter = (row: IDataRow, value: boolean) => value ? this.addDetail(row.i) : this.removeDetail(row.i); | ||
(<ISelectionColumnDesc>desc).setterAll = (rows: IDataRow[], value: boolean) => value ? this.addDetailAll(rows.map((d) => d.i)) : this.removeDetailAll(rows.map((d) => d.i)); | ||
} else if (desc.type === 'aggregate') { | ||
(<IAggregateGroupColumnDesc>desc).isAggregated = (ranking: Ranking, group: IGroup) => this.isAggregated(ranking, group); | ||
(<IAggregateGroupColumnDesc>desc).setAggregated = (ranking: Ranking, group: IGroup, value: boolean) => this.setAggregated(ranking, group, value); | ||
|
@@ -411,6 +429,7 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
return { | ||
uid: this.uid, | ||
selection: this.getSelection(), | ||
detail: this.getDetail(), | ||
aggregations: Array.from(this.aggregations), | ||
rankings: this.rankings.map((r) => r.dump(this.toDescRef)) | ||
}; | ||
|
@@ -472,6 +491,10 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
if (dump.selection) { | ||
dump.selection.forEach((s: number) => this.selection.add(s)); | ||
} | ||
//restore detail | ||
if (dump.detail) { | ||
dump.detail.forEach((s: number) => this.detail.add(s)); | ||
} | ||
if (dump.aggregations) { | ||
this.aggregations.clear(); | ||
dump.aggregations.forEach((a: string) => this.aggregations.add(a)); | ||
|
@@ -519,6 +542,8 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
return this.create(createRankDesc()); | ||
case 'selection': | ||
return this.create(createSelectionDesc()); | ||
case 'detail': | ||
return this.create(createDetailDesc()); | ||
case 'group': | ||
return this.create(createGroupDesc()); | ||
case 'aggregate': | ||
|
@@ -659,6 +684,10 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
return this.selection.has(index); | ||
} | ||
|
||
isDetail(index: number) { | ||
return this.detail.has(index); | ||
} | ||
|
||
/** | ||
* also select the given row | ||
* @param index | ||
|
@@ -674,6 +703,14 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
this.fire(ADataProvider.EVENT_SELECTION_CHANGED, this.getSelection()); | ||
} | ||
|
||
addDetail(index: number) { | ||
if (this.detail.has(index)) { | ||
return; //no change | ||
} | ||
this.detail.add(index); | ||
this.fire(ADataProvider.EVENT_DETAIL_CHANGED, this.getDetail()); | ||
} | ||
|
||
/** | ||
* hook for selecting elements matching the given arguments | ||
* @param search | ||
|
@@ -706,10 +743,24 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
this.fire(ADataProvider.EVENT_SELECTION_CHANGED, this.getSelection()); | ||
} | ||
|
||
addDetailAll(indices: number[]) { | ||
if (indices.every((i) => this.detail.has(i))) { | ||
return; //no change | ||
} | ||
indices.forEach((index) => { | ||
this.detail.add(index); | ||
}); | ||
this.fire(ADataProvider.EVENT_DETAIL_CHANGED, this.getDetail()); | ||
} | ||
|
||
selectAllOf(ranking: Ranking) { | ||
this.setSelection(ranking.getOrder()); | ||
} | ||
|
||
detailAllOf(ranking: Ranking) { | ||
this.setDetail(ranking.getOrder()); | ||
} | ||
|
||
/** | ||
* set the selection to the given rows | ||
* @param indices | ||
|
@@ -725,6 +776,17 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
this.selectAll(indices); | ||
} | ||
|
||
setDetail(indices: number[]) { | ||
if (indices.length === 0) { | ||
return this.clearDetail(); | ||
} | ||
if (this.detail.size === indices.length && indices.every((i) => this.detail.has(i))) { | ||
return; //no change | ||
} | ||
this.detail.clear(); | ||
this.addDetailAll(indices); | ||
} | ||
|
||
/** | ||
* toggles the selection of the given data index | ||
* @param index | ||
|
@@ -748,6 +810,23 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
return true; | ||
} | ||
|
||
toggleDetail(index: number, additional = false) { | ||
if (this.isDetail(index)) { | ||
if (additional) { | ||
this.removeDetail(index); | ||
} else { | ||
this.clearDetail(); | ||
} | ||
return false; | ||
} | ||
if (additional) { | ||
this.addDetail(index); | ||
} else { | ||
this.setDetail([index]); | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* deselect the given row | ||
* @param index | ||
|
@@ -760,6 +839,14 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
this.fire(ADataProvider.EVENT_SELECTION_CHANGED, this.getSelection()); | ||
} | ||
|
||
removeDetail(index: number) { | ||
if (!this.detail.has(index)) { | ||
return; //no change | ||
} | ||
this.detail.delete(index); | ||
this.fire(ADataProvider.EVENT_DETAIL_CHANGED, this.getDetail()); | ||
} | ||
|
||
/** | ||
* also select all the given rows | ||
* @param indices | ||
|
@@ -774,6 +861,16 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
this.fire(ADataProvider.EVENT_SELECTION_CHANGED, this.getSelection()); | ||
} | ||
|
||
removeDetailAll(indices: number[]) { | ||
if (indices.every((i) => !this.detail.has(i))) { | ||
return; //no change | ||
} | ||
indices.forEach((index) => { | ||
this.detail.delete(index); | ||
}); | ||
this.fire(ADataProvider.EVENT_DETAIL_CHANGED, this.getDetail()); | ||
} | ||
|
||
/** | ||
* returns a promise containing the selected rows | ||
* @return {Promise<any[]>} | ||
|
@@ -785,6 +882,13 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
return this.view(this.getSelection()); | ||
} | ||
|
||
detailRows(): Promise<any[]> | any[] { | ||
if (this.detail.size === 0) { | ||
return []; | ||
} | ||
return this.view(this.getDetail()); | ||
} | ||
|
||
/** | ||
* returns the currently selected indices | ||
* @returns {Array} | ||
|
@@ -793,6 +897,10 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
return Array.from(this.selection); | ||
} | ||
|
||
getDetail() { | ||
return Array.from(this.detail); | ||
} | ||
|
||
/** | ||
* clears the selection | ||
*/ | ||
|
@@ -804,6 +912,14 @@ abstract class ADataProvider extends AEventDispatcher implements IDataProvider { | |
this.fire(ADataProvider.EVENT_SELECTION_CHANGED, [], false); | ||
} | ||
|
||
clearDetail() { | ||
if (this.detail.size === 0) { | ||
return; //no change | ||
} | ||
this.detail.clear(); | ||
this.fire(ADataProvider.EVENT_DETAIL_CHANGED, [], false); | ||
} | ||
|
||
/** | ||
* utility to export a ranking to a table with the given separator | ||
* @param ranking | ||
|
Oops, something went wrong.