@atlaskit/editor-plugin-collab-edit 3.9.5 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @atlaskit/editor-plugin-collab-edit
2
2
 
3
+ ## 3.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#171561](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/171561)
8
+ [`d0c41c462c511`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d0c41c462c511) -
9
+ [ux] Telepointers now stay expanded even if there are other transactions received which will
10
+ re-create their DOM elements.
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+
3
16
  ## 3.9.5
4
17
 
5
18
  ### Patch Changes
@@ -33,6 +33,7 @@ var PluginState = exports.PluginState = /*#__PURE__*/function () {
33
33
  function PluginState(decorations, participants, sessionId) {
34
34
  var collabInitalised = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
35
35
  var onError = arguments.length > 4 ? arguments[4] : undefined;
36
+ var nudgeAnimations = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : new Map();
36
37
  (0, _classCallCheck2.default)(this, PluginState);
37
38
  // eslint-disable-next-line no-console
38
39
  (0, _defineProperty2.default)(this, "onError", function (error) {
@@ -43,6 +44,7 @@ var PluginState = exports.PluginState = /*#__PURE__*/function () {
43
44
  this.sid = sessionId;
44
45
  this.isReady = collabInitalised;
45
46
  this.onError = onError || this.onError;
47
+ this.nudgeAnimations = nudgeAnimations;
46
48
  }
47
49
  return (0, _createClass2.default)(PluginState, [{
48
50
  key: "decorations",
@@ -158,7 +160,7 @@ var PluginState = exports.PluginState = /*#__PURE__*/function () {
158
160
  } catch (err) {
159
161
  this.onError(err);
160
162
  }
161
- add = add.concat((0, _utils.createTelepointers)(from, to, sessionId, isSelection, this.getInitial(sessionId), this.getPresenceId(sessionId), this.getFullName(sessionId)));
163
+ add = add.concat((0, _utils.createTelepointers)(from, to, sessionId, isSelection, this.getInitial(sessionId), this.getPresenceId(sessionId), this.getFullName(sessionId), (0, _platformFeatureFlags.fg)('confluence_team_presence_scroll_to_pointer') ? (0, _utils.hasExistingNudge)(sessionId, this.nudgeAnimations) : false));
162
164
  }
163
165
  }
164
166
  if (tr.docChanged) {
@@ -177,7 +179,7 @@ var PluginState = exports.PluginState = /*#__PURE__*/function () {
177
179
  size = _ref.slice.content.size,
178
180
  _from = _ref.from;
179
181
  var pos = getValidPos(tr, size ? Math.min(_from + size, tr.doc.nodeSize - 3) : Math.max(_from, 1));
180
- add = add.concat((0, _utils.createTelepointers)(pos, pos, _sessionId, false, _this.getInitial(_sessionId), presenceId, _this.getFullName(_sessionId)));
182
+ add = add.concat((0, _utils.createTelepointers)(pos, pos, _sessionId, false, _this.getInitial(_sessionId), presenceId, _this.getFullName(_sessionId), (0, _platformFeatureFlags.fg)('confluence_team_presence_scroll_to_pointer') ? (0, _utils.hasExistingNudge)(_sessionId, _this.nudgeAnimations) : false));
181
183
  }
182
184
  }
183
185
  }
@@ -239,9 +241,11 @@ var PluginState = exports.PluginState = /*#__PURE__*/function () {
239
241
  var _deco$spec, _deco$spec2;
240
242
  if (deco.type.toDOM && participants.get(nudgeSessionId) && ((_deco$spec = deco.spec) === null || _deco$spec === void 0 || (_deco$spec = _deco$spec.pointer) === null || _deco$spec === void 0 ? void 0 : _deco$spec.sessionId) === nudgeSessionId && ((_deco$spec2 = deco.spec) === null || _deco$spec2 === void 0 ? void 0 : _deco$spec2.key) === "telepointer-".concat(nudgeSessionId)) {
241
243
  // Restart animation by removing and re-adding the class
244
+ deco.type.toDOM.classList.remove(_collab.TELEPOINTER_PULSE_DURING_TR_CLASS);
242
245
  deco.type.toDOM.classList.remove(_collab.TELEPOINTER_PULSE_CLASS);
243
246
  void deco.type.toDOM.offsetWidth; // Force reflow
244
247
  deco.type.toDOM.classList.add(_collab.TELEPOINTER_PULSE_CLASS);
248
+ _this.nudgeAnimations.set(nudgeSessionId, Date.now());
245
249
  }
246
250
  });
247
251
  }
@@ -263,6 +267,10 @@ var PluginState = exports.PluginState = /*#__PURE__*/function () {
263
267
  }
264
268
  }
265
269
  }
270
+ if ((0, _platformFeatureFlags.fg)('confluence_team_presence_scroll_to_pointer')) {
271
+ var _nextState = new PluginState(this.decorationSet, participants, sid, collabInitialised, this.onError, this.nudgeAnimations);
272
+ return PluginState.eq(_nextState, this) ? this : _nextState;
273
+ }
266
274
  var nextState = new PluginState(this.decorationSet, participants, sid, collabInitialised);
267
275
  return PluginState.eq(nextState, this) ? this : nextState;
268
276
  }
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.findPointers = exports.createTelepointers = exports._findPointers = void 0;
8
8
  exports.getAvatarColor = getAvatarColor;
9
- exports.scrollToCollabCursor = exports.replaceDocument = exports.originalTransactionHasMeta = exports.isReplaceStep = exports.isOrganicChange = exports.getPositionOfTelepointer = void 0;
9
+ exports.scrollToCollabCursor = exports.replaceDocument = exports.originalTransactionHasMeta = exports.isReplaceStep = exports.isOrganicChange = exports.hasExistingNudge = exports.getPositionOfTelepointer = void 0;
10
10
  var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
11
11
  var _steps = require("@atlaskit/adf-schema/steps");
12
12
  var _analytics = require("@atlaskit/editor-common/analytics");
@@ -46,7 +46,7 @@ function getAvatarColor(str) {
46
46
  textColor: participantColor.color.textColor
47
47
  };
48
48
  }
49
- var createTelepointers = exports.createTelepointers = function createTelepointers(from, to, sessionId, isSelection, initial, presenceId, fullName) {
49
+ var createTelepointers = exports.createTelepointers = function createTelepointers(from, to, sessionId, isSelection, initial, presenceId, fullName, isNudged) {
50
50
  var decorations = [];
51
51
  var avatarColor = getAvatarColor(presenceId);
52
52
  var color = avatarColor.index.toString();
@@ -78,6 +78,12 @@ var createTelepointers = exports.createTelepointers = function createTelepointer
78
78
  cursor.setAttribute('data-initial', initial);
79
79
  if ((0, _platformFeatureFlags.fg)('confluence_team_presence_scroll_to_pointer')) {
80
80
  cursor.setAttribute(_collab.TELEPOINTER_DATA_SESSION_ID_ATTR, sessionId);
81
+ // If there is an ongoing expand animation, we'll keep the telepointer expanded
82
+ // until the keyframe animation is complete. Please note that this will restart the anim timer
83
+ // from 0 everytime it's re-added.
84
+ if (isNudged) {
85
+ cursor.classList.add(_collab.TELEPOINTER_PULSE_DURING_TR_CLASS);
86
+ }
81
87
  var fullNameEl = document.createElement('span');
82
88
  fullNameEl.textContent = fullName;
83
89
  fullNameEl.className = 'telepointer-fullname';
@@ -239,4 +245,18 @@ var isOrganicChange = exports.isOrganicChange = function isOrganicChange(tr) {
239
245
  }
240
246
  }) && !tr.doc.eq(tr.before);
241
247
  });
248
+ };
249
+
250
+ // If we receive a transaction while there is an ongoing CSS expand animation in the telepointer,
251
+ // it will be cut off due to the removal of the element. We'll persist the animation state in the plugin,
252
+ // so we can keep the expanded version showing even when the telepointer element is recreated.
253
+
254
+ var hasExistingNudge = exports.hasExistingNudge = function hasExistingNudge(sessionId, nudgeAnimations) {
255
+ var nudgeAnimStartTime = nudgeAnimations.get(sessionId);
256
+ var hasExistingNudge = false;
257
+ if (nudgeAnimStartTime) {
258
+ var timeElapsed = Date.now() - nudgeAnimStartTime;
259
+ hasExistingNudge = timeElapsed < _collab.TELEPOINTER_PULSE_DURING_TR_DURATION_MS;
260
+ }
261
+ return hasExistingNudge;
242
262
  };
@@ -1,11 +1,11 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { browser } from '@atlaskit/editor-common/browser';
3
- import { TELEPOINTER_DIM_CLASS, TELEPOINTER_PULSE_CLASS } from '@atlaskit/editor-common/collab';
3
+ import { TELEPOINTER_DIM_CLASS, TELEPOINTER_PULSE_CLASS, TELEPOINTER_PULSE_DURING_TR_CLASS } from '@atlaskit/editor-common/collab';
4
4
  import { Selection } from '@atlaskit/editor-prosemirror/state';
5
5
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
6
6
  import { fg } from '@atlaskit/platform-feature-flags';
7
7
  import { Participants } from '../participants';
8
- import { createTelepointers, _findPointers, findPointers, getPositionOfTelepointer, isReplaceStep } from '../utils';
8
+ import { createTelepointers, _findPointers, findPointers, getPositionOfTelepointer, isReplaceStep, hasExistingNudge } from '../utils';
9
9
 
10
10
  /**
11
11
  * Returns position where it's possible to place a decoration.
@@ -31,7 +31,7 @@ export class PluginState {
31
31
  get sessionId() {
32
32
  return this.sid;
33
33
  }
34
- constructor(decorations, participants, sessionId, collabInitalised = false, onError) {
34
+ constructor(decorations, participants, sessionId, collabInitalised = false, onError, nudgeAnimations = new Map()) {
35
35
  // eslint-disable-next-line no-console
36
36
  _defineProperty(this, "onError", error => console.error(error));
37
37
  this.decorationSet = decorations;
@@ -39,6 +39,7 @@ export class PluginState {
39
39
  this.sid = sessionId;
40
40
  this.isReady = collabInitalised;
41
41
  this.onError = onError || this.onError;
42
+ this.nudgeAnimations = nudgeAnimations;
42
43
  }
43
44
  getFullName(sessionId) {
44
45
  const participant = this.participants.get(sessionId);
@@ -132,7 +133,7 @@ export class PluginState {
132
133
  } catch (err) {
133
134
  this.onError(err);
134
135
  }
135
- add = add.concat(createTelepointers(from, to, sessionId, isSelection, this.getInitial(sessionId), this.getPresenceId(sessionId), this.getFullName(sessionId)));
136
+ add = add.concat(createTelepointers(from, to, sessionId, isSelection, this.getInitial(sessionId), this.getPresenceId(sessionId), this.getFullName(sessionId), fg('confluence_team_presence_scroll_to_pointer') ? hasExistingNudge(sessionId, this.nudgeAnimations) : false));
136
137
  }
137
138
  }
138
139
  if (tr.docChanged) {
@@ -159,7 +160,7 @@ export class PluginState {
159
160
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
160
161
  } = step;
161
162
  const pos = getValidPos(tr, size ? Math.min(from + size, tr.doc.nodeSize - 3) : Math.max(from, 1));
162
- add = add.concat(createTelepointers(pos, pos, sessionId, false, this.getInitial(sessionId), presenceId, this.getFullName(sessionId)));
163
+ add = add.concat(createTelepointers(pos, pos, sessionId, false, this.getInitial(sessionId), presenceId, this.getFullName(sessionId), fg('confluence_team_presence_scroll_to_pointer') ? hasExistingNudge(sessionId, this.nudgeAnimations) : false));
163
164
  }
164
165
  }
165
166
  }
@@ -224,9 +225,11 @@ export class PluginState {
224
225
  var _deco$spec, _deco$spec$pointer, _deco$spec2;
225
226
  if (deco.type.toDOM && participants.get(nudgeSessionId) && ((_deco$spec = deco.spec) === null || _deco$spec === void 0 ? void 0 : (_deco$spec$pointer = _deco$spec.pointer) === null || _deco$spec$pointer === void 0 ? void 0 : _deco$spec$pointer.sessionId) === nudgeSessionId && ((_deco$spec2 = deco.spec) === null || _deco$spec2 === void 0 ? void 0 : _deco$spec2.key) === `telepointer-${nudgeSessionId}`) {
226
227
  // Restart animation by removing and re-adding the class
228
+ deco.type.toDOM.classList.remove(TELEPOINTER_PULSE_DURING_TR_CLASS);
227
229
  deco.type.toDOM.classList.remove(TELEPOINTER_PULSE_CLASS);
228
230
  void deco.type.toDOM.offsetWidth; // Force reflow
229
231
  deco.type.toDOM.classList.add(TELEPOINTER_PULSE_CLASS);
232
+ this.nudgeAnimations.set(nudgeSessionId, Date.now());
230
233
  }
231
234
  });
232
235
  }
@@ -250,6 +253,10 @@ export class PluginState {
250
253
  }
251
254
  }
252
255
  }
256
+ if (fg('confluence_team_presence_scroll_to_pointer')) {
257
+ const nextState = new PluginState(this.decorationSet, participants, sid, collabInitialised, this.onError, this.nudgeAnimations);
258
+ return PluginState.eq(nextState, this) ? this : nextState;
259
+ }
253
260
  const nextState = new PluginState(this.decorationSet, participants, sid, collabInitialised);
254
261
  return PluginState.eq(nextState, this) ? this : nextState;
255
262
  }
@@ -1,6 +1,6 @@
1
1
  import { AnalyticsStep, SetAttrsStep } from '@atlaskit/adf-schema/steps';
2
2
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
3
- import { TELEPOINTER_DATA_SESSION_ID_ATTR } from '@atlaskit/editor-common/collab';
3
+ import { TELEPOINTER_DATA_SESSION_ID_ATTR, TELEPOINTER_PULSE_DURING_TR_CLASS, TELEPOINTER_PULSE_DURING_TR_DURATION_MS } from '@atlaskit/editor-common/collab';
4
4
  import { processRawValueWithoutValidation } from '@atlaskit/editor-common/process-raw-value';
5
5
  import { ZERO_WIDTH_JOINER } from '@atlaskit/editor-common/whitespace';
6
6
  import { Transaction, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
@@ -28,7 +28,7 @@ export function getAvatarColor(str) {
28
28
  textColor: participantColor.color.textColor
29
29
  };
30
30
  }
31
- export const createTelepointers = (from, to, sessionId, isSelection, initial, presenceId, fullName) => {
31
+ export const createTelepointers = (from, to, sessionId, isSelection, initial, presenceId, fullName, isNudged) => {
32
32
  const decorations = [];
33
33
  const avatarColor = getAvatarColor(presenceId);
34
34
  const color = avatarColor.index.toString();
@@ -60,6 +60,12 @@ export const createTelepointers = (from, to, sessionId, isSelection, initial, pr
60
60
  cursor.setAttribute('data-initial', initial);
61
61
  if (fg('confluence_team_presence_scroll_to_pointer')) {
62
62
  cursor.setAttribute(TELEPOINTER_DATA_SESSION_ID_ATTR, sessionId);
63
+ // If there is an ongoing expand animation, we'll keep the telepointer expanded
64
+ // until the keyframe animation is complete. Please note that this will restart the anim timer
65
+ // from 0 everytime it's re-added.
66
+ if (isNudged) {
67
+ cursor.classList.add(TELEPOINTER_PULSE_DURING_TR_CLASS);
68
+ }
63
69
  const fullNameEl = document.createElement('span');
64
70
  fullNameEl.textContent = fullName;
65
71
  fullNameEl.className = 'telepointer-fullname';
@@ -223,4 +229,18 @@ export const isOrganicChange = tr => {
223
229
  }
224
230
  }) && !tr.doc.eq(tr.before);
225
231
  });
232
+ };
233
+
234
+ // If we receive a transaction while there is an ongoing CSS expand animation in the telepointer,
235
+ // it will be cut off due to the removal of the element. We'll persist the animation state in the plugin,
236
+ // so we can keep the expanded version showing even when the telepointer element is recreated.
237
+
238
+ export const hasExistingNudge = (sessionId, nudgeAnimations) => {
239
+ const nudgeAnimStartTime = nudgeAnimations.get(sessionId);
240
+ let hasExistingNudge = false;
241
+ if (nudgeAnimStartTime) {
242
+ const timeElapsed = Date.now() - nudgeAnimStartTime;
243
+ hasExistingNudge = timeElapsed < TELEPOINTER_PULSE_DURING_TR_DURATION_MS;
244
+ }
245
+ return hasExistingNudge;
226
246
  };
@@ -2,12 +2,12 @@ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
2
  import _createClass from "@babel/runtime/helpers/createClass";
3
3
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
4
  import { browser } from '@atlaskit/editor-common/browser';
5
- import { TELEPOINTER_DIM_CLASS, TELEPOINTER_PULSE_CLASS } from '@atlaskit/editor-common/collab';
5
+ import { TELEPOINTER_DIM_CLASS, TELEPOINTER_PULSE_CLASS, TELEPOINTER_PULSE_DURING_TR_CLASS } from '@atlaskit/editor-common/collab';
6
6
  import { Selection } from '@atlaskit/editor-prosemirror/state';
7
7
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
8
8
  import { fg } from '@atlaskit/platform-feature-flags';
9
9
  import { Participants } from '../participants';
10
- import { createTelepointers, _findPointers, findPointers, getPositionOfTelepointer, isReplaceStep } from '../utils';
10
+ import { createTelepointers, _findPointers, findPointers, getPositionOfTelepointer, isReplaceStep, hasExistingNudge } from '../utils';
11
11
 
12
12
  /**
13
13
  * Returns position where it's possible to place a decoration.
@@ -27,6 +27,7 @@ export var PluginState = /*#__PURE__*/function () {
27
27
  function PluginState(decorations, participants, sessionId) {
28
28
  var collabInitalised = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
29
29
  var onError = arguments.length > 4 ? arguments[4] : undefined;
30
+ var nudgeAnimations = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : new Map();
30
31
  _classCallCheck(this, PluginState);
31
32
  // eslint-disable-next-line no-console
32
33
  _defineProperty(this, "onError", function (error) {
@@ -37,6 +38,7 @@ export var PluginState = /*#__PURE__*/function () {
37
38
  this.sid = sessionId;
38
39
  this.isReady = collabInitalised;
39
40
  this.onError = onError || this.onError;
41
+ this.nudgeAnimations = nudgeAnimations;
40
42
  }
41
43
  return _createClass(PluginState, [{
42
44
  key: "decorations",
@@ -152,7 +154,7 @@ export var PluginState = /*#__PURE__*/function () {
152
154
  } catch (err) {
153
155
  this.onError(err);
154
156
  }
155
- add = add.concat(createTelepointers(from, to, sessionId, isSelection, this.getInitial(sessionId), this.getPresenceId(sessionId), this.getFullName(sessionId)));
157
+ add = add.concat(createTelepointers(from, to, sessionId, isSelection, this.getInitial(sessionId), this.getPresenceId(sessionId), this.getFullName(sessionId), fg('confluence_team_presence_scroll_to_pointer') ? hasExistingNudge(sessionId, this.nudgeAnimations) : false));
156
158
  }
157
159
  }
158
160
  if (tr.docChanged) {
@@ -171,7 +173,7 @@ export var PluginState = /*#__PURE__*/function () {
171
173
  size = _ref.slice.content.size,
172
174
  _from = _ref.from;
173
175
  var pos = getValidPos(tr, size ? Math.min(_from + size, tr.doc.nodeSize - 3) : Math.max(_from, 1));
174
- add = add.concat(createTelepointers(pos, pos, _sessionId, false, _this.getInitial(_sessionId), presenceId, _this.getFullName(_sessionId)));
176
+ add = add.concat(createTelepointers(pos, pos, _sessionId, false, _this.getInitial(_sessionId), presenceId, _this.getFullName(_sessionId), fg('confluence_team_presence_scroll_to_pointer') ? hasExistingNudge(_sessionId, _this.nudgeAnimations) : false));
175
177
  }
176
178
  }
177
179
  }
@@ -233,9 +235,11 @@ export var PluginState = /*#__PURE__*/function () {
233
235
  var _deco$spec, _deco$spec2;
234
236
  if (deco.type.toDOM && participants.get(nudgeSessionId) && ((_deco$spec = deco.spec) === null || _deco$spec === void 0 || (_deco$spec = _deco$spec.pointer) === null || _deco$spec === void 0 ? void 0 : _deco$spec.sessionId) === nudgeSessionId && ((_deco$spec2 = deco.spec) === null || _deco$spec2 === void 0 ? void 0 : _deco$spec2.key) === "telepointer-".concat(nudgeSessionId)) {
235
237
  // Restart animation by removing and re-adding the class
238
+ deco.type.toDOM.classList.remove(TELEPOINTER_PULSE_DURING_TR_CLASS);
236
239
  deco.type.toDOM.classList.remove(TELEPOINTER_PULSE_CLASS);
237
240
  void deco.type.toDOM.offsetWidth; // Force reflow
238
241
  deco.type.toDOM.classList.add(TELEPOINTER_PULSE_CLASS);
242
+ _this.nudgeAnimations.set(nudgeSessionId, Date.now());
239
243
  }
240
244
  });
241
245
  }
@@ -257,6 +261,10 @@ export var PluginState = /*#__PURE__*/function () {
257
261
  }
258
262
  }
259
263
  }
264
+ if (fg('confluence_team_presence_scroll_to_pointer')) {
265
+ var _nextState = new PluginState(this.decorationSet, participants, sid, collabInitialised, this.onError, this.nudgeAnimations);
266
+ return PluginState.eq(_nextState, this) ? this : _nextState;
267
+ }
260
268
  var nextState = new PluginState(this.decorationSet, participants, sid, collabInitialised);
261
269
  return PluginState.eq(nextState, this) ? this : nextState;
262
270
  }
@@ -1,7 +1,7 @@
1
1
  import _typeof from "@babel/runtime/helpers/typeof";
2
2
  import { AnalyticsStep, SetAttrsStep } from '@atlaskit/adf-schema/steps';
3
3
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
4
- import { TELEPOINTER_DATA_SESSION_ID_ATTR } from '@atlaskit/editor-common/collab';
4
+ import { TELEPOINTER_DATA_SESSION_ID_ATTR, TELEPOINTER_PULSE_DURING_TR_CLASS, TELEPOINTER_PULSE_DURING_TR_DURATION_MS } from '@atlaskit/editor-common/collab';
5
5
  import { processRawValueWithoutValidation } from '@atlaskit/editor-common/process-raw-value';
6
6
  import { ZERO_WIDTH_JOINER } from '@atlaskit/editor-common/whitespace';
7
7
  import { Transaction, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
@@ -37,7 +37,7 @@ export function getAvatarColor(str) {
37
37
  textColor: participantColor.color.textColor
38
38
  };
39
39
  }
40
- export var createTelepointers = function createTelepointers(from, to, sessionId, isSelection, initial, presenceId, fullName) {
40
+ export var createTelepointers = function createTelepointers(from, to, sessionId, isSelection, initial, presenceId, fullName, isNudged) {
41
41
  var decorations = [];
42
42
  var avatarColor = getAvatarColor(presenceId);
43
43
  var color = avatarColor.index.toString();
@@ -69,6 +69,12 @@ export var createTelepointers = function createTelepointers(from, to, sessionId,
69
69
  cursor.setAttribute('data-initial', initial);
70
70
  if (fg('confluence_team_presence_scroll_to_pointer')) {
71
71
  cursor.setAttribute(TELEPOINTER_DATA_SESSION_ID_ATTR, sessionId);
72
+ // If there is an ongoing expand animation, we'll keep the telepointer expanded
73
+ // until the keyframe animation is complete. Please note that this will restart the anim timer
74
+ // from 0 everytime it's re-added.
75
+ if (isNudged) {
76
+ cursor.classList.add(TELEPOINTER_PULSE_DURING_TR_CLASS);
77
+ }
72
78
  var fullNameEl = document.createElement('span');
73
79
  fullNameEl.textContent = fullName;
74
80
  fullNameEl.className = 'telepointer-fullname';
@@ -231,4 +237,18 @@ export var isOrganicChange = function isOrganicChange(tr) {
231
237
  }
232
238
  }) && !tr.doc.eq(tr.before);
233
239
  });
240
+ };
241
+
242
+ // If we receive a transaction while there is an ongoing CSS expand animation in the telepointer,
243
+ // it will be cut off due to the removal of the element. We'll persist the animation state in the plugin,
244
+ // so we can keep the expanded version showing even when the telepointer element is recreated.
245
+
246
+ export var hasExistingNudge = function hasExistingNudge(sessionId, nudgeAnimations) {
247
+ var nudgeAnimStartTime = nudgeAnimations.get(sessionId);
248
+ var hasExistingNudge = false;
249
+ if (nudgeAnimStartTime) {
250
+ var timeElapsed = Date.now() - nudgeAnimStartTime;
251
+ hasExistingNudge = timeElapsed < TELEPOINTER_PULSE_DURING_TR_DURATION_MS;
252
+ }
253
+ return hasExistingNudge;
234
254
  };
@@ -2,6 +2,7 @@ import type { ReadonlyTransaction } from '@atlaskit/editor-prosemirror/state';
2
2
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
3
3
  import type { ReadOnlyParticipants } from '../../types';
4
4
  import { Participants } from '../participants';
5
+ import { type NudgeAnimationsMap } from '../utils';
5
6
  /**
6
7
  * Returns position where it's possible to place a decoration.
7
8
  */
@@ -9,13 +10,14 @@ export declare const getValidPos: (tr: ReadonlyTransaction, pos: number) => numb
9
10
  export declare class PluginState {
10
11
  private decorationSet;
11
12
  private participants;
13
+ private nudgeAnimations;
12
14
  private onError;
13
15
  private sid?;
14
16
  isReady: boolean;
15
17
  get decorations(): DecorationSet;
16
18
  get activeParticipants(): ReadOnlyParticipants;
17
19
  get sessionId(): string | undefined;
18
- constructor(decorations: DecorationSet, participants: Participants, sessionId?: string, collabInitalised?: boolean, onError?: (err: Error) => void);
20
+ constructor(decorations: DecorationSet, participants: Participants, sessionId?: string, collabInitalised?: boolean, onError?: (err: Error) => void, nudgeAnimations?: NudgeAnimationsMap);
19
21
  getFullName(sessionId: string): string;
20
22
  getInitial(sessionId: string): string;
21
23
  getPresenceId(sessionId: string): string;
@@ -11,7 +11,7 @@ export declare function getAvatarColor(str: string): {
11
11
  backgroundColor: string;
12
12
  textColor: string;
13
13
  };
14
- export declare const createTelepointers: (from: number, to: number, sessionId: string, isSelection: boolean, initial: string, presenceId: string, fullName: string) => Decoration[];
14
+ export declare const createTelepointers: (from: number, to: number, sessionId: string, isSelection: boolean, initial: string, presenceId: string, fullName: string, isNudged: boolean) => Decoration[];
15
15
  export declare const replaceDocument: (doc: any, state: EditorState, version?: number, options?: CollabEditOptions, reserveCursor?: boolean, editorAnalyticsAPI?: EditorAnalyticsAPI) => Transaction;
16
16
  export declare const scrollToCollabCursor: (editorView: EditorView, participants: CollabParticipant[], sessionId: string | undefined, index: number, editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => void;
17
17
  export declare const getPositionOfTelepointer: (sessionId: string, decorationSet: DecorationSet) => undefined | number;
@@ -23,3 +23,5 @@ export declare const originalTransactionHasMeta: (transaction: Transaction | Rea
23
23
  * @returns boolean
24
24
  */
25
25
  export declare const isOrganicChange: (tr: ReadonlyTransaction) => boolean;
26
+ export type NudgeAnimationsMap = Map<string, number>;
27
+ export declare const hasExistingNudge: (sessionId: string, nudgeAnimations: NudgeAnimationsMap) => boolean;
@@ -2,6 +2,7 @@ import type { ReadonlyTransaction } from '@atlaskit/editor-prosemirror/state';
2
2
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
3
3
  import type { ReadOnlyParticipants } from '../../types';
4
4
  import { Participants } from '../participants';
5
+ import { type NudgeAnimationsMap } from '../utils';
5
6
  /**
6
7
  * Returns position where it's possible to place a decoration.
7
8
  */
@@ -9,13 +10,14 @@ export declare const getValidPos: (tr: ReadonlyTransaction, pos: number) => numb
9
10
  export declare class PluginState {
10
11
  private decorationSet;
11
12
  private participants;
13
+ private nudgeAnimations;
12
14
  private onError;
13
15
  private sid?;
14
16
  isReady: boolean;
15
17
  get decorations(): DecorationSet;
16
18
  get activeParticipants(): ReadOnlyParticipants;
17
19
  get sessionId(): string | undefined;
18
- constructor(decorations: DecorationSet, participants: Participants, sessionId?: string, collabInitalised?: boolean, onError?: (err: Error) => void);
20
+ constructor(decorations: DecorationSet, participants: Participants, sessionId?: string, collabInitalised?: boolean, onError?: (err: Error) => void, nudgeAnimations?: NudgeAnimationsMap);
19
21
  getFullName(sessionId: string): string;
20
22
  getInitial(sessionId: string): string;
21
23
  getPresenceId(sessionId: string): string;
@@ -11,7 +11,7 @@ export declare function getAvatarColor(str: string): {
11
11
  backgroundColor: string;
12
12
  textColor: string;
13
13
  };
14
- export declare const createTelepointers: (from: number, to: number, sessionId: string, isSelection: boolean, initial: string, presenceId: string, fullName: string) => Decoration[];
14
+ export declare const createTelepointers: (from: number, to: number, sessionId: string, isSelection: boolean, initial: string, presenceId: string, fullName: string, isNudged: boolean) => Decoration[];
15
15
  export declare const replaceDocument: (doc: any, state: EditorState, version?: number, options?: CollabEditOptions, reserveCursor?: boolean, editorAnalyticsAPI?: EditorAnalyticsAPI) => Transaction;
16
16
  export declare const scrollToCollabCursor: (editorView: EditorView, participants: CollabParticipant[], sessionId: string | undefined, index: number, editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => void;
17
17
  export declare const getPositionOfTelepointer: (sessionId: string, decorationSet: DecorationSet) => undefined | number;
@@ -23,3 +23,5 @@ export declare const originalTransactionHasMeta: (transaction: Transaction | Rea
23
23
  * @returns boolean
24
24
  */
25
25
  export declare const isOrganicChange: (tr: ReadonlyTransaction) => boolean;
26
+ export type NudgeAnimationsMap = Map<string, number>;
27
+ export declare const hasExistingNudge: (sessionId: string, nudgeAnimations: NudgeAnimationsMap) => boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-collab-edit",
3
- "version": "3.9.5",
3
+ "version": "3.10.0",
4
4
  "description": "Collab Edit plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -33,7 +33,7 @@
33
33
  "dependencies": {
34
34
  "@atlaskit/adf-schema": "^47.6.0",
35
35
  "@atlaskit/custom-steps": "^0.11.0",
36
- "@atlaskit/editor-common": "^106.7.0",
36
+ "@atlaskit/editor-common": "^106.8.0",
37
37
  "@atlaskit/editor-json-transformer": "^8.24.0",
38
38
  "@atlaskit/editor-plugin-analytics": "^2.3.0",
39
39
  "@atlaskit/editor-plugin-connectivity": "^2.0.0",