@manuscripts/body-editor 3.5.12 → 3.5.13

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.
package/dist/cjs/icons.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fileMainDocumentIcon = exports.translateIcon = exports.draggableIcon = exports.fileCorruptedIcon = exports.imageDefaultIcon = exports.imageLeftIcon = exports.imageRightIcon = exports.addBtnIcon = exports.plusIcon = exports.lockIcon = exports.scrollIcon = exports.sectionCategoryIcon = exports.editIcon = exports.deleteIcon = exports.alertIcon = exports.arrowUp = exports.arrowDown = exports.addAuthorIcon = void 0;
3
+ exports.fileMainDocumentIcon = exports.linkIcon = exports.translateIcon = exports.draggableIcon = exports.fileCorruptedIcon = exports.imageDefaultIcon = exports.imageLeftIcon = exports.imageRightIcon = exports.addBtnIcon = exports.plusIcon = exports.lockIcon = exports.scrollIcon = exports.sectionCategoryIcon = exports.editIcon = exports.deleteIcon = exports.alertIcon = exports.arrowUp = exports.arrowDown = exports.addAuthorIcon = void 0;
4
4
  const style_guide_1 = require("@manuscripts/style-guide");
5
5
  const react_1 = require("react");
6
6
  const server_1 = require("react-dom/server");
@@ -22,4 +22,5 @@ exports.imageDefaultIcon = (0, server_1.renderToStaticMarkup)((0, react_1.create
22
22
  exports.fileCorruptedIcon = (0, server_1.renderToStaticMarkup)((0, react_1.createElement)(style_guide_1.FileCorruptedIcon));
23
23
  exports.draggableIcon = (0, server_1.renderToStaticMarkup)((0, react_1.createElement)(style_guide_1.DraggableIcon));
24
24
  exports.translateIcon = (0, server_1.renderToStaticMarkup)((0, react_1.createElement)(style_guide_1.TranslateIcon));
25
+ exports.linkIcon = (0, server_1.renderToStaticMarkup)((0, react_1.createElement)(style_guide_1.LinkIcon));
25
26
  exports.fileMainDocumentIcon = (0, server_1.renderToStaticMarkup)((0, react_1.createElement)(style_guide_1.FileMainDocumentIcon));
@@ -18,7 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
18
  return (mod && mod.__esModule) ? mod : { "default": mod };
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.addInteractionHandlers = exports.createFileUploader = exports.createReactTools = exports.createFileHandlers = exports.createMediaPlaceholder = exports.createUnsupportedFormat = void 0;
21
+ exports.addInteractionHandlers = exports.createFileUploader = exports.createReactTools = exports.createFileHandlers = exports.createMediaPlaceholder = exports.MediaType = exports.createUnsupportedFormat = void 0;
22
22
  const transform_1 = require("@manuscripts/transform");
23
23
  const InsertEmbedDialog_1 = require("../components/toolbar/InsertEmbedDialog");
24
24
  const FigureDropdown_1 = require("../components/views/FigureDropdown");
@@ -46,25 +46,38 @@ const createUnsupportedFormat = (filename, canEdit) => {
46
46
  return element;
47
47
  };
48
48
  exports.createUnsupportedFormat = createUnsupportedFormat;
49
- const createMediaPlaceholder = (mediaType = 'media', view, getPos) => {
49
+ var MediaType;
50
+ (function (MediaType) {
51
+ MediaType["Media"] = "media";
52
+ MediaType["Figure"] = "figure";
53
+ MediaType["ExternalLink"] = "external_link";
54
+ })(MediaType || (exports.MediaType = MediaType = {}));
55
+ const MediaLabels = {
56
+ [MediaType.Media]: 'media',
57
+ [MediaType.Figure]: 'figure',
58
+ [MediaType.ExternalLink]: 'a file to link',
59
+ };
60
+ const createMediaPlaceholder = (mediaType = MediaType.Media, view, getPos) => {
50
61
  const element = document.createElement('div');
51
62
  element.classList.add('figure', 'placeholder');
52
63
  const instructions = document.createElement('div');
53
64
  instructions.classList.add('instructions');
54
- const uploadText = mediaType === 'media' ? 'media' : 'image';
55
65
  instructions.innerHTML = `
56
66
  <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>
67
+ <p>Drag or click here to upload ${MediaLabels[mediaType]} <br>
68
+ ${mediaType === MediaType.ExternalLink
69
+ ? ` or drag items here from the file <a data-action='open-other-files'>'Other files'</a> in the inspector.</p>`
70
+ : `or drag items here from the file inspector tabs <br>
59
71
  <a data-action='open-other-files'>'Other files'</a> |
60
- <a data-action='open-supplement-files'>'Supplements'</a>
61
- ${mediaType === 'media' && view && getPos
72
+ <a data-action='open-supplement-files'>'Supplements'</a>`}
73
+
74
+ ${mediaType === MediaType.Media && view && getPos
62
75
  ? "| <a data-action='add-external-link'>'External link'</a>"
63
76
  : ''}
64
77
  </p>
65
78
  </div>
66
79
  `;
67
- if (mediaType === 'media' && view && getPos) {
80
+ if (mediaType === MediaType.Media && view && getPos) {
68
81
  const embedLink = instructions.querySelector("[data-action='add-external-link']");
69
82
  if (embedLink) {
70
83
  embedLink.addEventListener('click', (e) => {
@@ -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.5.12';
4
+ exports.VERSION = '3.5.13';
5
5
  exports.MATHJAX_VERSION = '3.2.2';
@@ -164,7 +164,7 @@ class EmbedView extends block_view_1.default {
164
164
  const href = this.node.attrs.href;
165
165
  let object;
166
166
  if (!href) {
167
- object = (0, media_1.createMediaPlaceholder)('media', this.view, this.getPos);
167
+ object = (0, media_1.createMediaPlaceholder)(media_1.MediaType.Media, this.view, this.getPos);
168
168
  }
169
169
  else if (this.isUploadedFile()) {
170
170
  const files = this.props.getFiles();
@@ -178,14 +178,14 @@ class EmbedView extends block_view_1.default {
178
178
  : (0, media_1.createUnsupportedFormat)(file.name, this.props.getCapabilities().editArticle);
179
179
  }
180
180
  else {
181
- object = (0, media_1.createMediaPlaceholder)('media', this.view, this.getPos);
181
+ object = (0, media_1.createMediaPlaceholder)(media_1.MediaType.Media, this.view, this.getPos);
182
182
  }
183
183
  }
184
184
  else if (this.isEmbedLink()) {
185
185
  object = await this.createEmbedPreview();
186
186
  }
187
187
  else {
188
- object = (0, media_1.createMediaPlaceholder)('media', this.view, this.getPos);
188
+ object = (0, media_1.createMediaPlaceholder)(media_1.MediaType.Media, this.view, this.getPos);
189
189
  }
190
190
  const can = this.props.getCapabilities();
191
191
  if (can.uploadFile && object.classList.contains('placeholder')) {
@@ -53,7 +53,7 @@ class FigureEditableView extends figure_1.FigureView {
53
53
  return img;
54
54
  };
55
55
  this.createPlaceholder = () => {
56
- return (0, media_1.createMediaPlaceholder)('figure');
56
+ return (0, media_1.createMediaPlaceholder)(media_1.MediaType.Figure);
57
57
  };
58
58
  }
59
59
  initialise() {
@@ -14,39 +14,6 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
- if (k2 === undefined) k2 = k;
19
- var desc = Object.getOwnPropertyDescriptor(m, k);
20
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
- desc = { enumerable: true, get: function() { return m[k]; } };
22
- }
23
- Object.defineProperty(o, k2, desc);
24
- }) : (function(o, m, k, k2) {
25
- if (k2 === undefined) k2 = k;
26
- o[k2] = m[k];
27
- }));
28
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
- Object.defineProperty(o, "default", { enumerable: true, value: v });
30
- }) : function(o, v) {
31
- o["default"] = v;
32
- });
33
- var __importStar = (this && this.__importStar) || (function () {
34
- var ownKeys = function(o) {
35
- ownKeys = Object.getOwnPropertyNames || function (o) {
36
- var ar = [];
37
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
- return ar;
39
- };
40
- return ownKeys(o);
41
- };
42
- return function (mod) {
43
- if (mod && mod.__esModule) return mod;
44
- var result = {};
45
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
- __setModuleDefault(result, mod);
47
- return result;
48
- };
49
- })();
50
17
  var __importDefault = (this && this.__importDefault) || function (mod) {
51
18
  return (mod && mod.__esModule) ? mod : { "default": mod };
52
19
  };
@@ -54,11 +21,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
54
21
  exports.ImageElementView = exports.figurePositions = void 0;
55
22
  const style_guide_1 = require("@manuscripts/style-guide");
56
23
  const transform_1 = require("@manuscripts/transform");
57
- const ExtLinkEditor_1 = require("../components/views/ExtLinkEditor");
24
+ const icons_1 = require("../icons");
25
+ const media_1 = require("../lib/media");
58
26
  const position_menu_1 = require("../lib/position-menu");
59
27
  const block_view_1 = __importDefault(require("./block_view"));
60
28
  const creators_1 = require("./creators");
61
- const ReactSubView_1 = __importStar(require("./ReactSubView"));
29
+ const ReactSubView_1 = __importDefault(require("./ReactSubView"));
62
30
  var figurePositions;
63
31
  (function (figurePositions) {
64
32
  figurePositions["left"] = "half-left";
@@ -70,6 +38,15 @@ class ImageElementView extends block_view_1.default {
70
38
  super(...arguments);
71
39
  this.isEditingExtLink = false;
72
40
  this.ignoreMutation = () => true;
41
+ this.upload = async (file) => {
42
+ const result = await this.props.fileManagement.upload(file);
43
+ if (!result) {
44
+ return;
45
+ }
46
+ this.setIsEditingExtLink(false);
47
+ this.setExtLink({ extLink: result.id });
48
+ this.updateContents();
49
+ };
73
50
  this.showPositionMenu = () => {
74
51
  const firstFigure = this.getFirstFigure();
75
52
  if (!firstFigure) {
@@ -110,6 +87,26 @@ class ImageElementView extends block_view_1.default {
110
87
  };
111
88
  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);
112
89
  };
90
+ this.removeExtLink = () => {
91
+ this.setExtLink({ extLink: '' });
92
+ };
93
+ this.setExtLink = (newAttrs) => {
94
+ this.setIsEditingExtLink(false);
95
+ const { tr } = this.view.state;
96
+ const pos = this.getPos();
97
+ tr.setNodeMarkup(pos, undefined, {
98
+ ...this.node.attrs,
99
+ ...newAttrs,
100
+ });
101
+ this.view.dispatch(tr);
102
+ };
103
+ this.setIsEditingExtLink = (val) => {
104
+ this.isEditingExtLink = val;
105
+ };
106
+ }
107
+ initialise() {
108
+ super.initialise();
109
+ this.upload = this.upload.bind(this);
113
110
  }
114
111
  createDOM() {
115
112
  super.createDOM();
@@ -133,9 +130,7 @@ class ImageElementView extends block_view_1.default {
133
130
  updateContents() {
134
131
  super.updateContents();
135
132
  this.addTools();
136
- if (this.node.type === transform_1.schema.nodes.image_element) {
137
- this.addExternalLinkedFileEditor();
138
- }
133
+ this.addExternalLinkedFileEditor();
139
134
  }
140
135
  addTools() {
141
136
  this.addPositionMenu();
@@ -201,25 +196,80 @@ class ImageElementView extends block_view_1.default {
201
196
  this.view.dispatch(tr);
202
197
  this.figurePosition = position;
203
198
  }
199
+ createDnDPlaceholder() {
200
+ const can = this.props.getCapabilities();
201
+ const label = document.createElement('div');
202
+ label.classList.add('accessibility_element_label');
203
+ label.innerText = 'Link';
204
+ const container = document.createElement('div');
205
+ container.classList.add('ext-link-editor-placeholder-container');
206
+ const placeholder = (0, media_1.createMediaPlaceholder)(media_1.MediaType.ExternalLink);
207
+ const closeButton = document.createElement('button');
208
+ closeButton.classList.add('close-button');
209
+ closeButton.setAttribute('aria-label', 'Close');
210
+ closeButton.addEventListener('click', () => {
211
+ this.setIsEditingExtLink(false);
212
+ this.updateContents();
213
+ });
214
+ container.append(placeholder, closeButton);
215
+ this.extLinkEditorContainer.append(label, container);
216
+ if (can.uploadFile) {
217
+ (0, media_1.addInteractionHandlers)(placeholder, this.upload, '*/*');
218
+ }
219
+ }
220
+ createAddLinkButton() {
221
+ const button = document.createElement('div');
222
+ button.innerHTML = icons_1.linkIcon;
223
+ const buttonText = document.createElement('span');
224
+ buttonText.textContent = 'Add link';
225
+ button.appendChild(buttonText);
226
+ button.setAttribute('aria-label', 'Add linked file');
227
+ button.classList.add('icon-button');
228
+ button.addEventListener('click', () => {
229
+ this.setIsEditingExtLink(true);
230
+ this.updateContents();
231
+ });
232
+ this.extLinkEditorContainer.appendChild(button);
233
+ }
234
+ createLinkedFile() {
235
+ const extLink = this.node.attrs.extLink;
236
+ const files = this.props.getFiles();
237
+ const file = extLink ? files.find((f) => f.id === extLink) : undefined;
238
+ const fileName = file ? `${file.name.trim()}` : 'File does not exist.';
239
+ const div = document.createElement('div');
240
+ div.classList.add('linked-file-info');
241
+ div.innerHTML = `<p>${icons_1.linkIcon} <span>${fileName}</span></p>`;
242
+ const removeButton = document.createElement('button');
243
+ removeButton.classList.add('icon-button', 'remove-button');
244
+ removeButton.setAttribute('aria-label', 'Remove link');
245
+ removeButton.innerHTML = icons_1.deleteIcon;
246
+ removeButton.addEventListener('click', () => {
247
+ this.isEditingExtLink = false;
248
+ this.removeExtLink();
249
+ });
250
+ div.appendChild(removeButton);
251
+ this.extLinkEditorContainer.appendChild(div);
252
+ }
204
253
  addExternalLinkedFileEditor() {
205
- if (this.props.dispatch && this.props.theme) {
206
- const componentProps = {
207
- node: this.node,
208
- nodePos: this.getPos(),
209
- view: this.view,
210
- editorProps: this.props,
211
- isEditing: this.isEditingExtLink,
212
- setIsEditing: (val) => {
213
- this.isEditingExtLink = val;
214
- this.updateContents();
215
- },
216
- };
217
- return (0, ReactSubView_1.createSubViewAsync)(this.props, ExtLinkEditor_1.ExtLinkEditor, componentProps, this.node, this.getPos, this.view, ['ext-link-editor-container']).then((elem) => {
218
- this.extLinkEditorContainer?.remove();
219
- this.extLinkEditorContainer = elem;
220
- this.subcontainer?.appendChild(this.extLinkEditorContainer);
221
- return;
222
- });
254
+ if (this.node.type === transform_1.schema.nodes.image_element) {
255
+ this.extLinkEditorContainer = this.container.querySelector('.ext-link-editor-container');
256
+ if (this.extLinkEditorContainer) {
257
+ this.extLinkEditorContainer.innerHTML = '';
258
+ }
259
+ else {
260
+ this.extLinkEditorContainer = document.createElement('div');
261
+ this.extLinkEditorContainer.classList.add('ext-link-editor-container');
262
+ }
263
+ if (!this.isEditingExtLink && !this.node.attrs.extLink) {
264
+ this.createAddLinkButton();
265
+ }
266
+ if (this.isEditingExtLink && !this.node.attrs.extLink) {
267
+ this.createDnDPlaceholder();
268
+ }
269
+ if (this.node.attrs.extLink) {
270
+ this.createLinkedFile();
271
+ }
272
+ this.subcontainer.appendChild(this.extLinkEditorContainer);
223
273
  }
224
274
  }
225
275
  }
package/dist/es/icons.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AddAuthorIcon, AlertIcon, ArrowDownCircleIcon, ArrowUpIcon, DeleteIcon, DraggableIcon, EditIcon, FileCorruptedIcon, FileMainDocumentIcon, ImageDefaultIcon, ImageLeftIcon, ImageRightIcon, LockIcon, PlusIcon, ScrollIcon, SectionCategoryIcon, TranslateIcon, } from '@manuscripts/style-guide';
1
+ import { AddAuthorIcon, AlertIcon, ArrowDownCircleIcon, ArrowUpIcon, DeleteIcon, DraggableIcon, EditIcon, FileCorruptedIcon, FileMainDocumentIcon, ImageDefaultIcon, ImageLeftIcon, ImageRightIcon, LinkIcon, LockIcon, PlusIcon, ScrollIcon, SectionCategoryIcon, TranslateIcon, } from '@manuscripts/style-guide';
2
2
  import { createElement } from 'react';
3
3
  import { renderToStaticMarkup } from 'react-dom/server';
4
4
  const renderIcon = (c) => renderToStaticMarkup(createElement(c));
@@ -19,4 +19,5 @@ export const imageDefaultIcon = renderToStaticMarkup(createElement(ImageDefaultI
19
19
  export const fileCorruptedIcon = renderToStaticMarkup(createElement(FileCorruptedIcon));
20
20
  export const draggableIcon = renderToStaticMarkup(createElement(DraggableIcon));
21
21
  export const translateIcon = renderToStaticMarkup(createElement(TranslateIcon));
22
+ export const linkIcon = renderToStaticMarkup(createElement(LinkIcon));
22
23
  export const fileMainDocumentIcon = renderToStaticMarkup(createElement(FileMainDocumentIcon));
@@ -39,25 +39,38 @@ export const createUnsupportedFormat = (filename, canEdit) => {
39
39
  element.appendChild(instructions);
40
40
  return element;
41
41
  };
42
- export const createMediaPlaceholder = (mediaType = 'media', view, getPos) => {
42
+ export var MediaType;
43
+ (function (MediaType) {
44
+ MediaType["Media"] = "media";
45
+ MediaType["Figure"] = "figure";
46
+ MediaType["ExternalLink"] = "external_link";
47
+ })(MediaType || (MediaType = {}));
48
+ const MediaLabels = {
49
+ [MediaType.Media]: 'media',
50
+ [MediaType.Figure]: 'figure',
51
+ [MediaType.ExternalLink]: 'a file to link',
52
+ };
53
+ export const createMediaPlaceholder = (mediaType = MediaType.Media, view, getPos) => {
43
54
  const element = document.createElement('div');
44
55
  element.classList.add('figure', 'placeholder');
45
56
  const instructions = document.createElement('div');
46
57
  instructions.classList.add('instructions');
47
- const uploadText = mediaType === 'media' ? 'media' : 'image';
48
58
  instructions.innerHTML = `
49
59
  <div class="placeholder-content">
50
- <p>Drag or click here to upload ${uploadText} <br>
51
- or drag items here from the file inspector tabs <br>
60
+ <p>Drag or click here to upload ${MediaLabels[mediaType]} <br>
61
+ ${mediaType === MediaType.ExternalLink
62
+ ? ` or drag items here from the file <a data-action='open-other-files'>'Other files'</a> in the inspector.</p>`
63
+ : `or drag items here from the file inspector tabs <br>
52
64
  <a data-action='open-other-files'>'Other files'</a> |
53
- <a data-action='open-supplement-files'>'Supplements'</a>
54
- ${mediaType === 'media' && view && getPos
65
+ <a data-action='open-supplement-files'>'Supplements'</a>`}
66
+
67
+ ${mediaType === MediaType.Media && view && getPos
55
68
  ? "| <a data-action='add-external-link'>'External link'</a>"
56
69
  : ''}
57
70
  </p>
58
71
  </div>
59
72
  `;
60
- if (mediaType === 'media' && view && getPos) {
73
+ if (mediaType === MediaType.Media && view && getPos) {
61
74
  const embedLink = instructions.querySelector("[data-action='add-external-link']");
62
75
  if (embedLink) {
63
76
  embedLink.addEventListener('click', (e) => {
@@ -1,2 +1,2 @@
1
- export const VERSION = '3.5.12';
1
+ export const VERSION = '3.5.13';
2
2
  export const MATHJAX_VERSION = '3.2.2';
@@ -2,7 +2,7 @@ import { isEqual } from 'lodash';
2
2
  import { NodeSelection } from 'prosemirror-state';
3
3
  import { NoPreviewMessageWithLink, openEmbedDialog, } from '../components/toolbar/InsertEmbedDialog';
4
4
  import { getMediaTypeInfo } from '../lib/get-media-type';
5
- import { addInteractionHandlers, createFileHandlers, createFileUploader, createMediaPlaceholder, createReactTools, createUnsupportedFormat, } from '../lib/media';
5
+ import { addInteractionHandlers, createFileHandlers, createFileUploader, createMediaPlaceholder, createReactTools, createUnsupportedFormat, MediaType, } from '../lib/media';
6
6
  import { getOEmbedHTML } from '../lib/oembed';
7
7
  import { allowedHref } from '../lib/url';
8
8
  import BlockView from './block_view';
@@ -158,7 +158,7 @@ export class EmbedView extends BlockView {
158
158
  const href = this.node.attrs.href;
159
159
  let object;
160
160
  if (!href) {
161
- object = createMediaPlaceholder('media', this.view, this.getPos);
161
+ object = createMediaPlaceholder(MediaType.Media, this.view, this.getPos);
162
162
  }
163
163
  else if (this.isUploadedFile()) {
164
164
  const files = this.props.getFiles();
@@ -172,14 +172,14 @@ export class EmbedView extends BlockView {
172
172
  : createUnsupportedFormat(file.name, this.props.getCapabilities().editArticle);
173
173
  }
174
174
  else {
175
- object = createMediaPlaceholder('media', this.view, this.getPos);
175
+ object = createMediaPlaceholder(MediaType.Media, this.view, this.getPos);
176
176
  }
177
177
  }
178
178
  else if (this.isEmbedLink()) {
179
179
  object = await this.createEmbedPreview();
180
180
  }
181
181
  else {
182
- object = createMediaPlaceholder('media', this.view, this.getPos);
182
+ object = createMediaPlaceholder(MediaType.Media, this.view, this.getPos);
183
183
  }
184
184
  const can = this.props.getCapabilities();
185
185
  if (can.uploadFile && object.classList.contains('placeholder')) {
@@ -17,7 +17,7 @@ import { schema } from '@manuscripts/transform';
17
17
  import { NodeSelection } from 'prosemirror-state';
18
18
  import { findParentNodeOfTypeClosestToPos } from 'prosemirror-utils';
19
19
  import { draggableIcon } from '../icons';
20
- import { addInteractionHandlers, createFileHandlers, createFileUploader, createMediaPlaceholder, createReactTools, createUnsupportedFormat, } from '../lib/media';
20
+ import { addInteractionHandlers, createFileHandlers, createFileUploader, createMediaPlaceholder, createReactTools, createUnsupportedFormat, MediaType, } from '../lib/media';
21
21
  import { isDeleted } from '../lib/track-changes-utils';
22
22
  import { createEditableNodeView } from './creators';
23
23
  import { FigureView } from './figure';
@@ -50,7 +50,7 @@ export class FigureEditableView extends FigureView {
50
50
  return img;
51
51
  };
52
52
  this.createPlaceholder = () => {
53
- return createMediaPlaceholder('figure');
53
+ return createMediaPlaceholder(MediaType.Figure);
54
54
  };
55
55
  }
56
56
  initialise() {
@@ -15,11 +15,12 @@
15
15
  */
16
16
  import { ContextMenu } from '@manuscripts/style-guide';
17
17
  import { schema } from '@manuscripts/transform';
18
- import { ExtLinkEditor, } from '../components/views/ExtLinkEditor';
18
+ import { deleteIcon, linkIcon } from '../icons';
19
+ import { addInteractionHandlers, createMediaPlaceholder, MediaType, } from '../lib/media';
19
20
  import { createPositionMenuWrapper } from '../lib/position-menu';
20
21
  import BlockView from './block_view';
21
22
  import { createNodeView } from './creators';
22
- import ReactSubView, { createSubViewAsync } from './ReactSubView';
23
+ import ReactSubView from './ReactSubView';
23
24
  export var figurePositions;
24
25
  (function (figurePositions) {
25
26
  figurePositions["left"] = "half-left";
@@ -31,6 +32,15 @@ export class ImageElementView extends BlockView {
31
32
  super(...arguments);
32
33
  this.isEditingExtLink = false;
33
34
  this.ignoreMutation = () => true;
35
+ this.upload = async (file) => {
36
+ const result = await this.props.fileManagement.upload(file);
37
+ if (!result) {
38
+ return;
39
+ }
40
+ this.setIsEditingExtLink(false);
41
+ this.setExtLink({ extLink: result.id });
42
+ this.updateContents();
43
+ };
34
44
  this.showPositionMenu = () => {
35
45
  const firstFigure = this.getFirstFigure();
36
46
  if (!firstFigure) {
@@ -71,6 +81,26 @@ export class ImageElementView extends BlockView {
71
81
  };
72
82
  this.props.popper.show(this.positionMenuWrapper, ReactSubView(this.props, ContextMenu, componentProps, this.node, this.getPos, this.view, ['context-menu', 'position-menu']), 'left', false);
73
83
  };
84
+ this.removeExtLink = () => {
85
+ this.setExtLink({ extLink: '' });
86
+ };
87
+ this.setExtLink = (newAttrs) => {
88
+ this.setIsEditingExtLink(false);
89
+ const { tr } = this.view.state;
90
+ const pos = this.getPos();
91
+ tr.setNodeMarkup(pos, undefined, {
92
+ ...this.node.attrs,
93
+ ...newAttrs,
94
+ });
95
+ this.view.dispatch(tr);
96
+ };
97
+ this.setIsEditingExtLink = (val) => {
98
+ this.isEditingExtLink = val;
99
+ };
100
+ }
101
+ initialise() {
102
+ super.initialise();
103
+ this.upload = this.upload.bind(this);
74
104
  }
75
105
  createDOM() {
76
106
  super.createDOM();
@@ -94,9 +124,7 @@ export class ImageElementView extends BlockView {
94
124
  updateContents() {
95
125
  super.updateContents();
96
126
  this.addTools();
97
- if (this.node.type === schema.nodes.image_element) {
98
- this.addExternalLinkedFileEditor();
99
- }
127
+ this.addExternalLinkedFileEditor();
100
128
  }
101
129
  addTools() {
102
130
  this.addPositionMenu();
@@ -162,25 +190,80 @@ export class ImageElementView extends BlockView {
162
190
  this.view.dispatch(tr);
163
191
  this.figurePosition = position;
164
192
  }
193
+ createDnDPlaceholder() {
194
+ const can = this.props.getCapabilities();
195
+ const label = document.createElement('div');
196
+ label.classList.add('accessibility_element_label');
197
+ label.innerText = 'Link';
198
+ const container = document.createElement('div');
199
+ container.classList.add('ext-link-editor-placeholder-container');
200
+ const placeholder = createMediaPlaceholder(MediaType.ExternalLink);
201
+ const closeButton = document.createElement('button');
202
+ closeButton.classList.add('close-button');
203
+ closeButton.setAttribute('aria-label', 'Close');
204
+ closeButton.addEventListener('click', () => {
205
+ this.setIsEditingExtLink(false);
206
+ this.updateContents();
207
+ });
208
+ container.append(placeholder, closeButton);
209
+ this.extLinkEditorContainer.append(label, container);
210
+ if (can.uploadFile) {
211
+ addInteractionHandlers(placeholder, this.upload, '*/*');
212
+ }
213
+ }
214
+ createAddLinkButton() {
215
+ const button = document.createElement('div');
216
+ button.innerHTML = linkIcon;
217
+ const buttonText = document.createElement('span');
218
+ buttonText.textContent = 'Add link';
219
+ button.appendChild(buttonText);
220
+ button.setAttribute('aria-label', 'Add linked file');
221
+ button.classList.add('icon-button');
222
+ button.addEventListener('click', () => {
223
+ this.setIsEditingExtLink(true);
224
+ this.updateContents();
225
+ });
226
+ this.extLinkEditorContainer.appendChild(button);
227
+ }
228
+ createLinkedFile() {
229
+ const extLink = this.node.attrs.extLink;
230
+ const files = this.props.getFiles();
231
+ const file = extLink ? files.find((f) => f.id === extLink) : undefined;
232
+ const fileName = file ? `${file.name.trim()}` : 'File does not exist.';
233
+ const div = document.createElement('div');
234
+ div.classList.add('linked-file-info');
235
+ div.innerHTML = `<p>${linkIcon} <span>${fileName}</span></p>`;
236
+ const removeButton = document.createElement('button');
237
+ removeButton.classList.add('icon-button', 'remove-button');
238
+ removeButton.setAttribute('aria-label', 'Remove link');
239
+ removeButton.innerHTML = deleteIcon;
240
+ removeButton.addEventListener('click', () => {
241
+ this.isEditingExtLink = false;
242
+ this.removeExtLink();
243
+ });
244
+ div.appendChild(removeButton);
245
+ this.extLinkEditorContainer.appendChild(div);
246
+ }
165
247
  addExternalLinkedFileEditor() {
166
- if (this.props.dispatch && this.props.theme) {
167
- const componentProps = {
168
- node: this.node,
169
- nodePos: this.getPos(),
170
- view: this.view,
171
- editorProps: this.props,
172
- isEditing: this.isEditingExtLink,
173
- setIsEditing: (val) => {
174
- this.isEditingExtLink = val;
175
- this.updateContents();
176
- },
177
- };
178
- return createSubViewAsync(this.props, ExtLinkEditor, componentProps, this.node, this.getPos, this.view, ['ext-link-editor-container']).then((elem) => {
179
- this.extLinkEditorContainer?.remove();
180
- this.extLinkEditorContainer = elem;
181
- this.subcontainer?.appendChild(this.extLinkEditorContainer);
182
- return;
183
- });
248
+ if (this.node.type === schema.nodes.image_element) {
249
+ this.extLinkEditorContainer = this.container.querySelector('.ext-link-editor-container');
250
+ if (this.extLinkEditorContainer) {
251
+ this.extLinkEditorContainer.innerHTML = '';
252
+ }
253
+ else {
254
+ this.extLinkEditorContainer = document.createElement('div');
255
+ this.extLinkEditorContainer.classList.add('ext-link-editor-container');
256
+ }
257
+ if (!this.isEditingExtLink && !this.node.attrs.extLink) {
258
+ this.createAddLinkButton();
259
+ }
260
+ if (this.isEditingExtLink && !this.node.attrs.extLink) {
261
+ this.createDnDPlaceholder();
262
+ }
263
+ if (this.node.attrs.extLink) {
264
+ this.createLinkedFile();
265
+ }
266
+ this.subcontainer.appendChild(this.extLinkEditorContainer);
184
267
  }
185
268
  }
186
269
  }
@@ -15,4 +15,5 @@ export declare const imageDefaultIcon: string;
15
15
  export declare const fileCorruptedIcon: string;
16
16
  export declare const draggableIcon: string;
17
17
  export declare const translateIcon: string;
18
+ export declare const linkIcon: string;
18
19
  export declare const fileMainDocumentIcon: string;
@@ -18,7 +18,12 @@ import { EditorProps } from '../configs/ManuscriptsEditor';
18
18
  import { Trackable } from '../types';
19
19
  import { FileAttachment } from './files';
20
20
  export declare const createUnsupportedFormat: (filename: string, canEdit: boolean) => HTMLElement;
21
- export declare const createMediaPlaceholder: (mediaType?: "media" | "figure", view?: ManuscriptEditorView, getPos?: () => number) => HTMLElement;
21
+ export declare enum MediaType {
22
+ Media = "media",
23
+ Figure = "figure",
24
+ ExternalLink = "external_link"
25
+ }
26
+ export declare const createMediaPlaceholder: (mediaType?: MediaType, view?: ManuscriptEditorView, getPos?: () => number) => HTMLElement;
22
27
  export interface FileHandlers {
23
28
  handleDownload?: () => void;
24
29
  handleUpload?: () => void;
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "3.5.12";
1
+ export declare const VERSION = "3.5.13";
2
2
  export declare const MATHJAX_VERSION = "3.2.2";