color by categorical

This commit is contained in:
Colin Megill
2017-12-23 15:24:16 -08:00
parent bc07ebd23d
commit ff7183d938
4 changed files with 121 additions and 54 deletions

View File

@@ -8,38 +8,63 @@ import SectionHeader from "../framework/sectionHeader"
import Value from "./value";
import { alphabeticallySortedValues } from "./util";
const Category = ({metadataField, values}) => (
<div style={{
display: "flex",
alignItems: "baseline",
maxWidth: globals.maxControlsWidth,
}}>
<p style={{
flexShrink: 0,
width: 200,
textAlign: "right",
fontFamily: globals.accentFont,
fontStyle: "italic",
marginRight: 20,
}}>
{metadataField}:
</p>
<div>
{
_.map(alphabeticallySortedValues(values), (v, i) => {
return (
<Value
key={v}
metadataField={metadataField}
count={values[v]}
value={v}
i={i} />
)
})
}
</div>
</div>
)
@connect()
class Category extends React.Component {
handleColorChange() {
this.props.dispatch({
type: "color by categorical metadata",
colorAccessor: this.props.metadataField
})
}
render() {
return (
<div style={{
// display: "flex",
// alignItems: "baseline",
maxWidth: globals.maxControlsWidth,
}}>
<div style={{
display: "flex",
justifyContent: "space-between",
alignItems: "baseline"
}}>
<p style={{
// flexShrink: 0,
width: 200,
fontWeight: 500,
// textAlign: "right",
// fontFamily: globals.accentFont,
// fontStyle: "italic",
marginRight: 20,
}}>
{this.props.metadataField}:
</p>
<button
onClick={this.handleColorChange.bind(this)}
style={{
font: globals.accentFont,
fontStyle: "italic",
fontSize: 10
}}> as color scale </button>
</div>
<div>
{
_.map(alphabeticallySortedValues(this.props.values), (v, i) => {
return (
<Value
key={v}
metadataField={this.props.metadataField}
count={this.props.values[v]}
value={v}
i={i} />
)
})
}
</div>
</div>
)
}
}
@connect((state) => {
@@ -59,14 +84,22 @@ class Categories extends React.Component {
render () {
if (!this.props.ranges) return null
return (
<div style={{marginTop: 50}}>
<SectionHeader text="Categorical Metadata"/>
<div style={{
position: "fixed",
left: 40,
height: 200,
}}>
{
_.map(this.props.ranges, (value, key) => {
const isColorField = key.includes("color") || key.includes("Color");
if (
value.options &&
key !== "CellName"
key !== "CellName" &&
!isColorField
) {
return (
<Category
@@ -85,7 +118,7 @@ class Categories extends React.Component {
export default Categories;
/*
<SectionHeader text="Categorical Metadata"/>
[on off] toggle hide deselected filters (shows a menu vs shows what you ordered in compact/narrative form. fold out animation.)
<p> Sort by: Alphabetical / Count || Show counts: true / false || Collapse: all / none</p>

View File

@@ -9,20 +9,11 @@ import actions from "../../actions";
}
})
class CategoryValue extends React.Component {
toggleOn() {
this.props.dispatch(
actions.attemptCategoricalMetadataSelection(
this.props.metadataField,
this.props.value
))
}
toggleOff() {
this.props.dispatch(
actions.attemptCategoricalMetadataDeselection(
this.props.metadataField,
this.props.value
))
handleChange() {
console.log("dispatch", this.props.metadataField, this.props.value)
}
render () {
/* has this value for this category already been selected? */
@@ -40,9 +31,7 @@ class CategoryValue extends React.Component {
return (
<div
key={this.props.i}
onClick={selected ? this.toggleOff.bind(this) : this.toggleOn.bind(this)}
style={{
cursor: "pointer",
display: "flex",
fontWeight: selected ? 700 : 400,
}}>
@@ -52,7 +41,7 @@ class CategoryValue extends React.Component {
margin: 0,
lineHeight: "1em"
}}>
{this.props.value}
<input onChange={this.handleChange.bind(this)} checked={true} type="checkbox"/> {this.props.value}
</p>
<p style={{
margin: 0,
@@ -66,3 +55,19 @@ class CategoryValue extends React.Component {
}
export default CategoryValue;
// onClick={selected ? this.toggleOff.bind(this) : this.toggleOn.bind(this)}
// toggleOn() {
// this.props.dispatch(
// actions.attemptCategoricalMetadataSelection(
// this.props.metadataField,
// this.props.value
// ))
// }
// toggleOff() {
// this.props.dispatch(
// actions.attemptCategoricalMetadataDeselection(
// this.props.metadataField,
// this.props.value
// ))
// }

View File

@@ -27,7 +27,8 @@ const updateCellSelectionMiddleware = (store) => {
/* this is a hardcoded map of the things we need to keep an eye on and update global cell selection in response to */
const filterJustChanged =
action.type === "color by expression" ||
action.type === "color by continuous metadata"
action.type === "color by continuous metadata" ||
action.type === "color by categorical metadata"
;
if (
@@ -39,6 +40,7 @@ const updateCellSelectionMiddleware = (store) => {
let currentSelectionWithUpdatedColors = s.controls.currentCellSelection.slice(0);
let colorScale;
/*
in plain language...
@@ -48,6 +50,18 @@ const updateCellSelectionMiddleware = (store) => {
This is available to all the draw functions as cell["__color__"]
*/
if (action.type === "color by categorical metadata") {
colorScale = d3.scaleOrdinal(d3.schemeCategory20);
_.each(currentSelectionWithUpdatedColors, (cell, i) => {
currentSelectionWithUpdatedColors[i]["__color__"] = colorScale(
cell[action.colorAccessor]
)
})
}
if (action.type === "color by continuous metadata") {
colorScale = d3.scaleLinear()
@@ -109,7 +123,17 @@ const updateCellSelectionMiddleware = (store) => {
}
let modifiedAction = Object.assign({}, action, {currentSelectionWithUpdatedColors}) /* append the result of all the filters to the action the user just triggered */
/*
append the result of all the filters to the action the user just triggered
*/
let modifiedAction = Object.assign(
{},
action,
{
currentSelectionWithUpdatedColors,
colorScale
}
)
return next(modifiedAction);
}

View File

@@ -67,6 +67,11 @@ const Controls = (state = {
colorAccessor: action.gene,
currentCellSelection: action.currentSelectionWithUpdatedColors /* this comes from middleware */
})
case "color by categorical metadata":
return Object.assign({}, state, {
colorAccessor: action.colorAccessor, /* pass the scale through additionally, and it's a legend! */
currentCellSelection: action.currentSelectionWithUpdatedColors /* this comes from middleware */
})
default:
return state;
}