substance
Version:
Substance is a JavaScript library for web-based content editing. It provides building blocks for realizing custom text editors and web-based publishing system. It is developed to power our online editing platform [Substance](http://substance.io).
141 lines (121 loc) • 3.67 kB
JavaScript
import { getSelectionRect, getRelativeRect } from '../util/windowUtils'
import getRelativeMouseBounds from '../util/getRelativeMouseBounds'
import platform from '../util/platform'
import Component from '../dom/Component'
export default class AbstractScrollPane extends Component {
getActionHandlers () {
return {
scrollSelectionIntoView: this._scrollSelectionIntoView
}
}
getChildContext () {
return {
scrollPane: this
}
}
getName () {
return this.props.name
}
/*
Determine mouse bounds relative to content element
and emit context-menu:opened event with positioning hints
*/
_onContextMenu (e) {
e.preventDefault()
const mouseBounds = this._getMouseBounds(e)
this.emit('context-menu:opened', {
mouseBounds: mouseBounds
})
}
_scrollRectIntoView (rect) {
if (!rect) return
// console.log('AbstractScrollPane._scrollRectIntoView()')
const upperBound = this.getScrollPosition()
const lowerBound = upperBound + this.getHeight()
const selTop = rect.top
const selBottom = rect.top + rect.height
if ((selTop < upperBound && selBottom < upperBound) ||
(selTop > lowerBound && selBottom > lowerBound)) {
this.setScrollPosition(selTop)
}
}
_scrollSelectionIntoView () {
this._scrollRectIntoView(this._getSelectionRect())
}
/**
Returns the height of scrollPane (inner content overflows)
*/
getHeight () {
throw new Error('Abstract method')
}
/**
Returns the cumulated height of a panel's content
*/
getContentHeight () {
throw new Error('Abstract method')
}
getContentElement () {
// TODO: should be wrapped in DefaultDOMElement
throw new Error('Abstract method')
}
/**
Get the `.se-scrollable` element
*/
getScrollableElement () {
throw new Error('Abstract method')
}
/**
Get current scroll position (scrollTop) of `.se-scrollable` element
*/
getScrollPosition () {
throw new Error('Abstract method')
}
setScrollPosition () {
throw new Error('Abstract method')
}
/**
Get offset relative to `.se-content`.
@param {DOMNode} el DOM node that lives inside the
*/
getPanelOffsetForElement(el) { // eslint-disable-line
throw new Error('Abstract method')
}
/**
Scroll to a given sub component.
@param {String} componentId component id, must be present in data-id attribute
*/
scrollTo(componentId, onlyIfNotVisible) { // eslint-disable-line
throw new Error('Abstract method')
}
_getContentRect () {
return this.getContentElement().getNativeElement().getBoundingClientRect()
}
/*
Get selection rectangle relative to panel content element
*/
_getSelectionRect () {
const editorState = this.context.editorState
const sel = editorState.selection
let selectionRect
if (platform.inBrowser && sel && !sel.isNull()) {
const contentEl = this.getContentElement()
const contentRect = contentEl.getNativeElement().getBoundingClientRect()
if (sel.isNodeSelection()) {
const nodeId = sel.nodeId
const nodeEl = contentEl.find(`*[data-id="${nodeId}"]`)
if (nodeEl) {
const nodeRect = nodeEl.getNativeElement().getBoundingClientRect()
selectionRect = getRelativeRect(contentRect, nodeRect)
} else {
console.error(`FIXME: could not find a node with data-id=${nodeId}`)
}
} else {
selectionRect = getSelectionRect(contentRect)
}
}
return selectionRect
}
_getMouseBounds (e) {
return getRelativeMouseBounds(e, this.getContentElement().getNativeElement())
}
}