@manuscripts/body-editor 3.2.35 → 3.2.37

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 (42) hide show
  1. package/dist/cjs/commands.js +19 -10
  2. package/dist/cjs/components/toolbar/InsertEmbedDialog.js +1 -1
  3. package/dist/cjs/components/views/FigureDropdown.js +30 -13
  4. package/dist/cjs/configs/editor-views.js +1 -1
  5. package/dist/cjs/index.js +2 -0
  6. package/dist/cjs/lib/context-menu.js +12 -36
  7. package/dist/cjs/lib/get-media-type.js +77 -0
  8. package/dist/cjs/lib/media.js +207 -0
  9. package/dist/cjs/lib/position-menu.js +122 -0
  10. package/dist/cjs/menus.js +1 -2
  11. package/dist/cjs/versions.js +1 -1
  12. package/dist/cjs/views/embed.js +188 -64
  13. package/dist/cjs/views/figure_editable.js +11 -134
  14. package/dist/cjs/views/image_element.js +36 -57
  15. package/dist/es/commands.js +17 -8
  16. package/dist/es/components/toolbar/InsertEmbedDialog.js +1 -1
  17. package/dist/es/components/views/FigureDropdown.js +31 -14
  18. package/dist/es/configs/editor-views.js +1 -1
  19. package/dist/es/index.js +2 -0
  20. package/dist/es/lib/context-menu.js +13 -37
  21. package/dist/es/lib/get-media-type.js +73 -0
  22. package/dist/es/lib/media.js +195 -0
  23. package/dist/es/lib/position-menu.js +111 -0
  24. package/dist/es/menus.js +2 -3
  25. package/dist/es/versions.js +1 -1
  26. package/dist/es/views/embed.js +187 -63
  27. package/dist/es/views/figure_editable.js +12 -132
  28. package/dist/es/views/image_element.js +36 -57
  29. package/dist/types/commands.d.ts +1 -1
  30. package/dist/types/components/views/FigureDropdown.d.ts +3 -0
  31. package/dist/types/index.d.ts +2 -0
  32. package/dist/types/lib/files.d.ts +1 -0
  33. package/dist/types/lib/get-media-type.d.ts +11 -0
  34. package/dist/types/lib/media.d.ts +33 -0
  35. package/dist/types/lib/position-menu.d.ts +34 -0
  36. package/dist/types/versions.d.ts +1 -1
  37. package/dist/types/views/embed.d.ts +43 -22
  38. package/dist/types/views/figure_editable.d.ts +4 -2
  39. package/dist/types/views/image_element.d.ts +0 -1
  40. package/package.json +4 -4
  41. package/styles/AdvancedEditor.css +95 -33
  42. package/styles/Editor.css +4 -2
package/dist/cjs/menus.js CHANGED
@@ -20,7 +20,6 @@ const transform_1 = require("@manuscripts/transform");
20
20
  const prosemirror_commands_1 = require("prosemirror-commands");
21
21
  const prosemirror_history_1 = require("prosemirror-history");
22
22
  const commands_1 = require("./commands");
23
- const InsertEmbedDialog_1 = require("./components/toolbar/InsertEmbedDialog");
24
23
  const InsertTableDialog_1 = require("./components/toolbar/InsertTableDialog");
25
24
  const ListMenuItem_1 = require("./components/toolbar/ListMenuItem");
26
25
  const InsertSpecialCharacter_1 = require("./components/views/InsertSpecialCharacter");
@@ -263,7 +262,7 @@ const getEditorMenus = (editor) => {
263
262
  label: 'Embedded Media',
264
263
  isActive: (0, commands_1.blockActive)(transform_1.schema.nodes.embed)(state),
265
264
  isEnabled: (0, utils_1.isEditAllowed)(state) && isCommandValid((0, commands_1.canInsert)(transform_1.schema.nodes.embed)),
266
- run: () => (0, InsertEmbedDialog_1.openEmbedDialog)(editor.view),
265
+ run: doCommand(commands_1.insertEmbed),
267
266
  },
268
267
  {
269
268
  id: 'insert-link',
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MATHJAX_VERSION = exports.VERSION = void 0;
4
- exports.VERSION = '3.2.35';
4
+ exports.VERSION = '3.2.37';
5
5
  exports.MATHJAX_VERSION = '3.2.2';
@@ -1,101 +1,225 @@
1
1
  "use strict";
2
- /*!
3
- * © 2019 Atypon Systems LLC
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */
17
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
18
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
4
  };
20
5
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.EmbedMediaView = void 0;
22
- const style_guide_1 = require("@manuscripts/style-guide");
23
- const commands_1 = require("../commands");
6
+ exports.EmbedView = void 0;
7
+ const lodash_1 = require("lodash");
8
+ const prosemirror_state_1 = require("prosemirror-state");
24
9
  const InsertEmbedDialog_1 = require("../components/toolbar/InsertEmbedDialog");
25
- const DeleteEmbedDialog_1 = require("../components/views/DeleteEmbedDialog");
10
+ const get_media_type_1 = require("../lib/get-media-type");
11
+ const media_1 = require("../lib/media");
26
12
  const oembed_1 = require("../lib/oembed");
13
+ const url_1 = require("../lib/url");
27
14
  const block_view_1 = __importDefault(require("./block_view"));
28
15
  const creators_1 = require("./creators");
16
+ const editable_block_1 = require("./editable_block");
29
17
  const ReactSubView_1 = __importDefault(require("./ReactSubView"));
30
- class EmbedMediaView extends block_view_1.default {
18
+ class EmbedView extends block_view_1.default {
31
19
  constructor() {
32
20
  super(...arguments);
21
+ this.preview = null;
22
+ this.reactTools = null;
33
23
  this.ignoreMutation = () => true;
34
- this.stopEvent = () => true;
24
+ this.initialized = false;
25
+ this.previousAttrs = {};
35
26
  this.createElement = () => {
36
27
  this.container = document.createElement('div');
37
28
  this.container.classList.add('block');
38
29
  this.dom.appendChild(this.container);
30
+ const figureBlock = document.createElement('div');
31
+ figureBlock.classList.add('figure-block');
32
+ this.container.appendChild(figureBlock);
39
33
  this.contentDOM = document.createElement('div');
40
- this.container.appendChild(this.contentDOM);
41
- this.buildContextMenu(this.container);
34
+ figureBlock.appendChild(this.contentDOM);
35
+ this.figureBlock = figureBlock;
42
36
  };
43
- this.buildContextMenu = (preview) => {
44
- const can = this.props.getCapabilities();
45
- if (!can.editArticle) {
46
- return;
37
+ this.upload = async (file) => {
38
+ const mediaInfo = (0, get_media_type_1.getMediaTypeInfo)(file);
39
+ const result = await this.props.fileManagement.upload(file);
40
+ const pos = this.getPos();
41
+ const tr = this.view.state.tr;
42
+ tr.setNodeMarkup(pos, undefined, {
43
+ ...this.node.attrs,
44
+ href: result.id,
45
+ mimetype: mediaInfo.mimetype,
46
+ mimeSubtype: mediaInfo.mimeSubtype,
47
+ });
48
+ this.view.dispatch(tr);
49
+ };
50
+ this.setHref = (href) => {
51
+ const { tr } = this.view.state;
52
+ const pos = this.getPos();
53
+ tr.setNodeMarkup(pos, undefined, {
54
+ ...this.node.attrs,
55
+ href: href,
56
+ });
57
+ tr.setSelection(prosemirror_state_1.NodeSelection.create(tr.doc, pos));
58
+ this.view.dispatch(tr);
59
+ };
60
+ this.createMedia = () => {
61
+ const { href } = this.node.attrs;
62
+ if (!href) {
63
+ return null;
64
+ }
65
+ const files = this.props.getFiles();
66
+ const file = files.find((f) => f.id === href);
67
+ if (!file) {
68
+ return null;
69
+ }
70
+ const mediaUrl = file.link || file.id;
71
+ if (!mediaUrl) {
72
+ return null;
73
+ }
74
+ const mediaInfo = (0, get_media_type_1.getMediaTypeInfo)(file.name);
75
+ if (mediaInfo.isVideo) {
76
+ const video = document.createElement('video');
77
+ video.controls = true;
78
+ video.style.maxWidth = '100%';
79
+ video.style.height = '250px';
80
+ const source = document.createElement('source');
81
+ source.src = mediaUrl;
82
+ video.appendChild(source);
83
+ video.appendChild(document.createTextNode('Your browser does not support the video tag.'));
84
+ return video;
85
+ }
86
+ else if (mediaInfo.isAudio) {
87
+ const audio = document.createElement('audio');
88
+ audio.controls = true;
89
+ audio.style.width = '100%';
90
+ const source = document.createElement('source');
91
+ source.src = mediaUrl;
92
+ audio.appendChild(source);
93
+ audio.appendChild(document.createTextNode('Your browser does not support the audio tag.'));
94
+ return audio;
95
+ }
96
+ else {
97
+ return (0, media_1.createUnsupportedFormat)(file.name, this.props.getCapabilities().editArticle);
47
98
  }
48
- const componentProps = {
49
- actions: [
50
- {
51
- label: 'Comment',
52
- action: () => (0, commands_1.addNodeComment)(this.node, this.view.state, this.view.dispatch),
53
- icon: 'AddComment',
54
- },
55
- {
56
- label: 'Delete',
57
- action: () => (0, DeleteEmbedDialog_1.openDeleteEmbedDialog)(this.view, this.node, this.getPos()),
58
- icon: 'Delete',
59
- },
60
- {
61
- label: 'Edit',
62
- action: () => (0, InsertEmbedDialog_1.openEmbedDialog)(this.view, this.getPos()),
63
- icon: 'Edit',
64
- },
65
- ],
66
- };
67
- preview.appendChild((0, ReactSubView_1.default)(this.props, style_guide_1.ContextMenu, componentProps, this.node, this.getPos, this.view, ['embed-context-menu']));
68
99
  };
69
100
  }
70
- async updateContents() {
101
+ updateContents() {
71
102
  super.updateContents();
72
- if (this.href !== this.node.attrs.href) {
73
- this.href = this.node.attrs.href;
74
- await this.updateOEmbedPreview();
103
+ const { href, mimetype, mimeSubtype } = this.node.attrs;
104
+ const currentAttrs = { href, mimetype, mimeSubtype };
105
+ const contentChanged = !this.initialized || !(0, lodash_1.isEqual)(this.previousAttrs, currentAttrs);
106
+ if (contentChanged) {
107
+ this.initialized = true;
108
+ this.previousAttrs = currentAttrs;
109
+ this.updateMediaPreview();
110
+ this.manageReactTools();
111
+ }
112
+ }
113
+ manageReactTools() {
114
+ this.reactTools?.remove();
115
+ let handlers;
116
+ const can = this.props.getCapabilities();
117
+ if (this.isUploadedFile()) {
118
+ handlers = (0, media_1.createFileHandlers)(this.node, this.view, this.getPos, this.props, this.setHref);
119
+ if (can.uploadFile) {
120
+ handlers.handleUpload = (0, media_1.createFileUploader)(this.upload, 'video/*,audio/*');
121
+ }
122
+ }
123
+ else if (this.isEmbedLink()) {
124
+ handlers = this.createEmbedHandlers();
125
+ }
126
+ if (handlers) {
127
+ this.reactTools = (0, media_1.createReactTools)(this.node, this.view, this.getPos, this.props, handlers, true, () => false);
128
+ if (this.reactTools) {
129
+ if (this.preview) {
130
+ this.preview.insertBefore(this.reactTools, this.preview.firstChild);
131
+ }
132
+ else {
133
+ this.dom.insertBefore(this.reactTools, this.dom.firstChild);
134
+ }
135
+ }
136
+ }
137
+ }
138
+ isUploadedFile() {
139
+ const { href } = this.node.attrs;
140
+ if (!href) {
141
+ return false;
75
142
  }
143
+ const files = this.props.getFiles();
144
+ return files.some((file) => file.id === href);
145
+ }
146
+ isEmbedLink() {
147
+ const { href } = this.node.attrs;
148
+ return !!(href && (0, url_1.allowedHref)(href) && !this.isUploadedFile());
76
149
  }
77
- async updateOEmbedPreview() {
150
+ async updateMediaPreview() {
78
151
  const preview = document.createElement('div');
79
- preview.classList.add('embed-media-preview');
152
+ preview.classList.add('media-preview');
80
153
  preview.setAttribute('contenteditable', 'false');
81
- const oldPreview = this.container.querySelector('.embed-media-preview');
154
+ const oldPreview = this.preview
155
+ ? this.preview
156
+ : this.figureBlock.querySelector('.media-preview');
82
157
  if (oldPreview) {
83
- this.container.replaceChild(preview, oldPreview);
158
+ this.figureBlock.replaceChild(preview, oldPreview);
84
159
  }
85
160
  else {
86
- this.container.prepend(preview);
161
+ this.figureBlock.prepend(preview);
87
162
  }
88
- const html = await (0, oembed_1.getOEmbedHTML)(this.href, 643, 363);
89
- if (html) {
90
- preview.innerHTML = html;
163
+ this.preview = preview;
164
+ const href = this.node.attrs.href;
165
+ let object;
166
+ if (!href) {
167
+ object = (0, media_1.createMediaPlaceholder)('media', this.view, this.getPos);
168
+ }
169
+ else if (this.isUploadedFile()) {
170
+ const files = this.props.getFiles();
171
+ const file = files.find((f) => f.id === href);
172
+ if (file) {
173
+ const mediaInfo = (0, get_media_type_1.getMediaTypeInfo)(file.name);
174
+ const isValidMediaFile = mediaInfo.isAudio || mediaInfo.isVideo;
175
+ object = isValidMediaFile
176
+ ? this.createMedia() ||
177
+ (0, media_1.createUnsupportedFormat)(file.name, this.props.getCapabilities().editArticle)
178
+ : (0, media_1.createUnsupportedFormat)(file.name, this.props.getCapabilities().editArticle);
179
+ }
180
+ else {
181
+ object = (0, media_1.createMediaPlaceholder)('media', this.view, this.getPos);
182
+ }
183
+ }
184
+ else if (this.isEmbedLink()) {
185
+ object = await this.createEmbedPreview();
91
186
  }
92
187
  else {
93
- this.showUnavailableMessage(preview);
188
+ object = (0, media_1.createMediaPlaceholder)('media', this.view, this.getPos);
189
+ }
190
+ const can = this.props.getCapabilities();
191
+ if (can.uploadFile && object.classList.contains('placeholder')) {
192
+ (0, media_1.addInteractionHandlers)(object, this.upload, 'video/*,audio/*');
193
+ }
194
+ preview.appendChild(object);
195
+ }
196
+ createEmbedHandlers() {
197
+ const handlers = {};
198
+ handlers.handleReplaceEmbed = () => {
199
+ (0, InsertEmbedDialog_1.openEmbedDialog)(this.view, this.getPos());
200
+ };
201
+ return handlers;
202
+ }
203
+ async createEmbedPreview() {
204
+ const container = document.createElement('div');
205
+ container.classList.add('embed-preview');
206
+ try {
207
+ const html = await (0, oembed_1.getOEmbedHTML)(this.node.attrs.href, 643, 363);
208
+ if (html) {
209
+ container.innerHTML = html;
210
+ }
211
+ else {
212
+ this.showUnavailableMessage(container);
213
+ }
214
+ }
215
+ catch (error) {
216
+ this.showUnavailableMessage(container);
94
217
  }
218
+ return container;
95
219
  }
96
- showUnavailableMessage(preview) {
97
- preview.appendChild((0, ReactSubView_1.default)(this.props, InsertEmbedDialog_1.NoPreviewMessageWithLink, { href: this.node.attrs.href }, this.node, this.getPos, this.view));
220
+ showUnavailableMessage(container) {
221
+ container.appendChild((0, ReactSubView_1.default)(this.props, InsertEmbedDialog_1.NoPreviewMessageWithLink, { href: this.node.attrs.href }, this.node, this.getPos, this.view));
98
222
  }
99
223
  }
100
- exports.EmbedMediaView = EmbedMediaView;
101
- exports.default = (0, creators_1.createNodeView)(EmbedMediaView);
224
+ exports.EmbedView = EmbedView;
225
+ exports.default = (0, creators_1.createEditableNodeView)((0, editable_block_1.EditableBlock)(EmbedView));
@@ -14,24 +14,20 @@
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
- };
20
17
  Object.defineProperty(exports, "__esModule", { value: true });
21
18
  exports.FigureEditableView = void 0;
22
19
  const transform_1 = require("@manuscripts/transform");
23
20
  const prosemirror_state_1 = require("prosemirror-state");
24
21
  const prosemirror_utils_1 = require("prosemirror-utils");
25
- const FigureDropdown_1 = require("../components/views/FigureDropdown");
26
22
  const icons_1 = require("../icons");
23
+ const media_1 = require("../lib/media");
27
24
  const track_changes_utils_1 = require("../lib/track-changes-utils");
28
25
  const creators_1 = require("./creators");
29
26
  const figure_1 = require("./figure");
30
- const figure_uploader_1 = require("./figure_uploader");
31
- const ReactSubView_1 = __importDefault(require("./ReactSubView"));
32
27
  class FigureEditableView extends figure_1.FigureView {
33
28
  constructor() {
34
29
  super(...arguments);
30
+ this.reactTools = null;
35
31
  this.dragAndDropInitialized = false;
36
32
  this.upload = async (file) => {
37
33
  const result = await this.props.fileManagement.upload(file);
@@ -48,27 +44,7 @@ class FigureEditableView extends figure_1.FigureView {
48
44
  this.view.dispatch(tr);
49
45
  };
50
46
  this.createUnsupportedFormat = (name) => {
51
- const element = document.createElement('div');
52
- element.classList.add('figure', 'placeholder');
53
- const instructions = document.createElement('div');
54
- instructions.classList.add('instructions');
55
- const iconHtml = icons_1.fileCorruptedIcon;
56
- instructions.innerHTML = `
57
- <div>
58
- <div class="unsupported-icon-wrapper">${iconHtml}</div>
59
- <div>${name}</div>
60
- <div class="unsupported-format-label">
61
- Unsupported file format
62
- </div>
63
- <div>
64
- ${this.props.getCapabilities()?.editArticle
65
- ? 'Click to add image'
66
- : 'No image here yet…'}
67
- </div>
68
- </div>
69
- `;
70
- element.appendChild(instructions);
71
- return element;
47
+ return (0, media_1.createUnsupportedFormat)(name, this.props.getCapabilities().editArticle);
72
48
  };
73
49
  this.createImg = (src) => {
74
50
  const img = document.createElement('img');
@@ -77,18 +53,7 @@ class FigureEditableView extends figure_1.FigureView {
77
53
  return img;
78
54
  };
79
55
  this.createPlaceholder = () => {
80
- const element = document.createElement('div');
81
- element.classList.add('figure', 'placeholder');
82
- const instructions = document.createElement('div');
83
- instructions.classList.add('instructions');
84
- instructions.innerHTML = `
85
- <p>Drag or click here to upload image <br>
86
- or drag items here from the file inspector tabs <br>
87
- <a data-action='open-other-files'>'Other files'</a> |
88
- <a data-action='open-supplement-files'>'Supplements'</a></p>
89
- `;
90
- element.appendChild(instructions);
91
- return element;
56
+ return (0, media_1.createMediaPlaceholder)('figure');
92
57
  };
93
58
  }
94
59
  initialise() {
@@ -218,44 +183,7 @@ class FigureEditableView extends figure_1.FigureView {
218
183
  ? this.createUnsupportedFormat(file.name)
219
184
  : this.createPlaceholder();
220
185
  if (can.uploadFile && !(0, track_changes_utils_1.isDeleted)(this.node)) {
221
- const handlePlaceholderClick = (event) => {
222
- const target = event.target;
223
- if (target.dataset && target.dataset.action) {
224
- return;
225
- }
226
- const triggerUpload = (0, figure_uploader_1.figureUploader)(this.upload);
227
- triggerUpload();
228
- };
229
- img.addEventListener('click', handlePlaceholderClick);
230
- img.addEventListener('mouseenter', () => {
231
- img.classList.toggle('over', true);
232
- });
233
- img.addEventListener('mouseleave', () => {
234
- img.classList.toggle('over', false);
235
- });
236
- img.addEventListener('dragenter', (event) => {
237
- event.preventDefault();
238
- img.classList.toggle('over', true);
239
- });
240
- img.addEventListener('dragleave', () => {
241
- img.classList.toggle('over', false);
242
- });
243
- img.addEventListener('dragover', (e) => {
244
- if (e.dataTransfer && e.dataTransfer.items) {
245
- for (const item of e.dataTransfer.items) {
246
- if (item.kind === 'file' && item.type.startsWith('image/')) {
247
- e.preventDefault();
248
- e.dataTransfer.dropEffect = 'copy';
249
- }
250
- }
251
- }
252
- });
253
- img.addEventListener('drop', async (e) => {
254
- if (e.dataTransfer && e.dataTransfer.files.length) {
255
- e.preventDefault();
256
- await this.upload(e.dataTransfer.files[0]);
257
- }
258
- });
186
+ (0, media_1.addInteractionHandlers)(img, this.upload, 'image/*');
259
187
  }
260
188
  this.container.innerHTML = '';
261
189
  this.container.appendChild(img);
@@ -289,73 +217,22 @@ class FigureEditableView extends figure_1.FigureView {
289
217
  return count;
290
218
  }
291
219
  manageReactTools() {
292
- let handleDownload;
293
- let handleUpload;
294
- let handleReplace;
295
- let handleDetach;
296
- let handleDelete;
297
- const src = this.node.attrs.src;
298
- const files = this.props.getFiles();
299
- const file = src && files.filter((f) => f.id === src)[0];
220
+ this.reactTools?.remove();
221
+ const handlers = (0, media_1.createFileHandlers)(this.node, this.view, this.getPos, this.props, this.setSrc);
300
222
  const can = this.props.getCapabilities();
301
- if (src) {
302
- if (file) {
303
- handleDownload = () => {
304
- this.props.fileManagement.download(file);
305
- };
306
- }
307
- handleDetach = () => {
308
- this.setSrc('');
309
- };
310
- }
311
- if (can.replaceFile) {
312
- handleReplace = (file, isSupplement = false) => {
313
- this.setSrc(file.id);
314
- if (isSupplement) {
315
- const tr = this.view.state.tr;
316
- this.view.state.doc.descendants((node, pos) => {
317
- if (node.type === node.type.schema.nodes.supplement) {
318
- const href = node.attrs.href;
319
- if (href === file.id) {
320
- tr.delete(pos, pos + node.nodeSize);
321
- this.view.dispatch(tr);
322
- }
323
- }
324
- if (node.type !== node.type.schema.nodes.supplements &&
325
- node.type !== node.type.schema.nodes.manuscript) {
326
- return false;
327
- }
328
- });
329
- }
330
- };
331
- }
332
223
  if (can.uploadFile) {
333
- handleUpload = (0, figure_uploader_1.figureUploader)(this.upload);
224
+ handlers.handleUpload = (0, media_1.createFileUploader)(this.upload, 'image/*');
334
225
  }
335
226
  if (can.detachFile) {
336
- handleDelete = () => {
227
+ handlers.handleDelete = () => {
337
228
  const pos = this.getPos();
338
229
  const tr = this.view.state.tr;
339
230
  tr.delete(pos, pos + this.node.nodeSize);
340
231
  this.view.dispatch(tr);
341
232
  };
342
233
  }
343
- this.reactTools?.remove();
344
- if (this.props.dispatch && this.props.theme) {
345
- const componentProps = {
346
- can,
347
- getDoc: () => this.view.state.doc,
348
- getFiles: this.props.getFiles,
349
- onDownload: handleDownload,
350
- onUpload: handleUpload,
351
- onDetach: handleDetach,
352
- onReplace: handleReplace,
353
- onDelete: handleDelete,
354
- hasSiblings: () => {
355
- return this.countFigures() > 1;
356
- },
357
- };
358
- this.reactTools = (0, ReactSubView_1.default)(this.props, FigureDropdown_1.FigureOptions, componentProps, this.node, this.getPos, this.view);
234
+ this.reactTools = (0, media_1.createReactTools)(this.node, this.view, this.getPos, this.props, handlers, false, () => this.countFigures() > 1);
235
+ if (this.reactTools) {
359
236
  this.dom.insertBefore(this.reactTools, this.dom.firstChild);
360
237
  }
361
238
  }
@@ -21,7 +21,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
21
21
  exports.ImageElementView = exports.figurePositions = void 0;
22
22
  const style_guide_1 = require("@manuscripts/style-guide");
23
23
  const transform_1 = require("@manuscripts/transform");
24
- const icons_1 = require("../icons");
24
+ const position_menu_1 = require("../lib/position-menu");
25
25
  const block_view_1 = __importDefault(require("./block_view"));
26
26
  const creators_1 = require("./creators");
27
27
  const ReactSubView_1 = __importDefault(require("./ReactSubView"));
@@ -35,67 +35,43 @@ class ImageElementView extends block_view_1.default {
35
35
  constructor() {
36
36
  super(...arguments);
37
37
  this.ignoreMutation = () => true;
38
- this.createPositionMenuWrapper = () => {
39
- const can = this.props.getCapabilities();
40
- this.positionMenuWrapper = document.createElement('div');
41
- this.positionMenuWrapper.classList.add('position-menu');
42
- const positionMenuButton = document.createElement('div');
43
- positionMenuButton.classList.add('position-menu-button');
38
+ this.showPositionMenu = () => {
44
39
  const firstFigure = this.getFirstFigure();
45
- this.figurePosition = firstFigure?.attrs.type || figurePositions.default;
46
- let icon;
47
- switch (this.figurePosition) {
48
- case figurePositions.left:
49
- icon = icons_1.imageLeftIcon;
50
- break;
51
- case figurePositions.right:
52
- icon = icons_1.imageRightIcon;
53
- break;
54
- default:
55
- icon = icons_1.imageDefaultIcon;
56
- break;
57
- }
58
- if (icon) {
59
- positionMenuButton.innerHTML = icon;
40
+ if (!firstFigure) {
41
+ return;
60
42
  }
61
- if (can.editArticle) {
62
- positionMenuButton.addEventListener('click', this.showPositionMenu);
63
- }
64
- this.positionMenuWrapper.appendChild(positionMenuButton);
65
- return this.positionMenuWrapper;
66
- };
67
- this.showPositionMenu = () => {
68
43
  this.props.popper.destroy();
69
- const componentProps = {
70
- actions: [
71
- {
72
- label: 'Left',
73
- action: () => {
74
- this.props.popper.destroy();
75
- this.updateAllFiguresPosition(figurePositions.left);
76
- },
77
- icon: 'ImageLeft',
78
- selected: this.figurePosition === figurePositions.left,
44
+ const options = [
45
+ {
46
+ label: 'Left',
47
+ action: () => {
48
+ this.props.popper.destroy();
49
+ this.updateAllFiguresPosition(figurePositions.left);
79
50
  },
80
- {
81
- label: 'Default',
82
- action: () => {
83
- this.props.popper.destroy();
84
- this.updateAllFiguresPosition(figurePositions.default);
85
- },
86
- icon: 'ImageDefault',
87
- selected: !this.figurePosition,
51
+ icon: 'ImageLeft',
52
+ selected: this.figurePosition === figurePositions.left,
53
+ },
54
+ {
55
+ label: 'Default',
56
+ action: () => {
57
+ this.props.popper.destroy();
58
+ this.updateAllFiguresPosition(figurePositions.default);
88
59
  },
89
- {
90
- label: 'Right',
91
- action: () => {
92
- this.props.popper.destroy();
93
- this.updateAllFiguresPosition(figurePositions.right);
94
- },
95
- icon: 'ImageRight',
96
- selected: this.figurePosition === figurePositions.right,
60
+ icon: 'ImageDefault',
61
+ selected: !this.figurePosition,
62
+ },
63
+ {
64
+ label: 'Right',
65
+ action: () => {
66
+ this.props.popper.destroy();
67
+ this.updateAllFiguresPosition(figurePositions.right);
97
68
  },
98
- ],
69
+ icon: 'ImageRight',
70
+ selected: this.figurePosition === figurePositions.right,
71
+ },
72
+ ];
73
+ const componentProps = {
74
+ actions: options,
99
75
  };
100
76
  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);
101
77
  };
@@ -128,7 +104,10 @@ class ImageElementView extends block_view_1.default {
128
104
  if (existingMenu) {
129
105
  existingMenu.remove();
130
106
  }
131
- this.container.prepend(this.createPositionMenuWrapper());
107
+ const firstFigure = this.getFirstFigure();
108
+ this.figurePosition = firstFigure?.attrs.type || figurePositions.default;
109
+ this.positionMenuWrapper = (0, position_menu_1.createPositionMenuWrapper)(this.figurePosition, this.showPositionMenu, this.props);
110
+ this.container.prepend(this.positionMenuWrapper);
132
111
  }
133
112
  }
134
113
  getFirstFigure() {