@seafile/sdoc-editor 0.4.34 → 0.4.36

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 (34) hide show
  1. package/dist/api/seafile-api.js +4 -0
  2. package/dist/basic-sdk/comment/components/global-comment/index.js +1 -2
  3. package/dist/basic-sdk/comment/helper.js +2 -2
  4. package/dist/basic-sdk/comment/utils/index.js +3 -1
  5. package/dist/basic-sdk/editor/editable-article.js +1 -1
  6. package/dist/basic-sdk/editor/sdoc-editor.js +1 -2
  7. package/dist/basic-sdk/extension/commons/history-files/index.css +5 -0
  8. package/dist/basic-sdk/extension/commons/history-files/index.js +84 -29
  9. package/dist/basic-sdk/extension/commons/select-file-dialog/local-files/index.css +2 -0
  10. package/dist/basic-sdk/extension/constants/font.js +1 -0
  11. package/dist/basic-sdk/extension/constants/index.js +1 -0
  12. package/dist/basic-sdk/extension/plugins/callout/render-elem/index.js +2 -0
  13. package/dist/basic-sdk/extension/plugins/code-block/helpers.js +6 -3
  14. package/dist/basic-sdk/extension/plugins/code-block/render-elem.js +2 -0
  15. package/dist/basic-sdk/extension/plugins/file-link/helpers.js +1 -0
  16. package/dist/basic-sdk/extension/plugins/html/helper.js +1 -0
  17. package/dist/basic-sdk/extension/plugins/image/hover-menu/index.js +3 -3
  18. package/dist/basic-sdk/extension/plugins/link/helpers.js +1 -0
  19. package/dist/basic-sdk/extension/plugins/sdoc-link/helpers.js +1 -1
  20. package/dist/basic-sdk/extension/plugins/table/helpers.js +22 -0
  21. package/dist/basic-sdk/extension/plugins/table/render/index.js +5 -3
  22. package/dist/basic-sdk/extension/plugins/table/render/render-cell.js +6 -7
  23. package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/column-resize-handler.js +1 -1
  24. package/dist/basic-sdk/extension/plugins/table/render/resize-handlers/row-resize-handler.js +2 -3
  25. package/dist/basic-sdk/extension/plugins/table/render/table-header/rows-header/row-header.js +1 -1
  26. package/dist/basic-sdk/extension/toolbar/side-toolbar/helpers.js +1 -0
  27. package/dist/basic-sdk/extension/toolbar/side-toolbar/transform-menus.js +11 -4
  28. package/dist/basic-sdk/utils/dom-utils.js +2 -2
  29. package/dist/constants/index.js +2 -1
  30. package/dist/context.js +4 -0
  31. package/dist/layout/layout.js +27 -0
  32. package/dist/utils/get-event-transfer.js +3 -1
  33. package/dist/utils/index.js +13 -6
  34. package/package.json +1 -1
@@ -117,6 +117,10 @@ class SeafileAPI {
117
117
  const url = 'api/v2.1/seadoc/query-copy-move-progress/' + docUuid + '/?&doc_uuid=' + docUuid + '&task_id=' + taskId;
118
118
  return this.req.get(url);
119
119
  }
120
+ searchSdocFiles(docUuid, query, page, per_page) {
121
+ const url = 'api/v2.1/seadoc/search-filename/' + docUuid + '/?query=' + query + '&page=' + page + '&per_page=' + per_page;
122
+ return this.req.get(url);
123
+ }
120
124
 
121
125
  // participants
122
126
  listParticipants(docUuid) {
@@ -1,8 +1,7 @@
1
1
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
2
  import React, { useCallback, useEffect, useRef, useState } from 'react';
3
3
  import dayjs from 'dayjs';
4
- import { useSlateStatic } from '@seafile/slate-react';
5
- import { ReactEditor } from '@seafile/slate-react';
4
+ import { useSlateStatic, ReactEditor } from '@seafile/slate-react';
6
5
  import { ElementPopover } from '../../../extension/commons';
7
6
  import EventBus from '../../../utils/event-bus';
8
7
  import useCommentList from '../../hooks/comment-hooks/use-comment-list';
@@ -13,8 +13,8 @@ export const getSelectionRange = () => {
13
13
  return null;
14
14
  };
15
15
  export const getCursorPosition = () => {
16
- let x = 0,
17
- y = 0;
16
+ let x = 0;
17
+ let y = 0;
18
18
  let range = getSelectionRange();
19
19
  if (range) {
20
20
  const rect = range.getBoundingClientRect();
@@ -100,7 +100,9 @@ class CommentUtilities {
100
100
  content,
101
101
  nodeType
102
102
  } = _ref2;
103
- let spanNode1, spanNode2, imageContainer;
103
+ let spanNode1;
104
+ let spanNode2;
105
+ let imageContainer;
104
106
  if (nodeType === 'image') {
105
107
  spanNode1 = document.createElement('div');
106
108
  spanNode1.className = 'image-container';
@@ -154,7 +154,7 @@ const EditableArticle = _ref => {
154
154
  block: 'nearest'
155
155
  });
156
156
  } catch (error) {
157
- //
157
+ //
158
158
  }
159
159
  }, []);
160
160
  return /*#__PURE__*/React.createElement(Slate, {
@@ -5,7 +5,7 @@ import deepCopy from 'deep-copy';
5
5
  import context from '../../context';
6
6
  import CommonLoading from '../../components/common-loading';
7
7
  import { INTERNAL_EVENT, PAGE_EDIT_AREA_WIDTH } from '../constants';
8
- import { createDefaultEditor } from '../extension';
8
+ import { createDefaultEditor, HeaderToolbar } from '../extension';
9
9
  import withNodeId from '../node-id';
10
10
  import { withSocketIO } from '../socket';
11
11
  import { focusEditor } from '../extension/core';
@@ -13,7 +13,6 @@ import InsertElementDialog from '../extension/commons/insert-element-dialog';
13
13
  import { EditorContainer, EditorContent } from '../layout';
14
14
  import EditableArticle from './editable-article';
15
15
  import { ColorProvider } from '../hooks/use-color-context';
16
- import { HeaderToolbar } from '../extension';
17
16
  import ReadOnlyArticle from '../views/readonly-article';
18
17
  import { isMobile } from '../../utils';
19
18
  import { CollaboratorsProvider } from '../../hooks';
@@ -14,6 +14,7 @@
14
14
  padding: 0px;
15
15
  color: #212529;
16
16
  font-size: 14px;
17
+ text-decoration: underline;
17
18
  }
18
19
 
19
20
  .sdoc-history-files-wrapper .sdoc-history-files-content .sdoc-history-files-header {
@@ -31,6 +32,10 @@
31
32
  overflow-y: scroll;
32
33
  }
33
34
 
35
+ .sdoc-history-files-wrapper .sdoc-history-files-content .no-header {
36
+ margin-top: 8px;
37
+ }
38
+
34
39
  .sdoc-history-files-wrapper .sdoc-history-files-content .sdoc-history-files .sdoc-history-files-item {
35
40
  font-size: 14px;
36
41
  line-height: 32px;
@@ -3,9 +3,14 @@ import React, { useCallback, useEffect, useState, useRef } from 'react';
3
3
  import { Editor } from '@seafile/slate';
4
4
  import { ReactEditor } from '@seafile/slate-react';
5
5
  import { withTranslation } from 'react-i18next';
6
+ import classNames from 'classnames';
7
+ import debounce from 'lodash.debounce';
8
+ import toaster from '../../../../components/toast';
6
9
  import EventBus from '../../../utils/event-bus';
10
+ import context from '../../../../context';
11
+ import { focusEditor } from '../../core';
7
12
  import { insertSdocFileLink } from '../../plugins/sdoc-link/helpers';
8
- import { LocalStorage } from '../../../../utils';
13
+ import { LocalStorage, isEnglish } from '../../../../utils';
9
14
  import { INTERNAL_EVENT } from '../../../constants';
10
15
  import { EXTERNAL_EVENT } from '../../../../constants';
11
16
  import { SDOC_LINK } from '../../constants';
@@ -54,6 +59,13 @@ const HistoryFiles = _ref => {
54
59
  const files = LocalStorage.getItem('sdoc-recent-files') || [];
55
60
  setFiles(files);
56
61
  }, []);
62
+ const onInsertLink = useCallback(params => {
63
+ const {
64
+ insertSdocFileLinkCallback
65
+ } = insertLinkCallback;
66
+ insertSdocFileLinkCallback(editor, params.data.obj_name, params.data.doc_uuid);
67
+ closeDialog();
68
+ }, [closeDialog, editor, insertLinkCallback]);
57
69
  useEffect(() => {
58
70
  getPosition();
59
71
  getHistoryFiles();
@@ -62,41 +74,83 @@ const HistoryFiles = _ref => {
62
74
  }, 0);
63
75
  const sdocScrollContainer = document.getElementById('sdoc-scroll-container');
64
76
  sdocScrollContainer.addEventListener('scroll', onScroll);
77
+ const eventBus = EventBus.getInstance();
78
+ const unsubscribeInsertLink = eventBus.subscribe(EXTERNAL_EVENT.INSERT_LINK, onInsertLink);
65
79
  document.addEventListener('click', onClick);
66
80
  return () => {
67
81
  sdocScrollContainer.removeEventListener('scroll', onScroll);
82
+ unsubscribeInsertLink();
68
83
  document.removeEventListener('click', onClick);
69
84
  };
70
85
  // eslint-disable-next-line react-hooks/exhaustive-deps
71
86
  }, []);
72
- const onSearch = useCallback(e => {
73
- let newFiles = LocalStorage.getItem('sdoc-recent-files') || [];
74
- if (e.target.value) {
75
- newFiles = newFiles.filter(item => item.file_name.includes(e.target.value));
76
- if (newFiles.length === 0) {
77
- setHeader(t('The_document_does_not_exist'));
78
- setNewFileName(e.target.value);
79
- } else {
80
- setHeader(t('Recent_previews'));
81
- setNewFileName('');
82
- }
83
- setFiles(newFiles);
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
+
110
+ // Show history files when search is empty
111
+ if (e.target.value.trim().length === 0) {
112
+ setHeader(t('Recent_previews'));
113
+ setNewFileName('');
114
+ getHistoryFiles();
115
+ return;
116
+ }
117
+
118
+ // Cannot be found if the search is less than three characters.
119
+ if (isEnglish(e.target.value.trim()) && e.target.value.length < 3) {
120
+ setFiles([]);
121
+ setHeader(t('The_document_does_not_exist'));
122
+ setNewFileName(e.target.value);
84
123
  return;
85
124
  }
86
- setHeader(t('Recent_previews'));
87
- setNewFileName('');
88
- setFiles(newFiles);
125
+ try {
126
+ var _res$data;
127
+ const res = await context.searchSdocFiles(e.target.value, 1, 10);
128
+ if (res === null || res === void 0 ? void 0 : (_res$data = res.data) === null || _res$data === void 0 ? void 0 : _res$data.results) {
129
+ let newFiles = res.data.results;
130
+ if (newFiles.length === 0) {
131
+ setHeader(t('The_document_does_not_exist'));
132
+ setNewFileName(e.target.value);
133
+ } else {
134
+ setHeader('');
135
+ setNewFileName('');
136
+ }
137
+ setFiles(newFiles);
138
+ return;
139
+ }
140
+ } catch (error) {
141
+ toaster.danger(error.message);
142
+ }
89
143
  // eslint-disable-next-line react-hooks/exhaustive-deps
90
144
  }, []);
91
145
  const onSelect = useCallback(fileInfo => {
92
146
  const {
93
- file_uuid,
94
- file_name
147
+ doc_uuid,
148
+ name
95
149
  } = fileInfo;
96
150
  const {
97
151
  insertSdocFileLinkCallback
98
152
  } = insertLinkCallback;
99
- insertSdocFileLinkCallback(editor, file_name, file_uuid);
153
+ insertSdocFileLinkCallback(editor, name, doc_uuid);
100
154
  closeDialog();
101
155
  // eslint-disable-next-line react-hooks/exhaustive-deps
102
156
  }, []);
@@ -112,8 +166,7 @@ const HistoryFiles = _ref => {
112
166
  eventBus.dispatch(EXTERNAL_EVENT.CREATE_SDOC_FILE, {
113
167
  newFileName: newFileName.trim()
114
168
  });
115
- closeDialog();
116
- }, [closeDialog, newFileName]);
169
+ }, [newFileName]);
117
170
  return /*#__PURE__*/React.createElement("div", {
118
171
  ref: historyFilesRef,
119
172
  className: "sdoc-history-files-wrapper popover",
@@ -123,26 +176,28 @@ const HistoryFiles = _ref => {
123
176
  className: "sdoc-history-files-search-input",
124
177
  ref: historyFilesInputRef,
125
178
  autoComplete: "off",
126
- onChange: onSearch,
127
- onCompositionStart: e => {
128
- e.stopPropagation();
129
- }
179
+ onChange: debounce(onSearch, 200),
180
+ onCompositionStart: onCompositionStart,
181
+ onCompositionEnd: onCompositionEnd,
182
+ onKeyDown: onKeydown
130
183
  }), /*#__PURE__*/React.createElement("div", {
131
184
  className: "sdoc-history-files-content"
132
- }, /*#__PURE__*/React.createElement("div", {
185
+ }, header.length !== 0 && /*#__PURE__*/React.createElement("div", {
133
186
  className: "sdoc-history-files-header"
134
187
  }, header), /*#__PURE__*/React.createElement("div", {
135
- className: "sdoc-history-files"
188
+ className: classNames('sdoc-history-files', {
189
+ 'no-header': header.length === 0
190
+ })
136
191
  }, files.map(item => {
137
192
  return /*#__PURE__*/React.createElement("div", {
138
- key: item.file_uuid,
193
+ key: item.doc_uuid,
139
194
  className: "sdoc-history-files-item",
140
195
  onClick: () => {
141
196
  onSelect(item);
142
197
  }
143
198
  }, /*#__PURE__*/React.createElement("i", {
144
199
  className: "sdocfont sdoc-document"
145
- }), /*#__PURE__*/React.createElement("span", null, item.file_name));
200
+ }), /*#__PURE__*/React.createElement("span", null, item.name));
146
201
  }), /*#__PURE__*/React.createElement("div", {
147
202
  className: "sdoc-history-files-item",
148
203
  onClick: onShowMore
@@ -25,6 +25,8 @@
25
25
  }
26
26
 
27
27
  .sdoc-files-tree .sdoc-file-info .sdoc-file-icon-container {
28
+ display: flex;
29
+ align-items: center;
28
30
  width: 2.8rem;
29
31
  height: 100%;
30
32
  padding-left: 1.5rem;
@@ -517,6 +517,7 @@ export const FONT = [
517
517
  }
518
518
  } // 楷体
519
519
  ];
520
+
520
521
  export const SDOC_FONT_SIZE = {
521
522
  DEFAULT: 11,
522
523
  [ELEMENT_TYPE.TITLE]: 26,
@@ -1,5 +1,6 @@
1
1
  // extension plugin
2
2
  import * as ELEMENT_TYPE from './element-type';
3
+ // eslint-disable-next-line no-duplicate-imports
3
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 } from './element-type';
4
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';
5
6
  export { FONT_SIZE, DEFAULT_FONT, FONT, GOOGLE_FONT_CLASS, RECENT_USED_FONTS_KEY, SDOC_FONT_SIZE } from './font';
@@ -72,6 +72,7 @@ const renderCallout = (_ref, editor) => {
72
72
  top: menuTop,
73
73
  left: left // left = code-block left distance
74
74
  };
75
+
75
76
  setPopoverPosition(newMenuPosition);
76
77
  }
77
78
  }, [isShowColorSelector, readOnly]);
@@ -107,6 +108,7 @@ const renderCallout = (_ref, editor) => {
107
108
  top: menuTop,
108
109
  left: left // left = callout left distance
109
110
  };
111
+
110
112
  setPopoverPosition(newMenuPosition);
111
113
  setIsShowColorSelector(true);
112
114
  }, [readOnly]);
@@ -23,6 +23,7 @@ export const isMenuDisabled = (editor, readonly) => {
23
23
  if (isMatch) return false; // enable
24
24
  return true; // disable
25
25
  };
26
+
26
27
  export const getSelectCodeElem = editor => {
27
28
  const codeNode = getSelectedNodeByType(editor, CODE_BLOCK);
28
29
  if (codeNode == null) return null;
@@ -32,8 +33,8 @@ export const changeToCodeBlock = function (editor) {
32
33
  let language = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
33
34
  let position = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : INSERT_POSITION.CURRENT;
34
35
  // Summarizes the strings for the selected highest-level node
35
- let strArr = [],
36
- path = Editor.path(editor, editor.selection);
36
+ let strArr = [];
37
+ let path = Editor.path(editor, editor.selection);
37
38
  if (position === INSERT_POSITION.AFTER) {
38
39
  strArr = [''];
39
40
  } else {
@@ -62,6 +63,7 @@ export const changeToCodeBlock = function (editor) {
62
63
  style: {
63
64
  white_space: 'nowrap' // default nowrap
64
65
  },
66
+
65
67
  children: [{
66
68
  id: slugid.nice(),
67
69
  type: CODE_LINE,
@@ -125,7 +127,8 @@ export const changeToPlainText = editor => {
125
127
  };
126
128
  export const setClipboardCodeBlockData = value => {
127
129
  // Insert text into the clipboard for use on other pages
128
- const text = value.children.map(line => Node.string(line)).join('\n');
130
+ // Empty string cannot apply in `copy`
131
+ const text = value.children.map(line => Node.string(line)).join('\n') || ' ';
129
132
  copy(text, {
130
133
  format: 'text/plain',
131
134
  onCopy: data => {
@@ -81,6 +81,7 @@ const CodeBlock = _ref => {
81
81
  top: menuTop,
82
82
  left: left // left = code-block left distance
83
83
  };
84
+
84
85
  setMenuPosition(newMenuPosition);
85
86
  }
86
87
  setShowHoverMenu(true);
@@ -104,6 +105,7 @@ const CodeBlock = _ref => {
104
105
  top: menuTop,
105
106
  left: left // left = code-block left distance
106
107
  };
108
+
107
109
  setMenuPosition(newMenuPosition);
108
110
  }
109
111
  }, [readOnly, showHoverMenu]);
@@ -20,6 +20,7 @@ export const isMenuDisabled = (editor, readonly) => {
20
20
  if (notMatch) return true; // disabled
21
21
  return false; // enable
22
22
  };
23
+
23
24
  export const generateFileNode = (uuid, text) => {
24
25
  const fileNode = {
25
26
  id: slugid.nice(),
@@ -68,6 +68,7 @@ const deserializeElements = function () {
68
68
  // nothing todo
69
69
  }
70
70
  });
71
+
71
72
  return nodes;
72
73
  };
73
74
  const formatElementNodes = nodes => {
@@ -71,9 +71,9 @@ const ImageHoverMenu = _ref => {
71
71
  const beforeLeaf = newNodeEntry[0].children.slice(0, index);
72
72
  const imageLeaf = newNodeEntry[0].children.slice(index, index + 1);
73
73
  const afterLeaf = newNodeEntry[0].children.slice(index + 1);
74
- let beforeNode = null,
75
- centerNode = null,
76
- afterNode = null;
74
+ let beforeNode = null;
75
+ let centerNode = null;
76
+ let afterNode = null;
77
77
  let p = path[0];
78
78
  if (!beforeLeaf.every(item => {
79
79
  var _item$text;
@@ -19,6 +19,7 @@ export const isMenuDisabled = (editor, readonly) => {
19
19
  if (notMatch) return true; // disabled
20
20
  return false; // enable
21
21
  };
22
+
22
23
  export const checkLink = url => {
23
24
  if (url.indexOf('http') !== 0) {
24
25
  return true;
@@ -5,7 +5,6 @@ import slugid from 'slugid';
5
5
  import copy from 'copy-to-clipboard';
6
6
  import context from '../../../../context';
7
7
  import { focusEditor, getNodeType, getSelectedElems } from '../../core';
8
- import { LocalStorage } from '../../../../utils';
9
8
  import { SDOC_LINK, LINK, INSERT_FILE_DISPLAY_TYPE, CODE_BLOCK, CODE_LINE, PARAGRAPH } from '../../constants';
10
9
  export const isMenuDisabled = (editor, readonly) => {
11
10
  if (readonly) return true;
@@ -22,6 +21,7 @@ export const isMenuDisabled = (editor, readonly) => {
22
21
  if (notMatch) return true; // disabled
23
22
  return false; // enable
24
23
  };
24
+
25
25
  export const generateSdocFileNode = (uuid, text) => {
26
26
  const sdocFileNode = {
27
27
  id: slugid.nice(),
@@ -175,6 +175,7 @@ export const isAllInTable = editor => {
175
175
  if (firstSelectedNode.type !== ELEMENT_TYPE.TABLE) return false;
176
176
  return selectedNodes.slice(1).every(node => [ELEMENT_TYPE.TABLE_ROW, ELEMENT_TYPE.TABLE_CELL].includes(node.type)); // same table element
177
177
  };
178
+
178
179
  export const setCellStyle = (editor, style) => {
179
180
  // Select single cell
180
181
  if (ObjectUtils.isSameObject(editor.tableSelectedRange, EMPTY_SELECTED_RANGE)) {
@@ -1512,4 +1513,25 @@ export const getHighlightClass = (editor, cellPath) => {
1512
1513
  const rowIndex = cellPath[cellPath.length - 2];
1513
1514
  const className = getCellHighlightClassName(alternate_highlight_color, rowIndex);
1514
1515
  return className;
1516
+ };
1517
+
1518
+ // Correct the selected range when combined cell are selected
1519
+ export const adjustCombinedCellRange = (table, range) => {
1520
+ const {
1521
+ minRowIndex,
1522
+ maxRowIndex,
1523
+ minColIndex,
1524
+ maxColIndex
1525
+ } = range;
1526
+ const firstCell = table.children[minRowIndex].children[minColIndex];
1527
+ const {
1528
+ colspan = 0,
1529
+ rowspan = 0
1530
+ } = firstCell;
1531
+ if (rowspan > 1 || colspan > 1) {
1532
+ const isRowCombined = minRowIndex + rowspan === maxRowIndex + 1;
1533
+ const isColCombined = minColIndex + colspan === maxColIndex + 1;
1534
+ if (isRowCombined && isColCombined) return EMPTY_SELECTED_RANGE;
1535
+ }
1536
+ return range;
1515
1537
  };
@@ -2,13 +2,13 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
2
  import React, { useRef, useState, useEffect, useCallback } from 'react';
3
3
  import classnames from 'classnames';
4
4
  import throttle from 'lodash.throttle';
5
- import { useSelected, useSlateStatic, useReadOnly } from '@seafile/slate-react';
5
+ import { useSelected, useSlateStatic, useReadOnly, ReactEditor } from '@seafile/slate-react';
6
6
  import { Transforms, Editor } from '@seafile/slate';
7
7
  import { EMPTY_SELECTED_RANGE } from '../constants';
8
8
  import { ResizeHandlersContext, TableSelectedRangeContext, SettingSelectRangeContext } from './hooks';
9
9
  import EventBus from '../../../../utils/event-bus';
10
10
  import { INTERNAL_EVENT } from '../../../../constants';
11
- import { getTableColumns, setTableSelectedRange, getFirstTableCell, getRowHeight } from '../helpers';
11
+ import { getTableColumns, setTableSelectedRange, getFirstTableCell, getRowHeight, adjustCombinedCellRange } from '../helpers';
12
12
  import ObjectUtils from '../../../../utils/object-utils';
13
13
  import ResizeHandlers from './resize-handlers';
14
14
  import { registerResizeEvents, unregisterResizeEvents } from '../../../../utils/mouse-event';
@@ -102,12 +102,14 @@ const Table = _ref => {
102
102
  const cellData = tableCell.style.gridArea.split(' / ');
103
103
  const endRowSpan = Number(cellData[2].split(' ')[1]);
104
104
  const endColSpan = Number(cellData[3].split(' ')[1]);
105
- const newSelectedRange = {
105
+ const tableSlateNode = ReactEditor.toSlateNode(editor, table.current);
106
+ let newSelectedRange = {
106
107
  minRowIndex: Math.min(startRowIndex, endRowIndex),
107
108
  maxRowIndex: startRowIndex < endRowIndex ? endRowIndex + endRowSpan - 1 : startRowIndex + startRowSpan - 1,
108
109
  minColIndex: Math.min(startColIndex, endColIndex),
109
110
  maxColIndex: startColIndex < endColIndex ? endColIndex + endColSpan - 1 : startColIndex + startColSpan - 1
110
111
  };
112
+ newSelectedRange = adjustCombinedCellRange(tableSlateNode, newSelectedRange);
111
113
  if (!ObjectUtils.isSameObject(selectedRange, EMPTY_SELECTED_RANGE)) {
112
114
  event.preventDefault();
113
115
  Editor.withoutNormalizing(editor, () => {
@@ -5,9 +5,9 @@ import { useSlateStatic, useReadOnly } from '@seafile/slate-react';
5
5
  import { Editor, Transforms } from '@seafile/slate';
6
6
  import ObjectUtils from '../../../../utils/object-utils';
7
7
  import { findPath, focusEditor } from '../../../core';
8
- import { useResizeHandlersContext, useTableSelectedRangeContext } from './hooks';
8
+ import { useTableSelectedRangeContext } from './hooks';
9
9
  import { EMPTY_SELECTED_RANGE, SELECTED_TABLE_CELL_BACKGROUND_COLOR, TABLE_CELL_STYLE } from '../constants';
10
- import { getTableColumns, colorBlend, getHighlightClass } from '../helpers';
10
+ import { colorBlend, getHighlightClass } from '../helpers';
11
11
  import EventBus from '../../../../utils/event-bus';
12
12
  import { INTERNAL_EVENT } from '../../../../constants';
13
13
  const TableCell = _ref => {
@@ -19,7 +19,6 @@ const TableCell = _ref => {
19
19
  const editor = useSlateStatic();
20
20
  const selectedRange = useTableSelectedRangeContext() || EMPTY_SELECTED_RANGE;
21
21
  const cellPath = findPath(editor, element, [0, 0]);
22
- const columns = useResizeHandlersContext() || getTableColumns(editor, element);
23
22
  const pathLength = cellPath.length;
24
23
  const rowIndex = cellPath[pathLength - 2];
25
24
  const cellIndex = cellPath[pathLength - 1];
@@ -60,10 +59,10 @@ const TableCell = _ref => {
60
59
  if (element.is_combined) {
61
60
  style.display = 'none';
62
61
  }
63
- if (rowIndex == 0) {
62
+ if (rowIndex === 0) {
64
63
  style.borderTop = '1px solid #ddd';
65
64
  }
66
- if (cellIndex == 0) {
65
+ if (cellIndex === 0) {
67
66
  style.borderLeft = '1px solid #ddd';
68
67
  }
69
68
  const {
@@ -134,10 +133,10 @@ function renderTableCell(props) {
134
133
  if (element.is_combined) {
135
134
  style.display = 'none';
136
135
  }
137
- if (rowIndex == 0) {
136
+ if (rowIndex === 0) {
138
137
  style.borderTop = '1px solid #ddd';
139
138
  }
140
- if (cellIndex == 0) {
139
+ if (cellIndex === 0) {
141
140
  style.borderLeft = '1px solid #ddd';
142
141
  }
143
142
  const {
@@ -1,5 +1,5 @@
1
1
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
- import React, { useRef, useState, useEffect, useCallback, useLayoutEffect } from 'react';
2
+ import React, { useRef, useState, useEffect, useLayoutEffect } from 'react';
3
3
  import { useSlateStatic } from '@seafile/slate-react';
4
4
  import { useTableRootContext } from '../hooks';
5
5
  import { TABLE_CELL_MIN_WIDTH } from '../../constants';
@@ -1,6 +1,5 @@
1
1
  import React, { useEffect, useState, useRef, useLayoutEffect } from 'react';
2
- import { useSlateStatic } from '@seafile/slate-react';
3
- import { ReactEditor } from '@seafile/slate-react';
2
+ import { useSlateStatic, ReactEditor } from '@seafile/slate-react';
4
3
  import { TABLE_ROW_MIN_HEIGHT } from '../../constants';
5
4
  import { focusClosestCellWhenJustifyCellSize, getRowHeight, updateTableRowHeight } from '../../helpers';
6
5
  import { eventStopPropagation, getMouseDownInfo, getMouseMoveInfo, registerResizeEvents, unregisterResizeEvents } from '../../../../../utils/mouse-event';
@@ -65,7 +64,7 @@ const RowResizeHandler = _ref => {
65
64
  // eslint-disable-next-line react-hooks/exhaustive-deps
66
65
  }, [isDraggingResizeHandler, mouseDownInfo, rowBottom, table, height]);
67
66
  useEffect(() => {
68
- const cell = row.children.filter(cell => !cell.is_combined && (!cell.rowspan || cell.rowspan == 1))[0];
67
+ const cell = row.children.filter(cell => !cell.is_combined && (!cell.rowspan || cell.rowspan === 1))[0];
69
68
  if (!cell) return;
70
69
  const rowDom = ReactEditor.toDOMNode(editor, cell);
71
70
  if (!rowDom) return;
@@ -1,4 +1,4 @@
1
- import React, { useRef, useCallback, useState, useEffect, useLayoutEffect } from 'react';
1
+ import React, { useRef, useCallback, useState, useEffect } from 'react';
2
2
  import { useSlateStatic } from '@seafile/slate-react';
3
3
  import classnames from 'classnames';
4
4
  import ObjectUtils from '../../../../../../utils/object-utils';
@@ -92,6 +92,7 @@ export const getDomTopHeight = (dom, slateNode) => {
92
92
  if (ADD_POSITION_OFFSET_TYPE.includes(slateNode.type)) {
93
93
  offsetY = lightHight / 2 + paddingTop - 12; // side toolbar icon is 12 px
94
94
  }
95
+
95
96
  const HEADER_HEIGHT = 56 + 44;
96
97
  return rect.y - HEADER_HEIGHT + offsetY;
97
98
  };
@@ -3,7 +3,7 @@ import { UncontrolledPopover } from 'reactstrap';
3
3
  import { withTranslation } from 'react-i18next';
4
4
  import { ReactEditor, useSlateStatic } from '@seafile/slate-react';
5
5
  import { onSetNodeType } from './helpers';
6
- import { SIDE_TRANSFORM_MENUS_CONFIG, LIST_ITEM_SUPPORTED_TRANSFORMATION, LIST_ITEM_CORRELATION_TYPE, BLOCKQUOTE, CALL_OUT } from '../../constants';
6
+ import { SIDE_TRANSFORM_MENUS_CONFIG, LIST_ITEM_SUPPORTED_TRANSFORMATION, LIST_ITEM_CORRELATION_TYPE, BLOCKQUOTE, CALL_OUT, HEADERS } from '../../constants';
7
7
  import DropdownMenuItem from '../../commons/dropdown-menu-item';
8
8
  const TransformMenus = _ref => {
9
9
  let {
@@ -24,11 +24,18 @@ const TransformMenus = _ref => {
24
24
  newSideMenusConfig = SIDE_TRANSFORM_MENUS_CONFIG.filter(item => LIST_ITEM_SUPPORTED_TRANSFORMATION.includes(item.type));
25
25
  }
26
26
  const path = ReactEditor.findPath(editor, slateNode);
27
- if (path && path.length > 1) {
27
+ if (path) {
28
28
  const nodeIndex = path[0];
29
29
  const highestNode = editor.children[nodeIndex];
30
- if (highestNode.type === BLOCKQUOTE) {
31
- newSideMenusConfig = SIDE_TRANSFORM_MENUS_CONFIG.filter(item => item.type !== CALL_OUT);
30
+ if (path.length > 1) {
31
+ if (highestNode.type === BLOCKQUOTE) {
32
+ newSideMenusConfig = SIDE_TRANSFORM_MENUS_CONFIG.filter(item => item.type !== CALL_OUT);
33
+ }
34
+ }
35
+
36
+ // headers can't be nested by quote block
37
+ if (HEADERS.includes(highestNode.type)) {
38
+ newSideMenusConfig = SIDE_TRANSFORM_MENUS_CONFIG.filter(item => item.type !== BLOCKQUOTE);
32
39
  }
33
40
  }
34
41
  return newSideMenusConfig;
@@ -27,8 +27,8 @@ export const getSelectionRange = () => {
27
27
  };
28
28
  export const getCursorPosition = function () {
29
29
  let isScrollUp = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
30
- let x = 0,
31
- y = 0;
30
+ let x = 0;
31
+ let y = 0;
32
32
  let range = getSelectionRange();
33
33
  if (range) {
34
34
  const rect = range.getBoundingClientRect();
@@ -17,7 +17,8 @@ export const EXTERNAL_EVENT = {
17
17
  NEW_NOTIFICATION: 'new_notification',
18
18
  PARTICIPANT_ADDED: 'participant-added',
19
19
  PARTICIPANT_REMOVED: 'participant-removed',
20
- CREATE_SDOC_FILE: 'create_sdoc_file'
20
+ CREATE_SDOC_FILE: 'create_sdoc_file',
21
+ INSERT_LINK: 'insert_link'
21
22
  };
22
23
  export const TIP_TYPE = {
23
24
  DELETE_NO_CHANGES_REVISION: 'delete_no_changes_revision',
package/dist/context.js CHANGED
@@ -204,6 +204,10 @@ class Context {
204
204
  const docUuid = this.getSetting('docUuid');
205
205
  return this.api.getCopyMoveProgressView(docUuid, taskId);
206
206
  }
207
+ searchSdocFiles(query, page, per_page) {
208
+ const docUuid = this.getSetting('docUuid');
209
+ return this.api.searchSdocFiles(docUuid, query, page, per_page);
210
+ }
207
211
 
208
212
  // participants
209
213
  listParticipants() {
@@ -2,12 +2,38 @@ import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutPr
2
2
  const _excluded = ["children", "className"];
3
3
  import React, { useEffect } from 'react';
4
4
  import classnames from 'classnames';
5
+ import context from '../context';
6
+ import { LocalStorage } from '../utils';
5
7
  const Layout = _ref => {
6
8
  let {
7
9
  children,
8
10
  className
9
11
  } = _ref,
10
12
  restProps = _objectWithoutProperties(_ref, _excluded);
13
+ const cacheHistoryfiles = () => {
14
+ const docUuid = context.getSetting('docUuid');
15
+ const docName = context.getSetting('docName');
16
+ const rencentFiles = LocalStorage.getItem('sdoc-recent-files', []);
17
+ let arr = [];
18
+ const newFile = {
19
+ doc_uuid: docUuid,
20
+ name: docName
21
+ };
22
+ if (rencentFiles.length > 0) {
23
+ const isExist = rencentFiles.find(item => item.doc_uuid === docUuid);
24
+ if (isExist) return;
25
+ if (!isExist) {
26
+ let newRencentFiles = rencentFiles.slice(0);
27
+ if (rencentFiles.length === 10) {
28
+ newRencentFiles.shift();
29
+ }
30
+ arr = [newFile, ...newRencentFiles];
31
+ }
32
+ } else {
33
+ arr.push(newFile);
34
+ }
35
+ LocalStorage.setItem('sdoc-recent-files', arr);
36
+ };
11
37
  useEffect(() => {
12
38
  setTimeout(() => {
13
39
  const url = window.location.href;
@@ -17,6 +43,7 @@ const Layout = _ref => {
17
43
  element && element.scrollIntoView(true);
18
44
  }
19
45
  }, 500);
46
+ cacheHistoryfiles();
20
47
  }, []);
21
48
  return /*#__PURE__*/React.createElement("div", Object.assign({
22
49
  className: classnames('sdoc-editor-page-wrapper', className)
@@ -4,7 +4,9 @@ const {
4
4
  TEXT
5
5
  } = TransferTypes;
6
6
  function getEventTransfer(event) {
7
- let html, text, files;
7
+ let html;
8
+ let text;
9
+ let files;
8
10
  if (window.isMobile) {
9
11
  if (window.dtableTransfer) {
10
12
  text = window.dtableTransfer['TEXT'];
@@ -56,12 +56,12 @@ export const resetWebTitle = t => {
56
56
  };
57
57
  export const getSelectionCoords = () => {
58
58
  let doc = window.document;
59
- let sel = doc.selection,
60
- range,
61
- rects,
62
- rect;
63
- let x = 0,
64
- y = 0;
59
+ let sel = doc.selection;
60
+ let range;
61
+ let rects;
62
+ let rect;
63
+ let x = 0;
64
+ let y = 0;
65
65
  if (sel) {
66
66
  if (sel.type !== 'Control') {
67
67
  range = sel.createRange();
@@ -109,4 +109,11 @@ export const getSelectionCoords = () => {
109
109
  y: y
110
110
  };
111
111
  };
112
+ export const isEnglish = str => {
113
+ const pattern = new RegExp('[A-Za-z]+');
114
+ if (pattern.test(str)) {
115
+ return true;
116
+ }
117
+ return false;
118
+ };
112
119
  export { DateUtils, LocalStorage, getEventTransfer, Hotkey };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "0.4.34",
3
+ "version": "0.4.36",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",