Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/js/actions/historyActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const getInVis = require('../util/immutable-utils').getInVis;

import {createStandardAction} from 'typesafe-actions';
import {HistoryRecord} from '../store/factory/History';


/**
* TODO: Action creator to derive a history based on an existing dataset source.
* A derived history shares an existing source dataset.
*
* @param {Object} historyId - The id of the history to derive.
*/
export function mergeHistory (historyId: number) {

return null;
}


// action creators prefixed with "base" should only be called by their redux-thunk function wrappers - jzong
export const baseAddHistory = createStandardAction('ADD_HISTORY')<HistoryRecord, number>();
export const updateHistoryProperty = createStandardAction('UPDATE_HISTORY_PROPERTY')<{property: string, value: any}, number>();
1 change: 1 addition & 0 deletions src/js/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
signals: require('./signalActions'),
scene: require('./sceneActions'),
pipelines: require('./pipelineActions'),
history: require('./historyActions'),
datasets: require('./datasetActions'),
scales: require('./scaleActions'),
guides: require('./guideActions'),
Expand Down
17 changes: 17 additions & 0 deletions src/js/components/HistoryToolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';
import {HistoryList} from './history/HistoryList';

export class HistoryToolbar extends React.PureComponent<{}, {modalIsOpen: boolean}> {
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
constructor(props) {
super(props);
}

public render() {
return (
<div id='history-toolbar'>
<h2>History</h2>
<HistoryList />
</div>
);
}
}
192 changes: 192 additions & 0 deletions src/js/components/history/HistoryItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import * as React from 'react';
import {connect} from 'react-redux';
import {View, parse, Spec} from 'vega';
import {HistoryRecord, HistoryState} from '../../store/factory/History';
import {cleanSpecForPreview} from '../../ctrl/demonstrations';
import {startDragging, stopDragging} from '../../actions/inspectorActions';
import {DraggingStateRecord, HistoryDraggingState} from '../../store/factory/Inspector';
import sg from '../../ctrl/signals';
import {MODE, SELECTED, CELL} from '../../store/factory/Signal';
import {NumericValueRef, StringValueRef, tupleid} from 'vega';
import {channelName} from '../../actions/bindChannel';
import {setMarkVisual} from '../../actions/markActions';
import * as vega from 'vega';
import bindChannel from '../../actions/bindChannel';
import {ColumnRecord, Schema} from '../../store/factory/Dataset';
import { AnyAction } from 'redux';
import {ThunkDispatch} from 'redux-thunk';
import {State} from '../../store';
import {setSignal} from '../../actions/signalActions';
import {SignalValue} from 'vega-typings/types';
import {batchGroupBy} from '../../reducers/historyOptions';

const ctrl = require('../../ctrl');


interface OwnProps {
id: string,
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
history: HistoryRecord // TODO: use History.ts for you just have to pass the id
Comment thread
NanaKwame marked this conversation as resolved.
Outdated

groupNames: any[];
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
}
interface DispatchProps {
startDragging: (d: DraggingStateRecord) => void;
stopDragging: () => void;
setMarkVisual: (payload: {property: string, def: NumericValueRef | StringValueRef}, markId: number) => void;

bindChannel: (dsId: number, field: ColumnRecord, markId: number, property: string) => void;

setSignal: (value: SignalValue, signal: string) => void;
}

function mapDispatchToProps(dispatch: ThunkDispatch<State, null, AnyAction>, ownProps: OwnProps): DispatchProps {
return {
startDragging: (d: DraggingStateRecord) => {
dispatch(startDragging(d)); },
stopDragging: () => {
dispatch(stopDragging()); },
setMarkVisual: (p, markId) => {
dispatch(setMarkVisual(p, markId));
},
bindChannel: (dsId: number, field: ColumnRecord, markId: number, property: string) => {
dispatch(bindChannel(dsId, field, markId, property));
},
setSignal: (value: SignalValue, signal: string) => {
dispatch(setSignal(value, signal));
}
};
}
Comment thread
NanaKwame marked this conversation as resolved.
Outdated

export class HistoryItemInspector extends React.Component<OwnProps & DispatchProps> {

constructor(props) {
super(props);
}

private width = 100; // these should match baseSignals in demonstrations.ts
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
private height = 100; //

public handleClick = (historyId: number) => {

}
public handleDragStart = (historyId: number) => {
this.props.startDragging(HistoryDraggingState({historyId}));
Comment thread
NanaKwame marked this conversation as resolved.
Outdated

sg.set(MODE, 'channels');
ctrl.update();
}

public handleDragEnd = (evt: React.DragEvent<HTMLDivElement>, opts?) => {
const props = this.props;
const sel = sg.get(SELECTED);
const cell = sg.get(CELL);
const dropped = tupleid(sel) && tupleid(cell);

try {
const lyraId = +sel.mark.role.split('lyra_')[1]; // id of thing that was dropped onto

if (dropped) {
const channel = channelName(cell.key);
let fieldName, dsId;
if (channel === 'x' || channel === 'y' || channel === 'color' || channel === 'size') {
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
// set scale
let channelScaleIds = this.props.history.getIn(["guides"])
.map((g) => {
return channel == 'x' || channel == 'y' ? g.scale : channel == 'size' ? g[channel] : g.fill;
})
.filter((scaleId) => {
let scaleName = scaleId ? this.props.history.getIn(["scales"]).get(scaleId).name : null;
return scaleName === channel;
});

fieldName = channelScaleIds.map((scaleId) => {
let scaleRecord = this.props.history.getIn(["scales"]).get(scaleId);
return scaleRecord.get('_domain').length > 0 ? scaleRecord.get('_domain')[0].field : null;
}).first();

dsId = channelScaleIds.map((scaleId) => {
let scaleRecord = this.props.history.getIn(["scales"]).get(scaleId);
return scaleRecord.get('_domain').length > 0 ? scaleRecord.get('_domain')[0].data : null;
}).first();


}
Comment thread
NanaKwame marked this conversation as resolved.
Outdated

const bindField = this.props.history.getIn(["datasets", String(dsId), "_schema", fieldName]).toJS();
vega.extend(bindField, opts); // Aggregate or Bin passed in opts.
props.bindChannel(dsId, bindField, lyraId, cell.key);
} else {
// update the whole symbol/mark: shape, size
// the history has the signals
let relevantProps = ["size", "shape", "fill", "fillOpacity", "stroke", "strokeWidth"];
let historyMark = this.props.history.getIn(["marks", String(lyraId)]); // assume mark is the same as before. wanna revert to old settings of old mark

batchGroupBy.start();
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
relevantProps.forEach((prop) => {
let historyProp = historyMark.getIn(["encode", "update", prop]);
if (historyProp.signal) {
let historySigVal = this.props.history.getIn(["signals", historyProp.signal]).value;
this.props.setSignal(historySigVal, historyProp.signal); // update Store
sg.set(historyProp.signal, historySigVal, false); // update Vega
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
}
});
batchGroupBy.end();
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
}
} catch (e) {
console.error('Unable to bind primitive');
console.error(e);
}

this.props.stopDragging();
Comment thread
NanaKwame marked this conversation as resolved.
sg.set(MODE, 'handles');
sg.set(CELL, {});

ctrl.update(); // Apply changes

}

private historyToSpec(preview: HistoryRecord): Spec {
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
let historySpec = ctrl.export(false, this.props.history);
if (historySpec.marks) {
historySpec = cleanSpecForPreview(historySpec, this.width, this.height, null, parseInt(this.props.id), true);
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
}

return historySpec;
}

private view;


public componentDidMount() {
const spec = this.historyToSpec(this.props.history);
this.view = new View(parse(spec), {
renderer: 'svg', // renderer (canvas or svg)
container: `#history-test-${this.props.id}` // parent DOM container
});
this.view.width(this.width);
// this.view.signal("width", this.width);
this.view.height(this.height);
// this.view.signal("height", this.height);
this.view.runAsync();
}

public render() {

return (
<div id={`history-test-${this.props.id}`}
className={"history-preview"}
draggable={true}
key={this.props.id}
onClick={() => this.handleClick(parseInt(this.props.id))}
onDragStart={() => this.handleDragStart(parseInt(this.props.id))}
onDragEnd={this.handleDragEnd}></div>
);

}

}

export const HistoryItem = connect(
null,
mapDispatchToProps
)(HistoryItemInspector);
56 changes: 56 additions & 0 deletions src/js/components/history/HistoryList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {Map} from 'immutable';
import * as React from 'react';
import {connect} from 'react-redux';
import {updateHistoryProperty} from '../../actions/historyActions';
import {State} from '../../store';
import {HistoryItem} from './HistoryItem';
import {MarkRecord} from '../../store/factory/Mark';

const getIn = require('../../util/immutable-utils').getIn;

interface OwnProps {
}
interface StateProps {
history: any[];
groupNames: string[];
}

interface DispatchProps {
updateHistoryProperty: (payload: {property: string, value: any}, id: number) => void;
}

function mapState(state: State): StateProps {
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
const marks: Map<string, MarkRecord> = state.getIn(['vis', 'present', 'marks']);
const groupNames = marks.filter((mark: MarkRecord) => {
return mark.type === 'group';
}).map((v) => {
return v.name.replace(" ", "_");
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
}).toList().toJSON();
let history = [...getIn(state, 'vis').past];
history.push(getIn(state, 'vis').present);
return {
history,
groupNames: groupNames
};
}

const mapDispatch: DispatchProps = {
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
updateHistoryProperty
};

class BaseHistoryList extends React.Component<OwnProps & StateProps & DispatchProps> {
public render() {

return (
<div id='history-list' >
{this.props.history.map(
(item, idx) => {
return <HistoryItem id={idx+''} key={idx+''} history={item} groupNames={this.props.groupNames} />
Comment thread
NanaKwame marked this conversation as resolved.
Outdated
}
)}
</div>
);
}
}

export const HistoryList = connect(mapState, mapDispatch)(BaseHistoryList);
2 changes: 2 additions & 0 deletions src/js/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {store} from '../store';
import {EncodingsSidebar} from './EncodingsSidebar';
import {InspectorSidebar} from './InspectorSidebar';
import {PipelinesToolbar} from './PipelinesSidebar';
import {HistoryToolbar} from './HistoryToolbar';
import {Toolbar} from './Toolbar';
import WidgetDropzone from './interactions/WidgetDropzone';

Expand All @@ -27,6 +28,7 @@ module.exports = ReactDOM.render(
</div>
<Toolbar />
</div>
<HistoryToolbar />
</div>

<ReactTooltip
Expand Down
2 changes: 1 addition & 1 deletion src/js/components/interactions/InteractionPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class InteractionPreview extends React.Component<OwnProps> {

private previewToSpec(preview: SelectionRecord | ApplicationRecord): Spec {
const groupName = (preview as TransformApplicationRecord).targetGroupName || this.props.groupName;
let spec = cleanSpecForPreview(ctrl.export(false), this.width, this.height, groupName, this.props.interaction.id);
let spec = cleanSpecForPreview(ctrl.export(false), this.width, this.height, groupName, this.props.interaction.id, false);
Comment thread
NanaKwame marked this conversation as resolved.
Outdated

switch (preview.type) {
case 'point':
Expand Down
2 changes: 1 addition & 1 deletion src/js/components/interactions/WidgetPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class WidgetPreview extends React.Component<OwnProps> {

private previewToSpec(preview: MarkApplicationRecord): Spec {
const groupName = this.props.groupName;
let spec = cleanSpecForPreview(ctrl.export(false), this.width, this.height, groupName, this.props.widget.id);
let spec = cleanSpecForPreview(ctrl.export(false), this.width, this.height, groupName, this.props.widget.id, false);
Comment thread
NanaKwame marked this conversation as resolved.
Outdated

if (this.props.widget.selection) {
spec = addWidgetSelectionToScene(spec, this.props.widget, this.props.widget.selection);
Expand Down
Loading