UNPKG

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 systems.

200 lines (165 loc) 5.33 kB
import { keys, platform } from '../util' import Component from './Component' class AbstractIsolatedNodeComponent extends Component { constructor(...args) { super(...args) this.name = this.props.node.id this._id = this.context.surface.id +'/'+this.name this._state = { selectionFragment: null } this.handleAction('escape', this.escape) this.ContentClass = this._getContentClass(this.props.node) // NOTE: FF does not allow to navigate contenteditable isles let useBlocker = platform.isFF || !this.ContentClass.noBlocker this.blockingMode = useBlocker ? 'closed' : 'open' } getChildContext() { return { isolatedNodeComponent: this, // TODO: we should clear 'surface' here // so that we know that we are not controlled by a surface surface: undefined } } getInitialState() { let selState = this.context.editorSession.getSelectionState() return this._deriveStateFromSelectionState(selState) } didMount() { super.didMount() let editorSession = this.context.editorSession editorSession.onRender('selection', this._onSelectionChanged, this) } dispose() { super.dispose.call(this) let editorSession = this.context.editorSession editorSession.off(this) } renderContent($$, node, options = {}) { let ComponentClass = this.ContentClass if (!ComponentClass) { console.error('Could not resolve a component for type: ' + node.type) return $$(this.__elementTag) } else { let props = Object.assign({ disabled: this.props.disabled, node: node, isolatedNodeState: this.state.mode, focused: (this.state.mode === 'focused') }, options) return $$(ComponentClass, props) } } getId() { return this._id } get id() { return this.getId() } getMode() { return this.state.mode } isOpen() { return this.blockingMode === 'open' } isClosed() { return this.blockingMode === 'closed' } isNotSelected() { return !this.state.mode } isSelected() { return this.state.mode === 'selected' } isCoSelected() { return this.state.mode === 'co-selected' } isFocused() { return this.state.mode === 'focused' } isCoFocused() { return this.state.mode === 'co-focused' } getParentSurface() { return this.context.surface } escape() { // console.log('Escaping from IsolatedNode', this.id) this.selectNode() } _onSelectionChanged() { let editorSession = this.context.editorSession let newState = this._deriveStateFromSelectionState(editorSession.getSelectionState()) if (!newState && this.state.mode) { this.extendState({ mode: null }) } else if (newState && newState.mode !== this.state.mode) { this.extendState(newState) } } onKeydown(event) { // console.log('####', event.keyCode, event.metaKey, event.ctrlKey, event.shiftKey); // TODO: while this works when we have an isolated node with input or CE, // there is no built-in way of receiving key events in other cases // We need a global event listener for keyboard events which dispatches to the current isolated node if (event.keyCode === keys.ESCAPE && this.state.mode === 'focused') { event.stopPropagation() event.preventDefault() this.escape() } } _getContentClass(node) { let ComponentClass // first try to get the component registered for this node ComponentClass = this.getComponent(node.type, true) // otherwise just use an empty Component if (!ComponentClass) { ComponentClass = Component } return ComponentClass } _getSurface(selState) { let surface = selState.get('surface') if (surface === undefined) { let sel = selState.getSelection() if (sel && sel.surfaceId) { let surfaceManager = this.context.surfaceManager surface = surfaceManager.getSurface(sel.surfaceId) } else { surface = null } selState.set('surface', surface) } return surface } // compute the list of surfaces and isolated nodes // for the given selection _getIsolatedNodes(selState) { let isolatedNodes = selState.get('isolatedNodes') if (!isolatedNodes) { let sel = selState.getSelection() isolatedNodes = [] if (sel && sel.surfaceId) { let surfaceManager = this.context.surfaceManager let surface = surfaceManager.getSurface(sel.surfaceId) isolatedNodes = surface.getComponentPath().filter(comp => comp._isAbstractIsolatedNodeComponent) } selState.set('isolatedNodes', isolatedNodes) } return isolatedNodes } _shouldConsumeEvent(event) { let comp = Component.unwrap(event.target) let isolatedNodeComponent = this._getIsolatedNode(comp) return (isolatedNodeComponent === this) } _getIsolatedNode(comp) { if (comp._isAbstractIsolatedNodeComponent) { return this } else if (comp.context.isolatedNodeComponent) { return comp.context.isolatedNodeComponent } else if (comp.context.surface) { return comp.context.surface.context.isolatedNodeComponent } } } AbstractIsolatedNodeComponent.prototype._isAbstractIsolatedNodeComponent = true export default AbstractIsolatedNodeComponent