Add avoidOverflow: false option for overlays

This commit is contained in:
Nathan Sobo 2017-03-22 12:22:25 -06:00 committed by Antonio Scandurra
parent f2e2475c62
commit 5297e7ab1a
2 changed files with 52 additions and 28 deletions

View File

@ -876,17 +876,22 @@ describe('TextEditorComponent', () => {
})
describe('overlay decorations', () => {
it('renders overlay elements at the specified screen position unless it would overflow the window', async () => {
const {component, element, editor} = buildComponent({width: 200, height: 100, attach: false})
function attachFakeWindow (component) {
const fakeWindow = document.createElement('div')
fakeWindow.style.position = 'absolute'
fakeWindow.style.padding = 20 + 'px'
fakeWindow.style.backgroundColor = 'blue'
fakeWindow.appendChild(element)
fakeWindow.appendChild(component.element)
jasmine.attachToDOM(fakeWindow)
spyOn(component, 'getWindowInnerWidth').andCallFake(() => fakeWindow.getBoundingClientRect().width)
spyOn(component, 'getWindowInnerHeight').andCallFake(() => fakeWindow.getBoundingClientRect().height)
return fakeWindow
}
it('renders overlay elements at the specified screen position unless it would overflow the window', async () => {
const {component, element, editor} = buildComponent({width: 200, height: 100, attach: false})
const fakeWindow = attachFakeWindow(component)
await setScrollTop(component, 50)
await setScrollLeft(component, 100)
@ -949,6 +954,24 @@ describe('TextEditorComponent', () => {
await component.getNextUpdatePromise()
expect(overlayWrapper.classList.contains('b')).toBe(false)
})
it('does not attempt to avoid overflowing the window if `avoidOverflow` is false on the decoration', async () => {
const {component, element, editor} = buildComponent({width: 200, height: 100, attach: false})
const fakeWindow = attachFakeWindow(component)
const overlayElement = document.createElement('div')
overlayElement.style.width = '50px'
overlayElement.style.height = '50px'
overlayElement.style.margin = '3px'
overlayElement.style.backgroundColor = 'red'
const marker = editor.markScreenPosition([4, 25])
const decoration = editor.decorateMarker(marker, {type: 'overlay', item: overlayElement, avoidOverflow: false})
await component.getNextUpdatePromise()
await setScrollLeft(component, 30)
expect(overlayElement.getBoundingClientRect().right).toBeGreaterThan(fakeWindow.getBoundingClientRect().right)
await setScrollLeft(component, 280)
expect(overlayElement.getBoundingClientRect().left).toBeLessThan(fakeWindow.getBoundingClientRect().left)
})
})
describe('mouse input', () => {

View File

@ -576,7 +576,10 @@ class TextEditorComponent {
renderOverlayDecorations () {
return this.decorationsToRender.overlays.map((overlayProps) =>
$(OverlayComponent, Object.assign({didResize: this.updateSync}, overlayProps))
$(OverlayComponent, Object.assign(
{key: overlayProps.element, didResize: this.updateSync},
overlayProps
))
)
}
@ -728,17 +731,14 @@ class TextEditorComponent {
}
addOverlayDecorationToRender (decoration, marker) {
const {class: className, item, position} = decoration
const {class: className, item, position, avoidOverflow} = decoration
const element = atom.views.getView(item)
const screenPosition = (position === 'tail')
? marker.getTailScreenPosition()
: marker.getHeadScreenPosition()
this.requestHorizontalMeasurement(screenPosition.row, screenPosition.column)
this.decorationsToRender.overlays.push({
key: element,
className, element, screenPosition
})
this.decorationsToRender.overlays.push({className, element, avoidOverflow, screenPosition})
}
updateAbsolutePositionedDecorations () {
@ -792,27 +792,28 @@ class TextEditorComponent {
const contentClientRect = this.refs.content.getBoundingClientRect()
for (let i = 0; i < overlayCount; i++) {
const decoration = this.decorationsToRender.overlays[i]
const {element, screenPosition} = decoration
const {element, screenPosition, avoidOverflow} = decoration
const {row, column} = screenPosition
const computedStyle = window.getComputedStyle(element)
let wrapperTop = contentClientRect.top + this.pixelTopForRow(row) + this.getLineHeight()
const elementHeight = element.offsetHeight
const elementTop = wrapperTop + parseInt(computedStyle.marginTop)
const elementBottom = elementTop + elementHeight
const flippedElementTop = wrapperTop - this.getLineHeight() - elementHeight - parseInt(computedStyle.marginBottom)
if (elementBottom > windowInnerHeight && flippedElementTop >= 0) {
wrapperTop -= (elementTop - flippedElementTop)
}
let wrapperLeft = contentClientRect.left + this.pixelLeftForRowAndColumn(row, column)
const elementLeft = wrapperLeft + parseInt(computedStyle.marginLeft)
const elementRight = elementLeft + element.offsetWidth
if (elementLeft < 0) {
wrapperLeft -= elementLeft
} else if (elementRight > windowInnerWidth) {
wrapperLeft -= (elementRight - windowInnerWidth)
if (avoidOverflow !== false) {
const computedStyle = window.getComputedStyle(element)
const elementHeight = element.offsetHeight
const elementTop = wrapperTop + parseInt(computedStyle.marginTop)
const elementBottom = elementTop + elementHeight
const flippedElementTop = wrapperTop - this.getLineHeight() - elementHeight - parseInt(computedStyle.marginBottom)
const elementLeft = wrapperLeft + parseInt(computedStyle.marginLeft)
const elementRight = elementLeft + element.offsetWidth
if (elementBottom > windowInnerHeight && flippedElementTop >= 0) {
wrapperTop -= (elementTop - flippedElementTop)
}
if (elementLeft < 0) {
wrapperLeft -= elementLeft
} else if (elementRight > windowInnerWidth) {
wrapperLeft -= (elementRight - windowInnerWidth)
}
}
decoration.pixelTop = wrapperTop