diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 31d941b22..3aa509754 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -271,7 +271,20 @@ describe "EditorComponent", -> Object.defineProperty(event, 'which', get: -> properties.which) if properties.which? event - it "transfers focus to the hidden input", -> - expect(document.activeElement).toBe document.body - node.focus() - expect(document.activeElement).toBe node.querySelector('.hidden-input') + describe "focus handling", -> + inputNode = null + + beforeEach -> + inputNode = node.querySelector('.hidden-input') + + it "transfers focus to the hidden input", -> + expect(document.activeElement).toBe document.body + node.focus() + expect(document.activeElement).toBe inputNode + + it "adds the 'is-focused' class to the editor when the hidden input is focused", -> + expect(document.activeElement).toBe document.body + inputNode.focus() + expect(node.classList.contains('is-focused')).toBe true + inputNode.blur() + expect(node.classList.contains('is-focused')).toBe false diff --git a/src/editor-component.coffee b/src/editor-component.coffee index 920cf05c1..06dbcaf18 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -24,12 +24,14 @@ EditorCompont = React.createClass mixins: [CustomEventMixin, SubscriberMixin] render: -> - {fontSize, lineHeight, fontFamily} = @state + {fontSize, lineHeight, fontFamily, focused} = @state {editor} = @props + className = 'editor react' + className += ' is-focused' if focused - div className: 'editor react', tabIndex: -1, style: {fontSize, lineHeight, fontFamily}, + div className: className, tabIndex: -1, style: {fontSize, lineHeight, fontFamily}, div className: 'scroll-view', ref: 'scrollView', - InputComponent ref: 'hiddenInput', className: 'hidden-input', onInput: @onInput + InputComponent ref: 'input', className: 'hidden-input', onInput: @onInput, onFocus: @onInputFocused, onBlur: @onInputBlurred @renderScrollableContent() div className: 'vertical-scrollbar', ref: 'verticalScrollbar', onScroll: @onVerticalScroll, div outlet: 'verticalScrollbarContent', style: {height: editor.getScrollHeight()} @@ -245,7 +247,16 @@ EditorCompont = React.createClass @updateLineDimensions() onFocus: -> - @refs.hiddenInput.focus() + @refs.input.focus() + + onInputFocused: -> + @setState(focused: true) + + onInputBlurred: -> + @setState(focused: false) unless document.activeElement is @getDOMNode() + + onInput: (char, replaceLastChar) -> + ReactUpdates.batchedUpdates => @props.editor.insertText(char) onVerticalScroll: -> scrollTop = @refs.verticalScrollbar.getDOMNode().scrollTop @@ -343,9 +354,6 @@ EditorCompont = React.createClass onOverflowChanged: -> @props.editor.setHeight(@refs.scrollView.getDOMNode().clientHeight) - onInput: (char, replaceLastChar) -> - ReactUpdates.batchedUpdates => @props.editor.insertText(char) - onScreenLinesChanged: ({start, end}) -> {editor} = @props @requestUpdate() if editor.intersectsVisibleRowRange(start, end + 1) # TODO: Use closed-open intervals for change events diff --git a/src/input-component.coffee b/src/input-component.coffee index 55ff458bc..6ea54490c 100644 --- a/src/input-component.coffee +++ b/src/input-component.coffee @@ -6,7 +6,9 @@ React = require 'react' module.exports = InputComponent = React.createClass render: -> - input className: @props.className, ref: 'input' + {className, onFocus, onBlur} = @props + + input {className, onFocus, onBlur} getInitialState: -> {lastChar: ''} @@ -35,5 +37,11 @@ InputComponent = React.createClass lastChar = String.fromCharCode(last(valueCharCodes)) @props.onInput?(lastChar, replaceLastChar) + onFocus: -> + @props.onFocus?() + + onBlur: -> + @props.onBlur?() + focus: -> @getDOMNode().focus()