From 194905660088bfa14d7b2b39fd00e7f569e54512 Mon Sep 17 00:00:00 2001 From: ktbacher Date: Fri, 16 Apr 2021 17:48:14 -0400 Subject: [PATCH 01/13] basic UI for faceting dropzones --- src/js/components/index.tsx | 2 + src/js/components/pipelines/FacetDropzone.tsx | 64 +++++++++++++++++++ .../pipelines/FacetOptionsHolder.tsx | 33 ++++++++++ src/scss/scaffold.scss | 15 +++++ 4 files changed, 114 insertions(+) create mode 100644 src/js/components/pipelines/FacetDropzone.tsx create mode 100644 src/js/components/pipelines/FacetOptionsHolder.tsx diff --git a/src/js/components/index.tsx b/src/js/components/index.tsx index e162914c..2d3023f6 100644 --- a/src/js/components/index.tsx +++ b/src/js/components/index.tsx @@ -10,6 +10,7 @@ import {Toolbar} from './Toolbar'; import WidgetDropzone from './interactions/WidgetDropzone'; import MarkDropzoneGroup from './toolbar/MarkDropzoneGroup'; import MarkDropPlaceGroup from './toolbar/MarkDropPlaceGroup'; +import FacetOptionsHolder from './pipelines/FacetOptionsHolder'; // React requires you only have one wrapper element called in your provider module.exports = ReactDOM.render( @@ -28,6 +29,7 @@ module.exports = ReactDOM.render( + diff --git a/src/js/components/pipelines/FacetDropzone.tsx b/src/js/components/pipelines/FacetDropzone.tsx new file mode 100644 index 00000000..86f85e90 --- /dev/null +++ b/src/js/components/pipelines/FacetDropzone.tsx @@ -0,0 +1,64 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import {State} from '../../store'; +import {FieldDraggingStateRecord} from '../../store/factory/Inspector'; +import {getClosestGroupId} from '../../util/hierarchy'; +const ctrl = require('../../ctrl'); +interface StateProps { + dragging: FieldDraggingStateRecord; + groupId: number; +} + +interface OwnProps { + layoutOrientation: string +} +interface DispatchProps { + facetField: (payload) => void; +} + +function mapStateToProps(state: State): StateProps { + const groupId = getClosestGroupId(); + + const draggingRecord = state.getIn(['inspector', 'dragging']); + const isFieldDrag = draggingRecord && (draggingRecord as FieldDraggingStateRecord).dsId; + + return { + dragging: isFieldDrag ? draggingRecord : null, + groupId + }; +} + +function mapDispatchToProps(dispatch, ownProps: OwnProps): DispatchProps { + return { + facetField: () => { + + } + } +} + +class FacetDropzone extends React.Component { + + public handleDragOver = (evt) => { + if (evt.preventDefault) { + evt.preventDefault(); + } + + return false; + }; + + public handleDrop = () => { + console.log("field dropped, layout orientation", this.props.layoutOrientation); + }; + + public render() { + if (!this.props.dragging) return null; + return ( +
this.handleDragOver(e)} onDrop={() => this.handleDrop()}> +
Facet {this.props.layoutOrientation}
+
+ ); + } + +} + +export default connect(mapStateToProps, mapDispatchToProps)(FacetDropzone); diff --git a/src/js/components/pipelines/FacetOptionsHolder.tsx b/src/js/components/pipelines/FacetOptionsHolder.tsx new file mode 100644 index 00000000..32764923 --- /dev/null +++ b/src/js/components/pipelines/FacetOptionsHolder.tsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import { connect } from 'react-redux'; +import {State} from '../../store'; +import FacetDropzone from './FacetDropzone'; + +const layoutOrientaions = ['Row', 'Column']; +interface StateProps { + layouts: number[]; +} + +function mapStateToProps(state: State): StateProps { + const layoutList = state.getIn(['vis', 'present', 'layouts']); + return { + layouts: Array.from(layoutList.keys()) + }; +} + +class FacetOptionsHolder extends React.Component { + public render() { + + return ( +
+ {layoutOrientaions.map(function(dir,i) { + return ( + + ); + }, this)} +
+ )} + +} + +export default connect(mapStateToProps, null)(FacetOptionsHolder); \ No newline at end of file diff --git a/src/scss/scaffold.scss b/src/scss/scaffold.scss index c6a198ab..eba0d544 100644 --- a/src/scss/scaffold.scss +++ b/src/scss/scaffold.scss @@ -117,6 +117,21 @@ body { transform-origin: top left; } + .facet-container { + // position: i; + display: flex; + flex-direction: row; + } + + .facet-dropzone { + border: 1px solid #333333; + box-shadow: 0 0 0 3px lightgray; + background-color: lightgray; + padding: $outer-padding; + border-radius: 3px; + margin: $outer-padding 5px; + } + .vega-bindings { margin-top: $outer-padding; .vega-bind-name { From 21394eec108c43b655b6ff1bd2e143d47c78adf3 Mon Sep 17 00:00:00 2001 From: ktbacher Date: Thu, 22 Apr 2021 23:07:35 -0400 Subject: [PATCH 02/13] add facet layout store actions reducer --- src/js/actions/facetLayoutActions.ts | 14 ++++++++ src/js/reducers/facetLayoutsReducer.ts | 24 ++++++++++++++ src/js/reducers/index.ts | 4 ++- src/js/store/factory/FacetLayout.ts | 46 ++++++++++++++++++++++++++ src/js/store/index.ts | 5 ++- 5 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 src/js/actions/facetLayoutActions.ts create mode 100644 src/js/reducers/facetLayoutsReducer.ts create mode 100644 src/js/store/factory/FacetLayout.ts diff --git a/src/js/actions/facetLayoutActions.ts b/src/js/actions/facetLayoutActions.ts new file mode 100644 index 00000000..5cacef23 --- /dev/null +++ b/src/js/actions/facetLayoutActions.ts @@ -0,0 +1,14 @@ +import {createStandardAction} from 'typesafe-actions'; +import {FacetLayoutRecord} from '../store/factory/FacetLayout'; +import {assignId} from '../util/counter'; +import {State} from '../store'; +import {Dispatch} from 'redux'; + +export function addFacetLayout (payload: FacetLayoutRecord) { + return function(dispatch: Dispatch, getState: () => State) { + const id = payload._id || assignId(dispatch, getState()); + dispatch(baseAddFacetLayout(payload.merge({_id: id}), id)); + }; +} + +export const baseAddFacetLayout = createStandardAction('ADD_FACET_LAYOUT')(); \ No newline at end of file diff --git a/src/js/reducers/facetLayoutsReducer.ts b/src/js/reducers/facetLayoutsReducer.ts new file mode 100644 index 00000000..d7cf2dea --- /dev/null +++ b/src/js/reducers/facetLayoutsReducer.ts @@ -0,0 +1,24 @@ +import {Map} from 'immutable'; +import {ActionType, getType} from 'typesafe-actions'; +import {FacetLayoutState} from '../store/factory/FacetLayout'; +import * as FacetLayoutActions from '../actions/FacetLayoutActions'; + +/** + * This reducer handles layout updates + * @param {Object} state - An Immutable state object + * @param {Object} action - An action object + */ +export function facetLayoutsReducer(state: FacetLayoutState, + action: ActionType): FacetLayoutState { + const id = String(action.meta); + + if (typeof state === 'undefined') { + return Map(); + } + + if (action.type === getType(FacetLayoutActions.baseAddFacetLayout)) { + return state.set(id, action.payload); + } + + return state; +} \ No newline at end of file diff --git a/src/js/reducers/index.ts b/src/js/reducers/index.ts index 11798f77..6bd8c08f 100644 --- a/src/js/reducers/index.ts +++ b/src/js/reducers/index.ts @@ -17,6 +17,7 @@ import {invalidateVegaReducer as vega} from './vegaReducer'; import {lyraGlobalsReducer as lyra} from './lyraReducer'; import {walkthroughReducer as walkthrough} from './walkthroughReducer'; import {layoutsReducer as layouts} from './layoutsReducer'; +import {facetLayoutsReducer as facetLayouts} from './facetLayoutsReducer'; const visReducers = combineReducers({ signals, @@ -28,7 +29,8 @@ const visReducers = combineReducers({ marks, interactions, widgets, - layouts + layouts, + facetLayouts }); // order matters here diff --git a/src/js/store/factory/FacetLayout.ts b/src/js/store/factory/FacetLayout.ts new file mode 100644 index 00000000..63d8db4a --- /dev/null +++ b/src/js/store/factory/FacetLayout.ts @@ -0,0 +1,46 @@ +import {Map, Record, RecordOf} from 'immutable'; + +/** + * Layouts align multiple groups + */ +export interface FacetLayout { + /** + * The Lyra ID of this vega layout. + */ + _id: number; + /** + * Name of this layout. + */ + name: string; + /** + * Number of columns in this layout. + */ + columns: number; + /** + * Spacing between groups in this layout. + */ + padding: number; + /** + * Bounds for this layout. + */ + bounds: string; + /** + * Group alignment for this layout. + */ + align: string; + +} + +export const FacetLayout = Record({ + _id: null, + name: "", + columns: 0, + padding: 30, + bounds: "full", + align: "all" +}, 'VegaLayout'); + +export type FacetLayoutRecord = RecordOf; + +export type FacetLayoutState = Map; + diff --git a/src/js/store/index.ts b/src/js/store/index.ts index 311a1264..2531c8a7 100644 --- a/src/js/store/index.ts +++ b/src/js/store/index.ts @@ -7,6 +7,7 @@ import {DatasetRecord} from './factory/Dataset'; import {GuideRecord} from './factory/Guide'; import {Hints, HintsRecord} from './factory/Hints'; import {LayoutRecord} from './factory/Layout'; +import {FacetLayoutRecord} from './factory/FacetLayout'; import {Inspector, InspectorRecord} from './factory/Inspector'; import {InteractionRecord} from './factory/Interaction'; import {MarkRecord} from './factory/Mark'; @@ -20,7 +21,8 @@ import {LyraGlobalsRecord, LyraGlobals} from './factory/Lyra'; export type VisStateTree = Map>; + MarkRecord | SignalRecord | InteractionRecord | WidgetRecord | + LayoutRecord | FacetLayoutRecord>>; export interface VisState { past: VisStateTree[]; @@ -52,6 +54,7 @@ const getDefaultState = Record({ widgets: Map(), signals: defaultSignalState, layouts: Map(), + facetLayouts: Map(), }), future: [] }, From bde9678421ddfe6e43aa9404511bd23cdf5e62f6 Mon Sep 17 00:00:00 2001 From: ktbacher Date: Fri, 23 Apr 2021 00:03:36 -0400 Subject: [PATCH 03/13] group facet store, action, reducer, and called in facet dropzone --- src/js/actions/markActions.ts | 4 ++-- src/js/components/pipelines/FacetDropzone.tsx | 20 ++++++++++++++----- src/js/reducers/marksReducer.ts | 4 ++++ src/js/store/factory/FacetLayout.ts | 9 ++------- src/js/store/factory/marks/Group.ts | 12 +++++++++++ 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/js/actions/markActions.ts b/src/js/actions/markActions.ts index 89a72890..8651a578 100644 --- a/src/js/actions/markActions.ts +++ b/src/js/actions/markActions.ts @@ -6,7 +6,7 @@ import {UnitSpec} from 'vega-lite/src/spec'; import {batchGroupBy} from '../reducers/historyOptions'; import {State} from '../store'; import {LyraMarkType, Mark, MarkRecord, HandleStreams} from '../store/factory/Mark'; -import {GroupRecord} from '../store/factory/marks/Group'; +import {LyraGroupFacet, GroupRecord} from '../store/factory/marks/Group'; import {addGrouptoLayout} from './layoutActions'; import {assignId} from '../util/counter'; import {ThunkDispatch} from 'redux-thunk'; @@ -53,7 +53,7 @@ export function addGroup(record: GroupRecord, layoutId: number, dir: string) { } export const baseAddMark = createStandardAction('ADD_MARK')<{name: string, streams: HandleStreams, props: MarkRecord}, number>(); - +export const addGroupFacet = createStandardAction('ADD_GROUP_FACET')(); // number of Group ID export const updateMarkProperty = createStandardAction('UPDATE_MARK_PROPERTY')<{property: string, value: any}, number>(); export const setParent = createStandardAction('SET_PARENT_MARK')(); // parentId, childId diff --git a/src/js/components/pipelines/FacetDropzone.tsx b/src/js/components/pipelines/FacetDropzone.tsx index 86f85e90..159bb943 100644 --- a/src/js/components/pipelines/FacetDropzone.tsx +++ b/src/js/components/pipelines/FacetDropzone.tsx @@ -2,8 +2,11 @@ import * as React from 'react'; import { connect } from 'react-redux'; import {State} from '../../store'; import {FieldDraggingStateRecord} from '../../store/factory/Inspector'; +import {GroupFacet} from "../../store/factory/marks/Group"; import {getClosestGroupId} from '../../util/hierarchy'; -const ctrl = require('../../ctrl'); +import {addFacetLayout} from '../../actions/facetLayoutActions'; +import {addGroupFacet} from '../../actions/markActions'; +import {FacetLayout} from '../../store/factory/FacetLayout'; interface StateProps { dragging: FieldDraggingStateRecord; groupId: number; @@ -13,7 +16,7 @@ interface OwnProps { layoutOrientation: string } interface DispatchProps { - facetField: (payload) => void; + facetField: (field: string, groupId: number) => void; } function mapStateToProps(state: State): StateProps { @@ -30,8 +33,15 @@ function mapStateToProps(state: State): StateProps { function mapDispatchToProps(dispatch, ownProps: OwnProps): DispatchProps { return { - facetField: () => { - + facetField: (field, groupId) => { + let numCols; + if (ownProps.layoutOrientation == "vertical") { + numCols = 1; + } else { + numCols = null; + } + dispatch(addFacetLayout(FacetLayout({columns: numCols}))); + dispatch(addGroupFacet(GroupFacet({name: "facet", data: "cars_source_5", groupby: [field]}), groupId)); // remove hardcoded data name } } } @@ -47,7 +57,7 @@ class FacetDropzone extends React.Component { - console.log("field dropped, layout orientation", this.props.layoutOrientation); + this.props.facetField(this.props.dragging.fieldDef.name, this.props.groupId); }; public render() { diff --git a/src/js/reducers/marksReducer.ts b/src/js/reducers/marksReducer.ts index 0dfa6a11..4306bad9 100644 --- a/src/js/reducers/marksReducer.ts +++ b/src/js/reducers/marksReducer.ts @@ -250,6 +250,10 @@ export function marksReducer( return ensureValuePresentImmutable(state, [String(groupId), '_widgets'], action.payload); } + if (action.type == getType(markActions.addGroupFacet)) { + return state.setIn([String(groupId), "from"], action.payload); + } + const id = action.meta; if (action.type === getType(guideActions.deleteGuide)) { diff --git a/src/js/store/factory/FacetLayout.ts b/src/js/store/factory/FacetLayout.ts index 63d8db4a..a1849fac 100644 --- a/src/js/store/factory/FacetLayout.ts +++ b/src/js/store/factory/FacetLayout.ts @@ -8,10 +8,6 @@ export interface FacetLayout { * The Lyra ID of this vega layout. */ _id: number; - /** - * Name of this layout. - */ - name: string; /** * Number of columns in this layout. */ @@ -33,12 +29,11 @@ export interface FacetLayout { export const FacetLayout = Record({ _id: null, - name: "", - columns: 0, + columns: null, padding: 30, bounds: "full", align: "all" -}, 'VegaLayout'); +}, 'FacetLayout'); export type FacetLayoutRecord = RecordOf; diff --git a/src/js/store/factory/marks/Group.ts b/src/js/store/factory/marks/Group.ts index c26513b8..ef61220c 100644 --- a/src/js/store/factory/marks/Group.ts +++ b/src/js/store/factory/marks/Group.ts @@ -49,3 +49,15 @@ export const Group = Record({ }, 'LyraGroupMark'); export type GroupRecord = RecordOf; + +export type LyraGroupFacet = { + name: string, + data: string, // refers to dataset name + groupby: string[], // string of field +} + +export const GroupFacet = Record({ + name: "", + data: "", // refers to dataset name + groupby: [], +}, 'LyraGroupFacet'); \ No newline at end of file From f0671b6389b4fee9237c51a70fde9617ccf349f4 Mon Sep 17 00:00:00 2001 From: ktbacher Date: Fri, 23 Apr 2021 17:11:35 -0400 Subject: [PATCH 04/13] corrected group and mark faceting --- src/js/actions/markActions.ts | 17 +++++++++++-- src/js/components/pipelines/FacetDropzone.tsx | 8 ++++--- src/js/ctrl/export.ts | 6 ++++- src/js/reducers/facetLayoutsReducer.ts | 2 +- src/js/reducers/marksReducer.ts | 7 +++++- src/js/store/factory/Mark.ts | 4 +--- src/js/store/factory/marks/Area.ts | 1 + src/js/store/factory/marks/Group.ts | 24 +++++++++++-------- src/js/store/factory/marks/Line.ts | 4 ++-- src/js/store/factory/marks/Rect.ts | 1 + src/js/store/factory/marks/Symbol.ts | 1 + src/js/store/factory/marks/Text.ts | 1 + 12 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/js/actions/markActions.ts b/src/js/actions/markActions.ts index 8651a578..948a00e6 100644 --- a/src/js/actions/markActions.ts +++ b/src/js/actions/markActions.ts @@ -6,7 +6,8 @@ import {UnitSpec} from 'vega-lite/src/spec'; import {batchGroupBy} from '../reducers/historyOptions'; import {State} from '../store'; import {LyraMarkType, Mark, MarkRecord, HandleStreams} from '../store/factory/Mark'; -import {LyraGroupFacet, GroupRecord} from '../store/factory/marks/Group'; +import {GroupRecord} from '../store/factory/marks/Group'; +import {Facet} from 'vega-typings'; import {addGrouptoLayout} from './layoutActions'; import {assignId} from '../util/counter'; import {ThunkDispatch} from 'redux-thunk'; @@ -53,7 +54,19 @@ export function addGroup(record: GroupRecord, layoutId: number, dir: string) { } export const baseAddMark = createStandardAction('ADD_MARK')<{name: string, streams: HandleStreams, props: MarkRecord}, number>(); -export const addGroupFacet = createStandardAction('ADD_GROUP_FACET')(); // number of Group ID +export function addFacet(facet: Facet, groupId: number) { + return function(dispatch: ThunkDispatch, getState: () => State) { + batchGroupBy.start(); + dispatch(baseAddGroupFacet(facet, groupId)); + const childrenMarks = getState().getIn(['vis', 'present', 'marks', String(groupId), 'marks']); + childrenMarks.forEach(mark => { + dispatch(baseAddFacet(facet,mark)); + }); + batchGroupBy.end(); + }; +} +export const baseAddFacet = createStandardAction('ADD_FACET')(); // number of mark ID +export const baseAddGroupFacet = createStandardAction('ADD_GROUP_FACET')(); // number of Group ID export const updateMarkProperty = createStandardAction('UPDATE_MARK_PROPERTY')<{property: string, value: any}, number>(); export const setParent = createStandardAction('SET_PARENT_MARK')(); // parentId, childId diff --git a/src/js/components/pipelines/FacetDropzone.tsx b/src/js/components/pipelines/FacetDropzone.tsx index 159bb943..c8030eda 100644 --- a/src/js/components/pipelines/FacetDropzone.tsx +++ b/src/js/components/pipelines/FacetDropzone.tsx @@ -2,10 +2,11 @@ import * as React from 'react'; import { connect } from 'react-redux'; import {State} from '../../store'; import {FieldDraggingStateRecord} from '../../store/factory/Inspector'; -import {GroupFacet} from "../../store/factory/marks/Group"; +// import {GroupFacet} from "../../store/factory/marks/Group"; +import {Facet} from 'vega-typings'; import {getClosestGroupId} from '../../util/hierarchy'; import {addFacetLayout} from '../../actions/facetLayoutActions'; -import {addGroupFacet} from '../../actions/markActions'; +import {addFacet} from '../../actions/markActions'; import {FacetLayout} from '../../store/factory/FacetLayout'; interface StateProps { dragging: FieldDraggingStateRecord; @@ -41,7 +42,8 @@ function mapDispatchToProps(dispatch, ownProps: OwnProps): DispatchProps { numCols = null; } dispatch(addFacetLayout(FacetLayout({columns: numCols}))); - dispatch(addGroupFacet(GroupFacet({name: "facet", data: "cars_source_5", groupby: [field]}), groupId)); // remove hardcoded data name + // dispatch(addGroupFacet(GroupFacet({facet: {name: "facet", data: "cars_source_5", groupby: [field]}}), groupId)); // remove hardcoded data name + dispatch(addFacet({name: "facet",data: "5", groupby: field} as Facet, groupId)); } } } diff --git a/src/js/ctrl/export.ts b/src/js/ctrl/export.ts index 86c62bbf..84bf0852 100644 --- a/src/js/ctrl/export.ts +++ b/src/js/ctrl/export.ts @@ -216,12 +216,16 @@ exporter.mark = function(state: State, internal: boolean, id: number) { spec.from = {data: facet.name}; } else if (spec.from) { let fromId; - if ((fromId = spec.from.data)) { + if ((fromId = spec.from.name)) { + spec.from = {"data": fromId}; + } else if ((fromId = spec.from.data)) { spec.from.data = name(getInVis(state, 'datasets.' + fromId + '.name')); const count = counts.data[fromId] || (counts.data[fromId] = duplicate(DATA_COUNT)); count.marks[id] = true; } else if ((fromId = spec.from.mark)) { spec.from.mark = name(getInVis(state, 'marks.' + fromId + '.name')); + } else if ((fromId = spec.from.facet.data)) { + spec.from.facet.data = name(getInVis(state, 'datasets.' + fromId + '.name')); } } diff --git a/src/js/reducers/facetLayoutsReducer.ts b/src/js/reducers/facetLayoutsReducer.ts index d7cf2dea..d9bb8092 100644 --- a/src/js/reducers/facetLayoutsReducer.ts +++ b/src/js/reducers/facetLayoutsReducer.ts @@ -1,7 +1,7 @@ import {Map} from 'immutable'; import {ActionType, getType} from 'typesafe-actions'; import {FacetLayoutState} from '../store/factory/FacetLayout'; -import * as FacetLayoutActions from '../actions/FacetLayoutActions'; +import * as FacetLayoutActions from '../actions/facetLayoutActions'; /** * This reducer handles layout updates diff --git a/src/js/reducers/marksReducer.ts b/src/js/reducers/marksReducer.ts index 4306bad9..e4560e7f 100644 --- a/src/js/reducers/marksReducer.ts +++ b/src/js/reducers/marksReducer.ts @@ -250,7 +250,12 @@ export function marksReducer( return ensureValuePresentImmutable(state, [String(groupId), '_widgets'], action.payload); } - if (action.type == getType(markActions.addGroupFacet)) { + if (action.type == getType(markActions.baseAddGroupFacet)) { + return state.setIn([String(groupId), "from"], {facet: action.payload}); + } + + if (action.type == getType(markActions.baseAddFacet)) { + console.log("id " + String(groupId) + ", payload ", action.payload); return state.setIn([String(groupId), "from"], action.payload); } diff --git a/src/js/store/factory/Mark.ts b/src/js/store/factory/Mark.ts index f2d10816..e853f22a 100644 --- a/src/js/store/factory/Mark.ts +++ b/src/js/store/factory/Mark.ts @@ -34,11 +34,9 @@ export interface LyraMarkMeta { _id: number; _parent: number; _vlUnit: LyraVegaLiteSpec; + _facet: Facet; } -export interface LyraPathFacet { - _facet: Facet -} export type LyraMark = LyraAreaMark | LyraGroupMark | LyraLineMark | LyraRectMark | LyraSymbolMark | LyraTextMark; export type MarkRecord = AreaRecord | GroupRecord | LineRecord | RectRecord | SymbolRecord | TextRecord; diff --git a/src/js/store/factory/marks/Area.ts b/src/js/store/factory/marks/Area.ts index d1ba8ac2..04572e11 100644 --- a/src/js/store/factory/marks/Area.ts +++ b/src/js/store/factory/marks/Area.ts @@ -14,6 +14,7 @@ export const Area = Record({ _id: null, _parent: null, _vlUnit: null, + _facet: null, type: 'area', name: null, from: null, diff --git a/src/js/store/factory/marks/Group.ts b/src/js/store/factory/marks/Group.ts index ef61220c..d20a1e38 100644 --- a/src/js/store/factory/marks/Group.ts +++ b/src/js/store/factory/marks/Group.ts @@ -50,14 +50,18 @@ export const Group = Record({ export type GroupRecord = RecordOf; -export type LyraGroupFacet = { - name: string, - data: string, // refers to dataset name - groupby: string[], // string of field -} +// export type LyraGroupFacet = { +// facet: { +// name: string, +// data: string, // refers to dataset name +// groupby: any[], // string of field +// } +// } -export const GroupFacet = Record({ - name: "", - data: "", // refers to dataset name - groupby: [], -}, 'LyraGroupFacet'); \ No newline at end of file +// export const GroupFacet = Record({ +// facet: { +// name: "", +// data: "", // refers to dataset name +// groupby: [], +// } +// }, 'LyraGroupFacet'); \ No newline at end of file diff --git a/src/js/store/factory/marks/Line.ts b/src/js/store/factory/marks/Line.ts index b5b227e3..45e95b6f 100644 --- a/src/js/store/factory/marks/Line.ts +++ b/src/js/store/factory/marks/Line.ts @@ -3,10 +3,10 @@ import {LineMark} from 'vega-typings'; import anchorTarget from '../../../util/anchor-target'; import {propSg} from '../../../util/prop-signal'; import test from '../../../util/test-if'; -import {HandleStreams, LyraMarkMeta, LyraPathFacet} from '../Mark'; +import {HandleStreams, LyraMarkMeta} from '../Mark'; import {DELTA} from '../Signal'; -export type LyraLineMark = LyraMarkMeta & LineMark & LyraPathFacet; +export type LyraLineMark = LyraMarkMeta & LineMark; export const Line = Record({ _id: null, diff --git a/src/js/store/factory/marks/Rect.ts b/src/js/store/factory/marks/Rect.ts index c792018d..587a9c6f 100644 --- a/src/js/store/factory/marks/Rect.ts +++ b/src/js/store/factory/marks/Rect.ts @@ -13,6 +13,7 @@ export const Rect = Record({ _id: null, _parent: null, _vlUnit: null, + _facet: null, type: 'rect', name: null, from: null, diff --git a/src/js/store/factory/marks/Symbol.ts b/src/js/store/factory/marks/Symbol.ts index 4cede943..65cb5070 100644 --- a/src/js/store/factory/marks/Symbol.ts +++ b/src/js/store/factory/marks/Symbol.ts @@ -13,6 +13,7 @@ export const Symbol = Record({ _id: null, _parent: null, _vlUnit: null, + _facet: null, type: 'symbol', name: null, from: null, diff --git a/src/js/store/factory/marks/Text.ts b/src/js/store/factory/marks/Text.ts index 84a13646..caef388a 100644 --- a/src/js/store/factory/marks/Text.ts +++ b/src/js/store/factory/marks/Text.ts @@ -12,6 +12,7 @@ export const Text = Record({ _id: null, _parent: null, _vlUnit: null, + _facet: null, type: 'text', name: null, from: null, From a1b3a83f68259de7e5eebb0017fb351239024484 Mon Sep 17 00:00:00 2001 From: ktbacher Date: Fri, 23 Apr 2021 17:15:57 -0400 Subject: [PATCH 05/13] remove unused group facet --- src/js/store/factory/marks/Group.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/js/store/factory/marks/Group.ts b/src/js/store/factory/marks/Group.ts index d20a1e38..fb2a5649 100644 --- a/src/js/store/factory/marks/Group.ts +++ b/src/js/store/factory/marks/Group.ts @@ -48,20 +48,4 @@ export const Group = Record({ } }, 'LyraGroupMark'); -export type GroupRecord = RecordOf; - -// export type LyraGroupFacet = { -// facet: { -// name: string, -// data: string, // refers to dataset name -// groupby: any[], // string of field -// } -// } - -// export const GroupFacet = Record({ -// facet: { -// name: "", -// data: "", // refers to dataset name -// groupby: [], -// } -// }, 'LyraGroupFacet'); \ No newline at end of file +export type GroupRecord = RecordOf; \ No newline at end of file From d05fcc126bd34795511bc7d26373375a45c84838 Mon Sep 17 00:00:00 2001 From: ktbacher Date: Fri, 23 Apr 2021 18:30:16 -0400 Subject: [PATCH 06/13] update exporter with facet layout --- src/js/components/pipelines/FacetDropzone.tsx | 2 +- src/js/ctrl/export.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/js/components/pipelines/FacetDropzone.tsx b/src/js/components/pipelines/FacetDropzone.tsx index c8030eda..39d1ec6e 100644 --- a/src/js/components/pipelines/FacetDropzone.tsx +++ b/src/js/components/pipelines/FacetDropzone.tsx @@ -36,7 +36,7 @@ function mapDispatchToProps(dispatch, ownProps: OwnProps): DispatchProps { return { facetField: (field, groupId) => { let numCols; - if (ownProps.layoutOrientation == "vertical") { + if (ownProps.layoutOrientation == "Column") { numCols = 1; } else { numCols = null; diff --git a/src/js/ctrl/export.ts b/src/js/ctrl/export.ts index 84bf0852..7e860323 100644 --- a/src/js/ctrl/export.ts +++ b/src/js/ctrl/export.ts @@ -49,9 +49,21 @@ export function exporter(internal: boolean = false): Spec { // Add interactions and widgets from store spec = exporter.interactions(state, spec); spec = exporter.widgets(state, spec); + + if (state.getIn(['vis', 'present', 'facetLayouts']).size > 0){ + spec.layout = exporter.layouts(state, int); + } return spec; } +exporter.layouts = function (state: State, internal: boolean) { + const facetLayouts = state.getIn(['vis', 'present', 'facetLayouts']); + const layout = clean(duplicate(facetLayouts), internal); + console.log("layout keys", Object.keys(layout)[Object.keys(layout).length -1]); + const id = Object.keys(layout)[Object.keys(layout).length -1]; + return layout[id]; +} + exporter.interactions = function(state: State, spec) { state.getIn(['vis', 'present', 'interactions']).forEach((interaction: InteractionRecord) => { const group: GroupRecord = state.getIn(['vis', 'present', 'marks', String(interaction.groupId)]); From 26ea74e694b08f75164d05b10b99f1ec23b735a4 Mon Sep 17 00:00:00 2001 From: ktbacher Date: Mon, 26 Apr 2021 10:15:29 -0400 Subject: [PATCH 07/13] rerender view on facet --- src/js/ctrl/export.ts | 1 - src/js/reducers/marksReducer.ts | 1 - src/js/reducers/vegaReducer.ts | 1 + 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/ctrl/export.ts b/src/js/ctrl/export.ts index 7e860323..2aa029ad 100644 --- a/src/js/ctrl/export.ts +++ b/src/js/ctrl/export.ts @@ -59,7 +59,6 @@ export function exporter(internal: boolean = false): Spec { exporter.layouts = function (state: State, internal: boolean) { const facetLayouts = state.getIn(['vis', 'present', 'facetLayouts']); const layout = clean(duplicate(facetLayouts), internal); - console.log("layout keys", Object.keys(layout)[Object.keys(layout).length -1]); const id = Object.keys(layout)[Object.keys(layout).length -1]; return layout[id]; } diff --git a/src/js/reducers/marksReducer.ts b/src/js/reducers/marksReducer.ts index e4560e7f..79959c7a 100644 --- a/src/js/reducers/marksReducer.ts +++ b/src/js/reducers/marksReducer.ts @@ -255,7 +255,6 @@ export function marksReducer( } if (action.type == getType(markActions.baseAddFacet)) { - console.log("id " + String(groupId) + ", payload ", action.payload); return state.setIn([String(groupId), "from"], action.payload); } diff --git a/src/js/reducers/vegaReducer.ts b/src/js/reducers/vegaReducer.ts index 31ed8af0..5e7b3f72 100644 --- a/src/js/reducers/vegaReducer.ts +++ b/src/js/reducers/vegaReducer.ts @@ -76,6 +76,7 @@ export function invalidateVegaReducer(state: VegaReparseRecord, case getType(datasetActions.sortDataset): case getType(datasetActions.addTransform): case getType(datasetActions.updateTransform): + case getType(markActions.baseAddFacet): case getType(hydrate): case historyActions.UNDO: case historyActions.REDO: From 2b26d536c94e27e8eb75df6f231a6cce95259c5c Mon Sep 17 00:00:00 2001 From: ktbacher Date: Fri, 30 Apr 2021 14:44:08 -0400 Subject: [PATCH 08/13] don't overwrite facet data --- src/js/actions/bindChannel/parseMarks.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/js/actions/bindChannel/parseMarks.ts b/src/js/actions/bindChannel/parseMarks.ts index a468ffed..9b3583b3 100644 --- a/src/js/actions/bindChannel/parseMarks.ts +++ b/src/js/actions/bindChannel/parseMarks.ts @@ -44,6 +44,9 @@ export default function parseMarks(dispatch: Dispatch, state: State, parsed: Com bindProperty(dispatch, parsed, def.encode.update); } + const parentID = state.getIn(['vis', 'present', 'marks', String(markId), '_parent']); + const isFacetMark = parentID && state.getIn(['vis', 'present', 'marks', String(parentID), 'from', 'facet']); + if (pathgroup) { dispatch(updateMarkProperty({ property: '_facet', @@ -52,7 +55,7 @@ export default function parseMarks(dispatch: Dispatch, state: State, parsed: Com data: map.data[pathgroup.from.facet.data] } }, markId)); - } else if (def.from && def.from.data) { + } else if (!isFacetMark && def.from && def.from.data) { dispatch(updateMarkProperty({property: 'from', value: {data: map.data[def.from.data]}}, markId)); } } From 10ebfda577ee688738a3fe9f4c24c7d82b4c9887 Mon Sep 17 00:00:00 2001 From: ktbacher Date: Fri, 7 May 2021 13:52:44 -0400 Subject: [PATCH 09/13] grabbed dataset id --- src/js/components/pipelines/FacetDropzone.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/components/pipelines/FacetDropzone.tsx b/src/js/components/pipelines/FacetDropzone.tsx index 39d1ec6e..efc471cd 100644 --- a/src/js/components/pipelines/FacetDropzone.tsx +++ b/src/js/components/pipelines/FacetDropzone.tsx @@ -17,7 +17,7 @@ interface OwnProps { layoutOrientation: string } interface DispatchProps { - facetField: (field: string, groupId: number) => void; + facetField: (field: string, datasetId: number, groupId: number) => void; } function mapStateToProps(state: State): StateProps { @@ -34,7 +34,7 @@ function mapStateToProps(state: State): StateProps { function mapDispatchToProps(dispatch, ownProps: OwnProps): DispatchProps { return { - facetField: (field, groupId) => { + facetField: (field, datasetId, groupId) => { let numCols; if (ownProps.layoutOrientation == "Column") { numCols = 1; @@ -43,7 +43,7 @@ function mapDispatchToProps(dispatch, ownProps: OwnProps): DispatchProps { } dispatch(addFacetLayout(FacetLayout({columns: numCols}))); // dispatch(addGroupFacet(GroupFacet({facet: {name: "facet", data: "cars_source_5", groupby: [field]}}), groupId)); // remove hardcoded data name - dispatch(addFacet({name: "facet",data: "5", groupby: field} as Facet, groupId)); + dispatch(addFacet({name: "facet",data: String(datasetId), groupby: field} as Facet, groupId)); } } } @@ -59,7 +59,7 @@ class FacetDropzone extends React.Component { - this.props.facetField(this.props.dragging.fieldDef.name, this.props.groupId); + this.props.facetField(this.props.dragging.fieldDef.name, this.props.dragging.dsId, this.props.groupId); }; public render() { From ae32532701c8c20ec904e093ac9d3a63487e2bf8 Mon Sep 17 00:00:00 2001 From: ktbacher Date: Mon, 10 May 2021 21:40:25 -0400 Subject: [PATCH 10/13] reformat if statement --- src/js/ctrl/export.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/js/ctrl/export.ts b/src/js/ctrl/export.ts index 2aa029ad..7cd041c5 100644 --- a/src/js/ctrl/export.ts +++ b/src/js/ctrl/export.ts @@ -227,15 +227,19 @@ exporter.mark = function(state: State, internal: boolean, id: number) { spec.from = {data: facet.name}; } else if (spec.from) { let fromId; - if ((fromId = spec.from.name)) { + if (spec.from.name) { + fromId = spec.from.name; spec.from = {"data": fromId}; - } else if ((fromId = spec.from.data)) { + } else if ( spec.from.data) { + fromId = spec.from.data; spec.from.data = name(getInVis(state, 'datasets.' + fromId + '.name')); const count = counts.data[fromId] || (counts.data[fromId] = duplicate(DATA_COUNT)); count.marks[id] = true; - } else if ((fromId = spec.from.mark)) { + } else if (spec.from.mark) { + fromId = spec.from.mark; spec.from.mark = name(getInVis(state, 'marks.' + fromId + '.name')); - } else if ((fromId = spec.from.facet.data)) { + } else if (spec.from.facet.data) { + fromId = spec.from.facet.data; spec.from.facet.data = name(getInVis(state, 'datasets.' + fromId + '.name')); } } From 21abfcedf20a3ec4a3d3a6015b51af879031a15e Mon Sep 17 00:00:00 2001 From: ktbacher Date: Wed, 9 Jun 2021 22:16:58 -0400 Subject: [PATCH 11/13] create wrapper group for facet layout --- src/js/ctrl/export.ts | 26 ++++++++++++++++---------- src/js/store/factory/FacetLayout.ts | 2 +- src/js/store/index.ts | 2 +- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/js/ctrl/export.ts b/src/js/ctrl/export.ts index 7cd041c5..3a27a1b9 100644 --- a/src/js/ctrl/export.ts +++ b/src/js/ctrl/export.ts @@ -50,18 +50,10 @@ export function exporter(internal: boolean = false): Spec { spec = exporter.interactions(state, spec); spec = exporter.widgets(state, spec); - if (state.getIn(['vis', 'present', 'facetLayouts']).size > 0){ - spec.layout = exporter.layouts(state, int); - } + console.log("final spec", spec); return spec; } -exporter.layouts = function (state: State, internal: boolean) { - const facetLayouts = state.getIn(['vis', 'present', 'facetLayouts']); - const layout = clean(duplicate(facetLayouts), internal); - const id = Object.keys(layout)[Object.keys(layout).length -1]; - return layout[id]; -} exporter.interactions = function(state: State, spec) { state.getIn(['vis', 'present', 'interactions']).forEach((interaction: InteractionRecord) => { @@ -299,7 +291,7 @@ function pathgroup(state, marks, facet) { exporter.group = function(state: State, internal: boolean, id: number) { const mark: GroupRecord = getInVis(state, `marks.${id}`); - const spec = exporter.mark(state, internal, id); + let spec = exporter.mark(state, internal, id); const group = internal ? spec[0] : spec; ['scale', 'mark', 'axe', 'legend'].forEach(function(childType) { @@ -335,6 +327,20 @@ exporter.group = function(state: State, internal: boolean, id: number) { ); } + if (mark.from) { + if (mark.from.facet) { + const facetLayouts = state.getIn(['vis', 'present', 'facetLayouts']); + const layout = clean(facetLayouts.toJS(), internal); + spec = { + "type": "group", + "layout": layout, + "marks": [ + spec + ] + }; + } + } + return spec; }; diff --git a/src/js/store/factory/FacetLayout.ts b/src/js/store/factory/FacetLayout.ts index a1849fac..e5d9e786 100644 --- a/src/js/store/factory/FacetLayout.ts +++ b/src/js/store/factory/FacetLayout.ts @@ -37,5 +37,5 @@ export const FacetLayout = Record({ export type FacetLayoutRecord = RecordOf; -export type FacetLayoutState = Map; +export type FacetLayoutState = FacetLayoutRecord; diff --git a/src/js/store/index.ts b/src/js/store/index.ts index 2531c8a7..39228905 100644 --- a/src/js/store/index.ts +++ b/src/js/store/index.ts @@ -7,7 +7,7 @@ import {DatasetRecord} from './factory/Dataset'; import {GuideRecord} from './factory/Guide'; import {Hints, HintsRecord} from './factory/Hints'; import {LayoutRecord} from './factory/Layout'; -import {FacetLayoutRecord} from './factory/FacetLayout'; +import {FacetLayoutRecord, FacetLayout} from './factory/FacetLayout'; import {Inspector, InspectorRecord} from './factory/Inspector'; import {InteractionRecord} from './factory/Interaction'; import {MarkRecord} from './factory/Mark'; From 4449e6254b0bab97e160bc95757a82f67b947156 Mon Sep 17 00:00:00 2001 From: ktbacher Date: Wed, 9 Jun 2021 22:24:00 -0400 Subject: [PATCH 12/13] use facet id --- src/js/ctrl/export.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/js/ctrl/export.ts b/src/js/ctrl/export.ts index 3a27a1b9..5d1bd0f6 100644 --- a/src/js/ctrl/export.ts +++ b/src/js/ctrl/export.ts @@ -329,8 +329,12 @@ exporter.group = function(state: State, internal: boolean, id: number) { if (mark.from) { if (mark.from.facet) { - const facetLayouts = state.getIn(['vis', 'present', 'facetLayouts']); - const layout = clean(facetLayouts.toJS(), internal); + const facetLayouts = state.getIn(['vis', 'present', 'facetLayouts']).toJS(); + console.log("layouts", facetLayouts); + const id = Object.keys(facetLayouts)[Object.keys(facetLayouts).length -1]; //add facet id to groups instead + console.log("id", id) + const layout = clean(facetLayouts[id], internal); + console.log("layout", layout); spec = { "type": "group", "layout": layout, From db08fe8b900d74d16910ca5686847a7b3f77b7c3 Mon Sep 17 00:00:00 2001 From: Jonathan Zong Date: Thu, 10 Jun 2021 01:58:41 -0400 Subject: [PATCH 13/13] fix vega parse error --- src/js/ctrl/export.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/js/ctrl/export.ts b/src/js/ctrl/export.ts index 5d1bd0f6..d2c81787 100644 --- a/src/js/ctrl/export.ts +++ b/src/js/ctrl/export.ts @@ -338,9 +338,7 @@ exporter.group = function(state: State, internal: boolean, id: number) { spec = { "type": "group", "layout": layout, - "marks": [ - spec - ] + "marks": spec }; } }