diff --git a/.gitignore b/.gitignore index e44031e5057361cb1195da5cda6c37dd14ff18d8..e67bc2eecc5e8f3d1683a278fefca11f4f137389 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ logs/* node_modules public/assets/* pubsweet.log +.DS_Store diff --git a/app/components/SimpleEditor/Editor.js b/app/components/SimpleEditor/Editor.js index b02447c7d178e27a870ef2c389845872ff9e53b0..5ed5fc84dfefb3183230b3d40ba81b802c97d019 100644 --- a/app/components/SimpleEditor/Editor.js +++ b/app/components/SimpleEditor/Editor.js @@ -21,10 +21,28 @@ class Editor extends ProseEditor { this.handleActions({ 'showComments': function () { this.toggleCommentsArea(true) }, - 'hideComments': function () { this.toggleCommentsArea(false) } + 'hideComments': function () { this.toggleCommentsArea(false) }, + 'trackChangesUpdate': function () { this.updateTrackChange() }, + 'trackChangesViewUpdate': function () { this.updateTrackChangeView() } }) } + updateTrackChange () { + this.extendProps({ + trackChanges: !this.props.trackChanges + }) + + this.props.updateTrackChangesStatus(!this.props.trackChanges) + + this.extendState({ trackChanges: !this.props.trackChanges }) + console.log(this.el) + } + + updateTrackChangeView () { + this.extendState({ trackChangesView: !this.state.trackChangesView }) + console.log('in editor', this.state.trackChangesView) + } + willUpdateState () {} didMount () { @@ -90,7 +108,7 @@ class Editor extends ProseEditor { contentPanelWithSplitPane ) ) - + if (this.state.trackChangesView) el.addClass('track-changes-mode') return el } @@ -103,13 +121,16 @@ class Editor extends ProseEditor { commands: configurator.getSurfaceCommandNames(), containerId: 'body', textTypes: configurator.getTextTypes(), - trackChanges: this.props.trackChanges + trackChanges: this.props.trackChanges, + updateTrackChangesStatus: this.props.updateTrackChangesStatus }).ref('body') } getInitialState () { return { - editorReady: false + editorReady: false, + trackChanges: this.props.trackChanges, + trackChangesView: true } } diff --git a/app/components/SimpleEditor/SimpleEditor.jsx b/app/components/SimpleEditor/SimpleEditor.jsx index 4e5a19db8101ef2ca8895d5a5ed256f3de2013be..c76b7ab684166663f76a7bf68e0cf0eff1190b2d 100644 --- a/app/components/SimpleEditor/SimpleEditor.jsx +++ b/app/components/SimpleEditor/SimpleEditor.jsx @@ -18,11 +18,7 @@ export default class SimpleEditor extends React.Component { constructor (props) { super(props) this.save = this.save.bind(this) - this.toggleTrackChangesStatus = this.toggleTrackChangesStatus.bind(this) - - this.state = { - trackChanges: props.fragment.trackChanges - } + this.updateTrackChangesStatus = this.updateTrackChangesStatus.bind(this) } createSession () { @@ -47,13 +43,10 @@ export default class SimpleEditor extends React.Component { } } - toggleTrackChangesStatus () { + updateTrackChangesStatus () { const { fragment, update } = this.props fragment.trackChanges = !fragment.trackChanges update(fragment) - this.setState({ - trackChanges: fragment.trackChanges - }) } save (source, changes, callback) { @@ -89,6 +82,7 @@ export default class SimpleEditor extends React.Component { history: this.props.history, onSave: onSave, trackChanges: this.props.fragment.trackChanges, + updateTrackChangesStatus: this.updateTrackChangesStatus, updateComments: this.props.updateComments, user: this.props.user }, el) @@ -125,25 +119,9 @@ export default class SimpleEditor extends React.Component { ) : null - let trackChangeStatus = 'Off' - let trackChangesIconClass = 'fa-eye-slash' - - if (this.state.trackChanges === true) { - trackChangeStatus = 'On' - trackChangesIconClass = 'fa-eye' - } - - let trackChanges = - <Alert bsStyle='warning' className='track-changes'> - <span> - <i className={'fa ' + trackChangesIconClass} onClick={this.toggleTrackChangesStatus} /> - Track Changes is {trackChangeStatus} - </span> - </Alert> - return ( <div className='editor-wrapper'> - {viewMode} {trackChanges} + {viewMode} </div> ) } diff --git a/app/components/SimpleEditor/SimpleEditor.scss b/app/components/SimpleEditor/SimpleEditor.scss index 98652dd0fa6a7ac7d28386b50b9aa83bc05c0333..852a679405ba21b3098e01caa8dc365329e2d92a 100644 --- a/app/components/SimpleEditor/SimpleEditor.scss +++ b/app/components/SimpleEditor/SimpleEditor.scss @@ -19,6 +19,9 @@ $teal: #46b9ba; $toolbar-active-bg: rgba(204, 204, 204, .75); $transparent-black: rgba(0, 0, 0, .75); $white: #fff; +// track changes +$inactive-grey: #404040; +$active-blue: #4a90e2; .editor-wrapper { height: 90vh; @@ -39,24 +42,9 @@ $white: #fff; } } - .track-changes { - height: 44px; - position: fixed; - right: 0; - text-align: center; - width: 20%; - z-index: 9999; - - span { - font-size: 14px; - position: relative; - top: 10px; - - i { - font-size: 20px; - padding-right: 10px; - cursor: pointer; - } + .track-changes-mode { + .se-content { + line-height: 38px; } } } @@ -73,27 +61,116 @@ $white: #fff; border: 1px solid $border; border-right: 0; padding-left: 0; - - button { - color: $transparent-black; + vertical-align: middle; + @-moz-document url-prefix() { + .sc-tool-group > .sc-switch-text-type { + margin: 0; + position: relative; + top: 8px; + } } .sm-target-text { - border-right: 1px solid $border; + bottom: 5px; + height: 100%; padding: 0; + position: relative; + .sc-switch-text-type { + margin-left: 1px; + width: 150px; + button { + color: $transparent-black; + height: inherit; + position: relative; + top: 5px; + } + .se-toggle { + background: transparent; + border: 0; + font-family: 'Fira Sans'; + font-size: 14px; + outline: none; + } + + .se-options { + top: 36px; + } + + .se-option { + background-color: $white; + border: 0; + font-family: 'Fira Sans'; + } + } + } // end dropdown + + .sm-target-track-change-enable { + border-right: 1px solid $border; + + button { + background-color: $inactive-grey; + border-radius: 0; + color: $white; + cursor: pointer; + padding: 0 19px; + position: relative; + } + + button::after { + content: 'Record Changes'; + } + + .track-changes-active { + background-color: $active-blue; + color: $white; + padding: 0 10px; + + &:after { + content: 'Recording Changes'; + } + } + } + + .sm-target-track-change-toggle-view { + cursor: pointer; + + button { + background-color: $inactive-grey; + border-radius: 0; + color: $white; + cursor: pointer; + padding: 0 19px; + position: relative; + } + + button::after { + content: 'Changes View Off'; + } + .track-changes-view-active { + background-color: #228b46; + color: $white; + padding: 0 19px + } + .track-changes-view-active::after { + content: 'Changes View On'; + } } .sm-target-document { - border-right: 1px solid $border; - padding-right: 9px; + border-left: 1px solid $border; } - .sm-target-annotations { - border-right: 1px solid $border; + .sm-target-track-change-enable, + .sm-target-track-change-toggle-view { padding: 0 9px; } - .sc-tool-group { + .sm-target-document, + .sm-target-annotations, + .sm-target-insert { + border-right: 1px solid $border; + padding: 0px 9px; + .sc-button { background: transparent; border: 0; @@ -109,49 +186,27 @@ $white: #fff; background: transparent; } } - } + } - .sm-active { - background: $toolbar-active-bg; - color: $black; - margin: 0; - outline: none; - padding: 0; + .sm-active { + background: $toolbar-active-bg; + color: $black; + margin: 0; + outline: none; + padding: 0; - &:after { - bottom: 17px; - color: $black; - content: 'x'; - font-size: 8px; - left: 21px; - position: absolute; - } + &:after { + bottom: 17px; + color: $black; + content: 'x'; + font-size: 8px; + left: 21px; + position: absolute; } } } - .sc-switch-text-type { - margin-left: 1px; - width: 150px; - - .se-toggle { - background: transparent; - border: 0; - font-family: 'Fira Sans'; - font-size: 14px; - outline: none; - } - - .se-options { - top: 36px; - } - - .se-option { - background-color: $white; - border: 0; - font-family: 'Fira Sans'; - } - } + } // End sc-toolbar .se-scrollable { background-color: $primary; @@ -163,7 +218,6 @@ $white: #fff; .se-content { color: $transparent-black; font-family: 'Fira Sans'; - // line-height: 24px; word-wrap: break-word; ::selection { @@ -207,7 +261,7 @@ $white: #fff; border-style: none; border-width: 0; } -} +} // end sc-content .sc-has-comments { .se-content { diff --git a/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js b/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js index fb744b265dd6a9b0d94eaee6a10b3dc159165271..c9ef672c744d68c6b070d882db9b4f4676d09c8a 100644 --- a/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js +++ b/app/components/SimpleEditor/elements/track_change/TrackChangeComponent.js @@ -1,16 +1,43 @@ -import { AnnotationComponent } from 'substance' +import { AnnotationComponent, createAnnotation, deleteNode } from 'substance' class TrackChangeComponent extends AnnotationComponent { render ($$) { const user = this.context.controller.props.user const status = this.props.node.status + const trackChangesView = this.toggleTrackChangeView() + let hideDeletes = '' + let ShowAdditions = '' - var el = $$('span') + if (trackChangesView === false && status === 'delete') { + hideDeletes = 'sc-track-delete-hide' + } + + if (trackChangesView === false && status === 'add') { + ShowAdditions = 'sc-track-add-show' + } + + const accept = $$('a').addClass('sc-track-item') + .on('click', this.acceptTrackChange) + .append('accept') + const reject = $$('a').addClass('sc-track-item') + .on('click', this.rejectTrackChange) + .append('reject') + const seperator = $$('span').addClass('sc-track-separator') + .append(' / ') + const container = $$('span').addClass('sc-accept-reject-container') + .append(accept) + .append(seperator) + .append(reject) + + let el = $$('span') .attr('data-id', this.props.node.id) .attr('data-user', user.username) .attr('data-role', user.teams[0].name) .addClass(this.getClassNames()) + .addClass(hideDeletes) + .addClass(ShowAdditions) .append(this.props.children) + .append(container) const className = 'sc-track-change-' + status el.addClass(className) @@ -18,6 +45,75 @@ class TrackChangeComponent extends AnnotationComponent { return el } +// TODO Move most of the funcs into provider + getSurface () { + const surfaceManager = this.context.surfaceManager + const containerId = this.context.controller.props.containerId + + return surfaceManager.getSurface(containerId) + } + + toggleTrackChangeView () { + const self = this + const surface = self.getSurface() + + return surface._owner.state.trackChangesView + } + + acceptTrackChange () { + const nodeId = this.props.node.id + const status = this.props.node.status + const ds = this.getDocumentSession() + const doc = ds.getDocument() + const nodeData = this.buildNodeData() + + var self = this + + ds.transaction(function (tx, args) { + if (status === 'add') createAnnotation(doc, nodeData) + deleteNode(tx, { nodeId: nodeId }) + if (status === 'delete') self.context.surface.delete(tx, nodeData) + }) + } + + rejectTrackChange () { + const nodeId = this.props.node.id + const status = this.props.node.status + const ds = this.getDocumentSession() + const doc = ds.getDocument() + const nodeData = this.buildNodeData() + + var self = this + + ds.transaction(function (tx, args) { + if (status === 'delete') createAnnotation(doc, nodeData) + deleteNode(tx, { nodeId: nodeId }) + if (status === 'add') self.context.surface.delete(tx, nodeData) + }) + } + + buildNodeData () { + const nodeId = this.props.node.id + const ds = this.getDocumentSession() + const doc = ds.getDocument() + const trackChangeNode = doc.get(nodeId) + + const path = trackChangeNode.path + const startOffset = trackChangeNode.startOffset + const endOffset = trackChangeNode.endOffset + + const sel = ds.createSelection(path, startOffset, endOffset) + + return { + selection: sel, + node: { type: 'paragraph' } + } + } + + getDocumentSession () { + return this.context.documentSession + } + } export default TrackChangeComponent diff --git a/app/components/SimpleEditor/elements/track_change/TrackChangeControlCommand.js b/app/components/SimpleEditor/elements/track_change/TrackChangeControlCommand.js new file mode 100644 index 0000000000000000000000000000000000000000..d3a7f5d04077c345dddd2d51dcc06d1810eeb5f2 --- /dev/null +++ b/app/components/SimpleEditor/elements/track_change/TrackChangeControlCommand.js @@ -0,0 +1,21 @@ +import {Command} from 'substance' + +class TrackChangeControlCommand extends Command { + getCommandState (params) { + let newState = { + disabled: false, + active: false + } + + return newState + } + execute (params, context) { + const surface = context.surfaceManager.getSurface('body') + surface.send('trackChangesUpdate') + return true + } +} + +TrackChangeControlCommand.type = 'track-change-enable' + +export default TrackChangeControlCommand diff --git a/app/components/SimpleEditor/elements/track_change/TrackChangeControlTool.js b/app/components/SimpleEditor/elements/track_change/TrackChangeControlTool.js new file mode 100644 index 0000000000000000000000000000000000000000..e12deb6aa01a61e621d3d61a20d30fb72d1d1376 --- /dev/null +++ b/app/components/SimpleEditor/elements/track_change/TrackChangeControlTool.js @@ -0,0 +1,26 @@ +import { Tool } from 'substance' + +class TrackChangeControlTool extends Tool { + renderButton ($$) { + const el = super.renderButton($$) + let trackChangesMode = this.isTrackChangesOn() + if (trackChangesMode === true) el.addClass('track-changes-active') + return el + } + + getSurface () { + const surfaceManager = this.context.surfaceManager + const containerId = this.context.controller.props.containerId + return surfaceManager.getSurface(containerId) + } + + isTrackChangesOn () { + const surface = this.getSurface() + if (!surface) return + return surface.props.trackChanges + } +} + +TrackChangeControlTool.type = 'track-change-enable' + +export default TrackChangeControlTool diff --git a/app/components/SimpleEditor/elements/track_change/TrackChangeControlViewCommand.js b/app/components/SimpleEditor/elements/track_change/TrackChangeControlViewCommand.js new file mode 100644 index 0000000000000000000000000000000000000000..fa0d4cc8c338ef9479a42bfb69e8f53569045b4d --- /dev/null +++ b/app/components/SimpleEditor/elements/track_change/TrackChangeControlViewCommand.js @@ -0,0 +1,23 @@ +import {Command} from 'substance' + +class TrackChangeControlViewCommand extends Command { + getCommandState (params) { + // console.log(params) + let newState = { + disabled: false, + active: false + } + + return newState + } + execute (params, context) { + const surface = context.surfaceManager.getSurface('body') + surface.send('trackChangesViewUpdate') + surface.selectAll() + surface.selectFirst() + } +} + +TrackChangeControlViewCommand.type = 'track-change-toggle-view' + +export default TrackChangeControlViewCommand diff --git a/app/components/SimpleEditor/elements/track_change/TrackChangeControlViewTool.js b/app/components/SimpleEditor/elements/track_change/TrackChangeControlViewTool.js new file mode 100644 index 0000000000000000000000000000000000000000..1bb44a8e40a06aca4a85f32b335377462002a463 --- /dev/null +++ b/app/components/SimpleEditor/elements/track_change/TrackChangeControlViewTool.js @@ -0,0 +1,26 @@ +import { Tool } from 'substance' + +class TrackChangeControlViewTool extends Tool { + renderButton ($$) { + const el = super.renderButton($$) + let trackChangesViewMode = this.isTrackChangesViewOn() + if (trackChangesViewMode === true) el.addClass('track-changes-view-active') + return el + } + + getSurface () { + const surfaceManager = this.context.surfaceManager + const containerId = this.context.controller.props.containerId + return surfaceManager.getSurface(containerId) + } + + isTrackChangesViewOn () { + const surface = this.getSurface() + if (!surface) return + return surface._owner.state.trackChangesView + } +} + +TrackChangeControlViewTool.type = 'track-change-toggle-view' + +export default TrackChangeControlViewTool diff --git a/app/components/SimpleEditor/elements/track_change/TrackChangePackage.js b/app/components/SimpleEditor/elements/track_change/TrackChangePackage.js index ac7a5ad50b85f96d4d73f99f0f397f2e6b556096..1b7964623c4a1024c6383e031a4276033574bf62 100644 --- a/app/components/SimpleEditor/elements/track_change/TrackChangePackage.js +++ b/app/components/SimpleEditor/elements/track_change/TrackChangePackage.js @@ -2,6 +2,10 @@ import TrackChange from './TrackChange' import TrackChangeCommand from './TrackChangeCommand' import TrackChangeComponent from './TrackChangeComponent' import TrackChangeHTMLConverter from './TrackChangeHTMLConverter' +import TrackChangeControlTool from './TrackChangeControlTool' +import TrackChangeControlViewTool from './TrackChangeControlViewTool' +import TrackChangeControlCommand from './TrackChangeControlCommand' +import TrackChangeControlViewCommand from './TrackChangeControlViewCommand' export default { name: 'track-change', @@ -11,7 +15,18 @@ export default { config.addComponent(TrackChange.type, TrackChangeComponent) config.addConverter('html', TrackChangeHTMLConverter) + config.addTool('track-change-enable', TrackChangeControlTool, { + target: 'track-change-enable' + }) + + config.addTool('track-change-toggle-view', TrackChangeControlViewTool, { + target: 'track-change-toggle-view' + }) + + config.addCommand('track-change-enable', TrackChangeControlCommand) + config.addCommand('track-change-toggle-view', TrackChangeControlViewCommand) config.addCommand(TrackChange.type, TrackChangeCommand, { nodeType: TrackChange.type }) + config.addIcon('track-change', { 'fontawesome': 'fa-eye' }) config.addLabel('track-change', { diff --git a/app/components/SimpleEditor/elements/track_change/trackChange.scss b/app/components/SimpleEditor/elements/track_change/trackChange.scss index 067dcee1646564c1af77d992401205517b9f974d..6bbb6a2ba3e1eac4c3ba3364f8050a8f708f6b0e 100644 --- a/app/components/SimpleEditor/elements/track_change/trackChange.scss +++ b/app/components/SimpleEditor/elements/track_change/trackChange.scss @@ -1,3 +1,4 @@ +$blue: #4a90e2; $red: #f00; $yellow: #f7f70c; @@ -6,12 +7,41 @@ $yellow: #f7f70c; } .sc-track-change-add { - background-color: $yellow; + color: $blue; // display: inline-block; text-decoration: none; } .sc-track-change-delete { - background-color: $red; + // background-color: $red; text-decoration: line-through; } + +.sc-track-delete-hide { + display: none; +} + +.sc-track-add-show { + color: inherit !important; + + .sc-accept-reject-container { + display: none; + } +} + +.sc-accept-reject-container { + display: inline-block; + font-size: 14px; + margin-left: -90px; + margin-top: 20px; + position: absolute; + + .sc-track-item { + color: $blue; + cursor: pointer; + } + + .sc-track-separator { + color: $blue; + } +}