@seafile/sdoc-editor 0.4.34 → 0.4.35

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.
@@ -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) {
@@ -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,13 @@ 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';
7
11
  import { insertSdocFileLink } from '../../plugins/sdoc-link/helpers';
8
- import { LocalStorage } from '../../../../utils';
12
+ import { LocalStorage, isEnglish } from '../../../../utils';
9
13
  import { INTERNAL_EVENT } from '../../../constants';
10
14
  import { EXTERNAL_EVENT } from '../../../../constants';
11
15
  import { SDOC_LINK } from '../../constants';
@@ -54,6 +58,13 @@ const HistoryFiles = _ref => {
54
58
  const files = LocalStorage.getItem('sdoc-recent-files') || [];
55
59
  setFiles(files);
56
60
  }, []);
61
+ const onInsertLink = useCallback(params => {
62
+ const {
63
+ insertSdocFileLinkCallback
64
+ } = insertLinkCallback;
65
+ insertSdocFileLinkCallback(editor, params.data.obj_name, params.data.doc_uuid);
66
+ closeDialog();
67
+ }, [closeDialog, editor, insertLinkCallback]);
57
68
  useEffect(() => {
58
69
  getPosition();
59
70
  getHistoryFiles();
@@ -62,41 +73,73 @@ const HistoryFiles = _ref => {
62
73
  }, 0);
63
74
  const sdocScrollContainer = document.getElementById('sdoc-scroll-container');
64
75
  sdocScrollContainer.addEventListener('scroll', onScroll);
76
+ const eventBus = EventBus.getInstance();
77
+ const unsubscribeInsertLink = eventBus.subscribe(EXTERNAL_EVENT.INSERT_LINK, onInsertLink);
65
78
  document.addEventListener('click', onClick);
66
79
  return () => {
67
80
  sdocScrollContainer.removeEventListener('scroll', onScroll);
81
+ unsubscribeInsertLink();
68
82
  document.removeEventListener('click', onClick);
69
83
  };
70
84
  // eslint-disable-next-line react-hooks/exhaustive-deps
71
85
  }, []);
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);
86
+ const onCompositionStart = e => {
87
+ e.stopPropagation();
88
+ historyFilesInputRef.current.typing = true;
89
+ };
90
+ const onCompositionEnd = e => {
91
+ e.stopPropagation();
92
+ historyFilesInputRef.current.typing = false;
93
+ onSearch(e);
94
+ };
95
+ const onSearch = useCallback(async e => {
96
+ // Ignore when entering Chinese
97
+ if (historyFilesInputRef.current.typing) return;
98
+
99
+ // Show history files when search is empty
100
+ if (e.target.value.trim().length === 0) {
101
+ setHeader(t('Recent_previews'));
102
+ setNewFileName('');
103
+ getHistoryFiles();
84
104
  return;
85
105
  }
86
- setHeader(t('Recent_previews'));
87
- setNewFileName('');
88
- setFiles(newFiles);
106
+
107
+ // Cannot be found if the search is less than three characters.
108
+ if (isEnglish(e.target.value.trim()) && e.target.value.length < 3) {
109
+ setFiles([]);
110
+ setHeader(t('The_document_does_not_exist'));
111
+ setNewFileName(e.target.value);
112
+ return;
113
+ }
114
+ try {
115
+ var _res$data;
116
+ const res = await context.searchSdocFiles(e.target.value, 1, 10);
117
+ if (res === null || res === void 0 ? void 0 : (_res$data = res.data) === null || _res$data === void 0 ? void 0 : _res$data.results) {
118
+ let newFiles = res.data.results;
119
+ if (newFiles.length === 0) {
120
+ setHeader(t('The_document_does_not_exist'));
121
+ setNewFileName(e.target.value);
122
+ } else {
123
+ setHeader('');
124
+ setNewFileName('');
125
+ }
126
+ setFiles(newFiles);
127
+ return;
128
+ }
129
+ } catch (error) {
130
+ toaster.danger(error.message);
131
+ }
89
132
  // eslint-disable-next-line react-hooks/exhaustive-deps
90
133
  }, []);
91
134
  const onSelect = useCallback(fileInfo => {
92
135
  const {
93
- file_uuid,
94
- file_name
136
+ doc_uuid,
137
+ name
95
138
  } = fileInfo;
96
139
  const {
97
140
  insertSdocFileLinkCallback
98
141
  } = insertLinkCallback;
99
- insertSdocFileLinkCallback(editor, file_name, file_uuid);
142
+ insertSdocFileLinkCallback(editor, name, doc_uuid);
100
143
  closeDialog();
101
144
  // eslint-disable-next-line react-hooks/exhaustive-deps
102
145
  }, []);
@@ -112,8 +155,7 @@ const HistoryFiles = _ref => {
112
155
  eventBus.dispatch(EXTERNAL_EVENT.CREATE_SDOC_FILE, {
113
156
  newFileName: newFileName.trim()
114
157
  });
115
- closeDialog();
116
- }, [closeDialog, newFileName]);
158
+ }, [newFileName]);
117
159
  return /*#__PURE__*/React.createElement("div", {
118
160
  ref: historyFilesRef,
119
161
  className: "sdoc-history-files-wrapper popover",
@@ -123,26 +165,27 @@ const HistoryFiles = _ref => {
123
165
  className: "sdoc-history-files-search-input",
124
166
  ref: historyFilesInputRef,
125
167
  autoComplete: "off",
126
- onChange: onSearch,
127
- onCompositionStart: e => {
128
- e.stopPropagation();
129
- }
168
+ onChange: debounce(onSearch, 200),
169
+ onCompositionStart: onCompositionStart,
170
+ onCompositionEnd: onCompositionEnd
130
171
  }), /*#__PURE__*/React.createElement("div", {
131
172
  className: "sdoc-history-files-content"
132
- }, /*#__PURE__*/React.createElement("div", {
173
+ }, header.length !== 0 && /*#__PURE__*/React.createElement("div", {
133
174
  className: "sdoc-history-files-header"
134
175
  }, header), /*#__PURE__*/React.createElement("div", {
135
- className: "sdoc-history-files"
176
+ className: classNames('sdoc-history-files', {
177
+ 'no-header': header.length === 0
178
+ })
136
179
  }, files.map(item => {
137
180
  return /*#__PURE__*/React.createElement("div", {
138
- key: item.file_uuid,
181
+ key: item.doc_uuid,
139
182
  className: "sdoc-history-files-item",
140
183
  onClick: () => {
141
184
  onSelect(item);
142
185
  }
143
186
  }, /*#__PURE__*/React.createElement("i", {
144
187
  className: "sdocfont sdoc-document"
145
- }), /*#__PURE__*/React.createElement("span", null, item.file_name));
188
+ }), /*#__PURE__*/React.createElement("span", null, item.name));
146
189
  }), /*#__PURE__*/React.createElement("div", {
147
190
  className: "sdoc-history-files-item",
148
191
  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;
@@ -125,7 +125,8 @@ export const changeToPlainText = editor => {
125
125
  };
126
126
  export const setClipboardCodeBlockData = value => {
127
127
  // Insert text into the clipboard for use on other pages
128
- const text = value.children.map(line => Node.string(line)).join('\n');
128
+ // Empty string cannot apply in `copy`
129
+ const text = value.children.map(line => Node.string(line)).join('\n') || ' ';
129
130
  copy(text, {
130
131
  format: 'text/plain',
131
132
  onCopy: data => {
@@ -1512,4 +1512,25 @@ export const getHighlightClass = (editor, cellPath) => {
1512
1512
  const rowIndex = cellPath[cellPath.length - 2];
1513
1513
  const className = getCellHighlightClassName(alternate_highlight_color, rowIndex);
1514
1514
  return className;
1515
+ };
1516
+
1517
+ // Correct the selected range when combined cell are selected
1518
+ export const adjustCombinedCellRange = (table, range) => {
1519
+ const {
1520
+ minRowIndex,
1521
+ maxRowIndex,
1522
+ minColIndex,
1523
+ maxColIndex
1524
+ } = range;
1525
+ const firstCell = table.children[minRowIndex].children[minColIndex];
1526
+ const {
1527
+ colspan = 0,
1528
+ rowspan = 0
1529
+ } = firstCell;
1530
+ if (rowspan > 1 || colspan > 1) {
1531
+ const isRowCombined = minRowIndex + rowspan === maxRowIndex + 1;
1532
+ const isColCombined = minColIndex + colspan === maxColIndex + 1;
1533
+ if (isRowCombined && isColCombined) return EMPTY_SELECTED_RANGE;
1534
+ }
1535
+ return range;
1515
1536
  };
@@ -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, () => {
@@ -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;
@@ -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)
@@ -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.35",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",