diff --git a/src/highlight-component.coffee b/src/highlight-component.coffee deleted file mode 100644 index ef090ea7b..000000000 --- a/src/highlight-component.coffee +++ /dev/null @@ -1,50 +0,0 @@ -React = require 'react-atom-fork' -{div} = require 'reactionary-atom-fork' -{isEqualForProperties} = require 'underscore-plus' - -module.exports = -HighlightComponent = React.createClass - displayName: 'HighlightComponent' - currentFlashCount: 0 - currentFlashClass: null - - render: -> - {state} = @props - - className = 'highlight' - className += " #{state.class}" if state.class? - - div {className}, - for region, i in state.regions - regionClassName = 'region' - regionClassName += " #{state.deprecatedRegionClass}" if state.deprecatedRegionClass? - div className: regionClassName, key: i, style: region - - componentDidMount: -> - @flashIfRequested() - - componentDidUpdate: -> - @flashIfRequested() - - flashIfRequested: -> - if @props.state.flashCount > @currentFlashCount - @currentFlashCount = @props.state.flashCount - - node = @getDOMNode() - {flashClass, flashDuration} = @props.state - - addFlashClass = => - node.classList.add(flashClass) - @currentFlashClass = flashClass - @flashTimeoutId = setTimeout(removeFlashClass, flashDuration) - - removeFlashClass = => - node.classList.remove(@currentFlashClass) - @currentFlashClass = null - clearTimeout(@flashTimeoutId) - - if @currentFlashClass? - removeFlashClass() - requestAnimationFrame(addFlashClass) - else - addFlashClass() diff --git a/src/highlights-component.coffee b/src/highlights-component.coffee index 1d7ae4de3..e402ccbe2 100644 --- a/src/highlights-component.coffee +++ b/src/highlights-component.coffee @@ -1,25 +1,118 @@ React = require 'react-atom-fork' {div} = require 'reactionary-atom-fork' -{isEqualForProperties} = require 'underscore-plus' -HighlightComponent = require './highlight-component' + +RegionStyleProperties = ['top', 'left', 'right', 'width', 'height'] module.exports = HighlightsComponent = React.createClass displayName: 'HighlightsComponent' + oldState: null + highlightNodesById: null + regionNodesByHighlightId: null render: -> - div className: 'highlights', - @renderHighlights() + div className: 'highlights' - renderHighlights: -> - {presenter} = @props - highlightComponents = [] - for key, state of presenter.state.content.highlights - highlightComponents.push(HighlightComponent({key, state})) - highlightComponents + componentWillMount: -> + @highlightNodesById = {} + @regionNodesByHighlightId = {} componentDidMount: -> if atom.config.get('editor.useShadowDOM') insertionPoint = document.createElement('content') insertionPoint.setAttribute('select', '.underlayer') @getDOMNode().appendChild(insertionPoint) + + componentDidUpdate: -> + @updateSync() + + updateSync: -> + node = @getDOMNode() + newState = @props.presenter.state.content.highlights + @oldState ?= {} + + # remove highlights + for id of @oldState + unless newState[id]? + @highlightNodesById[id].remove() + delete @highlightNodesById[id] + delete @regionNodesByHighlightId[id] + delete @oldState[id] + + # add or update highlights + for id, highlightState of newState + unless @oldState[id]? + highlightNode = document.createElement('div') + highlightNode.classList.add('highlight') + @highlightNodesById[id] = highlightNode + @regionNodesByHighlightId[id] = {} + node.appendChild(highlightNode) + @updateHighlightNode(id, highlightState) + + updateHighlightNode: (id, newHighlightState) -> + highlightNode = @highlightNodesById[id] + oldHighlightState = (@oldState[id] ?= {regions: [], flashCount: 0}) + + # update class + if newHighlightState.class isnt oldHighlightState.class + highlightNode.classList.remove(oldHighlightState.class) if oldHighlightState.class? + highlightNode.classList.add(newHighlightState.class) + oldHighlightState.class = newHighlightState.class + + @updateHighlightRegions(id, newHighlightState) + @flashHighlightNodeIfRequested(id, newHighlightState) + + updateHighlightRegions: (id, newHighlightState) -> + oldHighlightState = @oldState[id] + highlightNode = @highlightNodesById[id] + + # remove regions + while oldHighlightState.regions.length > newHighlightState.regions.length + oldHighlightState.regions.pop() + @regionNodesByHighlightId[id][oldHighlightState.regions.length].remove() + delete @regionNodesByHighlightId[id][oldHighlightState.regions.length] + + # add or update regions + for newRegionState, i in newHighlightState.regions + unless oldHighlightState.regions[i]? + oldHighlightState.regions[i] = {} + regionNode = document.createElement('div') + regionNode.classList.add('region') + regionNode.classList.add(newHighlightState.deprecatedRegionClass) if newHighlightState.deprecatedRegionClass? + @regionNodesByHighlightId[id][i] = regionNode + highlightNode.appendChild(regionNode) + + oldRegionState = oldHighlightState.regions[i] + regionNode = @regionNodesByHighlightId[id][i] + + for property in RegionStyleProperties + if newRegionState[property] isnt oldRegionState[property] + oldRegionState[property] = newRegionState[property] + if newRegionState[property]? + regionNode.style[property] = newRegionState[property] + 'px' + else + regionNode.style[property] = '' + + flashHighlightNodeIfRequested: (id, newHighlightState) -> + oldHighlightState = @oldState[id] + return unless newHighlightState.flashCount > oldHighlightState.flashCount + + highlightNode = @highlightNodesById[id] + + addFlashClass = => + highlightNode.classList.add(newHighlightState.flashClass) + oldHighlightState.flashClass = newHighlightState.flashClass + @flashTimeoutId = setTimeout(removeFlashClass, newHighlightState.flashDuration) + + removeFlashClass = => + highlightNode.classList.remove(oldHighlightState.flashClass) + oldHighlightState.flashClass = null + clearTimeout(@flashTimeoutId) + + if oldHighlightState.flashClass? + removeFlashClass() + requestAnimationFrame(addFlashClass) + else + addFlashClass() + + oldHighlightState.flashCount = newHighlightState.flashCount