@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.
- package/dist/cjs/components/toolbar/type-selector/styles.js +2 -1
- package/dist/cjs/versions.js +1 -1
- package/dist/es/components/toolbar/type-selector/styles.js +2 -1
- package/dist/es/versions.js +1 -1
- package/dist/types/components/toolbar/type-selector/styles.d.ts +2 -2
- package/dist/types/lib/footnotes.d.ts +4 -2
- package/dist/types/lib/utils.d.ts +5 -4
- package/dist/types/versions.d.ts +1 -1
- package/package.json +37 -36
- package/{dist/es/lib/statistics.js → src/__mocks__/styleMock.ts} +2 -14
- package/src/assets.ts +20 -0
- package/src/classes/collabProvider.ts +38 -0
- package/src/clipboard.ts +54 -0
- package/src/commands.ts +2109 -0
- package/src/components/ChangeHandlingForm.tsx +54 -0
- package/src/components/LanguageDropdown/index.tsx +343 -0
- package/src/components/affiliations/AffiliationForm.tsx +238 -0
- package/src/components/affiliations/AffiliationItem.tsx +151 -0
- package/src/components/affiliations/AffiliationList.tsx +71 -0
- package/src/components/affiliations/AffiliationsModal.tsx +676 -0
- package/src/components/affiliations/AffiliationsPanel.tsx +76 -0
- package/src/components/affiliations/CreateAffiliationModal.tsx +135 -0
- package/src/components/authors/AuthorDetailsForm.tsx +438 -0
- package/src/components/authors/AuthorList.tsx +81 -0
- package/src/components/authors/AuthorsModal.tsx +642 -0
- package/src/components/authors/AuthorsPanel.tsx +69 -0
- package/src/components/authors/CreateAuthorModal.tsx +172 -0
- package/src/components/authors/CreditDrawer.tsx +71 -0
- package/src/components/authors/DraggableAuthor.tsx +228 -0
- package/src/components/authors/useManageAffiliations.tsx +77 -0
- package/src/components/authors/useManageCredit.tsx +63 -0
- package/src/components/authors-affiliations/AuthorsAndAffiliationsModals.tsx +170 -0
- package/src/components/authors-affiliations/GenericPanel.tsx +164 -0
- package/src/components/authors-affiliations/ModalTabs.tsx +100 -0
- package/src/components/awards/AwardForm.tsx +338 -0
- package/src/components/awards/AwardModal.tsx +120 -0
- package/src/components/awards/DeleteAwardDiaolog.tsx +52 -0
- package/src/components/dialog/ConfirmationDialog.tsx +131 -0
- package/src/components/form/CreateModalStyles.tsx +37 -0
- package/src/components/form/FormFooter.tsx +54 -0
- package/src/components/form/FormPlaceholder.tsx +71 -0
- package/src/components/form/ModalFormActions.tsx +88 -0
- package/src/components/form/UnsavedLabel.tsx +49 -0
- package/src/components/hooks/use-debounce.ts +56 -0
- package/src/components/hooks/useAffiliationShowsErrorIndicator.ts +51 -0
- package/src/components/hooks/useAuthorShowsErrorIndicator.ts +73 -0
- package/src/components/keyboard-shortcuts-modal/FormattedShortcut.tsx +56 -0
- package/src/components/keyboard-shortcuts-modal/KeyboardShortcutsModal.tsx +283 -0
- package/src/components/keyboard-shortcuts-modal/keyboard-shortcuts.ts +304 -0
- package/src/components/keywords/AddKeywordInline.tsx +284 -0
- package/src/components/keywords/DeleteKeywordDialog.tsx +52 -0
- package/src/components/modal-drawer/GenericDrawer.tsx +65 -0
- package/src/components/modal-drawer/GenericDrawerGroup.tsx +125 -0
- package/src/components/outline/DraggableTree.tsx +438 -0
- package/src/components/outline/ManuscriptOutline.tsx +127 -0
- package/src/components/outline/Outline.tsx +141 -0
- package/src/components/references/BibliographyItemSource.ts +80 -0
- package/src/components/references/CitationEditor.tsx +301 -0
- package/src/components/references/CitationViewer.tsx +62 -0
- package/src/components/references/ImportBibliographyForm.tsx +232 -0
- package/src/components/references/ImportBibliographyModal.tsx +159 -0
- package/src/components/references/ReferenceForm/PersonDropDown.tsx +93 -0
- package/src/components/references/ReferenceForm/ReferenceForm.tsx +701 -0
- package/src/components/references/ReferenceForm/config.ts +133 -0
- package/src/components/references/ReferenceForm/styled-components.ts +156 -0
- package/src/components/references/ReferenceLine.tsx +43 -0
- package/src/components/references/ReferenceSearch.tsx +163 -0
- package/src/components/references/ReferenceSearchResults.tsx +113 -0
- package/src/components/references/ReferenceSearchResultsPlaceholder.tsx +71 -0
- package/src/components/references/ReferenceSearchSection.tsx +124 -0
- package/src/components/references/ReferencesEditor.tsx +61 -0
- package/src/components/references/ReferencesModal.tsx +352 -0
- package/src/components/references/SearchInput.tsx +98 -0
- package/src/components/toolbar/InsertEmbedDialog.tsx +222 -0
- package/src/components/toolbar/InsertTableDialog.tsx +208 -0
- package/src/components/toolbar/ListMenuItem.tsx +203 -0
- package/src/components/toolbar/helpers.ts +547 -0
- package/src/components/toolbar/type-selector/OptionComponent.tsx +89 -0
- package/src/components/toolbar/type-selector/TypeSelector.tsx +157 -0
- package/src/components/toolbar/type-selector/styles.ts +79 -0
- package/src/components/views/CrossReferenceItems.tsx +199 -0
- package/src/components/views/DeleteEmbedDialog.tsx +83 -0
- package/src/components/views/DeleteFootnoteDialog.tsx +54 -0
- package/src/components/views/FigureDropdown.tsx +423 -0
- package/src/components/views/FootnotesSelector.tsx +255 -0
- package/src/components/views/InsertSpecialCharacter.tsx +245 -0
- package/src/components/views/LinkForm.tsx +220 -0
- package/src/components/views/TableCellContextMenu.tsx +241 -0
- package/src/configs/ManuscriptsEditor.ts +140 -0
- package/src/configs/editor-plugins.ts +119 -0
- package/src/configs/editor-views.ts +139 -0
- package/src/icons.ts +62 -0
- package/src/index.ts +65 -0
- package/src/keys/highlight.ts +65 -0
- package/src/keys/index.ts +33 -0
- package/src/keys/keyword.ts +69 -0
- package/src/keys/list.ts +156 -0
- package/src/keys/misc.ts +112 -0
- package/src/keys/title.ts +221 -0
- package/src/lib/__tests__/__fixtures__/doc.json +388 -0
- package/src/lib/__tests__/__snapshots__/paste.test.ts.snap +24 -0
- package/src/lib/__tests__/authors.test.ts +202 -0
- package/src/lib/__tests__/helpers.test.ts +46 -0
- package/src/lib/__tests__/paste.test.ts +57 -0
- package/src/lib/__tests__/plugins.test.ts +176 -0
- package/src/lib/__tests__/url.test.ts +31 -0
- package/src/lib/array-reducer.ts +68 -0
- package/src/lib/authors-and-affiliations.ts +81 -0
- package/src/lib/authors.ts +67 -0
- package/src/lib/capabilities.ts +55 -0
- package/src/lib/codemirror.ts +37 -0
- package/src/lib/comments.ts +123 -0
- package/src/lib/context-menu.ts +745 -0
- package/src/lib/copy.ts +30 -0
- package/src/lib/crossref.ts +90 -0
- package/src/lib/dnd.ts +29 -0
- package/src/lib/doc.ts +167 -0
- package/src/lib/dompurify.ts +28 -0
- package/src/lib/files.ts +180 -0
- package/src/lib/footnotes.ts +83 -0
- package/src/lib/get-media-type.ts +105 -0
- package/src/lib/helpers.ts +199 -0
- package/src/lib/hierarchy.ts +117 -0
- package/src/lib/languages.ts +37 -0
- package/src/lib/lists.ts +56 -0
- package/src/lib/math.ts +111 -0
- package/src/lib/media.ts +334 -0
- package/src/lib/navigation-utils.ts +235 -0
- package/src/lib/normalize.ts +70 -0
- package/src/lib/paste.ts +304 -0
- package/src/lib/placeholder.ts +32 -0
- package/src/lib/platform.ts +18 -0
- package/src/lib/plugins.ts +22 -0
- package/src/lib/popper.ts +180 -0
- package/src/lib/position-menu.ts +184 -0
- package/src/lib/references.ts +94 -0
- package/src/lib/template.ts +29 -0
- package/src/lib/track-changes-utils.ts +34 -0
- package/{dist/types/lib/statistics.d.ts → src/lib/url.ts} +11 -8
- package/src/lib/use-do-with-debounce.ts +39 -0
- package/src/lib/utils.ts +323 -0
- package/src/lib/view.ts +100 -0
- package/src/menus.tsx +684 -0
- package/src/node-type-icons.tsx +83 -0
- package/src/plugins/__tests__/__fixtures__/deleted-footnote.json +140 -0
- package/src/plugins/__tests__/__fixtures__/inserted-footnote.json +153 -0
- package/src/plugins/__tests__/footnotes.test.ts +67 -0
- package/src/plugins/accessibility_element.ts +219 -0
- package/src/plugins/add-subtitle.ts +102 -0
- package/src/plugins/affiliations.ts +136 -0
- package/src/plugins/alt-titles.ts +219 -0
- package/src/plugins/bibliography.ts +235 -0
- package/src/plugins/comments.ts +328 -0
- package/src/plugins/cross-references.ts +70 -0
- package/src/plugins/detect-inconsistency/detect-inconsistency-utils.ts +102 -0
- package/src/plugins/detect-inconsistency/index.ts +55 -0
- package/src/plugins/detect-inconsistency/validators.ts +283 -0
- package/src/plugins/doi.ts +66 -0
- package/src/plugins/editor-props.ts +39 -0
- package/src/plugins/elements.ts +60 -0
- package/src/plugins/footnotes.ts +298 -0
- package/src/plugins/link.ts +53 -0
- package/src/plugins/lock-body.ts +136 -0
- package/src/plugins/move-node.ts +55 -0
- package/src/plugins/objects.ts +125 -0
- package/src/plugins/paragraphs.ts +90 -0
- package/src/plugins/persist.ts +64 -0
- package/src/plugins/persistent-cursor.ts +66 -0
- package/src/plugins/placeholder.ts +219 -0
- package/src/plugins/prevent-empty.ts +50 -0
- package/src/plugins/search-replace/index.ts +157 -0
- package/src/plugins/search-replace/lib.ts +95 -0
- package/src/plugins/section_category.ts +212 -0
- package/src/plugins/section_title/autocompletion.ts +87 -0
- package/src/plugins/section_title/index.ts +105 -0
- package/src/plugins/selected-suggestion.ts +243 -0
- package/src/plugins/tables-cursor-fix.ts +143 -0
- package/src/plugins/translations.ts +263 -0
- package/src/rules.ts +60 -0
- package/src/selection.ts +112 -0
- package/src/testing/default-capabilities.ts +48 -0
- package/src/testing/default-editor-data.ts +73 -0
- package/src/testing/empty-editor-doc.json +62 -0
- package/src/testing/polyfill-dom.ts +43 -0
- package/src/testing/section-categories.json +161 -0
- package/src/testing/setup-editor.ts +55 -0
- package/src/tests.ts +42 -0
- package/src/toolbar.tsx +259 -0
- package/src/types/@citation-js/core.d.ts +16 -0
- package/src/types/@citation-js/index.d.ts +16 -0
- package/src/types/@citation-js/plugin-bibtex.d.ts +16 -0
- package/src/types/@citation-js/plugin-csl.d.ts +16 -0
- package/src/types/@citation-js/plugin-doi.d.ts +16 -0
- package/src/types/@citation-js/plugin-enw.d.ts +16 -0
- package/src/types/@citation-js/plugin-pubmed.d.ts +16 -0
- package/src/types/@citation-js/plugin-ris.d.ts +16 -0
- package/src/types/citeproc.d.ts +124 -0
- package/src/types/css.d.ts +16 -0
- package/src/types/json.d.ts +17 -0
- package/src/types/prosemirror-dev-tools.d.ts +16 -0
- package/src/types/prosemirror-dropcursor.d.ts +16 -0
- package/src/types/styled-components.d.ts +23 -0
- package/src/types.ts +49 -0
- package/src/useEditor.ts +244 -0
- package/src/versions.ts +3 -0
- package/src/views/ReactSubView.tsx +145 -0
- package/src/views/abstracts.ts +46 -0
- package/src/views/accessibility_element.ts +86 -0
- package/src/views/affiliations.ts +254 -0
- package/src/views/alt_title.ts +85 -0
- package/src/views/alt_titles_section.ts +97 -0
- package/src/views/attachment.ts +160 -0
- package/src/views/attachments.ts +72 -0
- package/src/views/author_notes.ts +96 -0
- package/src/views/award.ts +194 -0
- package/src/views/awards.ts +59 -0
- package/src/views/base_node_view.ts +85 -0
- package/src/views/bibliography_element.ts +313 -0
- package/src/views/block_view.ts +100 -0
- package/src/views/blockquote_element.ts +26 -0
- package/src/views/blockquote_element_editable.ts +24 -0
- package/src/views/box_element.ts +33 -0
- package/src/views/caption.ts +37 -0
- package/src/views/caption_title.ts +33 -0
- package/src/views/citation.ts +67 -0
- package/src/views/citation_editable.ts +312 -0
- package/src/views/contributors.ts +341 -0
- package/src/views/creators.ts +82 -0
- package/src/views/cross_reference.ts +74 -0
- package/src/views/cross_reference_editable.ts +113 -0
- package/src/views/editable_block.ts +141 -0
- package/src/views/embed.ts +345 -0
- package/src/views/empty.ts +26 -0
- package/src/views/equation.ts +47 -0
- package/src/views/equation_editable.ts +84 -0
- package/src/views/equation_element.ts +29 -0
- package/src/views/equation_element_editable.ts +21 -0
- package/src/views/figure.ts +84 -0
- package/src/views/figure_editable.ts +361 -0
- package/src/views/figure_element.ts +197 -0
- package/src/views/figure_element_editable.ts +21 -0
- package/src/views/footnote.ts +236 -0
- package/src/views/footnotes_element.ts +54 -0
- package/src/views/general_table_footnote.ts +140 -0
- package/src/views/hero_image.ts +76 -0
- package/src/views/hero_image_editable.ts +21 -0
- package/src/views/image_element.ts +373 -0
- package/src/views/image_element_editable.ts +21 -0
- package/src/views/inline_equation.ts +47 -0
- package/src/views/inline_equation_editable.ts +70 -0
- package/src/views/inline_footnote.ts +264 -0
- package/src/views/inline_footnote_editable.ts +20 -0
- package/src/views/keyword.ts +127 -0
- package/src/views/keyword_group.ts +109 -0
- package/src/views/link.ts +27 -0
- package/src/views/link_editable.ts +169 -0
- package/src/views/list.ts +46 -0
- package/src/views/list_item.ts +26 -0
- package/src/views/paragraph.ts +26 -0
- package/src/views/paragraph_editable.ts +21 -0
- package/src/views/placeholder.ts +35 -0
- package/src/views/placeholder_element.ts +38 -0
- package/src/views/placeholder_element_editable.ts +21 -0
- package/src/views/pullquote_element.ts +106 -0
- package/src/views/pullquote_element_editable.ts +24 -0
- package/src/views/quote_image_editable.ts +84 -0
- package/src/views/section.ts +64 -0
- package/src/views/section_label.ts +20 -0
- package/src/views/section_title.ts +87 -0
- package/src/views/section_title_editable.ts +21 -0
- package/src/views/subtitle.ts +42 -0
- package/src/views/subtitle_editable.ts +20 -0
- package/src/views/subtitles.ts +34 -0
- package/src/views/subtitles_editable.ts +21 -0
- package/src/views/supplement.ts +236 -0
- package/src/views/supplement_editable.ts +20 -0
- package/src/views/supplements.ts +65 -0
- package/src/views/supplements_editable.ts +21 -0
- package/src/views/table_cell.ts +150 -0
- package/src/views/table_element.ts +33 -0
- package/src/views/table_element_editable.ts +21 -0
- package/src/views/table_element_footer.ts +41 -0
- package/src/views/title.ts +46 -0
- package/src/views/title_editable.ts +20 -0
- package/src/views/trans_graphical_abstract.ts +55 -0
- package/src/views/trans_graphical_abstract_editable.ts +21 -0
- package/src/views/translated_abstract.ts +53 -0
- package/src/views/translated_abstract_editable.ts +21 -0
- 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
|
+
}
|