@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,254 @@
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 { AffiliationNode, schema } from '@manuscripts/transform'
23
+ import { NodeSelection } from 'prosemirror-state'
24
+
25
+ import {
26
+ AuthorsAndAffiliationsModals,
27
+ AuthorsAndAffiliationsModalsProps,
28
+ } from '../components/authors-affiliations/AuthorsAndAffiliationsModals'
29
+ import { alertIcon } from '../icons'
30
+ import { AffiliationAttrs, affiliationName } from '../lib/authors'
31
+ import { handleComment } from '../lib/comments'
32
+
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
+
41
+ //todo update AffiliationNode to AffiliationsNode
42
+ export class AffiliationsView extends BlockView<Trackable<AffiliationNode>> {
43
+ contextMenu: HTMLElement
44
+ version: string
45
+ container: HTMLElement
46
+ popper?: HTMLElement
47
+
48
+ public ignoreMutation = () => true
49
+ public stopEvent = () => true
50
+
51
+ public createElement() {
52
+ this.container = document.createElement('div')
53
+ this.container.classList.add('affiliations', 'block')
54
+ this.container.contentEditable = 'false'
55
+ this.container.addEventListener('click', this.handleClick)
56
+ this.dom.setAttribute('contenteditable', 'false')
57
+ this.dom.appendChild(this.container)
58
+ }
59
+
60
+ public updateContents() {
61
+ super.updateContents()
62
+ const affs = affiliationsKey.getState(this.view.state)
63
+
64
+ if (!affs) {
65
+ return
66
+ }
67
+ if (this.version === affs.version) {
68
+ this.updateSelection()
69
+ return
70
+ }
71
+ this.version = affs.version
72
+ this.container.innerHTML = ''
73
+ this.buildAffiliations(affs)
74
+ this.updateSelection()
75
+ }
76
+
77
+ private buildAffiliations(affs: PluginState) {
78
+ const elements = []
79
+ for (const affiliation of affs.affiliations) {
80
+ const index = affs.indexedAffiliationIds.get(affiliation.id)
81
+ elements.push({
82
+ index,
83
+ element: this.buildAffiliation(affiliation, index),
84
+ })
85
+ }
86
+
87
+ elements
88
+ .sort(this.sortAffiliations)
89
+ .forEach((e) => this.container.appendChild(e.element))
90
+ }
91
+
92
+ private buildAffiliation(attrs: AffiliationAttrs, index?: number) {
93
+ const element = document.createElement('div')
94
+ element.classList.add('affiliation')
95
+ element.id = attrs.id
96
+ addTrackChangesAttributes(attrs, element)
97
+
98
+ const marker = document.createElement('span')
99
+ if (index) {
100
+ marker.classList.add('affiliation-label')
101
+ marker.innerText = String(index)
102
+ } else {
103
+ marker.innerHTML = alertIcon
104
+ }
105
+ element.appendChild(marker)
106
+
107
+ const name = document.createElement('span')
108
+ name.classList.add('affiliation-name')
109
+ name.innerHTML = affiliationName(attrs)
110
+ element.appendChild(name)
111
+ return element
112
+ }
113
+
114
+ private sortAffiliations(aff1: { index?: number }, aff2: { index?: number }) {
115
+ const index1 = aff1.index || 10000
116
+ const index2 = aff2.index || 10000
117
+ return index1 - index2
118
+ }
119
+
120
+ private handleClick = (event: Event) => {
121
+ const element = event.target as HTMLElement
122
+ const affiliation = element.closest('.affiliation')
123
+ if (!affiliation) {
124
+ return
125
+ }
126
+ const { node, pos } = findChildByID(this.view, affiliation.id) || {}
127
+ if (!node || !pos) {
128
+ return
129
+ }
130
+ if (!isDeleted(node)) {
131
+ this.showContextMenu(affiliation)
132
+ }
133
+
134
+ const view = this.view
135
+ const tr = view.state.tr
136
+ tr.setSelection(NodeSelection.create(view.state.doc, pos))
137
+ view.dispatch(tr)
138
+ }
139
+
140
+ private updateSelection() {
141
+ const state = this.view.state
142
+ const selection = selectedSuggestionKey.getState(state)?.suggestion
143
+ this.container
144
+ .querySelectorAll('.selected-suggestion')
145
+ .forEach((e) => e.classList.remove('selected-suggestion'))
146
+ if (selection) {
147
+ const item = this.container.querySelector(
148
+ `[data-track-id="${selection.id}"]`
149
+ )
150
+ item?.classList.add('selected-suggestion')
151
+ }
152
+ }
153
+
154
+ handleEdit = (id: string, addNew?: boolean) => {
155
+ this.props.popper.destroy()
156
+
157
+ const affiliations = findChildrenAttrsByType<AffiliationAttrs>(
158
+ this.view,
159
+ schema.nodes.affiliation
160
+ )
161
+ const affiliation = id ? affiliations.find((a) => a.id === id) : undefined
162
+ const componentProps: AuthorsAndAffiliationsModalsProps = {
163
+ initialModal: 'affiliations',
164
+ view: this.view,
165
+ affiliation,
166
+ addNewAffiliation: addNew,
167
+ }
168
+
169
+ this.popper?.remove()
170
+
171
+ this.popper = ReactSubView(
172
+ this.props,
173
+ AuthorsAndAffiliationsModals,
174
+ componentProps,
175
+ this.node,
176
+ this.getPos,
177
+ this.view
178
+ )
179
+ this.container.appendChild(this.popper)
180
+ }
181
+
182
+ public showGroupContextMenu = (): HTMLElement | undefined => {
183
+ const can = this.props.getCapabilities()
184
+ const componentProps: ContextMenuProps = {
185
+ actions: [],
186
+ }
187
+
188
+ if (can.editArticle) {
189
+ componentProps.actions.push({
190
+ label: 'Comment',
191
+ action: () => handleComment(this.node, this.view),
192
+ icon: 'AddComment',
193
+ })
194
+ componentProps.actions.push({
195
+ label: 'New Affiliation',
196
+ action: () => this.handleEdit('', true),
197
+ icon: 'AddOutline',
198
+ })
199
+ componentProps.actions.push({
200
+ label: 'Edit',
201
+ action: () => this.handleEdit(''),
202
+ icon: 'Edit',
203
+ })
204
+
205
+ this.contextMenu = ReactSubView(
206
+ this.props,
207
+ ContextMenu,
208
+ componentProps,
209
+ this.node,
210
+ this.getPos,
211
+ this.view,
212
+ ['context-menu']
213
+ )
214
+ return this.contextMenu
215
+ }
216
+
217
+ return undefined
218
+ }
219
+
220
+ public showContextMenu = (element: Element) => {
221
+ const affiliationNameBlock = element.querySelector('.affiliation-name')
222
+ this.props.popper.destroy() // destroy the old context menu
223
+ const componentProps: ContextMenuProps = {
224
+ actions: [
225
+ {
226
+ label: 'Edit',
227
+ action: () => this.handleEdit(element.id),
228
+ icon: 'Edit',
229
+ },
230
+ ],
231
+ }
232
+ this.contextMenu = ReactSubView(
233
+ this.props,
234
+ ContextMenu,
235
+ componentProps,
236
+ this.node,
237
+ this.getPos,
238
+ this.view,
239
+ ['context-menu']
240
+ )
241
+ this.props.popper.show(
242
+ affiliationNameBlock || element,
243
+ this.contextMenu,
244
+ 'right-start'
245
+ )
246
+ }
247
+
248
+ public actionGutterButtons = (): HTMLElement[] => {
249
+ const contextMenu = this.showGroupContextMenu()
250
+ return contextMenu ? [contextMenu] : []
251
+ }
252
+ }
253
+
254
+ export default createNodeView(AffiliationsView)
@@ -0,0 +1,85 @@
1
+ /*!
2
+ * © 2025 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 { AltTitleNode, ManuscriptNodeView } from '@manuscripts/transform'
18
+ import { TextSelection } from 'prosemirror-state'
19
+
20
+ import { createKeyboardInteraction } from '../lib/navigation-utils'
21
+ import { BaseNodeView } from './base_node_view'
22
+ import { createNodeView } from './creators'
23
+
24
+ export class AltTitleView
25
+ extends BaseNodeView<AltTitleNode>
26
+ implements ManuscriptNodeView
27
+ {
28
+ public contentDOM: HTMLElement
29
+ private removeKeydownListener?: () => void
30
+
31
+ public initialise = () => {
32
+ this.createDOM()
33
+ }
34
+
35
+ protected createDOM = () => {
36
+ this.dom = document.createElement('div')
37
+ this.dom.classList.add('manuscript-alt-title')
38
+ const label = document.createElement('div')
39
+ label.classList.add('alt-title-label')
40
+ label.innerHTML = this.node.attrs.type + ' title'
41
+ label.contentEditable = 'false'
42
+ this.dom.setAttribute('data-type', this.node.attrs.type)
43
+ this.contentDOM = document.createElement('div')
44
+ this.contentDOM.classList.add('alt-title-text')
45
+ this.contentDOM.tabIndex = this.node.attrs.type === 'running' ? 0 : -1
46
+
47
+ this.removeKeydownListener = createKeyboardInteraction({
48
+ container: this.contentDOM,
49
+ navigation: {
50
+ getItems: () => {
51
+ const allAltTitles = Array.from(
52
+ this.view.dom.querySelectorAll<HTMLElement>('.alt-title-text')
53
+ )
54
+ return allAltTitles
55
+ },
56
+ arrowKeys: { forward: 'ArrowDown', backward: 'ArrowUp' },
57
+ getCurrentElement: () => this.contentDOM,
58
+ },
59
+ additionalKeys: {
60
+ Enter: (e) => {
61
+ e.preventDefault()
62
+ const pos = this.getPos()
63
+ if (typeof pos === 'number') {
64
+ const tr = this.view.state.tr.setSelection(
65
+ TextSelection.create(this.view.state.doc, pos + 1)
66
+ )
67
+ this.view.dispatch(tr)
68
+ this.view.focus()
69
+ }
70
+ },
71
+ },
72
+ })
73
+
74
+ this.dom.appendChild(label)
75
+ this.dom.appendChild(this.contentDOM)
76
+ this.updateContents()
77
+ }
78
+
79
+ public destroy() {
80
+ this.removeKeydownListener?.()
81
+ super.destroy()
82
+ }
83
+ }
84
+
85
+ export default createNodeView(AltTitleView)
@@ -0,0 +1,97 @@
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 { AltTitlesSectionNode } from '@manuscripts/transform'
18
+ import { TextSelection } from 'prosemirror-state'
19
+ import { findChildrenByType } from 'prosemirror-utils'
20
+
21
+ import { arrowDown } from '../icons'
22
+ import { altTitlesKey } from '../plugins/alt-titles'
23
+ import { Trackable } from '../types'
24
+ import BlockView from './block_view'
25
+ import { createNodeView } from './creators'
26
+ import { handleEnterKey } from '../lib/navigation-utils'
27
+
28
+ export class AltTitleSectionView extends BlockView<
29
+ Trackable<AltTitlesSectionNode>
30
+ > {
31
+ private container: HTMLElement
32
+
33
+ public ignoreMutation = () => true
34
+
35
+ public createElement = () => {
36
+ this.container = document.createElement('div')
37
+ this.container.classList.add('block')
38
+ this.dom.appendChild(this.container)
39
+
40
+ // figure group
41
+ this.contentDOM = document.createElement('div')
42
+ this.contentDOM.classList.add('alt-titles-section')
43
+ this.contentDOM.setAttribute('id', this.node.attrs.id)
44
+ this.container.appendChild(this.contentDOM)
45
+ this.container.appendChild(this.createClosingPanel())
46
+ }
47
+
48
+ createClosingPanel() {
49
+ const closingPanel = document.createElement('div')
50
+ closingPanel.classList.add('alt-titles-closing-panel')
51
+ const button = document.createElement('button')
52
+ button.classList.add('alt-titles-closing-button', 'button-reset')
53
+ button.setAttribute('aria-label', 'Collapse alternative titles')
54
+
55
+ button.innerHTML = arrowDown
56
+ const handleCollapse = () => {
57
+ const tr = this.view.state.tr.setMeta(altTitlesKey, {
58
+ collapsed: true,
59
+ })
60
+ const titleNode = findChildrenByType(
61
+ this.view.state.doc,
62
+ this.view.state.schema.nodes.title
63
+ )[0]
64
+ const subtitleNode = findChildrenByType(
65
+ this.view.state.doc,
66
+ this.view.state.schema.nodes.subtitle
67
+ )[0]
68
+
69
+ const prev = subtitleNode || titleNode
70
+ if (prev) {
71
+ const titleEndPos = prev.pos + prev.node.nodeSize
72
+ tr.setSelection(TextSelection.create(tr.doc, titleEndPos))
73
+ }
74
+ this.view.dispatch(tr)
75
+
76
+ // Focus the toggle button after collapse
77
+ const toggleButton = this.view.dom.querySelector(
78
+ '.toggle-button-open'
79
+ ) as HTMLElement
80
+ toggleButton?.focus()
81
+ }
82
+
83
+ button.addEventListener('click', (e) => {
84
+ e.preventDefault()
85
+ handleCollapse()
86
+ })
87
+
88
+ button.addEventListener(
89
+ 'keydown',
90
+ handleEnterKey(() => handleCollapse())
91
+ )
92
+ closingPanel.appendChild(button)
93
+ return closingPanel
94
+ }
95
+ }
96
+
97
+ export default createNodeView(AltTitleSectionView)
@@ -0,0 +1,160 @@
1
+ /*!
2
+ * © 2025 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 { AttachmentNode } from '@manuscripts/transform'
18
+
19
+ import { fileMainDocumentIcon } from '../icons'
20
+ import { FileAttachment } from '../lib/files'
21
+ import { Trackable } from '../types'
22
+ import BlockView from './block_view'
23
+ import { createNodeView } from './creators'
24
+
25
+ // Extended file type that includes properties from the article editor
26
+ interface ExtendedFileAttachment extends FileAttachment {
27
+ type?: string
28
+ }
29
+
30
+ export class AttachmentView extends BlockView<Trackable<AttachmentNode>> {
31
+ private container: HTMLElement
32
+
33
+ public createElement = () => {
34
+ this.createContainer()
35
+ this.renderView()
36
+ }
37
+
38
+ public updateContents() {
39
+ this.renderView()
40
+ }
41
+
42
+ private renderView() {
43
+ this.container.innerHTML = ''
44
+
45
+ const file = this.getFileFromAttachment()
46
+ if (!file) {
47
+ return
48
+ }
49
+
50
+ if (this.isPDF(file)) {
51
+ this.createPDFPreview(file)
52
+ }
53
+ }
54
+
55
+ private createContainer() {
56
+ this.container = document.createElement('div')
57
+ this.container.classList.add('attachment-item')
58
+ this.container.addEventListener('click', (e) => {
59
+ e.stopPropagation()
60
+ this.setMainDocumentSelection()
61
+ })
62
+ this.dom.appendChild(this.container)
63
+ }
64
+
65
+ private isPDF(file: ExtendedFileAttachment) {
66
+ return file.name?.toLowerCase().endsWith('.pdf') ?? false
67
+ }
68
+
69
+ private getFileFromAttachment() {
70
+ const { href } = this.node.attrs
71
+ if (!href) {
72
+ return null
73
+ }
74
+
75
+ const files = this.props?.getFiles?.() || []
76
+ return files.find((f: ExtendedFileAttachment) => f.id === href) || null
77
+ }
78
+
79
+ private createPDFPreview(file: ExtendedFileAttachment) {
80
+ this.container.setAttribute('data-pdf-preview', file.id)
81
+
82
+ const header = this.createHeader(file)
83
+ const content = this.createContent(file)
84
+
85
+ this.container.appendChild(header)
86
+ this.container.appendChild(content)
87
+ }
88
+
89
+ private createHeader(file: ExtendedFileAttachment) {
90
+ const header = document.createElement('div')
91
+ header.className = 'attachment-header'
92
+
93
+ const icon = this.createIcon()
94
+ const name = this.createFileName(file.name)
95
+
96
+ header.append(icon, name)
97
+ return header
98
+ }
99
+
100
+ private createIcon() {
101
+ const icon = document.createElement('span')
102
+ icon.className = 'attachment-icon'
103
+ icon.innerHTML = fileMainDocumentIcon
104
+ return icon
105
+ }
106
+
107
+ private createFileName(fileName: string) {
108
+ const name = document.createElement('span')
109
+ Object.assign(name, {
110
+ textContent: fileName,
111
+ className: 'attachment-name',
112
+ title: fileName,
113
+ })
114
+ return name
115
+ }
116
+
117
+ private createContent(file: ExtendedFileAttachment) {
118
+ const content = document.createElement('div')
119
+ content.className = 'attachment-content'
120
+
121
+ const embed = document.createElement('embed')
122
+ Object.assign(embed, {
123
+ src: this.getPDFUrl(file),
124
+ type: 'application/pdf',
125
+ className: 'attachment-pdf',
126
+ height: '400px',
127
+ width: '100%',
128
+ })
129
+ embed.style.border = 'none'
130
+
131
+ content.appendChild(embed)
132
+ return content
133
+ }
134
+
135
+ private getPDFUrl(file: ExtendedFileAttachment) {
136
+ const baseUrl = file.link || file.id || '#'
137
+
138
+ if (baseUrl && baseUrl !== '#') {
139
+ const separator = baseUrl.includes('?') ? '&' : '?'
140
+ return `${baseUrl}${separator}inline=true`
141
+ }
142
+
143
+ return baseUrl
144
+ }
145
+
146
+ private setMainDocumentSelection() {
147
+ if (this.props.onEditorClick) {
148
+ const event = {
149
+ target: { dataset: { action: 'select-main-document' } },
150
+ stopPropagation: () => {
151
+ // Prevent event bubbling - no-op
152
+ },
153
+ } as unknown as MouseEvent
154
+
155
+ this.props.onEditorClick(event)
156
+ }
157
+ }
158
+ }
159
+
160
+ export default createNodeView(AttachmentView)
@@ -0,0 +1,72 @@
1
+ /*!
2
+ * © 2025 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 { ManuscriptNode } from '@manuscripts/transform'
18
+
19
+ import { arrowUp } from '../icons'
20
+ import { Trackable } from '../types'
21
+ import BlockView from './block_view'
22
+ import { createNodeView } from './creators'
23
+
24
+ export class AttachmentsView extends BlockView<Trackable<ManuscriptNode>> {
25
+ private container: HTMLElement
26
+ private collapsed = false
27
+
28
+ public ignoreMutation = () => true
29
+
30
+ public createElement = () => {
31
+ this.container = document.createElement('div')
32
+ this.container.classList.add('block', 'attachments-container')
33
+ this.dom.appendChild(this.container)
34
+
35
+ this.container.appendChild(this.createPanel())
36
+
37
+ const content = document.createElement('div')
38
+ content.className = 'attachments-content'
39
+
40
+ this.container.appendChild(content)
41
+ this.contentDOM = content
42
+ }
43
+
44
+ createPanel() {
45
+ const panel = document.createElement('div')
46
+ panel.classList.add('panel-header')
47
+
48
+ const label = document.createElement('span')
49
+ label.textContent = 'Main document'
50
+ label.contentEditable = 'false'
51
+
52
+ const toggleBtn = document.createElement('button')
53
+ toggleBtn.classList.add('toggle-btn', 'button-reset')
54
+
55
+ toggleBtn.innerHTML = arrowUp
56
+ toggleBtn.classList.toggle('collapsed', this.collapsed)
57
+
58
+ toggleBtn.onclick = () => {
59
+ this.collapsed = !this.collapsed
60
+ if (this.contentDOM) {
61
+ this.contentDOM.style.display = this.collapsed ? 'none' : ''
62
+ }
63
+ toggleBtn.classList.toggle('collapsed', this.collapsed)
64
+ }
65
+
66
+ panel.appendChild(label)
67
+ panel.appendChild(toggleBtn)
68
+ return panel
69
+ }
70
+ }
71
+
72
+ export default createNodeView(AttachmentsView)