@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,95 @@
|
|
|
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
|
+
|
|
17
|
+
import { isDeleted, isDeletedText } from '@manuscripts/track-changes-plugin'
|
|
18
|
+
import { Node as ProseMirrorNode } from 'prosemirror-model'
|
|
19
|
+
|
|
20
|
+
export function getNewMatch(
|
|
21
|
+
side: 'left' | 'right',
|
|
22
|
+
current: number,
|
|
23
|
+
selection: { from: number; to: number },
|
|
24
|
+
matches: { from: number; to: number }[]
|
|
25
|
+
) {
|
|
26
|
+
if (current < 0) {
|
|
27
|
+
// it means we need to recalc againt the new pointer
|
|
28
|
+
return getClosestMatch(side, selection, matches)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let newMatch = 0
|
|
32
|
+
if (side == 'left') {
|
|
33
|
+
newMatch = current - 1 >= 0 ? current - 1 : matches.length - 1
|
|
34
|
+
}
|
|
35
|
+
if (side == 'right') {
|
|
36
|
+
newMatch = current + 1 < matches.length ? current + 1 : 0
|
|
37
|
+
}
|
|
38
|
+
return newMatch
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getClosestMatch(
|
|
42
|
+
side: 'left' | 'right',
|
|
43
|
+
selection: { from: number; to: number },
|
|
44
|
+
matches: { from: number; to: number }[]
|
|
45
|
+
) {
|
|
46
|
+
for (let i = 0; i < matches.length; i++) {
|
|
47
|
+
const match = matches[i]
|
|
48
|
+
if (match.from >= selection.from) {
|
|
49
|
+
return side == 'right' ? i : Math.max(0, i - 1)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return 0
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function removeDiacritics(str: string) {
|
|
56
|
+
return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function getMatches(
|
|
60
|
+
doc: ProseMirrorNode,
|
|
61
|
+
value: string,
|
|
62
|
+
caseSensitive: boolean,
|
|
63
|
+
ignoreDiacritics: boolean
|
|
64
|
+
) {
|
|
65
|
+
if (!value) {
|
|
66
|
+
return []
|
|
67
|
+
}
|
|
68
|
+
let normalised = caseSensitive ? value : value.toLocaleLowerCase()
|
|
69
|
+
if (ignoreDiacritics) {
|
|
70
|
+
normalised = removeDiacritics(normalised)
|
|
71
|
+
}
|
|
72
|
+
const matches: Array<{ from: number; to: number }> = []
|
|
73
|
+
|
|
74
|
+
doc.descendants((node, pos) => {
|
|
75
|
+
if (isDeleted(node) || isDeletedText(node)) {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
if (node.isText && node.text) {
|
|
79
|
+
let base = caseSensitive ? node.text : node.text.toLocaleLowerCase()
|
|
80
|
+
if (ignoreDiacritics) {
|
|
81
|
+
base = removeDiacritics(base)
|
|
82
|
+
}
|
|
83
|
+
let index = base.indexOf(normalised)
|
|
84
|
+
while (index !== -1) {
|
|
85
|
+
matches.push({
|
|
86
|
+
from: pos + index,
|
|
87
|
+
to: pos + index + normalised.length,
|
|
88
|
+
})
|
|
89
|
+
index = base.indexOf(normalised, index + normalised.length)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
return matches
|
|
95
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
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 {
|
|
17
|
+
getGroupCategories,
|
|
18
|
+
isSectionNode,
|
|
19
|
+
schema,
|
|
20
|
+
SectionCategory,
|
|
21
|
+
} from '@manuscripts/transform'
|
|
22
|
+
import { ResolvedPos } from 'prosemirror-model'
|
|
23
|
+
import { EditorState, Plugin, PluginKey } from 'prosemirror-state'
|
|
24
|
+
import {
|
|
25
|
+
findChildrenByType,
|
|
26
|
+
findParentNodeOfTypeClosestToPos,
|
|
27
|
+
} from 'prosemirror-utils'
|
|
28
|
+
import { Decoration, DecorationSet, EditorView } from 'prosemirror-view'
|
|
29
|
+
|
|
30
|
+
import { EditorProps } from '../configs/ManuscriptsEditor'
|
|
31
|
+
import { sectionCategoryIcon } from '../icons'
|
|
32
|
+
|
|
33
|
+
export const sectionCategoryKey = new PluginKey<PluginState>('section-category')
|
|
34
|
+
|
|
35
|
+
export interface PluginState {
|
|
36
|
+
decorations: DecorationSet
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default (props: EditorProps) =>
|
|
40
|
+
new Plugin<PluginState>({
|
|
41
|
+
key: sectionCategoryKey,
|
|
42
|
+
state: {
|
|
43
|
+
init: (_, state) => buildPluginState(state, props),
|
|
44
|
+
apply: (tr, value, oldState, newState) => {
|
|
45
|
+
if (!tr.docChanged) {
|
|
46
|
+
return value
|
|
47
|
+
}
|
|
48
|
+
return buildPluginState(newState, props)
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
props: {
|
|
52
|
+
decorations: (state) =>
|
|
53
|
+
sectionCategoryKey.getState(state)?.decorations || DecorationSet.empty,
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const createMenuItem = (
|
|
58
|
+
props: EditorProps,
|
|
59
|
+
contents: string,
|
|
60
|
+
handler: EventListener,
|
|
61
|
+
isDisabled: boolean,
|
|
62
|
+
isSelected: boolean
|
|
63
|
+
) => {
|
|
64
|
+
const item = document.createElement('div')
|
|
65
|
+
item.classList.add('menu-item')
|
|
66
|
+
if (isSelected) {
|
|
67
|
+
item.classList.add('selected')
|
|
68
|
+
}
|
|
69
|
+
if (isDisabled) {
|
|
70
|
+
item.classList.add('disabled')
|
|
71
|
+
}
|
|
72
|
+
item.textContent = contents
|
|
73
|
+
item.addEventListener('mousedown', (event) => {
|
|
74
|
+
handler(event)
|
|
75
|
+
props.popper.destroy()
|
|
76
|
+
})
|
|
77
|
+
return item
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const createMenu = (
|
|
81
|
+
props: EditorProps,
|
|
82
|
+
currentCategory: SectionCategory | undefined,
|
|
83
|
+
categories: SectionCategory[],
|
|
84
|
+
usedCategoryIDs: Set<string>,
|
|
85
|
+
onSelect: (category: SectionCategory) => void
|
|
86
|
+
) => {
|
|
87
|
+
const menu = document.createElement('div')
|
|
88
|
+
menu.className = 'section-category menu'
|
|
89
|
+
categories.forEach((category) => {
|
|
90
|
+
const item = createMenuItem(
|
|
91
|
+
props,
|
|
92
|
+
category.titles[0],
|
|
93
|
+
() => onSelect(category),
|
|
94
|
+
category.isUnique && usedCategoryIDs.has(category.id),
|
|
95
|
+
currentCategory === category
|
|
96
|
+
)
|
|
97
|
+
menu.appendChild(item)
|
|
98
|
+
})
|
|
99
|
+
return menu
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const createButton = (
|
|
103
|
+
props: EditorProps,
|
|
104
|
+
view: EditorView,
|
|
105
|
+
pos: number,
|
|
106
|
+
currentCategory: SectionCategory | undefined,
|
|
107
|
+
categories: SectionCategory[],
|
|
108
|
+
usedCategoryIDs: Set<string>,
|
|
109
|
+
canEdit = true,
|
|
110
|
+
disabled: boolean
|
|
111
|
+
) => {
|
|
112
|
+
const handleSelect = (category: SectionCategory) => {
|
|
113
|
+
const tr = view.state.tr
|
|
114
|
+
tr.setNodeAttribute(pos, 'category', category.id)
|
|
115
|
+
view.dispatch(tr)
|
|
116
|
+
}
|
|
117
|
+
const button = document.createElement('button')
|
|
118
|
+
button.innerHTML = sectionCategoryIcon
|
|
119
|
+
button.classList.add('section-category-button')
|
|
120
|
+
button.setAttribute('aria-label', 'Section categories menu')
|
|
121
|
+
if (currentCategory) {
|
|
122
|
+
button.setAttribute('data-tooltip-content', currentCategory.titles[0])
|
|
123
|
+
button.classList.add('assigned')
|
|
124
|
+
}
|
|
125
|
+
if (disabled) {
|
|
126
|
+
button.classList.add('disabled')
|
|
127
|
+
} else if (canEdit) {
|
|
128
|
+
button.addEventListener('mousedown', () => {
|
|
129
|
+
const menu = createMenu(
|
|
130
|
+
props,
|
|
131
|
+
currentCategory,
|
|
132
|
+
categories,
|
|
133
|
+
usedCategoryIDs,
|
|
134
|
+
handleSelect
|
|
135
|
+
)
|
|
136
|
+
props.popper.show(button, menu, 'bottom-end', false)
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return button
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const buildPluginState = (
|
|
144
|
+
state: EditorState,
|
|
145
|
+
props: EditorProps
|
|
146
|
+
): PluginState => {
|
|
147
|
+
const decorations: Decoration[] = []
|
|
148
|
+
const can = props.getCapabilities()
|
|
149
|
+
const categories = props.sectionCategories
|
|
150
|
+
const usedCategoryIDs = getUsedSectionCategoryIDs(state)
|
|
151
|
+
const canEdit = !!can?.editArticle
|
|
152
|
+
|
|
153
|
+
state.doc.descendants((node, pos) => {
|
|
154
|
+
if (node.type === schema.nodes.box_element) {
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
if (isSectionNode(node)) {
|
|
158
|
+
const categoryID = node.attrs.category
|
|
159
|
+
const category = categories.get(categoryID)
|
|
160
|
+
const $pos = state.doc.resolve(pos)
|
|
161
|
+
const group = getGroup($pos)
|
|
162
|
+
const groupCategories = getGroupCategories(categories, group)
|
|
163
|
+
|
|
164
|
+
const numOptions = groupCategories.length
|
|
165
|
+
|
|
166
|
+
const shouldShow = !!category || (canEdit && numOptions >= 2)
|
|
167
|
+
|
|
168
|
+
if (shouldShow) {
|
|
169
|
+
const isEditable = canEdit && numOptions >= 2
|
|
170
|
+
|
|
171
|
+
decorations.push(
|
|
172
|
+
Decoration.widget(pos + 1, (view) =>
|
|
173
|
+
createButton(
|
|
174
|
+
props,
|
|
175
|
+
view,
|
|
176
|
+
pos,
|
|
177
|
+
category,
|
|
178
|
+
groupCategories,
|
|
179
|
+
usedCategoryIDs,
|
|
180
|
+
isEditable,
|
|
181
|
+
categories.size === 0
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
return false
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
return { decorations: DecorationSet.create(state.doc, decorations) }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const getUsedSectionCategoryIDs = (state: EditorState): Set<string> => {
|
|
194
|
+
const sections = findChildrenByType(state.doc, schema.nodes.section)
|
|
195
|
+
const used = new Set<string>()
|
|
196
|
+
sections.forEach(({ node }) => {
|
|
197
|
+
if (node.attrs.category) {
|
|
198
|
+
used.add(node.attrs.category)
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
return used
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const getGroup = ($pos: ResolvedPos) => {
|
|
205
|
+
if (findParentNodeOfTypeClosestToPos($pos, schema.nodes.abstracts)) {
|
|
206
|
+
return 'abstracts'
|
|
207
|
+
}
|
|
208
|
+
if (findParentNodeOfTypeClosestToPos($pos, schema.nodes.backmatter)) {
|
|
209
|
+
return 'backmatter'
|
|
210
|
+
}
|
|
211
|
+
return 'body'
|
|
212
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
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 { ManuscriptNode, schema, SectionCategory } from '@manuscripts/transform'
|
|
17
|
+
import { EditorState } from 'prosemirror-state'
|
|
18
|
+
import { findParentNodeOfTypeClosestToPos } from 'prosemirror-utils'
|
|
19
|
+
|
|
20
|
+
import { getEditorProps } from '../editor-props'
|
|
21
|
+
import { getActualTextContent } from '@manuscripts/track-changes-plugin'
|
|
22
|
+
|
|
23
|
+
function cursorAtTheEndOfText(
|
|
24
|
+
state: EditorState,
|
|
25
|
+
nodeSize: number,
|
|
26
|
+
nodePos: number
|
|
27
|
+
) {
|
|
28
|
+
const { from, to } = state.selection
|
|
29
|
+
return from === to && to === nodePos + nodeSize - 1
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const isUpperCase = (test: string) =>
|
|
33
|
+
test === test.toUpperCase() && test.length > 1
|
|
34
|
+
|
|
35
|
+
export function hasAutoCompletionSlack(
|
|
36
|
+
node: ManuscriptNode,
|
|
37
|
+
category: SectionCategory
|
|
38
|
+
) {
|
|
39
|
+
const titles = category.titles
|
|
40
|
+
|
|
41
|
+
if (titles.length && node.textContent) {
|
|
42
|
+
const actualTextContent = getActualTextContent(node.content)
|
|
43
|
+
const title = titles.find((t) =>
|
|
44
|
+
t.toLowerCase().startsWith(actualTextContent.toLowerCase())
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if (title) {
|
|
48
|
+
const suggestionPart = title.slice(actualTextContent.length)
|
|
49
|
+
const suggestion = isUpperCase(actualTextContent)
|
|
50
|
+
? suggestionPart.toUpperCase()
|
|
51
|
+
: suggestionPart
|
|
52
|
+
return {
|
|
53
|
+
suggestion,
|
|
54
|
+
title: isUpperCase(actualTextContent) ? title.toUpperCase() : title,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function checkForCompletion(state: EditorState) {
|
|
62
|
+
const section = findParentNodeOfTypeClosestToPos(
|
|
63
|
+
state.selection.$from,
|
|
64
|
+
schema.nodes.section
|
|
65
|
+
)
|
|
66
|
+
if (!section) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const title = findParentNodeOfTypeClosestToPos(
|
|
71
|
+
state.selection.$from,
|
|
72
|
+
schema.nodes.section_title
|
|
73
|
+
)
|
|
74
|
+
if (!title || !cursorAtTheEndOfText(state, title.node.nodeSize, title.pos)) {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const props = getEditorProps(state)
|
|
79
|
+
const category = props.sectionCategories.get(section.node.attrs.category)
|
|
80
|
+
|
|
81
|
+
if (category) {
|
|
82
|
+
const text = hasAutoCompletionSlack(title.node, category)
|
|
83
|
+
if (text) {
|
|
84
|
+
return text
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
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 { schema } from '@manuscripts/transform'
|
|
17
|
+
import { Node as ProseMirrorNode } from 'prosemirror-model'
|
|
18
|
+
import { EditorState, Plugin, PluginKey, Transaction } from 'prosemirror-state'
|
|
19
|
+
import { findChildrenByType } from 'prosemirror-utils'
|
|
20
|
+
import { Decoration, DecorationSet } from 'prosemirror-view'
|
|
21
|
+
|
|
22
|
+
import { checkForCompletion } from './autocompletion'
|
|
23
|
+
import { clear } from '@manuscripts/track-changes-plugin'
|
|
24
|
+
|
|
25
|
+
type NumberingArray = number[]
|
|
26
|
+
export type PluginState = Map<string, string>
|
|
27
|
+
|
|
28
|
+
export const sectionTitleKey = new PluginKey<PluginState>('sectionNumbering')
|
|
29
|
+
|
|
30
|
+
const calculateSectionLevels = (
|
|
31
|
+
node: ProseMirrorNode,
|
|
32
|
+
startPos: number,
|
|
33
|
+
sectionNumberMap: Map<string, string>,
|
|
34
|
+
numbering: NumberingArray = [0]
|
|
35
|
+
) => {
|
|
36
|
+
clear(node).forEach((childNode, offset) => {
|
|
37
|
+
if (
|
|
38
|
+
childNode.type === schema.nodes.section ||
|
|
39
|
+
childNode.type === schema.nodes.box_element
|
|
40
|
+
) {
|
|
41
|
+
numbering[numbering.length - 1] += 1
|
|
42
|
+
const sectionNumber = numbering.join('.')
|
|
43
|
+
const sectionStartPos = startPos + offset + 1
|
|
44
|
+
|
|
45
|
+
childNode.forEach((innerChildNode) => {
|
|
46
|
+
if (innerChildNode.type === schema.nodes.section_title) {
|
|
47
|
+
sectionNumberMap.set(childNode.attrs.id, sectionNumber)
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// Process child nodes to handle subsections
|
|
52
|
+
calculateSectionLevels(childNode, sectionStartPos, sectionNumberMap, [
|
|
53
|
+
...numbering,
|
|
54
|
+
0,
|
|
55
|
+
])
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const getPluginState = (doc: ProseMirrorNode): PluginState => {
|
|
61
|
+
const bodyNodes = findChildrenByType(doc, schema.nodes.body)
|
|
62
|
+
const bodyNode = bodyNodes[0]
|
|
63
|
+
const sectionNumberMap = new Map<string, string>()
|
|
64
|
+
calculateSectionLevels(bodyNode.node, bodyNode.pos, sectionNumberMap)
|
|
65
|
+
|
|
66
|
+
return sectionNumberMap
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default () => {
|
|
70
|
+
return new Plugin<PluginState>({
|
|
71
|
+
key: sectionTitleKey,
|
|
72
|
+
props: {
|
|
73
|
+
decorations(state) {
|
|
74
|
+
const text = checkForCompletion(state)
|
|
75
|
+
if (text) {
|
|
76
|
+
const decoration = Decoration.widget(state.selection.from, () => {
|
|
77
|
+
const node = document.createElement('span')
|
|
78
|
+
node.classList.add('completion-bearer')
|
|
79
|
+
node.dataset.suggest = text.suggestion
|
|
80
|
+
return node
|
|
81
|
+
})
|
|
82
|
+
return DecorationSet.create(state.doc, [decoration])
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return DecorationSet.empty
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
state: {
|
|
89
|
+
init: (_, state: EditorState) => {
|
|
90
|
+
return getPluginState(state.doc)
|
|
91
|
+
},
|
|
92
|
+
apply: (
|
|
93
|
+
tr: Transaction,
|
|
94
|
+
oldSectionNumberMap: PluginState,
|
|
95
|
+
oldState: EditorState,
|
|
96
|
+
newState: EditorState
|
|
97
|
+
) => {
|
|
98
|
+
if (tr.docChanged) {
|
|
99
|
+
return getPluginState(newState.doc)
|
|
100
|
+
}
|
|
101
|
+
return oldSectionNumberMap
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
}
|