@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.
Files changed (45) hide show
  1. package/dist/basic-sdk/editor/editable-article.js +4 -3
  2. package/dist/basic-sdk/editor/sdoc-editor.js +1 -18
  3. package/dist/basic-sdk/editor/wiki-editor.js +161 -0
  4. package/dist/basic-sdk/extension/commons/file-insert-dialog/index.js +39 -13
  5. package/dist/basic-sdk/extension/commons/insert-element-dialog/index.js +12 -2
  6. package/dist/basic-sdk/extension/constants/element-type.js +1 -0
  7. package/dist/basic-sdk/extension/constants/index.js +2 -2
  8. package/dist/basic-sdk/extension/plugins/column/column-list-menu.css +35 -0
  9. package/dist/basic-sdk/extension/plugins/column/column-list-menu.js +47 -0
  10. package/dist/basic-sdk/extension/plugins/column/helpers.js +13 -8
  11. package/dist/basic-sdk/extension/plugins/column/index.js +0 -2
  12. package/dist/basic-sdk/extension/plugins/column/menu/seatable-column.js +31 -0
  13. package/dist/basic-sdk/extension/plugins/column/model.js +6 -8
  14. package/dist/basic-sdk/extension/plugins/column/render-elem.js +39 -101
  15. package/dist/basic-sdk/extension/plugins/index.js +3 -2
  16. package/dist/basic-sdk/extension/plugins/link/dialog/add-link-dialog/index.js +5 -2
  17. package/dist/basic-sdk/extension/plugins/link/plugin.js +26 -11
  18. package/dist/basic-sdk/extension/plugins/sdoc-link/hover-menu/index.js +4 -3
  19. package/dist/basic-sdk/extension/plugins/sdoc-link/plugin.js +3 -2
  20. package/dist/basic-sdk/extension/plugins/sdoc-link/render/render-elem.js +8 -2
  21. package/dist/basic-sdk/extension/plugins/table/plugin.js +8 -1
  22. package/dist/basic-sdk/extension/plugins/wiki-link/helpers.js +60 -0
  23. package/dist/basic-sdk/extension/plugins/wiki-link/index.js +9 -0
  24. package/dist/basic-sdk/extension/render/custom-element.js +10 -6
  25. package/dist/basic-sdk/extension/toolbar/header-toolbar/index.js +1 -6
  26. package/dist/basic-sdk/extension/toolbar/header-toolbar/insert-toolbar/index.js +2 -2
  27. package/dist/basic-sdk/utils/dom-utils.js +1 -2
  28. package/dist/constants/index.js +2 -0
  29. package/dist/pages/document-plugin-editor.js +3 -1
  30. package/dist/pages/document-plugin-viewer.js +1 -1
  31. package/dist/pages/sdoc-wiki-viewer.js +11 -10
  32. package/package.json +1 -1
  33. package/public/locales/cs/sdoc-editor.json +2 -1
  34. package/public/locales/de/sdoc-editor.json +2 -1
  35. package/public/locales/en/sdoc-editor.json +2 -1
  36. package/public/locales/es/sdoc-editor.json +2 -1
  37. package/public/locales/es_AR/sdoc-editor.json +465 -0
  38. package/public/locales/es_MX/sdoc-editor.json +465 -0
  39. package/public/locales/fr/sdoc-editor.json +2 -1
  40. package/public/locales/it/sdoc-editor.json +2 -1
  41. package/public/locales/ru/sdoc-editor.json +5 -4
  42. package/public/locales/zh_CN/sdoc-editor.json +2 -1
  43. package/dist/basic-sdk/extension/plugins/column/menu/index.js +0 -28
  44. package/public/locales/es-AR/sdoc-editor.json +0 -169
  45. 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
- if (prevNode && isSelectionAtBlockStart(editor) && !isCurrentLineEmpty(editor) && prevNode.type === CODE_LINE && currentNode.type !== CODE_LINE) {
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 files = LocalStorage.getItem('sdoc-recent-files') || [];
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.children[0].text;
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
- insertSdocFileLink(editor, name, doc_uuid);
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
- eventBus.dispatch(EXTERNAL_EVENT.CREATE_SDOC_FILE, {
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: "".concat(newFileName, ".sdoc")
229
- }) : t('Create_a_new_sdoc_file'))));
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
- if (editor.selection == null) return true;
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 === PARAGRAPH) return true;
16
- if (type === BLOCKQUOTE) return true;
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 false;
35
+ return true;
31
36
  }
32
- return true;
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(options) {
4
- this.type = options.type || 'column';
5
- this.data = options.data || {
6
- key: '',
7
- name: '',
8
- bold: false,
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;