_ = require 'underscore-plus'
React = require 'react'
{CompositeDisposable} = require 'event-kit'
module.exports =
FloatingToolbar = React.createClass
getInitialState: ->
mode: "buttons"
urlInputValue: @_initialUrl() ? ""
componentDidMount: ->
@isHovering = false
@subscriptions = new CompositeDisposable()
@_saveUrl = _.debounce @__saveUrl, 10
componentWillReceiveProps: (nextProps) ->
@setState
mode: nextProps.initialMode
urlInputValue: @_initialUrl(nextProps)
componentWillUnmount: ->
@subscriptions?.dispose()
@isHovering = false
componentDidUpdate: ->
if @state.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.
@refs.urlInput.getDOMNode().focus() if @isMounted()
render: ->
{@_toolbarType()}
_toolbarType: ->
if @state.mode is "buttons" then @_renderButtons()
else if @state.mode is "edit-link" then @_renderLink()
else return
_renderButtons: ->
_renderLink: ->
removeBtn = ""
if @_initialUrl()
removeBtn =
{removeBtn}
_onMouseEnter: ->
@isHovering = true
@props.onMouseEnter?()
_onMouseLeave: ->
@isHovering = false
if @props.linkToModify and document.activeElement isnt @refs.urlInput.getDOMNode()
@props.onMouseLeave?()
_initialUrl: (props=@props) ->
props.linkToModify?.getAttribute?('href')
_onInputChange: (event) ->
@setState urlInputValue: event.target.value
_saveUrlOnEnter: (event) ->
if event.key is "Enter" and @state.urlInputValue.trim().length > 0
@_saveUrl()
# 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
__saveUrl: ->
@props.onSaveUrl @state.urlInputValue, @props.linkToModify
_execCommand: (event) ->
cmd = event.currentTarget.getAttribute 'data-command-name'
document.execCommand(cmd, false, null)
true
_toolbarStyles: ->
styles =
left: @_toolbarLeft()
top: @props.top
display: if @props.visible then "block" else "none"
return styles
_toolbarLeft: ->
CONTENT_PADDING = @props.contentPadding ? 15
max = @props.editAreaWidth - @_halfWidth()*2 - CONTENT_PADDING
left = Math.min(Math.max(@props.left - @_halfWidth(), 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, @_halfWidth()*2-POINTER_WIDTH), POINTER_WIDTH)
styles =
left: left
return styles
_halfWidth: ->
# 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 = 86#px
TOOLBAR_URL_WIDTH = 210#px
if @state.mode is "buttons"
TOOLBAR_BUTTONS_WIDTH / 2
else if @state.mode is "edit-link"
TOOLBAR_URL_WIDTH / 2
else
TOOLBAR_BUTTONS_WIDTH / 2
_showLink: ->
@setState mode: "edit-link"