Skip to content
Snippets Groups Projects
Editor.js 7.92 KiB
Newer Older
import { includes, some } from 'lodash'

john's avatar
john committed
import {
  ProseEditor,
chris's avatar
chris committed
  // TOCProvider,
john's avatar
john committed
} from 'substance'

chris's avatar
chris committed
import Comments from './panes/Comments/CommentBoxList'
import CommentsProvider from './panes/Comments/CommentsProvider'
john's avatar
john committed
import ContainerEditor from './ContainerEditor'
chris's avatar
chris committed
import Notes from './panes/Notes/Notes'
import NotesProvider from './panes/Notes/NotesProvider'
// import TableOfContents from './panes/TableOfContents/TableOfContents'
chris's avatar
chris committed
import TrackChangesProvider from './elements/track_change/TrackChangesProvider'
chris's avatar
chris committed

import ModalWarning from './elements/modal_warning/ModalWarning'
john's avatar
john committed

class Editor extends ProseEditor {
  constructor (parent, props) {
    super(parent, props)

    this.handleActions({
      // TODO -- clean them up like changesNotSaved
      'trackChangesUpdate': function () { this.updateTrackChange() },
      'trackChangesViewToggle': function () { this.trackChangesViewToggle() },
      'changesNotSaved': this.changesNotSaved,
      'closeModal': this.closeModal
john's avatar
john committed
    })
  }
  trackChangesViewToggle () {
    this.extendState({
      trackChangesView: !this.state.trackChangesView
    })
  updateTrackChange () {
    // TODO -- clean up this.props and this.refs
    this.extendProps({ trackChanges: !this.props.trackChanges })
    this.props.updateTrackChangesStatus(!this.props.trackChanges)
chris's avatar
chris committed
    this.refs.toolbar.extendProps({trackChanges: this.props.trackChanges})
john's avatar
john committed
  willUpdateState () {}
john's avatar
john committed
  didMount () {
    this.extendState({ editorReady: true })
    this.on('noteSelected', this.scrollTo, this)
john's avatar
john committed
  render ($$) {
    // const { trackChangesView } = this.state
    // const canToggleTrackChanges = this.canToggleTrackChanges()

    const el = $$('div').addClass('sc-prose-editor')

    // left side: editor and toolbar
    let toolbar = this._renderToolbar($$)
chris's avatar
chris committed
    let toolbarLeft = this._renderToolbarLeft($$)
    let editor = this._renderEditor($$)
john's avatar
john committed

    let SplitPane = this.componentRegistry.get('split-pane')
    let ScrollPane = this.componentRegistry.get('scroll-pane')
    let Overlay = this.componentRegistry.get('overlay')

    // TODO -- unnecessary // posssibly breaks book builder dnd
    let ContextMenu = this.componentRegistry.get('context-menu') // new what does it do?
john's avatar
john committed
    // let Dropzones = this.componentRegistry.get('dropzones') // new what does it do?
    const notesEditorProps = {
      book: this.props.book,
      comments: this.props.fragment.comments,
      containerId: 'notes',
      history: this.props.history,
      disabled: this.props.disabled,
      fragment: this.props.fragment,
      trackChanges: this.props.trackChanges,
      trackChangesView: this.state.trackChangesView,
      update: this.props.update,
      user: this.props.user
    }
    const footerNotes = $$(Notes, notesEditorProps).ref('footer-notes')
    // const props = {
    //   book: this.props.book,
    //   fragment: this.props.fragment,
    //   history: this.props.history
    // }
    // const toc = $$(TableOfContents, props)
    var commentsPane = this.state.editorReady
    ? $$(Comments, {
      // TODO -- should probably remove the || {}
      comments: this.props.fragment.comments || {},
      fragment: this.props.fragment,
      update: this.props.update,
      user: this.props.user
    }).addClass('sc-comments-pane')
    : $$('div')

chris's avatar
chris committed
    const editorWithComments = $$(SplitPane, { sizeA: '80%', splitType: 'vertical' })
        editor,
    const sideNav = $$('div').addClass('sidenav').append(toolbarLeft)
chris's avatar
chris committed

    const contentPanel = $$(ScrollPane, {
      name: 'contentPanel',
      scrollbarPosition: 'right'
    .append(editorWithComments,
      $$(Overlay),
      $$(ContextMenu),
chris's avatar
chris committed
      sideNav
john's avatar
john committed
    )
    .attr('id', 'content-panel')
    .ref('contentPanel')

chris's avatar
chris committed
    const contentPanelWithSplitPane = $$(SplitPane, { sizeA: '95%', splitType: 'vertical' })
      .append(
        contentPanel,
        $$('div')
      )
chris's avatar
chris committed
    const ToolbarWithEditor = $$(SplitPane, { splitType: 'horizontal' })
      .append(
        toolbar,
        contentPanelWithSplitPane
      )

chris's avatar
chris committed
      $$(SplitPane, { sizeA: '85%', splitType: 'horizontal' })
john's avatar
john committed
        .append(
chris's avatar
chris committed
          ToolbarWithEditor,
          footerNotes
john's avatar
john committed
    )
    const modal = $$(ModalWarning, {
      width: 'medium',
      textAlign: 'center'
    })

    if (this.state.changesNotSaved) {
      return el.append(modal)
    }
  // TODO -- leverage ProseEditor's this._renderToolbar maybe?
john's avatar
john committed
  _renderToolbar ($$) {
    let viewMode = this.props.disabled ? $$('span')
        .addClass('view-mode')
        .append('Editor is in Read-Only mode')
        : ''
    let commandStates = this.commandManager.getCommandStates()

    return $$('div')
      .addClass('se-toolbar-wrapper')
      .append(
        $$(Toolbar, {
          commandStates: commandStates,
          trackChanges: this.props.trackChanges,
          trackChangesView: this.state.trackChangesView,
          toolGroups: [
john's avatar
john committed
            'document',
            'annotations',
            'track-change-enable',
            'track-change-toggle-view'
          ]
        }).ref('toolbar')
      )
      .append(viewMode)
john's avatar
john committed
  }

chris's avatar
chris committed
  _renderToolbarLeft ($$) {
    let commandStates = this.commandManager.getCommandStates()

    return $$('div')
      .addClass('se-toolbar-wrapper')
      .append(
        $$(Toolbar, {
          commandStates: commandStates,
          trackChanges: this.props.trackChanges,
          trackChangesView: this.state.trackChangesView,
chris's avatar
chris committed
          toolGroups: ['text', 'default']
        }
      )
      .ref('toolbarLeft')
    )
john's avatar
john committed
  _renderEditor ($$) {
    const configurator = this.props.configurator
    const editing = this.props.disabled ? 'selection' : 'full'
    return $$(ContainerEditor, {
      commands: configurator.getSurfaceCommandNames(),
      containerId: 'body',
chris's avatar
chris committed
      spellcheck: 'native',
      textTypes: configurator.getTextTypes(),
      trackChanges: this.props.trackChanges,
      updateTrackChangesStatus: this.props.updateTrackChangesStatus,
      history: this.props.history,
      book: this.props.book
john's avatar
john committed
  getInitialState () {
      changesNotSaved: false,
      editorReady: false,
      trackChangesView: true
  canToggleTrackChanges () {
    const { user } = this.props
    const accepted = ['admin', 'production-editor', 'copy-editor']
    return some(accepted, (role) => includes(user.roles, role))
john's avatar
john committed
  scrollTo (nodeId) {
    this.refs.contentPanel.scrollTo(nodeId)
  }

chris's avatar
chris committed
  // Modal funcionality

  changesNotSaved () {
    this.extendState({ changesNotSaved: true })
  }

  closeModal () {
    this.extendState({ changesNotSaved: false })
  }

john's avatar
john committed
  getChildContext () {
    const oldContext = super.getChildContext()
    const doc = this.doc
    // const tocProvider = new TOCProvider(doc, {
    //   containerId: this.props.containerId
    // })
    // notes provider
chris's avatar
chris committed
    const notesProvider = new NotesProvider(doc)
    // comments provider
    const commentsProvider = new CommentsProvider(doc, {
      commandManager: this.commandManager,
      comments: this.props.fragment.comments,
      containerId: this.props.containerId,
      controller: this,
      editorSession: this.editorSession,
      fragment: this.props.fragment,
      surfaceManager: this.surfaceManager,
      update: this.props.update
    })
    // TODO -- do I need all of these?
    // track changes provider
chris's avatar
chris committed
    const trackChangesProvider = new TrackChangesProvider(doc, {
      commandManager: this.commandManager,
      containerId: this.props.containerId,
      controller: this,
      editorSession: this.editorSession,
chris's avatar
chris committed
      surfaceManager: this.surfaceManager,
      user: this.props.user
    })
    // attach all to context
      notesProvider,
      commentsProvider,
      trackChangesProvider

  dispose () {
    this.editorSession.dragManager.dispose()
  }
john's avatar
john committed
export default Editor