Skip to content

Commit eda4840

Browse files
authored
Merge pull request #1206 from substance/issue-1187
Author and reference editing from manuscript
2 parents 0766a26 + f8deaf0 commit eda4840

File tree

12 files changed

+240
-14
lines changed

12 files changed

+240
-14
lines changed

src/article/ArticleAPI.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { documentHelpers, includes, orderBy, without, copySelection, selectionHelpers } from 'substance'
1+
import { documentHelpers, includes, orderBy, without, copySelection, selectionHelpers, isString } from 'substance'
22
import TableEditingAPI from './shared/TableEditingAPI'
33
import { importFigures } from './articleHelpers'
44
import { findParentByType } from './shared/nodeHelpers'
@@ -132,6 +132,10 @@ export default class ArticleAPI {
132132
this._setSelection(this._createValueSelection(path))
133133
}
134134

135+
selectFirstRequiredPropertyOfMetadataCard (nodeId) {
136+
this._setSelection(this._selectFirstRequiredPropertyOfMetadataCard(nodeId))
137+
}
138+
135139
_appendChild (collectionPath, data) {
136140
this.editorSession.transaction(tx => {
137141
let node = tx.create(data)
@@ -391,6 +395,9 @@ export default class ArticleAPI {
391395

392396
// ATTENTION: this only works for meta-data cards, thus the special naming
393397
_selectFirstRequiredPropertyOfMetadataCard (node) {
398+
if (isString(node)) {
399+
node = this.getDocument().get(node)
400+
}
394401
let propName = this._getFirstRequiredPropertyName(node)
395402
if (propName) {
396403
let path = [node.id, propName]

src/article/editor/EditorPackage.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import DownloadSupplementaryFileTool from './DownloadSupplementaryFileTool'
4040
import DropFigure from './DropFigure'
4141
import EditBlockFormulaCommand from '../shared/EditBlockFormulaCommand'
4242
import EditDispFormulaTool from './EditDispFormulaTool'
43+
import EditEntityCommand from '../shared/EditEntityCommand'
4344
import EditInlineFormulaCommand from '../shared/EditInlineFormulaCommand'
4445
import EditInlineFormulaTool from './EditInlineFormulaTool'
4546
import EditXrefCommand from '../shared/EditXrefCommand'
@@ -130,6 +131,10 @@ export default {
130131
config.addCommand('download-file', DownloadSupplementaryFileCommand, {
131132
commandGroup: 'file'
132133
})
134+
config.addCommand('edit-author', EditEntityCommand, {
135+
selectionType: 'author',
136+
commandGroup: 'author'
137+
})
133138
config.addCommand('edit-block-formula', EditBlockFormulaCommand, {
134139
commandGroup: 'prompt'
135140
})
@@ -141,6 +146,10 @@ export default {
141146
nodeType: 'inline-formula',
142147
commandGroup: 'prompt'
143148
})
149+
config.addCommand('edit-reference', EditEntityCommand, {
150+
selectionType: 'reference',
151+
commandGroup: 'reference'
152+
})
144153
config.addCommand('edit-xref', EditXrefCommand, {
145154
nodeType: 'xref',
146155
commandGroup: 'prompt'

src/article/editor/styles/_authors-list.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,20 @@
66
cursor: default;
77
}
88

9+
.sc-authors-list .se-contrib {
10+
padding: var(--t-input-padding);
11+
margin: var(--t-negative-list-padding);
12+
cursor: default;
13+
}
14+
915
.sc-authors-list .se-contrib.sm-empty {
1016
color: var(--t-light-text-color);
1117
}
1218

19+
.sc-authors-list .se-contrib.sm-selected {
20+
outline: var(--t-input-focus-border);
21+
}
22+
1323
.sc-authors-list > .se-content {
1424
font-size: var(--t-large-font-size);
1525
}

src/article/editor/styles/_reference-list.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
margin: var(--t-text-spacing) 0;
77
}
88

9+
.sc-reference-list .sc-reference.sm-selected {
10+
outline: var(--t-input-focus-border);
11+
}
12+
913
.sc-reference-list .sc-reference:first-child {
1014
margin-top: 0 !important;
1115
}

src/article/metadata/MetadataPackage.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
AddFigurePanelCommand, MoveFigurePanelCommand,
1919
ReplaceFigurePanelImageCommand, RemoveFigurePanelCommand, OpenFigurePanelImageCommand
2020
} from '../shared/FigurePanelCommands'
21+
import EditEntityCommand from '../shared/EditEntityCommand'
2122
import FiguresSectionComponent from './FiguresSectionComponent'
2223
import InsertFigurePanelTool from '../shared/InsertFigurePanelTool'
2324
import InsertFootnoteCommand from '../shared/InsertFootnoteCommand'
@@ -61,6 +62,14 @@ export default {
6162
config.addCommand('add-figure-panel', AddFigurePanelCommand, {
6263
commandGroup: 'figure-panel'
6364
})
65+
config.addCommand('edit-author', EditEntityCommand, {
66+
selectionType: 'author',
67+
commandGroup: 'author'
68+
})
69+
config.addCommand('edit-reference', EditEntityCommand, {
70+
selectionType: 'reference',
71+
commandGroup: 'reference'
72+
})
6473
config.addCommand('move-down-col-item', MoveCollectionItemCommand, {
6574
direction: 'down',
6675
commandGroup: 'collection'

src/article/shared/ArticleToolbarPackage.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,22 @@ export default {
171171
{ type: 'command-group', name: 'custom-metadata-fields' }
172172
]
173173
},
174+
{
175+
type: 'group',
176+
name: 'author',
177+
style: 'descriptive',
178+
items: [
179+
{ type: 'command-group', name: 'author' }
180+
]
181+
},
182+
{
183+
type: 'group',
184+
name: 'reference',
185+
style: 'descriptive',
186+
items: [
187+
{ type: 'command-group', name: 'reference' }
188+
]
189+
},
174190
{
175191
type: 'group',
176192
name: 'collection',
@@ -194,6 +210,8 @@ export default {
194210
{ type: 'command-group', name: 'file' },
195211
{ type: 'command-group', name: 'figure-panel' },
196212
{ type: 'command-group', name: 'footnote' },
213+
{ type: 'command-group', name: 'author' },
214+
{ type: 'command-group', name: 'reference' },
197215
{ type: 'command-group', name: 'collection' },
198216
{ type: 'command-group', name: 'list' },
199217
{ type: 'command-group', name: 'custom-metadata-fields' }
@@ -326,6 +344,10 @@ export default {
326344
config.addLabel('move-down-metadata-field', 'Move Down Metadata Field')
327345
config.addLabel('move-up-metadata-field', 'Move Up Metadata Field')
328346
config.addLabel('remove-metadata-field', 'Remove Metadata Field')
347+
// Author tools
348+
config.addLabel('edit-author', 'Edit Author')
349+
// Reference tools
350+
config.addLabel('edit-reference', 'Edit Reference')
329351
// Context tools
330352
config.addLabel('context-tools', 'Edit')
331353
// Mode

src/article/shared/AuthorsListComponent.js

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,18 @@ export default class AuthorsListComponent extends CustomSurface {
99
}
1010
}
1111

12+
didMount () {
13+
super.didMount()
14+
15+
const appState = this.context.appState
16+
appState.addObserver(['selection'], this.rerender, this, { stage: 'render' })
17+
}
18+
19+
dispose () {
20+
super.dispose()
21+
this.context.appState.removeObserver(this)
22+
}
23+
1224
render ($$) {
1325
let el = $$('div').addClass('sc-authors-list')
1426
el.append(
@@ -18,15 +30,19 @@ export default class AuthorsListComponent extends CustomSurface {
1830
}
1931

2032
_renderAuthors ($$) {
33+
const sel = this.context.appState.selection
34+
2135
const authors = this._getAuthors()
2236
let els = []
2337
authors.forEach((author, index) => {
24-
let short = author.type === 'organisation'
25-
els.push(
26-
$$('span').addClass('se-contrib').html(
27-
this.context.api._renderEntity(author, { short })
28-
)
29-
)
38+
const short = author.type === 'organisation'
39+
const authorEl = $$('span').addClass('se-contrib').html(
40+
this.context.api._renderEntity(author, { short })
41+
).on('click', this._selectAuthor.bind(this, author.id))
42+
if (sel && sel.customType === 'author' && sel.data.authorId === author.id) {
43+
authorEl.addClass('sm-selected')
44+
}
45+
els.push(authorEl)
3046
if (index < authors.length - 1) {
3147
els.push(', ')
3248
}
@@ -41,4 +57,16 @@ export default class AuthorsListComponent extends CustomSurface {
4157
_getAuthors () {
4258
return this.props.model.getItems()
4359
}
60+
61+
_selectAuthor (authorId) {
62+
const newSel = {
63+
type: 'custom',
64+
customType: 'author',
65+
nodeId: authorId,
66+
data: {
67+
authorId
68+
}
69+
}
70+
this.context.editorSession.setSelection(newSel)
71+
}
4472
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Command } from 'substance'
2+
3+
/*
4+
This command intended to switch view and scroll to the selected node.
5+
Command state becoming active only for certain type of custom selection,
6+
e.g. if you want to use it, provide config with selectionType property.
7+
*/
8+
export default class EditEntityCommand extends Command {
9+
getCommandState (params, context) {
10+
let sel = params.selection
11+
let newState = {
12+
disabled: true
13+
}
14+
15+
if (sel.isCustomSelection()) {
16+
if (sel.customType === this.config.selectionType) {
17+
newState.disabled = false
18+
newState.nodeId = sel.nodeId
19+
}
20+
}
21+
22+
return newState
23+
}
24+
25+
execute (params, context) {
26+
const appState = context.appState
27+
const viewName = appState.get('viewName')
28+
if (viewName !== 'metadata') {
29+
const sel = params.selection
30+
const nodeId = sel.nodeId
31+
context.editor.send('updateViewName', 'metadata')
32+
// HACK: using the ArticlePanel instance to get to the current editor
33+
// so that we can dispatch 'executeCommand'
34+
let editor = context.articlePanel.refs.content
35+
editor.send('scrollTo', { nodeId })
36+
// HACK: this is a mess because context.api is a different instance after
37+
// switching to metadata view
38+
// TODO: we should extend ArticleAPI to allow for this
39+
editor.api.selectFirstRequiredPropertyOfMetadataCard(nodeId)
40+
}
41+
}
42+
}

src/article/shared/ReferenceListComponent.js

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
import { Component } from 'substance'
1+
import { CustomSurface } from 'substance'
22
import { renderNode } from '../../kit'
33
import { getPos } from './nodeHelpers'
44

5-
export default class ReferenceListComponent extends Component {
5+
export default class ReferenceListComponent extends CustomSurface {
66
didMount () {
7-
this.context.appState.addObserver(['document'], this.rerender, this, { stage: 'render', document: { path: ['article', 'references'] } })
7+
super.didMount()
8+
9+
const appState = this.context.appState
10+
appState.addObserver(['document'], this.rerender, this, { stage: 'render', document: { path: ['article', 'references'] } })
11+
appState.addObserver(['selection'], this.rerender, this, { stage: 'render' })
812
}
913

1014
dispose () {
15+
super.dispose()
1116
// TODO: as we have a node for references now, we should turn this into a NodeComponent instead
1217
this.context.appState.removeObserver(this)
1318
}
@@ -20,6 +25,7 @@ export default class ReferenceListComponent extends Component {
2025
}
2126

2227
render ($$) {
28+
const sel = this.context.appState.selection
2329
const bibliography = this._getBibliography()
2430

2531
let el = $$('div').addClass('sc-reference-list')
@@ -31,19 +37,41 @@ export default class ReferenceListComponent extends Component {
3137
}
3238

3339
bibliography.forEach(ref => {
34-
el.append(
35-
renderNode($$, this, ref)
36-
)
40+
const referenceEl = renderNode($$, this, ref)
41+
.ref(ref.id)
42+
.on('click', this._selectReference.bind(this, ref.id))
43+
44+
if (sel && sel.customType === 'reference' && sel.data.referenceId === ref.id) {
45+
referenceEl.addClass('sm-selected')
46+
}
47+
48+
el.append(referenceEl)
3749
})
3850

3951
return el
4052
}
4153

54+
_getCustomResourceId () {
55+
return 'reference-list'
56+
}
57+
4258
_getBibliography () {
4359
let references = this.props.model.getItems()
4460
references.sort((a, b) => {
4561
return getPos(a) - getPos(b)
4662
})
4763
return references
4864
}
65+
66+
_selectReference (referenceId) {
67+
const newSel = {
68+
type: 'custom',
69+
customType: 'reference',
70+
nodeId: referenceId,
71+
data: {
72+
referenceId
73+
}
74+
}
75+
this.context.editorSession.setSelection(newSel)
76+
}
4977
}

src/styles/_texture.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
--t-input-outline-border: 2px solid var(--t-border-color);
9898
--t-input-focus-border: 2px solid rgb(145, 189, 240);
9999
--t-negative-input-padding: -6px; /* This must be the negative of input padding + default border width */
100+
--t-negative-list-padding: -4px; /* Same, but without borders. Used in comma-separated lists. */
100101
--t-border-radius: 5px; /* Default border radius for rounded corners */
101102
--t-tool-border-radius: 3px; /* Default border radius for tools */
102103
--t-default-border: 1px solid var(--t-border-color);

0 commit comments

Comments
 (0)