@manuscripts/body-editor 2.0.50 → 2.0.51

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.
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.mergeCellsWithSpace = exports.addColumns = exports.addRows = exports.insertTableFootnote = exports.addInlineComment = exports.addNodeComment = exports.createAndFillTableElement = exports.selectAllIsolating = exports.ignoreAtomBlockNodeForward = exports.isAtEndOfTextBlock = exports.ignoreMetaNodeBackspaceCommand = exports.ignoreAtomBlockNodeBackward = exports.isTextSelection = exports.isAtStartOfTextBlock = exports.insertTOCSection = exports.insertBibliographySection = exports.insertList = exports.insertKeywords = exports.insertContributors = exports.insertAbstract = exports.insertBackMatterSection = exports.insertSection = exports.insertGraphicalAbstract = exports.insertInlineFootnote = exports.insertFootnote = exports.createFootnote = exports.insertInlineEquation = exports.insertCrossReference = exports.insertInlineCitation = exports.insertLink = exports.insertSectionLabel = exports.findPosBeforeFirstSubsection = exports.insertBreak = exports.deleteBlock = exports.insertBlock = exports.insertSupplement = exports.insertTable = exports.insertFigure = exports.insertGeneralFootnote = exports.createBlock = exports.createSelection = exports.canInsert = exports.blockActive = exports.isNodeSelection = exports.markActive = void 0;
18
+ exports.autoComplete = exports.mergeCellsWithSpace = exports.addColumns = exports.addRows = exports.insertTableFootnote = exports.addInlineComment = exports.addNodeComment = exports.createAndFillTableElement = exports.selectAllIsolating = exports.ignoreAtomBlockNodeForward = exports.isAtEndOfTextBlock = exports.ignoreMetaNodeBackspaceCommand = exports.ignoreAtomBlockNodeBackward = exports.isTextSelection = exports.isAtStartOfTextBlock = exports.insertTOCSection = exports.insertBibliographySection = exports.insertList = exports.insertKeywords = exports.insertContributors = exports.insertAbstract = exports.insertBackMatterSection = exports.insertSection = exports.insertGraphicalAbstract = exports.insertInlineFootnote = exports.insertFootnote = exports.createFootnote = exports.insertInlineEquation = exports.insertCrossReference = exports.insertInlineCitation = exports.insertLink = exports.insertSectionLabel = exports.findPosBeforeFirstSubsection = exports.insertBreak = exports.deleteBlock = exports.insertBlock = exports.insertSupplement = exports.insertTable = exports.insertFigure = exports.insertGeneralFootnote = exports.createBlock = exports.createSelection = exports.canInsert = exports.blockActive = exports.isNodeSelection = exports.markActive = void 0;
19
19
  const json_schema_1 = require("@manuscripts/json-schema");
20
20
  const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
21
21
  const transform_1 = require("@manuscripts/transform");
@@ -29,11 +29,13 @@ const comments_1 = require("./lib/comments");
29
29
  const doc_1 = require("./lib/doc");
30
30
  const footnotes_1 = require("./lib/footnotes");
31
31
  const helpers_1 = require("./lib/helpers");
32
+ const section_titles_1 = require("./lib/section-titles");
32
33
  const track_changes_utils_1 = require("./lib/track-changes-utils");
33
34
  const utils_1 = require("./lib/utils");
34
35
  const comments_2 = require("./plugins/comments");
35
36
  const editor_props_1 = require("./plugins/editor-props");
36
37
  const footnotes_2 = require("./plugins/footnotes");
38
+ const autocompletion_1 = require("./plugins/section_title/autocompletion");
37
39
  const markActive = (type) => (state) => {
38
40
  const { from, $from, to, empty } = state.selection;
39
41
  return empty
@@ -489,16 +491,6 @@ const insertSection = (subsection = false) => (state, dispatch, view) => {
489
491
  return true;
490
492
  };
491
493
  exports.insertSection = insertSection;
492
- const sectionTitles = new Map([
493
- ['MPSectionCategory:acknowledgement', 'Acknowledgments'],
494
- ['MPSectionCategory:availability', 'Availability'],
495
- ['MPSectionCategory:competing-interests', 'COI Statement'],
496
- ['MPSectionCategory:con', 'Contributed-by information'],
497
- ['MPSectionCategory:ethics-statement', 'Ethics Statement'],
498
- ['MPSectionCategory:financial-disclosure', 'Financial Disclosure'],
499
- ['MPSectionCategory:supplementary-material', 'Supplementary Material'],
500
- ['MPSectionCategory:supported-by', 'Supported By'],
501
- ]);
502
494
  const insertBackMatterSection = (category) => (state, dispatch, view) => {
503
495
  const backmatter = (0, doc_1.findBackmatter)(state.doc);
504
496
  const sections = (0, prosemirror_utils_1.findChildrenByType)(backmatter.node, transform_1.schema.nodes.section);
@@ -516,7 +508,7 @@ const insertBackMatterSection = (category) => (state, dispatch, view) => {
516
508
  const node = transform_1.schema.nodes.section.createAndFill({
517
509
  category,
518
510
  }, [
519
- transform_1.schema.nodes.section_title.create({}, transform_1.schema.text(sectionTitles.get(category) || '')),
511
+ transform_1.schema.nodes.section_title.create({}, transform_1.schema.text(section_titles_1.sectionTitles.get(category) || '')),
520
512
  ]);
521
513
  const tr = state.tr.insert(pos, node);
522
514
  if (dispatch) {
@@ -1147,3 +1139,17 @@ function mergeCellsWithSpace(state, dispatch) {
1147
1139
  return true;
1148
1140
  }
1149
1141
  exports.mergeCellsWithSpace = mergeCellsWithSpace;
1142
+ const autoComplete = (state, dispatch) => {
1143
+ const complete = (0, autocompletion_1.checkForCompletion)(state);
1144
+ if (complete) {
1145
+ const tr = state.tr.insertText(complete.suggestion, state.selection.from);
1146
+ const inserted = complete.title.substring(0, complete.title.length - complete.suggestion.length);
1147
+ if (inserted) {
1148
+ tr.replaceWith(state.selection.from - inserted.length, state.selection.from, transform_1.schema.text(inserted));
1149
+ }
1150
+ dispatch && dispatch(tr);
1151
+ return true;
1152
+ }
1153
+ return false;
1154
+ };
1155
+ exports.autoComplete = autoComplete;
@@ -39,6 +39,7 @@ const customKeymap = {
39
39
  'Mod-Alt-=': (0, prosemirror_commands_1.toggleMark)(transform_1.schema.marks.superscript),
40
40
  'Mod-Alt--': (0, prosemirror_commands_1.toggleMark)(transform_1.schema.marks.subscript),
41
41
  'Ctrl->': (0, prosemirror_commands_1.wrapIn)(transform_1.schema.nodes.blockquote),
42
+ Enter: (0, prosemirror_commands_1.chainCommands)(commands_1.autoComplete, prosemirror_commands_1.newlineInCode, prosemirror_commands_1.createParagraphNear, prosemirror_commands_1.liftEmptyBlock, prosemirror_commands_1.splitBlock),
42
43
  'Shift-Mod-Enter': (0, commands_1.insertSection)(true),
43
44
  'Mod-Enter': (0, prosemirror_commands_1.chainCommands)(prosemirror_commands_1.exitCode, (0, commands_1.insertSection)()),
44
45
  'Shift-Enter': (0, prosemirror_commands_1.chainCommands)(prosemirror_commands_1.exitCode, commands_1.insertBreak),
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /*!
3
+ * © 2019 Atypon Systems LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.sectionTitles = void 0;
19
+ exports.sectionTitles = new Map([
20
+ ['MPSectionCategory:abstract', 'Abstract'],
21
+ ['MPSectionCategory:abstract-graphical', 'Graphical Abstract'],
22
+ ['MPSectionCategory:introduction', 'Introduction'],
23
+ ['MPSectionCategory:materials-method', 'Materials & Methods'],
24
+ ['MPSectionCategory:results', 'Results'],
25
+ ['MPSectionCategory:discussion', 'Discussion'],
26
+ ['MPSectionCategory:conclusions', 'Conclusions'],
27
+ ['MPSectionCategory:acknowledgement', 'Acknowledgments'],
28
+ ['MPSectionCategory:availability', 'Availability'],
29
+ ['MPSectionCategory:competing-interests', 'COI Statement'],
30
+ ['MPSectionCategory:con', 'Contributed-by information'],
31
+ ['MPSectionCategory:ethics-statement', 'Ethics Statement'],
32
+ ['MPSectionCategory:financial-disclosure', 'Financial Disclosure'],
33
+ ['MPSectionCategory:supplementary-material', 'Supplementary Material'],
34
+ ['MPSectionCategory:supported-by', 'Supported By'],
35
+ ]);
@@ -15,8 +15,9 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.sanitizeAttrsChange = exports.isHidden = exports.getAttrsTrackingButton = exports.getActualAttrs = exports.isTracked = exports.getChangeClasses = exports.isPendingSetAttrs = exports.isPending = exports.isPendingInsert = exports.isDeleted = exports.isRejectedInsert = void 0;
18
+ exports.sanitizeAttrsChange = exports.getActualTextContent = exports.isRejectedText = exports.isDeletedText = exports.isHidden = exports.getAttrsTrackingButton = exports.getActualAttrs = exports.isTracked = exports.getChangeClasses = exports.isPendingSetAttrs = exports.isPending = exports.isPendingInsert = exports.isDeleted = exports.isRejectedInsert = void 0;
19
19
  const style_guide_1 = require("@manuscripts/style-guide");
20
+ const transform_1 = require("@manuscripts/transform");
20
21
  const react_1 = require("react");
21
22
  const server_1 = require("react-dom/server");
22
23
  function isRejectedInsert(node) {
@@ -114,6 +115,46 @@ function isHidden(node) {
114
115
  return isDeleted(node) || isRejectedInsert(node);
115
116
  }
116
117
  exports.isHidden = isHidden;
118
+ function isDeletedText(node) {
119
+ var _a, _b, _c, _d;
120
+ if (node.type === transform_1.schema.nodes.text && node.marks.length) {
121
+ const deleteMark = node.marks.find((mark) => mark.type === transform_1.schema.marks.tracked_delete);
122
+ if (deleteMark &&
123
+ ((_b = (_a = deleteMark.attrs) === null || _a === void 0 ? void 0 : _a.dataTracked) === null || _b === void 0 ? void 0 : _b.status) &&
124
+ ['pending', 'approved'].includes((_d = (_c = deleteMark.attrs) === null || _c === void 0 ? void 0 : _c.dataTracked) === null || _d === void 0 ? void 0 : _d.status)) {
125
+ return true;
126
+ }
127
+ }
128
+ return false;
129
+ }
130
+ exports.isDeletedText = isDeletedText;
131
+ function isRejectedText(node) {
132
+ var _a, _b;
133
+ if (node.type === transform_1.schema.nodes.text) {
134
+ const insertMark = node.marks.find((mark) => mark.type === transform_1.schema.marks.tracked_insert);
135
+ if (insertMark && ((_b = (_a = insertMark.attrs) === null || _a === void 0 ? void 0 : _a.dataTracked) === null || _b === void 0 ? void 0 : _b.status) === 'rejected') {
136
+ return true;
137
+ }
138
+ }
139
+ return false;
140
+ }
141
+ exports.isRejectedText = isRejectedText;
142
+ function getActualTextContent(fragment) {
143
+ let finalContent = '';
144
+ function getContent(fragment) {
145
+ fragment.forEach((node) => {
146
+ if (node.type !== transform_1.schema.nodes.text) {
147
+ finalContent += getContent(node.content);
148
+ }
149
+ if (!isDeletedText(node) && !isRejectedText(node)) {
150
+ finalContent += node.textContent;
151
+ }
152
+ });
153
+ }
154
+ getContent(fragment);
155
+ return finalContent;
156
+ }
157
+ exports.getActualTextContent = getActualTextContent;
117
158
  function sanitizeAttrsChange(newAttr, currentAttrs) {
118
159
  return Object.keys(newAttr).reduce((acc, attr) => {
119
160
  const key = attr;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkForCompletion = exports.hasAutoCompletionSlack = void 0;
4
+ const transform_1 = require("@manuscripts/transform");
5
+ const prosemirror_utils_1 = require("prosemirror-utils");
6
+ const section_titles_1 = require("../../lib/section-titles");
7
+ const track_changes_utils_1 = require("../../lib/track-changes-utils");
8
+ function cursorAtTheEndOfText(state, nodeSize, nodePos) {
9
+ const { from, to } = state.selection;
10
+ return from === to && to === nodePos + nodeSize - 1;
11
+ }
12
+ const isUpperCase = (test) => test === test.toUpperCase() && test.length > 1;
13
+ function hasAutoCompletionSlack(parentSection, titleSection) {
14
+ const category = (0, track_changes_utils_1.getActualAttrs)(parentSection).category;
15
+ const title = section_titles_1.sectionTitles.get(category);
16
+ if (category && title && titleSection.textContent) {
17
+ const actualTextContent = (0, track_changes_utils_1.getActualTextContent)(titleSection.content);
18
+ if (title.toLowerCase().startsWith(actualTextContent.toLowerCase())) {
19
+ const suggestionPart = title.slice(actualTextContent.length);
20
+ const suggestion = isUpperCase(actualTextContent)
21
+ ? suggestionPart.toUpperCase()
22
+ : suggestionPart;
23
+ return {
24
+ suggestion,
25
+ title: isUpperCase(actualTextContent) ? title.toUpperCase() : title,
26
+ };
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+ exports.hasAutoCompletionSlack = hasAutoCompletionSlack;
32
+ function checkForCompletion(state) {
33
+ const section = (0, prosemirror_utils_1.findParentNodeOfTypeClosestToPos)(state.selection.$from, transform_1.schema.nodes.section);
34
+ const title = (0, prosemirror_utils_1.findParentNodeOfTypeClosestToPos)(state.selection.$from, transform_1.schema.nodes.section_title);
35
+ if (section &&
36
+ title &&
37
+ cursorAtTheEndOfText(state, title.node.nodeSize, title.pos)) {
38
+ const text = hasAutoCompletionSlack(section.node, title.node);
39
+ if (text) {
40
+ return text;
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+ exports.checkForCompletion = checkForCompletion;
@@ -4,7 +4,9 @@ exports.sectionTitleKey = void 0;
4
4
  const transform_1 = require("@manuscripts/transform");
5
5
  const prosemirror_state_1 = require("prosemirror-state");
6
6
  const prosemirror_utils_1 = require("prosemirror-utils");
7
- const track_changes_utils_1 = require("../lib/track-changes-utils");
7
+ const prosemirror_view_1 = require("prosemirror-view");
8
+ const track_changes_utils_1 = require("../../lib/track-changes-utils");
9
+ const autocompletion_1 = require("./autocompletion");
8
10
  exports.sectionTitleKey = new prosemirror_state_1.PluginKey('sectionNumbering');
9
11
  const calculateSectionLevels = (node, startPos, sectionNumberMap, numbering = [0]) => {
10
12
  node.forEach((childNode, offset) => {
@@ -35,6 +37,21 @@ const getPluginState = (doc) => {
35
37
  exports.default = () => {
36
38
  return new prosemirror_state_1.Plugin({
37
39
  key: exports.sectionTitleKey,
40
+ props: {
41
+ decorations(state) {
42
+ const text = (0, autocompletion_1.checkForCompletion)(state);
43
+ if (text) {
44
+ const decoration = prosemirror_view_1.Decoration.widget(state.selection.from, () => {
45
+ const node = document.createElement('span');
46
+ node.classList.add('completion-bearer');
47
+ node.dataset.suggest = text.suggestion;
48
+ return node;
49
+ });
50
+ return prosemirror_view_1.DecorationSet.create(state.doc, [decoration]);
51
+ }
52
+ return prosemirror_view_1.DecorationSet.empty;
53
+ },
54
+ },
38
55
  state: {
39
56
  init: (_, state) => {
40
57
  return getPluginState(state.doc);
@@ -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 = '2.0.50';
4
+ exports.VERSION = '2.0.51';
5
5
  exports.MATHJAX_VERSION = '3.2.2';
@@ -26,11 +26,13 @@ import { getCommentKey, getCommentRange } from './lib/comments';
26
26
  import { findBackmatter, findBibliographySection, findBody, insertSupplementsNode, } from './lib/doc';
27
27
  import { getNewFootnotePos } from './lib/footnotes';
28
28
  import { findWordBoundaries, isNodeOfType, nearestAncestor, } from './lib/helpers';
29
+ import { sectionTitles } from './lib/section-titles';
29
30
  import { isDeleted, isRejectedInsert } from './lib/track-changes-utils';
30
31
  import { findParentNodeWithId, getChildOfType, getMatchingChild, } from './lib/utils';
31
32
  import { setCommentSelection } from './plugins/comments';
32
33
  import { getEditorProps } from './plugins/editor-props';
33
34
  import { footnotesKey } from './plugins/footnotes';
35
+ import { checkForCompletion } from './plugins/section_title/autocompletion';
34
36
  export const markActive = (type) => (state) => {
35
37
  const { from, $from, to, empty } = state.selection;
36
38
  return empty
@@ -462,16 +464,6 @@ export const insertSection = (subsection = false) => (state, dispatch, view) =>
462
464
  }
463
465
  return true;
464
466
  };
465
- const sectionTitles = new Map([
466
- ['MPSectionCategory:acknowledgement', 'Acknowledgments'],
467
- ['MPSectionCategory:availability', 'Availability'],
468
- ['MPSectionCategory:competing-interests', 'COI Statement'],
469
- ['MPSectionCategory:con', 'Contributed-by information'],
470
- ['MPSectionCategory:ethics-statement', 'Ethics Statement'],
471
- ['MPSectionCategory:financial-disclosure', 'Financial Disclosure'],
472
- ['MPSectionCategory:supplementary-material', 'Supplementary Material'],
473
- ['MPSectionCategory:supported-by', 'Supported By'],
474
- ]);
475
467
  export const insertBackMatterSection = (category) => (state, dispatch, view) => {
476
468
  const backmatter = findBackmatter(state.doc);
477
469
  const sections = findChildrenByType(backmatter.node, schema.nodes.section);
@@ -1099,3 +1091,16 @@ export function mergeCellsWithSpace(state, dispatch) {
1099
1091
  }
1100
1092
  return true;
1101
1093
  }
1094
+ export const autoComplete = (state, dispatch) => {
1095
+ const complete = checkForCompletion(state);
1096
+ if (complete) {
1097
+ const tr = state.tr.insertText(complete.suggestion, state.selection.from);
1098
+ const inserted = complete.title.substring(0, complete.title.length - complete.suggestion.length);
1099
+ if (inserted) {
1100
+ tr.replaceWith(state.selection.from - inserted.length, state.selection.from, schema.text(inserted));
1101
+ }
1102
+ dispatch && dispatch(tr);
1103
+ return true;
1104
+ }
1105
+ return false;
1106
+ };
@@ -14,11 +14,11 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { schema } from '@manuscripts/transform';
17
- import { chainCommands, exitCode, joinDown, joinUp, lift, toggleMark, wrapIn, } from 'prosemirror-commands';
17
+ import { chainCommands, createParagraphNear, exitCode, joinDown, joinUp, lift, liftEmptyBlock, newlineInCode, splitBlock, toggleMark, wrapIn, } from 'prosemirror-commands';
18
18
  import { redo, undo } from 'prosemirror-history';
19
19
  import { undoInputRule } from 'prosemirror-inputrules';
20
20
  import { goToNextCell } from 'prosemirror-tables';
21
- import { ignoreAtomBlockNodeBackward, ignoreAtomBlockNodeForward, ignoreMetaNodeBackspaceCommand, insertBlock, insertBreak, insertCrossReference, insertInlineCitation, insertInlineEquation, insertSection, selectAllIsolating, } from '../commands';
21
+ import { autoComplete, ignoreAtomBlockNodeBackward, ignoreAtomBlockNodeForward, ignoreMetaNodeBackspaceCommand, insertBlock, insertBreak, insertCrossReference, insertInlineCitation, insertInlineEquation, insertSection, selectAllIsolating, } from '../commands';
22
22
  const customKeymap = {
23
23
  Backspace: chainCommands(undoInputRule, ignoreAtomBlockNodeBackward, ignoreMetaNodeBackspaceCommand),
24
24
  Delete: ignoreAtomBlockNodeForward,
@@ -37,6 +37,7 @@ const customKeymap = {
37
37
  'Mod-Alt-=': toggleMark(schema.marks.superscript),
38
38
  'Mod-Alt--': toggleMark(schema.marks.subscript),
39
39
  'Ctrl->': wrapIn(schema.nodes.blockquote),
40
+ Enter: chainCommands(autoComplete, newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock),
40
41
  'Shift-Mod-Enter': insertSection(true),
41
42
  'Mod-Enter': chainCommands(exitCode, insertSection()),
42
43
  'Shift-Enter': chainCommands(exitCode, insertBreak),
@@ -0,0 +1,32 @@
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
+ export const sectionTitles = new Map([
17
+ ['MPSectionCategory:abstract', 'Abstract'],
18
+ ['MPSectionCategory:abstract-graphical', 'Graphical Abstract'],
19
+ ['MPSectionCategory:introduction', 'Introduction'],
20
+ ['MPSectionCategory:materials-method', 'Materials & Methods'],
21
+ ['MPSectionCategory:results', 'Results'],
22
+ ['MPSectionCategory:discussion', 'Discussion'],
23
+ ['MPSectionCategory:conclusions', 'Conclusions'],
24
+ ['MPSectionCategory:acknowledgement', 'Acknowledgments'],
25
+ ['MPSectionCategory:availability', 'Availability'],
26
+ ['MPSectionCategory:competing-interests', 'COI Statement'],
27
+ ['MPSectionCategory:con', 'Contributed-by information'],
28
+ ['MPSectionCategory:ethics-statement', 'Ethics Statement'],
29
+ ['MPSectionCategory:financial-disclosure', 'Financial Disclosure'],
30
+ ['MPSectionCategory:supplementary-material', 'Supplementary Material'],
31
+ ['MPSectionCategory:supported-by', 'Supported By'],
32
+ ]);
@@ -14,6 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { EditAttrsTrackingIcon } from '@manuscripts/style-guide';
17
+ import { schema } from '@manuscripts/transform';
17
18
  import { createElement } from 'react';
18
19
  import { renderToStaticMarkup } from 'react-dom/server';
19
20
  export function isRejectedInsert(node) {
@@ -101,6 +102,43 @@ export const getAttrsTrackingButton = (changeID) => {
101
102
  export function isHidden(node) {
102
103
  return isDeleted(node) || isRejectedInsert(node);
103
104
  }
105
+ export function isDeletedText(node) {
106
+ var _a, _b, _c, _d;
107
+ if (node.type === schema.nodes.text && node.marks.length) {
108
+ const deleteMark = node.marks.find((mark) => mark.type === schema.marks.tracked_delete);
109
+ if (deleteMark &&
110
+ ((_b = (_a = deleteMark.attrs) === null || _a === void 0 ? void 0 : _a.dataTracked) === null || _b === void 0 ? void 0 : _b.status) &&
111
+ ['pending', 'approved'].includes((_d = (_c = deleteMark.attrs) === null || _c === void 0 ? void 0 : _c.dataTracked) === null || _d === void 0 ? void 0 : _d.status)) {
112
+ return true;
113
+ }
114
+ }
115
+ return false;
116
+ }
117
+ export function isRejectedText(node) {
118
+ var _a, _b;
119
+ if (node.type === schema.nodes.text) {
120
+ const insertMark = node.marks.find((mark) => mark.type === schema.marks.tracked_insert);
121
+ if (insertMark && ((_b = (_a = insertMark.attrs) === null || _a === void 0 ? void 0 : _a.dataTracked) === null || _b === void 0 ? void 0 : _b.status) === 'rejected') {
122
+ return true;
123
+ }
124
+ }
125
+ return false;
126
+ }
127
+ export function getActualTextContent(fragment) {
128
+ let finalContent = '';
129
+ function getContent(fragment) {
130
+ fragment.forEach((node) => {
131
+ if (node.type !== schema.nodes.text) {
132
+ finalContent += getContent(node.content);
133
+ }
134
+ if (!isDeletedText(node) && !isRejectedText(node)) {
135
+ finalContent += node.textContent;
136
+ }
137
+ });
138
+ }
139
+ getContent(fragment);
140
+ return finalContent;
141
+ }
104
142
  export function sanitizeAttrsChange(newAttr, currentAttrs) {
105
143
  return Object.keys(newAttr).reduce((acc, attr) => {
106
144
  const key = attr;
@@ -0,0 +1,40 @@
1
+ import { schema, } from '@manuscripts/transform';
2
+ import { findParentNodeOfTypeClosestToPos } from 'prosemirror-utils';
3
+ import { sectionTitles } from '../../lib/section-titles';
4
+ import { getActualAttrs, getActualTextContent, } from '../../lib/track-changes-utils';
5
+ function cursorAtTheEndOfText(state, nodeSize, nodePos) {
6
+ const { from, to } = state.selection;
7
+ return from === to && to === nodePos + nodeSize - 1;
8
+ }
9
+ const isUpperCase = (test) => test === test.toUpperCase() && test.length > 1;
10
+ export function hasAutoCompletionSlack(parentSection, titleSection) {
11
+ const category = getActualAttrs(parentSection).category;
12
+ const title = sectionTitles.get(category);
13
+ if (category && title && titleSection.textContent) {
14
+ const actualTextContent = getActualTextContent(titleSection.content);
15
+ if (title.toLowerCase().startsWith(actualTextContent.toLowerCase())) {
16
+ const suggestionPart = title.slice(actualTextContent.length);
17
+ const suggestion = isUpperCase(actualTextContent)
18
+ ? suggestionPart.toUpperCase()
19
+ : suggestionPart;
20
+ return {
21
+ suggestion,
22
+ title: isUpperCase(actualTextContent) ? title.toUpperCase() : title,
23
+ };
24
+ }
25
+ }
26
+ return null;
27
+ }
28
+ export function checkForCompletion(state) {
29
+ const section = findParentNodeOfTypeClosestToPos(state.selection.$from, schema.nodes.section);
30
+ const title = findParentNodeOfTypeClosestToPos(state.selection.$from, schema.nodes.section_title);
31
+ if (section &&
32
+ title &&
33
+ cursorAtTheEndOfText(state, title.node.nodeSize, title.pos)) {
34
+ const text = hasAutoCompletionSlack(section.node, title.node);
35
+ if (text) {
36
+ return text;
37
+ }
38
+ }
39
+ return null;
40
+ }
@@ -1,7 +1,9 @@
1
1
  import { schema } from '@manuscripts/transform';
2
2
  import { Plugin, PluginKey } from 'prosemirror-state';
3
3
  import { findChildrenByType } from 'prosemirror-utils';
4
- import { isRejectedInsert } from '../lib/track-changes-utils';
4
+ import { Decoration, DecorationSet } from 'prosemirror-view';
5
+ import { isRejectedInsert } from '../../lib/track-changes-utils';
6
+ import { checkForCompletion } from './autocompletion';
5
7
  export const sectionTitleKey = new PluginKey('sectionNumbering');
6
8
  const calculateSectionLevels = (node, startPos, sectionNumberMap, numbering = [0]) => {
7
9
  node.forEach((childNode, offset) => {
@@ -32,6 +34,21 @@ const getPluginState = (doc) => {
32
34
  export default () => {
33
35
  return new Plugin({
34
36
  key: sectionTitleKey,
37
+ props: {
38
+ decorations(state) {
39
+ const text = checkForCompletion(state);
40
+ if (text) {
41
+ const decoration = Decoration.widget(state.selection.from, () => {
42
+ const node = document.createElement('span');
43
+ node.classList.add('completion-bearer');
44
+ node.dataset.suggest = text.suggestion;
45
+ return node;
46
+ });
47
+ return DecorationSet.create(state.doc, [decoration]);
48
+ }
49
+ return DecorationSet.empty;
50
+ },
51
+ },
35
52
  state: {
36
53
  init: (_, state) => {
37
54
  return getPluginState(state.doc);
@@ -1,2 +1,2 @@
1
- export const VERSION = '2.0.50';
1
+ export const VERSION = '2.0.51';
2
2
  export const MATHJAX_VERSION = '3.2.2';
@@ -75,4 +75,5 @@ export declare const insertTableFootnote: (tableElementNode: ManuscriptNode, pos
75
75
  export declare const addRows: (direction: 'top' | 'bottom') => (state: EditorState, dispatch?: ((tr: Transaction) => void) | undefined) => boolean;
76
76
  export declare const addColumns: (direction: 'right' | 'left') => (state: EditorState, dispatch?: ((tr: Transaction) => void) | undefined) => boolean;
77
77
  export declare function mergeCellsWithSpace(state: EditorState, dispatch?: (tr: Transaction) => void): boolean;
78
+ export declare const autoComplete: (state: ManuscriptEditorState, dispatch?: Dispatch) => boolean;
78
79
  export {};
@@ -0,0 +1,17 @@
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 { SectionCategory } from '@manuscripts/transform';
17
+ export declare const sectionTitles: Map<SectionCategory, string>;
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { TrackedAttrs } from '@manuscripts/track-changes-plugin';
17
17
  import { ManuscriptNode } from '@manuscripts/transform';
18
- import { Node as ProsemirrorNode } from 'prosemirror-model';
18
+ import { Fragment, Node as ProsemirrorNode } from 'prosemirror-model';
19
19
  export declare function isRejectedInsert(node: ProsemirrorNode): boolean;
20
20
  export declare function isDeleted(node: ProsemirrorNode): boolean;
21
21
  export declare function isPendingInsert(node: ProsemirrorNode): boolean;
@@ -26,4 +26,7 @@ export declare function isTracked(node: ProsemirrorNode): boolean;
26
26
  export declare function getActualAttrs<T extends ManuscriptNode>(node: T): T["attrs"];
27
27
  export declare const getAttrsTrackingButton: (changeID: string) => HTMLButtonElement;
28
28
  export declare function isHidden(node: ProsemirrorNode): boolean;
29
+ export declare function isDeletedText(node: ProsemirrorNode): boolean;
30
+ export declare function isRejectedText(node: ProsemirrorNode): boolean;
31
+ export declare function getActualTextContent(fragment: Fragment): string;
29
32
  export declare function sanitizeAttrsChange<T extends ProsemirrorNode>(newAttr: T['attrs'], currentAttrs: T['attrs']): T["attrs"];
@@ -0,0 +1,10 @@
1
+ import { SectionNode, SectionTitleNode } from '@manuscripts/transform';
2
+ import { EditorState } from 'prosemirror-state';
3
+ export declare function hasAutoCompletionSlack(parentSection: SectionNode, titleSection: SectionTitleNode): {
4
+ suggestion: string;
5
+ title: string;
6
+ } | null;
7
+ export declare function checkForCompletion(state: EditorState): {
8
+ suggestion: string;
9
+ title: string;
10
+ } | null;
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "2.0.50";
1
+ export declare const VERSION = "2.0.51";
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": "2.0.50",
4
+ "version": "2.0.51",
5
5
  "repository": "github:Atypon-OpenSource/manuscripts-body-editor",
6
6
  "license": "Apache-2.0",
7
7
  "main": "dist/cjs",
@@ -116,4 +116,4 @@
116
116
  "engines": {
117
117
  "node": ">=20.16.0"
118
118
  }
119
- }
119
+ }
@@ -1,20 +1,15 @@
1
1
  .ProseMirror
2
2
  > div.body
3
- section:not(.toc)
4
- .block-section_title
3
+ section:not(.toc)
4
+ .block-section_title
5
5
  h1.empty-node[data-placeholder]::before {
6
6
  content: attr(data-section-number) '. ' attr(data-placeholder);
7
7
  }
8
8
 
9
- .ProseMirror
10
- > div.body
11
- section:not(.toc)
12
- .block-section_title
13
- h1::before {
9
+ .ProseMirror > div.body section:not(.toc) .block-section_title h1::before {
14
10
  content: attr(data-section-number) '. ';
15
11
  }
16
12
 
17
-
18
13
  .ProseMirror > div .block-section_title > h1 {
19
14
  font-size: 16pt !important;
20
15
  font-style: normal !important;
@@ -39,8 +34,6 @@
39
34
  font-size: 13.5pt !important;
40
35
  }
41
36
 
42
-
43
-
44
37
  .ProseMirror div.backmatter::before {
45
38
  content: '';
46
39
  border-bottom: 1px dashed #c9c9c9;
@@ -51,6 +44,15 @@
51
44
  font-size: 13.5pt !important;
52
45
  color: #6e6e6e !important;
53
46
  }
47
+ .completion-bearer {
48
+ pointer-events: none;
49
+ }
50
+ .block-section_title .completion-bearer:after {
51
+ content: attr(data-suggest);
52
+ opacity: 0.5;
53
+ pointer-events: none;
54
+ font-style: italic;
55
+ }
54
56
 
55
57
  .ProseMirror p.block {
56
58
  font-size: 12pt !important;
@@ -154,8 +156,8 @@
154
156
  position: static;
155
157
  }
156
158
 
157
-
158
- .comment-marker.selected-comment > svg > path, .comment-marker:active > svg > path {
159
+ .comment-marker.selected-comment > svg > path,
160
+ .comment-marker:active > svg > path {
159
161
  stroke: #f7b314;
160
162
  fill: #ffe0b2;
161
163
  }
@@ -170,7 +172,8 @@
170
172
  position: relative;
171
173
  }
172
174
 
173
- .block > div.comment-marker, .figure-block > div.comment-marker {
175
+ .block > div.comment-marker,
176
+ .figure-block > div.comment-marker {
174
177
  top: -25px;
175
178
  left: 100%;
176
179
  }
@@ -209,7 +212,7 @@ span.comment-marker {
209
212
  top: 12px;
210
213
  }
211
214
 
212
- .comment-marker[data-count="0"] {
215
+ .comment-marker[data-count='0'] {
213
216
  display: none;
214
217
  }
215
218
 
@@ -221,7 +224,7 @@ span.comment-marker {
221
224
  content: attr(data-count);
222
225
  top: 8px;
223
226
  left: 8px;
224
- background-color: #F7B314;
227
+ background-color: #f7b314;
225
228
  border-radius: 50%;
226
229
  width: 12px;
227
230
  height: 12px;
@@ -287,7 +290,7 @@ span.comment-marker {
287
290
  font-size: 16px !important;
288
291
  font-weight: 700 !important;
289
292
  line-height: 24px !important;
290
- margin: 0 !important
293
+ margin: 0 !important;
291
294
  }
292
295
 
293
296
  .ProseMirror .author-notes-container > div {
@@ -309,7 +312,7 @@ span.comment-marker {
309
312
  position: relative;
310
313
  cursor: default;
311
314
  padding: 4px 8px;
312
- border: 1px solid #C9C9C9;
315
+ border: 1px solid #c9c9c9;
313
316
  margin-top: 10px;
314
317
  margin-bottom: 4px;
315
318
  }
@@ -375,7 +378,7 @@ span.comment-marker {
375
378
 
376
379
  .keywords .keyword-group-container {
377
380
  position: relative;
378
- margin: 0 0 36px !important;
381
+ margin: 0 0 36px !important;
379
382
  }
380
383
 
381
384
  .keywords .keyword-group {
@@ -449,8 +452,8 @@ span.comment-marker {
449
452
  --inserted-pending-bg-color: #e6ffdb;
450
453
  --inserted-pending-color: #01872e;
451
454
  --accepted-bg-color: #e2e2e2;
452
- --deleted-color: #F35143;
453
- --deleted-pending-bg-color: #FFF1F0;
455
+ --deleted-color: #f35143;
456
+ --deleted-pending-bg-color: #fff1f0;
454
457
  --highlight-bg-color: #ffeebf;
455
458
  --common-color: #353535;
456
459
  --selected-pending-background-color: #ddf3fa;
@@ -479,22 +482,28 @@ span.selected-suggestion,
479
482
  span.accepted .selected-suggestion,
480
483
  .accepted .selected-suggestion > .block-container .block,
481
484
  .accepted .selected-suggestion:not(:has(> .block-container)),
482
- .selected-suggestion[data-track-status="accepted"],
483
- .selected-suggestion > .block-container .block.accepted,
484
- .selected-suggestion > .block-container .block:has([data-track-status='accepted']),
485
- .selected-suggestion:not(:has(> .block-container)):has([data-track-status='accepted']) {
485
+ .selected-suggestion[data-track-status='accepted'],
486
+ .selected-suggestion > .block-container .block.accepted,
487
+ .selected-suggestion
488
+ > .block-container
489
+ .block:has([data-track-status='accepted']),
490
+ .selected-suggestion:not(:has(> .block-container)):has(
491
+ [data-track-status='accepted']
492
+ ) {
486
493
  background-color: var(--selected-accepted-background-color) !important;
487
494
  border-width: 2px 0 2px 0 !important;
488
- border-color: #C9C9C9 !important;
495
+ border-color: #c9c9c9 !important;
489
496
  border-radius: 3px !important;
490
497
  border-style: solid !important;
491
498
  }
492
499
 
493
- .block-bullet_list .selected-suggestion p, .block-ordered_list .selected-suggestion p {
500
+ .block-bullet_list .selected-suggestion p,
501
+ .block-ordered_list .selected-suggestion p {
494
502
  margin: 0;
495
503
  }
496
504
 
497
- .block-bullet_list .selected-suggestion, .block-ordered_list .selected-suggestion {
505
+ .block-bullet_list .selected-suggestion,
506
+ .block-ordered_list .selected-suggestion {
498
507
  margin: 16px 0;
499
508
  }
500
509
 
@@ -506,7 +515,10 @@ span.accepted .selected-suggestion,
506
515
  }
507
516
 
508
517
  .ProseMirror .selected-suggestion .inserted.pending,
509
- .ProseMirror .selected-suggestion [data-track-op='insert'][data-track-status='pending'] .block,
518
+ .ProseMirror
519
+ .selected-suggestion
520
+ [data-track-op='insert'][data-track-status='pending']
521
+ .block,
510
522
  .ProseMirror .selected-suggestion .deleted.pending {
511
523
  background-color: var(--selected-pending-background-color) !important;
512
524
  color: var(--common-color) !important;
@@ -549,7 +561,11 @@ span.accepted .selected-suggestion,
549
561
  .ProseMirror .inserted.pending:has(.selected-suggestion),
550
562
  .ProseMirror .deleted.pending:has(.selected-suggestion),
551
563
  .ProseMirror .selected-suggestion .highlight,
552
- .ProseMirror .selected-suggestion .graphical-abstract:has([data-track-status='pending'][data-track-op='insert']) {
564
+ .ProseMirror
565
+ .selected-suggestion
566
+ .graphical-abstract:has(
567
+ [data-track-status='pending'][data-track-op='insert']
568
+ ) {
553
569
  background: transparent !important;
554
570
  color: var(--common-color);
555
571
  text-decoration: none;
@@ -588,7 +604,7 @@ span.accepted .selected-suggestion,
588
604
  .affiliation[data-track-status='pending'][data-track-op='delete'] {
589
605
  text-decoration: line-through;
590
606
  color: var(--deleted-color);
591
- background-color: var(--deleted-pending-bg-color)
607
+ background-color: var(--deleted-pending-bg-color);
592
608
  }
593
609
 
594
610
  .contributor[data-track-op='delete']:not([data-track-status='rejected']),
@@ -602,7 +618,9 @@ span.accepted .selected-suggestion,
602
618
  color: var(--inserted-pending-color);
603
619
  text-decoration: underline;
604
620
  }
605
- .block-contributors, .block-affiliations, .keywords {
621
+ .block-contributors,
622
+ .block-affiliations,
623
+ .keywords {
606
624
  margin: 15px 0px;
607
625
  }
608
626
  .contributors {
@@ -703,7 +721,6 @@ span.accepted .selected-suggestion,
703
721
  margin: 1rem 64px 0;
704
722
  font-size: 12px;
705
723
  color: #6e6e6e;
706
-
707
724
  }
708
725
 
709
726
  * {
@@ -741,7 +758,9 @@ span.accepted .selected-suggestion,
741
758
  z-index: 10;
742
759
  }
743
760
 
744
- .ProseMirror td:hover > .table-context-menu-button, th:hover > .table-context-menu-button, .open-context-menu {
761
+ .ProseMirror td:hover > .table-context-menu-button,
762
+ th:hover > .table-context-menu-button,
763
+ .open-context-menu {
745
764
  visibility: visible !important;
746
765
  }
747
766
 
@@ -766,7 +785,7 @@ span.accepted .selected-suggestion,
766
785
  }
767
786
 
768
787
  .section-category-button:not(.assigned) path {
769
- fill: #FFE0B2;
788
+ fill: #ffe0b2;
770
789
  }
771
790
 
772
791
  .section-category-button.assigned:after {
@@ -776,8 +795,8 @@ span.accepted .selected-suggestion,
776
795
  content: '';
777
796
  height: 8px;
778
797
  width: 4px;
779
- border-bottom: 2px solid #36B260;
780
- border-right: 2px solid #36B260;
798
+ border-bottom: 2px solid #36b260;
799
+ border-right: 2px solid #36b260;
781
800
  transform: rotate(45deg);
782
801
  }
783
802
 
@@ -786,8 +805,8 @@ span.accepted .selected-suggestion,
786
805
  }
787
806
  .section-category.menu {
788
807
  border-radius: 8px;
789
- border: 1px solid #E2E2E2;
790
- box-shadow: 0px 4px 9px 0px rgba(0, 0, 0, 0.30);
808
+ border: 1px solid #e2e2e2;
809
+ box-shadow: 0px 4px 9px 0px rgba(0, 0, 0, 0.3);
791
810
  }
792
811
 
793
812
  .section-category.menu .menu-item {
@@ -814,8 +833,8 @@ span.accepted .selected-suggestion,
814
833
  content: '';
815
834
  height: 10px;
816
835
  width: 5px;
817
- border-bottom: 2px solid #36B260;
818
- border-right: 2px solid #36B260;
836
+ border-bottom: 2px solid #36b260;
837
+ border-right: 2px solid #36b260;
819
838
  transform: rotate(45deg);
820
839
  }
821
840
 
@@ -831,16 +850,16 @@ span.accepted .selected-suggestion,
831
850
  display: flex;
832
851
  align-items: center;
833
852
  border-radius: 6px;
834
- background: #E2E2E2;
853
+ background: #e2e2e2;
835
854
  font-size: 12px;
836
855
  font-style: normal;
837
856
  font-weight: 400;
838
- color: #353535;
857
+ color: #353535;
839
858
  line-height: 16px;
840
859
  }
841
860
 
842
861
  .section-category.tooltip span {
843
- color: #353535;
862
+ color: #353535;
844
863
  font-size: 12px;
845
864
  font-style: normal;
846
865
  font-weight: 700;