@seafile/sdoc-editor 0.1.46 → 0.1.47

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.
@@ -32,3 +32,7 @@
32
32
  border: 1px solid #e5e6e8;
33
33
  box-shadow: 0 0 15px rgba(0, 0, 0, 0.06);
34
34
  }
35
+
36
+ .sdoc-editor-container .sdoc-editor-content .article > div {
37
+ caret-color: blue;
38
+ }
@@ -0,0 +1,35 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
3
+ import { Text, Path, Range } from '@seafile/slate';
4
+ export var getDecorate = function getDecorate(editor) {
5
+ return function (nodeEntry) {
6
+ var ranges = [];
7
+ var _nodeEntry = _slicedToArray(nodeEntry, 2),
8
+ node = _nodeEntry[0],
9
+ path = _nodeEntry[1];
10
+ var cursors = Object.values(editor.cursors || {});
11
+ if (Text.isText(node) && (cursors === null || cursors === void 0 ? void 0 : cursors.length)) {
12
+ cursors.forEach(function (cursor) {
13
+ if (Range.includes(cursor, path)) {
14
+ var focus = cursor.focus,
15
+ anchor = cursor.anchor,
16
+ isForward = cursor.isForward;
17
+ var isFocusNode = Path.equals(focus.path, path);
18
+ var isAnchorNode = Path.equals(anchor.path, path);
19
+ ranges.push(_objectSpread(_objectSpread({}, cursor), {}, {
20
+ isCaret: isFocusNode,
21
+ anchor: {
22
+ path: path,
23
+ offset: isAnchorNode ? anchor.offset : isForward ? 0 : node.text.length
24
+ },
25
+ focus: {
26
+ path: path,
27
+ offset: isFocusNode ? focus.offset : isForward ? node.text.length : 0
28
+ }
29
+ }));
30
+ }
31
+ });
32
+ }
33
+ return ranges;
34
+ };
35
+ };
@@ -0,0 +1,43 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import { Range } from '@seafile/slate';
3
+ import randomColor from 'randomcolor';
4
+
5
+ // selection: { anchor, focus }
6
+ // cursor: { anchor, focus }
7
+
8
+ export var setCursor = function setCursor(editor, operations, user, selection, cursorData) {
9
+ var clientId = user.username;
10
+ var cursorOps = operations.filter(function (operation) {
11
+ return operation.type === 'set_selection';
12
+ });
13
+ if (!editor.cursors) editor.cursors = {};
14
+ var oldCursor = editor.cursors[clientId] ? editor.cursors[clientId] : {};
15
+ var lastCursorOp = cursorOps[cursorOps.length - 1];
16
+ if (selection) {
17
+ var newCursor = lastCursorOp && lastCursorOp.newProperties || {};
18
+ var newCursorData = _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, oldCursor), newCursor), selection), _objectSpread(_objectSpread({}, cursorData), {}, {
19
+ isForward: Range.isForward(selection)
20
+ }));
21
+ editor.cursors[clientId] = newCursorData;
22
+ } else {
23
+ delete editor.cursors[clientId];
24
+ }
25
+ return editor;
26
+ };
27
+ export var deleteCursor = function deleteCursor(editor, username) {
28
+ delete editor.cursors[username];
29
+ };
30
+ export var generateCursorData = function generateCursorData(config) {
31
+ var user = config.user;
32
+ var options = {
33
+ luminosity: 'dark',
34
+ format: 'rgba',
35
+ alpha: 1
36
+ };
37
+ var color = randomColor(options);
38
+ return {
39
+ name: user.name,
40
+ color: color,
41
+ alphaColor: color.slice(0, -2) + '0.2)'
42
+ };
43
+ };
@@ -13,6 +13,7 @@ import withNodeId from './node-id';
13
13
  import SDocOutline from './outline';
14
14
  import EventProxy from './utils/event-handler';
15
15
  import { focusEditor } from './extension/core';
16
+ import { getDecorate } from './cursor/decorate';
16
17
  import './assets/css/layout.css';
17
18
  import './assets/css/sdoc-editor-plugins.css';
18
19
  var SDocEditor = /*#__PURE__*/function (_React$Component) {
@@ -45,7 +46,9 @@ var SDocEditor = /*#__PURE__*/function (_React$Component) {
45
46
  });
46
47
  }
47
48
  };
48
- var children = props.document.children;
49
+ var _props$document = props.document,
50
+ children = _props$document.children,
51
+ cursors = _props$document.cursors;
49
52
  _this.state = {
50
53
  slateValue: children,
51
54
  isLoading: true
@@ -60,6 +63,7 @@ var SDocEditor = /*#__PURE__*/function (_React$Component) {
60
63
  config: config
61
64
  });
62
65
  }
66
+ _this.editor.cursors = cursors;
63
67
  _this.eventProxy = new EventProxy(_this.editor);
64
68
  return _this;
65
69
  }
@@ -84,6 +88,7 @@ var SDocEditor = /*#__PURE__*/function (_React$Component) {
84
88
  var slateValue = this.state.slateValue;
85
89
  var config = this.props.config;
86
90
  var docUuid = config.docUuid;
91
+ var decorate = getDecorate(this.editor);
87
92
  return /*#__PURE__*/React.createElement("div", {
88
93
  className: "sdoc-editor-container"
89
94
  }, /*#__PURE__*/React.createElement(Toolbar, {
@@ -109,7 +114,8 @@ var SDocEditor = /*#__PURE__*/function (_React$Component) {
109
114
  return _renderLeaf(props, _this2.editor);
110
115
  },
111
116
  onKeyDown: this.eventProxy.onKeyDown,
112
- onDOMBeforeInput: this.onDOMBeforeInput
117
+ onDOMBeforeInput: this.onDOMBeforeInput,
118
+ decorate: decorate
113
119
  }))))));
114
120
  }
115
121
  }]);
@@ -0,0 +1,47 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import React from 'react';
3
+ var cursorStyleBase = {
4
+ position: 'absolute',
5
+ top: -2,
6
+ pointerEvents: 'none',
7
+ userSelect: 'none',
8
+ transform: 'translateY(-100%)',
9
+ fontSize: 10,
10
+ color: 'white',
11
+ background: 'palevioletred',
12
+ whiteSpace: 'nowrap'
13
+ };
14
+ var caretStyleBase = {
15
+ position: 'absolute',
16
+ pointerEvents: 'none',
17
+ userSelect: 'none',
18
+ height: '1.2em',
19
+ width: 2,
20
+ background: 'palevioletred'
21
+ };
22
+ var Caret = function Caret(_ref) {
23
+ var color = _ref.color,
24
+ isForward = _ref.isForward,
25
+ name = _ref.name;
26
+ var cursorStyles = _objectSpread(_objectSpread({}, cursorStyleBase), {}, {
27
+ background: color,
28
+ left: isForward ? '100%' : '0%'
29
+ });
30
+ var caretStyles = _objectSpread(_objectSpread({}, caretStyleBase), {}, {
31
+ background: color,
32
+ left: isForward ? '100%' : '0%'
33
+ });
34
+ caretStyles[isForward ? 'bottom' : 'top'] = 0;
35
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
36
+ contentEditable: false,
37
+ style: caretStyles
38
+ }, /*#__PURE__*/React.createElement("span", {
39
+ style: {
40
+ position: 'relative'
41
+ }
42
+ }, /*#__PURE__*/React.createElement("span", {
43
+ contentEditable: false,
44
+ style: cursorStyles
45
+ }, name))));
46
+ };
47
+ export default Caret;
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import Caret from './caret';
2
3
  var renderText = function renderText(props, editor) {
3
4
  var attributes = props.attributes,
4
5
  children = props.children,
@@ -40,6 +41,10 @@ var renderText = function renderText(props, editor) {
40
41
  }
41
42
  return /*#__PURE__*/React.createElement("span", Object.assign({
42
43
  "data-id": leaf.id
43
- }, attributes), markedChildren);
44
+ }, attributes, {
45
+ style: {
46
+ position: 'relative'
47
+ }
48
+ }), leaf.isCaret ? /*#__PURE__*/React.createElement(Caret, leaf) : null, markedChildren);
44
49
  };
45
50
  export default renderText;
@@ -3,6 +3,8 @@ import deepCopy from 'deep-copy';
3
3
  import { Editor, Operation } from '@seafile/slate';
4
4
  import { getNode } from '../extension/core';
5
5
  import * as OPERATION from '../node-id/constants';
6
+ import { setCursor } from '../cursor/helper';
7
+ import context from '../../context';
6
8
  export var getNodePathById = function getNodePathById(rootNode, nodeId) {
7
9
  var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
8
10
  if (rootNode.id === nodeId) return path;
@@ -250,11 +252,21 @@ export var reExecRevertOperationList = function reExecRevertOperationList(editor
250
252
  _loop2();
251
253
  }
252
254
  };
253
- export var syncRemoteOperations = function syncRemoteOperations(editor, remoteOperations) {
255
+ export var syncRemoteOperations = function syncRemoteOperations(editor, remoteOperations, user, selection, cursorData) {
254
256
  if (remoteOperations.length === 0) return;
255
257
  Editor.withoutNormalizing(editor, function () {
256
- remoteOperations.forEach(function (item) {
257
- editor.apply(item);
258
- });
258
+ for (var i = 0; i < remoteOperations.length; i++) {
259
+ var op = remoteOperations[i];
260
+ if (op.type === 'set_selection') {
261
+ continue;
262
+ }
263
+ editor.apply(op);
264
+ }
265
+ var username = context.getSetting('username');
266
+ if (user && user.username !== username) {
267
+ setCursor(editor, remoteOperations, user, selection, cursorData);
268
+ // sync cursor position
269
+ editor.onChange();
270
+ }
259
271
  });
260
272
  };
@@ -11,10 +11,12 @@ var SocketClient = /*#__PURE__*/_createClass(function SocketClient(config) {
11
11
  var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
12
12
  var _this$config = _this.config,
13
13
  docUuid = _this$config.docUuid,
14
- user = _this$config.user;
14
+ user = _this$config.user,
15
+ cursorData = _this$config.cursorData;
15
16
  return _objectSpread({
16
17
  doc_uuid: docUuid,
17
- user: user
18
+ user: user,
19
+ cursor_data: cursorData
18
20
  }, params);
19
21
  };
20
22
  this.onConnected = function () {
@@ -76,13 +78,14 @@ var SocketClient = /*#__PURE__*/_createClass(function SocketClient(config) {
76
78
  var socketManager = SocketManager.getInstance();
77
79
  socketManager.dispatchConnectState('leave-room', username);
78
80
  };
79
- this.sendOperations = function (operations, version, callback) {
81
+ this.sendOperations = function (operations, version, selection, callback) {
80
82
  debug('=========== send operations ==========');
81
83
  debug('%O', operations);
82
84
  debug('======================================');
83
85
  _this.socket.emit('update-document', _this.getParams({
84
86
  operations: operations,
85
- version: version
87
+ version: version,
88
+ selection: selection
86
89
  }), function (result) {
87
90
  callback && callback(result);
88
91
  });
@@ -4,6 +4,7 @@ import EventBus from '../utils/event-bus';
4
4
  import { syncRemoteOperations, reExecRevertOperationList, revertOperationList } from './helpers';
5
5
  import SocketClient from './socket-client';
6
6
  import debug from '../utils/debug';
7
+ import { deleteCursor } from '../cursor/helper';
7
8
  var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, document, config) {
8
9
  var _this = this;
9
10
  _classCallCheck(this, SocketManager);
@@ -28,7 +29,8 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
28
29
  _this.dispatchConnectState('is-saving');
29
30
  var version = _this.document.version;
30
31
  var operations = _this.pendingOperationList.shift();
31
- _this.socketClient.sendOperations(operations, version, _this.sendOperationsCallback);
32
+ var selection = _this.editor.selection;
33
+ _this.socketClient.sendOperations(operations, version, selection, _this.sendOperationsCallback);
32
34
  };
33
35
  this.sendOperationsCallback = function (result) {
34
36
  if (result && result.success) {
@@ -83,10 +85,13 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
83
85
  }
84
86
 
85
87
  // 2. execute operations
86
- var operations = params.operations;
88
+ var operations = params.operations,
89
+ user = params.user,
90
+ selection = params.selection,
91
+ cursorData = params.cursor_data;
87
92
  // 2.1 Update content & version
88
93
  debug('execute remote operations: %O', operations);
89
- syncRemoteOperations(_this.editor, operations);
94
+ syncRemoteOperations(_this.editor, operations, user, selection, cursorData);
90
95
 
91
96
  // 2.2 Update document
92
97
  _this.document.version = serverVersion;
@@ -157,6 +162,10 @@ var SocketManager = /*#__PURE__*/_createClass(function SocketManager(editor, doc
157
162
  });
158
163
  };
159
164
  this.dispatchConnectState = function (type, message) {
165
+ if (type === 'leave-room') {
166
+ deleteCursor(_this.editor, message);
167
+ _this.editor.onChange();
168
+ }
160
169
  _this.eventBus.dispatch(type, message);
161
170
  };
162
171
  this.closeSocketConnect = function () {
@@ -1,12 +1,15 @@
1
1
  var _this = this;
2
+ import { generateCursorData } from '../cursor/helper';
2
3
  import SocketManager from './socket-manager';
3
4
  var withSocketIO = function withSocketIO(editor, options) {
4
5
  var onChange = editor.onChange;
5
6
  var newEditor = editor;
7
+ var cursorData = generateCursorData(options.config);
6
8
  var socketManager = null;
7
9
  newEditor.openConnection = function () {
8
10
  var document = options.document,
9
11
  config = options.config;
12
+ config['cursorData'] = cursorData;
10
13
  socketManager = SocketManager.getInstance(newEditor, document, config);
11
14
  };
12
15
  newEditor.closeConnection = function () {
@@ -14,9 +17,7 @@ var withSocketIO = function withSocketIO(editor, options) {
14
17
  };
15
18
  newEditor.onChange = function () {
16
19
  var operations = newEditor.operations;
17
- operations = operations.filter(function (item) {
18
- return item.type !== 'set_selection';
19
- });
20
+ // operations = operations.filter(item => item.type !== 'set_selection');
20
21
  if (!newEditor.isRemote && operations.length > 0) {
21
22
  var _socketManager = SocketManager.getInstance();
22
23
  _socketManager.addOperations && _socketManager.addOperations(operations);
@@ -68,7 +68,8 @@ var SimpleEditor = /*#__PURE__*/function (_React$Component) {
68
68
  if (result && !result.children) {
69
69
  result = {
70
70
  version: 0,
71
- children: result.content
71
+ children: result.content,
72
+ cursors: result.cursors || {}
72
73
  };
73
74
  }
74
75
  this.setState({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "0.1.46",
3
+ "version": "0.1.47",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",
@@ -14,6 +14,7 @@
14
14
  "deep-copy": "1.4.2",
15
15
  "is-hotkey": "0.2.0",
16
16
  "is-url": "^1.2.4",
17
+ "randomcolor": "0.6.2",
17
18
  "react-cookies": "0.1.1",
18
19
  "reactstrap": "8.9.0",
19
20
  "slugid": "3.2.0",