mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-19 19:08:11 +08:00
color by categorical
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
// ))
|
||||
// }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
5
src/reducers/controls.js
vendored
5
src/reducers/controls.js
vendored
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user