_ = require 'underscore-plus' React = require 'react/addons' {CompositeDisposable} = require 'event-kit' {RetinaImg} = require 'ui-components' module.exports = FloatingToolbar = React.createClass displayName: "FloatingToolbar" 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()}
_toolbarClasses: -> classes = {} classes[@props.pos] = true React.addons.classSet _.extend classes, "floating-toolbar": true "toolbar": true "toolbar-visible": @props.visible _toolbarStyles: -> styles = left: @_toolbarLeft() top: @props.top width: @_width() return styles _toolbarType: -> if @state.mode is "buttons" then @_renderButtons() else if @state.mode is "edit-link" then @_renderLink() else return
_renderButtons: ->
_renderLink: -> removeBtn = "" withRemove = "" if @_initialUrl() withRemove = "with-remove" 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: -> return unless @isMounted() and @state.urlInputValue? @props.onSaveUrl @state.urlInputValue, @props.linkToModify _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 # super important to get the lenght 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 @state.mode is "buttons" return TOOLBAR_BUTTONS_WIDTH else if @state.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 _showLink: -> @setState mode: "edit-link"