@manuscripts/body-editor 2.7.28 → 2.7.30-LEAN-4096.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/cjs/components/affiliations/AffiliationItem.js +2 -2
  2. package/dist/cjs/components/affiliations/AffiliationsModal.js +3 -3
  3. package/dist/cjs/components/authors/AuthorsModal.js +3 -3
  4. package/dist/cjs/components/views/FigureDropdown.js +8 -47
  5. package/dist/cjs/configs/editor-plugins.js +2 -0
  6. package/dist/cjs/index.js +1 -0
  7. package/dist/cjs/lib/context-menu.js +71 -4
  8. package/dist/cjs/plugins/inspector-tabs.js +84 -0
  9. package/dist/cjs/versions.js +1 -1
  10. package/dist/cjs/views/ReactSubView.js +4 -4
  11. package/dist/cjs/views/affiliations.js +1 -1
  12. package/dist/cjs/views/award.js +3 -3
  13. package/dist/cjs/views/bibliography_element.js +2 -2
  14. package/dist/cjs/views/citation_editable.js +3 -3
  15. package/dist/cjs/views/contributors.js +2 -2
  16. package/dist/cjs/views/cross_reference_editable.js +1 -1
  17. package/dist/cjs/views/embed.js +1 -1
  18. package/dist/cjs/views/figure_editable.js +91 -7
  19. package/dist/cjs/views/figure_element.js +0 -79
  20. package/dist/cjs/views/inline_footnote.js +2 -2
  21. package/dist/cjs/views/keyword.js +1 -1
  22. package/dist/cjs/views/keyword_group.js +1 -1
  23. package/dist/cjs/views/link_editable.js +1 -1
  24. package/dist/cjs/views/table_cell.js +1 -1
  25. package/dist/es/components/affiliations/AffiliationItem.js +2 -2
  26. package/dist/es/components/affiliations/AffiliationsModal.js +3 -3
  27. package/dist/es/components/authors/AuthorsModal.js +3 -3
  28. package/dist/es/components/views/FigureDropdown.js +8 -46
  29. package/dist/es/configs/editor-plugins.js +2 -0
  30. package/dist/es/index.js +1 -0
  31. package/dist/es/lib/context-menu.js +72 -5
  32. package/dist/es/plugins/inspector-tabs.js +81 -0
  33. package/dist/es/versions.js +1 -1
  34. package/dist/es/views/ReactSubView.js +4 -4
  35. package/dist/es/views/affiliations.js +1 -1
  36. package/dist/es/views/award.js +3 -3
  37. package/dist/es/views/bibliography_element.js +2 -2
  38. package/dist/es/views/citation_editable.js +3 -3
  39. package/dist/es/views/contributors.js +2 -2
  40. package/dist/es/views/cross_reference_editable.js +1 -1
  41. package/dist/es/views/embed.js +1 -1
  42. package/dist/es/views/figure_editable.js +91 -7
  43. package/dist/es/views/figure_element.js +0 -79
  44. package/dist/es/views/inline_footnote.js +2 -2
  45. package/dist/es/views/keyword.js +1 -1
  46. package/dist/es/views/keyword_group.js +1 -1
  47. package/dist/es/views/link_editable.js +1 -1
  48. package/dist/es/views/table_cell.js +1 -1
  49. package/dist/types/components/views/FigureDropdown.d.ts +0 -1
  50. package/dist/types/index.d.ts +1 -0
  51. package/dist/types/lib/context-menu.d.ts +3 -0
  52. package/dist/types/plugins/inspector-tabs.d.ts +35 -0
  53. package/dist/types/versions.d.ts +1 -1
  54. package/dist/types/views/ReactSubView.d.ts +1 -1
  55. package/dist/types/views/figure_editable.d.ts +9 -0
  56. package/dist/types/views/figure_element.d.ts +0 -3
  57. package/package.json +3 -3
  58. package/styles/AdvancedEditor.css +15 -5
  59. package/styles/Editor.css +10 -1
  60. package/styles/popper.css +33 -11
@@ -27,16 +27,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  return (mod && mod.__esModule) ? mod : { "default": mod };
28
28
  };
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.FigureEditableView = void 0;
30
+ exports.FigureEditableView = exports.figurePositions = void 0;
31
31
  const style_guide_1 = require("@manuscripts/style-guide");
32
+ const transform_1 = require("@manuscripts/transform");
32
33
  const react_1 = require("react");
33
34
  const server_1 = require("react-dom/server");
34
35
  const FigureDropdown_1 = require("../components/views/FigureDropdown");
35
36
  const files_1 = require("../lib/files");
37
+ const view_1 = require("../lib/view");
36
38
  const creators_1 = require("./creators");
37
39
  const figure_1 = require("./figure");
38
40
  const figure_uploader_1 = require("./figure_uploader");
39
41
  const ReactSubView_1 = __importDefault(require("./ReactSubView"));
42
+ var figurePositions;
43
+ (function (figurePositions) {
44
+ figurePositions["left"] = "half-left";
45
+ figurePositions["right"] = "half-right";
46
+ figurePositions["default"] = "";
47
+ })(figurePositions = exports.figurePositions || (exports.figurePositions = {}));
40
48
  class FigureEditableView extends figure_1.FigureView {
41
49
  constructor() {
42
50
  super(...arguments);
@@ -84,11 +92,75 @@ class FigureEditableView extends figure_1.FigureView {
84
92
  element.classList.add('figure', 'placeholder');
85
93
  const instructions = document.createElement('div');
86
94
  instructions.classList.add('instructions');
87
- instructions.textContent = 'Click to select an image file';
88
- instructions.style.pointerEvents = 'none';
95
+ instructions.innerHTML = `
96
+ <p>Drag or click here to upload image <br>
97
+ or drag items here from the file inspector tabs <br>
98
+ <a data-action='open-other-files'>'Other files'</a> |
99
+ <a data-action='open-supplement-files'>'Supplements'</a></p>
100
+ `;
89
101
  element.appendChild(instructions);
90
102
  return element;
91
103
  };
104
+ this.createPositionMenuWrapper = () => {
105
+ this.positionMenuWrapper = document.createElement('div');
106
+ this.positionMenuWrapper.classList.add('position-menu');
107
+ const positionMenuButton = document.createElement('div');
108
+ positionMenuButton.classList.add('position-menu-button');
109
+ let icon;
110
+ switch (this.figurePosition) {
111
+ case figurePositions.left:
112
+ icon = style_guide_1.ImageLeftIcon;
113
+ break;
114
+ case figurePositions.right:
115
+ icon = style_guide_1.ImageRightIcon;
116
+ break;
117
+ default:
118
+ icon = style_guide_1.ImageDefaultIcon;
119
+ break;
120
+ }
121
+ if (icon) {
122
+ positionMenuButton.innerHTML = (0, server_1.renderToStaticMarkup)((0, react_1.createElement)(icon));
123
+ }
124
+ positionMenuButton.addEventListener('click', this.showPositionMenu);
125
+ this.positionMenuWrapper.appendChild(positionMenuButton);
126
+ return this.positionMenuWrapper;
127
+ };
128
+ this.showPositionMenu = () => {
129
+ this.props.popper.destroy();
130
+ const figure = this.node;
131
+ const componentProps = {
132
+ actions: [
133
+ {
134
+ label: 'Left',
135
+ action: () => {
136
+ this.props.popper.destroy();
137
+ (0, view_1.updateNodeAttrs)(this.view, transform_1.schema.nodes.figure, Object.assign(Object.assign({}, figure.attrs), { type: figurePositions.left }));
138
+ },
139
+ icon: 'ImageLeft',
140
+ selected: this.figurePosition === figurePositions.left,
141
+ },
142
+ {
143
+ label: 'Default',
144
+ action: () => {
145
+ this.props.popper.destroy();
146
+ (0, view_1.updateNodeAttrs)(this.view, transform_1.schema.nodes.figure, Object.assign(Object.assign({}, figure.attrs), { type: figurePositions.default }));
147
+ },
148
+ icon: 'ImageDefault',
149
+ selected: !this.figurePosition,
150
+ },
151
+ {
152
+ label: 'Right',
153
+ action: () => {
154
+ this.props.popper.destroy();
155
+ (0, view_1.updateNodeAttrs)(this.view, transform_1.schema.nodes.figure, Object.assign(Object.assign({}, figure.attrs), { type: figurePositions.right }));
156
+ },
157
+ icon: 'ImageRight',
158
+ selected: this.figurePosition === figurePositions.right,
159
+ },
160
+ ],
161
+ };
162
+ this.props.popper.show(this.positionMenuWrapper, (0, ReactSubView_1.default)(this.props, style_guide_1.ContextMenu, componentProps, this.node, this.getPos, this.view, ['context-menu', 'position-menu']), 'left', false);
163
+ };
92
164
  }
93
165
  updateContents() {
94
166
  var _a;
@@ -97,6 +169,7 @@ class FigureEditableView extends figure_1.FigureView {
97
169
  const src = attrs.src;
98
170
  const files = this.props.getFiles();
99
171
  const file = src && files.filter((f) => f.id === src)[0];
172
+ this.figurePosition = attrs.type;
100
173
  this.container.innerHTML = '';
101
174
  const can = this.props.getCapabilities();
102
175
  const link = file && this.props.fileManagement.previewLink(file);
@@ -115,20 +188,30 @@ class FigureEditableView extends figure_1.FigureView {
115
188
  this.props.fileManagement.download(file);
116
189
  };
117
190
  }
118
- handleReplace = (file) => {
119
- this.setSrc(file.id);
120
- };
121
191
  handleDetach = () => {
122
192
  this.setSrc('');
123
193
  };
124
194
  }
195
+ if (can.replaceFile) {
196
+ handleReplace = (file) => {
197
+ this.setSrc(file.id);
198
+ };
199
+ }
125
200
  if (can.uploadFile) {
126
201
  const upload = (file) => __awaiter(this, void 0, void 0, function* () {
127
202
  const result = yield this.props.fileManagement.upload(file);
128
203
  this.setSrc(result.id);
129
204
  });
130
205
  handleUpload = (0, figure_uploader_1.figureUploader)(upload);
131
- img.addEventListener('click', handleUpload);
206
+ const handlePlaceholderClick = (event) => {
207
+ const target = event.target;
208
+ if (target.dataset && target.dataset.action) {
209
+ return;
210
+ }
211
+ const triggerUpload = (0, figure_uploader_1.figureUploader)(upload);
212
+ triggerUpload();
213
+ };
214
+ img.addEventListener('click', handlePlaceholderClick);
132
215
  img.addEventListener('mouseenter', () => {
133
216
  img.classList.toggle('over', true);
134
217
  });
@@ -176,6 +259,7 @@ class FigureEditableView extends figure_1.FigureView {
176
259
  this.reactTools = (0, ReactSubView_1.default)(this.props, FigureDropdown_1.FigureOptions, componentProps, this.node, this.getPos, this.view);
177
260
  this.dom.insertBefore(this.reactTools, this.dom.firstChild);
178
261
  }
262
+ this.container.appendChild(this.createPositionMenuWrapper());
179
263
  }
180
264
  }
181
265
  exports.FigureEditableView = FigureEditableView;
@@ -14,28 +14,13 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
18
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
19
- return new (P || (P = Promise))(function (resolve, reject) {
20
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
22
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
23
- step((generator = generator.apply(thisArg, _arguments || [])).next());
24
- });
25
- };
26
17
  var __importDefault = (this && this.__importDefault) || function (mod) {
27
18
  return (mod && mod.__esModule) ? mod : { "default": mod };
28
19
  };
29
20
  Object.defineProperty(exports, "__esModule", { value: true });
30
21
  exports.FigureElementView = void 0;
31
- const transform_1 = require("@manuscripts/transform");
32
- const FigureDropdown_1 = require("../components/views/FigureDropdown");
33
- const files_1 = require("../lib/files");
34
- const utils_1 = require("../lib/utils");
35
22
  const block_view_1 = __importDefault(require("./block_view"));
36
23
  const creators_1 = require("./creators");
37
- const figure_uploader_1 = require("./figure_uploader");
38
- const ReactSubView_1 = __importDefault(require("./ReactSubView"));
39
24
  class FigureElementView extends block_view_1.default {
40
25
  constructor() {
41
26
  super(...arguments);
@@ -50,70 +35,6 @@ class FigureElementView extends block_view_1.default {
50
35
  this.container.appendChild(this.contentDOM);
51
36
  };
52
37
  }
53
- updateContents() {
54
- var _a;
55
- super.updateContents();
56
- if (!this.contentDOM) {
57
- throw new Error('No contentDOM');
58
- }
59
- const can = this.props.getCapabilities();
60
- let handleUpload = () => {
61
- };
62
- const hasUploadedImage = !!(0, utils_1.getMatchingChild)(this.node, (node) => node.type === transform_1.schema.nodes.figure && node.attrs.src);
63
- const handleAdd = (file) => __awaiter(this, void 0, void 0, function* () {
64
- const { state: { tr, schema, selection }, dispatch, } = this.view;
65
- const src = file.id;
66
- const figure = (0, utils_1.getMatchingChild)(this.node, (node) => node.type === schema.nodes.figure);
67
- if (figure === null || figure === void 0 ? void 0 : figure.attrs.src) {
68
- const figure = schema.nodes.figure.createAndFill({
69
- src,
70
- }, []);
71
- let position = 0;
72
- this.node.forEach((node, pos) => {
73
- if (node.type === schema.nodes.figure) {
74
- position = pos + node.nodeSize;
75
- }
76
- });
77
- dispatch(tr.insert(this.getPos() + position + 1, figure));
78
- }
79
- else if (figure) {
80
- tr.setNodeMarkup(this.getPos() + 1, undefined, Object.assign(Object.assign({}, figure.attrs), { src })).setSelection(selection.map(tr.doc, tr.mapping));
81
- dispatch(tr);
82
- }
83
- this.deleteSupplementNode(file);
84
- });
85
- if (can.uploadFile) {
86
- const upload = (file) => __awaiter(this, void 0, void 0, function* () {
87
- const result = yield this.props.fileManagement.upload(file);
88
- yield handleAdd(result);
89
- });
90
- handleUpload = (0, figure_uploader_1.figureUploader)(upload);
91
- }
92
- if (this.props.dispatch && this.props.theme) {
93
- const files = this.props.getFiles();
94
- const doc = this.view.state.doc;
95
- const componentProps = {
96
- can: can,
97
- files: (0, files_1.groupFiles)(doc, files),
98
- onUpload: handleUpload,
99
- onAdd: handleAdd,
100
- hasUploadedImage: hasUploadedImage,
101
- };
102
- (_a = this.reactTools) === null || _a === void 0 ? void 0 : _a.remove();
103
- this.reactTools = (0, ReactSubView_1.default)(this.props, FigureDropdown_1.FigureElementOptions, componentProps, this.node, this.getPos, this.view);
104
- this.dom.insertBefore(this.reactTools, this.dom.firstChild);
105
- }
106
- }
107
- deleteSupplementNode(file) {
108
- const tr = this.view.state.tr;
109
- this.view.state.doc.descendants((node, pos) => {
110
- if (node.type === transform_1.schema.nodes.supplement &&
111
- node.attrs.href === file.id) {
112
- tr.delete(pos, pos + node.nodeSize);
113
- }
114
- });
115
- this.view.dispatch(tr);
116
- }
117
38
  }
118
39
  exports.FigureElementView = FigureElementView;
119
40
  exports.default = (0, creators_1.createNodeView)(FigureElementView);
@@ -58,7 +58,7 @@ class InlineFootnoteView extends base_node_view_1.BaseNodeView {
58
58
  },
59
59
  ],
60
60
  };
61
- this.props.popper.show(this.dom, (0, ReactSubView_1.default)(this.props, style_guide_1.ContextMenu, componentProps, this.node, this.getPos, this.view, 'context-menu'), 'right-start', false);
61
+ this.props.popper.show(this.dom, (0, ReactSubView_1.default)(this.props, style_guide_1.ContextMenu, componentProps, this.node, this.getPos, this.view, ['context-menu']), 'right-start', false);
62
62
  };
63
63
  this.handleClick = () => {
64
64
  if ((0, track_changes_utils_1.isDeleted)(this.node)) {
@@ -106,7 +106,7 @@ class InlineFootnoteView extends base_node_view_1.BaseNodeView {
106
106
  onAdd: this.handleAdd,
107
107
  onInsert: this.handleInsert,
108
108
  };
109
- this.popperContainer = (0, ReactSubView_1.default)(this.props, FootnotesSelector_1.FootnotesSelector, props, this.node, this.getPos, this.view, 'footnote-editor');
109
+ this.popperContainer = (0, ReactSubView_1.default)(this.props, FootnotesSelector_1.FootnotesSelector, props, this.node, this.getPos, this.view, ['footnote-editor']);
110
110
  this.props.popper.show(this.dom, this.popperContainer, 'auto', false);
111
111
  };
112
112
  this.initialise = () => {
@@ -58,7 +58,7 @@ class KeywordView extends base_node_view_1.BaseNodeView {
58
58
  keyword: keyword.textContent,
59
59
  handleDelete: handleDelete,
60
60
  };
61
- this.dialog = (0, ReactSubView_1.default)(this.props, DeleteKeywordDialog_1.DeleteKeywordDialog, componentProps, this.node, this.getPos, this.view, 'keywords-delete');
61
+ this.dialog = (0, ReactSubView_1.default)(this.props, DeleteKeywordDialog_1.DeleteKeywordDialog, componentProps, this.node, this.getPos, this.view, ['keywords-delete']);
62
62
  if (this.dialog) {
63
63
  this.dom.appendChild(this.dialog);
64
64
  }
@@ -37,7 +37,7 @@ class KeywordGroupView extends block_view_1.default {
37
37
  this.contentDOM.setAttribute('contenteditable', 'false');
38
38
  this.element.appendChild(this.contentDOM);
39
39
  if (this.props.getCapabilities().editArticle) {
40
- this.addingTools = (0, ReactSubView_1.default)(this.props, AddKeywordInline_1.AddKeywordInline, { getUpdatedNode: () => this.node }, this.node, this.getPos, this.view, 'keywords-editor');
40
+ this.addingTools = (0, ReactSubView_1.default)(this.props, AddKeywordInline_1.AddKeywordInline, { getUpdatedNode: () => this.node }, this.node, this.getPos, this.view, ['keywords-editor']);
41
41
  }
42
42
  if (this.addingTools) {
43
43
  this.element.appendChild(this.addingTools);
@@ -71,7 +71,7 @@ class LinkEditableView extends link_1.LinkView {
71
71
  onRemove: this.handleRemove,
72
72
  onSave: this.handleSave,
73
73
  };
74
- this.popperContainer = (0, ReactSubView_1.default)(this.props, LinkForm_1.LinkForm, componentProps, this.node, this.getPos, this.view, 'link-editor');
74
+ this.popperContainer = (0, ReactSubView_1.default)(this.props, LinkForm_1.LinkForm, componentProps, this.node, this.getPos, this.view, ['link-editor']);
75
75
  this.props.popper.show(this.dom, this.popperContainer, 'bottom');
76
76
  };
77
77
  this.handleClick = (e) => {
@@ -79,7 +79,7 @@ class TableCellView extends block_view_1.default {
79
79
  contextMenuButton.classList.toggle('open-context-menu');
80
80
  },
81
81
  onCancelColumnDialog: () => this.addOutClickListener(contextMenuButton),
82
- }, this.view.state.selection.$from.node(), this.getPos, this.view, 'table-cell-context-menu');
82
+ }, this.view.state.selection.$from.node(), this.getPos, this.view, ['table-cell-context-menu']);
83
83
  contextMenuButton.classList.toggle('open-context-menu');
84
84
  this.props.popper.show(contextMenuButton, contextMenu, 'right', false);
85
85
  this.addOutClickListener(contextMenuButton);
@@ -63,10 +63,10 @@ export const AffiliationItem = ({ affiliation, isSelected, onClick, onDelete, sh
63
63
  block: 'nearest',
64
64
  });
65
65
  }
66
- return (React.createElement(AffiliationContainer, { onClick: onClick, className: isSelected ? 'active' : '', ref: ref },
66
+ return (React.createElement(AffiliationContainer, { "data-cy": "affiliation-item", onClick: onClick, className: isSelected ? 'active' : '', ref: ref },
67
67
  React.createElement(AffiliationIcon, { opacity: showSuccessIcon ? 0.05 : 1, style: { minWidth: 24, minHeight: 24 } }),
68
68
  React.createElement(AffiliationBox, null,
69
- React.createElement(AffiliationName, null, affiliation.institution),
69
+ React.createElement(AffiliationName, { "data-cy": "affiliation-name" }, affiliation.institution),
70
70
  React.createElement(AffiliationDetails, null,
71
71
  affiliation.city && (React.createElement(React.Fragment, null,
72
72
  affiliation.city,
@@ -355,7 +355,7 @@ export const AffiliationsModal = ({ authors: $authors, affiliations: $affiliatio
355
355
  }
356
356
  };
357
357
  return (React.createElement(StyledModal, { isOpen: isOpen, onRequestClose: () => handleClose(), shouldCloseOnOverlayClick: true },
358
- React.createElement(ModalContainer, null,
358
+ React.createElement(ModalContainer, { "data-cy": "affiliations-modal" },
359
359
  React.createElement(ModalHeader, null,
360
360
  React.createElement(CloseButton, { onClick: () => handleClose(), "data-cy": "modal-close-button" })),
361
361
  React.createElement(StyledModalBody, null,
@@ -378,9 +378,9 @@ export const AffiliationsModal = ({ authors: $authors, affiliations: $affiliatio
378
378
  React.createElement(AffiliateButton, { onClick: () => setShowAuthorDrawer(true), "data-cy": "affiliate-authors-button" },
379
379
  React.createElement(AddUserIcon, { width: 16, height: 16 }),
380
380
  "Affiliate Authors")),
381
- React.createElement(SelectedItemsBox, { items: selectedAuthors, onRemove: (id) => {
381
+ React.createElement(SelectedItemsBox, { "data-cy": "affiliation-authors", items: selectedAuthors, onRemove: (id) => {
382
382
  setSelectedIds((prev) => prev.filter((authorId) => authorId !== id));
383
383
  }, placeholder: "No authors assigned" })),
384
- showAuthorDrawer && (React.createElement(Drawer, { items: authorItems, selectedIds: selectedIds, title: "Authors", onSelect: handleAuthorSelect, onBack: () => setShowAuthorDrawer(false), width: "100%" })))) : (React.createElement(FormPlaceholder, { type: "affiliation", title: 'Affiliation Details', message: "Select an affiliation from the list to display it's details here.", placeholderIcon: React.createElement(AffiliationPlaceholderIcon, null) })))),
384
+ showAuthorDrawer && (React.createElement(Drawer, { items: authorItems, selectedIds: selectedIds, title: "Authors", onSelect: handleAuthorSelect, onBack: () => setShowAuthorDrawer(false), width: "100%" })))) : (React.createElement(FormPlaceholder, { type: "affiliation", title: "Affiliation Details", message: "Select an affiliation from the list to display it's details here.", placeholderIcon: React.createElement(AffiliationPlaceholderIcon, null) })))),
385
385
  React.createElement(FormFooter, { onCancel: handleClose }))));
386
386
  };
@@ -432,7 +432,7 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
432
432
  setSelectedAffiliations(affiliationItems.filter((item) => newAffiliations.includes(item.id)));
433
433
  };
434
434
  return (React.createElement(StyledModal, { isOpen: isOpen, onRequestClose: () => handleClose(), shouldCloseOnOverlayClick: true },
435
- React.createElement(ModalContainer, null,
435
+ React.createElement(ModalContainer, { "data-cy": "authors-modal" },
436
436
  React.createElement(ModalHeader, null,
437
437
  React.createElement(CloseButton, { onClick: () => handleClose(), "data-cy": "modal-close-button" })),
438
438
  React.createElement(StyledModalBody, null,
@@ -459,7 +459,7 @@ export const AuthorsModal = ({ authors: $authors, affiliations: $affiliations, a
459
459
  React.createElement(AffiliateButton, { onClick: () => setShowAffiliationDrawer(true), "data-cy": "affiliate-authors-button" },
460
460
  React.createElement(AddInstitutionIcon, { width: 16, height: 16 }),
461
461
  "Assign Institutions")),
462
- React.createElement(SelectedItemsBox, { items: selectedAffiliations, onRemove: handleRemoveAffiliation, placeholder: "No institutions assigned" })),
463
- showAffiliationDrawer && (React.createElement(Drawer, { items: affiliationItems, selectedIds: selectedAffiliationIds, title: "Authors", onSelect: handleAffiliationSelect, onBack: () => setShowAffiliationDrawer(false), width: "100%" })))) : (React.createElement(FormPlaceholder, { type: "author", title: 'Author Details', message: 'Select an author from the list to display their details here.', placeholderIcon: React.createElement(AuthorPlaceholderIcon, null) })))),
462
+ React.createElement(SelectedItemsBox, { "data-cy": "author-affiliations", items: selectedAffiliations, onRemove: handleRemoveAffiliation, placeholder: "No institutions assigned" })),
463
+ showAffiliationDrawer && (React.createElement(Drawer, { items: affiliationItems, selectedIds: selectedAffiliationIds, title: "Authors", onSelect: handleAffiliationSelect, onBack: () => setShowAffiliationDrawer(false), width: "100%" })))) : (React.createElement(FormPlaceholder, { type: "author", title: "Author Details", message: "Select an author from the list to display their details here.", placeholderIcon: React.createElement(AuthorPlaceholderIcon, null) })))),
464
464
  React.createElement(FormFooter, { onCancel: handleClose }))));
465
465
  };
@@ -1,26 +1,6 @@
1
- import { AddIcon, AttachIcon, DotsIcon, DropdownList, getFileIcon, IconButton, IconTextButton, isImageFile, RoundIconButton, TriangleCollapsedIcon, UploadIcon, useDropdown, } from '@manuscripts/style-guide';
1
+ import { DotsIcon, DropdownList, getFileIcon, IconButton, IconTextButton, isImageFile, TriangleCollapsedIcon, UploadIcon, useDropdown, } from '@manuscripts/style-guide';
2
2
  import React from 'react';
3
3
  import styled from 'styled-components';
4
- export const FigureElementOptions = ({ can, files, onAdd, onUpload, hasUploadedImage, }) => {
5
- const { isOpen, toggleOpen, wrapperRef } = useDropdown();
6
- const supplements = files.supplements
7
- .map((s) => s.file)
8
- .filter((f) => isImageFile(f.name));
9
- const others = files.others.filter((f) => isImageFile(f.name));
10
- return (React.createElement(FilesDropdownWrapper, { onClick: toggleOpen, ref: wrapperRef },
11
- React.createElement(FilesButton, { disabled: hasUploadedImage },
12
- React.createElement(AttachIcon, null)),
13
- isOpen && (React.createElement(DropdownList, { direction: 'left', width: 208, height: 187, onClick: toggleOpen, top: 7 },
14
- React.createElement(NestedDropdown, { disabled: !can.replaceFile || supplements.length < 1, parentToggleOpen: toggleOpen, buttonText: 'Supplements', list: React.createElement(React.Fragment, null, supplements.map((file) => (React.createElement(ListItemButton, { key: file.id, onClick: () => onAdd(file) },
15
- getFileIcon(file.name),
16
- React.createElement(ListItemText, null, file.name))))) }),
17
- React.createElement(NestedDropdown, { disabled: !can.replaceFile || others.length < 1, parentToggleOpen: toggleOpen, buttonText: 'Other files', list: React.createElement(React.Fragment, null, others.map((file) => (React.createElement(ListItemButton, { key: file.id, onClick: () => onAdd(file) },
18
- getFileIcon(file.name),
19
- React.createElement(ListItemText, null, file.name))))) }),
20
- React.createElement(UploadButton, { onClick: onUpload, disabled: !can.uploadFile },
21
- React.createElement(AddIcon, null),
22
- " New file...")))));
23
- };
24
4
  export const FigureOptions = ({ can, files, onDownload, onUpload, onDetach, onReplace, }) => {
25
5
  const { isOpen, toggleOpen, wrapperRef } = useDropdown();
26
6
  const otherFiles = files.others.filter((f) => isImageFile(f.name));
@@ -28,26 +8,26 @@ export const FigureOptions = ({ can, files, onDownload, onUpload, onDetach, onRe
28
8
  const showUpload = onUpload && can.uploadFile;
29
9
  const showDetach = onDetach && can.detachFile;
30
10
  const showReplace = onReplace && can.replaceFile;
11
+ const replaceBtnText = onDownload ? 'Replace' : 'Choose file';
31
12
  return (React.createElement(DropdownWrapper, { ref: wrapperRef },
32
13
  React.createElement(OptionsButton, { className: 'options-button', onClick: toggleOpen },
33
14
  React.createElement(DotsIcon, null)),
34
15
  isOpen && (React.createElement(OptionsDropdownList, { direction: 'right', width: 128, top: 5 },
35
- React.createElement(ListItemButton, { onClick: onDownload, disabled: !showDownload }, "Download"),
36
- React.createElement(NestedDropdown, { disabled: !showReplace, parentToggleOpen: toggleOpen, buttonText: 'Replace', moveLeft: true, list: React.createElement(React.Fragment, null,
16
+ React.createElement(NestedDropdown, { disabled: !showReplace, parentToggleOpen: toggleOpen, buttonText: replaceBtnText, moveLeft: true, list: React.createElement(React.Fragment, null,
37
17
  otherFiles.map((file, index) => (React.createElement(ListItemButton, { key: file.id, id: index.toString(), onClick: () => onReplace && onReplace(file) },
38
18
  getFileIcon(file.name),
39
19
  React.createElement(ListItemText, null, file.name)))),
40
20
  React.createElement(UploadButton, { onClick: onUpload, disabled: !showUpload },
41
21
  React.createElement(UploadIcon, null),
42
22
  " Upload new...")) }),
23
+ React.createElement(ListItemButton, { onClick: onDownload, disabled: !showDownload }, "Download"),
43
24
  React.createElement(ListItemButton, { onClick: onDetach, disabled: !showDetach }, "Detach")))));
44
25
  };
45
26
  const NestedDropdown = ({ parentToggleOpen, buttonText, disabled, list, moveLeft }) => {
46
27
  const { isOpen, toggleOpen, wrapperRef } = useDropdown();
47
28
  return (React.createElement(DropdownWrapper, { ref: wrapperRef },
48
29
  React.createElement(NestedListButton, { onClick: toggleOpen, disabled: disabled },
49
- buttonText,
50
- " ",
30
+ React.createElement("div", null, buttonText),
51
31
  React.createElement(TriangleCollapsedIcon, null)),
52
32
  isOpen && (React.createElement(NestedListDropdownList, { direction: 'right', moveLeft: moveLeft, width: 192, onClick: (e) => {
53
33
  toggleOpen();
@@ -67,9 +47,6 @@ const OptionsButton = styled(IconButton) `
67
47
  margin: ${(props) => props.theme.grid.unit}px;
68
48
  visibility: hidden;
69
49
  background: white;
70
- position: absolute;
71
- top: -4px;
72
- right: 4%;
73
50
 
74
51
  &:hover {
75
52
  background: #f2fbfc !important;
@@ -111,30 +88,15 @@ const ListItemText = styled.div `
111
88
  text-overflow: ellipsis;
112
89
  text-align: start;
113
90
  `;
114
- const FilesButton = styled(RoundIconButton) `
115
- path {
116
- stroke: #6e6e6e;
117
- }
118
-
119
- &:active,
120
- &:focus {
121
- path {
122
- stroke: #1a9bc7;
123
- }
124
- }
125
- `;
126
- const FilesDropdownWrapper = styled.div `
127
- position: absolute;
128
- top: 8px;
129
- left: 70px;
130
- z-index: 1;
131
- `;
132
91
  const NestedListButton = styled(ListItemButton) `
133
92
  width: 100%;
134
93
  &:active,
135
94
  &:focus {
136
95
  background: #f2fbfc;
137
96
  }
97
+ svg {
98
+ margin-right: 0;
99
+ }
138
100
  `;
139
101
  const NestedListDropdownList = styled(DropdownList) `
140
102
  top: 0;
@@ -29,6 +29,7 @@ import doi from '../plugins/doi';
29
29
  import editorProps from '../plugins/editor-props';
30
30
  import elements from '../plugins/elements';
31
31
  import footnotes from '../plugins/footnotes';
32
+ import inspector_tabs from '../plugins/inspector-tabs';
32
33
  import objects from '../plugins/objects';
33
34
  import paragraphs from '../plugins/paragraphs';
34
35
  import persist from '../plugins/persist';
@@ -70,6 +71,7 @@ export default (props) => {
70
71
  doi(),
71
72
  section_category(props),
72
73
  cross_references(),
74
+ inspector_tabs(),
73
75
  ];
74
76
  if (props.collabProvider) {
75
77
  allPlugins.push(collab({ version: props.collabProvider.currentVersion }));
package/dist/es/index.js CHANGED
@@ -39,3 +39,4 @@ export { footnotesKey as footnotesPluginKey } from './plugins/footnotes';
39
39
  export { bibliographyKey as bibliographyPluginKey } from './plugins/bibliography';
40
40
  export { metadata } from './lib/references';
41
41
  export { authorLabel, affiliationLabel, } from './lib/authors';
42
+ export * from './plugins/inspector-tabs';
@@ -13,11 +13,16 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ import { ImageDefaultIcon, ImageLeftIcon, ImageRightIcon, TriangleCollapsedIcon, } from '@manuscripts/style-guide';
16
17
  import { getListType, isInBibliographySection, isSectionTitleNode, nodeNames, schema, } from '@manuscripts/transform';
17
18
  import { findChildrenByType } from 'prosemirror-utils';
19
+ import { createElement } from 'react';
20
+ import { renderToStaticMarkup } from 'react-dom/server';
18
21
  import { addNodeComment, createBlock, findPosBeforeFirstSubsection, insertGeneralTableFootnote, insertInlineTableFootnote, } from '../commands';
22
+ import { figurePositions } from '../views/figure_editable';
19
23
  import { PopperManager } from './popper';
20
- import { isChildOfNodeTypes, isSelectionInNode } from './utils';
24
+ import { getMatchingChild, isChildOfNodeTypes, isSelectionInNode, } from './utils';
25
+ import { updateNodeAttrs } from './view';
21
26
  const popper = new PopperManager();
22
27
  const readonlyTypes = [
23
28
  schema.nodes.keywords,
@@ -38,6 +43,7 @@ const hasAny = (set, ...items) => {
38
43
  return items.some((i) => set.has(i));
39
44
  };
40
45
  export const contextMenuBtnClass = 'btn-context-menu';
46
+ const contextSubmenuBtnClass = 'context-submenu-trigger';
41
47
  export class ContextMenu {
42
48
  constructor(node, view, getPos, actions = {}) {
43
49
  this.showAddMenu = (target, after) => {
@@ -146,6 +152,36 @@ export class ContextMenu {
146
152
  const $pos = this.resolvePos();
147
153
  const isBox = isBoxElementSectionTitle($pos, this.node);
148
154
  const type = isBox ? schema.nodes.box_element : this.node.type;
155
+ if (type === schema.nodes.figure_element ||
156
+ type === schema.nodes.image_element) {
157
+ const figure = getMatchingChild(this.node, (node) => node.type === schema.nodes.figure);
158
+ if (figure) {
159
+ const attrType = figure.attrs.type;
160
+ const submenuOptions = [
161
+ {
162
+ title: 'Left',
163
+ action: () => updateNodeAttrs(this.view, schema.nodes.figure, Object.assign(Object.assign({}, figure.attrs), { type: figurePositions.left })),
164
+ Icon: ImageLeftIcon,
165
+ selected: attrType === figurePositions.left,
166
+ },
167
+ {
168
+ title: 'Default',
169
+ action: () => updateNodeAttrs(this.view, schema.nodes.figure, Object.assign(Object.assign({}, figure.attrs), { type: figurePositions.default })),
170
+ Icon: ImageDefaultIcon,
171
+ selected: !attrType,
172
+ },
173
+ {
174
+ title: 'Right',
175
+ action: () => updateNodeAttrs(this.view, schema.nodes.figure, Object.assign(Object.assign({}, figure.attrs), { type: figurePositions.right })),
176
+ Icon: ImageRightIcon,
177
+ selected: attrType === figurePositions.right,
178
+ },
179
+ ];
180
+ const submenuLabel = 'Position';
181
+ const submenu = this.createSubmenu(submenuLabel, submenuOptions);
182
+ menu.appendChild(submenu);
183
+ }
184
+ }
149
185
  if (type === schema.nodes.list) {
150
186
  menu.appendChild(this.createMenuSection((section) => {
151
187
  const attrs = this.node.attrs;
@@ -210,22 +246,48 @@ export class ContextMenu {
210
246
  popper.show(target, menu, 'right', true);
211
247
  this.addPopperEventListeners();
212
248
  };
213
- this.createMenuItem = (contents, handler) => {
249
+ this.createSubmenuTrigger = (contents) => {
250
+ const item = document.createElement('div');
251
+ item.className = 'menu-item';
252
+ const textNode = document.createTextNode(contents);
253
+ item.innerHTML = renderToStaticMarkup(createElement(TriangleCollapsedIcon));
254
+ item.prepend(textNode);
255
+ item.classList.add(contextSubmenuBtnClass);
256
+ item.addEventListener('mousedown', this.toggleSubmenu);
257
+ return item;
258
+ };
259
+ this.createMenuItem = (contents, handler, Icon = null, selected = false) => {
214
260
  const item = document.createElement('div');
215
261
  item.className = 'menu-item';
216
- item.textContent = contents;
262
+ selected && item.classList.add('selected');
263
+ if (Icon) {
264
+ item.innerHTML = renderToStaticMarkup(createElement(Icon));
265
+ }
266
+ const textNode = document.createTextNode(contents);
267
+ item.appendChild(textNode);
217
268
  item.addEventListener('mousedown', (event) => {
218
269
  event.preventDefault();
219
270
  handler(event);
220
271
  });
221
272
  return item;
222
273
  };
223
- this.createMenuSection = (createMenuItems) => {
274
+ this.createMenuSection = (createMenuItems, isSubmenu = false) => {
224
275
  const section = document.createElement('div');
225
276
  section.className = 'menu-section';
277
+ isSubmenu && section.classList.add('menu');
226
278
  createMenuItems(section);
227
279
  return section;
228
280
  };
281
+ this.createSubmenu = (submenuLabel, items) => {
282
+ const submenu = document.createElement('div');
283
+ submenu.classList.add('menu-section', 'context-submenu');
284
+ submenu.append(this.createSubmenuTrigger(submenuLabel), this.createMenuSection((section) => {
285
+ items.forEach(({ title, action, Icon, selected }) => {
286
+ section.appendChild(this.createMenuItem(title, action, Icon, selected));
287
+ });
288
+ }, true));
289
+ return submenu;
290
+ };
229
291
  this.insertableTypes = (after, insertPos, endPos) => {
230
292
  const { nodes } = schema;
231
293
  const insertable = new Set();
@@ -287,7 +349,8 @@ export class ContextMenu {
287
349
  this.addPopperEventListeners = () => {
288
350
  const mouseListener = (event) => {
289
351
  const target = event.target;
290
- if (target.classList.contains(contextMenuBtnClass)) {
352
+ if (target.classList.contains(contextMenuBtnClass) ||
353
+ target.classList.contains(contextSubmenuBtnClass)) {
291
354
  return;
292
355
  }
293
356
  window.requestAnimationFrame(() => {
@@ -319,6 +382,10 @@ export class ContextMenu {
319
382
  }
320
383
  return this.node;
321
384
  };
385
+ this.toggleSubmenu = (ev) => {
386
+ const submenu = ev.target.nextElementSibling;
387
+ submenu === null || submenu === void 0 ? void 0 : submenu.classList.toggle('show');
388
+ };
322
389
  this.node = node;
323
390
  this.view = view;
324
391
  this.getPos = getPos;