@atlaskit/editor-plugin-show-diff 13.0.6 → 13.0.8
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 +18 -0
- package/dist/cjs/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +13 -32
- package/dist/cjs/pm-plugins/decorations/utils/diffableAttrs.js +96 -0
- package/dist/cjs/pm-plugins/decorations/utils/getAttrChangeRanges.js +12 -23
- package/dist/cjs/pm-plugins/main.js +2 -2
- package/dist/cjs/pm-plugins/scrollToDiff.js +5 -21
- package/dist/es2019/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +14 -32
- package/dist/es2019/pm-plugins/decorations/utils/diffableAttrs.js +88 -0
- package/dist/es2019/pm-plugins/decorations/utils/getAttrChangeRanges.js +12 -23
- package/dist/es2019/pm-plugins/main.js +3 -3
- package/dist/es2019/pm-plugins/scrollToDiff.js +3 -18
- package/dist/esm/pm-plugins/calculateDiff/attrAwareTokenEncoder.js +14 -32
- package/dist/esm/pm-plugins/decorations/utils/diffableAttrs.js +90 -0
- package/dist/esm/pm-plugins/decorations/utils/getAttrChangeRanges.js +12 -23
- package/dist/esm/pm-plugins/main.js +3 -3
- package/dist/esm/pm-plugins/scrollToDiff.js +4 -20
- package/dist/types/pm-plugins/calculateDiff/attrAwareTokenEncoder.d.ts +0 -5
- package/dist/types/pm-plugins/decorations/utils/diffableAttrs.d.ts +20 -0
- package/dist/types/pm-plugins/scrollToDiff.d.ts +3 -17
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-show-diff
|
|
2
2
|
|
|
3
|
+
## 13.0.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`d4fbe7dee465e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d4fbe7dee465e) -
|
|
8
|
+
Consolidate diff decoration scrolling into a single internal helper.
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
|
|
11
|
+
## 13.0.7
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [`2c712e57d3793`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/2c712e57d3793) -
|
|
16
|
+
Consolidate diffable-attribute rules into a single shared source of truth (`diffableAttrs`) used
|
|
17
|
+
by both the changeset token encoder (detection) and `getAttrChangeRanges` (rendering), and extend
|
|
18
|
+
the encoder to fold all mapped node types.
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
|
|
3
21
|
## 13.0.6
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -4,33 +4,19 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.attrAwareTokenEncoder = void 0;
|
|
7
|
+
var _diffableAttrs = require("../decorations/utils/diffableAttrs");
|
|
7
8
|
/**
|
|
8
|
-
* Attribute-aware token encoder for `prosemirror-changeset`.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* change at all.
|
|
14
|
-
*
|
|
15
|
-
* This encoder folds an allow-listed set of attributes into the open token so
|
|
16
|
-
* such changes register. Everything else encodes exactly as the default. The
|
|
17
|
-
* allow-list is deliberately narrow — folding in ephemeral attrs like `localId`
|
|
18
|
-
* would produce phantom diffs.
|
|
9
|
+
* Attribute-aware token encoder for `prosemirror-changeset`. The default encoder
|
|
10
|
+
* reduces a node to `node.type.name`, so an attribute-only change (e.g. a table
|
|
11
|
+
* cell recolour) tokenises identically on both sides and goes undetected. This
|
|
12
|
+
* folds the diffable attributes (from the shared `diffableAttrs` map) into the
|
|
13
|
+
* open token so such changes register.
|
|
19
14
|
*/
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
tableCell: ['background', 'colspan', 'rowspan'],
|
|
23
|
-
tableHeader: ['background', 'colspan', 'rowspan']
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Builds a stable composite token. Iterates the allow-list rather than
|
|
28
|
-
* `Object.keys` so ordering is deterministic for string comparison.
|
|
29
|
-
*/
|
|
16
|
+
// Stable composite token; attr names are pre-ordered by the caller.
|
|
30
17
|
var encodeNodeWithAttrs = function encodeNodeWithAttrs(node, attrNames) {
|
|
31
18
|
var _node$attrs;
|
|
32
19
|
var attrs = (_node$attrs = node.attrs) !== null && _node$attrs !== void 0 ? _node$attrs : {};
|
|
33
|
-
// Deterministic order: iterate the allow-list, not `Object.keys(attrs)`.
|
|
34
20
|
var parts = attrNames.map(function (name) {
|
|
35
21
|
var _attrs$name;
|
|
36
22
|
return "".concat(name, "=").concat(JSON.stringify((_attrs$name = attrs[name]) !== null && _attrs$name !== void 0 ? _attrs$name : null));
|
|
@@ -38,18 +24,16 @@ var encodeNodeWithAttrs = function encodeNodeWithAttrs(node, attrNames) {
|
|
|
38
24
|
return "".concat(node.type.name, "|").concat(parts.join('|'));
|
|
39
25
|
};
|
|
40
26
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
* their allow-listed attributes into the open token. Characters and node-end
|
|
44
|
-
* tokens are left as-is, hence the `string | number` token type.
|
|
45
|
-
*/
|
|
27
|
+
// Like the library default, but node types with a `diffableAttrs` rule fold their
|
|
28
|
+
// attrs into the open token. Chars/node-end are unchanged, hence `string | number`.
|
|
46
29
|
var attrAwareTokenEncoder = exports.attrAwareTokenEncoder = {
|
|
47
30
|
encodeCharacter: function encodeCharacter(char, _marks) {
|
|
48
31
|
return char;
|
|
49
32
|
},
|
|
50
33
|
encodeNodeStart: function encodeNodeStart(node) {
|
|
51
|
-
var
|
|
52
|
-
|
|
34
|
+
var _node$attrs2;
|
|
35
|
+
var attrNames = (0, _diffableAttrs.getDiffableAttrNames)(node.type.name, (_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 ? _node$attrs2 : {});
|
|
36
|
+
if (attrNames && attrNames.length > 0) {
|
|
53
37
|
return encodeNodeWithAttrs(node, attrNames);
|
|
54
38
|
}
|
|
55
39
|
return node.type.name;
|
|
@@ -62,10 +46,7 @@ var attrAwareTokenEncoder = exports.attrAwareTokenEncoder = {
|
|
|
62
46
|
}
|
|
63
47
|
};
|
|
64
48
|
|
|
65
|
-
|
|
66
|
-
* Mirrors the library's private `typeID` so node-end tokens match the default
|
|
67
|
-
* encoding exactly. Reimplemented here because it is not exported.
|
|
68
|
-
*/
|
|
49
|
+
// Mirrors the library's private (unexported) `typeID` so node-end tokens match.
|
|
69
50
|
function typeID(type) {
|
|
70
51
|
var cache = type.schema.cached.changeSetIDs || (type.schema.cached.changeSetIDs = Object.create(null));
|
|
71
52
|
var id = cache[type.name];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isDiffableAttr = exports.getRequiredIncludedDiffableAttrs = exports.getIncludedDiffableAttrs = exports.getDiffableAttrNames = exports.DIFFABLE_ATTRS_BY_NODE_TYPE = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Single source of truth for which attributes of a node type represent a
|
|
9
|
+
* meaningful change. Shared by `attrAwareTokenEncoder` (changeset detection)
|
|
10
|
+
* and `getAttrChangeRanges` (decoration rendering) so the two stages agree.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Node types absent from this map have no attribute-level diffing.
|
|
14
|
+
var DIFFABLE_ATTRS_BY_NODE_TYPE = exports.DIFFABLE_ATTRS_BY_NODE_TYPE = {
|
|
15
|
+
tableCell: {
|
|
16
|
+
include: ['background', 'colspan', 'rowspan']
|
|
17
|
+
},
|
|
18
|
+
tableHeader: {
|
|
19
|
+
include: ['background', 'colspan', 'rowspan']
|
|
20
|
+
},
|
|
21
|
+
// Inline nodes whose identity is their attrs.
|
|
22
|
+
date: {
|
|
23
|
+
include: ['timestamp']
|
|
24
|
+
},
|
|
25
|
+
emoji: {
|
|
26
|
+
include: ['shortName', 'id', 'text']
|
|
27
|
+
},
|
|
28
|
+
mention: {
|
|
29
|
+
include: ['id', 'text']
|
|
30
|
+
},
|
|
31
|
+
status: {
|
|
32
|
+
include: ['text', 'color']
|
|
33
|
+
},
|
|
34
|
+
taskItem: {
|
|
35
|
+
include: ['state']
|
|
36
|
+
},
|
|
37
|
+
// Media: id/collection/url identify the image.
|
|
38
|
+
media: {
|
|
39
|
+
include: ['id', 'collection', 'url']
|
|
40
|
+
},
|
|
41
|
+
// Extensions: any attribute change is meaningful except localId.
|
|
42
|
+
extension: {
|
|
43
|
+
exclude: ['localId']
|
|
44
|
+
},
|
|
45
|
+
inlineExtension: {
|
|
46
|
+
exclude: ['localId']
|
|
47
|
+
},
|
|
48
|
+
bodiedExtension: {
|
|
49
|
+
exclude: ['localId']
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Whether `attrName` is a meaningful change for `nodeTypeName`. False if no rule.
|
|
54
|
+
var isDiffableAttr = exports.isDiffableAttr = function isDiffableAttr(nodeTypeName, attrName) {
|
|
55
|
+
var rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
56
|
+
if (!rule) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return 'include' in rule ? rule.include.includes(attrName) : !rule.exclude.includes(attrName);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// The fixed include list for a node type, or `undefined` for no/exclude rule.
|
|
63
|
+
// Use where a node-independent attr list is needed (e.g. the inline node map).
|
|
64
|
+
var getIncludedDiffableAttrs = exports.getIncludedDiffableAttrs = function getIncludedDiffableAttrs(nodeTypeName) {
|
|
65
|
+
var rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
66
|
+
return rule && 'include' in rule ? rule.include : undefined;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Like `getIncludedDiffableAttrs` but asserts the include rule exists, so a
|
|
70
|
+
// consumer relying on a fixed list fails loudly if the rule is removed or
|
|
71
|
+
// switched to an exclude rule rather than silently drifting.
|
|
72
|
+
var getRequiredIncludedDiffableAttrs = exports.getRequiredIncludedDiffableAttrs = function getRequiredIncludedDiffableAttrs(nodeTypeName) {
|
|
73
|
+
var attrs = getIncludedDiffableAttrs(nodeTypeName);
|
|
74
|
+
if (!attrs) {
|
|
75
|
+
throw new Error("diffableAttrs: expected an include rule for \"".concat(nodeTypeName, "\""));
|
|
76
|
+
}
|
|
77
|
+
return attrs;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Concrete attr names to diff for a node instance, honouring include and exclude
|
|
82
|
+
* rules; `undefined` when the type has no rule. Exclude rules read the node's
|
|
83
|
+
* attrs and sort them, since key order isn't stable across the two diffed docs.
|
|
84
|
+
*/
|
|
85
|
+
var getDiffableAttrNames = exports.getDiffableAttrNames = function getDiffableAttrNames(nodeTypeName, attrs) {
|
|
86
|
+
var rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
87
|
+
if (!rule) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
if ('include' in rule) {
|
|
91
|
+
return rule.include;
|
|
92
|
+
}
|
|
93
|
+
return Object.keys(attrs).filter(function (name) {
|
|
94
|
+
return !rule.exclude.includes(name);
|
|
95
|
+
}).sort();
|
|
96
|
+
};
|
|
@@ -10,29 +10,20 @@ var _steps = require("@atlaskit/adf-schema/steps");
|
|
|
10
10
|
var _deprecatedPlatformFeatureExperiments = require("@atlaskit/editor-common/deprecated-platform-feature-experiments");
|
|
11
11
|
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
12
12
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
13
|
+
var _diffableAttrs = require("./diffableAttrs");
|
|
13
14
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
14
15
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
15
16
|
var filterUndefined = function filterUndefined(x) {
|
|
16
17
|
return !!x;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
|
-
//
|
|
20
|
-
var mediaAttrs =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
var
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
var taskItemAttrs = ['state'];
|
|
27
|
-
|
|
28
|
-
// Attributes that indicate an emoji change
|
|
29
|
-
var emojiAttrs = ['shortName', 'id', 'text'];
|
|
30
|
-
|
|
31
|
-
// Attributes that indicate a mention change (who is mentioned)
|
|
32
|
-
var mentionAttrs = ['id', 'text'];
|
|
33
|
-
|
|
34
|
-
// Attributes that indicate a status change (label or colour)
|
|
35
|
-
var statusAttrs = ['text', 'color'];
|
|
20
|
+
// Attr lists sourced from the shared `diffableAttrs` map (shared with the encoder).
|
|
21
|
+
var mediaAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('media');
|
|
22
|
+
var dateAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('date');
|
|
23
|
+
var taskItemAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('taskItem');
|
|
24
|
+
var emojiAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('emoji');
|
|
25
|
+
var mentionAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('mention');
|
|
26
|
+
var statusAttrs = (0, _diffableAttrs.getRequiredIncludedDiffableAttrs)('status');
|
|
36
27
|
|
|
37
28
|
// Map of node type name → the attrs that represent a meaningful content change for that node
|
|
38
29
|
var inlineNodeAttrMap = {
|
|
@@ -45,10 +36,8 @@ var isInlineAttrChangeNodeName = function isInlineAttrChangeNodeName(nodeName) {
|
|
|
45
36
|
return nodeName in inlineNodeAttrMap;
|
|
46
37
|
};
|
|
47
38
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Extension node type names
|
|
39
|
+
// Extension node type names. Their "any attr except localId" exclude rule lives
|
|
40
|
+
// in the shared `diffableAttrs` map and is applied via `isDiffableAttr` below.
|
|
52
41
|
var extensionNodeNames = ['extension', 'inlineExtension', 'bodiedExtension'];
|
|
53
42
|
var getStepAttrs = function getStepAttrs(step) {
|
|
54
43
|
if (step instanceof _transform.AttrStep) {
|
|
@@ -118,9 +107,9 @@ var getAttrChangeRanges = exports.getAttrChangeRanges = function getAttrChangeRa
|
|
|
118
107
|
};
|
|
119
108
|
}
|
|
120
109
|
|
|
121
|
-
// extension nodes: any
|
|
110
|
+
// extension nodes: highlight on any diffable attr change (exclude rule in shared map)
|
|
122
111
|
if (nodeAtPos && extensionNodeNames.includes(nodeAtPos.type.name) && stepAttrs.some(function (v) {
|
|
123
|
-
return
|
|
112
|
+
return (0, _diffableAttrs.isDiffableAttr)(nodeAtPos.type.name, v);
|
|
124
113
|
})) {
|
|
125
114
|
var isInline = nodeAtPos.type.name === 'inlineExtension';
|
|
126
115
|
return {
|
|
@@ -187,7 +187,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
|
|
|
187
187
|
if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && (0, _isExtendedEnabled.isExtendedEnabled)(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
|
|
188
188
|
var _cancelPendingScrollT;
|
|
189
189
|
(_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 || _cancelPendingScrollT();
|
|
190
|
-
cancelPendingScrollToDecoration = (0, _scrollToDiff.
|
|
190
|
+
cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToDecoration)(view, (0, _getScrollableDecorations.getScrollableDecorations)(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
|
|
191
191
|
|
|
192
192
|
// Reset the flag so we don't scroll again on subsequent updates
|
|
193
193
|
view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
|
|
@@ -208,7 +208,7 @@ var createPlugin = exports.createPlugin = function createPlugin(config, getIntl,
|
|
|
208
208
|
// avoid a circular dependency; expand picks up the meta if loaded (no-op if not).
|
|
209
209
|
api === null || api === void 0 || api.core.actions.execute((0, _expand.toggleExpandRange)(activeDecoration.from, activeDecoration.to, true));
|
|
210
210
|
}
|
|
211
|
-
cancelPendingScrollToDecoration = (0, _scrollToDiff.
|
|
211
|
+
cancelPendingScrollToDecoration = (0, _scrollToDiff.scrollToDecoration)(view, scrollableDecorations, pluginState.activeIndex);
|
|
212
212
|
}
|
|
213
213
|
},
|
|
214
214
|
destroy: function destroy() {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.scrollToDecoration = void 0;
|
|
7
7
|
var _decorationKeys = require("./decorations/decorationKeys");
|
|
8
8
|
/**
|
|
9
9
|
* Extra space above the scrolled-to element so it does not sit flush under the
|
|
@@ -40,29 +40,13 @@ function scrollToSelection(node) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
|
-
* Schedules scrolling to the
|
|
44
|
-
*
|
|
45
|
-
* it simply scrolls to bring the first decoration into view.
|
|
46
|
-
*
|
|
47
|
-
* The caller must pass the list of scrollable decorations as produced by
|
|
48
|
-
* `getScrollableDecorations`, which is filtered (non-scrollable decorations
|
|
49
|
-
* removed) and sorted by document position. This guarantees "first" means the
|
|
50
|
-
* topmost scrollable diff in the document rather than an arbitrary decoration
|
|
51
|
-
* from the raw `DecorationSet` ordering. This is delegated to
|
|
52
|
-
* `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
|
|
53
|
-
*
|
|
54
|
-
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
55
|
-
*/
|
|
56
|
-
var scrollToFirstDecoration = exports.scrollToFirstDecoration = function scrollToFirstDecoration(view, scrollableDecorations) {
|
|
57
|
-
return scrollToActiveDecoration(view, scrollableDecorations, 0);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Schedules scrolling to the decoration at the given index after the next frame.
|
|
43
|
+
* Schedules scrolling to the decoration at the given index after the next frame. Defaults to the
|
|
44
|
+
* first decoration when no index is provided.
|
|
62
45
|
*
|
|
63
46
|
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
64
47
|
*/
|
|
65
|
-
var
|
|
48
|
+
var scrollToDecoration = exports.scrollToDecoration = function scrollToDecoration(view, decorations) {
|
|
49
|
+
var activeIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
66
50
|
var decoration = decorations[activeIndex];
|
|
67
51
|
if (!decoration) {
|
|
68
52
|
return function () {};
|
|
@@ -1,30 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
* Attribute-aware token encoder for `prosemirror-changeset`.
|
|
3
|
-
*
|
|
4
|
-
* The library's default encoder reduces a node's open token to `node.type.name`,
|
|
5
|
-
* ignoring attributes. An attribute-only change (e.g. recolouring a table cell's
|
|
6
|
-
* `background`) therefore tokenises identically on both sides and reports no
|
|
7
|
-
* change at all.
|
|
8
|
-
*
|
|
9
|
-
* This encoder folds an allow-listed set of attributes into the open token so
|
|
10
|
-
* such changes register. Everything else encodes exactly as the default. The
|
|
11
|
-
* allow-list is deliberately narrow — folding in ephemeral attrs like `localId`
|
|
12
|
-
* would produce phantom diffs.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const DIFFED_ATTRS_BY_NODE_TYPE = {
|
|
16
|
-
tableCell: ['background', 'colspan', 'rowspan'],
|
|
17
|
-
tableHeader: ['background', 'colspan', 'rowspan']
|
|
18
|
-
};
|
|
1
|
+
import { getDiffableAttrNames } from '../decorations/utils/diffableAttrs';
|
|
19
2
|
|
|
20
3
|
/**
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
4
|
+
* Attribute-aware token encoder for `prosemirror-changeset`. The default encoder
|
|
5
|
+
* reduces a node to `node.type.name`, so an attribute-only change (e.g. a table
|
|
6
|
+
* cell recolour) tokenises identically on both sides and goes undetected. This
|
|
7
|
+
* folds the diffable attributes (from the shared `diffableAttrs` map) into the
|
|
8
|
+
* open token so such changes register.
|
|
23
9
|
*/
|
|
10
|
+
|
|
11
|
+
// Stable composite token; attr names are pre-ordered by the caller.
|
|
24
12
|
const encodeNodeWithAttrs = (node, attrNames) => {
|
|
25
13
|
var _node$attrs;
|
|
26
14
|
const attrs = (_node$attrs = node.attrs) !== null && _node$attrs !== void 0 ? _node$attrs : {};
|
|
27
|
-
// Deterministic order: iterate the allow-list, not `Object.keys(attrs)`.
|
|
28
15
|
const parts = attrNames.map(name => {
|
|
29
16
|
var _attrs$name;
|
|
30
17
|
return `${name}=${JSON.stringify((_attrs$name = attrs[name]) !== null && _attrs$name !== void 0 ? _attrs$name : null)}`;
|
|
@@ -32,16 +19,14 @@ const encodeNodeWithAttrs = (node, attrNames) => {
|
|
|
32
19
|
return `${node.type.name}|${parts.join('|')}`;
|
|
33
20
|
};
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
* their allow-listed attributes into the open token. Characters and node-end
|
|
38
|
-
* tokens are left as-is, hence the `string | number` token type.
|
|
39
|
-
*/
|
|
22
|
+
// Like the library default, but node types with a `diffableAttrs` rule fold their
|
|
23
|
+
// attrs into the open token. Chars/node-end are unchanged, hence `string | number`.
|
|
40
24
|
export const attrAwareTokenEncoder = {
|
|
41
25
|
encodeCharacter: (char, _marks) => char,
|
|
42
26
|
encodeNodeStart: node => {
|
|
43
|
-
|
|
44
|
-
|
|
27
|
+
var _node$attrs2;
|
|
28
|
+
const attrNames = getDiffableAttrNames(node.type.name, (_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 ? _node$attrs2 : {});
|
|
29
|
+
if (attrNames && attrNames.length > 0) {
|
|
45
30
|
return encodeNodeWithAttrs(node, attrNames);
|
|
46
31
|
}
|
|
47
32
|
return node.type.name;
|
|
@@ -50,10 +35,7 @@ export const attrAwareTokenEncoder = {
|
|
|
50
35
|
compareTokens: (a, b) => a === b
|
|
51
36
|
};
|
|
52
37
|
|
|
53
|
-
|
|
54
|
-
* Mirrors the library's private `typeID` so node-end tokens match the default
|
|
55
|
-
* encoding exactly. Reimplemented here because it is not exported.
|
|
56
|
-
*/
|
|
38
|
+
// Mirrors the library's private (unexported) `typeID` so node-end tokens match.
|
|
57
39
|
function typeID(type) {
|
|
58
40
|
const cache = type.schema.cached.changeSetIDs || (type.schema.cached.changeSetIDs = Object.create(null));
|
|
59
41
|
let id = cache[type.name];
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for which attributes of a node type represent a
|
|
3
|
+
* meaningful change. Shared by `attrAwareTokenEncoder` (changeset detection)
|
|
4
|
+
* and `getAttrChangeRanges` (decoration rendering) so the two stages agree.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Node types absent from this map have no attribute-level diffing.
|
|
8
|
+
export const DIFFABLE_ATTRS_BY_NODE_TYPE = {
|
|
9
|
+
tableCell: {
|
|
10
|
+
include: ['background', 'colspan', 'rowspan']
|
|
11
|
+
},
|
|
12
|
+
tableHeader: {
|
|
13
|
+
include: ['background', 'colspan', 'rowspan']
|
|
14
|
+
},
|
|
15
|
+
// Inline nodes whose identity is their attrs.
|
|
16
|
+
date: {
|
|
17
|
+
include: ['timestamp']
|
|
18
|
+
},
|
|
19
|
+
emoji: {
|
|
20
|
+
include: ['shortName', 'id', 'text']
|
|
21
|
+
},
|
|
22
|
+
mention: {
|
|
23
|
+
include: ['id', 'text']
|
|
24
|
+
},
|
|
25
|
+
status: {
|
|
26
|
+
include: ['text', 'color']
|
|
27
|
+
},
|
|
28
|
+
taskItem: {
|
|
29
|
+
include: ['state']
|
|
30
|
+
},
|
|
31
|
+
// Media: id/collection/url identify the image.
|
|
32
|
+
media: {
|
|
33
|
+
include: ['id', 'collection', 'url']
|
|
34
|
+
},
|
|
35
|
+
// Extensions: any attribute change is meaningful except localId.
|
|
36
|
+
extension: {
|
|
37
|
+
exclude: ['localId']
|
|
38
|
+
},
|
|
39
|
+
inlineExtension: {
|
|
40
|
+
exclude: ['localId']
|
|
41
|
+
},
|
|
42
|
+
bodiedExtension: {
|
|
43
|
+
exclude: ['localId']
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Whether `attrName` is a meaningful change for `nodeTypeName`. False if no rule.
|
|
48
|
+
export const isDiffableAttr = (nodeTypeName, attrName) => {
|
|
49
|
+
const rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
50
|
+
if (!rule) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return 'include' in rule ? rule.include.includes(attrName) : !rule.exclude.includes(attrName);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// The fixed include list for a node type, or `undefined` for no/exclude rule.
|
|
57
|
+
// Use where a node-independent attr list is needed (e.g. the inline node map).
|
|
58
|
+
export const getIncludedDiffableAttrs = nodeTypeName => {
|
|
59
|
+
const rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
60
|
+
return rule && 'include' in rule ? rule.include : undefined;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Like `getIncludedDiffableAttrs` but asserts the include rule exists, so a
|
|
64
|
+
// consumer relying on a fixed list fails loudly if the rule is removed or
|
|
65
|
+
// switched to an exclude rule rather than silently drifting.
|
|
66
|
+
export const getRequiredIncludedDiffableAttrs = nodeTypeName => {
|
|
67
|
+
const attrs = getIncludedDiffableAttrs(nodeTypeName);
|
|
68
|
+
if (!attrs) {
|
|
69
|
+
throw new Error(`diffableAttrs: expected an include rule for "${nodeTypeName}"`);
|
|
70
|
+
}
|
|
71
|
+
return attrs;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Concrete attr names to diff for a node instance, honouring include and exclude
|
|
76
|
+
* rules; `undefined` when the type has no rule. Exclude rules read the node's
|
|
77
|
+
* attrs and sort them, since key order isn't stable across the two diffed docs.
|
|
78
|
+
*/
|
|
79
|
+
export const getDiffableAttrNames = (nodeTypeName, attrs) => {
|
|
80
|
+
const rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
81
|
+
if (!rule) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
if ('include' in rule) {
|
|
85
|
+
return rule.include;
|
|
86
|
+
}
|
|
87
|
+
return Object.keys(attrs).filter(name => !rule.exclude.includes(name)).sort();
|
|
88
|
+
};
|
|
@@ -2,25 +2,16 @@ import { SetAttrsStep } from '@atlaskit/adf-schema/steps';
|
|
|
2
2
|
import { isExperimentEnabled } from '@atlaskit/editor-common/deprecated-platform-feature-experiments';
|
|
3
3
|
import { AttrStep } from '@atlaskit/editor-prosemirror/transform';
|
|
4
4
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
5
|
+
import { getRequiredIncludedDiffableAttrs, isDiffableAttr } from './diffableAttrs';
|
|
5
6
|
const filterUndefined = x => !!x;
|
|
6
7
|
|
|
7
|
-
//
|
|
8
|
-
const mediaAttrs =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const taskItemAttrs = ['state'];
|
|
15
|
-
|
|
16
|
-
// Attributes that indicate an emoji change
|
|
17
|
-
const emojiAttrs = ['shortName', 'id', 'text'];
|
|
18
|
-
|
|
19
|
-
// Attributes that indicate a mention change (who is mentioned)
|
|
20
|
-
const mentionAttrs = ['id', 'text'];
|
|
21
|
-
|
|
22
|
-
// Attributes that indicate a status change (label or colour)
|
|
23
|
-
const statusAttrs = ['text', 'color'];
|
|
8
|
+
// Attr lists sourced from the shared `diffableAttrs` map (shared with the encoder).
|
|
9
|
+
const mediaAttrs = getRequiredIncludedDiffableAttrs('media');
|
|
10
|
+
const dateAttrs = getRequiredIncludedDiffableAttrs('date');
|
|
11
|
+
const taskItemAttrs = getRequiredIncludedDiffableAttrs('taskItem');
|
|
12
|
+
const emojiAttrs = getRequiredIncludedDiffableAttrs('emoji');
|
|
13
|
+
const mentionAttrs = getRequiredIncludedDiffableAttrs('mention');
|
|
14
|
+
const statusAttrs = getRequiredIncludedDiffableAttrs('status');
|
|
24
15
|
|
|
25
16
|
// Map of node type name → the attrs that represent a meaningful content change for that node
|
|
26
17
|
const inlineNodeAttrMap = {
|
|
@@ -31,10 +22,8 @@ const inlineNodeAttrMap = {
|
|
|
31
22
|
};
|
|
32
23
|
const isInlineAttrChangeNodeName = nodeName => nodeName in inlineNodeAttrMap;
|
|
33
24
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// Extension node type names
|
|
25
|
+
// Extension node type names. Their "any attr except localId" exclude rule lives
|
|
26
|
+
// in the shared `diffableAttrs` map and is applied via `isDiffableAttr` below.
|
|
38
27
|
const extensionNodeNames = ['extension', 'inlineExtension', 'bodiedExtension'];
|
|
39
28
|
const getStepAttrs = step => {
|
|
40
29
|
if (step instanceof AttrStep) {
|
|
@@ -95,8 +84,8 @@ export const getAttrChangeRanges = (doc, steps, originalDoc) => {
|
|
|
95
84
|
};
|
|
96
85
|
}
|
|
97
86
|
|
|
98
|
-
// extension nodes: any
|
|
99
|
-
if (nodeAtPos && extensionNodeNames.includes(nodeAtPos.type.name) && stepAttrs.some(v =>
|
|
87
|
+
// extension nodes: highlight on any diffable attr change (exclude rule in shared map)
|
|
88
|
+
if (nodeAtPos && extensionNodeNames.includes(nodeAtPos.type.name) && stepAttrs.some(v => isDiffableAttr(nodeAtPos.type.name, v))) {
|
|
100
89
|
const isInline = nodeAtPos.type.name === 'inlineExtension';
|
|
101
90
|
return {
|
|
102
91
|
fromB: step.pos,
|
|
@@ -9,7 +9,7 @@ import { enforceCustomStepRegisters } from './enforceCustomStepRegisters';
|
|
|
9
9
|
import { getScrollableDecorations } from './getScrollableDecorations';
|
|
10
10
|
import { isExtendedEnabled } from './isExtendedEnabled';
|
|
11
11
|
import { NodeViewSerializer } from './NodeViewSerializer';
|
|
12
|
-
import {
|
|
12
|
+
import { scrollToDecoration } from './scrollToDiff';
|
|
13
13
|
export const showDiffPluginKey = new PluginKey('showDiffPlugin');
|
|
14
14
|
export const createPlugin = (config, getIntl, api, onEditorView) => {
|
|
15
15
|
enforceCustomStepRegisters();
|
|
@@ -194,7 +194,7 @@ export const createPlugin = (config, getIntl, api, onEditorView) => {
|
|
|
194
194
|
if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && isExtendedEnabled(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
|
|
195
195
|
var _cancelPendingScrollT;
|
|
196
196
|
(_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 ? void 0 : _cancelPendingScrollT();
|
|
197
|
-
cancelPendingScrollToDecoration =
|
|
197
|
+
cancelPendingScrollToDecoration = scrollToDecoration(view, getScrollableDecorations(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
|
|
198
198
|
|
|
199
199
|
// Reset the flag so we don't scroll again on subsequent updates
|
|
200
200
|
view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
|
|
@@ -215,7 +215,7 @@ export const createPlugin = (config, getIntl, api, onEditorView) => {
|
|
|
215
215
|
// avoid a circular dependency; expand picks up the meta if loaded (no-op if not).
|
|
216
216
|
api === null || api === void 0 ? void 0 : api.core.actions.execute(toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
|
|
217
217
|
}
|
|
218
|
-
cancelPendingScrollToDecoration =
|
|
218
|
+
cancelPendingScrollToDecoration = scrollToDecoration(view, scrollableDecorations, pluginState.activeIndex);
|
|
219
219
|
}
|
|
220
220
|
},
|
|
221
221
|
destroy() {
|
|
@@ -35,27 +35,12 @@ function scrollToSelection(node) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Schedules scrolling to the
|
|
39
|
-
*
|
|
40
|
-
* it simply scrolls to bring the first decoration into view.
|
|
41
|
-
*
|
|
42
|
-
* The caller must pass the list of scrollable decorations as produced by
|
|
43
|
-
* `getScrollableDecorations`, which is filtered (non-scrollable decorations
|
|
44
|
-
* removed) and sorted by document position. This guarantees "first" means the
|
|
45
|
-
* topmost scrollable diff in the document rather than an arbitrary decoration
|
|
46
|
-
* from the raw `DecorationSet` ordering. This is delegated to
|
|
47
|
-
* `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
|
|
48
|
-
*
|
|
49
|
-
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
50
|
-
*/
|
|
51
|
-
export const scrollToFirstDecoration = (view, scrollableDecorations) => scrollToActiveDecoration(view, scrollableDecorations, 0);
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Schedules scrolling to the decoration at the given index after the next frame.
|
|
38
|
+
* Schedules scrolling to the decoration at the given index after the next frame. Defaults to the
|
|
39
|
+
* first decoration when no index is provided.
|
|
55
40
|
*
|
|
56
41
|
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
57
42
|
*/
|
|
58
|
-
export const
|
|
43
|
+
export const scrollToDecoration = (view, decorations, activeIndex = 0) => {
|
|
59
44
|
const decoration = decorations[activeIndex];
|
|
60
45
|
if (!decoration) {
|
|
61
46
|
return () => {};
|
|
@@ -1,30 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
* Attribute-aware token encoder for `prosemirror-changeset`.
|
|
3
|
-
*
|
|
4
|
-
* The library's default encoder reduces a node's open token to `node.type.name`,
|
|
5
|
-
* ignoring attributes. An attribute-only change (e.g. recolouring a table cell's
|
|
6
|
-
* `background`) therefore tokenises identically on both sides and reports no
|
|
7
|
-
* change at all.
|
|
8
|
-
*
|
|
9
|
-
* This encoder folds an allow-listed set of attributes into the open token so
|
|
10
|
-
* such changes register. Everything else encodes exactly as the default. The
|
|
11
|
-
* allow-list is deliberately narrow — folding in ephemeral attrs like `localId`
|
|
12
|
-
* would produce phantom diffs.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
var DIFFED_ATTRS_BY_NODE_TYPE = {
|
|
16
|
-
tableCell: ['background', 'colspan', 'rowspan'],
|
|
17
|
-
tableHeader: ['background', 'colspan', 'rowspan']
|
|
18
|
-
};
|
|
1
|
+
import { getDiffableAttrNames } from '../decorations/utils/diffableAttrs';
|
|
19
2
|
|
|
20
3
|
/**
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
4
|
+
* Attribute-aware token encoder for `prosemirror-changeset`. The default encoder
|
|
5
|
+
* reduces a node to `node.type.name`, so an attribute-only change (e.g. a table
|
|
6
|
+
* cell recolour) tokenises identically on both sides and goes undetected. This
|
|
7
|
+
* folds the diffable attributes (from the shared `diffableAttrs` map) into the
|
|
8
|
+
* open token so such changes register.
|
|
23
9
|
*/
|
|
10
|
+
|
|
11
|
+
// Stable composite token; attr names are pre-ordered by the caller.
|
|
24
12
|
var encodeNodeWithAttrs = function encodeNodeWithAttrs(node, attrNames) {
|
|
25
13
|
var _node$attrs;
|
|
26
14
|
var attrs = (_node$attrs = node.attrs) !== null && _node$attrs !== void 0 ? _node$attrs : {};
|
|
27
|
-
// Deterministic order: iterate the allow-list, not `Object.keys(attrs)`.
|
|
28
15
|
var parts = attrNames.map(function (name) {
|
|
29
16
|
var _attrs$name;
|
|
30
17
|
return "".concat(name, "=").concat(JSON.stringify((_attrs$name = attrs[name]) !== null && _attrs$name !== void 0 ? _attrs$name : null));
|
|
@@ -32,18 +19,16 @@ var encodeNodeWithAttrs = function encodeNodeWithAttrs(node, attrNames) {
|
|
|
32
19
|
return "".concat(node.type.name, "|").concat(parts.join('|'));
|
|
33
20
|
};
|
|
34
21
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
* their allow-listed attributes into the open token. Characters and node-end
|
|
38
|
-
* tokens are left as-is, hence the `string | number` token type.
|
|
39
|
-
*/
|
|
22
|
+
// Like the library default, but node types with a `diffableAttrs` rule fold their
|
|
23
|
+
// attrs into the open token. Chars/node-end are unchanged, hence `string | number`.
|
|
40
24
|
export var attrAwareTokenEncoder = {
|
|
41
25
|
encodeCharacter: function encodeCharacter(char, _marks) {
|
|
42
26
|
return char;
|
|
43
27
|
},
|
|
44
28
|
encodeNodeStart: function encodeNodeStart(node) {
|
|
45
|
-
var
|
|
46
|
-
|
|
29
|
+
var _node$attrs2;
|
|
30
|
+
var attrNames = getDiffableAttrNames(node.type.name, (_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 ? _node$attrs2 : {});
|
|
31
|
+
if (attrNames && attrNames.length > 0) {
|
|
47
32
|
return encodeNodeWithAttrs(node, attrNames);
|
|
48
33
|
}
|
|
49
34
|
return node.type.name;
|
|
@@ -56,10 +41,7 @@ export var attrAwareTokenEncoder = {
|
|
|
56
41
|
}
|
|
57
42
|
};
|
|
58
43
|
|
|
59
|
-
|
|
60
|
-
* Mirrors the library's private `typeID` so node-end tokens match the default
|
|
61
|
-
* encoding exactly. Reimplemented here because it is not exported.
|
|
62
|
-
*/
|
|
44
|
+
// Mirrors the library's private (unexported) `typeID` so node-end tokens match.
|
|
63
45
|
function typeID(type) {
|
|
64
46
|
var cache = type.schema.cached.changeSetIDs || (type.schema.cached.changeSetIDs = Object.create(null));
|
|
65
47
|
var id = cache[type.name];
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for which attributes of a node type represent a
|
|
3
|
+
* meaningful change. Shared by `attrAwareTokenEncoder` (changeset detection)
|
|
4
|
+
* and `getAttrChangeRanges` (decoration rendering) so the two stages agree.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Node types absent from this map have no attribute-level diffing.
|
|
8
|
+
export var DIFFABLE_ATTRS_BY_NODE_TYPE = {
|
|
9
|
+
tableCell: {
|
|
10
|
+
include: ['background', 'colspan', 'rowspan']
|
|
11
|
+
},
|
|
12
|
+
tableHeader: {
|
|
13
|
+
include: ['background', 'colspan', 'rowspan']
|
|
14
|
+
},
|
|
15
|
+
// Inline nodes whose identity is their attrs.
|
|
16
|
+
date: {
|
|
17
|
+
include: ['timestamp']
|
|
18
|
+
},
|
|
19
|
+
emoji: {
|
|
20
|
+
include: ['shortName', 'id', 'text']
|
|
21
|
+
},
|
|
22
|
+
mention: {
|
|
23
|
+
include: ['id', 'text']
|
|
24
|
+
},
|
|
25
|
+
status: {
|
|
26
|
+
include: ['text', 'color']
|
|
27
|
+
},
|
|
28
|
+
taskItem: {
|
|
29
|
+
include: ['state']
|
|
30
|
+
},
|
|
31
|
+
// Media: id/collection/url identify the image.
|
|
32
|
+
media: {
|
|
33
|
+
include: ['id', 'collection', 'url']
|
|
34
|
+
},
|
|
35
|
+
// Extensions: any attribute change is meaningful except localId.
|
|
36
|
+
extension: {
|
|
37
|
+
exclude: ['localId']
|
|
38
|
+
},
|
|
39
|
+
inlineExtension: {
|
|
40
|
+
exclude: ['localId']
|
|
41
|
+
},
|
|
42
|
+
bodiedExtension: {
|
|
43
|
+
exclude: ['localId']
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Whether `attrName` is a meaningful change for `nodeTypeName`. False if no rule.
|
|
48
|
+
export var isDiffableAttr = function isDiffableAttr(nodeTypeName, attrName) {
|
|
49
|
+
var rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
50
|
+
if (!rule) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return 'include' in rule ? rule.include.includes(attrName) : !rule.exclude.includes(attrName);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// The fixed include list for a node type, or `undefined` for no/exclude rule.
|
|
57
|
+
// Use where a node-independent attr list is needed (e.g. the inline node map).
|
|
58
|
+
export var getIncludedDiffableAttrs = function getIncludedDiffableAttrs(nodeTypeName) {
|
|
59
|
+
var rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
60
|
+
return rule && 'include' in rule ? rule.include : undefined;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Like `getIncludedDiffableAttrs` but asserts the include rule exists, so a
|
|
64
|
+
// consumer relying on a fixed list fails loudly if the rule is removed or
|
|
65
|
+
// switched to an exclude rule rather than silently drifting.
|
|
66
|
+
export var getRequiredIncludedDiffableAttrs = function getRequiredIncludedDiffableAttrs(nodeTypeName) {
|
|
67
|
+
var attrs = getIncludedDiffableAttrs(nodeTypeName);
|
|
68
|
+
if (!attrs) {
|
|
69
|
+
throw new Error("diffableAttrs: expected an include rule for \"".concat(nodeTypeName, "\""));
|
|
70
|
+
}
|
|
71
|
+
return attrs;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Concrete attr names to diff for a node instance, honouring include and exclude
|
|
76
|
+
* rules; `undefined` when the type has no rule. Exclude rules read the node's
|
|
77
|
+
* attrs and sort them, since key order isn't stable across the two diffed docs.
|
|
78
|
+
*/
|
|
79
|
+
export var getDiffableAttrNames = function getDiffableAttrNames(nodeTypeName, attrs) {
|
|
80
|
+
var rule = DIFFABLE_ATTRS_BY_NODE_TYPE[nodeTypeName];
|
|
81
|
+
if (!rule) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
if ('include' in rule) {
|
|
85
|
+
return rule.include;
|
|
86
|
+
}
|
|
87
|
+
return Object.keys(attrs).filter(function (name) {
|
|
88
|
+
return !rule.exclude.includes(name);
|
|
89
|
+
}).sort();
|
|
90
|
+
};
|
|
@@ -5,27 +5,18 @@ import { SetAttrsStep } from '@atlaskit/adf-schema/steps';
|
|
|
5
5
|
import { isExperimentEnabled } from '@atlaskit/editor-common/deprecated-platform-feature-experiments';
|
|
6
6
|
import { AttrStep } from '@atlaskit/editor-prosemirror/transform';
|
|
7
7
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
8
|
+
import { getRequiredIncludedDiffableAttrs, isDiffableAttr } from './diffableAttrs';
|
|
8
9
|
var filterUndefined = function filterUndefined(x) {
|
|
9
10
|
return !!x;
|
|
10
11
|
};
|
|
11
12
|
|
|
12
|
-
//
|
|
13
|
-
var mediaAttrs =
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
var
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
var taskItemAttrs = ['state'];
|
|
20
|
-
|
|
21
|
-
// Attributes that indicate an emoji change
|
|
22
|
-
var emojiAttrs = ['shortName', 'id', 'text'];
|
|
23
|
-
|
|
24
|
-
// Attributes that indicate a mention change (who is mentioned)
|
|
25
|
-
var mentionAttrs = ['id', 'text'];
|
|
26
|
-
|
|
27
|
-
// Attributes that indicate a status change (label or colour)
|
|
28
|
-
var statusAttrs = ['text', 'color'];
|
|
13
|
+
// Attr lists sourced from the shared `diffableAttrs` map (shared with the encoder).
|
|
14
|
+
var mediaAttrs = getRequiredIncludedDiffableAttrs('media');
|
|
15
|
+
var dateAttrs = getRequiredIncludedDiffableAttrs('date');
|
|
16
|
+
var taskItemAttrs = getRequiredIncludedDiffableAttrs('taskItem');
|
|
17
|
+
var emojiAttrs = getRequiredIncludedDiffableAttrs('emoji');
|
|
18
|
+
var mentionAttrs = getRequiredIncludedDiffableAttrs('mention');
|
|
19
|
+
var statusAttrs = getRequiredIncludedDiffableAttrs('status');
|
|
29
20
|
|
|
30
21
|
// Map of node type name → the attrs that represent a meaningful content change for that node
|
|
31
22
|
var inlineNodeAttrMap = {
|
|
@@ -38,10 +29,8 @@ var isInlineAttrChangeNodeName = function isInlineAttrChangeNodeName(nodeName) {
|
|
|
38
29
|
return nodeName in inlineNodeAttrMap;
|
|
39
30
|
};
|
|
40
31
|
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// Extension node type names
|
|
32
|
+
// Extension node type names. Their "any attr except localId" exclude rule lives
|
|
33
|
+
// in the shared `diffableAttrs` map and is applied via `isDiffableAttr` below.
|
|
45
34
|
var extensionNodeNames = ['extension', 'inlineExtension', 'bodiedExtension'];
|
|
46
35
|
var getStepAttrs = function getStepAttrs(step) {
|
|
47
36
|
if (step instanceof AttrStep) {
|
|
@@ -111,9 +100,9 @@ export var getAttrChangeRanges = function getAttrChangeRanges(doc, steps, origin
|
|
|
111
100
|
};
|
|
112
101
|
}
|
|
113
102
|
|
|
114
|
-
// extension nodes: any
|
|
103
|
+
// extension nodes: highlight on any diffable attr change (exclude rule in shared map)
|
|
115
104
|
if (nodeAtPos && extensionNodeNames.includes(nodeAtPos.type.name) && stepAttrs.some(function (v) {
|
|
116
|
-
return
|
|
105
|
+
return isDiffableAttr(nodeAtPos.type.name, v);
|
|
117
106
|
})) {
|
|
118
107
|
var isInline = nodeAtPos.type.name === 'inlineExtension';
|
|
119
108
|
return {
|
|
@@ -12,7 +12,7 @@ import { enforceCustomStepRegisters } from './enforceCustomStepRegisters';
|
|
|
12
12
|
import { getScrollableDecorations } from './getScrollableDecorations';
|
|
13
13
|
import { isExtendedEnabled } from './isExtendedEnabled';
|
|
14
14
|
import { NodeViewSerializer } from './NodeViewSerializer';
|
|
15
|
-
import {
|
|
15
|
+
import { scrollToDecoration } from './scrollToDiff';
|
|
16
16
|
export var showDiffPluginKey = new PluginKey('showDiffPlugin');
|
|
17
17
|
export var createPlugin = function createPlugin(config, getIntl, api, onEditorView) {
|
|
18
18
|
enforceCustomStepRegisters();
|
|
@@ -180,7 +180,7 @@ export var createPlugin = function createPlugin(config, getIntl, api, onEditorVi
|
|
|
180
180
|
if (pluginState !== null && pluginState !== void 0 && pluginState.scrollIntoView && isExtendedEnabled(pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType)) {
|
|
181
181
|
var _cancelPendingScrollT;
|
|
182
182
|
(_cancelPendingScrollT = cancelPendingScrollToDecoration) === null || _cancelPendingScrollT === void 0 || _cancelPendingScrollT();
|
|
183
|
-
cancelPendingScrollToDecoration =
|
|
183
|
+
cancelPendingScrollToDecoration = scrollToDecoration(view, getScrollableDecorations(pluginState.decorations, view.state.doc, pluginState === null || pluginState === void 0 ? void 0 : pluginState.diffType));
|
|
184
184
|
|
|
185
185
|
// Reset the flag so we don't scroll again on subsequent updates
|
|
186
186
|
view.dispatch(view.state.tr.setMeta(showDiffPluginKey, {
|
|
@@ -201,7 +201,7 @@ export var createPlugin = function createPlugin(config, getIntl, api, onEditorVi
|
|
|
201
201
|
// avoid a circular dependency; expand picks up the meta if loaded (no-op if not).
|
|
202
202
|
api === null || api === void 0 || api.core.actions.execute(toggleExpandRange(activeDecoration.from, activeDecoration.to, true));
|
|
203
203
|
}
|
|
204
|
-
cancelPendingScrollToDecoration =
|
|
204
|
+
cancelPendingScrollToDecoration = scrollToDecoration(view, scrollableDecorations, pluginState.activeIndex);
|
|
205
205
|
}
|
|
206
206
|
},
|
|
207
207
|
destroy: function destroy() {
|
|
@@ -35,29 +35,13 @@ function scrollToSelection(node) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Schedules scrolling to the
|
|
39
|
-
*
|
|
40
|
-
* it simply scrolls to bring the first decoration into view.
|
|
41
|
-
*
|
|
42
|
-
* The caller must pass the list of scrollable decorations as produced by
|
|
43
|
-
* `getScrollableDecorations`, which is filtered (non-scrollable decorations
|
|
44
|
-
* removed) and sorted by document position. This guarantees "first" means the
|
|
45
|
-
* topmost scrollable diff in the document rather than an arbitrary decoration
|
|
46
|
-
* from the raw `DecorationSet` ordering. This is delegated to
|
|
47
|
-
* `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
|
|
48
|
-
*
|
|
49
|
-
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
50
|
-
*/
|
|
51
|
-
export var scrollToFirstDecoration = function scrollToFirstDecoration(view, scrollableDecorations) {
|
|
52
|
-
return scrollToActiveDecoration(view, scrollableDecorations, 0);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Schedules scrolling to the decoration at the given index after the next frame.
|
|
38
|
+
* Schedules scrolling to the decoration at the given index after the next frame. Defaults to the
|
|
39
|
+
* first decoration when no index is provided.
|
|
57
40
|
*
|
|
58
41
|
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
59
42
|
*/
|
|
60
|
-
export var
|
|
43
|
+
export var scrollToDecoration = function scrollToDecoration(view, decorations) {
|
|
44
|
+
var activeIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
|
|
61
45
|
var decoration = decorations[activeIndex];
|
|
62
46
|
if (!decoration) {
|
|
63
47
|
return function () {};
|
|
@@ -1,7 +1,2 @@
|
|
|
1
1
|
import type { TokenEncoder } from 'prosemirror-changeset';
|
|
2
|
-
/**
|
|
3
|
-
* Identical to the library default except that allow-listed node types encode
|
|
4
|
-
* their allow-listed attributes into the open token. Characters and node-end
|
|
5
|
-
* tokens are left as-is, hence the `string | number` token type.
|
|
6
|
-
*/
|
|
7
2
|
export declare const attrAwareTokenEncoder: TokenEncoder<string | number>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for which attributes of a node type represent a
|
|
3
|
+
* meaningful change. Shared by `attrAwareTokenEncoder` (changeset detection)
|
|
4
|
+
* and `getAttrChangeRanges` (decoration rendering) so the two stages agree.
|
|
5
|
+
*/
|
|
6
|
+
export type DiffableAttrsRule = {
|
|
7
|
+
include: readonly string[];
|
|
8
|
+
} | {
|
|
9
|
+
exclude: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
export declare const DIFFABLE_ATTRS_BY_NODE_TYPE: Record<string, DiffableAttrsRule>;
|
|
12
|
+
export declare const isDiffableAttr: (nodeTypeName: string, attrName: string) => boolean;
|
|
13
|
+
export declare const getIncludedDiffableAttrs: (nodeTypeName: string) => readonly string[] | undefined;
|
|
14
|
+
export declare const getRequiredIncludedDiffableAttrs: (nodeTypeName: string) => readonly string[];
|
|
15
|
+
/**
|
|
16
|
+
* Concrete attr names to diff for a node instance, honouring include and exclude
|
|
17
|
+
* rules; `undefined` when the type has no rule. Exclude rules read the node's
|
|
18
|
+
* attrs and sort them, since key order isn't stable across the two diffed docs.
|
|
19
|
+
*/
|
|
20
|
+
export declare const getDiffableAttrNames: (nodeTypeName: string, attrs: Record<string, unknown>) => readonly string[] | undefined;
|
|
@@ -1,22 +1,8 @@
|
|
|
1
1
|
import type { EditorView, Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
2
2
|
/**
|
|
3
|
-
* Schedules scrolling to the
|
|
4
|
-
*
|
|
5
|
-
* it simply scrolls to bring the first decoration into view.
|
|
6
|
-
*
|
|
7
|
-
* The caller must pass the list of scrollable decorations as produced by
|
|
8
|
-
* `getScrollableDecorations`, which is filtered (non-scrollable decorations
|
|
9
|
-
* removed) and sorted by document position. This guarantees "first" means the
|
|
10
|
-
* topmost scrollable diff in the document rather than an arbitrary decoration
|
|
11
|
-
* from the raw `DecorationSet` ordering. This is delegated to
|
|
12
|
-
* `scrollToActiveDecoration` at index 0 so both scroll paths behave identically.
|
|
13
|
-
*
|
|
14
|
-
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
15
|
-
*/
|
|
16
|
-
export declare const scrollToFirstDecoration: (view: EditorView, scrollableDecorations: Decoration[]) => (() => void);
|
|
17
|
-
/**
|
|
18
|
-
* Schedules scrolling to the decoration at the given index after the next frame.
|
|
3
|
+
* Schedules scrolling to the decoration at the given index after the next frame. Defaults to the
|
|
4
|
+
* first decoration when no index is provided.
|
|
19
5
|
*
|
|
20
6
|
* @returns A function that cancels the scheduled `requestAnimationFrame` if it has not run yet.
|
|
21
7
|
*/
|
|
22
|
-
export declare const
|
|
8
|
+
export declare const scrollToDecoration: (view: EditorView, decorations: Decoration[], activeIndex?: number) => (() => void);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-show-diff",
|
|
3
|
-
"version": "13.0.
|
|
3
|
+
"version": "13.0.8",
|
|
4
4
|
"description": "ShowDiff plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@atlaskit/editor-prosemirror": "^8.0.0",
|
|
28
28
|
"@atlaskit/editor-tables": "^3.0.0",
|
|
29
29
|
"@atlaskit/platform-feature-flags": "^2.1.0",
|
|
30
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
30
|
+
"@atlaskit/tmp-editor-statsig": "^149.0.0",
|
|
31
31
|
"@atlaskit/tokens": "^16.7.0",
|
|
32
32
|
"@babel/runtime": "^7.0.0",
|
|
33
33
|
"@compiled/react": "^1.0.2",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@atlaskit/adf-utils": "^20.6.0",
|
|
40
|
-
"@atlaskit/button": "^25.
|
|
40
|
+
"@atlaskit/button": "^25.1.0",
|
|
41
41
|
"@atlaskit/css": "^1.0.0",
|
|
42
42
|
"@atlaskit/dropdown-menu": "^18.0.0",
|
|
43
|
-
"@atlaskit/editor-core": "^224.
|
|
43
|
+
"@atlaskit/editor-core": "^224.1.0",
|
|
44
44
|
"@atlaskit/editor-json-transformer": "^9.4.0",
|
|
45
45
|
"@atlaskit/form": "^17.0.0",
|
|
46
46
|
"@atlaskit/primitives": "^22.2.0",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"react-intl": "^7.0.0"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@atlaskit/editor-common": "^119.
|
|
56
|
+
"@atlaskit/editor-common": "^119.8.0",
|
|
57
57
|
"react": "^18.2.0 || ^19.2.0"
|
|
58
58
|
},
|
|
59
59
|
"techstack": {
|