@manuscripts/body-editor 3.17.2 → 3.17.5

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.
Files changed (38) hide show
  1. package/dist/cjs/commands.js +37 -21
  2. package/dist/cjs/components/references/ReferenceSearch.js +1 -1
  3. package/dist/cjs/configs/editor-views.js +1 -0
  4. package/dist/cjs/lib/__tests__/plugins.test.js +1 -1
  5. package/dist/cjs/lib/context-menu.js +4 -1
  6. package/dist/cjs/menus.js +1 -1
  7. package/dist/cjs/node-type-icons.js +1 -0
  8. package/dist/cjs/plugins/section_category.js +5 -2
  9. package/dist/cjs/plugins/translations.js +8 -20
  10. package/dist/cjs/versions.js +1 -1
  11. package/dist/cjs/views/link_editable.js +55 -4
  12. package/dist/es/commands.js +34 -19
  13. package/dist/es/components/references/ReferenceSearch.js +1 -1
  14. package/dist/es/configs/editor-views.js +1 -0
  15. package/dist/es/lib/__tests__/plugins.test.js +1 -1
  16. package/dist/es/lib/context-menu.js +4 -1
  17. package/dist/es/menus.js +1 -1
  18. package/dist/es/node-type-icons.js +1 -0
  19. package/dist/es/plugins/section_category.js +6 -3
  20. package/dist/es/plugins/translations.js +9 -21
  21. package/dist/es/versions.js +1 -1
  22. package/dist/es/views/link_editable.js +55 -4
  23. package/dist/types/commands.d.ts +3 -2
  24. package/dist/types/versions.d.ts +1 -1
  25. package/dist/types/views/link_editable.d.ts +5 -1
  26. package/package.json +3 -3
  27. package/src/commands.ts +56 -35
  28. package/src/components/references/ReferenceSearch.tsx +1 -8
  29. package/src/configs/editor-views.ts +1 -0
  30. package/src/lib/__tests__/plugins.test.ts +1 -1
  31. package/src/lib/context-menu.ts +4 -1
  32. package/src/menus.tsx +1 -1
  33. package/src/node-type-icons.tsx +1 -0
  34. package/src/plugins/section_category.ts +7 -2
  35. package/src/plugins/translations.ts +16 -27
  36. package/src/versions.ts +1 -1
  37. package/src/views/link_editable.ts +73 -4
  38. package/styles/Editor.css +7 -0
@@ -1,2 +1,2 @@
1
- export const VERSION = '3.17.2';
1
+ export const VERSION = '3.17.5';
2
2
  export const MATHJAX_VERSION = '3.2.2';
@@ -13,14 +13,16 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ import { ContextMenu } from '@manuscripts/style-guide';
17
+ import { isDeleted } from '@manuscripts/track-changes-plugin';
16
18
  import { schema } from '@manuscripts/transform';
17
19
  import { TextSelection } from 'prosemirror-state';
20
+ import { convertLinkToSupplementWeblink } from '../commands';
18
21
  import { LinkForm, } from '../components/views/LinkForm';
19
22
  import { allowedHref } from '../lib/url';
20
23
  import { createEditableNodeView } from './creators';
21
24
  import { LinkView } from './link';
22
25
  import ReactSubView from './ReactSubView';
23
- import { isDeleted } from '@manuscripts/track-changes-plugin';
24
26
  export class LinkEditableView extends LinkView {
25
27
  constructor() {
26
28
  super(...arguments);
@@ -35,7 +37,7 @@ export class LinkEditableView extends LinkView {
35
37
  this.dom.addEventListener('click', this.handleClick);
36
38
  };
37
39
  this.selectNode = () => {
38
- if (!isDeleted(this.node)) {
40
+ if (!isDeleted(this.node) && !this.node.attrs.href) {
39
41
  this.showForm();
40
42
  }
41
43
  };
@@ -45,6 +47,50 @@ export class LinkEditableView extends LinkView {
45
47
  this.deselectNode = () => {
46
48
  this.closeForm();
47
49
  };
50
+ this.showContextMenu = () => {
51
+ if (!this.props.getCapabilities().editArticle) {
52
+ return;
53
+ }
54
+ this.closeForm();
55
+ const href = this.node.attrs.href;
56
+ const componentProps = {
57
+ actions: [
58
+ {
59
+ label: 'Edit',
60
+ action: this.handleEdit,
61
+ icon: 'Edit',
62
+ },
63
+ {
64
+ label: 'Convert to Weblink',
65
+ action: this.handleConvertToWebLink,
66
+ icon: 'ConvertToWebLink',
67
+ },
68
+ {
69
+ label: 'Open Link',
70
+ action: this.handleOpenLink,
71
+ icon: 'ExternalLink',
72
+ disabled: !allowedHref(href),
73
+ },
74
+ ],
75
+ };
76
+ this.popperContainer = ReactSubView(this.props, ContextMenu, componentProps, this.node, this.getPos, this.view, ['context-menu', 'link-context-menu']);
77
+ this.props.popper.show(this.dom, this.popperContainer, 'bottom');
78
+ };
79
+ this.handleEdit = () => {
80
+ this.closeForm();
81
+ this.showForm();
82
+ };
83
+ this.handleConvertToWebLink = () => {
84
+ this.closeForm();
85
+ convertLinkToSupplementWeblink(this.getPos(), this.node, this.view);
86
+ };
87
+ this.handleOpenLink = () => {
88
+ const href = this.node.attrs.href;
89
+ if (allowedHref(href)) {
90
+ window.open(href, '_blank', 'noopener,noreferrer');
91
+ }
92
+ this.closeForm();
93
+ };
48
94
  this.showForm = () => {
49
95
  if (!this.props.getCapabilities().editArticle) {
50
96
  return;
@@ -65,9 +111,14 @@ export class LinkEditableView extends LinkView {
65
111
  this.props.popper.show(this.dom, this.popperContainer, 'bottom');
66
112
  };
67
113
  this.handleClick = (e) => {
68
- if (this.props.getCapabilities().editArticle) {
69
- e.preventDefault();
114
+ if (!this.props.getCapabilities().editArticle) {
115
+ return;
116
+ }
117
+ e.preventDefault();
118
+ if (isDeleted(this.node) || !this.node.attrs.href) {
119
+ return;
70
120
  }
121
+ this.showContextMenu();
71
122
  };
72
123
  this.handleCancel = () => {
73
124
  const attrs = this.node.attrs;
@@ -35,6 +35,7 @@ export declare const insertEmbed: (state: ManuscriptEditorState, dispatch?: Disp
35
35
  export declare const insertTable: (config: TableConfig, state: ManuscriptEditorState, dispatch?: Dispatch) => boolean;
36
36
  export declare const insertSupplement: (file: FileAttachment, view: ManuscriptEditorView, setSelection?: boolean) => boolean;
37
37
  export declare const insertSupplementWeblink: (url: string, title: string, view: ManuscriptEditorView) => boolean;
38
+ export declare const convertLinkToSupplementWeblink: (pos: number, node: ManuscriptNode, view: ManuscriptEditorView) => boolean;
38
39
  export declare const updateSupplementWeblink: (pos: number, url: string, title: string, view: ManuscriptEditorView) => boolean;
39
40
  export declare const insertAttachment: (file: FileAttachment, state: ManuscriptEditorState, type: string, dispatch?: Dispatch) => boolean;
40
41
  export declare const insertBlock: (nodeType: ManuscriptNodeType) => (state: ManuscriptEditorState, dispatch?: Dispatch) => boolean;
@@ -66,8 +67,8 @@ export declare const insertKeywords: (state: ManuscriptEditorState, dispatch?: D
66
67
  export declare const insertList: (type: ManuscriptNodeType, style?: string) => (state: ManuscriptEditorState, dispatch?: Dispatch, view?: EditorView) => boolean;
67
68
  export declare const insertBibliographySection: () => boolean;
68
69
  export declare const insertTOCSection: () => boolean;
69
- export declare const insertTransAbstract: (state: ManuscriptEditorState, dispatch?: Dispatch, category?: string, insertAfterPos?: number) => boolean;
70
- export declare const insertTransGraphicalAbstract: (category: SectionCategory, insertAfterPos?: number) => (state: ManuscriptEditorState, dispatch?: Dispatch, view?: EditorView) => boolean;
70
+ export declare const insertTransAbstract: (state: ManuscriptEditorState, dispatch?: Dispatch, category?: string) => boolean;
71
+ export declare const insertTransGraphicalAbstract: (category: SectionCategory) => (state: ManuscriptEditorState, dispatch?: Dispatch, view?: EditorView) => boolean;
71
72
  export declare const isAtStartOfTextBlock: (state: ManuscriptEditorState, $cursor: ResolvedPos, view?: ManuscriptEditorView) => boolean;
72
73
  export declare const isTextSelection: (selection: Selection) => selection is ManuscriptTextSelection;
73
74
  export declare const ignoreAtomBlockNodeBackward: (state: ManuscriptEditorState, dispatch?: Dispatch, view?: ManuscriptEditorView) => boolean;
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "3.17.2";
1
+ export declare const VERSION = "3.17.5";
2
2
  export declare const MATHJAX_VERSION = "3.2.2";
@@ -23,6 +23,10 @@ export declare class LinkEditableView extends LinkView {
23
23
  selectNode: () => void;
24
24
  destroy: () => void;
25
25
  deselectNode: () => void;
26
+ private showContextMenu;
27
+ private handleEdit;
28
+ private handleConvertToWebLink;
29
+ private handleOpenLink;
26
30
  private showForm;
27
31
  private handleClick;
28
32
  private handleCancel;
@@ -31,5 +35,5 @@ export declare class LinkEditableView extends LinkView {
31
35
  private removeLink;
32
36
  private closeForm;
33
37
  }
34
- declare const _default: (props: import("../configs/ManuscriptsEditor").EditorProps, dispatch?: import("..").Dispatch) => import("../types").NodeViewCreator<LinkEditableView>;
38
+ declare const _default: (props: import("../configs/ManuscriptsEditor").EditorProps, dispatch?: import("../commands").Dispatch) => import("../types").NodeViewCreator<LinkEditableView>;
35
39
  export default _default;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@manuscripts/body-editor",
3
3
  "description": "Prosemirror components for editing and viewing manuscripts",
4
- "version": "3.17.2",
4
+ "version": "3.17.5",
5
5
  "repository": "github:Atypon-OpenSource/manuscripts-body-editor",
6
6
  "license": "Apache-2.0",
7
7
  "main": "dist/cjs",
@@ -41,9 +41,9 @@
41
41
  "@citation-js/plugin-pubmed": "0.3.0",
42
42
  "@citation-js/plugin-ris": "0.7.18",
43
43
  "@iarna/word-count": "1.1.2",
44
- "@manuscripts/style-guide": "3.7.1",
44
+ "@manuscripts/style-guide": "3.7.2",
45
45
  "@manuscripts/track-changes-plugin": "2.4.0",
46
- "@manuscripts/transform": "4.5.2",
46
+ "@manuscripts/transform": "4.5.4",
47
47
  "@popperjs/core": "2.11.8",
48
48
  "citeproc": "2.4.63",
49
49
  "codemirror": "5.65.19",
package/src/commands.ts CHANGED
@@ -543,6 +543,40 @@ export const insertSupplementWeblink = (
543
543
  return true
544
544
  }
545
545
 
546
+ export const convertLinkToSupplementWeblink = (
547
+ pos: number,
548
+ node: ManuscriptNode,
549
+ view: ManuscriptEditorView
550
+ ) => {
551
+ const href = node.attrs.href
552
+ if (!href) {
553
+ return false
554
+ }
555
+
556
+ const text = node.textContent.trim()
557
+ const id = generateNodeID(schema.nodes.supplement)
558
+
559
+ const supplement = schema.nodes.supplement.createAndFill(
560
+ { id, href },
561
+ createAndFillCaption()
562
+ ) as SupplementNode
563
+
564
+ const crossReference = schema.nodes.cross_reference.create({
565
+ id: generateNodeID(schema.nodes.cross_reference),
566
+ rids: [id],
567
+ label: text,
568
+ })
569
+
570
+ const tr = view.state.tr
571
+ tr.replaceWith(pos, pos + node.nodeSize, crossReference)
572
+ upsertSupplementsSection(tr, supplement)
573
+ tr.setSelection(TextSelection.create(tr.doc, pos))
574
+
575
+ view.focus()
576
+ view.dispatch(tr)
577
+ return true
578
+ }
579
+
546
580
  export const updateSupplementWeblink = (
547
581
  pos: number,
548
582
  url: string,
@@ -1109,24 +1143,20 @@ export const insertAbstractSection =
1109
1143
  return false
1110
1144
  }
1111
1145
  const abstracts = findAbstractsNode(state.doc)
1112
- const sections = findChildrenByType(abstracts.node, schema.nodes.section)
1113
- // Check if the section already exists
1114
- if (sections.some((s) => s.node.attrs.category === category.id)) {
1146
+ const abstractNodes = findChildrenByType(
1147
+ abstracts.node,
1148
+ schema.nodes.abstract
1149
+ )
1150
+ if (abstractNodes.some((s) => s.node.attrs.category === category.id)) {
1115
1151
  return false
1116
1152
  }
1117
1153
 
1118
- // check if graphical abstract node exist to insert before it.
1119
- const ga = findChildrenByType(
1120
- state.doc,
1121
- schema.nodes.graphical_abstract_section
1122
- )[0]
1123
-
1124
- let pos = ga ? ga.pos : abstracts.pos + abstracts.node.content.size + 1
1125
- if (category.id === 'abstract') {
1126
- pos = abstracts.pos + 1
1127
- }
1154
+ const pos =
1155
+ findInsertionPosition(schema.nodes.abstract, abstracts.node) +
1156
+ abstracts.pos +
1157
+ 1
1128
1158
 
1129
- const node = schema.nodes.section.create({ category: category.id }, [
1159
+ const node = schema.nodes.abstract.create({ category: category.id }, [
1130
1160
  schema.nodes.section_title.create({}, schema.text(category.titles[0])),
1131
1161
  schema.nodes.paragraph.create({ placeholder: 'Type abstract here...' }),
1132
1162
  ])
@@ -1204,15 +1234,13 @@ export const insertGraphicalAbstract =
1204
1234
  return false
1205
1235
  }
1206
1236
 
1207
- const ga = findChildrenByType(
1208
- state.doc,
1209
- schema.nodes.graphical_abstract_section
1210
- )[0]
1211
-
1212
- // insert at the end of abstracts section
1213
- let pos = abstracts.pos + abstracts.node.content.size + 1
1214
- // abstract-key-image insert before abstract-graphical
1215
- pos = ga && category.id === 'abstract-key-image' ? ga.pos : pos
1237
+ const pos =
1238
+ findInsertionPosition(
1239
+ schema.nodes.graphical_abstract_section,
1240
+ abstracts.node
1241
+ ) +
1242
+ abstracts.pos +
1243
+ 1
1216
1244
 
1217
1245
  const node = schema.nodes.graphical_abstract_section.createAndFill(
1218
1246
  { category: category.id },
@@ -1503,8 +1531,7 @@ export const insertTOCSection = () => {
1503
1531
  export const insertTransAbstract = (
1504
1532
  state: ManuscriptEditorState,
1505
1533
  dispatch?: Dispatch,
1506
- category?: string,
1507
- insertAfterPos?: number
1534
+ category?: string
1508
1535
  ) => {
1509
1536
  if (!templateAllows(state, schema.nodes.trans_abstract)) {
1510
1537
  return false
@@ -1532,11 +1559,8 @@ export const insertTransAbstract = (
1532
1559
  )
1533
1560
 
1534
1561
  const abstracts = findAbstractsNode(state.doc)
1535
-
1536
- const pos =
1537
- insertAfterPos != null
1538
- ? insertAfterPos
1539
- : abstracts.pos + abstracts.node.nodeSize - 1
1562
+ // append at the end of the abstracts node, not just the first valid-but-earlier slot.
1563
+ const pos = abstracts.node.content.size + abstracts.pos + 1
1540
1564
  const tr = state.tr.insert(pos, node)
1541
1565
 
1542
1566
  const selection = TextSelection.create(tr.doc, pos + 1)
@@ -1547,7 +1571,7 @@ export const insertTransAbstract = (
1547
1571
  }
1548
1572
 
1549
1573
  export const insertTransGraphicalAbstract =
1550
- (category: SectionCategory, insertAfterPos?: number) =>
1574
+ (category: SectionCategory) =>
1551
1575
  (state: ManuscriptEditorState, dispatch?: Dispatch, view?: EditorView) => {
1552
1576
  if (!templateAllows(state, schema.nodes.trans_graphical_abstract)) {
1553
1577
  return false
@@ -1559,10 +1583,7 @@ export const insertTransGraphicalAbstract =
1559
1583
  const lang = state.doc.attrs.primaryLanguageCode || 'en'
1560
1584
 
1561
1585
  const abstracts = findAbstractsNode(state.doc)
1562
- const pos =
1563
- insertAfterPos != null
1564
- ? insertAfterPos
1565
- : abstracts.pos + abstracts.node.content.size + 1
1586
+ const pos = abstracts.node.content.size + abstracts.pos + 1
1566
1587
 
1567
1588
  const node = schema.nodes.trans_graphical_abstract.createAndFill(
1568
1589
  {
@@ -75,14 +75,7 @@ export const ReferenceSearch: React.FC<{
75
75
  onAdd: () => void
76
76
  onCite: (items: BibliographyItemAttrs[]) => void
77
77
  onCancel: () => void
78
- }> = ({
79
- query: initialQuery,
80
- sources,
81
- items,
82
- onAdd,
83
- onCite,
84
- onCancel,
85
- }) => {
78
+ }> = ({ query: initialQuery, sources, items, onAdd, onCite, onCancel }) => {
86
79
  const [query, setQuery] = useState<string>(initialQuery || '')
87
80
  const [selections, setSelections] = useState(
88
81
  new Map<string, BibliographyItemAttrs>()
@@ -137,6 +137,7 @@ export default (
137
137
  headshot_element: headshotElement(props, dispatch),
138
138
  headshot_image: headshotImage(props, dispatch),
139
139
  abstracts: abstracts(props),
140
+ abstract: section(props),
140
141
  trans_abstract: transAbstract(props),
141
142
  trans_graphical_abstract: transGraphicalAbstract(props),
142
143
  attachment: attachment(props, dispatch),
@@ -34,7 +34,7 @@ import { defaultTheme } from '@manuscripts/style-guide'
34
34
  const userProfile: UserProfile = {
35
35
  _id: 'MPUserProfile:1',
36
36
  userID: 'MPUser:1',
37
- connectID: 'connectID1'
37
+ connectID: 'connectID1',
38
38
  }
39
39
 
40
40
  const history = createMemoryHistory()
@@ -397,7 +397,10 @@ export class ContextMenu {
397
397
  this.createMenuSection((section: HTMLElement) => {
398
398
  let nodeName = nodeNames.get(type) || ''
399
399
  if (type === schema.nodes.section_title) {
400
- nodeName = nodeNames.get(schema.nodes.section) as string
400
+ const containerType = $pos.parent.type
401
+ nodeName =
402
+ nodeNames.get(containerType) ??
403
+ (nodeNames.get(schema.nodes.section) as string)
401
404
  }
402
405
  section.appendChild(
403
406
  this.createMenuItem(`Delete ${nodeName}`, () => {
package/src/menus.tsx CHANGED
@@ -563,7 +563,7 @@ export const getEditorMenus = (
563
563
  const abstractSection = allAbstractsCategories.find(
564
564
  (s) => s.id === ABSTRACT_ID
565
565
  )
566
- const ABSTRACT_GRAPHICAL_ID = 'abstract-graphical'
566
+ const ABSTRACT_GRAPHICAL_ID = 'graphical'
567
567
  const graphicalAbstractSection = allAbstractsCategories.find(
568
568
  (s) => s.id === ABSTRACT_GRAPHICAL_ID
569
569
  )
@@ -69,6 +69,7 @@ const icons: Map<
69
69
  [nodes.paragraph, OutlineParagraphIcon],
70
70
  [nodes.pullquote_element, OutlinePullQuoteIcon],
71
71
  [nodes.section, OutlineSectionIcon],
72
+ [nodes.abstract, OutlineSectionIcon],
72
73
  [nodes.table_element, OutlineTableIcon],
73
74
  [nodes.graphical_abstract_section, OutlineSectionIcon],
74
75
  [nodes.trans_graphical_abstract, OutlineSectionIcon],
@@ -15,6 +15,7 @@
15
15
  */
16
16
  import {
17
17
  getGroupCategories,
18
+ isAbstractNode,
18
19
  isSectionNode,
19
20
  schema,
20
21
  SectionCategory,
@@ -154,7 +155,8 @@ const buildPluginState = (
154
155
  if (node.type === schema.nodes.box_element) {
155
156
  return false
156
157
  }
157
- if (isSectionNode(node)) {
158
+
159
+ if (isSectionNode(node) || isAbstractNode(node)) {
158
160
  const categoryID = node.attrs.category
159
161
  const category = categories.get(categoryID)
160
162
  const $pos = state.doc.resolve(pos)
@@ -191,7 +193,10 @@ const buildPluginState = (
191
193
  }
192
194
 
193
195
  const getUsedSectionCategoryIDs = (state: EditorState): Set<string> => {
194
- const sections = findChildrenByType(state.doc, schema.nodes.section)
196
+ const sections = [
197
+ ...findChildrenByType(state.doc, schema.nodes.section),
198
+ ...findChildrenByType(state.doc, schema.nodes.abstract),
199
+ ]
195
200
  const used = new Set<string>()
196
201
  sections.forEach(({ node }) => {
197
202
  if (node.attrs.category) {
@@ -14,8 +14,13 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { schema } from '@manuscripts/transform'
18
- import { Node } from 'prosemirror-model'
17
+ import {
18
+ isAbstractNode,
19
+ isGraphicalAbstractSectionNode,
20
+ isTransAbstractNode,
21
+ isTransGraphicalAbstractNode,
22
+ schema,
23
+ } from '@manuscripts/transform'
19
24
  import { Plugin } from 'prosemirror-state'
20
25
  import { Decoration, DecorationSet } from 'prosemirror-view'
21
26
 
@@ -23,11 +28,11 @@ import { insertTransAbstract, insertTransGraphicalAbstract } from '../commands'
23
28
  import { EditorProps } from '../configs/ManuscriptsEditor'
24
29
  import { addAuthorIcon, translateIcon } from '../icons'
25
30
  import { getLanguage, getLanguageLabel } from '../lib/languages'
26
- import { templateAllows } from '../lib/template'
27
31
  import {
28
- handleEnterKey,
29
32
  createKeyboardInteraction,
33
+ handleEnterKey,
30
34
  } from '../lib/navigation-utils'
35
+ import { templateAllows } from '../lib/template'
31
36
 
32
37
  const createMenuItem = (
33
38
  props: EditorProps,
@@ -90,11 +95,6 @@ const createLanguageMenu = (
90
95
  return { menu, destroy }
91
96
  }
92
97
 
93
- const getInsertionPos = (doc: Node, nodePos: number): number | null => {
94
- const node = doc.nodeAt(nodePos)
95
- return node ? nodePos + node.nodeSize : null
96
- }
97
-
98
98
  export default (props: EditorProps) =>
99
99
  new Plugin<null>({
100
100
  props: {
@@ -110,19 +110,14 @@ export default (props: EditorProps) =>
110
110
 
111
111
  const widgets: Decoration[] = []
112
112
 
113
- state.doc.descendants((node, pos, parent) => {
113
+ state.doc.descendants((node, pos) => {
114
114
  const isAbstractSection =
115
- (node.type === schema.nodes.section ||
116
- node.type === schema.nodes.graphical_abstract_section) &&
117
- parent?.type === schema.nodes.abstracts
115
+ isAbstractNode(node) || isGraphicalAbstractSectionNode(node)
118
116
 
119
117
  // Show "Add translation" for abstract sections
120
118
  if (isAbstractSection) {
121
- const isGraphical =
122
- node.type === schema.nodes.graphical_abstract_section
123
119
  const category = props.sectionCategories.get(node.attrs.category)
124
-
125
- const canEdit = isGraphical
120
+ const canEdit = isGraphicalAbstractSectionNode(node)
126
121
  ? canEditTransGraphicalAbstract &&
127
122
  category &&
128
123
  insertTransGraphicalAbstract(category)(state)
@@ -142,12 +137,8 @@ export default (props: EditorProps) =>
142
137
  const handleActivate = (event: Event) => {
143
138
  event.preventDefault()
144
139
  event.stopPropagation()
145
- const insertPos = getInsertionPos(view.state.doc, pos)
146
- if (insertPos == null) {
147
- return
148
- }
149
- if (isGraphical && category) {
150
- insertTransGraphicalAbstract(category, insertPos)(
140
+ if (isGraphicalAbstractSectionNode(node) && category) {
141
+ insertTransGraphicalAbstract(category)(
151
142
  view.state,
152
143
  view.dispatch,
153
144
  view
@@ -156,8 +147,7 @@ export default (props: EditorProps) =>
156
147
  insertTransAbstract(
157
148
  view.state,
158
149
  view.dispatch,
159
- node.attrs.category,
160
- insertPos
150
+ node.attrs.category
161
151
  )
162
152
  }
163
153
  }
@@ -177,8 +167,7 @@ export default (props: EditorProps) =>
177
167
 
178
168
  // Language selector for trans_abstract and trans_graphical_abstract nodes
179
169
  const isTransNode =
180
- node.type === schema.nodes.trans_abstract ||
181
- node.type === schema.nodes.trans_graphical_abstract
170
+ isTransAbstractNode(node) || isTransGraphicalAbstractNode(node)
182
171
 
183
172
  if (isTransNode) {
184
173
  const canEdit =
package/src/versions.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  //THIS FILE WAS GENERATED BY versions.mjs
2
- export const VERSION = '3.17.2';
2
+ export const VERSION = '3.17.5';
3
3
  export const MATHJAX_VERSION = '3.2.2';
@@ -14,9 +14,12 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
+ import { ContextMenu, ContextMenuProps } from '@manuscripts/style-guide'
18
+ import { isDeleted } from '@manuscripts/track-changes-plugin'
17
19
  import { schema } from '@manuscripts/transform'
18
20
  import { TextSelection } from 'prosemirror-state'
19
21
 
22
+ import { convertLinkToSupplementWeblink } from '../commands'
20
23
  import {
21
24
  LinkForm,
22
25
  LinkFormProps,
@@ -26,7 +29,6 @@ import { allowedHref } from '../lib/url'
26
29
  import { createEditableNodeView } from './creators'
27
30
  import { LinkView } from './link'
28
31
  import ReactSubView from './ReactSubView'
29
- import { isDeleted } from '@manuscripts/track-changes-plugin'
30
32
 
31
33
  export class LinkEditableView extends LinkView {
32
34
  protected popperContainer: HTMLDivElement
@@ -59,7 +61,7 @@ export class LinkEditableView extends LinkView {
59
61
  }
60
62
 
61
63
  public selectNode = () => {
62
- if (!isDeleted(this.node)) {
64
+ if (!isDeleted(this.node) && !this.node.attrs.href) {
63
65
  this.showForm()
64
66
  }
65
67
  }
@@ -72,6 +74,67 @@ export class LinkEditableView extends LinkView {
72
74
  this.closeForm()
73
75
  }
74
76
 
77
+ private showContextMenu = () => {
78
+ if (!this.props.getCapabilities().editArticle) {
79
+ return
80
+ }
81
+
82
+ this.closeForm()
83
+
84
+ const href = this.node.attrs.href
85
+
86
+ const componentProps: ContextMenuProps = {
87
+ actions: [
88
+ {
89
+ label: 'Edit',
90
+ action: this.handleEdit,
91
+ icon: 'Edit',
92
+ },
93
+ {
94
+ label: 'Convert to Weblink',
95
+ action: this.handleConvertToWebLink,
96
+ icon: 'ConvertToWebLink',
97
+ },
98
+ {
99
+ label: 'Open Link',
100
+ action: this.handleOpenLink,
101
+ icon: 'ExternalLink',
102
+ disabled: !allowedHref(href),
103
+ },
104
+ ],
105
+ }
106
+
107
+ this.popperContainer = ReactSubView(
108
+ this.props,
109
+ ContextMenu,
110
+ componentProps,
111
+ this.node,
112
+ this.getPos,
113
+ this.view,
114
+ ['context-menu', 'link-context-menu']
115
+ )
116
+
117
+ this.props.popper.show(this.dom, this.popperContainer, 'bottom')
118
+ }
119
+
120
+ private handleEdit = () => {
121
+ this.closeForm()
122
+ this.showForm()
123
+ }
124
+
125
+ private handleConvertToWebLink = () => {
126
+ this.closeForm()
127
+ convertLinkToSupplementWeblink(this.getPos(), this.node, this.view)
128
+ }
129
+
130
+ private handleOpenLink = () => {
131
+ const href = this.node.attrs.href
132
+ if (allowedHref(href)) {
133
+ window.open(href, '_blank', 'noopener,noreferrer')
134
+ }
135
+ this.closeForm()
136
+ }
137
+
75
138
  private showForm = () => {
76
139
  if (!this.props.getCapabilities().editArticle) {
77
140
  return
@@ -106,9 +169,15 @@ export class LinkEditableView extends LinkView {
106
169
  }
107
170
 
108
171
  private handleClick = (e: Event) => {
109
- if (this.props.getCapabilities().editArticle) {
110
- e.preventDefault()
172
+ if (!this.props.getCapabilities().editArticle) {
173
+ return
174
+ }
175
+ e.preventDefault()
176
+
177
+ if (isDeleted(this.node) || !this.node.attrs.href) {
178
+ return
111
179
  }
180
+ this.showContextMenu()
112
181
  }
113
182
 
114
183
  private handleCancel = () => {
package/styles/Editor.css CHANGED
@@ -694,12 +694,14 @@
694
694
  grid-template-columns: 52px auto 100px 28px;
695
695
  }
696
696
  .ProseMirror .block-container.block-section,
697
+ .ProseMirror .block-container.block-abstract,
697
698
  .ProseMirror .block-container.block-graphical_abstract_section {
698
699
  position: relative;
699
700
  display: grid;
700
701
  grid-template-columns: auto;
701
702
  }
702
703
  .ProseMirror .block-container.block-section section,
704
+ .ProseMirror .block-container.block-abstract section,
703
705
  .ProseMirror .block-container.block-graphical_abstract_section section {
704
706
  position: relative;
705
707
  }
@@ -707,6 +709,7 @@
707
709
  .ProseMirror
708
710
  .block-container:not(
709
711
  .block-section,
712
+ .block-abstract,
710
713
  .block-graphical_abstract_section
711
714
  ):hover {
712
715
  z-index: 2;
@@ -789,6 +792,7 @@
789
792
  .block-container:not(
790
793
  .block-section,
791
794
  .block-box_element,
795
+ .block-abstract,
792
796
  .block-graphical_abstract_section
793
797
  ):hover
794
798
  .block-gutter,
@@ -796,6 +800,7 @@
796
800
  .block-container:not(
797
801
  .block-section,
798
802
  .block-box_element,
803
+ .block-abstract,
799
804
  .block-graphical_abstract_section
800
805
  ):focus-within
801
806
  .block-gutter {
@@ -818,6 +823,7 @@
818
823
  .block-container:not(
819
824
  .block-section,
820
825
  .block-box_element,
826
+ .block-abstract,
821
827
  .block-graphical_abstract_section
822
828
  ):hover
823
829
  .action-gutter,
@@ -825,6 +831,7 @@
825
831
  .block-container:not(
826
832
  .block-section,
827
833
  .block-box_element,
834
+ .block-abstract,
828
835
  .block-graphical_abstract_section
829
836
  ):focus-within
830
837
  .action-gutter {