@manuscripts/body-editor 3.12.67 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,361 @@
|
|
|
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 { ManuscriptNode, schema } from '@manuscripts/transform'
|
|
18
|
+
import { findParentNodeOfTypeClosestToPos } from 'prosemirror-utils'
|
|
19
|
+
|
|
20
|
+
import { draggableIcon } from '../icons'
|
|
21
|
+
import {
|
|
22
|
+
addInteractionHandlers,
|
|
23
|
+
createFileHandlers,
|
|
24
|
+
createFileUploader,
|
|
25
|
+
createMediaPlaceholder,
|
|
26
|
+
createReactTools,
|
|
27
|
+
createUnsupportedFormat,
|
|
28
|
+
MediaType,
|
|
29
|
+
} from '../lib/media'
|
|
30
|
+
import { createEditableNodeView } from './creators'
|
|
31
|
+
import { FigureView } from './figure'
|
|
32
|
+
import { isDeleted } from '@manuscripts/track-changes-plugin'
|
|
33
|
+
|
|
34
|
+
export class FigureEditableView extends FigureView {
|
|
35
|
+
public reactTools: HTMLDivElement | null = null
|
|
36
|
+
positionMenuWrapper: HTMLDivElement
|
|
37
|
+
figurePosition: string
|
|
38
|
+
private dragHandle: HTMLDivElement | undefined
|
|
39
|
+
private static currentDragFigureId: string | null = null
|
|
40
|
+
private dragAndDropInitialized = false
|
|
41
|
+
|
|
42
|
+
public initialise() {
|
|
43
|
+
this.upload = this.upload.bind(this)
|
|
44
|
+
this.createDOM()
|
|
45
|
+
this.updateContents()
|
|
46
|
+
const domElement = this.dom as HTMLElement & {
|
|
47
|
+
__figureView?: FigureEditableView
|
|
48
|
+
}
|
|
49
|
+
domElement.__figureView = this
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public update(newNode: ManuscriptNode): boolean {
|
|
53
|
+
const handledBySuper = super.update(newNode)
|
|
54
|
+
if (handledBySuper) {
|
|
55
|
+
this.addTools()
|
|
56
|
+
}
|
|
57
|
+
return handledBySuper
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private clearTargetClass(
|
|
61
|
+
target: Element,
|
|
62
|
+
classes: string[] = ['drop-target-above', 'drop-target-below']
|
|
63
|
+
) {
|
|
64
|
+
target.classList.remove(...classes)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private handleDragStart() {
|
|
68
|
+
const figureId = this.node.attrs.id
|
|
69
|
+
FigureEditableView.currentDragFigureId = figureId
|
|
70
|
+
this.container.classList.add('dragging')
|
|
71
|
+
// Add drag-active to siblings only
|
|
72
|
+
const parent = this.container.parentElement
|
|
73
|
+
if (parent) {
|
|
74
|
+
const siblingFigures = parent.querySelectorAll('.figure')
|
|
75
|
+
siblingFigures.forEach((el) => {
|
|
76
|
+
if (el !== this.container) {
|
|
77
|
+
el.classList.add('drag-active')
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private setupDragAndDrop() {
|
|
84
|
+
this.container.draggable = true
|
|
85
|
+
|
|
86
|
+
// Drag events for container
|
|
87
|
+
this.container.addEventListener('dragstart', () => {
|
|
88
|
+
// Only start figure dragging if we have a figure ID and figure is not deleted
|
|
89
|
+
if (this.node.attrs.id && !isDeleted(this.node)) {
|
|
90
|
+
this.handleDragStart()
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
// Drag events for drag handle (if present)
|
|
95
|
+
if (this.dragHandle) {
|
|
96
|
+
this.dragHandle.addEventListener('dragstart', () => {
|
|
97
|
+
this.handleDragStart()
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.container.addEventListener('dragend', () => {
|
|
102
|
+
// Clear the static variable when drag ends
|
|
103
|
+
FigureEditableView.currentDragFigureId = null
|
|
104
|
+
this.clearTargetClass(this.container, ['dragging'])
|
|
105
|
+
const parent = this.container.parentElement
|
|
106
|
+
if (parent) {
|
|
107
|
+
const figures = parent.querySelectorAll('.figure')
|
|
108
|
+
figures.forEach((el) => {
|
|
109
|
+
this.clearTargetClass(el, [
|
|
110
|
+
'drag-active',
|
|
111
|
+
'drop-target-above',
|
|
112
|
+
'drop-target-below',
|
|
113
|
+
])
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
// TODO: Check if setting decorations will work for this case
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
this.container.addEventListener('dragover', (e) => {
|
|
120
|
+
// Only handle figure drops when we're dragging a figure (to avoid conflicts with other drag events for image)
|
|
121
|
+
if (FigureEditableView.currentDragFigureId) {
|
|
122
|
+
e.preventDefault()
|
|
123
|
+
e.stopPropagation()
|
|
124
|
+
const rect = this.container.getBoundingClientRect()
|
|
125
|
+
const relativeY = e.clientY - rect.top
|
|
126
|
+
const isAbove = relativeY < rect.height / 2
|
|
127
|
+
this.clearTargetClass(this.container)
|
|
128
|
+
this.container.classList.add(
|
|
129
|
+
isAbove ? 'drop-target-above' : 'drop-target-below'
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
this.container.addEventListener('dragleave', (e) => {
|
|
135
|
+
if (!this.container.contains(e.relatedTarget as Node)) {
|
|
136
|
+
this.clearTargetClass(this.container)
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
this.container.addEventListener('drop', (e) => {
|
|
141
|
+
// Only handle figure drops when we're dragging a figure (to avoid conflicts with other drop events for image)
|
|
142
|
+
if (!FigureEditableView.currentDragFigureId) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
e.preventDefault()
|
|
147
|
+
e.stopPropagation()
|
|
148
|
+
|
|
149
|
+
// Get figure ID from static variable
|
|
150
|
+
const figureId = FigureEditableView.currentDragFigureId
|
|
151
|
+
|
|
152
|
+
if (!figureId) {
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const figure = this.getFigureById(figureId)
|
|
157
|
+
if (!figure) {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
const toPos = this.getPos()
|
|
161
|
+
if (figure.pos === toPos) {
|
|
162
|
+
return
|
|
163
|
+
} // prevent self-move
|
|
164
|
+
|
|
165
|
+
this.moveFigure(figure.pos, figure.node, toPos)
|
|
166
|
+
this.clearTargetClass(this.container)
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private getFigureById(
|
|
171
|
+
figureId: string
|
|
172
|
+
): { pos: number; node: ManuscriptNode } | null {
|
|
173
|
+
let result: { pos: number; node: ManuscriptNode } | null = null
|
|
174
|
+
this.view.state.doc.descendants((node, pos) => {
|
|
175
|
+
if (node.type === schema.nodes.figure && node.attrs.id === figureId) {
|
|
176
|
+
result = { pos, node }
|
|
177
|
+
return false // Stop descending into children
|
|
178
|
+
}
|
|
179
|
+
if (result) {
|
|
180
|
+
return false // Stop entire traversal since we found the result
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
return result
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private moveFigure(
|
|
187
|
+
fromPos: number,
|
|
188
|
+
fromNode: ManuscriptNode,
|
|
189
|
+
targetPos: number
|
|
190
|
+
) {
|
|
191
|
+
const { state } = this.view
|
|
192
|
+
const { tr } = state
|
|
193
|
+
|
|
194
|
+
tr.delete(fromPos, fromPos + fromNode.nodeSize)
|
|
195
|
+
tr.insert(this.view.state.tr.mapping.map(targetPos), fromNode)
|
|
196
|
+
this.view.dispatch(tr)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
upload = async (file: File) => {
|
|
200
|
+
const result = await this.props.fileManagement.upload(file)
|
|
201
|
+
this.setSrc(result.id)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
public updateContents() {
|
|
205
|
+
super.updateContents()
|
|
206
|
+
this.clearTargetClass(this.container, ['dragging'])
|
|
207
|
+
|
|
208
|
+
// Setup drag and drop if capabilities allow, not already initialized
|
|
209
|
+
if (
|
|
210
|
+
this.props.getCapabilities().editArticle &&
|
|
211
|
+
!this.dragAndDropInitialized
|
|
212
|
+
) {
|
|
213
|
+
this.setupDragAndDrop()
|
|
214
|
+
this.dragAndDropInitialized = true
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const src = this.node.attrs.src
|
|
218
|
+
const files = this.props.getFiles()
|
|
219
|
+
const file = src && files.filter((f) => f.id === src)[0]
|
|
220
|
+
|
|
221
|
+
this.container.innerHTML = ''
|
|
222
|
+
|
|
223
|
+
const can = this.props.getCapabilities()
|
|
224
|
+
|
|
225
|
+
const link = file && this.props.fileManagement.previewLink(file)
|
|
226
|
+
const img = link
|
|
227
|
+
? this.createImg(link)
|
|
228
|
+
: file
|
|
229
|
+
? this.createUnsupportedFormat(file.name)
|
|
230
|
+
: this.createPlaceholder()
|
|
231
|
+
|
|
232
|
+
if (can.uploadFile && !isDeleted(this.node)) {
|
|
233
|
+
addInteractionHandlers(img, this.upload, 'image/*')
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
this.container.innerHTML = ''
|
|
237
|
+
this.container.appendChild(img)
|
|
238
|
+
|
|
239
|
+
this.addTools()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
public addTools() {
|
|
243
|
+
this.manageReactTools()
|
|
244
|
+
|
|
245
|
+
const existingDragHandlers =
|
|
246
|
+
this.container.querySelectorAll('.drag-handler')
|
|
247
|
+
existingDragHandlers.forEach((handler) => handler.remove())
|
|
248
|
+
|
|
249
|
+
const $pos = this.view.state.doc.resolve(this.getPos())
|
|
250
|
+
|
|
251
|
+
const parent = $pos.parent
|
|
252
|
+
// Create drag handle for figure elements with multiple figures (not simple image)
|
|
253
|
+
if (
|
|
254
|
+
this.props.getCapabilities()?.editArticle &&
|
|
255
|
+
parent.type === schema.nodes.figure_element &&
|
|
256
|
+
!isDeleted(this.node) &&
|
|
257
|
+
this.countFigures() > 1
|
|
258
|
+
) {
|
|
259
|
+
const dragHandle = document.createElement('div')
|
|
260
|
+
dragHandle.className = 'drag-handler'
|
|
261
|
+
dragHandle.innerHTML = draggableIcon
|
|
262
|
+
dragHandle.draggable = true
|
|
263
|
+
|
|
264
|
+
dragHandle.addEventListener('mousedown', (e) => {
|
|
265
|
+
e.stopPropagation()
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
this.container.appendChild(dragHandle)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Helper function to count non-deleted figures in current figure element
|
|
273
|
+
countFigures() {
|
|
274
|
+
const parent = findParentNodeOfTypeClosestToPos(
|
|
275
|
+
this.view.state.doc.resolve(this.getPos()),
|
|
276
|
+
schema.nodes.figure_element
|
|
277
|
+
)
|
|
278
|
+
let count = 0
|
|
279
|
+
parent?.node.descendants((node) => {
|
|
280
|
+
if (node.type === schema.nodes.figure && !isDeleted(node)) {
|
|
281
|
+
count++
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
return count
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private manageReactTools() {
|
|
288
|
+
this.reactTools?.remove()
|
|
289
|
+
|
|
290
|
+
const handlers = createFileHandlers(
|
|
291
|
+
this.node.attrs,
|
|
292
|
+
'src',
|
|
293
|
+
this.view,
|
|
294
|
+
this.props,
|
|
295
|
+
this.setSrc
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
const can = this.props.getCapabilities()
|
|
299
|
+
if (can.uploadFile) {
|
|
300
|
+
handlers.handleUpload = createFileUploader(this.upload, 'image/*')
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (can.detachFile) {
|
|
304
|
+
handlers.handleDelete = () => {
|
|
305
|
+
const pos = this.getPos()
|
|
306
|
+
const tr = this.view.state.tr
|
|
307
|
+
tr.delete(pos, pos + this.node.nodeSize)
|
|
308
|
+
this.view.dispatch(tr)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
this.reactTools = createReactTools(
|
|
312
|
+
this.node,
|
|
313
|
+
this.view,
|
|
314
|
+
this.getPos,
|
|
315
|
+
this.props,
|
|
316
|
+
handlers,
|
|
317
|
+
false,
|
|
318
|
+
() => this.countFigures() > 1
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
if (this.reactTools) {
|
|
322
|
+
this.dom.insertBefore(this.reactTools, this.dom.firstChild)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
protected setSrc = (src: string) => {
|
|
327
|
+
const { tr } = this.view.state
|
|
328
|
+
const pos = this.getPos()
|
|
329
|
+
tr.setNodeMarkup(pos, undefined, {
|
|
330
|
+
...this.node.attrs,
|
|
331
|
+
src: src,
|
|
332
|
+
})
|
|
333
|
+
this.view.dispatch(tr)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private createUnsupportedFormat = (name: string) => {
|
|
337
|
+
return createUnsupportedFormat(
|
|
338
|
+
name,
|
|
339
|
+
this.props.getCapabilities().editArticle
|
|
340
|
+
)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
protected createImg = (src: string) => {
|
|
344
|
+
const img = document.createElement('img')
|
|
345
|
+
img.classList.add('figure-image')
|
|
346
|
+
img.src = src
|
|
347
|
+
img.alt = 'figure image'
|
|
348
|
+
return img
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
protected createPlaceholder = () => {
|
|
352
|
+
return createMediaPlaceholder(
|
|
353
|
+
MediaType.Figure,
|
|
354
|
+
this.view,
|
|
355
|
+
this.getPos,
|
|
356
|
+
this.props
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export default createEditableNodeView(FigureEditableView)
|
|
@@ -0,0 +1,197 @@
|
|
|
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 { schema } from '@manuscripts/transform'
|
|
18
|
+
import { Node } from 'prosemirror-model'
|
|
19
|
+
import { TextSelection } from 'prosemirror-state'
|
|
20
|
+
|
|
21
|
+
import { addAuthorIcon } from '../icons'
|
|
22
|
+
import { handleEnterKey } from '../lib/navigation-utils'
|
|
23
|
+
import { createNodeView } from './creators'
|
|
24
|
+
import { FigureEditableView } from './figure_editable'
|
|
25
|
+
import { ImageElementView } from './image_element'
|
|
26
|
+
|
|
27
|
+
export class FigureElementView extends ImageElementView {
|
|
28
|
+
public ignoreMutation = () => true
|
|
29
|
+
private addFigureBtn: HTMLButtonElement
|
|
30
|
+
private resizeObserver: ResizeObserver | null = null
|
|
31
|
+
|
|
32
|
+
public createElement = () => {
|
|
33
|
+
super.createElement()
|
|
34
|
+
this.addFigureElementButtons()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public initialise() {
|
|
38
|
+
super.initialise()
|
|
39
|
+
this.setupResizeObserver()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private setupResizeObserver() {
|
|
43
|
+
if (this.resizeObserver) {
|
|
44
|
+
this.resizeObserver.disconnect()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
48
|
+
// Reposition button when figure dimensions change
|
|
49
|
+
requestAnimationFrame(() => this.updateButtonPosition())
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// Observe all current figures
|
|
53
|
+
this.container.querySelectorAll('figure').forEach((figure) => {
|
|
54
|
+
this.resizeObserver?.observe(figure)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private addFigureElementButtons() {
|
|
59
|
+
if (this.props.getCapabilities()?.editArticle) {
|
|
60
|
+
this.addFigureBtn = Object.assign(document.createElement('button'), {
|
|
61
|
+
className: 'add-button',
|
|
62
|
+
innerHTML: addAuthorIcon,
|
|
63
|
+
title: 'Add figure',
|
|
64
|
+
})
|
|
65
|
+
this.addFigureBtn.addEventListener('click', () => this.addFigure())
|
|
66
|
+
this.addFigureBtn.addEventListener(
|
|
67
|
+
'keydown',
|
|
68
|
+
handleEnterKey(() => this.addFigure())
|
|
69
|
+
)
|
|
70
|
+
this.addFigureBtn.tabIndex = 0
|
|
71
|
+
this.container.prepend(this.addFigureBtn)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private updateButtonPosition() {
|
|
76
|
+
if (!this.addFigureBtn) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const figures = this.container.querySelectorAll('figure')
|
|
81
|
+
const lastFigure = figures[figures.length - 1] as HTMLElement
|
|
82
|
+
|
|
83
|
+
if (!lastFigure) {
|
|
84
|
+
return // No figures found
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const lastFigureRect = lastFigure.getBoundingClientRect()
|
|
88
|
+
const containerRect = this.container.getBoundingClientRect()
|
|
89
|
+
|
|
90
|
+
// Calculate position relative to container
|
|
91
|
+
const relativeTop = lastFigureRect.bottom - containerRect.top + 20
|
|
92
|
+
this.addFigureBtn.style.top = `${relativeTop}px`
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private updateAddButtonState() {
|
|
96
|
+
if (!this.addFigureBtn) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Check if there's already an empty figure placeholder
|
|
101
|
+
let hasEmptyFigure = false
|
|
102
|
+
this.node.forEach((node) => {
|
|
103
|
+
if (node.type === schema.nodes.figure) {
|
|
104
|
+
// Check if this figure is empty (no src or empty src)
|
|
105
|
+
const src = node.attrs.src || ''
|
|
106
|
+
if (src.trim().length === 0) {
|
|
107
|
+
hasEmptyFigure = true
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// Disable button if there's already an empty figure
|
|
113
|
+
if (hasEmptyFigure) {
|
|
114
|
+
this.addFigureBtn.classList.add('disabled')
|
|
115
|
+
this.addFigureBtn.disabled = true
|
|
116
|
+
} else {
|
|
117
|
+
this.addFigureBtn.classList.remove('disabled')
|
|
118
|
+
this.addFigureBtn.disabled = false
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Updates button position and re-observes figures.
|
|
124
|
+
*/
|
|
125
|
+
public update(node: Node): boolean {
|
|
126
|
+
const handledBySuper = super.update(node)
|
|
127
|
+
|
|
128
|
+
if (handledBySuper) {
|
|
129
|
+
this.setupResizeObserver() // Re-observe figures after node update
|
|
130
|
+
requestAnimationFrame(() => {
|
|
131
|
+
this.updateButtonPosition() // Reposition after DOM update
|
|
132
|
+
this.updateAddButtonState() // Update button state after DOM update
|
|
133
|
+
this.updateChildDragHandlers()
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return handledBySuper
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private updateChildDragHandlers() {
|
|
141
|
+
const dragHandlers = this.container.querySelectorAll('.drag-handler')
|
|
142
|
+
dragHandlers.forEach((handler) => handler.remove())
|
|
143
|
+
|
|
144
|
+
const figureElements = this.container.querySelectorAll('figure')
|
|
145
|
+
figureElements.forEach((figureElement) => {
|
|
146
|
+
const figureView = (
|
|
147
|
+
figureElement as HTMLElement & { __figureView?: FigureEditableView }
|
|
148
|
+
).__figureView
|
|
149
|
+
figureView?.addTools()
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public updateContents() {
|
|
154
|
+
super.updateContents()
|
|
155
|
+
requestAnimationFrame(() => {
|
|
156
|
+
this.updateButtonPosition()
|
|
157
|
+
this.updateAddButtonState()
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private addFigure = () => {
|
|
162
|
+
const { state } = this.view
|
|
163
|
+
const { tr } = state
|
|
164
|
+
const figureElementPos = this.getPos()
|
|
165
|
+
|
|
166
|
+
let insertPos = figureElementPos + 1
|
|
167
|
+
let lastFigureEndPos = insertPos
|
|
168
|
+
let hasFigures = false
|
|
169
|
+
|
|
170
|
+
this.node.forEach((node) => {
|
|
171
|
+
if (node.type === schema.nodes.figure) {
|
|
172
|
+
lastFigureEndPos = insertPos + node.nodeSize
|
|
173
|
+
hasFigures = true
|
|
174
|
+
}
|
|
175
|
+
insertPos += node.nodeSize
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
const finalInsertPos = hasFigures ? lastFigureEndPos : figureElementPos + 1
|
|
179
|
+
|
|
180
|
+
const figureNode = state.schema.nodes.figure.create()
|
|
181
|
+
|
|
182
|
+
tr.insert(finalInsertPos, figureNode)
|
|
183
|
+
tr.setSelection(TextSelection.create(tr.doc, finalInsertPos + 1))
|
|
184
|
+
this.view.dispatch(tr.scrollIntoView())
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public destroy() {
|
|
188
|
+
// Disconnect ResizeObserver on destroy
|
|
189
|
+
if (this.resizeObserver) {
|
|
190
|
+
this.resizeObserver.disconnect()
|
|
191
|
+
this.resizeObserver = null
|
|
192
|
+
}
|
|
193
|
+
super.destroy()
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export default createNodeView(FigureElementView)
|
|
@@ -0,0 +1,21 @@
|
|
|
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 { createEditableNodeView } from './creators'
|
|
18
|
+
import { EditableBlock } from './editable_block'
|
|
19
|
+
import { FigureElementView } from './figure_element'
|
|
20
|
+
|
|
21
|
+
export default createEditableNodeView(EditableBlock(FigureElementView))
|