@manuscripts/body-editor 3.2.36 → 3.3.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/components/DragAndDropUploader.js +138 -0
- package/dist/cjs/components/views/ExtLinkEditor.js +127 -0
- package/dist/cjs/lib/files.js +11 -0
- package/dist/cjs/versions.js +1 -1
- package/dist/cjs/views/ReactSubView.js +23 -2
- package/dist/cjs/views/accessibility_element.js +1 -0
- package/dist/cjs/views/image_element.js +58 -5
- package/dist/es/components/DragAndDropUploader.js +108 -0
- package/dist/es/components/views/ExtLinkEditor.js +97 -0
- package/dist/es/lib/files.js +11 -0
- package/dist/es/versions.js +1 -1
- package/dist/es/views/ReactSubView.js +21 -2
- package/dist/es/views/accessibility_element.js +1 -0
- package/dist/es/views/image_element.js +35 -5
- package/dist/types/components/DragAndDropUploader.d.ts +9 -0
- package/dist/types/components/views/ExtLinkEditor.d.ts +13 -0
- package/dist/types/lib/files.d.ts +1 -0
- package/dist/types/versions.d.ts +1 -1
- package/dist/types/views/ReactSubView.d.ts +1 -0
- package/dist/types/views/image_element.d.ts +6 -2
- package/package.json +3 -3
- package/styles/AdvancedEditor.css +59 -14
- package/styles/Editor.css +1 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.DragAndDropUploader = void 0;
|
|
30
|
+
const react_1 = __importStar(require("react"));
|
|
31
|
+
const styled_components_1 = __importDefault(require("styled-components"));
|
|
32
|
+
const DragAndDropUploader = ({ upload, onUploadError, allowedFileTypes, maxFileSizeMB, }) => {
|
|
33
|
+
const [isUploading, setIsUploading] = (0, react_1.useState)(false);
|
|
34
|
+
const fileInputRef = (0, react_1.useRef)(null);
|
|
35
|
+
const validateFile = (file) => {
|
|
36
|
+
if (!file) {
|
|
37
|
+
onUploadError('No file selected.');
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (allowedFileTypes && allowedFileTypes.length > 0) {
|
|
41
|
+
if (!allowedFileTypes.includes(file.type)) {
|
|
42
|
+
onUploadError(`Invalid file type. Allowed types: ${allowedFileTypes.join(', ')}`);
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (maxFileSizeMB && file.size > maxFileSizeMB * 1024 * 1024) {
|
|
47
|
+
onUploadError(`File size exceeds limit of ${maxFileSizeMB} MB.`);
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
const processFiles = async (files) => {
|
|
53
|
+
if (files && files.length > 0) {
|
|
54
|
+
const file = files[0];
|
|
55
|
+
if (validateFile(file)) {
|
|
56
|
+
try {
|
|
57
|
+
setIsUploading(true);
|
|
58
|
+
await upload(file);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
onUploadError('Upload failed. Please try again.');
|
|
62
|
+
console.error('Upload error:', error);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
setIsUploading(false);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
onUploadError('No file selected or dropped.');
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const handleDragOver = (e) => {
|
|
74
|
+
if (e.dataTransfer && e.dataTransfer.items) {
|
|
75
|
+
for (const item of e.dataTransfer.items) {
|
|
76
|
+
if (item.kind === 'file' && item.type.startsWith('image/')) {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
e.dataTransfer.dropEffect = 'copy';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const handleDrop = async (e) => {
|
|
84
|
+
e.preventDefault();
|
|
85
|
+
await processFiles(e.dataTransfer.files);
|
|
86
|
+
};
|
|
87
|
+
const handleFileChange = async (e) => {
|
|
88
|
+
await processFiles(e.target.files);
|
|
89
|
+
};
|
|
90
|
+
const handleAreaClick = (event) => {
|
|
91
|
+
const target = event.target;
|
|
92
|
+
if (target.dataset && target.dataset.action) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
fileInputRef.current?.click();
|
|
96
|
+
};
|
|
97
|
+
return (react_1.default.createElement(DragAndDropContainer, { onDragOver: handleDragOver, onDrop: !isUploading ? handleDrop : undefined, onClick: !isUploading ? handleAreaClick : undefined, tabIndex: isUploading ? -1 : 0, "aria-label": "Drag and drop area for file upload, or click to open file dialog.", role: "button", onKeyDown: (e) => {
|
|
98
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
fileInputRef.current?.click();
|
|
101
|
+
}
|
|
102
|
+
} },
|
|
103
|
+
react_1.default.createElement("input", { type: "file", ref: fileInputRef, onChange: handleFileChange, "aria-hidden": "true" }),
|
|
104
|
+
react_1.default.createElement("p", null,
|
|
105
|
+
"Drag or click here to upload a file to link",
|
|
106
|
+
react_1.default.createElement("br", null),
|
|
107
|
+
"or drag items here from the file",
|
|
108
|
+
' ',
|
|
109
|
+
react_1.default.createElement("span", { "data-action": "open-other-files" }, "'Other files'"),
|
|
110
|
+
" in the inspector.")));
|
|
111
|
+
};
|
|
112
|
+
exports.DragAndDropUploader = DragAndDropUploader;
|
|
113
|
+
const DragAndDropContainer = styled_components_1.default.div `
|
|
114
|
+
display: flex;
|
|
115
|
+
flex-direction: column;
|
|
116
|
+
align-items: center;
|
|
117
|
+
justify-content: center;
|
|
118
|
+
padding: 16px;
|
|
119
|
+
border: 1px dashed ${(props) => props.theme.colors.border.secondary};
|
|
120
|
+
border-radius: 8px;
|
|
121
|
+
background-color: ${(props) => props.theme.colors.background.secondary};
|
|
122
|
+
color: ${(props) => props.theme.colors.text.primary};
|
|
123
|
+
font-size: ${(props) => props.theme.font.size.normal};
|
|
124
|
+
|
|
125
|
+
input[type='file'] {
|
|
126
|
+
display: none;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
p {
|
|
130
|
+
margin: 0;
|
|
131
|
+
font-size: 14px;
|
|
132
|
+
text-align: center;
|
|
133
|
+
}
|
|
134
|
+
span {
|
|
135
|
+
text-decoration: underline;
|
|
136
|
+
cursor: pointer;
|
|
137
|
+
}
|
|
138
|
+
`;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.ExtLinkEditor = void 0;
|
|
30
|
+
const style_guide_1 = require("@manuscripts/style-guide");
|
|
31
|
+
const react_1 = __importStar(require("react"));
|
|
32
|
+
const styled_components_1 = __importDefault(require("styled-components"));
|
|
33
|
+
const DragAndDropUploader_1 = require("../DragAndDropUploader");
|
|
34
|
+
const ExtLinkEditor = ({ node, nodePos, view, editorProps, isEditing, setIsEditing, }) => {
|
|
35
|
+
const [uploadError, setUploadError] = (0, react_1.useState)(null);
|
|
36
|
+
const fileManagement = editorProps.fileManagement;
|
|
37
|
+
const extLink = node.attrs.extLink;
|
|
38
|
+
const files = editorProps.getFiles();
|
|
39
|
+
const file = extLink ? files.find((f) => f.id === extLink) : undefined;
|
|
40
|
+
const onUpdate = (newAttrs) => {
|
|
41
|
+
const tr = view.state.tr.setNodeMarkup(nodePos, undefined, {
|
|
42
|
+
...node.attrs,
|
|
43
|
+
...newAttrs,
|
|
44
|
+
});
|
|
45
|
+
view.dispatch(tr);
|
|
46
|
+
};
|
|
47
|
+
const handleFileUpload = async (file) => {
|
|
48
|
+
setUploadError(null);
|
|
49
|
+
const result = await fileManagement.upload(file);
|
|
50
|
+
if (!result) {
|
|
51
|
+
handleUploadError('File upload failed');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
setIsEditing(false);
|
|
55
|
+
onUpdate({ extLink: result.id });
|
|
56
|
+
};
|
|
57
|
+
const handleUploadError = (message) => {
|
|
58
|
+
setUploadError(message);
|
|
59
|
+
};
|
|
60
|
+
const handleRemove = () => {
|
|
61
|
+
setUploadError(null);
|
|
62
|
+
onUpdate({ extLink: '' });
|
|
63
|
+
setIsEditing(false);
|
|
64
|
+
};
|
|
65
|
+
return (react_1.default.createElement("div", null,
|
|
66
|
+
!isEditing && !extLink && (react_1.default.createElement(style_guide_1.IconTextButton, { onClick: () => setIsEditing(true), "aria-label": "Add linked file" },
|
|
67
|
+
react_1.default.createElement(style_guide_1.LinkIcon, null),
|
|
68
|
+
"Add link")),
|
|
69
|
+
isEditing && !extLink && (react_1.default.createElement("div", null,
|
|
70
|
+
react_1.default.createElement(ExtLinkEditorLabel, null, "Link"),
|
|
71
|
+
react_1.default.createElement(ExtLinkEditorContainer, null,
|
|
72
|
+
react_1.default.createElement(DragAndDropUploader_1.DragAndDropUploader, { upload: handleFileUpload, onUploadError: handleUploadError }),
|
|
73
|
+
react_1.default.createElement(CloseUploaderButton, { onClick: () => setIsEditing(false) })))),
|
|
74
|
+
extLink && (react_1.default.createElement(LinkedFileInfoBox, null,
|
|
75
|
+
react_1.default.createElement("p", null,
|
|
76
|
+
react_1.default.createElement(style_guide_1.LinkIcon, null),
|
|
77
|
+
file ? file.name : 'File does not exist.'),
|
|
78
|
+
react_1.default.createElement(style_guide_1.IconButton, { onClick: handleRemove, "aria-label": "Remove linked file" },
|
|
79
|
+
react_1.default.createElement(style_guide_1.DeleteIcon, null)))),
|
|
80
|
+
uploadError && (react_1.default.createElement("div", null,
|
|
81
|
+
react_1.default.createElement("p", { className: "font-semibold" }, "Error:"),
|
|
82
|
+
react_1.default.createElement("p", null, uploadError)))));
|
|
83
|
+
};
|
|
84
|
+
exports.ExtLinkEditor = ExtLinkEditor;
|
|
85
|
+
const ExtLinkEditorLabel = styled_components_1.default.div `
|
|
86
|
+
color: var(--label-color);
|
|
87
|
+
font-size: 18px;
|
|
88
|
+
font-style: normal;
|
|
89
|
+
font-weight: 400;
|
|
90
|
+
line-height: 24px;
|
|
91
|
+
letter-spacing: -0.369px;
|
|
92
|
+
margin-top: 12px;
|
|
93
|
+
cursor: pointer;
|
|
94
|
+
`;
|
|
95
|
+
const ExtLinkEditorContainer = styled_components_1.default.div `
|
|
96
|
+
position: relative;
|
|
97
|
+
`;
|
|
98
|
+
const LinkedFileInfoBox = styled_components_1.default.div `
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: row;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: space-between;
|
|
103
|
+
padding: 4px 8px;
|
|
104
|
+
background-color: ${(props) => props.theme.colors.background.secondary};
|
|
105
|
+
color: ${(props) => props.theme.colors.text.primary};
|
|
106
|
+
border: 1px solid ${(props) => props.theme.colors.border.secondary};
|
|
107
|
+
border-radius: 8px;
|
|
108
|
+
|
|
109
|
+
p {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
justify-content: flex-start;
|
|
113
|
+
gap: 8px;
|
|
114
|
+
|
|
115
|
+
font-family: ${(props) => props.theme.font.family.sans};
|
|
116
|
+
font-weight: ${(props) => props.theme.font.weight.normal};
|
|
117
|
+
font-size: ${(props) => props.theme.font.size.medium};
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
120
|
+
const CloseUploaderButton = (0, styled_components_1.default)(style_guide_1.CloseButton) `
|
|
121
|
+
border: 1px solid ${(props) => props.theme.colors.border.tertiary};
|
|
122
|
+
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.12);
|
|
123
|
+
|
|
124
|
+
position: absolute;
|
|
125
|
+
top: -8px;
|
|
126
|
+
right: -8px;
|
|
127
|
+
`;
|
package/dist/cjs/lib/files.js
CHANGED
|
@@ -31,6 +31,7 @@ const groupFiles = (doc, files) => {
|
|
|
31
31
|
const fileMap = new Map(files.map((f) => [f.id, f]));
|
|
32
32
|
const figures = [];
|
|
33
33
|
const supplements = [];
|
|
34
|
+
const linkedFiles = [];
|
|
34
35
|
const attachments = [];
|
|
35
36
|
const getFile = (href) => {
|
|
36
37
|
const file = fileMap.get(href);
|
|
@@ -67,6 +68,15 @@ const groupFiles = (doc, files) => {
|
|
|
67
68
|
if (figureTypes.includes(node.type)) {
|
|
68
69
|
figures.push(getFigureElementFiles(node, pos));
|
|
69
70
|
}
|
|
71
|
+
if (node.type === transform_1.schema.nodes.image_element) {
|
|
72
|
+
if (node.attrs.extLink) {
|
|
73
|
+
linkedFiles.push({
|
|
74
|
+
node,
|
|
75
|
+
pos,
|
|
76
|
+
file: getFile(node.attrs.extLink),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
70
80
|
if (node.type === transform_1.schema.nodes.supplement) {
|
|
71
81
|
supplements.push({
|
|
72
82
|
node,
|
|
@@ -86,6 +96,7 @@ const groupFiles = (doc, files) => {
|
|
|
86
96
|
figures,
|
|
87
97
|
supplements,
|
|
88
98
|
attachments,
|
|
99
|
+
linkedFiles,
|
|
89
100
|
others: [...fileMap.values()],
|
|
90
101
|
};
|
|
91
102
|
};
|
package/dist/cjs/versions.js
CHANGED
|
@@ -18,11 +18,19 @@ 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.createSubViewAsync = void 0;
|
|
21
22
|
const react_1 = __importDefault(require("react"));
|
|
23
|
+
const react_dom_1 = require("react-dom");
|
|
22
24
|
const client_1 = require("react-dom/client");
|
|
23
25
|
const styled_components_1 = require("styled-components");
|
|
24
26
|
function createSubView(props, Component, componentProps, node, getPos, view, classNames = []) {
|
|
25
27
|
const container = document.createElement('div');
|
|
28
|
+
const Wrapped = createView(props, Component, componentProps, node, getPos, view, classNames, container);
|
|
29
|
+
const root = (0, client_1.createRoot)(container);
|
|
30
|
+
root.render(react_1.default.createElement(Wrapped, null));
|
|
31
|
+
return container;
|
|
32
|
+
}
|
|
33
|
+
function createView(props, Component, componentProps, node, getPos, view, classNames = [], container) {
|
|
26
34
|
container.classList.add('tools-panel');
|
|
27
35
|
if (classNames.length) {
|
|
28
36
|
container.classList.add(...classNames);
|
|
@@ -41,8 +49,21 @@ function createSubView(props, Component, componentProps, node, getPos, view, cla
|
|
|
41
49
|
return (react_1.default.createElement(styled_components_1.ThemeProvider, { theme: props.theme },
|
|
42
50
|
react_1.default.createElement(Component, { nodeAttrs: node.attrs, setNodeAttrs: setNodeAttrs, viewProps: { node, view, getPos }, container: container, ...props, ...componentProps })));
|
|
43
51
|
};
|
|
52
|
+
return Wrapped;
|
|
53
|
+
}
|
|
54
|
+
function createSubViewAsync(props, Component, componentProps, node, getPos, view, classNames = []) {
|
|
55
|
+
const container = document.createElement('div');
|
|
56
|
+
const Wrapped = createView(props, Component, componentProps, node, getPos, view, classNames, container);
|
|
44
57
|
const root = (0, client_1.createRoot)(container);
|
|
45
|
-
|
|
46
|
-
|
|
58
|
+
const res = new Promise((resolve) => {
|
|
59
|
+
queueMicrotask(() => {
|
|
60
|
+
(0, react_dom_1.flushSync)(() => {
|
|
61
|
+
root.render(react_1.default.createElement(Wrapped, null));
|
|
62
|
+
});
|
|
63
|
+
resolve(container);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
return res;
|
|
47
67
|
}
|
|
68
|
+
exports.createSubViewAsync = createSubViewAsync;
|
|
48
69
|
exports.default = createSubView;
|
|
@@ -27,6 +27,7 @@ class AccessibilityElementView extends block_view_1.default {
|
|
|
27
27
|
createElement() {
|
|
28
28
|
super.createElement();
|
|
29
29
|
this.contentDOM.className = 'accessibility_element_input';
|
|
30
|
+
this.contentDOM.setAttribute('contenteditable', 'true');
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
exports.AccessibilityElementView = AccessibilityElementView;
|
|
@@ -14,6 +14,29 @@
|
|
|
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 (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
17
40
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
18
41
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
42
|
};
|
|
@@ -21,10 +44,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
21
44
|
exports.ImageElementView = exports.figurePositions = void 0;
|
|
22
45
|
const style_guide_1 = require("@manuscripts/style-guide");
|
|
23
46
|
const transform_1 = require("@manuscripts/transform");
|
|
47
|
+
const ExtLinkEditor_1 = require("../components/views/ExtLinkEditor");
|
|
24
48
|
const position_menu_1 = require("../lib/position-menu");
|
|
25
49
|
const block_view_1 = __importDefault(require("./block_view"));
|
|
26
50
|
const creators_1 = require("./creators");
|
|
27
|
-
const ReactSubView_1 =
|
|
51
|
+
const ReactSubView_1 = __importStar(require("./ReactSubView"));
|
|
28
52
|
var figurePositions;
|
|
29
53
|
(function (figurePositions) {
|
|
30
54
|
figurePositions["left"] = "half-left";
|
|
@@ -34,6 +58,7 @@ var figurePositions;
|
|
|
34
58
|
class ImageElementView extends block_view_1.default {
|
|
35
59
|
constructor() {
|
|
36
60
|
super(...arguments);
|
|
61
|
+
this.isEditingExtLink = false;
|
|
37
62
|
this.ignoreMutation = () => true;
|
|
38
63
|
this.showPositionMenu = () => {
|
|
39
64
|
const firstFigure = this.getFirstFigure();
|
|
@@ -83,17 +108,24 @@ class ImageElementView extends block_view_1.default {
|
|
|
83
108
|
createElement() {
|
|
84
109
|
this.container = document.createElement('div');
|
|
85
110
|
this.container.classList.add('block');
|
|
86
|
-
this.container.setAttribute('contenteditable', '
|
|
111
|
+
this.container.setAttribute('contenteditable', 'false');
|
|
87
112
|
this.dom.appendChild(this.container);
|
|
88
|
-
this.
|
|
89
|
-
this.
|
|
113
|
+
this.subcontainer = document.createElement('div');
|
|
114
|
+
this.subcontainer.classList.add('figure-block-group');
|
|
115
|
+
this.contentDOM = document.createElement('div');
|
|
116
|
+
this.contentDOM.setAttribute('contenteditable', 'false');
|
|
90
117
|
this.contentDOM.setAttribute('id', this.node.attrs.id);
|
|
91
|
-
this.
|
|
118
|
+
this.contentDOM.classList.add('figure-block');
|
|
119
|
+
this.subcontainer.appendChild(this.contentDOM);
|
|
120
|
+
this.container.appendChild(this.subcontainer);
|
|
92
121
|
this.addTools();
|
|
93
122
|
}
|
|
94
123
|
updateContents() {
|
|
95
124
|
super.updateContents();
|
|
96
125
|
this.addTools();
|
|
126
|
+
if (this.node.type === transform_1.schema.nodes.image_element) {
|
|
127
|
+
this.addExternalLinkedFileEditor();
|
|
128
|
+
}
|
|
97
129
|
}
|
|
98
130
|
addTools() {
|
|
99
131
|
this.addPositionMenu();
|
|
@@ -159,6 +191,27 @@ class ImageElementView extends block_view_1.default {
|
|
|
159
191
|
this.view.dispatch(tr);
|
|
160
192
|
this.figurePosition = position;
|
|
161
193
|
}
|
|
194
|
+
addExternalLinkedFileEditor() {
|
|
195
|
+
if (this.props.dispatch && this.props.theme) {
|
|
196
|
+
const componentProps = {
|
|
197
|
+
node: this.node,
|
|
198
|
+
nodePos: this.getPos(),
|
|
199
|
+
view: this.view,
|
|
200
|
+
editorProps: this.props,
|
|
201
|
+
isEditing: this.isEditingExtLink,
|
|
202
|
+
setIsEditing: (val) => {
|
|
203
|
+
this.isEditingExtLink = val;
|
|
204
|
+
this.updateContents();
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
return (0, ReactSubView_1.createSubViewAsync)(this.props, ExtLinkEditor_1.ExtLinkEditor, componentProps, this.node, this.getPos, this.view, ['ext-link-editor-container']).then((elem) => {
|
|
208
|
+
this.extLinkEditorContainer?.remove();
|
|
209
|
+
this.extLinkEditorContainer = elem;
|
|
210
|
+
this.subcontainer?.appendChild(this.extLinkEditorContainer);
|
|
211
|
+
return;
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
162
215
|
}
|
|
163
216
|
exports.ImageElementView = ImageElementView;
|
|
164
217
|
exports.default = (0, creators_1.createNodeView)(ImageElementView);
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import React, { useRef, useState, } from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
export const DragAndDropUploader = ({ upload, onUploadError, allowedFileTypes, maxFileSizeMB, }) => {
|
|
4
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
5
|
+
const fileInputRef = useRef(null);
|
|
6
|
+
const validateFile = (file) => {
|
|
7
|
+
if (!file) {
|
|
8
|
+
onUploadError('No file selected.');
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
if (allowedFileTypes && allowedFileTypes.length > 0) {
|
|
12
|
+
if (!allowedFileTypes.includes(file.type)) {
|
|
13
|
+
onUploadError(`Invalid file type. Allowed types: ${allowedFileTypes.join(', ')}`);
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (maxFileSizeMB && file.size > maxFileSizeMB * 1024 * 1024) {
|
|
18
|
+
onUploadError(`File size exceeds limit of ${maxFileSizeMB} MB.`);
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
};
|
|
23
|
+
const processFiles = async (files) => {
|
|
24
|
+
if (files && files.length > 0) {
|
|
25
|
+
const file = files[0];
|
|
26
|
+
if (validateFile(file)) {
|
|
27
|
+
try {
|
|
28
|
+
setIsUploading(true);
|
|
29
|
+
await upload(file);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
onUploadError('Upload failed. Please try again.');
|
|
33
|
+
console.error('Upload error:', error);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
setIsUploading(false);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
onUploadError('No file selected or dropped.');
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const handleDragOver = (e) => {
|
|
45
|
+
if (e.dataTransfer && e.dataTransfer.items) {
|
|
46
|
+
for (const item of e.dataTransfer.items) {
|
|
47
|
+
if (item.kind === 'file' && item.type.startsWith('image/')) {
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
e.dataTransfer.dropEffect = 'copy';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const handleDrop = async (e) => {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
await processFiles(e.dataTransfer.files);
|
|
57
|
+
};
|
|
58
|
+
const handleFileChange = async (e) => {
|
|
59
|
+
await processFiles(e.target.files);
|
|
60
|
+
};
|
|
61
|
+
const handleAreaClick = (event) => {
|
|
62
|
+
const target = event.target;
|
|
63
|
+
if (target.dataset && target.dataset.action) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
fileInputRef.current?.click();
|
|
67
|
+
};
|
|
68
|
+
return (React.createElement(DragAndDropContainer, { onDragOver: handleDragOver, onDrop: !isUploading ? handleDrop : undefined, onClick: !isUploading ? handleAreaClick : undefined, tabIndex: isUploading ? -1 : 0, "aria-label": "Drag and drop area for file upload, or click to open file dialog.", role: "button", onKeyDown: (e) => {
|
|
69
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
fileInputRef.current?.click();
|
|
72
|
+
}
|
|
73
|
+
} },
|
|
74
|
+
React.createElement("input", { type: "file", ref: fileInputRef, onChange: handleFileChange, "aria-hidden": "true" }),
|
|
75
|
+
React.createElement("p", null,
|
|
76
|
+
"Drag or click here to upload a file to link",
|
|
77
|
+
React.createElement("br", null),
|
|
78
|
+
"or drag items here from the file",
|
|
79
|
+
' ',
|
|
80
|
+
React.createElement("span", { "data-action": "open-other-files" }, "'Other files'"),
|
|
81
|
+
" in the inspector.")));
|
|
82
|
+
};
|
|
83
|
+
const DragAndDropContainer = styled.div `
|
|
84
|
+
display: flex;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
align-items: center;
|
|
87
|
+
justify-content: center;
|
|
88
|
+
padding: 16px;
|
|
89
|
+
border: 1px dashed ${(props) => props.theme.colors.border.secondary};
|
|
90
|
+
border-radius: 8px;
|
|
91
|
+
background-color: ${(props) => props.theme.colors.background.secondary};
|
|
92
|
+
color: ${(props) => props.theme.colors.text.primary};
|
|
93
|
+
font-size: ${(props) => props.theme.font.size.normal};
|
|
94
|
+
|
|
95
|
+
input[type='file'] {
|
|
96
|
+
display: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
p {
|
|
100
|
+
margin: 0;
|
|
101
|
+
font-size: 14px;
|
|
102
|
+
text-align: center;
|
|
103
|
+
}
|
|
104
|
+
span {
|
|
105
|
+
text-decoration: underline;
|
|
106
|
+
cursor: pointer;
|
|
107
|
+
}
|
|
108
|
+
`;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { CloseButton, DeleteIcon, IconButton, IconTextButton, LinkIcon, } from '@manuscripts/style-guide';
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { DragAndDropUploader } from '../DragAndDropUploader';
|
|
5
|
+
export const ExtLinkEditor = ({ node, nodePos, view, editorProps, isEditing, setIsEditing, }) => {
|
|
6
|
+
const [uploadError, setUploadError] = useState(null);
|
|
7
|
+
const fileManagement = editorProps.fileManagement;
|
|
8
|
+
const extLink = node.attrs.extLink;
|
|
9
|
+
const files = editorProps.getFiles();
|
|
10
|
+
const file = extLink ? files.find((f) => f.id === extLink) : undefined;
|
|
11
|
+
const onUpdate = (newAttrs) => {
|
|
12
|
+
const tr = view.state.tr.setNodeMarkup(nodePos, undefined, {
|
|
13
|
+
...node.attrs,
|
|
14
|
+
...newAttrs,
|
|
15
|
+
});
|
|
16
|
+
view.dispatch(tr);
|
|
17
|
+
};
|
|
18
|
+
const handleFileUpload = async (file) => {
|
|
19
|
+
setUploadError(null);
|
|
20
|
+
const result = await fileManagement.upload(file);
|
|
21
|
+
if (!result) {
|
|
22
|
+
handleUploadError('File upload failed');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
setIsEditing(false);
|
|
26
|
+
onUpdate({ extLink: result.id });
|
|
27
|
+
};
|
|
28
|
+
const handleUploadError = (message) => {
|
|
29
|
+
setUploadError(message);
|
|
30
|
+
};
|
|
31
|
+
const handleRemove = () => {
|
|
32
|
+
setUploadError(null);
|
|
33
|
+
onUpdate({ extLink: '' });
|
|
34
|
+
setIsEditing(false);
|
|
35
|
+
};
|
|
36
|
+
return (React.createElement("div", null,
|
|
37
|
+
!isEditing && !extLink && (React.createElement(IconTextButton, { onClick: () => setIsEditing(true), "aria-label": "Add linked file" },
|
|
38
|
+
React.createElement(LinkIcon, null),
|
|
39
|
+
"Add link")),
|
|
40
|
+
isEditing && !extLink && (React.createElement("div", null,
|
|
41
|
+
React.createElement(ExtLinkEditorLabel, null, "Link"),
|
|
42
|
+
React.createElement(ExtLinkEditorContainer, null,
|
|
43
|
+
React.createElement(DragAndDropUploader, { upload: handleFileUpload, onUploadError: handleUploadError }),
|
|
44
|
+
React.createElement(CloseUploaderButton, { onClick: () => setIsEditing(false) })))),
|
|
45
|
+
extLink && (React.createElement(LinkedFileInfoBox, null,
|
|
46
|
+
React.createElement("p", null,
|
|
47
|
+
React.createElement(LinkIcon, null),
|
|
48
|
+
file ? file.name : 'File does not exist.'),
|
|
49
|
+
React.createElement(IconButton, { onClick: handleRemove, "aria-label": "Remove linked file" },
|
|
50
|
+
React.createElement(DeleteIcon, null)))),
|
|
51
|
+
uploadError && (React.createElement("div", null,
|
|
52
|
+
React.createElement("p", { className: "font-semibold" }, "Error:"),
|
|
53
|
+
React.createElement("p", null, uploadError)))));
|
|
54
|
+
};
|
|
55
|
+
const ExtLinkEditorLabel = styled.div `
|
|
56
|
+
color: var(--label-color);
|
|
57
|
+
font-size: 18px;
|
|
58
|
+
font-style: normal;
|
|
59
|
+
font-weight: 400;
|
|
60
|
+
line-height: 24px;
|
|
61
|
+
letter-spacing: -0.369px;
|
|
62
|
+
margin-top: 12px;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
`;
|
|
65
|
+
const ExtLinkEditorContainer = styled.div `
|
|
66
|
+
position: relative;
|
|
67
|
+
`;
|
|
68
|
+
const LinkedFileInfoBox = styled.div `
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: row;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: space-between;
|
|
73
|
+
padding: 4px 8px;
|
|
74
|
+
background-color: ${(props) => props.theme.colors.background.secondary};
|
|
75
|
+
color: ${(props) => props.theme.colors.text.primary};
|
|
76
|
+
border: 1px solid ${(props) => props.theme.colors.border.secondary};
|
|
77
|
+
border-radius: 8px;
|
|
78
|
+
|
|
79
|
+
p {
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
justify-content: flex-start;
|
|
83
|
+
gap: 8px;
|
|
84
|
+
|
|
85
|
+
font-family: ${(props) => props.theme.font.family.sans};
|
|
86
|
+
font-weight: ${(props) => props.theme.font.weight.normal};
|
|
87
|
+
font-size: ${(props) => props.theme.font.size.medium};
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
const CloseUploaderButton = styled(CloseButton) `
|
|
91
|
+
border: 1px solid ${(props) => props.theme.colors.border.tertiary};
|
|
92
|
+
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.12);
|
|
93
|
+
|
|
94
|
+
position: absolute;
|
|
95
|
+
top: -8px;
|
|
96
|
+
right: -8px;
|
|
97
|
+
`;
|
package/dist/es/lib/files.js
CHANGED
|
@@ -27,6 +27,7 @@ export const groupFiles = (doc, files) => {
|
|
|
27
27
|
const fileMap = new Map(files.map((f) => [f.id, f]));
|
|
28
28
|
const figures = [];
|
|
29
29
|
const supplements = [];
|
|
30
|
+
const linkedFiles = [];
|
|
30
31
|
const attachments = [];
|
|
31
32
|
const getFile = (href) => {
|
|
32
33
|
const file = fileMap.get(href);
|
|
@@ -63,6 +64,15 @@ export const groupFiles = (doc, files) => {
|
|
|
63
64
|
if (figureTypes.includes(node.type)) {
|
|
64
65
|
figures.push(getFigureElementFiles(node, pos));
|
|
65
66
|
}
|
|
67
|
+
if (node.type === schema.nodes.image_element) {
|
|
68
|
+
if (node.attrs.extLink) {
|
|
69
|
+
linkedFiles.push({
|
|
70
|
+
node,
|
|
71
|
+
pos,
|
|
72
|
+
file: getFile(node.attrs.extLink),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
66
76
|
if (node.type === schema.nodes.supplement) {
|
|
67
77
|
supplements.push({
|
|
68
78
|
node,
|
|
@@ -82,6 +92,7 @@ export const groupFiles = (doc, files) => {
|
|
|
82
92
|
figures,
|
|
83
93
|
supplements,
|
|
84
94
|
attachments,
|
|
95
|
+
linkedFiles,
|
|
85
96
|
others: [...fileMap.values()],
|
|
86
97
|
};
|
|
87
98
|
};
|
package/dist/es/versions.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '3.
|
|
1
|
+
export const VERSION = '3.3.0';
|
|
2
2
|
export const MATHJAX_VERSION = '3.2.2';
|
|
@@ -14,10 +14,17 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import React from 'react';
|
|
17
|
+
import { flushSync } from 'react-dom';
|
|
17
18
|
import { createRoot } from 'react-dom/client';
|
|
18
19
|
import { ThemeProvider } from 'styled-components';
|
|
19
20
|
function createSubView(props, Component, componentProps, node, getPos, view, classNames = []) {
|
|
20
21
|
const container = document.createElement('div');
|
|
22
|
+
const Wrapped = createView(props, Component, componentProps, node, getPos, view, classNames, container);
|
|
23
|
+
const root = createRoot(container);
|
|
24
|
+
root.render(React.createElement(Wrapped, null));
|
|
25
|
+
return container;
|
|
26
|
+
}
|
|
27
|
+
function createView(props, Component, componentProps, node, getPos, view, classNames = [], container) {
|
|
21
28
|
container.classList.add('tools-panel');
|
|
22
29
|
if (classNames.length) {
|
|
23
30
|
container.classList.add(...classNames);
|
|
@@ -36,8 +43,20 @@ function createSubView(props, Component, componentProps, node, getPos, view, cla
|
|
|
36
43
|
return (React.createElement(ThemeProvider, { theme: props.theme },
|
|
37
44
|
React.createElement(Component, { nodeAttrs: node.attrs, setNodeAttrs: setNodeAttrs, viewProps: { node, view, getPos }, container: container, ...props, ...componentProps })));
|
|
38
45
|
};
|
|
46
|
+
return Wrapped;
|
|
47
|
+
}
|
|
48
|
+
export function createSubViewAsync(props, Component, componentProps, node, getPos, view, classNames = []) {
|
|
49
|
+
const container = document.createElement('div');
|
|
50
|
+
const Wrapped = createView(props, Component, componentProps, node, getPos, view, classNames, container);
|
|
39
51
|
const root = createRoot(container);
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
const res = new Promise((resolve) => {
|
|
53
|
+
queueMicrotask(() => {
|
|
54
|
+
flushSync(() => {
|
|
55
|
+
root.render(React.createElement(Wrapped, null));
|
|
56
|
+
});
|
|
57
|
+
resolve(container);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
return res;
|
|
42
61
|
}
|
|
43
62
|
export default createSubView;
|
|
@@ -21,6 +21,7 @@ export class AccessibilityElementView extends BlockView {
|
|
|
21
21
|
createElement() {
|
|
22
22
|
super.createElement();
|
|
23
23
|
this.contentDOM.className = 'accessibility_element_input';
|
|
24
|
+
this.contentDOM.setAttribute('contenteditable', 'true');
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
export default createNodeView(AccessibilityElementView);
|
|
@@ -15,10 +15,11 @@
|
|
|
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
19
|
import { createPositionMenuWrapper } from '../lib/position-menu';
|
|
19
20
|
import BlockView from './block_view';
|
|
20
21
|
import { createNodeView } from './creators';
|
|
21
|
-
import ReactSubView from './ReactSubView';
|
|
22
|
+
import ReactSubView, { createSubViewAsync } from './ReactSubView';
|
|
22
23
|
export var figurePositions;
|
|
23
24
|
(function (figurePositions) {
|
|
24
25
|
figurePositions["left"] = "half-left";
|
|
@@ -28,6 +29,7 @@ export var figurePositions;
|
|
|
28
29
|
export class ImageElementView extends BlockView {
|
|
29
30
|
constructor() {
|
|
30
31
|
super(...arguments);
|
|
32
|
+
this.isEditingExtLink = false;
|
|
31
33
|
this.ignoreMutation = () => true;
|
|
32
34
|
this.showPositionMenu = () => {
|
|
33
35
|
const firstFigure = this.getFirstFigure();
|
|
@@ -77,17 +79,24 @@ export class ImageElementView extends BlockView {
|
|
|
77
79
|
createElement() {
|
|
78
80
|
this.container = document.createElement('div');
|
|
79
81
|
this.container.classList.add('block');
|
|
80
|
-
this.container.setAttribute('contenteditable', '
|
|
82
|
+
this.container.setAttribute('contenteditable', 'false');
|
|
81
83
|
this.dom.appendChild(this.container);
|
|
82
|
-
this.
|
|
83
|
-
this.
|
|
84
|
+
this.subcontainer = document.createElement('div');
|
|
85
|
+
this.subcontainer.classList.add('figure-block-group');
|
|
86
|
+
this.contentDOM = document.createElement('div');
|
|
87
|
+
this.contentDOM.setAttribute('contenteditable', 'false');
|
|
84
88
|
this.contentDOM.setAttribute('id', this.node.attrs.id);
|
|
85
|
-
this.
|
|
89
|
+
this.contentDOM.classList.add('figure-block');
|
|
90
|
+
this.subcontainer.appendChild(this.contentDOM);
|
|
91
|
+
this.container.appendChild(this.subcontainer);
|
|
86
92
|
this.addTools();
|
|
87
93
|
}
|
|
88
94
|
updateContents() {
|
|
89
95
|
super.updateContents();
|
|
90
96
|
this.addTools();
|
|
97
|
+
if (this.node.type === schema.nodes.image_element) {
|
|
98
|
+
this.addExternalLinkedFileEditor();
|
|
99
|
+
}
|
|
91
100
|
}
|
|
92
101
|
addTools() {
|
|
93
102
|
this.addPositionMenu();
|
|
@@ -153,5 +162,26 @@ export class ImageElementView extends BlockView {
|
|
|
153
162
|
this.view.dispatch(tr);
|
|
154
163
|
this.figurePosition = position;
|
|
155
164
|
}
|
|
165
|
+
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
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
156
186
|
}
|
|
157
187
|
export default createNodeView(ImageElementView);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface DragAndDropUploaderProps {
|
|
3
|
+
upload: (file: File) => Promise<void>;
|
|
4
|
+
onUploadError: (message: string) => void;
|
|
5
|
+
allowedFileTypes?: string[];
|
|
6
|
+
maxFileSizeMB?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare const DragAndDropUploader: React.FC<DragAndDropUploaderProps>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ManuscriptNode } from '@manuscripts/transform';
|
|
2
|
+
import { EditorView } from 'prosemirror-view';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { EditorProps } from '../../configs/ManuscriptsEditor';
|
|
5
|
+
export interface ExtLinkEditorProps {
|
|
6
|
+
node: ManuscriptNode;
|
|
7
|
+
nodePos: number;
|
|
8
|
+
view: EditorView;
|
|
9
|
+
editorProps: EditorProps;
|
|
10
|
+
isEditing: boolean;
|
|
11
|
+
setIsEditing: (val: boolean) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare const ExtLinkEditor: React.FC<ExtLinkEditorProps>;
|
package/dist/types/versions.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "3.
|
|
1
|
+
export declare const VERSION = "3.3.0";
|
|
2
2
|
export declare const MATHJAX_VERSION = "3.2.2";
|
|
@@ -28,4 +28,5 @@ export interface ReactViewComponentProps<NodeT extends ManuscriptNode> {
|
|
|
28
28
|
container: HTMLDivElement;
|
|
29
29
|
}
|
|
30
30
|
declare function createSubView<T extends Trackable<ManuscriptNode>>(props: EditorProps, Component: React.FC<any>, componentProps: object, node: T, getPos: () => number, view: ManuscriptEditorView, classNames?: string[]): HTMLDivElement;
|
|
31
|
+
export declare function createSubViewAsync<T extends Trackable<ManuscriptNode>>(props: EditorProps, Component: React.FC<any>, componentProps: object, node: T, getPos: () => number, view: ManuscriptEditorView, classNames?: string[]): Promise<HTMLDivElement>;
|
|
31
32
|
export default createSubView;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
16
|
+
import { ImageElementNode } from '@manuscripts/transform';
|
|
17
17
|
import { Trackable } from '../types';
|
|
18
18
|
import BlockView from './block_view';
|
|
19
19
|
export declare enum figurePositions {
|
|
@@ -21,10 +21,13 @@ export declare enum figurePositions {
|
|
|
21
21
|
right = "half-right",
|
|
22
22
|
default = ""
|
|
23
23
|
}
|
|
24
|
-
export declare class ImageElementView extends BlockView<Trackable<
|
|
24
|
+
export declare class ImageElementView extends BlockView<Trackable<ImageElementNode>> {
|
|
25
25
|
container: HTMLElement;
|
|
26
|
+
subcontainer: HTMLElement;
|
|
27
|
+
extLinkEditorContainer: HTMLDivElement;
|
|
26
28
|
private positionMenuWrapper;
|
|
27
29
|
private figurePosition;
|
|
30
|
+
private isEditingExtLink;
|
|
28
31
|
ignoreMutation: () => boolean;
|
|
29
32
|
createDOM(): void;
|
|
30
33
|
createElement(): void;
|
|
@@ -35,6 +38,7 @@ export declare class ImageElementView extends BlockView<Trackable<FigureElementN
|
|
|
35
38
|
private getAllFigures;
|
|
36
39
|
private showPositionMenu;
|
|
37
40
|
private updateAllFiguresPosition;
|
|
41
|
+
private addExternalLinkedFileEditor;
|
|
38
42
|
}
|
|
39
43
|
declare const _default: (props: import("../configs/ManuscriptsEditor").EditorProps, dispatch?: import("..").Dispatch | undefined) => import("../types").NodeViewCreator<ImageElementView>;
|
|
40
44
|
export default _default;
|
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": "3.
|
|
4
|
+
"version": "3.3.0",
|
|
5
5
|
"repository": "github:Atypon-OpenSource/manuscripts-body-editor",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"main": "dist/cjs",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"@citation-js/plugin-pubmed": "0.3.0",
|
|
39
39
|
"@citation-js/plugin-ris": "0.7.18",
|
|
40
40
|
"@manuscripts/json-schema": "2.2.12",
|
|
41
|
-
"@manuscripts/style-guide": "3.
|
|
41
|
+
"@manuscripts/style-guide": "3.2.0",
|
|
42
42
|
"@manuscripts/track-changes-plugin": "2.0.9",
|
|
43
|
-
"@manuscripts/transform": "4.2.
|
|
43
|
+
"@manuscripts/transform": "4.2.11",
|
|
44
44
|
"@popperjs/core": "2.11.8",
|
|
45
45
|
"citeproc": "2.4.63",
|
|
46
46
|
"codemirror": "5.65.19",
|
|
@@ -129,8 +129,8 @@
|
|
|
129
129
|
border: 1px solid #f2f2f2;
|
|
130
130
|
border-radius: 4px;
|
|
131
131
|
|
|
132
|
-
padding:
|
|
133
|
-
gap:
|
|
132
|
+
padding: 8px;
|
|
133
|
+
gap: 8px !important;
|
|
134
134
|
|
|
135
135
|
grid-template-columns: repeat(3, auto) !important;
|
|
136
136
|
grid-template-rows: repeat(1, minmax(min-content, max-content)) [caption listing] auto !important;
|
|
@@ -140,6 +140,27 @@
|
|
|
140
140
|
justify-self: center;
|
|
141
141
|
position: relative;
|
|
142
142
|
}
|
|
143
|
+
.ProseMirror .figure-block-group {
|
|
144
|
+
width: 100%;
|
|
145
|
+
border: 1px solid #f2f2f2;
|
|
146
|
+
border-radius: 4px;
|
|
147
|
+
position: relative;
|
|
148
|
+
padding: 8px;
|
|
149
|
+
gap: 8px !important;
|
|
150
|
+
|
|
151
|
+
grid-template-columns: repeat(3, auto) !important;
|
|
152
|
+
grid-template-rows: repeat(1, minmax(min-content, max-content)) [caption listing] auto !important;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.ProseMirror .figure-block-group .figure-block {
|
|
156
|
+
border: none;
|
|
157
|
+
border-radius: 0;
|
|
158
|
+
position: static;
|
|
159
|
+
padding: 0;
|
|
160
|
+
margin: 0 !important;
|
|
161
|
+
grid-template-columns: auto;
|
|
162
|
+
grid-template-rows: auto;
|
|
163
|
+
}
|
|
143
164
|
|
|
144
165
|
.ProseMirror .figure-group {
|
|
145
166
|
border: 1px solid #f2f2f2;
|
|
@@ -267,8 +288,12 @@
|
|
|
267
288
|
padding: 16px 32px;
|
|
268
289
|
}
|
|
269
290
|
|
|
270
|
-
.ProseMirror
|
|
291
|
+
.ProseMirror a[data-action] {
|
|
271
292
|
text-decoration: underline;
|
|
293
|
+
cursor: pointer;
|
|
294
|
+
}
|
|
295
|
+
.ProseMirror a[data-action]:hover {
|
|
296
|
+
text-decoration: none;
|
|
272
297
|
}
|
|
273
298
|
|
|
274
299
|
.ProseMirror .figure.placeholder.over {
|
|
@@ -345,7 +370,9 @@
|
|
|
345
370
|
text-align: center !important;
|
|
346
371
|
}
|
|
347
372
|
|
|
348
|
-
.ProseMirror .block-figure_element .position-menu,
|
|
373
|
+
.ProseMirror .block-figure_element .position-menu,
|
|
374
|
+
.ProseMirror .block-image_element .position-menu,
|
|
375
|
+
ProseMirror .block-embed .position-menu {
|
|
349
376
|
position: absolute;
|
|
350
377
|
top: 15px;
|
|
351
378
|
right: 35px;
|
|
@@ -371,9 +398,8 @@
|
|
|
371
398
|
align-items: center;
|
|
372
399
|
pointer-events: auto;
|
|
373
400
|
transition: opacity 0.2s;
|
|
374
|
-
background-color:
|
|
375
|
-
border: 1px solid #
|
|
376
|
-
|
|
401
|
+
background-color: #fafafa;
|
|
402
|
+
border: 1px solid #f2f2f2;
|
|
377
403
|
}
|
|
378
404
|
.ProseMirror .figure-block .figure:hover .drag-handler {
|
|
379
405
|
opacity: 1;
|
|
@@ -382,13 +408,17 @@
|
|
|
382
408
|
/* Drop area blue dotted outline */
|
|
383
409
|
.ProseMirror .figure-block .figure.drop-target-above,
|
|
384
410
|
.ProseMirror .figure-block .figure.drop-target-below {
|
|
385
|
-
border: 2px dotted #
|
|
411
|
+
border: 2px dotted #20aedf !important;
|
|
386
412
|
transition: border 0.2s, box-shadow 0.2s;
|
|
387
413
|
position: relative;
|
|
388
414
|
}
|
|
389
415
|
|
|
390
|
-
.ProseMirror .figure-block .figure.drop-target-above::before {
|
|
391
|
-
|
|
416
|
+
.ProseMirror .figure-block .figure.drop-target-above::before {
|
|
417
|
+
top: 0;
|
|
418
|
+
}
|
|
419
|
+
.ProseMirror .figure-block .figure.drop-target-below::after {
|
|
420
|
+
bottom: 0;
|
|
421
|
+
}
|
|
392
422
|
|
|
393
423
|
.ProseMirror .figure.dragging {
|
|
394
424
|
opacity: 0.6;
|
|
@@ -1052,8 +1082,12 @@ figure.block:has(.equation.selected-suggestion) {
|
|
|
1052
1082
|
.block,
|
|
1053
1083
|
.tracking-visible
|
|
1054
1084
|
.selected-suggestion[data-track-op='move'][data-track-status='pending']
|
|
1055
|
-
.block
|
|
1056
|
-
|
|
1085
|
+
.block,
|
|
1086
|
+
.block:has(
|
|
1087
|
+
figure.selected-suggestion,
|
|
1088
|
+
[data-track-op='move'][data-track-status='pending']
|
|
1089
|
+
) {
|
|
1090
|
+
box-shadow: inset 3px 0 0 var(--updated-border-color);
|
|
1057
1091
|
}
|
|
1058
1092
|
|
|
1059
1093
|
.tracking-visible .selected-suggestion[data-track-op='set_attrs'] .block,
|
|
@@ -1527,7 +1561,8 @@ th:hover > .table-context-menu-button,
|
|
|
1527
1561
|
justify-content: center;
|
|
1528
1562
|
}
|
|
1529
1563
|
|
|
1530
|
-
.ProseMirror .accessibility_element
|
|
1564
|
+
.ProseMirror .accessibility_element,
|
|
1565
|
+
.ProseMirror .ext-link-editor-container {
|
|
1531
1566
|
margin-top: 12px;
|
|
1532
1567
|
grid-column-start: 1;
|
|
1533
1568
|
grid-column-end: -1;
|
|
@@ -1535,10 +1570,15 @@ th:hover > .table-context-menu-button,
|
|
|
1535
1570
|
display: none;
|
|
1536
1571
|
}
|
|
1537
1572
|
|
|
1538
|
-
.ProseMirror .show_accessibility_element .accessibility_element
|
|
1573
|
+
.ProseMirror .show_accessibility_element .accessibility_element,
|
|
1574
|
+
.ProseMirror .show_accessibility_element .ext-link-editor-container {
|
|
1539
1575
|
display: block;
|
|
1540
1576
|
}
|
|
1541
1577
|
|
|
1578
|
+
.ProseMirror .ext-link-editor-container {
|
|
1579
|
+
position: static;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1542
1582
|
.ProseMirror .accessibility_element_label {
|
|
1543
1583
|
color: var(--label-color);
|
|
1544
1584
|
font-size: 18px;
|
|
@@ -1550,6 +1590,11 @@ th:hover > .table-context-menu-button,
|
|
|
1550
1590
|
cursor: pointer;
|
|
1551
1591
|
}
|
|
1552
1592
|
|
|
1593
|
+
.ProseMirror .accessibility_element_input:focus,
|
|
1594
|
+
.ProseMirror .accessibility_element_input:focus-visible {
|
|
1595
|
+
outline: none;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1553
1598
|
.ProseMirror .accessibility_element_input {
|
|
1554
1599
|
border-radius: 4px;
|
|
1555
1600
|
border: 1px solid var(--accepted-bg-color);
|