Skip to content
Snippets Groups Projects
ContainerEditor.js 5.52 KiB
Newer Older
import { each, keys, includes } from 'lodash'
john's avatar
john committed
import {
  ContainerEditor as SubstanceContainerEditor,
  // deleteCharacter,
  // deleteSelection,
john's avatar
john committed
  Surface,
john's avatar
john committed
} from 'substance'

class ContainerEditor extends SubstanceContainerEditor {
  render ($$) {
    // TODO -- better ways to write this in es6
    var el = Surface.prototype.render.call(this, $$)

    var doc = this.getDocument()
    var containerId = this.getContainerId()
    var containerNode = doc.get(containerId)

    if (!containerNode) {
      console.warn('No container node found for ', containerId)
    }

    el.addClass('sc-container-editor container-node ' + containerId)
      .attr({
        spellCheck: false,
        'data-id': containerId
      })

    // if it IS empty, handle in didMount
    if (!this.isEmpty()) {
      containerNode.getNodes().forEach(function (node) {
        el.append(this._renderNode($$, node).ref(node.id))
      }.bind(this))
    }

    // TODO -- should maybe change to isEditable ?
    // or maybe delete it? we never pass a disabled prop explicitly
    if (!this.props.disabled) {
      el.addClass('sm-enabled')
      el.setAttribute('contenteditable', true)
    }

  didMount () {
    // TODO -- replace with super.didMount()
    Surface.prototype.didMount.apply(this, arguments)

    this.container.on('nodes:changed', this.onContainerChange, this)

    // this.context.documentSession.on('didUpdate', (change, info) => {
    //   // console.log(this.getSelection())
    //   // console.log(this.context.commandManager.getCommandStates().strong.mode)
    //   // console.log(this.context.commandManager.getCommandStates()['track-change'].mode)
    //   console.log('\n did update')
    //   console.log(this.getCommandStates())
    //   console.log('change', change)
    //   console.log('info', info)
    //
    //   if (info.track) change.change.info.track = true
    // }, this)

    if (this.isEmpty()) this.createText()

    if (this.isReadOnlyMode()) {
      const documentSession = this.getDocumentSession()
      documentSession.on('didUpdate', this.disableToolbar, this)

      this.addTargetToLinks()
    }
  }

    // console.log(this.context)
    event.preventDefault()
    event.stopPropagation()

    if (!event.data) return
    this._state.skipNextObservation = true

    if (!this.props.trackChanges) {
      this.transaction(function (tx, args) {
        if (this.domSelection) this.domSelection.clear()
        args.text = event.data
        return this.insertText(tx, args)
      }.bind(this), { action: 'type' })
      return
    }

    const trackChangesProvider = this.context.trackChangesProvider
    const status = 'add'
    trackChangesProvider.handleTransaction(event, status)

    // const isSelCollapsed = this.getSelection().isCollapsed()
    // if (!isSelCollapsed) {
    //   const trackChangesProvider = this.context.trackChangesProvider
    //   const status = 'delete'
    //   trackChangesProvider.handleTransaction(status)
    // }
    //
    // this.transaction(function (tx, args) {
    //   if (this.domSelection) this.domSelection.clear()
    //   args.text = event.data
    //   return this.insertText(tx, args)
    // }.bind(this), { action: 'type' })
    //
    // // don't rewrite the above, call it with super
    // if (this.props.trackChanges) {
    //   const status = 'add'
    //   trackChangesProvider.handleTransaction(status)
    // }
  }

  _handleDeleteKey (event) {
    event.preventDefault()
    event.stopPropagation()
    let direction = (event.keyCode === keys.BACKSPACE) ? 'left' : 'right'
    if (this.props.trackChanges) {
      const trackChangesProvider = this.context.trackChangesProvider
      const status = 'delete'
      trackChangesProvider.handleTransaction(event, status, direction)
    } else {
      this.transaction(function (tx, args) {
        args.direction = direction
        return this.delete(tx, args)
      }.bind(this), { action: 'delete' })
  // delete (tx, args) {
  //   let sel = args.selection
  //   if (!sel.isCollapsed()) {
  //     return deleteSelection(tx, args)
  //   } else if (sel.isPropertySelection() || sel.isNodeSelection()) {
  //     return deleteCharacter(tx, args)
  //   }
  // }

  // create an empty paragraph with an empty node
  // then select it for cursor focus
john's avatar
john committed
  createText () {
    var newSel

    this.transaction(function (tx) {
      var container = tx.get(this.props.containerId)
      var textType = tx.getSchema().getDefaultTextType()

      var node = tx.create({
        id: uuid(textType),
        type: textType,
        content: ''
      })

      container.show(node.id)

      newSel = tx.createSelection({
        type: 'property',
        path: [ node.id, 'content' ],
        startOffset: 0,
        endOffset: 0
      })
    }.bind(this))

    this.rerender()
    this.setSelection(newSel)
  }

  // only runs if editor is in read-only mode
  // disables all tools, apart from comments
  disableToolbar () {
    const commandStates = this.getCommandStates()

    each(keys(commandStates), key => {
      const allowed = ['comment', 'note', 'save', 'undo', 'redo']
      if (!includes(allowed, key)) commandStates[key].disabled = true
    })
  }

  getCommandStates () {
    const commandManager = this.context.commandManager
    return commandManager.getCommandStates()
  }

  isReadOnlyMode () {
    return !this.isEditable() && this.isSelectable()
  }

john's avatar
john committed
  addTargetToLinks () {
    const allLinks = this.el.findAll('a')
    each(allLinks, link =>
      link.attr('target', '_blank')
    )
  }
john's avatar
john committed
export default ContainerEditor