@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.
- package/dist/cjs/commands.js +14 -12
- package/dist/cjs/components/views/LinkForm.js +30 -36
- package/dist/cjs/views/link.js +0 -35
- package/dist/cjs/views/link_editable.js +70 -27
- package/dist/es/commands.js +14 -12
- package/dist/es/components/views/LinkForm.js +29 -35
- package/dist/es/views/link.js +0 -35
- package/dist/es/views/link_editable.js +71 -28
- package/dist/types/components/views/LinkForm.d.ts +6 -5
- package/dist/types/views/link.d.ts +0 -3
- package/dist/types/views/link_editable.d.ts +6 -0
- package/package.json +3 -3
package/dist/cjs/commands.js
CHANGED
|
@@ -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
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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,
|
|
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;
|
package/dist/cjs/views/link.js
CHANGED
|
@@ -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.
|
|
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 = (
|
|
40
|
-
if (event) {
|
|
41
|
-
event.preventDefault();
|
|
42
|
-
}
|
|
73
|
+
this.showForm = () => {
|
|
43
74
|
if (!this.props.getCapabilities().editArticle) {
|
|
44
75
|
return;
|
|
45
76
|
}
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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
|
|
110
|
+
const attrs = (0, track_changes_utils_1.getActualAttrs)(this.node);
|
|
80
111
|
const pos = this.getPos();
|
|
81
|
-
if (value.href !==
|
|
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 !==
|
|
85
|
-
tr.
|
|
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();
|
package/dist/es/commands.js
CHANGED
|
@@ -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
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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,
|
|
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
|
+
};
|
package/dist/es/views/link.js
CHANGED
|
@@ -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.
|
|
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 = (
|
|
34
|
-
if (event) {
|
|
35
|
-
event.preventDefault();
|
|
36
|
-
}
|
|
67
|
+
this.showForm = () => {
|
|
37
68
|
if (!this.props.getCapabilities().editArticle) {
|
|
38
69
|
return;
|
|
39
70
|
}
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
|
104
|
+
const attrs = getActualAttrs(this.node);
|
|
74
105
|
const pos = this.getPos();
|
|
75
|
-
if (value.href !==
|
|
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 !==
|
|
79
|
-
tr.
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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.
|
|
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.
|
|
34
|
+
"@manuscripts/style-guide": "1.11.1",
|
|
35
35
|
"@manuscripts/track-changes-plugin": "1.7.9",
|
|
36
|
-
"@manuscripts/transform": "2.
|
|
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",
|