@manuscripts/body-editor 3.12.66 → 3.13.0

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 (289) hide show
  1. package/dist/cjs/components/toolbar/type-selector/styles.js +2 -1
  2. package/dist/cjs/versions.js +1 -1
  3. package/dist/es/components/toolbar/type-selector/styles.js +2 -1
  4. package/dist/es/versions.js +1 -1
  5. package/dist/types/components/toolbar/type-selector/styles.d.ts +2 -2
  6. package/dist/types/lib/footnotes.d.ts +4 -2
  7. package/dist/types/lib/utils.d.ts +5 -4
  8. package/dist/types/versions.d.ts +1 -1
  9. package/package.json +37 -36
  10. package/{dist/es/lib/statistics.js → src/__mocks__/styleMock.ts} +2 -14
  11. package/src/assets.ts +20 -0
  12. package/src/classes/collabProvider.ts +38 -0
  13. package/src/clipboard.ts +54 -0
  14. package/src/commands.ts +2109 -0
  15. package/src/components/ChangeHandlingForm.tsx +54 -0
  16. package/src/components/LanguageDropdown/index.tsx +343 -0
  17. package/src/components/affiliations/AffiliationForm.tsx +238 -0
  18. package/src/components/affiliations/AffiliationItem.tsx +151 -0
  19. package/src/components/affiliations/AffiliationList.tsx +71 -0
  20. package/src/components/affiliations/AffiliationsModal.tsx +676 -0
  21. package/src/components/affiliations/AffiliationsPanel.tsx +76 -0
  22. package/src/components/affiliations/CreateAffiliationModal.tsx +135 -0
  23. package/src/components/authors/AuthorDetailsForm.tsx +438 -0
  24. package/src/components/authors/AuthorList.tsx +81 -0
  25. package/src/components/authors/AuthorsModal.tsx +642 -0
  26. package/src/components/authors/AuthorsPanel.tsx +69 -0
  27. package/src/components/authors/CreateAuthorModal.tsx +172 -0
  28. package/src/components/authors/CreditDrawer.tsx +71 -0
  29. package/src/components/authors/DraggableAuthor.tsx +228 -0
  30. package/src/components/authors/useManageAffiliations.tsx +77 -0
  31. package/src/components/authors/useManageCredit.tsx +63 -0
  32. package/src/components/authors-affiliations/AuthorsAndAffiliationsModals.tsx +170 -0
  33. package/src/components/authors-affiliations/GenericPanel.tsx +164 -0
  34. package/src/components/authors-affiliations/ModalTabs.tsx +100 -0
  35. package/src/components/awards/AwardForm.tsx +338 -0
  36. package/src/components/awards/AwardModal.tsx +120 -0
  37. package/src/components/awards/DeleteAwardDiaolog.tsx +52 -0
  38. package/src/components/dialog/ConfirmationDialog.tsx +131 -0
  39. package/src/components/form/CreateModalStyles.tsx +37 -0
  40. package/src/components/form/FormFooter.tsx +54 -0
  41. package/src/components/form/FormPlaceholder.tsx +71 -0
  42. package/src/components/form/ModalFormActions.tsx +88 -0
  43. package/src/components/form/UnsavedLabel.tsx +49 -0
  44. package/src/components/hooks/use-debounce.ts +56 -0
  45. package/src/components/hooks/useAffiliationShowsErrorIndicator.ts +51 -0
  46. package/src/components/hooks/useAuthorShowsErrorIndicator.ts +73 -0
  47. package/src/components/keyboard-shortcuts-modal/FormattedShortcut.tsx +56 -0
  48. package/src/components/keyboard-shortcuts-modal/KeyboardShortcutsModal.tsx +283 -0
  49. package/src/components/keyboard-shortcuts-modal/keyboard-shortcuts.ts +304 -0
  50. package/src/components/keywords/AddKeywordInline.tsx +284 -0
  51. package/src/components/keywords/DeleteKeywordDialog.tsx +52 -0
  52. package/src/components/modal-drawer/GenericDrawer.tsx +65 -0
  53. package/src/components/modal-drawer/GenericDrawerGroup.tsx +125 -0
  54. package/src/components/outline/DraggableTree.tsx +438 -0
  55. package/src/components/outline/ManuscriptOutline.tsx +127 -0
  56. package/src/components/outline/Outline.tsx +141 -0
  57. package/src/components/references/BibliographyItemSource.ts +80 -0
  58. package/src/components/references/CitationEditor.tsx +301 -0
  59. package/src/components/references/CitationViewer.tsx +62 -0
  60. package/src/components/references/ImportBibliographyForm.tsx +232 -0
  61. package/src/components/references/ImportBibliographyModal.tsx +159 -0
  62. package/src/components/references/ReferenceForm/PersonDropDown.tsx +93 -0
  63. package/src/components/references/ReferenceForm/ReferenceForm.tsx +701 -0
  64. package/src/components/references/ReferenceForm/config.ts +133 -0
  65. package/src/components/references/ReferenceForm/styled-components.ts +156 -0
  66. package/src/components/references/ReferenceLine.tsx +43 -0
  67. package/src/components/references/ReferenceSearch.tsx +163 -0
  68. package/src/components/references/ReferenceSearchResults.tsx +113 -0
  69. package/src/components/references/ReferenceSearchResultsPlaceholder.tsx +71 -0
  70. package/src/components/references/ReferenceSearchSection.tsx +124 -0
  71. package/src/components/references/ReferencesEditor.tsx +61 -0
  72. package/src/components/references/ReferencesModal.tsx +352 -0
  73. package/src/components/references/SearchInput.tsx +98 -0
  74. package/src/components/toolbar/InsertEmbedDialog.tsx +222 -0
  75. package/src/components/toolbar/InsertTableDialog.tsx +208 -0
  76. package/src/components/toolbar/ListMenuItem.tsx +203 -0
  77. package/src/components/toolbar/helpers.ts +547 -0
  78. package/src/components/toolbar/type-selector/OptionComponent.tsx +89 -0
  79. package/src/components/toolbar/type-selector/TypeSelector.tsx +157 -0
  80. package/src/components/toolbar/type-selector/styles.ts +79 -0
  81. package/src/components/views/CrossReferenceItems.tsx +199 -0
  82. package/src/components/views/DeleteEmbedDialog.tsx +83 -0
  83. package/src/components/views/DeleteFootnoteDialog.tsx +54 -0
  84. package/src/components/views/FigureDropdown.tsx +423 -0
  85. package/src/components/views/FootnotesSelector.tsx +255 -0
  86. package/src/components/views/InsertSpecialCharacter.tsx +245 -0
  87. package/src/components/views/LinkForm.tsx +220 -0
  88. package/src/components/views/TableCellContextMenu.tsx +241 -0
  89. package/src/configs/ManuscriptsEditor.ts +140 -0
  90. package/src/configs/editor-plugins.ts +119 -0
  91. package/src/configs/editor-views.ts +139 -0
  92. package/src/icons.ts +62 -0
  93. package/src/index.ts +65 -0
  94. package/src/keys/highlight.ts +65 -0
  95. package/src/keys/index.ts +33 -0
  96. package/src/keys/keyword.ts +69 -0
  97. package/src/keys/list.ts +156 -0
  98. package/src/keys/misc.ts +112 -0
  99. package/src/keys/title.ts +221 -0
  100. package/src/lib/__tests__/__fixtures__/doc.json +388 -0
  101. package/src/lib/__tests__/__snapshots__/paste.test.ts.snap +24 -0
  102. package/src/lib/__tests__/authors.test.ts +202 -0
  103. package/src/lib/__tests__/helpers.test.ts +46 -0
  104. package/src/lib/__tests__/paste.test.ts +57 -0
  105. package/src/lib/__tests__/plugins.test.ts +176 -0
  106. package/src/lib/__tests__/url.test.ts +31 -0
  107. package/src/lib/array-reducer.ts +68 -0
  108. package/src/lib/authors-and-affiliations.ts +81 -0
  109. package/src/lib/authors.ts +67 -0
  110. package/src/lib/capabilities.ts +55 -0
  111. package/src/lib/codemirror.ts +37 -0
  112. package/src/lib/comments.ts +123 -0
  113. package/src/lib/context-menu.ts +745 -0
  114. package/src/lib/copy.ts +30 -0
  115. package/src/lib/crossref.ts +90 -0
  116. package/src/lib/dnd.ts +29 -0
  117. package/src/lib/doc.ts +167 -0
  118. package/src/lib/dompurify.ts +28 -0
  119. package/src/lib/files.ts +180 -0
  120. package/src/lib/footnotes.ts +83 -0
  121. package/src/lib/get-media-type.ts +105 -0
  122. package/src/lib/helpers.ts +199 -0
  123. package/src/lib/hierarchy.ts +117 -0
  124. package/src/lib/languages.ts +37 -0
  125. package/src/lib/lists.ts +56 -0
  126. package/src/lib/math.ts +111 -0
  127. package/src/lib/media.ts +334 -0
  128. package/src/lib/navigation-utils.ts +235 -0
  129. package/src/lib/normalize.ts +70 -0
  130. package/src/lib/paste.ts +304 -0
  131. package/src/lib/placeholder.ts +32 -0
  132. package/src/lib/platform.ts +18 -0
  133. package/src/lib/plugins.ts +22 -0
  134. package/src/lib/popper.ts +180 -0
  135. package/src/lib/position-menu.ts +184 -0
  136. package/src/lib/references.ts +94 -0
  137. package/src/lib/template.ts +29 -0
  138. package/src/lib/track-changes-utils.ts +34 -0
  139. package/{dist/types/lib/statistics.d.ts → src/lib/url.ts} +11 -8
  140. package/src/lib/use-do-with-debounce.ts +39 -0
  141. package/src/lib/utils.ts +323 -0
  142. package/src/lib/view.ts +100 -0
  143. package/src/menus.tsx +684 -0
  144. package/src/node-type-icons.tsx +83 -0
  145. package/src/plugins/__tests__/__fixtures__/deleted-footnote.json +140 -0
  146. package/src/plugins/__tests__/__fixtures__/inserted-footnote.json +153 -0
  147. package/src/plugins/__tests__/footnotes.test.ts +67 -0
  148. package/src/plugins/accessibility_element.ts +219 -0
  149. package/src/plugins/add-subtitle.ts +102 -0
  150. package/src/plugins/affiliations.ts +136 -0
  151. package/src/plugins/alt-titles.ts +219 -0
  152. package/src/plugins/bibliography.ts +235 -0
  153. package/src/plugins/comments.ts +328 -0
  154. package/src/plugins/cross-references.ts +70 -0
  155. package/src/plugins/detect-inconsistency/detect-inconsistency-utils.ts +102 -0
  156. package/src/plugins/detect-inconsistency/index.ts +55 -0
  157. package/src/plugins/detect-inconsistency/validators.ts +283 -0
  158. package/src/plugins/doi.ts +66 -0
  159. package/src/plugins/editor-props.ts +39 -0
  160. package/src/plugins/elements.ts +60 -0
  161. package/src/plugins/footnotes.ts +298 -0
  162. package/src/plugins/link.ts +53 -0
  163. package/src/plugins/lock-body.ts +136 -0
  164. package/src/plugins/move-node.ts +55 -0
  165. package/src/plugins/objects.ts +125 -0
  166. package/src/plugins/paragraphs.ts +90 -0
  167. package/src/plugins/persist.ts +64 -0
  168. package/src/plugins/persistent-cursor.ts +66 -0
  169. package/src/plugins/placeholder.ts +219 -0
  170. package/src/plugins/prevent-empty.ts +50 -0
  171. package/src/plugins/search-replace/index.ts +157 -0
  172. package/src/plugins/search-replace/lib.ts +95 -0
  173. package/src/plugins/section_category.ts +212 -0
  174. package/src/plugins/section_title/autocompletion.ts +87 -0
  175. package/src/plugins/section_title/index.ts +105 -0
  176. package/src/plugins/selected-suggestion.ts +243 -0
  177. package/src/plugins/tables-cursor-fix.ts +143 -0
  178. package/src/plugins/translations.ts +263 -0
  179. package/src/rules.ts +60 -0
  180. package/src/selection.ts +112 -0
  181. package/src/testing/default-capabilities.ts +48 -0
  182. package/src/testing/default-editor-data.ts +73 -0
  183. package/src/testing/empty-editor-doc.json +62 -0
  184. package/src/testing/polyfill-dom.ts +43 -0
  185. package/src/testing/section-categories.json +161 -0
  186. package/src/testing/setup-editor.ts +55 -0
  187. package/src/tests.ts +42 -0
  188. package/src/toolbar.tsx +259 -0
  189. package/src/types/@citation-js/core.d.ts +16 -0
  190. package/src/types/@citation-js/index.d.ts +16 -0
  191. package/src/types/@citation-js/plugin-bibtex.d.ts +16 -0
  192. package/src/types/@citation-js/plugin-csl.d.ts +16 -0
  193. package/src/types/@citation-js/plugin-doi.d.ts +16 -0
  194. package/src/types/@citation-js/plugin-enw.d.ts +16 -0
  195. package/src/types/@citation-js/plugin-pubmed.d.ts +16 -0
  196. package/src/types/@citation-js/plugin-ris.d.ts +16 -0
  197. package/src/types/citeproc.d.ts +124 -0
  198. package/src/types/css.d.ts +16 -0
  199. package/src/types/json.d.ts +17 -0
  200. package/src/types/prosemirror-dev-tools.d.ts +16 -0
  201. package/src/types/prosemirror-dropcursor.d.ts +16 -0
  202. package/src/types/styled-components.d.ts +23 -0
  203. package/src/types.ts +49 -0
  204. package/src/useEditor.ts +244 -0
  205. package/src/versions.ts +3 -0
  206. package/src/views/ReactSubView.tsx +145 -0
  207. package/src/views/abstracts.ts +46 -0
  208. package/src/views/accessibility_element.ts +86 -0
  209. package/src/views/affiliations.ts +254 -0
  210. package/src/views/alt_title.ts +85 -0
  211. package/src/views/alt_titles_section.ts +97 -0
  212. package/src/views/attachment.ts +160 -0
  213. package/src/views/attachments.ts +72 -0
  214. package/src/views/author_notes.ts +96 -0
  215. package/src/views/award.ts +194 -0
  216. package/src/views/awards.ts +59 -0
  217. package/src/views/base_node_view.ts +85 -0
  218. package/src/views/bibliography_element.ts +313 -0
  219. package/src/views/block_view.ts +100 -0
  220. package/src/views/blockquote_element.ts +26 -0
  221. package/src/views/blockquote_element_editable.ts +24 -0
  222. package/src/views/box_element.ts +33 -0
  223. package/src/views/caption.ts +37 -0
  224. package/src/views/caption_title.ts +33 -0
  225. package/src/views/citation.ts +67 -0
  226. package/src/views/citation_editable.ts +312 -0
  227. package/src/views/contributors.ts +341 -0
  228. package/src/views/creators.ts +82 -0
  229. package/src/views/cross_reference.ts +74 -0
  230. package/src/views/cross_reference_editable.ts +113 -0
  231. package/src/views/editable_block.ts +141 -0
  232. package/src/views/embed.ts +345 -0
  233. package/src/views/empty.ts +26 -0
  234. package/src/views/equation.ts +47 -0
  235. package/src/views/equation_editable.ts +84 -0
  236. package/src/views/equation_element.ts +29 -0
  237. package/src/views/equation_element_editable.ts +21 -0
  238. package/src/views/figure.ts +84 -0
  239. package/src/views/figure_editable.ts +361 -0
  240. package/src/views/figure_element.ts +197 -0
  241. package/src/views/figure_element_editable.ts +21 -0
  242. package/src/views/footnote.ts +236 -0
  243. package/src/views/footnotes_element.ts +54 -0
  244. package/src/views/general_table_footnote.ts +140 -0
  245. package/src/views/hero_image.ts +76 -0
  246. package/src/views/hero_image_editable.ts +21 -0
  247. package/src/views/image_element.ts +373 -0
  248. package/src/views/image_element_editable.ts +21 -0
  249. package/src/views/inline_equation.ts +47 -0
  250. package/src/views/inline_equation_editable.ts +70 -0
  251. package/src/views/inline_footnote.ts +264 -0
  252. package/src/views/inline_footnote_editable.ts +20 -0
  253. package/src/views/keyword.ts +127 -0
  254. package/src/views/keyword_group.ts +109 -0
  255. package/src/views/link.ts +27 -0
  256. package/src/views/link_editable.ts +169 -0
  257. package/src/views/list.ts +46 -0
  258. package/src/views/list_item.ts +26 -0
  259. package/src/views/paragraph.ts +26 -0
  260. package/src/views/paragraph_editable.ts +21 -0
  261. package/src/views/placeholder.ts +35 -0
  262. package/src/views/placeholder_element.ts +38 -0
  263. package/src/views/placeholder_element_editable.ts +21 -0
  264. package/src/views/pullquote_element.ts +106 -0
  265. package/src/views/pullquote_element_editable.ts +24 -0
  266. package/src/views/quote_image_editable.ts +84 -0
  267. package/src/views/section.ts +64 -0
  268. package/src/views/section_label.ts +20 -0
  269. package/src/views/section_title.ts +87 -0
  270. package/src/views/section_title_editable.ts +21 -0
  271. package/src/views/subtitle.ts +42 -0
  272. package/src/views/subtitle_editable.ts +20 -0
  273. package/src/views/subtitles.ts +34 -0
  274. package/src/views/subtitles_editable.ts +21 -0
  275. package/src/views/supplement.ts +236 -0
  276. package/src/views/supplement_editable.ts +20 -0
  277. package/src/views/supplements.ts +65 -0
  278. package/src/views/supplements_editable.ts +21 -0
  279. package/src/views/table_cell.ts +150 -0
  280. package/src/views/table_element.ts +33 -0
  281. package/src/views/table_element_editable.ts +21 -0
  282. package/src/views/table_element_footer.ts +41 -0
  283. package/src/views/title.ts +46 -0
  284. package/src/views/title_editable.ts +20 -0
  285. package/src/views/trans_graphical_abstract.ts +55 -0
  286. package/src/views/trans_graphical_abstract_editable.ts +21 -0
  287. package/src/views/translated_abstract.ts +53 -0
  288. package/src/views/translated_abstract_editable.ts +21 -0
  289. package/dist/cjs/lib/statistics.js +0 -38
@@ -0,0 +1,312 @@
1
+ /*!
2
+ * © 2019 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { ContextMenu, ContextMenuProps } from '@manuscripts/style-guide'
18
+ import { isDeleted } from '@manuscripts/track-changes-plugin'
19
+ import {
20
+ BibliographyItemAttrs,
21
+ ManuscriptNode,
22
+ schema,
23
+ } from '@manuscripts/transform'
24
+ import { TextSelection } from 'prosemirror-state'
25
+ import { findChildrenByType } from 'prosemirror-utils'
26
+
27
+ import {
28
+ CitationEditor,
29
+ CitationEditorProps,
30
+ } from '../components/references/CitationEditor'
31
+ import {
32
+ CitationViewer,
33
+ CitationViewerProps,
34
+ } from '../components/references/CitationViewer'
35
+ import { handleComment } from '../lib/comments'
36
+ import { Crossref } from '../lib/crossref'
37
+ import { handleEnterKey } from '../lib/navigation-utils'
38
+ import { deleteNode, findChildByID, updateNodeAttrs } from '../lib/view'
39
+ import { getBibliographyPluginState } from '../plugins/bibliography'
40
+ import { CitationView } from './citation'
41
+ import { createEditableNodeView } from './creators'
42
+ import ReactSubView from './ReactSubView'
43
+
44
+ const createBibliographySection = (node: ManuscriptNode) =>
45
+ schema.nodes.bibliography_section.createAndFill({}, [
46
+ schema.nodes.section_title.create({}, schema.text('References')),
47
+ schema.nodes.bibliography_element.create({}, node ? [node] : []),
48
+ ]) as ManuscriptNode
49
+
50
+ export class CitationEditableView extends CitationView {
51
+ private editor: HTMLElement
52
+ private contextMenu: HTMLElement
53
+ private can = this.props.getCapabilities()
54
+
55
+ createDOM() {
56
+ super.createDOM()
57
+ this.dom.addEventListener('mouseup', () => this.handleClick(false))
58
+ this.dom.addEventListener(
59
+ 'keydown',
60
+ handleEnterKey(() => this.handleClick(true))
61
+ )
62
+ }
63
+
64
+ // we added this to stop select events in case th e user clicks on the comment,
65
+ // so it won't interfere with the context menu
66
+ public stopEvent = (event: Event) => {
67
+ const element = event.target as Element
68
+ return !!(
69
+ element.classList.contains('point-comment') ||
70
+ element.classList.contains('comment-icon') ||
71
+ element.parentElement?.classList.contains('comment-icon')
72
+ )
73
+ }
74
+
75
+ public handleClick = (fromKeyboard: boolean) => {
76
+ if (
77
+ !this.can.seeReferencesButtons ||
78
+ this.dom.classList.contains('inconsistency-highlight')
79
+ ) {
80
+ this.showPopper()
81
+ } else if (!isDeleted(this.node)) {
82
+ const attrs = this.node.attrs
83
+ if (attrs.rids.length) {
84
+ this.showContextMenu(fromKeyboard)
85
+ }
86
+ }
87
+ }
88
+ public selectNode = () => {
89
+ this.dom.classList.add('ProseMirror-selectednode')
90
+ if (
91
+ this.can.seeReferencesButtons &&
92
+ !isDeleted(this.node) &&
93
+ !this.dom.classList.contains('inconsistency-highlight')
94
+ ) {
95
+ const attrs = this.node.attrs
96
+ if (!attrs.rids.length) {
97
+ this.showPopper()
98
+ }
99
+ }
100
+ }
101
+
102
+ public destroy = () => {
103
+ this.editor?.remove()
104
+ this.props.popper.destroy()
105
+ }
106
+
107
+ public showContextMenu = (autoFocus = true) => {
108
+ this.props.popper.destroy()
109
+
110
+ const can = this.props.getCapabilities()
111
+ const actions = [
112
+ {
113
+ label: 'Comment',
114
+ action: () => handleComment(this.node, this.view),
115
+ icon: 'AddComment',
116
+ },
117
+ ]
118
+
119
+ if (can.editArticle) {
120
+ actions.unshift({
121
+ label: 'Edit',
122
+ action: () => this.handleEdit(),
123
+ icon: 'Edit',
124
+ })
125
+ }
126
+ const componentProps: ContextMenuProps = {
127
+ actions,
128
+ }
129
+ this.contextMenu = ReactSubView(
130
+ this.props,
131
+ ContextMenu,
132
+ componentProps,
133
+ this.node,
134
+ this.getPos,
135
+ this.view,
136
+ ['context-menu']
137
+ )
138
+ this.props.popper.show(
139
+ this.dom,
140
+ this.contextMenu,
141
+ 'right-start',
142
+ false,
143
+ [],
144
+ autoFocus
145
+ )
146
+ }
147
+
148
+ public showPopper = () => {
149
+ this.props.popper.destroy()
150
+ const bib = getBibliographyPluginState(this.view.state)
151
+ if (!bib) {
152
+ return
153
+ }
154
+ const can = this.props.getCapabilities()
155
+
156
+ const attrs = this.node.attrs
157
+ const rids = attrs.rids
158
+
159
+ const items = Array.from(bib.bibliographyItems.values())
160
+
161
+ if (can.editArticle) {
162
+ const query = this.node.attrs.selectedText
163
+ const componentProps: CitationEditorProps = {
164
+ query,
165
+ rids,
166
+ items,
167
+ citationCounts: bib.citationCounts,
168
+ sources: [Crossref],
169
+ onCite: this.handleCite,
170
+ onUncite: this.handleUncite,
171
+ onSave: this.handleSave,
172
+ onDelete: this.handleDelete,
173
+ onCancel: this.handleCancel,
174
+ canEdit: can.editCitationsAndRefs,
175
+ }
176
+
177
+ this.editor = ReactSubView(
178
+ this.props,
179
+ CitationEditor,
180
+ componentProps,
181
+ this.node,
182
+ this.getPos,
183
+ this.view,
184
+ ['citation-editor']
185
+ )
186
+ } else {
187
+ const componentProps: CitationViewerProps = {
188
+ rids,
189
+ items,
190
+ }
191
+ this.editor = ReactSubView(
192
+ this.props,
193
+ CitationViewer,
194
+ componentProps,
195
+ this.node,
196
+ this.getPos,
197
+ this.view,
198
+ ['citation-editor']
199
+ )
200
+ }
201
+ this.props.popper.show(this.dom, this.editor, 'auto')
202
+ }
203
+
204
+ private handleEdit = () => {
205
+ this.showPopper()
206
+ }
207
+
208
+ private handleCancel = () => {
209
+ // move the cursor after this node
210
+ const selection = TextSelection.create(
211
+ this.view.state.tr.doc,
212
+ this.getPos() + 1
213
+ )
214
+ this.view.dispatch(this.view.state.tr.setSelection(selection))
215
+
216
+ this.props.popper.destroy()
217
+ }
218
+
219
+ private handleSave = (attrs: BibliographyItemAttrs) => {
220
+ if (!findChildByID(this.view, attrs.id)) {
221
+ this.insertBibliographyNode(attrs)
222
+ } else {
223
+ updateNodeAttrs(this.view, schema.nodes.bibliography_item, attrs)
224
+ }
225
+ }
226
+
227
+ private handleUncite = (id: string) => {
228
+ const attrs = this.node.attrs
229
+ const rids = attrs.rids.filter((i) => i !== id)
230
+ const pos = this.getPos()
231
+ const tr = this.view.state.tr
232
+
233
+ if (rids.length > 0) {
234
+ tr.setNodeMarkup(pos, undefined, {
235
+ ...attrs,
236
+ rids,
237
+ })
238
+ } else {
239
+ tr.delete(pos, pos + 1)
240
+ }
241
+
242
+ this.view.dispatch(tr)
243
+ this.props.popper.destroy()
244
+ }
245
+
246
+ private handleCite = (items: BibliographyItemAttrs[]) => {
247
+ const bib = getBibliographyPluginState(this.view.state)
248
+ if (!bib) {
249
+ return
250
+ }
251
+
252
+ const attrs = this.node.attrs
253
+ const rids = [...attrs.rids]
254
+
255
+ items = items.filter((i) => !rids.includes(i.id))
256
+
257
+ for (const item of items) {
258
+ const existingItem = bib.bibliographyItems.get(item.id)
259
+
260
+ if (existingItem) {
261
+ item.id = existingItem.id
262
+ } else {
263
+ this.insertBibliographyNode(item)
264
+ }
265
+
266
+ rids.push(item.id)
267
+ }
268
+
269
+ const tr = this.view.state.tr
270
+ const pos = this.getPos()
271
+ tr.setNodeAttribute(pos, 'rids', rids)
272
+
273
+ this.view.dispatch(tr)
274
+ this.handleCancel()
275
+ }
276
+
277
+ private handleDelete = (item: BibliographyItemAttrs) => {
278
+ return deleteNode(this.view, item.id)
279
+ }
280
+
281
+ private insertBibliographyNode(attrs: BibliographyItemAttrs) {
282
+ const { doc, tr } = this.view.state
283
+
284
+ const biblioSection = findChildrenByType(
285
+ doc,
286
+ schema.nodes.bibliography_element,
287
+ true
288
+ )
289
+
290
+ const backmatter = findChildrenByType(doc, schema.nodes.backmatter, true)
291
+ const backmatterEnd = backmatter[0]
292
+ ? backmatter[0].node.nodeSize + backmatter[0].pos
293
+ : 0
294
+
295
+ const node = schema.nodes.bibliography_item.create(attrs)
296
+
297
+ if (biblioSection.length) {
298
+ this.view.dispatch(tr.insert(biblioSection[0].pos + 1, node))
299
+ } else {
300
+ this.view.dispatch(
301
+ tr.insert(
302
+ backmatterEnd ? backmatterEnd - 1 : tr.doc.content.size,
303
+ createBibliographySection(node)
304
+ )
305
+ )
306
+ }
307
+
308
+ return false
309
+ }
310
+ }
311
+
312
+ export default createEditableNodeView(CitationEditableView)
@@ -0,0 +1,341 @@
1
+ /*!
2
+ * © 2023 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { ContextMenu, ContextMenuProps } from '@manuscripts/style-guide'
18
+ import {
19
+ addTrackChangesAttributes,
20
+ isDeleted,
21
+ } from '@manuscripts/track-changes-plugin'
22
+ import { ContributorsNode, schema } from '@manuscripts/transform'
23
+
24
+ import { NodeSelection } from 'prosemirror-state'
25
+
26
+ import {
27
+ AuthorsAndAffiliationsModals,
28
+ AuthorsAndAffiliationsModalsProps,
29
+ } from '../components/authors-affiliations/AuthorsAndAffiliationsModals'
30
+ import { authorComparator, authorLabel, ContributorAttrs } from '../lib/authors'
31
+ import { handleComment } from '../lib/comments'
32
+ import { createKeyboardInteraction } from '../lib/navigation-utils'
33
+ import { findChildByID, findChildrenAttrsByType } from '../lib/view'
34
+ import { affiliationsKey, PluginState } from '../plugins/affiliations'
35
+ import { selectedSuggestionKey } from '../plugins/selected-suggestion'
36
+ import { Trackable } from '../types'
37
+ import BlockView from './block_view'
38
+ import { createNodeView } from './creators'
39
+ import ReactSubView from './ReactSubView'
40
+ import { ORCIDIcon } from '../icons'
41
+ export class ContributorsView extends BlockView<Trackable<ContributorsNode>> {
42
+ contextMenu: HTMLElement
43
+ container: HTMLElement
44
+ inner: HTMLElement
45
+ popper?: HTMLElement
46
+ version: string
47
+
48
+ public ignoreMutation = () => true
49
+ public stopEvent = () => true
50
+ private removeKeydownListener?: () => void
51
+
52
+ public updateContents() {
53
+ super.updateContents()
54
+ const affs = affiliationsKey.getState(this.view.state)
55
+ if (!affs) {
56
+ return
57
+ }
58
+ if (affs.version === this.version) {
59
+ this.updateSelection()
60
+ return
61
+ }
62
+ this.version = affs.version
63
+ this.container.innerHTML = ''
64
+
65
+ this.buildAuthors(affs)
66
+ this.createLegend()
67
+ this.updateSelection()
68
+ }
69
+
70
+ buildAuthors = (affs: PluginState) => {
71
+ const wrapper = document.createElement('div')
72
+ wrapper.classList.add('contributors-list')
73
+
74
+ const can = this.props.getCapabilities()
75
+ if (can.editMetadata) {
76
+ wrapper.addEventListener('click', this.handleClick)
77
+
78
+ this.removeKeydownListener = createKeyboardInteraction({
79
+ container: wrapper,
80
+ navigation: {
81
+ getItems: () => {
82
+ return Array.from(
83
+ wrapper.querySelectorAll<HTMLElement>('.contributor')
84
+ )
85
+ },
86
+ arrowKeys: { forward: 'ArrowRight', backward: 'ArrowLeft' },
87
+ },
88
+ additionalKeys: {
89
+ Enter: (e) => {
90
+ e.preventDefault()
91
+ this.handleClick(e)
92
+ },
93
+ },
94
+ })
95
+ }
96
+
97
+ const authors = affs.contributors
98
+
99
+ authors.sort(authorComparator).forEach((author, i) => {
100
+ const jointAuthors = this.isJointFirstAuthor(authors, i)
101
+ wrapper.appendChild(this.buildAuthor(author, jointAuthors, i))
102
+ if (i !== authors.length - 1) {
103
+ const separator = document.createElement('span')
104
+ separator.classList.add('separator')
105
+ separator.innerHTML = ','
106
+ wrapper.appendChild(separator)
107
+ }
108
+ })
109
+
110
+ this.container.appendChild(wrapper)
111
+ }
112
+
113
+ buildAuthor = (
114
+ attrs: ContributorAttrs,
115
+ isJointFirstAuthor: boolean,
116
+ index: number
117
+ ) => {
118
+ const state = this.view.state
119
+ const affs = affiliationsKey.getState(state)?.indexedAffiliationIds
120
+
121
+ const container = document.createElement('span')
122
+ container.classList.add('contributor')
123
+ container.setAttribute('id', attrs.id)
124
+ container.setAttribute('contenteditable', 'false')
125
+ container.tabIndex = index === 0 ? 0 : -1
126
+
127
+ addTrackChangesAttributes(attrs, container)
128
+
129
+ const name = authorLabel(attrs)
130
+ container.innerHTML =
131
+ attrs.isCorresponding && attrs.email
132
+ ? `<span class="name">${name} (${attrs.email})</span>`
133
+ : `<span class="name">${name}</span>`
134
+
135
+ if (attrs.ORCID) {
136
+ container.innerHTML += `<a href="${attrs.ORCID}" target="_blank" class="orcid-link">${ORCIDIcon}</span>`
137
+ }
138
+
139
+ const noteText: string[] = []
140
+ if (affs) {
141
+ attrs.affiliationIDs?.map((a) => {
142
+ const index = affs.get(a)
143
+ if (index) {
144
+ noteText.push(index.toString())
145
+ }
146
+ })
147
+ }
148
+
149
+ if (isJointFirstAuthor) {
150
+ container.appendChild(this.createNote('†', 'Joint contributor'))
151
+ }
152
+
153
+ if (noteText) {
154
+ container.appendChild(this.createNote(noteText.join(',')))
155
+ }
156
+
157
+ if (attrs.isCorresponding) {
158
+ container.appendChild(this.createNote('*', 'Corresponding author'))
159
+ }
160
+
161
+ return container
162
+ }
163
+
164
+ private createNote(text = '', title = '') {
165
+ const note = document.createElement('span')
166
+ note.innerHTML = text
167
+ if (title) {
168
+ note.setAttribute('title', title)
169
+ }
170
+ note.classList.add('contributor-note')
171
+ return note
172
+ }
173
+
174
+ private isJointFirstAuthor = (authors: ContributorAttrs[], index: number) => {
175
+ const author = index === 0 ? authors[index] : authors[index - 1]
176
+ return Boolean(author.isJointContributor)
177
+ }
178
+
179
+ public createElement = () => {
180
+ this.inner = document.createElement('div')
181
+ this.inner.classList.add('authors-container', 'block')
182
+ this.dom.appendChild(this.inner)
183
+
184
+ this.container = document.createElement('div')
185
+ this.container.classList.add('contributors')
186
+ this.inner.appendChild(this.container)
187
+ }
188
+
189
+ public createDOM = () => {
190
+ this.dom = document.createElement('div')
191
+ this.dom.classList.add('block-container', `block-${this.node.type.name}`)
192
+ }
193
+
194
+ public authorContextMenu = (): HTMLElement | undefined => {
195
+ const can = this.props.getCapabilities()
196
+ const componentProps: ContextMenuProps = {
197
+ actions: [],
198
+ }
199
+ if (can.editArticle) {
200
+ componentProps.actions.push({
201
+ label: 'Comment',
202
+ action: () => handleComment(this.node, this.view),
203
+ icon: 'AddComment',
204
+ })
205
+ componentProps.actions.push({
206
+ label: 'New Author',
207
+ action: () => this.handleEdit('', true),
208
+ icon: 'AddOutline',
209
+ })
210
+ componentProps.actions.push({
211
+ label: 'Edit',
212
+ action: () => this.handleEdit(''),
213
+ icon: 'Edit',
214
+ })
215
+ }
216
+ this.contextMenu = ReactSubView(
217
+ this.props,
218
+ ContextMenu,
219
+ componentProps,
220
+ this.node,
221
+ this.getPos,
222
+ this.view,
223
+ ['context-menu']
224
+ )
225
+ return this.contextMenu
226
+ }
227
+
228
+ public actionGutterButtons = (): HTMLElement[] => {
229
+ const contextMenu = this.authorContextMenu()
230
+ return contextMenu ? [contextMenu] : []
231
+ }
232
+
233
+ createLegend = () => {
234
+ const state = affiliationsKey.getState(this.view.state)
235
+ if (state?.contributors) {
236
+ const isThereJointContributor = state.contributors.find(
237
+ (attrs) => attrs.isJointContributor
238
+ )
239
+ if (isThereJointContributor) {
240
+ const element = document.createElement('p')
241
+ element.classList.add('contributors-legend')
242
+ element.innerHTML =
243
+ '<span class="symbol">†</span>These authors contributed equally to this work.'
244
+ this.container.appendChild(element)
245
+ }
246
+ }
247
+ }
248
+ private handleClick = (event: Event) => {
249
+ this.props.popper.destroy()
250
+ const element = event.target as HTMLElement
251
+ const author = element.closest('.contributor')
252
+ if (!author) {
253
+ return
254
+ }
255
+ const { node, pos } = findChildByID(this.view, author.id) || {}
256
+ if (!node || !pos) {
257
+ return
258
+ }
259
+ if (!isDeleted(node)) {
260
+ this.showContextMenu(author)
261
+ }
262
+ const view = this.view
263
+ const tr = view.state.tr
264
+ tr.setSelection(NodeSelection.create(view.state.doc, pos))
265
+ view.dispatch(tr)
266
+ }
267
+
268
+ private updateSelection = () => {
269
+ const state = this.view.state
270
+ const selection = selectedSuggestionKey.getState(state)?.suggestion
271
+ this.container
272
+ .querySelectorAll('.selected-suggestion')
273
+ .forEach((e) => e.classList.remove('selected-suggestion'))
274
+ if (selection) {
275
+ const item = this.container.querySelector(
276
+ `[data-track-id="${selection.id}"]`
277
+ )
278
+ item?.classList.add('selected-suggestion')
279
+ }
280
+ }
281
+
282
+ public showContextMenu = (element: Element) => {
283
+ this.props.popper.destroy() // destroy the old context menu
284
+
285
+ const componentProps: ContextMenuProps = {
286
+ actions: [
287
+ {
288
+ label: 'Edit',
289
+ action: () => this.handleEdit(element.id),
290
+ icon: 'Edit',
291
+ },
292
+ ],
293
+ }
294
+ this.contextMenu = ReactSubView(
295
+ this.props,
296
+ ContextMenu,
297
+ componentProps,
298
+ this.node,
299
+ this.getPos,
300
+ this.view,
301
+ ['context-menu']
302
+ )
303
+ this.props.popper.show(element, this.contextMenu, 'right-start')
304
+ }
305
+
306
+ handleEdit = (id: string, addNew?: boolean) => {
307
+ this.props.popper.destroy()
308
+
309
+ const contributors = findChildrenAttrsByType<ContributorAttrs>(
310
+ this.view,
311
+ schema.nodes.contributor
312
+ )
313
+ const author = id ? contributors.filter((a) => a.id === id)[0] : undefined
314
+ const componentProps: AuthorsAndAffiliationsModalsProps = {
315
+ initialModal: 'authors',
316
+ view: this.view,
317
+ author,
318
+ addNewAuthor: addNew,
319
+ }
320
+
321
+ this.popper?.remove()
322
+
323
+ this.popper = ReactSubView(
324
+ this.props,
325
+ AuthorsAndAffiliationsModals,
326
+ componentProps,
327
+ this.node,
328
+ this.getPos,
329
+ this.view
330
+ )
331
+
332
+ this.container.appendChild(this.popper)
333
+ }
334
+
335
+ public destroy() {
336
+ this.removeKeydownListener?.()
337
+ super.destroy()
338
+ }
339
+ }
340
+
341
+ export default createNodeView(ContributorsView)