@manuscripts/body-editor 3.14.0 → 3.15.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.
Files changed (50) hide show
  1. package/dist/cjs/components/affiliations/AffiliationsModal.js +9 -9
  2. package/dist/cjs/components/authors/AuthorsModal.js +9 -7
  3. package/dist/cjs/components/authors-affiliations/AuthorsAndAffiliationsModals.js +1 -0
  4. package/dist/cjs/components/cross-ref-check-modal/CrossRefWarningModal.js +182 -0
  5. package/dist/cjs/components/cross-ref-check-modal/openModal.js +38 -0
  6. package/dist/cjs/components/references/ImportBibliographyForm.js +5 -2
  7. package/dist/cjs/components/references/ReferenceSearch.js +2 -2
  8. package/dist/cjs/components/references/ReferencesModal.js +2 -2
  9. package/dist/cjs/plugins/affiliations.js +6 -3
  10. package/dist/cjs/plugins/bibliography.js +6 -3
  11. package/dist/cjs/plugins/cross-references.js +165 -2
  12. package/dist/cjs/plugins/footnotes.js +6 -3
  13. package/dist/cjs/versions.js +1 -1
  14. package/dist/cjs/views/embed.js +2 -2
  15. package/dist/cjs/views/footnote.js +2 -2
  16. package/dist/es/components/affiliations/AffiliationsModal.js +1 -1
  17. package/dist/es/components/authors/AuthorsModal.js +3 -1
  18. package/dist/es/components/authors-affiliations/AuthorsAndAffiliationsModals.js +1 -0
  19. package/dist/es/components/cross-ref-check-modal/CrossRefWarningModal.js +142 -0
  20. package/dist/es/components/cross-ref-check-modal/openModal.js +31 -0
  21. package/dist/es/components/references/ImportBibliographyForm.js +1 -1
  22. package/dist/es/components/references/ReferenceSearch.js +1 -1
  23. package/dist/es/components/references/ReferencesModal.js +1 -1
  24. package/dist/es/plugins/affiliations.js +1 -1
  25. package/dist/es/plugins/bibliography.js +1 -1
  26. package/dist/es/plugins/cross-references.js +163 -3
  27. package/dist/es/plugins/footnotes.js +1 -1
  28. package/dist/es/versions.js +1 -1
  29. package/dist/es/views/embed.js +1 -1
  30. package/dist/es/views/footnote.js +1 -1
  31. package/dist/types/components/authors-affiliations/AuthorsAndAffiliationsModals.d.ts +1 -1
  32. package/dist/types/components/cross-ref-check-modal/CrossRefWarningModal.d.ts +29 -0
  33. package/dist/types/components/cross-ref-check-modal/openModal.d.ts +19 -0
  34. package/dist/types/versions.d.ts +1 -1
  35. package/package.json +2 -2
  36. package/src/components/affiliations/AffiliationsModal.tsx +1 -1
  37. package/src/components/authors/AuthorsModal.tsx +3 -1
  38. package/src/components/authors-affiliations/AuthorsAndAffiliationsModals.tsx +2 -0
  39. package/src/components/cross-ref-check-modal/CrossRefWarningModal.tsx +223 -0
  40. package/src/components/cross-ref-check-modal/openModal.ts +50 -0
  41. package/src/components/references/ImportBibliographyForm.tsx +1 -1
  42. package/src/components/references/ReferenceSearch.tsx +1 -1
  43. package/src/components/references/ReferencesModal.tsx +1 -1
  44. package/src/plugins/affiliations.ts +1 -1
  45. package/src/plugins/bibliography.ts +1 -1
  46. package/src/plugins/cross-references.ts +233 -7
  47. package/src/plugins/footnotes.ts +1 -1
  48. package/src/versions.ts +1 -1
  49. package/src/views/embed.ts +1 -1
  50. package/src/views/footnote.ts +1 -1
@@ -14,11 +14,14 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
17
20
  Object.defineProperty(exports, "__esModule", { value: true });
18
21
  exports.footnotesKey = void 0;
19
22
  const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
20
23
  const transform_1 = require("@manuscripts/transform");
21
- const lodash_1 = require("lodash");
24
+ const isEqual_1 = __importDefault(require("lodash/isEqual"));
22
25
  const prosemirror_state_1 = require("prosemirror-state");
23
26
  const prosemirror_utils_1 = require("prosemirror-utils");
24
27
  const prosemirror_view_1 = require("prosemirror-view");
@@ -99,8 +102,8 @@ const buildFootnotesElementState = (container, element) => {
99
102
  const hasChanged = ($new, $old) => {
100
103
  const nids = $new.footnotes.map(([node]) => node.attrs.id);
101
104
  const oids = $old?.footnotes.map(([node]) => node.attrs.id);
102
- return !((0, lodash_1.isEqual)(nids, oids) &&
103
- (0, lodash_1.isEqual)($new.unusedFootnoteIDs, $old?.unusedFootnoteIDs));
105
+ return !((0, isEqual_1.default)(nids, oids) &&
106
+ (0, isEqual_1.default)($new.unusedFootnoteIDs, $old?.unusedFootnoteIDs));
104
107
  };
105
108
  exports.default = (props) => {
106
109
  return new prosemirror_state_1.Plugin({
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MATHJAX_VERSION = exports.VERSION = void 0;
4
- exports.VERSION = '3.14.0';
4
+ exports.VERSION = '3.15.1';
5
5
  exports.MATHJAX_VERSION = '3.2.2';
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.EmbedView = void 0;
7
- const lodash_1 = require("lodash");
7
+ const isEqual_1 = __importDefault(require("lodash/isEqual"));
8
8
  const prosemirror_state_1 = require("prosemirror-state");
9
9
  const InsertEmbedDialog_1 = require("../components/toolbar/InsertEmbedDialog");
10
10
  const get_media_type_1 = require("../lib/get-media-type");
@@ -101,7 +101,7 @@ class EmbedView extends block_view_1.default {
101
101
  super.updateContents();
102
102
  const { href, mimetype, mimeSubtype } = this.node.attrs;
103
103
  const currentAttrs = { href, mimetype, mimeSubtype };
104
- const contentChanged = !this.initialized || !(0, lodash_1.isEqual)(this.previousAttrs, currentAttrs);
104
+ const contentChanged = !this.initialized || !(0, isEqual_1.default)(this.previousAttrs, currentAttrs);
105
105
  if (contentChanged) {
106
106
  this.initialized = true;
107
107
  this.previousAttrs = currentAttrs;
@@ -22,7 +22,7 @@ exports.FootnoteView = void 0;
22
22
  const style_guide_1 = require("@manuscripts/style-guide");
23
23
  const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
24
24
  const transform_1 = require("@manuscripts/transform");
25
- const lodash_1 = require("lodash");
25
+ const isEqual_1 = __importDefault(require("lodash/isEqual"));
26
26
  const prosemirror_state_1 = require("prosemirror-state");
27
27
  const prosemirror_utils_1 = require("prosemirror-utils");
28
28
  const DeleteFootnoteDialog_1 = require("../components/views/DeleteFootnoteDialog");
@@ -93,7 +93,7 @@ class FootnoteView extends base_node_view_1.BaseNodeView {
93
93
  fns?.inlineFootnotes.forEach(([node, pos]) => {
94
94
  pos = tr.mapping.map(pos);
95
95
  const rids = node.attrs.rids.filter((rid) => rid !== id);
96
- if ((0, lodash_1.isEqual)(rids, node.attrs.rids)) {
96
+ if ((0, isEqual_1.default)(rids, node.attrs.rids)) {
97
97
  return;
98
98
  }
99
99
  if (!rids.length) {
@@ -1,6 +1,6 @@
1
1
  import { AddIcon, AffiliationPlaceholderIcon, CloseButton, InspectorTabPanel, InspectorTabPanels, InspectorTabs, outlineStyle, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SidebarContent, StyledModal, } from '@manuscripts/style-guide';
2
2
  import { generateNodeID, schema } from '@manuscripts/transform';
3
- import { isEqual } from 'lodash';
3
+ import isEqual from 'lodash/isEqual';
4
4
  import React, { useCallback, useEffect, useReducer, useRef, useState, } from 'react';
5
5
  import styled from 'styled-components';
6
6
  import { authorComparator, } from '../../lib/authors';
@@ -15,7 +15,9 @@
15
15
  */
16
16
  import { AddIcon, AuthorPlaceholderIcon, CloseButton, FormSubtitle, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, outlineStyle, ScrollableModalContent, SidebarContent, StyledModal, InspectorTabs, InspectorTabPanel, InspectorTabPanels, } from '@manuscripts/style-guide';
17
17
  import { generateNodeID, schema } from '@manuscripts/transform';
18
- import { cloneDeep, isEqual, omit } from 'lodash';
18
+ import cloneDeep from 'lodash/cloneDeep';
19
+ import isEqual from 'lodash/isEqual';
20
+ import omit from 'lodash/omit';
19
21
  import React, { useCallback, useEffect, useReducer, useRef, useState, } from 'react';
20
22
  import styled from 'styled-components';
21
23
  import { arrayReducer } from '../../lib/array-reducer';
@@ -74,4 +74,5 @@ export const openAuthorsAndAffiliationsModals = (pos, view, initialModal) => {
74
74
  const dialog = ReactSubView(props, AuthorsAndAffiliationsModals, componentProps, state.doc, () => pos, view);
75
75
  view.focus();
76
76
  document.body.appendChild(dialog);
77
+ return dialog;
77
78
  };
@@ -0,0 +1,142 @@
1
+ /*!
2
+ * © 2026 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 React, { useState } from 'react';
17
+ import { AttentionOrangeIcon, CloseButton, ModalContainer, ModalHeader, PrimaryButton, StyledModal, TertiaryButton, TextButton, } from '@manuscripts/style-guide';
18
+ import { startCase } from 'lodash';
19
+ import styled from 'styled-components';
20
+ export const CrossRefWarningModal = ({ onClose, xrefs, onConfirm, selectAndScrollTo }) => {
21
+ const [isOpen, setIsOpen] = useState(true);
22
+ const handleClose = () => {
23
+ setIsOpen(false);
24
+ onClose();
25
+ };
26
+ return (React.createElement(Modal, { isOpen: isOpen, onRequestClose: () => handleClose(), shouldCloseOnOverlayClick: false, hideOverlay: true },
27
+ React.createElement(Container, { "data-cy": "cross-reference-warning-modal" },
28
+ React.createElement(ModalHeader, null,
29
+ React.createElement(CloseButton, { onClick: () => handleClose(), "data-cy": "modal-close-button" })),
30
+ React.createElement(Body, null,
31
+ React.createElement(Title, null,
32
+ React.createElement(AttentionOrangeIcon, { width: 24, height: 22 }),
33
+ " Delete referenced content?"),
34
+ React.createElement("p", null, "You are deleting content referenced elsewhere in the document:"),
35
+ React.createElement(ScrolableItems, null, xrefs.map((group, i) => (React.createElement(XrefGroupDisplay, { key: i, group: group, selectAndScrollTo: selectAndScrollTo })))),
36
+ React.createElement(Actions, null,
37
+ React.createElement(TertiaryButton, { type: "button", onClick: () => handleClose() }, "Cancel"),
38
+ React.createElement(PrimaryButton, { "$danger": true, type: "button", onClick: () => {
39
+ onConfirm();
40
+ setIsOpen(false);
41
+ } }, "Delete & remove citation"))))));
42
+ };
43
+ const XrefGroupDisplay = ({ group, selectAndScrollTo }) => {
44
+ return (React.createElement("div", null,
45
+ React.createElement("h3", null, group.label),
46
+ React.createElement(ReferencesList, null, group.xrefs.map(([, pos], i) => {
47
+ return (React.createElement("li", { key: i },
48
+ React.createElement(TextButton, { onClick: () => selectAndScrollTo(pos) }, `${startCase(pos.parent.type.name)} - ${pos.parent.textContent}`)));
49
+ }))));
50
+ };
51
+ const Container = styled(ModalContainer) `
52
+ position: absolute;
53
+ top: 1rem;
54
+ left: 50%;
55
+ right: 0;
56
+ max-height: calc(50vh - 2rem);
57
+ min-height: 280px;
58
+ transform: translate(-50%, 0);
59
+ max-width: 480px;
60
+ transition:
61
+ top 0.2s,
62
+ transform 0.2s;
63
+ `;
64
+ const Modal = styled(StyledModal) `
65
+ position: fixed;
66
+ top: 0;
67
+ left: 0;
68
+ width: 100%;
69
+ height: 100%;
70
+ z-index: 1100;
71
+ color: #6e6e6e;
72
+ margin: auto;
73
+
74
+ &.modal-bottom ${Container} {
75
+ top: calc(100% - 2rem);
76
+ transform: translate(-50%, -100%);
77
+ }
78
+
79
+ &:after {
80
+ content: '';
81
+ display: block;
82
+ position: fixed;
83
+ z-index: -1;
84
+ left: 0;
85
+ top: 0;
86
+ right: 0;
87
+ bottom: 0;
88
+ background: rgba(0, 0, 0, 0.2);
89
+ }
90
+ h3 {
91
+ font-size: 16px;
92
+ margin: 0.5em 0;
93
+ }
94
+ p {
95
+ margin: 0.5em 0;
96
+ }
97
+ `;
98
+ const Body = styled.div `
99
+ margin: 1.5rem;
100
+ display: flex;
101
+ flex-flow: column;
102
+ `;
103
+ const Title = styled.h2 `
104
+ font-size: 18px;
105
+ font-weight: 700;
106
+ line-height: 1.5;
107
+ margin: 0;
108
+ color: #353535;
109
+ svg {
110
+ vertical-align: text-top;
111
+ }
112
+ `;
113
+ const ReferencesList = styled.ul `
114
+ padding: 8px;
115
+ margin-left: 0;
116
+ list-style: none;
117
+ background: #f2f2f2;
118
+ border: 1px solid #e2e2e2;
119
+ border-radius: 3px;
120
+
121
+ ${TextButton} {
122
+ margin-left: 0;
123
+ text-decoration: underline;
124
+ &:hover {
125
+ text-decoration: none;
126
+ }
127
+ display: block;
128
+ max-width: 100%;
129
+ overflow: hidden;
130
+ color: #353535;
131
+ text-overflow: ellipsis;
132
+ }
133
+ `;
134
+ const Actions = styled.footer `
135
+ text-align: right;
136
+ padding-top: 1rem;
137
+ `;
138
+ const ScrolableItems = styled.div `
139
+ max-height: 16vh;
140
+ min-height: 100px;
141
+ overflow-y: auto;
142
+ `;
@@ -0,0 +1,31 @@
1
+ /*!
2
+ * © 2026 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 { getEditorProps } from '../../plugins/editor-props';
17
+ import ReactSubView from '../../views/ReactSubView';
18
+ import { CrossRefWarningModal } from './CrossRefWarningModal';
19
+ export const openCrossRefWarningModal = (view, xrefGroups, onConfirm, onClose, selectAndScrollTo) => {
20
+ const { state } = view;
21
+ const props = getEditorProps(state);
22
+ const componentProps = {
23
+ xrefs: xrefGroups,
24
+ onConfirm,
25
+ onClose,
26
+ selectAndScrollTo,
27
+ };
28
+ const dialog = ReactSubView(props, CrossRefWarningModal, componentProps, state.doc, () => 0, view);
29
+ document.body.appendChild(dialog);
30
+ return dialog;
31
+ };
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { FormActionsBar, FormRow, Label, outlineStyle, PrimaryButton, SecondaryButton, TextArea, } from '@manuscripts/style-guide';
17
17
  import { useFormik } from 'formik';
18
- import { debounce } from 'lodash';
18
+ import debounce from 'lodash/debounce';
19
19
  import React, { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
20
20
  import styled, { css } from 'styled-components';
21
21
  import { importBibliographyItems } from '../../lib/references';
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { AddNewIcon, ButtonGroup, IconTextButton, PrimaryButton, SecondaryButton, UploadIcon, withFocusTrap, } from '@manuscripts/style-guide';
17
- import { debounce } from 'lodash';
17
+ import debounce from 'lodash/debounce';
18
18
  import React, { useState } from 'react';
19
19
  import styled from 'styled-components';
20
20
  import { DocumentReferenceSource, } from './BibliographyItemSource';
@@ -1,5 +1,5 @@
1
1
  import { Category, CitationCountIcon, CloseButton, Dialog, ModalBody, ModalContainer, ModalHeader, ModalSidebar, ModalSidebarHeader, ModalSidebarTitle, ScrollableModalContent, SidebarContent, StyledModal, useScrollDetection, withListNavigation, withNavigableListItem, } from '@manuscripts/style-guide';
2
- import { isEqual } from 'lodash';
2
+ import isEqual from 'lodash/isEqual';
3
3
  import React, { useEffect, useRef, useState } from 'react';
4
4
  import styled from 'styled-components';
5
5
  import { ReferenceForm, } from './ReferenceForm/ReferenceForm';
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { isDeleted } from '@manuscripts/track-changes-plugin';
17
17
  import { isAffiliationNode, isContributorNode, } from '@manuscripts/transform';
18
- import { isEqual } from 'lodash';
18
+ import isEqual from 'lodash/isEqual';
19
19
  import { Plugin, PluginKey } from 'prosemirror-state';
20
20
  import { Decoration, DecorationSet } from 'prosemirror-view';
21
21
  import { authorComparator, } from '../lib/authors';
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { buildCiteprocCitation, isBibliographyElementNode, isBibliographyItemNode, isCitationNode, } from '@manuscripts/transform';
17
17
  import * as Citeproc from 'citeproc';
18
- import { isEqual } from 'lodash';
18
+ import isEqual from 'lodash/isEqual';
19
19
  import { Plugin, PluginKey } from 'prosemirror-state';
20
20
  import { Decoration, DecorationSet } from 'prosemirror-view';
21
21
  export const bibliographyKey = new PluginKey('bibliography');
@@ -13,13 +13,62 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { schema } from '@manuscripts/transform';
17
- import { isEqual } from 'lodash';
18
- import { Plugin } from 'prosemirror-state';
16
+ import { schema, } from '@manuscripts/transform';
17
+ import { trackChangesPluginKey } from '@manuscripts/track-changes-plugin';
18
+ import isEqual from 'lodash/isEqual';
19
+ import { NodeSelection, Plugin } from 'prosemirror-state';
19
20
  import { Decoration, DecorationSet } from 'prosemirror-view';
21
+ import { openCrossRefWarningModal } from '../components/cross-ref-check-modal/openModal';
20
22
  import { objectsKey } from './objects';
23
+ let modalActive = false;
24
+ let modalElement = null;
21
25
  export default () => {
26
+ let view = null;
22
27
  return new Plugin({
28
+ view(editorView) {
29
+ view = editorView;
30
+ return {
31
+ update(v) {
32
+ view = v;
33
+ },
34
+ destroy() {
35
+ view = null;
36
+ },
37
+ };
38
+ },
39
+ filterTransaction(tr, state) {
40
+ if (!view ||
41
+ !tr.docChanged ||
42
+ tr.getMeta(trackChangesPluginKey) ||
43
+ tr.getMeta('addToHistory') === false) {
44
+ return true;
45
+ }
46
+ if (modalActive) {
47
+ return false;
48
+ }
49
+ const targets = objectsKey.getState(state);
50
+ const xrefGroups = createXrefGroups(targets, state.doc, tr.doc);
51
+ if (xrefGroups.length === 0) {
52
+ return true;
53
+ }
54
+ modalActive = true;
55
+ const cleanup = () => {
56
+ modalActive = false;
57
+ if (modalElement) {
58
+ modalElement.classList.remove('modal-bottom');
59
+ modalElement.remove();
60
+ modalElement = null;
61
+ }
62
+ };
63
+ const deletedIds = new Set(xrefGroups.map((g) => g.referenced.attrs.id));
64
+ const onClose = () => {
65
+ cleanup();
66
+ };
67
+ if (view) {
68
+ modalElement = openCrossRefWarningModal(view, xrefGroups, onConfirmCreator(view, cleanup, deletedIds, tr), onClose, selectAndScrollToCreator(view));
69
+ }
70
+ return false;
71
+ },
23
72
  state: {
24
73
  init() {
25
74
  return DecorationSet.empty;
@@ -53,3 +102,114 @@ function createDecorations(doc) {
53
102
  });
54
103
  return decorations;
55
104
  }
105
+ function createXrefGroups(targets, stateDoc, resultDoc) {
106
+ const newIds = new Set();
107
+ const xrefRids = new Set();
108
+ const xrefGroups = [];
109
+ resultDoc.descendants((node) => {
110
+ if (node.attrs.id) {
111
+ newIds.add(node.attrs.id);
112
+ }
113
+ if (node.type === schema.nodes.cross_reference) {
114
+ for (const rid of node.attrs.rids) {
115
+ xrefRids.add(rid);
116
+ }
117
+ }
118
+ });
119
+ const orphanedRids = new Set();
120
+ for (const rid of xrefRids) {
121
+ if (!newIds.has(rid)) {
122
+ orphanedRids.add(rid);
123
+ }
124
+ }
125
+ if (orphanedRids.size === 0) {
126
+ return [];
127
+ }
128
+ const referencedNodes = new Map();
129
+ const xrefsByRid = new Map();
130
+ stateDoc.descendants((node, pos) => {
131
+ const id = node.attrs.id;
132
+ if (id && orphanedRids.has(id)) {
133
+ referencedNodes.set(id, node);
134
+ }
135
+ if (node.type === schema.nodes.cross_reference) {
136
+ for (const rid of node.attrs.rids) {
137
+ if (orphanedRids.has(rid)) {
138
+ let entries = xrefsByRid.get(rid);
139
+ if (!entries) {
140
+ entries = [];
141
+ xrefsByRid.set(rid, entries);
142
+ }
143
+ entries.push([node, stateDoc.resolve(pos)]);
144
+ }
145
+ }
146
+ }
147
+ });
148
+ for (const [id, referenced] of referencedNodes) {
149
+ const xrefs = xrefsByRid.get(id);
150
+ if (xrefs?.length) {
151
+ const label = targets.get(referenced.attrs.id)?.label || '';
152
+ xrefGroups.push({ referenced, label, xrefs });
153
+ }
154
+ }
155
+ return xrefGroups;
156
+ }
157
+ const onConfirmCreator = (view, cleanup, deletedIds, tr) => () => {
158
+ cleanup();
159
+ if (!view) {
160
+ return;
161
+ }
162
+ const newTr = view.state.tr;
163
+ for (const step of tr.steps) {
164
+ const result = step.apply(newTr.doc);
165
+ if (result.failed) {
166
+ console.warn('Cross-ref deletion warning: could not replay step —', result.failed);
167
+ return;
168
+ }
169
+ newTr.step(step);
170
+ }
171
+ const xrefPositions = [];
172
+ newTr.doc.descendants((node, pos) => {
173
+ if (node.type === schema.nodes.cross_reference) {
174
+ const rids = node.attrs.rids;
175
+ if (rids.some((rid) => deletedIds.has(rid))) {
176
+ xrefPositions.push({ from: pos, to: pos + node.nodeSize });
177
+ }
178
+ }
179
+ });
180
+ for (let i = xrefPositions.length - 1; i >= 0; i--) {
181
+ const { from, to } = xrefPositions[i];
182
+ newTr.delete(from, to);
183
+ }
184
+ view.dispatch(newTr);
185
+ };
186
+ const selectAndScrollToCreator = (view) => ($pos) => {
187
+ if (!view) {
188
+ return;
189
+ }
190
+ const selTr = view.state.tr;
191
+ selTr.setSelection(NodeSelection.create(view.state.doc, $pos.pos));
192
+ view.focus();
193
+ view.dispatch(selTr);
194
+ let scrollable = view.dom.parentElement;
195
+ while (scrollable != null &&
196
+ scrollable.scrollHeight <= scrollable.clientHeight) {
197
+ scrollable = scrollable.parentElement;
198
+ }
199
+ if (!scrollable) {
200
+ return;
201
+ }
202
+ const coords = view.coordsAtPos($pos.pos);
203
+ const containerRect = scrollable.getBoundingClientRect();
204
+ const offsetInContainer = coords.top - containerRect.top + scrollable.scrollTop;
205
+ const containerHeight = scrollable.clientHeight;
206
+ const scrollTo = offsetInContainer - containerHeight * 0.75;
207
+ if (scrollTo < 0) {
208
+ modalElement?.classList.add('modal-bottom');
209
+ scrollable.scrollTo({ top: 0, behavior: 'smooth' });
210
+ }
211
+ else {
212
+ modalElement?.classList.remove('modal-bottom');
213
+ scrollable.scrollTo({ top: scrollTo, behavior: 'smooth' });
214
+ }
215
+ };
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { skipTracking } from '@manuscripts/track-changes-plugin';
17
17
  import { generateAlphaFootnoteLabel, isTableElementNode, schema, } from '@manuscripts/transform';
18
- import { isEqual } from 'lodash';
18
+ import isEqual from 'lodash/isEqual';
19
19
  import { Plugin, PluginKey, TextSelection } from 'prosemirror-state';
20
20
  import { findChildrenByType } from 'prosemirror-utils';
21
21
  import { Decoration, DecorationSet } from 'prosemirror-view';
@@ -1,2 +1,2 @@
1
- export const VERSION = '3.14.0';
1
+ export const VERSION = '3.15.1';
2
2
  export const MATHJAX_VERSION = '3.2.2';
@@ -1,4 +1,4 @@
1
- import { isEqual } from 'lodash';
1
+ import isEqual from 'lodash/isEqual';
2
2
  import { NodeSelection } from 'prosemirror-state';
3
3
  import { NoPreviewMessageWithLink, openEmbedDialog, } from '../components/toolbar/InsertEmbedDialog';
4
4
  import { getMediaTypeInfo } from '../lib/get-media-type';
@@ -16,7 +16,7 @@
16
16
  import { ContextMenu } from '@manuscripts/style-guide';
17
17
  import { isDeleted, isPendingInsert } from '@manuscripts/track-changes-plugin';
18
18
  import { schema } from '@manuscripts/transform';
19
- import { isEqual } from 'lodash';
19
+ import isEqual from 'lodash/isEqual';
20
20
  import { NodeSelection } from 'prosemirror-state';
21
21
  import { findParentNodeOfTypeClosestToPos } from 'prosemirror-utils';
22
22
  import { DeleteFootnoteDialog, } from '../components/views/DeleteFootnoteDialog';
@@ -26,4 +26,4 @@ export interface AuthorsAndAffiliationsModalsProps {
26
26
  addNewAffiliation?: boolean;
27
27
  }
28
28
  export declare const AuthorsAndAffiliationsModals: React.FC<AuthorsAndAffiliationsModalsProps>;
29
- export declare const openAuthorsAndAffiliationsModals: (pos: number, view: ManuscriptEditorView | EditorView | undefined, initialModal: "authors" | "affiliations") => void;
29
+ export declare const openAuthorsAndAffiliationsModals: (pos: number, view: ManuscriptEditorView | EditorView | undefined, initialModal: "authors" | "affiliations") => HTMLDivElement | undefined;
@@ -0,0 +1,29 @@
1
+ /*!
2
+ * © 2026 Atypon Systems LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { ManuscriptNode } from '@manuscripts/transform';
17
+ import React from 'react';
18
+ import { ResolvedPos } from 'prosemirror-model';
19
+ export type XrefGroup = {
20
+ referenced: ManuscriptNode;
21
+ label: string;
22
+ xrefs: [ManuscriptNode, ResolvedPos][];
23
+ };
24
+ export declare const CrossRefWarningModal: React.FC<{
25
+ onClose: () => void;
26
+ xrefs: XrefGroup[];
27
+ onConfirm: () => void;
28
+ selectAndScrollTo: ($pos: ResolvedPos) => void;
29
+ }>;
@@ -0,0 +1,19 @@
1
+ /*!
2
+ * © 2026 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 { ManuscriptEditorView } from '@manuscripts/transform';
17
+ import { ResolvedPos } from 'prosemirror-model';
18
+ import { XrefGroup } from './CrossRefWarningModal';
19
+ export declare const openCrossRefWarningModal: (view: ManuscriptEditorView, xrefGroups: XrefGroup[], onConfirm: () => void, onClose: () => void, selectAndScrollTo: ($pos: ResolvedPos) => void) => HTMLDivElement;
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "3.14.0";
1
+ export declare const VERSION = "3.15.1";
2
2
  export declare const MATHJAX_VERSION = "3.2.2";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@manuscripts/body-editor",
3
3
  "description": "Prosemirror components for editing and viewing manuscripts",
4
- "version": "3.14.0",
4
+ "version": "3.15.1",
5
5
  "repository": "github:Atypon-OpenSource/manuscripts-body-editor",
6
6
  "license": "Apache-2.0",
7
7
  "main": "dist/cjs",
@@ -108,7 +108,7 @@
108
108
  "jest": "30.4.2",
109
109
  "jest-environment-jsdom": "30.4.1",
110
110
  "jest-prosemirror": "3.0.1",
111
- "npm-run-all2": "^8.0.4",
111
+ "npm-run-all2": "8.0.4",
112
112
  "prettier": "3.8.3",
113
113
  "regenerator-runtime": "0.14.1",
114
114
  "rimraf": "6.1.3",
@@ -32,7 +32,7 @@ import {
32
32
  StyledModal,
33
33
  } from '@manuscripts/style-guide'
34
34
  import { generateNodeID, schema } from '@manuscripts/transform'
35
- import { isEqual } from 'lodash'
35
+ import isEqual from 'lodash/isEqual'
36
36
  import React, {
37
37
  useCallback,
38
38
  useEffect,
@@ -34,7 +34,9 @@ import {
34
34
  InspectorTabPanels,
35
35
  } from '@manuscripts/style-guide'
36
36
  import { generateNodeID, schema } from '@manuscripts/transform'
37
- import { cloneDeep, isEqual, omit } from 'lodash'
37
+ import cloneDeep from 'lodash/cloneDeep'
38
+ import isEqual from 'lodash/isEqual'
39
+ import omit from 'lodash/omit'
38
40
  import React, {
39
41
  useCallback,
40
42
  useEffect,
@@ -167,4 +167,6 @@ export const openAuthorsAndAffiliationsModals = (
167
167
  )
168
168
  view.focus()
169
169
  document.body.appendChild(dialog)
170
+ // @TODO refactor to allow cleaning it up
171
+ return dialog
170
172
  }