Merge pull request #661 from metamaps/feature/file-upload-component
set up a dialog box to help with import/export
This commit is contained in:
commit
0764133d11
22 changed files with 261 additions and 84 deletions
|
@ -1,6 +1,11 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"parser": "babel-eslint",
|
"parser": "babel-eslint",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"extends": "standard",
|
"extends": "standard",
|
||||||
"installedESLint": true,
|
"installedESLint": true,
|
||||||
"env": {
|
"env": {
|
||||||
|
@ -13,6 +18,8 @@ module.exports = {
|
||||||
"react"
|
"react"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"react/jsx-uses-react": [2],
|
||||||
|
"react/jsx-uses-vars": [2],
|
||||||
"yoda": [2, "never", { "exceptRange": true }]
|
"yoda": [2, "never", { "exceptRange": true }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
app/assets/images/import-example.png
Normal file
BIN
app/assets/images/import-example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
BIN
app/assets/images/import.png
Normal file
BIN
app/assets/images/import.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 325 B |
|
@ -15,6 +15,7 @@ Metamaps.Erb['icons/wildcard.png'] = '<%= asset_path('icons/wildcard.png') %>'
|
||||||
Metamaps.Erb['topic_description_signifier.png'] = '<%= asset_path('topic_description_signifier.png') %>'
|
Metamaps.Erb['topic_description_signifier.png'] = '<%= asset_path('topic_description_signifier.png') %>'
|
||||||
Metamaps.Erb['topic_link_signifier.png'] = '<%= asset_path('topic_link_signifier.png') %>'
|
Metamaps.Erb['topic_link_signifier.png'] = '<%= asset_path('topic_link_signifier.png') %>'
|
||||||
Metamaps.Erb['synapse16.png'] = '<%= asset_path('synapse16.png') %>'
|
Metamaps.Erb['synapse16.png'] = '<%= asset_path('synapse16.png') %>'
|
||||||
|
Metamaps.Erb['import-example.png'] = '<%= asset_path('import-example.png') %>'
|
||||||
Metamaps.Erb['sounds/MM_sounds.mp3'] = '<%= asset_path 'sounds/MM_sounds.mp3' %>'
|
Metamaps.Erb['sounds/MM_sounds.mp3'] = '<%= asset_path 'sounds/MM_sounds.mp3' %>'
|
||||||
Metamaps.Erb['sounds/MM_sounds.ogg'] = '<%= asset_path 'sounds/MM_sounds.ogg' %>'
|
Metamaps.Erb['sounds/MM_sounds.ogg'] = '<%= asset_path 'sounds/MM_sounds.ogg' %>'
|
||||||
Metamaps.Metacodes = <%= Metacode.all.to_json.gsub(%r[(icon.*?)(\"},)], '\1?purple=stupid\2').html_safe %>
|
Metamaps.Metacodes = <%= Metacode.all.to_json.gsub(%r[(icon.*?)(\"},)], '\1?purple=stupid\2').html_safe %>
|
||||||
|
|
|
@ -1526,9 +1526,8 @@ h3.filterBox {
|
||||||
background-image: url(<%= asset_data_uri('permissions32_sprite.png') %>);
|
background-image: url(<%= asset_data_uri('permissions32_sprite.png') %>);
|
||||||
}
|
}
|
||||||
/* map info box */
|
/* map info box */
|
||||||
/* map info box */
|
|
||||||
|
|
||||||
.wrapper div.mapInfoBox {
|
.wrapper .mapInfoBox {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 40px;
|
bottom: 40px;
|
||||||
|
@ -1536,12 +1535,40 @@ h3.filterBox {
|
||||||
background-color: #424242;
|
background-color: #424242;
|
||||||
color: #F5F5F5;
|
color: #F5F5F5;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 3px 3px rgba(0,0,0,0.23), 0px 3px 3px rgba(0,0,0,0.16);
|
||||||
|
text-align: center;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
.import-dialog{
|
||||||
|
button {
|
||||||
|
margin: 1em 0.5em;
|
||||||
|
}
|
||||||
|
.import-blue-button {
|
||||||
|
display: inline-block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0.75em;
|
||||||
|
padding: 0.75em;
|
||||||
|
height: 3em;
|
||||||
|
background-color: #AAB0FB;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.fileupload {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0.75em;
|
||||||
|
padding: 0.75em;
|
||||||
|
height: 3em;
|
||||||
|
border: 3px dashed #AAB0FB;
|
||||||
|
width: 75%;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.wrapper .mapInfoBox {
|
||||||
width: 360px;
|
width: 360px;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-style: normal;
|
|
||||||
text-align: center;
|
|
||||||
box-shadow: 0 3px 3px rgba(0,0,0,0.23), 0px 3px 3px rgba(0,0,0,0.16);
|
|
||||||
}
|
}
|
||||||
.requestTitle {
|
.requestTitle {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -2028,17 +2055,17 @@ and it won't be important on password protected instances */
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: fixed;
|
position: absolute;
|
||||||
z-index: 1000000;
|
z-index: 1000000;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#lightbox_main {
|
#lightbox_main {
|
||||||
width: 800px;
|
width: 800px;
|
||||||
height: auto;
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 50%;
|
top: 5vh;
|
||||||
|
height: 90vh;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
@ -2077,8 +2104,11 @@ and it won't be important on password protected instances */
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
}
|
}
|
||||||
#lightbox_content {
|
#lightbox_content {
|
||||||
width: 552px;
|
width: 800px;
|
||||||
height: 434px;
|
height: 500px;
|
||||||
|
max-height: 90vh;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-y: auto;
|
||||||
background-color: #e0e0e0;
|
background-color: #e0e0e0;
|
||||||
padding: 64px 124px 64px 124px;
|
padding: 64px 124px 64px 124px;
|
||||||
box-shadow: 0px 6px 3px rgba(0, 0, 0, 0.23), 10px 10px 10px rgba(0, 0, 0, 0.19);
|
box-shadow: 0px 6px 3px rgba(0, 0, 0, 0.23), 10px 10px 10px rgba(0, 0, 0, 0.19);
|
|
@ -188,7 +188,7 @@
|
||||||
.upperRightIcon {
|
.upperRightIcon {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
background-image: url(<%= asset_data_uri('topright_sprite.png') %>);
|
background-image: url(<%= asset_path('topright_sprite.png') %>);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullWidthWrapper.withPartners {
|
.fullWidthWrapper.withPartners {
|
||||||
background: url(<%= asset_data_uri('homepage_bg_fade.png') %>) no-repeat center -300px;
|
background: url(<%= asset_path('homepage_bg_fade.png') %>) no-repeat center -300px;
|
||||||
}
|
}
|
||||||
.homeWrapper.homePartners {
|
.homeWrapper.homePartners {
|
||||||
padding: 64px 0 280px;
|
padding: 64px 0 280px;
|
||||||
|
@ -364,7 +364,7 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.openCheatsheet {
|
.openCheatsheet {
|
||||||
background-image: url(<%= asset_data_uri('help_sprite.png') %>);
|
background-image: url(<%= asset_path('help_sprite.png') %>);
|
||||||
background-repeat:no-repeat;
|
background-repeat:no-repeat;
|
||||||
}
|
}
|
||||||
.openCheatsheet:hover {
|
.openCheatsheet:hover {
|
||||||
|
@ -373,7 +373,7 @@
|
||||||
.mapInfoIcon {
|
.mapInfoIcon {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 56px; /* puts it just offscreen */
|
top: 56px; /* puts it just offscreen */
|
||||||
background-image: url(<%= asset_data_uri('mapinfo_sprite.png') %>);
|
background-image: url(<%= asset_path('mapinfo_sprite.png') %>);
|
||||||
background-repeat:no-repeat;
|
background-repeat:no-repeat;
|
||||||
}
|
}
|
||||||
.mapInfoIcon:hover {
|
.mapInfoIcon:hover {
|
||||||
|
@ -382,8 +382,14 @@
|
||||||
.mapPage .mapInfoIcon {
|
.mapPage .mapInfoIcon {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
.importDialog {
|
||||||
|
background-image: url(<%= asset_path('import.png') %>);
|
||||||
|
background-position: 0 0;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
.starMap {
|
.starMap {
|
||||||
background-image: url(<%= asset_data_uri('starmap_sprite.png') %>);
|
background-image: url(<%= asset_path('starmap_sprite.png') %>);
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
@ -437,7 +443,7 @@
|
||||||
.takeScreenshot {
|
.takeScreenshot {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-image: url(<%= asset_data_uri 'screenshot_sprite.png' %>);
|
background-image: url(<%= asset_path 'screenshot_sprite.png' %>);
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.takeScreenshot:hover {
|
.takeScreenshot:hover {
|
||||||
|
@ -450,7 +456,7 @@
|
||||||
.zoomExtents {
|
.zoomExtents {
|
||||||
margin-bottom:5px;
|
margin-bottom:5px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-image: url(<%= asset_data_uri('extents_sprite.png') %>);
|
background-image: url(<%= asset_path('extents_sprite.png') %>);
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoomExtents:hover {
|
.zoomExtents:hover {
|
||||||
|
@ -458,7 +464,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoomExtents:hover .tooltips, .zoomIn:hover .tooltips, .zoomOut:hover .tooltips, .takeScreenshot:hover .tooltips, .sidebarFilterIcon:hover .tooltipsUnder, .sidebarForkIcon:hover .tooltipsUnder, .addMap:hover .tooltipsUnder, .authenticated .sidebarAccountIcon:hover .tooltipsUnder,
|
.zoomExtents:hover .tooltips, .zoomIn:hover .tooltips, .zoomOut:hover .tooltips, .takeScreenshot:hover .tooltips, .sidebarFilterIcon:hover .tooltipsUnder, .sidebarForkIcon:hover .tooltipsUnder, .addMap:hover .tooltipsUnder, .authenticated .sidebarAccountIcon:hover .tooltipsUnder,
|
||||||
.mapInfoIcon:hover .tooltipsAbove, .openCheatsheet:hover .tooltipsAbove, .chat-button:hover .tooltips, .starMap:hover .tooltipsAbove, .openMetacodeSwitcher:hover .tooltipsAbove, .pinCarousel:not(.isPinned):hover .tooltipsAbove.helpPin, .pinCarousel.isPinned:hover .tooltipsAbove.helpUnpin {
|
.mapInfoIcon:hover .tooltipsAbove, .openCheatsheet:hover .tooltipsAbove, .chat-button:hover .tooltips, importDialog:hover .tooltipsAbove, .starMap:hover .tooltipsAbove, .openMetacodeSwitcher:hover .tooltipsAbove, .pinCarousel:not(.isPinned):hover .tooltipsAbove.helpPin, .pinCarousel.isPinned:hover .tooltipsAbove.helpUnpin {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,7 +629,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoomIn {
|
.zoomIn {
|
||||||
background-image: url(<%= asset_data_uri('zoom_sprite.png') %>);
|
background-image: url(<%= asset_path('zoom_sprite.png') %>);
|
||||||
background-position: 0 /…0;
|
background-position: 0 /…0;
|
||||||
border-top-left-radius: 2px;
|
border-top-left-radius: 2px;
|
||||||
border-top-right-radius: 2px;
|
border-top-right-radius: 2px;
|
||||||
|
@ -632,7 +638,7 @@
|
||||||
background-position: -32px 0;
|
background-position: -32px 0;
|
||||||
}
|
}
|
||||||
.zoomOut {
|
.zoomOut {
|
||||||
background-image: url(<%= asset_data_uri('zoom_sprite.png') %>);
|
background-image: url(<%= asset_path('zoom_sprite.png') %>);
|
||||||
background-position:0 -32px;
|
background-position:0 -32px;
|
||||||
border-bottom-left-radius: 2px;
|
border-bottom-left-radius: 2px;
|
||||||
border-bottom-right-radius: 2px;
|
border-bottom-right-radius: 2px;
|
||||||
|
@ -740,23 +746,23 @@
|
||||||
left:5px;
|
left:5px;
|
||||||
}
|
}
|
||||||
.exploreMapsCenter .myMaps .exploreMapsIcon {
|
.exploreMapsCenter .myMaps .exploreMapsIcon {
|
||||||
background-image: url(<%= asset_data_uri 'exploremaps_sprite.png' %>);
|
background-image: url(<%= asset_path 'exploremaps_sprite.png' %>);
|
||||||
background-position: -32px 0;
|
background-position: -32px 0;
|
||||||
}
|
}
|
||||||
.exploreMapsCenter .sharedMaps .exploreMapsIcon {
|
.exploreMapsCenter .sharedMaps .exploreMapsIcon {
|
||||||
background-image: url(<%= asset_data_uri 'exploremaps_sprite.png' %>);
|
background-image: url(<%= asset_path 'exploremaps_sprite.png' %>);
|
||||||
background-position: -128px 0;
|
background-position: -128px 0;
|
||||||
}
|
}
|
||||||
.exploreMapsCenter .activeMaps .exploreMapsIcon {
|
.exploreMapsCenter .activeMaps .exploreMapsIcon {
|
||||||
background-image: url(<%= asset_data_uri 'exploremaps_sprite.png' %>);
|
background-image: url(<%= asset_path 'exploremaps_sprite.png' %>);
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
}
|
}
|
||||||
.exploreMapsCenter .featuredMaps .exploreMapsIcon {
|
.exploreMapsCenter .featuredMaps .exploreMapsIcon {
|
||||||
background-image: url(<%= asset_data_uri 'exploremaps_sprite.png' %>);
|
background-image: url(<%= asset_path 'exploremaps_sprite.png' %>);
|
||||||
background-position: -96px 0;
|
background-position: -96px 0;
|
||||||
}
|
}
|
||||||
.exploreMapsCenter .starredMaps .exploreMapsIcon {
|
.exploreMapsCenter .starredMaps .exploreMapsIcon {
|
||||||
background-image: url(<%= asset_data_uri 'exploremaps_sprite.png' %>);
|
background-image: url(<%= asset_path 'exploremaps_sprite.png' %>);
|
||||||
background-position: -96px 0;
|
background-position: -96px 0;
|
||||||
}
|
}
|
||||||
.myMaps:hover .exploreMapsIcon, .myMaps.active .exploreMapsIcon {
|
.myMaps:hover .exploreMapsIcon, .myMaps.active .exploreMapsIcon {
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper div.mapInfoBox {
|
.wrapper .mapInfoBox {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
<div class="infoAndHelp">
|
<div class="infoAndHelp">
|
||||||
<%= render :partial => 'maps/mapinfobox' %>
|
<%= render :partial => 'maps/mapinfobox' %>
|
||||||
|
|
||||||
<% starred = current_user && @map && current_user.starred_map?(@map)
|
<% starred = current_user && @map && current_user.starred_map?(@map)
|
||||||
starClass = starred ? 'starred' : ''
|
starClass = starred ? 'starred' : ''
|
||||||
tooltip = starred ? 'Star' : 'Unstar' %>
|
tooltip = starred ? 'Star' : 'Unstar' %>
|
||||||
<div class="starMap infoElement mapElement <%= starClass %>"><div class="tooltipsAbove"><%= tooltip %></div></div>
|
<div class="starMap infoElement mapElement <%= starClass %>"><div class="tooltipsAbove"><%= tooltip %></div></div>
|
||||||
<div class="mapInfoIcon infoElement mapElement"><div class="tooltipsAbove">Map Info</div></div>
|
<div class="mapInfoIcon infoElement mapElement"><div class="tooltipsAbove">Map Info</div></div>
|
||||||
<div class="openCheatsheet openLightbox infoElement mapElement" data-open="cheatsheet"><div class="tooltipsAbove">Help</div></div>
|
<div class="openCheatsheet openLightbox infoElement mapElement" data-open="cheatsheet"><div class="tooltipsAbove">Help</div></div>
|
||||||
<div class="clearfloat"></div>
|
<div class="clearfloat"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,6 +19,10 @@
|
||||||
|
|
||||||
<div class="upperRightUI">
|
<div class="upperRightUI">
|
||||||
<div class="mapElement upperRightEl upperRightMapButtons">
|
<div class="mapElement upperRightEl upperRightMapButtons">
|
||||||
|
<div class="importDialog infoElement mapElement openLightbox" data-open="import-dialog-lightbox">
|
||||||
|
<div class="tooltipsAbove">Import data</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- filtering -->
|
<!-- filtering -->
|
||||||
<div class="sidebarFilter upperRightEl">
|
<div class="sidebarFilter upperRightEl">
|
||||||
<div class="sidebarFilterIcon upperRightIcon"><div class="tooltipsUnder">Filter</div></div>
|
<div class="sidebarFilterIcon upperRightIcon"><div class="tooltipsUnder">Filter</div></div>
|
||||||
|
|
38
frontend/src/Metamaps/GlobalUI/ImportDialog.js
Normal file
38
frontend/src/Metamaps/GlobalUI/ImportDialog.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/* global $ */
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import outdent from 'outdent'
|
||||||
|
|
||||||
|
import ImportDialogBox from '../../components/ImportDialogBox'
|
||||||
|
|
||||||
|
import PasteInput from '../PasteInput'
|
||||||
|
|
||||||
|
const ImportDialog = {
|
||||||
|
openLightbox: null,
|
||||||
|
closeLightbox: null,
|
||||||
|
|
||||||
|
init: function (serverData, openLightbox, closeLightbox) {
|
||||||
|
const self = ImportDialog
|
||||||
|
self.openLightbox = openLightbox
|
||||||
|
self.closeLightbox = closeLightbox
|
||||||
|
|
||||||
|
$('#lightbox_content').append($(outdent`
|
||||||
|
<div class="lightboxContent" id="import-dialog-lightbox">
|
||||||
|
<div class="importDialogWrapper" />
|
||||||
|
</div>
|
||||||
|
`))
|
||||||
|
ReactDOM.render(React.createElement(ImportDialogBox, {
|
||||||
|
onFileAdded: PasteInput.handleFile,
|
||||||
|
exampleImageUrl: serverData['import-example.png']
|
||||||
|
}), $('.importDialogWrapper').get(0))
|
||||||
|
},
|
||||||
|
show: function () {
|
||||||
|
ImportDialog.openLightbox('import-dialog')
|
||||||
|
},
|
||||||
|
hide: function () {
|
||||||
|
ImportDialog.closeLightbox('import-dialog')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImportDialog
|
|
@ -6,6 +6,7 @@ import Create from '../Create'
|
||||||
import Search from './Search'
|
import Search from './Search'
|
||||||
import CreateMap from './CreateMap'
|
import CreateMap from './CreateMap'
|
||||||
import Account from './Account'
|
import Account from './Account'
|
||||||
|
import ImportDialog from './ImportDialog'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Metamaps.Backbone
|
* Metamaps.Backbone
|
||||||
|
@ -21,6 +22,7 @@ const GlobalUI = {
|
||||||
self.Search.init()
|
self.Search.init()
|
||||||
self.CreateMap.init()
|
self.CreateMap.init()
|
||||||
self.Account.init()
|
self.Account.init()
|
||||||
|
self.ImportDialog.init(Metamaps.Erb, self.openLightbox, self.closeLightbox)
|
||||||
|
|
||||||
if ($('#toast').html().trim()) self.notifyUser($('#toast').html())
|
if ($('#toast').html().trim()) self.notifyUser($('#toast').html())
|
||||||
|
|
||||||
|
@ -141,5 +143,5 @@ const GlobalUI = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Search, CreateMap, Account }
|
export { Search, CreateMap, Account, ImportDialog }
|
||||||
export default GlobalUI
|
export default GlobalUI
|
||||||
|
|
|
@ -411,6 +411,7 @@ const Import = {
|
||||||
newKey = newKey.replace(/\s/g, '') // remove whitespace
|
newKey = newKey.replace(/\s/g, '') // remove whitespace
|
||||||
if (newKey === 'url') newKey = 'link'
|
if (newKey === 'url') newKey = 'link'
|
||||||
if (newKey === 'title') newKey = 'name'
|
if (newKey === 'title') newKey = 'name'
|
||||||
|
if (newKey === 'label') newKey = 'desc'
|
||||||
if (newKey === 'description') newKey = 'desc'
|
if (newKey === 'description') newKey = 'desc'
|
||||||
if (newKey === 'direction') newKey = 'category'
|
if (newKey === 'direction') newKey = 'category'
|
||||||
return newKey
|
return newKey
|
||||||
|
|
|
@ -40,6 +40,11 @@ const Map = {
|
||||||
init: function () {
|
init: function () {
|
||||||
var self = Map
|
var self = Map
|
||||||
|
|
||||||
|
// prevent right clicks on the main canvas, so as to not get in the way of our right clicks
|
||||||
|
$('#wrapper').on('contextmenu', function (e) {
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
$('.starMap').click(function () {
|
$('.starMap').click(function () {
|
||||||
if ($(this).is('.starred')) self.unstar()
|
if ($(this).is('.starred')) self.unstar()
|
||||||
else self.star()
|
else self.star()
|
||||||
|
@ -52,7 +57,7 @@ const Map = {
|
||||||
GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html()
|
GlobalUI.CreateMap.emptyForkMapForm = $('#fork_map').html()
|
||||||
|
|
||||||
self.updateStar()
|
self.updateStar()
|
||||||
self.InfoBox.init()
|
InfoBox.init()
|
||||||
CheatSheet.init()
|
CheatSheet.init()
|
||||||
|
|
||||||
$(document).on(Map.events.editedByActiveMapper, self.editedByActiveMapper)
|
$(document).on(Map.events.editedByActiveMapper, self.editedByActiveMapper)
|
||||||
|
@ -102,7 +107,7 @@ const Map = {
|
||||||
Selected.reset()
|
Selected.reset()
|
||||||
|
|
||||||
// set the proper mapinfobox content
|
// set the proper mapinfobox content
|
||||||
Map.InfoBox.load()
|
InfoBox.load()
|
||||||
|
|
||||||
// these three update the actual filter box with the right list items
|
// these three update the actual filter box with the right list items
|
||||||
Filter.checkMetacodes()
|
Filter.checkMetacodes()
|
||||||
|
@ -132,7 +137,7 @@ const Map = {
|
||||||
Create.newTopic.hide(true) // true means force (and override pinned)
|
Create.newTopic.hide(true) // true means force (and override pinned)
|
||||||
Create.newSynapse.hide()
|
Create.newSynapse.hide()
|
||||||
Filter.close()
|
Filter.close()
|
||||||
Map.InfoBox.close()
|
InfoBox.close()
|
||||||
Realtime.endActiveMap()
|
Realtime.endActiveMap()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,16 +21,7 @@ const PasteInput = {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var coords = Util.pixelsToCoords({ x: e.clientX, y: e.clientY })
|
var coords = Util.pixelsToCoords({ x: e.clientX, y: e.clientY })
|
||||||
if (e.dataTransfer.files.length > 0) {
|
if (e.dataTransfer.files.length > 0) {
|
||||||
var fileReader = new window.FileReader()
|
self.handleFile(e.dataTransfer.files[0], coords)
|
||||||
fileReader.readAsText(e.dataTransfer.files[0])
|
|
||||||
fileReader.onload = function(e) {
|
|
||||||
var text = e.currentTarget.result
|
|
||||||
if (text.substring(0,5) === '<?xml') {
|
|
||||||
// assume this is a macOS .webloc link
|
|
||||||
text = text.replace(/[\s\S]*<string>(.*)<\/string>[\s\S]*/m, '$1')
|
|
||||||
}
|
|
||||||
self.handle(text, coords)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// OMG import bookmarks 😍
|
// OMG import bookmarks 😍
|
||||||
if (e.dataTransfer.items.length > 0) {
|
if (e.dataTransfer.items.length > 0) {
|
||||||
|
@ -52,7 +43,21 @@ const PasteInput = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
handle: function(text, coords) {
|
handleFile: (file, coords = null) => {
|
||||||
|
var self = PasteInput
|
||||||
|
var fileReader = new window.FileReader()
|
||||||
|
fileReader.readAsText(file)
|
||||||
|
fileReader.onload = function(e) {
|
||||||
|
var text = e.currentTarget.result
|
||||||
|
if (text.substring(0,5) === '<?xml') {
|
||||||
|
// assume this is a macOS .webloc link
|
||||||
|
text = text.replace(/[\s\S]*<string>(.*)<\/string>[\s\S]*/m, '$1')
|
||||||
|
}
|
||||||
|
self.handle(text, coords)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handle: function(text, coords = null) {
|
||||||
var self = PasteInput
|
var self = PasteInput
|
||||||
|
|
||||||
if (text.match(self.URL_REGEX)) {
|
if (text.match(self.URL_REGEX)) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import Create from './Create'
|
||||||
import Debug from './Debug'
|
import Debug from './Debug'
|
||||||
import Filter from './Filter'
|
import Filter from './Filter'
|
||||||
import GlobalUI, {
|
import GlobalUI, {
|
||||||
Search, CreateMap, Account as GlobalUI_Account
|
Search, CreateMap, ImportDialog, Account as GlobalUI_Account
|
||||||
} from './GlobalUI'
|
} from './GlobalUI'
|
||||||
import Import from './Import'
|
import Import from './Import'
|
||||||
import JIT from './JIT'
|
import JIT from './JIT'
|
||||||
|
@ -47,6 +47,7 @@ Metamaps.GlobalUI = GlobalUI
|
||||||
Metamaps.GlobalUI.Search = Search
|
Metamaps.GlobalUI.Search = Search
|
||||||
Metamaps.GlobalUI.CreateMap = CreateMap
|
Metamaps.GlobalUI.CreateMap = CreateMap
|
||||||
Metamaps.GlobalUI.Account = GlobalUI_Account
|
Metamaps.GlobalUI.Account = GlobalUI_Account
|
||||||
|
Metamaps.GlobalUI.ImportDialog = ImportDialog
|
||||||
Metamaps.Import = Import
|
Metamaps.Import = Import
|
||||||
Metamaps.JIT = JIT
|
Metamaps.JIT = JIT
|
||||||
Metamaps.Listeners = Listeners
|
Metamaps.Listeners = Listeners
|
||||||
|
|
76
frontend/src/components/ImportDialogBox.js
Normal file
76
frontend/src/components/ImportDialogBox.js
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
import React, { PropTypes, Component } from 'react'
|
||||||
|
import Dropzone from 'react-dropzone'
|
||||||
|
|
||||||
|
class ImportDialogBox extends Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
showImportInstructions: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleExport = format => () => {
|
||||||
|
window.open(`${window.location.pathname}/export.${format}`, '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFile = (files, e) => {
|
||||||
|
// for some reason it uploads twice, so we need this debouncer
|
||||||
|
// eslint-disable-next-line no-return-assign
|
||||||
|
this.debouncer = this.debouncer || window.setTimeout(() => this.debouncer = null, 10)
|
||||||
|
if (!this.debouncer) {
|
||||||
|
this.props.onFileAdded(files[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleShowInstructions = e => {
|
||||||
|
this.setState({
|
||||||
|
showImportInstructions: !this.state.showImportInstructions
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render = () => {
|
||||||
|
return (
|
||||||
|
<div className="import-dialog">
|
||||||
|
<h3>EXPORT</h3>
|
||||||
|
<div className="import-blue-button" onClick={this.handleExport('csv')}>
|
||||||
|
Export as CSV
|
||||||
|
</div>
|
||||||
|
<div className="import-blue-button" onClick={this.handleExport('json')}>
|
||||||
|
Export as JSON
|
||||||
|
</div>
|
||||||
|
<h3>IMPORT</h3>
|
||||||
|
<p>To upload a file, drop it here:</p>
|
||||||
|
<Dropzone onDropAccepted={this.handleFile}
|
||||||
|
className="fileupload"
|
||||||
|
>
|
||||||
|
Drop files here!
|
||||||
|
</Dropzone>
|
||||||
|
<p>
|
||||||
|
<a onClick={this.toggleShowInstructions} style={{ textDecoration: 'underline', cursor: 'pointer' }}>
|
||||||
|
Show/hide import instructions
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{!this.state.showImportInstructions ? null : (<div>
|
||||||
|
<p>
|
||||||
|
You can import topics and synapses by uploading a spreadsheet here.
|
||||||
|
The file should be in comma-separated format (when you save, change the
|
||||||
|
filetype from .xls to .csv).
|
||||||
|
</p>
|
||||||
|
<img src={this.props.exampleImageUrl} style={{ width: '100%' }} />
|
||||||
|
<p style={{ marginTop: '1em' }}>You can choose which columns to include in your data. Topics must have a name field. Synapses must have Topic 1 and Topic 2.</p>
|
||||||
|
<p> </p>
|
||||||
|
<p> * There are many valid import formats. Try exporting a map to see what columns you can include in your import data. You can also copy-paste from Excel to import, or import JSON.</p>
|
||||||
|
<p> * If you are importing a list of links, you can use a Link column in place of the Name column.</p>
|
||||||
|
</div>)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImportDialogBox.propTypes = {
|
||||||
|
onFileAdded: PropTypes.func,
|
||||||
|
exampleImageUrl: PropTypes.string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImportDialogBox
|
|
@ -21,15 +21,15 @@ class Header extends Component {
|
||||||
const { signedIn, section } = this.props
|
const { signedIn, section } = this.props
|
||||||
|
|
||||||
const activeClass = (title) => {
|
const activeClass = (title) => {
|
||||||
let forClass = "exploreMapsButton"
|
let forClass = 'exploreMapsButton'
|
||||||
forClass += " " + title + "Maps"
|
forClass += ' ' + title + 'Maps'
|
||||||
if (title == "my" && section == "mine" ||
|
if (title === 'my' && section === 'mine' ||
|
||||||
title == section) forClass += " active"
|
title === section) forClass += ' active'
|
||||||
return forClass
|
return forClass
|
||||||
}
|
}
|
||||||
|
|
||||||
const explore = section == "mine" || section == "active" || section == "starred" || section == "shared" || section == "featured"
|
const explore = section === 'mine' || section === 'active' || section === 'starred' || section === 'shared' || section === 'featured'
|
||||||
const mapper = section == "mapper"
|
const mapper = section === 'mapper'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="exploreMapsHeader">
|
<div id="exploreMapsHeader">
|
||||||
|
@ -38,31 +38,31 @@ class Header extends Component {
|
||||||
<div className="exploreMapsCenter">
|
<div className="exploreMapsCenter">
|
||||||
<MapLink show={signedIn && explore}
|
<MapLink show={signedIn && explore}
|
||||||
href="/explore/mine"
|
href="/explore/mine"
|
||||||
linkClass={activeClass("my")}
|
linkClass={activeClass('my')}
|
||||||
data-router="true"
|
data-router="true"
|
||||||
text="My Maps"
|
text="My Maps"
|
||||||
/>
|
/>
|
||||||
<MapLink show={signedIn && explore}
|
<MapLink show={signedIn && explore}
|
||||||
href="/explore/shared"
|
href="/explore/shared"
|
||||||
linkClass={activeClass("shared")}
|
linkClass={activeClass('shared')}
|
||||||
data-router="true"
|
data-router="true"
|
||||||
text="Shared With Me"
|
text="Shared With Me"
|
||||||
/>
|
/>
|
||||||
<MapLink show={signedIn && explore}
|
<MapLink show={signedIn && explore}
|
||||||
href="/explore/starred"
|
href="/explore/starred"
|
||||||
linkClass={activeClass("starred")}
|
linkClass={activeClass('starred')}
|
||||||
data-router="true"
|
data-router="true"
|
||||||
text="Starred By Me"
|
text="Starred By Me"
|
||||||
/>
|
/>
|
||||||
<MapLink show={explore}
|
<MapLink show={explore}
|
||||||
href={signedIn ? "/" : "/explore/active"}
|
href={signedIn ? '/' : '/explore/active'}
|
||||||
linkClass={activeClass("active")}
|
linkClass={activeClass('active')}
|
||||||
data-router="true"
|
data-router="true"
|
||||||
text="Global"
|
text="Global"
|
||||||
/>
|
/>
|
||||||
<MapLink show={!signedIn && explore}
|
<MapLink show={!signedIn && explore}
|
||||||
href="/explore/featured"
|
href="/explore/featured"
|
||||||
linkClass={activeClass("featured")}
|
linkClass={activeClass('featured')}
|
||||||
data-router="true"
|
data-router="true"
|
||||||
text="Featured Maps"
|
text="Featured Maps"
|
||||||
/>
|
/>
|
|
@ -3,7 +3,7 @@ import React, { Component, PropTypes } from 'react'
|
||||||
class MapCard extends Component {
|
class MapCard extends Component {
|
||||||
render = () => {
|
render = () => {
|
||||||
const { map, currentUser } = this.props
|
const { map, currentUser } = this.props
|
||||||
|
|
||||||
function capitalize (string) {
|
function capitalize (string) {
|
||||||
return string.charAt(0).toUpperCase() + string.slice(1)
|
return string.charAt(0).toUpperCase() + string.slice(1)
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,12 @@ class MapCard extends Component {
|
||||||
const truncatedName = n ? (n.length > maxNameLength ? n.substring(0, maxNameLength) + '...' : n) : ''
|
const truncatedName = n ? (n.length > maxNameLength ? n.substring(0, maxNameLength) + '...' : n) : ''
|
||||||
const truncatedDesc = d ? (d.length > maxDescLength ? d.substring(0, maxDescLength) + '...' : d) : ''
|
const truncatedDesc = d ? (d.length > maxDescLength ? d.substring(0, maxDescLength) + '...' : d) : ''
|
||||||
const editPermission = map.authorizeToEdit(currentUser) ? 'canEdit' : 'cannotEdit'
|
const editPermission = map.authorizeToEdit(currentUser) ? 'canEdit' : 'cannotEdit'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="map" id={ map.id }>
|
<div className="map" id={ map.id }>
|
||||||
<a href={ '/maps/' + map.id } data-router="true">
|
<a href={ '/maps/' + map.id } data-router="true">
|
||||||
<div className={ 'permission ' + editPermission }>
|
<div className={ 'permission ' + editPermission }>
|
||||||
<div className="mapCard">
|
<div className="mapCard">
|
||||||
<span className="title" title={ map.get('name') }>
|
<span className="title" title={ map.get('name') }>
|
||||||
{ truncatedName }
|
{ truncatedName }
|
||||||
</span>
|
</span>
|
||||||
|
@ -46,7 +46,7 @@ class MapCard extends Component {
|
||||||
{ map.get('topic_count') }
|
{ map.get('topic_count') }
|
||||||
</span>
|
</span>
|
||||||
{ map.get('topic_count') === 1 ? ' topic' : ' topics' }
|
{ map.get('topic_count') === 1 ? ' topic' : ' topics' }
|
||||||
</div>
|
</div>
|
||||||
<div className="metadataSection mapPermission">
|
<div className="metadataSection mapPermission">
|
||||||
{ map.get('permission') ? capitalize(map.get('permission')) : 'Commons' }
|
{ map.get('permission') ? capitalize(map.get('permission')) : 'Commons' }
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,7 +57,7 @@ class MapCard extends Component {
|
||||||
{ map.get('synapse_count') === 1 ? ' synapse' : ' synapses' }
|
{ map.get('synapse_count') === 1 ? ' synapse' : ' synapses' }
|
||||||
</div>
|
</div>
|
||||||
<div className="clearfloat"></div>
|
<div className="clearfloat"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
|
@ -3,14 +3,14 @@ import React, { Component, PropTypes } from 'react'
|
||||||
class MapperCard extends Component {
|
class MapperCard extends Component {
|
||||||
render = () => {
|
render = () => {
|
||||||
const { user } = this.props
|
const { user } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mapper">
|
<div className="mapper">
|
||||||
<div className="mapperCard">
|
<div className="mapperCard">
|
||||||
<div className="mapperImage">
|
<div className="mapperImage">
|
||||||
<img src={ user.image } width="96" height="96" />
|
<img src={ user.image } width="96" height="96" />
|
||||||
</div>
|
</div>
|
||||||
<div className="mapperName" title={ user.name }>
|
<div className="mapperName" title={ user.name }>
|
||||||
{ user.name }
|
{ user.name }
|
||||||
</div>
|
</div>
|
||||||
<div className="mapperInfo">
|
<div className="mapperInfo">
|
||||||
|
@ -19,10 +19,10 @@ class MapperCard extends Component {
|
||||||
</div>
|
</div>
|
||||||
<div className="mapperMetadata">
|
<div className="mapperMetadata">
|
||||||
<div className="metadataSection metadataMaps"><div>{ user.numMaps }</div>maps</div>
|
<div className="metadataSection metadataMaps"><div>{ user.numMaps }</div>maps</div>
|
||||||
<div className="metadataSection metadataTopics"><div>{ user.numTopics }</div>topics</div>
|
<div className="metadataSection metadataTopics"><div>{ user.numTopics }</div>topics</div>
|
||||||
<div className="metadataSection metadataSynapses"><div>{ user.numSynapses }</div>synapses</div>
|
<div className="metadataSection metadataSynapses"><div>{ user.numSynapses }</div>synapses</div>
|
||||||
<div className="clearfloat"></div>
|
<div className="clearfloat"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
|
@ -1,20 +1,19 @@
|
||||||
import React, { Component, PropTypes } from 'react'
|
import React, { Component, PropTypes } from 'react'
|
||||||
import Header from './Header.js'
|
import Header from './Header'
|
||||||
import MapperCard from './MapperCard.js'
|
import MapperCard from './MapperCard'
|
||||||
import MapCard from './MapCard.js'
|
import MapCard from './MapCard'
|
||||||
import MapListItem from './MapListItem.js'
|
import MapListItem from './MapListItem'
|
||||||
|
|
||||||
class Maps extends Component {
|
class Maps extends Component {
|
||||||
render = () => {
|
render = () => {
|
||||||
const { maps, currentUser, section, displayStyle, user, moreToLoad, loadMore } = this.props
|
const { maps, currentUser, section, displayStyle, user, moreToLoad, loadMore } = this.props
|
||||||
let mapElements
|
let mapElements
|
||||||
|
|
||||||
if (displayStyle == 'grid') {
|
if (displayStyle === 'grid') {
|
||||||
mapElements = maps.models.map(function (map) {
|
mapElements = maps.models.map(function (map) {
|
||||||
return <MapCard key={ map.id } map={ map } currentUser={ currentUser } />
|
return <MapCard key={ map.id } map={ map } currentUser={ currentUser } />
|
||||||
})
|
})
|
||||||
}
|
} else if (displayStyle === 'list') {
|
||||||
else if (displayStyle == 'list') {
|
|
||||||
mapElements = maps.models.map(function (map) {
|
mapElements = maps.models.map(function (map) {
|
||||||
return <MapListItem key={ map.id } map={ map } />
|
return <MapListItem key={ map.id } map={ map } />
|
||||||
})
|
})
|
||||||
|
@ -28,9 +27,10 @@ class Maps extends Component {
|
||||||
{ currentUser && !user ? <div className="map newMap"><a href="/maps/new"><div className="newMapImage"></div><span>Create new map...</span></a></div> : null }
|
{ currentUser && !user ? <div className="map newMap"><a href="/maps/new"><div className="newMapImage"></div><span>Create new map...</span></a></div> : null }
|
||||||
{ mapElements }
|
{ mapElements }
|
||||||
<div className='clearfloat'></div>
|
<div className='clearfloat'></div>
|
||||||
{ moreToLoad ?
|
{!moreToLoad ? null : [
|
||||||
[<button className="button loadMore" onClick={ loadMore }>load more</button>, <div className='clearfloat'></div>]
|
<button className="button loadMore" onClick={ loadMore }>load more</button>,
|
||||||
: null }
|
<div className='clearfloat'></div>
|
||||||
|
]}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Header signedIn={ !!currentUser }
|
<Header signedIn={ !!currentUser }
|
|
@ -33,6 +33,7 @@
|
||||||
"outdent": "0.2.1",
|
"outdent": "0.2.1",
|
||||||
"react": "15.3.2",
|
"react": "15.3.2",
|
||||||
"react-dom": "15.3.2",
|
"react-dom": "15.3.2",
|
||||||
|
"react-dropzone": "3.6.0",
|
||||||
"socket.io": "0.9.12",
|
"socket.io": "0.9.12",
|
||||||
"webpack": "1.13.2"
|
"webpack": "1.13.2"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue