@atlaskit/editor-plugin-breakout 16.0.14 → 16.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 +31 -0
- package/dist/cjs/pm-plugins/pragmatic-resizer.js +168 -21
- package/dist/cjs/pm-plugins/resizing-plugin.js +1 -2
- package/dist/es2019/pm-plugins/pragmatic-resizer.js +161 -13
- package/dist/es2019/pm-plugins/resizing-plugin.js +1 -2
- package/dist/esm/pm-plugins/pragmatic-resizer.js +168 -21
- package/dist/esm/pm-plugins/resizing-plugin.js +1 -2
- package/package.json +3 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-breakout
|
|
2
2
|
|
|
3
|
+
## 16.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`4b796b0158182`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/4b796b0158182) -
|
|
8
|
+
EDITOR-8561 Render the breakout resize handle tooltip with `VanillaTooltip` behind the
|
|
9
|
+
`platform_editor_use_vanilla_components` experiment, replacing a React tree per resize handle.
|
|
10
|
+
When the experiment is off the tooltip is still rendered with React through the node view portal
|
|
11
|
+
provider.
|
|
12
|
+
|
|
13
|
+
The tooltip is anchored to a zero-height element parked where the pointer entered the rail, rather
|
|
14
|
+
than to the rail itself, which spans the node's full height. This matches the ADS tooltip's
|
|
15
|
+
`position="mouse"`, which latches the cursor position as the tooltip opens and does not move it
|
|
16
|
+
afterwards. The anchor is positioned by `.pm-breakout-resize-tooltip-anchor` in the editor's
|
|
17
|
+
resizer styles, which reads the pointer's offset from a custom property; the rule is inert while
|
|
18
|
+
the experiment is off, because nothing carries the class.
|
|
19
|
+
|
|
20
|
+
On the experiment the tooltip always mounts on the first hover, whatever
|
|
21
|
+
`platform_editor_reduce_event_listener_count` is set to: there is no portal to defer, and the
|
|
22
|
+
handles are built before ProseMirror populates `contentDOM`, so mounting any earlier would label
|
|
23
|
+
the tooltip with a generic "node" rather than the node being resized.
|
|
24
|
+
|
|
25
|
+
`VanillaTooltip` accepts an optional `placement`, forwarded to Popper when the tooltip is
|
|
26
|
+
positioned. Defaults to `top`, so existing callers are unaffected.
|
|
27
|
+
|
|
28
|
+
### Patch Changes
|
|
29
|
+
|
|
30
|
+
- [`0f76719a50b8f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0f76719a50b8f) -
|
|
31
|
+
Clean up feature gate `platform_editor_breakout_auto_normalization_history_patch`
|
|
32
|
+
- Updated dependencies
|
|
33
|
+
|
|
3
34
|
## 16.0.14
|
|
4
35
|
|
|
5
36
|
### Patch Changes
|
|
@@ -11,6 +11,7 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
|
|
|
11
11
|
var _react = _interopRequireWildcard(require("react"));
|
|
12
12
|
var _bindEventListener = require("bind-event-listener");
|
|
13
13
|
var _messages = require("@atlaskit/editor-common/messages");
|
|
14
|
+
var _vanillaTooltip = require("@atlaskit/editor-common/vanilla-tooltip");
|
|
14
15
|
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
|
|
15
16
|
var _elementAdapter = require("@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter");
|
|
16
17
|
var _disableNativeDragPreview = require("@atlaskit/pragmatic-drag-and-drop/utils/disable-native-drag-preview");
|
|
@@ -32,6 +33,12 @@ var getNodeName = function getNodeName(nodeName) {
|
|
|
32
33
|
return 'node';
|
|
33
34
|
}
|
|
34
35
|
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Read by `.pm-breakout-resize-tooltip-anchor`, declared in `editor-core`'s `resizerStyles.ts` and
|
|
39
|
+
* its Compiled mirror. Rename in all three or the tooltip pins to the top of the rail.
|
|
40
|
+
*/
|
|
41
|
+
var tooltipAnchorTopVar = '--pm-breakout-resize-tooltip-anchor-top';
|
|
35
42
|
var resizeHandleMessage = exports.resizeHandleMessage = {
|
|
36
43
|
expand: _messages.breakoutMessages.resizeExpand,
|
|
37
44
|
codeBlock: _messages.breakoutMessages.resizeCodeBlock,
|
|
@@ -44,6 +51,10 @@ var resizeHandleMessage = exports.resizeHandleMessage = {
|
|
|
44
51
|
bodiedExtension: _messages.breakoutMessages.resizeExtension,
|
|
45
52
|
multiBodiedExtension: _messages.breakoutMessages.resizeExtension
|
|
46
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Remove during `platform_editor_use_vanilla_components` cleanup — replaced by
|
|
56
|
+
* `createHandleTooltip`, which does the same job without a React tree per resize handle.
|
|
57
|
+
*/
|
|
47
58
|
var RailWithTooltip = function RailWithTooltip(_ref) {
|
|
48
59
|
var rail = _ref.rail,
|
|
49
60
|
target = _ref.target,
|
|
@@ -70,13 +81,54 @@ var RailWithTooltip = function RailWithTooltip(_ref) {
|
|
|
70
81
|
}
|
|
71
82
|
}));
|
|
72
83
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
|
|
85
|
+
/** Identifies the vanilla tooltip for tests and debugging. */
|
|
86
|
+
var TOOLTIP_CLASS_NAME = 'pm-breakout-resize-tooltip';
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* `ak-editor-vanilla-tooltip-default` opts into `vanillaTooltipDefaultStyles` in the
|
|
90
|
+
* `EditorContentContainer` stylesheets, which still reaches a `[popover]` because it is painted in
|
|
91
|
+
* the top layer but stays in the DOM under `.ProseMirror`.
|
|
92
|
+
*/
|
|
93
|
+
var TOOLTIP_CLASS_NAMES = "ak-editor-vanilla-tooltip-default ".concat(TOOLTIP_CLASS_NAME);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Vanilla DOM equivalent of `RailWithTooltip`, behind `platform_editor_use_vanilla_components`.
|
|
97
|
+
*
|
|
98
|
+
* Anchored to a point rather than to the rail, which spans the node's full height and would strand
|
|
99
|
+
* the tooltip at the top of a tall node. This reproduces the control arm's `position="mouse"`,
|
|
100
|
+
* which latches the cursor as the tooltip opens and does not move it afterwards — so no cursor
|
|
101
|
+
* tracking is needed, just one style write per hover.
|
|
102
|
+
*/
|
|
103
|
+
var createHandleTooltip = function createHandleTooltip(_ref2) {
|
|
104
|
+
var anchor = _ref2.anchor,
|
|
78
105
|
intl = _ref2.intl,
|
|
79
|
-
|
|
106
|
+
target = _ref2.target;
|
|
107
|
+
// Only correct once ProseMirror has populated `contentDOM`, so callers must build on first
|
|
108
|
+
// hover — earlier reads fall back to the generic 'node' label.
|
|
109
|
+
var node = target.querySelector('[data-prosemirror-node-name]');
|
|
110
|
+
var nodeName = getNodeName(node === null || node === void 0 ? void 0 : node.dataset.prosemirrorNodeName);
|
|
111
|
+
return new _vanillaTooltip.VanillaTooltip(anchor, intl.formatMessage(resizeHandleMessage[nodeName]),
|
|
112
|
+
// Let the tooltip generate its own id.
|
|
113
|
+
undefined, TOOLTIP_CLASS_NAMES,
|
|
114
|
+
// Default delay, matching the ADS tooltip.
|
|
115
|
+
300, {
|
|
116
|
+
// The only thing the shared look does not cover. Already inherited from the anchor,
|
|
117
|
+
// but set explicitly so a change there can never let this swallow a resize drag.
|
|
118
|
+
pointerEvents: 'none'
|
|
119
|
+
},
|
|
120
|
+
// No `onShow`.
|
|
121
|
+
undefined,
|
|
122
|
+
// Below the cursor, matching the control arm's `position="mouse"`.
|
|
123
|
+
'bottom');
|
|
124
|
+
};
|
|
125
|
+
var createPragmaticResizer = exports.createPragmaticResizer = function createPragmaticResizer(_ref3) {
|
|
126
|
+
var target = _ref3.target,
|
|
127
|
+
_onDragStart = _ref3.onDragStart,
|
|
128
|
+
onDrag = _ref3.onDrag,
|
|
129
|
+
_onDrop = _ref3.onDrop,
|
|
130
|
+
intl = _ref3.intl,
|
|
131
|
+
nodeViewPortalProviderAPI = _ref3.nodeViewPortalProviderAPI;
|
|
80
132
|
var state = 'default';
|
|
81
133
|
// Read once per resizer: `isExperimentEnabled` fires an exposure event, and `registerEvents` is
|
|
82
134
|
// bound to several elements so checking it inside the handler would emit one per element hovered.
|
|
@@ -102,6 +154,21 @@ var createPragmaticResizer = exports.createPragmaticResizer = function createPra
|
|
|
102
154
|
thumb.classList.add('pm-breakout-resize-handle-thumb');
|
|
103
155
|
thumb.style.pointerEvents = 'none';
|
|
104
156
|
rail.appendChild(thumb);
|
|
157
|
+
|
|
158
|
+
// Marks the point the pointer entered the rail, and anchors the tooltip there: zero height
|
|
159
|
+
// so it is a point not a box, full rail width so the tooltip centres like the control arm.
|
|
160
|
+
//
|
|
161
|
+
// Must stay a child of `rail` — `openTooltipAt` sets `top` from the rail's `offsetY`, which
|
|
162
|
+
// only lines up while the rail is the containing block. jsdom cannot catch a wrong parent.
|
|
163
|
+
//
|
|
164
|
+
// Not built on the control arm, which must keep byte-identical DOM.
|
|
165
|
+
var tooltipAnchor;
|
|
166
|
+
if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_use_vanilla_components')) {
|
|
167
|
+
// Positioned by `.pm-breakout-resize-tooltip-anchor`; only the pointer's Y is passed in.
|
|
168
|
+
tooltipAnchor = document.createElement('div');
|
|
169
|
+
tooltipAnchor.classList.add('pm-breakout-resize-tooltip-anchor');
|
|
170
|
+
rail.appendChild(tooltipAnchor);
|
|
171
|
+
}
|
|
105
172
|
var tooltipContainer = document.createElement('div');
|
|
106
173
|
tooltipContainer.classList.add('pm-breakout-resize-handle-rail-wrapper');
|
|
107
174
|
handle.appendChild(tooltipContainer);
|
|
@@ -116,17 +183,29 @@ var createPragmaticResizer = exports.createPragmaticResizer = function createPra
|
|
|
116
183
|
});
|
|
117
184
|
}, tooltipContainer, key);
|
|
118
185
|
};
|
|
186
|
+
var vanillaTooltip;
|
|
119
187
|
|
|
120
|
-
//
|
|
121
|
-
//
|
|
188
|
+
// Tracks whether there is anything to tear down. On the deferred path the tooltip mounts
|
|
189
|
+
// on first hover and so may never mount at all.
|
|
122
190
|
var isTooltipMounted = false;
|
|
123
191
|
var mountTooltip = function mountTooltip() {
|
|
124
192
|
if (isTooltipMounted) {
|
|
125
193
|
return;
|
|
126
194
|
}
|
|
127
195
|
isTooltipMounted = true;
|
|
196
|
+
|
|
197
|
+
// `tooltipAnchor` is always set when the experiment is on; the check also narrows it.
|
|
198
|
+
if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_use_vanilla_components') && tooltipAnchor) {
|
|
199
|
+
vanillaTooltip = createHandleTooltip({
|
|
200
|
+
anchor: tooltipAnchor,
|
|
201
|
+
target: target,
|
|
202
|
+
intl: intl
|
|
203
|
+
});
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
128
207
|
// `RailWithTooltip`'s ref moves `rail` into the tooltip wrapper, so the DOM ends up
|
|
129
|
-
// identical to the
|
|
208
|
+
// identical to the pre-mount state below.
|
|
130
209
|
renderTooltip();
|
|
131
210
|
};
|
|
132
211
|
|
|
@@ -139,22 +218,45 @@ var createPragmaticResizer = exports.createPragmaticResizer = function createPra
|
|
|
139
218
|
// `mouseenter` of the node (see `registerEvents` below). The rail has `opacity: 0` until
|
|
140
219
|
// the pointer is inside the node, so the pre-mount DOM is never visible, and the
|
|
141
220
|
// `mouseenter` fires on the whole node — long before the pointer reaches the 7px handle.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
//
|
|
221
|
+
//
|
|
222
|
+
// Parking the rail also needs `width: 100%`: without the ADS tooltip's wrappers (stretched
|
|
223
|
+
// by `pragmaticResizerStylesForTooltip`) it would size to its content as a flex item.
|
|
224
|
+
if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_use_vanilla_components')) {
|
|
225
|
+
// Always parks, and always defers whatever `platform_editor_reduce_event_listener_count`
|
|
226
|
+
// says: no portal means no listener cost, and first hover is when the node name reads
|
|
227
|
+
// correctly.
|
|
147
228
|
rail.style.width = '100%';
|
|
148
229
|
tooltipContainer.appendChild(rail);
|
|
149
230
|
} else {
|
|
150
|
-
|
|
231
|
+
// Remove this while at `platform_editor_use_vanilla_components` cleanup.
|
|
232
|
+
if (isDeferredTooltipEnabled) {
|
|
233
|
+
rail.style.width = '100%';
|
|
234
|
+
tooltipContainer.appendChild(rail);
|
|
235
|
+
} else {
|
|
236
|
+
// Not `mountTooltip`: that would set `isTooltipMounted` where the control arm never did.
|
|
237
|
+
renderTooltip();
|
|
238
|
+
}
|
|
151
239
|
}
|
|
152
240
|
return {
|
|
153
241
|
handle: handle,
|
|
154
242
|
rail: rail,
|
|
243
|
+
// Exposed so the rail's hover listeners can position and open the tooltip.
|
|
244
|
+
tooltipAnchor: tooltipAnchor,
|
|
155
245
|
handleHitBox: handleHitBox,
|
|
156
246
|
mountTooltip: mountTooltip,
|
|
157
247
|
destroyTooltip: function destroyTooltip() {
|
|
248
|
+
// Re-read is safe: the gate is fixed for the page, so it matches what `mountTooltip`
|
|
249
|
+
// saw. At cleanup, keep this body and delete the control arm below.
|
|
250
|
+
if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_use_vanilla_components')) {
|
|
251
|
+
// May never have mounted, and there is no portal to remove either way.
|
|
252
|
+
if (vanillaTooltip) {
|
|
253
|
+
vanillaTooltip.destroy();
|
|
254
|
+
vanillaTooltip = undefined;
|
|
255
|
+
}
|
|
256
|
+
isTooltipMounted = false;
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
158
260
|
// The eager path always mounted, so it always removes — unchanged from before. Only
|
|
159
261
|
// the deferred path can reach `destroyTooltip` without having mounted anything.
|
|
160
262
|
if (isDeferredTooltipEnabled && !isTooltipMounted) {
|
|
@@ -171,8 +273,8 @@ var createPragmaticResizer = exports.createPragmaticResizer = function createPra
|
|
|
171
273
|
var registerHandle = function registerHandle(handleElement, handleSide) {
|
|
172
274
|
return (0, _elementAdapter.draggable)({
|
|
173
275
|
element: handleElement,
|
|
174
|
-
onGenerateDragPreview: function onGenerateDragPreview(
|
|
175
|
-
var nativeSetDragImage =
|
|
276
|
+
onGenerateDragPreview: function onGenerateDragPreview(_ref4) {
|
|
277
|
+
var nativeSetDragImage = _ref4.nativeSetDragImage;
|
|
176
278
|
(0, _disableNativeDragPreview.disableNativeDragPreview)({
|
|
177
279
|
nativeSetDragImage: nativeSetDragImage
|
|
178
280
|
});
|
|
@@ -186,6 +288,14 @@ var createPragmaticResizer = exports.createPragmaticResizer = function createPra
|
|
|
186
288
|
onDragStart: function onDragStart(args) {
|
|
187
289
|
state = 'resizing';
|
|
188
290
|
handleElement.classList.add('pm-breakout-resize-handle-container--active');
|
|
291
|
+
|
|
292
|
+
// The ADS tooltip hides itself on press; the vanilla one has no pointer-press
|
|
293
|
+
// listener, and its anchor moves with the rail, so it would follow the drag.
|
|
294
|
+
// Tearing it down hides it immediately; the next hover rebuilds it.
|
|
295
|
+
if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_use_vanilla_components')) {
|
|
296
|
+
rightHandle.destroyTooltip();
|
|
297
|
+
leftHandle === null || leftHandle === void 0 || leftHandle.destroyTooltip();
|
|
298
|
+
}
|
|
189
299
|
_onDragStart(args);
|
|
190
300
|
},
|
|
191
301
|
onDrag: onDrag,
|
|
@@ -197,20 +307,57 @@ var createPragmaticResizer = exports.createPragmaticResizer = function createPra
|
|
|
197
307
|
}
|
|
198
308
|
});
|
|
199
309
|
};
|
|
200
|
-
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Parks the anchor where the pointer entered the rail, then opens the tooltip. The anchor is
|
|
313
|
+
* `pointer-events: none`, so the rail's hover listeners drive it. `offsetY` is already in the
|
|
314
|
+
* anchor's coordinate space, so no layout read is needed.
|
|
315
|
+
*
|
|
316
|
+
* Positions on enter only, matching the ADS tooltip as observed — though `Tooltip` does bind an
|
|
317
|
+
* `onMouseMove`, so start here if the two arms disagree on placement.
|
|
318
|
+
*/
|
|
319
|
+
var openTooltipAt = function openTooltipAt(anchor, event) {
|
|
320
|
+
// Only the rail bindings pass an anchor; `target` and the hit boxes do not.
|
|
321
|
+
if (!anchor) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
anchor.style.setProperty(tooltipAnchorTopVar, "".concat(event.offsetY, "px"));
|
|
325
|
+
anchor.dispatchEvent(new MouseEvent('mouseenter'));
|
|
326
|
+
};
|
|
327
|
+
var closeTooltip = function closeTooltip(anchor) {
|
|
328
|
+
if (!anchor) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
anchor.dispatchEvent(new MouseEvent('mouseleave'));
|
|
332
|
+
};
|
|
333
|
+
var registerEvents = function registerEvents(element, hoverAnchor) {
|
|
201
334
|
return [(0, _bindEventListener.bind)(element, {
|
|
202
335
|
type: 'mouseenter',
|
|
203
|
-
listener: function listener() {
|
|
336
|
+
listener: function listener(event) {
|
|
204
337
|
if (isDeferredTooltipEnabled) {
|
|
205
338
|
rightHandle.mountTooltip();
|
|
206
339
|
leftHandle === null || leftHandle === void 0 || leftHandle.mountTooltip();
|
|
207
340
|
}
|
|
341
|
+
// Always mounts on first hover, so it also mounts when the deferred experiment is
|
|
342
|
+
// off. `mountTooltip` is a no-op once mounted, so the overlap is safe. At
|
|
343
|
+
// cleanup, drop the gate and delete the block above — this covers both.
|
|
344
|
+
if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_use_vanilla_components')) {
|
|
345
|
+
rightHandle.mountTooltip();
|
|
346
|
+
leftHandle === null || leftHandle === void 0 || leftHandle.mountTooltip();
|
|
347
|
+
// After the mount above, so the tooltip always exists to be opened.
|
|
348
|
+
openTooltipAt(hoverAnchor, event);
|
|
349
|
+
}
|
|
208
350
|
rightHandle.rail.style.setProperty('opacity', '1');
|
|
209
351
|
leftHandle === null || leftHandle === void 0 || leftHandle.rail.style.setProperty('opacity', '1');
|
|
210
352
|
}
|
|
211
353
|
}), (0, _bindEventListener.bind)(element, {
|
|
212
354
|
type: 'mouseleave',
|
|
213
355
|
listener: function listener() {
|
|
356
|
+
// Ahead of the resize guard below: the ADS tooltip hides whenever the pointer
|
|
357
|
+
// leaves the rail, including part-way through a resize.
|
|
358
|
+
if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_use_vanilla_components')) {
|
|
359
|
+
closeTooltip(hoverAnchor);
|
|
360
|
+
}
|
|
214
361
|
if (state === 'resizing') {
|
|
215
362
|
return;
|
|
216
363
|
}
|
|
@@ -220,8 +367,8 @@ var createPragmaticResizer = exports.createPragmaticResizer = function createPra
|
|
|
220
367
|
})];
|
|
221
368
|
};
|
|
222
369
|
var unbindFns = leftHandle ? // old code - left + right
|
|
223
|
-
[].concat((0, _toConsumableArray2.default)(registerEvents(target)), (0, _toConsumableArray2.default)(registerEvents(rightHandle.handleHitBox)), (0, _toConsumableArray2.default)(registerEvents(leftHandle.handleHitBox)), (0, _toConsumableArray2.default)(registerEvents(rightHandle.rail)), (0, _toConsumableArray2.default)(registerEvents(leftHandle.rail))) : // new code - right only
|
|
224
|
-
[].concat((0, _toConsumableArray2.default)(registerEvents(target)), (0, _toConsumableArray2.default)(registerEvents(rightHandle.handleHitBox)), (0, _toConsumableArray2.default)(registerEvents(rightHandle.rail)));
|
|
370
|
+
[].concat((0, _toConsumableArray2.default)(registerEvents(target)), (0, _toConsumableArray2.default)(registerEvents(rightHandle.handleHitBox)), (0, _toConsumableArray2.default)(registerEvents(leftHandle.handleHitBox)), (0, _toConsumableArray2.default)(registerEvents(rightHandle.rail, rightHandle.tooltipAnchor)), (0, _toConsumableArray2.default)(registerEvents(leftHandle.rail, leftHandle.tooltipAnchor))) : // new code - right only
|
|
371
|
+
[].concat((0, _toConsumableArray2.default)(registerEvents(target)), (0, _toConsumableArray2.default)(registerEvents(rightHandle.handleHitBox)), (0, _toConsumableArray2.default)(registerEvents(rightHandle.rail, rightHandle.tooltipAnchor)));
|
|
225
372
|
var handleElement = 'rail';
|
|
226
373
|
var destroyFns = leftHandle ?
|
|
227
374
|
// old code - left + right
|
|
@@ -12,7 +12,6 @@ var _document = require("@atlaskit/editor-common/utils/document");
|
|
|
12
12
|
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
13
13
|
var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
|
|
14
14
|
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
|
|
15
|
-
var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
16
15
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
17
16
|
var _handleKeyDown = require("./handle-key-down");
|
|
18
17
|
var _resizingMarkView = require("./resizing-mark-view");
|
|
@@ -265,7 +264,7 @@ var createResizingPlugin = exports.createResizingPlugin = function createResizin
|
|
|
265
264
|
// This transaction normalizes a newly inserted node to the editor's default breakout
|
|
266
265
|
// width. It is not a user resize and must not become an undo step — in particular, a
|
|
267
266
|
// source synced block can be deleted permanently before Undo reaches this normalization.
|
|
268
|
-
return
|
|
267
|
+
return newTr.setMeta('addToHistory', false);
|
|
269
268
|
}
|
|
270
269
|
}
|
|
271
270
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useLayoutEffect, useState } from 'react';
|
|
2
2
|
import { bind } from 'bind-event-listener';
|
|
3
3
|
import { breakoutMessages as messages } from '@atlaskit/editor-common/messages';
|
|
4
|
+
import { VanillaTooltip } from '@atlaskit/editor-common/vanilla-tooltip';
|
|
4
5
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
5
6
|
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter';
|
|
6
7
|
import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/utils/disable-native-drag-preview';
|
|
@@ -21,6 +22,12 @@ const getNodeName = nodeName => {
|
|
|
21
22
|
return 'node';
|
|
22
23
|
}
|
|
23
24
|
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read by `.pm-breakout-resize-tooltip-anchor`, declared in `editor-core`'s `resizerStyles.ts` and
|
|
28
|
+
* its Compiled mirror. Rename in all three or the tooltip pins to the top of the rail.
|
|
29
|
+
*/
|
|
30
|
+
const tooltipAnchorTopVar = '--pm-breakout-resize-tooltip-anchor-top';
|
|
24
31
|
export const resizeHandleMessage = {
|
|
25
32
|
expand: messages.resizeExpand,
|
|
26
33
|
codeBlock: messages.resizeCodeBlock,
|
|
@@ -33,6 +40,10 @@ export const resizeHandleMessage = {
|
|
|
33
40
|
bodiedExtension: messages.resizeExtension,
|
|
34
41
|
multiBodiedExtension: messages.resizeExtension
|
|
35
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Remove during `platform_editor_use_vanilla_components` cleanup — replaced by
|
|
45
|
+
* `createHandleTooltip`, which does the same job without a React tree per resize handle.
|
|
46
|
+
*/
|
|
36
47
|
const RailWithTooltip = ({
|
|
37
48
|
rail,
|
|
38
49
|
target,
|
|
@@ -57,6 +68,48 @@ const RailWithTooltip = ({
|
|
|
57
68
|
}
|
|
58
69
|
}));
|
|
59
70
|
};
|
|
71
|
+
|
|
72
|
+
/** Identifies the vanilla tooltip for tests and debugging. */
|
|
73
|
+
const TOOLTIP_CLASS_NAME = 'pm-breakout-resize-tooltip';
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* `ak-editor-vanilla-tooltip-default` opts into `vanillaTooltipDefaultStyles` in the
|
|
77
|
+
* `EditorContentContainer` stylesheets, which still reaches a `[popover]` because it is painted in
|
|
78
|
+
* the top layer but stays in the DOM under `.ProseMirror`.
|
|
79
|
+
*/
|
|
80
|
+
const TOOLTIP_CLASS_NAMES = `ak-editor-vanilla-tooltip-default ${TOOLTIP_CLASS_NAME}`;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Vanilla DOM equivalent of `RailWithTooltip`, behind `platform_editor_use_vanilla_components`.
|
|
84
|
+
*
|
|
85
|
+
* Anchored to a point rather than to the rail, which spans the node's full height and would strand
|
|
86
|
+
* the tooltip at the top of a tall node. This reproduces the control arm's `position="mouse"`,
|
|
87
|
+
* which latches the cursor as the tooltip opens and does not move it afterwards — so no cursor
|
|
88
|
+
* tracking is needed, just one style write per hover.
|
|
89
|
+
*/
|
|
90
|
+
const createHandleTooltip = ({
|
|
91
|
+
anchor,
|
|
92
|
+
intl,
|
|
93
|
+
target
|
|
94
|
+
}) => {
|
|
95
|
+
// Only correct once ProseMirror has populated `contentDOM`, so callers must build on first
|
|
96
|
+
// hover — earlier reads fall back to the generic 'node' label.
|
|
97
|
+
const node = target.querySelector('[data-prosemirror-node-name]');
|
|
98
|
+
const nodeName = getNodeName(node === null || node === void 0 ? void 0 : node.dataset.prosemirrorNodeName);
|
|
99
|
+
return new VanillaTooltip(anchor, intl.formatMessage(resizeHandleMessage[nodeName]),
|
|
100
|
+
// Let the tooltip generate its own id.
|
|
101
|
+
undefined, TOOLTIP_CLASS_NAMES,
|
|
102
|
+
// Default delay, matching the ADS tooltip.
|
|
103
|
+
300, {
|
|
104
|
+
// The only thing the shared look does not cover. Already inherited from the anchor,
|
|
105
|
+
// but set explicitly so a change there can never let this swallow a resize drag.
|
|
106
|
+
pointerEvents: 'none'
|
|
107
|
+
},
|
|
108
|
+
// No `onShow`.
|
|
109
|
+
undefined,
|
|
110
|
+
// Below the cursor, matching the control arm's `position="mouse"`.
|
|
111
|
+
'bottom');
|
|
112
|
+
};
|
|
60
113
|
export const createPragmaticResizer = ({
|
|
61
114
|
target,
|
|
62
115
|
onDragStart,
|
|
@@ -90,6 +143,21 @@ export const createPragmaticResizer = ({
|
|
|
90
143
|
thumb.classList.add('pm-breakout-resize-handle-thumb');
|
|
91
144
|
thumb.style.pointerEvents = 'none';
|
|
92
145
|
rail.appendChild(thumb);
|
|
146
|
+
|
|
147
|
+
// Marks the point the pointer entered the rail, and anchors the tooltip there: zero height
|
|
148
|
+
// so it is a point not a box, full rail width so the tooltip centres like the control arm.
|
|
149
|
+
//
|
|
150
|
+
// Must stay a child of `rail` — `openTooltipAt` sets `top` from the rail's `offsetY`, which
|
|
151
|
+
// only lines up while the rail is the containing block. jsdom cannot catch a wrong parent.
|
|
152
|
+
//
|
|
153
|
+
// Not built on the control arm, which must keep byte-identical DOM.
|
|
154
|
+
let tooltipAnchor;
|
|
155
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
156
|
+
// Positioned by `.pm-breakout-resize-tooltip-anchor`; only the pointer's Y is passed in.
|
|
157
|
+
tooltipAnchor = document.createElement('div');
|
|
158
|
+
tooltipAnchor.classList.add('pm-breakout-resize-tooltip-anchor');
|
|
159
|
+
rail.appendChild(tooltipAnchor);
|
|
160
|
+
}
|
|
93
161
|
const tooltipContainer = document.createElement('div');
|
|
94
162
|
tooltipContainer.classList.add('pm-breakout-resize-handle-rail-wrapper');
|
|
95
163
|
handle.appendChild(tooltipContainer);
|
|
@@ -100,17 +168,29 @@ export const createPragmaticResizer = ({
|
|
|
100
168
|
target: target,
|
|
101
169
|
intl: intl
|
|
102
170
|
}), tooltipContainer, key);
|
|
171
|
+
let vanillaTooltip;
|
|
103
172
|
|
|
104
|
-
//
|
|
105
|
-
//
|
|
173
|
+
// Tracks whether there is anything to tear down. On the deferred path the tooltip mounts
|
|
174
|
+
// on first hover and so may never mount at all.
|
|
106
175
|
let isTooltipMounted = false;
|
|
107
176
|
const mountTooltip = () => {
|
|
108
177
|
if (isTooltipMounted) {
|
|
109
178
|
return;
|
|
110
179
|
}
|
|
111
180
|
isTooltipMounted = true;
|
|
181
|
+
|
|
182
|
+
// `tooltipAnchor` is always set when the experiment is on; the check also narrows it.
|
|
183
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components') && tooltipAnchor) {
|
|
184
|
+
vanillaTooltip = createHandleTooltip({
|
|
185
|
+
anchor: tooltipAnchor,
|
|
186
|
+
target,
|
|
187
|
+
intl
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
112
192
|
// `RailWithTooltip`'s ref moves `rail` into the tooltip wrapper, so the DOM ends up
|
|
113
|
-
// identical to the
|
|
193
|
+
// identical to the pre-mount state below.
|
|
114
194
|
renderTooltip();
|
|
115
195
|
};
|
|
116
196
|
|
|
@@ -123,22 +203,45 @@ export const createPragmaticResizer = ({
|
|
|
123
203
|
// `mouseenter` of the node (see `registerEvents` below). The rail has `opacity: 0` until
|
|
124
204
|
// the pointer is inside the node, so the pre-mount DOM is never visible, and the
|
|
125
205
|
// `mouseenter` fires on the whole node — long before the pointer reaches the 7px handle.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
//
|
|
206
|
+
//
|
|
207
|
+
// Parking the rail also needs `width: 100%`: without the ADS tooltip's wrappers (stretched
|
|
208
|
+
// by `pragmaticResizerStylesForTooltip`) it would size to its content as a flex item.
|
|
209
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
210
|
+
// Always parks, and always defers whatever `platform_editor_reduce_event_listener_count`
|
|
211
|
+
// says: no portal means no listener cost, and first hover is when the node name reads
|
|
212
|
+
// correctly.
|
|
131
213
|
rail.style.width = '100%';
|
|
132
214
|
tooltipContainer.appendChild(rail);
|
|
133
215
|
} else {
|
|
134
|
-
|
|
216
|
+
// Remove this while at `platform_editor_use_vanilla_components` cleanup.
|
|
217
|
+
if (isDeferredTooltipEnabled) {
|
|
218
|
+
rail.style.width = '100%';
|
|
219
|
+
tooltipContainer.appendChild(rail);
|
|
220
|
+
} else {
|
|
221
|
+
// Not `mountTooltip`: that would set `isTooltipMounted` where the control arm never did.
|
|
222
|
+
renderTooltip();
|
|
223
|
+
}
|
|
135
224
|
}
|
|
136
225
|
return {
|
|
137
226
|
handle,
|
|
138
227
|
rail,
|
|
228
|
+
// Exposed so the rail's hover listeners can position and open the tooltip.
|
|
229
|
+
tooltipAnchor,
|
|
139
230
|
handleHitBox,
|
|
140
231
|
mountTooltip,
|
|
141
232
|
destroyTooltip: () => {
|
|
233
|
+
// Re-read is safe: the gate is fixed for the page, so it matches what `mountTooltip`
|
|
234
|
+
// saw. At cleanup, keep this body and delete the control arm below.
|
|
235
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
236
|
+
// May never have mounted, and there is no portal to remove either way.
|
|
237
|
+
if (vanillaTooltip) {
|
|
238
|
+
vanillaTooltip.destroy();
|
|
239
|
+
vanillaTooltip = undefined;
|
|
240
|
+
}
|
|
241
|
+
isTooltipMounted = false;
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
142
245
|
// The eager path always mounted, so it always removes — unchanged from before. Only
|
|
143
246
|
// the deferred path can reach `destroyTooltip` without having mounted anything.
|
|
144
247
|
if (isDeferredTooltipEnabled && !isTooltipMounted) {
|
|
@@ -169,6 +272,14 @@ export const createPragmaticResizer = ({
|
|
|
169
272
|
onDragStart(args) {
|
|
170
273
|
state = 'resizing';
|
|
171
274
|
handleElement.classList.add('pm-breakout-resize-handle-container--active');
|
|
275
|
+
|
|
276
|
+
// The ADS tooltip hides itself on press; the vanilla one has no pointer-press
|
|
277
|
+
// listener, and its anchor moves with the rail, so it would follow the drag.
|
|
278
|
+
// Tearing it down hides it immediately; the next hover rebuilds it.
|
|
279
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
280
|
+
rightHandle.destroyTooltip();
|
|
281
|
+
leftHandle === null || leftHandle === void 0 ? void 0 : leftHandle.destroyTooltip();
|
|
282
|
+
}
|
|
172
283
|
onDragStart(args);
|
|
173
284
|
},
|
|
174
285
|
onDrag,
|
|
@@ -180,20 +291,57 @@ export const createPragmaticResizer = ({
|
|
|
180
291
|
}
|
|
181
292
|
});
|
|
182
293
|
};
|
|
183
|
-
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Parks the anchor where the pointer entered the rail, then opens the tooltip. The anchor is
|
|
297
|
+
* `pointer-events: none`, so the rail's hover listeners drive it. `offsetY` is already in the
|
|
298
|
+
* anchor's coordinate space, so no layout read is needed.
|
|
299
|
+
*
|
|
300
|
+
* Positions on enter only, matching the ADS tooltip as observed — though `Tooltip` does bind an
|
|
301
|
+
* `onMouseMove`, so start here if the two arms disagree on placement.
|
|
302
|
+
*/
|
|
303
|
+
const openTooltipAt = (anchor, event) => {
|
|
304
|
+
// Only the rail bindings pass an anchor; `target` and the hit boxes do not.
|
|
305
|
+
if (!anchor) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
anchor.style.setProperty(tooltipAnchorTopVar, `${event.offsetY}px`);
|
|
309
|
+
anchor.dispatchEvent(new MouseEvent('mouseenter'));
|
|
310
|
+
};
|
|
311
|
+
const closeTooltip = anchor => {
|
|
312
|
+
if (!anchor) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
anchor.dispatchEvent(new MouseEvent('mouseleave'));
|
|
316
|
+
};
|
|
317
|
+
const registerEvents = (element, hoverAnchor) => {
|
|
184
318
|
return [bind(element, {
|
|
185
319
|
type: 'mouseenter',
|
|
186
|
-
listener:
|
|
320
|
+
listener: event => {
|
|
187
321
|
if (isDeferredTooltipEnabled) {
|
|
188
322
|
rightHandle.mountTooltip();
|
|
189
323
|
leftHandle === null || leftHandle === void 0 ? void 0 : leftHandle.mountTooltip();
|
|
190
324
|
}
|
|
325
|
+
// Always mounts on first hover, so it also mounts when the deferred experiment is
|
|
326
|
+
// off. `mountTooltip` is a no-op once mounted, so the overlap is safe. At
|
|
327
|
+
// cleanup, drop the gate and delete the block above — this covers both.
|
|
328
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
329
|
+
rightHandle.mountTooltip();
|
|
330
|
+
leftHandle === null || leftHandle === void 0 ? void 0 : leftHandle.mountTooltip();
|
|
331
|
+
// After the mount above, so the tooltip always exists to be opened.
|
|
332
|
+
openTooltipAt(hoverAnchor, event);
|
|
333
|
+
}
|
|
191
334
|
rightHandle.rail.style.setProperty('opacity', '1');
|
|
192
335
|
leftHandle === null || leftHandle === void 0 ? void 0 : leftHandle.rail.style.setProperty('opacity', '1');
|
|
193
336
|
}
|
|
194
337
|
}), bind(element, {
|
|
195
338
|
type: 'mouseleave',
|
|
196
339
|
listener: () => {
|
|
340
|
+
// Ahead of the resize guard below: the ADS tooltip hides whenever the pointer
|
|
341
|
+
// leaves the rail, including part-way through a resize.
|
|
342
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
343
|
+
closeTooltip(hoverAnchor);
|
|
344
|
+
}
|
|
197
345
|
if (state === 'resizing') {
|
|
198
346
|
return;
|
|
199
347
|
}
|
|
@@ -204,9 +352,9 @@ export const createPragmaticResizer = ({
|
|
|
204
352
|
};
|
|
205
353
|
const unbindFns = leftHandle ?
|
|
206
354
|
// old code - left + right
|
|
207
|
-
[...registerEvents(target), ...registerEvents(rightHandle.handleHitBox), ...registerEvents(leftHandle.handleHitBox), ...registerEvents(rightHandle.rail), ...registerEvents(leftHandle.rail)] :
|
|
355
|
+
[...registerEvents(target), ...registerEvents(rightHandle.handleHitBox), ...registerEvents(leftHandle.handleHitBox), ...registerEvents(rightHandle.rail, rightHandle.tooltipAnchor), ...registerEvents(leftHandle.rail, leftHandle.tooltipAnchor)] :
|
|
208
356
|
// new code - right only
|
|
209
|
-
[...registerEvents(target), ...registerEvents(rightHandle.handleHitBox), ...registerEvents(rightHandle.rail)];
|
|
357
|
+
[...registerEvents(target), ...registerEvents(rightHandle.handleHitBox), ...registerEvents(rightHandle.rail, rightHandle.tooltipAnchor)];
|
|
210
358
|
const handleElement = 'rail';
|
|
211
359
|
const destroyFns = leftHandle ?
|
|
212
360
|
// old code - left + right
|
|
@@ -4,7 +4,6 @@ import { getChangedNodes, isReplaceDocOperation } from '@atlaskit/editor-common/
|
|
|
4
4
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
5
5
|
import { akEditorDefaultLayoutWidth, akEditorFullWidthLayoutWidth, akEditorMaxWidthLayoutWidth, akEditorCalculatedWideLayoutWidth } from '@atlaskit/editor-shared-styles';
|
|
6
6
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
7
|
-
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
8
7
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
9
8
|
import { handleKeyDown } from './handle-key-down';
|
|
10
9
|
import { ResizingMarkView } from './resizing-mark-view';
|
|
@@ -266,7 +265,7 @@ export const createResizingPlugin = (api, getIntl, nodeViewPortalProviderAPI, op
|
|
|
266
265
|
// This transaction normalizes a newly inserted node to the editor's default breakout
|
|
267
266
|
// width. It is not a user resize and must not become an undo step — in particular, a
|
|
268
267
|
// source synced block can be deleted permanently before Undo reaches this normalization.
|
|
269
|
-
return
|
|
268
|
+
return newTr.setMeta('addToHistory', false);
|
|
270
269
|
}
|
|
271
270
|
}
|
|
272
271
|
});
|
|
@@ -3,6 +3,7 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
|
3
3
|
import React, { useLayoutEffect, useState } from 'react';
|
|
4
4
|
import { bind } from 'bind-event-listener';
|
|
5
5
|
import { breakoutMessages as messages } from '@atlaskit/editor-common/messages';
|
|
6
|
+
import { VanillaTooltip } from '@atlaskit/editor-common/vanilla-tooltip';
|
|
6
7
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
7
8
|
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter';
|
|
8
9
|
import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/utils/disable-native-drag-preview';
|
|
@@ -23,6 +24,12 @@ var getNodeName = function getNodeName(nodeName) {
|
|
|
23
24
|
return 'node';
|
|
24
25
|
}
|
|
25
26
|
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Read by `.pm-breakout-resize-tooltip-anchor`, declared in `editor-core`'s `resizerStyles.ts` and
|
|
30
|
+
* its Compiled mirror. Rename in all three or the tooltip pins to the top of the rail.
|
|
31
|
+
*/
|
|
32
|
+
var tooltipAnchorTopVar = '--pm-breakout-resize-tooltip-anchor-top';
|
|
26
33
|
export var resizeHandleMessage = {
|
|
27
34
|
expand: messages.resizeExpand,
|
|
28
35
|
codeBlock: messages.resizeCodeBlock,
|
|
@@ -35,6 +42,10 @@ export var resizeHandleMessage = {
|
|
|
35
42
|
bodiedExtension: messages.resizeExtension,
|
|
36
43
|
multiBodiedExtension: messages.resizeExtension
|
|
37
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Remove during `platform_editor_use_vanilla_components` cleanup — replaced by
|
|
47
|
+
* `createHandleTooltip`, which does the same job without a React tree per resize handle.
|
|
48
|
+
*/
|
|
38
49
|
var RailWithTooltip = function RailWithTooltip(_ref) {
|
|
39
50
|
var rail = _ref.rail,
|
|
40
51
|
target = _ref.target,
|
|
@@ -61,13 +72,54 @@ var RailWithTooltip = function RailWithTooltip(_ref) {
|
|
|
61
72
|
}
|
|
62
73
|
}));
|
|
63
74
|
};
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
|
|
76
|
+
/** Identifies the vanilla tooltip for tests and debugging. */
|
|
77
|
+
var TOOLTIP_CLASS_NAME = 'pm-breakout-resize-tooltip';
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* `ak-editor-vanilla-tooltip-default` opts into `vanillaTooltipDefaultStyles` in the
|
|
81
|
+
* `EditorContentContainer` stylesheets, which still reaches a `[popover]` because it is painted in
|
|
82
|
+
* the top layer but stays in the DOM under `.ProseMirror`.
|
|
83
|
+
*/
|
|
84
|
+
var TOOLTIP_CLASS_NAMES = "ak-editor-vanilla-tooltip-default ".concat(TOOLTIP_CLASS_NAME);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Vanilla DOM equivalent of `RailWithTooltip`, behind `platform_editor_use_vanilla_components`.
|
|
88
|
+
*
|
|
89
|
+
* Anchored to a point rather than to the rail, which spans the node's full height and would strand
|
|
90
|
+
* the tooltip at the top of a tall node. This reproduces the control arm's `position="mouse"`,
|
|
91
|
+
* which latches the cursor as the tooltip opens and does not move it afterwards — so no cursor
|
|
92
|
+
* tracking is needed, just one style write per hover.
|
|
93
|
+
*/
|
|
94
|
+
var createHandleTooltip = function createHandleTooltip(_ref2) {
|
|
95
|
+
var anchor = _ref2.anchor,
|
|
69
96
|
intl = _ref2.intl,
|
|
70
|
-
|
|
97
|
+
target = _ref2.target;
|
|
98
|
+
// Only correct once ProseMirror has populated `contentDOM`, so callers must build on first
|
|
99
|
+
// hover — earlier reads fall back to the generic 'node' label.
|
|
100
|
+
var node = target.querySelector('[data-prosemirror-node-name]');
|
|
101
|
+
var nodeName = getNodeName(node === null || node === void 0 ? void 0 : node.dataset.prosemirrorNodeName);
|
|
102
|
+
return new VanillaTooltip(anchor, intl.formatMessage(resizeHandleMessage[nodeName]),
|
|
103
|
+
// Let the tooltip generate its own id.
|
|
104
|
+
undefined, TOOLTIP_CLASS_NAMES,
|
|
105
|
+
// Default delay, matching the ADS tooltip.
|
|
106
|
+
300, {
|
|
107
|
+
// The only thing the shared look does not cover. Already inherited from the anchor,
|
|
108
|
+
// but set explicitly so a change there can never let this swallow a resize drag.
|
|
109
|
+
pointerEvents: 'none'
|
|
110
|
+
},
|
|
111
|
+
// No `onShow`.
|
|
112
|
+
undefined,
|
|
113
|
+
// Below the cursor, matching the control arm's `position="mouse"`.
|
|
114
|
+
'bottom');
|
|
115
|
+
};
|
|
116
|
+
export var createPragmaticResizer = function createPragmaticResizer(_ref3) {
|
|
117
|
+
var target = _ref3.target,
|
|
118
|
+
_onDragStart = _ref3.onDragStart,
|
|
119
|
+
onDrag = _ref3.onDrag,
|
|
120
|
+
_onDrop = _ref3.onDrop,
|
|
121
|
+
intl = _ref3.intl,
|
|
122
|
+
nodeViewPortalProviderAPI = _ref3.nodeViewPortalProviderAPI;
|
|
71
123
|
var state = 'default';
|
|
72
124
|
// Read once per resizer: `isExperimentEnabled` fires an exposure event, and `registerEvents` is
|
|
73
125
|
// bound to several elements so checking it inside the handler would emit one per element hovered.
|
|
@@ -93,6 +145,21 @@ export var createPragmaticResizer = function createPragmaticResizer(_ref2) {
|
|
|
93
145
|
thumb.classList.add('pm-breakout-resize-handle-thumb');
|
|
94
146
|
thumb.style.pointerEvents = 'none';
|
|
95
147
|
rail.appendChild(thumb);
|
|
148
|
+
|
|
149
|
+
// Marks the point the pointer entered the rail, and anchors the tooltip there: zero height
|
|
150
|
+
// so it is a point not a box, full rail width so the tooltip centres like the control arm.
|
|
151
|
+
//
|
|
152
|
+
// Must stay a child of `rail` — `openTooltipAt` sets `top` from the rail's `offsetY`, which
|
|
153
|
+
// only lines up while the rail is the containing block. jsdom cannot catch a wrong parent.
|
|
154
|
+
//
|
|
155
|
+
// Not built on the control arm, which must keep byte-identical DOM.
|
|
156
|
+
var tooltipAnchor;
|
|
157
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
158
|
+
// Positioned by `.pm-breakout-resize-tooltip-anchor`; only the pointer's Y is passed in.
|
|
159
|
+
tooltipAnchor = document.createElement('div');
|
|
160
|
+
tooltipAnchor.classList.add('pm-breakout-resize-tooltip-anchor');
|
|
161
|
+
rail.appendChild(tooltipAnchor);
|
|
162
|
+
}
|
|
96
163
|
var tooltipContainer = document.createElement('div');
|
|
97
164
|
tooltipContainer.classList.add('pm-breakout-resize-handle-rail-wrapper');
|
|
98
165
|
handle.appendChild(tooltipContainer);
|
|
@@ -107,17 +174,29 @@ export var createPragmaticResizer = function createPragmaticResizer(_ref2) {
|
|
|
107
174
|
});
|
|
108
175
|
}, tooltipContainer, key);
|
|
109
176
|
};
|
|
177
|
+
var vanillaTooltip;
|
|
110
178
|
|
|
111
|
-
//
|
|
112
|
-
//
|
|
179
|
+
// Tracks whether there is anything to tear down. On the deferred path the tooltip mounts
|
|
180
|
+
// on first hover and so may never mount at all.
|
|
113
181
|
var isTooltipMounted = false;
|
|
114
182
|
var mountTooltip = function mountTooltip() {
|
|
115
183
|
if (isTooltipMounted) {
|
|
116
184
|
return;
|
|
117
185
|
}
|
|
118
186
|
isTooltipMounted = true;
|
|
187
|
+
|
|
188
|
+
// `tooltipAnchor` is always set when the experiment is on; the check also narrows it.
|
|
189
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components') && tooltipAnchor) {
|
|
190
|
+
vanillaTooltip = createHandleTooltip({
|
|
191
|
+
anchor: tooltipAnchor,
|
|
192
|
+
target: target,
|
|
193
|
+
intl: intl
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
119
198
|
// `RailWithTooltip`'s ref moves `rail` into the tooltip wrapper, so the DOM ends up
|
|
120
|
-
// identical to the
|
|
199
|
+
// identical to the pre-mount state below.
|
|
121
200
|
renderTooltip();
|
|
122
201
|
};
|
|
123
202
|
|
|
@@ -130,22 +209,45 @@ export var createPragmaticResizer = function createPragmaticResizer(_ref2) {
|
|
|
130
209
|
// `mouseenter` of the node (see `registerEvents` below). The rail has `opacity: 0` until
|
|
131
210
|
// the pointer is inside the node, so the pre-mount DOM is never visible, and the
|
|
132
211
|
// `mouseenter` fires on the whole node — long before the pointer reaches the 7px handle.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
//
|
|
212
|
+
//
|
|
213
|
+
// Parking the rail also needs `width: 100%`: without the ADS tooltip's wrappers (stretched
|
|
214
|
+
// by `pragmaticResizerStylesForTooltip`) it would size to its content as a flex item.
|
|
215
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
216
|
+
// Always parks, and always defers whatever `platform_editor_reduce_event_listener_count`
|
|
217
|
+
// says: no portal means no listener cost, and first hover is when the node name reads
|
|
218
|
+
// correctly.
|
|
138
219
|
rail.style.width = '100%';
|
|
139
220
|
tooltipContainer.appendChild(rail);
|
|
140
221
|
} else {
|
|
141
|
-
|
|
222
|
+
// Remove this while at `platform_editor_use_vanilla_components` cleanup.
|
|
223
|
+
if (isDeferredTooltipEnabled) {
|
|
224
|
+
rail.style.width = '100%';
|
|
225
|
+
tooltipContainer.appendChild(rail);
|
|
226
|
+
} else {
|
|
227
|
+
// Not `mountTooltip`: that would set `isTooltipMounted` where the control arm never did.
|
|
228
|
+
renderTooltip();
|
|
229
|
+
}
|
|
142
230
|
}
|
|
143
231
|
return {
|
|
144
232
|
handle: handle,
|
|
145
233
|
rail: rail,
|
|
234
|
+
// Exposed so the rail's hover listeners can position and open the tooltip.
|
|
235
|
+
tooltipAnchor: tooltipAnchor,
|
|
146
236
|
handleHitBox: handleHitBox,
|
|
147
237
|
mountTooltip: mountTooltip,
|
|
148
238
|
destroyTooltip: function destroyTooltip() {
|
|
239
|
+
// Re-read is safe: the gate is fixed for the page, so it matches what `mountTooltip`
|
|
240
|
+
// saw. At cleanup, keep this body and delete the control arm below.
|
|
241
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
242
|
+
// May never have mounted, and there is no portal to remove either way.
|
|
243
|
+
if (vanillaTooltip) {
|
|
244
|
+
vanillaTooltip.destroy();
|
|
245
|
+
vanillaTooltip = undefined;
|
|
246
|
+
}
|
|
247
|
+
isTooltipMounted = false;
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
149
251
|
// The eager path always mounted, so it always removes — unchanged from before. Only
|
|
150
252
|
// the deferred path can reach `destroyTooltip` without having mounted anything.
|
|
151
253
|
if (isDeferredTooltipEnabled && !isTooltipMounted) {
|
|
@@ -162,8 +264,8 @@ export var createPragmaticResizer = function createPragmaticResizer(_ref2) {
|
|
|
162
264
|
var registerHandle = function registerHandle(handleElement, handleSide) {
|
|
163
265
|
return draggable({
|
|
164
266
|
element: handleElement,
|
|
165
|
-
onGenerateDragPreview: function onGenerateDragPreview(
|
|
166
|
-
var nativeSetDragImage =
|
|
267
|
+
onGenerateDragPreview: function onGenerateDragPreview(_ref4) {
|
|
268
|
+
var nativeSetDragImage = _ref4.nativeSetDragImage;
|
|
167
269
|
disableNativeDragPreview({
|
|
168
270
|
nativeSetDragImage: nativeSetDragImage
|
|
169
271
|
});
|
|
@@ -177,6 +279,14 @@ export var createPragmaticResizer = function createPragmaticResizer(_ref2) {
|
|
|
177
279
|
onDragStart: function onDragStart(args) {
|
|
178
280
|
state = 'resizing';
|
|
179
281
|
handleElement.classList.add('pm-breakout-resize-handle-container--active');
|
|
282
|
+
|
|
283
|
+
// The ADS tooltip hides itself on press; the vanilla one has no pointer-press
|
|
284
|
+
// listener, and its anchor moves with the rail, so it would follow the drag.
|
|
285
|
+
// Tearing it down hides it immediately; the next hover rebuilds it.
|
|
286
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
287
|
+
rightHandle.destroyTooltip();
|
|
288
|
+
leftHandle === null || leftHandle === void 0 || leftHandle.destroyTooltip();
|
|
289
|
+
}
|
|
180
290
|
_onDragStart(args);
|
|
181
291
|
},
|
|
182
292
|
onDrag: onDrag,
|
|
@@ -188,20 +298,57 @@ export var createPragmaticResizer = function createPragmaticResizer(_ref2) {
|
|
|
188
298
|
}
|
|
189
299
|
});
|
|
190
300
|
};
|
|
191
|
-
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Parks the anchor where the pointer entered the rail, then opens the tooltip. The anchor is
|
|
304
|
+
* `pointer-events: none`, so the rail's hover listeners drive it. `offsetY` is already in the
|
|
305
|
+
* anchor's coordinate space, so no layout read is needed.
|
|
306
|
+
*
|
|
307
|
+
* Positions on enter only, matching the ADS tooltip as observed — though `Tooltip` does bind an
|
|
308
|
+
* `onMouseMove`, so start here if the two arms disagree on placement.
|
|
309
|
+
*/
|
|
310
|
+
var openTooltipAt = function openTooltipAt(anchor, event) {
|
|
311
|
+
// Only the rail bindings pass an anchor; `target` and the hit boxes do not.
|
|
312
|
+
if (!anchor) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
anchor.style.setProperty(tooltipAnchorTopVar, "".concat(event.offsetY, "px"));
|
|
316
|
+
anchor.dispatchEvent(new MouseEvent('mouseenter'));
|
|
317
|
+
};
|
|
318
|
+
var closeTooltip = function closeTooltip(anchor) {
|
|
319
|
+
if (!anchor) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
anchor.dispatchEvent(new MouseEvent('mouseleave'));
|
|
323
|
+
};
|
|
324
|
+
var registerEvents = function registerEvents(element, hoverAnchor) {
|
|
192
325
|
return [bind(element, {
|
|
193
326
|
type: 'mouseenter',
|
|
194
|
-
listener: function listener() {
|
|
327
|
+
listener: function listener(event) {
|
|
195
328
|
if (isDeferredTooltipEnabled) {
|
|
196
329
|
rightHandle.mountTooltip();
|
|
197
330
|
leftHandle === null || leftHandle === void 0 || leftHandle.mountTooltip();
|
|
198
331
|
}
|
|
332
|
+
// Always mounts on first hover, so it also mounts when the deferred experiment is
|
|
333
|
+
// off. `mountTooltip` is a no-op once mounted, so the overlap is safe. At
|
|
334
|
+
// cleanup, drop the gate and delete the block above — this covers both.
|
|
335
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
336
|
+
rightHandle.mountTooltip();
|
|
337
|
+
leftHandle === null || leftHandle === void 0 || leftHandle.mountTooltip();
|
|
338
|
+
// After the mount above, so the tooltip always exists to be opened.
|
|
339
|
+
openTooltipAt(hoverAnchor, event);
|
|
340
|
+
}
|
|
199
341
|
rightHandle.rail.style.setProperty('opacity', '1');
|
|
200
342
|
leftHandle === null || leftHandle === void 0 || leftHandle.rail.style.setProperty('opacity', '1');
|
|
201
343
|
}
|
|
202
344
|
}), bind(element, {
|
|
203
345
|
type: 'mouseleave',
|
|
204
346
|
listener: function listener() {
|
|
347
|
+
// Ahead of the resize guard below: the ADS tooltip hides whenever the pointer
|
|
348
|
+
// leaves the rail, including part-way through a resize.
|
|
349
|
+
if (isExperimentEnabled('platform_editor_use_vanilla_components')) {
|
|
350
|
+
closeTooltip(hoverAnchor);
|
|
351
|
+
}
|
|
205
352
|
if (state === 'resizing') {
|
|
206
353
|
return;
|
|
207
354
|
}
|
|
@@ -211,8 +358,8 @@ export var createPragmaticResizer = function createPragmaticResizer(_ref2) {
|
|
|
211
358
|
})];
|
|
212
359
|
};
|
|
213
360
|
var unbindFns = leftHandle ? // old code - left + right
|
|
214
|
-
[].concat(_toConsumableArray(registerEvents(target)), _toConsumableArray(registerEvents(rightHandle.handleHitBox)), _toConsumableArray(registerEvents(leftHandle.handleHitBox)), _toConsumableArray(registerEvents(rightHandle.rail)), _toConsumableArray(registerEvents(leftHandle.rail))) : // new code - right only
|
|
215
|
-
[].concat(_toConsumableArray(registerEvents(target)), _toConsumableArray(registerEvents(rightHandle.handleHitBox)), _toConsumableArray(registerEvents(rightHandle.rail)));
|
|
361
|
+
[].concat(_toConsumableArray(registerEvents(target)), _toConsumableArray(registerEvents(rightHandle.handleHitBox)), _toConsumableArray(registerEvents(leftHandle.handleHitBox)), _toConsumableArray(registerEvents(rightHandle.rail, rightHandle.tooltipAnchor)), _toConsumableArray(registerEvents(leftHandle.rail, leftHandle.tooltipAnchor))) : // new code - right only
|
|
362
|
+
[].concat(_toConsumableArray(registerEvents(target)), _toConsumableArray(registerEvents(rightHandle.handleHitBox)), _toConsumableArray(registerEvents(rightHandle.rail, rightHandle.tooltipAnchor)));
|
|
216
363
|
var handleElement = 'rail';
|
|
217
364
|
var destroyFns = leftHandle ?
|
|
218
365
|
// old code - left + right
|
|
@@ -7,7 +7,6 @@ import { getChangedNodes, isReplaceDocOperation } from '@atlaskit/editor-common/
|
|
|
7
7
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
8
8
|
import { akEditorDefaultLayoutWidth, akEditorFullWidthLayoutWidth, akEditorMaxWidthLayoutWidth, akEditorCalculatedWideLayoutWidth } from '@atlaskit/editor-shared-styles';
|
|
9
9
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
10
|
-
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
11
10
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
12
11
|
import { handleKeyDown } from './handle-key-down';
|
|
13
12
|
import { ResizingMarkView } from './resizing-mark-view';
|
|
@@ -258,7 +257,7 @@ export var createResizingPlugin = function createResizingPlugin(api, getIntl, no
|
|
|
258
257
|
// This transaction normalizes a newly inserted node to the editor's default breakout
|
|
259
258
|
// width. It is not a user resize and must not become an undo step — in particular, a
|
|
260
259
|
// source synced block can be deleted permanently before Undo reaches this normalization.
|
|
261
|
-
return
|
|
260
|
+
return newTr.setMeta('addToHistory', false);
|
|
262
261
|
}
|
|
263
262
|
}
|
|
264
263
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-breakout",
|
|
3
|
-
"version": "16.0
|
|
3
|
+
"version": "16.1.0",
|
|
4
4
|
"description": "Breakout plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"atlaskit:src": "src/index.ts",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@atlaskit/adf-schema": "^57.
|
|
24
|
+
"@atlaskit/adf-schema": "^57.3.0",
|
|
25
25
|
"@atlaskit/editor-plugin-block-controls": "^17.3.0",
|
|
26
26
|
"@atlaskit/editor-plugin-editor-disabled": "^16.1.0",
|
|
27
27
|
"@atlaskit/editor-plugin-editor-viewmode": "^18.0.0",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"uuid": "^3.1.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@atlaskit/editor-common": "^120.
|
|
49
|
+
"@atlaskit/editor-common": "^120.13.0",
|
|
50
50
|
"react": "^18.2.0 || ^19.2.0",
|
|
51
51
|
"react-dom": "^18.2.0 || ^19.2.0",
|
|
52
52
|
"react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
|
|
@@ -64,9 +64,6 @@
|
|
|
64
64
|
},
|
|
65
65
|
"platform_editor_lovability_resize_gracefully": {
|
|
66
66
|
"type": "boolean"
|
|
67
|
-
},
|
|
68
|
-
"platform_editor_breakout_auto_normalization_history_patch": {
|
|
69
|
-
"type": "boolean"
|
|
70
67
|
}
|
|
71
68
|
}
|
|
72
69
|
}
|