all files / ui/ AbstractIsolatedNodeComponent.js

77.46% Statements 55/71
68.57% Branches 24/35
58.33% Functions 14/24
77.14% Lines 54/70
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184            85×   85× 85× 85×       85× 85×     85× 85×       265×                 85× 85×       85×   85× 85×       19×   19× 19×       122× 122×       122×           122×         28×           20×                                                                             110× 110× 110× 109× 69×                                 85× 85× 85×       195× 195× 150× 150× 148× 148×     150×   195×           38×                            
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) || Component
 
    // 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
    Iif (!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 componentRegistry = this.context.componentRegistry
    let ComponentClass = componentRegistry.get(node.type)
    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 = []
      Eif (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)
    return (comp && (comp === this || comp.context.isolatedNodeComponent === this))
  }
 
}
 
AbstractIsolatedNodeComponent.prototype._isAbstractIsolatedNodeComponent = true
 
export default AbstractIsolatedNodeComponent