diff --git a/.gitignore b/.gitignore index de3cc231..91b6ef85 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ app/assets/javascripts/webpacked #secrets and config .env +*.swp # Ignore bundler config .bundle diff --git a/app/assets/images/arrowperms_sprite.png b/app/assets/images/arrowperms_sprite.png deleted file mode 100644 index eff00341..00000000 Binary files a/app/assets/images/arrowperms_sprite.png and /dev/null differ diff --git a/app/assets/stylesheets/application.scss.erb b/app/assets/stylesheets/application.scss.erb index 52b0fc29..d255b652 100644 --- a/app/assets/stylesheets/application.scss.erb +++ b/app/assets/stylesheets/application.scss.erb @@ -1914,14 +1914,10 @@ input.collaboratorSearchField { background-position: -32px 0; } .yourMap .mapPermission:hover { - background-image: url(<%= asset_data_uri('arrowperms_sprite.png') %>); cursor: pointer; - background-position: -32px 0; } .yourMap .mapPermission.minimize { - background-image: url(<%= asset_data_uri('arrowperms_sprite.png') %>) !important; cursor: pointer; - background-position: 0 0; } .mapInfoBox .mapPermission .permissionSelect { list-style: none; @@ -3150,11 +3146,15 @@ script.data-gratipay-username { } .topicFollow { - text-align: center; - height: 48px; - line-height: 48px; - border-top: 1px solid #BDBDBD; - background: #FFF; + height: 24px; + line-height: 24px; cursor: pointer; - font-family: din-regular; + font-family: helvetica, sans-serif; + float: left; + width: 72px; + text-align: right; + padding: 12px 0; + color: #4fb5c0; + font-size: 13px; + font-weight: bold; } diff --git a/app/assets/stylesheets/base.scss.erb b/app/assets/stylesheets/base.scss.erb index fd2349a7..6415d535 100644 --- a/app/assets/stylesheets/base.scss.erb +++ b/app/assets/stylesheets/base.scss.erb @@ -1,9 +1,12 @@ +$mid-gray: #8A8A8A; +$mid-gray-opacity: rgba(66, 66, 66, 0.6); + .nameCounter { position: absolute; bottom: 1px; right: 2px; font-size: 11px; - font-family: helvetica; + font-family: helvetica, sans-serif; color: #727272; line-height: 11px; display: none; @@ -42,7 +45,7 @@ z-index:2; color: #424242; border-radius:2px; - box-shadow: 0px 3px 3px rgba(0,0,0,0.23), 0 3px 3px rgba(0,0,0,0.16); + box-shadow: 2px 3px 3px rgba(125, 125, 125, 0.23), -2px -1px 3px rgba(125, 125, 125, 0.16); } .text { @@ -64,7 +67,6 @@ display:block; position:relative; width:100%; - min-height:360px; z-index: 25; } .CardOnGraph.hasAttachment { @@ -73,11 +75,10 @@ .CardOnGraph .title { word-break: break-word; - font-size: 18px; - line-height: 22px; + font-size: 20px; + line-height: 24px; display: table; - padding: 8px 0 16px; - height: 80px; + padding: 20px 0; text-align: center; font-family: 'din-regular', sans-serif; width: 300px; @@ -88,11 +89,6 @@ display: table-cell; vertical-align: middle; padding: 0 16px; - - &.riek-editing { - position: absolute; - top: 32px; - } } .canEdit #titleActivator:hover { background-image: url(<%= asset_data_uri('edit.png') %>); @@ -104,9 +100,8 @@ .showcard .title .riek-editing { font-family: 'din-regular', sans-serif; color: #424242; - font-size: 18px; - line-height: 22px; - height: 3em; + font-size: 20px; + line-height: 24px; padding: 5px 0; width: 100%; margin: 0; @@ -120,11 +115,19 @@ .CardOnGraph .scroll { display:block; padding: 8px 0 8px 16px; - height: 152px; font-size: 13px; line-height:15px; font-family: helvetica, sans-serif; overflow-y: auto; + + p.emptyDesc { + color: $mid-gray-opacity; + } + + a.mdSupport { + color: #4fb5c0; + font-size: 11px; + } } .CardOnGraph.hasAttachment .scroll { height: auto; @@ -180,7 +183,6 @@ margin-top:2px; padding-right: 18px; margin-right: 8px; - min-height: 7em; } .canEdit .CardOnGraph .riek_desc:hover { background-image: url(<%= asset_data_uri('edit.png') %>); @@ -195,9 +197,7 @@ .CardOnGraph .links { position: relative; - border-bottom: 1px solid #BDBDBD; - border-top: 1px solid #BDBDBD; - background-color: #e0e0e0; + z-index: 2; .linkItem { float: left; @@ -213,37 +213,52 @@ } .icon { - position: absolute; - z-index: 1; padding: 0; height: 48px; - width: 100%; + margin-right: 10px; .metacodeImage { cursor: move; - position: relative; - left: -23px; - top: 1px; - width: 46px; - height: 46px; - background-size:46px 46px; + position: absolute; + left: -18px; + top: 6px; + width: 36px; + height: 36px; + background-size:36px 36px; background-position:0 0; background-repeat:no-repeat; } } +} + +.CardOnGraph .info { + position: relative; + + .linkItem { + float: left; + z-index: 1; + position: relative; + color: $mid-gray-opacity; + font-size: 14px; + line-height: 14px; + + a { + color: $mid-gray-opacity; + } + } .contributor { bottom: 7px; - margin-left: 40px; + margin-left: 16px; .contributorIcon { position: relative; + display: inline-block; vertical-align: middle; border-radius: 16px; - margin: 5px; - top: 8px; + margin: 5px 5px 5px 0; + top: 11px; left: 0; - border-radius: 16px; } span { @@ -252,52 +267,34 @@ } .contributorName { - display: none; - position: absolute; - background: black; - text-align: center; - color: white; - border-radius: 2px; font-family: din-regular; - line-height: 15px; - font-size: 12px; - padding: 3px 5px 2px; + margin-top: 20px; + display: inline-block; + vertical-align: middle; + width: 97px; + padding: 0 8px 0 4px; white-space: nowrap; - margin-top: 8px; - - &:before { - content: ''; - position: absolute; - top: 26px; - left: 10px; - margin-top: -30px; - width: 0; - height: 0; - border-bottom: 4px solid #000000; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - } - } - - &:hover .contributorName { - display: block; + overflow: hidden; + text-overflow: ellipsis; } } .mapCount { - padding:17px 0 17px 36px; - margin-left: 12px; + padding:17px 38px 17px 0; + width: 22px; + text-align: right; .mapCountIcon { position: absolute; top: 8px; - left: 0; + right: 0; width: 32px; height: 32px; background-image: url(<%= asset_data_uri('map32_sprite.png') %>); background-repeat: no-repeat; background-position: 0 0; cursor: pointer; + opacity: 0.6; } &:hover .mapCountIcon { @@ -306,18 +303,18 @@ .tip, .hoverTip { top: 44px; - left: 0px; + right: 0px; font-size: 12px !important; &:before { content: ''; position: absolute; top: 26px; - left: 10px; + right: 10px; margin-top: -30px; width: 0; height: 0; - border-bottom: 4px solid #000000; + border-bottom: 4px solid $mid-gray; border-left: 5px solid transparent; border-right: 5px solid transparent; } @@ -327,10 +324,9 @@ white-space: nowrap; font-family: 'din-regular'; top: 44px; - left: 0px; font-size: 12px !important; position: absolute; - background: black; + background: $mid-gray; color: white; border-radius: 4px; line-height: 17px; @@ -362,28 +358,31 @@ } .synapseCount { - margin-left: 26px; - width: 24px; - padding:17px 0 17px 32px; + width: 22px; + padding:17px 38px 17px 0; + text-align: right; + margin-right: 4px; .synapseCountIcon { position: absolute; top: 8px; - left: 0; + right: 0; width: 32px; height: 32px; background-image: url(<%= asset_data_uri('synapse32_sprite.png') %>); background-repeat: no-repeat; background-position: 0 0; + opacity: 0.6; } hover .synapseCountIcon { background-position: 0 -32px; } .tip { position: absolute; - background: black; + background: $mid-gray; width: auto; top: 44px; + right: 0px; color: white; white-space: nowrap; border-radius: 2px; @@ -398,10 +397,10 @@ content: ''; position: absolute; margin-top: -8px; - margin-left: 6px; + right: 12px; width: 0; height: 0; - border-bottom: 4px solid black; + border-bottom: 4px solid $mid-gray; border-left: 5px solid transparent; border-right: 5px solid transparent; } @@ -413,6 +412,10 @@ color: #4FC059; } +.linkItem.mapPerm { + margin-right: 8px; +} + .mapPerm { width: 32px; height: 32px; @@ -434,14 +437,10 @@ } .yourTopic .mapPerm:hover, .yourEdge .mapPerm:hover { - background-image: url(<%= asset_data_uri('arrowperms_sprite.png') %>); - background-position: -32px 0; cursor:pointer; } .yourTopic .mapPerm.minimize, .yourEdge .mapPerm.minimize { - background-image: url(<%= asset_data_uri('arrowperms_sprite.png') %>) !important; -background-position: 0 0; -cursor: pointer; + cursor: pointer; } .mapPerm .permissionSelect { list-style: none; @@ -477,32 +476,34 @@ cursor: pointer; } .CardOnGraph .metacodeTitle { - font-style: italic; - font-family: 'vinyl'; - text-transform: uppercase; - position: absolute; + font-family: 'din-regular'; line-height: 24px; height: 26px; - font-size: 24px; - display: none; - width: 90%; - padding: 13px 0 9px 10%; - background-color: #E0E0E0; + font-size: 18px; + padding: 13px 24px 9px 24px; color: #424242; + width: 120px; + max-width: 120px; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; } .permission.canEdit .metacodeTitle { cursor:pointer; } .permission.canEdit .expandMetacodeSelect { - position: absolute; - top: 16px; - right: 16px; + position: relative; + top: 2px; + left: 4px; width: 16px; height: 16px; background-image: url(<%= asset_data_uri('arrowright_sprite.png') %>); background-repeat: no-repeat; background-position: 0 -32px; + display: inline-block; + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } .permission.canEdit .minimize .expandMetacodeSelect { @@ -519,8 +520,8 @@ cursor: pointer; background: #EAEAEA; white-space: nowrap; position: absolute; - left: 300px; - top: -1px; + top: 48px; + box-shadow: 2px 2px 2px rgba(125, 125, 125, 0.23), -2px -1px 3px rgba(125, 125, 125, 0.16); } .CardOnGraph .metacodeSelect ul { position: relative; @@ -613,9 +614,9 @@ background-color: #E0E0E0; } .CardOnGraph .tip { position: absolute; - background: black; + background: $mid-gray; top: 35px; - left: 0; + right: 0; color: white; border-radius: 4px; font-size:15px !important; @@ -633,7 +634,6 @@ background-color: #E0E0E0; width:100%; height:47px; position: relative; - border-top: 1px solid #BDBDBD; } .link-adder a { @@ -695,9 +695,9 @@ background-color: #E0E0E0; } #addLinkInput input{ -padding: 9px 7px 9px 31px; +padding: 9px 27px 9px 31px; height: 12px; -width: 198px; +width: 210px; margin: 0 0 0 0; border: none; outline: none; diff --git a/app/views/layouts/_foot.html.erb b/app/views/layouts/_foot.html.erb index fd35e765..bc3d4223 100644 --- a/app/views/layouts/_foot.html.erb +++ b/app/views/layouts/_foot.html.erb @@ -15,7 +15,7 @@ <%= render :partial => 'layouts/lightboxes' %> <%= render :partial => 'layouts/templates' %> -<%= render :partial => 'shared/metacodeBgColors' %> +<%= render :partial => 'shared/metacodeCssColors' %> <%= render :partial => 'layouts/googleanalytics' if ENV["GA_TRACKING_CODE"].present? %> diff --git a/app/views/shared/_metacodeBgColors.html.erb b/app/views/shared/_metacodeBgColors.html.erb deleted file mode 100644 index 09396a3b..00000000 --- a/app/views/shared/_metacodeBgColors.html.erb +++ /dev/null @@ -1,9 +0,0 @@ - \ No newline at end of file diff --git a/app/views/shared/_metacodeCssColors.html.erb b/app/views/shared/_metacodeCssColors.html.erb new file mode 100644 index 00000000..5ef6119e --- /dev/null +++ b/app/views/shared/_metacodeCssColors.html.erb @@ -0,0 +1,10 @@ + diff --git a/frontend/src/Metamaps/DataModel/Map.js b/frontend/src/Metamaps/DataModel/Map.js index b64f9f89..1f618763 100644 --- a/frontend/src/Metamaps/DataModel/Map.js +++ b/frontend/src/Metamaps/DataModel/Map.js @@ -35,7 +35,7 @@ const Map = Backbone.Model.extend({ } }, isFollowedBy: function(mapper) { - return mapper.get('follows') && mapper.get('follows').maps.indexOf(this.get('id')) > -1 + return mapper && mapper.get('follows') && mapper.get('follows').maps.indexOf(this.get('id')) > -1 }, getUser: function() { return Mapper.get(this.get('user_id')) diff --git a/frontend/src/Metamaps/DataModel/Topic.js b/frontend/src/Metamaps/DataModel/Topic.js index 29c271d4..6ce59d6e 100644 --- a/frontend/src/Metamaps/DataModel/Topic.js +++ b/frontend/src/Metamaps/DataModel/Topic.js @@ -48,7 +48,7 @@ const Topic = Backbone.Model.extend({ else return false }, isFollowedBy: function(mapper) { - return mapper.get('follows') && mapper.get('follows').topics.indexOf(this.get('id')) > -1 + return mapper && mapper.get('follows') && mapper.get('follows').topics.indexOf(this.get('id')) > -1 }, getDate: function() {}, getMetacode: function() { diff --git a/frontend/src/components/MetacodeSelect.js b/frontend/src/components/MetacodeSelect.js index 3f4146c7..191d67c0 100644 --- a/frontend/src/components/MetacodeSelect.js +++ b/frontend/src/components/MetacodeSelect.js @@ -13,6 +13,7 @@ import PropTypes from 'prop-types' class MetacodeSelect extends Component { render = () => { + if (!this.props.metacodeSets) return null return (
diff --git a/frontend/src/components/TopicCard/Follow.js b/frontend/src/components/TopicCard/Follow.js index 2b4c8162..1aec6525 100644 --- a/frontend/src/components/TopicCard/Follow.js +++ b/frontend/src/components/TopicCard/Follow.js @@ -3,16 +3,27 @@ import PropTypes from 'prop-types' class Follow extends Component { render = () => { - const { isFollowing, onTopicFollow } = this.props - return
- {isFollowing ? 'Unfollow' : 'Follow'} + const { ActiveMapper, isFollowing, onTopicFollow } = this.props + function onClick () { + if (ActiveMapper) { + onTopicFollow() + } + } + let innerValue + // only display either option if there is a user signed in + if (ActiveMapper) { + innerValue = isFollowing ? 'Unfollow' : 'Follow' + } + return
+ {innerValue}
} } Follow.propTypes = { isFollowing: PropTypes.bool, - onTopicFollow: PropTypes.func + onTopicFollow: PropTypes.func, + ActiveMapper: PropTypes.object } export default Follow diff --git a/frontend/src/components/TopicCard/Info.js b/frontend/src/components/TopicCard/Info.js new file mode 100644 index 00000000..22c51189 --- /dev/null +++ b/frontend/src/components/TopicCard/Info.js @@ -0,0 +1,105 @@ +/* global $ */ + +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { Link } from 'react-router' + +class Info extends Component { + constructor(props) { + super(props) + + this.state = { + showInMaps: false, + showMoreMaps: false, + hoveringMapCount: false, + hoveringSynapseCount: false + } + } + + toggleShowMoreMaps = e => { + e.stopPropagation() + e.preventDefault() + this.setState({ showMoreMaps: !this.state.showMoreMaps }) + } + + updateState = (key, value) => () => { + this.setState({ [key]: value }) + } + + inMaps = (topic) => { + const inmapsArray = topic.get('inmaps') || [] + const inmapsLinks = topic.get('inmapsLinks') || [] + + let firstFiveLinks = [] + let extraLinks = [] + for (let i = 0; i < inmapsArray.length; i++) { + if (i < 5) { + firstFiveLinks.push({ mapName: inmapsArray[i], mapId: inmapsLinks[i] }) + } else { + extraLinks.push({ mapName: inmapsArray[i], mapId: inmapsLinks[i] }) + } + } + + let output = [] + + firstFiveLinks.forEach(obj => { + output.push(
  • {obj.mapName}
  • ) + }) + + if (extraLinks.length > 0) { + if (this.state.showMoreMaps) { + extraLinks.forEach(obj => { + output.push(
  • {obj.mapName}
  • ) + }) + } + const text = this.state.showMoreMaps ? 'See less...' : `See ${extraLinks.length} more...` + output.push(
  • {text}
  • ) + } + + return output + } + + render = () => { + const { topic } = this.props + + return ( +
    +
    + + +
    {topic.get('user_name')}
    +
    +
    + +
    + {topic.get('synapse_count').toString()} + {this.state.hoveringSynapseCount &&
    Click to see this topics synapses
    } +
    +
    +
    + {topic.get('map_count').toString()} + {!this.state.showInMaps && this.state.hoveringMapCount && ( +
    Click to see which maps topic appears on
    + )} + {this.state.showInMaps &&
      {this.inMaps(topic)}
    } +
    +
    +
    + ) + } +} + +Info.propTypes = { + topic: PropTypes.object // backbone object +} + +export default Info diff --git a/frontend/src/components/TopicCard/Links.js b/frontend/src/components/TopicCard/Links.js index a20e637c..1c822c34 100644 --- a/frontend/src/components/TopicCard/Links.js +++ b/frontend/src/components/TopicCard/Links.js @@ -2,22 +2,17 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import { Link } from 'react-router' import MetacodeSelect from '../MetacodeSelect' import Permission from './Permission' +import Follow from './Follow' class Links extends Component { constructor(props) { super(props) this.state = { - showMetacodeTitle: false, - showMetacodeSelect: false, - showInMaps: false, - showMoreMaps: false, - hoveringMapCount: false, - hoveringSynapseCount: false + showMetacodeSelect: false } } @@ -30,70 +25,23 @@ class Links extends Component { // which it currently does using backbone. If backbone comes out, make sure it still does } - toggleShowMoreMaps = e => { - e.stopPropagation() - e.preventDefault() - this.setState({ showMoreMaps: !this.state.showMoreMaps }) - } - - updateState = (key, value) => () => { - this.setState({ [key]: value }) - } - - inMaps = (topic) => { - const inmapsArray = topic.get('inmaps') || [] - const inmapsLinks = topic.get('inmapsLinks') || [] - - let firstFiveLinks = [] - let extraLinks = [] - for (let i = 0; i < inmapsArray.length; i ++) { - if (i < 5) { - firstFiveLinks.push({ mapName: inmapsArray[i], mapId: inmapsLinks[i] }) - } else { - extraLinks.push({ mapName: inmapsArray[i], mapId: inmapsLinks[i] }) - } - } - - let output = [] - - firstFiveLinks.forEach(obj => { - output.push(
  • {obj.mapName}
  • ) - }) - - if (extraLinks.length > 0) { - if (this.state.showMoreMaps) { - extraLinks.forEach(obj => { - output.push(
  • {obj.mapName}
  • ) - }) - } - const text = this.state.showMoreMaps ? 'See less...' : `See ${extraLinks.length} more...` - output.push(
  • {text}
  • ) - } - - return output - } - handleMetacodeBarClick = () => { - if (this.state.showMetacodeTitle) { - this.setState({ showMetacodeSelect: !this.state.showMetacodeSelect }) - } + this.setState({ showMetacodeSelect: !this.state.showMetacodeSelect }) } render = () => { - const { topic, ActiveMapper } = this.props + const { topic, onTopicFollow, ActiveMapper } = this.props const authorizedToEdit = topic.authorizeToEdit(ActiveMapper) const authorizedPermissionChange = topic.authorizePermissionChange(ActiveMapper) const metacode = topic.getMetacode() + const wrappedTopicFollow = () => onTopicFollow(topic) + const isFollowing = topic.isFollowedBy(ActiveMapper) return (
    -
    this.setState({ showMetacodeTitle: false, showMetacodeSelect: false })} - onClick={() => authorizedToEdit && this.handleMetacodeBarClick()} - > -
    +
    authorizedToEdit && this.handleMetacodeBarClick()} > {metacode.get('name')}
    @@ -101,7 +49,6 @@ class Links extends Component {
    this.setState({ showMetacodeTitle: true })} />
    -
    - -
    {topic.get('user_name')}
    -
    -
    -
    - {topic.get('map_count').toString()} - {!this.state.showInMaps && this.state.hoveringMapCount && ( -
    Click to see which maps topic appears on
    - )} - {this.state.showInMaps &&
      {this.inMaps(topic)}
    } -
    - -
    - {topic.get('synapse_count').toString()} - {this.state.hoveringSynapseCount &&
    Click to see this topics synapses
    } -
    + { @@ -16,10 +16,8 @@ class ReactTopicCard extends Component { if (!topic) return null const wrappedUpdateTopic = obj => updateTopic(topic, obj) - const wrappedTopicFollow = () => onTopicFollow(topic) const authorizedToEdit = topic.authorizeToEdit(currentUser) - const isFollowing = topic.isFollowedBy(currentUser) const hasAttachment = topic.get('link') && topic.get('link') !== '' let classname = 'permission' @@ -31,20 +29,21 @@ class ReactTopicCard extends Component { if (topic.authorizePermissionChange(currentUser)) classname += ' yourTopic' return ( - -
    + +
    - <Links topic={topic} + onTopicFollow={onTopicFollow} ActiveMapper={this.props.currentUser} updateTopic={wrappedUpdateTopic} metacodeSets={this.props.metacodeSets} redrawCanvas={this.props.redrawCanvas} /> + <Title name={topic.get('name')} + authorizedToEdit={authorizedToEdit} + onChange={wrappedUpdateTopic} + /> <Desc desc={topic.get('desc')} authorizedToEdit={authorizedToEdit} onChange={wrappedUpdateTopic} @@ -53,8 +52,8 @@ class ReactTopicCard extends Component { authorizedToEdit={authorizedToEdit} updateTopic={wrappedUpdateTopic} /> - <Follow isFollowing={isFollowing} onTopicFollow={wrappedTopicFollow} /> - <div className="clearfloat"></div> + <Info topic={topic} /> + <div className='clearfloat' /> </div> </div> </div> diff --git a/package.json b/package.json index 1dde78ad..f0afc5f7 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "react-draggable": "3.0.3", "react-dropzone": "4.1.2", "react-onclickoutside": "6.5.0", - "react-router": "4.2.0", + "react-router": "3.0.5", "redux": "3.7.2", "riek": "1.1.0", "simplewebrtc": "2.2.2", diff --git a/webpack.config.js b/webpack.config.js index d55d151f..b24e1513 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -47,7 +47,7 @@ module.exports = { 'metamaps.bundle': './frontend/src/index.js' }, output: { - path: './app/assets/javascripts/webpacked', + path: __dirname + '/app/assets/javascripts/webpacked', filename: '[name].js', devtoolModuleFilenameTemplate: '[absolute-resource-path]' }