@atlaskit/prosemirror-collab 0.16.2 → 0.16.4

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,20 @@
1
1
  # @atlaskit/prosemirror-collab
2
2
 
3
+ ## 0.16.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 0.16.3
10
+
11
+ ### Patch Changes
12
+
13
+ - [#144227](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/144227)
14
+ [`6da190d8e3a24`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6da190d8e3a24) -
15
+ Add ability to rebase drag and drop content
16
+ - Updated dependencies
17
+
3
18
  ## 0.16.2
4
19
 
5
20
  ### Patch Changes
package/dist/cjs/index.js CHANGED
@@ -4,6 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
+ exports.Rebaseable = void 0;
7
8
  exports.collab = collab;
8
9
  exports.getCollabState = getCollabState;
9
10
  exports.getDocBeforeUnconfirmedSteps = getDocBeforeUnconfirmedSteps;
@@ -16,13 +17,13 @@ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/cl
16
17
  var _uuid = require("uuid");
17
18
  var _state = require("@atlaskit/editor-prosemirror/state");
18
19
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
19
- var Rebaseable = /*#__PURE__*/(0, _createClass2.default)(function Rebaseable(step, inverted, origin) {
20
+ var _movedContent = require("./movedContent");
21
+ var Rebaseable = exports.Rebaseable = /*#__PURE__*/(0, _createClass2.default)(function Rebaseable(step, inverted, origin) {
20
22
  (0, _classCallCheck2.default)(this, Rebaseable);
21
23
  this.step = step;
22
24
  this.inverted = inverted;
23
25
  this.origin = origin;
24
- }); /// Undo a given set of steps, apply a set of other steps, and then
25
- /// redo them @internal
26
+ });
26
27
  function rebaseSteps(steps, over, transform) {
27
28
  for (var i = steps.length - 1; i >= 0; i--) {
28
29
  transform.step(steps[i].inverted);
@@ -33,6 +34,7 @@ function rebaseSteps(steps, over, transform) {
33
34
  var result = [];
34
35
  for (var _i2 = 0, mapFrom = steps.length; _i2 < steps.length; _i2++) {
35
36
  var mapped = steps[_i2].step.map(transform.mapping.slice(mapFrom));
37
+ var movedStep = (0, _experiments.editorExperiment)('platform_editor_offline_editing_web', true) ? (0, _movedContent.mapStep)(steps, transform, _i2, mapped) : undefined;
36
38
  mapFrom--;
37
39
  if (mapped && !transform.maybeStep(mapped).failed) {
38
40
  // Open ticket for setMirror https://github.com/ProseMirror/prosemirror/issues/869
@@ -40,6 +42,13 @@ function rebaseSteps(steps, over, transform) {
40
42
  transform.mapping.setMirror(mapFrom, transform.steps.length - 1);
41
43
  result.push(new Rebaseable(mapped, mapped.invert(transform.docs[transform.docs.length - 1]), steps[_i2].origin));
42
44
  }
45
+
46
+ // If the step is a "move" step - apply the additional step
47
+ if ((0, _experiments.editorExperiment)('platform_editor_offline_editing_web', true)) {
48
+ if (movedStep && !transform.maybeStep(movedStep).failed) {
49
+ result.push(new Rebaseable(movedStep, movedStep.invert(transform.docs[transform.docs.length - 1]), transform));
50
+ }
51
+ }
43
52
  }
44
53
  return result;
45
54
  }
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.mapStep = exports.isMoveSequence = exports.createMoveMapStep = void 0;
7
+ var _steps = require("@atlaskit/adf-schema/steps");
8
+ var _model = require("@atlaskit/editor-prosemirror/model");
9
+ var _transform = require("@atlaskit/editor-prosemirror/transform");
10
+ // Iterate from the specified starting index down to 0
11
+ var findPreviousStepInverseIndex = function findPreviousStepInverseIndex(steps) {
12
+ var startIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : steps.length - 1;
13
+ for (var i = startIndex; i >= 0; i--) {
14
+ if (!(steps[i] instanceof _steps.AnalyticsStep)) {
15
+ return i;
16
+ }
17
+ }
18
+ };
19
+ var mapStep = exports.mapStep = function mapStep(steps, transform, index, mapped) {
20
+ if (index < 1) {
21
+ return undefined;
22
+ }
23
+ var previousRebaseableStepIndex = findPreviousStepInverseIndex(steps.map(function (s) {
24
+ return s.step;
25
+ }), index - 1);
26
+
27
+ // This checks the local steps are a "move" sequence
28
+ if (previousRebaseableStepIndex !== undefined && isMoveSequence(steps[previousRebaseableStepIndex], steps[index].step)) {
29
+ var previousStepInverseIndex = findPreviousStepInverseIndex(transform.steps);
30
+
31
+ // Creates a new step based on the "current" steps (partially through the rebase)
32
+ if (previousStepInverseIndex !== undefined) {
33
+ return createMoveMapStep(mapped, transform.steps[previousStepInverseIndex], transform, previousStepInverseIndex);
34
+ }
35
+ }
36
+ };
37
+
38
+ // Only consider ReplaceStep (ReplaceAroundStep do not occur for moves)
39
+ var isReplaceTypeStep = function isReplaceTypeStep(step) {
40
+ return step instanceof _transform.ReplaceStep;
41
+ };
42
+
43
+ /**
44
+ * Determines if a step pairing is a move sequence (ie. drag + drop or cut + paste).
45
+ *
46
+ * We determine this if we have a deletion followed by insertion and their content matches
47
+ *
48
+ * @param previousStep
49
+ * @param currentStep
50
+ * @param previousRebaseable
51
+ */
52
+ var isMoveSequence = exports.isMoveSequence = function isMoveSequence(previousRebaseableStep, currentStep) {
53
+ if (
54
+ // The both steps are replace
55
+ isReplaceTypeStep(previousRebaseableStep.step) && isReplaceTypeStep(currentStep) &&
56
+ // The current step is a deletion
57
+ previousRebaseableStep.step.slice.size === 0 &&
58
+ // The following step is an insertion with the same length that was deleted by the current step
59
+ Math.abs(previousRebaseableStep.step.to - previousRebaseableStep.step.from) === currentStep.slice.size) {
60
+ // Ensure we're getting the doc before our step changes so we can compare node contents
61
+ var originStepIndex = previousRebaseableStep.origin.steps.findIndex(function (s) {
62
+ return s === previousRebaseableStep.step;
63
+ });
64
+ var originalDoc = previousRebaseableStep.origin.docs[originStepIndex];
65
+ var currentSlice = originalDoc.slice(previousRebaseableStep.step.from, previousRebaseableStep.step.to);
66
+ // The content from the deleted + inserted slice is exactly the same (cut + paste or drag + drop)
67
+ if (currentSlice.eq(currentStep.slice)) {
68
+ return true;
69
+ }
70
+ }
71
+ return false;
72
+ };
73
+
74
+ /**
75
+ * Update the replace step slice of the insert part of a move
76
+ * to contain the slice of the current document rather than what was sliced originally.
77
+ *
78
+ * @param mapped
79
+ * @param previousStep
80
+ * @param transform
81
+ * @returns Step to apply missing changes
82
+ */
83
+ var createMoveMapStep = exports.createMoveMapStep = function createMoveMapStep(mapped, previousStep, transform, previousStepIndex) {
84
+ if (!isReplaceTypeStep(previousStep) || mapped && !isReplaceTypeStep(mapped) || !mapped) {
85
+ return undefined;
86
+ }
87
+ var newSlice = transform.docs[previousStepIndex].slice(previousStep === null || previousStep === void 0 ? void 0 : previousStep.from, previousStep === null || previousStep === void 0 ? void 0 : previousStep.to);
88
+ var diff = getDiffRange(mapped.slice.content, newSlice.content);
89
+ if (diff === undefined) {
90
+ return undefined;
91
+ }
92
+ var start = mapped.from + diff.start;
93
+ var offset = newSlice.content.size - mapped.slice.content.size === 0 ? diff.end - diff.start : 0;
94
+
95
+ // If the new slice is smaller then we're doing a deletion of content - this needs
96
+ // to be a replace step with empty content to delete content
97
+ if (newSlice.content.size - mapped.slice.content.size < 0) {
98
+ return new _transform.ReplaceStep(start, start + mapped.slice.content.size - newSlice.content.size, _model.Slice.empty);
99
+ }
100
+
101
+ // Replace the diff range with the latest content in the document (at the old position)
102
+ return new _transform.ReplaceStep(start, start + offset, transform.docs[previousStepIndex].slice((previousStep === null || previousStep === void 0 ? void 0 : previousStep.from) + diff.start, (previousStep === null || previousStep === void 0 ? void 0 : previousStep.from) + diff.end));
103
+ };
104
+
105
+ /**
106
+ * Get start and end diff position values for two fragments (old, new).
107
+ * @param {Fragment} before - content that is planned to move
108
+ * @param {Fragment} after - content that was in paragraph being deleted (updated content)
109
+ * @returns {object} - { start, end }
110
+ */
111
+ function getDiffRange(before, after) {
112
+ // https://prosemirror.net/docs/ref/#model.Fragment.findDiffStart
113
+ var start = before.findDiffStart(after);
114
+ // Important: diffEnd value is {a,b} object since end pos will differ.
115
+ // https://prosemirror.net/docs/ref/#model.Fragment.findDiffEnd
116
+ var diffEnd = before.findDiffEnd(after);
117
+ if (start === null || diffEnd === null) {
118
+ return undefined;
119
+ }
120
+ // WARNING: diffEnd may be lower than diffStart.
121
+ // If so, add overlap to get correct range.
122
+ // https://discuss.prosemirror.net/t/overlap-handling-of-finddiffstart-and-finddiffend/2856
123
+ var overlap = start - Math.min(diffEnd.a, diffEnd.b);
124
+ var end = diffEnd.b;
125
+ if (overlap > 0) {
126
+ end += overlap;
127
+ }
128
+ return {
129
+ start: start,
130
+ end: end
131
+ };
132
+ }
@@ -1,16 +1,14 @@
1
1
  import { v4 as uuidv4 } from 'uuid';
2
2
  import { Plugin, PluginKey, TextSelection } from '@atlaskit/editor-prosemirror/state';
3
3
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
4
- class Rebaseable {
4
+ import { mapStep } from './movedContent';
5
+ export class Rebaseable {
5
6
  constructor(step, inverted, origin) {
6
7
  this.step = step;
7
8
  this.inverted = inverted;
8
9
  this.origin = origin;
9
10
  }
10
11
  }
11
-
12
- /// Undo a given set of steps, apply a set of other steps, and then
13
- /// redo them @internal
14
12
  export function rebaseSteps(steps, over, transform) {
15
13
  for (let i = steps.length - 1; i >= 0; i--) {
16
14
  transform.step(steps[i].inverted);
@@ -21,6 +19,7 @@ export function rebaseSteps(steps, over, transform) {
21
19
  const result = [];
22
20
  for (let i = 0, mapFrom = steps.length; i < steps.length; i++) {
23
21
  const mapped = steps[i].step.map(transform.mapping.slice(mapFrom));
22
+ const movedStep = editorExperiment('platform_editor_offline_editing_web', true) ? mapStep(steps, transform, i, mapped) : undefined;
24
23
  mapFrom--;
25
24
  if (mapped && !transform.maybeStep(mapped).failed) {
26
25
  // Open ticket for setMirror https://github.com/ProseMirror/prosemirror/issues/869
@@ -28,6 +27,13 @@ export function rebaseSteps(steps, over, transform) {
28
27
  transform.mapping.setMirror(mapFrom, transform.steps.length - 1);
29
28
  result.push(new Rebaseable(mapped, mapped.invert(transform.docs[transform.docs.length - 1]), steps[i].origin));
30
29
  }
30
+
31
+ // If the step is a "move" step - apply the additional step
32
+ if (editorExperiment('platform_editor_offline_editing_web', true)) {
33
+ if (movedStep && !transform.maybeStep(movedStep).failed) {
34
+ result.push(new Rebaseable(movedStep, movedStep.invert(transform.docs[transform.docs.length - 1]), transform));
35
+ }
36
+ }
31
37
  }
32
38
  return result;
33
39
  }
@@ -0,0 +1,119 @@
1
+ import { AnalyticsStep } from '@atlaskit/adf-schema/steps';
2
+ import { Slice } from '@atlaskit/editor-prosemirror/model';
3
+ import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
4
+ // Iterate from the specified starting index down to 0
5
+ const findPreviousStepInverseIndex = (steps, startIndex = steps.length - 1) => {
6
+ for (let i = startIndex; i >= 0; i--) {
7
+ if (!(steps[i] instanceof AnalyticsStep)) {
8
+ return i;
9
+ }
10
+ }
11
+ };
12
+ export const mapStep = (steps, transform, index, mapped) => {
13
+ if (index < 1) {
14
+ return undefined;
15
+ }
16
+ const previousRebaseableStepIndex = findPreviousStepInverseIndex(steps.map(s => s.step), index - 1);
17
+
18
+ // This checks the local steps are a "move" sequence
19
+ if (previousRebaseableStepIndex !== undefined && isMoveSequence(steps[previousRebaseableStepIndex], steps[index].step)) {
20
+ const previousStepInverseIndex = findPreviousStepInverseIndex(transform.steps);
21
+
22
+ // Creates a new step based on the "current" steps (partially through the rebase)
23
+ if (previousStepInverseIndex !== undefined) {
24
+ return createMoveMapStep(mapped, transform.steps[previousStepInverseIndex], transform, previousStepInverseIndex);
25
+ }
26
+ }
27
+ };
28
+
29
+ // Only consider ReplaceStep (ReplaceAroundStep do not occur for moves)
30
+ const isReplaceTypeStep = step => step instanceof ReplaceStep;
31
+
32
+ /**
33
+ * Determines if a step pairing is a move sequence (ie. drag + drop or cut + paste).
34
+ *
35
+ * We determine this if we have a deletion followed by insertion and their content matches
36
+ *
37
+ * @param previousStep
38
+ * @param currentStep
39
+ * @param previousRebaseable
40
+ */
41
+ export const isMoveSequence = (previousRebaseableStep, currentStep) => {
42
+ if (
43
+ // The both steps are replace
44
+ isReplaceTypeStep(previousRebaseableStep.step) && isReplaceTypeStep(currentStep) &&
45
+ // The current step is a deletion
46
+ previousRebaseableStep.step.slice.size === 0 &&
47
+ // The following step is an insertion with the same length that was deleted by the current step
48
+ Math.abs(previousRebaseableStep.step.to - previousRebaseableStep.step.from) === currentStep.slice.size) {
49
+ // Ensure we're getting the doc before our step changes so we can compare node contents
50
+ const originStepIndex = previousRebaseableStep.origin.steps.findIndex(s => s === previousRebaseableStep.step);
51
+ const originalDoc = previousRebaseableStep.origin.docs[originStepIndex];
52
+ const currentSlice = originalDoc.slice(previousRebaseableStep.step.from, previousRebaseableStep.step.to);
53
+ // The content from the deleted + inserted slice is exactly the same (cut + paste or drag + drop)
54
+ if (currentSlice.eq(currentStep.slice)) {
55
+ return true;
56
+ }
57
+ }
58
+ return false;
59
+ };
60
+
61
+ /**
62
+ * Update the replace step slice of the insert part of a move
63
+ * to contain the slice of the current document rather than what was sliced originally.
64
+ *
65
+ * @param mapped
66
+ * @param previousStep
67
+ * @param transform
68
+ * @returns Step to apply missing changes
69
+ */
70
+ export const createMoveMapStep = (mapped, previousStep, transform, previousStepIndex) => {
71
+ if (!isReplaceTypeStep(previousStep) || mapped && !isReplaceTypeStep(mapped) || !mapped) {
72
+ return undefined;
73
+ }
74
+ const newSlice = transform.docs[previousStepIndex].slice(previousStep === null || previousStep === void 0 ? void 0 : previousStep.from, previousStep === null || previousStep === void 0 ? void 0 : previousStep.to);
75
+ const diff = getDiffRange(mapped.slice.content, newSlice.content);
76
+ if (diff === undefined) {
77
+ return undefined;
78
+ }
79
+ const start = mapped.from + diff.start;
80
+ const offset = newSlice.content.size - mapped.slice.content.size === 0 ? diff.end - diff.start : 0;
81
+
82
+ // If the new slice is smaller then we're doing a deletion of content - this needs
83
+ // to be a replace step with empty content to delete content
84
+ if (newSlice.content.size - mapped.slice.content.size < 0) {
85
+ return new ReplaceStep(start, start + mapped.slice.content.size - newSlice.content.size, Slice.empty);
86
+ }
87
+
88
+ // Replace the diff range with the latest content in the document (at the old position)
89
+ return new ReplaceStep(start, start + offset, transform.docs[previousStepIndex].slice((previousStep === null || previousStep === void 0 ? void 0 : previousStep.from) + diff.start, (previousStep === null || previousStep === void 0 ? void 0 : previousStep.from) + diff.end));
90
+ };
91
+
92
+ /**
93
+ * Get start and end diff position values for two fragments (old, new).
94
+ * @param {Fragment} before - content that is planned to move
95
+ * @param {Fragment} after - content that was in paragraph being deleted (updated content)
96
+ * @returns {object} - { start, end }
97
+ */
98
+ function getDiffRange(before, after) {
99
+ // https://prosemirror.net/docs/ref/#model.Fragment.findDiffStart
100
+ const start = before.findDiffStart(after);
101
+ // Important: diffEnd value is {a,b} object since end pos will differ.
102
+ // https://prosemirror.net/docs/ref/#model.Fragment.findDiffEnd
103
+ const diffEnd = before.findDiffEnd(after);
104
+ if (start === null || diffEnd === null) {
105
+ return undefined;
106
+ }
107
+ // WARNING: diffEnd may be lower than diffStart.
108
+ // If so, add overlap to get correct range.
109
+ // https://discuss.prosemirror.net/t/overlap-handling-of-finddiffstart-and-finddiffend/2856
110
+ const overlap = start - Math.min(diffEnd.a, diffEnd.b);
111
+ let end = diffEnd.b;
112
+ if (overlap > 0) {
113
+ end += overlap;
114
+ }
115
+ return {
116
+ start,
117
+ end
118
+ };
119
+ }
package/dist/esm/index.js CHANGED
@@ -3,13 +3,13 @@ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
3
3
  import { v4 as uuidv4 } from 'uuid';
4
4
  import { Plugin, PluginKey, TextSelection } from '@atlaskit/editor-prosemirror/state';
5
5
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
6
- var Rebaseable = /*#__PURE__*/_createClass(function Rebaseable(step, inverted, origin) {
6
+ import { mapStep } from './movedContent';
7
+ export var Rebaseable = /*#__PURE__*/_createClass(function Rebaseable(step, inverted, origin) {
7
8
  _classCallCheck(this, Rebaseable);
8
9
  this.step = step;
9
10
  this.inverted = inverted;
10
11
  this.origin = origin;
11
- }); /// Undo a given set of steps, apply a set of other steps, and then
12
- /// redo them @internal
12
+ });
13
13
  export function rebaseSteps(steps, over, transform) {
14
14
  for (var i = steps.length - 1; i >= 0; i--) {
15
15
  transform.step(steps[i].inverted);
@@ -20,6 +20,7 @@ export function rebaseSteps(steps, over, transform) {
20
20
  var result = [];
21
21
  for (var _i2 = 0, mapFrom = steps.length; _i2 < steps.length; _i2++) {
22
22
  var mapped = steps[_i2].step.map(transform.mapping.slice(mapFrom));
23
+ var movedStep = editorExperiment('platform_editor_offline_editing_web', true) ? mapStep(steps, transform, _i2, mapped) : undefined;
23
24
  mapFrom--;
24
25
  if (mapped && !transform.maybeStep(mapped).failed) {
25
26
  // Open ticket for setMirror https://github.com/ProseMirror/prosemirror/issues/869
@@ -27,6 +28,13 @@ export function rebaseSteps(steps, over, transform) {
27
28
  transform.mapping.setMirror(mapFrom, transform.steps.length - 1);
28
29
  result.push(new Rebaseable(mapped, mapped.invert(transform.docs[transform.docs.length - 1]), steps[_i2].origin));
29
30
  }
31
+
32
+ // If the step is a "move" step - apply the additional step
33
+ if (editorExperiment('platform_editor_offline_editing_web', true)) {
34
+ if (movedStep && !transform.maybeStep(movedStep).failed) {
35
+ result.push(new Rebaseable(movedStep, movedStep.invert(transform.docs[transform.docs.length - 1]), transform));
36
+ }
37
+ }
30
38
  }
31
39
  return result;
32
40
  }
@@ -0,0 +1,126 @@
1
+ import { AnalyticsStep } from '@atlaskit/adf-schema/steps';
2
+ import { Slice } from '@atlaskit/editor-prosemirror/model';
3
+ import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
4
+ // Iterate from the specified starting index down to 0
5
+ var findPreviousStepInverseIndex = function findPreviousStepInverseIndex(steps) {
6
+ var startIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : steps.length - 1;
7
+ for (var i = startIndex; i >= 0; i--) {
8
+ if (!(steps[i] instanceof AnalyticsStep)) {
9
+ return i;
10
+ }
11
+ }
12
+ };
13
+ export var mapStep = function mapStep(steps, transform, index, mapped) {
14
+ if (index < 1) {
15
+ return undefined;
16
+ }
17
+ var previousRebaseableStepIndex = findPreviousStepInverseIndex(steps.map(function (s) {
18
+ return s.step;
19
+ }), index - 1);
20
+
21
+ // This checks the local steps are a "move" sequence
22
+ if (previousRebaseableStepIndex !== undefined && isMoveSequence(steps[previousRebaseableStepIndex], steps[index].step)) {
23
+ var previousStepInverseIndex = findPreviousStepInverseIndex(transform.steps);
24
+
25
+ // Creates a new step based on the "current" steps (partially through the rebase)
26
+ if (previousStepInverseIndex !== undefined) {
27
+ return createMoveMapStep(mapped, transform.steps[previousStepInverseIndex], transform, previousStepInverseIndex);
28
+ }
29
+ }
30
+ };
31
+
32
+ // Only consider ReplaceStep (ReplaceAroundStep do not occur for moves)
33
+ var isReplaceTypeStep = function isReplaceTypeStep(step) {
34
+ return step instanceof ReplaceStep;
35
+ };
36
+
37
+ /**
38
+ * Determines if a step pairing is a move sequence (ie. drag + drop or cut + paste).
39
+ *
40
+ * We determine this if we have a deletion followed by insertion and their content matches
41
+ *
42
+ * @param previousStep
43
+ * @param currentStep
44
+ * @param previousRebaseable
45
+ */
46
+ export var isMoveSequence = function isMoveSequence(previousRebaseableStep, currentStep) {
47
+ if (
48
+ // The both steps are replace
49
+ isReplaceTypeStep(previousRebaseableStep.step) && isReplaceTypeStep(currentStep) &&
50
+ // The current step is a deletion
51
+ previousRebaseableStep.step.slice.size === 0 &&
52
+ // The following step is an insertion with the same length that was deleted by the current step
53
+ Math.abs(previousRebaseableStep.step.to - previousRebaseableStep.step.from) === currentStep.slice.size) {
54
+ // Ensure we're getting the doc before our step changes so we can compare node contents
55
+ var originStepIndex = previousRebaseableStep.origin.steps.findIndex(function (s) {
56
+ return s === previousRebaseableStep.step;
57
+ });
58
+ var originalDoc = previousRebaseableStep.origin.docs[originStepIndex];
59
+ var currentSlice = originalDoc.slice(previousRebaseableStep.step.from, previousRebaseableStep.step.to);
60
+ // The content from the deleted + inserted slice is exactly the same (cut + paste or drag + drop)
61
+ if (currentSlice.eq(currentStep.slice)) {
62
+ return true;
63
+ }
64
+ }
65
+ return false;
66
+ };
67
+
68
+ /**
69
+ * Update the replace step slice of the insert part of a move
70
+ * to contain the slice of the current document rather than what was sliced originally.
71
+ *
72
+ * @param mapped
73
+ * @param previousStep
74
+ * @param transform
75
+ * @returns Step to apply missing changes
76
+ */
77
+ export var createMoveMapStep = function createMoveMapStep(mapped, previousStep, transform, previousStepIndex) {
78
+ if (!isReplaceTypeStep(previousStep) || mapped && !isReplaceTypeStep(mapped) || !mapped) {
79
+ return undefined;
80
+ }
81
+ var newSlice = transform.docs[previousStepIndex].slice(previousStep === null || previousStep === void 0 ? void 0 : previousStep.from, previousStep === null || previousStep === void 0 ? void 0 : previousStep.to);
82
+ var diff = getDiffRange(mapped.slice.content, newSlice.content);
83
+ if (diff === undefined) {
84
+ return undefined;
85
+ }
86
+ var start = mapped.from + diff.start;
87
+ var offset = newSlice.content.size - mapped.slice.content.size === 0 ? diff.end - diff.start : 0;
88
+
89
+ // If the new slice is smaller then we're doing a deletion of content - this needs
90
+ // to be a replace step with empty content to delete content
91
+ if (newSlice.content.size - mapped.slice.content.size < 0) {
92
+ return new ReplaceStep(start, start + mapped.slice.content.size - newSlice.content.size, Slice.empty);
93
+ }
94
+
95
+ // Replace the diff range with the latest content in the document (at the old position)
96
+ return new ReplaceStep(start, start + offset, transform.docs[previousStepIndex].slice((previousStep === null || previousStep === void 0 ? void 0 : previousStep.from) + diff.start, (previousStep === null || previousStep === void 0 ? void 0 : previousStep.from) + diff.end));
97
+ };
98
+
99
+ /**
100
+ * Get start and end diff position values for two fragments (old, new).
101
+ * @param {Fragment} before - content that is planned to move
102
+ * @param {Fragment} after - content that was in paragraph being deleted (updated content)
103
+ * @returns {object} - { start, end }
104
+ */
105
+ function getDiffRange(before, after) {
106
+ // https://prosemirror.net/docs/ref/#model.Fragment.findDiffStart
107
+ var start = before.findDiffStart(after);
108
+ // Important: diffEnd value is {a,b} object since end pos will differ.
109
+ // https://prosemirror.net/docs/ref/#model.Fragment.findDiffEnd
110
+ var diffEnd = before.findDiffEnd(after);
111
+ if (start === null || diffEnd === null) {
112
+ return undefined;
113
+ }
114
+ // WARNING: diffEnd may be lower than diffStart.
115
+ // If so, add overlap to get correct range.
116
+ // https://discuss.prosemirror.net/t/overlap-handling-of-finddiffstart-and-finddiffend/2856
117
+ var overlap = start - Math.min(diffEnd.a, diffEnd.b);
118
+ var end = diffEnd.b;
119
+ if (overlap > 0) {
120
+ end += overlap;
121
+ }
122
+ return {
123
+ start: start,
124
+ end: end
125
+ };
126
+ }
@@ -1,7 +1,7 @@
1
1
  import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
2
2
  import { Plugin } from '@atlaskit/editor-prosemirror/state';
3
3
  import type { Step as ProseMirrorStep, Transform as ProseMirrorTransform } from '@atlaskit/editor-prosemirror/transform';
4
- declare class Rebaseable {
4
+ export declare class Rebaseable {
5
5
  readonly step: ProseMirrorStep;
6
6
  readonly inverted: ProseMirrorStep;
7
7
  readonly origin: ProseMirrorTransform;
@@ -0,0 +1,24 @@
1
+ import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
2
+ import type { Step as ProseMirrorStep, Transform as ProseMirrorTransform } from '@atlaskit/editor-prosemirror/transform';
3
+ import type { Rebaseable } from './index';
4
+ export declare const mapStep: (steps: readonly Rebaseable[], transform: ProseMirrorTransform, index: number, mapped: ProseMirrorStep | null) => ReplaceStep | undefined;
5
+ /**
6
+ * Determines if a step pairing is a move sequence (ie. drag + drop or cut + paste).
7
+ *
8
+ * We determine this if we have a deletion followed by insertion and their content matches
9
+ *
10
+ * @param previousStep
11
+ * @param currentStep
12
+ * @param previousRebaseable
13
+ */
14
+ export declare const isMoveSequence: (previousRebaseableStep: Rebaseable, currentStep: ProseMirrorStep) => boolean;
15
+ /**
16
+ * Update the replace step slice of the insert part of a move
17
+ * to contain the slice of the current document rather than what was sliced originally.
18
+ *
19
+ * @param mapped
20
+ * @param previousStep
21
+ * @param transform
22
+ * @returns Step to apply missing changes
23
+ */
24
+ export declare const createMoveMapStep: (mapped: ProseMirrorStep | null, previousStep: ProseMirrorStep, transform: ProseMirrorTransform, previousStepIndex: number) => ReplaceStep | undefined;
@@ -1,7 +1,7 @@
1
1
  import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
2
2
  import { Plugin } from '@atlaskit/editor-prosemirror/state';
3
3
  import type { Step as ProseMirrorStep, Transform as ProseMirrorTransform } from '@atlaskit/editor-prosemirror/transform';
4
- declare class Rebaseable {
4
+ export declare class Rebaseable {
5
5
  readonly step: ProseMirrorStep;
6
6
  readonly inverted: ProseMirrorStep;
7
7
  readonly origin: ProseMirrorTransform;
@@ -0,0 +1,24 @@
1
+ import { ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
2
+ import type { Step as ProseMirrorStep, Transform as ProseMirrorTransform } from '@atlaskit/editor-prosemirror/transform';
3
+ import type { Rebaseable } from './index';
4
+ export declare const mapStep: (steps: readonly Rebaseable[], transform: ProseMirrorTransform, index: number, mapped: ProseMirrorStep | null) => ReplaceStep | undefined;
5
+ /**
6
+ * Determines if a step pairing is a move sequence (ie. drag + drop or cut + paste).
7
+ *
8
+ * We determine this if we have a deletion followed by insertion and their content matches
9
+ *
10
+ * @param previousStep
11
+ * @param currentStep
12
+ * @param previousRebaseable
13
+ */
14
+ export declare const isMoveSequence: (previousRebaseableStep: Rebaseable, currentStep: ProseMirrorStep) => boolean;
15
+ /**
16
+ * Update the replace step slice of the insert part of a move
17
+ * to contain the slice of the current document rather than what was sliced originally.
18
+ *
19
+ * @param mapped
20
+ * @param previousStep
21
+ * @param transform
22
+ * @returns Step to apply missing changes
23
+ */
24
+ export declare const createMoveMapStep: (mapped: ProseMirrorStep | null, previousStep: ProseMirrorStep, transform: ProseMirrorTransform, previousStepIndex: number) => ReplaceStep | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/prosemirror-collab",
3
- "version": "0.16.2",
3
+ "version": "0.16.4",
4
4
  "description": "Collaborative editing for ProseMirror - Atlassian Fork",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -30,14 +30,12 @@
30
30
  ".": "./src/index.ts"
31
31
  },
32
32
  "dependencies": {
33
+ "@atlaskit/adf-schema": "^47.6.0",
33
34
  "@atlaskit/editor-prosemirror": "7.0.0",
34
- "@atlaskit/tmp-editor-statsig": "^4.8.0",
35
+ "@atlaskit/tmp-editor-statsig": "^5.0.0",
35
36
  "@babel/runtime": "^7.0.0",
36
37
  "uuid": "^3.1.0"
37
38
  },
38
- "devDependencies": {
39
- "@atlaskit/adf-schema": "^47.6.0"
40
- },
41
39
  "techstack": {
42
40
  "@atlassian/frontend": {
43
41
  "import-structure": [