@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,124 @@
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 {
18
+ IconTextButton,
19
+ TriangleCollapsedIcon,
20
+ TriangleExpandedIcon,
21
+ } from '@manuscripts/style-guide'
22
+ import { BibliographyItemAttrs } from '@manuscripts/transform'
23
+ import React, { useEffect, useState } from 'react'
24
+ import styled from 'styled-components'
25
+
26
+ import {
27
+ BibliographyItemSearch,
28
+ BibliographyItemSource,
29
+ } from './BibliographyItemSource'
30
+ import { ReferenceSearchResults } from './ReferenceSearchResults'
31
+ import { ReferenceSearchResultsPlaceholder } from './ReferenceSearchResultsPlaceholder'
32
+
33
+ const SearchSourceLabel = styled(IconTextButton)`
34
+ margin: ${(props) => props.theme.grid.unit * 2}px
35
+ ${(props) => props.theme.grid.unit * 4}px
36
+ ${(props) => props.theme.grid.unit * 2}px;
37
+ color: ${(props) => props.theme.colors.text.secondary};
38
+
39
+ &:hover {
40
+ color: ${(props) => props.theme.colors.text.muted};
41
+ }
42
+ `
43
+
44
+ type RunningState = {
45
+ type: 'running'
46
+ promise: BibliographyItemSearch
47
+ }
48
+
49
+ type CompletedState = {
50
+ type: 'completed'
51
+ items: BibliographyItemAttrs[]
52
+ total: number
53
+ }
54
+
55
+ type State = RunningState | CompletedState
56
+
57
+ export const ReferenceSearchSection: React.FC<{
58
+ query: string
59
+ source: BibliographyItemSource
60
+ isSelected: (item: BibliographyItemAttrs) => boolean
61
+ onSelect: (item: BibliographyItemAttrs) => void
62
+ }> = ({ query, source, isSelected, onSelect }) => {
63
+ const [expanded, setExpanded] = useState(true)
64
+ const [state, setState] = useState<State>()
65
+ const [limit, setLimit] = useState(3)
66
+
67
+ const toggleExpanded = () => {
68
+ setExpanded((value) => !value)
69
+ }
70
+
71
+ useEffect(() => {
72
+ const search = async () => {
73
+ const promise = source.search(query, limit)
74
+ setState((s) => {
75
+ if (s?.type === 'running') {
76
+ s.promise.controller?.abort()
77
+ }
78
+ return {
79
+ type: 'running',
80
+ promise,
81
+ }
82
+ })
83
+ const response = await promise
84
+ if (promise.controller?.signal.aborted) {
85
+ return
86
+ }
87
+ setState({
88
+ type: 'completed',
89
+ ...response,
90
+ })
91
+ }
92
+ search()
93
+ }, [source, query, limit])
94
+
95
+ const handleShowMore = () => {
96
+ setLimit(25)
97
+ }
98
+
99
+ return (
100
+ <>
101
+ <SearchSourceLabel onClick={toggleExpanded}>
102
+ {expanded ? (
103
+ <TriangleExpandedIcon className={'icon'} />
104
+ ) : (
105
+ <TriangleCollapsedIcon className={'icon'} />
106
+ )}
107
+ {source.label}
108
+ </SearchSourceLabel>
109
+
110
+ {expanded && state?.type === 'running' && (
111
+ <ReferenceSearchResultsPlaceholder />
112
+ )}
113
+ {expanded && state?.type === 'completed' && (
114
+ <ReferenceSearchResults
115
+ items={state.items}
116
+ total={state.total}
117
+ isSelected={isSelected}
118
+ onSelect={onSelect}
119
+ onShowMore={handleShowMore}
120
+ />
121
+ )}
122
+ </>
123
+ )
124
+ }
@@ -0,0 +1,61 @@
1
+ /*!
2
+ * © 2024 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
+ import { BibliographyItemAttrs } from '@manuscripts/transform'
17
+ import React, { useReducer, useState } from 'react'
18
+
19
+ import { attrsReducer } from '../../lib/array-reducer'
20
+ import { cleanItemValues } from '../../lib/utils'
21
+ import { ReferencesModal, ReferencesModalProps } from './ReferencesModal'
22
+
23
+ export type ReferencesEditorProps = Omit<
24
+ ReferencesModalProps,
25
+ 'isOpen' | 'onCancel'
26
+ >
27
+
28
+ const itemsReducer = attrsReducer<BibliographyItemAttrs>()
29
+
30
+ export const ReferencesEditor: React.FC<ReferencesEditorProps> = (props) => {
31
+ const [isOpen, setOpen] = useState(true)
32
+ const [items, dispatch] = useReducer(itemsReducer, props.items)
33
+
34
+ const handleSave = (item: BibliographyItemAttrs) => {
35
+ const cleanedItem = cleanItemValues(item)
36
+ props.onSave(cleanedItem)
37
+ dispatch({
38
+ type: 'update',
39
+ items: [cleanedItem],
40
+ })
41
+ }
42
+
43
+ const handleDelete = (item: BibliographyItemAttrs) => {
44
+ props.onDelete(item)
45
+ dispatch({
46
+ type: 'delete',
47
+ item: item,
48
+ })
49
+ }
50
+
51
+ return (
52
+ <ReferencesModal
53
+ {...props}
54
+ isOpen={isOpen}
55
+ onCancel={() => setOpen(false)}
56
+ items={items}
57
+ onSave={handleSave}
58
+ onDelete={handleDelete}
59
+ />
60
+ )
61
+ }
@@ -0,0 +1,352 @@
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
+ import {
17
+ Category,
18
+ CitationCountIcon,
19
+ CloseButton,
20
+ Dialog,
21
+ ModalBody,
22
+ ModalContainer,
23
+ ModalHeader,
24
+ ModalSidebar,
25
+ ModalSidebarHeader,
26
+ ModalSidebarTitle,
27
+ ScrollableModalContent,
28
+ SidebarContent,
29
+ StyledModal,
30
+ useScrollDetection,
31
+ withListNavigation,
32
+ withNavigableListItem,
33
+ } from '@manuscripts/style-guide'
34
+ import { BibliographyItemAttrs } from '@manuscripts/transform'
35
+ import { isEqual } from 'lodash'
36
+ import React, { useEffect, useRef, useState } from 'react'
37
+ import styled from 'styled-components'
38
+
39
+ import {
40
+ ReferenceForm,
41
+ ReferenceFormActions,
42
+ } from './ReferenceForm/ReferenceForm'
43
+ import { ReferenceLine } from './ReferenceLine'
44
+
45
+ const ReferencesModalContainer = styled(ModalContainer)`
46
+ min-width: 960px;
47
+ `
48
+
49
+ const ReferencesSidebar = styled(ModalSidebar)`
50
+ width: 70%;
51
+ `
52
+
53
+ const ReferencesSidebarContent = styled(SidebarContent)`
54
+ overflow-y: auto;
55
+ `
56
+
57
+ const ReferencesInnerWrapper = withListNavigation(styled.div`
58
+ width: 100%;
59
+ padding: 12px 0;
60
+ `)
61
+
62
+ const ReferenceButton = withNavigableListItem(styled.div`
63
+ cursor: pointer;
64
+ display: flex;
65
+ justify-content: flex-start;
66
+ padding: ${(props) => props.theme.grid.unit * 4}px 0;
67
+ border-top: 1px solid transparent;
68
+ border-bottom: 1px solid transparent;
69
+
70
+ path {
71
+ fill: #c9c9c9;
72
+ }
73
+
74
+ :hover {
75
+ background: ${(props) => props.theme.colors.background.info};
76
+ }
77
+
78
+ &.selected {
79
+ background: ${(props) => props.theme.colors.background.info};
80
+ border-top-color: #bce7f6;
81
+ border-bottom-color: #bce7f6;
82
+ }
83
+
84
+ .tooltip {
85
+ max-width: ${(props) => props.theme.grid.unit * 25}px;
86
+ padding: ${(props) => props.theme.grid.unit * 2}px;
87
+ border-radius: 6px;
88
+ }
89
+ `)
90
+
91
+ const IconContainer = styled.div`
92
+ padding-right: ${(props) => props.theme.grid.unit * 5}px;
93
+ position: relative;
94
+ `
95
+
96
+ const CitationCount = styled.div`
97
+ border-radius: 50%;
98
+ width: 12px;
99
+ height: 12px;
100
+ position: absolute;
101
+ color: #ffffff;
102
+ background-color: #bce7f6;
103
+ text-align: center;
104
+ vertical-align: top;
105
+ top: 0;
106
+ left: 16px;
107
+ font-size: 9px;
108
+
109
+ &.unused {
110
+ background-color: #fe8f1f;
111
+ }
112
+ `
113
+
114
+ const selectionTopOffset = 10 // to be able to place the selected item in the middle and allow for some scroll at the top
115
+ const pageSize = 12
116
+ const topTrigger = 0.2 // says: notify when x% of the offsetHeight remains hidden at the top
117
+ const bottomTrigger = 0.8 // says: notify when x% of the offsetHeight remains hidden at the bottom
118
+ const dropLimit = 36 // basically maximum amount of items that can exist at the same time
119
+
120
+ const normalize = (item: BibliographyItemAttrs) => ({
121
+ id: item.id,
122
+ type: item.type,
123
+ author: item.author || [],
124
+ editor: item.editor || [],
125
+ issued: item.issued,
126
+ ['container-title']: item['container-title'] || '',
127
+ ['collection-title']: item['collection-title'] || '',
128
+ DOI: item.DOI || '',
129
+ URL: item.URL || '',
130
+ volume: item.volume || '',
131
+ issue: item.issue || '',
132
+ supplement: item.supplement || '',
133
+ edition: item.edition || '',
134
+ page: item.page || '',
135
+ ['number-of-pages']: item['number-of-pages'] || '',
136
+ title: item.title || '',
137
+ literal: item.literal || '',
138
+ std: item.std || '',
139
+ publisher: item.publisher || '',
140
+ ['publisher-place']: item['publisher-place'] || '',
141
+ event: item.event || '',
142
+ ['event-place']: item['event-place'] || '',
143
+ ['event-date']: item['event-date'],
144
+ institution: item.institution || '',
145
+ locator: item.locator || '',
146
+ accessed: item.accessed,
147
+ comment: item.comment || '',
148
+ })
149
+
150
+ export interface ReferencesModalProps {
151
+ isOpen: boolean
152
+ onCancel: () => void
153
+ items: BibliographyItemAttrs[]
154
+ item?: BibliographyItemAttrs
155
+ citationCounts: Map<string, number>
156
+ onSave: (item: BibliographyItemAttrs) => void
157
+ onDelete: (item: BibliographyItemAttrs) => void
158
+ }
159
+
160
+ export const ReferencesModal: React.FC<ReferencesModalProps> = ({
161
+ isOpen,
162
+ onCancel,
163
+ items,
164
+ item,
165
+ citationCounts,
166
+ onSave,
167
+ onDelete,
168
+ }) => {
169
+ const [confirm, setConfirm] = useState(false)
170
+ const valuesRef = useRef<BibliographyItemAttrs>(undefined)
171
+
172
+ const [selection, setSelection] = useState<BibliographyItemAttrs>()
173
+ const selectionRef = useRef<HTMLDivElement>(null)
174
+ const isSelected = (item: BibliographyItemAttrs) => {
175
+ return item.id === selection?.id
176
+ }
177
+ const selectionIndex = items.findIndex(isSelected)
178
+
179
+ useEffect(() => {
180
+ setSelection(item)
181
+ }, [item])
182
+
183
+ useEffect(() => {
184
+ setTimeout(() => {
185
+ selectionRef.current?.scrollIntoView({
186
+ block: 'center',
187
+ behavior: 'auto',
188
+ })
189
+ }, 100)
190
+ }, [selectionIndex])
191
+
192
+ const { ref, triggers } = useScrollDetection(topTrigger, bottomTrigger)
193
+
194
+ const [startIndex, setStartIndex] = useState(
195
+ Math.max(0, selectionIndex - selectionTopOffset)
196
+ )
197
+ const [endIndex, setEndIndex] = useState(pageSize)
198
+
199
+ useEffect(() => {
200
+ const base = Math.max(0, selectionIndex - selectionTopOffset)
201
+ setStartIndex(base)
202
+ setEndIndex(Math.min(items.length - 1, base + pageSize))
203
+ }, [selectionIndex, items])
204
+
205
+ useEffect(() => {
206
+ if (triggers.top) {
207
+ const newFirst = Math.max(0, startIndex - pageSize)
208
+ setStartIndex(newFirst)
209
+ setEndIndex(Math.min(newFirst + dropLimit, endIndex))
210
+ }
211
+ if (triggers.bottom) {
212
+ const newLast = Math.min(items.length - 1, endIndex + pageSize)
213
+ setEndIndex(newLast)
214
+ setStartIndex(Math.max(newLast - dropLimit, startIndex))
215
+ }
216
+ // eslint-disable-next-line react-hooks/exhaustive-deps
217
+ }, [triggers, items])
218
+
219
+ const actionsRef = useRef<ReferenceFormActions>(undefined)
220
+
221
+ const reset = () => {
222
+ actionsRef.current?.reset()
223
+ setConfirm(false)
224
+ }
225
+ // Determine if the newly added citation is a new item by checking if it has properties beyond just 'id' and 'type'
226
+ const isNewItem = (
227
+ obj: BibliographyItemAttrs,
228
+ basicProps: string | string[]
229
+ ) => {
230
+ const allKeys = Object.keys(obj)
231
+ const extraKeys = allKeys.filter((key) => !basicProps.includes(key))
232
+ return extraKeys.length > 0
233
+ }
234
+
235
+ const handleSave = (values: BibliographyItemAttrs | undefined) => {
236
+ if (!values || !selection) {
237
+ return
238
+ }
239
+ const item = {
240
+ ...selection,
241
+ ...values,
242
+ }
243
+ // Check if citation count for the new item is undefined, and set it to 1
244
+ const currentCitationCount = citationCounts.get(item.id)
245
+
246
+ if (currentCitationCount === undefined) {
247
+ citationCounts.set(item.id, 1) // update the citation count in the Map
248
+ }
249
+
250
+ onSave(item)
251
+ setSelection(item)
252
+ setConfirm(false)
253
+ }
254
+
255
+ const handleDelete = () => {
256
+ if (!selection) {
257
+ return
258
+ }
259
+ onDelete(selection)
260
+ setSelection(undefined)
261
+ }
262
+
263
+ const handleItemClick = (item: BibliographyItemAttrs) => {
264
+ const values = valuesRef.current
265
+ if (values && selection && !isEqual(values, normalize(selection))) {
266
+ setConfirm(true)
267
+ return
268
+ }
269
+ setSelection(item)
270
+ }
271
+
272
+ const handleChange = (values: BibliographyItemAttrs) => {
273
+ valuesRef.current = values
274
+ }
275
+ if (items.length <= 0) {
276
+ return <></>
277
+ }
278
+
279
+ return (
280
+ <StyledModal isOpen={isOpen} onRequestClose={onCancel}>
281
+ <Dialog
282
+ isOpen={confirm}
283
+ category={Category.confirmation}
284
+ header="You've made changes to this option"
285
+ message="Would you like to save or discard your changes?"
286
+ actions={{
287
+ secondary: {
288
+ action: () => reset(),
289
+ title: 'Discard',
290
+ },
291
+ primary: {
292
+ action: () => handleSave(valuesRef.current),
293
+ title: 'Save',
294
+ },
295
+ }}
296
+ />
297
+ <ReferencesModalContainer data-cy={'references-editor'}>
298
+ <ModalHeader>
299
+ <CloseButton onClick={onCancel} />
300
+ </ModalHeader>
301
+ <ModalBody>
302
+ <ReferencesSidebar>
303
+ <ModalSidebarHeader>
304
+ <ModalSidebarTitle>References</ModalSidebarTitle>
305
+ </ModalSidebarHeader>
306
+ <ReferencesSidebarContent ref={ref}>
307
+ <ReferencesInnerWrapper>
308
+ {items.slice(startIndex, endIndex + 1).map((item) => (
309
+ <ReferenceButton
310
+ key={item.id}
311
+ id={item.id}
312
+ className={isSelected(item) ? 'selected' : ''}
313
+ onClick={() => handleItemClick(item)}
314
+ ref={isSelected(item) ? selectionRef : null}
315
+ >
316
+ <IconContainer>
317
+ <CitationCountIcon />
318
+ {(citationCounts.get(item.id) || 0) > 0 ? (
319
+ <CitationCount data-tooltip-content="Number of times used in the document">
320
+ {citationCounts.get(item.id)}
321
+ </CitationCount>
322
+ ) : (
323
+ <CitationCount className="unused">0</CitationCount>
324
+ )}
325
+ </IconContainer>
326
+ <ReferenceLine item={item} />
327
+ </ReferenceButton>
328
+ ))}
329
+ </ReferencesInnerWrapper>
330
+ </ReferencesSidebarContent>
331
+ </ReferencesSidebar>
332
+ <ScrollableModalContent>
333
+ {selection && (
334
+ <ReferenceForm
335
+ values={normalize(selection)}
336
+ showDelete={
337
+ !citationCounts.get(selection.id) &&
338
+ isNewItem(selection, ['id', 'type']) // disable the delete button for the new citations
339
+ }
340
+ onChange={handleChange}
341
+ onCancel={onCancel}
342
+ onDelete={handleDelete}
343
+ onSave={handleSave}
344
+ actionsRef={actionsRef}
345
+ />
346
+ )}
347
+ </ScrollableModalContent>
348
+ </ModalBody>
349
+ </ReferencesModalContainer>
350
+ </StyledModal>
351
+ )
352
+ }
@@ -0,0 +1,98 @@
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 { SearchIcon, TextField } from '@manuscripts/style-guide'
18
+ import React, { InputHTMLAttributes, useState } from 'react'
19
+ import styled from 'styled-components'
20
+
21
+ const SearchContainer = styled.div`
22
+ align-items: center;
23
+ display: flex;
24
+ flex: 1 0 auto;
25
+ position: relative;
26
+ margin: 12px;
27
+ `
28
+
29
+ const SearchIconContainer = styled.span`
30
+ display: flex;
31
+ left: ${(props) => props.theme.grid.unit * 4}px;
32
+ position: absolute;
33
+ z-index: 2;
34
+
35
+ path {
36
+ stroke: ${(props) => props.theme.colors.text.primary};
37
+ }
38
+
39
+ &.active path {
40
+ stroke: ${(props) => props.theme.colors.brand.medium};
41
+ }
42
+ `
43
+
44
+ const SearchTextField = styled(TextField)`
45
+ -webkit-appearance: textfield;
46
+ padding-left: ${(props) => props.theme.grid.unit * 11}px;
47
+ &:hover,
48
+ &:focus {
49
+ background-color: ${(props) => props.theme.colors.background.fifth};
50
+ }
51
+ `
52
+
53
+ export const SearchWrapper = styled.div`
54
+ display: flex;
55
+ align-items: center;
56
+ padding: ${(props) => props.theme.grid.unit * 3}px;
57
+ `
58
+
59
+ export const SearchInput: React.FC<InputHTMLAttributes<HTMLInputElement>> = (
60
+ props
61
+ ) => {
62
+ const [hover, setHover] = useState(false)
63
+ const [focus, setFocus] = useState(false)
64
+
65
+ const onFocus = () => {
66
+ setFocus(true)
67
+ }
68
+ const onBlur = () => {
69
+ setFocus(false)
70
+ }
71
+
72
+ const onMouseEnter = () => {
73
+ setHover(true)
74
+ }
75
+ const onMouseLeave = () => {
76
+ setHover(false)
77
+ }
78
+
79
+ return (
80
+ <SearchContainer
81
+ onMouseEnter={onMouseEnter}
82
+ onMouseLeave={onMouseLeave}
83
+ onFocus={onFocus}
84
+ onBlur={onBlur}
85
+ >
86
+ <SearchIconContainer className={hover || focus ? 'active' : ''}>
87
+ <SearchIcon />
88
+ </SearchIconContainer>
89
+
90
+ <SearchTextField
91
+ {...props}
92
+ type="search"
93
+ placeholder="Search"
94
+ autoComplete="off"
95
+ />
96
+ </SearchContainer>
97
+ )
98
+ }