Render a basic gutter

This commit is contained in:
Nathan Sobo
2014-04-07 18:13:19 -06:00
parent a931aaff53
commit 1162af61ed
3 changed files with 82 additions and 16 deletions

View File

@@ -39,14 +39,39 @@ describe "EditorComponent", ->
node.querySelector('.vertical-scrollbar').scrollTop = 2.5 * lineHeightInPixels
component.onVerticalScroll()
expect(node.querySelector('.scrollable-content').style['-webkit-transform']).toBe "translateY(#{-2.5 * lineHeightInPixels}px)"
expect(node.querySelector('.scroll-view-content').style['-webkit-transform']).toBe "translateY(#{-2.5 * lineHeightInPixels}px)"
lines = node.querySelectorAll('.line')
expect(lines.length).toBe 6
expect(lines[0].textContent).toBe editor.lineForScreenRow(2).text
expect(lines[5].textContent).toBe editor.lineForScreenRow(7).text
spacers = node.querySelectorAll('.spacer')
spacers = node.querySelectorAll('.lines .spacer')
expect(spacers[0].offsetHeight).toBe 2 * lineHeightInPixels
expect(spacers[1].offsetHeight).toBe (editor.getScreenLineCount() - 8) * lineHeightInPixels
describe "gutter rendering", ->
it "renders the currently-visible line numbers", ->
nbsp = String.fromCharCode(160)
node.style.height = 4.5 * lineHeightInPixels + 'px'
component.updateAllDimensions()
lines = node.querySelectorAll('.line-number')
expect(lines.length).toBe 6
expect(lines[0].textContent).toBe "#{nbsp}1"
expect(lines[5].textContent).toBe "#{nbsp}6"
node.querySelector('.vertical-scrollbar').scrollTop = 2.5 * lineHeightInPixels
component.onVerticalScroll()
expect(node.querySelector('.line-numbers').style['-webkit-transform']).toBe "translateY(#{-2.5 * lineHeightInPixels}px)"
lines = node.querySelectorAll('.line-number')
expect(lines.length).toBe 6
expect(lines[0].textContent).toBe "#{nbsp}3"
expect(lines[5].textContent).toBe "#{nbsp}8"
spacers = node.querySelectorAll('.line-numbers .spacer')
expect(spacers[0].offsetHeight).toBe 2 * lineHeightInPixels
expect(spacers[1].offsetHeight).toBe (editor.getScreenLineCount() - 8) * lineHeightInPixels
@@ -148,6 +173,11 @@ describe "EditorComponent", ->
expect(cursorNode2.classList.contains('blink-off')).toBe false
describe "selection rendering", ->
scrollViewClientLeft = null
beforeEach ->
scrollViewClientLeft = node.querySelector('.scroll-view').getBoundingClientRect().left
it "renders 1 region for 1-line selections", ->
# 1-line selection
editor.setSelectedScreenRange([[1, 6], [1, 10]])
@@ -156,7 +186,7 @@ describe "EditorComponent", ->
regionRect = regions[0].getBoundingClientRect()
expect(regionRect.top).toBe 1 * lineHeightInPixels
expect(regionRect.height).toBe 1 * lineHeightInPixels
expect(regionRect.left).toBe 6 * charWidth
expect(regionRect.left).toBe scrollViewClientLeft + 6 * charWidth
expect(regionRect.width).toBe 4 * charWidth
it "renders 2 regions for 2-line selections", ->
@@ -167,13 +197,13 @@ describe "EditorComponent", ->
region1Rect = regions[0].getBoundingClientRect()
expect(region1Rect.top).toBe 1 * lineHeightInPixels
expect(region1Rect.height).toBe 1 * lineHeightInPixels
expect(region1Rect.left).toBe 6 * charWidth
expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth
expect(region1Rect.right).toBe node.clientWidth
region2Rect = regions[1].getBoundingClientRect()
expect(region2Rect.top).toBe 2 * lineHeightInPixels
expect(region2Rect.height).toBe 1 * lineHeightInPixels
expect(region2Rect.left).toBe 0
expect(region2Rect.left).toBe scrollViewClientLeft + 0
expect(region2Rect.width).toBe 10 * charWidth
it "renders 3 regions for selections with more than 2 lines", ->
@@ -184,19 +214,19 @@ describe "EditorComponent", ->
region1Rect = regions[0].getBoundingClientRect()
expect(region1Rect.top).toBe 1 * lineHeightInPixels
expect(region1Rect.height).toBe 1 * lineHeightInPixels
expect(region1Rect.left).toBe 6 * charWidth
expect(region1Rect.left).toBe scrollViewClientLeft + 6 * charWidth
expect(region1Rect.right).toBe node.clientWidth
region2Rect = regions[1].getBoundingClientRect()
expect(region2Rect.top).toBe 2 * lineHeightInPixels
expect(region2Rect.height).toBe 3 * lineHeightInPixels
expect(region2Rect.left).toBe 0
expect(region2Rect.left).toBe scrollViewClientLeft + 0
expect(region2Rect.right).toBe node.clientWidth
region3Rect = regions[2].getBoundingClientRect()
expect(region3Rect.top).toBe 5 * lineHeightInPixels
expect(region3Rect.height).toBe 1 * lineHeightInPixels
expect(region3Rect.left).toBe 0
expect(region3Rect.left).toBe scrollViewClientLeft + 0
expect(region3Rect.width).toBe 10 * charWidth
describe "mouse interactions", ->
@@ -290,9 +320,9 @@ describe "EditorComponent", ->
clientCoordinatesForScreenPosition = (screenPosition) ->
positionOffset = editor.pixelPositionForScreenPosition(screenPosition)
editorClientRect = node.getBoundingClientRect()
clientX = editorClientRect.left + positionOffset.left
clientY = editorClientRect.top + positionOffset.top - editor.getScrollTop()
scrollViewClientRect = node.querySelector('.scroll-view').getBoundingClientRect()
clientX = scrollViewClientRect.left + positionOffset.left
clientY = scrollViewClientRect.top + positionOffset.top - editor.getScrollTop()
{clientX, clientY}
buildMouseEvent = (type, properties...) ->

View File

@@ -2,7 +2,7 @@ React = require 'react'
ReactUpdates = require 'react/lib/ReactUpdates'
{div, span} = require 'reactionary'
{$$} = require 'space-pen'
{debounce} = require 'underscore-plus'
{debounce, multiplyString} = require 'underscore-plus'
InputComponent = require './input-component'
SelectionComponent = require './selection-component'
@@ -30,19 +30,40 @@ EditorCompont = React.createClass
className += ' is-focused' if focused
div className: className, tabIndex: -1, style: {fontSize, lineHeight, fontFamily},
div className: 'gutter',
@renderGutterContent()
div className: 'scroll-view', ref: 'scrollView',
InputComponent ref: 'input', className: 'hidden-input', onInput: @onInput, onFocus: @onInputFocused, onBlur: @onInputBlurred
@renderScrollableContent()
@renderScrollViewContent()
div className: 'vertical-scrollbar', ref: 'verticalScrollbar', onScroll: @onVerticalScroll,
div outlet: 'verticalScrollbarContent', style: {height: editor.getScrollHeight()}
renderScrollableContent: ->
renderGutterContent: ->
{editor} = @props
[startRow, endRow] = @getVisibleRowRange()
lineHeightInPixels = editor.getLineHeight()
precedingHeight = startRow * lineHeightInPixels
followingHeight = (editor.getScreenLineCount() - endRow) * lineHeightInPixels
maxDigits = editor.getLastBufferRow().toString().length
style =
height: editor.getScrollHeight()
WebkitTransform: "translateY(#{-editor.getScrollTop()}px)"
div className: 'line-numbers', style: style, [
div className: 'spacer', key: 'top-spacer', style: {height: precedingHeight}
(for bufferRow in @props.editor.bufferRowsForScreenRows(startRow, endRow - 1)
lineNumber = bufferRow + 1
LineNumberComponent({lineNumber, maxDigits, key: lineNumber}))...
div className: 'spacer', key: 'bottom-spacer', style: {height: followingHeight}
]
renderScrollViewContent: ->
{editor} = @props
style =
height: editor.getScrollHeight()
WebkitTransform: "translateY(#{-editor.getScrollTop()}px)"
div {className: 'scrollable-content', style, @onMouseDown},
div {className: 'scroll-view-content', style, @onMouseDown},
@renderCursors()
@renderVisibleLines()
@renderUnderlayer()
@@ -469,3 +490,18 @@ LineComponent = React.createClass
"<span>#{scopeTree.getValueAsHtml({})}</span>"
shouldComponentUpdate: -> false
LineNumberComponent = React.createClass
render: ->
div className: 'line-number', dangerouslySetInnerHTML: {__html: @buildInnerHTML()}
buildInnerHTML: ->
{lineNumber, maxDigits} = @props
lineNumber = lineNumber.toString()
if lineNumber.length < maxDigits
padding = multiplyString('&nbsp;', maxDigits - lineNumber.length)
padding + lineNumber + @iconDivHTML
else
lineNumber + @iconDivHTML
iconDivHTML: '<div class="icon-right"></div>'

View File

@@ -210,7 +210,7 @@
}
.react-wrapper > .editor {
.scrollable-content {
.scroll-view-content {
position: relative;
}
}