@seafile/sdoc-editor 0.5.69 → 0.5.71
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/editable-article.js +4 -3
- package/dist/basic-sdk/editor/sdoc-editor.js +1 -18
- package/dist/basic-sdk/editor/wiki-editor.js +161 -0
- package/dist/basic-sdk/extension/commons/file-insert-dialog/index.js +39 -13
- package/dist/basic-sdk/extension/commons/insert-element-dialog/index.js +12 -2
- 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/column/column-list-menu.css +35 -0
- package/dist/basic-sdk/extension/plugins/column/column-list-menu.js +47 -0
- package/dist/basic-sdk/extension/plugins/column/helpers.js +13 -8
- package/dist/basic-sdk/extension/plugins/column/index.js +0 -2
- package/dist/basic-sdk/extension/plugins/column/menu/seatable-column.js +31 -0
- package/dist/basic-sdk/extension/plugins/column/model.js +6 -8
- package/dist/basic-sdk/extension/plugins/column/render-elem.js +39 -101
- package/dist/basic-sdk/extension/plugins/index.js +3 -2
- package/dist/basic-sdk/extension/plugins/link/dialog/add-link-dialog/index.js +5 -2
- package/dist/basic-sdk/extension/plugins/link/plugin.js +26 -11
- package/dist/basic-sdk/extension/plugins/sdoc-link/hover-menu/index.js +4 -3
- package/dist/basic-sdk/extension/plugins/sdoc-link/plugin.js +3 -2
- package/dist/basic-sdk/extension/plugins/sdoc-link/render/render-elem.js +8 -2
- package/dist/basic-sdk/extension/plugins/table/plugin.js +8 -1
- package/dist/basic-sdk/extension/plugins/wiki-link/helpers.js +60 -0
- package/dist/basic-sdk/extension/plugins/wiki-link/index.js +9 -0
- package/dist/basic-sdk/extension/render/custom-element.js +10 -6
- package/dist/basic-sdk/extension/toolbar/header-toolbar/index.js +1 -6
- package/dist/basic-sdk/extension/toolbar/header-toolbar/insert-toolbar/index.js +2 -2
- package/dist/basic-sdk/utils/dom-utils.js +1 -2
- package/dist/constants/index.js +2 -0
- package/dist/pages/document-plugin-editor.js +3 -1
- package/dist/pages/document-plugin-viewer.js +1 -1
- package/dist/pages/sdoc-wiki-viewer.js +11 -10
- package/package.json +1 -1
- package/public/locales/cs/sdoc-editor.json +2 -1
- package/public/locales/de/sdoc-editor.json +2 -1
- package/public/locales/en/sdoc-editor.json +2 -1
- package/public/locales/es/sdoc-editor.json +2 -1
- package/public/locales/es_AR/sdoc-editor.json +465 -0
- package/public/locales/es_MX/sdoc-editor.json +465 -0
- package/public/locales/fr/sdoc-editor.json +2 -1
- package/public/locales/it/sdoc-editor.json +2 -1
- package/public/locales/ru/sdoc-editor.json +5 -4
- package/public/locales/zh_CN/sdoc-editor.json +2 -1
- package/dist/basic-sdk/extension/plugins/column/menu/index.js +0 -28
- package/public/locales/es-AR/sdoc-editor.json +0 -169
- package/public/locales/es-MX/sdoc-editor.json +0 -169
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useMemo, Fragment } from 'react';
|
|
2
2
|
import { Editable, ReactEditor, Slate } from '@seafile/slate-react';
|
|
3
|
-
import { Editor, Node, Transforms } from '@seafile/slate';
|
|
3
|
+
import { Editor, Node, Range, Transforms } from '@seafile/slate';
|
|
4
4
|
import scrollIntoView from 'scroll-into-view-if-needed';
|
|
5
5
|
import { renderLeaf, renderElement, ContextToolbar, SideToolbar } from '../extension';
|
|
6
6
|
import { getAboveBlockNode, getNextNode, getPrevNode, isSelectionAtBlockEnd, isSelectionAtBlockStart, getCurrentNode, isCurrentLineEmpty, isCurrentLineHasText } from '../extension/core';
|
|
@@ -89,8 +89,9 @@ const EditableArticle = _ref => {
|
|
|
89
89
|
if (getPrevNode(editor)) {
|
|
90
90
|
[prevNode, prevPath] = getPrevNode(editor);
|
|
91
91
|
}
|
|
92
|
-
// If the cursor is at the beginning, and the current line is not empty, is not a CODE_LINE, and the previous line is a CODE_LINE.
|
|
93
|
-
|
|
92
|
+
// If the cursor is collapsed at the beginning, and the current line is not empty, is not a CODE_LINE, and the previous line is a CODE_LINE.
|
|
93
|
+
const isCursorCollapsed = Range.isCollapsed(editor.selection);
|
|
94
|
+
if (isCursorCollapsed && prevNode && isSelectionAtBlockStart(editor) && !isCurrentLineEmpty(editor) && prevNode.type === CODE_LINE && currentNode.type !== CODE_LINE) {
|
|
94
95
|
if (!isCurrentLineHasText(currentNode)) {
|
|
95
96
|
const prevNodeText = Node.string(prevNode);
|
|
96
97
|
Transforms.removeNodes(editor, {
|
|
@@ -25,8 +25,7 @@ const SdocEditor = forwardRef((_ref, ref) => {
|
|
|
25
25
|
isReloading,
|
|
26
26
|
showComment,
|
|
27
27
|
isShowHeaderToolbar = true,
|
|
28
|
-
showOutline = true
|
|
29
|
-
isWikiReadOnly
|
|
28
|
+
showOutline = true
|
|
30
29
|
} = _ref;
|
|
31
30
|
const validEditor = propsEditor || useMemo(() => {
|
|
32
31
|
const defaultEditor = createDefaultEditor();
|
|
@@ -167,22 +166,6 @@ const SdocEditor = forwardRef((_ref, ref) => {
|
|
|
167
166
|
}
|
|
168
167
|
const isShowComment = typeof showComment === 'boolean' ? showComment : true;
|
|
169
168
|
const Provider = isShowComment ? CollaboratorsProvider : Fragment;
|
|
170
|
-
if (isWikiReadOnly) {
|
|
171
|
-
return /*#__PURE__*/React.createElement(EditorContainer, {
|
|
172
|
-
editor: validEditor,
|
|
173
|
-
readonly: isWikiReadOnly
|
|
174
|
-
}, /*#__PURE__*/React.createElement(ColorProvider, null, /*#__PURE__*/React.createElement(EditorContent, {
|
|
175
|
-
docValue: slateValue,
|
|
176
|
-
showOutline: showOutline !== null && showOutline !== void 0 ? showOutline : true,
|
|
177
|
-
readonly: isWikiReadOnly,
|
|
178
|
-
editor: validEditor,
|
|
179
|
-
showComment: isShowComment
|
|
180
|
-
}, /*#__PURE__*/React.createElement(ReadOnlyArticle, {
|
|
181
|
-
editor: validEditor,
|
|
182
|
-
slateValue: slateValue,
|
|
183
|
-
showComment: isShowComment
|
|
184
|
-
}))));
|
|
185
|
-
}
|
|
186
169
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(EditorContainer, {
|
|
187
170
|
editor: validEditor
|
|
188
171
|
}, /*#__PURE__*/React.createElement(Provider, null, /*#__PURE__*/React.createElement(ColorProvider, null, isShowHeaderToolbar && /*#__PURE__*/React.createElement(HeaderToolbar, {
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
|
+
import React, { useEffect, useState, useImperativeHandle, forwardRef, useMemo, useCallback } from 'react';
|
|
3
|
+
import { Editor } from '@seafile/slate';
|
|
4
|
+
import deepCopy from 'deep-copy';
|
|
5
|
+
import context from '../../context';
|
|
6
|
+
import CommonLoading from '../../components/common-loading';
|
|
7
|
+
import { INTERNAL_EVENT, PAGE_EDIT_AREA_WIDTH } from '../constants';
|
|
8
|
+
import { createDefaultEditor } from '../extension';
|
|
9
|
+
import withNodeId from '../node-id';
|
|
10
|
+
import { withSocketIO } from '../socket';
|
|
11
|
+
import { focusEditor } from '../extension/core';
|
|
12
|
+
import InsertElementDialog from '../extension/commons/insert-element-dialog';
|
|
13
|
+
import { EditorContainer, EditorContent } from '../layout';
|
|
14
|
+
import EditableArticle from './editable-article';
|
|
15
|
+
import { ColorProvider } from '../hooks/use-color-context';
|
|
16
|
+
import ReadOnlyArticle from '../views/readonly-article';
|
|
17
|
+
import { isMobile } from '../../utils';
|
|
18
|
+
import { EventBus } from '../../basic-sdk';
|
|
19
|
+
import { EXTERNAL_EVENT } from '../../constants';
|
|
20
|
+
const WikiEditor = forwardRef((_ref, ref) => {
|
|
21
|
+
let {
|
|
22
|
+
editor: propsEditor,
|
|
23
|
+
document,
|
|
24
|
+
isReloading,
|
|
25
|
+
isWikiReadOnly,
|
|
26
|
+
topSlot
|
|
27
|
+
} = _ref;
|
|
28
|
+
const validEditor = propsEditor || useMemo(() => {
|
|
29
|
+
const defaultEditor = createDefaultEditor();
|
|
30
|
+
const editorConfig = context.getEditorConfig();
|
|
31
|
+
const newEditor = withNodeId(withSocketIO(defaultEditor, {
|
|
32
|
+
document,
|
|
33
|
+
config: editorConfig
|
|
34
|
+
}));
|
|
35
|
+
const {
|
|
36
|
+
cursors
|
|
37
|
+
} = document;
|
|
38
|
+
newEditor.cursors = cursors || {};
|
|
39
|
+
newEditor.width = PAGE_EDIT_AREA_WIDTH; // default width
|
|
40
|
+
return newEditor;
|
|
41
|
+
|
|
42
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
43
|
+
}, []);
|
|
44
|
+
const [slateValue, setSlateValue] = useState(document.children);
|
|
45
|
+
|
|
46
|
+
// Fix: The editor's children are not updated when the document is updated in revision
|
|
47
|
+
// In revision mode, the document is updated, but the editor's children are not updated,as onValueChange override the new document.children. This unexpected action cause the editor to display the old content
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
setSlateValue(document.children);
|
|
50
|
+
}, [document.children]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
validEditor.readonly = false;
|
|
53
|
+
return () => {
|
|
54
|
+
validEditor.selection = null;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
58
|
+
}, []);
|
|
59
|
+
// useMount: init socket connection
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (propsEditor) return;
|
|
62
|
+
validEditor.openConnection();
|
|
63
|
+
return () => {
|
|
64
|
+
validEditor.closeConnection();
|
|
65
|
+
};
|
|
66
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
// useMount: focus editor
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const timer = setTimeout(() => {
|
|
72
|
+
const [firstNode] = validEditor.children;
|
|
73
|
+
if (firstNode) {
|
|
74
|
+
const [firstNodeFirstChild] = firstNode.children;
|
|
75
|
+
if (firstNodeFirstChild) {
|
|
76
|
+
const endOfFirstNode = Editor.end(validEditor, [0, 0]);
|
|
77
|
+
const range = {
|
|
78
|
+
anchor: endOfFirstNode,
|
|
79
|
+
focus: endOfFirstNode
|
|
80
|
+
};
|
|
81
|
+
focusEditor(validEditor, range);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}, 300);
|
|
85
|
+
return () => {
|
|
86
|
+
clearTimeout(timer);
|
|
87
|
+
};
|
|
88
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
89
|
+
}, []);
|
|
90
|
+
const onRefreshDocument = useCallback(() => {
|
|
91
|
+
window.location.reload();
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
// useMount: refresh document
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
const eventBus = EventBus.getInstance();
|
|
97
|
+
eventBus.subscribe(EXTERNAL_EVENT.REFRESH_DOCUMENT, onRefreshDocument);
|
|
98
|
+
}, [onRefreshDocument]);
|
|
99
|
+
|
|
100
|
+
// The parent component can call the method of this component through ref
|
|
101
|
+
useImperativeHandle(ref, () => ({
|
|
102
|
+
setSlateValue: document => {
|
|
103
|
+
// Force update of editor's child elements
|
|
104
|
+
validEditor.children = document.children;
|
|
105
|
+
setSlateValue([...document.children]);
|
|
106
|
+
},
|
|
107
|
+
updateDocumentVersion: document => {
|
|
108
|
+
validEditor.updateDocumentVersion(document);
|
|
109
|
+
},
|
|
110
|
+
// get value
|
|
111
|
+
getSlateValue: () => {
|
|
112
|
+
return deepCopy(_objectSpread(_objectSpread({}, document), {}, {
|
|
113
|
+
children: slateValue
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
118
|
+
}), [document, validEditor, slateValue]);
|
|
119
|
+
const onValueChange = value => {
|
|
120
|
+
const eventBus = EventBus.getInstance();
|
|
121
|
+
eventBus.dispatch(INTERNAL_EVENT.UPDATE_SEARCH_REPLACE_HIGHLIGHT, value);
|
|
122
|
+
setSlateValue(value);
|
|
123
|
+
};
|
|
124
|
+
if (isReloading) {
|
|
125
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
126
|
+
className: "h-100 w-100 d-flex align-items-center justify-content-center"
|
|
127
|
+
}, /*#__PURE__*/React.createElement(CommonLoading, null));
|
|
128
|
+
}
|
|
129
|
+
if (isMobile || isWikiReadOnly) {
|
|
130
|
+
return /*#__PURE__*/React.createElement(EditorContainer, {
|
|
131
|
+
editor: validEditor,
|
|
132
|
+
readonly: true
|
|
133
|
+
}, /*#__PURE__*/React.createElement(ColorProvider, null, /*#__PURE__*/React.createElement(EditorContent, {
|
|
134
|
+
docValue: slateValue,
|
|
135
|
+
readonly: true,
|
|
136
|
+
showOutline: false,
|
|
137
|
+
editor: validEditor,
|
|
138
|
+
showComment: false
|
|
139
|
+
}, topSlot, /*#__PURE__*/React.createElement(ReadOnlyArticle, {
|
|
140
|
+
editor: validEditor,
|
|
141
|
+
slateValue: slateValue,
|
|
142
|
+
showComment: false
|
|
143
|
+
}))));
|
|
144
|
+
}
|
|
145
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(EditorContainer, {
|
|
146
|
+
editor: validEditor
|
|
147
|
+
}, /*#__PURE__*/React.createElement(ColorProvider, null, /*#__PURE__*/React.createElement(EditorContent, {
|
|
148
|
+
docValue: slateValue,
|
|
149
|
+
showOutline: false,
|
|
150
|
+
editor: validEditor,
|
|
151
|
+
showComment: false
|
|
152
|
+
}, topSlot, /*#__PURE__*/React.createElement(EditableArticle, {
|
|
153
|
+
editor: validEditor,
|
|
154
|
+
slateValue: slateValue,
|
|
155
|
+
updateSlateValue: onValueChange,
|
|
156
|
+
showComment: false
|
|
157
|
+
})))), /*#__PURE__*/React.createElement(InsertElementDialog, {
|
|
158
|
+
editor: validEditor
|
|
159
|
+
}));
|
|
160
|
+
});
|
|
161
|
+
export default WikiEditor;
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
|
-
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { ReactEditor } from '@seafile/slate-react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import { Editor } from '@seafile/slate';
|
|
5
|
+
import { Editor, Node } from '@seafile/slate';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
7
|
import context from '../../../../context';
|
|
8
8
|
import { LocalStorage, isEnglish } from '../../../../utils';
|
|
9
9
|
import { EXTERNAL_EVENT } from '../../../../constants';
|
|
10
10
|
import EventBus from '../../../utils/event-bus';
|
|
11
|
-
import { INTERNAL_EVENT } from '../../../constants';
|
|
11
|
+
import { INTERNAL_EVENT, WIKI_EDITOR } from '../../../constants';
|
|
12
12
|
import { SDOC_LINK } from '../../constants';
|
|
13
13
|
import toaster from '../../../../components/toast';
|
|
14
14
|
import { insertSdocFileLink, insertTextWhenRemoveFileNameCollector, removeTempInput } from '../../plugins/sdoc-link/helpers';
|
|
15
|
+
import { insertWikiPageLink } from '../../plugins/wiki-link/helpers';
|
|
15
16
|
import './style.css';
|
|
16
17
|
const FileLinkInsertDialog = _ref => {
|
|
17
18
|
let {
|
|
@@ -19,7 +20,6 @@ const FileLinkInsertDialog = _ref => {
|
|
|
19
20
|
element,
|
|
20
21
|
closeDialog
|
|
21
22
|
} = _ref;
|
|
22
|
-
const eventBus = EventBus.getInstance();
|
|
23
23
|
const historyFileWrapperRef = useRef(document.querySelector('.sdoc-history-files-wrapper'));
|
|
24
24
|
const [files, setFiles] = useState([]);
|
|
25
25
|
const [position, setPosition] = useState({
|
|
@@ -31,6 +31,7 @@ const FileLinkInsertDialog = _ref => {
|
|
|
31
31
|
t
|
|
32
32
|
} = useTranslation();
|
|
33
33
|
const [header, setHeader] = useState(t('Recent_visited'));
|
|
34
|
+
const eventBus = EventBus.getInstance();
|
|
34
35
|
const deleteInputAndInsertText = useCallback(function () {
|
|
35
36
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
36
37
|
args[_key] = arguments[_key];
|
|
@@ -63,6 +64,17 @@ const FileLinkInsertDialog = _ref => {
|
|
|
63
64
|
const counterTopGap = 8;
|
|
64
65
|
popoverTop = top - popoverHeight - counterTopGap;
|
|
65
66
|
}
|
|
67
|
+
if (editor.editorType === WIKI_EDITOR) {
|
|
68
|
+
const wikiEditorContainer = document.querySelector('.sdoc-editor-container');
|
|
69
|
+
if (wikiEditorContainer) {
|
|
70
|
+
const {
|
|
71
|
+
left,
|
|
72
|
+
top
|
|
73
|
+
} = wikiEditorContainer.getBoundingClientRect();
|
|
74
|
+
popoverLeft -= left;
|
|
75
|
+
popoverTop -= top;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
66
78
|
setPosition({
|
|
67
79
|
top: popoverTop,
|
|
68
80
|
left: popoverLeft
|
|
@@ -81,9 +93,10 @@ const FileLinkInsertDialog = _ref => {
|
|
|
81
93
|
getPosition();
|
|
82
94
|
}, [getPosition]);
|
|
83
95
|
const getHistoryFiles = useCallback(e => {
|
|
84
|
-
const
|
|
96
|
+
const getItemKey = editor.editorType === WIKI_EDITOR ? 'wiki-recent-files' : 'sdoc-recent-files';
|
|
97
|
+
const files = LocalStorage.getItem(getItemKey) || [];
|
|
85
98
|
setFiles(files);
|
|
86
|
-
}, []);
|
|
99
|
+
}, [editor.editorType]);
|
|
87
100
|
useEffect(() => {
|
|
88
101
|
getHistoryFiles();
|
|
89
102
|
}, [getHistoryFiles]);
|
|
@@ -168,17 +181,23 @@ const FileLinkInsertDialog = _ref => {
|
|
|
168
181
|
}, []);
|
|
169
182
|
useEffect(() => {
|
|
170
183
|
if (!(element === null || element === void 0 ? void 0 : element.children)) return;
|
|
171
|
-
const searchText = element
|
|
184
|
+
const searchText = Node.string(element);
|
|
172
185
|
onSearch(searchText);
|
|
173
186
|
}, [element, onSearch]);
|
|
174
187
|
const onSelect = useCallback(fileInfo => {
|
|
175
188
|
const {
|
|
176
189
|
doc_uuid,
|
|
177
|
-
name
|
|
190
|
+
name,
|
|
191
|
+
wikiRepoId,
|
|
192
|
+
pageId
|
|
178
193
|
} = fileInfo;
|
|
179
194
|
removeTempInput(editor, element);
|
|
180
195
|
closeDialog();
|
|
181
|
-
|
|
196
|
+
if (editor.editorType === WIKI_EDITOR) {
|
|
197
|
+
insertWikiPageLink(editor, name, wikiRepoId, pageId);
|
|
198
|
+
} else {
|
|
199
|
+
insertSdocFileLink(editor, name, doc_uuid);
|
|
200
|
+
}
|
|
182
201
|
}, [closeDialog, editor, element]);
|
|
183
202
|
const onShowMore = useCallback(() => {
|
|
184
203
|
eventBus.dispatch(INTERNAL_EVENT.INSERT_ELEMENT, {
|
|
@@ -189,10 +208,17 @@ const FileLinkInsertDialog = _ref => {
|
|
|
189
208
|
}, [editor, element, eventBus]);
|
|
190
209
|
const onCreateFile = useCallback(() => {
|
|
191
210
|
const eventBus = EventBus.getInstance();
|
|
192
|
-
|
|
211
|
+
const dispatchEventKey = editor.editorType === WIKI_EDITOR ? EXTERNAL_EVENT.CREATE_WIKI_PAGE : EXTERNAL_EVENT.CREATE_SDOC_FILE;
|
|
212
|
+
eventBus.dispatch(dispatchEventKey, {
|
|
193
213
|
newFileName: newFileName.trim()
|
|
194
214
|
});
|
|
195
|
-
}, [newFileName]);
|
|
215
|
+
}, [editor.editorType, newFileName]);
|
|
216
|
+
const createFileTipDefault = useMemo(() => {
|
|
217
|
+
return editor.editorType === WIKI_EDITOR ? 'New_page' : 'Create_a_new_sdoc_file';
|
|
218
|
+
}, [editor.editorType]);
|
|
219
|
+
const createFileName = useMemo(() => {
|
|
220
|
+
return editor.editorType === WIKI_EDITOR ? newFileName : "".concat(newFileName, ".sdoc");
|
|
221
|
+
}, [editor.editorType, newFileName]);
|
|
196
222
|
return /*#__PURE__*/React.createElement("div", {
|
|
197
223
|
className: "sdoc-history-files-content popover",
|
|
198
224
|
style: _objectSpread(_objectSpread({}, position), {}, {
|
|
@@ -225,7 +251,7 @@ const FileLinkInsertDialog = _ref => {
|
|
|
225
251
|
}), /*#__PURE__*/React.createElement("span", {
|
|
226
252
|
className: "new-file-name"
|
|
227
253
|
}, newFileName ? t('Create_file_name_sdoc', {
|
|
228
|
-
file_name_sdoc:
|
|
229
|
-
}) : t(
|
|
254
|
+
file_name_sdoc: createFileName
|
|
255
|
+
}) : t(createFileTipDefault))));
|
|
230
256
|
};
|
|
231
257
|
export default FileLinkInsertDialog;
|
|
@@ -18,6 +18,8 @@ const InsertElementDialog = _ref => {
|
|
|
18
18
|
const [slateNode, setSlateNode] = useState(null);
|
|
19
19
|
const [insertLinkCallback, setInsertLinkCallback] = useState(null);
|
|
20
20
|
const [validEditor, setValidEditor] = useState(editor);
|
|
21
|
+
const [linkTitle, setLinkTitle] = useState('');
|
|
22
|
+
const [handleSubmit, setHandleSubmit] = useState(() => void 0);
|
|
21
23
|
const uploadLocalImageInputRef = useRef();
|
|
22
24
|
const onFileChanged = useCallback(event => {
|
|
23
25
|
const files = event.target.files;
|
|
@@ -45,7 +47,10 @@ const InsertElementDialog = _ref => {
|
|
|
45
47
|
slateNode,
|
|
46
48
|
insertFileLinkCallback,
|
|
47
49
|
insertSdocFileLinkCallback,
|
|
48
|
-
editor: paramEditor
|
|
50
|
+
editor: paramEditor,
|
|
51
|
+
linkTitle,
|
|
52
|
+
// link shortcut wrapping link
|
|
53
|
+
handleSubmit
|
|
49
54
|
} = _ref2;
|
|
50
55
|
setInsertPosition(insertPosition);
|
|
51
56
|
setSlateNode(slateNode);
|
|
@@ -55,6 +60,8 @@ const InsertElementDialog = _ref => {
|
|
|
55
60
|
insertSdocFileLinkCallback,
|
|
56
61
|
insertFileLinkCallback
|
|
57
62
|
});
|
|
63
|
+
setLinkTitle(linkTitle);
|
|
64
|
+
setHandleSubmit(handleSubmit);
|
|
58
65
|
// Apply for comment editor, as it has a different editor instance
|
|
59
66
|
setValidEditor(paramEditor || editor);
|
|
60
67
|
if (type === LOCAL_IMAGE) {
|
|
@@ -70,13 +77,16 @@ const InsertElementDialog = _ref => {
|
|
|
70
77
|
setDialogType('');
|
|
71
78
|
setInsertLinkCallback(null);
|
|
72
79
|
setValidEditor(null);
|
|
80
|
+
setLinkTitle('');
|
|
73
81
|
}, []);
|
|
74
82
|
const props = {
|
|
75
83
|
insertPosition,
|
|
76
84
|
slateNode,
|
|
77
85
|
editor: validEditor,
|
|
78
86
|
element,
|
|
79
|
-
closeDialog
|
|
87
|
+
closeDialog,
|
|
88
|
+
linkTitle,
|
|
89
|
+
handleSubmit
|
|
80
90
|
};
|
|
81
91
|
switch (dialogType) {
|
|
82
92
|
case ELEMENT_TYPE.TABLE:
|
|
@@ -20,6 +20,7 @@ export const TABLE_ROW = 'table_row';
|
|
|
20
20
|
export const TABLE_CELL = 'table_cell';
|
|
21
21
|
export const LINK = 'link';
|
|
22
22
|
export const SDOC_LINK = 'sdoc_link';
|
|
23
|
+
export const WIKI_LINK = 'wiki_link';
|
|
23
24
|
export const FILE_LINK = 'file_link';
|
|
24
25
|
export const IMAGE = 'image';
|
|
25
26
|
export const IMAGE_BLOCK = 'image_block';
|
|
@@ -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, FILE_LINK_INSET_INPUT_TEMP, QUICK_INSERT } 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, QUICK_INSERT, COLUMN } 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';
|
|
@@ -38,7 +38,7 @@ export const FILE_TYPE = {
|
|
|
38
38
|
[FILE_LINK]: 'file',
|
|
39
39
|
[SDOC_LINK]: 'sdoc'
|
|
40
40
|
};
|
|
41
|
-
export const SUPPORTED_SIDE_OPERATION_TYPE = [PARAGRAPH, SUBTITLE, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, CHECK_LIST_ITEM, CODE_BLOCK, TABLE, BLOCKQUOTE, CALL_OUT, IMAGE_BLOCK];
|
|
41
|
+
export const SUPPORTED_SIDE_OPERATION_TYPE = [PARAGRAPH, SUBTITLE, HEADER1, HEADER2, HEADER3, HEADER4, HEADER5, HEADER6, CHECK_LIST_ITEM, CODE_BLOCK, TABLE, BLOCKQUOTE, CALL_OUT, IMAGE_BLOCK, COLUMN];
|
|
42
42
|
export const MOUSE_ENTER_EVENT_DISABLED_MAP = {
|
|
43
43
|
[PARAGRAPH]: [CALL_OUT],
|
|
44
44
|
[TITLE]: [CALL_OUT],
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.column-list-menu {
|
|
2
|
+
position: absolute;
|
|
3
|
+
left: -8px;
|
|
4
|
+
background-color: #fff;
|
|
5
|
+
min-width: 12rem;
|
|
6
|
+
width: 200px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.column-list-menu .column-list-menu-item-container {
|
|
10
|
+
padding: 3px 12px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.column-list-menu .column-list-menu-item-container .column-list-menu-item {
|
|
14
|
+
height: fit-content;
|
|
15
|
+
width: 100%;
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.column-list-menu .column-list-menu-item-container .column-list-menu-item .control-icon {
|
|
21
|
+
margin-right: 10px;
|
|
22
|
+
flex-shrink: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.column-list-menu .column-list-menu-item-container .column-list-menu-item .control-label {
|
|
26
|
+
white-space: nowrap;
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
text-overflow: ellipsis;
|
|
29
|
+
flex-grow: 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.column-list-menu .column-list-menu-item-container:hover {
|
|
33
|
+
background-color: #f5f5f5;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { useMemo, useCallback } from 'react';
|
|
2
|
+
import { COLUMNS_ICON_CONFIG } from './constants/column';
|
|
3
|
+
import { insertSeaTableColumn, getColumnType } from './helpers';
|
|
4
|
+
import { COLUMN } from '../../constants/element-type';
|
|
5
|
+
import './column-list-menu.css';
|
|
6
|
+
const NOT_SUPPORT_COLUMN_TYPES = ['button', 'file'];
|
|
7
|
+
export default function ColumnListMenu(_ref) {
|
|
8
|
+
let {
|
|
9
|
+
editor
|
|
10
|
+
} = _ref;
|
|
11
|
+
const columns = useMemo(() => {
|
|
12
|
+
if (!editor.columns) return [];
|
|
13
|
+
return editor.columns.filter(column => !NOT_SUPPORT_COLUMN_TYPES.includes(column.type));
|
|
14
|
+
}, [editor.columns]);
|
|
15
|
+
const options = useMemo(() => {
|
|
16
|
+
return columns.map(item => {
|
|
17
|
+
const iconClass = COLUMNS_ICON_CONFIG[item.type];
|
|
18
|
+
return {
|
|
19
|
+
value: item.key,
|
|
20
|
+
label: item.name,
|
|
21
|
+
iconClass
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
}, [columns]);
|
|
25
|
+
const isActive = editor => {
|
|
26
|
+
return getColumnType(editor) === COLUMN;
|
|
27
|
+
};
|
|
28
|
+
const onMousedown = useCallback(option => {
|
|
29
|
+
const active = isActive(editor);
|
|
30
|
+
insertSeaTableColumn(editor, active, option);
|
|
31
|
+
}, [editor]);
|
|
32
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
33
|
+
className: "column-list-menu"
|
|
34
|
+
}, options.map(option => {
|
|
35
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
36
|
+
key: option.value,
|
|
37
|
+
className: "column-list-menu-item-container",
|
|
38
|
+
onClick: () => onMousedown(option)
|
|
39
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
40
|
+
className: "column-list-menu-item"
|
|
41
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
42
|
+
className: "control-icon ".concat(option.iconClass)
|
|
43
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
44
|
+
className: "control-label"
|
|
45
|
+
}, option.label)));
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
|
-
import { Editor, Transforms } from '@seafile/slate';
|
|
2
|
+
import { Editor, Transforms, Range } from '@seafile/slate';
|
|
3
3
|
import slugid from 'slugid';
|
|
4
4
|
import { BLOCKQUOTE, CHECK_LIST_ITEM, COLUMN, IMAGE, ORDERED_LIST, PARAGRAPH, TABLE_CELL, UNORDERED_LIST } from '../../constants/element-type';
|
|
5
5
|
import { focusEditor, getNodeType } from '../../core';
|
|
6
6
|
import Column from './model';
|
|
7
7
|
export const isMenuDisabled = (editor, readonly) => {
|
|
8
8
|
if (readonly) return true;
|
|
9
|
-
|
|
9
|
+
const {
|
|
10
|
+
selection
|
|
11
|
+
} = editor;
|
|
12
|
+
if (selection == null) return true;
|
|
13
|
+
if (!Range.isCollapsed(selection)) return true;
|
|
10
14
|
const [nodeEntry] = Editor.nodes(editor, {
|
|
11
15
|
match: n => {
|
|
12
16
|
const type = getNodeType(n);
|
|
13
17
|
|
|
14
18
|
// Only available for p and blockquote
|
|
15
|
-
if (type ===
|
|
16
|
-
if (type ===
|
|
19
|
+
if (type === BLOCKQUOTE) return false;
|
|
20
|
+
if (type === PARAGRAPH) return false;
|
|
17
21
|
if (type === UNORDERED_LIST) return true;
|
|
18
22
|
if (type === ORDERED_LIST) return true;
|
|
19
23
|
if (type === CHECK_LIST_ITEM) return true;
|
|
20
24
|
if (type === IMAGE) return true;
|
|
21
25
|
if (type === TABLE_CELL) return true;
|
|
26
|
+
if (Editor.isVoid(editor, n)) return true;
|
|
22
27
|
return false;
|
|
23
28
|
},
|
|
24
29
|
universal: true,
|
|
@@ -27,9 +32,9 @@ export const isMenuDisabled = (editor, readonly) => {
|
|
|
27
32
|
|
|
28
33
|
// Match to p blockquote, do not disable
|
|
29
34
|
if (nodeEntry) {
|
|
30
|
-
return
|
|
35
|
+
return true;
|
|
31
36
|
}
|
|
32
|
-
return
|
|
37
|
+
return false;
|
|
33
38
|
};
|
|
34
39
|
export const getColumnType = editor => {
|
|
35
40
|
const [match] = Editor.nodes(editor, {
|
|
@@ -40,9 +45,9 @@ export const getColumnType = editor => {
|
|
|
40
45
|
const [n] = match;
|
|
41
46
|
return getNodeType(n);
|
|
42
47
|
};
|
|
43
|
-
export const insertSeaTableColumn = (editor, active) => {
|
|
48
|
+
export const insertSeaTableColumn = (editor, active, option) => {
|
|
44
49
|
if (!active) {
|
|
45
|
-
const column = new Column(
|
|
50
|
+
const column = new Column(option);
|
|
46
51
|
column.id = slugid.nice();
|
|
47
52
|
Transforms.insertNodes(editor, _objectSpread({}, column));
|
|
48
53
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { COLUMN } from '../../constants/element-type';
|
|
2
|
-
import ColumnMenu from './menu';
|
|
3
2
|
import withColumn from './plugin';
|
|
4
3
|
import renderColumn from './render-elem';
|
|
5
4
|
const ColumnPlugin = {
|
|
6
5
|
type: COLUMN,
|
|
7
6
|
nodeType: 'element',
|
|
8
|
-
editorMenus: [ColumnMenu],
|
|
9
7
|
editorPlugin: withColumn,
|
|
10
8
|
renderElements: [renderColumn]
|
|
11
9
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { MENUS_CONFIG_MAP, ELEMENT_TYPE } from '../../../constants';
|
|
3
|
+
import DropdownMenuItem from '../../../commons/dropdown-menu-item';
|
|
4
|
+
import ColumnListMenu from '../column-list-menu';
|
|
5
|
+
import { UncontrolledPopover } from 'reactstrap';
|
|
6
|
+
import { isMenuDisabled } from '../helpers';
|
|
7
|
+
const SeatableColumnMenu = _ref => {
|
|
8
|
+
let {
|
|
9
|
+
editor,
|
|
10
|
+
readonly
|
|
11
|
+
} = _ref;
|
|
12
|
+
const disabled = isMenuDisabled(editor, readonly);
|
|
13
|
+
const menuConfig = MENUS_CONFIG_MAP[ELEMENT_TYPE.COLUMN];
|
|
14
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DropdownMenuItem, {
|
|
15
|
+
disabled: disabled,
|
|
16
|
+
menuConfig: menuConfig,
|
|
17
|
+
className: "pr-2"
|
|
18
|
+
}, !disabled && /*#__PURE__*/React.createElement("i", {
|
|
19
|
+
className: "sdocfont sdoc-right-slide sdoc-dropdown-item-right-icon"
|
|
20
|
+
})), !disabled && /*#__PURE__*/React.createElement(UncontrolledPopover, {
|
|
21
|
+
target: menuConfig.id,
|
|
22
|
+
trigger: "hover",
|
|
23
|
+
placement: "right-start",
|
|
24
|
+
hideArrow: true,
|
|
25
|
+
fade: false
|
|
26
|
+
}, /*#__PURE__*/React.createElement(ColumnListMenu, {
|
|
27
|
+
editor: editor,
|
|
28
|
+
readonly: readonly
|
|
29
|
+
})));
|
|
30
|
+
};
|
|
31
|
+
export default SeatableColumnMenu;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { generateDefaultText } from '../../core';
|
|
2
2
|
class Column {
|
|
3
|
-
constructor(
|
|
4
|
-
this.type =
|
|
5
|
-
this.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
italic: false
|
|
3
|
+
constructor(option) {
|
|
4
|
+
this.type = 'column';
|
|
5
|
+
this.children = [generateDefaultText()];
|
|
6
|
+
this.data = {
|
|
7
|
+
key: option.value,
|
|
8
|
+
name: option.label
|
|
10
9
|
};
|
|
11
|
-
this.children = options.children || [generateDefaultText()];
|
|
12
10
|
}
|
|
13
11
|
}
|
|
14
12
|
export default Column;
|