@seafile/sdoc-editor 0.5.41 → 0.5.42
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/constants/index.js +2 -1
- package/dist/basic-sdk/editor/comment-article.js +2 -1
- package/dist/basic-sdk/editor/editable-article.js +1 -0
- package/dist/basic-sdk/extension/commons/{history-files → file-insert-dialog}/index.js +115 -95
- package/dist/basic-sdk/extension/commons/file-insert-dialog/style.css +76 -0
- package/dist/basic-sdk/extension/commons/insert-element-dialog/index.js +9 -8
- package/dist/basic-sdk/extension/commons/menu-shortcut-indicator/style.css +5 -7
- package/dist/basic-sdk/extension/constants/element-type.js +1 -0
- package/dist/basic-sdk/extension/constants/index.js +2 -2
- package/dist/basic-sdk/extension/plugins/code-block/hover-menu/index.css +9 -0
- package/dist/basic-sdk/extension/plugins/code-block/hover-menu/index.js +41 -24
- package/dist/basic-sdk/extension/plugins/sdoc-link/helpers.js +62 -13
- package/dist/basic-sdk/extension/plugins/sdoc-link/index.js +3 -2
- package/dist/basic-sdk/extension/plugins/sdoc-link/plugin.js +33 -23
- package/dist/basic-sdk/extension/plugins/sdoc-link/{render-elem.css → render/render-elem.css} +5 -1
- package/dist/basic-sdk/extension/plugins/sdoc-link/{render-elem.js → render/render-elem.js} +6 -6
- package/dist/basic-sdk/extension/plugins/sdoc-link/render/render-file-link-temp-input.js +26 -0
- package/dist/basic-sdk/extension/render/custom-element.js +6 -1
- package/dist/components/doc-operations/more-operations.js +13 -10
- package/dist/components/doc-operations/style.css +15 -0
- package/package.json +1 -1
- package/public/locales/en/sdoc-editor.json +3 -1
- package/public/locales/zh_CN/sdoc-editor.json +3 -1
- package/dist/basic-sdk/extension/commons/history-files/index.css +0 -70
|
@@ -24,7 +24,8 @@ export const INTERNAL_EVENT = {
|
|
|
24
24
|
TABLE_SHOW_DRAG_HANDLER: 'table_show_drag_handler',
|
|
25
25
|
TABLE_HIDE_DRAG_HANDLER: 'table_show_drag_handler',
|
|
26
26
|
ON_PRINT: 'on_print',
|
|
27
|
-
COMMENT_EDITOR_POST_COMMENT: 'comment_editor_post_comment'
|
|
27
|
+
COMMENT_EDITOR_POST_COMMENT: 'comment_editor_post_comment',
|
|
28
|
+
CLOSE_FILE_INSET_DIALOG: 'close_file_insert_dialog'
|
|
28
29
|
};
|
|
29
30
|
export const REVISION_DIFF_KEY = 'diff';
|
|
30
31
|
export const REVISION_DIFF_VALUE = '1';
|
|
@@ -85,7 +85,8 @@ const CommentArticle = _ref => {
|
|
|
85
85
|
onCompositionStart: eventProxy.onCompositionStart,
|
|
86
86
|
onCompositionUpdate: eventProxy.onCompositionUpdate,
|
|
87
87
|
onCompositionEnd: eventProxy.onCompositionEnd,
|
|
88
|
-
onKeyDown: onKeyDown
|
|
88
|
+
onKeyDown: onKeyDown,
|
|
89
|
+
onBeforeInput: eventProxy.onBeforeInput
|
|
89
90
|
}));
|
|
90
91
|
};
|
|
91
92
|
export default CommentArticle;
|
|
@@ -178,6 +178,7 @@ const EditableArticle = _ref => {
|
|
|
178
178
|
decorate: decorate,
|
|
179
179
|
onCut: eventProxy.onCut,
|
|
180
180
|
onCopy: eventProxy.onCopy,
|
|
181
|
+
onCompositionStart: eventProxy.onCompositionStart,
|
|
181
182
|
id: "sdoc-editor"
|
|
182
183
|
})), /*#__PURE__*/React.createElement(SideToolbar, null), isShowComment && /*#__PURE__*/React.createElement(CommentWrapper, {
|
|
183
184
|
editor: editor,
|
|
@@ -1,33 +1,42 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
|
-
import React, { useCallback, useEffect,
|
|
3
|
-
import { Editor } from '@seafile/slate';
|
|
2
|
+
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
3
|
import { ReactEditor } from '@seafile/slate-react';
|
|
5
|
-
import {
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { Editor } from '@seafile/slate';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
|
-
import debounce from 'lodash.debounce';
|
|
8
|
-
import toaster from '../../../../components/toast';
|
|
9
|
-
import EventBus from '../../../utils/event-bus';
|
|
10
7
|
import context from '../../../../context';
|
|
11
|
-
import { focusEditor } from '../../core';
|
|
12
|
-
import { insertSdocFileLink } from '../../plugins/sdoc-link/helpers';
|
|
13
8
|
import { LocalStorage, isEnglish } from '../../../../utils';
|
|
14
|
-
import { INTERNAL_EVENT } from '../../../constants';
|
|
15
9
|
import { EXTERNAL_EVENT } from '../../../../constants';
|
|
10
|
+
import EventBus from '../../../utils/event-bus';
|
|
11
|
+
import { INTERNAL_EVENT } from '../../../constants';
|
|
16
12
|
import { SDOC_LINK } from '../../constants';
|
|
17
|
-
import '
|
|
18
|
-
|
|
13
|
+
import toaster from '../../../../components/toast';
|
|
14
|
+
import { insertSdocFileLink, insertTextWhenRemoveFileNameCollector, removeTempInput } from '../../plugins/sdoc-link/helpers';
|
|
15
|
+
import './style.css';
|
|
16
|
+
const FileLinkInsertDialog = _ref => {
|
|
19
17
|
let {
|
|
20
18
|
editor,
|
|
21
|
-
|
|
22
|
-
closeDialog
|
|
23
|
-
t
|
|
19
|
+
element,
|
|
20
|
+
closeDialog
|
|
24
21
|
} = _ref;
|
|
25
|
-
const
|
|
26
|
-
const
|
|
22
|
+
const eventBus = EventBus.getInstance();
|
|
23
|
+
const historyFileWrapperRef = useRef(document.querySelector('.sdoc-history-files-wrapper'));
|
|
27
24
|
const [files, setFiles] = useState([]);
|
|
28
|
-
const [position, setPosition] = useState({
|
|
29
|
-
|
|
25
|
+
const [position, setPosition] = useState({
|
|
26
|
+
top: 0,
|
|
27
|
+
left: 0
|
|
28
|
+
});
|
|
30
29
|
const [newFileName, setNewFileName] = useState('');
|
|
30
|
+
const {
|
|
31
|
+
t
|
|
32
|
+
} = useTranslation();
|
|
33
|
+
const [header, setHeader] = useState(t('Recent_visited'));
|
|
34
|
+
const deleteInputAndInsertText = useCallback(function () {
|
|
35
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
36
|
+
args[_key] = arguments[_key];
|
|
37
|
+
}
|
|
38
|
+
return insertTextWhenRemoveFileNameCollector(editor, element, ...args);
|
|
39
|
+
}, [editor, element]);
|
|
31
40
|
const getPosition = useCallback(() => {
|
|
32
41
|
const {
|
|
33
42
|
selection
|
|
@@ -35,80 +44,95 @@ const HistoryFiles = _ref => {
|
|
|
35
44
|
const nodeEntry = Editor.node(editor, selection);
|
|
36
45
|
const domNode = ReactEditor.toDOMNode(editor, nodeEntry[0]);
|
|
37
46
|
if (domNode) {
|
|
47
|
+
const topGap = 20;
|
|
48
|
+
const leftGap = 5;
|
|
38
49
|
const {
|
|
39
50
|
top,
|
|
40
|
-
|
|
51
|
+
left
|
|
41
52
|
} = domNode.getBoundingClientRect();
|
|
53
|
+
let popoverTop = top + topGap;
|
|
54
|
+
let popoverLeft = left + leftGap;
|
|
55
|
+
// Insert gap between the popover and the selected node
|
|
56
|
+
// file item height 32px, header height 32px, add button height 32px, margin-top 8px, max-height 306px
|
|
57
|
+
const popoverHeight = Math.min(files.length * 32 + 32 * 3 + 8, 300);
|
|
58
|
+
// Insert gap between the popover and the selected node
|
|
59
|
+
const popoverBottomY = top + popoverHeight + topGap;
|
|
60
|
+
const viewportHeight = window.innerHeight;
|
|
61
|
+
if (popoverBottomY > viewportHeight) {
|
|
62
|
+
// 8px for the gap between the popover and the bottom of the viewport
|
|
63
|
+
const counterTopGap = 8;
|
|
64
|
+
popoverTop = top - popoverHeight - counterTopGap;
|
|
65
|
+
}
|
|
42
66
|
setPosition({
|
|
43
|
-
|
|
44
|
-
|
|
67
|
+
top: popoverTop,
|
|
68
|
+
left: popoverLeft
|
|
45
69
|
});
|
|
46
70
|
}
|
|
47
71
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
48
|
-
}, []);
|
|
72
|
+
}, [files]);
|
|
49
73
|
const onClick = useCallback(e => {
|
|
50
|
-
|
|
74
|
+
var _historyFileWrapperRe, _historyFileWrapperRe2;
|
|
75
|
+
const isClickInside = (_historyFileWrapperRe = historyFileWrapperRef.current) === null || _historyFileWrapperRe === void 0 ? void 0 : (_historyFileWrapperRe2 = _historyFileWrapperRe.contains) === null || _historyFileWrapperRe2 === void 0 ? void 0 : _historyFileWrapperRe2.call(_historyFileWrapperRe, e.target);
|
|
76
|
+
if (isClickInside) return;
|
|
77
|
+
deleteInputAndInsertText();
|
|
51
78
|
closeDialog();
|
|
52
|
-
|
|
53
|
-
}, []);
|
|
79
|
+
}, [closeDialog, deleteInputAndInsertText]);
|
|
54
80
|
const onScroll = useCallback(e => {
|
|
55
81
|
getPosition();
|
|
56
|
-
|
|
57
|
-
}, []);
|
|
82
|
+
}, [getPosition]);
|
|
58
83
|
const getHistoryFiles = useCallback(e => {
|
|
59
84
|
const files = LocalStorage.getItem('sdoc-recent-files') || [];
|
|
60
85
|
setFiles(files);
|
|
61
86
|
}, []);
|
|
62
|
-
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
getHistoryFiles();
|
|
89
|
+
}, [getHistoryFiles]);
|
|
90
|
+
const onKeydown = useCallback(e => {
|
|
63
91
|
const {
|
|
64
|
-
|
|
65
|
-
} =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
92
|
+
key
|
|
93
|
+
} = e;
|
|
94
|
+
switch (key) {
|
|
95
|
+
case 'Escape':
|
|
96
|
+
deleteInputAndInsertText();
|
|
97
|
+
closeDialog();
|
|
98
|
+
break;
|
|
99
|
+
case 'ArrowRight':
|
|
100
|
+
case 'ArrowLeft':
|
|
101
|
+
deleteInputAndInsertText();
|
|
102
|
+
closeDialog();
|
|
103
|
+
break;
|
|
104
|
+
case 'ArrowUp':
|
|
105
|
+
case 'ArrowDown':
|
|
106
|
+
deleteInputAndInsertText();
|
|
107
|
+
closeDialog();
|
|
108
|
+
break;
|
|
109
|
+
default:
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}, [closeDialog, deleteInputAndInsertText]);
|
|
113
|
+
const onInsertLink = useCallback(params => {
|
|
114
|
+
insertSdocFileLink(editor, params.data.obj_name, params.data.doc_uuid);
|
|
115
|
+
removeTempInput();
|
|
116
|
+
}, [editor]);
|
|
69
117
|
useEffect(() => {
|
|
70
118
|
getPosition();
|
|
71
|
-
getHistoryFiles();
|
|
72
|
-
setTimeout(() => {
|
|
73
|
-
historyFilesInputRef.current.focus();
|
|
74
|
-
}, 0);
|
|
75
119
|
const sdocScrollContainer = document.getElementById('sdoc-scroll-container');
|
|
120
|
+
document.addEventListener('click', onClick);
|
|
121
|
+
document.addEventListener('keydown', onKeydown);
|
|
76
122
|
sdocScrollContainer.addEventListener('scroll', onScroll);
|
|
77
|
-
const eventBus = EventBus.getInstance();
|
|
78
123
|
const unsubscribeInsertLink = eventBus.subscribe(EXTERNAL_EVENT.INSERT_LINK, onInsertLink);
|
|
79
|
-
|
|
124
|
+
const unsubscribeCloseDialog = eventBus.subscribe(INTERNAL_EVENT.CLOSE_FILE_INSET_DIALOG, closeDialog);
|
|
80
125
|
return () => {
|
|
81
126
|
sdocScrollContainer.removeEventListener('scroll', onScroll);
|
|
82
|
-
unsubscribeInsertLink();
|
|
83
127
|
document.removeEventListener('click', onClick);
|
|
128
|
+
document.removeEventListener('keydown', onKeydown);
|
|
129
|
+
unsubscribeInsertLink();
|
|
130
|
+
unsubscribeCloseDialog();
|
|
84
131
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const onKeydown = useCallback(e => {
|
|
88
|
-
const {
|
|
89
|
-
key,
|
|
90
|
-
target
|
|
91
|
-
} = e;
|
|
92
|
-
if (key === 'Backspace' && !target.selectionStart && !target.selectionEnd && !target.value) {
|
|
93
|
-
focusEditor(editor);
|
|
94
|
-
closeDialog();
|
|
95
|
-
}
|
|
96
|
-
}, [closeDialog, editor]);
|
|
97
|
-
const onCompositionStart = e => {
|
|
98
|
-
e.stopPropagation();
|
|
99
|
-
historyFilesInputRef.current.typing = true;
|
|
100
|
-
};
|
|
101
|
-
const onCompositionEnd = e => {
|
|
102
|
-
e.stopPropagation();
|
|
103
|
-
historyFilesInputRef.current.typing = false;
|
|
104
|
-
onSearch(e);
|
|
105
|
-
};
|
|
106
|
-
const onSearch = useCallback(async e => {
|
|
107
|
-
// Ignore when entering Chinese
|
|
108
|
-
if (historyFilesInputRef.current.typing) return;
|
|
109
|
-
|
|
132
|
+
}, [closeDialog, editor, eventBus, files, getPosition, onClick, onInsertLink, onKeydown, onScroll]);
|
|
133
|
+
const onSearch = useCallback(async searchText => {
|
|
110
134
|
// Show history files when search is empty
|
|
111
|
-
if (
|
|
135
|
+
if (searchText.trim().length === 0) {
|
|
112
136
|
setHeader(t('Recent_visited'));
|
|
113
137
|
setNewFileName('');
|
|
114
138
|
getHistoryFiles();
|
|
@@ -116,20 +140,20 @@ const HistoryFiles = _ref => {
|
|
|
116
140
|
}
|
|
117
141
|
|
|
118
142
|
// Cannot be found if the search is less than three characters.
|
|
119
|
-
if (isEnglish(
|
|
143
|
+
if (isEnglish(searchText.trim()) && searchText.length < 3) {
|
|
120
144
|
setFiles([]);
|
|
121
|
-
setHeader(t('
|
|
122
|
-
setNewFileName(
|
|
145
|
+
setHeader(t('Enter_more_character_start_search'));
|
|
146
|
+
setNewFileName(searchText);
|
|
123
147
|
return;
|
|
124
148
|
}
|
|
125
149
|
try {
|
|
126
150
|
var _res$data;
|
|
127
|
-
const res = await context.searchSdocFiles(
|
|
151
|
+
const res = await context.searchSdocFiles(searchText, 1, 10);
|
|
128
152
|
if (res === null || res === void 0 ? void 0 : (_res$data = res.data) === null || _res$data === void 0 ? void 0 : _res$data.results) {
|
|
129
153
|
let newFiles = res.data.results;
|
|
130
154
|
if (newFiles.length === 0) {
|
|
131
155
|
setHeader(t('The_document_does_not_exist'));
|
|
132
|
-
setNewFileName(
|
|
156
|
+
setNewFileName(searchText);
|
|
133
157
|
} else {
|
|
134
158
|
setHeader('');
|
|
135
159
|
setNewFileName('');
|
|
@@ -142,25 +166,27 @@ const HistoryFiles = _ref => {
|
|
|
142
166
|
}
|
|
143
167
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
144
168
|
}, []);
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
if (!(element === null || element === void 0 ? void 0 : element.children)) return;
|
|
171
|
+
const searchText = element.children[0].text;
|
|
172
|
+
onSearch(searchText);
|
|
173
|
+
}, [element, onSearch]);
|
|
145
174
|
const onSelect = useCallback(fileInfo => {
|
|
146
175
|
const {
|
|
147
176
|
doc_uuid,
|
|
148
177
|
name
|
|
149
178
|
} = fileInfo;
|
|
150
|
-
|
|
151
|
-
insertSdocFileLinkCallback
|
|
152
|
-
} = insertLinkCallback;
|
|
153
|
-
insertSdocFileLinkCallback(editor, name, doc_uuid);
|
|
179
|
+
removeTempInput(editor, element);
|
|
154
180
|
closeDialog();
|
|
155
|
-
|
|
156
|
-
}, []);
|
|
181
|
+
insertSdocFileLink(editor, name, doc_uuid);
|
|
182
|
+
}, [closeDialog, editor, element]);
|
|
157
183
|
const onShowMore = useCallback(() => {
|
|
158
|
-
const eventBus = EventBus.getInstance();
|
|
159
184
|
eventBus.dispatch(INTERNAL_EVENT.INSERT_ELEMENT, {
|
|
160
185
|
type: SDOC_LINK,
|
|
161
186
|
insertSdocFileLinkCallback: insertSdocFileLink
|
|
162
187
|
});
|
|
163
|
-
|
|
188
|
+
removeTempInput(editor, element);
|
|
189
|
+
}, [editor, element, eventBus]);
|
|
164
190
|
const onCreateFile = useCallback(() => {
|
|
165
191
|
const eventBus = EventBus.getInstance();
|
|
166
192
|
eventBus.dispatch(EXTERNAL_EVENT.CREATE_SDOC_FILE, {
|
|
@@ -168,20 +194,10 @@ const HistoryFiles = _ref => {
|
|
|
168
194
|
});
|
|
169
195
|
}, [newFileName]);
|
|
170
196
|
return /*#__PURE__*/React.createElement("div", {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
id: "sdoc-history-files-search-input",
|
|
176
|
-
className: "sdoc-history-files-search-input",
|
|
177
|
-
ref: historyFilesInputRef,
|
|
178
|
-
autoComplete: "off",
|
|
179
|
-
onChange: debounce(onSearch, 200),
|
|
180
|
-
onCompositionStart: onCompositionStart,
|
|
181
|
-
onCompositionEnd: onCompositionEnd,
|
|
182
|
-
onKeyDown: onKeydown
|
|
183
|
-
}), /*#__PURE__*/React.createElement("div", {
|
|
184
|
-
className: "sdoc-history-files-content"
|
|
197
|
+
className: "sdoc-history-files-content popover",
|
|
198
|
+
style: _objectSpread(_objectSpread({}, position), {}, {
|
|
199
|
+
position: 'absolute'
|
|
200
|
+
})
|
|
185
201
|
}, header.length !== 0 && /*#__PURE__*/React.createElement("div", {
|
|
186
202
|
className: "sdoc-history-files-header"
|
|
187
203
|
}, header), /*#__PURE__*/React.createElement("div", {
|
|
@@ -206,6 +222,10 @@ const HistoryFiles = _ref => {
|
|
|
206
222
|
onClick: onCreateFile
|
|
207
223
|
}, /*#__PURE__*/React.createElement("i", {
|
|
208
224
|
className: "sdocfont sdoc-append"
|
|
209
|
-
}), /*#__PURE__*/React.createElement("span",
|
|
225
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
226
|
+
className: "new-file-name"
|
|
227
|
+
}, newFileName ? t('Create_file_name_sdoc', {
|
|
228
|
+
file_name_sdoc: "".concat(newFileName, ".sdoc")
|
|
229
|
+
}) : t('Create_a_new_sdoc_file'))));
|
|
210
230
|
};
|
|
211
|
-
export default
|
|
231
|
+
export default FileLinkInsertDialog;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
.sdoc-history-files-content {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
position: absolute;
|
|
5
|
+
width: 400px;
|
|
6
|
+
max-height: 300px;
|
|
7
|
+
/* The same as hierarchy of comment drawer */
|
|
8
|
+
z-index: 103;
|
|
9
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, .15), 0 4px 8px 3px rgba(0, 0, 0, .15);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.sdoc-history-files-content .sdoc-history-files-header {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-shrink: 0;
|
|
15
|
+
align-items: center;
|
|
16
|
+
margin-top: 8px;
|
|
17
|
+
padding: 0px 16px;
|
|
18
|
+
height: 32px;
|
|
19
|
+
width: 100%;
|
|
20
|
+
color: #999999;
|
|
21
|
+
font-size: 14px;
|
|
22
|
+
user-select: none;
|
|
23
|
+
cursor: default;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.sdoc-history-files-content .sdoc-history-files {
|
|
27
|
+
flex-grow: 1;
|
|
28
|
+
overflow-y: auto;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.sdoc-history-files-content .no-header {
|
|
33
|
+
margin-top: 8px;
|
|
34
|
+
width: 100%;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.sdoc-history-files-content .sdoc-history-files .sdoc-history-files-item {
|
|
38
|
+
padding: 0px 16px;
|
|
39
|
+
height: 32px;
|
|
40
|
+
line-height: 32px;
|
|
41
|
+
text-overflow: ellipsis;
|
|
42
|
+
font-size: 14px;
|
|
43
|
+
white-space: nowrap;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.sdoc-history-files-content .sdoc-history-files .sdoc-history-files-item :first-child {
|
|
47
|
+
font-size: 12px;
|
|
48
|
+
margin-right: 5px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.sdoc-history-files-content .sdoc-history-files .sdoc-history-files-item:hover {
|
|
52
|
+
background-color: #f5f5f5;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.sdoc-history-files-content .sdoc-history-files-add {
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-shrink: 0;
|
|
58
|
+
align-items: center;
|
|
59
|
+
padding: 0px 16px;
|
|
60
|
+
height: 32px;
|
|
61
|
+
width: 100%;
|
|
62
|
+
border-top: 1px solid #e9ecef;
|
|
63
|
+
font-size: 14px;
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.sdoc-history-files-content .sdoc-history-files-add :first-child {
|
|
68
|
+
color: #444444;
|
|
69
|
+
margin-right: 10px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.sdoc-history-files-content .sdoc-history-files-add .new-file-name {
|
|
73
|
+
text-wrap: nowrap;
|
|
74
|
+
text-overflow: ellipsis;
|
|
75
|
+
overflow: hidden;
|
|
76
|
+
}
|
|
@@ -2,12 +2,12 @@ import React, { useCallback, useEffect, useState, useRef } from 'react';
|
|
|
2
2
|
import { CustomTableSizeDialog, SplitCellSettingDialog } from '../../plugins/table/dialogs';
|
|
3
3
|
import AddLinkDialog from '../../plugins/link/dialog/add-link-dialog';
|
|
4
4
|
import SelectFileDialog from '../select-file-dialog/index.js';
|
|
5
|
-
import HistoryFiles from '../history-files';
|
|
6
5
|
import EventBus from '../../../utils/event-bus';
|
|
7
6
|
import { INTERNAL_EVENT } from '../../../constants';
|
|
8
7
|
import { ELEMENT_TYPE, INSERT_POSITION, LOCAL_IMAGE } from '../../constants';
|
|
9
8
|
import { insertImage } from '../../plugins/image/helpers';
|
|
10
9
|
import context from '../../../../context.js';
|
|
10
|
+
import FileLinkInsertDialog from '../file-insert-dialog/index.js';
|
|
11
11
|
const InsertElementDialog = _ref => {
|
|
12
12
|
let {
|
|
13
13
|
editor
|
|
@@ -17,7 +17,6 @@ const InsertElementDialog = _ref => {
|
|
|
17
17
|
const [insertPosition, setInsertPosition] = useState(INSERT_POSITION.CURRENT);
|
|
18
18
|
const [slateNode, setSlateNode] = useState(null);
|
|
19
19
|
const [insertLinkCallback, setInsertLinkCallback] = useState(null);
|
|
20
|
-
const [isShowHistoryFiles, setIsShowHistoryFiles] = useState(false);
|
|
21
20
|
const [validEditor, setValidEditor] = useState(editor);
|
|
22
21
|
const uploadLocalImageInputRef = useRef();
|
|
23
22
|
const onFileChanged = useCallback(event => {
|
|
@@ -46,7 +45,6 @@ const InsertElementDialog = _ref => {
|
|
|
46
45
|
slateNode,
|
|
47
46
|
insertFileLinkCallback,
|
|
48
47
|
insertSdocFileLinkCallback,
|
|
49
|
-
isShowHistoryFiles,
|
|
50
48
|
editor: paramEditor
|
|
51
49
|
} = _ref2;
|
|
52
50
|
setInsertPosition(insertPosition);
|
|
@@ -57,7 +55,6 @@ const InsertElementDialog = _ref => {
|
|
|
57
55
|
insertSdocFileLinkCallback,
|
|
58
56
|
insertFileLinkCallback
|
|
59
57
|
});
|
|
60
|
-
setIsShowHistoryFiles(isShowHistoryFiles);
|
|
61
58
|
// Apply for comment editor, as it has a different editor instance
|
|
62
59
|
setValidEditor(paramEditor || editor);
|
|
63
60
|
if (type === LOCAL_IMAGE) {
|
|
@@ -72,7 +69,6 @@ const InsertElementDialog = _ref => {
|
|
|
72
69
|
setElement('');
|
|
73
70
|
setDialogType('');
|
|
74
71
|
setInsertLinkCallback(null);
|
|
75
|
-
setIsShowHistoryFiles(false);
|
|
76
72
|
setValidEditor(null);
|
|
77
73
|
}, []);
|
|
78
74
|
const props = {
|
|
@@ -103,9 +99,6 @@ const InsertElementDialog = _ref => {
|
|
|
103
99
|
insertLinkCallback,
|
|
104
100
|
closeDialog
|
|
105
101
|
};
|
|
106
|
-
if (isShowHistoryFiles) {
|
|
107
|
-
return /*#__PURE__*/React.createElement(HistoryFiles, sdocLinkProps);
|
|
108
|
-
}
|
|
109
102
|
return /*#__PURE__*/React.createElement(SelectFileDialog, sdocLinkProps);
|
|
110
103
|
}
|
|
111
104
|
case ELEMENT_TYPE.FILE_LINK:
|
|
@@ -132,6 +125,14 @@ const InsertElementDialog = _ref => {
|
|
|
132
125
|
onChange: onFileChanged
|
|
133
126
|
});
|
|
134
127
|
}
|
|
128
|
+
case ELEMENT_TYPE.FILE_LINK_INSET_INPUT_TEMP:
|
|
129
|
+
{
|
|
130
|
+
return /*#__PURE__*/React.createElement(FileLinkInsertDialog, {
|
|
131
|
+
element: slateNode,
|
|
132
|
+
editor: editor,
|
|
133
|
+
closeDialog: closeDialog
|
|
134
|
+
});
|
|
135
|
+
}
|
|
135
136
|
default:
|
|
136
137
|
{
|
|
137
138
|
return null;
|
|
@@ -5,16 +5,14 @@
|
|
|
5
5
|
margin-right: 1px;
|
|
6
6
|
padding: 1px 3px;
|
|
7
7
|
min-width: 12px;
|
|
8
|
-
border
|
|
9
|
-
border-color: rgba(0, 0, 0, 0.1);
|
|
10
|
-
border-radius: 3px;
|
|
11
|
-
border-width: 1px 1px 2px;
|
|
8
|
+
border: none;
|
|
12
9
|
font: inherit;
|
|
13
10
|
text-align: center;
|
|
14
|
-
color:
|
|
15
|
-
background-color:
|
|
11
|
+
color: #999;
|
|
12
|
+
background-color: transparent;
|
|
16
13
|
}
|
|
17
14
|
|
|
18
|
-
.sdoc-shortcut-prompt-container:last-child {
|
|
15
|
+
.sdoc-shortcut-prompt-container kbd:last-child {
|
|
19
16
|
margin-right: 0px;
|
|
17
|
+
padding-right: 0px;
|
|
20
18
|
}
|
|
@@ -26,6 +26,7 @@ export const IMAGE_BLOCK = 'image_block';
|
|
|
26
26
|
export const CALL_OUT = 'callout';
|
|
27
27
|
export const MENTION = 'mention';
|
|
28
28
|
export const MENTION_TEMP = 'mention_temp';
|
|
29
|
+
export const FILE_LINK_INSET_INPUT_TEMP = 'file_link_insert_input_temp';
|
|
29
30
|
|
|
30
31
|
// font
|
|
31
32
|
export const FONT_SIZE = 'font-size';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// extension plugin
|
|
2
2
|
import * as ELEMENT_TYPE from './element-type';
|
|
3
3
|
// eslint-disable-next-line no-duplicate-imports
|
|
4
|
-
import { BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, IMAGE_BLOCK, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES, CALL_OUT, MENTION, MENTION_TEMP } from './element-type';
|
|
4
|
+
import { BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, IMAGE_BLOCK, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES, CALL_OUT, MENTION, MENTION_TEMP, FILE_LINK_INSET_INPUT_TEMP } from './element-type';
|
|
5
5
|
export { DEFAULT_COLORS, STANDARD_COLORS, DEFAULT_RECENT_USED_LIST, DEFAULT_FONT_COLOR, RECENT_USED_HIGHLIGHT_COLORS_KEY, RECENT_USED_FONT_COLORS_KEY, RECENT_USED_TABLE_CELL_BACKGROUND_COLORS_KEY, DEFAULT_LAST_USED_FONT_COLOR, DEFAULT_LAST_USED_HIGHLIGHT_COLOR, DEFAULT_LAST_USED_TABLE_CELL_BACKGROUND_COLOR } from './color';
|
|
6
6
|
export { FONT_SIZE, DEFAULT_FONT, FONT, GOOGLE_FONT_CLASS, RECENT_USED_FONTS_KEY, SDOC_FONT_SIZE } from './font';
|
|
7
7
|
export { DIFF_TYPE, ADDED_STYLE, DELETED_STYLE } from './diff-view';
|
|
@@ -57,4 +57,4 @@ export const MOUSE_ENTER_EVENT_DISABLED_MAP = {
|
|
|
57
57
|
[CALL_OUT]: [CALL_OUT]
|
|
58
58
|
};
|
|
59
59
|
export const ROOT_ELEMENT_TYPES = [PARAGRAPH, TITLE, SUBTITLE, CHECK_LIST_ITEM, ORDERED_LIST, UNORDERED_LIST, BLOCKQUOTE, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, CALL_OUT, TABLE];
|
|
60
|
-
export { ELEMENT_TYPE, BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, IMAGE_BLOCK, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES, CALL_OUT, MENTION, MENTION_TEMP };
|
|
60
|
+
export { ELEMENT_TYPE, BLOCKQUOTE, TITLE, SUBTITLE, HEADER, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, PARAGRAPH, ORDERED_LIST, UNORDERED_LIST, LIST_ITEM, CHECK_LIST_ITEM, CODE_BLOCK, CODE_LINE, TABLE, TABLE_CELL, TABLE_ROW, LINK, SDOC_LINK, FILE_LINK, IMAGE, IMAGE_BLOCK, TOP_LEVEL_TYPES, INLINE_LEVEL_TYPES, CALL_OUT, MENTION, MENTION_TEMP, FILE_LINK_INSET_INPUT_TEMP };
|
|
@@ -101,6 +101,15 @@
|
|
|
101
101
|
font-size: 14px;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
.langs-list-empty {
|
|
105
|
+
padding: 10px;
|
|
106
|
+
width: 100%;
|
|
107
|
+
font-size: 13px;
|
|
108
|
+
text-align: center;
|
|
109
|
+
line-height: 30px;
|
|
110
|
+
vertical-align: middle;
|
|
111
|
+
}
|
|
112
|
+
|
|
104
113
|
.sdoc-langs-list-container .langs-list-ul {
|
|
105
114
|
list-style: none;
|
|
106
115
|
overflow-y: auto;
|
|
@@ -1,12 +1,43 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { Input } from 'reactstrap';
|
|
3
|
-
import { withTranslation } from 'react-i18next';
|
|
3
|
+
import { useTranslation, withTranslation } from 'react-i18next';
|
|
4
4
|
import Tooltip from '../../../../../components/tooltip';
|
|
5
5
|
import { ElementPopover } from '../../../commons/';
|
|
6
6
|
import { genCodeLangs } from '../prismjs';
|
|
7
7
|
import { getSelectedLangOption } from '../helpers';
|
|
8
8
|
import './index.css';
|
|
9
|
-
const
|
|
9
|
+
const LangList = _ref => {
|
|
10
|
+
let {
|
|
11
|
+
langsData,
|
|
12
|
+
onSelectLang,
|
|
13
|
+
selectedLanguageText
|
|
14
|
+
} = _ref;
|
|
15
|
+
const {
|
|
16
|
+
t
|
|
17
|
+
} = useTranslation();
|
|
18
|
+
if (!langsData.length) {
|
|
19
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
20
|
+
className: "langs-list-empty"
|
|
21
|
+
}, /*#__PURE__*/React.createElement("span", null, t('Search_not_found')));
|
|
22
|
+
}
|
|
23
|
+
return /*#__PURE__*/React.createElement("ul", {
|
|
24
|
+
className: "langs-list-ul"
|
|
25
|
+
}, langsData.map(item => {
|
|
26
|
+
return /*#__PURE__*/React.createElement("li", {
|
|
27
|
+
className: "langs-list-li ".concat(selectedLanguageText === item.text ? 'active' : ''),
|
|
28
|
+
id: item.value,
|
|
29
|
+
key: item.value,
|
|
30
|
+
onClick: () => {
|
|
31
|
+
onSelectLang(item);
|
|
32
|
+
}
|
|
33
|
+
}, item.text, /*#__PURE__*/React.createElement("span", {
|
|
34
|
+
className: "li-check-mark ".concat(selectedLanguageText === item.text ? 'li-checked' : '')
|
|
35
|
+
}, /*#__PURE__*/React.createElement("i", {
|
|
36
|
+
className: "sdocfont sdoc-check-mark icon-font"
|
|
37
|
+
})));
|
|
38
|
+
}));
|
|
39
|
+
};
|
|
40
|
+
const CodeBlockHoverMenu = _ref2 => {
|
|
10
41
|
let {
|
|
11
42
|
style,
|
|
12
43
|
language,
|
|
@@ -16,7 +47,7 @@ const CodeBlockHoverMenu = _ref => {
|
|
|
16
47
|
onCopyCodeBlock,
|
|
17
48
|
onDeleteCodeBlock,
|
|
18
49
|
t
|
|
19
|
-
} =
|
|
50
|
+
} = _ref2;
|
|
20
51
|
const {
|
|
21
52
|
white_space = 'nowrap'
|
|
22
53
|
} = style;
|
|
@@ -74,15 +105,12 @@ const CodeBlockHoverMenu = _ref => {
|
|
|
74
105
|
}, [language]);
|
|
75
106
|
const onChange = useCallback(e => {
|
|
76
107
|
const filterData = [];
|
|
77
|
-
const restData = [];
|
|
78
108
|
genCodeLangs().forEach(item => {
|
|
79
109
|
if (item.value.startsWith(e.currentTarget.value.toLowerCase())) {
|
|
80
110
|
filterData.push(item);
|
|
81
|
-
} else {
|
|
82
|
-
restData.push(item);
|
|
83
111
|
}
|
|
84
112
|
});
|
|
85
|
-
setLangsData(
|
|
113
|
+
setLangsData(filterData);
|
|
86
114
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
87
115
|
}, []);
|
|
88
116
|
return /*#__PURE__*/React.createElement(ElementPopover, null, /*#__PURE__*/React.createElement("div", {
|
|
@@ -150,21 +178,10 @@ const CodeBlockHoverMenu = _ref => {
|
|
|
150
178
|
}, /*#__PURE__*/React.createElement(Input, {
|
|
151
179
|
placeholder: t('Search_language'),
|
|
152
180
|
onChange: onChange
|
|
153
|
-
})), /*#__PURE__*/React.createElement(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
id: item.value,
|
|
159
|
-
key: item.value,
|
|
160
|
-
onClick: () => {
|
|
161
|
-
onSelectLang(item);
|
|
162
|
-
}
|
|
163
|
-
}, item.text, /*#__PURE__*/React.createElement("span", {
|
|
164
|
-
className: "li-check-mark ".concat(selectedLanguageText === item.text ? 'li-checked' : '')
|
|
165
|
-
}, /*#__PURE__*/React.createElement("i", {
|
|
166
|
-
className: "sdocfont sdoc-check-mark icon-font"
|
|
167
|
-
})));
|
|
168
|
-
}))))));
|
|
181
|
+
})), /*#__PURE__*/React.createElement(LangList, {
|
|
182
|
+
langsData: langsData,
|
|
183
|
+
onSelectLang: onSelectLang,
|
|
184
|
+
selectedLanguageText: selectedLanguageText
|
|
185
|
+
})))));
|
|
169
186
|
};
|
|
170
187
|
export default withTranslation('sdoc-editor')(CodeBlockHoverMenu);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
2
|
import { ReactEditor } from '@seafile/slate-react';
|
|
3
|
-
import { Editor, Transforms, Range, Text } from '@seafile/slate';
|
|
3
|
+
import { Editor, Transforms, Range, Text, Path, Node } from '@seafile/slate';
|
|
4
4
|
import slugid from 'slugid';
|
|
5
5
|
import copy from 'copy-to-clipboard';
|
|
6
6
|
import context from '../../../../context';
|
|
7
|
-
import { focusEditor, getNodeType, getSelectedElems } from '../../core';
|
|
8
|
-
import { SDOC_LINK, LINK, INSERT_FILE_DISPLAY_TYPE, CODE_BLOCK, CODE_LINE, PARAGRAPH } from '../../constants';
|
|
7
|
+
import { focusEditor, generateEmptyElement, getNodeType, getSelectedElems } from '../../core';
|
|
8
|
+
import { SDOC_LINK, LINK, INSERT_FILE_DISPLAY_TYPE, CODE_BLOCK, CODE_LINE, PARAGRAPH, FILE_LINK_INSET_INPUT_TEMP } from '../../constants';
|
|
9
9
|
export const isMenuDisabled = (editor, readonly) => {
|
|
10
10
|
if (readonly) return true;
|
|
11
11
|
if (editor.selection == null) return true;
|
|
@@ -149,17 +149,13 @@ export const getBeforeText = editor => {
|
|
|
149
149
|
range
|
|
150
150
|
};
|
|
151
151
|
};
|
|
152
|
-
export const
|
|
153
|
-
const {
|
|
154
|
-
selection
|
|
155
|
-
} = editor;
|
|
156
|
-
const {
|
|
157
|
-
anchor
|
|
158
|
-
} = selection;
|
|
152
|
+
export const isTriggeredByShortcut = editor => {
|
|
159
153
|
const {
|
|
160
154
|
beforeText
|
|
161
155
|
} = getBeforeText(editor);
|
|
162
|
-
|
|
156
|
+
const fileSearchInput = getFileSearchInputEntry(editor);
|
|
157
|
+
if (fileSearchInput) return false;
|
|
158
|
+
return beforeText.endsWith('[');
|
|
163
159
|
};
|
|
164
160
|
|
|
165
161
|
// If insert operation is triggered by shortcut, remove the '[[' symbol
|
|
@@ -172,8 +168,8 @@ export const removeShortCutSymbol = editor => {
|
|
|
172
168
|
beforeText,
|
|
173
169
|
range: beforeRange
|
|
174
170
|
} = getBeforeText(editor);
|
|
175
|
-
const
|
|
176
|
-
|
|
171
|
+
const isTriggeredByShortCut = beforeText.slice(-2) === '[[';
|
|
172
|
+
isTriggeredByShortCut && Transforms.delete(editor, {
|
|
177
173
|
at: {
|
|
178
174
|
anchor: {
|
|
179
175
|
path: beforeRange.focus.path,
|
|
@@ -184,4 +180,57 @@ export const removeShortCutSymbol = editor => {
|
|
|
184
180
|
voids: true
|
|
185
181
|
});
|
|
186
182
|
focusEditor(editor);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// Generate temp input for collecting file name
|
|
186
|
+
const generateFileLinkInput = () => {
|
|
187
|
+
const input = generateEmptyElement(FILE_LINK_INSET_INPUT_TEMP);
|
|
188
|
+
return input;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Insert temp input collecting file name
|
|
192
|
+
export const insertTempInput = editor => {
|
|
193
|
+
const {
|
|
194
|
+
selection
|
|
195
|
+
} = editor;
|
|
196
|
+
if (!Range.isCollapsed(selection)) return;
|
|
197
|
+
const tempInput = generateFileLinkInput();
|
|
198
|
+
const insertPoint = Editor.start(editor, selection);
|
|
199
|
+
Transforms.insertNodes(editor, tempInput, {
|
|
200
|
+
at: insertPoint
|
|
201
|
+
});
|
|
202
|
+
const path = Editor.path(editor, insertPoint);
|
|
203
|
+
const insertPath = Path.next(path);
|
|
204
|
+
const focusPoint = insertPath.concat(0);
|
|
205
|
+
focusEditor(editor, focusPoint);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Remove temp input collecting file name
|
|
209
|
+
export const removeTempInput = (editor, element) => {
|
|
210
|
+
const path = ReactEditor.findPath(editor, element);
|
|
211
|
+
Transforms.delete(editor, {
|
|
212
|
+
at: path
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
export const getFileSearchInputEntry = editor => {
|
|
216
|
+
const [searchInputNodeEntry] = Editor.nodes(editor, {
|
|
217
|
+
match: n => n.type === FILE_LINK_INSET_INPUT_TEMP
|
|
218
|
+
});
|
|
219
|
+
return searchInputNodeEntry;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Insert text when remove file name collector, such as inserting 'filename' into prevNode when close collector entered '[[filename'
|
|
223
|
+
export const insertTextWhenRemoveFileNameCollector = (editor, searchInputNode) => {
|
|
224
|
+
const inputNodePath = ReactEditor.findPath(editor, searchInputNode);
|
|
225
|
+
if (!inputNodePath) return;
|
|
226
|
+
const prevNodeEntry = Editor.previous(editor, {
|
|
227
|
+
at: inputNodePath
|
|
228
|
+
});
|
|
229
|
+
if (!prevNodeEntry) return;
|
|
230
|
+
const searchContent = Node.string(searchInputNode);
|
|
231
|
+
const insertPoint = Editor.end(editor, prevNodeEntry[1]);
|
|
232
|
+
Transforms.insertText(editor, searchContent, {
|
|
233
|
+
at: insertPoint
|
|
234
|
+
});
|
|
235
|
+
removeTempInput(editor, searchInputNode);
|
|
187
236
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { SDOC_LINK } from '../../constants';
|
|
2
2
|
import SdocLinkMenu from './menu';
|
|
3
3
|
import withSdocLink from './plugin';
|
|
4
|
-
import renderSdocLink from './render-elem';
|
|
4
|
+
import renderSdocLink from './render/render-elem';
|
|
5
|
+
import renderFileLinkTempInput from './render/render-file-link-temp-input';
|
|
5
6
|
const SdocLinkPlugin = {
|
|
6
7
|
type: SDOC_LINK,
|
|
7
8
|
editorMenus: [SdocLinkMenu],
|
|
8
9
|
editorPlugin: withSdocLink,
|
|
9
|
-
renderElements: [renderSdocLink]
|
|
10
|
+
renderElements: [renderSdocLink, renderFileLinkTempInput]
|
|
10
11
|
};
|
|
11
12
|
export default SdocLinkPlugin;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Transforms, Node, Editor } from '@seafile/slate';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { ReactEditor } from '@seafile/slate-react';
|
|
3
|
+
import { FILE_LINK_INSET_INPUT_TEMP, SDOC_LINK } from '../../constants';
|
|
4
|
+
import { getFileSearchInputEntry, insertTempInput, isTriggeredByShortcut } from './helpers';
|
|
5
|
+
import { getSelectedElems } from '../../core';
|
|
6
6
|
const withSdocLink = editor => {
|
|
7
7
|
const {
|
|
8
8
|
isInline,
|
|
9
9
|
deleteBackward,
|
|
10
|
-
|
|
10
|
+
insertText,
|
|
11
|
+
onCompositionStart
|
|
11
12
|
} = editor;
|
|
12
13
|
const newEditor = editor;
|
|
13
14
|
|
|
@@ -16,9 +17,8 @@ const withSdocLink = editor => {
|
|
|
16
17
|
const {
|
|
17
18
|
type
|
|
18
19
|
} = elem;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
20
|
+
const isInlineElem = [SDOC_LINK, FILE_LINK_INSET_INPUT_TEMP].includes(type);
|
|
21
|
+
if (isInlineElem) return true;
|
|
22
22
|
return isInline(elem);
|
|
23
23
|
};
|
|
24
24
|
newEditor.deleteBackward = unit => {
|
|
@@ -29,13 +29,21 @@ const withSdocLink = editor => {
|
|
|
29
29
|
return deleteBackward(unit);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
// Delete file link node
|
|
32
|
+
// Delete file link node when the input is empty
|
|
33
|
+
const selectedElems = getSelectedElems(newEditor);
|
|
34
|
+
const fileLinkSearchInputNode = selectedElems.find(elem => elem.type === FILE_LINK_INSET_INPUT_TEMP);
|
|
35
|
+
if (fileLinkSearchInputNode) {
|
|
36
|
+
const path = ReactEditor.findPath(editor, fileLinkSearchInputNode);
|
|
37
|
+
if (Node.string(fileLinkSearchInputNode).length === 0) return Transforms.delete(newEditor, {
|
|
38
|
+
at: path
|
|
39
|
+
});
|
|
40
|
+
}
|
|
33
41
|
const nodeEntry = Editor.node(newEditor, newEditor.selection);
|
|
34
42
|
if (nodeEntry && Node.string(nodeEntry[0]).length === 0) {
|
|
35
43
|
const beforePath = nodeEntry[1];
|
|
36
44
|
beforePath.splice(-1, 1, Math.max(nodeEntry[1].at(-1) - 1, 0));
|
|
37
45
|
const beforeNodeEntry = Editor.node(newEditor, beforePath);
|
|
38
|
-
if (beforeNodeEntry && beforeNodeEntry[0].type
|
|
46
|
+
if (beforeNodeEntry && [SDOC_LINK, FILE_LINK_INSET_INPUT_TEMP].includes(beforeNodeEntry[0].type)) {
|
|
39
47
|
Transforms.delete(newEditor, {
|
|
40
48
|
at: beforeNodeEntry[1]
|
|
41
49
|
});
|
|
@@ -45,21 +53,23 @@ const withSdocLink = editor => {
|
|
|
45
53
|
}
|
|
46
54
|
return deleteBackward(unit);
|
|
47
55
|
};
|
|
48
|
-
newEditor.
|
|
49
|
-
|
|
50
|
-
key
|
|
51
|
-
} = event;
|
|
52
|
-
if (key !== '[') return onHotKeyDown && onHotKeyDown(event);
|
|
56
|
+
newEditor.insertText = text => {
|
|
57
|
+
if (text !== '[') return insertText(text);
|
|
53
58
|
// If user press '[[', open file modal
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
if (isTriggeredByShortcut(newEditor)) {
|
|
60
|
+
insertText(text);
|
|
61
|
+
insertTempInput(newEditor);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
return insertText(text);
|
|
65
|
+
};
|
|
66
|
+
newEditor.onCompositionStart = event => {
|
|
67
|
+
const fileSearchInputNodeEntry = getFileSearchInputEntry(newEditor);
|
|
68
|
+
if (fileSearchInputNodeEntry) {
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
return true;
|
|
61
71
|
}
|
|
62
|
-
return
|
|
72
|
+
return onCompositionStart && onCompositionStart(event);
|
|
63
73
|
};
|
|
64
74
|
return newEditor;
|
|
65
75
|
};
|
package/dist/basic-sdk/extension/plugins/sdoc-link/{render-elem.css → render/render-elem.css}
RENAMED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
.sdoc-file-card-link .sdoc-file-link-icon :first-child {
|
|
23
|
-
|
|
23
|
+
font-size: 24px;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
.sdoc-file-card-link .sdoc-file-link-icon {
|
|
@@ -39,3 +39,7 @@
|
|
|
39
39
|
text-decoration: none;
|
|
40
40
|
color: #333;
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
.sdoc-file-name-insert-collector {
|
|
44
|
+
padding: 0 .25em;
|
|
45
|
+
}
|
|
@@ -2,12 +2,12 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
|
2
2
|
import React, { useCallback, useEffect, useState, useRef } from 'react';
|
|
3
3
|
import { Editor, Range } from '@seafile/slate';
|
|
4
4
|
import { ReactEditor, useReadOnly } from '@seafile/slate-react';
|
|
5
|
-
import { useScrollContext } from '
|
|
6
|
-
import { unwrapLinkNode, getUrl } from '
|
|
7
|
-
import HoverMenu from '
|
|
8
|
-
import { DELETED_STYLE, ADDED_STYLE } from '
|
|
9
|
-
import { SDOC_LINK_TYPE } from '
|
|
10
|
-
import { focusEditor } from '
|
|
5
|
+
import { useScrollContext } from '../../../../hooks/use-scroll-context';
|
|
6
|
+
import { unwrapLinkNode, getUrl } from '../helpers';
|
|
7
|
+
import HoverMenu from '../hover-menu';
|
|
8
|
+
import { DELETED_STYLE, ADDED_STYLE } from '../../../constants';
|
|
9
|
+
import { SDOC_LINK_TYPE } from '../constants';
|
|
10
|
+
import { focusEditor } from '../../../core';
|
|
11
11
|
import './render-elem.css';
|
|
12
12
|
const SdocFileLink = _ref => {
|
|
13
13
|
let {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import EventBus from '../../../../utils/event-bus';
|
|
3
|
+
import { INTERNAL_EVENT } from '../../../../constants';
|
|
4
|
+
import { FILE_LINK_INSET_INPUT_TEMP } from '../../../constants';
|
|
5
|
+
import './render-elem.css';
|
|
6
|
+
const RenderFileLinkTempInput = _ref => {
|
|
7
|
+
let {
|
|
8
|
+
element,
|
|
9
|
+
attributes,
|
|
10
|
+
children
|
|
11
|
+
} = _ref;
|
|
12
|
+
const eventBus = EventBus.getInstance();
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
eventBus.dispatch(INTERNAL_EVENT.INSERT_ELEMENT, {
|
|
15
|
+
type: FILE_LINK_INSET_INPUT_TEMP,
|
|
16
|
+
slateNode: element
|
|
17
|
+
});
|
|
18
|
+
return () => {
|
|
19
|
+
eventBus.dispatch(INTERNAL_EVENT.CLOSE_FILE_INSET_DIALOG);
|
|
20
|
+
};
|
|
21
|
+
}, [element, eventBus]);
|
|
22
|
+
return /*#__PURE__*/React.createElement("span", Object.assign({}, attributes, {
|
|
23
|
+
className: "sdoc-file-name-insert-collector"
|
|
24
|
+
}), children);
|
|
25
|
+
};
|
|
26
|
+
export default RenderFileLinkTempInput;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
2
|
import { useReadOnly, useSlateStatic } from '@seafile/slate-react';
|
|
3
|
-
import { BLOCKQUOTE, LINK, CHECK_LIST_ITEM, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, LIST_ITEM, ORDERED_LIST, PARAGRAPH, UNORDERED_LIST, CODE_BLOCK, CODE_LINE, IMAGE, IMAGE_BLOCK, ELEMENT_TYPE, SDOC_LINK, FILE_LINK, TITLE, SUBTITLE, CALL_OUT, SUPPORTED_SIDE_OPERATION_TYPE, MENTION, MENTION_TEMP } from '../constants';
|
|
3
|
+
import { BLOCKQUOTE, LINK, CHECK_LIST_ITEM, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, LIST_ITEM, ORDERED_LIST, PARAGRAPH, UNORDERED_LIST, CODE_BLOCK, CODE_LINE, IMAGE, IMAGE_BLOCK, ELEMENT_TYPE, SDOC_LINK, FILE_LINK, TITLE, SUBTITLE, CALL_OUT, SUPPORTED_SIDE_OPERATION_TYPE, MENTION, MENTION_TEMP, FILE_LINK_INSET_INPUT_TEMP } from '../constants';
|
|
4
4
|
import { BlockquotePlugin, LinkPlugin, CheckListPlugin, HeaderPlugin, ListPlugin, CodeBlockPlugin, ImagePlugin, TablePlugin, SdocLinkPlugin, ParagraphPlugin, FileLinkPlugin, CalloutPlugin, MentionPlugin } from '../plugins';
|
|
5
5
|
import { onDragOver, onDragLeave, onDrop } from '../toolbar/side-toolbar/event';
|
|
6
6
|
import { getParentNode } from '../core';
|
|
@@ -134,6 +134,11 @@ const CustomRenderElement = props => {
|
|
|
134
134
|
const [renderFileLink] = FileLinkPlugin.renderElements;
|
|
135
135
|
return renderFileLink(props, editor);
|
|
136
136
|
}
|
|
137
|
+
case FILE_LINK_INSET_INPUT_TEMP:
|
|
138
|
+
{
|
|
139
|
+
const [, renderFileLinkFileSearchInput] = SdocLinkPlugin.renderElements;
|
|
140
|
+
return renderFileLinkFileSearchInput(props, editor);
|
|
141
|
+
}
|
|
137
142
|
case CALL_OUT:
|
|
138
143
|
{
|
|
139
144
|
const [renderCallout] = CalloutPlugin.renderElements;
|
|
@@ -62,6 +62,7 @@ const MoreOperations = _ref => {
|
|
|
62
62
|
return printTexts;
|
|
63
63
|
}, []);
|
|
64
64
|
return /*#__PURE__*/React.createElement(Dropdown, {
|
|
65
|
+
className: "sdoc-operator-folder",
|
|
65
66
|
isOpen: isDropdownOpen,
|
|
66
67
|
toggle: () => toggleDropdown(isDropdownOpen)
|
|
67
68
|
}, /*#__PURE__*/React.createElement(DropdownToggle, {
|
|
@@ -72,26 +73,28 @@ const MoreOperations = _ref => {
|
|
|
72
73
|
})), /*#__PURE__*/React.createElement(DropdownMenu, {
|
|
73
74
|
className: "sdoc-dropdown-menu",
|
|
74
75
|
right: true
|
|
75
|
-
},
|
|
76
|
-
className: "sdoc-dropdown-menu-item",
|
|
77
|
-
tag: "a",
|
|
78
|
-
href: parentFolderURL
|
|
79
|
-
}, t('Open_parent_folder')), isPro && isFreezed && /*#__PURE__*/React.createElement(DropdownItem, {
|
|
80
|
-
className: "sdoc-dropdown-menu-item",
|
|
81
|
-
onClick: unFreeze
|
|
82
|
-
}, t('Unfreeze')), /*#__PURE__*/React.createElement(DropdownItem, {
|
|
76
|
+
}, /*#__PURE__*/React.createElement(DropdownItem, {
|
|
83
77
|
className: "sdoc-dropdown-menu-item",
|
|
84
78
|
onClick: handlePrint
|
|
85
79
|
}, /*#__PURE__*/React.createElement("div", {
|
|
86
80
|
className: "sdoc-dropdown-print-container"
|
|
87
81
|
}, /*#__PURE__*/React.createElement("div", null, t('Print')), /*#__PURE__*/React.createElement(MenuShortcutPrompt, {
|
|
88
82
|
shortcuts: printShortCutTexts
|
|
89
|
-
}))), isPro &&
|
|
83
|
+
}))), isPro && isFreezed && /*#__PURE__*/React.createElement(DropdownItem, {
|
|
84
|
+
className: "sdoc-dropdown-menu-item",
|
|
85
|
+
onClick: unFreeze
|
|
86
|
+
}, t('Unfreeze')), isPro && !isFreezed && /*#__PURE__*/React.createElement(DropdownItem, {
|
|
90
87
|
className: "sdoc-dropdown-menu-item",
|
|
91
88
|
onClick: onFreezeDocument
|
|
92
89
|
}, t('Freeze_document')), /*#__PURE__*/React.createElement(DropdownItem, {
|
|
93
90
|
className: "sdoc-dropdown-menu-item",
|
|
94
91
|
onClick: handleClickHistory
|
|
95
|
-
}, t('Document_history'))
|
|
92
|
+
}, t('Document_history')), parentFolderURL && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
93
|
+
className: "sdoc-operator-folder-divider"
|
|
94
|
+
}), /*#__PURE__*/React.createElement(DropdownItem, {
|
|
95
|
+
className: "sdoc-dropdown-menu-item",
|
|
96
|
+
tag: "a",
|
|
97
|
+
href: parentFolderURL
|
|
98
|
+
}, t('Open_parent_folder')))));
|
|
96
99
|
};
|
|
97
100
|
export default withTranslation('sdoc-editor')(MoreOperations);
|
|
@@ -32,3 +32,18 @@
|
|
|
32
32
|
align-items: center;
|
|
33
33
|
width: 100%;
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
.sdoc-operator-folder .sdoc-dropdown-menu {
|
|
37
|
+
padding: 8px 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.sdoc-operator-folder .sdoc-dropdown-menu .sdoc-dropdown-menu-item {
|
|
41
|
+
padding: 4px 16px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.sdoc-operator-folder .sdoc-dropdown-menu .sdoc-operator-folder-divider {
|
|
45
|
+
margin: 8px 0;
|
|
46
|
+
border-top: 1px solid #e6e9ed;
|
|
47
|
+
height: 1px;
|
|
48
|
+
width: 100%;
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -453,5 +453,7 @@
|
|
|
453
453
|
"Move_row_count": "Moving {{count}} row(s)",
|
|
454
454
|
"Mark_all_as_read": "Mark all as read",
|
|
455
455
|
"Alignment_type": "Alignment",
|
|
456
|
-
"Print": "Print"
|
|
456
|
+
"Print": "Print",
|
|
457
|
+
"Enter_more_character_start_search": "Enter more characters to start search",
|
|
458
|
+
"Create_file_name_sdoc": "Create {{file_name_sdoc}}"
|
|
457
459
|
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
.sdoc-history-files-wrapper {
|
|
2
|
-
position: absolute;
|
|
3
|
-
z-index: 101;
|
|
4
|
-
width: 400px;
|
|
5
|
-
cursor: pointer;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.sdoc-history-files-wrapper .sdoc-history-files-search-input {
|
|
9
|
-
border: 0;
|
|
10
|
-
outline: none;
|
|
11
|
-
margin-top: -21px;
|
|
12
|
-
position: absolute;
|
|
13
|
-
background-color: transparent;
|
|
14
|
-
padding: 0px;
|
|
15
|
-
color: #212529;
|
|
16
|
-
font-size: 14px;
|
|
17
|
-
text-decoration: underline;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.sdoc-history-files-wrapper .sdoc-history-files-content .sdoc-history-files-header {
|
|
21
|
-
color: #999999;
|
|
22
|
-
font-size: 14px;
|
|
23
|
-
margin-top: 8px;
|
|
24
|
-
height: 32px;
|
|
25
|
-
display: flex;
|
|
26
|
-
align-items: center;
|
|
27
|
-
padding: 0px 16px;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.sdoc-history-files-wrapper .sdoc-history-files-content .sdoc-history-files {
|
|
31
|
-
max-height: 306px;
|
|
32
|
-
overflow-y: scroll;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
.sdoc-history-files-wrapper .sdoc-history-files-content .no-header {
|
|
36
|
-
margin-top: 8px;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.sdoc-history-files-wrapper .sdoc-history-files-content .sdoc-history-files .sdoc-history-files-item {
|
|
40
|
-
font-size: 14px;
|
|
41
|
-
line-height: 32px;
|
|
42
|
-
height: 32px;
|
|
43
|
-
padding: 0px 16px;
|
|
44
|
-
overflow:hidden;
|
|
45
|
-
text-overflow:ellipsis;
|
|
46
|
-
white-space:nowrap;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
.sdoc-history-files-wrapper .sdoc-history-files-content .sdoc-history-files .sdoc-history-files-item :first-child {
|
|
50
|
-
font-size: 12px;
|
|
51
|
-
margin-right: 5px;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.sdoc-history-files-wrapper .sdoc-history-files-content .sdoc-history-files .sdoc-history-files-item:hover {
|
|
55
|
-
background-color: #f5f5f5;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.sdoc-history-files-wrapper .sdoc-history-files-content .sdoc-history-files-add {
|
|
59
|
-
height: 32px;
|
|
60
|
-
padding: 0px 16px;
|
|
61
|
-
border-top: 1px solid #e9ecef;
|
|
62
|
-
font-size: 14px;
|
|
63
|
-
display: flex;
|
|
64
|
-
align-items: center;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.sdoc-history-files-wrapper .sdoc-history-files-content .sdoc-history-files-add :first-child {
|
|
68
|
-
color: #444444;
|
|
69
|
-
margin-right: 10px;
|
|
70
|
-
}
|