@manuscripts/body-editor 3.12.67 → 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,373 @@
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 { ContextMenu } from '@manuscripts/style-guide'
18
+ import { FigureNode, ImageElementNode, schema } from '@manuscripts/transform'
19
+
20
+ import { deleteIcon, linkIcon } from '../icons'
21
+ import {
22
+ addInteractionHandlers,
23
+ createMediaPlaceholder,
24
+ MediaType,
25
+ } from '../lib/media'
26
+ import { handleEnterKey } from '../lib/navigation-utils'
27
+ import { createPositionMenuWrapper } from '../lib/position-menu'
28
+ import { Trackable } from '../types'
29
+ import BlockView from './block_view'
30
+ import { createNodeView } from './creators'
31
+ import ReactSubView from './ReactSubView'
32
+
33
+ export enum figurePositions {
34
+ left = 'half-left',
35
+ right = 'half-right',
36
+ default = '',
37
+ }
38
+
39
+ export class ImageElementView extends BlockView<Trackable<ImageElementNode>> {
40
+ public container: HTMLElement
41
+ public subcontainer: HTMLElement
42
+ public extLinkEditorContainer: HTMLDivElement
43
+ private positionMenuWrapper: HTMLDivElement
44
+ private figurePosition: string
45
+ private isEditingExtLink = false
46
+
47
+ public ignoreMutation = () => true
48
+
49
+ upload = async (file: File) => {
50
+ const result = await this.props.fileManagement.upload(file)
51
+ if (!result) {
52
+ return
53
+ }
54
+ this.setIsEditingExtLink(false)
55
+ this.setExtLink({ extLink: result.id })
56
+ this.updateContents()
57
+ }
58
+
59
+ public initialise() {
60
+ super.initialise()
61
+ this.upload = this.upload.bind(this)
62
+ }
63
+
64
+ public createDOM() {
65
+ super.createDOM()
66
+ }
67
+
68
+ public createElement() {
69
+ this.container = document.createElement('div')
70
+ this.container.classList.add('block')
71
+ this.dom.appendChild(this.container)
72
+
73
+ // figure group
74
+ this.subcontainer = document.createElement('div')
75
+ this.subcontainer.classList.add('figure-block-group')
76
+
77
+ this.contentDOM = document.createElement('div')
78
+ this.contentDOM.setAttribute('id', this.node.attrs.id)
79
+ this.contentDOM.classList.add('figure-block')
80
+
81
+ this.subcontainer.appendChild(this.contentDOM)
82
+ this.container.appendChild(this.subcontainer)
83
+ this.addTools()
84
+ }
85
+
86
+ public updateContents() {
87
+ super.updateContents()
88
+ this.addTools()
89
+ this.addExternalLinkedFileEditor()
90
+ }
91
+
92
+ protected addTools() {
93
+ this.addPositionMenu()
94
+ }
95
+
96
+ protected addPositionMenu() {
97
+ if (this.props.getCapabilities()?.editArticle) {
98
+ // Remove existing position menu if it exists
99
+ const existingMenu = this.container.querySelector('.position-menu')
100
+ if (existingMenu) {
101
+ existingMenu.remove()
102
+ }
103
+
104
+ const firstFigure = this.getFirstFigure()
105
+ this.figurePosition = firstFigure?.attrs.type || figurePositions.default
106
+
107
+ this.positionMenuWrapper = createPositionMenuWrapper(
108
+ this.figurePosition,
109
+ this.showPositionMenu,
110
+ this.props
111
+ )
112
+
113
+ this.container.prepend(this.positionMenuWrapper)
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Gets the first figure node from the element.
119
+ * Optimized for image_element (direct access) vs figure_element (loop through all).
120
+ * @returns The first figure node or null if no figure exists
121
+ */
122
+ private getFirstFigure(): FigureNode | null {
123
+ // Optimize for image_element which only has one figure
124
+ if (this.node.type === schema.nodes.image_element) {
125
+ // Direct access to the single figure in image_element
126
+ const figureNode = this.node.firstChild
127
+ if (figureNode && figureNode.type === schema.nodes.figure) {
128
+ return figureNode as FigureNode
129
+ }
130
+ return null
131
+ } else {
132
+ // For figure_element, find the first figure
133
+ let firstFigure: FigureNode | null = null
134
+ this.node.forEach((node) => {
135
+ if (node.type === schema.nodes.figure && !firstFigure) {
136
+ firstFigure = node as FigureNode
137
+ }
138
+ })
139
+ return firstFigure
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Gets all figure nodes and their positions from the element.
145
+ * Optimized for image_element (direct access) vs figure_element (loop through all).
146
+ * @returns Array of figure nodes with their positions
147
+ */
148
+ private getAllFigures() {
149
+ const figures: Array<{ node: FigureNode; pos: number }> = []
150
+ let pos = this.getPos() + 1
151
+
152
+ if (this.node.type === schema.nodes.image_element) {
153
+ const figureNode = this.node.firstChild
154
+ if (figureNode && figureNode.type === schema.nodes.figure) {
155
+ figures.push({ node: figureNode as FigureNode, pos })
156
+ }
157
+ } else {
158
+ this.node.forEach((node) => {
159
+ if (node.type === schema.nodes.figure) {
160
+ figures.push({ node: node as FigureNode, pos })
161
+ }
162
+ pos += node.nodeSize
163
+ })
164
+ }
165
+
166
+ return figures
167
+ }
168
+
169
+ private showPositionMenu = () => {
170
+ const firstFigure = this.getFirstFigure()
171
+ if (!firstFigure) {
172
+ return
173
+ }
174
+
175
+ this.props.popper.destroy()
176
+
177
+ const options = [
178
+ {
179
+ label: 'Left',
180
+ action: () => {
181
+ this.props.popper.destroy()
182
+ this.updateAllFiguresPosition(figurePositions.left)
183
+ },
184
+ icon: 'ImageLeft',
185
+ selected: this.figurePosition === figurePositions.left,
186
+ },
187
+ {
188
+ label: 'Default',
189
+ action: () => {
190
+ this.props.popper.destroy()
191
+ this.updateAllFiguresPosition(figurePositions.default)
192
+ },
193
+ icon: 'ImageDefault',
194
+ selected: !this.figurePosition,
195
+ },
196
+ {
197
+ label: 'Right',
198
+ action: () => {
199
+ this.props.popper.destroy()
200
+ this.updateAllFiguresPosition(figurePositions.right)
201
+ },
202
+ icon: 'ImageRight',
203
+ selected: this.figurePosition === figurePositions.right,
204
+ },
205
+ ]
206
+
207
+ const componentProps = {
208
+ actions: options,
209
+ }
210
+
211
+ this.props.popper.show(
212
+ this.positionMenuWrapper,
213
+ ReactSubView(
214
+ this.props,
215
+ ContextMenu,
216
+ componentProps,
217
+ this.node,
218
+ this.getPos,
219
+ this.view,
220
+ ['context-menu', 'position-menu']
221
+ ),
222
+ 'left',
223
+ false
224
+ )
225
+ }
226
+
227
+ /**
228
+ * Updates the position type for all figures within this element.
229
+ * This affects all figures in figure_element, or the single figure in image_element.
230
+ * @param position - The new position type ('half-left', 'half-right', or '')
231
+ */
232
+ private updateAllFiguresPosition(position: string) {
233
+ const figures = this.getAllFigures()
234
+ const { tr } = this.view.state
235
+
236
+ figures.forEach(({ node, pos }) => {
237
+ tr.setNodeMarkup(pos, undefined, {
238
+ ...node.attrs,
239
+ type: position,
240
+ })
241
+ })
242
+
243
+ this.view.dispatch(tr)
244
+ this.figurePosition = position
245
+ }
246
+
247
+ // external link editor
248
+ private removeExtLink = () => {
249
+ this.setExtLink({ extLink: '' })
250
+ }
251
+
252
+ private setExtLink = (newAttrs: { extLink: string }) => {
253
+ this.setIsEditingExtLink(false)
254
+ const { tr } = this.view.state
255
+ const pos = this.getPos()
256
+ tr.setNodeMarkup(pos, undefined, {
257
+ ...this.node.attrs,
258
+ ...newAttrs,
259
+ })
260
+
261
+ this.view.dispatch(tr)
262
+ }
263
+
264
+ private setIsEditingExtLink = (val: boolean) => {
265
+ this.isEditingExtLink = val
266
+ }
267
+
268
+ private createDnDPlaceholder() {
269
+ const can = this.props.getCapabilities()
270
+ const label = document.createElement('div')
271
+ label.classList.add('accessibility_element_label')
272
+ label.innerText = 'Link'
273
+ const container = document.createElement('div')
274
+ container.classList.add('ext-link-editor-placeholder-container')
275
+
276
+ const placeholder = createMediaPlaceholder(
277
+ MediaType.ExternalLink,
278
+ this.view,
279
+ this.getPos,
280
+ this.props
281
+ )
282
+
283
+ const closeButton = document.createElement('button')
284
+ closeButton.classList.add('close-button')
285
+ closeButton.setAttribute('aria-label', 'Close')
286
+ const handleClose = () => {
287
+ this.setIsEditingExtLink(false)
288
+ this.updateContents()
289
+ }
290
+ closeButton.addEventListener('click', handleClose)
291
+ closeButton.addEventListener('keydown', handleEnterKey(handleClose))
292
+
293
+ container.append(placeholder, closeButton)
294
+ this.extLinkEditorContainer.append(label, container)
295
+ if (can.uploadFile) {
296
+ addInteractionHandlers(placeholder, this.upload, '*/*')
297
+ }
298
+ }
299
+
300
+ private createAddLinkButton() {
301
+ const button = document.createElement('div')
302
+ button.innerHTML = linkIcon
303
+ const buttonText = document.createElement('span')
304
+ buttonText.textContent = 'Add link'
305
+ button.appendChild(buttonText)
306
+ button.setAttribute('aria-label', 'Add linked file')
307
+ button.classList.add('icon-button')
308
+ button.tabIndex = 0
309
+ const handleAdd = () => {
310
+ this.setIsEditingExtLink(true)
311
+ this.updateContents()
312
+ }
313
+ button.addEventListener('click', handleAdd)
314
+ button.addEventListener('keydown', handleEnterKey(handleAdd))
315
+ this.extLinkEditorContainer.appendChild(button)
316
+ }
317
+
318
+ private createLinkedFile() {
319
+ const extLink = this.node.attrs.extLink
320
+ const files = this.props.getFiles()
321
+ const file = extLink ? files.find((f) => f.id === extLink) : undefined
322
+
323
+ const fileName = file ? `${file.name.trim()}` : 'File does not exist.'
324
+ const div = document.createElement('div')
325
+ div.classList.add('linked-file-info')
326
+ div.innerHTML = `<p>${linkIcon} <span>${fileName}</span></p>`
327
+
328
+ const removeButton = document.createElement('button')
329
+ removeButton.classList.add('icon-button', 'remove-button')
330
+ removeButton.setAttribute('aria-label', 'Remove link')
331
+ removeButton.innerHTML = deleteIcon
332
+ const handleRemove = () => {
333
+ this.isEditingExtLink = false
334
+ this.removeExtLink()
335
+ }
336
+ removeButton.addEventListener('click', handleRemove)
337
+ removeButton.addEventListener('keydown', handleEnterKey(handleRemove))
338
+
339
+ div.appendChild(removeButton)
340
+ this.extLinkEditorContainer.appendChild(div)
341
+ }
342
+
343
+ private addExternalLinkedFileEditor() {
344
+ if (this.node.type === schema.nodes.image_element) {
345
+ this.extLinkEditorContainer = this.container.querySelector(
346
+ '.ext-link-editor-container'
347
+ ) as HTMLDivElement
348
+ if (this.extLinkEditorContainer) {
349
+ this.extLinkEditorContainer.innerHTML = ''
350
+ } else {
351
+ this.extLinkEditorContainer = document.createElement('div')
352
+ this.extLinkEditorContainer.contentEditable = 'false'
353
+ this.extLinkEditorContainer.classList.add('ext-link-editor-container')
354
+ }
355
+ // add link button
356
+ if (!this.isEditingExtLink && !this.node.attrs.extLink) {
357
+ this.createAddLinkButton()
358
+ }
359
+ //dnd placeholder
360
+ if (this.isEditingExtLink && !this.node.attrs.extLink) {
361
+ this.createDnDPlaceholder()
362
+ }
363
+ // linked file exists
364
+ if (this.node.attrs.extLink) {
365
+ this.createLinkedFile()
366
+ }
367
+
368
+ this.subcontainer.appendChild(this.extLinkEditorContainer)
369
+ }
370
+ }
371
+ }
372
+
373
+ export default createNodeView(ImageElementView)
@@ -0,0 +1,21 @@
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 { createEditableNodeView } from './creators'
18
+ import { EditableBlock } from './editable_block'
19
+ import { ImageElementView } from './image_element'
20
+
21
+ export default createEditableNodeView(EditableBlock(ImageElementView))
@@ -0,0 +1,47 @@
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 { InlineEquationNode, ManuscriptNodeView } from '@manuscripts/transform'
18
+
19
+ import { renderMath } from '../lib/math'
20
+ import { Trackable } from '../types'
21
+ import { BaseNodeView } from './base_node_view'
22
+ import { createNodeView } from './creators'
23
+
24
+ export class InlineEquationView
25
+ extends BaseNodeView<Trackable<InlineEquationNode>>
26
+ implements ManuscriptNodeView
27
+ {
28
+ public initialise() {
29
+ this.createDOM()
30
+ this.updateContents()
31
+ }
32
+
33
+ public updateContents() {
34
+ super.updateContents()
35
+ this.dom.innerHTML = this.node.attrs.contents
36
+ renderMath(this.dom)
37
+ }
38
+
39
+ public ignoreMutation = () => true
40
+
41
+ protected createDOM() {
42
+ this.dom = document.createElement('span')
43
+ this.dom.classList.add('equation')
44
+ }
45
+ }
46
+
47
+ export default createNodeView(InlineEquationView)
@@ -0,0 +1,70 @@
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 { NodeSelection } from 'prosemirror-state'
18
+
19
+ import { createEditableNodeView } from './creators'
20
+ import { InlineEquationView } from './inline_equation'
21
+
22
+ export class InlineEquationEditableView extends InlineEquationView {
23
+ public selectNode = async () => {
24
+ if (!this.props.getCapabilities().editArticle) {
25
+ return
26
+ }
27
+
28
+ const { createEditor } = await import('../lib/codemirror')
29
+
30
+ const placeholder = 'Enter LaTeX equation, e.g. "E=mc^2"'
31
+
32
+ const input = await createEditor({
33
+ value: this.node.attrs.contents || '',
34
+ mode: 'stex',
35
+ placeholder,
36
+ autofocus: true,
37
+ extraKeys: {
38
+ Esc: () => {
39
+ this.props.popper.destroy()
40
+ this.view.focus()
41
+ },
42
+ },
43
+ })
44
+
45
+ input.on('changes', async () => {
46
+ const contents = input.getValue()
47
+
48
+ const pos = this.getPos()
49
+
50
+ const { tr } = this.view.state
51
+ const format = contents.includes('mml:math') ? 'mathml' : 'tex'
52
+ tr.setNodeMarkup(pos, undefined, {
53
+ ...this.node.attrs,
54
+ format,
55
+ contents,
56
+ }).setSelection(NodeSelection.create(tr.doc, pos))
57
+
58
+ this.view.dispatch(tr)
59
+ })
60
+
61
+ this.props.popper.show(this.dom, input.getWrapperElement(), 'bottom')
62
+
63
+ window.requestAnimationFrame(() => {
64
+ input.refresh()
65
+ input.focus()
66
+ })
67
+ }
68
+ }
69
+
70
+ export default createEditableNodeView(InlineEquationEditableView)