@manuscripts/body-editor 3.12.67 → 3.13.1
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,298 @@
|
|
|
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 { skipTracking } from '@manuscripts/track-changes-plugin'
|
|
18
|
+
import {
|
|
19
|
+
FootnoteNode,
|
|
20
|
+
FootnotesElementNode,
|
|
21
|
+
generateAlphaFootnoteLabel,
|
|
22
|
+
InlineFootnoteNode,
|
|
23
|
+
isTableElementNode,
|
|
24
|
+
ManuscriptNode,
|
|
25
|
+
schema,
|
|
26
|
+
} from '@manuscripts/transform'
|
|
27
|
+
import { isEqual } from 'lodash'
|
|
28
|
+
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state'
|
|
29
|
+
import { findChildrenByType } from 'prosemirror-utils'
|
|
30
|
+
import { Decoration, DecorationSet } from 'prosemirror-view'
|
|
31
|
+
|
|
32
|
+
import { EditorProps } from '../configs/ManuscriptsEditor'
|
|
33
|
+
import { findNodeByID } from '../lib/doc'
|
|
34
|
+
import {
|
|
35
|
+
findFootnotesContainerNode,
|
|
36
|
+
findParentFootnote,
|
|
37
|
+
} from '../lib/footnotes'
|
|
38
|
+
import { isInit } from '../lib/plugins'
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The state for a single footnotes_element.
|
|
42
|
+
*/
|
|
43
|
+
export type FootnotesElementState = {
|
|
44
|
+
/**
|
|
45
|
+
* A list of inline_footnote nodes that reference footnote nodes in
|
|
46
|
+
* this element.
|
|
47
|
+
*/
|
|
48
|
+
inlineFootnotes: [InlineFootnoteNode, number][]
|
|
49
|
+
/**
|
|
50
|
+
* A set of footnote node IDs that are not referenced from any
|
|
51
|
+
* inline_footnote node.
|
|
52
|
+
*/
|
|
53
|
+
unusedFootnoteIDs: Set<string>
|
|
54
|
+
/**
|
|
55
|
+
* A list of footnote nodes, in reference order. i.e. footnote nodes
|
|
56
|
+
* that are referenced earlier appear earlier in the list.
|
|
57
|
+
*/
|
|
58
|
+
footnotes: [FootnoteNode, number][]
|
|
59
|
+
element: [FootnotesElementNode, number]
|
|
60
|
+
/**
|
|
61
|
+
* footnote node ID => label
|
|
62
|
+
*/
|
|
63
|
+
labels: Map<string, string>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type FootnotesPluginState = {
|
|
67
|
+
/**
|
|
68
|
+
* footnote_element ID => state
|
|
69
|
+
*/
|
|
70
|
+
footnotesElements: Map<string, FootnotesElementState>
|
|
71
|
+
/**
|
|
72
|
+
* Tracks the footnote_element node ID corresponding to a few important
|
|
73
|
+
* node IDs. The node IDs tracked are:
|
|
74
|
+
* - inline_footnote node IDs
|
|
75
|
+
* - footnote node IDs
|
|
76
|
+
* - table_element node IDs
|
|
77
|
+
*/
|
|
78
|
+
footnotesElementIDs: Map<string, string>
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const footnotesKey = new PluginKey<FootnotesPluginState>('footnotes')
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* This plugin provides support of footnotes related behaviours
|
|
85
|
+
*/
|
|
86
|
+
const buildPluginState = (doc: ManuscriptNode) => {
|
|
87
|
+
const states = new Map()
|
|
88
|
+
const ids = new Map()
|
|
89
|
+
const elements = findChildrenByType(doc, schema.nodes.footnotes_element)
|
|
90
|
+
elements.map(({ node, pos }) => {
|
|
91
|
+
const container = findFootnotesContainerNode(doc, pos)
|
|
92
|
+
const newState = buildFootnotesElementState(
|
|
93
|
+
[container.node, container.pos],
|
|
94
|
+
[node as FootnotesElementNode, pos]
|
|
95
|
+
)
|
|
96
|
+
const elementID = node.attrs.id
|
|
97
|
+
states.set(elementID, newState)
|
|
98
|
+
newState.footnotes.forEach(([n]) => ids.set(n.attrs.id, elementID))
|
|
99
|
+
newState.inlineFootnotes.forEach(([n]) => ids.set(n.attrs.id, elementID))
|
|
100
|
+
ids.set(container.node.attrs.id, elementID)
|
|
101
|
+
})
|
|
102
|
+
return {
|
|
103
|
+
footnotesElements: states,
|
|
104
|
+
footnotesElementIDs: ids,
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Build the state for a single footnotes_element node.
|
|
110
|
+
*/
|
|
111
|
+
const buildFootnotesElementState = (
|
|
112
|
+
container: [ManuscriptNode, number],
|
|
113
|
+
element: [FootnotesElementNode, number]
|
|
114
|
+
): FootnotesElementState => {
|
|
115
|
+
const fn: FootnotesElementState = {
|
|
116
|
+
element,
|
|
117
|
+
inlineFootnotes: [],
|
|
118
|
+
unusedFootnoteIDs: new Set(),
|
|
119
|
+
footnotes: [],
|
|
120
|
+
labels: new Map(),
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// process footnote nodes
|
|
124
|
+
const footnotes = findChildrenByType(element[0], schema.nodes.footnote)
|
|
125
|
+
const footnoteIDs = new Set()
|
|
126
|
+
footnotes.forEach(({ node, pos }) => {
|
|
127
|
+
fn.footnotes.push([node as FootnoteNode, element[1] + pos + 1])
|
|
128
|
+
// this set will start with every footnote node ID. Used IDs
|
|
129
|
+
// will be removed from it while processing inline_footnote nodes.
|
|
130
|
+
fn.unusedFootnoteIDs.add(node.attrs.id)
|
|
131
|
+
footnoteIDs.add(node.attrs.id)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// process inline_footnote nodes
|
|
135
|
+
let index = 0
|
|
136
|
+
const inlineFootnotes = findChildrenByType(
|
|
137
|
+
container[0],
|
|
138
|
+
schema.nodes.inline_footnote
|
|
139
|
+
)
|
|
140
|
+
inlineFootnotes.sort((a, b) => a.pos - b.pos)
|
|
141
|
+
// this is used to track footnote node IDs based on the order of
|
|
142
|
+
// the corresponding inline_footnote nodes. It will be used later to
|
|
143
|
+
// properly generate labels for footnotes, and to order the footnotes
|
|
144
|
+
// in the state object.
|
|
145
|
+
const orderedFootnoteIDs: string[] = []
|
|
146
|
+
inlineFootnotes.forEach(({ node, pos }) => {
|
|
147
|
+
const inlineFootnote = node as InlineFootnoteNode
|
|
148
|
+
const rids = inlineFootnote.attrs.rids.filter((rid) => footnoteIDs.has(rid))
|
|
149
|
+
if (!rids.length) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
if (container[1]) {
|
|
153
|
+
pos += container[1] + 1
|
|
154
|
+
}
|
|
155
|
+
fn.inlineFootnotes.push([node as InlineFootnoteNode, pos])
|
|
156
|
+
const label = rids
|
|
157
|
+
.map((rid) => {
|
|
158
|
+
//this is to ensure footnotes are processed only once
|
|
159
|
+
if (fn.labels.has(rid)) {
|
|
160
|
+
return fn.labels.get(rid)
|
|
161
|
+
} else {
|
|
162
|
+
const label = isTableElementNode(container[0])
|
|
163
|
+
? String(++index)
|
|
164
|
+
: generateAlphaFootnoteLabel(index++)
|
|
165
|
+
fn.labels.set(rid, label)
|
|
166
|
+
fn.unusedFootnoteIDs.delete(rid)
|
|
167
|
+
orderedFootnoteIDs.push(rid)
|
|
168
|
+
return label
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
.join(', ')
|
|
172
|
+
fn.labels.set(node.attrs.id, label)
|
|
173
|
+
})
|
|
174
|
+
// At this point, the set includes only footnote node IDs that have no
|
|
175
|
+
// corresponding inline_footnote. These should be added to the end
|
|
176
|
+
// of the footnote node list.
|
|
177
|
+
fn.unusedFootnoteIDs.forEach((id) => orderedFootnoteIDs.push(id))
|
|
178
|
+
// Now the footnote list is sorted based on the appearance of the
|
|
179
|
+
// corresponding inline_footnote nodes.
|
|
180
|
+
fn.footnotes.sort(
|
|
181
|
+
([a], [b]) =>
|
|
182
|
+
orderedFootnoteIDs.indexOf(a.attrs.id) -
|
|
183
|
+
orderedFootnoteIDs.indexOf(b.attrs.id)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
return fn
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const hasChanged = (
|
|
190
|
+
$new: FootnotesElementState,
|
|
191
|
+
$old?: FootnotesElementState
|
|
192
|
+
) => {
|
|
193
|
+
const nids = $new.footnotes.map(([node]) => node.attrs.id)
|
|
194
|
+
const oids = $old?.footnotes.map(([node]) => node.attrs.id)
|
|
195
|
+
return !(
|
|
196
|
+
isEqual(nids, oids) &&
|
|
197
|
+
isEqual($new.unusedFootnoteIDs, $old?.unusedFootnoteIDs)
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export default (props: EditorProps) => {
|
|
202
|
+
return new Plugin<FootnotesPluginState>({
|
|
203
|
+
key: footnotesKey,
|
|
204
|
+
state: {
|
|
205
|
+
init(config, instance): FootnotesPluginState {
|
|
206
|
+
return buildPluginState(instance.doc)
|
|
207
|
+
},
|
|
208
|
+
apply(tr, value, oldState, newState): FootnotesPluginState {
|
|
209
|
+
const $old = footnotesKey.getState(oldState)
|
|
210
|
+
if (!tr.docChanged && $old) {
|
|
211
|
+
return $old
|
|
212
|
+
}
|
|
213
|
+
return buildPluginState(newState.doc)
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
appendTransaction(transactions, oldState, newState) {
|
|
217
|
+
if (!transactions.find((tr) => tr.docChanged || isInit(tr))) {
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
const $old = footnotesKey.getState(oldState)
|
|
221
|
+
const $new = footnotesKey.getState(newState)
|
|
222
|
+
if (!$new) {
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const tr = newState.tr
|
|
227
|
+
|
|
228
|
+
$new.footnotesElements.forEach((newState, key) => {
|
|
229
|
+
const element = newState.element[0]
|
|
230
|
+
const pos = newState.element[1]
|
|
231
|
+
const oldState = $old?.footnotesElements.get(key)
|
|
232
|
+
const footnotes = newState.footnotes.map(([node]) => node)
|
|
233
|
+
|
|
234
|
+
if (hasChanged(newState, oldState)) {
|
|
235
|
+
const newElement = schema.nodes.footnotes_element.create(
|
|
236
|
+
element.attrs,
|
|
237
|
+
// footnotes here is already in the correct order.
|
|
238
|
+
footnotes
|
|
239
|
+
)
|
|
240
|
+
tr.replaceWith(pos, pos + element.nodeSize, newElement)
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
if (!tr.steps.length) {
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// this is to ensure that the selection remains valid if it
|
|
249
|
+
// was within a footnotes_element node that was replaced.
|
|
250
|
+
const selection = newState.selection
|
|
251
|
+
const selectedFootnote = findParentFootnote(selection)
|
|
252
|
+
if (selectedFootnote) {
|
|
253
|
+
const pos = findNodeByID(tr.doc, selectedFootnote.node.attrs.id)?.pos
|
|
254
|
+
if (pos) {
|
|
255
|
+
tr.setSelection(TextSelection.create(tr.doc, pos + 2))
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
skipTracking(tr)
|
|
260
|
+
return tr
|
|
261
|
+
},
|
|
262
|
+
props: {
|
|
263
|
+
decorations: (state) => {
|
|
264
|
+
const fns = footnotesKey.getState(state)
|
|
265
|
+
if (!fns) {
|
|
266
|
+
return DecorationSet.empty
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const decorations: Decoration[] = []
|
|
270
|
+
fns.footnotesElements.forEach((fn) => {
|
|
271
|
+
fn.footnotes.forEach(([node, pos]) => {
|
|
272
|
+
const to = pos + node.nodeSize
|
|
273
|
+
const label = fn.labels.get(node.attrs.id)
|
|
274
|
+
decorations.push(Decoration.node(pos, to, {}, { label }))
|
|
275
|
+
})
|
|
276
|
+
fn.inlineFootnotes.forEach(([node, pos]) => {
|
|
277
|
+
const to = pos + node.nodeSize
|
|
278
|
+
const label = fn.labels.get(node.attrs.id)
|
|
279
|
+
decorations.push(Decoration.node(pos, to, {}, { label }))
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
if (props.getCapabilities().editArticle) {
|
|
283
|
+
const footnote = findParentFootnote(state.selection)
|
|
284
|
+
if (footnote) {
|
|
285
|
+
const pos = footnote.pos
|
|
286
|
+
const to = pos + footnote.node.nodeSize
|
|
287
|
+
decorations.push(
|
|
288
|
+
Decoration.node(pos, to, {
|
|
289
|
+
class: 'footnote-selected',
|
|
290
|
+
})
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return DecorationSet.create(state.doc, decorations)
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
})
|
|
298
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
CHANGE_STATUS,
|
|
18
|
+
skipTracking,
|
|
19
|
+
TrackChangesAction,
|
|
20
|
+
} from '@manuscripts/track-changes-plugin'
|
|
21
|
+
import { schema } from '@manuscripts/transform'
|
|
22
|
+
import { NodeSelection, Plugin } from 'prosemirror-state'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* This plugin make sure unselected link with empty href get deleted
|
|
26
|
+
*/
|
|
27
|
+
export default () =>
|
|
28
|
+
new Plugin({
|
|
29
|
+
appendTransaction(transactions, oldState, newState) {
|
|
30
|
+
if (
|
|
31
|
+
!transactions.some(
|
|
32
|
+
(tr) =>
|
|
33
|
+
oldState.selection instanceof NodeSelection &&
|
|
34
|
+
oldState.selection.node.type === schema.nodes.link &&
|
|
35
|
+
(tr.getMeta(TrackChangesAction.setChangeStatuses)?.status ===
|
|
36
|
+
CHANGE_STATUS.accepted ||
|
|
37
|
+
(tr.getMeta('pointer') && tr.selectionSet))
|
|
38
|
+
)
|
|
39
|
+
) {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
const {
|
|
43
|
+
$from: { pos },
|
|
44
|
+
node,
|
|
45
|
+
} = oldState.selection as NodeSelection
|
|
46
|
+
if (!node.content.size || !node.attrs.href) {
|
|
47
|
+
const tr = newState.tr.setMeta('clear-empty-link', true)
|
|
48
|
+
tr.delete(pos, pos + node.nodeSize)
|
|
49
|
+
return skipTracking(tr.insert(pos, node.content))
|
|
50
|
+
}
|
|
51
|
+
return null
|
|
52
|
+
},
|
|
53
|
+
})
|
|
@@ -0,0 +1,136 @@
|
|
|
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 { schema } from '@manuscripts/transform'
|
|
18
|
+
import { Node } from 'prosemirror-model'
|
|
19
|
+
import { Plugin } from 'prosemirror-state'
|
|
20
|
+
import { Decoration, DecorationSet } from 'prosemirror-view'
|
|
21
|
+
|
|
22
|
+
import { lockIcon } from '../icons'
|
|
23
|
+
import { isBodyLocked, isSelectionInBody } from '../lib/utils'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* This plugin prevents editing in the body of the document when it is locked.
|
|
27
|
+
* It also adds a lock icon to indicate that the body is non-editable.
|
|
28
|
+
*/
|
|
29
|
+
export default () => {
|
|
30
|
+
return new Plugin({
|
|
31
|
+
state: {
|
|
32
|
+
init(_, state) {
|
|
33
|
+
const decorations = isBodyLocked(state) ? getDecorations(state.doc) : []
|
|
34
|
+
return DecorationSet.create(state.doc, decorations)
|
|
35
|
+
},
|
|
36
|
+
apply(tr, oldDecorationSet, oldState, newState) {
|
|
37
|
+
if (
|
|
38
|
+
tr.docChanged ||
|
|
39
|
+
isBodyLocked(oldState) !== isBodyLocked(newState)
|
|
40
|
+
) {
|
|
41
|
+
const decorations = isBodyLocked(newState)
|
|
42
|
+
? getDecorations(newState.doc)
|
|
43
|
+
: []
|
|
44
|
+
return DecorationSet.create(newState.doc, decorations)
|
|
45
|
+
}
|
|
46
|
+
return oldDecorationSet
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
props: {
|
|
50
|
+
decorations(state) {
|
|
51
|
+
return this.getState(state) || DecorationSet.empty
|
|
52
|
+
},
|
|
53
|
+
handleDOMEvents: {
|
|
54
|
+
beforeinput(view, event) {
|
|
55
|
+
if (isSelectionInBody(view.state) && isBodyLocked(view.state)) {
|
|
56
|
+
event.preventDefault()
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
return false
|
|
60
|
+
},
|
|
61
|
+
keydown(view, event) {
|
|
62
|
+
if (isSelectionInBody(view.state) && isBodyLocked(view.state)) {
|
|
63
|
+
event.preventDefault()
|
|
64
|
+
return true
|
|
65
|
+
}
|
|
66
|
+
return false
|
|
67
|
+
},
|
|
68
|
+
paste(view, event) {
|
|
69
|
+
if (isSelectionInBody(view.state) && isBodyLocked(view.state)) {
|
|
70
|
+
event.preventDefault()
|
|
71
|
+
return true
|
|
72
|
+
}
|
|
73
|
+
return false
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
handleClickOn: (view, pos, node, nodePos, event) => {
|
|
77
|
+
if (isSelectionInBody(view.state) && isBodyLocked(view.state)) {
|
|
78
|
+
event.preventDefault()
|
|
79
|
+
return true
|
|
80
|
+
}
|
|
81
|
+
return false
|
|
82
|
+
},
|
|
83
|
+
handleTripleClickOn: (view, pos, node, nodePos, event) => {
|
|
84
|
+
event.preventDefault()
|
|
85
|
+
return true
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const getDecorations = (doc: Node): Decoration[] => {
|
|
92
|
+
const decorations: Decoration[] = []
|
|
93
|
+
doc.descendants((node, pos) => {
|
|
94
|
+
if (node.type === schema.nodes.body) {
|
|
95
|
+
decorations.push(
|
|
96
|
+
Decoration.node(pos, pos + node.nodeSize, { class: 'non-editable' })
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
decorations.push(
|
|
100
|
+
Decoration.widget(pos + 1, () => {
|
|
101
|
+
const div = document.createElement('div')
|
|
102
|
+
div.innerHTML = lockIcon
|
|
103
|
+
div.className = 'icon'
|
|
104
|
+
return div
|
|
105
|
+
})
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
decorations.push(
|
|
109
|
+
Decoration.widget(
|
|
110
|
+
pos + 1,
|
|
111
|
+
() => {
|
|
112
|
+
const overlay = document.createElement('div')
|
|
113
|
+
overlay.className = 'body-overlay'
|
|
114
|
+
return overlay
|
|
115
|
+
},
|
|
116
|
+
{ side: 1 }
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (node.type.name === 'figure') {
|
|
122
|
+
decorations.push(
|
|
123
|
+
Decoration.widget(
|
|
124
|
+
pos + 1,
|
|
125
|
+
() => {
|
|
126
|
+
const overlay = document.createElement('div')
|
|
127
|
+
overlay.className = 'figure-overlay'
|
|
128
|
+
return overlay
|
|
129
|
+
},
|
|
130
|
+
{ side: 1 }
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
return decorations
|
|
136
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
CHANGE_OPERATION,
|
|
19
|
+
CHANGE_STATUS,
|
|
20
|
+
TrackedAttrs,
|
|
21
|
+
} from '@manuscripts/track-changes-plugin'
|
|
22
|
+
import { Plugin } from 'prosemirror-state'
|
|
23
|
+
import { Decoration, DecorationSet } from 'prosemirror-view'
|
|
24
|
+
|
|
25
|
+
export default () => {
|
|
26
|
+
return new Plugin({
|
|
27
|
+
props: {
|
|
28
|
+
decorations(state) {
|
|
29
|
+
const decos: Decoration[] = []
|
|
30
|
+
|
|
31
|
+
// Find all nodes that are deleted parts of move operations
|
|
32
|
+
state.doc.descendants((node, pos) => {
|
|
33
|
+
const moveTrack = node.attrs.dataTracked?.find(
|
|
34
|
+
(t: TrackedAttrs) =>
|
|
35
|
+
t.operation === CHANGE_OPERATION.delete &&
|
|
36
|
+
t.status === CHANGE_STATUS.pending &&
|
|
37
|
+
t.moveNodeId // Has an associated move operation
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if (moveTrack) {
|
|
41
|
+
decos.push(
|
|
42
|
+
Decoration.node(pos, pos + node.nodeSize, {
|
|
43
|
+
class: 'move-deleted-node',
|
|
44
|
+
'data-move-id': moveTrack.moveNodeId,
|
|
45
|
+
'data-original-id': node.attrs.id,
|
|
46
|
+
})
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return DecorationSet.create(state.doc, decos)
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* © 2019 Atypon Systems LLC
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
buildTargets,
|
|
19
|
+
ManuscriptNode,
|
|
20
|
+
schema,
|
|
21
|
+
Target,
|
|
22
|
+
} from '@manuscripts/transform'
|
|
23
|
+
import { Plugin, PluginKey } from 'prosemirror-state'
|
|
24
|
+
import { Decoration, DecorationSet } from 'prosemirror-view'
|
|
25
|
+
|
|
26
|
+
import { findChildren } from 'prosemirror-utils'
|
|
27
|
+
import { clear } from '@manuscripts/track-changes-plugin'
|
|
28
|
+
|
|
29
|
+
export const objectsKey = new PluginKey<Map<string, Target>>('objects')
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* This plugin sets the labels for cross-references, and adds the label as a decoration to cross-referenceable elements.
|
|
33
|
+
*/
|
|
34
|
+
export default () => {
|
|
35
|
+
return new Plugin<Map<string, Target>>({
|
|
36
|
+
key: objectsKey,
|
|
37
|
+
|
|
38
|
+
state: {
|
|
39
|
+
init: (config, state) => {
|
|
40
|
+
// Build targets from filtered document to exclude moved nodes
|
|
41
|
+
return buildTargets(clear(state.doc).descendants) // @TODO - make sure binding works ok here - same problem as in transform
|
|
42
|
+
},
|
|
43
|
+
apply: (tr) => {
|
|
44
|
+
// TODO: use decorations to track figure deletion?
|
|
45
|
+
// TODO: map decorations?
|
|
46
|
+
// TODO: use setMeta to update labels
|
|
47
|
+
|
|
48
|
+
// Build targets from filtered document to exclude moved nodes
|
|
49
|
+
return buildTargets(clear(tr.doc).descendants)
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
props: {
|
|
53
|
+
decorations: (state) => {
|
|
54
|
+
const decorations: Decoration[] = []
|
|
55
|
+
const targets = objectsKey.getState(state)
|
|
56
|
+
|
|
57
|
+
if (targets) {
|
|
58
|
+
state.doc.descendants((node, pos) => {
|
|
59
|
+
const { id } = node.attrs
|
|
60
|
+
if (id) {
|
|
61
|
+
const target = targets.get(id)
|
|
62
|
+
|
|
63
|
+
if (target) {
|
|
64
|
+
const caption = findChildren(
|
|
65
|
+
node,
|
|
66
|
+
(node) =>
|
|
67
|
+
node.type === schema.nodes.caption ||
|
|
68
|
+
node.type === schema.nodes.caption_title,
|
|
69
|
+
false
|
|
70
|
+
)[0]
|
|
71
|
+
|
|
72
|
+
if (caption) {
|
|
73
|
+
let from: number
|
|
74
|
+
let to: number
|
|
75
|
+
if (caption.node.type === schema.nodes.caption_title) {
|
|
76
|
+
from = pos + 1 + caption.pos
|
|
77
|
+
to = from + caption.node.nodeSize
|
|
78
|
+
} else {
|
|
79
|
+
// caption node: decorate its first child (text_block) so the label
|
|
80
|
+
// appears inline before the caption text
|
|
81
|
+
const firstChild = caption.node.firstChild
|
|
82
|
+
from = pos + 1 + caption.pos + 1
|
|
83
|
+
to = from + (firstChild?.nodeSize || caption.node.nodeSize)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
decorations.push(
|
|
87
|
+
Decoration.node(from, to, {
|
|
88
|
+
'element-label': target.label,
|
|
89
|
+
})
|
|
90
|
+
)
|
|
91
|
+
} else {
|
|
92
|
+
// No caption at all — fall back to a widget label at the end of the node
|
|
93
|
+
const labelPos = getDecorationPos(target, state.doc, pos)
|
|
94
|
+
const labelNode = document.createElement('span')
|
|
95
|
+
labelNode.className = 'element-label'
|
|
96
|
+
labelNode.textContent = target.label
|
|
97
|
+
decorations.push(
|
|
98
|
+
Decoration.widget(labelPos, labelNode, {
|
|
99
|
+
side: -1,
|
|
100
|
+
key: `element-label-${id}-${target.label}`,
|
|
101
|
+
})
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
return DecorationSet.create(state.doc, decorations)
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Returns the position for a widget decoration when there is no caption.
|
|
116
|
+
* Falls back to the end of the node.
|
|
117
|
+
*/
|
|
118
|
+
const getDecorationPos = (
|
|
119
|
+
target: Target,
|
|
120
|
+
doc: ManuscriptNode,
|
|
121
|
+
pos: number
|
|
122
|
+
) => {
|
|
123
|
+
const $pos = doc.resolve(pos + 1)
|
|
124
|
+
return $pos.end()
|
|
125
|
+
}
|