anatomogram
Advanced tools
Comparing version 0.1.1 to 0.2.0
{ | ||
"name": "anatomogram", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "Expression Atlas anatomogram", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -27,9 +27,9 @@ Expression Atlas Anatomogram React component | ||
To update the anatomograms or ontology IDs edit them in the `assets/svg` directory, then run | ||
To update the anatomograms or ontology IDs edit them in the `resources/svg` directory, then run | ||
``` | ||
scripts/idsForSvgs.py | ||
``` | ||
This regenerates the file `assets/json/idsForSvgs.svg`. | ||
This regenerates the file `resources/json/idsForSvgs.svg`. | ||
To add new anatomograms put them in the assets/svg directory, then edit the config `assets/json/svgsForSpecies.json`. | ||
To add new anatomograms put them in the `resources/svg` directory, then edit the config `resources/json/svgsForSpecies.json`. | ||
@@ -39,3 +39,3 @@ | ||
``` | ||
webpack-dev-server --port 9000 | ||
webpack-dev-server -d | ||
``` | ||
@@ -42,0 +42,0 @@ Go to localhost:9000/html and see that the tissues show up like you want them to. |
@@ -1,13 +0,6 @@ | ||
"use strict"; | ||
const React = require(`react`); | ||
const AnatomogramImage = require(`./AnatomogramImage.jsx`); | ||
const SelectionIcon = require(`./SelectionIcon.jsx`); | ||
//*------------------------------------------------------------------* | ||
var React = require('react'); | ||
var AnatomogramImage = require('./AnatomogramImage.jsx'); | ||
var SelectionIcon = require('./SelectionIcon.jsx'); | ||
//*------------------------------------------------------------------* | ||
var Anatomogram = React.createClass({ | ||
const Anatomogram = React.createClass({ | ||
propTypes: { | ||
@@ -18,7 +11,7 @@ pathToFolderWithBundledResources: React.PropTypes.string.isRequired, | ||
availableAnatomograms : React.PropTypes.arrayOf( | ||
React.PropTypes.shape({ | ||
type:React.PropTypes.string.isRequired, | ||
path:React.PropTypes.string.isRequired, | ||
ids: React.PropTypes.arrayOf(React.PropTypes.string).isRequired | ||
}) | ||
React.PropTypes.shape({ | ||
type:React.PropTypes.string.isRequired, | ||
path:React.PropTypes.string.isRequired, | ||
ids: React.PropTypes.arrayOf(React.PropTypes.string).isRequired | ||
}) | ||
).isRequired, | ||
@@ -31,21 +24,18 @@ height: React.PropTypes.number.isRequired, | ||
getInitialState: function() { | ||
return { | ||
selectedType: this.props.availableAnatomograms[0].type, | ||
}; | ||
getInitialState() { | ||
return { selectedType: this.props.availableAnatomograms[0].type }; | ||
}, | ||
render: function () { | ||
render() { | ||
return ( | ||
<div className="gxaAnatomogram" style={{display: "table", paddingTop: "4px"}}> | ||
<div style={{display: "table-row"}}> | ||
<div style={{display: "table-cell", verticalAlign: "top"}}> | ||
{this._anatomogramSelectImageButtons()} | ||
<div className="gxaAnatomogram" style={{display: `table`, paddingTop: `4px`}}> | ||
<div style={{display: `table-row`}}> | ||
<div style={{display: `table-cell`, verticalAlign: `top`}}> | ||
{this._anatomogramSelectImageButtons()} | ||
</div> | ||
<AnatomogramImage | ||
key={this.state.selectedType} | ||
ref="currentImage" | ||
file={this._selectedAnatomogram().path} | ||
allSvgPathIds={this.props.allSvgPathIds || this._selectedAnatomogram().ids} | ||
{...this.props} /> | ||
key={this.state.selectedType} | ||
file={this._selectedAnatomogram().path} | ||
allSvgPathIds={this.props.allSvgPathIds || this._selectedAnatomogram().ids} | ||
{...this.props} /> | ||
</div> | ||
@@ -56,48 +46,39 @@ </div> | ||
_anatomogramSelectImageButtons : function(){ | ||
return ( | ||
this.props.availableAnatomograms.length < 2 | ||
? [] | ||
: this.props.availableAnatomograms | ||
.map(function(availableAnatomogram) { | ||
return( | ||
<SelectionIcon | ||
key={availableAnatomogram.type + "_toggle"} | ||
pathToFolderWithBundledResources={this.props.pathToFolderWithBundledResources} | ||
anatomogramType={availableAnatomogram.type} | ||
selected={this.state.selectedType === availableAnatomogram.type} | ||
onClick={function(){this._afterUserSelectedAnatomogram(availableAnatomogram.type);}.bind(this)}/> | ||
) | ||
}.bind(this)) | ||
); | ||
_anatomogramSelectImageButtons() { | ||
return ( | ||
this.props.availableAnatomograms.length < 2 ? | ||
[] : | ||
this.props.availableAnatomograms | ||
.map(availableAnatomogram => | ||
( | ||
<SelectionIcon | ||
key={`${availableAnatomogram.type}_toggle`} | ||
pathToFolderWithBundledResources={this.props.pathToFolderWithBundledResources} | ||
anatomogramType={availableAnatomogram.type} | ||
selected={this.state.selectedType === availableAnatomogram.type} | ||
onClick={() => { this._afterUserSelectedAnatomogram(availableAnatomogram.type); }}/> | ||
) | ||
) | ||
); | ||
}, | ||
_afterUserSelectedAnatomogram: function(newSelectedType) { | ||
_afterUserSelectedAnatomogram(newSelectedType) { | ||
if (newSelectedType !== this.state.selectedType) { | ||
this.setState({selectedType: newSelectedType}); | ||
this.setState({ selectedType: newSelectedType }); | ||
} | ||
}, | ||
_selectedAnatomogram: function() { | ||
var type = this.state.selectedType; | ||
return ( | ||
this.props.availableAnatomograms | ||
.filter(function(e,ix){ | ||
return ( | ||
e.type === type | ||
) | ||
}) | ||
.concat({ | ||
type:"_", | ||
path:"__invalid__.svg", | ||
ids: [] | ||
}) | ||
[0] | ||
); | ||
_selectedAnatomogram() { | ||
return ( | ||
this.props.availableAnatomograms | ||
.filter(e => e.type === this.state.selectedType) | ||
.concat({ | ||
type: `_`, | ||
path: `__invalid__.svg`, | ||
ids: [] | ||
})[0] | ||
); | ||
}, | ||
}); | ||
//*------------------------------------------------------------------* | ||
module.exports = Anatomogram; |
@@ -7,90 +7,85 @@ const React = require(`react`); | ||
const ArraysEqual = (a, b) => { | ||
if (a === b) return true; | ||
if (a == null || b == null) return false; | ||
if (a.length != b.length) return false; | ||
for (let i = 0; i < a.length; ++i) { | ||
if (a[i] !== b[i]) return false; | ||
} | ||
return true; | ||
if (a === b) return true; | ||
if (a == null || b == null) return false; | ||
if (a.length != b.length) return false; | ||
for (let i = 0; i < a.length; ++i) { | ||
if (a[i] !== b[i]) return false; | ||
} | ||
return true; | ||
}; | ||
const AnatomogramImageParts = React.createClass({ | ||
propTypes: { | ||
idsExpressedInExperiment: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
idsHeatmapWantsHighlighted: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
idsMousedOver: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
idsNotHighlighted: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
expressedTissueColour: React.PropTypes.string.isRequired, | ||
hoveredTissueColour: React.PropTypes.string.isRequired, | ||
whenMousedOverIdsChange: React.PropTypes.func | ||
}, | ||
propTypes: { | ||
idsExpressedInExperiment: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
idsHeatmapWantsHighlighted: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
idsMousedOver: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
idsNotHighlighted: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
expressedTissueColour: React.PropTypes.string.isRequired, | ||
hoveredTissueColour: React.PropTypes.string.isRequired, | ||
whenMousedOverIdsChange: React.PropTypes.func | ||
}, | ||
getDefaultProps () { | ||
return ({whenMousedOverIdsChange: (nextIds, oldIds) => {}}); | ||
}, | ||
getDefaultProps() { | ||
return ({ whenMousedOverIdsChange: (nextIds, oldIds) => {} }); | ||
}, | ||
getInitialState () { | ||
return {toDraw: [].concat( | ||
this._idsThatShouldBeStronglyHighlighted(this.props) | ||
.map(this._highlightStrongly) | ||
, | ||
this.props.idsExpressedInExperiment | ||
.map(this._highlightSlightly) | ||
, | ||
this.props.idsNotHighlighted | ||
.map(this._highlightAsBackground) | ||
)}; | ||
}, | ||
getInitialState() { | ||
return { | ||
toDraw: [].concat( | ||
this._idsThatShouldBeStronglyHighlighted(this.props).map(this._highlightStrongly), | ||
this.props.idsExpressedInExperiment.map(this._highlightSlightly), | ||
this.props.idsNotHighlighted.map(this._highlightAsBackground) | ||
)}; | ||
}, | ||
render () { | ||
return <span/>; | ||
}, | ||
render() { | ||
return <span/>; | ||
}, | ||
_highlightStrongly (svgPathId) { | ||
return {id: svgPathId, colour: this.props.hoveredTissueColour, opacity: 0.7 }; | ||
}, | ||
_highlightStrongly(svgPathId) { | ||
return { id: svgPathId, colour: this.props.hoveredTissueColour, opacity: 0.7 }; | ||
}, | ||
_highlightSlightly (svgPathId) { | ||
return {id: svgPathId, colour: this.props.expressedTissueColour, opacity: 0.5 }; | ||
}, | ||
_highlightSlightly(svgPathId) { | ||
return { id: svgPathId, colour: this.props.expressedTissueColour, opacity: 0.5 }; | ||
}, | ||
_highlightAsBackground (svgPathId) { | ||
return {id: svgPathId, colour: `gray`, opacity: 0.5 }; | ||
}, | ||
componentWillUnmount (){ | ||
this.props.whenMousedOverIdsChange([],this.props.idsMousedOver); | ||
}, | ||
_highlightAsBackground(svgPathId) { | ||
return { id: svgPathId, colour: `gray`, opacity: 0.5 }; | ||
}, | ||
componentWillReceiveProps (nextProps) { | ||
if(!ArraysEqual(nextProps.idsMousedOver, this.props.idsMousedOver)){ | ||
this.props.whenMousedOverIdsChange(nextProps.idsMousedOver,this.props.idsMousedOver); | ||
} | ||
let oldStrong = this._idsThatShouldBeStronglyHighlighted(this.props); | ||
let newStrong = this._idsThatShouldBeStronglyHighlighted(nextProps); | ||
let oldWeak = this.props.idsExpressedInExperiment; | ||
let newWeak = nextProps.idsExpressedInExperiment; | ||
componentWillUnmount() { | ||
this.props.whenMousedOverIdsChange([],this.props.idsMousedOver); | ||
}, | ||
let toDraw = [].concat( | ||
//ids that heatmap wants highlighted are the most highlighted | ||
newStrong | ||
.filter(id => !oldStrong.includes(id)) | ||
.map(this._highlightStrongly) | ||
, | ||
//ids that are expressed in the experiment are highlighted with a weaker colour, often the same as background | ||
newWeak | ||
.filter(id => !newStrong.includes(id)) | ||
.filter(id => !oldWeak.includes(id)) | ||
.map(this._highlightSlightly) | ||
, | ||
nextProps.idsNotHighlighted | ||
.filter(id => !this.props.idsNotHighlighted.includes(id)) | ||
.map(this._highlightAsBackground) | ||
); | ||
componentWillReceiveProps(nextProps) { | ||
if(!ArraysEqual(nextProps.idsMousedOver, this.props.idsMousedOver)){ | ||
this.props.whenMousedOverIdsChange(nextProps.idsMousedOver,this.props.idsMousedOver); | ||
} | ||
const oldStrong = this._idsThatShouldBeStronglyHighlighted(this.props); | ||
const newStrong = this._idsThatShouldBeStronglyHighlighted(nextProps); | ||
const oldWeak = this.props.idsExpressedInExperiment; | ||
const newWeak = nextProps.idsExpressedInExperiment; | ||
this.setState({toDraw:toDraw}); | ||
}, | ||
const toDraw = [].concat( | ||
//ids that heatmap wants highlighted are the most highlighted | ||
newStrong | ||
.filter(id => !oldStrong.includes(id)) | ||
.map(this._highlightStrongly), | ||
//ids that are expressed in the experiment are highlighted with a weaker colour, often the same as background | ||
newWeak | ||
.filter(id => !newStrong.includes(id)) | ||
.filter(id => !oldWeak.includes(id)) | ||
.map(this._highlightSlightly), | ||
nextProps.idsNotHighlighted | ||
.filter(id => !this.props.idsNotHighlighted.includes(id)) | ||
.map(this._highlightAsBackground) | ||
); | ||
_idsThatShouldBeStronglyHighlighted (properties){ | ||
return properties.idsHeatmapWantsHighlighted.concat(properties.idsMousedOver); | ||
} | ||
this.setState({ toDraw: toDraw }); | ||
}, | ||
_idsThatShouldBeStronglyHighlighted(properties) { | ||
return properties.idsHeatmapWantsHighlighted.concat(properties.idsMousedOver); | ||
} | ||
}); | ||
@@ -100,183 +95,183 @@ | ||
const AnatomogramImage = React.createClass({ | ||
propTypes: { | ||
file: (props, propName, componentName) => { | ||
if(propName === `file`){ | ||
if(typeof props[propName]!== `string`){ | ||
return new Error(`Expected string to specify file, got: ${props[propName]}`); | ||
} | ||
if(!props[propName]){ | ||
return new Error(`Path to file empty!`); | ||
} | ||
} | ||
return ``; | ||
propTypes: { | ||
file: (props, propName, componentName) => { | ||
if(propName === `file`){ | ||
if(typeof props[propName]!== `string`){ | ||
return new Error(`Expected string to specify file, got: ${props[propName]}`); | ||
} | ||
if(!props[propName]){ | ||
return new Error(`Path to file empty!`); | ||
} | ||
} | ||
return ``; | ||
}, | ||
height: React.PropTypes.number.isRequired, | ||
allSvgPathIds: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
idsExpressedInExperiment: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
idsToBeHighlighted: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
expressedTissueColour: React.PropTypes.string.isRequired, | ||
hoveredTissueColour: React.PropTypes.string.isRequired, | ||
whenMousedOverIdsChange: React.PropTypes.func | ||
}, | ||
height: React.PropTypes.number.isRequired, | ||
allSvgPathIds: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
idsExpressedInExperiment: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
idsToBeHighlighted: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, | ||
expressedTissueColour: React.PropTypes.string.isRequired, | ||
hoveredTissueColour: React.PropTypes.string.isRequired, | ||
whenMousedOverIdsChange: React.PropTypes.func | ||
}, | ||
getInitialState () { | ||
return { | ||
mousedOverSvgIds: [] | ||
}; | ||
}, | ||
getInitialState() { | ||
return { mousedOverSvgIds: [] }; | ||
}, | ||
componentWillReceiveProps (nextProps) { | ||
if(nextProps.file!==this.props.file){ | ||
this._loadAnatomogram(nextProps.file); | ||
} | ||
}, | ||
componentWillReceiveProps(nextProps) { | ||
if (nextProps.file!==this.props.file) { | ||
this._loadAnatomogram(nextProps.file); | ||
} | ||
}, | ||
componentDidMount () { | ||
this._loadAnatomogram(this.props.file); | ||
this._draw(); | ||
}, | ||
componentDidMount() { | ||
this._loadAnatomogram(this.props.file); | ||
this._draw(); | ||
}, | ||
componentDidUpdate (){ | ||
this._draw(); | ||
}, | ||
componentDidUpdate() { | ||
this._draw(); | ||
}, | ||
_draw () { | ||
let svg= Snap(ReactDOM.findDOMNode(this._anatomogram)).select(`#LAYER_EFO`); | ||
if(svg!==null){ | ||
this._drawOnSvg(svg, this._imageParts.state.toDraw); | ||
this._imageParts.setState({toDraw:[]}); | ||
} | ||
}, | ||
_draw() { | ||
const svg = Snap(ReactDOM.findDOMNode(this._anatomogram)).select(`#LAYER_EFO`); | ||
if(svg !== null){ | ||
this._drawOnSvg(svg, this._imageParts.state.toDraw); | ||
this._imageParts.setState({ toDraw: [] }); | ||
} | ||
}, | ||
_drawInitialLayout (svg){ | ||
if(this._imageParts){ | ||
this._drawOnSvg(svg, this._imageParts.getInitialState().toDraw); | ||
this._imageParts.setState({toDraw:[]}); | ||
} | ||
}, | ||
_drawInitialLayout(svg) { | ||
if(this._imageParts) { | ||
this._drawOnSvg(svg, this._imageParts.getInitialState().toDraw); | ||
this._imageParts.setState({ toDraw: [] }); | ||
} | ||
}, | ||
_drawOnSvg (svg, instructions){ | ||
instructions.forEach(instruction => { | ||
this._highlightOrganismParts(svg,instruction.id, instruction.colour, instruction.opacity); | ||
}); | ||
}, | ||
_drawOnSvg(svg, instructions) { | ||
instructions.forEach(instruction => { | ||
this._highlightOrganismParts(svg,instruction.id, instruction.colour, instruction.opacity); | ||
}); | ||
}, | ||
render () { | ||
let idsExpressedInExperiment =[], | ||
idsHoveredOver=[], | ||
idsHeatmapWantsHighlighted = [], | ||
idsNotHighlighted = []; | ||
render () { | ||
let idsExpressedInExperiment = [], | ||
idsHoveredOver = [], | ||
idsHeatmapWantsHighlighted = [], | ||
idsNotHighlighted = []; | ||
this.props.allSvgPathIds.forEach(id => { | ||
if(this.state.mousedOverSvgIds.includes(id)){ | ||
idsHoveredOver.push(id); | ||
} else if(this.props.idsToBeHighlighted.includes(id)){ | ||
idsHeatmapWantsHighlighted.push(id); | ||
} else if(this.props.idsExpressedInExperiment.includes(id)){ | ||
idsExpressedInExperiment.push(id); | ||
} else { | ||
idsNotHighlighted.push(id); | ||
} | ||
}); | ||
this.props.allSvgPathIds.forEach(id => { | ||
if (this.state.mousedOverSvgIds.includes(id)) { | ||
idsHoveredOver.push(id); | ||
} else if (this.props.idsToBeHighlighted.includes(id)) { | ||
idsHeatmapWantsHighlighted.push(id); | ||
} else if (this.props.idsExpressedInExperiment.includes(id)) { | ||
idsExpressedInExperiment.push(id); | ||
} else { | ||
idsNotHighlighted.push(id); | ||
} | ||
}); | ||
return ( | ||
<span> | ||
<svg ref={c => this._anatomogram = c} style={{display: "table-cell", width: "230px", height:this.props.height + "px"}} /> | ||
<AnatomogramImageParts ref={c => this._imageParts = c} key={this.props.file} | ||
idsExpressedInExperiment={idsExpressedInExperiment} | ||
idsHeatmapWantsHighlighted={idsHeatmapWantsHighlighted} | ||
idsMousedOver={idsHoveredOver} | ||
idsNotHighlighted={idsNotHighlighted} | ||
expressedTissueColour={this.props.expressedTissueColour} | ||
hoveredTissueColour={this.props.hoveredTissueColour} | ||
whenMousedOverIdsChange={this.props.whenMousedOverIdsChange}/> | ||
</span>); | ||
}, | ||
<span> | ||
<svg ref={c => this._anatomogram = c} style={{display: "table-cell", width: "230px", height:this.props.height + "px"}} /> | ||
_highlightPath (svgPathId) { | ||
this.setState({hoveredPathId: svgPathId}); | ||
}, | ||
<AnatomogramImageParts | ||
ref={c => this._imageParts = c} key={this.props.file} | ||
idsExpressedInExperiment={idsExpressedInExperiment} | ||
idsHeatmapWantsHighlighted={idsHeatmapWantsHighlighted} | ||
idsMousedOver={idsHoveredOver} | ||
idsNotHighlighted={idsNotHighlighted} | ||
expressedTissueColour={this.props.expressedTissueColour} | ||
hoveredTissueColour={this.props.hoveredTissueColour} | ||
whenMousedOverIdsChange={this.props.whenMousedOverIdsChange} | ||
/> | ||
</span>); | ||
}, | ||
_loadAnatomogram (svgFile) { | ||
_highlightPath(svgPathId) { | ||
this.setState({ hoveredPathId: svgPathId }); | ||
}, | ||
let svgCanvas = Snap(ReactDOM.findDOMNode(this._anatomogram)), | ||
allElements = svgCanvas.selectAll(`*`); | ||
_loadAnatomogram(svgFile) { | ||
let svgCanvas = Snap(ReactDOM.findDOMNode(this._anatomogram)), | ||
allElements = svgCanvas.selectAll(`*`); | ||
if (allElements) { | ||
allElements.remove(); | ||
} | ||
if (allElements) { | ||
allElements.remove(); | ||
} | ||
let displayAllOrganismPartsCallback = this._drawInitialLayout; | ||
let registerHoverEventsCallback = this._registerHoverEvents; | ||
Snap.load( | ||
svgFile, | ||
fragment => { | ||
displayAllOrganismPartsCallback(fragment.select(`#LAYER_EFO`)); | ||
registerHoverEventsCallback(fragment.select(`#LAYER_EFO`)); | ||
fragment.selectAll(`svg > g`).forEach(g => { | ||
g.transform(`S1.6,0,0`); | ||
svgCanvas.append(g); | ||
}); | ||
var img = fragment.select(`#ccLogo`); | ||
if(img){ | ||
let heightTranslate = svgCanvas.node.clientHeight - 15; | ||
let widthTranslate = svgCanvas.node.clientWidth / 2 - 40; | ||
img.transform(`t`+widthTranslate+`,`+heightTranslate); | ||
svgCanvas.append(img); | ||
} | ||
} | ||
); | ||
}, | ||
const displayAllOrganismPartsCallback = this._drawInitialLayout; | ||
const registerHoverEventsCallback = this._registerHoverEvents; | ||
_registerHoverEvents (svg) { | ||
if (svg) { // Sometimes svg is null... why? | ||
const MaxOverlappingTissues = 5; | ||
let mouseoverCallback = svgPathId => { | ||
this.setState((previousState) => | ||
({mousedOverSvgIds: [...previousState.mousedOverSvgIds, svgPathId].slice(-MaxOverlappingTissues)}) | ||
); | ||
}; | ||
Snap.load( | ||
svgFile, | ||
fragment => { | ||
displayAllOrganismPartsCallback(fragment.select(`#LAYER_EFO`)); | ||
registerHoverEventsCallback(fragment.select(`#LAYER_EFO`)); | ||
fragment.selectAll(`svg > g`).forEach(g => { | ||
g.transform(`S1.6,0,0`); | ||
svgCanvas.append(g); | ||
}); | ||
debugger; | ||
const img = fragment.select(`#ccLogo`); | ||
if (img) { | ||
// svgCanvas.node.clientHeight and svgCanvas.node.clientWidth is more “correct” but are 0 in Firefox | ||
const heightTranslate = Number.parseInt(this._anatomogram.style.height) - 15; | ||
const widthTranslate = Number.parseInt(this._anatomogram.style.width) / 2 - 40; | ||
img.transform(`t${widthTranslate},${heightTranslate}`); | ||
svgCanvas.append(img); | ||
} | ||
} | ||
); | ||
}, | ||
let mouseoutCallback = svgPathId => { | ||
this.setState((previousState) => | ||
({mousedOverSvgIds: previousState.mousedOverSvgIds.map(el => el===svgPathId ? `` : el)}) | ||
); | ||
}; | ||
_registerHoverEvents(svg) { | ||
if (svg) { // Sometimes svg is null... why? | ||
const MaxOverlappingTissues = 5; | ||
const mouseoverCallback = svgPathId => { | ||
this.setState((previousState) => | ||
({ mousedOverSvgIds: [...previousState.mousedOverSvgIds, svgPathId].slice(-MaxOverlappingTissues) }) | ||
); | ||
}; | ||
let attachCallbacks = (svgElement,svgPathId) => { | ||
if(svgElement){ | ||
svgElement.mouseover(() => {mouseoverCallback(svgPathId)}); | ||
svgElement.mouseout(() => {mouseoutCallback(svgPathId)}); | ||
} | ||
}; | ||
const mouseoutCallback = svgPathId => { | ||
this.setState((previousState) => | ||
({ mousedOverSvgIds: previousState.mousedOverSvgIds.map(el => el === svgPathId ? `` : el) }) | ||
); | ||
}; | ||
this.props.allSvgPathIds.forEach(svgPathId => { | ||
let svgElement = svg.select(`#${svgPathId}`); | ||
attachCallbacks(svgElement, svgPathId); | ||
if(svgElement && svgElement.type === `use`){ | ||
attachCallbacks(svg.select(svgElement.node.getAttribute(`xlink:href`)), svgPathId); | ||
} | ||
}); | ||
} | ||
}, | ||
const attachCallbacks = (svgElement, svgPathId) => { | ||
if (svgElement) { | ||
svgElement.mouseover(() => { mouseoverCallback(svgPathId) }); | ||
svgElement.mouseout(() => { mouseoutCallback(svgPathId) }); | ||
} | ||
}; | ||
_highlightOrganismParts (svg, svgPathId, colour, opacity) { | ||
let el = svg.select(`#${svgPathId}`); | ||
if(el && el.type === `use`){ | ||
this._recursivelyChangeProperties(svg.select(el.node.getAttribute(`xlink:href`)), colour, opacity); | ||
} | ||
this._recursivelyChangeProperties(el, colour, opacity); | ||
}, | ||
this.props.allSvgPathIds.forEach(svgPathId => { | ||
const svgElement = svg.select(`#${svgPathId}`); | ||
attachCallbacks(svgElement, svgPathId); | ||
if(svgElement && svgElement.type === `use`){ | ||
attachCallbacks(svg.select(svgElement.node.getAttribute(`xlink:href`)), svgPathId); | ||
} | ||
}); | ||
} | ||
}, | ||
_recursivelyChangeProperties (svgElement, colour, opacity) { | ||
if (svgElement) { | ||
svgElement.selectAll(`*`).forEach( | ||
innerElement => { | ||
this._recursivelyChangeProperties(innerElement); | ||
}); | ||
svgElement.attr({"fill": colour, "fill-opacity": opacity}); | ||
_highlightOrganismParts(svg, svgPathId, colour, opacity) { | ||
let el = svg.select(`#${svgPathId}`); | ||
if (el && el.type === `use`) { | ||
this._recursivelyChangeProperties(svg.select(el.node.getAttribute(`xlink:href`)), colour, opacity); | ||
} | ||
this._recursivelyChangeProperties(el, colour, opacity); | ||
}, | ||
_recursivelyChangeProperties(svgElement, colour, opacity) { | ||
if (svgElement) { | ||
svgElement.selectAll(`*`).forEach(innerElement => { this._recursivelyChangeProperties(innerElement); }); | ||
svgElement.attr({"fill": colour, "fill-opacity": opacity}); | ||
} | ||
} | ||
} | ||
}); | ||
module.exports = AnatomogramImage; |
const Url = require(`url`); | ||
const Path = require(`path`); | ||
const SvgsForSpecies = require(`../assets/json/svgsForSpecies.json`); | ||
const IdsForSvgs = require(`../assets/json/idsForSvgs.json`); | ||
const SvgsForSpecies = require(`../resources/json/svgsForSpecies.json`); | ||
const IdsForSvgs = require(`../resources/json/idsForSvgs.json`); | ||
@@ -9,3 +9,3 @@ const ResolvePathToIcon = (pathToFolderWithBundledResources, type, selected) => | ||
pathToFolderWithBundledResources, | ||
Path.basename(require(`../assets/icons/${type}_${selected ? `` : `un`}selected.png`)) | ||
Path.basename(require(`../resources/icons/${type}_${selected ? `` : `un`}selected.png`)) | ||
); | ||
@@ -16,3 +16,3 @@ | ||
pathToFolderWithBundledResources, | ||
Path.basename(require(`../assets/svg/${svg}`)) | ||
Path.basename(require(`../resources/svg/${svg}`)) | ||
); | ||
@@ -19,0 +19,0 @@ |
@@ -6,21 +6,27 @@ const React = require(`react`); | ||
const SelectionIcon = React.createClass({ | ||
propTypes: { | ||
pathToFolderWithBundledResources: React.PropTypes.string.isRequired, | ||
anatomogramType: React.PropTypes.oneOf([`brain`,`female`,`male`,`whole_plant`,`flower_parts`]).isRequired, | ||
selected: React.PropTypes.bool.isRequired, | ||
onClick: React.PropTypes.func.isRequired | ||
}, | ||
propTypes: { | ||
pathToFolderWithBundledResources: React.PropTypes.string.isRequired, | ||
anatomogramType: React.PropTypes.oneOf([`brain`,`female`,`male`,`whole_plant`,`flower_parts`]).isRequired, | ||
selected: React.PropTypes.bool.isRequired, | ||
onClick: React.PropTypes.func.isRequired | ||
}, | ||
render () { | ||
return ( | ||
<img className={"selection-icon"} onClick={this.props.onClick} | ||
src={ResolvePathToIcon(this.props.pathToFolderWithBundledResources, this.props.anatomogramType, this.props.selected)}/> | ||
); | ||
}, | ||
render() { | ||
return ( | ||
<img className={"selection-icon"} onClick={this.props.onClick} | ||
src={ | ||
ResolvePathToIcon( | ||
this.props.pathToFolderWithBundledResources, | ||
this.props.anatomogramType, | ||
this.props.selected | ||
) | ||
}/> | ||
); | ||
}, | ||
shouldComponentUpdate (nextProps) { | ||
return this.props.selected !== nextProps.selected; | ||
} | ||
shouldComponentUpdate(nextProps) { | ||
return this.props.selected !== nextProps.selected; | ||
} | ||
}); | ||
module.exports = SelectionIcon; |
@@ -8,3 +8,3 @@ var webpack = require('webpack'); | ||
anatomogram: ['babel-polyfill', './index.js'], | ||
demo:'./html/demo.js', | ||
demoRenderer:'./html/demoRenderer.js', | ||
dependencies: ['react', 'react-dom', 'react-prop-types-check', 'imports-loader?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js'] | ||
@@ -23,3 +23,2 @@ }, | ||
new CleanWebpackPlugin(['dist'], {verbose: true, dry: false}), | ||
new webpack.optimize.DedupePlugin(), | ||
new webpack.optimize.CommonsChunkPlugin({ | ||
@@ -29,7 +28,2 @@ name: 'dependencies', | ||
minChunks: Infinity // Explicit definition-based split. Don’t put shared modules between main and demo entries in vendor.bundle.js (e.g. Anatomogram.jsx) | ||
}), | ||
new webpack.DefinePlugin({ | ||
"process.env": { | ||
NODE_ENV: process.env.NODE_ENV === 'production' ? JSON.stringify('production') : JSON.stringify('development') | ||
} | ||
}) | ||
@@ -40,4 +34,6 @@ ], | ||
loaders: [ | ||
{test: /\.js?$/, loader: 'babel', query: {presets: ['es2015']}}, | ||
{test: /\.jsx?$/, loader: 'babel', query: {presets: ['es2015', 'react']}}, | ||
{test: /\.js$/, loader: 'babel', query: {presets: ['es2015']}, | ||
// Place here all the packages that we own | ||
exclude: /node_modules\/(?!(expression-atlas|anatomogram|react-ebi-species))/}, | ||
{test: /\.jsx$/, loader: 'babel', query: {presets: ['es2015', 'react']}}, | ||
{test: /\.less$/, loader: 'style!css!less'}, | ||
@@ -44,0 +40,0 @@ {test: /\.json$/, loader: 'json'}, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
8588304
1286
9
1