diff --git a/frontend/src/components/TopicCard/Links.js b/frontend/src/components/TopicCard/Links.js index 93bebd82..1eb85a44 100644 --- a/frontend/src/components/TopicCard/Links.js +++ b/frontend/src/components/TopicCard/Links.js @@ -1,7 +1,7 @@ import React, { PropTypes, Component } from 'react' -import DataModel from '../../Metamaps/DataModel' -import Visualize from '../../Metamaps/Visualize' +import Metacode from './Metacode' +import Permission from './Permission' const inmaps = (topic) => { const inmapsArray = topic.get('inmaps') || [] @@ -31,128 +31,6 @@ const inmaps = (topic) => { // TODO all of these should be largely turned into passed-in callbacks const bindShowCardListeners = (topic, ActiveMapper) => { - var authorized = topic.authorizeToEdit(ActiveMapper) - var selectingMetacode = false - // attach the listener that shows the metacode title when you hover over the image - $('.showcard .metacodeImage').mouseenter(function() { - $('.showcard .icon').css('z-index', '4') - $('.showcard .metacodeTitle').show() - }) - $('.showcard .linkItem.icon').mouseleave(function() { - if (!selectingMetacode) { - $('.showcard .metacodeTitle').hide() - $('.showcard .icon').css('z-index', '1') - } - }) - - var metacodeLiClick = function() { - selectingMetacode = false - var metacodeId = parseInt($(this).attr('data-id')) - var metacode = DataModel.Metacodes.get(metacodeId) - $('.CardOnGraph').find('.metacodeTitle').html(metacode.get('name')) - .append('<div class="expandMetacodeSelect"></div>') - .attr('class', 'metacodeTitle mbg' + metacode.id) - $('.CardOnGraph').find('.metacodeImage').css('background-image', 'url(' + metacode.get('icon') + ')') - topic.save({ - metacode_id: metacode.id - }) - Visualize.mGraph.plot() - $('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge') - $('.metacodeTitle').hide() - $('.showcard .icon').css('z-index', '1') - } - - var openMetacodeSelect = function(event) { - var TOPICCARD_WIDTH = 300 - var METACODESELECT_WIDTH = 404 - var MAX_METACODELIST_HEIGHT = 270 - - if (!selectingMetacode) { - selectingMetacode = true - - // this is to make sure the metacode - // select is accessible onscreen, when opened - // while topic card is close to the right - // edge of the screen - var windowWidth = $(window).width() - var showcardLeft = parseInt($('.showcard').css('left')) - var distanceFromEdge = windowWidth - (showcardLeft + TOPICCARD_WIDTH) - if (distanceFromEdge < METACODESELECT_WIDTH) { - $('.metacodeSelect').addClass('onRightEdge') - } - - // this is to make sure the metacode - // select is accessible onscreen, when opened - // while topic card is close to the bottom - // edge of the screen - var windowHeight = $(window).height() - var showcardTop = parseInt($('.showcard').css('top')) - var topicTitleHeight = $('.showcard .title').height() + parseInt($('.showcard .title').css('padding-top')) + parseInt($('.showcard .title').css('padding-bottom')) - var distanceFromBottom = windowHeight - (showcardTop + topicTitleHeight) - if (distanceFromBottom < MAX_METACODELIST_HEIGHT) { - $('.metacodeSelect').addClass('onBottomEdge') - } - - $('.metacodeSelect').show() - event.stopPropagation() - } - } - - var hideMetacodeSelect = function() { - selectingMetacode = false - $('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge') - $('.metacodeTitle').hide() - $('.showcard .icon').css('z-index', '1') - } - - if (authorized) { - $('.showcard .metacodeTitle').click(openMetacodeSelect) - $('.showcard').click(hideMetacodeSelect) - $('.metacodeSelect > ul > li').click(function(event) { - event.stopPropagation() - }) - $('.metacodeSelect li li').click(metacodeLiClick) - } - - var hidePermissionSelect = function() { - selectingPermission = false - $('.showcard .yourTopic .mapPerm').removeClass('minimize') // this line flips the pull up arrow to a drop down arrow - $('.showcard .permissionSelect').remove() - } - - var permissionLiClick = function(event) { - selectingPermission = false - var permission = $(this).attr('class') - topic.save({ - permission: permission, - defer_to_map_id: null - }) - $('.showcard .mapPerm').removeClass('co pu pr').addClass(permission.substring(0, 2)) - hidePermissionSelect() - } - - var openPermissionSelect = function(event) { - if (!selectingPermission) { - selectingPermission = true - $(this).addClass('minimize') // this line flips the drop down arrow to a pull up arrow - if ($(this).hasClass('co')) { - $(this).append('<ul class="permissionSelect"><li class="public"></li><li class="private"></li></ul>') - } else if ($(this).hasClass('pu')) { - $(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="private"></li></ul>') - } else if ($(this).hasClass('pr')) { - $(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="public"></li></ul>') - } - $('.showcard .permissionSelect li').click(permissionLiClick) - event.stopPropagation() - } - } - // ability to change permission - var selectingPermission = false - if (topic.authorizePermissionChange(ActiveMapper)) { - $('.showcard .yourTopic .mapPerm').click(openPermissionSelect) - $('.showcard').click(hidePermissionSelect) - } - $('.links .mapCount').unbind().click(function(event) { $('.mapCount .tip').toggle() $('.showcard .hoverTip').toggleClass('hide') @@ -181,22 +59,18 @@ class Links extends Component { } render = () => { - const { topic } = this.props + const { topic, ActiveMapper } = this.props const topicId = topic.isNew() ? topic.cid : topic.id // TODO should we really be using cid here?!? - const permission = topic.get('permission') - // the code for this is stored in /views/main/_metacodeOptions.html.erb - const metacodeSelectHTML = $('#metacodeOptions').html() + const metacode = topic.getMetacode() return ( <div className="links"> - <div className="linkItem icon"> - <div className={`metacodeTitle mbg${topic.get('metacode_id')}`}> - {topic.getMetacode().get('name')} - <div className="expandMetacodeSelect"></div> - </div> - <div className="metacodeImage" style={{backgroundImage: `url(${topic.getMetacode().get('icon')})`}} title="click and drag to move card"></div> - <div className="metacodeSelect" dangerouslySetInnerHTML={{ __html: metacodeSelectHTML }} /> - </div> + <Metacode + topic={topic} + metacode={metacode} + ActiveMapper={ActiveMapper} + updateTopic={this.props.updateTopic} + /> <div className="linkItem contributor"> <a href={`/explore/mapper/${topic.get('user_id')}`} target="_blank"><img src={topic.get('user_image')} className="contributorIcon" width="32" height="32" /></a> <div className="contributorName">{topic.get('user_name')}</div> @@ -205,14 +79,18 @@ class Links extends Component { <div className="mapCountIcon"></div> {topic.get('map_count').toString()} <div className="hoverTip">Click to see which maps topic appears on</div> - <div className="tip"><ul>{inmaps(this.props.topic)}</ul></div> + <div className="tip"><ul>{inmaps(topic)}</ul></div> </div> <a href={`/topics/${topicId}`} target="_blank" className="linkItem synapseCount"> <div className="synapseCountIcon"></div> {topic.get('synapse_count').toString()} <div className="tip">Click to see this topics synapses</div> </a> - <div className={`linkItem mapPerm ${permission.substring(0, 2)}`} title={permission}></div> + <Permission + topic={topic} + ActiveMapper={ActiveMapper} + updateTopic={this.props.updateTopic} + /> <div className="clearfloat"></div> </div> ) @@ -221,7 +99,8 @@ class Links extends Component { Links.propTypes = { topic: PropTypes.object, // backbone object - ActiveMapper: PropTypes.object + ActiveMapper: PropTypes.object, + updateTopic: PropTypes.func } export default Links diff --git a/frontend/src/components/TopicCard/Metacode.js b/frontend/src/components/TopicCard/Metacode.js new file mode 100644 index 00000000..574759f1 --- /dev/null +++ b/frontend/src/components/TopicCard/Metacode.js @@ -0,0 +1,124 @@ +import React, { PropTypes, Component } from 'react' + +import DataModel from '../../Metamaps/DataModel' +import Visualize from '../../Metamaps/Visualize' + +// TODO all of these should be largely turned into passed-in callbacks +const bindShowCardListeners = (topic, ActiveMapper) => { + var authorized = topic.authorizeToEdit(ActiveMapper) + var selectingMetacode = false + // attach the listener that shows the metacode title when you hover over the image + $('.showcard .metacodeImage').mouseenter(function() { + $('.showcard .icon').css('z-index', '4') + $('.showcard .metacodeTitle').show() + }) + $('.showcard .linkItem.icon').mouseleave(function() { + if (!selectingMetacode) { + $('.showcard .metacodeTitle').hide() + $('.showcard .icon').css('z-index', '1') + } + }) + + var metacodeLiClick = function() { + selectingMetacode = false + var metacodeId = parseInt($(this).attr('data-id')) + var metacode = DataModel.Metacodes.get(metacodeId) + $('.CardOnGraph').find('.metacodeTitle').html(metacode.get('name')) + .append('<div class="expandMetacodeSelect"></div>') + .attr('class', 'metacodeTitle mbg' + metacode.id) + $('.CardOnGraph').find('.metacodeImage').css('background-image', 'url(' + metacode.get('icon') + ')') + topic.save({ + metacode_id: metacode.id + }) + Visualize.mGraph.plot() + $('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge') + $('.metacodeTitle').hide() + $('.showcard .icon').css('z-index', '1') + } + + var openMetacodeSelect = function(event) { + var TOPICCARD_WIDTH = 300 + var METACODESELECT_WIDTH = 404 + var MAX_METACODELIST_HEIGHT = 270 + + if (!selectingMetacode) { + selectingMetacode = true + + // this is to make sure the metacode + // select is accessible onscreen, when opened + // while topic card is close to the right + // edge of the screen + var windowWidth = $(window).width() + var showcardLeft = parseInt($('.showcard').css('left')) + var distanceFromEdge = windowWidth - (showcardLeft + TOPICCARD_WIDTH) + if (distanceFromEdge < METACODESELECT_WIDTH) { + $('.metacodeSelect').addClass('onRightEdge') + } + + // this is to make sure the metacode + // select is accessible onscreen, when opened + // while topic card is close to the bottom + // edge of the screen + var windowHeight = $(window).height() + var showcardTop = parseInt($('.showcard').css('top')) + var topicTitleHeight = $('.showcard .title').height() + parseInt($('.showcard .title').css('padding-top')) + parseInt($('.showcard .title').css('padding-bottom')) + var distanceFromBottom = windowHeight - (showcardTop + topicTitleHeight) + if (distanceFromBottom < MAX_METACODELIST_HEIGHT) { + $('.metacodeSelect').addClass('onBottomEdge') + } + + $('.metacodeSelect').show() + event.stopPropagation() + } + } + + var hideMetacodeSelect = function() { + selectingMetacode = false + $('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge') + $('.metacodeTitle').hide() + $('.showcard .icon').css('z-index', '1') + } + + if (authorized) { + $('.showcard .metacodeTitle').click(openMetacodeSelect) + $('.showcard').click(hideMetacodeSelect) + $('.metacodeSelect > ul > li').click(function(event) { + event.stopPropagation() + }) + $('.metacodeSelect li li').click(metacodeLiClick) + } +} + +class Links extends Component { + componentDidMount = () => { + bindShowCardListeners(this.props.topic, this.props.ActiveMapper) + } + + render = () => { + const { metacode } = this.props + // the code for this is stored in /views/main/_metacodeOptions.html.erb + const metacodeSelectHTML = $('#metacodeOptions').html() + + return ( + <div className="linkItem icon"> + <div className={`metacodeTitle mbg${metacode.get('id')}`}> + {metacode.get('name')} + <div className="expandMetacodeSelect"></div> + </div> + <div className="metacodeImage" style={{backgroundImage: `url(${metacode.get('icon')})`}} title="click and drag to move card"></div> + <div className="metacodeSelect" dangerouslySetInnerHTML={{ __html: metacodeSelectHTML }} /> + </div> + ) + } +} + +Links.propTypes = { + topic: PropTypes.object, // backbone object + metacode: PropTypes.object, // backbone object + ActiveMapper: PropTypes.object, + updateTopic: PropTypes.func +} + +export default Links + + diff --git a/frontend/src/components/TopicCard/Permission.js b/frontend/src/components/TopicCard/Permission.js new file mode 100644 index 00000000..cef32cc8 --- /dev/null +++ b/frontend/src/components/TopicCard/Permission.js @@ -0,0 +1,81 @@ +import React, { PropTypes, Component } from 'react' + +// TODO how do we make it so that clicking elsewhere on the topic +// card cancels this +class Permission extends Component { + + constructor(props) { + super(props) + this.state = { + selectingPermission: false + } + } + + componentDidMount = () => { + bindShowCardListeners(this.props.topic, this.props.ActiveMapper, this) + } + + togglePermissionSelect = () => { + this.setState({selectingPermission: !this.state.selectingPermission}) + } + + openPermissionSelect = () => { + this.setState({selectingPermission: true}) + } + + closePermissionSelect = () => { + this.setState({selectingPermission: false}) + } + + render = () => { + const self = this + const { topic, ActiveMapper, updateTopic } = this.props + const { selectingPermission } = this.state + const permission = topic.get('permission') + const canChange = topic.authorizePermissionChange(ActiveMapper) + const onClick = canChange ? this.togglePermissionSelect : () => {} + let classes = `linkItem mapPerm ${permission.substring(0, 2)}` + if (selectingPermission) classes += ' minimize' + const liClick = value => { + return event => { + self.closePermissionSelect() + updateTopic({ + permission: value, + defer_to_map_id: null + }) + // prevents it from also firing the event listener on the parent + event.preventDefault() + } + } + const selectCommons = <li key='1' className='commons' onClick={liClick('commons')}></li> + const selectPublic = <li key='2' className='public' onClick={liClick('public')}></li> + const selectPrivate = <li key='3' className='private' onClick={liClick('private')}></li> + let permOptions + if (permission === 'commons') { + permOptions = [selectPublic, selectPrivate] + } else if (permission === 'public') { + permOptions = [selectCommons, selectPrivate] + } else if (permission === 'private') { + permOptions = [selectCommons, selectPublic] + } + + return ( + <div + className={classes} + title={permission} + onClick={onClick}> + {selectingPermission && <ul className="permissionSelect"> + {permOptions} + </ul>} + </div> + ) + } +} + +Permission.propTypes = { + topic: PropTypes.object, // backbone object + ActiveMapper: PropTypes.object, + updateTopic: PropTypes.func +} + +export default Permission diff --git a/frontend/src/components/TopicCard/index.js b/frontend/src/components/TopicCard/index.js index 1cd6f3f9..8af548c7 100644 --- a/frontend/src/components/TopicCard/index.js +++ b/frontend/src/components/TopicCard/index.js @@ -26,6 +26,7 @@ class ReactTopicCard extends Component { <Title name={topic.get('name')} onChange={this.props.updateTopic} /> <Links topic={topic} ActiveMapper={this.props.ActiveMapper} + updateTopic={this.props.updateTopic} /> <Desc desc={topic.get('desc')} authorizedToEdit={topic.authorizeToEdit(ActiveMapper)}