@manuscripts/body-editor 2.7.29 → 2.7.30-LEAN-4096.2

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 (56) hide show
  1. package/dist/cjs/components/views/FigureDropdown.js +12 -45
  2. package/dist/cjs/configs/editor-plugins.js +2 -0
  3. package/dist/cjs/index.js +1 -0
  4. package/dist/cjs/lib/context-menu.js +71 -4
  5. package/dist/cjs/lib/view.js +3 -1
  6. package/dist/cjs/plugins/inspector-tabs.js +84 -0
  7. package/dist/cjs/versions.js +1 -1
  8. package/dist/cjs/views/ReactSubView.js +4 -4
  9. package/dist/cjs/views/affiliations.js +1 -1
  10. package/dist/cjs/views/award.js +3 -3
  11. package/dist/cjs/views/bibliography_element.js +2 -2
  12. package/dist/cjs/views/citation_editable.js +3 -3
  13. package/dist/cjs/views/contributors.js +2 -2
  14. package/dist/cjs/views/cross_reference_editable.js +1 -1
  15. package/dist/cjs/views/embed.js +1 -1
  16. package/dist/cjs/views/figure_editable.js +96 -9
  17. package/dist/cjs/views/figure_element.js +0 -79
  18. package/dist/cjs/views/inline_footnote.js +2 -2
  19. package/dist/cjs/views/keyword.js +1 -1
  20. package/dist/cjs/views/keyword_group.js +1 -1
  21. package/dist/cjs/views/link_editable.js +1 -1
  22. package/dist/cjs/views/table_cell.js +1 -1
  23. package/dist/es/components/views/FigureDropdown.js +12 -44
  24. package/dist/es/configs/editor-plugins.js +2 -0
  25. package/dist/es/index.js +1 -0
  26. package/dist/es/lib/context-menu.js +72 -5
  27. package/dist/es/lib/view.js +3 -1
  28. package/dist/es/plugins/inspector-tabs.js +81 -0
  29. package/dist/es/versions.js +1 -1
  30. package/dist/es/views/ReactSubView.js +4 -4
  31. package/dist/es/views/affiliations.js +1 -1
  32. package/dist/es/views/award.js +3 -3
  33. package/dist/es/views/bibliography_element.js +2 -2
  34. package/dist/es/views/citation_editable.js +3 -3
  35. package/dist/es/views/contributors.js +2 -2
  36. package/dist/es/views/cross_reference_editable.js +1 -1
  37. package/dist/es/views/embed.js +1 -1
  38. package/dist/es/views/figure_editable.js +96 -9
  39. package/dist/es/views/figure_element.js +0 -79
  40. package/dist/es/views/inline_footnote.js +2 -2
  41. package/dist/es/views/keyword.js +1 -1
  42. package/dist/es/views/keyword_group.js +1 -1
  43. package/dist/es/views/link_editable.js +1 -1
  44. package/dist/es/views/table_cell.js +1 -1
  45. package/dist/types/components/views/FigureDropdown.d.ts +0 -1
  46. package/dist/types/index.d.ts +1 -0
  47. package/dist/types/lib/context-menu.d.ts +3 -0
  48. package/dist/types/plugins/inspector-tabs.d.ts +35 -0
  49. package/dist/types/versions.d.ts +1 -1
  50. package/dist/types/views/ReactSubView.d.ts +1 -1
  51. package/dist/types/views/figure_editable.d.ts +9 -0
  52. package/dist/types/views/figure_element.d.ts +0 -3
  53. package/package.json +3 -3
  54. package/styles/AdvancedEditor.css +15 -5
  55. package/styles/Editor.css +10 -1
  56. package/styles/popper.css +33 -11
@@ -27,16 +27,25 @@ 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");
33
+ const prosemirror_state_1 = require("prosemirror-state");
32
34
  const react_1 = require("react");
33
35
  const server_1 = require("react-dom/server");
34
36
  const FigureDropdown_1 = require("../components/views/FigureDropdown");
35
37
  const files_1 = require("../lib/files");
38
+ const view_1 = require("../lib/view");
36
39
  const creators_1 = require("./creators");
37
40
  const figure_1 = require("./figure");
38
41
  const figure_uploader_1 = require("./figure_uploader");
39
42
  const ReactSubView_1 = __importDefault(require("./ReactSubView"));
43
+ var figurePositions;
44
+ (function (figurePositions) {
45
+ figurePositions["left"] = "half-left";
46
+ figurePositions["right"] = "half-right";
47
+ figurePositions["default"] = "";
48
+ })(figurePositions = exports.figurePositions || (exports.figurePositions = {}));
40
49
  class FigureEditableView extends figure_1.FigureView {
41
50
  constructor() {
42
51
  super(...arguments);
@@ -45,8 +54,10 @@ class FigureEditableView extends figure_1.FigureView {
45
54
  this.updateContents();
46
55
  };
47
56
  this.setSrc = (src) => {
48
- const { selection, tr } = this.view.state;
49
- tr.setNodeMarkup(this.getPos(), undefined, Object.assign(Object.assign({}, this.node.attrs), { src: src })).setSelection(selection.map(tr.doc, tr.mapping));
57
+ const { tr } = this.view.state;
58
+ const pos = this.getPos();
59
+ tr.setNodeMarkup(pos, undefined, Object.assign(Object.assign({}, this.node.attrs), { src: src }));
60
+ tr.setSelection(prosemirror_state_1.NodeSelection.create(tr.doc, pos));
50
61
  this.view.dispatch(tr);
51
62
  };
52
63
  this.createUnsupportedFormat = (name) => {
@@ -84,11 +95,75 @@ class FigureEditableView extends figure_1.FigureView {
84
95
  element.classList.add('figure', 'placeholder');
85
96
  const instructions = document.createElement('div');
86
97
  instructions.classList.add('instructions');
87
- instructions.textContent = 'Click to select an image file';
88
- instructions.style.pointerEvents = 'none';
98
+ instructions.innerHTML = `
99
+ <p>Drag or click here to upload image <br>
100
+ or drag items here from the file inspector tabs <br>
101
+ <a data-action='open-other-files'>'Other files'</a> |
102
+ <a data-action='open-supplement-files'>'Supplements'</a></p>
103
+ `;
89
104
  element.appendChild(instructions);
90
105
  return element;
91
106
  };
107
+ this.createPositionMenuWrapper = () => {
108
+ this.positionMenuWrapper = document.createElement('div');
109
+ this.positionMenuWrapper.classList.add('position-menu');
110
+ const positionMenuButton = document.createElement('div');
111
+ positionMenuButton.classList.add('position-menu-button');
112
+ let icon;
113
+ switch (this.figurePosition) {
114
+ case figurePositions.left:
115
+ icon = style_guide_1.ImageLeftIcon;
116
+ break;
117
+ case figurePositions.right:
118
+ icon = style_guide_1.ImageRightIcon;
119
+ break;
120
+ default:
121
+ icon = style_guide_1.ImageDefaultIcon;
122
+ break;
123
+ }
124
+ if (icon) {
125
+ positionMenuButton.innerHTML = (0, server_1.renderToStaticMarkup)((0, react_1.createElement)(icon));
126
+ }
127
+ positionMenuButton.addEventListener('click', this.showPositionMenu);
128
+ this.positionMenuWrapper.appendChild(positionMenuButton);
129
+ return this.positionMenuWrapper;
130
+ };
131
+ this.showPositionMenu = () => {
132
+ this.props.popper.destroy();
133
+ const figure = this.node;
134
+ const componentProps = {
135
+ actions: [
136
+ {
137
+ label: 'Left',
138
+ action: () => {
139
+ this.props.popper.destroy();
140
+ (0, view_1.updateNodeAttrs)(this.view, transform_1.schema.nodes.figure, Object.assign(Object.assign({}, figure.attrs), { type: figurePositions.left }));
141
+ },
142
+ icon: 'ImageLeft',
143
+ selected: this.figurePosition === figurePositions.left,
144
+ },
145
+ {
146
+ label: 'Default',
147
+ action: () => {
148
+ this.props.popper.destroy();
149
+ (0, view_1.updateNodeAttrs)(this.view, transform_1.schema.nodes.figure, Object.assign(Object.assign({}, figure.attrs), { type: figurePositions.default }));
150
+ },
151
+ icon: 'ImageDefault',
152
+ selected: !this.figurePosition,
153
+ },
154
+ {
155
+ label: 'Right',
156
+ action: () => {
157
+ this.props.popper.destroy();
158
+ (0, view_1.updateNodeAttrs)(this.view, transform_1.schema.nodes.figure, Object.assign(Object.assign({}, figure.attrs), { type: figurePositions.right }));
159
+ },
160
+ icon: 'ImageRight',
161
+ selected: this.figurePosition === figurePositions.right,
162
+ },
163
+ ],
164
+ };
165
+ 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);
166
+ };
92
167
  }
93
168
  updateContents() {
94
169
  var _a;
@@ -97,6 +172,7 @@ class FigureEditableView extends figure_1.FigureView {
97
172
  const src = attrs.src;
98
173
  const files = this.props.getFiles();
99
174
  const file = src && files.filter((f) => f.id === src)[0];
175
+ this.figurePosition = attrs.type;
100
176
  this.container.innerHTML = '';
101
177
  const can = this.props.getCapabilities();
102
178
  const link = file && this.props.fileManagement.previewLink(file);
@@ -115,20 +191,30 @@ class FigureEditableView extends figure_1.FigureView {
115
191
  this.props.fileManagement.download(file);
116
192
  };
117
193
  }
118
- handleReplace = (file) => {
119
- this.setSrc(file.id);
120
- };
121
194
  handleDetach = () => {
122
195
  this.setSrc('');
123
196
  };
124
197
  }
198
+ if (can.replaceFile) {
199
+ handleReplace = (file) => {
200
+ this.setSrc(file.id);
201
+ };
202
+ }
125
203
  if (can.uploadFile) {
126
204
  const upload = (file) => __awaiter(this, void 0, void 0, function* () {
127
205
  const result = yield this.props.fileManagement.upload(file);
128
206
  this.setSrc(result.id);
129
207
  });
130
208
  handleUpload = (0, figure_uploader_1.figureUploader)(upload);
131
- img.addEventListener('click', handleUpload);
209
+ const handlePlaceholderClick = (event) => {
210
+ const target = event.target;
211
+ if (target.dataset && target.dataset.action) {
212
+ return;
213
+ }
214
+ const triggerUpload = (0, figure_uploader_1.figureUploader)(upload);
215
+ triggerUpload();
216
+ };
217
+ img.addEventListener('click', handlePlaceholderClick);
132
218
  img.addEventListener('mouseenter', () => {
133
219
  img.classList.toggle('over', true);
134
220
  });
@@ -176,6 +262,7 @@ class FigureEditableView extends figure_1.FigureView {
176
262
  this.reactTools = (0, ReactSubView_1.default)(this.props, FigureDropdown_1.FigureOptions, componentProps, this.node, this.getPos, this.view);
177
263
  this.dom.insertBefore(this.reactTools, this.dom.firstChild);
178
264
  }
265
+ this.container.appendChild(this.createPositionMenuWrapper());
179
266
  }
180
267
  }
181
268
  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);
@@ -1,53 +1,39 @@
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, }) => {
4
+ export const FigureOptions = ({ can, files, onDownload, onUpload, onDetach, onReplace, }) => {
5
5
  const { isOpen, toggleOpen, wrapperRef } = useDropdown();
6
6
  const supplements = files.supplements
7
7
  .map((s) => s.file)
8
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
- export const FigureOptions = ({ can, files, onDownload, onUpload, onDetach, onReplace, }) => {
25
- const { isOpen, toggleOpen, wrapperRef } = useDropdown();
26
9
  const otherFiles = files.others.filter((f) => isImageFile(f.name));
27
10
  const showDownload = onDownload && can.downloadFiles;
28
11
  const showUpload = onUpload && can.uploadFile;
29
12
  const showDetach = onDetach && can.detachFile;
30
13
  const showReplace = onReplace && can.replaceFile;
14
+ const replaceBtnText = onDownload ? 'Replace' : 'Choose file';
31
15
  return (React.createElement(DropdownWrapper, { ref: wrapperRef },
32
16
  React.createElement(OptionsButton, { className: 'options-button', onClick: toggleOpen },
33
17
  React.createElement(DotsIcon, null)),
34
18
  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,
19
+ React.createElement(NestedDropdown, { disabled: !showReplace, parentToggleOpen: toggleOpen, buttonText: replaceBtnText, moveLeft: true, list: React.createElement(React.Fragment, null,
20
+ supplements.map((file, index) => (React.createElement(ListItemButton, { key: file.id, id: index.toString(), onClick: () => onReplace && onReplace(file) },
21
+ getFileIcon(file.name),
22
+ React.createElement(ListItemText, null, file.name)))),
37
23
  otherFiles.map((file, index) => (React.createElement(ListItemButton, { key: file.id, id: index.toString(), onClick: () => onReplace && onReplace(file) },
38
24
  getFileIcon(file.name),
39
25
  React.createElement(ListItemText, null, file.name)))),
40
26
  React.createElement(UploadButton, { onClick: onUpload, disabled: !showUpload },
41
27
  React.createElement(UploadIcon, null),
42
28
  " Upload new...")) }),
29
+ React.createElement(ListItemButton, { onClick: onDownload, disabled: !showDownload }, "Download"),
43
30
  React.createElement(ListItemButton, { onClick: onDetach, disabled: !showDetach }, "Detach")))));
44
31
  };
45
32
  const NestedDropdown = ({ parentToggleOpen, buttonText, disabled, list, moveLeft }) => {
46
33
  const { isOpen, toggleOpen, wrapperRef } = useDropdown();
47
34
  return (React.createElement(DropdownWrapper, { ref: wrapperRef },
48
35
  React.createElement(NestedListButton, { onClick: toggleOpen, disabled: disabled },
49
- buttonText,
50
- " ",
36
+ React.createElement("div", null, buttonText),
51
37
  React.createElement(TriangleCollapsedIcon, null)),
52
38
  isOpen && (React.createElement(NestedListDropdownList, { direction: 'right', moveLeft: moveLeft, width: 192, onClick: (e) => {
53
39
  toggleOpen();
@@ -67,9 +53,6 @@ const OptionsButton = styled(IconButton) `
67
53
  margin: ${(props) => props.theme.grid.unit}px;
68
54
  visibility: hidden;
69
55
  background: white;
70
- position: absolute;
71
- top: -4px;
72
- right: 4%;
73
56
 
74
57
  &:hover {
75
58
  background: #f2fbfc !important;
@@ -111,30 +94,15 @@ const ListItemText = styled.div `
111
94
  text-overflow: ellipsis;
112
95
  text-align: start;
113
96
  `;
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
97
  const NestedListButton = styled(ListItemButton) `
133
98
  width: 100%;
134
99
  &:active,
135
100
  &:focus {
136
101
  background: #f2fbfc;
137
102
  }
103
+ svg {
104
+ margin-right: 0;
105
+ }
138
106
  `;
139
107
  const NestedListDropdownList = styled(DropdownList) `
140
108
  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;
@@ -14,6 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { schema, } from '@manuscripts/transform';
17
+ import { NodeSelection } from 'prosemirror-state';
17
18
  import * as utils from 'prosemirror-utils';
18
19
  import { isHidden, sanitizeAttrsChange } from './track-changes-utils';
19
20
  const metaNodeTypes = [
@@ -44,7 +45,8 @@ export const updateNodeAttrs = (view, type, attrs) => {
44
45
  const copy = sanitizeAttrsChange(attrs, child.node.attrs);
45
46
  delete copy.dataTracked;
46
47
  const pos = child.pos;
47
- const tr = view.state.tr.setNodeMarkup(pos, undefined, copy);
48
+ const tr = view.state.tr;
49
+ tr.setNodeMarkup(pos, undefined, copy).setSelection(NodeSelection.create(tr.doc, pos));
48
50
  if (metaNodeTypes.includes(type)) {
49
51
  tr.setMeta(updateMetaNode, true);
50
52
  }