@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,243 @@
|
|
|
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
|
+
CHANGE_OPERATION,
|
|
18
|
+
CHANGE_STATUS,
|
|
19
|
+
isTracked,
|
|
20
|
+
TrackedAttrs,
|
|
21
|
+
TrackedChange,
|
|
22
|
+
} from '@manuscripts/track-changes-plugin'
|
|
23
|
+
import {
|
|
24
|
+
DataTrackedAttrs,
|
|
25
|
+
ManuscriptEditorState,
|
|
26
|
+
ManuscriptNode,
|
|
27
|
+
schema,
|
|
28
|
+
} from '@manuscripts/transform'
|
|
29
|
+
import { ResolvedPos } from 'prosemirror-model'
|
|
30
|
+
import { Plugin, PluginKey } from 'prosemirror-state'
|
|
31
|
+
import { Decoration, DecorationSet } from 'prosemirror-view'
|
|
32
|
+
|
|
33
|
+
import { isTextSelection } from '../commands'
|
|
34
|
+
import { getSelectionChangeGroup } from '../selection'
|
|
35
|
+
|
|
36
|
+
export const selectedSuggestionKey = new PluginKey<PluginState>(
|
|
37
|
+
'selected-suggestion'
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
type EffectiveSelection = {
|
|
41
|
+
node: ManuscriptNode
|
|
42
|
+
from: number
|
|
43
|
+
to: number
|
|
44
|
+
}
|
|
45
|
+
export interface PluginState {
|
|
46
|
+
decorations: DecorationSet
|
|
47
|
+
suggestion?: TrackedAttrs
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const EMPTY: PluginState = {
|
|
51
|
+
decorations: DecorationSet.empty,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* This plugin is responsible for designating a single suggestion as
|
|
56
|
+
* "selected" based on the current
|
|
57
|
+
* [editor selection]{@link ManuscriptEditorState#selection}.
|
|
58
|
+
*
|
|
59
|
+
* The selected suggestion is wrapped with a "selected-suggestion"
|
|
60
|
+
* decoration to apply different styling to it in the editor.
|
|
61
|
+
*
|
|
62
|
+
* For node views that render other nodes (e.g. the `bibliography_element`
|
|
63
|
+
* view is responsible for rendering `bibliography_item` nodes), the
|
|
64
|
+
* decoration has to be applied manually, for which the
|
|
65
|
+
* selected suggestion (if any) is [provided in the state]{@link PluginState#suggestion}.
|
|
66
|
+
*/
|
|
67
|
+
export default () => {
|
|
68
|
+
return new Plugin<PluginState>({
|
|
69
|
+
key: selectedSuggestionKey,
|
|
70
|
+
state: {
|
|
71
|
+
init: (_, state) => buildPluginState(state),
|
|
72
|
+
apply: (tr, value, oldState, newState) => buildPluginState(newState),
|
|
73
|
+
},
|
|
74
|
+
props: {
|
|
75
|
+
decorations: (state) => {
|
|
76
|
+
const suggestion = selectedSuggestionKey.getState(state)
|
|
77
|
+
return suggestion?.decorations || DecorationSet.empty
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const buildPluginState = (state: ManuscriptEditorState): PluginState => {
|
|
84
|
+
const selection = state.selection
|
|
85
|
+
const changes = getSelectionChangeGroup(state)
|
|
86
|
+
if (changes.length) {
|
|
87
|
+
return buildGroupOfChangesDecoration(state.doc, changes)
|
|
88
|
+
}
|
|
89
|
+
const $pos =
|
|
90
|
+
isTextSelection(selection) && selection.$cursor
|
|
91
|
+
? selection.$cursor
|
|
92
|
+
: selection.$to
|
|
93
|
+
if (!$pos) {
|
|
94
|
+
return EMPTY
|
|
95
|
+
}
|
|
96
|
+
const effective = getEffectiveSelection($pos)
|
|
97
|
+
|
|
98
|
+
if (!effective) {
|
|
99
|
+
return EMPTY
|
|
100
|
+
}
|
|
101
|
+
if (effective.node.isText) {
|
|
102
|
+
return buildTextDecoration(state.doc, effective)
|
|
103
|
+
} else {
|
|
104
|
+
return buildNodeDecoration(state.doc, effective)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Find the node containing `$pos` with the _least depth_ (closest to the root)
|
|
110
|
+
* that changed. This is mainly for cases where inserting a node automatically
|
|
111
|
+
* inserts a bunch of children (e.g. inserting a `figure_element` automatically
|
|
112
|
+
* inserts a `figure`, `figure_caption`, etc.)
|
|
113
|
+
* @param $pos
|
|
114
|
+
*/
|
|
115
|
+
const getEffectiveSelection = ($pos: ResolvedPos) => {
|
|
116
|
+
let current
|
|
117
|
+
for (let depth = $pos.depth; depth > 0; depth--) {
|
|
118
|
+
const node = $pos.node(depth)
|
|
119
|
+
// @TODO - abstract reference processing to tc-plugin
|
|
120
|
+
if (
|
|
121
|
+
node.attrs.dataTracked &&
|
|
122
|
+
!node.attrs.dataTracked?.find(
|
|
123
|
+
(c: DataTrackedAttrs) => c.operation === 'reference'
|
|
124
|
+
)
|
|
125
|
+
) {
|
|
126
|
+
current = {
|
|
127
|
+
node,
|
|
128
|
+
from: $pos.before(depth),
|
|
129
|
+
to: $pos.after(depth),
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (current) {
|
|
134
|
+
return current
|
|
135
|
+
}
|
|
136
|
+
const parent = $pos.parent
|
|
137
|
+
// if selection is longer than a text node by a single char, still select the previous node
|
|
138
|
+
// @TODO - define how selection should behave when multiple text nodes are selected at once - which tracked-changes should be highlighted? (maybe multiple highlights)
|
|
139
|
+
const child = parent.childBefore(Math.max($pos.parentOffset - 1, 0))
|
|
140
|
+
const node = child.node
|
|
141
|
+
if (node) {
|
|
142
|
+
const from = $pos.start() + child.offset
|
|
143
|
+
const to = from + node.nodeSize
|
|
144
|
+
return {
|
|
145
|
+
node,
|
|
146
|
+
from,
|
|
147
|
+
to,
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const buildNodeDecoration = (
|
|
153
|
+
doc: ManuscriptNode,
|
|
154
|
+
selection: EffectiveSelection
|
|
155
|
+
) => {
|
|
156
|
+
const node = selection.node
|
|
157
|
+
const suggestion = node.attrs.dataTracked?.[0]
|
|
158
|
+
if (!suggestion?.status || suggestion.status === CHANGE_STATUS.rejected) {
|
|
159
|
+
return EMPTY
|
|
160
|
+
}
|
|
161
|
+
const from = selection.from
|
|
162
|
+
const to = selection.to
|
|
163
|
+
//hack for keywords since they're really inline nodes but not
|
|
164
|
+
//marked as such in the schema
|
|
165
|
+
const inline = node.type === schema.nodes.keyword || node.isInline
|
|
166
|
+
const decorationType = inline ? Decoration.inline : Decoration.node
|
|
167
|
+
const decoration = decorationType(from, to, {
|
|
168
|
+
class: 'selected-suggestion',
|
|
169
|
+
})
|
|
170
|
+
return {
|
|
171
|
+
suggestion,
|
|
172
|
+
decorations: DecorationSet.create(doc, [decoration]),
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const buildTextDecoration = (
|
|
177
|
+
doc: ManuscriptNode,
|
|
178
|
+
selection: EffectiveSelection
|
|
179
|
+
) => {
|
|
180
|
+
const node = selection.node
|
|
181
|
+
let suggestion = getTrackedMark(node)?.attrs.dataTracked as TrackedAttrs
|
|
182
|
+
|
|
183
|
+
if (!suggestion) {
|
|
184
|
+
for (const mark of node.marks) {
|
|
185
|
+
if (isTracked(mark)) {
|
|
186
|
+
suggestion = mark.attrs.dataTracked[0] as TrackedAttrs
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!suggestion) {
|
|
192
|
+
return EMPTY
|
|
193
|
+
}
|
|
194
|
+
const from = selection.from
|
|
195
|
+
const to = selection.to
|
|
196
|
+
|
|
197
|
+
const decoration = Decoration.inline(from, to, {
|
|
198
|
+
nodeName: 'span',
|
|
199
|
+
class: 'selected-suggestion',
|
|
200
|
+
})
|
|
201
|
+
return {
|
|
202
|
+
suggestion,
|
|
203
|
+
decorations: DecorationSet.create(doc, [decoration]),
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const buildGroupOfChangesDecoration = (
|
|
208
|
+
doc: ManuscriptNode,
|
|
209
|
+
changes: TrackedChange[]
|
|
210
|
+
) => {
|
|
211
|
+
const decorations = []
|
|
212
|
+
if (changes[0].dataTracked.operation === CHANGE_OPERATION.structure) {
|
|
213
|
+
changes.map((c) =>
|
|
214
|
+
decorations.push(
|
|
215
|
+
Decoration.node(c.from, c.to, { class: 'selected-suggestion' })
|
|
216
|
+
)
|
|
217
|
+
)
|
|
218
|
+
} else {
|
|
219
|
+
const from = changes[0].from,
|
|
220
|
+
to = changes[changes.length - 1].to
|
|
221
|
+
decorations.push(
|
|
222
|
+
Decoration.inline(from, to, {
|
|
223
|
+
class: 'selected-suggestion',
|
|
224
|
+
})
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
decorations: DecorationSet.create(doc, decorations),
|
|
229
|
+
suggestion: changes[0].dataTracked,
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const trackedMarkTypes = new Set([
|
|
234
|
+
schema.marks.tracked_insert,
|
|
235
|
+
schema.marks.tracked_delete,
|
|
236
|
+
])
|
|
237
|
+
const getTrackedMark = (node: ManuscriptNode) => {
|
|
238
|
+
for (const mark of node.marks) {
|
|
239
|
+
if (trackedMarkTypes.has(mark.type)) {
|
|
240
|
+
return mark
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
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
|
+
import { skipTracking } from '@manuscripts/track-changes-plugin'
|
|
17
|
+
import { ManuscriptNode, schema } from '@manuscripts/transform'
|
|
18
|
+
import { Fragment } from 'prosemirror-model'
|
|
19
|
+
import { EditorState, Plugin, TextSelection } from 'prosemirror-state'
|
|
20
|
+
import { CellSelection, selectedRect } from 'prosemirror-tables'
|
|
21
|
+
import { findParentNodeOfTypeClosestToPos } from 'prosemirror-utils'
|
|
22
|
+
|
|
23
|
+
import { isTextSelection } from '../commands'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* This plugins fixes cursos jump caused by tables-fixes plugin from prosemirror tables
|
|
27
|
+
* also will include table_element node with the selection, if the selection start/end from table node
|
|
28
|
+
*/
|
|
29
|
+
export default () => {
|
|
30
|
+
return new Plugin<null>({
|
|
31
|
+
appendTransaction: (transactions, oldState, newState) => {
|
|
32
|
+
if (
|
|
33
|
+
isTextSelection(newState.selection) &&
|
|
34
|
+
!oldState.selection.eq(newState.selection)
|
|
35
|
+
) {
|
|
36
|
+
const slice = newState.selection.$from.doc.slice(
|
|
37
|
+
newState.selection.from,
|
|
38
|
+
newState.selection.to
|
|
39
|
+
)
|
|
40
|
+
const selectionTable = getTable(slice.content)
|
|
41
|
+
const selection = addTableElementToSelection(newState, selectionTable)
|
|
42
|
+
if (selection) {
|
|
43
|
+
const newTr = newState.tr
|
|
44
|
+
newTr.setSelection(selection)
|
|
45
|
+
return newTr
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (
|
|
49
|
+
isTableShapeSelected(newState) &&
|
|
50
|
+
!oldState.selection.eq(newState.selection)
|
|
51
|
+
) {
|
|
52
|
+
const contentNode = findParentNodeOfTypeClosestToPos(
|
|
53
|
+
newState.selection.$from,
|
|
54
|
+
schema.nodes.table_element
|
|
55
|
+
)
|
|
56
|
+
if (contentNode) {
|
|
57
|
+
const newTr = newState.tr
|
|
58
|
+
newTr.setSelection(
|
|
59
|
+
TextSelection.create(
|
|
60
|
+
newState.doc,
|
|
61
|
+
contentNode.pos,
|
|
62
|
+
contentNode.pos + contentNode.node.nodeSize
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
return newTr
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const tablesFixedTr = transactions.find((tr) => tr.getMeta('fix-tables$'))
|
|
70
|
+
if (!tablesFixedTr) {
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// const pos = tablesFixedTr.selection.$anchor.pos;
|
|
75
|
+
const newTr = newState.tr
|
|
76
|
+
skipTracking(newTr)
|
|
77
|
+
newTr.setMeta('origin', 'tables-cursor-fix')
|
|
78
|
+
newTr.setSelection(
|
|
79
|
+
TextSelection.create(newState.doc, tablesFixedTr.selection.$anchor.pos)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
return newTr
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const isTableShapeSelected = (state: EditorState) => {
|
|
88
|
+
if (state.selection instanceof CellSelection) {
|
|
89
|
+
const rect = selectedRect(state)
|
|
90
|
+
return (
|
|
91
|
+
rect.left === 0 &&
|
|
92
|
+
rect.top === 0 &&
|
|
93
|
+
rect.right === rect.map.width &&
|
|
94
|
+
rect.bottom === rect.map.height
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const getTable = (fragment?: Fragment) => {
|
|
101
|
+
let table: ManuscriptNode | undefined
|
|
102
|
+
fragment?.descendants((node) => {
|
|
103
|
+
if (node?.type === schema.nodes.table) {
|
|
104
|
+
table = node
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
return table
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Expand selection to include table_element node
|
|
113
|
+
*/
|
|
114
|
+
const addTableElementToSelection = (
|
|
115
|
+
state: EditorState,
|
|
116
|
+
selectedTable?: ManuscriptNode
|
|
117
|
+
) => {
|
|
118
|
+
const { doc, selection } = state
|
|
119
|
+
const fromContentNode = findParentNodeOfTypeClosestToPos(
|
|
120
|
+
selection.$from,
|
|
121
|
+
schema.nodes.table_element
|
|
122
|
+
)
|
|
123
|
+
const toContentNode = findParentNodeOfTypeClosestToPos(
|
|
124
|
+
selection.$to,
|
|
125
|
+
schema.nodes.table_element
|
|
126
|
+
)
|
|
127
|
+
const docTableElement = fromContentNode?.node || toContentNode?.node
|
|
128
|
+
|
|
129
|
+
if (
|
|
130
|
+
selectedTable?.nodeSize !== getTable(docTableElement?.content)?.nodeSize
|
|
131
|
+
) {
|
|
132
|
+
return undefined
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const from =
|
|
136
|
+
((fromContentNode && toContentNode) || fromContentNode) &&
|
|
137
|
+
fromContentNode.pos
|
|
138
|
+
const to =
|
|
139
|
+
((fromContentNode && toContentNode) || toContentNode) &&
|
|
140
|
+
toContentNode.pos + toContentNode.node.nodeSize
|
|
141
|
+
|
|
142
|
+
return TextSelection.create(doc, from || selection.from, to || selection.to)
|
|
143
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
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 { Plugin } from 'prosemirror-state'
|
|
20
|
+
import { Decoration, DecorationSet } from 'prosemirror-view'
|
|
21
|
+
|
|
22
|
+
import { insertTransAbstract, insertTransGraphicalAbstract } from '../commands'
|
|
23
|
+
import { EditorProps } from '../configs/ManuscriptsEditor'
|
|
24
|
+
import { addAuthorIcon, translateIcon } from '../icons'
|
|
25
|
+
import { getLanguage, getLanguageLabel } from '../lib/languages'
|
|
26
|
+
import { templateAllows } from '../lib/template'
|
|
27
|
+
import {
|
|
28
|
+
handleEnterKey,
|
|
29
|
+
createKeyboardInteraction,
|
|
30
|
+
} from '../lib/navigation-utils'
|
|
31
|
+
|
|
32
|
+
const createMenuItem = (
|
|
33
|
+
props: EditorProps,
|
|
34
|
+
contents: string,
|
|
35
|
+
handler: EventListener,
|
|
36
|
+
isSelected = false,
|
|
37
|
+
tabIndex: number
|
|
38
|
+
) => {
|
|
39
|
+
const item = document.createElement('div')
|
|
40
|
+
item.className = `menu-item ${isSelected ? 'selected' : ''}`
|
|
41
|
+
item.textContent = contents
|
|
42
|
+
item.tabIndex = tabIndex
|
|
43
|
+
|
|
44
|
+
item.addEventListener('mousedown', handler)
|
|
45
|
+
item.addEventListener('keydown', handleEnterKey(handler))
|
|
46
|
+
return item
|
|
47
|
+
}
|
|
48
|
+
interface MenuInstance {
|
|
49
|
+
menu: HTMLElement
|
|
50
|
+
destroy: () => void
|
|
51
|
+
}
|
|
52
|
+
const createLanguageMenu = (
|
|
53
|
+
props: EditorProps,
|
|
54
|
+
selectedCode: string,
|
|
55
|
+
onSelect: (code: string) => void
|
|
56
|
+
): MenuInstance => {
|
|
57
|
+
const menu = document.createElement('div')
|
|
58
|
+
menu.className = 'language menu'
|
|
59
|
+
const menuItems: HTMLElement[] = []
|
|
60
|
+
|
|
61
|
+
const removeKeydownListener = createKeyboardInteraction({
|
|
62
|
+
container: document,
|
|
63
|
+
navigation: {
|
|
64
|
+
getItems: () => menuItems,
|
|
65
|
+
arrowKeys: {
|
|
66
|
+
forward: 'ArrowDown',
|
|
67
|
+
backward: 'ArrowUp',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
const destroy = () => {
|
|
72
|
+
removeKeydownListener()
|
|
73
|
+
props.popper.destroy()
|
|
74
|
+
}
|
|
75
|
+
props.languages.forEach((language, index) => {
|
|
76
|
+
const item = createMenuItem(
|
|
77
|
+
props,
|
|
78
|
+
getLanguageLabel(language),
|
|
79
|
+
() => {
|
|
80
|
+
onSelect(language.code)
|
|
81
|
+
destroy()
|
|
82
|
+
},
|
|
83
|
+
selectedCode === language.code,
|
|
84
|
+
index === 0 ? 0 : -1
|
|
85
|
+
)
|
|
86
|
+
menuItems.push(item)
|
|
87
|
+
menu.appendChild(item)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
return { menu, destroy }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const getInsertionPos = (doc: Node, nodePos: number): number | null => {
|
|
94
|
+
const node = doc.nodeAt(nodePos)
|
|
95
|
+
return node ? nodePos + node.nodeSize : null
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default (props: EditorProps) =>
|
|
99
|
+
new Plugin<null>({
|
|
100
|
+
props: {
|
|
101
|
+
decorations: (state) => {
|
|
102
|
+
const can = props.getCapabilities()
|
|
103
|
+
const canEditTransAbstract =
|
|
104
|
+
can.editArticle &&
|
|
105
|
+
templateAllows(state, schema.nodes.trans_abstract) &&
|
|
106
|
+
insertTransAbstract(state)
|
|
107
|
+
const canEditTransGraphicalAbstract =
|
|
108
|
+
can.editArticle &&
|
|
109
|
+
templateAllows(state, schema.nodes.trans_graphical_abstract)
|
|
110
|
+
|
|
111
|
+
const widgets: Decoration[] = []
|
|
112
|
+
|
|
113
|
+
state.doc.descendants((node, pos, parent) => {
|
|
114
|
+
const isAbstractSection =
|
|
115
|
+
(node.type === schema.nodes.section ||
|
|
116
|
+
node.type === schema.nodes.graphical_abstract_section) &&
|
|
117
|
+
parent?.type === schema.nodes.abstracts
|
|
118
|
+
|
|
119
|
+
// Show "Add translation" for abstract sections
|
|
120
|
+
if (isAbstractSection) {
|
|
121
|
+
const isGraphical =
|
|
122
|
+
node.type === schema.nodes.graphical_abstract_section
|
|
123
|
+
const category = props.sectionCategories.get(node.attrs.category)
|
|
124
|
+
|
|
125
|
+
const canEdit = isGraphical
|
|
126
|
+
? canEditTransGraphicalAbstract &&
|
|
127
|
+
category &&
|
|
128
|
+
insertTransGraphicalAbstract(category)(state)
|
|
129
|
+
: canEditTransAbstract
|
|
130
|
+
|
|
131
|
+
if (canEdit) {
|
|
132
|
+
widgets.push(
|
|
133
|
+
Decoration.widget(
|
|
134
|
+
pos + 1,
|
|
135
|
+
(view) => {
|
|
136
|
+
const $span = document.createElement('span')
|
|
137
|
+
$span.tabIndex = 0
|
|
138
|
+
$span.className = 'add-trans-abstract'
|
|
139
|
+
$span.title = 'Add translation'
|
|
140
|
+
$span.innerHTML = `${addAuthorIcon} <span class="add-trans-abstract-text">Add translation</span>`
|
|
141
|
+
|
|
142
|
+
const handleActivate = (event: Event) => {
|
|
143
|
+
event.preventDefault()
|
|
144
|
+
event.stopPropagation()
|
|
145
|
+
const insertPos = getInsertionPos(view.state.doc, pos)
|
|
146
|
+
if (insertPos == null) {
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
if (isGraphical && category) {
|
|
150
|
+
insertTransGraphicalAbstract(category, insertPos)(
|
|
151
|
+
view.state,
|
|
152
|
+
view.dispatch,
|
|
153
|
+
view
|
|
154
|
+
)
|
|
155
|
+
} else {
|
|
156
|
+
insertTransAbstract(
|
|
157
|
+
view.state,
|
|
158
|
+
view.dispatch,
|
|
159
|
+
node.attrs.category,
|
|
160
|
+
insertPos
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
$span.addEventListener('mousedown', handleActivate)
|
|
166
|
+
$span.addEventListener(
|
|
167
|
+
'keydown',
|
|
168
|
+
handleEnterKey(handleActivate)
|
|
169
|
+
)
|
|
170
|
+
return $span
|
|
171
|
+
},
|
|
172
|
+
{ key: `add-trans-${node.attrs.id}-${pos}` }
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Language selector for trans_abstract and trans_graphical_abstract nodes
|
|
179
|
+
const isTransNode =
|
|
180
|
+
node.type === schema.nodes.trans_abstract ||
|
|
181
|
+
node.type === schema.nodes.trans_graphical_abstract
|
|
182
|
+
|
|
183
|
+
if (isTransNode) {
|
|
184
|
+
const canEdit =
|
|
185
|
+
node.type === schema.nodes.trans_abstract
|
|
186
|
+
? canEditTransAbstract
|
|
187
|
+
: canEditTransGraphicalAbstract
|
|
188
|
+
|
|
189
|
+
widgets.push(
|
|
190
|
+
Decoration.widget(
|
|
191
|
+
pos + 1,
|
|
192
|
+
(view) => {
|
|
193
|
+
const $btn = document.createElement('span')
|
|
194
|
+
$btn.className = 'language-selector-btn'
|
|
195
|
+
$btn.setAttribute('data-cy', 'language-selector-btn')
|
|
196
|
+
$btn.contentEditable = 'false'
|
|
197
|
+
$btn.tabIndex = canEdit ? 0 : -1
|
|
198
|
+
|
|
199
|
+
const code = node.attrs.lang || 'en'
|
|
200
|
+
const lang = getLanguage(code, props.languages)
|
|
201
|
+
const label = getLanguageLabel(lang)
|
|
202
|
+
$btn.innerHTML = `<span>${label}</span> ${translateIcon}`
|
|
203
|
+
|
|
204
|
+
if (canEdit) {
|
|
205
|
+
let menuInstance: MenuInstance | null = null
|
|
206
|
+
const handleOpenMenu = (event: Event) => {
|
|
207
|
+
event.preventDefault()
|
|
208
|
+
event.stopPropagation()
|
|
209
|
+
|
|
210
|
+
props.popper.destroy() // Ensure any existing popper is closed
|
|
211
|
+
|
|
212
|
+
const handleSelect = (code: string) => {
|
|
213
|
+
const tr = view.state.tr.setNodeAttribute(
|
|
214
|
+
pos,
|
|
215
|
+
'lang',
|
|
216
|
+
code
|
|
217
|
+
)
|
|
218
|
+
view.dispatch(tr)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
menuInstance = createLanguageMenu(
|
|
222
|
+
props,
|
|
223
|
+
code,
|
|
224
|
+
handleSelect
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
props.popper.show(
|
|
228
|
+
$btn,
|
|
229
|
+
menuInstance.menu,
|
|
230
|
+
'bottom-end',
|
|
231
|
+
false
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
createKeyboardInteraction({
|
|
236
|
+
container: $btn,
|
|
237
|
+
additionalKeys: {
|
|
238
|
+
Enter: handleOpenMenu,
|
|
239
|
+
Escape: (e) => {
|
|
240
|
+
e.preventDefault()
|
|
241
|
+
menuInstance?.destroy()
|
|
242
|
+
menuInstance = null
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
$btn.addEventListener('mousedown', handleOpenMenu)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return $btn
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
key: `lang-selector-${node.attrs.id}-${pos}-${node.attrs.lang}`,
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
return DecorationSet.create(state.doc, widgets)
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
})
|
package/src/rules.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
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 {
|
|
19
|
+
ellipsis,
|
|
20
|
+
emDash,
|
|
21
|
+
InputRule,
|
|
22
|
+
inputRules,
|
|
23
|
+
smartQuotes,
|
|
24
|
+
} from 'prosemirror-inputrules'
|
|
25
|
+
import { Node as ProsemirrorNode, NodeType } from 'prosemirror-model'
|
|
26
|
+
import { TextSelection } from 'prosemirror-state'
|
|
27
|
+
|
|
28
|
+
const creatingInputRule = (regexp: RegExp, nodeType: NodeType) =>
|
|
29
|
+
new InputRule(regexp, (state, match, start, end) => {
|
|
30
|
+
const tr = state.tr.delete(start, end)
|
|
31
|
+
const range = tr.doc.resolve(start).blockRange()
|
|
32
|
+
if (range) {
|
|
33
|
+
const node = nodeType.createAndFill() as ProsemirrorNode
|
|
34
|
+
tr.replaceRangeWith(range.start, range.end, node)
|
|
35
|
+
const $anchor = tr.doc.resolve(range.start)
|
|
36
|
+
tr.setSelection(TextSelection.between($anchor, $anchor))
|
|
37
|
+
return tr
|
|
38
|
+
}
|
|
39
|
+
return null
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
export default inputRules({
|
|
43
|
+
rules: [
|
|
44
|
+
...smartQuotes,
|
|
45
|
+
ellipsis,
|
|
46
|
+
emDash,
|
|
47
|
+
|
|
48
|
+
// * bullet | ordered list
|
|
49
|
+
creatingInputRule(/^(\d+[.)]\s|\s*\*\s)$/, schema.nodes.list),
|
|
50
|
+
|
|
51
|
+
// > blockquote
|
|
52
|
+
creatingInputRule(/^\s*>\s$/, schema.nodes.blockquote_element),
|
|
53
|
+
|
|
54
|
+
// ``` listing
|
|
55
|
+
creatingInputRule(/^```$/, schema.nodes.listing_element),
|
|
56
|
+
|
|
57
|
+
// # section (heading)
|
|
58
|
+
creatingInputRule(/^#\s$/, schema.nodes.section),
|
|
59
|
+
],
|
|
60
|
+
})
|