Skip to content

Commit

Permalink
Merge pull request #392 from lineupjs/release-4.1.1
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkh committed Aug 13, 2020
2 parents c173814 + 255dfc3 commit 4a97202
Show file tree
Hide file tree
Showing 9 changed files with 50 additions and 80 deletions.
3 changes: 2 additions & 1 deletion demo/missing.html
Expand Up @@ -23,7 +23,8 @@
cat: i >= 2 && isMissing() ? null : cats[Math.floor(Math.random() * 3)],
cat2: i >= 2 && isMissing() ? null : cats[Math.floor(Math.random() * 3)],
as: i >= 2 && isMissing() ? null : l.map((d) => Math.random() * 10),
date: i >= 2 && isMissing() ? null : new Date(Date.now() - Math.floor(Math.random() * 1000000000000))
date: i >= 2 && isMissing() ? null : new Date(Date.now() - Math.floor(Math.random() * 1000000000000)),
boolean: i >= 2 && isMissing() ? null : Math.random() >= 0.5
})
}
const builder = LineUpJS.builder(arr);
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
@@ -1,7 +1,7 @@
{
"name": "lineupjs",
"description": "LineUp is an interactive technique designed to create, visualize and explore rankings of items based on a set of heterogeneous attributes.",
"version": "4.1.0",
"version": "4.1.1",
"author": {
"name": "Samuel Gratzl",
"email": "sam@sgratzl.com",
Expand Down
62 changes: 39 additions & 23 deletions src/model/BooleanColumn.ts
Expand Up @@ -2,11 +2,13 @@ import {Category, toolbar} from './annotations';
import CategoricalColumn from './CategoricalColumn';
import Column, {widthChanged, labelChanged, metaDataChanged, dirty, dirtyHeader, dirtyValues, rendererTypeChanged, groupRendererChanged, summaryRendererChanged, visibilityChanged, dirtyCaches} from './Column';
import ValueColumn, {dataLoaded} from './ValueColumn';
import {ICategoricalColumn, ICategory, ICategoricalColorMappingFunction} from './ICategoricalColumn';
import {ICategoricalColumn, ICategory, ICategoricalColorMappingFunction, ICategoricalFilter} from './ICategoricalColumn';
import {IDataRow, ECompareValueType, IValueColumnDesc, ITypeFactory} from './interfaces';
import {IEventListener} from '../internal';
import {DEFAULT_CATEGORICAL_COLOR_FUNCTION} from './CategoricalColorMappingFunction';
import {integrateDefaults} from './internal';
import {missingGroup} from './missing';
import {isCategoryIncluded, isEqualCategoricalFilter} from './internalCategorical';

export interface IBooleanDesc {
/**
Expand Down Expand Up @@ -35,28 +37,31 @@ export declare function colorMappingChanged_BC(previous: ICategoricalColorMappin
* @asMemberOf BooleanColumn
* @event
*/
export declare function filterChanged_BC(previous: boolean | null, current: boolean | null): void;
export declare function filterChanged_BC(previous: ICategoricalFilter | null, current: ICategoricalFilter | null): void;

/**
* a string column with optional alignment
*/
@toolbar('rename', 'clone', 'sort', 'sortBy', 'group', 'groupBy', 'filterBoolean', 'colorMappedCategorical')
@toolbar('rename', 'clone', 'sort', 'sortBy', 'group', 'groupBy', 'filterCategorical', 'colorMappedCategorical')
@Category('categorical')
export default class BooleanColumn extends ValueColumn<boolean> implements ICategoricalColumn {
static readonly EVENT_FILTER_CHANGED = 'filterChanged';
static readonly EVENT_COLOR_MAPPING_CHANGED = 'colorMappingChanged';

static readonly GROUP_TRUE = {name: 'True', color: 'black'};
static readonly GROUP_FALSE = {name: 'False', color: 'white'};
static readonly GROUP_TRUE = {name: 'True', color: '#444444'};
static readonly GROUP_FALSE = {name: 'False', color: '#dddddd'};

private currentFilter: boolean | null = null;
private currentFilter: ICategoricalFilter | null = null;

private colorMapping: ICategoricalColorMappingFunction;
readonly categories: ICategory[];

constructor(id: string, desc: Readonly<IBooleanColumnDesc>) {
super(id, integrateDefaults(desc, {
width: 30
width: 30,
renderer: 'categorical',
groupRenderer: 'categorical',
summaryRenderer: 'categorical'
}));
this.categories = [
{
Expand All @@ -66,7 +71,7 @@ export default class BooleanColumn extends ValueColumn<boolean> implements ICate
value: 0
},
{
name: desc.trueMarker || '',
name: desc.falseMarker || '',
color: BooleanColumn.GROUP_FALSE.color,
label: BooleanColumn.GROUP_FALSE.name,
value: 1
Expand Down Expand Up @@ -110,14 +115,18 @@ export default class BooleanColumn extends ValueColumn<boolean> implements ICate
getValue(row: IDataRow) {
const v: any = super.getValue(row);
if (typeof (v) === 'undefined' || v == null) {
return false;
return null;
}
return v === true || v === 'true' || v === 'yes' || v === 'x';
}

getCategoryOfBoolean(v: boolean | null) {
return v == null ? null : this.categories[v ? 0 : 1];
}

getCategory(row: IDataRow) {
const v = this.getValue(row);
return this.categories[v ? 0 : 1];
return v == null ? null : this.categories[v ? 0 : 1];
}

getCategories(row: IDataRow) {
Expand Down Expand Up @@ -155,7 +164,9 @@ export default class BooleanColumn extends ValueColumn<boolean> implements ICate
getSet(row: IDataRow) {
const v = this.getValue(row);
const r = new Set<ICategory>();
r.add(this.categories[v ? 0 : 1]);
if (v != null) {
r.add(this.categories[v ? 0 : 1]);
}
return r;
}

Expand All @@ -171,7 +182,12 @@ export default class BooleanColumn extends ValueColumn<boolean> implements ICate
restore(dump: any, factory: ITypeFactory) {
super.restore(dump, factory);
this.colorMapping = factory.categoricalColorMappingFunction(dump.colorMapping, this.categories);
if (typeof dump.filter !== 'undefined') {
if (typeof dump.filter === 'boolean') {
this.currentFilter = {
filter: [this.getCategoryOfBoolean(dump.filter)!.name],
filterMissing: false
};
} else if (typeof dump.filter !== 'undefined') {
this.currentFilter = dump.filter;
}
}
Expand All @@ -192,22 +208,19 @@ export default class BooleanColumn extends ValueColumn<boolean> implements ICate
}

filter(row: IDataRow) {
if (!this.isFiltered()) {
return true;
}
const r = this.getValue(row);
return r === this.currentFilter;
return isCategoryIncluded(this.currentFilter, this.getCategory(row));
}

getFilter() {
return this.currentFilter;
return this.currentFilter == null ? null : Object.assign({}, this.currentFilter);
}

setFilter(filter: boolean | null) {
if (this.currentFilter === filter) {
setFilter(filter: boolean | null | ICategoricalFilter) {
const f: ICategoricalFilter | null = typeof filter === 'boolean' ? {filter: [this.getCategoryOfBoolean(filter)!.name], filterMissing: false} : filter;
if (isEqualCategoricalFilter(this.currentFilter, f)) {
return;
}
this.fire([BooleanColumn.EVENT_FILTER_CHANGED, Column.EVENT_DIRTY_VALUES, Column.EVENT_DIRTY], this.currentFilter, this.currentFilter = filter);
this.fire([BooleanColumn.EVENT_FILTER_CHANGED, Column.EVENT_DIRTY_VALUES, Column.EVENT_DIRTY], this.currentFilter, this.currentFilter = f);
}

clearFilter() {
Expand All @@ -229,7 +242,10 @@ export default class BooleanColumn extends ValueColumn<boolean> implements ICate
}

group(row: IDataRow) {
const enabled = this.getValue(row);
return Object.assign({}, enabled ? BooleanColumn.GROUP_TRUE : BooleanColumn.GROUP_FALSE);
const v = this.getValue(row);
if (v == null) {
return Object.assign({}, missingGroup);
}
return Object.assign({}, v ? BooleanColumn.GROUP_TRUE : BooleanColumn.GROUP_FALSE);
}
}
2 changes: 1 addition & 1 deletion src/model/CategoricalColumn.ts
Expand Up @@ -151,7 +151,7 @@ export default class CategoricalColumn extends ValueColumn<string> implements IC

this.colorMapping = factory.categoricalColorMappingFunction(dump.colorMapping, this.categories);

if ('filter' in dump) {
if (typeof dump.filter === 'undefined') {
this.currentFilter = null;
return;
}
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/CategoricalCellRenderer.ts
@@ -1,6 +1,6 @@
import {DENSE_HISTOGRAM} from '../constants';
import {ICategoricalStatistics, round} from '../internal';
import {OrdinalColumn, isCategoricalColumn, isCategoricalLikeColumn, ICategoricalLikeColumn, ICategory, Column, CategoricalColumn, ICategoricalColumn, IDataRow, IOrderedGroup, SetColumn} from '../model';
import {OrdinalColumn, isCategoricalColumn, isCategoricalLikeColumn, ICategoricalLikeColumn, ICategory, Column, CategoricalColumn, ICategoricalColumn, IDataRow, IOrderedGroup, SetColumn, BooleanColumn} from '../model';
import {CANVAS_HEIGHT, cssClass, FILTERED_OPACITY} from '../styles';
import {filterMissingNumberMarkup, updateFilterMissingNumberMarkup} from '../ui/missing';
import {IRenderContext, ICellRendererFactory, ERenderMode, ICellRenderer, IGroupCellRenderer, ISummaryRenderer} from './interfaces';
Expand All @@ -9,7 +9,7 @@ import {setText, wideEnough, forEach} from './utils';
import {color} from 'd3-color';

/** @internal */
export declare type HasCategoricalFilter = CategoricalColumn | OrdinalColumn | SetColumn;
export declare type HasCategoricalFilter = CategoricalColumn | OrdinalColumn | SetColumn | BooleanColumn;

export default class CategoricalCellRenderer implements ICellRendererFactory {
readonly title: string = 'Color';
Expand Down Expand Up @@ -63,7 +63,7 @@ export default class CategoricalCellRenderer implements ICellRendererFactory {
}

createSummary(col: ICategoricalLikeColumn, context: IRenderContext, interactive: boolean): ISummaryRenderer {
return (col instanceof CategoricalColumn || col instanceof OrdinalColumn || col instanceof SetColumn) ? interactiveSummary(col, context, interactive) : staticSummary(col, context, interactive);
return (col instanceof CategoricalColumn || col instanceof OrdinalColumn || col instanceof SetColumn || col instanceof BooleanColumn) ? interactiveSummary(col, context, interactive) : staticSummary(col, context, interactive);
}
}

Expand Down
45 changes: 0 additions & 45 deletions src/ui/dialogs/BooleanFilterDialog.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/ui/dialogs/CategoricalFilterDialog.ts
@@ -1,4 +1,4 @@
import {SetColumn, CategoricalColumn, ICategoricalFilter, ISetCategoricalFilter, Ranking} from '../../model';
import {SetColumn, CategoricalColumn, ICategoricalFilter, ISetCategoricalFilter, Ranking, BooleanColumn} from '../../model';
import {findFilterMissing, updateFilterMissingNumberMarkup, filterMissingNumberMarkup} from '../missing';
import ADialog, {IDialogContext} from './ADialog';
import {forEach} from './utils';
Expand All @@ -11,7 +11,7 @@ export default class CategoricalFilterDialog extends ADialog {

private readonly before: ICategoricalFilter;

constructor(private readonly column: CategoricalColumn | SetColumn, dialog: IDialogContext, private readonly ctx: IRankingHeaderContext) {
constructor(private readonly column: CategoricalColumn | SetColumn | BooleanColumn, dialog: IDialogContext, private readonly ctx: IRankingHeaderContext) {
super(dialog, {
livePreview: 'filter'
});
Expand Down Expand Up @@ -113,7 +113,7 @@ export default class CategoricalFilterDialog extends ADialog {
}

protected submit() {
let f: string[] | null = this.forEach('input[data-cat]', (n: HTMLInputElement) => n.checked ? n.dataset.cat! : '').filter(Boolean);
let f: string[] | null = this.forEach('input[data-cat]:checked', (n: HTMLInputElement) => n.dataset.cat!);
if (f.length === this.column.categories.length) { // all checked = no filter
f = null;
}
Expand Down
2 changes: 0 additions & 2 deletions src/ui/toolbar.ts
Expand Up @@ -2,7 +2,6 @@ import {getAllToolbarActions, getAllToolbarDialogAddons} from '../model/internal
import {Column, EDateSort, EAdvancedSortMethod, ESortMethod, isSupportType, CompositeColumn, IMultiLevelColumn, isSortingAscByDefault} from '../model';
import {cssClass} from '../styles';
import ADialog, {IDialogContext, dialogContext} from './dialogs/ADialog';
import BooleanFilterDialog from './dialogs/BooleanFilterDialog';
import CategoricalColorMappingDialog from './dialogs/CategoricalColorMappingDialog';
import CategoricalFilterDialog from './dialogs/CategoricalFilterDialog';
import CategoricalMappingFilterDialog from './dialogs/CategoricalMappingFilterDialog';
Expand Down Expand Up @@ -283,7 +282,6 @@ export const toolbarActions: {[key: string]: IToolbarAction} = {
filterString: uiDialog('Filter &hellip;', StringFilterDialog, () => [], {mode: 'menu+shortcut', featureCategory: 'ranking', featureLevel: 'basic'}),
filterCategorical: uiDialog('Filter &hellip;', CategoricalFilterDialog, (ctx) => [ctx], {mode: 'menu+shortcut', featureCategory: 'ranking', featureLevel: 'basic'}),
filterOrdinal: uiDialog('Filter &hellip;', CategoricalMappingFilterDialog, () => [], {mode: 'menu+shortcut', featureCategory: 'ranking', featureLevel: 'basic'}),
filterBoolean: uiDialog('Filter &hellip;', BooleanFilterDialog, () => [], {mode: 'menu+shortcut', featureCategory: 'ranking', featureLevel: 'basic'}),
colorMapped: uiDialog('Color Mapping &hellip;', ColorMappingDialog, (ctx) => [ctx], {mode: 'menu', featureCategory: 'ui', featureLevel: 'advanced'}),
colorMappedCategorical: uiDialog('Color Mapping &hellip;', CategoricalColorMappingDialog, () => [], {mode: 'menu', featureCategory: 'ui', featureLevel: 'advanced'}),
script: uiDialog('Edit Combine Script &hellip;', ScriptEditDialog, () => [], {mode: 'menu+shortcut', featureCategory: 'model', featureLevel: 'advanced'}),
Expand Down

0 comments on commit 4a97202

Please sign in to comment.