@manuscripts/body-editor 3.2.34 → 3.2.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.addRows = exports.addInlineComment = exports.addNodeComment = exports.isCommentingAllowed = exports.createAndFillTableElement = exports.selectAllIsolating = exports.ignoreAtomBlockNodeForward = exports.isAtEndOfTextBlock = exports.ignoreMetaNodeBackspaceCommand = exports.ignoreAtomBlockNodeBackward = exports.isTextSelection = exports.isAtStartOfTextBlock = exports.insertTOCSection = exports.insertBibliographySection = exports.insertList = exports.insertKeywords = exports.insertAward = exports.insertAffiliation = exports.insertContributors = exports.insertGraphicalAbstract = exports.insertBackmatterSection = exports.insertAbstractSection = exports.insertSection = exports.insertBoxElement = exports.insertInlineFootnote = exports.insertFootnotesElement = exports.insertTableElementFooter = exports.insertInlineEquation = exports.insertCrossReference = exports.insertInlineCitation = exports.insertLink = exports.insertSectionLabel = exports.findPosBeforeFirstSubsection = exports.insertBreak = exports.deleteBlock = exports.insertBlock = exports.insertAttachment = exports.insertSupplement = exports.insertTable = exports.insertFigure = exports.insertGeneralTableFootnote = exports.insertInlineTableFootnote = exports.insertEmbed = exports.createBlock = exports.createSelection = exports.canInsert = exports.blockActive = exports.isNodeSelection = exports.markActive = exports.addToStart = void 0;
18
+ exports.addRows = exports.addInlineComment = exports.addNodeComment = exports.isCommentingAllowed = exports.createAndFillTableElement = exports.selectAllIsolating = exports.ignoreAtomBlockNodeForward = exports.isAtEndOfTextBlock = exports.ignoreMetaNodeBackspaceCommand = exports.ignoreAtomBlockNodeBackward = exports.isTextSelection = exports.isAtStartOfTextBlock = exports.insertTOCSection = exports.insertBibliographySection = exports.insertList = exports.insertKeywords = exports.insertAward = exports.insertAffiliation = exports.insertContributors = exports.insertGraphicalAbstract = exports.insertBackmatterSection = exports.insertAbstractSection = exports.insertSection = exports.insertBoxElement = exports.insertInlineFootnote = exports.insertFootnotesElement = exports.insertTableElementFooter = exports.insertInlineEquation = exports.insertCrossReference = exports.insertInlineCitation = exports.insertLink = exports.insertSectionLabel = exports.findPosBeforeFirstSubsection = exports.insertBreak = exports.deleteBlock = exports.insertBlock = exports.insertAttachment = exports.insertSupplement = exports.insertTable = exports.insertEmbed = exports.insertFigure = exports.insertGeneralTableFootnote = exports.insertInlineTableFootnote = exports.createBlock = exports.createSelection = exports.canInsert = exports.blockActive = exports.isNodeSelection = exports.markActive = exports.addToStart = void 0;
19
19
  exports.insertHeroImage = exports.activateSearchReplace = exports.activateSearch = exports.autoComplete = exports.addColumns = exports.addHeaderRow = void 0;
20
20
  const json_schema_1 = require("@manuscripts/json-schema");
21
21
  const track_changes_plugin_1 = require("@manuscripts/track-changes-plugin");
@@ -195,15 +195,6 @@ const createBlock = (nodeType, position, state, dispatch, attrs) => {
195
195
  }
196
196
  };
197
197
  exports.createBlock = createBlock;
198
- const insertEmbed = (state, dispatch, attrs) => {
199
- const position = findBlockInsertPosition(state);
200
- if (position === null) {
201
- return false;
202
- }
203
- (0, exports.createBlock)(transform_1.schema.nodes.embed, position, state, dispatch, attrs);
204
- return true;
205
- };
206
- exports.insertEmbed = insertEmbed;
207
198
  const insertInlineTableFootnote = (state, dispatch) => {
208
199
  const $pos = state.selection.$to;
209
200
  const table = (0, prosemirror_utils_1.findParentNodeOfTypeClosestToPos)($pos, transform_1.schema.nodes.table);
@@ -257,6 +248,24 @@ const insertFigure = (file, state, dispatch) => {
257
248
  return true;
258
249
  };
259
250
  exports.insertFigure = insertFigure;
251
+ const insertEmbed = (state, dispatch, attrs) => {
252
+ const position = findBlockInsertPosition(state);
253
+ if (position === null) {
254
+ return false;
255
+ }
256
+ const embed = transform_1.schema.nodes.embed.create({
257
+ ...attrs,
258
+ id: (0, transform_1.generateNodeID)(transform_1.schema.nodes.embed),
259
+ }, [
260
+ createAndFillFigcaptionElement(),
261
+ transform_1.schema.nodes.alt_text.create(),
262
+ transform_1.schema.nodes.long_desc.create(),
263
+ ]);
264
+ const tr = state.tr.insert(position, embed);
265
+ dispatch && dispatch(tr);
266
+ return true;
267
+ };
268
+ exports.insertEmbed = insertEmbed;
260
269
  const insertTable = (config, state, dispatch) => {
261
270
  const pos = findBlockInsertPosition(state);
262
271
  if (!pos) {
@@ -95,7 +95,7 @@ const InsertEmbedDialog = ({ state, dispatch, pos, }) => {
95
95
  setOEmbedHTML(html);
96
96
  });
97
97
  }, [url]);
98
- const operation = pos ? 'Update' : 'Insert';
98
+ const operation = pos !== undefined && attrs?.href ? 'Update' : 'Insert';
99
99
  return (react_1.default.createElement(style_guide_1.StyledModal, { isOpen: isOpen, onRequestClose: () => setOpen(false) },
100
100
  react_1.default.createElement(DialogContainer, { "data-cy": "media-editor" },
101
101
  react_1.default.createElement(HeaderContainer, null,
@@ -31,20 +31,35 @@ const style_guide_1 = require("@manuscripts/style-guide");
31
31
  const react_1 = __importStar(require("react"));
32
32
  const styled_components_1 = __importDefault(require("styled-components"));
33
33
  const files_1 = require("../../lib/files");
34
- function getSupplements(getFiles, getDoc, groupFiles) {
34
+ const get_media_type_1 = require("../../lib/get-media-type");
35
+ function getSupplements(getFiles, getDoc, groupFiles, isEmbed, currentFileHref) {
35
36
  return groupFiles(getDoc(), getFiles())
36
37
  .supplements.map((s) => s.file)
37
- .filter((f) => (0, style_guide_1.isImageFile)(f.name));
38
+ .filter((f) => {
39
+ if (currentFileHref && f.id === currentFileHref) {
40
+ return false;
41
+ }
42
+ const mediaInfo = (0, get_media_type_1.getMediaTypeInfo)(f.name);
43
+ return isEmbed
44
+ ? mediaInfo.isVideo || mediaInfo.isAudio
45
+ : mediaInfo.isImage;
46
+ });
38
47
  }
39
- function getOtherFiles(getFiles, getDoc, groupFiles) {
40
- return groupFiles(getDoc(), getFiles()).others.filter((f) => (0, style_guide_1.isImageFile)(f.name));
48
+ function getOtherFiles(getFiles, getDoc, groupFiles, isEmbed, currentFileHref) {
49
+ return groupFiles(getDoc(), getFiles()).others.filter((f) => {
50
+ if (currentFileHref && f.id === currentFileHref) {
51
+ return false;
52
+ }
53
+ const mediaInfo = (0, get_media_type_1.getMediaTypeInfo)(f.name);
54
+ return isEmbed ? mediaInfo.isVideo || mediaInfo.isAudio : mediaInfo.isImage;
55
+ });
41
56
  }
42
- const FigureOptions = ({ can, getDoc, getFiles, onDownload, onUpload, onDetach, onReplace, onDelete, hasSiblings, container, }) => {
57
+ const FigureOptions = ({ can, getDoc, getFiles, onDownload, onUpload, onDetach, onReplace, onReplaceEmbed, onDelete, isEmbed, hasSiblings, container, currentFileHref, }) => {
43
58
  const { isOpen, toggleOpen, wrapperRef } = (0, style_guide_1.useDropdown)();
44
59
  const showDownload = onDownload && can.downloadFiles;
45
60
  const showUpload = onUpload && can.uploadFile;
46
61
  const showDetach = onDetach && can.detachFile;
47
- const showReplace = onReplace && can.replaceFile;
62
+ const showReplace = (onReplace && can.replaceFile) || (onReplaceEmbed && can.editArticle);
48
63
  const replaceBtnText = onDownload ? 'Replace' : 'Choose file';
49
64
  const showDelete = () => {
50
65
  if (!hasSiblings()) {
@@ -64,23 +79,25 @@ const FigureOptions = ({ can, getDoc, getFiles, onDownload, onUpload, onDetach,
64
79
  container.classList.remove(activeClass);
65
80
  }
66
81
  }, [isOpen, container.classList]);
82
+ const isEmbedMode = !!onReplaceEmbed;
67
83
  const groupFiles = (0, files_1.memoGroupFiles)();
68
84
  return (react_1.default.createElement(DropdownWrapper, { ref: wrapperRef },
69
85
  react_1.default.createElement(OptionsButton, { className: 'options-button', onClick: toggleOpen },
70
86
  react_1.default.createElement(style_guide_1.DotsIcon, null)),
71
87
  isOpen && (react_1.default.createElement(OptionsDropdownList, { direction: 'right', width: 128, top: 5 },
72
- react_1.default.createElement(NestedDropdown, { disabled: !showReplace, parentToggleOpen: toggleOpen, buttonText: replaceBtnText, moveLeft: true, list: react_1.default.createElement(react_1.default.Fragment, null,
73
- getSupplements(getFiles, getDoc, groupFiles).map((file, index) => (react_1.default.createElement(ListItemButton, { key: file.id, id: index.toString(), onClick: () => onReplace && onReplace(file, true) },
88
+ showReplace && isEmbedMode && (react_1.default.createElement(ListItemButton, { onClick: () => onReplaceEmbed && onReplaceEmbed() }, "Edit Link")),
89
+ showReplace && !isEmbedMode && (react_1.default.createElement(NestedDropdown, { disabled: !showReplace, parentToggleOpen: toggleOpen, buttonText: replaceBtnText, moveLeft: true, list: react_1.default.createElement(react_1.default.Fragment, null,
90
+ getSupplements(getFiles, getDoc, groupFiles, isEmbed, currentFileHref).map((file, index) => (react_1.default.createElement(ListItemButton, { key: file.id, id: index.toString(), onClick: () => onReplace && onReplace(file, true) },
74
91
  (0, style_guide_1.getFileIcon)(file.name),
75
92
  react_1.default.createElement(ListItemText, null, file.name)))),
76
- getOtherFiles(getFiles, getDoc, groupFiles).map((file, index) => (react_1.default.createElement(ListItemButton, { key: file.id, id: index.toString(), onClick: () => onReplace && onReplace(file) },
93
+ getOtherFiles(getFiles, getDoc, groupFiles, isEmbed, currentFileHref).map((file, index) => (react_1.default.createElement(ListItemButton, { key: file.id, id: index.toString(), onClick: () => onReplace && onReplace(file) },
77
94
  (0, style_guide_1.getFileIcon)(file.name),
78
95
  react_1.default.createElement(ListItemText, null, file.name)))),
79
- react_1.default.createElement(UploadButton, { onClick: onUpload, disabled: !showUpload },
96
+ showUpload && (react_1.default.createElement(UploadButton, { onClick: onUpload, disabled: !showUpload },
80
97
  react_1.default.createElement(style_guide_1.UploadIcon, null),
81
- " Upload new...")) }),
82
- react_1.default.createElement(ListItemButton, { onClick: onDownload, disabled: !showDownload }, "Download"),
83
- react_1.default.createElement(ListItemButton, { onClick: onDetach, disabled: !showDetach }, "Detach"),
98
+ " Upload new..."))) })),
99
+ showDownload && (react_1.default.createElement(ListItemButton, { onClick: onDownload, disabled: !showDownload }, "Download")),
100
+ showDetach && (react_1.default.createElement(ListItemButton, { onClick: onDetach, disabled: !showDetach }, "Detach")),
84
101
  showDelete() && (react_1.default.createElement(ListItemButton, { onClick: onDelete }, "Delete"))))));
85
102
  };
86
103
  exports.FigureOptions = FigureOptions;
@@ -61,7 +61,7 @@ exports.default = (props, dispatch) => {
61
61
  cross_reference: (0, cross_reference_editable_1.default)(props, dispatch),
62
62
  contributors: (0, contributors_1.default)(props, dispatch),
63
63
  affiliations: (0, affiliations_1.default)(props, dispatch),
64
- embed: (0, embed_1.default)(props),
64
+ embed: (0, embed_1.default)(props, dispatch),
65
65
  equation: (0, equation_editable_1.default)(props),
66
66
  equation_element: (0, equation_element_editable_1.default)(props),
67
67
  figure: (0, figure_editable_1.default)(props, dispatch),
package/dist/cjs/index.js CHANGED
@@ -49,6 +49,7 @@ __exportStar(require("./lib/comments"), exports);
49
49
  __exportStar(require("./lib/files"), exports);
50
50
  __exportStar(require("./lib/footnotes"), exports);
51
51
  __exportStar(require("./lib/doc"), exports);
52
+ __exportStar(require("./lib/media"), exports);
52
53
  __exportStar(require("./plugins/comments"), exports);
53
54
  var selected_suggestion_1 = require("./plugins/selected-suggestion");
54
55
  Object.defineProperty(exports, "selectedSuggestionKey", { enumerable: true, get: function () { return selected_suggestion_1.selectedSuggestionKey; } });
@@ -57,6 +58,7 @@ __exportStar(require("./lib/utils"), exports);
57
58
  __exportStar(require("./lib/track-changes-utils"), exports);
58
59
  __exportStar(require("./useEditor"), exports);
59
60
  __exportStar(require("./lib/math"), exports);
61
+ __exportStar(require("./lib/get-media-type"), exports);
60
62
  var objects_1 = require("./plugins/objects");
61
63
  Object.defineProperty(exports, "objectsPluginKey", { enumerable: true, get: function () { return objects_1.objectsKey; } });
62
64
  var footnotes_1 = require("./plugins/footnotes");
@@ -22,10 +22,9 @@ const prosemirror_utils_1 = require("prosemirror-utils");
22
22
  const react_1 = require("react");
23
23
  const server_1 = require("react-dom/server");
24
24
  const commands_1 = require("../commands");
25
- const image_element_1 = require("../views/image_element");
26
25
  const popper_1 = require("./popper");
26
+ const position_menu_1 = require("./position-menu");
27
27
  const utils_1 = require("./utils");
28
- const view_1 = require("./view");
29
28
  const popper = new popper_1.PopperManager();
30
29
  const readonlyTypes = [
31
30
  transform_1.schema.nodes.keywords,
@@ -159,35 +158,7 @@ class ContextMenu {
159
158
  const figure = (0, utils_1.getMatchingChild)(this.node, (node) => node.type === transform_1.schema.nodes.figure);
160
159
  if (figure) {
161
160
  const attrType = figure.attrs.type;
162
- const submenuOptions = [
163
- {
164
- title: 'Left',
165
- action: () => (0, view_1.updateNodeAttrs)(this.view, transform_1.schema.nodes.figure, {
166
- ...figure.attrs,
167
- type: image_element_1.figurePositions.left,
168
- }),
169
- Icon: style_guide_1.ImageLeftIcon,
170
- selected: attrType === image_element_1.figurePositions.left,
171
- },
172
- {
173
- title: 'Default',
174
- action: () => (0, view_1.updateNodeAttrs)(this.view, transform_1.schema.nodes.figure, {
175
- ...figure.attrs,
176
- type: image_element_1.figurePositions.default,
177
- }),
178
- Icon: style_guide_1.ImageDefaultIcon,
179
- selected: !attrType,
180
- },
181
- {
182
- title: 'Right',
183
- action: () => (0, view_1.updateNodeAttrs)(this.view, transform_1.schema.nodes.figure, {
184
- ...figure.attrs,
185
- type: image_element_1.figurePositions.right,
186
- }),
187
- Icon: style_guide_1.ImageRightIcon,
188
- selected: attrType === image_element_1.figurePositions.right,
189
- },
190
- ];
161
+ const submenuOptions = (0, position_menu_1.createPositionOptions)(transform_1.schema.nodes.figure, figure, attrType, this.view);
191
162
  const submenuLabel = 'Position';
192
163
  const submenu = this.createSubmenu(submenuLabel, submenuOptions);
193
164
  menu.appendChild(submenu);
@@ -314,12 +285,17 @@ class ContextMenu {
314
285
  item.addEventListener('mousedown', this.toggleSubmenu);
315
286
  return item;
316
287
  };
317
- this.createMenuItem = (contents, handler, Icon = null, selected = false) => {
288
+ this.createMenuItem = (contents, handler, IconComponent = null, selected = false) => {
318
289
  const item = document.createElement('div');
319
290
  item.className = 'menu-item';
320
291
  selected && item.classList.add('selected');
321
- if (Icon) {
322
- item.innerHTML = (0, server_1.renderToStaticMarkup)((0, react_1.createElement)(Icon));
292
+ if (IconComponent) {
293
+ if (typeof IconComponent === 'string') {
294
+ item.innerHTML = IconComponent;
295
+ }
296
+ else {
297
+ item.innerHTML = (0, server_1.renderToStaticMarkup)((0, react_1.createElement)(IconComponent));
298
+ }
323
299
  }
324
300
  const textNode = document.createTextNode(contents);
325
301
  item.appendChild(textNode);
@@ -340,8 +316,8 @@ class ContextMenu {
340
316
  const submenu = document.createElement('div');
341
317
  submenu.classList.add('context-submenu');
342
318
  submenu.append(this.createSubmenuTrigger(submenuLabel), this.createMenuSection((section) => {
343
- items.forEach(({ title, action, Icon, selected }) => {
344
- section.appendChild(this.createMenuItem(title, action, Icon, selected));
319
+ items.forEach(({ title, action, IconComponent, selected }) => {
320
+ section.appendChild(this.createMenuItem(title, action, IconComponent, selected));
345
321
  });
346
322
  }, true));
347
323
  return submenu;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getMediaTypeInfo = void 0;
4
+ const getMediaTypeInfo = (filenameOrFile) => {
5
+ const filename = typeof filenameOrFile === 'string'
6
+ ? filenameOrFile
7
+ : filenameOrFile?.name || '';
8
+ const extension = filename.toLowerCase().split('.').pop()?.trim() || '';
9
+ const videoExtensions = new Set([
10
+ 'mp4',
11
+ 'webm',
12
+ 'avi',
13
+ 'mov',
14
+ 'mkv',
15
+ 'flv',
16
+ 'wmv',
17
+ 'mpg',
18
+ 'mpeg',
19
+ ]);
20
+ const audioExtensions = new Set([
21
+ 'mp3',
22
+ 'wav',
23
+ 'aac',
24
+ 'ogg',
25
+ 'flac',
26
+ 'm4a',
27
+ 'wma',
28
+ ]);
29
+ const imageExtensions = new Set([
30
+ 'jpg',
31
+ 'jpeg',
32
+ 'png',
33
+ 'gif',
34
+ 'bmp',
35
+ 'svg',
36
+ 'webp',
37
+ 'tiff',
38
+ 'tif',
39
+ ]);
40
+ let mimetype;
41
+ let mimeSubtype;
42
+ if (typeof filenameOrFile !== 'string' && filenameOrFile?.type) {
43
+ const [type, subtype] = filenameOrFile.type.split('/');
44
+ if (type && subtype && ['video', 'audio', 'image'].includes(type)) {
45
+ mimetype = type;
46
+ mimeSubtype = subtype;
47
+ }
48
+ }
49
+ if (!mimetype || !mimeSubtype) {
50
+ if (videoExtensions.has(extension)) {
51
+ mimetype = 'video';
52
+ mimeSubtype = extension === 'mov' ? 'quicktime' : extension;
53
+ }
54
+ else if (audioExtensions.has(extension)) {
55
+ mimetype = 'audio';
56
+ mimeSubtype = extension === 'm4a' ? 'mp4' : extension;
57
+ }
58
+ else if (imageExtensions.has(extension)) {
59
+ mimetype = 'image';
60
+ mimeSubtype = extension === 'jpg' ? 'jpeg' : extension;
61
+ }
62
+ }
63
+ const isVideo = mimetype === 'video' || videoExtensions.has(extension);
64
+ const isAudio = mimetype === 'audio' || audioExtensions.has(extension);
65
+ const isImage = mimetype === 'image' || imageExtensions.has(extension);
66
+ const isSupported = isVideo || isAudio || isImage;
67
+ return {
68
+ extension,
69
+ isVideo,
70
+ isAudio,
71
+ isImage,
72
+ isSupported,
73
+ mimetype,
74
+ mimeSubtype,
75
+ };
76
+ };
77
+ exports.getMediaTypeInfo = getMediaTypeInfo;
@@ -0,0 +1,207 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.addInteractionHandlers = exports.createFileUploader = exports.createReactTools = exports.createFileHandlers = exports.createMediaPlaceholder = exports.createUnsupportedFormat = void 0;
22
+ const transform_1 = require("@manuscripts/transform");
23
+ const InsertEmbedDialog_1 = require("../components/toolbar/InsertEmbedDialog");
24
+ const FigureDropdown_1 = require("../components/views/FigureDropdown");
25
+ const icons_1 = require("../icons");
26
+ const ReactSubView_1 = __importDefault(require("../views/ReactSubView"));
27
+ const createUnsupportedFormat = (filename, canEdit) => {
28
+ const element = document.createElement('div');
29
+ element.classList.add('figure', 'placeholder');
30
+ const instructions = document.createElement('div');
31
+ instructions.classList.add('instructions');
32
+ const iconHtml = icons_1.fileCorruptedIcon;
33
+ instructions.innerHTML = `
34
+ <div>
35
+ <div class="unsupported-icon-wrapper">${iconHtml}</div>
36
+ <div>${filename}</div>
37
+ <div class="unsupported-format-label">
38
+ Unsupported file format
39
+ </div>
40
+ <div>
41
+ ${canEdit ? 'Click to add media' : 'No media here yet…'}
42
+ </div>
43
+ </div>
44
+ `;
45
+ element.appendChild(instructions);
46
+ return element;
47
+ };
48
+ exports.createUnsupportedFormat = createUnsupportedFormat;
49
+ const createMediaPlaceholder = (mediaType = 'media', view, getPos) => {
50
+ const element = document.createElement('div');
51
+ element.classList.add('figure', 'placeholder');
52
+ const instructions = document.createElement('div');
53
+ instructions.classList.add('instructions');
54
+ const uploadText = mediaType === 'media' ? 'media' : 'image';
55
+ instructions.innerHTML = `
56
+ <div class="placeholder-content">
57
+ <p>Drag or click here to upload ${uploadText} <br>
58
+ or drag items here from the file inspector tabs <br>
59
+ <a data-action='open-other-files'>'Other files'</a> |
60
+ <a data-action='open-supplement-files'>'Supplements'</a>
61
+ ${mediaType === 'media' && view && getPos
62
+ ? "| <a data-action='add-external-link'>'External link'</a>"
63
+ : ''}
64
+ </p>
65
+ </div>
66
+ `;
67
+ if (mediaType === 'media' && view && getPos) {
68
+ const embedLink = instructions.querySelector("[data-action='add-external-link']");
69
+ if (embedLink) {
70
+ embedLink.addEventListener('click', (e) => {
71
+ e.stopPropagation();
72
+ e.preventDefault();
73
+ (0, InsertEmbedDialog_1.openEmbedDialog)(view, getPos());
74
+ });
75
+ }
76
+ }
77
+ element.appendChild(instructions);
78
+ return element;
79
+ };
80
+ exports.createMediaPlaceholder = createMediaPlaceholder;
81
+ const createFileHandlers = (node, view, getPos, props, setHref) => {
82
+ const handlers = {};
83
+ const href = node.attrs.href;
84
+ const files = props.getFiles();
85
+ const file = href && files.find((f) => f.id === href);
86
+ const can = props.getCapabilities();
87
+ if (href && file) {
88
+ handlers.handleDownload = () => {
89
+ props.fileManagement.download(file);
90
+ };
91
+ handlers.handleDetach = () => {
92
+ setHref('');
93
+ };
94
+ }
95
+ if (can.replaceFile) {
96
+ handlers.handleReplace = (file, isSupplement = false) => {
97
+ setHref(file.id);
98
+ if (isSupplement) {
99
+ const tr = view.state.tr;
100
+ view.state.doc.descendants((node, pos) => {
101
+ if (node.type === transform_1.schema.nodes.supplement) {
102
+ const href = node.attrs.href;
103
+ if (href === file.id) {
104
+ tr.delete(pos, pos + node.nodeSize);
105
+ view.dispatch(tr);
106
+ }
107
+ }
108
+ if (node.type !== transform_1.schema.nodes.supplements &&
109
+ node.type !== transform_1.schema.nodes.manuscript) {
110
+ return false;
111
+ }
112
+ });
113
+ }
114
+ };
115
+ }
116
+ return handlers;
117
+ };
118
+ exports.createFileHandlers = createFileHandlers;
119
+ const createReactTools = (node, view, getPos, props, handlers, isEmbed, hasSiblings) => {
120
+ if (!props.dispatch || !props.theme) {
121
+ return null;
122
+ }
123
+ const can = props.getCapabilities();
124
+ const currentFileHref = node.attrs.href;
125
+ const componentProps = {
126
+ can,
127
+ getDoc: () => view.state.doc,
128
+ getFiles: props.getFiles,
129
+ onDownload: handlers.handleDownload,
130
+ onUpload: handlers.handleUpload,
131
+ onDetach: handlers.handleDetach,
132
+ onReplace: handlers.handleReplace,
133
+ onReplaceEmbed: handlers.handleReplaceEmbed,
134
+ onDelete: handlers.handleDelete,
135
+ isEmbed,
136
+ hasSiblings,
137
+ currentFileHref,
138
+ };
139
+ return (0, ReactSubView_1.default)(props, FigureDropdown_1.FigureOptions, componentProps, node, getPos, view);
140
+ };
141
+ exports.createReactTools = createReactTools;
142
+ const createFileUploader = (handler, accept = '*/*') => {
143
+ const handleFileChange = async (e) => {
144
+ const target = e.target;
145
+ if (target && target.files && target.files.length) {
146
+ await handler(target.files[0]);
147
+ }
148
+ };
149
+ const input = document.createElement('input');
150
+ input.accept = accept;
151
+ input.type = 'file';
152
+ input.addEventListener('change', handleFileChange);
153
+ return () => input.click();
154
+ };
155
+ exports.createFileUploader = createFileUploader;
156
+ const addInteractionHandlers = (element, uploadFn, accept = '*/*') => {
157
+ const handlePlaceholderClick = (event) => {
158
+ const target = event.target;
159
+ if (target.dataset && target.dataset.action) {
160
+ return;
161
+ }
162
+ const input = document.createElement('input');
163
+ input.type = 'file';
164
+ input.accept = accept;
165
+ input.addEventListener('change', async (e) => {
166
+ const files = e.target.files;
167
+ if (files && files.length > 0) {
168
+ await uploadFn(files[0]);
169
+ }
170
+ });
171
+ input.click();
172
+ };
173
+ element.addEventListener('click', handlePlaceholderClick);
174
+ element.addEventListener('mouseenter', () => {
175
+ element.classList.toggle('over', true);
176
+ });
177
+ element.addEventListener('mouseleave', () => {
178
+ element.classList.toggle('over', false);
179
+ });
180
+ element.addEventListener('dragenter', (event) => {
181
+ event.preventDefault();
182
+ element.classList.toggle('over', true);
183
+ });
184
+ element.addEventListener('dragleave', (event) => {
185
+ event.preventDefault();
186
+ element.classList.toggle('over', false);
187
+ });
188
+ element.addEventListener('dragover', (e) => {
189
+ const dragEvent = e;
190
+ if (dragEvent.dataTransfer && dragEvent.dataTransfer.items) {
191
+ for (const item of dragEvent.dataTransfer.items) {
192
+ if (item.kind === 'file') {
193
+ e.preventDefault();
194
+ dragEvent.dataTransfer.dropEffect = 'copy';
195
+ break;
196
+ }
197
+ }
198
+ }
199
+ });
200
+ element.addEventListener('drop', async (e) => {
201
+ if (e.dataTransfer && e.dataTransfer.files.length) {
202
+ e.preventDefault();
203
+ await uploadFn(e.dataTransfer.files[0]);
204
+ }
205
+ });
206
+ };
207
+ exports.addInteractionHandlers = addInteractionHandlers;
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ /*!
3
+ * © 2025 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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.createPositionMenuWrapper = exports.setElementPositionAlignment = exports.showPositionMenu = exports.createPopperMenuPositionOptions = exports.createPositionOptions = void 0;
22
+ const style_guide_1 = require("@manuscripts/style-guide");
23
+ const icons_1 = require("../icons");
24
+ const image_element_1 = require("../views/image_element");
25
+ const ReactSubView_1 = __importDefault(require("../views/ReactSubView"));
26
+ const view_1 = require("./view");
27
+ const createPositionOptions = (nodeType, node, currentPosition, view, onComplete) => {
28
+ const createAction = (position) => () => {
29
+ onComplete?.();
30
+ (0, view_1.updateNodeAttrs)(view, nodeType, {
31
+ ...node.attrs,
32
+ type: position,
33
+ });
34
+ };
35
+ return [
36
+ {
37
+ title: 'Left',
38
+ action: createAction(image_element_1.figurePositions.left),
39
+ IconComponent: icons_1.imageLeftIcon,
40
+ iconName: 'ImageLeft',
41
+ selected: currentPosition === image_element_1.figurePositions.left,
42
+ },
43
+ {
44
+ title: 'Center',
45
+ action: createAction(image_element_1.figurePositions.default),
46
+ IconComponent: icons_1.imageDefaultIcon,
47
+ iconName: 'ImageDefault',
48
+ selected: !currentPosition,
49
+ },
50
+ {
51
+ title: 'Right',
52
+ action: createAction(image_element_1.figurePositions.right),
53
+ IconComponent: icons_1.imageRightIcon,
54
+ iconName: 'ImageRight',
55
+ selected: currentPosition === image_element_1.figurePositions.right,
56
+ },
57
+ ];
58
+ };
59
+ exports.createPositionOptions = createPositionOptions;
60
+ const createPopperMenuPositionOptions = (nodeType, node, currentPosition, view, onComplete) => {
61
+ return (0, exports.createPositionOptions)(nodeType, node, currentPosition, view, onComplete).map((option) => ({
62
+ label: option.title,
63
+ action: option.action,
64
+ icon: option.iconName,
65
+ selected: option.selected,
66
+ }));
67
+ };
68
+ exports.createPopperMenuPositionOptions = createPopperMenuPositionOptions;
69
+ const showPositionMenu = (nodeType, node, currentPosition, positionMenuWrapper, view, getPos, props) => {
70
+ props.popper.destroy();
71
+ const options = (0, exports.createPopperMenuPositionOptions)(nodeType, node, currentPosition, view, () => props.popper.destroy());
72
+ const componentProps = {
73
+ actions: options,
74
+ };
75
+ props.popper.show(positionMenuWrapper, (0, ReactSubView_1.default)(props, style_guide_1.ContextMenu, componentProps, node, getPos, view, [
76
+ 'context-menu',
77
+ 'position-menu',
78
+ ]), 'left', false);
79
+ };
80
+ exports.showPositionMenu = showPositionMenu;
81
+ const setElementPositionAlignment = (element, position) => {
82
+ switch (position) {
83
+ case image_element_1.figurePositions.left:
84
+ element.setAttribute('data-alignment', 'left');
85
+ break;
86
+ case image_element_1.figurePositions.right:
87
+ element.setAttribute('data-alignment', 'right');
88
+ break;
89
+ default:
90
+ element.removeAttribute('data-alignment');
91
+ break;
92
+ }
93
+ };
94
+ exports.setElementPositionAlignment = setElementPositionAlignment;
95
+ const createPositionMenuWrapper = (currentPosition, onClick, props) => {
96
+ const can = props.getCapabilities();
97
+ const positionMenuWrapper = document.createElement('div');
98
+ positionMenuWrapper.classList.add('position-menu');
99
+ const positionMenuButton = document.createElement('div');
100
+ positionMenuButton.classList.add('position-menu-button');
101
+ let icon;
102
+ switch (currentPosition) {
103
+ case image_element_1.figurePositions.left:
104
+ icon = icons_1.imageLeftIcon;
105
+ break;
106
+ case image_element_1.figurePositions.right:
107
+ icon = icons_1.imageRightIcon;
108
+ break;
109
+ default:
110
+ icon = icons_1.imageDefaultIcon;
111
+ break;
112
+ }
113
+ if (icon) {
114
+ positionMenuButton.innerHTML = icon;
115
+ }
116
+ if (can.editArticle) {
117
+ positionMenuButton.addEventListener('click', onClick);
118
+ }
119
+ positionMenuWrapper.appendChild(positionMenuButton);
120
+ return positionMenuWrapper;
121
+ };
122
+ exports.createPositionMenuWrapper = createPositionMenuWrapper;