From 6e1878413f9384444ef4d1138fd96b716e4df5b3 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 12 Sep 2017 15:17:15 -0400 Subject: [PATCH 01/11] wip right click in react --- frontend/src/Metamaps/GlobalUI/ReactApp.js | 2 +- frontend/src/Metamaps/JIT.js | 216 +----------------- frontend/src/Metamaps/Views/ContextMenu.js | 113 +++++++++ frontend/src/Metamaps/Views/index.js | 4 +- frontend/src/components/common/ContextMenu.js | 77 +++++++ 5 files changed, 196 insertions(+), 216 deletions(-) create mode 100644 frontend/src/Metamaps/Views/ContextMenu.js create mode 100644 frontend/src/components/common/ContextMenu.js diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index db850091..3393a951 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -9,7 +9,7 @@ import { notifyUser } from './index.js' import ImportDialog from './ImportDialog' import Active from '../Active' import DataModel from '../DataModel' -import { ExploreMaps, ChatView, TopicCard } from '../Views' +import { ExploreMaps, ChatView, TopicCard, ContextMenu } from '../Views' import Filter from '../Filter' import JIT from '../JIT' import Realtime from '../Realtime' diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index ffaab923..7116854f 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -11,6 +11,7 @@ import $jit from '../patched/JIT' import MetacodeSelect from '../components/MetacodeSelect' import Active from './Active' +import ContextMenu from './Views/ContextMenu' import Control from './Control' import Create from './Create' import DataModel from './DataModel' @@ -1354,222 +1355,9 @@ const JIT = { // select the node Control.selectNode(node, e) + ContextMenu.selectNode(node, {x: e.clientX, y: e.clientY}) - // delete old right click menu - $('.rightclickmenu').remove() - // create new menu for clicked on node - const rightclickmenu = document.createElement('div') - rightclickmenu.className = 'rightclickmenu' - // prevent the custom context menu from immediately opening the default context menu as well - rightclickmenu.setAttribute('oncontextmenu', 'return false') - - // add the proper options to the menu - let menustring = '' - rightclickmenu.innerHTML = menustring - - // position the menu where the click happened - const position = {} - 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 = $(window).width() - const windowHeight = $(window).height() - - if (windowWidth - e.clientX < SUBMENUS_WIDTH) { - position.right = windowWidth - e.clientX - $(rightclickmenu).addClass('moveMenusToLeft') - } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) { - position.right = windowWidth - e.clientX - } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) { - position.left = e.clientX - $(rightclickmenu).addClass('moveMenusToLeft') - } else { - position.left = e.clientX - } - - if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) { - position.bottom = windowHeight - e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) { - position.top = e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else { - position.top = e.clientY - } - - $(rightclickmenu).css(position) - // add the menu to the page - $('#wrapper').append(rightclickmenu) - - ReactDOM.render( - React.createElement(MetacodeSelect, { - onMetacodeSelect: metacodeId => { - if (Selected.Nodes.length > 1) { - // batch update multiple topics - Control.updateSelectedMetacodes(metacodeId) - } else { - const topic = DataModel.Topics.get(node.id) - topic.save({ - metacode_id: metacodeId - }) - } - $(rightclickmenu).remove() - }, - metacodeSets: ReactApp.metacodeSets - }), - document.getElementById('metacodeOptionsWrapper') - ) - - // attach events to clicks on the list items - - // delete the selected things from the database - if (authorized) { - $('.rc-delete').click(function() { - $('.rightclickmenu').remove() - Control.deleteSelected() - }) - } - - // remove the selected things from the map - if (Active.Topic || authorized) { - $('.rc-remove').click(function() { - $('.rightclickmenu').remove() - Control.removeSelectedEdges() - Control.removeSelectedNodes() - }) - } - - // hide selected nodes and synapses until refresh - $('.rc-hide').click(function() { - $('.rightclickmenu').remove() - Control.hideSelectedEdges() - Control.hideSelectedNodes() - }) - - // when in radial, center on the topic you picked - $('.rc-center').click(function() { - $('.rightclickmenu').remove() - Topic.centerOn(node.id) - }) - - // open the entity in a new tab - $('.rc-popout').click(function() { - $('.rightclickmenu').remove() - const win = window.open('/topics/' + node.id, '_blank') - win.focus() - }) - - // change the permission of all the selected nodes and synapses that you were the originator of - $('.rc-permission li').click(function() { - $('.rightclickmenu').remove() - // $(this).text() will be 'commons' 'public' or 'private' - Control.updateSelectedPermissions($(this).text()) - }) - - // fetch relatives - let fetchSent = false - $('.rc-siblings').hover(function() { - if (!fetchSent) { - JIT.populateRightClickSiblings(node) - fetchSent = true - } - }) - $('.rc-siblings .fetchAll').click(function() { - $('.rightclickmenu').remove() - // data-id is a metacode id - Topic.fetchRelatives(node) - }) }, // selectNodeOnRightClickHandler, - populateRightClickSiblings: function(node) { - // depending on how many topics are selected, do different things - const topic = node.getData('topic') - - // add a loading icon for now - const loader = new CanvasLoader('loadingSiblings') - loader.setColor('#4FC059') // default is '#000000' - loader.setDiameter(15) // default is 40 - loader.setDensity(41) // default is 40 - loader.setRange(0.9) // default is 1.3 - loader.show() // Hidden by default - - const topics = DataModel.Topics.map(function(t) { return t.id }) - const topicsString = topics.join() - - const successCallback = function(data) { - $('#loadingSiblings').remove() - - for (var key in data) { - const string = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})` - $('#fetchSiblingList').append(`
  • ${string}
  • `) - } - - $('.rc-siblings .getSiblings').click(function() { - $('.rightclickmenu').remove() - // data-id is a metacode id - Topic.fetchRelatives(node, $(this).attr('data-id')) - }) - } - - $.ajax({ - type: 'GET', - url: '/topics/' + topic.id + '/relative_numbers.json?network=' + topicsString, - success: successCallback, - error: function() {} - }) - }, selectEdgeOnClickHandler: function(adj, e) { if (Visualize.mGraph.busy) return diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js new file mode 100644 index 00000000..be799b4b --- /dev/null +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -0,0 +1,113 @@ +import Control from '../Control' +import DataModel from '../DataModel' +import ReactApp from '../GlobalUI/ReactApp' +import Selected from '../Selected' +import Topic from '../Topic' + +const ContextMenu = { + clickedNode: null, + clickedEdge: null, + pos: {x: 0, y: 0}, + siblingsData: null, + selectNode: (node, pos) => { + ContextMenu.pos = pos + ContextMenu.clickedNode = node + ContextMenu.clickedEdge = null + ContextMenu.siblingsData = null + ReactApp.render() + }, + selectEdge: (edge, pos) => { + ContextMenu.pos = pos + ContextMenu.clickedNode = null + ContextMenu.clickedEdge = edge + ContextMenu.siblingsData = null + ReactApp.render() + }, + reset: () => { + ContextMenu.siblingsData = null + ContextMenu.clickedNode = null + ContextMenu.clickedEdge = null + ReactApp.render() + }, + delete: Control.deleteSelected, + remove: () => { + Control.removeSelectedEdges() + Control.removeSelectedNodes() + ContextMenu.reset() + }, + hide: () => { + Control.hideSelectedEdges() + Control.hideSelectedNodes() + ContextMenu.reset() + }, + centerOn: (id) => { + Topic.centerOn(id) + ContextMenu.reset() + }, + popoutTopic: (id) => { + ContextMenu.reset() + const win = window.open('/topics/' + id, '_blank') + win.focus() + }, + updatePermissions: (permission) => { + // will be 'commons' 'public' or 'private' + Control.updateSelectedPermissions(permission) + ContextMenu.reset() + }, + onMetacodeSelect: (id, metacodeId) => { + if (Selected.Nodes.length > 1) { + // batch update multiple topics + Control.updateSelectedMetacodes(metacodeId) + } else { + const topic = DataModel.Topics.get(id) + topic.save({ + metacode_id: metacodeId + }) + } + ContextMenu.reset() + }, + fetchRelatives: (node, metacodeId) => { + Topic.fetchRelatives(node, metacodeId) + ContextMenu.reset() + }, + populateSiblings: function(id) { + // depending on how many topics are selected, do different things + + // add a loading icon for now + /*const loader = new CanvasLoader('loadingSiblings') + loader.setColor('#4FC059') // default is '#000000' + loader.setDiameter(15) // default is 40 + loader.setDensity(41) // default is 40 + loader.setRange(0.9) // default is 1.3 + loader.show() // Hidden by default*/ + + const topics = DataModel.Topics.map(function(t) { return t.id }) + const topicsString = topics.join() + + const successCallback = function(data) { + ContextMenu.siblingsData = data + ReactApp.render() + /*$('#loadingSiblings').remove() + + for (var key in data) { + const string = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})` + $('#fetchSiblingList').append(`
  • ${string}
  • `) + } + + $('.rc-siblings .getSiblings').click(function() { + $('.rightclickmenu').remove() + // data-id is a metacode id + Topic.fetchRelatives(node, $(this).attr('data-id')) + })*/ + } + + $.ajax({ + type: 'GET', + url: '/topics/' + id + '/relative_numbers.json?network=' + topicsString, + success: successCallback, + error: function() {} + }) + } +} + +export default ContextMenu diff --git a/frontend/src/Metamaps/Views/index.js b/frontend/src/Metamaps/Views/index.js index 0f7cf566..de9d5aab 100644 --- a/frontend/src/Metamaps/Views/index.js +++ b/frontend/src/Metamaps/Views/index.js @@ -1,5 +1,6 @@ /* global $ */ +import ContextMenu from './ContextMenu' import ExploreMaps from './ExploreMaps' import ChatView from './ChatView' import VideoView from './VideoView' @@ -12,6 +13,7 @@ const Views = { $(document).on(JUNTO_UPDATED, () => ExploreMaps.render()) ChatView.init([serverData['sounds/MM_sounds.mp3'], serverData['sounds/MM_sounds.ogg']]) }, + ContextMenu, ExploreMaps, ChatView, VideoView, @@ -19,5 +21,5 @@ const Views = { TopicCard } -export { ExploreMaps, ChatView, VideoView, Room, TopicCard } +export { ContextMenu, ExploreMaps, ChatView, VideoView, Room, TopicCard } export default Views diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js new file mode 100644 index 00000000..abc0bad4 --- /dev/null +++ b/frontend/src/components/common/ContextMenu.js @@ -0,0 +1,77 @@ +import React, { Component, PropTypes } from 'react' + +class ContextMenu extends Component { + static propTypes = { + node: PropTypes.object, + edge: PropTypes.object + } + + render () { + const style = { + position: 'absolute', + top: '10px', + left: '10px' + } + return
    + +
    + + // position the menu where the click happened + /*const position = {} + 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 = $(window).width() + const windowHeight = $(window).height() + + if (windowWidth - e.clientX < SUBMENUS_WIDTH) { + position.right = windowWidth - e.clientX + $(rightclickmenu).addClass('moveMenusToLeft') + } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) { + position.right = windowWidth - e.clientX + } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) { + position.left = e.clientX + $(rightclickmenu).addClass('moveMenusToLeft') + } else { + position.left = e.clientX + } + + if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) { + position.bottom = windowHeight - e.clientY + $(rightclickmenu).addClass('moveMenusUp') + } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) { + position.top = e.clientY + $(rightclickmenu).addClass('moveMenusUp') + } else { + position.top = e.clientY + }*/ + } +} + +export default ContextMenu From e290244404ebb8f81eb3f1a9ee8909aaa0c6ef7e Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Mon, 18 Sep 2017 23:30:33 -0400 Subject: [PATCH 02/11] beginning to come together --- frontend/src/Metamaps/GlobalUI/ReactApp.js | 24 +++++ frontend/src/Metamaps/Listeners.js | 4 +- frontend/src/Metamaps/Topic.js | 6 +- frontend/src/Metamaps/Views/ContextMenu.js | 43 ++++---- frontend/src/components/MapView/index.js | 9 +- frontend/src/components/TopicView/index.js | 5 +- frontend/src/components/common/ContextMenu.js | 99 ++++++++++++++++--- 7 files changed, 144 insertions(+), 46 deletions(-) diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index 3393a951..5290fe12 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -113,6 +113,7 @@ const ReactApp = { self.getFilterProps(), self.getCommonProps(), self.getMapsProps(), + self.getContextMenuProps(), self.getTopicCardProps(), self.getChatProps()) }, @@ -155,6 +156,29 @@ const ReactApp = { onTopicFollow: Topic.onTopicFollow } }, + getContextMenuProps: function() { + const self = ReactApp + return { + // values + contextMenu: !!(ContextMenu.clickedNode || ContextMenu.clickedEdge), + contextNode: ContextMenu.clickedNode, + contextEdge: ContextMenu.clickedEdge, + contextPos: ContextMenu.pos, + contextFetchingSiblingsData: ContextMenu.fetchingSiblingsData, + contextSiblingsData: ContextMenu.siblingsData, + // functions + contextReset: ContextMenu.reset, + contextDelete: ContextMenu.delete, + contextRemove: ContextMenu.remove, + contextHide: ContextMenu.hide, + contextCenterOn: ContextMenu.centerOn, + contextPopoutTopic: ContextMenu.popoutTopic, + contextUpdatePermissions: ContextMenu.updatePermissions, + contextOnMetacodeSelect: ContextMenu.onMetacodeSelect, + contextFetchSiblings: ContextMenu.fetchSiblings, + contextPopulateSiblings: ContextMenu.populateSiblings + } + }, getTopicProps: function() { const self = ReactApp return { diff --git a/frontend/src/Metamaps/Listeners.js b/frontend/src/Metamaps/Listeners.js index 352b5f7f..a5dc630f 100644 --- a/frontend/src/Metamaps/Listeners.js +++ b/frontend/src/Metamaps/Listeners.js @@ -152,12 +152,12 @@ const Listeners = { var node = nodes[nodes.length - 1] if (opts.center && opts.reveal) { Topic.centerOn(node.id, function() { - Topic.fetchRelatives(nodes) + Topic.fetchSiblings(nodes) }) } else if (opts.center) { Topic.centerOn(node.id) } else if (opts.reveal) { - Topic.fetchRelatives(nodes) + Topic.fetchSiblings(nodes) } } } diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 0054ad55..16b28570 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -74,7 +74,7 @@ const Topic = { } }, centerOn: function(nodeid, callback) { - // don't clash with fetchRelatives + // don't clash with fetchSiblings if (!Visualize.mGraph.busy) { Visualize.mGraph.onClick(nodeid, { hideLabels: false, @@ -100,7 +100,7 @@ const Topic = { } ReactApp.render() }, - fetchRelatives: function(nodes, metacodeId) { + fetchSiblings: function(nodes, metacodeId) { var self = this var node = $.isArray(nodes) ? nodes[0] : nodes @@ -156,7 +156,7 @@ const Topic = { }) }) if ($.isArray(nodes) && nodes.length > 1) { - self.fetchRelatives(nodes.slice(1), metacodeId) + self.fetchSiblings(nodes.slice(1), metacodeId) } } diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js index be799b4b..427a50f6 100644 --- a/frontend/src/Metamaps/Views/ContextMenu.js +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -1,6 +1,6 @@ import Control from '../Control' import DataModel from '../DataModel' -import ReactApp from '../GlobalUI/ReactApp' +import { ReactApp } from '../GlobalUI' import Selected from '../Selected' import Topic from '../Topic' @@ -8,11 +8,13 @@ const ContextMenu = { clickedNode: null, clickedEdge: null, pos: {x: 0, y: 0}, + fetchingSiblingsData: false, siblingsData: null, selectNode: (node, pos) => { ContextMenu.pos = pos ContextMenu.clickedNode = node ContextMenu.clickedEdge = null + ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null ReactApp.render() }, @@ -20,16 +22,21 @@ const ContextMenu = { ContextMenu.pos = pos ContextMenu.clickedNode = null ContextMenu.clickedEdge = edge + ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null ReactApp.render() }, reset: () => { + ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null ContextMenu.clickedNode = null ContextMenu.clickedEdge = null ReactApp.render() }, - delete: Control.deleteSelected, + delete: () => { + Control.deleteSelected() + ContextMenu.reset() + }, remove: () => { Control.removeSelectedEdges() Control.removeSelectedNodes() @@ -66,39 +73,27 @@ const ContextMenu = { } ContextMenu.reset() }, - fetchRelatives: (node, metacodeId) => { - Topic.fetchRelatives(node, metacodeId) + fetchSiblings: (node, metacodeId) => { + Topic.fetchSiblings(node, metacodeId) ContextMenu.reset() }, populateSiblings: function(id) { // depending on how many topics are selected, do different things - - // add a loading icon for now - /*const loader = new CanvasLoader('loadingSiblings') - loader.setColor('#4FC059') // default is '#000000' - loader.setDiameter(15) // default is 40 - loader.setDensity(41) // default is 40 - loader.setRange(0.9) // default is 1.3 - loader.show() // Hidden by default*/ + ContextMenu.fetchingSiblingsData = true + ReactApp.render() const topics = DataModel.Topics.map(function(t) { return t.id }) const topicsString = topics.join() const successCallback = function(data) { + ContextMenu.fetchingSiblingsData = false + + // adjust the data for consumption by react + for (var key in data) { + data[key] = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})` + } ContextMenu.siblingsData = data ReactApp.render() - /*$('#loadingSiblings').remove() - - for (var key in data) { - const string = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})` - $('#fetchSiblingList').append(`
  • ${string}
  • `) - } - - $('.rc-siblings .getSiblings').click(function() { - $('.rightclickmenu').remove() - // data-id is a metacode id - Topic.fetchRelatives(node, $(this).attr('data-id')) - })*/ } $.ajax({ diff --git a/frontend/src/components/MapView/index.js b/frontend/src/components/MapView/index.js index fe8c23b1..3e51691d 100644 --- a/frontend/src/components/MapView/index.js +++ b/frontend/src/components/MapView/index.js @@ -1,6 +1,7 @@ -import React, { Component } from 'react' + import React, { Component } from 'react' import PropTypes from 'prop-types' +import ContextMenu from '../common/ContextMenu' import DataVis from '../common/DataVis' import UpperOptions from '../common/UpperOptions' import InfoAndHelp from '../common/InfoAndHelp' @@ -9,9 +10,11 @@ import VisualizationControls from '../common/VisualizationControls' import MapChat from './MapChat' import TopicCard from '../TopicCard' + export default class MapView extends Component { static propTypes = { + contextMenu: PropTypes.bool, mobile: PropTypes.bool, mapId: PropTypes.string, map: PropTypes.object, @@ -79,7 +82,8 @@ export default class MapView extends Component { filterAllMappers, filterAllSynapses, filterData, openImportLightbox, forkMap, openHelpLightbox, mapIsStarred, onMapStar, onMapUnstar, openTopic, - onZoomExtents, onZoomIn, onZoomOut, hasLearnedTopicCreation } = this.props + onZoomExtents, onZoomIn, onZoomOut, hasLearnedTopicCreation, + contextMenu } = this.props const { chatOpen } = this.state const onChatOpen = () => { this.setState({chatOpen: true}) @@ -109,6 +113,7 @@ export default class MapView extends Component { filterAllSynapses={filterAllSynapses} /> {openTopic && } + {contextMenu && } {currentUser && } {currentUser && this.mapChat = x} />} this.upperOptions = x} @@ -73,6 +75,7 @@ export default class TopicView extends Component { filterAllSynapses={filterAllSynapses} /> + {contextMenu && } { + if (!this.state.populateSiblingsSent) { + contextPopulateSiblings(contextNode.id) + this.setState({populateSiblingsSent: true}) + } + } + + // TODO: add checks for logged in and other context return
    • -
      Hide until refresh
      Ctrl+H
      +
      Hide until refresh +
      Ctrl+H
    • -
      Remove from map
      Ctrl+M
      +
      Remove from map +
      Ctrl+M
    • -
      Delete
      Ctrl+D
      +
      Delete +
      Ctrl+D
    • -
      Open in new tab +
      Open in new tab
    • -
      Change permissions +
      Change permissions
        -
      • commons
      • -
      • public
      • -
      • private
      • +
      • +
        commons +
      • +
      • +
        public +
      • +
      • +
        private +
      -
      +
    • +
      Change metacode +
      + +
      +
    • + {contextNode && topicId &&
    • +
      Reveal siblings +
        +
      • contextFetchSiblings(contextNode)}>All +
        Alt+R
        +
      • + {contextSiblingsData && Object.keys(contextSiblingsData).map(key => { + return
      • contextFetchSiblings(contextNode, key)}> + {contextSiblingsData[key]} +
      • + })} + {contextFetchingSiblingsData &&
      • loading
      • } +
      +
      +
    • }
    From da9e44afa0b3256a307c6cbabcbfd2f64da2d80a Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Mon, 18 Sep 2017 23:49:35 -0400 Subject: [PATCH 03/11] clear the right click menu the right way --- frontend/src/Metamaps/JIT.js | 8 ++++---- frontend/src/Metamaps/Map/index.js | 3 ++- frontend/src/Metamaps/Topic.js | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index 7116854f..57247ea0 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -350,7 +350,7 @@ const JIT = { // Add also a click handler to nodes onClick: function(node, eventInfo, e) { // remove the rightclickmenu - $('.rightclickmenu').remove() + ContextMenu.reset() if (Mouse.boxStartCoordinates) { if (e.ctrlKey) { @@ -391,7 +391,7 @@ const JIT = { // Add also a click handler to nodes onRightClick: function(node, eventInfo, e) { // remove the rightclickmenu - $('.rightclickmenu').remove() + ContextMenu.reset() if (Mouse.boxStartCoordinates) { Create.newSynapse.hide() @@ -1007,7 +1007,7 @@ const JIT = { TopicCard.hideCard() SynapseCard.hideCard() Create.newTopic.hide() - $('.rightclickmenu').remove() + ContextMenu.reset() // reset the draw synapse positions to false Mouse.synapseStartCoordinates = [] Mouse.synapseEndCoordinates = null @@ -1412,7 +1412,7 @@ const JIT = { Control.selectEdge(adj) // delete old right click menu - $('.rightclickmenu').remove() + ContextMenu.reset() // create new menu for clicked on node const rightclickmenu = document.createElement('div') rightclickmenu.className = 'rightclickmenu' diff --git a/frontend/src/Metamaps/Map/index.js b/frontend/src/Metamaps/Map/index.js index 2db12263..0e3360ac 100644 --- a/frontend/src/Metamaps/Map/index.js +++ b/frontend/src/Metamaps/Map/index.js @@ -16,6 +16,7 @@ import Loading from '../Loading' import Realtime from '../Realtime' import Selected from '../Selected' import SynapseCard from '../SynapseCard' +import ContextMenu from '../Views/ContextMenu' import TopicCard from '../Views/TopicCard' import Visualize from '../Visualize' @@ -137,7 +138,7 @@ const Map = { if (Active.Map) { $('.main').removeClass('compressed') AutoLayout.resetSpiral() - $('.rightclickmenu').remove() + ContextMenu.reset() TopicCard.hideCard() SynapseCard.hideCard() Create.newTopic.hide(true) // true means force (and override pinned) diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 16b28570..976bb82d 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -15,6 +15,7 @@ import Selected from './Selected' import Settings from './Settings' import SynapseCard from './SynapseCard' import TopicCard from './Views/TopicCard' +import ContextMenu from './Views/ContextMenu' import Util from './Util' import Visualize from './Visualize' @@ -68,7 +69,7 @@ const Topic = { }, end: function() { if (Active.Topic) { - $('.rightclickmenu').remove() + ContextMenu.reset() TopicCard.hideCard() SynapseCard.hideCard() } From a04cd0d395f082d499274540da8896c11b2eb874 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 00:34:37 -0400 Subject: [PATCH 04/11] call the right callbacks and show in the right context --- frontend/src/Metamaps/GlobalUI/ReactApp.js | 1 - frontend/src/components/common/ContextMenu.js | 61 ++++++++++++------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index 5290fe12..6087960e 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -167,7 +167,6 @@ const ReactApp = { contextFetchingSiblingsData: ContextMenu.fetchingSiblingsData, contextSiblingsData: ContextMenu.siblingsData, // functions - contextReset: ContextMenu.reset, contextDelete: ContextMenu.delete, contextRemove: ContextMenu.remove, contextHide: ContextMenu.hide, diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js index 84c2b44d..1d4b6687 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/common/ContextMenu.js @@ -6,13 +6,15 @@ import MetacodeSelect from '../MetacodeSelect' class ContextMenu extends Component { static propTypes = { topicId: PropTypes.string, + mapId: PropTypes.string, + currentUser: PropTypes.object, + map: PropTypes.object, contextNode: PropTypes.object, contextEdge: PropTypes.object, contextPos: PropTypes.object, contextFetchingSiblingsData: PropTypes.bool, contextSiblingsData: PropTypes.object, metacodeSets: PropTypes.array, - contextReset: PropTypes.func, contextDelete: PropTypes.func, contextRemove: PropTypes.func, contextHide: PropTypes.func, @@ -33,9 +35,12 @@ class ContextMenu extends Component { render () { const { contextNode, contextPos, contextOnMetacodeSelect, metacodeSets, - contextSiblingsData, contextFetchSiblings, - contextPopulateSiblings, contextFetchingSiblingsData, - topicId } = this.props + contextDelete, contextHide, contextRemove, contextCenterOn, + contextPopoutTopic, contextSiblingsData, contextFetchSiblings, + currentUser, contextPopulateSiblings, contextFetchingSiblingsData, + topicId, map, mapId, contextUpdatePermissions } = this.props + + const canEditMap = map && map.authorizeToEdit(currentUser) const style = { position: 'absolute', top: contextPos.y + 'px', @@ -49,48 +54,60 @@ class ContextMenu extends Component { } } - // TODO: add checks for logged in and other context return
      -
    • +
    • Hide until refresh
      Ctrl+H
    • -
    • + {canEditMap &&
    • Remove from map
      Ctrl+M
      -
    • -
    • +
    • } + {canEditMap &&
    • Delete
      Ctrl+D
      -
    • -
    • +
    • } + {contextNode && topicId &&
    • contextCenterOn(contextNode.id)}> +
      Center this topic +
      Alt+E
      +
    • } + {contextNode &&
    • contextPopoutTopic(contextNode.id)}>
      Open in new tab -
    • -
    • -
    • +
    • } + {(currentUser || (contextNode && topicId)) &&
    • +
    • } + {currentUser &&
    • Change permissions
        -
      • +
      • contextUpdatePermissions('commons')}>
        commons
      • -
      • +
      • contextUpdatePermissions('public')}>
        public
      • -
      • +
      • contextUpdatePermissions('private')}>
        private
      -
    • -
    • +
    • } + {currentUser &&
    • Change metacode
      - { + contextOnMetacodeSelect(contextNode && contextNode.id, id) + }} metacodeSets={metacodeSets} />
      -
    • + } {contextNode && topicId &&
    • Reveal siblings @@ -105,7 +122,7 @@ class ContextMenu extends Component { {contextSiblingsData[key]}
    • })} - {contextFetchingSiblingsData &&
    • loading
    • } + {contextFetchingSiblingsData &&
    • loading...
    • }
    } From 25150733932eca46a509b7bbd8615cc51d87f7e5 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 00:46:23 -0400 Subject: [PATCH 05/11] replace old edgeRightClick code --- frontend/src/Metamaps/JIT.js | 103 +---------------------------------- 1 file changed, 2 insertions(+), 101 deletions(-) diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index 57247ea0..716dcb27 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -1399,113 +1399,14 @@ const JIT = { } }, // selectEdgeOnClickHandler selectEdgeOnRightClickHandler: function(adj, e) { - // the 'node' variable is a JIT node, the one that was clicked on + // the 'adj' variable is a JIT adjacency, the one that was clicked on // the 'e' variable is the click event - if (adj.getData('alpha') === 0) return // don't do anything if the edge is filtered - e.preventDefault() e.stopPropagation() - if (Visualize.mGraph.busy) return - Control.selectEdge(adj) - - // delete old right click menu - ContextMenu.reset() - // create new menu for clicked on node - const rightclickmenu = document.createElement('div') - rightclickmenu.className = 'rightclickmenu' - // prevent the custom context menu from immediately opening the default context menu as well - rightclickmenu.setAttribute('oncontextmenu', 'return false') - - // add the proper options to the menu - let menustring = '
      ' - - const authorized = Active.Map && Active.Map.authorizeToEdit(Active.Mapper) - - const disabled = authorized ? '' : 'disabled' - - if (Active.Map) menustring += '
    • Hide until refresh
      Ctrl+H
    • ' - if (Active.Map && Active.Mapper) menustring += '
    • Remove from map
      Ctrl+M
    • ' - if (Active.Topic) menustring += '
    • Remove from view
      Ctrl+M
    • ' - if (Active.Map && Active.Mapper) menustring += '
    • Delete
      Ctrl+D
    • ' - - if (Active.Map && Active.Mapper) menustring += '
    • ' - - if (Active.Mapper) { - const permOptions = outdent` -
        -
      • commons
      • -
      • public
      • private
      ` - - menustring += '
    • Change permissions' + permOptions + '
    • ' - } - - menustring += '
    ' - rightclickmenu.innerHTML = menustring - - // position the menu where the click happened - const position = {} - 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 = $(window).width() - const windowHeight = $(window).height() - - if (windowWidth - e.clientX < SUBMENUS_WIDTH) { - position.right = windowWidth - e.clientX - $(rightclickmenu).addClass('moveMenusToLeft') - } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) { - position.right = windowWidth - e.clientX - } else position.left = e.clientX - - if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) { - position.bottom = windowHeight - e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) { - position.top = e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else position.top = e.clientY - - $(rightclickmenu).css(position) - - // add the menu to the page - $('#wrapper').append(rightclickmenu) - - // attach events to clicks on the list items - - // delete the selected things from the database - if (authorized) { - $('.rc-delete').click(function() { - $('.rightclickmenu').remove() - Control.deleteSelected() - }) - } - - // remove the selected things from the map - if (authorized) { - $('.rc-remove').click(function() { - $('.rightclickmenu').remove() - Control.removeSelectedEdges() - Control.removeSelectedNodes() - }) - } - - // hide selected nodes and synapses until refresh - $('.rc-hide').click(function() { - $('.rightclickmenu').remove() - Control.hideSelectedEdges() - Control.hideSelectedNodes() - }) - - // change the permission of all the selected nodes and synapses that you were the originator of - $('.rc-permission li').click(function() { - $('.rightclickmenu').remove() - // $(this).text() will be 'commons' 'public' or 'private' - Control.updateSelectedPermissions($(this).text()) - }) + ContextMenu.selectEdge(adj, {x: e.clientX, y: e.clientY}) }, // selectEdgeOnRightClickHandler SmoothPanning: function() { const sx = Visualize.mGraph.canvas.scaleOffsetX From 5f1fe4dc3f86421947a26faae576d380e3f1eeec Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 08:37:00 -0400 Subject: [PATCH 06/11] reimplement shifting menu according to click position --- frontend/src/components/common/ContextMenu.js | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js index 1d4b6687..273672b5 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/common/ContextMenu.js @@ -33,6 +33,49 @@ class ContextMenu extends Component { } } + getPositionData = () => { + const { contextPos } = this.props + let extraClasses = [] + const position = {} + 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 + } + } + render () { const { contextNode, contextPos, contextOnMetacodeSelect, metacodeSets, contextDelete, contextHide, contextRemove, contextCenterOn, @@ -41,12 +84,8 @@ class ContextMenu extends Component { topicId, map, mapId, contextUpdatePermissions } = this.props const canEditMap = map && map.authorizeToEdit(currentUser) - const style = { - position: 'absolute', - top: contextPos.y + 'px', - left: contextPos.x + 'px' - } - + const positionData = this.getPositionData() + const style = Object.assign({}, {position: 'absolute'}, positionData.pos) const populateSiblings = () => { if (!this.state.populateSiblingsSent) { contextPopulateSiblings(contextNode.id) @@ -54,7 +93,8 @@ class ContextMenu extends Component { } } - return
    + return
    • Hide until refresh @@ -128,37 +168,6 @@ class ContextMenu extends Component {
    • }
    - - // position the menu where the click happened - /*const position = {} - 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 = $(window).width() - const windowHeight = $(window).height() - - if (windowWidth - e.clientX < SUBMENUS_WIDTH) { - position.right = windowWidth - e.clientX - $(rightclickmenu).addClass('moveMenusToLeft') - } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) { - position.right = windowWidth - e.clientX - } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) { - position.left = e.clientX - $(rightclickmenu).addClass('moveMenusToLeft') - } else { - position.left = e.clientX - } - - if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) { - position.bottom = windowHeight - e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) { - position.top = e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else { - position.top = e.clientY - }*/ } } From a4c905df4e1cd04efbcdb4780108fab665771d9f Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 09:04:21 -0400 Subject: [PATCH 07/11] move each li into its own function --- frontend/src/components/common/ContextMenu.js | 216 +++++++++++------- 1 file changed, 138 insertions(+), 78 deletions(-) diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js index 273672b5..b5431c56 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/common/ContextMenu.js @@ -76,96 +76,156 @@ class ContextMenu extends Component { } } - render () { - const { contextNode, contextPos, contextOnMetacodeSelect, metacodeSets, - contextDelete, contextHide, contextRemove, contextCenterOn, - contextPopoutTopic, contextSiblingsData, contextFetchSiblings, - currentUser, contextPopulateSiblings, contextFetchingSiblingsData, - topicId, map, mapId, contextUpdatePermissions } = this.props + hide = () => { + const { contextHide } = this.props + return
  • +
    Hide until refresh +
    Ctrl+H
    +
  • + } + remove = () => { + const { contextRemove, map, currentUser } = this.props const canEditMap = map && map.authorizeToEdit(currentUser) - const positionData = this.getPositionData() - const style = Object.assign({}, {position: 'absolute'}, positionData.pos) + if (!canEditMap) { + return null + } + return
  • +
    Remove from map +
    Ctrl+M
    +
  • + } + + delete = () => { + const { contextDelete, map, currentUser } = this.props + const canEditMap = map && map.authorizeToEdit(currentUser) + if (!canEditMap) { + return null + } + return
  • +
    Delete +
    Ctrl+D
    +
  • + } + + center = () => { + const { contextCenterOn, contextNode, topicId } = this.props + if (!(contextNode && topicId)) { + return null + } + return
  • contextCenterOn(contextNode.id)}> +
    Center this topic +
    Alt+E
    +
  • + } + + popout = () => { + const { contextPopoutTopic, contextNode } = this.props + if (!contextNode) { + return null + } + return
  • contextPopoutTopic(contextNode.id)}> +
    Open in new tab +
  • + } + + permission = () => { + const { currentUser, contextUpdatePermissions } = this.props + if (!currentUser) { + return null + } + return
  • +
    Change permissions +
      +
    • contextUpdatePermissions('commons')}> +
      commons +
    • +
    • contextUpdatePermissions('public')}> +
      public +
    • +
    • contextUpdatePermissions('private')}> +
      private +
    • +
    +
    +
  • + } + + metacode = () => { + const { metacodeSets, contextOnMetacodeSelect, + currentUser, contextNode } = this.props + if (!currentUser) { + return null + } + return
  • +
    Change metacode +
    + { + contextOnMetacodeSelect(contextNode && contextNode.id, id) + }} + metacodeSets={metacodeSets} /> +
    +
    +
  • + } + + siblings = () => { + const { contextPopulateSiblings, contextFetchSiblings, + contextSiblingsData, contextFetchingSiblingsData, + topicId, contextNode } = this.props const populateSiblings = () => { if (!this.state.populateSiblingsSent) { contextPopulateSiblings(contextNode.id) this.setState({populateSiblingsSent: true}) } } + if (!(contextNode && topicId)) { + return null + } + return
  • +
    Reveal siblings +
      +
    • contextFetchSiblings(contextNode)}>All +
      Alt+R
      +
    • + {contextSiblingsData && Object.keys(contextSiblingsData).map(key => { + return
    • contextFetchSiblings(contextNode, key)}> + {contextSiblingsData[key]} +
    • + })} + {contextFetchingSiblingsData &&
    • loading...
    • } +
    +
    +
  • + } + + render () { + const { contextNode, currentUser, topicId } = this.props + const positionData = this.getPositionData() + const style = Object.assign({}, {position: 'absolute'}, positionData.pos) + const showSpacer = currentUser || (contextNode && topicId) return
      -
    • -
      Hide until refresh -
      Ctrl+H
      -
    • - {canEditMap &&
    • -
      Remove from map -
      Ctrl+M
      -
    • } - {canEditMap &&
    • -
      Delete -
      Ctrl+D
      -
    • } - {contextNode && topicId &&
    • contextCenterOn(contextNode.id)}> -
      Center this topic -
      Alt+E
      -
    • } - {contextNode &&
    • contextPopoutTopic(contextNode.id)}> -
      Open in new tab -
    • } - {(currentUser || (contextNode && topicId)) &&
    • -
    • } - {currentUser &&
    • -
      Change permissions -
        -
      • contextUpdatePermissions('commons')}> -
        commons -
      • -
      • contextUpdatePermissions('public')}> -
        public -
      • -
      • contextUpdatePermissions('private')}> -
        private -
      • -
      -
      -
    • } - {currentUser &&
    • -
      Change metacode -
      - { - contextOnMetacodeSelect(contextNode && contextNode.id, id) - }} - metacodeSets={metacodeSets} /> -
      -
      -
    • } - {contextNode && topicId &&
    • -
      Reveal siblings -
        -
      • contextFetchSiblings(contextNode)}>All -
        Alt+R
        -
      • - {contextSiblingsData && Object.keys(contextSiblingsData).map(key => { - return
      • contextFetchSiblings(contextNode, key)}> - {contextSiblingsData[key]} -
      • - })} - {contextFetchingSiblingsData &&
      • loading...
      • } -
      -
      -
    • } + {this.hide()} + {this.remove()} + {this.delete()} + {this.center()} + {this.popout()} + {showSpacer &&
    • } + {this.permission()} + {this.metacode()} + {this.siblings()}
    } From 3406f2e04dc7011ce60a3f63cabdb350a991abce Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 09:58:59 -0400 Subject: [PATCH 08/11] make render a callback and use async apply for ease of use --- frontend/src/Metamaps/GlobalUI/ReactApp.js | 21 ++++----- frontend/src/Metamaps/JIT.js | 10 ++--- frontend/src/Metamaps/Map/index.js | 2 +- frontend/src/Metamaps/Topic.js | 2 +- frontend/src/Metamaps/Views/ContextMenu.js | 51 +++++++++++----------- package.json | 1 + 6 files changed, 44 insertions(+), 43 deletions(-) diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index 6087960e..5cfbf255 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -4,6 +4,7 @@ import React from 'react' import ReactDOM from 'react-dom' import { Router, browserHistory } from 'react-router' import { merge } from 'lodash' +import apply from 'async/apply' import { notifyUser } from './index.js' import ImportDialog from './ImportDialog' @@ -157,7 +158,7 @@ const ReactApp = { } }, getContextMenuProps: function() { - const self = ReactApp + const { render } = ReactApp return { // values contextMenu: !!(ContextMenu.clickedNode || ContextMenu.clickedEdge), @@ -167,15 +168,15 @@ const ReactApp = { contextFetchingSiblingsData: ContextMenu.fetchingSiblingsData, contextSiblingsData: ContextMenu.siblingsData, // functions - contextDelete: ContextMenu.delete, - contextRemove: ContextMenu.remove, - contextHide: ContextMenu.hide, - contextCenterOn: ContextMenu.centerOn, - contextPopoutTopic: ContextMenu.popoutTopic, - contextUpdatePermissions: ContextMenu.updatePermissions, - contextOnMetacodeSelect: ContextMenu.onMetacodeSelect, - contextFetchSiblings: ContextMenu.fetchSiblings, - contextPopulateSiblings: ContextMenu.populateSiblings + contextDelete: apply(ContextMenu.delete, render), + contextRemove: apply(ContextMenu.remove, render), + contextHide: apply(ContextMenu.hide, render), + contextCenterOn: apply(ContextMenu.centerOn, render), + contextPopoutTopic: apply(ContextMenu.popoutTopic, render), + contextUpdatePermissions: apply(ContextMenu.updatePermissions, render), + contextOnMetacodeSelect: apply(ContextMenu.onMetacodeSelect, render), + contextFetchSiblings: apply(ContextMenu.fetchSiblings, render), + contextPopulateSiblings: apply(ContextMenu.populateSiblings, render) } }, getTopicProps: function() { diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index 716dcb27..c8da91f8 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -350,7 +350,7 @@ const JIT = { // Add also a click handler to nodes onClick: function(node, eventInfo, e) { // remove the rightclickmenu - ContextMenu.reset() + ContextMenu.reset(ReactApp.render) if (Mouse.boxStartCoordinates) { if (e.ctrlKey) { @@ -391,7 +391,7 @@ const JIT = { // Add also a click handler to nodes onRightClick: function(node, eventInfo, e) { // remove the rightclickmenu - ContextMenu.reset() + ContextMenu.reset(ReactApp.render) if (Mouse.boxStartCoordinates) { Create.newSynapse.hide() @@ -1007,7 +1007,7 @@ const JIT = { TopicCard.hideCard() SynapseCard.hideCard() Create.newTopic.hide() - ContextMenu.reset() + ContextMenu.reset(ReactApp.render) // reset the draw synapse positions to false Mouse.synapseStartCoordinates = [] Mouse.synapseEndCoordinates = null @@ -1355,7 +1355,7 @@ const JIT = { // select the node Control.selectNode(node, e) - ContextMenu.selectNode(node, {x: e.clientX, y: e.clientY}) + ContextMenu.selectNode(ReactApp.render, node, {x: e.clientX, y: e.clientY}) }, // selectNodeOnRightClickHandler, selectEdgeOnClickHandler: function(adj, e) { @@ -1406,7 +1406,7 @@ const JIT = { e.stopPropagation() if (Visualize.mGraph.busy) return Control.selectEdge(adj) - ContextMenu.selectEdge(adj, {x: e.clientX, y: e.clientY}) + ContextMenu.selectEdge(ReactApp.render, adj, {x: e.clientX, y: e.clientY}) }, // selectEdgeOnRightClickHandler SmoothPanning: function() { const sx = Visualize.mGraph.canvas.scaleOffsetX diff --git a/frontend/src/Metamaps/Map/index.js b/frontend/src/Metamaps/Map/index.js index 0e3360ac..f337d1d0 100644 --- a/frontend/src/Metamaps/Map/index.js +++ b/frontend/src/Metamaps/Map/index.js @@ -138,7 +138,7 @@ const Map = { if (Active.Map) { $('.main').removeClass('compressed') AutoLayout.resetSpiral() - ContextMenu.reset() + ContextMenu.reset(ReactApp.render) TopicCard.hideCard() SynapseCard.hideCard() Create.newTopic.hide(true) // true means force (and override pinned) diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 976bb82d..1a8a7414 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -69,7 +69,7 @@ const Topic = { }, end: function() { if (Active.Topic) { - ContextMenu.reset() + ContextMenu.reset(ReactApp.render) TopicCard.hideCard() SynapseCard.hideCard() } diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js index 427a50f6..0eae9b88 100644 --- a/frontend/src/Metamaps/Views/ContextMenu.js +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -1,6 +1,5 @@ import Control from '../Control' import DataModel from '../DataModel' -import { ReactApp } from '../GlobalUI' import Selected from '../Selected' import Topic from '../Topic' @@ -10,58 +9,58 @@ const ContextMenu = { pos: {x: 0, y: 0}, fetchingSiblingsData: false, siblingsData: null, - selectNode: (node, pos) => { + selectNode: (render, node, pos) => { ContextMenu.pos = pos ContextMenu.clickedNode = node ContextMenu.clickedEdge = null ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null - ReactApp.render() + render() }, - selectEdge: (edge, pos) => { + selectEdge: (render, edge, pos) => { ContextMenu.pos = pos ContextMenu.clickedNode = null ContextMenu.clickedEdge = edge ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null - ReactApp.render() + render() }, - reset: () => { + reset: (render) => { ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null ContextMenu.clickedNode = null ContextMenu.clickedEdge = null - ReactApp.render() + render() }, - delete: () => { + delete: (render) => { Control.deleteSelected() - ContextMenu.reset() + ContextMenu.reset(render) }, - remove: () => { + remove: (render) => { Control.removeSelectedEdges() Control.removeSelectedNodes() - ContextMenu.reset() + ContextMenu.reset(render) }, - hide: () => { + hide: (render) => { Control.hideSelectedEdges() Control.hideSelectedNodes() - ContextMenu.reset() + ContextMenu.reset(render) }, - centerOn: (id) => { + centerOn: (render, id) => { Topic.centerOn(id) - ContextMenu.reset() + ContextMenu.reset(render) }, - popoutTopic: (id) => { - ContextMenu.reset() + popoutTopic: (render, id) => { + ContextMenu.reset(render) const win = window.open('/topics/' + id, '_blank') win.focus() }, - updatePermissions: (permission) => { + updatePermissions: (render, permission) => { // will be 'commons' 'public' or 'private' Control.updateSelectedPermissions(permission) - ContextMenu.reset() + ContextMenu.reset(render) }, - onMetacodeSelect: (id, metacodeId) => { + onMetacodeSelect: (render, id, metacodeId) => { if (Selected.Nodes.length > 1) { // batch update multiple topics Control.updateSelectedMetacodes(metacodeId) @@ -71,16 +70,16 @@ const ContextMenu = { metacode_id: metacodeId }) } - ContextMenu.reset() + ContextMenu.reset(render) }, - fetchSiblings: (node, metacodeId) => { + fetchSiblings: (render, node, metacodeId) => { Topic.fetchSiblings(node, metacodeId) - ContextMenu.reset() + ContextMenu.reset(render) }, - populateSiblings: function(id) { + populateSiblings: (render, id) => { // depending on how many topics are selected, do different things ContextMenu.fetchingSiblingsData = true - ReactApp.render() + render() const topics = DataModel.Topics.map(function(t) { return t.id }) const topicsString = topics.join() @@ -93,7 +92,7 @@ const ContextMenu = { data[key] = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})` } ContextMenu.siblingsData = data - ReactApp.render() + render() } $.ajax({ diff --git a/package.json b/package.json index f0afc5f7..3be728c4 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "homepage": "https://github.com/metamaps/metamaps#readme", "dependencies": { "ajaxq": "0.0.7", + "async": "2.5.0", "attachmediastream": "2.0.0", "autolinker": "1.4.3", "babel-cli": "6.26.0", From c4d0bf8ce4065bd300bac160dfc8b68745bdcbc6 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 10:02:05 -0400 Subject: [PATCH 09/11] clean up jquery ref and strings --- frontend/src/Metamaps/Topic.js | 4 ++-- frontend/src/Metamaps/Views/ContextMenu.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 1a8a7414..1b1abe88 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -104,7 +104,7 @@ const Topic = { fetchSiblings: function(nodes, metacodeId) { var self = this - var node = $.isArray(nodes) ? nodes[0] : nodes + var node = Array.isArray(nodes) ? nodes[0] : nodes var topics = DataModel.Topics.map(function(t) { return t.id }) var topicsString = topics.join() @@ -156,7 +156,7 @@ const Topic = { } }) }) - if ($.isArray(nodes) && nodes.length > 1) { + if (Array.isArray(nodes) && nodes.length > 1) { self.fetchSiblings(nodes.slice(1), metacodeId) } } diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js index 0eae9b88..3aa9784a 100644 --- a/frontend/src/Metamaps/Views/ContextMenu.js +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -52,7 +52,7 @@ const ContextMenu = { }, popoutTopic: (render, id) => { ContextMenu.reset(render) - const win = window.open('/topics/' + id, '_blank') + const win = window.open(`/topics/${id}`, '_blank') win.focus() }, updatePermissions: (render, permission) => { From 0d6963ebb00cc6e9769f7c9aab2bbeca6d112e37 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 10:16:14 -0400 Subject: [PATCH 10/11] eslint --- frontend/src/Metamaps/JIT.js | 12 +-- frontend/src/Metamaps/Views/ContextMenu.js | 1 + frontend/src/components/MapView/index.js | 3 +- frontend/src/components/common/ContextMenu.js | 74 +++++++++---------- 4 files changed, 40 insertions(+), 50 deletions(-) diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index c8da91f8..b0aee7ea 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -1,15 +1,10 @@ -/* global $, Image, CanvasLoader */ +/* global $, Image */ import _ from 'lodash' -import outdent from 'outdent' import clipboard from 'clipboard-js' -import React from 'react' -import ReactDOM from 'react-dom' import $jit from '../patched/JIT' -import MetacodeSelect from '../components/MetacodeSelect' - import Active from './Active' import ContextMenu from './Views/ContextMenu' import Control from './Control' @@ -1347,16 +1342,11 @@ const JIT = { selectNodeOnRightClickHandler: function(node, e) { // the 'node' variable is a JIT node, the one that was clicked on // the 'e' variable is the click event - e.preventDefault() e.stopPropagation() - if (Visualize.mGraph.busy) return - - // select the node Control.selectNode(node, e) ContextMenu.selectNode(ReactApp.render, node, {x: e.clientX, y: e.clientY}) - }, // selectNodeOnRightClickHandler, selectEdgeOnClickHandler: function(adj, e) { if (Visualize.mGraph.busy) return diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js index 3aa9784a..74fa1751 100644 --- a/frontend/src/Metamaps/Views/ContextMenu.js +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -1,3 +1,4 @@ +/* global $ */ import Control from '../Control' import DataModel from '../DataModel' import Selected from '../Selected' diff --git a/frontend/src/components/MapView/index.js b/frontend/src/components/MapView/index.js index 3e51691d..d2cc90fd 100644 --- a/frontend/src/components/MapView/index.js +++ b/frontend/src/components/MapView/index.js @@ -1,4 +1,4 @@ - import React, { Component } from 'react' +import React, { Component } from 'react' import PropTypes from 'prop-types' import ContextMenu from '../common/ContextMenu' @@ -10,7 +10,6 @@ import VisualizationControls from '../common/VisualizationControls' import MapChat from './MapChat' import TopicCard from '../TopicCard' - export default class MapView extends Component { static propTypes = { diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js index b5431c56..5ffc7755 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/common/ContextMenu.js @@ -78,9 +78,9 @@ class ContextMenu extends Component { hide = () => { const { contextHide } = this.props - return
  • -
    Hide until refresh -
    Ctrl+H
    + return
  • +
    Hide until refresh +
    Ctrl+H
  • } @@ -90,9 +90,9 @@ class ContextMenu extends Component { if (!canEditMap) { return null } - return
  • -
    Remove from map -
    Ctrl+M
    + return
  • +
    Remove from map +
    Ctrl+M
  • } @@ -102,9 +102,9 @@ class ContextMenu extends Component { if (!canEditMap) { return null } - return
  • -
    Delete -
    Ctrl+D
    + return
  • +
    Delete +
    Ctrl+D
  • } @@ -113,10 +113,10 @@ class ContextMenu extends Component { if (!(contextNode && topicId)) { return null } - return
  • contextCenterOn(contextNode.id)}> -
    Center this topic -
    Alt+E
    +
    Center this topic +
    Alt+E
  • } @@ -125,9 +125,9 @@ class ContextMenu extends Component { if (!contextNode) { return null } - return
  • contextPopoutTopic(contextNode.id)}> -
    Open in new tab +
    Open in new tab
  • } @@ -136,23 +136,23 @@ class ContextMenu extends Component { if (!currentUser) { return null } - return
  • -
    Change permissions + return
  • +
    Change permissions
      -
    • contextUpdatePermissions('commons')}> -
      commons +
      commons
    • -
    • contextUpdatePermissions('public')}> -
      public +
      public
    • -
    • contextUpdatePermissions('private')}> -
      private +
      private
    -
    +
  • } @@ -162,16 +162,16 @@ class ContextMenu extends Component { if (!currentUser) { return null } - return
  • -
    Change metacode -
    + return
  • +
    Change metacode +
    { contextOnMetacodeSelect(contextNode && contextNode.id, id) }} metacodeSets={metacodeSets} />
    -
    +
  • } @@ -188,13 +188,13 @@ class ContextMenu extends Component { if (!(contextNode && topicId)) { return null } - return
  • -
    Reveal siblings -
      -
    • Reveal siblings +
        +
      • contextFetchSiblings(contextNode)}>All -
        Alt+R
        +
        Alt+R
      • {contextSiblingsData && Object.keys(contextSiblingsData).map(key => { return
      • })} - {contextFetchingSiblingsData &&
      • loading...
      • } + {contextFetchingSiblingsData &&
      • loading...
      • }
      -
      +
    • } - render () { + render() { const { contextNode, currentUser, topicId } = this.props const positionData = this.getPositionData() const style = Object.assign({}, {position: 'absolute'}, positionData.pos) const showSpacer = currentUser || (contextNode && topicId) return
      + className={'rightclickmenu ' + positionData.extraClasses.join(' ')}>
        {this.hide()} {this.remove()} {this.delete()} {this.center()} {this.popout()} - {showSpacer &&
      • } + {showSpacer &&
      • } {this.permission()} {this.metacode()} {this.siblings()} From 3c7c8812a4d384a9d9c1f276c2fe353289af666a Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 11:46:19 -0400 Subject: [PATCH 11/11] tempplate strings and new lines --- frontend/src/Metamaps/Views/ContextMenu.js | 2 +- frontend/src/components/common/ContextMenu.js | 42 ++++++++++++------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js index 74fa1751..f1553706 100644 --- a/frontend/src/Metamaps/Views/ContextMenu.js +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -98,7 +98,7 @@ const ContextMenu = { $.ajax({ type: 'GET', - url: '/topics/' + id + '/relative_numbers.json?network=' + topicsString, + url: `/topics/${id}/relative_numbers.json?network=${topicsString}`, success: successCallback, error: function() {} }) diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js index 5ffc7755..e3649e5d 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/common/ContextMenu.js @@ -37,6 +37,8 @@ class ContextMenu extends Component { const { contextPos } = this.props let extraClasses = [] const position = {} + // TODO: make these dynamic values so that the ContextMenu can + // change height and still work properly const RIGHTCLICK_WIDTH = 300 const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static const SUBMENUS_WIDTH = 256 @@ -79,8 +81,9 @@ class ContextMenu extends Component { hide = () => { const { contextHide } = this.props return
      • -
        Hide until refresh -
        Ctrl+H
        +
        + Hide until refresh +
        Ctrl+H
      • } @@ -91,8 +94,9 @@ class ContextMenu extends Component { return null } return
      • -
        Remove from map -
        Ctrl+M
        +
        + Remove from map +
        Ctrl+M
      • } @@ -103,7 +107,8 @@ class ContextMenu extends Component { return null } return
      • -
        Delete +
        + Delete
        Ctrl+D
      • } @@ -115,7 +120,8 @@ class ContextMenu extends Component { } return
      • contextCenterOn(contextNode.id)}> -
        Center this topic +
        + Center this topic
        Alt+E
      • } @@ -127,7 +133,8 @@ class ContextMenu extends Component { } return
      • contextPopoutTopic(contextNode.id)}> -
        Open in new tab +
        + Open in new tab
      • } @@ -137,19 +144,23 @@ class ContextMenu extends Component { return null } return
      • -
        Change permissions +
        + Change permissions
        • contextUpdatePermissions('commons')}> -
          commons +
          + commons
        • contextUpdatePermissions('public')}> -
          public +
          + public
        • contextUpdatePermissions('private')}> -
          private +
          + private
        @@ -163,7 +174,8 @@ class ContextMenu extends Component { return null } return
      • -
        Change metacode +
        + Change metacode
        { @@ -190,10 +202,12 @@ class ContextMenu extends Component { } return
      • -
        Reveal siblings +
        + Reveal siblings
        • contextFetchSiblings(contextNode)}>All + onClick={() => contextFetchSiblings(contextNode)}> + All
          Alt+R
        • {contextSiblingsData && Object.keys(contextSiblingsData).map(key => {