Mailspring/internal_packages/composer/lib/floating-toolbar.cjsx

243 lines
7.6 KiB
Text
Raw Normal View History

_ = require 'underscore'
React = require 'react/addons'
classNames = require 'classnames'
{CompositeDisposable} = require 'event-kit'
{RetinaImg} = require 'nylas-component-kit'
{DraftStore} = require 'nylas-exports'
class FloatingToolbar extends React.Component
@displayName = "FloatingToolbar"
@propTypes:
top: React.PropTypes.number
left: React.PropTypes.number
mode: React.PropTypes.string
onMouseEnter: React.PropTypes.func
onMouseLeave: React.PropTypes.func
# When an extension wants to mutate the DOM, it passes `onDomMutator`
# a mutator function. That mutator is expecting to be passed the
# latest DOM object and may modify it in place.
onDomMutator: React.PropTypes.func
@defaultProps:
mode: "buttons"
onMouseEnter: ->
onMouseLeave: ->
constructor: (@props) ->
@state =
urlInputValue: @_initialUrl() ? ""
componentWidth: 0
componentDidMount: =>
@subscriptions = new CompositeDisposable()
componentWillReceiveProps: (nextProps) =>
@setState
urlInputValue: @_initialUrl(nextProps)
componentWillUnmount: =>
@subscriptions?.dispose()
componentDidUpdate: =>
if @props.mode is "edit-link" and not @props.linkToModify
# Note, it's important that we're focused on the urlInput because
# the parent of this component needs to know to not hide us on their
# onBlur method.
React.findDOMNode(@refs.urlInput).focus()
render: =>
<div ref="floatingToolbar"
className={@_toolbarClasses()} style={@_toolbarStyles()}>
<div className="toolbar-pointer" style={@_toolbarPointerStyles()}></div>
{@_toolbarType()}
</div>
_toolbarClasses: =>
classes = {}
classes[@props.pos] = true
classNames _.extend classes,
"floating-toolbar": true
"toolbar": true
"toolbar-visible": @props.visible
_toolbarStyles: =>
styles =
left: @_toolbarLeft()
top: @props.top
width: @_width()
return styles
_toolbarType: =>
if @props.mode is "buttons" then @_renderButtons()
else if @props.mode is "edit-link" then @_renderLink()
else return <div></div>
_renderButtons: =>
<div className="toolbar-buttons">
<button className="btn btn-bold toolbar-btn"
onClick={@_execCommand}
data-command-name="bold"></button>
<button className="btn btn-italic toolbar-btn"
onClick={@_execCommand}
data-command-name="italic"></button>
<button className="btn btn-underline toolbar-btn"
onClick={@_execCommand}
data-command-name="underline"></button>
<button className="btn btn-link toolbar-btn"
onClick={@props.onClickLinkEditBtn}
data-command-name="link"></button>
{@_toolbarExtensions()}
</div>
_toolbarExtensions: ->
buttons = []
for extension in DraftStore.extensions()
toolbarItem = extension.composerToolbar?()
if toolbarItem
buttons.push(
<button className="btn btn-extension"
onClick={ => @_extensionMutateDom(toolbarItem.mutator)}
title="#{toolbarItem.tooltip}"><RetinaImg mode={RetinaImg.Mode.ContentIsMask} url="#{toolbarItem.iconUrl}" /></button>)
return buttons
_extensionMutateDom: (mutator) =>
@props.onDomMutator(mutator)
_renderLink: =>
removeBtn = ""
withRemove = ""
if @_initialUrl()
withRemove = "with-remove"
removeBtn = <button className="btn btn-icon"
ref="removeBtn"
onMouseDown={@_removeUrl}><i className="fa fa-times"></i></button>
<div className="toolbar-new-link"
onMouseEnter={@_onMouseEnter}
onMouseLeave={@_onMouseLeave}>
<i className="fa fa-link preview-btn-icon" onClick={@_onPreventToolbarClose}></i>
<input type="text"
ref="urlInput"
value={@state.urlInputValue}
onBlur={@_onBlur}
onFocus={@_onFocus}
onClick={@_onPreventToolbarClose}
onKeyPress={@_saveUrlOnEnter}
onChange={@_onInputChange}
className="floating-toolbar-input #{withRemove}"
placeholder="Paste or type a link" />
<button className="btn btn-icon"
ref="saveBtn"
onKeyPress={@_saveUrlOnEnter}
onMouseDown={@_saveUrl}><i className="fa fa-check"></i></button>
{removeBtn}
</div>
_onPreventToolbarClose: (event) =>
event.stopPropagation()
_onMouseEnter: =>
@props.onMouseEnter?()
_onMouseLeave: =>
if @props.linkToModify and document.activeElement isnt React.findDOMNode(@refs.urlInput)
@props.onMouseLeave?()
_initialUrl: (props=@props) =>
props.linkToModify?.getAttribute?('href')
_onInputChange: (event) =>
@setState urlInputValue: event.target.value
_saveUrlOnEnter: (event) =>
if event.key is "Enter"
if (@state.urlInputValue ? "").trim().length > 0
@_saveUrl()
else
@_removeUrl()
# We signify the removal of a url with an empty string. This protects us
# from the case where people delete the url text and hit save. In that
# case we also want to remove the link.
_removeUrl: =>
@setState urlInputValue: ""
@props.onSaveUrl "", @props.linkToModify
@props.onDoneWithLink()
_onFocus: =>
@props.onChangeFocus(true)
# Clicking the save or remove buttons will take precendent over simply
# bluring the field.
_onBlur: (event) =>
targets = []
if @refs["saveBtn"]
targets.push React.findDOMNode(@refs["saveBtn"])
if @refs["removeBtn"]
targets.push React.findDOMNode(@refs["removeBtn"])
if event.relatedTarget in targets
event.preventDefault()
return
else
@_saveUrl()
@props.onChangeFocus(false)
_saveUrl: =>
if (@state.urlInputValue ? "").trim().length > 0
@props.onSaveUrl @state.urlInputValue, @props.linkToModify
@props.onDoneWithLink()
_execCommand: (event) =>
cmd = event.currentTarget.getAttribute 'data-command-name'
document.execCommand(cmd, false, null)
true
_toolbarLeft: =>
CONTENT_PADDING = @props.contentPadding ? 15
max = @props.editAreaWidth - @_width() - CONTENT_PADDING
left = Math.min(Math.max(@props.left - @_width()/2, CONTENT_PADDING), max)
return left
_toolbarPointerStyles: =>
CONTENT_PADDING = @props.contentPadding ? 15
POINTER_WIDTH = 6 + 2 #2px of border-radius
max = @props.editAreaWidth - CONTENT_PADDING
min = CONTENT_PADDING
absoluteLeft = Math.max(Math.min(@props.left, max), min)
relativeLeft = absoluteLeft - @_toolbarLeft()
left = Math.max(Math.min(relativeLeft, @_width()-POINTER_WIDTH), POINTER_WIDTH)
styles =
left: left
return styles
_width: =>
# We can't calculate the width of the floating toolbar declaratively
# because it hasn't been rendered yet. As such, we'll keep the width
# fixed to make it much eaier.
TOOLBAR_BUTTONS_WIDTH = 114#px
TOOLBAR_URL_WIDTH = 210#px
# If we have a long link, we want to make a larger text area. It's not
feat(salesforce): add Salesforce creator Summary: tests on the schemas build input elements form builder pulls data grouping by row salesforce object store salesforce api logic successfully pulling salesforce objects into db object store saving to db refactoring tokenizing text field full documented tokenizing text field with specs linking in object picker component converting generated form to a controlled input form change handlers for controlled inputs Salesforce object creator store new way of opening windows removed atom.state.mode create new salesforce object creator in new window form creator loading in popup with generated form generated form renders select and multiselcet and textarea add checkbox creating related objects windnows know when others close remove debugger statements form submission converting data for salesforce posting hot window loading new hot window registration hot loading windows actions for listening to salesforce objects created generated form errors error handling for salesforce object creator rename saleforce object form store display errors to form submitting state passed through properly posts objects to Salesforce change name to salesforce object form add deep clone use formItemEach styling for Salesforce form creator salesforce required fields come back and populate form generated form loads related objects into fields remove console logs and fix sales schema adapter test fix task queue and formbuilder specs fix action bridge spec fix tokenizing text field spec fix draft store and tokenizing proptypes fix linter issues fix tokenizing text field bug rename to refresh window props remove console.log Test Plan: edgehill --test Reviewers: bengotow Reviewed By: bengotow Differential Revision: https://review.inboxapp.com/D1425
2015-04-25 00:36:19 +08:00
# super important to get the length exactly so let's just get within
# the ballpark by guessing charcter lengths
WIDTH_PER_CHAR = 11
max = @props.editAreaWidth - (@props.contentPadding ? 15)*2
if @props.mode is "buttons"
return TOOLBAR_BUTTONS_WIDTH
else if @props.mode is "edit-link"
url = @_initialUrl()
if url?.length > 0
fullWidth = Math.max(Math.min(url.length * WIDTH_PER_CHAR, max), TOOLBAR_URL_WIDTH)
return fullWidth
else
return TOOLBAR_URL_WIDTH
else
return TOOLBAR_BUTTONS_WIDTH
module.exports = FloatingToolbar