@manuscripts/body-editor 1.7.34 → 1.7.36

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.
@@ -727,22 +727,35 @@ const updateCommentAnnotationState = (state, dispatch) => {
727
727
  }
728
728
  };
729
729
  exports.updateCommentAnnotationState = updateCommentAnnotationState;
730
- const insertTableFootnote = (node, position, view) => {
730
+ const insertTableFootnote = (node, position, view, inlineFootnote) => {
731
731
  var _a;
732
732
  const { state, dispatch } = view;
733
+ const tr = state.tr;
733
734
  const footnote = state.schema.nodes.footnote.createAndFill({
734
735
  id: (0, transform_1.generateID)(json_schema_1.ObjectTypes.Footnote),
735
736
  kind: 'footnote',
736
737
  });
737
738
  const insertedAt = state.selection.to;
738
- const inlineFootnotes = (0, prosemirror_utils_1.findChildrenByType)(node, transform_1.schema.nodes.inline_footnote);
739
- const footnoteIndex = inlineFootnotes.filter(({ pos }) => position + pos <= insertedAt).length + 1;
740
- const inlineFootnoteNode = state.schema.nodes.inline_footnote.create({
741
- rids: [footnote.attrs.id],
742
- contents: footnoteIndex === -1 ? inlineFootnotes.length : footnoteIndex,
743
- });
744
- const tr = state.tr;
745
- tr.insert(insertedAt, inlineFootnoteNode);
739
+ let footnoteIndex;
740
+ if (inlineFootnote) {
741
+ const contents = inlineFootnote.node.attrs.contents.split(',').map(Number);
742
+ footnoteIndex = Math.max(...contents) + 1;
743
+ tr.setNodeMarkup(inlineFootnote.pos, undefined, {
744
+ rids: [...inlineFootnote.node.attrs.rids, footnote.attrs.id],
745
+ contents: inlineFootnote.node.attrs.contents + ',' + footnoteIndex,
746
+ });
747
+ }
748
+ else {
749
+ const inlineFootnotes = (0, prosemirror_utils_1.findChildrenByType)(node, transform_1.schema.nodes.inline_footnote);
750
+ footnoteIndex =
751
+ inlineFootnotes.filter(({ pos }) => position + pos <= insertedAt).length +
752
+ 1;
753
+ const inlineFootnoteNode = state.schema.nodes.inline_footnote.create({
754
+ rids: [footnote.attrs.id],
755
+ contents: footnoteIndex === -1 ? inlineFootnotes.length : footnoteIndex,
756
+ });
757
+ tr.insert(insertedAt, inlineFootnoteNode);
758
+ }
746
759
  let insertionPos = position;
747
760
  const footnotesElement = (0, prosemirror_utils_1.findChildrenByType)(node, transform_1.schema.nodes.footnotes_element).pop();
748
761
  if (footnotesElement &&
@@ -198,9 +198,10 @@ class ContextMenu {
198
198
  else {
199
199
  const tablesFootnoteLabels = (0, footnotes_utils_1.buildTableFootnoteLabels)(this.node);
200
200
  const footnotesWithPos = (0, prosemirror_utils_1.findChildrenByType)(footnotesElementWithPos.node, transform_1.schema.nodes.footnote);
201
- const footnotes = footnotesWithPos.map((nodeWithPos) => ({
202
- node: nodeWithPos.node,
203
- index: tablesFootnoteLabels.get(nodeWithPos.node.attrs.id),
201
+ const footnotes = footnotesWithPos
202
+ .map(({ node }) => ({
203
+ node: node,
204
+ index: tablesFootnoteLabels.get(node.attrs.id),
204
205
  }));
205
206
  const targetDom = this.view.domAtPos(this.view.state.selection.from);
206
207
  if (targetDom.node instanceof Element && this.props) {
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.AffiliationsView = void 0;
7
+ const prosemirror_state_1 = require("prosemirror-state");
7
8
  const block_view_1 = __importDefault(require("./block_view"));
8
9
  const creators_1 = require("./creators");
9
10
  class AffiliationsView extends block_view_1.default {
@@ -18,6 +19,14 @@ class AffiliationsView extends block_view_1.default {
18
19
  this.dom.setAttribute('contenteditable', 'false');
19
20
  this.dom.appendChild(this.contentDOM);
20
21
  };
22
+ this.updateContents = () => {
23
+ this.dom.addEventListener('click', () => this.onClickHandler());
24
+ };
25
+ this.onClickHandler = () => {
26
+ const { tr, doc } = this.view.state;
27
+ tr.setSelection(prosemirror_state_1.NodeSelection.create(doc, this.getPos()));
28
+ this.view.dispatch(tr);
29
+ };
21
30
  }
22
31
  }
23
32
  exports.AffiliationsView = AffiliationsView;
@@ -74,6 +74,7 @@ class CitationEditableView extends citation_1.CitationView {
74
74
  this.props.popper.destroy();
75
75
  };
76
76
  this.showContextMenu = () => {
77
+ this.props.popper.destroy();
77
78
  const componentProps = {
78
79
  actions: [
79
80
  { label: 'Edit', action: this.handleEdit, icon: 'EditIcon' },
@@ -20,7 +20,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
21
  exports.ContributorsView = void 0;
22
22
  const style_guide_1 = require("@manuscripts/style-guide");
23
+ const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
23
24
  const transform_1 = require("@manuscripts/transform");
25
+ const prosemirror_state_1 = require("prosemirror-state");
24
26
  const AuthorsModal_1 = require("../components/authors/AuthorsModal");
25
27
  const authors_1 = require("../lib/authors");
26
28
  const track_changes_utils_1 = require("../lib/track-changes-utils");
@@ -41,12 +43,14 @@ class ContributorsView extends block_view_1.default {
41
43
  this.updateContents();
42
44
  };
43
45
  this.updateContents = () => {
44
- this.updateClasses();
45
- this.updateAttributes();
46
- this.container.innerHTML = '';
47
- this.buildAuthors();
48
- this.createEditButton();
49
- this.createLegend();
46
+ setTimeout(() => {
47
+ this.updateClasses();
48
+ this.updateAttributes();
49
+ this.container.innerHTML = '';
50
+ this.buildAuthors();
51
+ this.createEditButton();
52
+ this.createLegend();
53
+ }, 0);
50
54
  };
51
55
  this.buildAuthors = () => {
52
56
  var _a;
@@ -79,7 +83,9 @@ class ContributorsView extends block_view_1.default {
79
83
  const pluginState = affiliations_1.affiliationsKey.getState(this.view.state);
80
84
  const attrs = node.attrs;
81
85
  const displayAttr = (0, track_changes_utils_1.getActualAttrs)(node);
86
+ const containerWrapper = document.createElement('div');
82
87
  const container = document.createElement('button');
88
+ containerWrapper.classList.add('contributor-wrapper');
83
89
  container.classList.add('contributor');
84
90
  container.setAttribute('id', attrs.id);
85
91
  container.setAttribute('contenteditable', 'false');
@@ -95,9 +101,12 @@ class ContributorsView extends block_view_1.default {
95
101
  }
96
102
  const can = this.props.getCapabilities();
97
103
  const disableEditButton = !can.editMetadata;
98
- const { isCorresponding, email } = displayAttr;
104
+ const { isCorresponding, email, id } = displayAttr;
99
105
  if (!disableEditButton) {
100
- container.addEventListener('click', this.handleClick);
106
+ container.addEventListener('click', () => {
107
+ const dataTracked = attrs.dataTracked;
108
+ this.onClickHandler(id, dataTracked ? dataTracked[0] : undefined);
109
+ });
101
110
  }
102
111
  const name = (0, authors_1.authorLabel)(displayAttr);
103
112
  container.innerHTML =
@@ -125,7 +134,8 @@ class ContributorsView extends block_view_1.default {
125
134
  if (selectedAuthor && selectedAuthor === attrs.id) {
126
135
  container.classList.add('selected-suggestion');
127
136
  }
128
- return container;
137
+ containerWrapper.appendChild(container);
138
+ return containerWrapper;
129
139
  };
130
140
  this.isJointFirstAuthor = (authors, index) => {
131
141
  const author = index === 0 ? authors[index] : authors[index - 1];
@@ -147,7 +157,7 @@ class ContributorsView extends block_view_1.default {
147
157
  const can = this.props.getCapabilities();
148
158
  const button = (0, ReactSubView_1.default)(this.props, style_guide_1.SecondaryButton, {
149
159
  mini: true,
150
- onClick: this.handleClick,
160
+ onClick: () => this.handleEdit(''),
151
161
  className: 'edit-authors-button',
152
162
  disabled: !can.editMetadata,
153
163
  children: 'Edit Authors',
@@ -167,17 +177,48 @@ class ContributorsView extends block_view_1.default {
167
177
  }
168
178
  }
169
179
  };
170
- this.handleClick = (e) => {
171
- var _a, _b;
172
- e.stopPropagation();
180
+ this.onClickHandler = (elementId, dataTracked) => {
181
+ var _a;
182
+ this.props.popper.destroy();
183
+ const isSelectedSuggestion = !!((_a = selected_suggestion_ui_1.selectedSuggestionKey
184
+ .getState(this.view.state)) === null || _a === void 0 ? void 0 : _a.find(this.getPos(), this.getPos() + this.node.nodeSize).length);
185
+ const { tr, doc } = this.view.state;
186
+ tr.setSelection(prosemirror_state_1.NodeSelection.create(doc, this.getPos()));
187
+ if (dataTracked && dataTracked.status !== track_changes_plugin_1.CHANGE_STATUS.rejected) {
188
+ tr.setMeta(selected_suggestion_ui_1.SET_SUGGESTION_ID, dataTracked.id);
189
+ }
190
+ else {
191
+ if (isSelectedSuggestion) {
192
+ tr.setMeta(selected_suggestion_ui_1.CLEAR_SUGGESTION_ID, true);
193
+ }
194
+ }
195
+ if (!((dataTracked === null || dataTracked === void 0 ? void 0 : dataTracked.operation) === track_changes_plugin_1.CHANGE_OPERATION.delete &&
196
+ (dataTracked === null || dataTracked === void 0 ? void 0 : dataTracked.status) !== track_changes_plugin_1.CHANGE_STATUS.rejected)) {
197
+ this.showContextMenu(elementId);
198
+ }
199
+ this.view.dispatch(tr);
200
+ };
201
+ this.showContextMenu = (elementId) => {
202
+ this.props.popper.destroy();
203
+ const element = document.getElementById(elementId);
204
+ const componentProps = {
205
+ actions: [
206
+ {
207
+ label: 'Edit',
208
+ action: () => this.handleEdit(elementId),
209
+ icon: 'EditIcon',
210
+ },
211
+ ],
212
+ };
213
+ this.contextMenu = (0, ReactSubView_1.default)(this.props, style_guide_1.ContextMenu, componentProps, this.node, this.getPos, this.view, 'context-menu');
214
+ this.props.popper.show(element, this.contextMenu, 'right-start');
215
+ };
216
+ this.handleEdit = (id) => {
217
+ var _a;
218
+ this.props.popper.destroy();
173
219
  const contributors = (0, view_1.findChildrenAttrsByType)(this.view, transform_1.schema.nodes.contributor);
174
220
  const affiliations = (0, view_1.findChildrenAttrsByType)(this.view, transform_1.schema.nodes.affiliation);
175
- let author = undefined;
176
- const target = e.target;
177
- if (target) {
178
- const id = (_a = target.closest('.contributor')) === null || _a === void 0 ? void 0 : _a.getAttribute('id');
179
- author = contributors.filter((a) => a.id === id)[0];
180
- }
221
+ const author = id ? contributors.filter((a) => a.id === id)[0] : undefined;
181
222
  const componentProps = {
182
223
  author,
183
224
  authors: contributors,
@@ -186,7 +227,7 @@ class ContributorsView extends block_view_1.default {
186
227
  onDeleteAuthor: this.handleDeleteAuthor,
187
228
  onSaveAffiliation: this.handleSaveAffiliation,
188
229
  };
189
- (_b = this.popper) === null || _b === void 0 ? void 0 : _b.remove();
230
+ (_a = this.popper) === null || _a === void 0 ? void 0 : _a.remove();
190
231
  this.popper = (0, ReactSubView_1.default)(this.props, AuthorsModal_1.AuthorsModal, componentProps, this.node, this.getPos, this.view);
191
232
  this.container.appendChild(this.popper);
192
233
  };
@@ -219,8 +260,6 @@ class ContributorsView extends block_view_1.default {
219
260
  const node = transform_1.schema.nodes.affiliation.create(attrs);
220
261
  this.view.dispatch(tr.insert(parent.pos + 1, node));
221
262
  };
222
- this.ignoreMutation = () => true;
223
- this.stopEvent = () => true;
224
263
  }
225
264
  createNote(text = '', title = '') {
226
265
  const el = document.createElement('span');
@@ -14,16 +14,43 @@
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.InlineFootnoteView = void 0;
22
+ const style_guide_1 = require("@manuscripts/style-guide");
23
+ const transform_1 = require("@manuscripts/transform");
24
+ const prosemirror_utils_1 = require("prosemirror-utils");
25
+ const commands_1 = require("../commands");
19
26
  const track_changes_utils_1 = require("../lib/track-changes-utils");
27
+ const footnotes_utils_1 = require("../plugins/footnotes/footnotes-utils");
20
28
  const base_node_view_1 = require("./base_node_view");
21
29
  const creators_1 = require("./creators");
30
+ const ReactSubView_1 = __importDefault(require("./ReactSubView"));
22
31
  class InlineFootnoteView extends base_node_view_1.BaseNodeView {
23
32
  constructor() {
24
33
  super(...arguments);
34
+ this.findParentTableElement = () => (0, prosemirror_utils_1.findParentNodeClosestToPos)(this.view.state.doc.resolve(this.getPos()), (node) => node.type === transform_1.schema.nodes.table_element);
25
35
  this.handleClick = () => {
26
- this.props.history.push(Object.assign(Object.assign({}, this.props.history.location), { hash: '#' + this.node.attrs.rid }));
36
+ if ((0, track_changes_utils_1.isDeleted)(this.node)) {
37
+ return;
38
+ }
39
+ const tableElement = this.findParentTableElement();
40
+ if (tableElement) {
41
+ const componentProps = {
42
+ notes: this.getNotes(tableElement),
43
+ onAdd: this.onAdd,
44
+ onInsert: this.onUpdate,
45
+ onCancel: this.destroy,
46
+ inlineFootnote: this.node,
47
+ };
48
+ this.popperContainer = (0, ReactSubView_1.default)(Object.assign(Object.assign({}, this.props), { dispatch: this.view.dispatch }), style_guide_1.TableFootnotesSelector, componentProps, this.node, this.getPos, this.view, 'table-footnote-editor');
49
+ this.props.popper.show(this.dom, this.popperContainer, 'bottom-end');
50
+ }
51
+ else {
52
+ this.props.history.push(Object.assign(Object.assign({}, this.props.history.location), { hash: '#' + this.node.attrs.rid }));
53
+ }
27
54
  };
28
55
  this.updateContents = () => {
29
56
  this.setDomAttrs(this.node, this.dom);
@@ -42,6 +69,49 @@ class InlineFootnoteView extends base_node_view_1.BaseNodeView {
42
69
  this.createDOM = () => {
43
70
  return document.createElement('span');
44
71
  };
72
+ this.destroy = () => {
73
+ var _a;
74
+ this.props.popper.destroy();
75
+ (_a = this.popperContainer) === null || _a === void 0 ? void 0 : _a.remove();
76
+ };
77
+ this.getNotes = (tableElement) => {
78
+ var _a;
79
+ const footnotesElement = (_a = (0, prosemirror_utils_1.findChildrenByType)(tableElement.node, transform_1.schema.nodes.footnotes_element).pop()) === null || _a === void 0 ? void 0 : _a.node;
80
+ let footnotes = [];
81
+ if (footnotesElement) {
82
+ const tablesFootnoteLabels = (0, footnotes_utils_1.buildTableFootnoteLabels)(tableElement.node);
83
+ footnotes = (0, prosemirror_utils_1.findChildrenByType)(footnotesElement, transform_1.schema.nodes.footnote)
84
+ .map(({ node }) => ({
85
+ node: node,
86
+ index: tablesFootnoteLabels.get(node.attrs.id),
87
+ }));
88
+ }
89
+ return footnotes;
90
+ };
91
+ this.onAdd = () => {
92
+ const tableElement = this.findParentTableElement();
93
+ if (tableElement) {
94
+ (0, commands_1.insertTableFootnote)(tableElement.node, tableElement.pos, this.view, {
95
+ node: this.node,
96
+ pos: this.getPos(),
97
+ });
98
+ this.destroy();
99
+ }
100
+ };
101
+ this.onUpdate = (notes) => {
102
+ const contents = this.node.attrs.contents.split(',');
103
+ const rids = notes.map((note) => note.node.attrs.id);
104
+ const { tr } = this.view.state;
105
+ this.view.dispatch(rids.length
106
+ ? tr.setNodeMarkup(this.getPos(), undefined, {
107
+ rids,
108
+ contents: notes
109
+ .map(({ index }) => (index ? index : Math.max(...contents) + 1))
110
+ .join(),
111
+ })
112
+ : tr.delete(this.getPos(), this.getPos() + this.node.nodeSize));
113
+ this.destroy();
114
+ };
45
115
  }
46
116
  }
47
117
  exports.InlineFootnoteView = InlineFootnoteView;
@@ -19,6 +19,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
21
  exports.KeywordGroupView = void 0;
22
+ const prosemirror_state_1 = require("prosemirror-state");
22
23
  const AddKeywordInline_1 = require("../components/keywords/AddKeywordInline");
23
24
  const block_view_1 = __importDefault(require("./block_view"));
24
25
  const creators_1 = require("./creators");
@@ -44,6 +45,15 @@ class KeywordGroupView extends block_view_1.default {
44
45
  this.element.appendChild(this.addingTools);
45
46
  }
46
47
  };
48
+ this.updateContents = () => {
49
+ this.dom.addEventListener('click', (event) => this.onClickHandler(event));
50
+ };
51
+ this.onClickHandler = (event) => {
52
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
53
+ const { tr, doc } = this.view.state;
54
+ tr.setSelection(prosemirror_state_1.NodeSelection.create(doc, this.getPos()));
55
+ this.view.dispatch(tr);
56
+ };
47
57
  }
48
58
  }
49
59
  exports.KeywordGroupView = KeywordGroupView;
@@ -690,22 +690,35 @@ export const updateCommentAnnotationState = (state, dispatch) => {
690
690
  dispatch(tr);
691
691
  }
692
692
  };
693
- export const insertTableFootnote = (node, position, view) => {
693
+ export const insertTableFootnote = (node, position, view, inlineFootnote) => {
694
694
  var _a;
695
695
  const { state, dispatch } = view;
696
+ const tr = state.tr;
696
697
  const footnote = state.schema.nodes.footnote.createAndFill({
697
698
  id: generateID(ObjectTypes.Footnote),
698
699
  kind: 'footnote',
699
700
  });
700
701
  const insertedAt = state.selection.to;
701
- const inlineFootnotes = findChildrenByType(node, schema.nodes.inline_footnote);
702
- const footnoteIndex = inlineFootnotes.filter(({ pos }) => position + pos <= insertedAt).length + 1;
703
- const inlineFootnoteNode = state.schema.nodes.inline_footnote.create({
704
- rids: [footnote.attrs.id],
705
- contents: footnoteIndex === -1 ? inlineFootnotes.length : footnoteIndex,
706
- });
707
- const tr = state.tr;
708
- tr.insert(insertedAt, inlineFootnoteNode);
702
+ let footnoteIndex;
703
+ if (inlineFootnote) {
704
+ const contents = inlineFootnote.node.attrs.contents.split(',').map(Number);
705
+ footnoteIndex = Math.max(...contents) + 1;
706
+ tr.setNodeMarkup(inlineFootnote.pos, undefined, {
707
+ rids: [...inlineFootnote.node.attrs.rids, footnote.attrs.id],
708
+ contents: inlineFootnote.node.attrs.contents + ',' + footnoteIndex,
709
+ });
710
+ }
711
+ else {
712
+ const inlineFootnotes = findChildrenByType(node, schema.nodes.inline_footnote);
713
+ footnoteIndex =
714
+ inlineFootnotes.filter(({ pos }) => position + pos <= insertedAt).length +
715
+ 1;
716
+ const inlineFootnoteNode = state.schema.nodes.inline_footnote.create({
717
+ rids: [footnote.attrs.id],
718
+ contents: footnoteIndex === -1 ? inlineFootnotes.length : footnoteIndex,
719
+ });
720
+ tr.insert(insertedAt, inlineFootnoteNode);
721
+ }
709
722
  let insertionPos = position;
710
723
  const footnotesElement = findChildrenByType(node, schema.nodes.footnotes_element).pop();
711
724
  if (footnotesElement &&
@@ -191,9 +191,10 @@ export class ContextMenu {
191
191
  else {
192
192
  const tablesFootnoteLabels = buildTableFootnoteLabels(this.node);
193
193
  const footnotesWithPos = findChildrenByType(footnotesElementWithPos.node, schema.nodes.footnote);
194
- const footnotes = footnotesWithPos.map((nodeWithPos) => ({
195
- node: nodeWithPos.node,
196
- index: tablesFootnoteLabels.get(nodeWithPos.node.attrs.id),
194
+ const footnotes = footnotesWithPos
195
+ .map(({ node }) => ({
196
+ node: node,
197
+ index: tablesFootnoteLabels.get(node.attrs.id),
197
198
  }));
198
199
  const targetDom = this.view.domAtPos(this.view.state.selection.from);
199
200
  if (targetDom.node instanceof Element && this.props) {
@@ -1,3 +1,4 @@
1
+ import { NodeSelection } from 'prosemirror-state';
1
2
  import BlockView from './block_view';
2
3
  import { createNodeView } from './creators';
3
4
  export class AffiliationsView extends BlockView {
@@ -12,6 +13,14 @@ export class AffiliationsView extends BlockView {
12
13
  this.dom.setAttribute('contenteditable', 'false');
13
14
  this.dom.appendChild(this.contentDOM);
14
15
  };
16
+ this.updateContents = () => {
17
+ this.dom.addEventListener('click', () => this.onClickHandler());
18
+ };
19
+ this.onClickHandler = () => {
20
+ const { tr, doc } = this.view.state;
21
+ tr.setSelection(NodeSelection.create(doc, this.getPos()));
22
+ this.view.dispatch(tr);
23
+ };
15
24
  }
16
25
  }
17
26
  export default createNodeView(AffiliationsView);
@@ -68,6 +68,7 @@ export class CitationEditableView extends CitationView {
68
68
  this.props.popper.destroy();
69
69
  };
70
70
  this.showContextMenu = () => {
71
+ this.props.popper.destroy();
71
72
  const componentProps = {
72
73
  actions: [
73
74
  { label: 'Edit', action: this.handleEdit, icon: 'EditIcon' },
@@ -13,14 +13,16 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { SecondaryButton } from '@manuscripts/style-guide';
16
+ import { ContextMenu, SecondaryButton, } from '@manuscripts/style-guide';
17
+ import { CHANGE_OPERATION, CHANGE_STATUS, } from '@manuscripts/track-changes-plugin';
17
18
  import { isContributorNode, schema, } from '@manuscripts/transform';
19
+ import { NodeSelection } from 'prosemirror-state';
18
20
  import { AuthorsModal, } from '../components/authors/AuthorsModal';
19
21
  import { authorLabel } from '../lib/authors';
20
22
  import { getActualAttrs } from '../lib/track-changes-utils';
21
23
  import { deleteNode, findChildByID, findChildByType, findChildrenAttrsByType, updateNodeAttrs, } from '../lib/view';
22
24
  import { affiliationsKey } from '../plugins/affiliations';
23
- import { selectedSuggestionKey } from '../plugins/selected-suggestion-ui';
25
+ import { CLEAR_SUGGESTION_ID, selectedSuggestionKey, SET_SUGGESTION_ID, } from '../plugins/selected-suggestion-ui';
24
26
  import BlockView from './block_view';
25
27
  import { createNodeView } from './creators';
26
28
  import ReactSubView from './ReactSubView';
@@ -35,12 +37,14 @@ export class ContributorsView extends BlockView {
35
37
  this.updateContents();
36
38
  };
37
39
  this.updateContents = () => {
38
- this.updateClasses();
39
- this.updateAttributes();
40
- this.container.innerHTML = '';
41
- this.buildAuthors();
42
- this.createEditButton();
43
- this.createLegend();
40
+ setTimeout(() => {
41
+ this.updateClasses();
42
+ this.updateAttributes();
43
+ this.container.innerHTML = '';
44
+ this.buildAuthors();
45
+ this.createEditButton();
46
+ this.createLegend();
47
+ }, 0);
44
48
  };
45
49
  this.buildAuthors = () => {
46
50
  var _a;
@@ -73,7 +77,9 @@ export class ContributorsView extends BlockView {
73
77
  const pluginState = affiliationsKey.getState(this.view.state);
74
78
  const attrs = node.attrs;
75
79
  const displayAttr = getActualAttrs(node);
80
+ const containerWrapper = document.createElement('div');
76
81
  const container = document.createElement('button');
82
+ containerWrapper.classList.add('contributor-wrapper');
77
83
  container.classList.add('contributor');
78
84
  container.setAttribute('id', attrs.id);
79
85
  container.setAttribute('contenteditable', 'false');
@@ -89,9 +95,12 @@ export class ContributorsView extends BlockView {
89
95
  }
90
96
  const can = this.props.getCapabilities();
91
97
  const disableEditButton = !can.editMetadata;
92
- const { isCorresponding, email } = displayAttr;
98
+ const { isCorresponding, email, id } = displayAttr;
93
99
  if (!disableEditButton) {
94
- container.addEventListener('click', this.handleClick);
100
+ container.addEventListener('click', () => {
101
+ const dataTracked = attrs.dataTracked;
102
+ this.onClickHandler(id, dataTracked ? dataTracked[0] : undefined);
103
+ });
95
104
  }
96
105
  const name = authorLabel(displayAttr);
97
106
  container.innerHTML =
@@ -119,7 +128,8 @@ export class ContributorsView extends BlockView {
119
128
  if (selectedAuthor && selectedAuthor === attrs.id) {
120
129
  container.classList.add('selected-suggestion');
121
130
  }
122
- return container;
131
+ containerWrapper.appendChild(container);
132
+ return containerWrapper;
123
133
  };
124
134
  this.isJointFirstAuthor = (authors, index) => {
125
135
  const author = index === 0 ? authors[index] : authors[index - 1];
@@ -141,7 +151,7 @@ export class ContributorsView extends BlockView {
141
151
  const can = this.props.getCapabilities();
142
152
  const button = ReactSubView(this.props, SecondaryButton, {
143
153
  mini: true,
144
- onClick: this.handleClick,
154
+ onClick: () => this.handleEdit(''),
145
155
  className: 'edit-authors-button',
146
156
  disabled: !can.editMetadata,
147
157
  children: 'Edit Authors',
@@ -161,17 +171,48 @@ export class ContributorsView extends BlockView {
161
171
  }
162
172
  }
163
173
  };
164
- this.handleClick = (e) => {
165
- var _a, _b;
166
- e.stopPropagation();
174
+ this.onClickHandler = (elementId, dataTracked) => {
175
+ var _a;
176
+ this.props.popper.destroy();
177
+ const isSelectedSuggestion = !!((_a = selectedSuggestionKey
178
+ .getState(this.view.state)) === null || _a === void 0 ? void 0 : _a.find(this.getPos(), this.getPos() + this.node.nodeSize).length);
179
+ const { tr, doc } = this.view.state;
180
+ tr.setSelection(NodeSelection.create(doc, this.getPos()));
181
+ if (dataTracked && dataTracked.status !== CHANGE_STATUS.rejected) {
182
+ tr.setMeta(SET_SUGGESTION_ID, dataTracked.id);
183
+ }
184
+ else {
185
+ if (isSelectedSuggestion) {
186
+ tr.setMeta(CLEAR_SUGGESTION_ID, true);
187
+ }
188
+ }
189
+ if (!((dataTracked === null || dataTracked === void 0 ? void 0 : dataTracked.operation) === CHANGE_OPERATION.delete &&
190
+ (dataTracked === null || dataTracked === void 0 ? void 0 : dataTracked.status) !== CHANGE_STATUS.rejected)) {
191
+ this.showContextMenu(elementId);
192
+ }
193
+ this.view.dispatch(tr);
194
+ };
195
+ this.showContextMenu = (elementId) => {
196
+ this.props.popper.destroy();
197
+ const element = document.getElementById(elementId);
198
+ const componentProps = {
199
+ actions: [
200
+ {
201
+ label: 'Edit',
202
+ action: () => this.handleEdit(elementId),
203
+ icon: 'EditIcon',
204
+ },
205
+ ],
206
+ };
207
+ this.contextMenu = ReactSubView(this.props, ContextMenu, componentProps, this.node, this.getPos, this.view, 'context-menu');
208
+ this.props.popper.show(element, this.contextMenu, 'right-start');
209
+ };
210
+ this.handleEdit = (id) => {
211
+ var _a;
212
+ this.props.popper.destroy();
167
213
  const contributors = findChildrenAttrsByType(this.view, schema.nodes.contributor);
168
214
  const affiliations = findChildrenAttrsByType(this.view, schema.nodes.affiliation);
169
- let author = undefined;
170
- const target = e.target;
171
- if (target) {
172
- const id = (_a = target.closest('.contributor')) === null || _a === void 0 ? void 0 : _a.getAttribute('id');
173
- author = contributors.filter((a) => a.id === id)[0];
174
- }
215
+ const author = id ? contributors.filter((a) => a.id === id)[0] : undefined;
175
216
  const componentProps = {
176
217
  author,
177
218
  authors: contributors,
@@ -180,7 +221,7 @@ export class ContributorsView extends BlockView {
180
221
  onDeleteAuthor: this.handleDeleteAuthor,
181
222
  onSaveAffiliation: this.handleSaveAffiliation,
182
223
  };
183
- (_b = this.popper) === null || _b === void 0 ? void 0 : _b.remove();
224
+ (_a = this.popper) === null || _a === void 0 ? void 0 : _a.remove();
184
225
  this.popper = ReactSubView(this.props, AuthorsModal, componentProps, this.node, this.getPos, this.view);
185
226
  this.container.appendChild(this.popper);
186
227
  };
@@ -213,8 +254,6 @@ export class ContributorsView extends BlockView {
213
254
  const node = schema.nodes.affiliation.create(attrs);
214
255
  this.view.dispatch(tr.insert(parent.pos + 1, node));
215
256
  };
216
- this.ignoreMutation = () => true;
217
- this.stopEvent = () => true;
218
257
  }
219
258
  createNote(text = '', title = '') {
220
259
  const el = document.createElement('span');
@@ -13,14 +13,38 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { getChangeClasses } from '../lib/track-changes-utils';
16
+ import { TableFootnotesSelector, } from '@manuscripts/style-guide';
17
+ import { schema, } from '@manuscripts/transform';
18
+ import { findChildrenByType, findParentNodeClosestToPos, } from 'prosemirror-utils';
19
+ import { insertTableFootnote } from '../commands';
20
+ import { getChangeClasses, isDeleted, } from '../lib/track-changes-utils';
21
+ import { buildTableFootnoteLabels } from '../plugins/footnotes/footnotes-utils';
17
22
  import { BaseNodeView } from './base_node_view';
18
23
  import { createNodeView } from './creators';
24
+ import ReactSubView from './ReactSubView';
19
25
  export class InlineFootnoteView extends BaseNodeView {
20
26
  constructor() {
21
27
  super(...arguments);
28
+ this.findParentTableElement = () => findParentNodeClosestToPos(this.view.state.doc.resolve(this.getPos()), (node) => node.type === schema.nodes.table_element);
22
29
  this.handleClick = () => {
23
- this.props.history.push(Object.assign(Object.assign({}, this.props.history.location), { hash: '#' + this.node.attrs.rid }));
30
+ if (isDeleted(this.node)) {
31
+ return;
32
+ }
33
+ const tableElement = this.findParentTableElement();
34
+ if (tableElement) {
35
+ const componentProps = {
36
+ notes: this.getNotes(tableElement),
37
+ onAdd: this.onAdd,
38
+ onInsert: this.onUpdate,
39
+ onCancel: this.destroy,
40
+ inlineFootnote: this.node,
41
+ };
42
+ this.popperContainer = ReactSubView(Object.assign(Object.assign({}, this.props), { dispatch: this.view.dispatch }), TableFootnotesSelector, componentProps, this.node, this.getPos, this.view, 'table-footnote-editor');
43
+ this.props.popper.show(this.dom, this.popperContainer, 'bottom-end');
44
+ }
45
+ else {
46
+ this.props.history.push(Object.assign(Object.assign({}, this.props.history.location), { hash: '#' + this.node.attrs.rid }));
47
+ }
24
48
  };
25
49
  this.updateContents = () => {
26
50
  this.setDomAttrs(this.node, this.dom);
@@ -39,6 +63,49 @@ export class InlineFootnoteView extends BaseNodeView {
39
63
  this.createDOM = () => {
40
64
  return document.createElement('span');
41
65
  };
66
+ this.destroy = () => {
67
+ var _a;
68
+ this.props.popper.destroy();
69
+ (_a = this.popperContainer) === null || _a === void 0 ? void 0 : _a.remove();
70
+ };
71
+ this.getNotes = (tableElement) => {
72
+ var _a;
73
+ const footnotesElement = (_a = findChildrenByType(tableElement.node, schema.nodes.footnotes_element).pop()) === null || _a === void 0 ? void 0 : _a.node;
74
+ let footnotes = [];
75
+ if (footnotesElement) {
76
+ const tablesFootnoteLabels = buildTableFootnoteLabels(tableElement.node);
77
+ footnotes = findChildrenByType(footnotesElement, schema.nodes.footnote)
78
+ .map(({ node }) => ({
79
+ node: node,
80
+ index: tablesFootnoteLabels.get(node.attrs.id),
81
+ }));
82
+ }
83
+ return footnotes;
84
+ };
85
+ this.onAdd = () => {
86
+ const tableElement = this.findParentTableElement();
87
+ if (tableElement) {
88
+ insertTableFootnote(tableElement.node, tableElement.pos, this.view, {
89
+ node: this.node,
90
+ pos: this.getPos(),
91
+ });
92
+ this.destroy();
93
+ }
94
+ };
95
+ this.onUpdate = (notes) => {
96
+ const contents = this.node.attrs.contents.split(',');
97
+ const rids = notes.map((note) => note.node.attrs.id);
98
+ const { tr } = this.view.state;
99
+ this.view.dispatch(rids.length
100
+ ? tr.setNodeMarkup(this.getPos(), undefined, {
101
+ rids,
102
+ contents: notes
103
+ .map(({ index }) => (index ? index : Math.max(...contents) + 1))
104
+ .join(),
105
+ })
106
+ : tr.delete(this.getPos(), this.getPos() + this.node.nodeSize));
107
+ this.destroy();
108
+ };
42
109
  }
43
110
  }
44
111
  export default createNodeView(InlineFootnoteView);
@@ -13,6 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ import { NodeSelection } from 'prosemirror-state';
16
17
  import { AddKeywordInline } from '../components/keywords/AddKeywordInline';
17
18
  import BlockView from './block_view';
18
19
  import { createNodeView } from './creators';
@@ -38,6 +39,15 @@ export class KeywordGroupView extends BlockView {
38
39
  this.element.appendChild(this.addingTools);
39
40
  }
40
41
  };
42
+ this.updateContents = () => {
43
+ this.dom.addEventListener('click', (event) => this.onClickHandler(event));
44
+ };
45
+ this.onClickHandler = (event) => {
46
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
47
+ const { tr, doc } = this.view.state;
48
+ tr.setSelection(NodeSelection.create(doc, this.getPos()));
49
+ this.view.dispatch(tr);
50
+ };
41
51
  }
42
52
  }
43
53
  export default createNodeView(KeywordGroupView);
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { CommentAnnotation } from '@manuscripts/json-schema';
17
17
  import { FileAttachment } from '@manuscripts/style-guide';
18
- import { ManuscriptEditorState, ManuscriptEditorView, ManuscriptMarkType, ManuscriptNode, ManuscriptNodeType, ManuscriptTransaction } from '@manuscripts/transform';
18
+ import { InlineFootnoteNode, ManuscriptEditorState, ManuscriptEditorView, ManuscriptMarkType, ManuscriptNode, ManuscriptNodeType, ManuscriptTransaction } from '@manuscripts/transform';
19
19
  import { ResolvedPos } from 'prosemirror-model';
20
20
  import { Command, NodeSelection, Selection, TextSelection, Transaction } from 'prosemirror-state';
21
21
  import { NodeWithPos } from 'prosemirror-utils';
@@ -57,4 +57,9 @@ export declare const deleteHighlightMarkers: (rid: string) => Command;
57
57
  export declare function addComment(state: ManuscriptEditorState, dispatch?: Dispatch): boolean;
58
58
  export declare function addComment(state: ManuscriptEditorState, dispatch?: Dispatch, viewNode?: ManuscriptNode, resolvePos?: ResolvedPos): boolean;
59
59
  export declare const updateCommentAnnotationState: (state: ManuscriptEditorState, dispatch?: Dispatch) => void;
60
- export declare const insertTableFootnote: (node: ManuscriptNode, position: number, view: EditorView) => void;
60
+ interface NodeWithPosition {
61
+ node: InlineFootnoteNode;
62
+ pos: number;
63
+ }
64
+ export declare const insertTableFootnote: (node: ManuscriptNode, position: number, view: EditorView, inlineFootnote?: NodeWithPosition) => void;
65
+ export {};
@@ -4,6 +4,8 @@ export declare class AffiliationsView<PropsType extends EditableBlockProps> exte
4
4
  ignoreMutation: () => boolean;
5
5
  stopEvent: () => boolean;
6
6
  createElement: () => void;
7
+ updateContents: () => void;
8
+ private onClickHandler;
7
9
  }
8
10
  declare const _default: (props: import("./base_node_view").BaseNodeProps, dispatch?: import("..").Dispatch | undefined) => import("../types").NodeViewCreator<AffiliationsView<any>>;
9
11
  export default _default;
@@ -24,27 +24,28 @@ export interface ContributorsProps extends EditableBlockProps {
24
24
  selectAuthorForEditing: (authorId: string) => void;
25
25
  }
26
26
  export declare class ContributorsView<PropsType extends ContributorsProps> extends BlockView<PropsType> {
27
+ contextMenu: HTMLElement;
27
28
  container: HTMLElement;
28
29
  inner: HTMLElement;
29
30
  popper?: HTMLElement;
30
31
  initialise: () => void;
31
32
  updateContents: () => void;
32
33
  buildAuthors: () => void;
33
- buildAuthor: (node: ContributorNode, isJointFirstAuthor: boolean, selectedAuthor?: string) => HTMLButtonElement;
34
+ buildAuthor: (node: ContributorNode, isJointFirstAuthor: boolean, selectedAuthor?: string) => HTMLDivElement;
34
35
  createNote(text?: string, title?: string): HTMLSpanElement;
35
36
  isJointFirstAuthor: (authors: ContributorNode[], index: number) => boolean;
36
37
  createElement: () => void;
37
38
  createDOM: () => void;
38
39
  createEditButton: () => void;
39
40
  createLegend: () => void;
40
- handleClick: (e: Event) => void;
41
+ private onClickHandler;
42
+ showContextMenu: (elementId: string) => void;
43
+ handleEdit: (id: string) => void;
41
44
  handleSaveAuthor: (author: ContributorAttrs) => void;
42
45
  handleDeleteAuthor: (author: ContributorAttrs) => void;
43
46
  handleSaveAffiliation: (affiliation: AffiliationAttrs) => void;
44
47
  insertAuthorNode: (attrs: ContributorAttrs) => void;
45
48
  insertAffiliationNode: (attrs: AffiliationAttrs) => void;
46
- ignoreMutation: () => boolean;
47
- stopEvent: () => boolean;
48
49
  }
49
50
  declare const _default: (props: import("./base_node_view").BaseNodeProps, dispatch?: import("..").Dispatch | undefined) => import("../types").NodeViewCreator<ContributorsView<any>>;
50
51
  export default _default;
@@ -13,18 +13,27 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ import { FootnoteWithIndex } from '@manuscripts/style-guide';
16
17
  import { ManuscriptNodeView } from '@manuscripts/transform';
17
18
  import { History } from 'history';
19
+ import { ContentNodeWithPos } from 'prosemirror-utils';
18
20
  import { BaseNodeProps, BaseNodeView } from './base_node_view';
21
+ import { EditableBlockProps } from './editable_block';
19
22
  export interface InlineFootnoteProps extends BaseNodeProps {
20
23
  history: History;
21
24
  }
22
- export declare class InlineFootnoteView<PropsType extends InlineFootnoteProps> extends BaseNodeView<PropsType> implements ManuscriptNodeView {
25
+ export declare class InlineFootnoteView<PropsType extends InlineFootnoteProps & EditableBlockProps> extends BaseNodeView<PropsType> implements ManuscriptNodeView {
26
+ protected popperContainer: HTMLDivElement;
27
+ findParentTableElement: () => ContentNodeWithPos | undefined;
23
28
  handleClick: () => void;
24
29
  updateContents: () => void;
25
30
  initialise: () => void;
26
31
  ignoreMutation: () => boolean;
27
32
  createDOM: () => HTMLSpanElement;
33
+ destroy: () => void;
34
+ getNotes: (tableElement: ContentNodeWithPos) => FootnoteWithIndex[];
35
+ onAdd: () => void;
36
+ onUpdate: (notes: FootnoteWithIndex[]) => void;
28
37
  }
29
- declare const _default: (props: BaseNodeProps, dispatch?: import("..").Dispatch | undefined) => import("../types").NodeViewCreator<InlineFootnoteView<any>>;
38
+ declare const _default: (props: BaseNodeProps, dispatch?: import("../commands").Dispatch | undefined) => import("../types").NodeViewCreator<InlineFootnoteView<any>>;
30
39
  export default _default;
@@ -21,6 +21,8 @@ export declare class KeywordGroupView extends BlockView<EditableBlockProps> {
21
21
  ignoreMutation: () => boolean;
22
22
  stopEvent: () => boolean;
23
23
  createElement: () => void;
24
+ updateContents: () => void;
25
+ private onClickHandler;
24
26
  }
25
27
  declare const _default: (props: import("./base_node_view").BaseNodeProps, dispatch?: import("..").Dispatch | undefined) => import("../types").NodeViewCreator<KeywordGroupView>;
26
28
  export default _default;
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": "1.7.34",
4
+ "version": "1.7.36",
5
5
  "repository": "github:Atypon-OpenSource/manuscripts-body-editor",
6
6
  "license": "Apache-2.0",
7
7
  "main": "dist/cjs",
@@ -31,9 +31,9 @@
31
31
  "@manuscripts/assets": "^0.6.4",
32
32
  "@manuscripts/json-schema": "^2.2.7",
33
33
  "@manuscripts/library": "^1.3.6",
34
- "@manuscripts/style-guide": "1.8.4",
34
+ "@manuscripts/style-guide": "1.8.5",
35
35
  "@manuscripts/track-changes-plugin": "1.7.9",
36
- "@manuscripts/transform": "2.1.12",
36
+ "@manuscripts/transform": "2.1.13",
37
37
  "astrocite-eutils": "^0.16.4",
38
38
  "axios": "^1.6.7",
39
39
  "mathjax-full": "^3.2.2",
@@ -724,11 +724,13 @@ Next levels are shifted 'up' by 1
724
724
  text-decoration: none;
725
725
  }
726
726
 
727
+ .ProseMirror .deleted.pending::before,
727
728
  .ProseMirror .deleted.pending {
728
729
  background: var(--pending-bg-color);
729
730
  color: var(--deleted-color);
730
731
  text-decoration: line-through;
731
732
  }
733
+ .ProseMirror .deleted.accepted::before,
732
734
  .ProseMirror .deleted.accepted {
733
735
  background: var(--accepted-bg-color);
734
736
  color: var(--deleted-color);
@@ -780,12 +782,6 @@ Next levels are shifted 'up' by 1
780
782
  color: var(--deleted-color);
781
783
  }
782
784
 
783
- .contributor[data-track-op='delete']:not([data-track-status='rejected']),
784
- .contributor[data-track-op='delete']:not([data-track-status='rejected']) .name,
785
- .affiliation[data-track-op='delete']:not([data-track-status='rejected']) {
786
- pointer-events: none;
787
- }
788
-
789
785
  .contributor[data-track-op='delete']:not([data-track-status='rejected']) {
790
786
  color: var(--deleted-color);
791
787
  }
@@ -803,29 +799,52 @@ Next levels are shifted 'up' by 1
803
799
  .contributors {
804
800
  white-space: initial;
805
801
  margin-top: 24px;
806
- align-items: center;
807
802
  display: flex;
808
803
  justify-content: space-between;
804
+ align-items: flex-start;
805
+ gap: 0 .5em;
809
806
  }
810
807
  .contributors-list {
808
+ display: flex;
809
+ max-width: 100%;
811
810
  flex: 0 1 auto;
811
+ flex-wrap: wrap;
812
+ gap: 0 .25em;
812
813
  }
813
-
814
814
  .contributors .tools-panel {
815
815
  position: initial;
816
+ display: flex;
816
817
  flex: 1;
818
+ align-items: center;
819
+ }
820
+ .contributor-wrapper {
821
+ display: flex;
822
+ }
823
+ .contributor-wrapper:after {
824
+ content: ', ';
825
+ color: #353535;
826
+ }
827
+ .contributor-wrapper:last-child::after {
828
+ content: '';
829
+ margin-right: 0;
830
+ }
831
+ .contributor-wrapper .contributor-comment {
832
+ left: 0;
833
+ top: -8px;
834
+ width: auto;
835
+ height: auto;
817
836
  }
818
-
819
837
  .contributor {
838
+ position: relative;
820
839
  display: inline-flex;
821
840
  background: transparent;
822
841
  border: 0;
823
- font-family: 'PT Sans', sans-serif;
824
842
  font-size: 1rem;
825
843
  text-decoration: none;
826
844
  color: inherit;
827
845
  outline: none;
828
- padding: 0;
846
+ padding: 4px;
847
+ /* margin: 0 6px; */
829
848
  }
830
849
 
831
850
  .contributor:not(:disabled):hover {
@@ -841,16 +860,6 @@ Next levels are shifted 'up' by 1
841
860
  cursor: pointer !important;
842
861
  }
843
862
 
844
- .contributor:after {
845
- content: ', ';
846
- color: #353535;
847
- margin-right: 5px;
848
- }
849
- .contributor:last-child::after {
850
- content: '';
851
- margin-right: 0;
852
- }
853
-
854
863
  .contributors-legend {
855
864
  margin: 16px 0 0 0;
856
865
  }
@@ -862,11 +871,7 @@ Next levels are shifted 'up' by 1
862
871
  vertical-align: top;
863
872
  }
864
873
 
865
- .contributors .tools-panel {
866
- align-items: center;
867
- display: flex;
868
- margin-left: 8px;
869
- }
874
+
870
875
 
871
876
  .contributor-note {
872
877
  display: inline-block;
@@ -895,4 +900,4 @@ Next levels are shifted 'up' by 1
895
900
 
896
901
  * {
897
902
  scroll-behavior: smooth;
898
- }
903
+ }
package/styles/Editor.css CHANGED
@@ -942,6 +942,7 @@
942
942
  cursor: pointer;
943
943
  vertical-align: super;
944
944
  font-size: 75%;
945
+ color: #0d79d0;
945
946
  }
946
947
 
947
948
  .ProseMirror .table-footer {