@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,284 @@
|
|
|
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 { Category, Dialog, PlusIcon } from '@manuscripts/style-guide'
|
|
18
|
+
import { isDeleted } from '@manuscripts/track-changes-plugin'
|
|
19
|
+
import {
|
|
20
|
+
generateNodeID,
|
|
21
|
+
ManuscriptEditorView,
|
|
22
|
+
ManuscriptNode,
|
|
23
|
+
schema,
|
|
24
|
+
} from '@manuscripts/transform'
|
|
25
|
+
|
|
26
|
+
import { TextSelection } from 'prosemirror-state'
|
|
27
|
+
import React, {
|
|
28
|
+
ChangeEvent,
|
|
29
|
+
useCallback,
|
|
30
|
+
useEffect,
|
|
31
|
+
useRef,
|
|
32
|
+
useState,
|
|
33
|
+
} from 'react'
|
|
34
|
+
import styled from 'styled-components'
|
|
35
|
+
|
|
36
|
+
const AddNewKeyword = styled.div`
|
|
37
|
+
position: relative;
|
|
38
|
+
display: inline-block;
|
|
39
|
+
`
|
|
40
|
+
|
|
41
|
+
const KeywordField = styled.input`
|
|
42
|
+
font-size: 13px;
|
|
43
|
+
margin: 0;
|
|
44
|
+
padding: 0;
|
|
45
|
+
border: none;
|
|
46
|
+
height: 16px;
|
|
47
|
+
width: 100px;
|
|
48
|
+
|
|
49
|
+
&:focus {
|
|
50
|
+
outline: none;
|
|
51
|
+
}
|
|
52
|
+
`
|
|
53
|
+
|
|
54
|
+
const NewKeywordButton = styled.button`
|
|
55
|
+
font-size: 13px;
|
|
56
|
+
color: #6e6e6e;
|
|
57
|
+
background: transparent;
|
|
58
|
+
border: none;
|
|
59
|
+
margin: 0;
|
|
60
|
+
padding: 0;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
|
|
63
|
+
&:focus-visible {
|
|
64
|
+
outline: 4px solid ${(props) => props.theme.colors.outline.focus};
|
|
65
|
+
outline-offset: 2px;
|
|
66
|
+
border-radius: 3px;
|
|
67
|
+
}
|
|
68
|
+
`
|
|
69
|
+
|
|
70
|
+
const CreateKeywordButtonWrapper = styled.div`
|
|
71
|
+
position: absolute;
|
|
72
|
+
left: 0;
|
|
73
|
+
top: 24px;
|
|
74
|
+
background: #fff;
|
|
75
|
+
border: 1px solid #e2e2e2;
|
|
76
|
+
border-radius: 8px;
|
|
77
|
+
padding: 8px 0;
|
|
78
|
+
`
|
|
79
|
+
|
|
80
|
+
const CreateKeywordButton = styled.button`
|
|
81
|
+
font-size: 12px;
|
|
82
|
+
white-space: nowrap;
|
|
83
|
+
color: #353535;
|
|
84
|
+
background: #f2fbfc;
|
|
85
|
+
border: 1px solid #f2f2f2;
|
|
86
|
+
border-width: 1px 0;
|
|
87
|
+
padding: 9px 13px;
|
|
88
|
+
cursor: pointer;
|
|
89
|
+
`
|
|
90
|
+
|
|
91
|
+
const PlusIconWrapper = styled.span`
|
|
92
|
+
padding: 0 8px 0 0;
|
|
93
|
+
`
|
|
94
|
+
|
|
95
|
+
interface KeywordEntry {
|
|
96
|
+
id: string
|
|
97
|
+
contents: string
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const AddKeywordInline: React.FC<{
|
|
101
|
+
viewProps: {
|
|
102
|
+
view: ManuscriptEditorView
|
|
103
|
+
getPos: () => number
|
|
104
|
+
node: ManuscriptNode
|
|
105
|
+
}
|
|
106
|
+
getUpdatedNode: () => ManuscriptNode
|
|
107
|
+
}> = ({ viewProps, getUpdatedNode }) => {
|
|
108
|
+
const nodeRef = useRef<HTMLDivElement>(null)
|
|
109
|
+
const [newKeyword, setNewKeyword] = useState<string>('')
|
|
110
|
+
const [isAddingNewKeyword, setIsAddingNewKeyword] = useState<boolean>(false)
|
|
111
|
+
const [isExistingKeywordError, setIsExistingKeywordError] =
|
|
112
|
+
useState<boolean>(false)
|
|
113
|
+
const { getPos, view } = viewProps
|
|
114
|
+
const getKeywords = (node: ManuscriptNode) => {
|
|
115
|
+
const keywords: KeywordEntry[] = []
|
|
116
|
+
node.content.descendants((descNode) => {
|
|
117
|
+
if (
|
|
118
|
+
descNode.type === descNode.type.schema.nodes.keyword &&
|
|
119
|
+
!isDeleted(descNode)
|
|
120
|
+
) {
|
|
121
|
+
keywords.push({
|
|
122
|
+
id: descNode.attrs.id,
|
|
123
|
+
contents: descNode.textContent,
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
return keywords
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const handleClickOutside = useCallback(
|
|
131
|
+
(event: Event) => {
|
|
132
|
+
if (
|
|
133
|
+
nodeRef.current &&
|
|
134
|
+
!nodeRef.current.contains(event.target as Node) &&
|
|
135
|
+
isAddingNewKeyword
|
|
136
|
+
) {
|
|
137
|
+
handleCancel()
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
[isAddingNewKeyword]
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
if (isAddingNewKeyword) {
|
|
145
|
+
document.addEventListener('mousedown', handleClickOutside)
|
|
146
|
+
} else {
|
|
147
|
+
document.removeEventListener('mousedown', handleClickOutside)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return () => {
|
|
151
|
+
document.removeEventListener('mousedown', handleClickOutside)
|
|
152
|
+
}
|
|
153
|
+
}, [handleClickOutside, isAddingNewKeyword])
|
|
154
|
+
|
|
155
|
+
const isValidNewKeyword = () => {
|
|
156
|
+
return newKeyword.trim().length > 0
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const isExistingKeyword = () => {
|
|
160
|
+
const keywordClean = newKeyword.trim()
|
|
161
|
+
const keywords: KeywordEntry[] = getKeywords(getUpdatedNode())
|
|
162
|
+
return keywords.some((keyword) => keyword.contents === keywordClean)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const handleCancel = () => {
|
|
166
|
+
setIsAddingNewKeyword(false)
|
|
167
|
+
setNewKeyword('')
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const handleAddKeyword = () => {
|
|
171
|
+
if (!isExistingKeyword() && isValidNewKeyword()) {
|
|
172
|
+
const node = getUpdatedNode()
|
|
173
|
+
const keywordNode = schema.nodes.keyword.create(
|
|
174
|
+
{
|
|
175
|
+
id: generateNodeID(schema.nodes.keyword),
|
|
176
|
+
},
|
|
177
|
+
node.type.schema.text(newKeyword)
|
|
178
|
+
)
|
|
179
|
+
const nodePosition = getPos() + node.nodeSize - 1
|
|
180
|
+
view.dispatch(view.state.tr.insert(nodePosition, keywordNode))
|
|
181
|
+
|
|
182
|
+
setIsAddingNewKeyword(false)
|
|
183
|
+
setNewKeyword('')
|
|
184
|
+
} else {
|
|
185
|
+
handleCancel()
|
|
186
|
+
setIsExistingKeywordError(true)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const KeywordInput: React.FC = () => {
|
|
191
|
+
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
192
|
+
setNewKeyword(event.target.value.trimStart())
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<KeywordField
|
|
198
|
+
value={newKeyword}
|
|
199
|
+
onChange={handleInputChange}
|
|
200
|
+
onFocus={() =>
|
|
201
|
+
view.dispatch(
|
|
202
|
+
view.state.tr.setSelection(TextSelection.create(view.state.doc, 0))
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
onKeyDown={(e) => {
|
|
206
|
+
e.stopPropagation()
|
|
207
|
+
if (e.key === 'Enter') {
|
|
208
|
+
e.preventDefault() // Prevent focus loss
|
|
209
|
+
handleAddKeyword()
|
|
210
|
+
} else if (e.key === 'Escape') {
|
|
211
|
+
handleCancel()
|
|
212
|
+
}
|
|
213
|
+
}}
|
|
214
|
+
onKeyUp={(e) => {
|
|
215
|
+
e.stopPropagation()
|
|
216
|
+
}}
|
|
217
|
+
onKeyPress={(e) => {
|
|
218
|
+
e.stopPropagation()
|
|
219
|
+
}}
|
|
220
|
+
autoFocus
|
|
221
|
+
/>
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const CreateKeywordButtonElement: React.FC = () => {
|
|
226
|
+
return (
|
|
227
|
+
<CreateKeywordButtonWrapper>
|
|
228
|
+
<CreateKeywordButton
|
|
229
|
+
onClick={() => {
|
|
230
|
+
handleAddKeyword()
|
|
231
|
+
}}
|
|
232
|
+
>
|
|
233
|
+
<PlusIconWrapper>
|
|
234
|
+
<PlusIcon />
|
|
235
|
+
</PlusIconWrapper>
|
|
236
|
+
{`Create keyword "${newKeyword}"`}
|
|
237
|
+
</CreateKeywordButton>
|
|
238
|
+
</CreateKeywordButtonWrapper>
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const actions = {
|
|
243
|
+
primary: {
|
|
244
|
+
action: () => {
|
|
245
|
+
setIsExistingKeywordError(false)
|
|
246
|
+
},
|
|
247
|
+
title: 'OK',
|
|
248
|
+
},
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return (
|
|
252
|
+
<AddNewKeyword ref={nodeRef}>
|
|
253
|
+
{!isAddingNewKeyword && (
|
|
254
|
+
<NewKeywordButton
|
|
255
|
+
tabIndex={-1}
|
|
256
|
+
className="keyword-add"
|
|
257
|
+
onClick={(e) => {
|
|
258
|
+
e.preventDefault()
|
|
259
|
+
setIsAddingNewKeyword(true)
|
|
260
|
+
}}
|
|
261
|
+
onKeyDown={(e) => {
|
|
262
|
+
if (e.key === 'Enter') {
|
|
263
|
+
e.preventDefault()
|
|
264
|
+
setIsAddingNewKeyword(true)
|
|
265
|
+
}
|
|
266
|
+
}}
|
|
267
|
+
>
|
|
268
|
+
New keyword...
|
|
269
|
+
</NewKeywordButton>
|
|
270
|
+
)}
|
|
271
|
+
{isAddingNewKeyword && <KeywordInput />}
|
|
272
|
+
{isAddingNewKeyword && isValidNewKeyword() && (
|
|
273
|
+
<CreateKeywordButtonElement />
|
|
274
|
+
)}
|
|
275
|
+
<Dialog
|
|
276
|
+
isOpen={isExistingKeywordError}
|
|
277
|
+
actions={actions}
|
|
278
|
+
category={Category.confirmation}
|
|
279
|
+
header={'Keyword already exists'}
|
|
280
|
+
message={`You can’t add this keyword because it already exists. You can add another one.`}
|
|
281
|
+
/>
|
|
282
|
+
</AddNewKeyword>
|
|
283
|
+
)
|
|
284
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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 { Category, Dialog } from '@manuscripts/style-guide'
|
|
17
|
+
import React, { useState } from 'react'
|
|
18
|
+
|
|
19
|
+
export interface DeleteKeywordDialogProps {
|
|
20
|
+
keyword: string
|
|
21
|
+
handleDelete: () => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const DeleteKeywordDialog: React.FC<DeleteKeywordDialogProps> = ({
|
|
25
|
+
keyword,
|
|
26
|
+
handleDelete,
|
|
27
|
+
}) => {
|
|
28
|
+
const [isOpen, setOpen] = useState(true)
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Dialog
|
|
32
|
+
isOpen={isOpen}
|
|
33
|
+
actions={{
|
|
34
|
+
primary: {
|
|
35
|
+
action: () => setOpen(false),
|
|
36
|
+
title: 'Cancel',
|
|
37
|
+
},
|
|
38
|
+
secondary: {
|
|
39
|
+
action: () => {
|
|
40
|
+
setOpen(false)
|
|
41
|
+
handleDelete()
|
|
42
|
+
},
|
|
43
|
+
title: 'Delete',
|
|
44
|
+
},
|
|
45
|
+
}}
|
|
46
|
+
category={Category.confirmation}
|
|
47
|
+
header={'Delete Keyword'}
|
|
48
|
+
message={`Are you sure you want to delete "${keyword}" keyword?
|
|
49
|
+
`}
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
import {
|
|
17
|
+
AddedIcon,
|
|
18
|
+
AddIcon,
|
|
19
|
+
Drawer,
|
|
20
|
+
DrawerIcon,
|
|
21
|
+
DrawerItemLabel,
|
|
22
|
+
DrawerItemsList,
|
|
23
|
+
DrawerLabelContainer,
|
|
24
|
+
DrawerListItem,
|
|
25
|
+
DrawerProps,
|
|
26
|
+
} from '@manuscripts/style-guide'
|
|
27
|
+
import React from 'react'
|
|
28
|
+
|
|
29
|
+
interface GenericDrawerProps {
|
|
30
|
+
items: { id: string; label: string }[]
|
|
31
|
+
selectedItems: {
|
|
32
|
+
id: string
|
|
33
|
+
}[]
|
|
34
|
+
onSelect: (id: string) => void
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const GenericDrawer: React.FC<
|
|
38
|
+
GenericDrawerProps & Omit<DrawerProps, 'children'>
|
|
39
|
+
> = ({ items, selectedItems = [], onSelect, ...drawerProps }) => {
|
|
40
|
+
return (
|
|
41
|
+
<Drawer {...drawerProps}>
|
|
42
|
+
<DrawerItemsList>
|
|
43
|
+
{items.map((item) => (
|
|
44
|
+
<DrawerListItem
|
|
45
|
+
data-cy="item"
|
|
46
|
+
key={item.id}
|
|
47
|
+
selected={selectedItems?.map((a) => a.id).includes(item.id)}
|
|
48
|
+
onClick={() => onSelect(item.id)}
|
|
49
|
+
>
|
|
50
|
+
<DrawerIcon>
|
|
51
|
+
{selectedItems?.map((a) => a.id).includes(item.id) ? (
|
|
52
|
+
<AddedIcon width={22} height={22} />
|
|
53
|
+
) : (
|
|
54
|
+
<AddIcon width={22} height={22} />
|
|
55
|
+
)}
|
|
56
|
+
</DrawerIcon>
|
|
57
|
+
<DrawerLabelContainer>
|
|
58
|
+
<DrawerItemLabel>{item.label}</DrawerItemLabel>
|
|
59
|
+
</DrawerLabelContainer>
|
|
60
|
+
</DrawerListItem>
|
|
61
|
+
))}
|
|
62
|
+
</DrawerItemsList>
|
|
63
|
+
</Drawer>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
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 {
|
|
18
|
+
DrawerProps,
|
|
19
|
+
outlineStyle,
|
|
20
|
+
SelectedItemsBox,
|
|
21
|
+
IconButton,
|
|
22
|
+
} from '@manuscripts/style-guide'
|
|
23
|
+
import React from 'react'
|
|
24
|
+
import styled from 'styled-components'
|
|
25
|
+
|
|
26
|
+
import { PartialExcept } from '../../types'
|
|
27
|
+
|
|
28
|
+
type Base = {
|
|
29
|
+
id: string
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
} & Record<string, any>
|
|
32
|
+
|
|
33
|
+
interface DrawerGroupProps<T extends Base> {
|
|
34
|
+
removeItem: (id: string) => void
|
|
35
|
+
selectedItems: PartialExcept<T, 'id'>[]
|
|
36
|
+
items: T[]
|
|
37
|
+
showDrawer: boolean
|
|
38
|
+
setShowDrawer: (on: boolean) => void
|
|
39
|
+
onSelect: (id: string) => void
|
|
40
|
+
title: string
|
|
41
|
+
labelField: keyof T
|
|
42
|
+
Drawer: React.ComponentType<
|
|
43
|
+
EmbeddedDrawerProps<T> & Omit<DrawerProps, 'children'>
|
|
44
|
+
>
|
|
45
|
+
cy: string
|
|
46
|
+
buttonText: string
|
|
47
|
+
Icon?: React.ReactNode
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface EmbeddedDrawerProps<T extends Base> {
|
|
51
|
+
items: T[]
|
|
52
|
+
selectedItems: DrawerGroupProps<T>['selectedItems']
|
|
53
|
+
onSelect: (id: string) => void
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function DrawerGroup<T extends Base>({
|
|
57
|
+
removeItem,
|
|
58
|
+
selectedItems,
|
|
59
|
+
onSelect,
|
|
60
|
+
items,
|
|
61
|
+
showDrawer,
|
|
62
|
+
setShowDrawer,
|
|
63
|
+
title,
|
|
64
|
+
labelField,
|
|
65
|
+
Drawer,
|
|
66
|
+
cy,
|
|
67
|
+
Icon,
|
|
68
|
+
buttonText,
|
|
69
|
+
}: DrawerGroupProps<T>) {
|
|
70
|
+
return (
|
|
71
|
+
<>
|
|
72
|
+
<AssignButton
|
|
73
|
+
onClick={() => setShowDrawer(true)}
|
|
74
|
+
data-cy={cy + 'assign-button'}
|
|
75
|
+
>
|
|
76
|
+
{Icon}
|
|
77
|
+
{buttonText}
|
|
78
|
+
</AssignButton>
|
|
79
|
+
<SelectedItemsBox
|
|
80
|
+
data-cy={cy + '-selected-items'}
|
|
81
|
+
items={selectedItems.map((i) => ({
|
|
82
|
+
id: String(i.id),
|
|
83
|
+
label: String((i as T)[labelField as keyof T] ?? ''),
|
|
84
|
+
}))}
|
|
85
|
+
onRemove={removeItem}
|
|
86
|
+
placeholder={`No ${title} assigned`}
|
|
87
|
+
/>
|
|
88
|
+
{showDrawer && (
|
|
89
|
+
<Drawer
|
|
90
|
+
items={items}
|
|
91
|
+
selectedItems={selectedItems}
|
|
92
|
+
title={title}
|
|
93
|
+
onSelect={onSelect}
|
|
94
|
+
onBack={() => setShowDrawer(false)}
|
|
95
|
+
width="100%"
|
|
96
|
+
/>
|
|
97
|
+
)}
|
|
98
|
+
</>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const AssignButton = styled(IconButton)`
|
|
103
|
+
color: ${(props) => props.theme.colors.brand.default};
|
|
104
|
+
background: none;
|
|
105
|
+
border: none;
|
|
106
|
+
cursor: pointer;
|
|
107
|
+
padding: 0;
|
|
108
|
+
font: ${(props) => props.theme.font.weight.normal}
|
|
109
|
+
${(props) => props.theme.font.size.normal}
|
|
110
|
+
${(props) => props.theme.font.family.sans};
|
|
111
|
+
gap: 4px;
|
|
112
|
+
width: auto;
|
|
113
|
+
height: 24px;
|
|
114
|
+
|
|
115
|
+
&:disabled {
|
|
116
|
+
color: #c9c9c9 !important;
|
|
117
|
+
background-color: unset !important;
|
|
118
|
+
border: unset;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
&:hover {
|
|
122
|
+
opacity: 0.8;
|
|
123
|
+
}
|
|
124
|
+
${outlineStyle}
|
|
125
|
+
`
|