@atlaskit/editor-plugin-show-diff 14.5.1 → 15.0.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 +34 -6
- package/dist/cjs/pm-plugins/calculateDiff/calculateDiffDecorations.js +3 -9
- package/dist/cjs/pm-plugins/calculateDiff/smart/classifySmartChanges.js +1 -2
- package/dist/cjs/pm-plugins/decorations/colorSchemes/attributions.js +21 -4
- package/dist/cjs/pm-plugins/decorations/colorSchemes/factory.js +6 -56
- package/dist/cjs/pm-plugins/decorations/colorSchemes/schemes.js +6 -20
- package/dist/cjs/pm-plugins/decorations/colorSchemes/types.js +4 -6
- package/dist/cjs/pm-plugins/decorations/createChangedRowDecorationWidgets.js +1 -2
- package/dist/cjs/pm-plugins/decorations/createContributorTagWidget.js +14 -29
- package/dist/cjs/pm-plugins/decorations/createInlineChangedDecoration.js +1 -2
- package/dist/cjs/pm-plugins/decorations/utils/getAttrChangeRanges.js +2 -25
- package/dist/cjs/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +1 -2
- package/dist/cjs/pm-plugins/main.js +4 -4
- package/dist/cjs/showDiffPlugin.js +4 -5
- package/dist/cjs/ui/ContributorTag/buildContributorTagDom.js +122 -0
- package/dist/cjs/ui/ContributorTag/contributorAvatarRenderer.js +140 -0
- package/dist/cjs/ui/ContributorTag/contributorTagController.js +500 -0
- package/dist/cjs/ui/ContributorTag/contributorTagIcons.js +46 -0
- package/dist/es2019/pm-plugins/calculateDiff/calculateDiffDecorations.js +3 -5
- package/dist/es2019/pm-plugins/calculateDiff/smart/classifySmartChanges.js +1 -2
- package/dist/es2019/pm-plugins/decorations/colorSchemes/attributions.js +20 -3
- package/dist/es2019/pm-plugins/decorations/colorSchemes/factory.js +6 -55
- package/dist/es2019/pm-plugins/decorations/colorSchemes/schemes.js +14 -26
- package/dist/es2019/pm-plugins/decorations/colorSchemes/types.js +4 -6
- package/dist/es2019/pm-plugins/decorations/createChangedRowDecorationWidgets.js +1 -2
- package/dist/es2019/pm-plugins/decorations/createContributorTagWidget.js +16 -30
- package/dist/es2019/pm-plugins/decorations/createInlineChangedDecoration.js +1 -2
- package/dist/es2019/pm-plugins/decorations/utils/getAttrChangeRanges.js +2 -17
- package/dist/es2019/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +1 -2
- package/dist/es2019/pm-plugins/main.js +4 -4
- package/dist/es2019/showDiffPlugin.js +4 -5
- package/dist/es2019/ui/ContributorTag/buildContributorTagDom.js +110 -0
- package/dist/es2019/ui/ContributorTag/contributorAvatarRenderer.js +136 -0
- package/dist/es2019/ui/ContributorTag/contributorTagController.js +403 -0
- package/dist/es2019/ui/ContributorTag/contributorTagIcons.js +40 -0
- package/dist/esm/pm-plugins/calculateDiff/calculateDiffDecorations.js +3 -9
- package/dist/esm/pm-plugins/calculateDiff/smart/classifySmartChanges.js +1 -2
- package/dist/esm/pm-plugins/decorations/colorSchemes/attributions.js +21 -4
- package/dist/esm/pm-plugins/decorations/colorSchemes/factory.js +6 -57
- package/dist/esm/pm-plugins/decorations/colorSchemes/schemes.js +7 -20
- package/dist/esm/pm-plugins/decorations/colorSchemes/types.js +4 -6
- package/dist/esm/pm-plugins/decorations/createChangedRowDecorationWidgets.js +1 -2
- package/dist/esm/pm-plugins/decorations/createContributorTagWidget.js +15 -30
- package/dist/esm/pm-plugins/decorations/createInlineChangedDecoration.js +1 -2
- package/dist/esm/pm-plugins/decorations/utils/getAttrChangeRanges.js +2 -25
- package/dist/esm/pm-plugins/decorations/utils/wrapBlockNodeViewStyles.js +1 -2
- package/dist/esm/pm-plugins/main.js +4 -4
- package/dist/esm/showDiffPlugin.js +4 -5
- package/dist/esm/ui/ContributorTag/buildContributorTagDom.js +115 -0
- package/dist/esm/ui/ContributorTag/contributorAvatarRenderer.js +135 -0
- package/dist/esm/ui/ContributorTag/contributorTagController.js +493 -0
- package/dist/esm/ui/ContributorTag/contributorTagIcons.js +40 -0
- package/dist/types/pm-plugins/decorations/colorSchemes/attributions.d.ts +3 -3
- package/dist/types/pm-plugins/decorations/colorSchemes/factory.d.ts +2 -2
- package/dist/types/pm-plugins/decorations/colorSchemes/schemes.d.ts +1 -1
- package/dist/types/pm-plugins/decorations/colorSchemes/types.d.ts +13 -16
- package/dist/types/pm-plugins/decorations/createContributorTagWidget.d.ts +11 -9
- package/dist/types/pm-plugins/main.d.ts +1 -2
- package/dist/types/ui/ContributorTag/buildContributorTagDom.d.ts +25 -0
- package/dist/types/ui/ContributorTag/contributorAvatarRenderer.d.ts +22 -0
- package/dist/types/ui/ContributorTag/contributorTagController.d.ts +85 -0
- package/dist/types/ui/ContributorTag/contributorTagIcons.d.ts +12 -0
- package/package.json +9 -13
- package/dist/cjs/ui/ContributorTag/ContributorTag.compiled.css +0 -29
- package/dist/cjs/ui/ContributorTag/ContributorTag.js +0 -383
- package/dist/cjs/ui/ContributorTag/ContributorTagWidget.js +0 -50
- package/dist/es2019/ui/ContributorTag/ContributorTag.compiled.css +0 -29
- package/dist/es2019/ui/ContributorTag/ContributorTag.js +0 -312
- package/dist/es2019/ui/ContributorTag/ContributorTagWidget.js +0 -43
- package/dist/esm/ui/ContributorTag/ContributorTag.compiled.css +0 -29
- package/dist/esm/ui/ContributorTag/ContributorTag.js +0 -375
- package/dist/esm/ui/ContributorTag/ContributorTagWidget.js +0 -43
- package/dist/types/ui/ContributorTag/ContributorTag.d.ts +0 -37
- package/dist/types/ui/ContributorTag/ContributorTagWidget.d.ts +0 -22
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.ContributorTagController = void 0;
|
|
8
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
|
+
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
10
|
+
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
11
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
12
|
+
var _bindEventListener = require("bind-event-listener");
|
|
13
|
+
var _vanillaTooltip = require("@atlaskit/editor-common/vanilla-tooltip");
|
|
14
|
+
var _factory = require("../../pm-plugins/decorations/colorSchemes/factory");
|
|
15
|
+
var _schemes = require("../../pm-plugins/decorations/colorSchemes/schemes");
|
|
16
|
+
var _buildContributorTagDom = require("./buildContributorTagDom");
|
|
17
|
+
var _contributorAvatarRenderer = require("./contributorAvatarRenderer");
|
|
18
|
+
var _messages = require("./messages");
|
|
19
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
20
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
21
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
22
|
+
var getContributorName = function getContributorName(contributor, formatMessage) {
|
|
23
|
+
var _contributor$name, _contributor$agentKin;
|
|
24
|
+
var name = (_contributor$name = contributor.name) === null || _contributor$name === void 0 ? void 0 : _contributor$name.trim();
|
|
25
|
+
if (name) {
|
|
26
|
+
return name;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// `agentKind` defaults to `'external'` per the plugin type contract; an unidentified external
|
|
30
|
+
// agent falls back to a generic label rather than rendering a nameless tag.
|
|
31
|
+
if (contributor.kind === 'agent' && ((_contributor$agentKin = contributor.agentKind) !== null && _contributor$agentKin !== void 0 ? _contributor$agentKin : 'external') === 'external') {
|
|
32
|
+
return formatMessage(_messages.contributorTagMessages.externalAgentName);
|
|
33
|
+
}
|
|
34
|
+
return '';
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Resolves the tag's accent from the same scheme value and token maps the highlight used, so the
|
|
39
|
+
* two can never drift.
|
|
40
|
+
*/
|
|
41
|
+
var getTagAccent = function getTagAccent(_ref) {
|
|
42
|
+
var colorScheme = _ref.colorScheme,
|
|
43
|
+
isActive = _ref.isActive,
|
|
44
|
+
isInserted = _ref.isInserted;
|
|
45
|
+
var colors = _schemes.colorSchemeRegistry[colorScheme !== null && colorScheme !== void 0 ? colorScheme : 'standard'];
|
|
46
|
+
var accent = isInserted ? isActive ? colors.insertActiveColor : colors.insertColor : isActive ? colors.deleteActiveColor : colors.deleteColor;
|
|
47
|
+
return (0, _factory.getAccentTokens)(accent);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** The line the change starts on: topmost of its fragments, leftmost among those. */
|
|
51
|
+
var findChangeStartLine = function findChangeStartLine(highlights) {
|
|
52
|
+
var start;
|
|
53
|
+
var _iterator = _createForOfIteratorHelper(highlights),
|
|
54
|
+
_step;
|
|
55
|
+
try {
|
|
56
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
57
|
+
var highlight = _step.value;
|
|
58
|
+
var _iterator2 = _createForOfIteratorHelper(highlight.getClientRects()),
|
|
59
|
+
_step2;
|
|
60
|
+
try {
|
|
61
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
62
|
+
var rect = _step2.value;
|
|
63
|
+
if (!start || rect.top < start.top - 1 || Math.abs(rect.top - start.top) <= 1 && rect.left < start.left) {
|
|
64
|
+
start = rect;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
_iterator2.e(err);
|
|
69
|
+
} finally {
|
|
70
|
+
_iterator2.f();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
_iterator.e(err);
|
|
75
|
+
} finally {
|
|
76
|
+
_iterator.f();
|
|
77
|
+
}
|
|
78
|
+
return start;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/** The contributors a tag draws, keyed so a republished model can be recognised as the same pair. */
|
|
82
|
+
var identityOf = function identityOf(model) {
|
|
83
|
+
return [model === null || model === void 0 ? void 0 : model.contributor, model === null || model === void 0 ? void 0 : model.connectedContributor].map(function (contributor) {
|
|
84
|
+
var _contributor$agentKin2, _contributor$avatarUr;
|
|
85
|
+
return contributor ? "".concat(contributor.kind, ":").concat((_contributor$agentKin2 = contributor.agentKind) !== null && _contributor$agentKin2 !== void 0 ? _contributor$agentKin2 : '', ":").concat(contributor.name, ":").concat((_contributor$avatarUr = contributor.avatarUrl) !== null && _contributor$avatarUr !== void 0 ? _contributor$avatarUr : '') : '';
|
|
86
|
+
}).join('|');
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* A small tag naming who made one diff, drawn into the host element its decoration places on the
|
|
91
|
+
* change's first character and positioned above it.
|
|
92
|
+
*
|
|
93
|
+
* The model is resolved from plugin state here rather than passed in, because ProseMirror skips
|
|
94
|
+
* `toDOM` for a widget it reuses: stepping to the next change republishes the tags without redrawing
|
|
95
|
+
* their hosts, so anything the tag reacts to — `isActive` above all — has to arrive through a
|
|
96
|
+
* subscription of its own (EDITOR-8702). Draws nothing when this diff has no tag, which is the case
|
|
97
|
+
* for the deleted half of a replacement: that half is folded into the inline tag.
|
|
98
|
+
*/
|
|
99
|
+
var ContributorTagController = exports.ContributorTagController = /*#__PURE__*/function () {
|
|
100
|
+
function ContributorTagController(options) {
|
|
101
|
+
(0, _classCallCheck2.default)(this, ContributorTagController);
|
|
102
|
+
(0, _defineProperty2.default)(this, "avatars", []);
|
|
103
|
+
/** Guards a deferred bind that a later model change has already superseded. */
|
|
104
|
+
(0, _defineProperty2.default)(this, "bindToken", 0);
|
|
105
|
+
(0, _defineProperty2.default)(this, "fullLabel", '');
|
|
106
|
+
(0, _defineProperty2.default)(this, "isDestroyed", false);
|
|
107
|
+
(0, _defineProperty2.default)(this, "isHighlightHovered", false);
|
|
108
|
+
(0, _defineProperty2.default)(this, "isHoveredOrFocused", false);
|
|
109
|
+
(0, _defineProperty2.default)(this, "unbindHighlights", []);
|
|
110
|
+
this.options = options;
|
|
111
|
+
}
|
|
112
|
+
return (0, _createClass2.default)(ContributorTagController, [{
|
|
113
|
+
key: "mount",
|
|
114
|
+
value: function mount(host) {
|
|
115
|
+
var _this$options$api$sho,
|
|
116
|
+
_this$options$api,
|
|
117
|
+
_this = this,
|
|
118
|
+
_sharedState$currentS;
|
|
119
|
+
this.host = host;
|
|
120
|
+
var _ref2 = (_this$options$api$sho = (_this$options$api = this.options.api) === null || _this$options$api === void 0 ? void 0 : _this$options$api.showDiff) !== null && _this$options$api$sho !== void 0 ? _this$options$api$sho : {},
|
|
121
|
+
sharedState = _ref2.sharedState;
|
|
122
|
+
this.unsubscribe = sharedState === null || sharedState === void 0 ? void 0 : sharedState.onChange(function (_ref3) {
|
|
123
|
+
var nextSharedState = _ref3.nextSharedState;
|
|
124
|
+
_this.applyModel(_this.findModel(nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.contributorTags));
|
|
125
|
+
});
|
|
126
|
+
this.applyModel(this.findModel(sharedState === null || sharedState === void 0 || (_sharedState$currentS = sharedState.currentState()) === null || _sharedState$currentS === void 0 ? void 0 : _sharedState$currentS.contributorTags));
|
|
127
|
+
}
|
|
128
|
+
}, {
|
|
129
|
+
key: "destroy",
|
|
130
|
+
value: function destroy() {
|
|
131
|
+
var _this$unsubscribe;
|
|
132
|
+
this.isDestroyed = true;
|
|
133
|
+
(_this$unsubscribe = this.unsubscribe) === null || _this$unsubscribe === void 0 || _this$unsubscribe.call(this);
|
|
134
|
+
this.unsubscribe = undefined;
|
|
135
|
+
this.teardownTag();
|
|
136
|
+
this.host = undefined;
|
|
137
|
+
}
|
|
138
|
+
}, {
|
|
139
|
+
key: "findModel",
|
|
140
|
+
value: function findModel(tags) {
|
|
141
|
+
var _this2 = this;
|
|
142
|
+
return tags === null || tags === void 0 ? void 0 : tags.find(function (tag) {
|
|
143
|
+
return tag.diffId === _this2.options.diffId;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}, {
|
|
147
|
+
key: "applyModel",
|
|
148
|
+
value: function applyModel(next) {
|
|
149
|
+
if (this.isDestroyed || next === this.model) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
var previous = this.model;
|
|
153
|
+
this.model = next;
|
|
154
|
+
var host = this.host;
|
|
155
|
+
if (!next || !host) {
|
|
156
|
+
this.teardownTag();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (!this.dom) {
|
|
160
|
+
this.dom = (0, _buildContributorTagDom.buildContributorTagDom)(host.ownerDocument);
|
|
161
|
+
this.bindTag(this.dom);
|
|
162
|
+
host.appendChild(this.dom.root);
|
|
163
|
+
}
|
|
164
|
+
if (!previous || identityOf(previous) !== identityOf(next)) {
|
|
165
|
+
this.renderAvatars(next);
|
|
166
|
+
}
|
|
167
|
+
this.renderLabels(next);
|
|
168
|
+
this.applyAccent(next);
|
|
169
|
+
this.syncHighlightBindings(next);
|
|
170
|
+
this.applyStyles();
|
|
171
|
+
this.syncTooltip();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Takes the tag's DOM down, keeping the subscription so a republished model redraws it. */
|
|
175
|
+
}, {
|
|
176
|
+
key: "teardownTag",
|
|
177
|
+
value: function teardownTag() {
|
|
178
|
+
var _this$unbindTag, _this$tooltip, _this$dom;
|
|
179
|
+
this.unbindHighlightState();
|
|
180
|
+
this.boundSelector = undefined;
|
|
181
|
+
(_this$unbindTag = this.unbindTag) === null || _this$unbindTag === void 0 || _this$unbindTag.call(this);
|
|
182
|
+
this.unbindTag = undefined;
|
|
183
|
+
(_this$tooltip = this.tooltip) === null || _this$tooltip === void 0 || _this$tooltip.destroy();
|
|
184
|
+
this.tooltip = undefined;
|
|
185
|
+
this.tooltipContent = undefined;
|
|
186
|
+
this.avatars.forEach(function (avatar) {
|
|
187
|
+
return avatar.destroy();
|
|
188
|
+
});
|
|
189
|
+
this.avatars = [];
|
|
190
|
+
(_this$dom = this.dom) === null || _this$dom === void 0 || _this$dom.root.remove();
|
|
191
|
+
this.dom = undefined;
|
|
192
|
+
this.maxWidthPx = undefined;
|
|
193
|
+
this.isHoveredOrFocused = false;
|
|
194
|
+
// Both hover flags belong to the listeners just unbound: a republished model rebinds them, and
|
|
195
|
+
// stale `true` would draw the tag as visible with the pointer nowhere near it.
|
|
196
|
+
this.isHighlightHovered = false;
|
|
197
|
+
}
|
|
198
|
+
}, {
|
|
199
|
+
key: "bindTag",
|
|
200
|
+
value: function bindTag(_ref4) {
|
|
201
|
+
var _this3 = this;
|
|
202
|
+
var tag = _ref4.tag;
|
|
203
|
+
var setHoveredOrFocused = function setHoveredOrFocused(isHoveredOrFocused) {
|
|
204
|
+
return function () {
|
|
205
|
+
_this3.isHoveredOrFocused = isHoveredOrFocused;
|
|
206
|
+
_this3.applyStyles();
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
this.unbindTag = (0, _bindEventListener.bindAll)(tag, [{
|
|
210
|
+
type: 'mouseover',
|
|
211
|
+
listener: setHoveredOrFocused(true)
|
|
212
|
+
}, {
|
|
213
|
+
type: 'mouseout',
|
|
214
|
+
listener: setHoveredOrFocused(false)
|
|
215
|
+
}, {
|
|
216
|
+
type: 'focus',
|
|
217
|
+
listener: setHoveredOrFocused(true)
|
|
218
|
+
}, {
|
|
219
|
+
type: 'blur',
|
|
220
|
+
listener: setHoveredOrFocused(false)
|
|
221
|
+
}]);
|
|
222
|
+
}
|
|
223
|
+
}, {
|
|
224
|
+
key: "renderAvatars",
|
|
225
|
+
value: function renderAvatars(model) {
|
|
226
|
+
var _this$dom2,
|
|
227
|
+
_this4 = this;
|
|
228
|
+
var _ref5 = (_this$dom2 = this.dom) !== null && _this$dom2 !== void 0 ? _this$dom2 : {},
|
|
229
|
+
container = _ref5.avatars;
|
|
230
|
+
if (!container) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
this.avatars.forEach(function (avatar) {
|
|
234
|
+
return avatar.destroy();
|
|
235
|
+
});
|
|
236
|
+
this.avatars = [];
|
|
237
|
+
var doc = container.ownerDocument;
|
|
238
|
+
var _this$accentOf = this.accentOf(model),
|
|
239
|
+
background = _this$accentOf.background;
|
|
240
|
+
var isPrimaryAgent = model.contributor.kind === 'agent';
|
|
241
|
+
// Resolved by `kind` so a reverse `connectedTo` (an attributed user pointing at its agent)
|
|
242
|
+
// renders identically.
|
|
243
|
+
var user = isPrimaryAgent ? model.connectedContributor : model.contributor;
|
|
244
|
+
var agent = isPrimaryAgent ? model.contributor : model.connectedContributor;
|
|
245
|
+
|
|
246
|
+
// Hand-rolled avatar stack: `@atlaskit/avatar-group` cannot render below 24px and the tag uses
|
|
247
|
+
// 16px avatars.
|
|
248
|
+
var stack = model.connectedContributor ? [].concat((0, _toConsumableArray2.default)(user ? [{
|
|
249
|
+
contributor: user,
|
|
250
|
+
stackIndex: 1
|
|
251
|
+
}] : []), (0, _toConsumableArray2.default)(agent ? [{
|
|
252
|
+
contributor: agent,
|
|
253
|
+
stackIndex: 0
|
|
254
|
+
}] : [])) : [{
|
|
255
|
+
contributor: model.contributor
|
|
256
|
+
}];
|
|
257
|
+
stack.forEach(function (_ref6, index) {
|
|
258
|
+
var contributor = _ref6.contributor,
|
|
259
|
+
stackIndex = _ref6.stackIndex;
|
|
260
|
+
var avatar = (0, _contributorAvatarRenderer.contributorAvatarRenderer)({
|
|
261
|
+
contributor: contributor,
|
|
262
|
+
doc: doc,
|
|
263
|
+
ringColor: background,
|
|
264
|
+
stackIndex: stackIndex
|
|
265
|
+
});
|
|
266
|
+
if (index < stack.length - 1) {
|
|
267
|
+
// Overlaps the leading avatar of a connected pair onto the one it sits in front of.
|
|
268
|
+
avatar.element.style.setProperty('margin-inline-end', "var(--ds-space-negative-050, -4px)");
|
|
269
|
+
}
|
|
270
|
+
container.appendChild(avatar.element);
|
|
271
|
+
_this4.avatars.push(avatar);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}, {
|
|
275
|
+
key: "renderLabels",
|
|
276
|
+
value: function renderLabels(model) {
|
|
277
|
+
if (!this.dom) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
var _this$options$getIntl = this.options.getIntl(),
|
|
281
|
+
formatMessage = _this$options$getIntl.formatMessage;
|
|
282
|
+
var isPrimaryAgent = model.contributor.kind === 'agent';
|
|
283
|
+
var primaryName = getContributorName(model.contributor, formatMessage);
|
|
284
|
+
var secondaryName = model.connectedContributor ? getContributorName(model.connectedContributor, formatMessage) : '';
|
|
285
|
+
var agentName = isPrimaryAgent ? primaryName : secondaryName;
|
|
286
|
+
var userName = isPrimaryAgent ? secondaryName : primaryName;
|
|
287
|
+
|
|
288
|
+
// A connected pair only ever shows the agent; the pair is spelled out in the label, because
|
|
289
|
+
// the tag is never wider than the highlight.
|
|
290
|
+
this.dom.name.textContent = model.connectedContributor ? agentName || primaryName : primaryName;
|
|
291
|
+
this.fullLabel = model.connectedContributor ? formatMessage(_messages.contributorTagMessages.changedByConnected, {
|
|
292
|
+
agentName: agentName,
|
|
293
|
+
userName: userName
|
|
294
|
+
}) : formatMessage(_messages.contributorTagMessages.changedBy, {
|
|
295
|
+
name: primaryName
|
|
296
|
+
});
|
|
297
|
+
this.dom.srLabel.textContent = this.fullLabel;
|
|
298
|
+
}
|
|
299
|
+
}, {
|
|
300
|
+
key: "accentOf",
|
|
301
|
+
value: function accentOf(model) {
|
|
302
|
+
var _model$isInserted;
|
|
303
|
+
return getTagAccent({
|
|
304
|
+
colorScheme: model.colorScheme,
|
|
305
|
+
isActive: Boolean(model.isActive),
|
|
306
|
+
isInserted: (_model$isInserted = model.isInserted) !== null && _model$isInserted !== void 0 ? _model$isInserted : true
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}, {
|
|
310
|
+
key: "applyAccent",
|
|
311
|
+
value: function applyAccent(model) {
|
|
312
|
+
if (!this.dom) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
var accent = this.accentOf(model);
|
|
316
|
+
this.dom.tag.style.setProperty('background-color', accent.background);
|
|
317
|
+
this.dom.tag.style.setProperty('border-bottom-color', accent.border);
|
|
318
|
+
this.dom.name.style.setProperty('color', accent.text);
|
|
319
|
+
this.avatars.forEach(function (avatar) {
|
|
320
|
+
return avatar.setRingColor(accent.background);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Width clamp and visibility — the two things that are not carried by the model alone. */
|
|
325
|
+
}, {
|
|
326
|
+
key: "applyStyles",
|
|
327
|
+
value: function applyStyles() {
|
|
328
|
+
var _this$model;
|
|
329
|
+
if (!this.dom) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
this.dom.root.style.setProperty('max-width',
|
|
333
|
+
// Clamped to the highlight's own width, floored at `MIN_TAG_WIDTH` so a one-character
|
|
334
|
+
// change still shows an avatar.
|
|
335
|
+
this.maxWidthPx === undefined ? '' : "max(".concat(Math.round(this.maxWidthPx), "px, ").concat(_buildContributorTagDom.MIN_TAG_WIDTH, ")"));
|
|
336
|
+
var isVisible = Boolean((_this$model = this.model) === null || _this$model === void 0 ? void 0 : _this$model.isActive) || this.isHighlightHovered || this.isHoveredOrFocused;
|
|
337
|
+
this.dom.tag.style.setProperty('opacity', isVisible ? '1' : '0');
|
|
338
|
+
this.dom.tag.style.setProperty('pointer-events', isVisible ? 'auto' : 'none');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* The full label is always the tooltip, whether or not the name it draws is clipped: the tag is
|
|
343
|
+
* never wider than the highlight, so a name that fits at one width is ellipsised at the next, and
|
|
344
|
+
* a connected pair spells out a contributor the tag does not show at any width.
|
|
345
|
+
*
|
|
346
|
+
* `VanillaTooltip` has no "set new content" call, so it is rebuilt when the label changes — the
|
|
347
|
+
* shape of `syncVanillaDisabledTooltip` in `mentionNodeView`.
|
|
348
|
+
*/
|
|
349
|
+
}, {
|
|
350
|
+
key: "syncTooltip",
|
|
351
|
+
value: function syncTooltip() {
|
|
352
|
+
var _this$tooltip2;
|
|
353
|
+
var content = this.dom ? this.fullLabel : undefined;
|
|
354
|
+
if (content === this.tooltipContent) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
(_this$tooltip2 = this.tooltip) === null || _this$tooltip2 === void 0 || _this$tooltip2.destroy();
|
|
358
|
+
this.tooltip = undefined;
|
|
359
|
+
this.tooltipContent = content;
|
|
360
|
+
if (!content || !this.dom) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
this.tooltip = new _vanillaTooltip.VanillaTooltip(this.dom.tag, content, undefined,
|
|
364
|
+
// `ak-editor-vanilla-tooltip-default` opts into the shared `VanillaTooltip` look, defined as
|
|
365
|
+
// `vanillaTooltipDefaultStyles` in both EditorContentContainer stylesheets — keep in sync when
|
|
366
|
+
// renaming. Those styles are scoped under `.ProseMirror`, and the tag renders inside it.
|
|
367
|
+
'ak-editor-vanilla-tooltip-default');
|
|
368
|
+
// `VanillaTooltip` sets `aria-describedby` on its trigger, which would announce the label a
|
|
369
|
+
// second time — the visually hidden child already announces it, after the change as design
|
|
370
|
+
// requires.
|
|
371
|
+
this.dom.tag.removeAttribute('aria-describedby');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Observes the highlight this tag describes, for what CSS here cannot see: its hover state (the
|
|
376
|
+
* highlight is a ProseMirror decoration in a different DOM subtree, so listeners are bound
|
|
377
|
+
* imperatively via `data-diff-id`) and the width the tag is clamped to.
|
|
378
|
+
*
|
|
379
|
+
* Deliberately not routed through plugin state: a transaction per pointer move would re-run the
|
|
380
|
+
* memoised decoration calculation.
|
|
381
|
+
*/
|
|
382
|
+
}, {
|
|
383
|
+
key: "syncHighlightBindings",
|
|
384
|
+
value: function syncHighlightBindings(model) {
|
|
385
|
+
var _model$linkedDiffIds,
|
|
386
|
+
_this5 = this;
|
|
387
|
+
var diffId = this.options.diffId;
|
|
388
|
+
var selector = [diffId].concat((0, _toConsumableArray2.default)((_model$linkedDiffIds = model.linkedDiffIds) !== null && _model$linkedDiffIds !== void 0 ? _model$linkedDiffIds : [])).map(function (id) {
|
|
389
|
+
return "[data-diff-id=\"".concat(id, "\"]");
|
|
390
|
+
}).join(', ');
|
|
391
|
+
if (selector === this.boundSelector) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
this.boundSelector = selector;
|
|
395
|
+
this.unbindHighlightState();
|
|
396
|
+
|
|
397
|
+
// ProseMirror draws the tag's host in the same pass as the highlights the tag measures, so
|
|
398
|
+
// those may not be in the document yet. A microtask lands once that pass has finished.
|
|
399
|
+
var pendingBind = ++this.bindToken;
|
|
400
|
+
queueMicrotask(function () {
|
|
401
|
+
if (!_this5.isDestroyed && pendingBind === _this5.bindToken) {
|
|
402
|
+
_this5.bindHighlightState(selector);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}, {
|
|
407
|
+
key: "bindHighlightState",
|
|
408
|
+
value: function bindHighlightState(selector) {
|
|
409
|
+
var _this6 = this;
|
|
410
|
+
// Both queries are scoped to this editor's content root, so a tag can only ever bind to the
|
|
411
|
+
// decorations its own plugin instance rendered — another editor on the page, or one nested
|
|
412
|
+
// inside this one, cannot supply the geometry.
|
|
413
|
+
var editorRoot = this.options.getEditorRoot();
|
|
414
|
+
var highlights = editorRoot === null || editorRoot === void 0 ? void 0 : editorRoot.querySelectorAll(selector);
|
|
415
|
+
if (!editorRoot || !(highlights !== null && highlights !== void 0 && highlights.length)) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// The width comes from this tag's OWN decoration; sibling halves of the change are here for
|
|
420
|
+
// hover only.
|
|
421
|
+
var ownElements = Array.from(editorRoot.querySelectorAll("[data-diff-id=\"".concat(this.options.diffId, "\"]")));
|
|
422
|
+
var measure = function measure() {
|
|
423
|
+
var _findChangeStartLine;
|
|
424
|
+
// Counted by distinct line box, not by client rect: an inline node inside the change (a
|
|
425
|
+
// mention, emoji, date) splits the highlight into several rects on the same line, and
|
|
426
|
+
// treating that as a wrap would drop the width clamp on changes that never wrapped.
|
|
427
|
+
var lineTops = new Set();
|
|
428
|
+
for (var _i = 0, _ownElements = ownElements; _i < _ownElements.length; _i++) {
|
|
429
|
+
var highlight = _ownElements[_i];
|
|
430
|
+
var _iterator3 = _createForOfIteratorHelper(highlight.getClientRects()),
|
|
431
|
+
_step3;
|
|
432
|
+
try {
|
|
433
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
434
|
+
var rect = _step3.value;
|
|
435
|
+
lineTops.add(Math.round(rect.top));
|
|
436
|
+
}
|
|
437
|
+
} catch (err) {
|
|
438
|
+
_iterator3.e(err);
|
|
439
|
+
} finally {
|
|
440
|
+
_iterator3.f();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// A wrapped change's first line says nothing useful about how wide the tag may be.
|
|
445
|
+
_this6.maxWidthPx = lineTops.size > 1 ? undefined : (_findChangeStartLine = findChangeStartLine(ownElements)) === null || _findChangeStartLine === void 0 ? void 0 : _findChangeStartLine.width;
|
|
446
|
+
_this6.applyStyles();
|
|
447
|
+
};
|
|
448
|
+
measure();
|
|
449
|
+
|
|
450
|
+
// An inline element cannot be observed itself (ResizeObserver skips boxes with no principal
|
|
451
|
+
// box), so the block it sits in is observed instead.
|
|
452
|
+
this.resizeObserver = new ResizeObserver(measure);
|
|
453
|
+
var _iterator4 = _createForOfIteratorHelper(new Set(ownElements.map(function (_ref7) {
|
|
454
|
+
var parentElement = _ref7.parentElement;
|
|
455
|
+
return parentElement;
|
|
456
|
+
}))),
|
|
457
|
+
_step4;
|
|
458
|
+
try {
|
|
459
|
+
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
|
|
460
|
+
var parent = _step4.value;
|
|
461
|
+
if (parent) {
|
|
462
|
+
this.resizeObserver.observe(parent);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// `bindAll` takes a single target, and a change can render more than one highlight.
|
|
467
|
+
} catch (err) {
|
|
468
|
+
_iterator4.e(err);
|
|
469
|
+
} finally {
|
|
470
|
+
_iterator4.f();
|
|
471
|
+
}
|
|
472
|
+
this.unbindHighlights = Array.from(highlights).map(function (highlight) {
|
|
473
|
+
return (0, _bindEventListener.bindAll)(highlight, [{
|
|
474
|
+
type: 'mouseenter',
|
|
475
|
+
listener: function listener() {
|
|
476
|
+
_this6.isHighlightHovered = true;
|
|
477
|
+
_this6.applyStyles();
|
|
478
|
+
}
|
|
479
|
+
}, {
|
|
480
|
+
type: 'mouseleave',
|
|
481
|
+
listener: function listener() {
|
|
482
|
+
_this6.isHighlightHovered = false;
|
|
483
|
+
_this6.applyStyles();
|
|
484
|
+
}
|
|
485
|
+
}]);
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
}, {
|
|
489
|
+
key: "unbindHighlightState",
|
|
490
|
+
value: function unbindHighlightState() {
|
|
491
|
+
var _this$resizeObserver;
|
|
492
|
+
(_this$resizeObserver = this.resizeObserver) === null || _this$resizeObserver === void 0 || _this$resizeObserver.disconnect();
|
|
493
|
+
this.resizeObserver = undefined;
|
|
494
|
+
this.unbindHighlights.forEach(function (unbind) {
|
|
495
|
+
return unbind();
|
|
496
|
+
});
|
|
497
|
+
this.unbindHighlights = [];
|
|
498
|
+
}
|
|
499
|
+
}]);
|
|
500
|
+
}();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getContributorTagIcon = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* The icons a contributor tag can draw, as inline SVG.
|
|
9
|
+
*
|
|
10
|
+
* Neither `@atlaskit/icon`, `@atlaskit/icon-lab` nor `@atlaskit/logo` exposes a raw-SVG entry
|
|
11
|
+
* point, so the markup is copied here rather than kept as a React island for three glyphs. Keep in
|
|
12
|
+
* sync with the sources named on each constant.
|
|
13
|
+
*
|
|
14
|
+
* The Rovo hex is the `brand` appearance, whose `--rovo-*-color` variables resolve to `initial` in
|
|
15
|
+
* both themes — the fallbacks below are the rendered colours, so no theme observer is needed.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** `@atlaskit/icon/core/person`, at the 16px the avatar renders it. */
|
|
19
|
+
var PERSON_ICON_SVG = "<svg width=\"16\" height=\"16\" fill=\"none\" viewBox=\"0 0 16 16\" role=\"presentation\" style=\"display:block\"><path fill=\"currentcolor\" fill-rule=\"evenodd\" d=\"M8 1.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5M4 4a4 4 0 1 1 8 0 4 4 0 0 1-8 0m-2 9a3.75 3.75 0 0 1 3.75-3.75h4.5A3.75 3.75 0 0 1 14 13v2h-1.5v-2a2.25 2.25 0 0 0-2.25-2.25h-4.5A2.25 2.25 0 0 0 3.5 13v2H2z\" clip-rule=\"evenodd\"/></svg>";
|
|
20
|
+
|
|
21
|
+
/** `@atlaskit/icon-lab/core/ai-bot`, at 12px so it fits inside the 16px hexagon. */
|
|
22
|
+
var AI_BOT_ICON_SVG = "<svg width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 16 16\" role=\"presentation\" style=\"display:block\"><path fill=\"currentcolor\" d=\"M13 5.5a.5.5 0 0 0-.5-.5h-9a.5.5 0 0 0-.5.5V13a.5.5 0 0 0 .5.5h9a.5.5 0 0 0 .5-.5zm-6.5 2v3H5v-3zm4.5 0v3H9.5v-3zm3.5 1H16V10h-1.5v3a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2v-3H0V8.5h1.5v-3a2 2 0 0 1 2-2h3.75v-2H4V0h4a.75.75 0 0 1 .75.75V3.5h3.75a2 2 0 0 1 2 2z\"/></svg>";
|
|
23
|
+
|
|
24
|
+
/** `RovoHexIcon` from `@atlaskit/logo/rovo-hex/icon`, at the 16px `xxsmall` renders. */
|
|
25
|
+
var ROVO_HEX_ICON_SVG = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" role=\"presentation\" style=\"display:block\"><path fill=\"#1868db\" fill-rule=\"evenodd\" d=\"m13.227 16.789-7.154 4.169-3.403-1.96a2.72 2.72 0 0 1-1.362-2.355V7.366c0-.973.518-1.87 1.361-2.354l6.044-3.487-.038.114a3.6 3.6 0 0 0-.172 1.088v9.278c0 1.274.68 2.45 1.786 3.085z\" clip-rule=\"evenodd\"/><path fill=\"#6a9a23\" fill-rule=\"evenodd\" d=\"m11.296 15.671-7.193 4.153 6.607 3.812a2.73 2.73 0 0 0 2.676.026l.048-.033a2.73 2.73 0 0 0 1.232-1.525 2.7 2.7 0 0 0 .127-.822v-3.594z\" clip-rule=\"evenodd\"/><path fill=\"#af59e1\" fill-rule=\"evenodd\" d=\"m21.477 5.003-4.404-2.539-5.904 4.89 2.69 1.556a3.56 3.56 0 0 1 1.785 3.086v9.277a3.6 3.6 0 0 1-.21 1.202l6.044-3.486a2.71 2.71 0 0 0 1.362-2.355V7.357c0-.97-.521-1.87-1.363-2.354\" clip-rule=\"evenodd\"/><path fill=\"#fca700\" fill-rule=\"evenodd\" d=\"M12.74 8.266 9.353 6.312V2.718c0-.283.044-.56.127-.821A2.73 2.73 0 0 1 10.71.372V.37a.4.4 0 0 0 .048-.033 2.73 2.73 0 0 1 2.676.026l6.499 3.75z\" clip-rule=\"evenodd\"/></svg>";
|
|
26
|
+
var markup = {
|
|
27
|
+
aiBot: AI_BOT_ICON_SVG,
|
|
28
|
+
person: PERSON_ICON_SVG,
|
|
29
|
+
rovoHex: ROVO_HEX_ICON_SVG
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parsing the markup is the same work for every tag, so each icon is parsed once into a template
|
|
34
|
+
* and cloned per use — appending a `DocumentFragment` moves its children out of it, so a shared
|
|
35
|
+
* fragment would leave every icon after the first empty.
|
|
36
|
+
*/
|
|
37
|
+
var templates = {};
|
|
38
|
+
var getContributorTagIcon = exports.getContributorTagIcon = function getContributorTagIcon(icon, doc) {
|
|
39
|
+
var template = templates[icon];
|
|
40
|
+
if (!template) {
|
|
41
|
+
template = doc.createElement('template');
|
|
42
|
+
template.innerHTML = markup[icon];
|
|
43
|
+
templates[icon] = template;
|
|
44
|
+
}
|
|
45
|
+
return template.content.cloneNode(true);
|
|
46
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import isEqual from 'lodash/isEqual';
|
|
2
2
|
import memoizeOne from 'memoize-one';
|
|
3
3
|
import { ChangeSet, simplifyChanges } from 'prosemirror-changeset';
|
|
4
|
-
import { isExperimentEnabled } from '@atlaskit/editor-common/deprecated-platform-feature-experiments';
|
|
5
4
|
import { areNodesEqualIgnoreAttrs } from '@atlaskit/editor-common/utils/document';
|
|
6
5
|
import { Mapping } from '@atlaskit/editor-prosemirror/transform';
|
|
7
6
|
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
@@ -576,22 +575,21 @@ const calculateDiffDecorationsInner = ({
|
|
|
576
575
|
if (change.isInline) {
|
|
577
576
|
// Inline nodes (e.g. date, emoji, mention, status) need an inline decoration rather than a block decoration
|
|
578
577
|
const isActive = isRangeActive(activeIndexPos, change.fromB, change.toB);
|
|
579
|
-
const isAtomicInlineNodeFlag = isExperimentEnabled('platform_editor_improve_inline_diffs', () => expValEquals('platform_editor_improve_inline_diffs', 'isEnabled', true));
|
|
580
578
|
decorations.push(...createInlineChangedDecoration({
|
|
581
579
|
change,
|
|
582
580
|
colorScheme,
|
|
583
581
|
isActive,
|
|
584
582
|
isInserted: true,
|
|
585
|
-
isAtomicInlineNode:
|
|
583
|
+
isAtomicInlineNode: true,
|
|
586
584
|
inlineNodeName: change.inlineNodeName,
|
|
587
585
|
// Suppress the border-bottom underline for atomic inline nodeviews —
|
|
588
586
|
// it doesn't render on custom nodeview DOM elements and is unnecessary noise.
|
|
589
|
-
hideAddedDiffsUnderline:
|
|
587
|
+
hideAddedDiffsUnderline: true,
|
|
590
588
|
diffType
|
|
591
589
|
}));
|
|
592
590
|
// If we have the original node position, also render the old node as a "deleted" widget
|
|
593
591
|
// so the user can see what it looked like before the change.
|
|
594
|
-
if (change.fromA !== undefined && change.toA !== undefined
|
|
592
|
+
if (change.fromA !== undefined && change.toA !== undefined) {
|
|
595
593
|
decorations.push(...createNodeChangedDecorationWidget({
|
|
596
594
|
change: {
|
|
597
595
|
fromA: change.fromA,
|
|
@@ -193,7 +193,6 @@ const TEXT_BLOCK_TYPES = new Set(['paragraph', 'heading']);
|
|
|
193
193
|
* layoutColumn → layoutSection, tableCell/tableHeader → tableRow → table.
|
|
194
194
|
*/
|
|
195
195
|
const RIGID_CHILD_TYPES = new Set(['layoutColumn', 'tableCell', 'tableHeader']);
|
|
196
|
-
const TABLE_TYPE = 'table';
|
|
197
196
|
|
|
198
197
|
/**
|
|
199
198
|
* Emit a single whole-block (node-level) change covering both sides of a group. Used for
|
|
@@ -466,7 +465,7 @@ const classifyContainer = (blockA, blockB, changes, originalDoc, newDoc, locale,
|
|
|
466
465
|
const typeName = blockB.node.type.name;
|
|
467
466
|
|
|
468
467
|
// Tables need cell-level counting across rows; handle them specially.
|
|
469
|
-
if (typeName ===
|
|
468
|
+
if (typeName === 'table') {
|
|
470
469
|
return classifyTable(blockA, blockB, changes, originalDoc, newDoc, locale, thresholds);
|
|
471
470
|
}
|
|
472
471
|
|
|
@@ -7,6 +7,19 @@ import { PARTICIPANT_COLOR_SCHEMES } from './types';
|
|
|
7
7
|
|
|
8
8
|
/** Contributors addressed by attribution key. The key format is internal. */
|
|
9
9
|
|
|
10
|
+
/** Maps the shared 18-slot telepointer palette onto the ten bolder diff colours. */
|
|
11
|
+
const HASHED_PARTICIPANT_COLOR_SCHEMES = [...PARTICIPANT_COLOR_SCHEMES, 'blue', 'red', 'orange', 'yellow', 'green', 'teal', 'purple', 'magenta'];
|
|
12
|
+
const getAvailableColor = (hashedColor, allocatedColors) => {
|
|
13
|
+
const startIndex = PARTICIPANT_COLOR_SCHEMES.indexOf(hashedColor);
|
|
14
|
+
for (let offset = 0; offset < PARTICIPANT_COLOR_SCHEMES.length; offset++) {
|
|
15
|
+
const color = PARTICIPANT_COLOR_SCHEMES[(startIndex + offset) % PARTICIPANT_COLOR_SCHEMES.length];
|
|
16
|
+
if (color && !allocatedColors.has(color)) {
|
|
17
|
+
return color;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return hashedColor;
|
|
21
|
+
};
|
|
22
|
+
|
|
10
23
|
/**
|
|
11
24
|
* Normalise the attribution identity once so a valid lookup key always has a valid participant
|
|
12
25
|
* colour seed. `userId` is primary; `agentId`, or `agentType` as its fallback, separates shared
|
|
@@ -89,6 +102,7 @@ export const isAttributionColoringEnabled = stepAttributions => {
|
|
|
89
102
|
export const isContributorTagsEnabled = (stepAttributions, contributors) => areAttributionColorGatesEnabled() && Boolean(stepAttributions === null || stepAttributions === void 0 ? void 0 : stepAttributions.some(attribution => getAttributionKey(attribution) !== undefined)) && contributors !== undefined && Object.keys(contributors).length > 0;
|
|
90
103
|
export const createAttributionColorMap = stepAttributions => {
|
|
91
104
|
const colors = new Map();
|
|
105
|
+
const allocatedColors = new Set();
|
|
92
106
|
for (const attribution of stepAttributions) {
|
|
93
107
|
const identity = getAttributionIdentity(attribution);
|
|
94
108
|
if (!identity || colors.has(identity.key)) {
|
|
@@ -97,10 +111,13 @@ export const createAttributionColorMap = stepAttributions => {
|
|
|
97
111
|
const {
|
|
98
112
|
index
|
|
99
113
|
} = getParticipantColor(identity.colorSeed);
|
|
100
|
-
const
|
|
101
|
-
if (
|
|
102
|
-
|
|
114
|
+
const hashedColor = HASHED_PARTICIPANT_COLOR_SCHEMES[index];
|
|
115
|
+
if (!hashedColor) {
|
|
116
|
+
continue;
|
|
103
117
|
}
|
|
118
|
+
const participantColorScheme = getAvailableColor(hashedColor, allocatedColors);
|
|
119
|
+
colors.set(identity.key, participantColorScheme);
|
|
120
|
+
allocatedColors.add(participantColorScheme);
|
|
104
121
|
}
|
|
105
122
|
return colors;
|
|
106
123
|
};
|