@manuscripts/body-editor 3.15.1 → 3.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/commands.js +45 -10
- package/dist/cjs/components/form/FormFooter.js +2 -2
- package/dist/cjs/components/keyboard-shortcuts-modal/KeyboardShortcutsModal.js +4 -2
- package/dist/cjs/components/views/CrossReferenceItems.js +139 -52
- package/dist/cjs/components/views/DeleteSupplementDialog.js +1 -1
- package/dist/cjs/lib/__tests__/plugins.test.js +1 -0
- package/dist/cjs/lib/supplements.js +9 -1
- package/dist/cjs/menus.js +35 -0
- package/dist/cjs/plugins/objects.js +1 -1
- package/dist/cjs/testing/default-editor-data.js +1 -0
- package/dist/cjs/toolbar.js +6 -0
- package/dist/cjs/versions.js +1 -1
- package/dist/cjs/views/cross_reference.js +12 -2
- package/dist/cjs/views/cross_reference_editable.js +95 -4
- package/dist/cjs/views/figure_editable.js +3 -0
- package/dist/es/commands.js +43 -9
- package/dist/es/components/form/FormFooter.js +2 -2
- package/dist/es/components/keyboard-shortcuts-modal/KeyboardShortcutsModal.js +4 -2
- package/dist/es/components/views/CrossReferenceItems.js +141 -54
- package/dist/es/components/views/DeleteSupplementDialog.js +2 -2
- package/dist/es/lib/__tests__/plugins.test.js +1 -0
- package/dist/es/lib/supplements.js +8 -1
- package/dist/es/menus.js +36 -1
- package/dist/es/plugins/objects.js +1 -1
- package/dist/es/testing/default-editor-data.js +1 -0
- package/dist/es/toolbar.js +8 -2
- package/dist/es/versions.js +1 -1
- package/dist/es/views/cross_reference.js +12 -2
- package/dist/es/views/cross_reference_editable.js +96 -5
- package/dist/es/views/figure_editable.js +3 -0
- package/dist/types/commands.d.ts +1 -0
- package/dist/types/components/form/FormFooter.d.ts +2 -1
- package/dist/types/components/views/CrossReferenceItems.d.ts +3 -0
- package/dist/types/lib/supplements.d.ts +1 -0
- package/dist/types/versions.d.ts +1 -1
- package/dist/types/views/cross_reference_editable.d.ts +6 -0
- package/package.json +3 -3
- package/src/commands.ts +52 -10
- package/src/components/ChangeHandlingForm.tsx +4 -2
- package/src/components/form/FormFooter.tsx +3 -1
- package/src/components/keyboard-shortcuts-modal/KeyboardShortcutsModal.tsx +6 -7
- package/src/components/toolbar/InsertEmbedDialog.tsx +6 -2
- package/src/components/toolbar/type-selector/styles.ts +2 -1
- package/src/components/views/CrossReferenceItems.tsx +227 -100
- package/src/components/views/DeleteSupplementDialog.tsx +3 -6
- package/src/lib/__tests__/plugins.test.ts +1 -0
- package/src/lib/footnotes.ts +3 -1
- package/src/lib/supplements.ts +13 -1
- package/src/lib/utils.ts +15 -4
- package/src/menus.tsx +39 -0
- package/src/plugins/add-subtitle.ts +18 -16
- package/src/plugins/objects.ts +2 -6
- package/src/testing/default-editor-data.ts +1 -0
- package/src/toolbar.tsx +9 -0
- package/src/versions.ts +1 -1
- package/src/views/cross_reference.ts +17 -3
- package/src/views/cross_reference_editable.ts +123 -11
- package/src/views/figure_editable.ts +4 -0
- package/src/views/supplement.ts +0 -1
- package/src/views/supplements.ts +2 -2
|
@@ -19,10 +19,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.CrossReferenceEditableView = void 0;
|
|
22
|
+
const style_guide_1 = require("@manuscripts/style-guide");
|
|
22
23
|
const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
|
|
23
24
|
const transform_1 = require("@manuscripts/transform");
|
|
24
25
|
const prosemirror_state_1 = require("prosemirror-state");
|
|
25
26
|
const CrossReferenceItems_1 = require("../components/views/CrossReferenceItems");
|
|
27
|
+
const comments_1 = require("../lib/comments");
|
|
28
|
+
const doc_1 = require("../lib/doc");
|
|
29
|
+
const supplements_1 = require("../lib/supplements");
|
|
26
30
|
const objects_1 = require("../plugins/objects");
|
|
27
31
|
const creators_1 = require("./creators");
|
|
28
32
|
const cross_reference_1 = require("./cross_reference");
|
|
@@ -37,14 +41,20 @@ class CrossReferenceEditableView extends cross_reference_1.CrossReferenceView {
|
|
|
37
41
|
if (!can?.editArticle || rids.length) {
|
|
38
42
|
return;
|
|
39
43
|
}
|
|
44
|
+
this.showPicker();
|
|
45
|
+
};
|
|
46
|
+
this.showPicker = () => {
|
|
47
|
+
const rids = this.node.attrs.rids;
|
|
40
48
|
const componentProps = {
|
|
41
49
|
handleSelect: this.handleSelect,
|
|
42
50
|
targets: this.getTargets(),
|
|
51
|
+
files: this.props.getFiles(),
|
|
43
52
|
handleCancel: this.handleCancel,
|
|
44
53
|
currentTargetId: rids[0],
|
|
45
54
|
currentCustomLabel: this.node.attrs.label,
|
|
55
|
+
isEdit: rids.length > 0,
|
|
46
56
|
};
|
|
47
|
-
this.popperContainer = (0, ReactSubView_1.default)(this.props, CrossReferenceItems_1.CrossReferenceItems, componentProps, this.node, this.getPos, this.view
|
|
57
|
+
this.popperContainer = (0, ReactSubView_1.default)(this.props, CrossReferenceItems_1.CrossReferenceItems, componentProps, this.node, this.getPos, this.view);
|
|
48
58
|
this.popperContainer.setAttribute('tabindex', '0');
|
|
49
59
|
this.props.popper.show(this.dom, this.popperContainer, 'auto');
|
|
50
60
|
};
|
|
@@ -56,15 +66,39 @@ class CrossReferenceEditableView extends cross_reference_1.CrossReferenceView {
|
|
|
56
66
|
this.handleCancel();
|
|
57
67
|
};
|
|
58
68
|
this.getTargets = () => {
|
|
59
|
-
const excludedTypes = [transform_1.schema.nodes.image_element.name];
|
|
60
69
|
const targets = objects_1.objectsKey.getState(this.view.state);
|
|
61
|
-
|
|
70
|
+
const files = this.props.getFiles();
|
|
71
|
+
const fileMap = new Map(files.map((f) => [f.id, f.name]));
|
|
72
|
+
const excludedTypes = [transform_1.schema.nodes.image_element.name];
|
|
73
|
+
const supplement = transform_1.schema.nodes.supplement.name;
|
|
74
|
+
return Array.from(targets.values()).reduce((acc, t) => {
|
|
75
|
+
if (excludedTypes.includes(t.type)) {
|
|
76
|
+
return acc;
|
|
77
|
+
}
|
|
78
|
+
if (t.type === supplement && t.href) {
|
|
79
|
+
const found = (0, doc_1.findNodeByID)(this.view.state.doc, t.id);
|
|
80
|
+
const label = found
|
|
81
|
+
? (0, supplements_1.getSupplementDisplayLabel)(found.node, files)
|
|
82
|
+
: (fileMap.get(t.href) ?? t.href);
|
|
83
|
+
acc.push({ ...t, label, caption: '' });
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
acc.push(t);
|
|
87
|
+
}
|
|
88
|
+
return acc;
|
|
89
|
+
}, []);
|
|
62
90
|
};
|
|
63
91
|
this.handleCancel = () => {
|
|
64
92
|
if (!this.node.attrs.rids.length) {
|
|
65
93
|
const { state } = this.view;
|
|
66
94
|
const pos = this.getPos();
|
|
67
|
-
|
|
95
|
+
if (pos === undefined) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const label = this.node.attrs.label;
|
|
99
|
+
const tr = label
|
|
100
|
+
? state.tr.replaceWith(pos, pos + this.node.nodeSize, state.schema.text(label))
|
|
101
|
+
: state.tr.delete(pos, pos + this.node.nodeSize);
|
|
68
102
|
tr.setSelection(prosemirror_state_1.TextSelection.create(tr.doc, pos));
|
|
69
103
|
(0, track_changes_plugin_1.skipTracking)(tr);
|
|
70
104
|
this.view.dispatch(tr);
|
|
@@ -85,6 +119,63 @@ class CrossReferenceEditableView extends cross_reference_1.CrossReferenceView {
|
|
|
85
119
|
this.view.dispatch(tr.setSelection(selection));
|
|
86
120
|
this.destroy();
|
|
87
121
|
};
|
|
122
|
+
this.handleClick = () => {
|
|
123
|
+
if ((0, track_changes_plugin_1.isDeleted)(this.node)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (!this.node.attrs.rids.length) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this.showContextMenu();
|
|
130
|
+
};
|
|
131
|
+
this.showContextMenu = () => {
|
|
132
|
+
this.props.popper.destroy();
|
|
133
|
+
const can = this.props.getCapabilities();
|
|
134
|
+
const targets = objects_1.objectsKey.getState(this.view.state);
|
|
135
|
+
const rid = this.node.attrs.rids[0];
|
|
136
|
+
const isOrphaned = !targets?.get(rid);
|
|
137
|
+
const actions = [
|
|
138
|
+
{
|
|
139
|
+
label: 'Comment',
|
|
140
|
+
icon: 'AddComment',
|
|
141
|
+
action: () => {
|
|
142
|
+
this.props.popper.destroy();
|
|
143
|
+
(0, comments_1.handleComment)(this.node, this.view);
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
label: 'Go to content',
|
|
148
|
+
icon: 'Scroll',
|
|
149
|
+
action: () => this.navigateToTarget(),
|
|
150
|
+
disabled: isOrphaned,
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
if (can?.editArticle) {
|
|
154
|
+
actions.unshift({
|
|
155
|
+
label: 'Edit',
|
|
156
|
+
icon: 'Edit',
|
|
157
|
+
action: () => this.handleEdit(),
|
|
158
|
+
disabled: isOrphaned,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
this.contextMenu = (0, ReactSubView_1.default)(this.props, style_guide_1.ContextMenu, { actions }, this.node, this.getPos, this.view, ['context-menu']);
|
|
162
|
+
this.props.popper.show(this.dom, this.contextMenu, 'right-start', false);
|
|
163
|
+
};
|
|
164
|
+
this.navigateToTarget = () => {
|
|
165
|
+
this.props.popper.destroy();
|
|
166
|
+
const rids = this.node.attrs.rids;
|
|
167
|
+
if (!rids.length) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
this.props.navigate({
|
|
171
|
+
pathname: this.props.location.pathname,
|
|
172
|
+
hash: '#' + rids[0],
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
this.handleEdit = () => {
|
|
176
|
+
this.props.popper.destroy();
|
|
177
|
+
this.showPicker();
|
|
178
|
+
};
|
|
88
179
|
}
|
|
89
180
|
}
|
|
90
181
|
exports.CrossReferenceEditableView = CrossReferenceEditableView;
|
|
@@ -198,6 +198,9 @@ class FigureEditableView extends figure_1.FigureView {
|
|
|
198
198
|
this.addTools();
|
|
199
199
|
}
|
|
200
200
|
addTools() {
|
|
201
|
+
if (this.getPos() === undefined) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
201
204
|
this.manageReactTools();
|
|
202
205
|
const existingDragHandlers = this.container.querySelectorAll('.drag-handler');
|
|
203
206
|
existingDragHandlers.forEach((handler) => handler.remove());
|
package/dist/es/commands.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { isDeleted, skipTracking, skipSelect } from '@manuscripts/track-changes-plugin';
|
|
16
|
+
import { isDeleted, skipTracking, skipSelect, } from '@manuscripts/track-changes-plugin';
|
|
17
17
|
import { generateNodeID, isElementNodeType, isListNode, isSectionNodeType, isTableElementNode, schema, } from '@manuscripts/transform';
|
|
18
18
|
import { Fragment, NodeRange, Slice, } from 'prosemirror-model';
|
|
19
19
|
import { wrapInList } from 'prosemirror-schema-list';
|
|
@@ -298,7 +298,7 @@ export const insertSupplement = (file, view, setSelection = true) => {
|
|
|
298
298
|
export const insertSupplementWeblink = (url, title, view) => {
|
|
299
299
|
const supplement = schema.nodes.supplement.createAndFill({
|
|
300
300
|
id: generateNodeID(schema.nodes.supplement),
|
|
301
|
-
href: url
|
|
301
|
+
href: url,
|
|
302
302
|
}, createAndFillCaption());
|
|
303
303
|
const tr = view.state.tr;
|
|
304
304
|
const { pos } = upsertSupplementsSection(tr, supplement);
|
|
@@ -466,15 +466,52 @@ export const insertInlineCitation = (state, dispatch) => {
|
|
|
466
466
|
}
|
|
467
467
|
return true;
|
|
468
468
|
};
|
|
469
|
+
export const canInsertCrossReference = (state) => {
|
|
470
|
+
if (!canInsert(schema.nodes.cross_reference)(state)) {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
const { from, to, empty, $from, $to } = state.selection;
|
|
474
|
+
if (empty) {
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
if (!$from.sameParent($to)) {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
let overlaps = false;
|
|
481
|
+
state.doc.nodesBetween(from, to, (node) => {
|
|
482
|
+
if (node.isAtom && !node.isText) {
|
|
483
|
+
overlaps = true;
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
return !overlaps;
|
|
487
|
+
});
|
|
488
|
+
return !overlaps;
|
|
489
|
+
};
|
|
469
490
|
export const insertCrossReference = (state, dispatch) => {
|
|
491
|
+
if (!canInsertCrossReference(state)) {
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
const text = selectedText();
|
|
470
495
|
const node = state.schema.nodes.cross_reference.create({
|
|
496
|
+
id: generateNodeID(schema.nodes.cross_reference),
|
|
471
497
|
rids: [],
|
|
498
|
+
label: text,
|
|
472
499
|
});
|
|
473
|
-
const
|
|
474
|
-
|
|
500
|
+
const { tr } = state;
|
|
501
|
+
let pos;
|
|
502
|
+
const isWrap = !state.selection.empty;
|
|
503
|
+
if (isWrap) {
|
|
504
|
+
pos = state.selection.from;
|
|
505
|
+
tr.replaceSelectionWith(node);
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
pos = state.selection.to;
|
|
509
|
+
tr.insert(pos, node);
|
|
510
|
+
}
|
|
475
511
|
if (dispatch) {
|
|
476
512
|
const selection = NodeSelection.create(tr.doc, pos);
|
|
477
|
-
|
|
513
|
+
tr.setSelection(selection).scrollIntoView();
|
|
514
|
+
dispatch(isWrap ? skipTracking(tr) : tr);
|
|
478
515
|
}
|
|
479
516
|
return true;
|
|
480
517
|
};
|
|
@@ -963,10 +1000,7 @@ export const insertTransGraphicalAbstract = (category, insertAfterPos) => (state
|
|
|
963
1000
|
const node = schema.nodes.trans_graphical_abstract.createAndFill({
|
|
964
1001
|
lang,
|
|
965
1002
|
category: category.id,
|
|
966
|
-
}, [
|
|
967
|
-
schema.nodes.section_title.create(),
|
|
968
|
-
createAndFillFigureElement(state),
|
|
969
|
-
]);
|
|
1003
|
+
}, [schema.nodes.section_title.create(), createAndFillFigureElement(state)]);
|
|
970
1004
|
const tr = state.tr.insert(pos, node);
|
|
971
1005
|
if (node.lastChild) {
|
|
972
1006
|
expandAccessibilitySection(tr, node.lastChild);
|
|
@@ -16,9 +16,9 @@ const Footer = styled.div `
|
|
|
16
16
|
const StyledIconTextButton = styled(IconTextButton) `
|
|
17
17
|
color: ${(props) => props.theme.colors.brand.default};
|
|
18
18
|
`;
|
|
19
|
-
const FormFooter = ({ onCancel, primaryAction, }) => {
|
|
19
|
+
const FormFooter = ({ onCancel, primaryAction, cancelLabel = 'Close', }) => {
|
|
20
20
|
return (React.createElement(Footer, null,
|
|
21
|
-
React.createElement(StyledIconTextButton, { color: "secondary", onClick: onCancel },
|
|
21
|
+
React.createElement(StyledIconTextButton, { color: "secondary", onClick: onCancel }, cancelLabel),
|
|
22
22
|
primaryAction));
|
|
23
23
|
};
|
|
24
24
|
export default FormFooter;
|
|
@@ -41,7 +41,8 @@ const ShortcutTabs = styled(InspectorTabs) `
|
|
|
41
41
|
min-height: 0;
|
|
42
42
|
`;
|
|
43
43
|
const ModalTabsWrapper = styled('div') `
|
|
44
|
-
margin: 0 ${(props) => props.theme.grid.unit * 8}px
|
|
44
|
+
margin: 0 ${(props) => props.theme.grid.unit * 8}px
|
|
45
|
+
${(props) => props.theme.grid.unit * 3}px;
|
|
45
46
|
`;
|
|
46
47
|
const ShortcutTabPanel = styled(InspectorTabPanel).attrs({
|
|
47
48
|
tabIndex: -1,
|
|
@@ -92,7 +93,8 @@ const Section = styled.section `
|
|
|
92
93
|
}
|
|
93
94
|
`;
|
|
94
95
|
const SectionTitle = styled.h3 `
|
|
95
|
-
margin: 0 ${(props) => props.theme.grid.unit * 8}px
|
|
96
|
+
margin: 0 ${(props) => props.theme.grid.unit * 8}px
|
|
97
|
+
${(props) => props.theme.grid.unit * 2}px;
|
|
96
98
|
font-size: ${(props) => props.theme.font.size.large};
|
|
97
99
|
font-weight: ${(props) => props.theme.font.weight.normal};
|
|
98
100
|
line-height: ${(props) => props.theme.font.lineHeight.large};
|
|
@@ -13,74 +13,122 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
17
|
-
import
|
|
16
|
+
import { CloseButton, ExpandableSection, PrimaryButton, withListNavigation, withNavigableListItem, FileUnknownIcon, getFileIcon, ArrowDownIcon, StyledModal, ModalContainer, ModalHeader, ModalBody, WebLinkIcon, } from '@manuscripts/style-guide';
|
|
17
|
+
import { schema } from '@manuscripts/transform';
|
|
18
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
18
19
|
import styled from 'styled-components';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
display: flex;
|
|
23
|
-
flex-direction: column;
|
|
24
|
-
max-height: 60vh;
|
|
25
|
-
overflow: hidden;
|
|
26
|
-
`);
|
|
27
|
-
const Actions = styled.div `
|
|
28
|
-
display: flex;
|
|
29
|
-
align-items: center;
|
|
30
|
-
justify-content: flex-end;
|
|
31
|
-
flex-shrink: 0;
|
|
32
|
-
`;
|
|
20
|
+
import { allowedHref } from '../../lib/url';
|
|
21
|
+
import { nodeTypeIcon } from '../../node-type-icons';
|
|
22
|
+
import FormFooter from '../form/FormFooter';
|
|
33
23
|
const Items = withListNavigation(styled.div `
|
|
34
24
|
flex: 1;
|
|
35
25
|
overflow-y: auto;
|
|
36
|
-
padding: ${(props) => props.theme.grid.unit * 3}px;
|
|
37
26
|
`);
|
|
38
27
|
const CrossReferenceItem = withNavigableListItem(styled.div `
|
|
39
28
|
cursor: pointer;
|
|
40
|
-
padding: ${(props) => props.theme.grid.unit *
|
|
41
|
-
background-color: ${(props) => props
|
|
42
|
-
? props.theme.colors.background.selected
|
|
43
|
-
: props.theme.colors.background.primary};
|
|
29
|
+
padding: ${(props) => props.theme.grid.unit * 2}px;
|
|
30
|
+
background-color: ${(props) => props.theme.colors.background.primary};
|
|
44
31
|
transition: background-color 0.1s;
|
|
45
|
-
border: solid
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
z-index: ${(props) => (props.$isSelected ? '1' : '0')};
|
|
32
|
+
border: 1px solid ${(props) => props.theme.colors.border.secondary};
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
max-width: 100%;
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
margin-bottom: ${(props) => props.theme.grid.unit * 2}px;
|
|
51
37
|
|
|
52
38
|
&:hover {
|
|
53
|
-
background-color:
|
|
39
|
+
background-color: #f2f2f2;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
&.active {
|
|
43
|
+
background-color: #f2f2f2;
|
|
44
|
+
z-index: 1;
|
|
54
45
|
}
|
|
55
46
|
`);
|
|
56
47
|
const Label = styled.span `
|
|
57
48
|
color: ${(props) => props.theme.colors.text.primary};
|
|
49
|
+
-webkit-line-clamp: 2;
|
|
50
|
+
-webkit-box-orient: vertical;
|
|
51
|
+
overflow: hidden;
|
|
52
|
+
word-break: break-all;
|
|
53
|
+
overflow-wrap: break-word;
|
|
54
|
+
min-width: fit-content;
|
|
58
55
|
`;
|
|
59
56
|
const Caption = styled.span `
|
|
60
57
|
color: ${(props) => props.theme.colors.text.secondary};
|
|
58
|
+
display: -webkit-box;
|
|
59
|
+
-webkit-line-clamp: 2;
|
|
60
|
+
-webkit-box-orient: vertical;
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
word-break: break-all;
|
|
63
|
+
overflow-wrap: break-word;
|
|
64
|
+
margin-left: 3.2px;
|
|
61
65
|
`;
|
|
62
66
|
const Heading = styled.div `
|
|
63
|
-
|
|
64
|
-
align-items: center;
|
|
65
|
-
justify-content: space-between;
|
|
66
|
-
flex-shrink: 0;
|
|
67
|
+
font-size: ${(props) => props.theme.font.size.medium};
|
|
67
68
|
font-weight: ${(props) => props.theme.font.weight.bold};
|
|
69
|
+
color: ${(props) => props.theme.colors.text.primary};
|
|
70
|
+
margin-bottom: ${(props) => props.theme.grid.unit * 3}px;
|
|
68
71
|
`;
|
|
69
72
|
const Empty = styled.div `
|
|
70
73
|
margin-bottom: ${(props) => props.theme.grid.unit * 4}px;
|
|
71
74
|
color: ${(props) => props.theme.colors.text.tertiary};
|
|
72
75
|
`;
|
|
76
|
+
const GroupExpandableSection = styled(ExpandableSection) `
|
|
77
|
+
& > div:first-child {
|
|
78
|
+
flex-direction: row-reverse;
|
|
79
|
+
justify-content: flex-end;
|
|
80
|
+
padding-left: 0;
|
|
81
|
+
gap: 10px;
|
|
82
|
+
font-weight: ${(props) => props.theme.font.weight.normal};
|
|
83
|
+
svg {
|
|
84
|
+
width: 20px;
|
|
85
|
+
height: 20px;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
`;
|
|
73
89
|
const DefaultLabelWrapper = styled.div `
|
|
74
|
-
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
min-width: 0;
|
|
75
93
|
`;
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
94
|
+
const ItemIcon = styled.span `
|
|
95
|
+
flex-shrink: 0;
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
79
98
|
color: ${(props) => props.theme.colors.text.secondary};
|
|
80
|
-
|
|
99
|
+
margin-right: ${(props) => props.theme.grid.unit * 2}px;
|
|
100
|
+
svg {
|
|
101
|
+
width: 20px;
|
|
102
|
+
height: 20px;
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
const FieldLabel = styled.label `
|
|
106
|
+
font-size: ${(props) => props.theme.font.size.normal};
|
|
107
|
+
color: ${(props) => props.theme.colors.text.secondary};
|
|
108
|
+
margin-top: ${(props) => props.theme.grid.unit * 4}px;
|
|
109
|
+
margin-bottom: ${(props) => props.theme.grid.unit}px;
|
|
110
|
+
`;
|
|
111
|
+
const CustomTextInput = styled.input `
|
|
112
|
+
color: ${(props) => props.theme.colors.text.secondary};
|
|
113
|
+
background-color: ${(props) => props.theme.colors.background.primary};
|
|
81
114
|
border: 1px solid ${(props) => props.theme.colors.border.secondary};
|
|
82
115
|
border-radius: ${(props) => props.theme.grid.radius.small};
|
|
83
116
|
padding: ${(props) => props.theme.grid.unit * 2}px;
|
|
117
|
+
&:disabled {
|
|
118
|
+
cursor: not-allowed;
|
|
119
|
+
background-color: ${(props) => props.theme.colors.background.disabled};
|
|
120
|
+
color: ${(props) => props.theme.colors.text.muted};
|
|
121
|
+
}
|
|
122
|
+
`;
|
|
123
|
+
const StyledModalBody = styled(ModalBody) `
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
padding: ${(props) => props.theme.grid.unit * 5}px
|
|
127
|
+
${(props) => props.theme.grid.unit * 6}px;
|
|
128
|
+
max-height: 580px;
|
|
129
|
+
min-width: 600px;
|
|
130
|
+
max-width: 600px;
|
|
131
|
+
overflow: hidden;
|
|
84
132
|
`;
|
|
85
133
|
const trimmedCaption = (caption, limit) => {
|
|
86
134
|
if (caption.length <= limit) {
|
|
@@ -89,7 +137,27 @@ const trimmedCaption = (caption, limit) => {
|
|
|
89
137
|
const captionSearch = new RegExp(`^(.{${limit}}[^\\s]*).*`);
|
|
90
138
|
return caption.replace(captionSearch, '$1…');
|
|
91
139
|
};
|
|
92
|
-
|
|
140
|
+
const getTargetIcon = (target, files) => {
|
|
141
|
+
if (target.type === schema.nodes.supplement.name) {
|
|
142
|
+
if (target.href && allowedHref(target.href)) {
|
|
143
|
+
return React.createElement(WebLinkIcon, { className: "file-icon" });
|
|
144
|
+
}
|
|
145
|
+
const fileName = files.find((f) => f.id === target.href)?.name ?? target.label ?? '';
|
|
146
|
+
return getFileIcon(fileName) ?? React.createElement(FileUnknownIcon, null);
|
|
147
|
+
}
|
|
148
|
+
return nodeTypeIcon(schema.nodes[target.type]);
|
|
149
|
+
};
|
|
150
|
+
const GROUP_LABELS = {
|
|
151
|
+
figure_element: 'Figures',
|
|
152
|
+
table_element: 'Tables',
|
|
153
|
+
equation_element: 'Equations',
|
|
154
|
+
listing_element: 'Listings',
|
|
155
|
+
box_element: 'Boxes',
|
|
156
|
+
embed: 'Media',
|
|
157
|
+
supplement: 'Supplementary files',
|
|
158
|
+
};
|
|
159
|
+
export const CrossReferenceItems = ({ targets, files, handleSelect, handleCancel, currentTargetId, currentCustomLabel, isEdit = false, }) => {
|
|
160
|
+
const [isOpen, setIsOpen] = useState(true);
|
|
93
161
|
const [selectedItem, setSelectedItem] = useState('');
|
|
94
162
|
const customTextRef = useRef(null);
|
|
95
163
|
useEffect(() => {
|
|
@@ -97,20 +165,39 @@ export const CrossReferenceItems = ({ targets, handleSelect, handleCancel, curre
|
|
|
97
165
|
setSelectedItem(currentTargetId);
|
|
98
166
|
}
|
|
99
167
|
}, [currentTargetId]);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
React.createElement(
|
|
112
|
-
React.createElement(
|
|
113
|
-
React.createElement(
|
|
114
|
-
|
|
115
|
-
|
|
168
|
+
const close = useCallback(() => {
|
|
169
|
+
setIsOpen(false);
|
|
170
|
+
handleCancel();
|
|
171
|
+
}, [handleCancel]);
|
|
172
|
+
const grouped = targets.reduce((acc, t) => {
|
|
173
|
+
var _a;
|
|
174
|
+
;
|
|
175
|
+
(acc[_a = t.type] ?? (acc[_a] = [])).push(t);
|
|
176
|
+
return acc;
|
|
177
|
+
}, {});
|
|
178
|
+
return (React.createElement(StyledModal, { isOpen: isOpen, onRequestClose: close, shouldCloseOnOverlayClick: true },
|
|
179
|
+
React.createElement(ModalContainer, { "data-cy": "cross-reference-editor" },
|
|
180
|
+
React.createElement(ModalHeader, null,
|
|
181
|
+
React.createElement(CloseButton, { onClick: close })),
|
|
182
|
+
React.createElement(StyledModalBody, null,
|
|
183
|
+
React.createElement(Heading, null, isEdit ? 'Edit Cross-reference' : 'Insert Cross-reference'),
|
|
184
|
+
React.createElement(FieldLabel, { htmlFor: "cross-reference-custom-text" }, "Custom display text"),
|
|
185
|
+
React.createElement(CustomTextInput, { ref: customTextRef, id: "cross-reference-custom-text", "data-cy": "cross-reference-custom-text", type: "text", placeholder: 'Custom display text...', disabled: !selectedItem, defaultValue: currentCustomLabel ?? '' }),
|
|
186
|
+
React.createElement(Items, null, Object.keys(grouped).length ? (Object.entries(grouped).map(([type, group]) => (React.createElement(GroupExpandableSection, { key: type, title: GROUP_LABELS[type], icon: ArrowDownIcon }, group.map((target) => {
|
|
187
|
+
const icon = getTargetIcon(target, files);
|
|
188
|
+
return (React.createElement(CrossReferenceItem, { key: target.id, "data-cy": "cross-reference-item", className: selectedItem === target.id ? 'active' : undefined, onClick: () => setSelectedItem(target.id) },
|
|
189
|
+
React.createElement(DefaultLabelWrapper, null,
|
|
190
|
+
icon ? React.createElement(ItemIcon, null, icon) : null,
|
|
191
|
+
React.createElement(Label, null,
|
|
192
|
+
target.label,
|
|
193
|
+
target.caption && `:`),
|
|
194
|
+
React.createElement(Caption, null, trimmedCaption(target.caption, 200)))));
|
|
195
|
+
}))))) : (React.createElement(Empty, null, "No cross-reference targets available.")))),
|
|
196
|
+
React.createElement(FormFooter, { onCancel: close, cancelLabel: "Cancel", primaryAction: React.createElement(PrimaryButton, { onClick: () => {
|
|
197
|
+
if (selectedItem) {
|
|
198
|
+
setIsOpen(false);
|
|
199
|
+
const customLabel = customTextRef.current?.value;
|
|
200
|
+
handleSelect(selectedItem, customLabel || '');
|
|
201
|
+
}
|
|
202
|
+
}, disabled: !selectedItem }, isEdit ? 'Update' : 'Insert') }))));
|
|
116
203
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Category, Dialog
|
|
1
|
+
import { Category, Dialog } from '@manuscripts/style-guide';
|
|
2
2
|
import React, { useState } from 'react';
|
|
3
3
|
import { getSupplementDisplayLabel, performDeleteSupplement, } from '../../lib/supplements';
|
|
4
4
|
import { getEditorProps } from '../../plugins/editor-props';
|
|
5
5
|
import ReactSubView from '../../views/ReactSubView';
|
|
6
6
|
const DeleteSupplementDialog = ({ label, onDelete }) => {
|
|
7
7
|
const [isOpen, setOpen] = useState(true);
|
|
8
|
-
return (React.createElement(Dialog, { isOpen: isOpen, category: Category.confirmation, header:
|
|
8
|
+
return (React.createElement(Dialog, { isOpen: isOpen, category: Category.confirmation, header: "Delete supplement", message: "Are you sure you want to delete \u201C{label}\u201D?", actions: {
|
|
9
9
|
primary: {
|
|
10
10
|
action: () => {
|
|
11
11
|
onDelete();
|
|
@@ -14,9 +14,16 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { schema, } from '@manuscripts/transform';
|
|
17
|
-
import { findChildrenByType, findParentNodeClosestToPos } from 'prosemirror-utils';
|
|
17
|
+
import { findChildrenByType, findParentNodeClosestToPos, } from 'prosemirror-utils';
|
|
18
18
|
import { allowedHref } from './url';
|
|
19
|
+
export const getSupplementCaptionTitle = (node) => {
|
|
20
|
+
return node.firstChild?.textContent.trim() ?? '';
|
|
21
|
+
};
|
|
19
22
|
export const getSupplementDisplayLabel = (node, files) => {
|
|
23
|
+
const captionTitle = getSupplementCaptionTitle(node);
|
|
24
|
+
if (captionTitle) {
|
|
25
|
+
return captionTitle;
|
|
26
|
+
}
|
|
20
27
|
const href = node.attrs.href;
|
|
21
28
|
if (allowedHref(href)) {
|
|
22
29
|
return href;
|
package/dist/es/menus.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { getGroupCategories, schema, } from '@manuscripts/transform';
|
|
17
17
|
import { toggleMark } from 'prosemirror-commands';
|
|
18
18
|
import { redo, undo } from 'prosemirror-history';
|
|
19
|
-
import { activateSearchReplace, addInlineComment, blockActive, canInsert, copySelection, insertAbstractSection, insertAffiliation, insertAward, insertBackmatterSection, insertBlock, insertBoxElement, insertContributors, insertCrossReference, insertEmbed, insertGraphicalAbstract, insertHeadshotGrid, insertHeroImage, insertInlineCitation, insertInlineEquation, insertInlineFootnote, insertKeywords, insertLink, insertList, insertSection, markActive, paste, } from './commands';
|
|
19
|
+
import { activateSearchReplace, addInlineComment, blockActive, canInsert, copySelection, insertAbstractSection, insertAffiliation, insertAward, insertBackmatterSection, insertBlock, insertBoxElement, insertContributors, insertCrossReference, canInsertCrossReference, insertEmbed, insertGraphicalAbstract, insertHeadshotGrid, insertHeroImage, insertInlineCitation, insertInlineEquation, insertInlineFootnote, insertKeywords, insertLink, insertList, insertSection, markActive, paste, } from './commands';
|
|
20
20
|
import { openInsertTableDialog } from './components/toolbar/InsertTableDialog';
|
|
21
21
|
import { ListMenuItem } from './components/toolbar/ListMenuItem';
|
|
22
22
|
import { openInsertSpecialCharacterDialog } from './components/views/InsertSpecialCharacter';
|
|
@@ -330,6 +330,41 @@ export const getEditorMenus = (editor) => {
|
|
|
330
330
|
{
|
|
331
331
|
role: 'separator',
|
|
332
332
|
},
|
|
333
|
+
{
|
|
334
|
+
id: 'insert-citation',
|
|
335
|
+
label: 'Citation',
|
|
336
|
+
shortcut: {
|
|
337
|
+
mac: 'Option+CommandOrControl+C',
|
|
338
|
+
pc: 'CommandOrControl+Option+C',
|
|
339
|
+
},
|
|
340
|
+
isEnabled: isEditAllowed(state) &&
|
|
341
|
+
isCommandValid(canInsert(schema.nodes.citation)),
|
|
342
|
+
run: doCommand(insertInlineCitation),
|
|
343
|
+
isHidden: !templateAllows(state, schema.nodes.citation),
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
id: 'insert-cross-reference',
|
|
347
|
+
label: 'Cross-reference',
|
|
348
|
+
shortcut: {
|
|
349
|
+
mac: 'Option+CommandOrControl+R',
|
|
350
|
+
pc: 'CommandOrControl+Option+R',
|
|
351
|
+
},
|
|
352
|
+
isEnabled: isEditAllowed(state) && isCommandValid(canInsertCrossReference),
|
|
353
|
+
run: doCommand(insertCrossReference),
|
|
354
|
+
isHidden: !templateAllows(state, schema.nodes.cross_reference),
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
id: 'insert-footnote',
|
|
358
|
+
label: 'Footnote',
|
|
359
|
+
shortcut: {
|
|
360
|
+
mac: 'Option+CommandOrControl+F',
|
|
361
|
+
pc: 'CommandOrControl+Option+F',
|
|
362
|
+
},
|
|
363
|
+
isEnabled: isEditAllowed(state) &&
|
|
364
|
+
isCommandValid(canInsert(schema.nodes.inline_footnote)),
|
|
365
|
+
run: doCommand(insertInlineFootnote),
|
|
366
|
+
isHidden: !templateAllows(state, schema.nodes.inline_footnote),
|
|
367
|
+
},
|
|
333
368
|
{
|
|
334
369
|
id: 'insert-special-character',
|
|
335
370
|
label: 'Special Characters',
|
|
@@ -39,7 +39,7 @@ export default () => {
|
|
|
39
39
|
const { id } = node.attrs;
|
|
40
40
|
if (id) {
|
|
41
41
|
const target = targets.get(id);
|
|
42
|
-
if (target) {
|
|
42
|
+
if (target && target.label) {
|
|
43
43
|
const caption = findChildren(node, (node) => node.type === schema.nodes.caption ||
|
|
44
44
|
node.type === schema.nodes.caption_title, false)[0];
|
|
45
45
|
if (caption) {
|