@seafile/sdoc-editor 0.5.42 → 0.5.44
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/basic-sdk/editor/comment-article.js +7 -9
- package/dist/basic-sdk/editor/editable-article.js +7 -9
- package/dist/basic-sdk/extension/plugins/image/helpers.js +26 -2
- package/dist/basic-sdk/extension/plugins/image/render-elem.js +10 -4
- package/dist/basic-sdk/extension/plugins/link/dialog/add-link-dialog/index.js +4 -2
- package/dist/basic-sdk/extension/plugins/markdown/plugin.js +6 -12
- package/dist/basic-sdk/extension/toolbar/context-toolbar/index.js +9 -1
- package/dist/basic-sdk/utils/diff.js +24 -0
- package/dist/components/doc-operations/revision-operations/changes-count/index.js +16 -15
- package/dist/components/doc-operations/revision-operations/index.js +1 -1
- package/dist/pages/published-revision-viewer.js +1 -1
- package/dist/pages/simple-editor.js +1 -1
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import { useCursors } from '../cursor/use-cursors';
|
|
|
9
9
|
import { INTERNAL_EVENT } from '../constants';
|
|
10
10
|
import { usePipDecorate } from '../decorates';
|
|
11
11
|
import EventBus from '../utils/event-bus';
|
|
12
|
-
import { IMAGE } from '../extension/constants';
|
|
12
|
+
import { IMAGE, IMAGE_BLOCK } from '../extension/constants';
|
|
13
13
|
import RenderCommentEditorCustomRenderElement from '../extension/render/render-comment-editor-element';
|
|
14
14
|
import EventProxy from '../utils/event-handler';
|
|
15
15
|
const CommentArticle = _ref => {
|
|
@@ -40,14 +40,12 @@ const CommentArticle = _ref => {
|
|
|
40
40
|
const {
|
|
41
41
|
selection
|
|
42
42
|
} = editor;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (imageNodeEntry) return;
|
|
50
|
-
}
|
|
43
|
+
// Do not scroll into view, when focus on image
|
|
44
|
+
const [imageNodeEntry] = Editor.nodes(editor, {
|
|
45
|
+
match: n => [IMAGE, IMAGE_BLOCK].includes(n.type),
|
|
46
|
+
at: selection
|
|
47
|
+
});
|
|
48
|
+
if (imageNodeEntry) return;
|
|
51
49
|
const focusedNode = Node.get(editor, selection.focus.path);
|
|
52
50
|
const domNode = ReactEditor.toDOMNode(editor, focusedNode);
|
|
53
51
|
if (!domNode) return;
|
|
@@ -14,7 +14,7 @@ import EventBus from '../utils/event-bus';
|
|
|
14
14
|
import { ArticleContainer } from '../layout';
|
|
15
15
|
import { useScrollContext } from '../hooks/use-scroll-context';
|
|
16
16
|
import { SetNodeToDecorations } from '../highlight';
|
|
17
|
-
import { IMAGE } from '../extension/constants';
|
|
17
|
+
import { IMAGE, IMAGE_BLOCK } from '../extension/constants';
|
|
18
18
|
import { isPreventResetTableSelectedRange } from '../extension/plugins/table/helpers';
|
|
19
19
|
const EditableArticle = _ref => {
|
|
20
20
|
let {
|
|
@@ -144,14 +144,12 @@ const EditableArticle = _ref => {
|
|
|
144
144
|
const {
|
|
145
145
|
selection
|
|
146
146
|
} = editor;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (imageNodeEntry) return;
|
|
154
|
-
}
|
|
147
|
+
// Do not scroll into view, when focus on image
|
|
148
|
+
const [imageNodeEntry] = Editor.nodes(editor, {
|
|
149
|
+
match: n => [IMAGE, IMAGE_BLOCK].includes(n.type),
|
|
150
|
+
at: selection
|
|
151
|
+
});
|
|
152
|
+
if (imageNodeEntry) return;
|
|
155
153
|
const focusedNode = Node.get(editor, selection.focus.path);
|
|
156
154
|
const domNode = ReactEditor.toDOMNode(editor, focusedNode);
|
|
157
155
|
if (!domNode) return;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
2
|
import urlJoin from 'url-join';
|
|
3
3
|
import { Editor, Range, Transforms, Path, Node } from '@seafile/slate';
|
|
4
|
+
import { ReactEditor } from '@seafile/slate-react';
|
|
4
5
|
import context from '../../../../context';
|
|
5
6
|
import EventBus from '../../../utils/event-bus';
|
|
6
|
-
import { generateEmptyElement, getNodeType, isTextNode, getParentNode, focusEditor } from '../../core';
|
|
7
|
+
import { generateEmptyElement, getNodeType, isTextNode, getParentNode, focusEditor, getSelectedElems } from '../../core';
|
|
7
8
|
import { isList } from '../../toolbar/side-toolbar/helpers';
|
|
8
9
|
import { COMMENT_EDITOR, INTERNAL_EVENT } from '../../../constants';
|
|
9
|
-
import { CODE_BLOCK, ELEMENT_TYPE, IMAGE, INSERT_POSITION } from '../../constants';
|
|
10
|
+
import { CODE_BLOCK, ELEMENT_TYPE, IMAGE, IMAGE_BLOCK, INSERT_POSITION } from '../../constants';
|
|
10
11
|
export const isInsertImageMenuDisabled = (editor, readonly) => {
|
|
11
12
|
if (readonly) return true;
|
|
12
13
|
const {
|
|
@@ -154,4 +155,27 @@ export const insertImageFiles = (files, editor, targetPath) => {
|
|
|
154
155
|
context.uploadLocalImage(files).then(fileUrl => {
|
|
155
156
|
insertImage(editor, fileUrl, targetPath, INSERT_POSITION.AFTER);
|
|
156
157
|
});
|
|
158
|
+
};
|
|
159
|
+
export const selectImageWhenSelectPartial = (event, editor, imageNode, isImageSelected) => {
|
|
160
|
+
if (isImageSelected) return;
|
|
161
|
+
const isMouseLeftDown = event.buttons === 1;
|
|
162
|
+
if (!isMouseLeftDown) return;
|
|
163
|
+
const {
|
|
164
|
+
selection
|
|
165
|
+
} = editor;
|
|
166
|
+
if (Range.isCollapsed(selection)) return;
|
|
167
|
+
let imagePath = ReactEditor.findPath(editor, imageNode);
|
|
168
|
+
if (imageNode.type === IMAGE_BLOCK) {
|
|
169
|
+
const imageIndex = imageNode.children.findIndex(item => item.type === IMAGE);
|
|
170
|
+
imagePath = imagePath.concat([imageIndex]);
|
|
171
|
+
}
|
|
172
|
+
const isIncludedSelection = Range.includes(selection, imagePath);
|
|
173
|
+
if (isIncludedSelection) return;
|
|
174
|
+
const focusRange = _objectSpread(_objectSpread({}, selection), {}, {
|
|
175
|
+
focus: {
|
|
176
|
+
offset: 0,
|
|
177
|
+
path: imagePath
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
focusEditor(editor, focusRange);
|
|
157
181
|
};
|
|
@@ -4,7 +4,7 @@ import { ReactEditor, useSelected, useReadOnly } from '@seafile/slate-react';
|
|
|
4
4
|
import { Transforms, Editor } from '@seafile/slate';
|
|
5
5
|
import classNames from 'classnames';
|
|
6
6
|
import { withTranslation } from 'react-i18next';
|
|
7
|
-
import { getImageURL, updateImage } from './helpers';
|
|
7
|
+
import { getImageURL, selectImageWhenSelectPartial, updateImage } from './helpers';
|
|
8
8
|
import EventBus from '../../../utils/event-bus';
|
|
9
9
|
import { INTERNAL_EVENT } from '../../../constants';
|
|
10
10
|
import ImageHoverMenu from './hover-menu';
|
|
@@ -195,7 +195,8 @@ const Image = _ref => {
|
|
|
195
195
|
return /*#__PURE__*/React.createElement(React.Fragment, null, isShowImagePlaceholder && /*#__PURE__*/React.createElement("span", Object.assign({
|
|
196
196
|
className: classNames('sdoc-image-wrapper', className)
|
|
197
197
|
}, attributes, {
|
|
198
|
-
style: _objectSpread({}, style)
|
|
198
|
+
style: _objectSpread({}, style),
|
|
199
|
+
onMouseOver: e => selectImageWhenSelectPartial(e, editor, element, isSelected)
|
|
199
200
|
}), /*#__PURE__*/React.createElement("img", {
|
|
200
201
|
ref: imageRef,
|
|
201
202
|
src: imagePlaceholder,
|
|
@@ -206,7 +207,8 @@ const Image = _ref => {
|
|
|
206
207
|
"data-id": element.id,
|
|
207
208
|
className: classNames('sdoc-image-wrapper', className)
|
|
208
209
|
}, attributes, {
|
|
209
|
-
style: _objectSpread({}, style)
|
|
210
|
+
style: _objectSpread({}, style),
|
|
211
|
+
onMouseOver: e => selectImageWhenSelectPartial(e, editor, element, isSelected)
|
|
210
212
|
}), /*#__PURE__*/React.createElement("span", {
|
|
211
213
|
className: "sdoc-image-inner"
|
|
212
214
|
}, /*#__PURE__*/React.createElement("span", {
|
|
@@ -294,6 +296,8 @@ export function renderImageBlock(props, editor) {
|
|
|
294
296
|
const {
|
|
295
297
|
align
|
|
296
298
|
} = element;
|
|
299
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
300
|
+
const isSelected = useSelected();
|
|
297
301
|
let justifyContent = '';
|
|
298
302
|
if (align) {
|
|
299
303
|
justifyContent = align === 'left' ? 'start' : align === 'right' ? 'end' : align;
|
|
@@ -305,5 +309,7 @@ export function renderImageBlock(props, editor) {
|
|
|
305
309
|
padding: '5px 0px',
|
|
306
310
|
justifyContent: "".concat(justifyContent)
|
|
307
311
|
}
|
|
308
|
-
}, attributes
|
|
312
|
+
}, attributes, {
|
|
313
|
+
onMouseOver: e => selectImageWhenSelectPartial(e, editor, element, isSelected)
|
|
314
|
+
}), children);
|
|
309
315
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React, { Fragment, useCallback, useState } from 'react';
|
|
1
|
+
import React, { Fragment, useCallback, useState, useMemo } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Alert, Label } from 'reactstrap';
|
|
4
|
+
import { getEditorString } from '../../../../core';
|
|
4
5
|
import { insertLink, updateLink, checkLink } from '../../helpers';
|
|
5
6
|
const AddLinkDialog = _ref => {
|
|
6
7
|
let {
|
|
@@ -23,7 +24,8 @@ const AddLinkDialog = _ref => {
|
|
|
23
24
|
title: '',
|
|
24
25
|
href: ''
|
|
25
26
|
};
|
|
26
|
-
const
|
|
27
|
+
const initTitle = useMemo(() => oldTitle ? oldTitle : getEditorString(editor, editor.selection), [editor, oldTitle]);
|
|
28
|
+
const [title, setTitle] = useState(initTitle);
|
|
27
29
|
const [url, setURL] = useState(oldURL);
|
|
28
30
|
const submit = useCallback(() => {
|
|
29
31
|
setLinkErrorMessage('');
|
|
@@ -43,21 +43,15 @@ const getBeforeText = editor => {
|
|
|
43
43
|
range: null
|
|
44
44
|
};
|
|
45
45
|
const {
|
|
46
|
-
anchor
|
|
46
|
+
anchor,
|
|
47
|
+
focus
|
|
47
48
|
} = selection;
|
|
48
|
-
// Find the block element above the current text
|
|
49
|
-
const block = Editor.above(editor, {
|
|
50
|
-
match: n => Editor.isBlock(editor, n)
|
|
51
|
-
});
|
|
52
|
-
if (block == null) return {
|
|
53
|
-
beforeText: '',
|
|
54
|
-
range: null
|
|
55
|
-
};
|
|
56
|
-
const blockPath = block[1];
|
|
57
|
-
const blockStart = Editor.start(editor, blockPath); // The starting position of the block element
|
|
58
49
|
const range = {
|
|
59
50
|
anchor,
|
|
60
|
-
focus:
|
|
51
|
+
focus: {
|
|
52
|
+
path: focus.path,
|
|
53
|
+
offset: 0
|
|
54
|
+
}
|
|
61
55
|
};
|
|
62
56
|
const beforeText = Editor.string(editor, range) || '';
|
|
63
57
|
return {
|
|
@@ -47,10 +47,18 @@ const ContextToolbar = () => {
|
|
|
47
47
|
const onMouseDown = useCallback(event => {
|
|
48
48
|
event.preventDefault(); // prevent toolbar from taking focus away from editor
|
|
49
49
|
}, []);
|
|
50
|
+
const onMouseMove = useCallback(e => {
|
|
51
|
+
const isMouseLeftDown = e.buttons === 1;
|
|
52
|
+
if (isMouseLeftDown) {
|
|
53
|
+
const el = ref.current;
|
|
54
|
+
el.removeAttribute('style');
|
|
55
|
+
}
|
|
56
|
+
}, []);
|
|
50
57
|
return createPortal( /*#__PURE__*/React.createElement("div", {
|
|
51
58
|
ref: ref,
|
|
52
59
|
className: "sdoc-context-toolbar",
|
|
53
|
-
onMouseDown: onMouseDown
|
|
60
|
+
onMouseDown: onMouseDown,
|
|
61
|
+
onMouseOver: onMouseMove
|
|
54
62
|
}, /*#__PURE__*/React.createElement(MenuGroup, null, /*#__PURE__*/React.createElement(TextStyleMenuList, {
|
|
55
63
|
editor: editor,
|
|
56
64
|
idPrefix: 'sdoc_context_toolbar'
|
|
@@ -37,6 +37,30 @@ export const getTopLevelChanges = changes => {
|
|
|
37
37
|
return Array.from(new Set(topLevelChanges));
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
// Merge consecutive added areas or deleted areas
|
|
41
|
+
export const getMergedChanges = (topLevelChanges, diffValue) => {
|
|
42
|
+
const topLevelChangesValue = [];
|
|
43
|
+
const changes = [];
|
|
44
|
+
diffValue.forEach(item => {
|
|
45
|
+
if (topLevelChanges.includes(item.id)) {
|
|
46
|
+
const obj = {
|
|
47
|
+
id: item.id,
|
|
48
|
+
value: item
|
|
49
|
+
};
|
|
50
|
+
topLevelChangesValue.push(obj);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
topLevelChangesValue.forEach(item => {
|
|
54
|
+
var _changes;
|
|
55
|
+
const preChange = (_changes = changes[changes.length - 1]) === null || _changes === void 0 ? void 0 : _changes.value;
|
|
56
|
+
const curChange = item.value;
|
|
57
|
+
if ((curChange === null || curChange === void 0 ? void 0 : curChange.add) && (preChange === null || preChange === void 0 ? void 0 : preChange.add)) return;
|
|
58
|
+
if ((curChange === null || curChange === void 0 ? void 0 : curChange.delete) && (preChange === null || preChange === void 0 ? void 0 : preChange.delete)) return;
|
|
59
|
+
changes.push(item);
|
|
60
|
+
});
|
|
61
|
+
return changes.map(item => item.id);
|
|
62
|
+
};
|
|
63
|
+
|
|
40
64
|
// Depth facilitates each child node, adding diffType to each end node
|
|
41
65
|
const generatorDiffElement = function (element, diffType) {
|
|
42
66
|
let style = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import Tooltip from '../../../tooltip';
|
|
4
|
-
import { getTopLevelChanges } from '../../../../basic-sdk/utils/diff';
|
|
4
|
+
import { getTopLevelChanges, getMergedChanges } from '../../../../basic-sdk/utils/diff';
|
|
5
5
|
import './index.css';
|
|
6
6
|
const ChangesCount = _ref => {
|
|
7
7
|
let {
|
|
8
|
-
|
|
8
|
+
allChanges
|
|
9
9
|
} = _ref;
|
|
10
10
|
const {
|
|
11
11
|
t
|
|
12
12
|
} = useTranslation();
|
|
13
13
|
const [currentDiffIndex, setDiffIndex] = useState(0);
|
|
14
|
-
const [
|
|
14
|
+
const [changes, setChanges] = useState([]);
|
|
15
15
|
const intervalRef = useRef();
|
|
16
16
|
useEffect(() => {
|
|
17
17
|
// Article rendering is delayed, so we need to wait for the Article to render before we can get the changes
|
|
@@ -25,18 +25,19 @@ const ChangesCount = _ref => {
|
|
|
25
25
|
}
|
|
26
26
|
}, 100);
|
|
27
27
|
}).then(() => {
|
|
28
|
-
if (changes.length !== 0) {
|
|
29
|
-
const topLevelChanges = getTopLevelChanges(changes);
|
|
30
|
-
|
|
28
|
+
if (allChanges.changes.length !== 0) {
|
|
29
|
+
const topLevelChanges = getTopLevelChanges(allChanges.changes);
|
|
30
|
+
const changes = getMergedChanges(topLevelChanges, allChanges.value);
|
|
31
|
+
setChanges(changes);
|
|
31
32
|
}
|
|
32
33
|
});
|
|
33
34
|
return () => {
|
|
34
35
|
intervalRef.current && clearInterval(intervalRef.current);
|
|
35
36
|
};
|
|
36
|
-
}, [
|
|
37
|
+
}, [allChanges]);
|
|
37
38
|
const jumpToElement = useCallback(currentDiffIndex => {
|
|
38
39
|
setDiffIndex(currentDiffIndex);
|
|
39
|
-
const change =
|
|
40
|
+
const change = changes[currentDiffIndex];
|
|
40
41
|
const changeElement = document.querySelectorAll("[data-id=\"".concat(change, "\"]"))[0];
|
|
41
42
|
if (changeElement) {
|
|
42
43
|
const scrollContainer = document.getElementById('sdoc-scroll-container');
|
|
@@ -46,31 +47,31 @@ const ChangesCount = _ref => {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
49
|
-
}, [
|
|
50
|
+
}, [changes, currentDiffIndex]);
|
|
50
51
|
const lastChange = useCallback(() => {
|
|
51
52
|
if (currentDiffIndex === 0) {
|
|
52
|
-
jumpToElement(
|
|
53
|
+
jumpToElement(changes.length - 1);
|
|
53
54
|
return;
|
|
54
55
|
}
|
|
55
56
|
jumpToElement(currentDiffIndex - 1);
|
|
56
57
|
|
|
57
58
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
58
|
-
}, [
|
|
59
|
+
}, [changes, currentDiffIndex]);
|
|
59
60
|
const nextChange = useCallback(() => {
|
|
60
|
-
if (currentDiffIndex ===
|
|
61
|
+
if (currentDiffIndex === changes.length - 1) {
|
|
61
62
|
jumpToElement(0);
|
|
62
63
|
return;
|
|
63
64
|
}
|
|
64
65
|
jumpToElement(currentDiffIndex + 1);
|
|
65
66
|
|
|
66
67
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
|
-
}, [
|
|
68
|
-
if (!Array.isArray(
|
|
68
|
+
}, [changes, currentDiffIndex]);
|
|
69
|
+
if (!Array.isArray(changes) || changes.length === 0) {
|
|
69
70
|
return /*#__PURE__*/React.createElement("div", {
|
|
70
71
|
className: "sdoc-revision-changes-container d-flex align-items-center pl-2 pr-2 ml-4"
|
|
71
72
|
}, t('No_changes'));
|
|
72
73
|
}
|
|
73
|
-
const changesCount =
|
|
74
|
+
const changesCount = changes.length;
|
|
74
75
|
return /*#__PURE__*/React.createElement("div", {
|
|
75
76
|
className: "sdoc-revision-changes-container d-flex align-items-center ml-4"
|
|
76
77
|
}, /*#__PURE__*/React.createElement("div", {
|
|
@@ -192,7 +192,7 @@ const RevisionOperations = _ref => {
|
|
|
192
192
|
setShowTip(false);
|
|
193
193
|
}, []);
|
|
194
194
|
return /*#__PURE__*/React.createElement(React.Fragment, null, !isSdocRevision && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(MoreRevisionOperations, null), /*#__PURE__*/React.createElement(Revisions, null)), isSdocRevision && isShowChanges && /*#__PURE__*/React.createElement(ChangesCount, {
|
|
195
|
-
|
|
195
|
+
allChanges: changes
|
|
196
196
|
}), isSdocRevision && /*#__PURE__*/React.createElement(ViewChanges, {
|
|
197
197
|
isShowChanges: isShowChanges,
|
|
198
198
|
onViewChangesToggle: onViewChangesToggle
|
|
@@ -50,7 +50,7 @@ const PublishedRevisionViewer = () => {
|
|
|
50
50
|
});
|
|
51
51
|
}, []);
|
|
52
52
|
const setDiffChanges = useCallback(diff => {
|
|
53
|
-
setChanges(
|
|
53
|
+
setChanges(diff);
|
|
54
54
|
}, []);
|
|
55
55
|
const handleViewChangesToggle = useCallback(isShowChanges => {
|
|
56
56
|
setShowChanges(isShowChanges);
|
|
@@ -40,7 +40,7 @@ const SimpleEditor = _ref => {
|
|
|
40
40
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
41
41
|
}, []);
|
|
42
42
|
const setDiffChanges = useCallback(diff => {
|
|
43
|
-
setChanges(diff
|
|
43
|
+
setChanges(diff);
|
|
44
44
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
45
45
|
}, [isShowChanges]);
|
|
46
46
|
const handleViewChangesToggle = useCallback(isShowChanges => {
|