Undo/redo (#659)

* immutable crossfilter

* first cut at reducer refactor with cascade model

* add initial redo/undo implementation

* small optimization

* integrate expression with history

* add tests for new reducers and fix a couple of small initialization bugs

* treat tiny lasso selections as a clear

* better function name for clarity

* fix undo for differential expression

* remove logging

* fix regression due to bad merge

* cleanup and comments for clarity

* improve undoable configuration for flexibility

* fix stale comments

* remove debugging code from production build

* rename categoricalSelectionState

* rename file

* improve comments
This commit is contained in:
Bruce Martin
2019-03-22 14:53:48 -07:00
committed by GitHub
parent 9420abfacc
commit 7ef5203564
25 changed files with 1106 additions and 555 deletions
@@ -6,12 +6,12 @@ import * as globals from "../../globals";
import Category from "./category";
@connect(state => ({
categoricalSelectionState: state.controls.categoricalSelectionState
categoricalSelection: state.categoricalSelection
}))
class Categories extends React.Component {
render() {
const { categoricalSelectionState } = this.props;
if (!categoricalSelectionState) return null;
const { categoricalSelection } = this.props;
if (!categoricalSelection) return null;
return (
<div
@@ -26,7 +26,7 @@ class Categories extends React.Component {
>
Categorical Metadata
</p>
{_.map(categoricalSelectionState, (catState, catName) => (
{_.map(categoricalSelection, (catState, catName) => (
<Category key={catName} metadataField={catName} />
))}
</div>
+9 -13
View File
@@ -9,8 +9,8 @@ import Value from "./value";
import sortedCategoryValues from "./util";
@connect(state => ({
colorAccessor: state.controls.colorAccessor,
categoricalSelectionState: state.controls.categoricalSelectionState
colorAccessor: state.colors.colorAccessor,
categoricalSelection: state.categoricalSelection
}))
class Category extends React.Component {
constructor(props) {
@@ -22,9 +22,9 @@ class Category extends React.Component {
}
componentDidUpdate(prevProps) {
const { categoricalSelectionState, metadataField } = this.props;
if (categoricalSelectionState !== prevProps.categoricalSelectionState) {
const cat = categoricalSelectionState[metadataField];
const { categoricalSelection, metadataField } = this.props;
if (categoricalSelection !== prevProps.categoricalSelection) {
const cat = categoricalSelection[metadataField];
const categoryCount = {
// total number of categories in this dimension
totalCatCount: cat.numCategories,
@@ -87,9 +87,9 @@ class Category extends React.Component {
}
renderCategoryItems() {
const { categoricalSelectionState, metadataField } = this.props;
const { categoricalSelection, metadataField } = this.props;
const cat = categoricalSelectionState[metadataField];
const cat = categoricalSelection[metadataField];
const optTuples = sortedCategoryValues([...cat.categoryIndices]);
return _.map(optTuples, (tuple, i) => (
<Value
@@ -104,12 +104,8 @@ class Category extends React.Component {
render() {
const { isExpanded, isChecked } = this.state;
const {
metadataField,
colorAccessor,
categoricalSelectionState
} = this.props;
const { isTruncated } = categoricalSelectionState[metadataField];
const { metadataField, colorAccessor, categoricalSelection } = this.props;
const { isTruncated } = categoricalSelection[metadataField];
return (
<div
style={{
+14 -16
View File
@@ -10,7 +10,7 @@ class Occupancy extends React.Component {
const {
occupancy,
colorScale,
categoricalSelectionState,
categoricalSelection,
colorAccessor,
schema
} = this.props;
@@ -29,23 +29,21 @@ class Occupancy extends React.Component {
let currentOffset = 0;
const stacks = categoricalSelectionState[colorAccessor].categoryValues.map(
d => {
const o = occupancy.get(d);
const stacks = categoricalSelection[colorAccessor].categoryValues.map(d => {
const o = occupancy.get(d);
const scaledValue = x(o);
const scaledValue = x(o);
const stackItem = {
key: d,
value: o || 0,
rectWidth: o ? scaledValue : 0,
offset: currentOffset,
fill: o ? colorScale(categories.indexOf(d)) : "rgb(255,255,255)"
};
currentOffset += o ? scaledValue : 0;
return stackItem;
}
);
const stackItem = {
key: d,
value: o || 0,
rectWidth: o ? scaledValue : 0,
offset: currentOffset,
fill: o ? colorScale(categories.indexOf(d)) : "rgb(255,255,255)"
};
currentOffset += o ? scaledValue : 0;
return stackItem;
});
return (
<svg
+10 -14
View File
@@ -7,11 +7,11 @@ import { countCategoryValues2D } from "../../util/stateManager/worldUtil";
import * as globals from "../../globals";
@connect(state => ({
categoricalSelectionState: state.controls.categoricalSelectionState,
colorScale: state.controls.colors.scale,
colorAccessor: state.controls.colorAccessor,
schema: _.get(state.controls.world, "schema", null),
world: state.controls.world
categoricalSelection: state.categoricalSelection,
colorScale: state.colors.scale,
colorAccessor: state.colors.colorAccessor,
schema: _.get(state.world, "schema", null),
world: state.world
}))
class CategoryValue extends React.Component {
toggleOff() {
@@ -34,7 +34,7 @@ class CategoryValue extends React.Component {
render() {
const {
categoricalSelectionState,
categoricalSelection,
metadataField,
categoryIndex,
colorAccessor,
@@ -44,9 +44,9 @@ class CategoryValue extends React.Component {
world
} = this.props;
if (!categoricalSelectionState) return null;
if (!categoricalSelection) return null;
const category = categoricalSelectionState[metadataField];
const category = categoricalSelection[metadataField];
const selected = category.categorySelected[categoryIndex];
const count = category.categoryCounts[categoryIndex];
const value = category.categoryValues[categoryIndex];
@@ -65,11 +65,7 @@ class CategoryValue extends React.Component {
})[0].categories;
}
if (
colorAccessor &&
!isColorBy &&
categoricalSelectionState[colorAccessor]
) {
if (colorAccessor && !isColorBy && categoricalSelection[colorAccessor]) {
occupancy = countCategoryValues2D(
metadataField,
colorAccessor,
@@ -112,7 +108,7 @@ class CategoryValue extends React.Component {
<span style={{ flexShrink: 0 }}>
{colorAccessor &&
!isColorBy &&
categoricalSelectionState[colorAccessor] ? (
categoricalSelection[colorAccessor] ? (
<Occupancy
occupancy={occupancy.get(
category.categoryValues[categoryIndex]