@atlaskit/editor-plugin-block-controls 20.0.3 → 20.1.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 +19 -0
- package/dist/cjs/blockControlsPlugin.js +53 -17
- package/dist/cjs/editor-commands/show-drag-handle.js +17 -5
- package/dist/cjs/pm-plugins/decorations-anchor.js +2 -1
- package/dist/cjs/pm-plugins/decorations-drop-target-active.js +2 -1
- package/dist/cjs/pm-plugins/decorations-drop-target.js +2 -1
- package/dist/cjs/pm-plugins/decorations-surface-anchor.js +72 -0
- package/dist/cjs/pm-plugins/handle-mouse-down.js +2 -1
- package/dist/cjs/pm-plugins/handle-mouse-over.js +3 -2
- package/dist/cjs/pm-plugins/handle-sparse-mouse-over.js +125 -0
- package/dist/cjs/pm-plugins/main.js +66 -54
- package/dist/cjs/pm-plugins/sparse-surfaces.js +49 -0
- package/dist/cjs/pm-plugins/utils/sparse-surface-candidates.js +419 -0
- package/dist/cjs/pm-plugins/utils/sparse-surfaces-mapping.js +108 -0
- package/dist/cjs/pm-plugins/utils/sparse-surfaces-state.js +53 -0
- package/dist/cjs/pm-plugins/utils/sparse-surfaces-view.js +134 -0
- package/dist/cjs/pm-plugins/utils/surface-viewport.js +223 -0
- package/dist/cjs/pm-plugins/utils/transactions.js +5 -0
- package/dist/cjs/ui/block-controls-left-surfaces.js +31 -25
- package/dist/cjs/ui/block-controls-right-surfaces.js +31 -25
- package/dist/cjs/ui/block-controls-surface-context.js +1 -0
- package/dist/cjs/ui/block-controls-surface-drag-handle-registration.js +6 -7
- package/dist/cjs/ui/block-controls-surface-drag-handle.js +6 -2
- package/dist/cjs/ui/drag-handle.js +5 -3
- package/dist/cjs/ui/drop-target-layout.js +3 -2
- package/dist/cjs/ui/drop-target.js +3 -2
- package/dist/cjs/ui/global-styles.js +69 -27
- package/dist/cjs/ui/inline-drop-target.js +6 -5
- package/dist/cjs/ui/utils/anchor-name.js +2 -1
- package/dist/cjs/ui/utils/dom-attr-name.js +3 -2
- package/dist/cjs/ui/utils/get-surface-placement.js +2 -1
- package/dist/es2019/blockControlsPlugin.js +42 -8
- package/dist/es2019/editor-commands/show-drag-handle.js +14 -2
- package/dist/es2019/pm-plugins/decorations-anchor.js +2 -1
- package/dist/es2019/pm-plugins/decorations-drop-target-active.js +2 -1
- package/dist/es2019/pm-plugins/decorations-drop-target.js +2 -1
- package/dist/es2019/pm-plugins/decorations-surface-anchor.js +56 -0
- package/dist/es2019/pm-plugins/handle-mouse-down.js +2 -1
- package/dist/es2019/pm-plugins/handle-mouse-over.js +3 -2
- package/dist/es2019/pm-plugins/handle-sparse-mouse-over.js +116 -0
- package/dist/es2019/pm-plugins/main.js +70 -54
- package/dist/es2019/pm-plugins/sparse-surfaces.js +37 -0
- package/dist/es2019/pm-plugins/utils/sparse-surface-candidates.js +305 -0
- package/dist/es2019/pm-plugins/utils/sparse-surfaces-mapping.js +78 -0
- package/dist/es2019/pm-plugins/utils/sparse-surfaces-state.js +39 -0
- package/dist/es2019/pm-plugins/utils/sparse-surfaces-view.js +121 -0
- package/dist/es2019/pm-plugins/utils/surface-viewport.js +192 -0
- package/dist/es2019/pm-plugins/utils/transactions.js +5 -0
- package/dist/es2019/ui/block-controls-left-surfaces.js +21 -14
- package/dist/es2019/ui/block-controls-right-surfaces.js +21 -14
- package/dist/es2019/ui/block-controls-surface-context.js +1 -0
- package/dist/es2019/ui/block-controls-surface-drag-handle-registration.js +4 -5
- package/dist/es2019/ui/block-controls-surface-drag-handle.js +6 -2
- package/dist/es2019/ui/drag-handle.js +5 -3
- package/dist/es2019/ui/drop-target-layout.js +3 -2
- package/dist/es2019/ui/drop-target.js +3 -2
- package/dist/es2019/ui/global-styles.js +67 -28
- package/dist/es2019/ui/inline-drop-target.js +6 -5
- package/dist/es2019/ui/utils/anchor-name.js +2 -1
- package/dist/es2019/ui/utils/dom-attr-name.js +3 -2
- package/dist/es2019/ui/utils/get-surface-placement.js +2 -1
- package/dist/esm/blockControlsPlugin.js +53 -17
- package/dist/esm/editor-commands/show-drag-handle.js +17 -5
- package/dist/esm/pm-plugins/decorations-anchor.js +2 -1
- package/dist/esm/pm-plugins/decorations-drop-target-active.js +2 -1
- package/dist/esm/pm-plugins/decorations-drop-target.js +2 -1
- package/dist/esm/pm-plugins/decorations-surface-anchor.js +65 -0
- package/dist/esm/pm-plugins/handle-mouse-down.js +2 -1
- package/dist/esm/pm-plugins/handle-mouse-over.js +3 -2
- package/dist/esm/pm-plugins/handle-sparse-mouse-over.js +119 -0
- package/dist/esm/pm-plugins/main.js +66 -54
- package/dist/esm/pm-plugins/sparse-surfaces.js +43 -0
- package/dist/esm/pm-plugins/utils/sparse-surface-candidates.js +413 -0
- package/dist/esm/pm-plugins/utils/sparse-surfaces-mapping.js +101 -0
- package/dist/esm/pm-plugins/utils/sparse-surfaces-state.js +46 -0
- package/dist/esm/pm-plugins/utils/sparse-surfaces-view.js +127 -0
- package/dist/esm/pm-plugins/utils/surface-viewport.js +217 -0
- package/dist/esm/pm-plugins/utils/transactions.js +5 -0
- package/dist/esm/ui/block-controls-left-surfaces.js +33 -27
- package/dist/esm/ui/block-controls-right-surfaces.js +33 -27
- package/dist/esm/ui/block-controls-surface-context.js +1 -0
- package/dist/esm/ui/block-controls-surface-drag-handle-registration.js +7 -8
- package/dist/esm/ui/block-controls-surface-drag-handle.js +6 -2
- package/dist/esm/ui/drag-handle.js +5 -3
- package/dist/esm/ui/drop-target-layout.js +3 -2
- package/dist/esm/ui/drop-target.js +3 -2
- package/dist/esm/ui/global-styles.js +70 -28
- package/dist/esm/ui/inline-drop-target.js +6 -5
- package/dist/esm/ui/utils/anchor-name.js +2 -1
- package/dist/esm/ui/utils/dom-attr-name.js +3 -2
- package/dist/esm/ui/utils/get-surface-placement.js +2 -1
- package/dist/types/blockControlsPluginType.d.ts +10 -3
- package/dist/types/pm-plugins/decorations-surface-anchor.d.ts +22 -0
- package/dist/types/pm-plugins/handle-sparse-mouse-over.d.ts +5 -0
- package/dist/types/pm-plugins/main.d.ts +2 -9
- package/dist/types/pm-plugins/sparse-surfaces.d.ts +8 -0
- package/dist/types/pm-plugins/utils/sparse-surface-candidates.d.ts +89 -0
- package/dist/types/pm-plugins/utils/sparse-surfaces-mapping.d.ts +20 -0
- package/dist/types/pm-plugins/utils/sparse-surfaces-state.d.ts +27 -0
- package/dist/types/pm-plugins/utils/sparse-surfaces-view.d.ts +15 -0
- package/dist/types/pm-plugins/utils/surface-viewport.d.ts +10 -0
- package/dist/types/ui/utils/get-surface-placement.d.ts +3 -2
- package/package.json +5 -5
- package/dist/cjs/pm-plugins/utils/surface-node-positions.js +0 -100
- package/dist/es2019/pm-plugins/utils/surface-node-positions.js +0 -78
- package/dist/esm/pm-plugins/utils/surface-node-positions.js +0 -93
- package/dist/types/pm-plugins/utils/surface-node-positions.d.ts +0 -14
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { BLOCK_CONTROL_UI_CONTEXT } from '@atlaskit/editor-common/block-controls/block-control-ui-context';
|
|
2
|
+
import { createSurfaceContext } from '@atlaskit/editor-ui-control-model/create-surface-context';
|
|
3
|
+
import { willComponentRender } from '@atlaskit/editor-ui-control-model/surface-renderer';
|
|
4
|
+
import { createBlockControlsSurfaceContextForPosition } from '../../ui/block-controls-surface-context';
|
|
5
|
+
import { hasSurfaceControls } from '../../ui/utils/has-surface-controls';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Viewport positions are post-transaction positions supplied by the DOM observer. The reducer
|
|
9
|
+
* never needs to inspect DOM or search document IDs for viewport discovery.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const NO_ACTIVE = 'none';
|
|
13
|
+
const EMPTY_CANDIDATES = new Map();
|
|
14
|
+
const EMPTY_POSITIONS = [];
|
|
15
|
+
const EMPTY_PROTECTED_POSITIONS = [];
|
|
16
|
+
export const emptySparseSurfaceCandidateState = {
|
|
17
|
+
activeRevision: NO_ACTIVE,
|
|
18
|
+
candidates: EMPTY_CANDIDATES,
|
|
19
|
+
positions: EMPTY_POSITIONS,
|
|
20
|
+
protectedPositions: EMPTY_PROTECTED_POSITIONS,
|
|
21
|
+
visiblePositions: EMPTY_POSITIONS
|
|
22
|
+
};
|
|
23
|
+
const getActiveRevision = activeNode => {
|
|
24
|
+
var _activeNode$rootPos, _activeNode$rootNodeT;
|
|
25
|
+
return activeNode ? [activeNode.pos, activeNode.nodeType, (_activeNode$rootPos = activeNode.rootPos) !== null && _activeNode$rootPos !== void 0 ? _activeNode$rootPos : activeNode.pos, (_activeNode$rootNodeT = activeNode.rootNodeType) !== null && _activeNode$rootNodeT !== void 0 ? _activeNode$rootNodeT : ''].join(':') : NO_ACTIVE;
|
|
26
|
+
};
|
|
27
|
+
const isInRange = (position, range) => position >= range.from && position <= range.to;
|
|
28
|
+
const normalizeRange = (range, contentSize) => ({
|
|
29
|
+
from: Math.max(0, Math.min(contentSize, Math.min(range.from, range.to))),
|
|
30
|
+
to: Math.max(0, Math.min(contentSize, Math.max(range.from, range.to)))
|
|
31
|
+
});
|
|
32
|
+
const getSurfaces = resolvedSurfaces => resolvedSurfaces.flatMap(({
|
|
33
|
+
childrenMap,
|
|
34
|
+
components,
|
|
35
|
+
root
|
|
36
|
+
}) => root && hasSurfaceControls(components) ? [{
|
|
37
|
+
childrenMap,
|
|
38
|
+
root
|
|
39
|
+
}] : []);
|
|
40
|
+
const getNodeId = node => {
|
|
41
|
+
var _node$attrs;
|
|
42
|
+
const localId = (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.localId;
|
|
43
|
+
return typeof localId === 'string' ? localId : undefined;
|
|
44
|
+
};
|
|
45
|
+
const sameCandidate = (first, second) => (first === null || first === void 0 ? void 0 : first.id) === (second === null || second === void 0 ? void 0 : second.id) && (first === null || first === void 0 ? void 0 : first.contextRevision) === (second === null || second === void 0 ? void 0 : second.contextRevision) && (first === null || first === void 0 ? void 0 : first.isPersistent) === (second === null || second === void 0 ? void 0 : second.isPersistent) && (first === null || first === void 0 ? void 0 : first.nodeRef) === (second === null || second === void 0 ? void 0 : second.nodeRef) && (first === null || first === void 0 ? void 0 : first.nodeType) === (second === null || second === void 0 ? void 0 : second.nodeType) && (first === null || first === void 0 ? void 0 : first.nodeSize) === (second === null || second === void 0 ? void 0 : second.nodeSize) && (first === null || first === void 0 ? void 0 : first.pos) === (second === null || second === void 0 ? void 0 : second.pos) && (first === null || first === void 0 ? void 0 : first.renders) === (second === null || second === void 0 ? void 0 : second.renders);
|
|
46
|
+
const sameCandidateMap = (first, second) => {
|
|
47
|
+
if (first.size !== second.size) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
for (const [position, candidate] of first) {
|
|
51
|
+
if (!sameCandidate(candidate, second.get(position))) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
};
|
|
57
|
+
const samePositions = (first, second) => first.length === second.length && first.every((position, index) => position === second[index]);
|
|
58
|
+
const normalizePositions = positions => Array.from(new Set(positions)).sort((first, second) => first - second);
|
|
59
|
+
const mapPositions = (positions, tr) => normalizePositions(positions.map(position => tr.docChanged ? tr.mapping.mapResult(position, 1) : {
|
|
60
|
+
deleted: false,
|
|
61
|
+
pos: position
|
|
62
|
+
}).filter(result => !result.deleted).map(result => result.pos));
|
|
63
|
+
const createSurfaceContextAtPosition = (state, position, activeNode) => {
|
|
64
|
+
const blockControlsContext = createBlockControlsSurfaceContextForPosition(state, position, activeNode);
|
|
65
|
+
return blockControlsContext ? createSurfaceContext(BLOCK_CONTROL_UI_CONTEXT, blockControlsContext) : undefined;
|
|
66
|
+
};
|
|
67
|
+
const willAnySurfaceRenderAt = (surfaces, state, position, activeNode) => {
|
|
68
|
+
const surfaceContext = createSurfaceContextAtPosition(state, position, activeNode);
|
|
69
|
+
return surfaces.some(({
|
|
70
|
+
childrenMap,
|
|
71
|
+
root
|
|
72
|
+
}) => willComponentRender(root, childrenMap, surfaceContext));
|
|
73
|
+
};
|
|
74
|
+
const addCandidateAt = ({
|
|
75
|
+
candidates,
|
|
76
|
+
position,
|
|
77
|
+
state,
|
|
78
|
+
surfaces,
|
|
79
|
+
activeNode
|
|
80
|
+
}) => {
|
|
81
|
+
const node = state.doc.nodeAt(position);
|
|
82
|
+
if (!(node !== null && node !== void 0 && node.isBlock)) {
|
|
83
|
+
candidates.delete(position);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const isPersistent = willAnySurfaceRenderAt(surfaces, state, position, undefined);
|
|
87
|
+
const renders = isPersistent || activeNode !== undefined && willAnySurfaceRenderAt(surfaces, state, position, activeNode);
|
|
88
|
+
candidates.set(position, {
|
|
89
|
+
contextRevision: getActiveRevision(activeNode),
|
|
90
|
+
id: getNodeId(node),
|
|
91
|
+
isPersistent,
|
|
92
|
+
nodeRef: node,
|
|
93
|
+
nodeType: node.type.name,
|
|
94
|
+
nodeSize: node.nodeSize,
|
|
95
|
+
renders,
|
|
96
|
+
pos: position
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
const isInvalidated = (candidate, position, invalidation, changedRange) => {
|
|
100
|
+
var _invalidation$ids, _invalidation$positio, _invalidation$ranges;
|
|
101
|
+
if (invalidation !== null && invalidation !== void 0 && invalidation.all) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
if (invalidation !== null && invalidation !== void 0 && (_invalidation$ids = invalidation.ids) !== null && _invalidation$ids !== void 0 && _invalidation$ids.some(id => id === candidate.id)) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
if (invalidation !== null && invalidation !== void 0 && (_invalidation$positio = invalidation.positions) !== null && _invalidation$positio !== void 0 && _invalidation$positio.includes(position)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
return Boolean(changedRange && isInRange(position, changedRange) || (invalidation === null || invalidation === void 0 ? void 0 : (_invalidation$ranges = invalidation.ranges) === null || _invalidation$ranges === void 0 ? void 0 : _invalidation$ranges.some(range => isInRange(position, range))));
|
|
111
|
+
};
|
|
112
|
+
const getPositions = candidates => Array.from(candidates.values()).filter(candidate => candidate.renders).map(candidate => candidate.pos).sort((first, second) => first - second);
|
|
113
|
+
const mapCandidates = (state, tr, retain) => {
|
|
114
|
+
const mapped = new Map();
|
|
115
|
+
for (const candidate of state.candidates.values()) {
|
|
116
|
+
const result = tr.docChanged ? tr.mapping.mapResult(candidate.pos, 1) : {
|
|
117
|
+
deleted: false,
|
|
118
|
+
pos: candidate.pos
|
|
119
|
+
};
|
|
120
|
+
if (!result.deleted && retain(result.pos, candidate)) {
|
|
121
|
+
mapped.set(result.pos, result.pos === candidate.pos ? candidate : {
|
|
122
|
+
...candidate,
|
|
123
|
+
pos: result.pos
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return mapped;
|
|
128
|
+
};
|
|
129
|
+
/** Map cache state during PM apply. This phase never calls isHidden or scans doc. */
|
|
130
|
+
export const mapSparseSurfaceCandidates = ({
|
|
131
|
+
newState,
|
|
132
|
+
previousState,
|
|
133
|
+
tr
|
|
134
|
+
}) => {
|
|
135
|
+
if (!tr.docChanged) {
|
|
136
|
+
return previousState;
|
|
137
|
+
}
|
|
138
|
+
const candidates = new Map();
|
|
139
|
+
for (const candidate of previousState.candidates.values()) {
|
|
140
|
+
const result = tr.mapping.mapResult(candidate.pos, 1);
|
|
141
|
+
if (!result.deleted) {
|
|
142
|
+
candidates.set(result.pos, result.pos === candidate.pos ? candidate : {
|
|
143
|
+
...candidate,
|
|
144
|
+
pos: result.pos
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const protectedPositions = normalizePositions(previousState.protectedPositions.map(position => tr.mapping.mapResult(position, 1)).filter(result => !result.deleted).map(result => result.pos));
|
|
149
|
+
const visiblePositions = mapPositions(previousState.visiblePositions, tr);
|
|
150
|
+
const positions = getPositions(candidates);
|
|
151
|
+
const candidatesUnchanged = sameCandidateMap(previousState.candidates, candidates);
|
|
152
|
+
const positionsUnchanged = samePositions(previousState.positions, positions);
|
|
153
|
+
const protectedUnchanged = samePositions(previousState.protectedPositions, protectedPositions);
|
|
154
|
+
if (candidatesUnchanged && positionsUnchanged && protectedUnchanged && samePositions(previousState.visiblePositions, visiblePositions)) {
|
|
155
|
+
return previousState;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
...previousState,
|
|
159
|
+
candidates: candidatesUnchanged ? previousState.candidates : candidates,
|
|
160
|
+
positions: positionsUnchanged ? previousState.positions : positions,
|
|
161
|
+
protectedPositions: protectedUnchanged ? previousState.protectedPositions : protectedPositions,
|
|
162
|
+
visiblePositions
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Reconcile after view state is current. Predicate work stays bounded to cache,
|
|
167
|
+
* exact visible positions, explicit ranges, and protected active/menu positions.
|
|
168
|
+
*/
|
|
169
|
+
export const reconcileSparseSurfaceCandidates = ({
|
|
170
|
+
activeNodesByPosition,
|
|
171
|
+
activeNode,
|
|
172
|
+
changedRange,
|
|
173
|
+
documentChanged = false,
|
|
174
|
+
invalidation,
|
|
175
|
+
newState,
|
|
176
|
+
protectedPositions,
|
|
177
|
+
resolvedSurfaces,
|
|
178
|
+
state,
|
|
179
|
+
visiblePositions
|
|
180
|
+
}) => reduceSparseSurfaceCandidates({
|
|
181
|
+
activeNode,
|
|
182
|
+
meta: {
|
|
183
|
+
activeNodesByPosition,
|
|
184
|
+
changedRange,
|
|
185
|
+
documentChanged,
|
|
186
|
+
invalidation,
|
|
187
|
+
protectedPositions,
|
|
188
|
+
visiblePositions
|
|
189
|
+
},
|
|
190
|
+
newState,
|
|
191
|
+
previousState: state,
|
|
192
|
+
resolvedSurfaces,
|
|
193
|
+
tr: newState.tr
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Update sparse candidates. Viewport and invalidation metadata are the only
|
|
198
|
+
* discovery inputs; active changes re-evaluate retained cache, never document.
|
|
199
|
+
*/
|
|
200
|
+
export const reduceSparseSurfaceCandidates = ({
|
|
201
|
+
activeNode,
|
|
202
|
+
meta,
|
|
203
|
+
newState,
|
|
204
|
+
previousState,
|
|
205
|
+
resolvedSurfaces,
|
|
206
|
+
tr
|
|
207
|
+
}) => {
|
|
208
|
+
var _meta$visiblePosition, _meta$protectedPositi, _meta$documentChanged, _invalidation$positio2;
|
|
209
|
+
const activeRevision = getActiveRevision(activeNode);
|
|
210
|
+
const activeChanged = activeRevision !== previousState.activeRevision;
|
|
211
|
+
const hasVisiblePositionsUpdate = Boolean(meta && Object.prototype.hasOwnProperty.call(meta, 'visiblePositions'));
|
|
212
|
+
const visiblePositions = hasVisiblePositionsUpdate ? normalizePositions((_meta$visiblePosition = meta === null || meta === void 0 ? void 0 : meta.visiblePositions) !== null && _meta$visiblePosition !== void 0 ? _meta$visiblePosition : EMPTY_POSITIONS) : tr.docChanged ? mapPositions(previousState.visiblePositions, tr) : previousState.visiblePositions;
|
|
213
|
+
const visiblePositionsChanged = !samePositions(visiblePositions, previousState.visiblePositions);
|
|
214
|
+
const requestedProtectedPositions = normalizePositions((_meta$protectedPositi = meta === null || meta === void 0 ? void 0 : meta.protectedPositions) !== null && _meta$protectedPositi !== void 0 ? _meta$protectedPositi : previousState.protectedPositions);
|
|
215
|
+
const protectedPositions = samePositions(previousState.protectedPositions, requestedProtectedPositions) ? previousState.protectedPositions : requestedProtectedPositions;
|
|
216
|
+
const protectedSet = new Set(protectedPositions);
|
|
217
|
+
if (activeNode) {
|
|
218
|
+
var _activeNode$rootPos2;
|
|
219
|
+
protectedSet.add(activeNode.pos);
|
|
220
|
+
protectedSet.add((_activeNode$rootPos2 = activeNode.rootPos) !== null && _activeNode$rootPos2 !== void 0 ? _activeNode$rootPos2 : activeNode.pos);
|
|
221
|
+
}
|
|
222
|
+
const visibleSet = new Set(visiblePositions);
|
|
223
|
+
const retain = (position, candidate) => ((candidate === null || candidate === void 0 ? void 0 : candidate.isPersistent) || protectedSet.has(position) || visibleSet.has(position)) && position >= 0 && position <= newState.doc.content.size;
|
|
224
|
+
const surfaces = getSurfaces(resolvedSurfaces);
|
|
225
|
+
const activeNodeAt = position => {
|
|
226
|
+
var _ref, _meta$activeNodesByPo, _meta$activeNodesByPo2;
|
|
227
|
+
return (_ref = (_meta$activeNodesByPo = meta === null || meta === void 0 ? void 0 : (_meta$activeNodesByPo2 = meta.activeNodesByPosition) === null || _meta$activeNodesByPo2 === void 0 ? void 0 : _meta$activeNodesByPo2.get(position)) !== null && _meta$activeNodesByPo !== void 0 ? _meta$activeNodesByPo : activeNode) !== null && _ref !== void 0 ? _ref : undefined;
|
|
228
|
+
};
|
|
229
|
+
const changedRange = meta !== null && meta !== void 0 && meta.changedRange ? normalizeRange(meta.changedRange, newState.doc.content.size) : undefined;
|
|
230
|
+
const invalidation = meta === null || meta === void 0 ? void 0 : meta.invalidation;
|
|
231
|
+
const documentChanged = (_meta$documentChanged = meta === null || meta === void 0 ? void 0 : meta.documentChanged) !== null && _meta$documentChanged !== void 0 ? _meta$documentChanged : tr.docChanged;
|
|
232
|
+
const previousCandidates = mapCandidates(previousState, tr, retain);
|
|
233
|
+
const dirty = (candidate, position) => {
|
|
234
|
+
const currentNode = newState.doc.nodeAt(position);
|
|
235
|
+
const currentContextRevision = getActiveRevision(activeNodeAt(position));
|
|
236
|
+
return activeChanged || candidate.contextRevision !== currentContextRevision || candidate.nodeRef !== currentNode || isInvalidated(candidate, position, invalidation, changedRange);
|
|
237
|
+
};
|
|
238
|
+
if (surfaces.length === 0) {
|
|
239
|
+
const noCandidates = previousCandidates.size === 0;
|
|
240
|
+
if (noCandidates && previousState.positions.length === 0 && samePositions(visiblePositions, previousState.visiblePositions) && activeRevision === previousState.activeRevision && protectedPositions === previousState.protectedPositions) {
|
|
241
|
+
return previousState;
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
activeRevision,
|
|
245
|
+
candidates: noCandidates ? previousState.candidates : EMPTY_CANDIDATES,
|
|
246
|
+
positions: EMPTY_POSITIONS,
|
|
247
|
+
protectedPositions,
|
|
248
|
+
visiblePositions
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
for (const [position, candidate] of previousCandidates) {
|
|
252
|
+
if (dirty(candidate, position)) {
|
|
253
|
+
addCandidateAt({
|
|
254
|
+
candidates: previousCandidates,
|
|
255
|
+
position,
|
|
256
|
+
state: newState,
|
|
257
|
+
surfaces,
|
|
258
|
+
activeNode: activeNodeAt(position)
|
|
259
|
+
});
|
|
260
|
+
const updatedCandidate = previousCandidates.get(position);
|
|
261
|
+
if (updatedCandidate && !retain(position, updatedCandidate)) {
|
|
262
|
+
previousCandidates.delete(position);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// IO supplies exact visible positions. Iterate those positions directly so a long visible
|
|
268
|
+
// container cannot cause a traversal of its entire nested document.
|
|
269
|
+
if (visiblePositionsChanged || documentChanged || invalidation !== null && invalidation !== void 0 && invalidation.all || changedRange) {
|
|
270
|
+
for (const position of visiblePositions) {
|
|
271
|
+
if (!previousCandidates.has(position)) {
|
|
272
|
+
addCandidateAt({
|
|
273
|
+
candidates: previousCandidates,
|
|
274
|
+
position,
|
|
275
|
+
state: newState,
|
|
276
|
+
surfaces,
|
|
277
|
+
activeNode: activeNodeAt(position)
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const directPositions = new Set([...protectedSet, ...((_invalidation$positio2 = invalidation === null || invalidation === void 0 ? void 0 : invalidation.positions) !== null && _invalidation$positio2 !== void 0 ? _invalidation$positio2 : [])]);
|
|
283
|
+
for (const position of directPositions) {
|
|
284
|
+
if (retain(position) && !previousCandidates.has(position)) {
|
|
285
|
+
addCandidateAt({
|
|
286
|
+
candidates: previousCandidates,
|
|
287
|
+
position,
|
|
288
|
+
state: newState,
|
|
289
|
+
surfaces,
|
|
290
|
+
activeNode: activeNodeAt(position)
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const positions = getPositions(previousCandidates);
|
|
295
|
+
if (sameCandidateMap(previousState.candidates, previousCandidates) && samePositions(previousState.positions, positions) && samePositions(previousState.visiblePositions, visiblePositions) && activeRevision === previousState.activeRevision && protectedPositions === previousState.protectedPositions) {
|
|
296
|
+
return previousState;
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
activeRevision,
|
|
300
|
+
candidates: sameCandidateMap(previousState.candidates, previousCandidates) ? previousState.candidates : previousCandidates,
|
|
301
|
+
positions: samePositions(previousState.positions, positions) ? previousState.positions : positions,
|
|
302
|
+
protectedPositions,
|
|
303
|
+
visiblePositions
|
|
304
|
+
};
|
|
305
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { mapSparseSurfaceCandidates } from './sparse-surface-candidates';
|
|
2
|
+
const mergeInvalidation = ({
|
|
3
|
+
current,
|
|
4
|
+
incoming,
|
|
5
|
+
tr
|
|
6
|
+
}) => {
|
|
7
|
+
var _current$ids, _incoming$ids, _current$positions, _incoming$positions, _current$ranges, _incoming$ranges;
|
|
8
|
+
if (!current && !incoming) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
if (!incoming && !tr.docChanged) {
|
|
12
|
+
return current;
|
|
13
|
+
}
|
|
14
|
+
const ids = [...((_current$ids = current === null || current === void 0 ? void 0 : current.ids) !== null && _current$ids !== void 0 ? _current$ids : []), ...((_incoming$ids = incoming === null || incoming === void 0 ? void 0 : incoming.ids) !== null && _incoming$ids !== void 0 ? _incoming$ids : [])];
|
|
15
|
+
const positions = [...((_current$positions = current === null || current === void 0 ? void 0 : current.positions) !== null && _current$positions !== void 0 ? _current$positions : []).map(position => tr.mapping.map(position, 1)), ...((_incoming$positions = incoming === null || incoming === void 0 ? void 0 : incoming.positions) !== null && _incoming$positions !== void 0 ? _incoming$positions : [])];
|
|
16
|
+
const ranges = [...((_current$ranges = current === null || current === void 0 ? void 0 : current.ranges) !== null && _current$ranges !== void 0 ? _current$ranges : []).map(({
|
|
17
|
+
from,
|
|
18
|
+
to
|
|
19
|
+
}) => ({
|
|
20
|
+
from: tr.mapping.map(from, -1),
|
|
21
|
+
to: tr.mapping.map(to, 1)
|
|
22
|
+
})), ...((_incoming$ranges = incoming === null || incoming === void 0 ? void 0 : incoming.ranges) !== null && _incoming$ranges !== void 0 ? _incoming$ranges : [])];
|
|
23
|
+
return ids.length || positions.length || ranges.length ? {
|
|
24
|
+
ids,
|
|
25
|
+
positions,
|
|
26
|
+
ranges
|
|
27
|
+
} : undefined;
|
|
28
|
+
};
|
|
29
|
+
const mapAnchors = (anchors, tr) => {
|
|
30
|
+
if (!tr.docChanged || anchors.size === 0) {
|
|
31
|
+
return anchors;
|
|
32
|
+
}
|
|
33
|
+
let changed = false;
|
|
34
|
+
const mappedAnchors = new Map();
|
|
35
|
+
for (const [position, name] of anchors) {
|
|
36
|
+
const mapped = tr.mapping.mapResult(position, 1);
|
|
37
|
+
if (!mapped.deleted) {
|
|
38
|
+
mappedAnchors.set(mapped.pos, name);
|
|
39
|
+
changed ||= mapped.pos !== position;
|
|
40
|
+
} else {
|
|
41
|
+
changed = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return changed ? mappedAnchors : anchors;
|
|
45
|
+
};
|
|
46
|
+
export const reducePendingState = ({
|
|
47
|
+
invalidation,
|
|
48
|
+
previous,
|
|
49
|
+
tr
|
|
50
|
+
}) => ({
|
|
51
|
+
invalidation: mergeInvalidation({
|
|
52
|
+
current: previous.invalidation,
|
|
53
|
+
incoming: invalidation,
|
|
54
|
+
tr
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
export const isUnchangedTransaction = ({
|
|
58
|
+
pending,
|
|
59
|
+
previous,
|
|
60
|
+
tr
|
|
61
|
+
}) => !tr.docChanged && pending.invalidation === previous.invalidation;
|
|
62
|
+
export const mapTransactionState = ({
|
|
63
|
+
newState,
|
|
64
|
+
pending,
|
|
65
|
+
previous,
|
|
66
|
+
tr
|
|
67
|
+
}) => ({
|
|
68
|
+
...previous,
|
|
69
|
+
anchors: mapAnchors(previous.anchors, tr),
|
|
70
|
+
candidates: tr.docChanged ? mapSparseSurfaceCandidates({
|
|
71
|
+
newState,
|
|
72
|
+
previousState: previous.candidates,
|
|
73
|
+
tr
|
|
74
|
+
}) : previous.candidates,
|
|
75
|
+
decorations: tr.docChanged ? previous.decorations.map(tr.mapping, tr.doc) : previous.decorations,
|
|
76
|
+
documentChanged: previous.documentChanged || tr.docChanged,
|
|
77
|
+
invalidation: pending.invalidation
|
|
78
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
2
|
+
import { isUnchangedTransaction, mapTransactionState, reducePendingState } from './sparse-surfaces-mapping';
|
|
3
|
+
export const createInitialSparseSurfacesState = candidates => ({
|
|
4
|
+
activeNodesByPosition: new Map(),
|
|
5
|
+
anchors: new Map(),
|
|
6
|
+
candidates,
|
|
7
|
+
decorations: DecorationSet.empty,
|
|
8
|
+
documentChanged: false
|
|
9
|
+
});
|
|
10
|
+
const isReconciliationCommit = meta => (meta === null || meta === void 0 ? void 0 : meta.type) === 'commitReconciliation';
|
|
11
|
+
const commitReconciliation = (previous, meta) => ({
|
|
12
|
+
...previous,
|
|
13
|
+
...meta.reconciliation,
|
|
14
|
+
documentChanged: false,
|
|
15
|
+
invalidation: undefined
|
|
16
|
+
});
|
|
17
|
+
export const applySparseSurfacesTransaction = input => {
|
|
18
|
+
if (isReconciliationCommit(input.meta)) {
|
|
19
|
+
return commitReconciliation(input.previous, input.meta);
|
|
20
|
+
}
|
|
21
|
+
const pending = reducePendingState({
|
|
22
|
+
invalidation: input.invalidation,
|
|
23
|
+
previous: input.previous,
|
|
24
|
+
tr: input.tr
|
|
25
|
+
});
|
|
26
|
+
if (isUnchangedTransaction({
|
|
27
|
+
pending,
|
|
28
|
+
previous: input.previous,
|
|
29
|
+
tr: input.tr
|
|
30
|
+
})) {
|
|
31
|
+
return input.previous;
|
|
32
|
+
}
|
|
33
|
+
return mapTransactionState({
|
|
34
|
+
newState: input.newState,
|
|
35
|
+
pending,
|
|
36
|
+
previous: input.previous,
|
|
37
|
+
tr: input.tr
|
|
38
|
+
});
|
|
39
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { BLOCK_CONTROLS_LEFT_SURFACE, BLOCK_CONTROLS_RIGHT_SURFACE } from '@atlaskit/editor-common/block-controls/surface-keys';
|
|
2
|
+
import { getNodeIdProvider } from '@atlaskit/editor-common/node-anchor';
|
|
3
|
+
import { resolveSurface } from '@atlaskit/editor-ui-control-model/surface-renderer';
|
|
4
|
+
import { createSurfaceAnchorDecorations } from '../decorations-surface-anchor';
|
|
5
|
+
import { key as blockControlsKey } from '../main';
|
|
6
|
+
import { reconcileSparseSurfaceCandidates } from './sparse-surface-candidates';
|
|
7
|
+
import { observeSurfaceViewport } from './surface-viewport';
|
|
8
|
+
const haveAnchorPositionsChanged = (positions, anchors) => positions.length !== anchors.size || positions.some(position => !anchors.has(position));
|
|
9
|
+
export const createSparseSurfacesView = ({
|
|
10
|
+
api,
|
|
11
|
+
pluginKey,
|
|
12
|
+
view
|
|
13
|
+
}) => {
|
|
14
|
+
var _api$uiControlRegistr3, _api$editorDisabled, _api$editorViewMode, _api$limitedMode2, _api$showDiff, _api$userIntent;
|
|
15
|
+
const win = view.dom.ownerDocument.defaultView;
|
|
16
|
+
let frame;
|
|
17
|
+
let visiblePositions = [];
|
|
18
|
+
let registryChanged = true;
|
|
19
|
+
const reconcile = () => {
|
|
20
|
+
var _api$limitedMode, _api$limitedMode$shar;
|
|
21
|
+
frame = undefined;
|
|
22
|
+
const previous = pluginKey.getState(view.state);
|
|
23
|
+
if (!previous || api !== null && api !== void 0 && (_api$limitedMode = api.limitedMode) !== null && _api$limitedMode !== void 0 && (_api$limitedMode$shar = _api$limitedMode.sharedState.currentState()) !== null && _api$limitedMode$shar !== void 0 && _api$limitedMode$shar.enabled) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const controls = blockControlsKey.getState(view.state);
|
|
27
|
+
const activeNode = controls === null || controls === void 0 ? void 0 : controls.activeNode;
|
|
28
|
+
const activeNodesByPosition = new Map();
|
|
29
|
+
const menu = controls !== null && controls !== void 0 && controls.isMenuOpen ? controls.menuTriggerByNode : undefined;
|
|
30
|
+
if (menu && controls !== null && controls !== void 0 && controls.menuTriggerBy && view.state.doc.nodeAt(menu.pos)) {
|
|
31
|
+
var _menu$rootPos;
|
|
32
|
+
const menuNode = {
|
|
33
|
+
...menu,
|
|
34
|
+
anchorName: controls.menuTriggerBy
|
|
35
|
+
};
|
|
36
|
+
activeNodesByPosition.set(menu.pos, menuNode);
|
|
37
|
+
activeNodesByPosition.set((_menu$rootPos = menu.rootPos) !== null && _menu$rootPos !== void 0 ? _menu$rootPos : menu.pos, menuNode);
|
|
38
|
+
}
|
|
39
|
+
const resolvedSurfaces = [BLOCK_CONTROLS_LEFT_SURFACE, BLOCK_CONTROLS_RIGHT_SURFACE].map(surface => {
|
|
40
|
+
var _api$uiControlRegistr, _api$uiControlRegistr2;
|
|
41
|
+
return resolveSurface((_api$uiControlRegistr = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.getComponents(surface)) !== null && _api$uiControlRegistr !== void 0 ? _api$uiControlRegistr : [], surface);
|
|
42
|
+
});
|
|
43
|
+
const candidates = reconcileSparseSurfaceCandidates({
|
|
44
|
+
activeNode,
|
|
45
|
+
activeNodesByPosition,
|
|
46
|
+
newState: view.state,
|
|
47
|
+
state: previous.candidates,
|
|
48
|
+
visiblePositions,
|
|
49
|
+
protectedPositions: [...activeNodesByPosition.keys()],
|
|
50
|
+
resolvedSurfaces,
|
|
51
|
+
documentChanged: previous.documentChanged,
|
|
52
|
+
invalidation: {
|
|
53
|
+
...previous.invalidation,
|
|
54
|
+
all: registryChanged
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const visibilityChanged = registryChanged;
|
|
58
|
+
registryChanged = false;
|
|
59
|
+
const anchorPositions = candidates.positions;
|
|
60
|
+
if (!visibilityChanged && !haveAnchorPositionsChanged(anchorPositions, previous.anchors) && candidates === previous.candidates && !previous.documentChanged && !previous.invalidation) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const nodeIdProvider = getNodeIdProvider(view);
|
|
64
|
+
const {
|
|
65
|
+
anchors,
|
|
66
|
+
decorations
|
|
67
|
+
} = createSurfaceAnchorDecorations(view.state, anchorPositions, (node, position, previousAnchorName) => {
|
|
68
|
+
if (previousAnchorName) {
|
|
69
|
+
nodeIdProvider.setIdForNode(node, previousAnchorName);
|
|
70
|
+
return previousAnchorName;
|
|
71
|
+
}
|
|
72
|
+
return nodeIdProvider.getOrGenerateId(node, position);
|
|
73
|
+
}, previous.anchors);
|
|
74
|
+
view.dispatch(view.state.tr.setMeta(pluginKey, {
|
|
75
|
+
reconciliation: {
|
|
76
|
+
activeNodesByPosition,
|
|
77
|
+
anchors,
|
|
78
|
+
candidates,
|
|
79
|
+
decorations
|
|
80
|
+
},
|
|
81
|
+
type: 'commitReconciliation'
|
|
82
|
+
}));
|
|
83
|
+
};
|
|
84
|
+
const schedule = () => {
|
|
85
|
+
if (frame === undefined && win) {
|
|
86
|
+
frame = win.requestAnimationFrame(reconcile);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const observer = observeSurfaceViewport(view, positions => {
|
|
90
|
+
visiblePositions = positions;
|
|
91
|
+
schedule();
|
|
92
|
+
});
|
|
93
|
+
const invalidate = () => {
|
|
94
|
+
registryChanged = true;
|
|
95
|
+
schedule();
|
|
96
|
+
};
|
|
97
|
+
const unsubscribe = api === null || api === void 0 ? void 0 : (_api$uiControlRegistr3 = api.uiControlRegistry) === null || _api$uiControlRegistr3 === void 0 ? void 0 : _api$uiControlRegistr3.actions.subscribe(invalidate);
|
|
98
|
+
const unsubscribers = [api === null || api === void 0 ? void 0 : (_api$editorDisabled = api.editorDisabled) === null || _api$editorDisabled === void 0 ? void 0 : _api$editorDisabled.sharedState.onChange(invalidate), api === null || api === void 0 ? void 0 : (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.sharedState.onChange(invalidate), api === null || api === void 0 ? void 0 : (_api$limitedMode2 = api.limitedMode) === null || _api$limitedMode2 === void 0 ? void 0 : _api$limitedMode2.sharedState.onChange(invalidate), api === null || api === void 0 ? void 0 : (_api$showDiff = api.showDiff) === null || _api$showDiff === void 0 ? void 0 : _api$showDiff.sharedState.onChange(invalidate), api === null || api === void 0 ? void 0 : (_api$userIntent = api.userIntent) === null || _api$userIntent === void 0 ? void 0 : _api$userIntent.sharedState.onChange(invalidate)];
|
|
99
|
+
return {
|
|
100
|
+
update(currentView, previousState) {
|
|
101
|
+
observer.update();
|
|
102
|
+
const sparse = pluginKey.getState(currentView.state);
|
|
103
|
+
const controls = blockControlsKey.getState(currentView.state);
|
|
104
|
+
const previousControls = blockControlsKey.getState(previousState);
|
|
105
|
+
if ((controls === null || controls === void 0 ? void 0 : controls.isDragging) !== (previousControls === null || previousControls === void 0 ? void 0 : previousControls.isDragging) || (controls === null || controls === void 0 ? void 0 : controls.isResizerResizing) !== (previousControls === null || previousControls === void 0 ? void 0 : previousControls.isResizerResizing)) {
|
|
106
|
+
invalidate();
|
|
107
|
+
}
|
|
108
|
+
if (sparse !== null && sparse !== void 0 && sparse.documentChanged || sparse !== null && sparse !== void 0 && sparse.invalidation || (controls === null || controls === void 0 ? void 0 : controls.activeNode) !== (previousControls === null || previousControls === void 0 ? void 0 : previousControls.activeNode) || (controls === null || controls === void 0 ? void 0 : controls.isMenuOpen) !== (previousControls === null || previousControls === void 0 ? void 0 : previousControls.isMenuOpen) || (controls === null || controls === void 0 ? void 0 : controls.menuTriggerByNode) !== (previousControls === null || previousControls === void 0 ? void 0 : previousControls.menuTriggerByNode)) {
|
|
109
|
+
schedule();
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
destroy() {
|
|
113
|
+
if (frame !== undefined) {
|
|
114
|
+
win === null || win === void 0 ? void 0 : win.cancelAnimationFrame(frame);
|
|
115
|
+
}
|
|
116
|
+
observer.destroy();
|
|
117
|
+
unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
|
|
118
|
+
unsubscribers.forEach(unsubscribe => unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe());
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
};
|