@manuscripts/body-editor 1.7.39 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -242,21 +242,23 @@ const insertSectionLabel = (state, dispatch, currentTr) => {
242
242
  return true;
243
243
  };
244
244
  exports.insertSectionLabel = insertSectionLabel;
245
+ const isLink = (text) => /^\s*(https?:\S+)/.test(text);
245
246
  const insertLink = (state, dispatch) => {
246
- const text = selectedText();
247
- const contents = text ? state.schema.text(text) : undefined;
248
- const matches = text.match(/^\s*(https?:\S+)/);
249
- const attrs = {
250
- href: matches ? matches[1] : '',
251
- };
252
- const range = new prosemirror_model_1.NodeRange(state.selection.$from, state.selection.$to, state.selection.$from.depth);
253
- const { tr } = state;
254
- const wrapping = (0, prosemirror_transform_1.findWrapping)(range, state.schema.nodes.link, attrs);
255
- if (wrapping) {
256
- tr.wrap(range, wrapping);
247
+ const tr = state.tr;
248
+ const selection = state.selection;
249
+ if (!selection.empty) {
250
+ const text = selectedText();
251
+ const attrs = {
252
+ href: isLink(text) ? text.trim() : '',
253
+ };
254
+ const range = new prosemirror_model_1.NodeRange(selection.$from, selection.$to, selection.$from.depth);
255
+ const wrapping = (0, prosemirror_transform_1.findWrapping)(range, transform_1.schema.nodes.link, attrs);
256
+ if (wrapping) {
257
+ tr.wrap(range, wrapping);
258
+ }
257
259
  }
258
260
  else {
259
- tr.insert(state.selection.anchor, state.schema.nodes.link.create(attrs, contents));
261
+ tr.insert(state.selection.anchor, transform_1.schema.nodes.link.create());
260
262
  }
261
263
  if (dispatch) {
262
264
  const selection = prosemirror_state_1.NodeSelection.create(tr.doc, state.tr.selection.from);
@@ -46,42 +46,6 @@ const style_guide_1 = require("@manuscripts/style-guide");
46
46
  const react_1 = __importStar(require("react"));
47
47
  const styled_components_1 = __importDefault(require("styled-components"));
48
48
  const url_1 = require("../../lib/url");
49
- const LinkForm = ({ handleCancel, handleRemove, handleSave, value }) => {
50
- const [href, setHref] = (0, react_1.useState)(value.href);
51
- const [text, setText] = (0, react_1.useState)(value.text);
52
- const [title, setTitle] = (0, react_1.useState)(value.title || '');
53
- const handleSubmit = (0, react_1.useCallback)((event) => {
54
- event.preventDefault();
55
- handleSave({ href, text, title });
56
- }, [href, text, title, handleSave]);
57
- return (react_1.default.createElement(Form, { onSubmit: handleSubmit },
58
- react_1.default.createElement(Field, null,
59
- react_1.default.createElement(FieldHeading, null,
60
- react_1.default.createElement(Label, null, "URL"),
61
- href && (0, url_1.allowedHref)(href) && (react_1.default.createElement(Open, { href: href, target: '_blank', rel: 'noopener' }))),
62
- react_1.default.createElement(style_guide_1.TextField, { type: 'url', name: 'href', value: href, autoComplete: 'off', autoFocus: true, required: true, onChange: (event) => {
63
- setHref(event.target.value);
64
- } })),
65
- react_1.default.createElement(Field, null,
66
- react_1.default.createElement(FieldHeading, null,
67
- react_1.default.createElement(Label, null, "Text")),
68
- react_1.default.createElement(style_guide_1.TextField, { type: 'text', name: 'text', value: text, autoComplete: 'off', required: true, onChange: (event) => {
69
- setText(event.target.value);
70
- } })),
71
- react_1.default.createElement(Field, null,
72
- react_1.default.createElement(FieldHeading, null,
73
- react_1.default.createElement(Label, null, "Title (optional)")),
74
- react_1.default.createElement(style_guide_1.TextField, { type: 'text', name: 'title', value: title, autoComplete: 'off', required: false, onChange: (event) => {
75
- setTitle(event.target.value);
76
- } })),
77
- react_1.default.createElement(Actions, null,
78
- react_1.default.createElement(ActionGroup, null,
79
- react_1.default.createElement(style_guide_1.SecondaryButton, { type: 'button', mini: true, onClick: handleRemove }, "Remove Link")),
80
- react_1.default.createElement(ActionGroup, null,
81
- react_1.default.createElement(style_guide_1.SecondaryButton, { type: 'button', onClick: handleCancel }, "Cancel"),
82
- react_1.default.createElement(style_guide_1.PrimaryButton, { type: 'submit' }, "Save")))));
83
- };
84
- exports.LinkForm = LinkForm;
85
49
  const Form = styled_components_1.default.form `
86
50
  padding: 16px;
87
51
  `;
@@ -127,3 +91,33 @@ const Open = styled_components_1.default.a `
127
91
  margin-left: 4px;
128
92
  }
129
93
  `;
94
+ const LinkForm = ({ onCancel, onRemove, onSave, value, }) => {
95
+ const [href, setHref] = (0, react_1.useState)(value.href);
96
+ const [text, setText] = (0, react_1.useState)(value.text);
97
+ const [title, setTitle] = (0, react_1.useState)(value.title || '');
98
+ const handleSubmit = (0, react_1.useCallback)((event) => {
99
+ event.preventDefault();
100
+ onSave({ href, text, title });
101
+ }, [href, text, title, onSave]);
102
+ return (react_1.default.createElement(Form, { onSubmit: handleSubmit },
103
+ react_1.default.createElement(Field, null,
104
+ react_1.default.createElement(FieldHeading, null,
105
+ react_1.default.createElement(Label, null, "URL"),
106
+ href && (0, url_1.allowedHref)(href) && (react_1.default.createElement(Open, { href: href, target: '_blank', rel: 'noopener' }))),
107
+ react_1.default.createElement(style_guide_1.TextField, { type: 'url', name: 'href', value: href, autoComplete: 'off', autoFocus: true, required: true, onChange: (e) => setHref(e.target.value) })),
108
+ react_1.default.createElement(Field, null,
109
+ react_1.default.createElement(FieldHeading, null,
110
+ react_1.default.createElement(Label, null, "Text")),
111
+ react_1.default.createElement(style_guide_1.TextField, { type: 'text', name: 'text', value: text, autoComplete: 'off', required: true, onChange: (e) => setText(e.target.value) })),
112
+ react_1.default.createElement(Field, null,
113
+ react_1.default.createElement(FieldHeading, null,
114
+ react_1.default.createElement(Label, null, "Title (optional)")),
115
+ react_1.default.createElement(style_guide_1.TextField, { type: 'text', name: 'title', value: title, autoComplete: 'off', required: false, onChange: (e) => setTitle(e.target.value) })),
116
+ react_1.default.createElement(Actions, null,
117
+ react_1.default.createElement(ActionGroup, null,
118
+ react_1.default.createElement(style_guide_1.SecondaryButton, { type: 'button', mini: true, onClick: onRemove }, "Remove Link")),
119
+ react_1.default.createElement(ActionGroup, null,
120
+ react_1.default.createElement(style_guide_1.SecondaryButton, { type: 'button', onClick: onCancel }, "Cancel"),
121
+ react_1.default.createElement(style_guide_1.PrimaryButton, { type: 'submit' }, "Save")))));
122
+ };
123
+ exports.LinkForm = LinkForm;
@@ -16,44 +16,9 @@
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.LinkView = void 0;
19
- const track_changes_utils_1 = require("../lib/track-changes-utils");
20
- const url_1 = require("../lib/url");
21
19
  const base_node_view_1 = require("./base_node_view");
22
20
  const creators_1 = require("./creators");
23
21
  class LinkView extends base_node_view_1.BaseNodeView {
24
- constructor() {
25
- super(...arguments);
26
- this.initialise = () => {
27
- this.createDOM();
28
- this.updateContents();
29
- };
30
- this.updateContents = () => {
31
- var _a, _b, _c;
32
- const linkText = ((_c = (_b = (_a = this.node) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? void 0 : _b.firstChild) === null || _c === void 0 ? void 0 : _c.text) || '';
33
- if (!(0, track_changes_utils_1.isRejectedInsert)(this.node)) {
34
- const { href, title } = this.node.attrs;
35
- const linkElement = document.createElement('a');
36
- linkElement.classList.add('link');
37
- linkElement.style.position = 'relative';
38
- if (href && (0, url_1.allowedHref)(href)) {
39
- linkElement.setAttribute('href', href);
40
- }
41
- if (title) {
42
- linkElement.setAttribute('title', title);
43
- }
44
- linkElement.innerHTML = linkText;
45
- this.dom.innerHTML = '';
46
- this.dom.appendChild(linkElement);
47
- }
48
- else {
49
- this.dom.innerHTML = linkText;
50
- }
51
- };
52
- this.createDOM = () => {
53
- this.dom = document.createElement('span');
54
- this.contentDOM = this.dom;
55
- };
56
- }
57
22
  }
58
23
  exports.LinkView = LinkView;
59
24
  exports.default = (0, creators_1.createNodeView)(LinkView);
@@ -19,76 +19,119 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
21
  exports.LinkEditableView = void 0;
22
+ const transform_1 = require("@manuscripts/transform");
22
23
  const prosemirror_state_1 = require("prosemirror-state");
23
24
  const LinkForm_1 = require("../components/views/LinkForm");
25
+ const track_changes_utils_1 = require("../lib/track-changes-utils");
26
+ const url_1 = require("../lib/url");
24
27
  const creators_1 = require("./creators");
25
28
  const link_1 = require("./link");
26
29
  const ReactSubView_1 = __importDefault(require("./ReactSubView"));
27
30
  class LinkEditableView extends link_1.LinkView {
28
31
  constructor() {
29
32
  super(...arguments);
33
+ this.ignoreMutation = () => true;
34
+ this.initialise = () => {
35
+ this.createDOM();
36
+ this.updateContents();
37
+ };
38
+ this.updateContents = () => {
39
+ if ((0, track_changes_utils_1.isRejectedInsert)(this.node)) {
40
+ this.dom.innerHTML = '';
41
+ this.contentDOM = undefined;
42
+ return;
43
+ }
44
+ this.contentDOM = this.dom;
45
+ const attrs = (0, track_changes_utils_1.getActualAttrs)(this.node);
46
+ const href = attrs.href;
47
+ const title = attrs.title;
48
+ const classes = ['link', ...(0, track_changes_utils_1.getChangeClasses)(this.node.attrs.dataTracked)];
49
+ this.dom.className = classes.join(' ');
50
+ this.dom.setAttribute('href', (0, url_1.allowedHref)(href) ? href : '');
51
+ this.dom.setAttribute('target', '_blank');
52
+ this.dom.setAttribute('title', title || '');
53
+ };
54
+ this.createDOM = () => {
55
+ this.dom = document.createElement('a');
56
+ this.dom.addEventListener('click', this.handleClick);
57
+ };
30
58
  this.selectNode = () => {
31
- this.showForm();
59
+ if (!(0, track_changes_utils_1.isDeleted)(this.node)) {
60
+ this.showForm();
61
+ }
32
62
  };
33
63
  this.destroy = () => {
34
64
  this.closeForm();
35
65
  };
36
66
  this.deselectNode = () => {
67
+ const attrs = (0, track_changes_utils_1.getActualAttrs)(this.node);
68
+ if (!this.node.content.size || !attrs.href) {
69
+ this.removeLink();
70
+ }
37
71
  this.closeForm();
38
72
  };
39
- this.showForm = (event) => {
40
- if (event) {
41
- event.preventDefault();
42
- }
73
+ this.showForm = () => {
43
74
  if (!this.props.getCapabilities().editArticle) {
44
75
  return;
45
76
  }
46
- const originalValue = {
47
- href: this.node.attrs.href,
48
- title: this.node.attrs.title,
77
+ const attrs = (0, track_changes_utils_1.getActualAttrs)(this.node);
78
+ const value = {
79
+ href: attrs.href,
80
+ title: attrs.title,
49
81
  text: this.node.textContent,
50
82
  };
51
83
  const componentProps = {
52
- value: originalValue,
53
- handleCancel: this.handleCancel,
54
- handleRemove: this.handleRemove,
55
- handleSave: this.handleSave,
84
+ value,
85
+ onCancel: this.handleCancel,
86
+ onRemove: this.handleRemove,
87
+ onSave: this.handleSave,
56
88
  };
57
89
  this.popperContainer = (0, ReactSubView_1.default)(this.props, LinkForm_1.LinkForm, componentProps, this.node, this.getPos, this.view, 'link-editor');
58
90
  this.props.popper.show(this.dom, this.popperContainer, 'bottom');
59
91
  };
92
+ this.handleClick = (e) => {
93
+ if (this.props.getCapabilities().editArticle) {
94
+ e.preventDefault();
95
+ }
96
+ };
60
97
  this.handleCancel = () => {
61
- const tr = this.view.state.tr;
62
- const pos = this.getPos();
63
- tr.setSelection(prosemirror_state_1.TextSelection.create(tr.doc, pos));
64
- this.view.focus();
65
- this.view.dispatch(tr);
98
+ const attrs = (0, track_changes_utils_1.getActualAttrs)(this.node);
99
+ if (!this.node.content.size || !attrs.href) {
100
+ this.removeLink();
101
+ }
66
102
  this.closeForm();
67
103
  };
68
104
  this.handleRemove = () => {
69
- const tr = this.view.state.tr;
70
- const pos = this.getPos();
71
- const to = pos + this.node.nodeSize;
72
- tr.replaceWith(pos, to, this.node.content).setSelection(prosemirror_state_1.TextSelection.create(tr.doc, pos));
73
- this.view.focus();
74
- this.view.dispatch(tr);
105
+ this.removeLink();
75
106
  this.closeForm();
76
107
  };
77
108
  this.handleSave = (value) => {
78
109
  const tr = this.view.state.tr;
79
- const link = this.node;
110
+ const attrs = (0, track_changes_utils_1.getActualAttrs)(this.node);
80
111
  const pos = this.getPos();
81
- if (value.href !== link.attrs.href || value.title !== link.attrs.title) {
112
+ if (value.href !== attrs.href || value.title !== attrs.title) {
82
113
  tr.setNodeMarkup(pos, undefined, Object.assign(Object.assign({}, this.node.attrs), { href: value.href, title: value.title }));
83
114
  }
84
- if (value.text !== link.textContent) {
85
- tr.insertText(value.text, pos + 1, pos + this.node.nodeSize - 1);
115
+ if (value.text !== this.node.textContent) {
116
+ tr.delete(pos + 1, pos + this.node.nodeSize - 1);
117
+ tr.insert(pos + 1, transform_1.schema.text(value.text));
86
118
  }
87
119
  tr.setSelection(prosemirror_state_1.TextSelection.create(tr.doc, pos));
88
120
  this.view.focus();
89
121
  this.view.dispatch(tr);
90
122
  this.closeForm();
91
123
  };
124
+ this.removeLink = () => {
125
+ const tr = this.view.state.tr;
126
+ const pos = this.getPos();
127
+ tr.delete(pos, pos + this.node.nodeSize);
128
+ const text = this.node.textContent;
129
+ if (text) {
130
+ tr.insert(pos, transform_1.schema.text(text));
131
+ }
132
+ tr.setSelection(prosemirror_state_1.TextSelection.create(tr.doc, pos));
133
+ this.view.dispatch(tr);
134
+ };
92
135
  this.closeForm = () => {
93
136
  var _a;
94
137
  this.props.popper.destroy();
@@ -227,21 +227,23 @@ export const insertSectionLabel = (state, dispatch, currentTr) => {
227
227
  }
228
228
  return true;
229
229
  };
230
+ const isLink = (text) => /^\s*(https?:\S+)/.test(text);
230
231
  export const insertLink = (state, dispatch) => {
231
- const text = selectedText();
232
- const contents = text ? state.schema.text(text) : undefined;
233
- const matches = text.match(/^\s*(https?:\S+)/);
234
- const attrs = {
235
- href: matches ? matches[1] : '',
236
- };
237
- const range = new NodeRange(state.selection.$from, state.selection.$to, state.selection.$from.depth);
238
- const { tr } = state;
239
- const wrapping = findWrapping(range, state.schema.nodes.link, attrs);
240
- if (wrapping) {
241
- tr.wrap(range, wrapping);
232
+ const tr = state.tr;
233
+ const selection = state.selection;
234
+ if (!selection.empty) {
235
+ const text = selectedText();
236
+ const attrs = {
237
+ href: isLink(text) ? text.trim() : '',
238
+ };
239
+ const range = new NodeRange(selection.$from, selection.$to, selection.$from.depth);
240
+ const wrapping = findWrapping(range, schema.nodes.link, attrs);
241
+ if (wrapping) {
242
+ tr.wrap(range, wrapping);
243
+ }
242
244
  }
243
245
  else {
244
- tr.insert(state.selection.anchor, state.schema.nodes.link.create(attrs, contents));
246
+ tr.insert(state.selection.anchor, schema.nodes.link.create());
245
247
  }
246
248
  if (dispatch) {
247
249
  const selection = NodeSelection.create(tr.doc, state.tr.selection.from);
@@ -17,41 +17,6 @@ import { PrimaryButton, SecondaryButton, TextField, } from '@manuscripts/style-g
17
17
  import React, { useCallback, useState } from 'react';
18
18
  import styled from 'styled-components';
19
19
  import { allowedHref } from '../../lib/url';
20
- export const LinkForm = ({ handleCancel, handleRemove, handleSave, value }) => {
21
- const [href, setHref] = useState(value.href);
22
- const [text, setText] = useState(value.text);
23
- const [title, setTitle] = useState(value.title || '');
24
- const handleSubmit = useCallback((event) => {
25
- event.preventDefault();
26
- handleSave({ href, text, title });
27
- }, [href, text, title, handleSave]);
28
- return (React.createElement(Form, { onSubmit: handleSubmit },
29
- React.createElement(Field, null,
30
- React.createElement(FieldHeading, null,
31
- React.createElement(Label, null, "URL"),
32
- href && allowedHref(href) && (React.createElement(Open, { href: href, target: '_blank', rel: 'noopener' }))),
33
- React.createElement(TextField, { type: 'url', name: 'href', value: href, autoComplete: 'off', autoFocus: true, required: true, onChange: (event) => {
34
- setHref(event.target.value);
35
- } })),
36
- React.createElement(Field, null,
37
- React.createElement(FieldHeading, null,
38
- React.createElement(Label, null, "Text")),
39
- React.createElement(TextField, { type: 'text', name: 'text', value: text, autoComplete: 'off', required: true, onChange: (event) => {
40
- setText(event.target.value);
41
- } })),
42
- React.createElement(Field, null,
43
- React.createElement(FieldHeading, null,
44
- React.createElement(Label, null, "Title (optional)")),
45
- React.createElement(TextField, { type: 'text', name: 'title', value: title, autoComplete: 'off', required: false, onChange: (event) => {
46
- setTitle(event.target.value);
47
- } })),
48
- React.createElement(Actions, null,
49
- React.createElement(ActionGroup, null,
50
- React.createElement(SecondaryButton, { type: 'button', mini: true, onClick: handleRemove }, "Remove Link")),
51
- React.createElement(ActionGroup, null,
52
- React.createElement(SecondaryButton, { type: 'button', onClick: handleCancel }, "Cancel"),
53
- React.createElement(PrimaryButton, { type: 'submit' }, "Save")))));
54
- };
55
20
  const Form = styled.form `
56
21
  padding: 16px;
57
22
  `;
@@ -97,3 +62,32 @@ const Open = styled.a `
97
62
  margin-left: 4px;
98
63
  }
99
64
  `;
65
+ export const LinkForm = ({ onCancel, onRemove, onSave, value, }) => {
66
+ const [href, setHref] = useState(value.href);
67
+ const [text, setText] = useState(value.text);
68
+ const [title, setTitle] = useState(value.title || '');
69
+ const handleSubmit = useCallback((event) => {
70
+ event.preventDefault();
71
+ onSave({ href, text, title });
72
+ }, [href, text, title, onSave]);
73
+ return (React.createElement(Form, { onSubmit: handleSubmit },
74
+ React.createElement(Field, null,
75
+ React.createElement(FieldHeading, null,
76
+ React.createElement(Label, null, "URL"),
77
+ href && allowedHref(href) && (React.createElement(Open, { href: href, target: '_blank', rel: 'noopener' }))),
78
+ React.createElement(TextField, { type: 'url', name: 'href', value: href, autoComplete: 'off', autoFocus: true, required: true, onChange: (e) => setHref(e.target.value) })),
79
+ React.createElement(Field, null,
80
+ React.createElement(FieldHeading, null,
81
+ React.createElement(Label, null, "Text")),
82
+ React.createElement(TextField, { type: 'text', name: 'text', value: text, autoComplete: 'off', required: true, onChange: (e) => setText(e.target.value) })),
83
+ React.createElement(Field, null,
84
+ React.createElement(FieldHeading, null,
85
+ React.createElement(Label, null, "Title (optional)")),
86
+ React.createElement(TextField, { type: 'text', name: 'title', value: title, autoComplete: 'off', required: false, onChange: (e) => setTitle(e.target.value) })),
87
+ React.createElement(Actions, null,
88
+ React.createElement(ActionGroup, null,
89
+ React.createElement(SecondaryButton, { type: 'button', mini: true, onClick: onRemove }, "Remove Link")),
90
+ React.createElement(ActionGroup, null,
91
+ React.createElement(SecondaryButton, { type: 'button', onClick: onCancel }, "Cancel"),
92
+ React.createElement(PrimaryButton, { type: 'submit' }, "Save")))));
93
+ };
@@ -13,43 +13,8 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { isRejectedInsert } from '../lib/track-changes-utils';
17
- import { allowedHref } from '../lib/url';
18
16
  import { BaseNodeView } from './base_node_view';
19
17
  import { createNodeView } from './creators';
20
18
  export class LinkView extends BaseNodeView {
21
- constructor() {
22
- super(...arguments);
23
- this.initialise = () => {
24
- this.createDOM();
25
- this.updateContents();
26
- };
27
- this.updateContents = () => {
28
- var _a, _b, _c;
29
- const linkText = ((_c = (_b = (_a = this.node) === null || _a === void 0 ? void 0 : _a.content) === null || _b === void 0 ? void 0 : _b.firstChild) === null || _c === void 0 ? void 0 : _c.text) || '';
30
- if (!isRejectedInsert(this.node)) {
31
- const { href, title } = this.node.attrs;
32
- const linkElement = document.createElement('a');
33
- linkElement.classList.add('link');
34
- linkElement.style.position = 'relative';
35
- if (href && allowedHref(href)) {
36
- linkElement.setAttribute('href', href);
37
- }
38
- if (title) {
39
- linkElement.setAttribute('title', title);
40
- }
41
- linkElement.innerHTML = linkText;
42
- this.dom.innerHTML = '';
43
- this.dom.appendChild(linkElement);
44
- }
45
- else {
46
- this.dom.innerHTML = linkText;
47
- }
48
- };
49
- this.createDOM = () => {
50
- this.dom = document.createElement('span');
51
- this.contentDOM = this.dom;
52
- };
53
- }
54
19
  }
55
20
  export default createNodeView(LinkView);
@@ -13,76 +13,119 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ import { schema } from '@manuscripts/transform';
16
17
  import { TextSelection } from 'prosemirror-state';
17
- import { LinkForm } from '../components/views/LinkForm';
18
+ import { LinkForm, } from '../components/views/LinkForm';
19
+ import { getActualAttrs, getChangeClasses, isDeleted, isRejectedInsert, } from '../lib/track-changes-utils';
20
+ import { allowedHref } from '../lib/url';
18
21
  import { createEditableNodeView } from './creators';
19
22
  import { LinkView } from './link';
20
23
  import ReactSubView from './ReactSubView';
21
24
  export class LinkEditableView extends LinkView {
22
25
  constructor() {
23
26
  super(...arguments);
27
+ this.ignoreMutation = () => true;
28
+ this.initialise = () => {
29
+ this.createDOM();
30
+ this.updateContents();
31
+ };
32
+ this.updateContents = () => {
33
+ if (isRejectedInsert(this.node)) {
34
+ this.dom.innerHTML = '';
35
+ this.contentDOM = undefined;
36
+ return;
37
+ }
38
+ this.contentDOM = this.dom;
39
+ const attrs = getActualAttrs(this.node);
40
+ const href = attrs.href;
41
+ const title = attrs.title;
42
+ const classes = ['link', ...getChangeClasses(this.node.attrs.dataTracked)];
43
+ this.dom.className = classes.join(' ');
44
+ this.dom.setAttribute('href', allowedHref(href) ? href : '');
45
+ this.dom.setAttribute('target', '_blank');
46
+ this.dom.setAttribute('title', title || '');
47
+ };
48
+ this.createDOM = () => {
49
+ this.dom = document.createElement('a');
50
+ this.dom.addEventListener('click', this.handleClick);
51
+ };
24
52
  this.selectNode = () => {
25
- this.showForm();
53
+ if (!isDeleted(this.node)) {
54
+ this.showForm();
55
+ }
26
56
  };
27
57
  this.destroy = () => {
28
58
  this.closeForm();
29
59
  };
30
60
  this.deselectNode = () => {
61
+ const attrs = getActualAttrs(this.node);
62
+ if (!this.node.content.size || !attrs.href) {
63
+ this.removeLink();
64
+ }
31
65
  this.closeForm();
32
66
  };
33
- this.showForm = (event) => {
34
- if (event) {
35
- event.preventDefault();
36
- }
67
+ this.showForm = () => {
37
68
  if (!this.props.getCapabilities().editArticle) {
38
69
  return;
39
70
  }
40
- const originalValue = {
41
- href: this.node.attrs.href,
42
- title: this.node.attrs.title,
71
+ const attrs = getActualAttrs(this.node);
72
+ const value = {
73
+ href: attrs.href,
74
+ title: attrs.title,
43
75
  text: this.node.textContent,
44
76
  };
45
77
  const componentProps = {
46
- value: originalValue,
47
- handleCancel: this.handleCancel,
48
- handleRemove: this.handleRemove,
49
- handleSave: this.handleSave,
78
+ value,
79
+ onCancel: this.handleCancel,
80
+ onRemove: this.handleRemove,
81
+ onSave: this.handleSave,
50
82
  };
51
83
  this.popperContainer = ReactSubView(this.props, LinkForm, componentProps, this.node, this.getPos, this.view, 'link-editor');
52
84
  this.props.popper.show(this.dom, this.popperContainer, 'bottom');
53
85
  };
86
+ this.handleClick = (e) => {
87
+ if (this.props.getCapabilities().editArticle) {
88
+ e.preventDefault();
89
+ }
90
+ };
54
91
  this.handleCancel = () => {
55
- const tr = this.view.state.tr;
56
- const pos = this.getPos();
57
- tr.setSelection(TextSelection.create(tr.doc, pos));
58
- this.view.focus();
59
- this.view.dispatch(tr);
92
+ const attrs = getActualAttrs(this.node);
93
+ if (!this.node.content.size || !attrs.href) {
94
+ this.removeLink();
95
+ }
60
96
  this.closeForm();
61
97
  };
62
98
  this.handleRemove = () => {
63
- const tr = this.view.state.tr;
64
- const pos = this.getPos();
65
- const to = pos + this.node.nodeSize;
66
- tr.replaceWith(pos, to, this.node.content).setSelection(TextSelection.create(tr.doc, pos));
67
- this.view.focus();
68
- this.view.dispatch(tr);
99
+ this.removeLink();
69
100
  this.closeForm();
70
101
  };
71
102
  this.handleSave = (value) => {
72
103
  const tr = this.view.state.tr;
73
- const link = this.node;
104
+ const attrs = getActualAttrs(this.node);
74
105
  const pos = this.getPos();
75
- if (value.href !== link.attrs.href || value.title !== link.attrs.title) {
106
+ if (value.href !== attrs.href || value.title !== attrs.title) {
76
107
  tr.setNodeMarkup(pos, undefined, Object.assign(Object.assign({}, this.node.attrs), { href: value.href, title: value.title }));
77
108
  }
78
- if (value.text !== link.textContent) {
79
- tr.insertText(value.text, pos + 1, pos + this.node.nodeSize - 1);
109
+ if (value.text !== this.node.textContent) {
110
+ tr.delete(pos + 1, pos + this.node.nodeSize - 1);
111
+ tr.insert(pos + 1, schema.text(value.text));
80
112
  }
81
113
  tr.setSelection(TextSelection.create(tr.doc, pos));
82
114
  this.view.focus();
83
115
  this.view.dispatch(tr);
84
116
  this.closeForm();
85
117
  };
118
+ this.removeLink = () => {
119
+ const tr = this.view.state.tr;
120
+ const pos = this.getPos();
121
+ tr.delete(pos, pos + this.node.nodeSize);
122
+ const text = this.node.textContent;
123
+ if (text) {
124
+ tr.insert(pos, schema.text(text));
125
+ }
126
+ tr.setSelection(TextSelection.create(tr.doc, pos));
127
+ this.view.dispatch(tr);
128
+ };
86
129
  this.closeForm = () => {
87
130
  var _a;
88
131
  this.props.popper.destroy();
@@ -19,9 +19,10 @@ export interface LinkValue {
19
19
  href: string;
20
20
  title?: string;
21
21
  }
22
- export declare const LinkForm: React.FC<{
23
- handleCancel: () => void;
24
- handleRemove: () => void;
25
- handleSave: (value: LinkValue) => void;
22
+ export interface LinkFormProps {
23
+ onCancel: () => void;
24
+ onRemove: () => void;
25
+ onSave: (value: LinkValue) => void;
26
26
  value: LinkValue;
27
- }>;
27
+ }
28
+ export declare const LinkForm: React.FC<LinkFormProps>;
@@ -16,9 +16,6 @@
16
16
  import { ManuscriptNodeView } from '@manuscripts/transform';
17
17
  import { BaseNodeProps, BaseNodeView } from './base_node_view';
18
18
  export declare class LinkView<PropsType extends BaseNodeProps> extends BaseNodeView<PropsType> implements ManuscriptNodeView {
19
- initialise: () => void;
20
- updateContents: () => void;
21
- protected createDOM: () => void;
22
19
  }
23
20
  declare const _default: (props: BaseNodeProps, dispatch?: import("..").Dispatch | undefined) => import("../types").NodeViewCreator<LinkView<any>>;
24
21
  export default _default;
@@ -17,13 +17,19 @@ import { EditableBlockProps } from './editable_block';
17
17
  import { LinkView } from './link';
18
18
  export declare class LinkEditableView extends LinkView<EditableBlockProps> {
19
19
  protected popperContainer: HTMLDivElement;
20
+ ignoreMutation: () => boolean;
21
+ initialise: () => void;
22
+ updateContents: () => void;
23
+ protected createDOM: () => void;
20
24
  selectNode: () => void;
21
25
  destroy: () => void;
22
26
  deselectNode: () => void;
23
27
  private showForm;
28
+ private handleClick;
24
29
  private handleCancel;
25
30
  private handleRemove;
26
31
  private handleSave;
32
+ private removeLink;
27
33
  private closeForm;
28
34
  }
29
35
  declare const _default: (props: EditableBlockProps, dispatch?: import("..").Dispatch | undefined) => import("../types").NodeViewCreator<LinkEditableView>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@manuscripts/body-editor",
3
3
  "description": "Prosemirror components for editing and viewing manuscripts",
4
- "version": "1.7.39",
4
+ "version": "1.8.0",
5
5
  "repository": "github:Atypon-OpenSource/manuscripts-body-editor",
6
6
  "license": "Apache-2.0",
7
7
  "main": "dist/cjs",
@@ -31,9 +31,9 @@
31
31
  "@manuscripts/assets": "^0.6.4",
32
32
  "@manuscripts/json-schema": "^2.2.7",
33
33
  "@manuscripts/library": "^1.3.6",
34
- "@manuscripts/style-guide": "1.8.7",
34
+ "@manuscripts/style-guide": "1.11.1",
35
35
  "@manuscripts/track-changes-plugin": "1.7.9",
36
- "@manuscripts/transform": "2.1.13",
36
+ "@manuscripts/transform": "2.2.0",
37
37
  "astrocite-eutils": "^0.16.4",
38
38
  "axios": "^1.6.7",
39
39
  "mathjax-full": "^3.2.2",