2017-09-18 23:30:33 -04:00
|
|
|
import React, { Component } from 'react'
|
|
|
|
import PropTypes from 'prop-types'
|
|
|
|
|
|
|
|
import MetacodeSelect from '../MetacodeSelect'
|
2017-09-12 15:17:15 -04:00
|
|
|
|
|
|
|
class ContextMenu extends Component {
|
|
|
|
static propTypes = {
|
2017-09-18 23:30:33 -04:00
|
|
|
topicId: PropTypes.string,
|
2017-09-19 00:34:37 -04:00
|
|
|
mapId: PropTypes.string,
|
|
|
|
currentUser: PropTypes.object,
|
|
|
|
map: PropTypes.object,
|
2017-09-18 23:30:33 -04:00
|
|
|
contextNode: PropTypes.object,
|
|
|
|
contextEdge: PropTypes.object,
|
|
|
|
contextPos: PropTypes.object,
|
|
|
|
contextFetchingSiblingsData: PropTypes.bool,
|
|
|
|
contextSiblingsData: PropTypes.object,
|
|
|
|
metacodeSets: PropTypes.array,
|
|
|
|
contextDelete: PropTypes.func,
|
|
|
|
contextRemove: PropTypes.func,
|
|
|
|
contextHide: PropTypes.func,
|
|
|
|
contextCenterOn: PropTypes.func,
|
|
|
|
contextPopoutTopic: PropTypes.func,
|
|
|
|
contextUpdatePermissions: PropTypes.func,
|
|
|
|
contextOnMetacodeSelect: PropTypes.func,
|
|
|
|
contextFetchSiblings: PropTypes.func,
|
|
|
|
contextPopulateSiblings: PropTypes.func
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props)
|
|
|
|
this.state = {
|
|
|
|
populateSiblingsSent: false
|
|
|
|
}
|
2017-09-12 15:17:15 -04:00
|
|
|
}
|
|
|
|
|
2017-09-19 08:37:00 -04:00
|
|
|
getPositionData = () => {
|
|
|
|
const { contextPos } = this.props
|
|
|
|
let extraClasses = []
|
|
|
|
const position = {}
|
2017-09-19 11:46:19 -04:00
|
|
|
// TODO: make these dynamic values so that the ContextMenu can
|
|
|
|
// change height and still work properly
|
2017-09-19 08:37:00 -04:00
|
|
|
const RIGHTCLICK_WIDTH = 300
|
|
|
|
const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static
|
|
|
|
const SUBMENUS_WIDTH = 256
|
|
|
|
const MAX_SUBMENU_HEIGHT = 270
|
|
|
|
const windowWidth = document.documentElement.clientWidth
|
|
|
|
const windowHeight = document.documentElement.clientHeight
|
|
|
|
|
|
|
|
if (windowWidth - contextPos.x < SUBMENUS_WIDTH) {
|
|
|
|
position.right = windowWidth - contextPos.x
|
|
|
|
extraClasses.push('moveMenusToLeft')
|
|
|
|
} else if (windowWidth - contextPos.x < RIGHTCLICK_WIDTH) {
|
|
|
|
position.right = windowWidth - contextPos.x
|
|
|
|
} else if (windowWidth - contextPos.x < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) {
|
|
|
|
position.left = contextPos.x
|
|
|
|
extraClasses.push('moveMenusToLeft')
|
|
|
|
} else {
|
|
|
|
position.left = contextPos.x
|
|
|
|
}
|
|
|
|
|
|
|
|
if (windowHeight - contextPos.y < MAX_SUBMENU_HEIGHT) {
|
|
|
|
position.bottom = windowHeight - contextPos.y
|
|
|
|
extraClasses.push('moveMenusUp')
|
|
|
|
} else if (windowHeight - contextPos.y < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) {
|
|
|
|
position.top = contextPos.y
|
|
|
|
extraClasses.push('moveMenusUp')
|
|
|
|
} else {
|
|
|
|
position.top = contextPos.y
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
pos: {
|
|
|
|
top: position.top && position.top + 'px',
|
|
|
|
bottom: position.bottom && position.bottom + 'px',
|
|
|
|
left: position.left && position.left + 'px',
|
|
|
|
right: position.right && position.right + 'px'
|
|
|
|
},
|
|
|
|
extraClasses
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-19 09:04:21 -04:00
|
|
|
hide = () => {
|
|
|
|
const { contextHide } = this.props
|
2017-09-19 10:16:14 -04:00
|
|
|
return <li className='rc-hide' onClick={contextHide}>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-icon' />
|
|
|
|
Hide until refresh
|
|
|
|
<div className='rc-keyboard'>Ctrl+H</div>
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
|
|
|
}
|
2017-09-19 00:34:37 -04:00
|
|
|
|
2017-09-19 09:04:21 -04:00
|
|
|
remove = () => {
|
|
|
|
const { contextRemove, map, currentUser } = this.props
|
2017-09-19 00:34:37 -04:00
|
|
|
const canEditMap = map && map.authorizeToEdit(currentUser)
|
2017-09-19 09:04:21 -04:00
|
|
|
if (!canEditMap) {
|
|
|
|
return null
|
|
|
|
}
|
2017-09-19 10:16:14 -04:00
|
|
|
return <li className='rc-remove' onClick={contextRemove}>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-icon' />
|
|
|
|
Remove from map
|
|
|
|
<div className='rc-keyboard'>Ctrl+M</div>
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
|
|
|
}
|
|
|
|
|
|
|
|
delete = () => {
|
|
|
|
const { contextDelete, map, currentUser } = this.props
|
|
|
|
const canEditMap = map && map.authorizeToEdit(currentUser)
|
|
|
|
if (!canEditMap) {
|
|
|
|
return null
|
|
|
|
}
|
2017-09-19 10:16:14 -04:00
|
|
|
return <li className='rc-delete' onClick={contextDelete}>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-icon' />
|
|
|
|
Delete
|
2017-09-19 10:16:14 -04:00
|
|
|
<div className='rc-keyboard'>Ctrl+D</div>
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
|
|
|
}
|
|
|
|
|
|
|
|
center = () => {
|
|
|
|
const { contextCenterOn, contextNode, topicId } = this.props
|
|
|
|
if (!(contextNode && topicId)) {
|
|
|
|
return null
|
|
|
|
}
|
2017-09-19 10:16:14 -04:00
|
|
|
return <li className='rc-center'
|
2017-09-19 09:04:21 -04:00
|
|
|
onClick={() => contextCenterOn(contextNode.id)}>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-icon' />
|
|
|
|
Center this topic
|
2017-09-19 10:16:14 -04:00
|
|
|
<div className='rc-keyboard'>Alt+E</div>
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
|
|
|
}
|
|
|
|
|
|
|
|
popout = () => {
|
|
|
|
const { contextPopoutTopic, contextNode } = this.props
|
|
|
|
if (!contextNode) {
|
|
|
|
return null
|
|
|
|
}
|
2017-09-19 10:16:14 -04:00
|
|
|
return <li className='rc-popout'
|
2017-09-19 09:04:21 -04:00
|
|
|
onClick={() => contextPopoutTopic(contextNode.id)}>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-icon' />
|
|
|
|
Open in new tab
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
|
|
|
}
|
|
|
|
|
|
|
|
permission = () => {
|
|
|
|
const { currentUser, contextUpdatePermissions } = this.props
|
|
|
|
if (!currentUser) {
|
|
|
|
return null
|
|
|
|
}
|
2017-09-19 10:16:14 -04:00
|
|
|
return <li className='rc-permission'>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-icon' />
|
|
|
|
Change permissions
|
2017-09-19 09:04:21 -04:00
|
|
|
<ul>
|
2017-09-19 10:16:14 -04:00
|
|
|
<li className='changeP toCommons'
|
2017-09-19 09:04:21 -04:00
|
|
|
onClick={() => contextUpdatePermissions('commons')}>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-perm-icon' />
|
|
|
|
commons
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
2017-09-19 10:16:14 -04:00
|
|
|
<li className='changeP toPublic'
|
2017-09-19 09:04:21 -04:00
|
|
|
onClick={() => contextUpdatePermissions('public')}>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-perm-icon' />
|
|
|
|
public
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
2017-09-19 10:16:14 -04:00
|
|
|
<li className='changeP toPrivate'
|
2017-09-19 09:04:21 -04:00
|
|
|
onClick={() => contextUpdatePermissions('private')}>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-perm-icon' />
|
|
|
|
private
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
|
|
|
</ul>
|
2017-09-19 10:16:14 -04:00
|
|
|
<div className='expandLi' />
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
|
|
|
}
|
|
|
|
|
|
|
|
metacode = () => {
|
|
|
|
const { metacodeSets, contextOnMetacodeSelect,
|
|
|
|
currentUser, contextNode } = this.props
|
|
|
|
if (!currentUser) {
|
|
|
|
return null
|
|
|
|
}
|
2017-09-19 10:16:14 -04:00
|
|
|
return <li className='rc-metacode'>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-icon' />
|
|
|
|
Change metacode
|
2017-09-19 10:16:14 -04:00
|
|
|
<div id='metacodeOptionsWrapper'>
|
2017-09-19 09:04:21 -04:00
|
|
|
<MetacodeSelect
|
|
|
|
onMetacodeSelect={id => {
|
|
|
|
contextOnMetacodeSelect(contextNode && contextNode.id, id)
|
|
|
|
}}
|
|
|
|
metacodeSets={metacodeSets} />
|
|
|
|
</div>
|
2017-09-19 10:16:14 -04:00
|
|
|
<div className='expandLi' />
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
|
|
|
}
|
|
|
|
|
|
|
|
siblings = () => {
|
|
|
|
const { contextPopulateSiblings, contextFetchSiblings,
|
|
|
|
contextSiblingsData, contextFetchingSiblingsData,
|
|
|
|
topicId, contextNode } = this.props
|
2017-09-18 23:30:33 -04:00
|
|
|
const populateSiblings = () => {
|
|
|
|
if (!this.state.populateSiblingsSent) {
|
|
|
|
contextPopulateSiblings(contextNode.id)
|
|
|
|
this.setState({populateSiblingsSent: true})
|
|
|
|
}
|
|
|
|
}
|
2017-09-19 09:04:21 -04:00
|
|
|
if (!(contextNode && topicId)) {
|
|
|
|
return null
|
|
|
|
}
|
2017-09-19 10:16:14 -04:00
|
|
|
return <li className='rc-siblings'
|
2017-09-19 09:04:21 -04:00
|
|
|
onMouseOver={populateSiblings}>
|
2017-09-19 11:46:19 -04:00
|
|
|
<div className='rc-icon' />
|
|
|
|
Reveal siblings
|
2017-09-19 10:16:14 -04:00
|
|
|
<ul id='fetchSiblingList'>
|
|
|
|
<li className='fetchAll'
|
2017-09-19 11:46:19 -04:00
|
|
|
onClick={() => contextFetchSiblings(contextNode)}>
|
|
|
|
All
|
2017-09-19 10:16:14 -04:00
|
|
|
<div className='rc-keyboard'>Alt+R</div>
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
|
|
|
{contextSiblingsData && Object.keys(contextSiblingsData).map(key => {
|
|
|
|
return <li key={key}
|
|
|
|
onClick={() => contextFetchSiblings(contextNode, key)}>
|
|
|
|
{contextSiblingsData[key]}
|
|
|
|
</li>
|
|
|
|
})}
|
2017-09-19 10:16:14 -04:00
|
|
|
{contextFetchingSiblingsData && <li id='loadingSiblings'>loading...</li>}
|
2017-09-19 09:04:21 -04:00
|
|
|
</ul>
|
2017-09-19 10:16:14 -04:00
|
|
|
<div className='expandLi' />
|
2017-09-19 09:04:21 -04:00
|
|
|
</li>
|
|
|
|
}
|
|
|
|
|
2017-09-19 10:16:14 -04:00
|
|
|
render() {
|
2017-09-19 09:04:21 -04:00
|
|
|
const { contextNode, currentUser, topicId } = this.props
|
|
|
|
const positionData = this.getPositionData()
|
|
|
|
const style = Object.assign({}, {position: 'absolute'}, positionData.pos)
|
|
|
|
const showSpacer = currentUser || (contextNode && topicId)
|
2017-09-18 23:30:33 -04:00
|
|
|
|
2017-09-19 08:37:00 -04:00
|
|
|
return <div style={style}
|
2017-09-19 10:16:14 -04:00
|
|
|
className={'rightclickmenu ' + positionData.extraClasses.join(' ')}>
|
2017-09-12 15:17:15 -04:00
|
|
|
<ul>
|
2017-09-19 09:04:21 -04:00
|
|
|
{this.hide()}
|
|
|
|
{this.remove()}
|
|
|
|
{this.delete()}
|
|
|
|
{this.center()}
|
|
|
|
{this.popout()}
|
2017-09-19 10:16:14 -04:00
|
|
|
{showSpacer && <li className='rc-spacer' />}
|
2017-09-19 09:04:21 -04:00
|
|
|
{this.permission()}
|
|
|
|
{this.metacode()}
|
|
|
|
{this.siblings()}
|
2017-09-12 15:17:15 -04:00
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default ContextMenu
|