mirror of
https://github.com/atom/atom.git
synced 2026-02-17 01:51:54 -05:00
Create, update and destroy highlights manually
Etch's reconciliation routine causes elements to be sometimes re-ordered. In order to move an element, however, Etch needs to first detach it from the DOM and then re-append it at the right location. This behavior is unacceptable for highlight decorations because it could re-start CSS animations on a certain highlight decoration when a completely different one is added or removed. Even though we are still interested in restructuring etch's reconciliation logic to prevent unwanted re-orderings, with this commit we are switching to a custom routine to create/update/remove highlight decorations that prevents unnecessary moves and, as a result, fixes the undesired behavior described above.
This commit is contained in:
@@ -3417,8 +3417,10 @@ class CursorsAndInputComponent {
|
||||
|
||||
class LinesTileComponent {
|
||||
constructor (props) {
|
||||
this.highlightComponentsByKey = new Map()
|
||||
this.props = props
|
||||
etch.initialize(this)
|
||||
this.updateHighlights()
|
||||
this.createLines()
|
||||
this.updateBlockDecorations({}, props)
|
||||
}
|
||||
@@ -3432,13 +3434,22 @@ class LinesTileComponent {
|
||||
this.updateLines(oldProps, newProps)
|
||||
this.updateBlockDecorations(oldProps, newProps)
|
||||
}
|
||||
this.updateHighlights()
|
||||
}
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.highlightComponentsByKey.forEach((highlightComponent) => {
|
||||
highlightComponent.destroy()
|
||||
})
|
||||
this.highlightComponentsByKey.clear()
|
||||
|
||||
for (let i = 0; i < this.lineComponents.length; i++) {
|
||||
this.lineComponents[i].destroy()
|
||||
}
|
||||
this.lineComponents.length = 0
|
||||
|
||||
return etch.destroy(this)
|
||||
}
|
||||
|
||||
render () {
|
||||
@@ -3456,34 +3467,12 @@ class LinesTileComponent {
|
||||
backgroundColor: 'inherit'
|
||||
}
|
||||
},
|
||||
this.renderHighlights()
|
||||
// Lines and block decorations will be manually inserted here for efficiency
|
||||
)
|
||||
}
|
||||
|
||||
renderHighlights () {
|
||||
const {top, lineHeight, highlightDecorations} = this.props
|
||||
|
||||
let children = null
|
||||
if (highlightDecorations) {
|
||||
const decorationCount = highlightDecorations.length
|
||||
children = new Array(decorationCount)
|
||||
for (let i = 0; i < decorationCount; i++) {
|
||||
const highlightProps = Object.assign(
|
||||
{parentTileTop: top, lineHeight},
|
||||
highlightDecorations[i]
|
||||
)
|
||||
children[i] = $(HighlightComponent, highlightProps)
|
||||
highlightDecorations[i].flashRequested = false
|
||||
}
|
||||
}
|
||||
|
||||
return $.div(
|
||||
{
|
||||
$.div({
|
||||
ref: 'highlights',
|
||||
className: 'highlights',
|
||||
style: {contain: 'layout'}
|
||||
},
|
||||
children
|
||||
style: {layout: 'contain'}
|
||||
})
|
||||
// Lines and block decorations will be manually inserted here for efficiency
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3676,6 +3665,40 @@ class LinesTileComponent {
|
||||
}
|
||||
}
|
||||
|
||||
updateHighlights () {
|
||||
const {top, lineHeight, highlightDecorations} = this.props
|
||||
|
||||
const visibleHighlightDecorations = new Set()
|
||||
if (highlightDecorations) {
|
||||
for (let i = 0; i < highlightDecorations.length; i++) {
|
||||
const highlightDecoration = highlightDecorations[i]
|
||||
|
||||
const highlightProps = Object.assign(
|
||||
{parentTileTop: top, lineHeight},
|
||||
highlightDecorations[i]
|
||||
)
|
||||
let highlightComponent = this.highlightComponentsByKey.get(highlightDecoration.key)
|
||||
if (highlightComponent) {
|
||||
highlightComponent.update(highlightProps)
|
||||
} else {
|
||||
highlightComponent = new HighlightComponent(highlightProps)
|
||||
this.refs.highlights.appendChild(highlightComponent.element)
|
||||
this.highlightComponentsByKey.set(highlightDecoration.key, highlightComponent)
|
||||
}
|
||||
|
||||
highlightDecorations[i].flashRequested = false
|
||||
visibleHighlightDecorations.add(highlightDecoration.key)
|
||||
}
|
||||
}
|
||||
|
||||
this.highlightComponentsByKey.forEach((highlightComponent, key) => {
|
||||
if (!visibleHighlightDecorations.has(key)) {
|
||||
highlightComponent.destroy()
|
||||
this.highlightComponentsByKey.delete(key)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
shouldUpdate (newProps) {
|
||||
const oldProps = this.props
|
||||
if (oldProps.top !== newProps.top) return true
|
||||
@@ -3876,6 +3899,17 @@ class HighlightComponent {
|
||||
if (this.props.flashRequested) this.performFlash()
|
||||
}
|
||||
|
||||
destroy () {
|
||||
if (this.timeoutsByClassName) {
|
||||
this.timeoutsByClassName.forEach((timeout) => {
|
||||
window.clearTimeout(timeout)
|
||||
})
|
||||
this.timeoutsByClassName.clear()
|
||||
}
|
||||
|
||||
return etch.destroy(this)
|
||||
}
|
||||
|
||||
update (newProps) {
|
||||
this.props = newProps
|
||||
etch.updateSync(this)
|
||||
|
||||
Reference in New Issue
Block a user