Skip to content

Commit

Permalink
added OverviewDetail column, for Caleydo/lineup_app#3 Caleydo/lineup_…
Browse files Browse the repository at this point in the history
  • Loading branch information
domdir committed Aug 26, 2018
1 parent ca0dcaa commit 957530f
Show file tree
Hide file tree
Showing 13 changed files with 597 additions and 98 deletions.
98 changes: 98 additions & 0 deletions src/model/OverviewDetailColumn.ts
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; }
}
2 changes: 2 additions & 0 deletions src/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import StringMapColumn from './StringMapColumn';
import StringsColumn from './StringsColumn';
import ValueColumn, {IValueColumnDesc} from './ValueColumn';
import ImpositionBoxPlotColumn from './ImpositionBoxPlotColumn';
import OverviewDetailColumn from './OverviewDetailColumn';

export {isSupportType, Category, SupportType} from './annotations';
export {isMissingValue, isUnknown, FIRST_IS_NAN, missingGroup} from './missing';
Expand Down Expand Up @@ -166,6 +167,7 @@ export function models() {
date: DateColumn,
dateMap: DatesMapColumn,
dates: DatesColumn,
detail: OverviewDetailColumn,
group: GroupColumn,
hierarchy: HierarchyColumn,
imposition: ImpositionCompositeColumn,
Expand Down
2 changes: 2 additions & 0 deletions src/provider/ACommonDataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {createAggregateDesc, createRankDesc, createSelectionDesc, IColumnDesc, I
import {IOrderedGroup} from '../model/Group';
import Ranking from '../model/Ranking';
import ADataProvider, {IDataProviderOptions} from './ADataProvider';
import {createDetailDesc} from '../model/OverviewDetailColumn';


function isComplexAccessor(column: any) {
Expand Down Expand Up @@ -164,6 +165,7 @@ abstract class ACommonDataProvider extends ADataProvider {
if (this.multiSelections) {
r.push(this.create(createSelectionDesc())!);
}
r.push(this.create(createDetailDesc())!);
}
this.getColumns().forEach((col) => {
const c = this.create(col);
Expand Down
118 changes: 117 additions & 1 deletion src/provider/ADataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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';
Expand All @@ -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>();

Expand Down Expand Up @@ -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]);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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))
};
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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':
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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[]>}
Expand All @@ -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}
Expand All @@ -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
*/
Expand All @@ -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
Expand Down
Loading

0 comments on commit 957530f

Please sign in to comment.