@atlaskit/editor-plugin-placeholder 12.0.33 → 12.0.35
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 +16 -0
- package/dist/cjs/pm-plugins/constants.js +2 -3
- package/dist/cjs/pm-plugins/main.js +6 -46
- package/dist/cjs/pm-plugins/utils.js +1 -1
- package/dist/es2019/pm-plugins/constants.js +1 -2
- package/dist/es2019/pm-plugins/main.js +36 -73
- package/dist/es2019/pm-plugins/utils.js +1 -1
- package/dist/esm/pm-plugins/constants.js +1 -2
- package/dist/esm/pm-plugins/main.js +6 -45
- package/dist/esm/pm-plugins/utils.js +1 -1
- package/dist/types/pm-plugins/constants.d.ts +0 -1
- package/dist/types/pm-plugins/types.d.ts +0 -3
- package/package.json +6 -6
- package/dist/cjs/pm-plugins/multi-bodied-extension-placeholder-decorations.js +0 -178
- package/dist/cjs/pm-plugins/source-sync-block-placeholder-decorations.js +0 -132
- package/dist/es2019/pm-plugins/multi-bodied-extension-placeholder-decorations.js +0 -163
- package/dist/es2019/pm-plugins/source-sync-block-placeholder-decorations.js +0 -121
- package/dist/esm/pm-plugins/multi-bodied-extension-placeholder-decorations.js +0 -171
- package/dist/esm/pm-plugins/source-sync-block-placeholder-decorations.js +0 -125
- package/dist/types/pm-plugins/multi-bodied-extension-placeholder-decorations.d.ts +0 -16
- package/dist/types/pm-plugins/source-sync-block-placeholder-decorations.d.ts +0 -17
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.createMultiBodiedExtensionPlaceholderDecorations = createMultiBodiedExtensionPlaceholderDecorations;
|
|
7
|
-
exports.updateMultiBodiedExtensionPlaceholderDecorations = updateMultiBodiedExtensionPlaceholderDecorations;
|
|
8
|
-
var _messages = require("@atlaskit/editor-common/messages");
|
|
9
|
-
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
10
|
-
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
11
|
-
var _constants = require("./constants");
|
|
12
|
-
var isMultiBodiedExtensionPlaceholderEnabled = function isMultiBodiedExtensionPlaceholderEnabled() {
|
|
13
|
-
return (0, _expValEquals.expValEquals)('confluence_native_tabs_experiment', 'isEnabled', true);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
// Empty MBE bodies are represented as extensionFrame -> empty paragraph.
|
|
17
|
-
// The paragraph gives the cursor a valid textblock, but there is no authored content yet.
|
|
18
|
-
var isEmptyExtensionFrame = function isEmptyExtensionFrame(node) {
|
|
19
|
-
var _node$firstChild, _node$firstChild2;
|
|
20
|
-
return _constants.nodeTypesWithMultiBodiedExtensionPlaceholderText.includes(node.type.name) && node.childCount === 1 && ((_node$firstChild = node.firstChild) === null || _node$firstChild === void 0 ? void 0 : _node$firstChild.content.size) === 0 && ((_node$firstChild2 = node.firstChild) === null || _node$firstChild2 === void 0 ? void 0 : _node$firstChild2.type.name) === 'paragraph';
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// Keep this generic for any MBE extension by checking the node shape rather
|
|
24
|
-
// than a product-specific extensionKey.
|
|
25
|
-
var isExtensionFrameInMultiBodiedExtension = function isExtensionFrameInMultiBodiedExtension(node, parent) {
|
|
26
|
-
return _constants.nodeTypesWithMultiBodiedExtensionPlaceholderText.includes(node.type.name) && (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'multiBodiedExtension';
|
|
27
|
-
};
|
|
28
|
-
var isEmptyExtensionFrameInMultiBodiedExtension = function isEmptyExtensionFrameInMultiBodiedExtension(node, parent) {
|
|
29
|
-
return isExtensionFrameInMultiBodiedExtension(node, parent) && isEmptyExtensionFrame(node);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// Widget decorations render UI without adding real content to the document.
|
|
33
|
-
var createMultiBodiedExtensionPlaceholderElement = function createMultiBodiedExtensionPlaceholderElement(intl) {
|
|
34
|
-
var placeholderDecoration = document.createElement('span');
|
|
35
|
-
placeholderDecoration.setAttribute('data-testid', _constants.placeholderTestId);
|
|
36
|
-
placeholderDecoration.className = 'placeholder-decoration';
|
|
37
|
-
placeholderDecoration.setAttribute('aria-hidden', 'true');
|
|
38
|
-
placeholderDecoration.textContent = intl.formatMessage(_messages.placeholderTextMessages.multiBodiedExtensionPlaceholderText);
|
|
39
|
-
return placeholderDecoration;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// `pos` is before extensionFrame; `pos + 2` is inside its paragraph, where
|
|
43
|
-
// typing starts and where the placeholder should render.
|
|
44
|
-
var createMultiBodiedExtensionPlaceholderDecoration = function createMultiBodiedExtensionPlaceholderDecoration(pos, intl) {
|
|
45
|
-
return _view.Decoration.widget(pos + 2, function () {
|
|
46
|
-
return createMultiBodiedExtensionPlaceholderElement(intl);
|
|
47
|
-
}, {
|
|
48
|
-
side: 0,
|
|
49
|
-
key: "multi-bodied-extension-placeholder-".concat(pos)
|
|
50
|
-
});
|
|
51
|
-
};
|
|
52
|
-
// Convert changed step ranges into final-document positions. A transaction can
|
|
53
|
-
// contain multiple steps, and each step can shift positions for later steps.
|
|
54
|
-
var getChangedRanges = function getChangedRanges(tr) {
|
|
55
|
-
var ranges = [];
|
|
56
|
-
tr.mapping.maps.forEach(function (stepMap, index) {
|
|
57
|
-
stepMap.forEach(function (_oldStart, _oldEnd, newStart, newEnd) {
|
|
58
|
-
var mappingAfterStep = tr.mapping.slice(index + 1);
|
|
59
|
-
ranges.push({
|
|
60
|
-
from: mappingAfterStep.map(newStart, -1),
|
|
61
|
-
to: mappingAfterStep.map(newEnd, 1)
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
return ranges;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Walk from a changed position up to its containing MBE frame. The frame may no
|
|
69
|
-
// longer be empty after the change, but we still need its range to remove stale decorations.
|
|
70
|
-
var getMultiBodiedExtensionFrameRange = function getMultiBodiedExtensionFrameRange(doc, pos) {
|
|
71
|
-
var resolvedPos = doc.resolve(Math.max(0, Math.min(pos, doc.content.size)));
|
|
72
|
-
for (var depth = resolvedPos.depth; depth > 0; depth--) {
|
|
73
|
-
var node = resolvedPos.node(depth);
|
|
74
|
-
var parent = depth > 0 ? resolvedPos.node(depth - 1) : undefined;
|
|
75
|
-
if (isExtensionFrameInMultiBodiedExtension(node, parent)) {
|
|
76
|
-
return {
|
|
77
|
-
from: resolvedPos.before(depth),
|
|
78
|
-
to: resolvedPos.after(depth)
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return;
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// Resolve just inside a frame to recover its parent; `doc.nodeAt(framePos)` only
|
|
86
|
-
// gives the frame node itself.
|
|
87
|
-
var getMultiBodiedExtensionFrameParent = function getMultiBodiedExtensionFrameParent(doc, framePos) {
|
|
88
|
-
var resolvedPos = doc.resolve(Math.max(0, Math.min(framePos + 1, doc.content.size)));
|
|
89
|
-
for (var depth = resolvedPos.depth; depth > 0; depth--) {
|
|
90
|
-
if (resolvedPos.before(depth) === framePos) {
|
|
91
|
-
return resolvedPos.node(depth - 1);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// Only update MBE frames touched by this transaction: either the change happened
|
|
98
|
-
// inside a frame, or the transaction inserted/replaced an empty frame.
|
|
99
|
-
var getAffectedMultiBodiedExtensionFrameRanges = function getAffectedMultiBodiedExtensionFrameRanges(doc, ranges) {
|
|
100
|
-
var affectedRanges = new Map();
|
|
101
|
-
ranges.forEach(function (_ref) {
|
|
102
|
-
var from = _ref.from,
|
|
103
|
-
to = _ref.to;
|
|
104
|
-
[from, to].forEach(function (pos) {
|
|
105
|
-
var multiBodiedExtensionFrameRange = getMultiBodiedExtensionFrameRange(doc, pos);
|
|
106
|
-
if (multiBodiedExtensionFrameRange) {
|
|
107
|
-
affectedRanges.set("".concat(multiBodiedExtensionFrameRange.from, "-").concat(multiBodiedExtensionFrameRange.to), multiBodiedExtensionFrameRange);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
doc.nodesBetween(from, to, function (node, pos, parent) {
|
|
111
|
-
if (isEmptyExtensionFrameInMultiBodiedExtension(node, parent)) {
|
|
112
|
-
var multiBodiedExtensionFrameRange = {
|
|
113
|
-
from: pos,
|
|
114
|
-
to: pos + node.nodeSize
|
|
115
|
-
};
|
|
116
|
-
affectedRanges.set("".concat(multiBodiedExtensionFrameRange.from, "-").concat(multiBodiedExtensionFrameRange.to), multiBodiedExtensionFrameRange);
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
return true;
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
return Array.from(affectedRanges.values());
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
// Initial creation scans the full document once. Later updates use affected
|
|
126
|
-
// ranges, and inactive tab frames stay hidden by the MBE renderer.
|
|
127
|
-
function createMultiBodiedExtensionPlaceholderDecorations(editorState, intl) {
|
|
128
|
-
if (!isMultiBodiedExtensionPlaceholderEnabled()) {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
var decorations = [];
|
|
132
|
-
editorState.doc.descendants(function (node, pos, parent) {
|
|
133
|
-
if (isEmptyExtensionFrameInMultiBodiedExtension(node, parent)) {
|
|
134
|
-
decorations.push(createMultiBodiedExtensionPlaceholderDecoration(pos, intl));
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
return true;
|
|
138
|
-
});
|
|
139
|
-
return decorations.length > 0 ? _view.DecorationSet.create(editorState.doc, decorations) : _view.DecorationSet.empty;
|
|
140
|
-
}
|
|
141
|
-
function updateMultiBodiedExtensionPlaceholderDecorations(_ref2) {
|
|
142
|
-
var currentDecorationSet = _ref2.currentDecorationSet,
|
|
143
|
-
intl = _ref2.intl,
|
|
144
|
-
newEditorState = _ref2.newEditorState,
|
|
145
|
-
tr = _ref2.tr;
|
|
146
|
-
if (!isMultiBodiedExtensionPlaceholderEnabled()) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
if (!currentDecorationSet) {
|
|
150
|
-
return createMultiBodiedExtensionPlaceholderDecorations(newEditorState, intl);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// No document change means no empty/non-empty frame state change; remap
|
|
154
|
-
// existing decorations only if ProseMirror reports positional mapping.
|
|
155
|
-
if (!tr.docChanged) {
|
|
156
|
-
return tr.mapping.maps.length > 0 ? currentDecorationSet.map(tr.mapping, newEditorState.doc) : currentDecorationSet;
|
|
157
|
-
}
|
|
158
|
-
var mappedDecorationSet = currentDecorationSet.map(tr.mapping, newEditorState.doc);
|
|
159
|
-
var affectedRanges = getAffectedMultiBodiedExtensionFrameRanges(newEditorState.doc, getChangedRanges(tr));
|
|
160
|
-
if (affectedRanges.length === 0) {
|
|
161
|
-
return mappedDecorationSet;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Remove placeholders in affected frames, then re-add only for frames that
|
|
165
|
-
// still have the empty extensionFrame -> paragraph shape.
|
|
166
|
-
var decorationsToRemove = affectedRanges.flatMap(function (_ref3) {
|
|
167
|
-
var from = _ref3.from,
|
|
168
|
-
to = _ref3.to;
|
|
169
|
-
return mappedDecorationSet.find(from, to);
|
|
170
|
-
});
|
|
171
|
-
var decorationsToAdd = affectedRanges.flatMap(function (_ref4) {
|
|
172
|
-
var from = _ref4.from;
|
|
173
|
-
var node = newEditorState.doc.nodeAt(from);
|
|
174
|
-
var parent = getMultiBodiedExtensionFrameParent(newEditorState.doc, from);
|
|
175
|
-
return node && isEmptyExtensionFrameInMultiBodiedExtension(node, parent) ? [createMultiBodiedExtensionPlaceholderDecoration(from, intl)] : [];
|
|
176
|
-
});
|
|
177
|
-
return mappedDecorationSet.remove(decorationsToRemove).add(newEditorState.doc, decorationsToAdd);
|
|
178
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.createSourceSyncBlockPlaceholderDecorations = createSourceSyncBlockPlaceholderDecorations;
|
|
7
|
-
exports.updateSourceSyncBlockPlaceholderDecorations = updateSourceSyncBlockPlaceholderDecorations;
|
|
8
|
-
var _messages = require("@atlaskit/editor-common/messages");
|
|
9
|
-
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
10
|
-
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
11
|
-
var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
|
|
12
|
-
var _constants = require("./constants");
|
|
13
|
-
var isEmptySourceSyncBlock = function isEmptySourceSyncBlock(node) {
|
|
14
|
-
var _node$firstChild, _node$firstChild2;
|
|
15
|
-
return _constants.nodeTypesWithSyncBlockPlaceholderText.includes(node.type.name) && node.childCount === 1 && ((_node$firstChild = node.firstChild) === null || _node$firstChild === void 0 ? void 0 : _node$firstChild.content.size) === 0 && ((_node$firstChild2 = node.firstChild) === null || _node$firstChild2 === void 0 ? void 0 : _node$firstChild2.type.name) === 'paragraph';
|
|
16
|
-
};
|
|
17
|
-
var createSourceSyncBlockPlaceholderElement = function createSourceSyncBlockPlaceholderElement(intl) {
|
|
18
|
-
var placeholderDecoration = document.createElement('span');
|
|
19
|
-
placeholderDecoration.setAttribute('data-testid', _constants.placeholderTestId);
|
|
20
|
-
placeholderDecoration.className = 'placeholder-decoration';
|
|
21
|
-
placeholderDecoration.setAttribute('aria-hidden', 'true');
|
|
22
|
-
placeholderDecoration.textContent = intl.formatMessage(_messages.placeholderTextMessages.sourceSyncBlockPlaceholderText);
|
|
23
|
-
return placeholderDecoration;
|
|
24
|
-
};
|
|
25
|
-
var createSourceSyncBlockPlaceholderDecoration = function createSourceSyncBlockPlaceholderDecoration(pos, intl) {
|
|
26
|
-
return _view.Decoration.widget(pos + 2, function () {
|
|
27
|
-
return createSourceSyncBlockPlaceholderElement(intl);
|
|
28
|
-
}, {
|
|
29
|
-
side: 0,
|
|
30
|
-
key: "source-sync-block-placeholder-".concat(pos)
|
|
31
|
-
});
|
|
32
|
-
};
|
|
33
|
-
var isSourceSyncBlockPlaceholderEnabled = function isSourceSyncBlockPlaceholderEnabled() {
|
|
34
|
-
return (0, _expValEquals.expValEquals)('platform_editor_sync_block_activation', 'isEnabled', true) && (0, _experiments.editorExperiment)('platform_synced_block', true);
|
|
35
|
-
};
|
|
36
|
-
var getChangedRanges = function getChangedRanges(tr) {
|
|
37
|
-
var ranges = [];
|
|
38
|
-
tr.mapping.maps.forEach(function (stepMap, index) {
|
|
39
|
-
stepMap.forEach(function (_oldStart, _oldEnd, newStart, newEnd) {
|
|
40
|
-
var mappingAfterStep = tr.mapping.slice(index + 1);
|
|
41
|
-
ranges.push({
|
|
42
|
-
from: mappingAfterStep.map(newStart, -1),
|
|
43
|
-
to: mappingAfterStep.map(newEnd, 1)
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
return ranges;
|
|
48
|
-
};
|
|
49
|
-
var getSourceSyncBlockRange = function getSourceSyncBlockRange(doc, pos) {
|
|
50
|
-
var resolvedPos = doc.resolve(Math.max(0, Math.min(pos, doc.content.size)));
|
|
51
|
-
for (var depth = resolvedPos.depth; depth > 0; depth--) {
|
|
52
|
-
var node = resolvedPos.node(depth);
|
|
53
|
-
if (_constants.nodeTypesWithSyncBlockPlaceholderText.includes(node.type.name)) {
|
|
54
|
-
return {
|
|
55
|
-
from: resolvedPos.before(depth),
|
|
56
|
-
to: resolvedPos.after(depth)
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return;
|
|
61
|
-
};
|
|
62
|
-
var getAffectedSourceSyncBlockRanges = function getAffectedSourceSyncBlockRanges(doc, ranges) {
|
|
63
|
-
var affectedRanges = new Map();
|
|
64
|
-
ranges.forEach(function (_ref) {
|
|
65
|
-
var from = _ref.from,
|
|
66
|
-
to = _ref.to;
|
|
67
|
-
[from, to].forEach(function (pos) {
|
|
68
|
-
var sourceSyncBlockRange = getSourceSyncBlockRange(doc, pos);
|
|
69
|
-
if (sourceSyncBlockRange) {
|
|
70
|
-
affectedRanges.set("".concat(sourceSyncBlockRange.from, "-").concat(sourceSyncBlockRange.to), sourceSyncBlockRange);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
doc.nodesBetween(from, to, function (node, pos) {
|
|
74
|
-
if (_constants.nodeTypesWithSyncBlockPlaceholderText.includes(node.type.name)) {
|
|
75
|
-
var sourceSyncBlockRange = {
|
|
76
|
-
from: pos,
|
|
77
|
-
to: pos + node.nodeSize
|
|
78
|
-
};
|
|
79
|
-
affectedRanges.set("".concat(sourceSyncBlockRange.from, "-").concat(sourceSyncBlockRange.to), sourceSyncBlockRange);
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
return true;
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
return Array.from(affectedRanges.values());
|
|
86
|
-
};
|
|
87
|
-
function createSourceSyncBlockPlaceholderDecorations(editorState, intl) {
|
|
88
|
-
if (!isSourceSyncBlockPlaceholderEnabled()) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
var decorations = [];
|
|
92
|
-
editorState.doc.descendants(function (node, pos) {
|
|
93
|
-
if (isEmptySourceSyncBlock(node)) {
|
|
94
|
-
decorations.push(createSourceSyncBlockPlaceholderDecoration(pos, intl));
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
return true;
|
|
98
|
-
});
|
|
99
|
-
return decorations.length > 0 ? _view.DecorationSet.create(editorState.doc, decorations) : _view.DecorationSet.empty;
|
|
100
|
-
}
|
|
101
|
-
function updateSourceSyncBlockPlaceholderDecorations(_ref2) {
|
|
102
|
-
var currentDecorationSet = _ref2.currentDecorationSet,
|
|
103
|
-
intl = _ref2.intl,
|
|
104
|
-
newEditorState = _ref2.newEditorState,
|
|
105
|
-
oldEditorState = _ref2.oldEditorState,
|
|
106
|
-
tr = _ref2.tr;
|
|
107
|
-
if (!isSourceSyncBlockPlaceholderEnabled()) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (!currentDecorationSet) {
|
|
111
|
-
return createSourceSyncBlockPlaceholderDecorations(newEditorState, intl);
|
|
112
|
-
}
|
|
113
|
-
if (!tr.docChanged) {
|
|
114
|
-
return tr.mapping.maps.length > 0 ? currentDecorationSet.map(tr.mapping, newEditorState.doc) : currentDecorationSet;
|
|
115
|
-
}
|
|
116
|
-
var mappedDecorationSet = currentDecorationSet.map(tr.mapping, newEditorState.doc);
|
|
117
|
-
var affectedRanges = getAffectedSourceSyncBlockRanges(newEditorState.doc, getChangedRanges(tr));
|
|
118
|
-
if (affectedRanges.length === 0) {
|
|
119
|
-
return mappedDecorationSet;
|
|
120
|
-
}
|
|
121
|
-
var decorationsToRemove = affectedRanges.flatMap(function (_ref3) {
|
|
122
|
-
var from = _ref3.from,
|
|
123
|
-
to = _ref3.to;
|
|
124
|
-
return mappedDecorationSet.find(from, to);
|
|
125
|
-
});
|
|
126
|
-
var decorationsToAdd = affectedRanges.flatMap(function (_ref4) {
|
|
127
|
-
var from = _ref4.from;
|
|
128
|
-
var node = newEditorState.doc.nodeAt(from);
|
|
129
|
-
return node && isEmptySourceSyncBlock(node) ? [createSourceSyncBlockPlaceholderDecoration(from, intl)] : [];
|
|
130
|
-
});
|
|
131
|
-
return mappedDecorationSet.remove(decorationsToRemove).add(newEditorState.doc, decorationsToAdd);
|
|
132
|
-
}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { placeholderTextMessages as messages } from '@atlaskit/editor-common/messages';
|
|
2
|
-
import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
-
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
4
|
-
import { nodeTypesWithMultiBodiedExtensionPlaceholderText, placeholderTestId } from './constants';
|
|
5
|
-
const isMultiBodiedExtensionPlaceholderEnabled = () => expValEquals('confluence_native_tabs_experiment', 'isEnabled', true);
|
|
6
|
-
|
|
7
|
-
// Empty MBE bodies are represented as extensionFrame -> empty paragraph.
|
|
8
|
-
// The paragraph gives the cursor a valid textblock, but there is no authored content yet.
|
|
9
|
-
const isEmptyExtensionFrame = node => {
|
|
10
|
-
var _node$firstChild, _node$firstChild2;
|
|
11
|
-
return nodeTypesWithMultiBodiedExtensionPlaceholderText.includes(node.type.name) && node.childCount === 1 && ((_node$firstChild = node.firstChild) === null || _node$firstChild === void 0 ? void 0 : _node$firstChild.content.size) === 0 && ((_node$firstChild2 = node.firstChild) === null || _node$firstChild2 === void 0 ? void 0 : _node$firstChild2.type.name) === 'paragraph';
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
// Keep this generic for any MBE extension by checking the node shape rather
|
|
15
|
-
// than a product-specific extensionKey.
|
|
16
|
-
const isExtensionFrameInMultiBodiedExtension = (node, parent) => nodeTypesWithMultiBodiedExtensionPlaceholderText.includes(node.type.name) && (parent === null || parent === void 0 ? void 0 : parent.type.name) === 'multiBodiedExtension';
|
|
17
|
-
const isEmptyExtensionFrameInMultiBodiedExtension = (node, parent) => isExtensionFrameInMultiBodiedExtension(node, parent) && isEmptyExtensionFrame(node);
|
|
18
|
-
|
|
19
|
-
// Widget decorations render UI without adding real content to the document.
|
|
20
|
-
const createMultiBodiedExtensionPlaceholderElement = intl => {
|
|
21
|
-
const placeholderDecoration = document.createElement('span');
|
|
22
|
-
placeholderDecoration.setAttribute('data-testid', placeholderTestId);
|
|
23
|
-
placeholderDecoration.className = 'placeholder-decoration';
|
|
24
|
-
placeholderDecoration.setAttribute('aria-hidden', 'true');
|
|
25
|
-
placeholderDecoration.textContent = intl.formatMessage(messages.multiBodiedExtensionPlaceholderText);
|
|
26
|
-
return placeholderDecoration;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// `pos` is before extensionFrame; `pos + 2` is inside its paragraph, where
|
|
30
|
-
// typing starts and where the placeholder should render.
|
|
31
|
-
const createMultiBodiedExtensionPlaceholderDecoration = (pos, intl) => Decoration.widget(pos + 2, () => createMultiBodiedExtensionPlaceholderElement(intl), {
|
|
32
|
-
side: 0,
|
|
33
|
-
key: `multi-bodied-extension-placeholder-${pos}`
|
|
34
|
-
});
|
|
35
|
-
// Convert changed step ranges into final-document positions. A transaction can
|
|
36
|
-
// contain multiple steps, and each step can shift positions for later steps.
|
|
37
|
-
const getChangedRanges = tr => {
|
|
38
|
-
const ranges = [];
|
|
39
|
-
tr.mapping.maps.forEach((stepMap, index) => {
|
|
40
|
-
stepMap.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
|
|
41
|
-
const mappingAfterStep = tr.mapping.slice(index + 1);
|
|
42
|
-
ranges.push({
|
|
43
|
-
from: mappingAfterStep.map(newStart, -1),
|
|
44
|
-
to: mappingAfterStep.map(newEnd, 1)
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
return ranges;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
// Walk from a changed position up to its containing MBE frame. The frame may no
|
|
52
|
-
// longer be empty after the change, but we still need its range to remove stale decorations.
|
|
53
|
-
const getMultiBodiedExtensionFrameRange = (doc, pos) => {
|
|
54
|
-
const resolvedPos = doc.resolve(Math.max(0, Math.min(pos, doc.content.size)));
|
|
55
|
-
for (let depth = resolvedPos.depth; depth > 0; depth--) {
|
|
56
|
-
const node = resolvedPos.node(depth);
|
|
57
|
-
const parent = depth > 0 ? resolvedPos.node(depth - 1) : undefined;
|
|
58
|
-
if (isExtensionFrameInMultiBodiedExtension(node, parent)) {
|
|
59
|
-
return {
|
|
60
|
-
from: resolvedPos.before(depth),
|
|
61
|
-
to: resolvedPos.after(depth)
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Resolve just inside a frame to recover its parent; `doc.nodeAt(framePos)` only
|
|
69
|
-
// gives the frame node itself.
|
|
70
|
-
const getMultiBodiedExtensionFrameParent = (doc, framePos) => {
|
|
71
|
-
const resolvedPos = doc.resolve(Math.max(0, Math.min(framePos + 1, doc.content.size)));
|
|
72
|
-
for (let depth = resolvedPos.depth; depth > 0; depth--) {
|
|
73
|
-
if (resolvedPos.before(depth) === framePos) {
|
|
74
|
-
return resolvedPos.node(depth - 1);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// Only update MBE frames touched by this transaction: either the change happened
|
|
81
|
-
// inside a frame, or the transaction inserted/replaced an empty frame.
|
|
82
|
-
const getAffectedMultiBodiedExtensionFrameRanges = (doc, ranges) => {
|
|
83
|
-
const affectedRanges = new Map();
|
|
84
|
-
ranges.forEach(({
|
|
85
|
-
from,
|
|
86
|
-
to
|
|
87
|
-
}) => {
|
|
88
|
-
[from, to].forEach(pos => {
|
|
89
|
-
const multiBodiedExtensionFrameRange = getMultiBodiedExtensionFrameRange(doc, pos);
|
|
90
|
-
if (multiBodiedExtensionFrameRange) {
|
|
91
|
-
affectedRanges.set(`${multiBodiedExtensionFrameRange.from}-${multiBodiedExtensionFrameRange.to}`, multiBodiedExtensionFrameRange);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
doc.nodesBetween(from, to, (node, pos, parent) => {
|
|
95
|
-
if (isEmptyExtensionFrameInMultiBodiedExtension(node, parent)) {
|
|
96
|
-
const multiBodiedExtensionFrameRange = {
|
|
97
|
-
from: pos,
|
|
98
|
-
to: pos + node.nodeSize
|
|
99
|
-
};
|
|
100
|
-
affectedRanges.set(`${multiBodiedExtensionFrameRange.from}-${multiBodiedExtensionFrameRange.to}`, multiBodiedExtensionFrameRange);
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
return true;
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
return Array.from(affectedRanges.values());
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
// Initial creation scans the full document once. Later updates use affected
|
|
110
|
-
// ranges, and inactive tab frames stay hidden by the MBE renderer.
|
|
111
|
-
export function createMultiBodiedExtensionPlaceholderDecorations(editorState, intl) {
|
|
112
|
-
if (!isMultiBodiedExtensionPlaceholderEnabled()) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const decorations = [];
|
|
116
|
-
editorState.doc.descendants((node, pos, parent) => {
|
|
117
|
-
if (isEmptyExtensionFrameInMultiBodiedExtension(node, parent)) {
|
|
118
|
-
decorations.push(createMultiBodiedExtensionPlaceholderDecoration(pos, intl));
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
return true;
|
|
122
|
-
});
|
|
123
|
-
return decorations.length > 0 ? DecorationSet.create(editorState.doc, decorations) : DecorationSet.empty;
|
|
124
|
-
}
|
|
125
|
-
export function updateMultiBodiedExtensionPlaceholderDecorations({
|
|
126
|
-
currentDecorationSet,
|
|
127
|
-
intl,
|
|
128
|
-
newEditorState,
|
|
129
|
-
tr
|
|
130
|
-
}) {
|
|
131
|
-
if (!isMultiBodiedExtensionPlaceholderEnabled()) {
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
if (!currentDecorationSet) {
|
|
135
|
-
return createMultiBodiedExtensionPlaceholderDecorations(newEditorState, intl);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// No document change means no empty/non-empty frame state change; remap
|
|
139
|
-
// existing decorations only if ProseMirror reports positional mapping.
|
|
140
|
-
if (!tr.docChanged) {
|
|
141
|
-
return tr.mapping.maps.length > 0 ? currentDecorationSet.map(tr.mapping, newEditorState.doc) : currentDecorationSet;
|
|
142
|
-
}
|
|
143
|
-
const mappedDecorationSet = currentDecorationSet.map(tr.mapping, newEditorState.doc);
|
|
144
|
-
const affectedRanges = getAffectedMultiBodiedExtensionFrameRanges(newEditorState.doc, getChangedRanges(tr));
|
|
145
|
-
if (affectedRanges.length === 0) {
|
|
146
|
-
return mappedDecorationSet;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Remove placeholders in affected frames, then re-add only for frames that
|
|
150
|
-
// still have the empty extensionFrame -> paragraph shape.
|
|
151
|
-
const decorationsToRemove = affectedRanges.flatMap(({
|
|
152
|
-
from,
|
|
153
|
-
to
|
|
154
|
-
}) => mappedDecorationSet.find(from, to));
|
|
155
|
-
const decorationsToAdd = affectedRanges.flatMap(({
|
|
156
|
-
from
|
|
157
|
-
}) => {
|
|
158
|
-
const node = newEditorState.doc.nodeAt(from);
|
|
159
|
-
const parent = getMultiBodiedExtensionFrameParent(newEditorState.doc, from);
|
|
160
|
-
return node && isEmptyExtensionFrameInMultiBodiedExtension(node, parent) ? [createMultiBodiedExtensionPlaceholderDecoration(from, intl)] : [];
|
|
161
|
-
});
|
|
162
|
-
return mappedDecorationSet.remove(decorationsToRemove).add(newEditorState.doc, decorationsToAdd);
|
|
163
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { placeholderTextMessages as messages } from '@atlaskit/editor-common/messages';
|
|
2
|
-
import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
3
|
-
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
4
|
-
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
5
|
-
import { nodeTypesWithSyncBlockPlaceholderText, placeholderTestId } from './constants';
|
|
6
|
-
const isEmptySourceSyncBlock = node => {
|
|
7
|
-
var _node$firstChild, _node$firstChild2;
|
|
8
|
-
return nodeTypesWithSyncBlockPlaceholderText.includes(node.type.name) && node.childCount === 1 && ((_node$firstChild = node.firstChild) === null || _node$firstChild === void 0 ? void 0 : _node$firstChild.content.size) === 0 && ((_node$firstChild2 = node.firstChild) === null || _node$firstChild2 === void 0 ? void 0 : _node$firstChild2.type.name) === 'paragraph';
|
|
9
|
-
};
|
|
10
|
-
const createSourceSyncBlockPlaceholderElement = intl => {
|
|
11
|
-
const placeholderDecoration = document.createElement('span');
|
|
12
|
-
placeholderDecoration.setAttribute('data-testid', placeholderTestId);
|
|
13
|
-
placeholderDecoration.className = 'placeholder-decoration';
|
|
14
|
-
placeholderDecoration.setAttribute('aria-hidden', 'true');
|
|
15
|
-
placeholderDecoration.textContent = intl.formatMessage(messages.sourceSyncBlockPlaceholderText);
|
|
16
|
-
return placeholderDecoration;
|
|
17
|
-
};
|
|
18
|
-
const createSourceSyncBlockPlaceholderDecoration = (pos, intl) => Decoration.widget(pos + 2, () => createSourceSyncBlockPlaceholderElement(intl), {
|
|
19
|
-
side: 0,
|
|
20
|
-
key: `source-sync-block-placeholder-${pos}`
|
|
21
|
-
});
|
|
22
|
-
const isSourceSyncBlockPlaceholderEnabled = () => expValEquals('platform_editor_sync_block_activation', 'isEnabled', true) && editorExperiment('platform_synced_block', true);
|
|
23
|
-
const getChangedRanges = tr => {
|
|
24
|
-
const ranges = [];
|
|
25
|
-
tr.mapping.maps.forEach((stepMap, index) => {
|
|
26
|
-
stepMap.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
|
|
27
|
-
const mappingAfterStep = tr.mapping.slice(index + 1);
|
|
28
|
-
ranges.push({
|
|
29
|
-
from: mappingAfterStep.map(newStart, -1),
|
|
30
|
-
to: mappingAfterStep.map(newEnd, 1)
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
return ranges;
|
|
35
|
-
};
|
|
36
|
-
const getSourceSyncBlockRange = (doc, pos) => {
|
|
37
|
-
const resolvedPos = doc.resolve(Math.max(0, Math.min(pos, doc.content.size)));
|
|
38
|
-
for (let depth = resolvedPos.depth; depth > 0; depth--) {
|
|
39
|
-
const node = resolvedPos.node(depth);
|
|
40
|
-
if (nodeTypesWithSyncBlockPlaceholderText.includes(node.type.name)) {
|
|
41
|
-
return {
|
|
42
|
-
from: resolvedPos.before(depth),
|
|
43
|
-
to: resolvedPos.after(depth)
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return;
|
|
48
|
-
};
|
|
49
|
-
const getAffectedSourceSyncBlockRanges = (doc, ranges) => {
|
|
50
|
-
const affectedRanges = new Map();
|
|
51
|
-
ranges.forEach(({
|
|
52
|
-
from,
|
|
53
|
-
to
|
|
54
|
-
}) => {
|
|
55
|
-
[from, to].forEach(pos => {
|
|
56
|
-
const sourceSyncBlockRange = getSourceSyncBlockRange(doc, pos);
|
|
57
|
-
if (sourceSyncBlockRange) {
|
|
58
|
-
affectedRanges.set(`${sourceSyncBlockRange.from}-${sourceSyncBlockRange.to}`, sourceSyncBlockRange);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
doc.nodesBetween(from, to, (node, pos) => {
|
|
62
|
-
if (nodeTypesWithSyncBlockPlaceholderText.includes(node.type.name)) {
|
|
63
|
-
const sourceSyncBlockRange = {
|
|
64
|
-
from: pos,
|
|
65
|
-
to: pos + node.nodeSize
|
|
66
|
-
};
|
|
67
|
-
affectedRanges.set(`${sourceSyncBlockRange.from}-${sourceSyncBlockRange.to}`, sourceSyncBlockRange);
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
return true;
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
return Array.from(affectedRanges.values());
|
|
74
|
-
};
|
|
75
|
-
export function createSourceSyncBlockPlaceholderDecorations(editorState, intl) {
|
|
76
|
-
if (!isSourceSyncBlockPlaceholderEnabled()) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const decorations = [];
|
|
80
|
-
editorState.doc.descendants((node, pos) => {
|
|
81
|
-
if (isEmptySourceSyncBlock(node)) {
|
|
82
|
-
decorations.push(createSourceSyncBlockPlaceholderDecoration(pos, intl));
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
return true;
|
|
86
|
-
});
|
|
87
|
-
return decorations.length > 0 ? DecorationSet.create(editorState.doc, decorations) : DecorationSet.empty;
|
|
88
|
-
}
|
|
89
|
-
export function updateSourceSyncBlockPlaceholderDecorations({
|
|
90
|
-
currentDecorationSet,
|
|
91
|
-
intl,
|
|
92
|
-
newEditorState,
|
|
93
|
-
oldEditorState,
|
|
94
|
-
tr
|
|
95
|
-
}) {
|
|
96
|
-
if (!isSourceSyncBlockPlaceholderEnabled()) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
if (!currentDecorationSet) {
|
|
100
|
-
return createSourceSyncBlockPlaceholderDecorations(newEditorState, intl);
|
|
101
|
-
}
|
|
102
|
-
if (!tr.docChanged) {
|
|
103
|
-
return tr.mapping.maps.length > 0 ? currentDecorationSet.map(tr.mapping, newEditorState.doc) : currentDecorationSet;
|
|
104
|
-
}
|
|
105
|
-
const mappedDecorationSet = currentDecorationSet.map(tr.mapping, newEditorState.doc);
|
|
106
|
-
const affectedRanges = getAffectedSourceSyncBlockRanges(newEditorState.doc, getChangedRanges(tr));
|
|
107
|
-
if (affectedRanges.length === 0) {
|
|
108
|
-
return mappedDecorationSet;
|
|
109
|
-
}
|
|
110
|
-
const decorationsToRemove = affectedRanges.flatMap(({
|
|
111
|
-
from,
|
|
112
|
-
to
|
|
113
|
-
}) => mappedDecorationSet.find(from, to));
|
|
114
|
-
const decorationsToAdd = affectedRanges.flatMap(({
|
|
115
|
-
from
|
|
116
|
-
}) => {
|
|
117
|
-
const node = newEditorState.doc.nodeAt(from);
|
|
118
|
-
return node && isEmptySourceSyncBlock(node) ? [createSourceSyncBlockPlaceholderDecoration(from, intl)] : [];
|
|
119
|
-
});
|
|
120
|
-
return mappedDecorationSet.remove(decorationsToRemove).add(newEditorState.doc, decorationsToAdd);
|
|
121
|
-
}
|