@atlaskit/editor-plugin-block-controls 20.0.2 → 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.
Files changed (148) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/cjs/blockControlsPlugin.js +53 -17
  3. package/dist/cjs/editor-commands/move-node.js +1 -1
  4. package/dist/cjs/editor-commands/show-drag-handle.js +17 -5
  5. package/dist/cjs/pm-plugins/decorations-anchor.js +3 -2
  6. package/dist/cjs/pm-plugins/decorations-drop-target-active.js +3 -2
  7. package/dist/cjs/pm-plugins/decorations-drop-target.js +3 -2
  8. package/dist/cjs/pm-plugins/decorations-surface-anchor.js +72 -0
  9. package/dist/cjs/pm-plugins/handle-mouse-down.js +2 -1
  10. package/dist/cjs/pm-plugins/handle-mouse-over.js +4 -3
  11. package/dist/cjs/pm-plugins/handle-sparse-mouse-over.js +125 -0
  12. package/dist/cjs/pm-plugins/interaction-tracking/pm-plugin.js +1 -1
  13. package/dist/cjs/pm-plugins/main.js +68 -56
  14. package/dist/cjs/pm-plugins/sparse-surfaces.js +49 -0
  15. package/dist/cjs/pm-plugins/utils/drag-handle-positions.js +1 -1
  16. package/dist/cjs/pm-plugins/utils/sparse-surface-candidates.js +419 -0
  17. package/dist/cjs/pm-plugins/utils/sparse-surfaces-mapping.js +108 -0
  18. package/dist/cjs/pm-plugins/utils/sparse-surfaces-state.js +53 -0
  19. package/dist/cjs/pm-plugins/utils/sparse-surfaces-view.js +134 -0
  20. package/dist/cjs/pm-plugins/utils/surface-viewport.js +223 -0
  21. package/dist/cjs/pm-plugins/utils/transactions.js +5 -0
  22. package/dist/cjs/pm-plugins/utils/validation.js +1 -1
  23. package/dist/cjs/ui/block-controls-left-surfaces.js +31 -25
  24. package/dist/cjs/ui/block-controls-right-surfaces.js +31 -25
  25. package/dist/cjs/ui/block-controls-surface-context.js +1 -0
  26. package/dist/cjs/ui/block-controls-surface-drag-handle-registration.js +6 -7
  27. package/dist/cjs/ui/block-controls-surface-drag-handle.js +6 -2
  28. package/dist/cjs/ui/consts.js +1 -1
  29. package/dist/cjs/ui/drag-handle.js +6 -4
  30. package/dist/cjs/ui/drop-target-layout.js +3 -2
  31. package/dist/cjs/ui/drop-target.js +4 -3
  32. package/dist/cjs/ui/global-styles.js +70 -28
  33. package/dist/cjs/ui/inline-drop-target.js +6 -5
  34. package/dist/cjs/ui/use-block-controls-surface-drag-source.js +1 -1
  35. package/dist/cjs/ui/utils/anchor-name.js +2 -1
  36. package/dist/cjs/ui/utils/dom-attr-name.js +3 -2
  37. package/dist/cjs/ui/utils/get-surface-placement.js +2 -1
  38. package/dist/cjs/ui/utils/partition-components-by-persistence.js +1 -1
  39. package/dist/cjs/ui/visibility-container.js +1 -1
  40. package/dist/es2019/blockControlsPlugin.js +42 -8
  41. package/dist/es2019/editor-commands/move-node.js +1 -1
  42. package/dist/es2019/editor-commands/show-drag-handle.js +14 -2
  43. package/dist/es2019/entry-points/block-decoration-utils-anchor-name.js +1 -0
  44. package/dist/es2019/entry-points/block-decoration-utils-consts.js +1 -0
  45. package/dist/es2019/entry-points/block-decoration-utils-dom-attr-name.js +1 -0
  46. package/dist/es2019/entry-points/block-decoration-utils-drag-handle-positions.js +1 -0
  47. package/dist/es2019/entry-points/block-decoration-utils-visibility-container.js +1 -0
  48. package/dist/es2019/entry-points/block-decoration-utils-widget-positions.js +1 -0
  49. package/dist/es2019/entry-points/blockControlsPlugin.js +1 -0
  50. package/dist/es2019/pm-plugins/decorations-anchor.js +3 -2
  51. package/dist/es2019/pm-plugins/decorations-drop-target-active.js +3 -2
  52. package/dist/es2019/pm-plugins/decorations-drop-target.js +3 -2
  53. package/dist/es2019/pm-plugins/decorations-surface-anchor.js +56 -0
  54. package/dist/es2019/pm-plugins/handle-mouse-down.js +2 -1
  55. package/dist/es2019/pm-plugins/handle-mouse-over.js +4 -3
  56. package/dist/es2019/pm-plugins/handle-sparse-mouse-over.js +116 -0
  57. package/dist/es2019/pm-plugins/interaction-tracking/pm-plugin.js +1 -1
  58. package/dist/es2019/pm-plugins/main.js +72 -56
  59. package/dist/es2019/pm-plugins/sparse-surfaces.js +37 -0
  60. package/dist/es2019/pm-plugins/utils/drag-handle-positions.js +1 -1
  61. package/dist/es2019/pm-plugins/utils/sparse-surface-candidates.js +305 -0
  62. package/dist/es2019/pm-plugins/utils/sparse-surfaces-mapping.js +78 -0
  63. package/dist/es2019/pm-plugins/utils/sparse-surfaces-state.js +39 -0
  64. package/dist/es2019/pm-plugins/utils/sparse-surfaces-view.js +121 -0
  65. package/dist/es2019/pm-plugins/utils/surface-viewport.js +192 -0
  66. package/dist/es2019/pm-plugins/utils/transactions.js +5 -0
  67. package/dist/es2019/pm-plugins/utils/update-selection.js +1 -2
  68. package/dist/es2019/pm-plugins/utils/validation.js +1 -1
  69. package/dist/es2019/ui/block-controls-left-surfaces.js +21 -14
  70. package/dist/es2019/ui/block-controls-right-surfaces.js +21 -14
  71. package/dist/es2019/ui/block-controls-surface-context.js +1 -0
  72. package/dist/es2019/ui/block-controls-surface-drag-handle-registration.js +4 -5
  73. package/dist/es2019/ui/block-controls-surface-drag-handle.js +6 -2
  74. package/dist/es2019/ui/consts.js +1 -1
  75. package/dist/es2019/ui/drag-handle.js +6 -4
  76. package/dist/es2019/ui/drop-target-layout.js +3 -2
  77. package/dist/es2019/ui/drop-target.js +4 -3
  78. package/dist/es2019/ui/global-styles.js +68 -29
  79. package/dist/es2019/ui/inline-drop-target.js +7 -5
  80. package/dist/es2019/ui/use-block-controls-surface-drag-source.js +1 -1
  81. package/dist/es2019/ui/utils/anchor-name.js +2 -1
  82. package/dist/es2019/ui/utils/dom-attr-name.js +3 -2
  83. package/dist/es2019/ui/utils/get-surface-placement.js +2 -1
  84. package/dist/es2019/ui/utils/partition-components-by-persistence.js +1 -1
  85. package/dist/es2019/ui/visibility-container.js +1 -1
  86. package/dist/esm/blockControlsPlugin.js +53 -17
  87. package/dist/esm/editor-commands/move-node.js +1 -1
  88. package/dist/esm/editor-commands/show-drag-handle.js +17 -5
  89. package/dist/esm/entry-points/block-decoration-utils-anchor-name.js +1 -0
  90. package/dist/esm/entry-points/block-decoration-utils-consts.js +1 -0
  91. package/dist/esm/entry-points/block-decoration-utils-dom-attr-name.js +1 -0
  92. package/dist/esm/entry-points/block-decoration-utils-drag-handle-positions.js +1 -0
  93. package/dist/esm/entry-points/block-decoration-utils-visibility-container.js +1 -0
  94. package/dist/esm/entry-points/block-decoration-utils-widget-positions.js +1 -0
  95. package/dist/esm/entry-points/blockControlsPlugin.js +1 -0
  96. package/dist/esm/pm-plugins/decorations-anchor.js +3 -2
  97. package/dist/esm/pm-plugins/decorations-drop-target-active.js +3 -2
  98. package/dist/esm/pm-plugins/decorations-drop-target.js +3 -2
  99. package/dist/esm/pm-plugins/decorations-surface-anchor.js +65 -0
  100. package/dist/esm/pm-plugins/handle-mouse-down.js +2 -1
  101. package/dist/esm/pm-plugins/handle-mouse-over.js +4 -3
  102. package/dist/esm/pm-plugins/handle-sparse-mouse-over.js +119 -0
  103. package/dist/esm/pm-plugins/interaction-tracking/pm-plugin.js +1 -1
  104. package/dist/esm/pm-plugins/main.js +68 -56
  105. package/dist/esm/pm-plugins/sparse-surfaces.js +43 -0
  106. package/dist/esm/pm-plugins/utils/drag-handle-positions.js +1 -1
  107. package/dist/esm/pm-plugins/utils/sparse-surface-candidates.js +413 -0
  108. package/dist/esm/pm-plugins/utils/sparse-surfaces-mapping.js +101 -0
  109. package/dist/esm/pm-plugins/utils/sparse-surfaces-state.js +46 -0
  110. package/dist/esm/pm-plugins/utils/sparse-surfaces-view.js +127 -0
  111. package/dist/esm/pm-plugins/utils/surface-viewport.js +217 -0
  112. package/dist/esm/pm-plugins/utils/transactions.js +5 -0
  113. package/dist/esm/pm-plugins/utils/update-selection.js +1 -2
  114. package/dist/esm/pm-plugins/utils/validation.js +1 -1
  115. package/dist/esm/ui/block-controls-left-surfaces.js +33 -27
  116. package/dist/esm/ui/block-controls-right-surfaces.js +33 -27
  117. package/dist/esm/ui/block-controls-surface-context.js +1 -0
  118. package/dist/esm/ui/block-controls-surface-drag-handle-registration.js +7 -8
  119. package/dist/esm/ui/block-controls-surface-drag-handle.js +6 -2
  120. package/dist/esm/ui/consts.js +1 -1
  121. package/dist/esm/ui/drag-handle.js +6 -4
  122. package/dist/esm/ui/drop-target-layout.js +3 -2
  123. package/dist/esm/ui/drop-target.js +4 -3
  124. package/dist/esm/ui/global-styles.js +71 -29
  125. package/dist/esm/ui/inline-drop-target.js +7 -5
  126. package/dist/esm/ui/use-block-controls-surface-drag-source.js +1 -1
  127. package/dist/esm/ui/utils/anchor-name.js +2 -1
  128. package/dist/esm/ui/utils/dom-attr-name.js +3 -2
  129. package/dist/esm/ui/utils/get-surface-placement.js +2 -1
  130. package/dist/esm/ui/utils/partition-components-by-persistence.js +1 -1
  131. package/dist/esm/ui/visibility-container.js +1 -1
  132. package/dist/types/blockControlsPluginType.d.ts +11 -4
  133. package/dist/types/pm-plugins/decorations-surface-anchor.d.ts +22 -0
  134. package/dist/types/pm-plugins/handle-sparse-mouse-over.d.ts +5 -0
  135. package/dist/types/pm-plugins/main.d.ts +2 -9
  136. package/dist/types/pm-plugins/sparse-surfaces.d.ts +8 -0
  137. package/dist/types/pm-plugins/utils/sparse-surface-candidates.d.ts +89 -0
  138. package/dist/types/pm-plugins/utils/sparse-surfaces-mapping.d.ts +20 -0
  139. package/dist/types/pm-plugins/utils/sparse-surfaces-state.d.ts +27 -0
  140. package/dist/types/pm-plugins/utils/sparse-surfaces-view.d.ts +15 -0
  141. package/dist/types/pm-plugins/utils/surface-viewport.d.ts +10 -0
  142. package/dist/types/ui/inline-drop-target.d.ts +4 -0
  143. package/dist/types/ui/utils/get-surface-placement.d.ts +3 -2
  144. package/package.json +7 -7
  145. package/dist/cjs/pm-plugins/utils/surface-node-positions.js +0 -100
  146. package/dist/es2019/pm-plugins/utils/surface-node-positions.js +0 -78
  147. package/dist/esm/pm-plugins/utils/surface-node-positions.js +0 -93
  148. 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
+ };