From 6338d78a289c3337976c3832072246c590524466 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Wed, 25 Jan 2017 15:49:11 -0500 Subject: [PATCH] create working Desc editor --- app/assets/stylesheets/base.scss.erb | 6 +- frontend/src/components/TopicCard/Desc.js | 62 +++++ frontend/src/components/TopicCard/index.js | 306 ++++++++++----------- 3 files changed, 210 insertions(+), 164 deletions(-) create mode 100644 frontend/src/components/TopicCard/Desc.js diff --git a/app/assets/stylesheets/base.scss.erb b/app/assets/stylesheets/base.scss.erb index 4e690717..663f033e 100644 --- a/app/assets/stylesheets/base.scss.erb +++ b/app/assets/stylesheets/base.scss.erb @@ -129,7 +129,7 @@ height: auto; } -.CardOnGraph .best_in_place_desc textarea { +.CardOnGraph .riek-editing textarea { font-size: 13px; line-height:15px; font-family: helvetica, sans-serif; @@ -174,13 +174,13 @@ * End Markdown styling */ -.CardOnGraph .best_in_place_desc { +.CardOnGraph .riek_desc { display:block; margin-top:2px; padding-right: 18px; margin-right: 8px; } -.canEdit .CardOnGraph .best_in_place_desc:hover { +.canEdit .CardOnGraph .riek_desc:hover { background-image: url(<%= asset_data_uri('edit.png') %>); background-position: top right; background-repeat: no-repeat; diff --git a/frontend/src/components/TopicCard/Desc.js b/frontend/src/components/TopicCard/Desc.js new file mode 100644 index 00000000..33eb324e --- /dev/null +++ b/frontend/src/components/TopicCard/Desc.js @@ -0,0 +1,62 @@ +import React, { PropTypes, Component } from 'react' +import { get } from 'lodash' +import { RIETextArea } from 'riek' +import Util from '../../Metamaps/Util' + +class MdTextArea extends RIETextArea { + renderNormalComponent = () => { + const value = this.state.newValue || this.props.value + + // defaultProps MUST use dangerouslySetInnerHTML + return + } +} + +class Desc extends Component { + render = () => { + const descHTML = (this.props.desc === '' && this.props.authorizedToEdit) + ? '' + : Util.mdToHTML(this.props.desc) + + const htmlSpan = + + return ( +
+
+ { + const ENTER = 13 + if (e.shiftKey && e.which === ENTER) { + e.preventDefault() + this.props.onChange({ desc: e.target.value }) + } + } + }} + defaultProps={{ + dangerouslySetInnerHTML: { __html: descHTML } + }} + /> +
+
+
+ ) + } +} + +Desc.propTypes = { + desc: PropTypes.string, // markdown + authorizedToEdit: PropTypes.bool, + onChange: PropTypes.func +} + +export default Desc diff --git a/frontend/src/components/TopicCard/index.js b/frontend/src/components/TopicCard/index.js index f7d0e365..30d70441 100644 --- a/frontend/src/components/TopicCard/index.js +++ b/frontend/src/components/TopicCard/index.js @@ -2,164 +2,153 @@ import React, { PropTypes, Component } from 'react' import { RIETextArea } from 'riek' -import Util from '../../Metamaps/Util' - import Title from './Title' import Links from './Links' +import Desc from './Desc' -const descHTML = (topic, ActiveMapper) => { - const authorized = topic.authorizeToEdit(ActiveMapper) - const descMarkdown = (topic.get('desc') === '' && authorized) - ? 'Click to add description...' - : topic.get('desc') - return Util.mdToHTML(descMarkdown) -} - -var funcs = { - bindShowCardListeners: function(topic, ActiveMapper) { - var authorized = topic.authorizeToEdit(ActiveMapper) - var selectingMetacode = false - // attach the listener that shows the metacode title when you hover over the image - $('.showcard .metacodeImage').mouseenter(function() { - $('.showcard .icon').css('z-index', '4') - $('.showcard .metacodeTitle').show() - }) - $('.showcard .linkItem.icon').mouseleave(function() { - if (!selectingMetacode) { - $('.showcard .metacodeTitle').hide() - $('.showcard .icon').css('z-index', '1') - } - }) - - var metacodeLiClick = function() { - selectingMetacode = false - var metacodeId = parseInt($(this).attr('data-id')) - var metacode = DataModel.Metacodes.get(metacodeId) - $('.CardOnGraph').find('.metacodeTitle').html(metacode.get('name')) - .append('
') - .attr('class', 'metacodeTitle mbg' + metacode.id) - $('.CardOnGraph').find('.metacodeImage').css('background-image', 'url(' + metacode.get('icon') + ')') - topic.save({ - metacode_id: metacode.id - }) - Visualize.mGraph.plot() - $('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge') - $('.metacodeTitle').hide() +const bindShowCardListeners = (topic, ActiveMapper) => { + var authorized = topic.authorizeToEdit(ActiveMapper) + var selectingMetacode = false + // attach the listener that shows the metacode title when you hover over the image + $('.showcard .metacodeImage').mouseenter(function() { + $('.showcard .icon').css('z-index', '4') + $('.showcard .metacodeTitle').show() + }) + $('.showcard .linkItem.icon').mouseleave(function() { + if (!selectingMetacode) { + $('.showcard .metacodeTitle').hide() $('.showcard .icon').css('z-index', '1') } + }) - var openMetacodeSelect = function(event) { - var TOPICCARD_WIDTH = 300 - var METACODESELECT_WIDTH = 404 - var MAX_METACODELIST_HEIGHT = 270 - - if (!selectingMetacode) { - selectingMetacode = true - - // this is to make sure the metacode - // select is accessible onscreen, when opened - // while topic card is close to the right - // edge of the screen - var windowWidth = $(window).width() - var showcardLeft = parseInt($('.showcard').css('left')) - var distanceFromEdge = windowWidth - (showcardLeft + TOPICCARD_WIDTH) - if (distanceFromEdge < METACODESELECT_WIDTH) { - $('.metacodeSelect').addClass('onRightEdge') - } - - // this is to make sure the metacode - // select is accessible onscreen, when opened - // while topic card is close to the bottom - // edge of the screen - var windowHeight = $(window).height() - var showcardTop = parseInt($('.showcard').css('top')) - var topicTitleHeight = $('.showcard .title').height() + parseInt($('.showcard .title').css('padding-top')) + parseInt($('.showcard .title').css('padding-bottom')) - var distanceFromBottom = windowHeight - (showcardTop + topicTitleHeight) - if (distanceFromBottom < MAX_METACODELIST_HEIGHT) { - $('.metacodeSelect').addClass('onBottomEdge') - } - - $('.metacodeSelect').show() - event.stopPropagation() - } - } - - var hideMetacodeSelect = function() { - selectingMetacode = false - $('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge') - $('.metacodeTitle').hide() - $('.showcard .icon').css('z-index', '1') - } - - if (authorized) { - $('.showcard .metacodeTitle').click(openMetacodeSelect) - $('.showcard').click(hideMetacodeSelect) - $('.metacodeSelect > ul > li').click(function(event) { - event.stopPropagation() - }) - $('.metacodeSelect li li').click(metacodeLiClick) - } - - var hidePermissionSelect = function() { - selectingPermission = false - $('.showcard .yourTopic .mapPerm').removeClass('minimize') // this line flips the pull up arrow to a drop down arrow - $('.showcard .permissionSelect').remove() - } - - var permissionLiClick = function(event) { - selectingPermission = false - var permission = $(this).attr('class') - topic.save({ - permission: permission, - defer_to_map_id: null - }) - $('.showcard .mapPerm').removeClass('co pu pr').addClass(permission.substring(0, 2)) - hidePermissionSelect() - } - - var openPermissionSelect = function(event) { - if (!selectingPermission) { - selectingPermission = true - $(this).addClass('minimize') // this line flips the drop down arrow to a pull up arrow - if ($(this).hasClass('co')) { - $(this).append('
') - } else if ($(this).hasClass('pu')) { - $(this).append('
') - } else if ($(this).hasClass('pr')) { - $(this).append('
') - } - $('.showcard .permissionSelect li').click(permissionLiClick) - event.stopPropagation() - } - } - // ability to change permission - var selectingPermission = false - if (topic.authorizePermissionChange(ActiveMapper)) { - $('.showcard .yourTopic .mapPerm').click(openPermissionSelect) - $('.showcard').click(hidePermissionSelect) - } - - $('.links .mapCount').unbind().click(function(event) { - $('.mapCount .tip').toggle() - $('.showcard .hoverTip').toggleClass('hide') + var metacodeLiClick = function() { + selectingMetacode = false + var metacodeId = parseInt($(this).attr('data-id')) + var metacode = DataModel.Metacodes.get(metacodeId) + $('.CardOnGraph').find('.metacodeTitle').html(metacode.get('name')) + .append('
') + .attr('class', 'metacodeTitle mbg' + metacode.id) + $('.CardOnGraph').find('.metacodeImage').css('background-image', 'url(' + metacode.get('icon') + ')') + topic.save({ + metacode_id: metacode.id }) - $('.showcard').unbind('.hideTip').bind('click.hideTip', function() { - $('.mapCount .tip').hide() - $('.showcard .hoverTip').removeClass('hide') - }) - - var originalText = $('.showMore').html() - $('.mapCount .tip .showMore').unbind().toggle( - function(event) { - $('.extraText').toggleClass('hideExtra') - $('.showMore').html('Show less...') - }, - function(event) { - $('.extraText').toggleClass('hideExtra') - $('.showMore').html(originalText) - } - ) + Visualize.mGraph.plot() + $('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge') + $('.metacodeTitle').hide() + $('.showcard .icon').css('z-index', '1') } + + var openMetacodeSelect = function(event) { + var TOPICCARD_WIDTH = 300 + var METACODESELECT_WIDTH = 404 + var MAX_METACODELIST_HEIGHT = 270 + + if (!selectingMetacode) { + selectingMetacode = true + + // this is to make sure the metacode + // select is accessible onscreen, when opened + // while topic card is close to the right + // edge of the screen + var windowWidth = $(window).width() + var showcardLeft = parseInt($('.showcard').css('left')) + var distanceFromEdge = windowWidth - (showcardLeft + TOPICCARD_WIDTH) + if (distanceFromEdge < METACODESELECT_WIDTH) { + $('.metacodeSelect').addClass('onRightEdge') + } + + // this is to make sure the metacode + // select is accessible onscreen, when opened + // while topic card is close to the bottom + // edge of the screen + var windowHeight = $(window).height() + var showcardTop = parseInt($('.showcard').css('top')) + var topicTitleHeight = $('.showcard .title').height() + parseInt($('.showcard .title').css('padding-top')) + parseInt($('.showcard .title').css('padding-bottom')) + var distanceFromBottom = windowHeight - (showcardTop + topicTitleHeight) + if (distanceFromBottom < MAX_METACODELIST_HEIGHT) { + $('.metacodeSelect').addClass('onBottomEdge') + } + + $('.metacodeSelect').show() + event.stopPropagation() + } + } + + var hideMetacodeSelect = function() { + selectingMetacode = false + $('.metacodeSelect').hide().removeClass('onRightEdge onBottomEdge') + $('.metacodeTitle').hide() + $('.showcard .icon').css('z-index', '1') + } + + if (authorized) { + $('.showcard .metacodeTitle').click(openMetacodeSelect) + $('.showcard').click(hideMetacodeSelect) + $('.metacodeSelect > ul > li').click(function(event) { + event.stopPropagation() + }) + $('.metacodeSelect li li').click(metacodeLiClick) + } + + var hidePermissionSelect = function() { + selectingPermission = false + $('.showcard .yourTopic .mapPerm').removeClass('minimize') // this line flips the pull up arrow to a drop down arrow + $('.showcard .permissionSelect').remove() + } + + var permissionLiClick = function(event) { + selectingPermission = false + var permission = $(this).attr('class') + topic.save({ + permission: permission, + defer_to_map_id: null + }) + $('.showcard .mapPerm').removeClass('co pu pr').addClass(permission.substring(0, 2)) + hidePermissionSelect() + } + + var openPermissionSelect = function(event) { + if (!selectingPermission) { + selectingPermission = true + $(this).addClass('minimize') // this line flips the drop down arrow to a pull up arrow + if ($(this).hasClass('co')) { + $(this).append('
') + } else if ($(this).hasClass('pu')) { + $(this).append('
') + } else if ($(this).hasClass('pr')) { + $(this).append('
') + } + $('.showcard .permissionSelect li').click(permissionLiClick) + event.stopPropagation() + } + } + // ability to change permission + var selectingPermission = false + if (topic.authorizePermissionChange(ActiveMapper)) { + $('.showcard .yourTopic .mapPerm').click(openPermissionSelect) + $('.showcard').click(hidePermissionSelect) + } + + $('.links .mapCount').unbind().click(function(event) { + $('.mapCount .tip').toggle() + $('.showcard .hoverTip').toggleClass('hide') + }) + $('.showcard').unbind('.hideTip').bind('click.hideTip', function() { + $('.mapCount .tip').hide() + $('.showcard .hoverTip').removeClass('hide') + }) + + var originalText = $('.showMore').html() + $('.mapCount .tip .showMore').unbind().toggle( + function(event) { + $('.extraText').toggleClass('hideExtra') + $('.showMore').html('Show less...') + }, + function(event) { + $('.extraText').toggleClass('hideExtra') + $('.showMore').html(originalText) + } + ) } class ReactTopicCard extends Component { @@ -180,7 +169,7 @@ class ReactTopicCard extends Component { const { topic, ActiveMapper } = this.props embedly('on', 'card.rendered', this.embedlyCardRendered) topic.get('link') && topic.get('link') !== '' && this.loadLink() - funcs.bindShowCardListeners(topic, ActiveMapper) + bindShowCardListeners(topic, ActiveMapper) } componentWillUnmount = () => { @@ -254,21 +243,16 @@ class ReactTopicCard extends Component { if (topic.authorizePermissionChange(ActiveMapper)) classname += ' yourTopic' const hasAttachment = topic.get('link') && topic.get('link') !== '' - const topicId = topic.isNew() ? topic.cid : topic.id + const topicId = topic.isNew() ? topic.cid : topic.id // TODO should we be using cid here??? return (
<Links topic={topic} /> - <div className="scroll"> - <div className="desc"> - <span className="best_in_place best_in_place_desc" - dangerouslySetInnerHTML={{ __html: descHTML(topic, ActiveMapper) }} - > - </span> - <div className="clearfloat"></div> - </div> - </div> + <Desc desc={topic.get('desc')} + authorizedToEdit={topic.authorizeToEdit(ActiveMapper)} + onChange={this.props.updateTopic} + /> {hasAttachment && <div className={`embeds ${embedlyLinkLoaded ? '' : 'nonEmbedlyLink'}`}> <a href={topic.get('link')} id="embedlyLink" target="_blank" data-card-description="0"> {topic.get('link')}