@atlaskit/editor-plugin-block-controls 13.1.13 → 13.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/cjs/pm-plugins/main.js +69 -47
- package/dist/cjs/ui/drag-handle.js +71 -2
- package/dist/cjs/ui/global-styles.js +5 -1
- package/dist/es2019/pm-plugins/main.js +69 -47
- package/dist/es2019/ui/drag-handle.js +71 -2
- package/dist/es2019/ui/global-styles.js +5 -1
- package/dist/esm/pm-plugins/main.js +69 -47
- package/dist/esm/ui/drag-handle.js +71 -2
- package/dist/esm/ui/global-styles.js +5 -1
- package/package.json +16 -39
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-block-controls
|
|
2
2
|
|
|
3
|
+
## 13.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`2f56c78f969b8`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/2f56c78f969b8) -
|
|
8
|
+
Update i18n NPM package versions for teamwork-graph (Group 16)
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- Updated dependencies
|
|
13
|
+
|
|
14
|
+
## 13.1.14
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- [`79402109ef24e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/79402109ef24e) -
|
|
19
|
+
fix block controls reliable anchor glitch
|
|
20
|
+
- Updated dependencies
|
|
21
|
+
|
|
3
22
|
## 13.1.13
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
|
@@ -442,32 +442,34 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
|
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
444
|
if (shouldRemoveHandle) {
|
|
445
|
-
var _activeNode5, _activeNode6
|
|
445
|
+
var _activeNode5, _activeNode6;
|
|
446
446
|
var oldHandle = (0, _decorationsDragHandle.findHandleDec)(decorations, (_activeNode5 = activeNode) === null || _activeNode5 === void 0 ? void 0 : _activeNode5.pos, (_activeNode6 = activeNode) === null || _activeNode6 === void 0 ? void 0 : _activeNode6.pos);
|
|
447
447
|
decorations = decorations.remove(oldHandle);
|
|
448
448
|
// When removing the handle, also remove the anchor-marker node decorations
|
|
449
449
|
// (data-active-drag-handle / data-active-quick-insert) so the DOM attributes
|
|
450
450
|
// don't linger on nodes that are no longer active.
|
|
451
|
-
|
|
452
|
-
|
|
451
|
+
// Use full-range search (0..contentSize) rather than a point-range at activeNode.pos/rootPos:
|
|
452
|
+
// after a doc change (e.g. pressing Enter splits a node), DecorationSet.map() can shift the
|
|
453
|
+
// decoration to a different position, so a point-range search would miss it and leave a stale
|
|
454
|
+
// attribute on the wrong DOM node.
|
|
455
|
+
if ((0, _expValEquals.expValEquals)('platform_editor_controls_reliable_anchor', 'isEnabled', true)) {
|
|
456
|
+
var oldActiveNodeDec = (0, _decorationsDragHandle.findActiveDragHandleNodeDec)(decorations, 0, newState.doc.content.size);
|
|
453
457
|
decorations = decorations.remove(oldActiveNodeDec);
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
decorations = decorations.remove(oldActiveQuickInsertDec);
|
|
457
|
-
}
|
|
458
|
+
var oldActiveQuickInsertDec = (0, _decorationsQuickInsertButton.findActiveQuickInsertNodeDec)(decorations, 0, newState.doc.content.size);
|
|
459
|
+
decorations = decorations.remove(oldActiveQuickInsertDec);
|
|
458
460
|
}
|
|
459
461
|
// platform_editor_controls note: enables quick insert
|
|
460
462
|
if (flags.toolbarFlagsEnabled && quickInsertButtonEnabled) {
|
|
461
|
-
var
|
|
462
|
-
var oldQuickInsertButton = (0, _decorationsQuickInsertButton.findQuickInsertInsertButtonDecoration)(decorations, (
|
|
463
|
+
var _activeNode7, _activeNode8;
|
|
464
|
+
var oldQuickInsertButton = (0, _decorationsQuickInsertButton.findQuickInsertInsertButtonDecoration)(decorations, (_activeNode7 = activeNode) === null || _activeNode7 === void 0 ? void 0 : _activeNode7.rootPos, (_activeNode8 = activeNode) === null || _activeNode8 === void 0 ? void 0 : _activeNode8.rootPos);
|
|
463
465
|
decorations = decorations.remove(oldQuickInsertButton);
|
|
464
466
|
var _iterator = _createForOfIteratorHelper(nodeDecorationRegistry),
|
|
465
467
|
_step;
|
|
466
468
|
try {
|
|
467
469
|
var _loop2 = function _loop2() {
|
|
468
|
-
var
|
|
470
|
+
var _activeNode1, _activeNode10;
|
|
469
471
|
var factory = _step.value;
|
|
470
|
-
var old = decorations.find((
|
|
472
|
+
var old = decorations.find((_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.rootPos, (_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.rootPos, function (spec) {
|
|
471
473
|
return spec.type === factory.type;
|
|
472
474
|
});
|
|
473
475
|
decorations = decorations.remove(old);
|
|
@@ -487,8 +489,8 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
|
|
|
487
489
|
var _loop = function _loop() {
|
|
488
490
|
var factory = _step2.value;
|
|
489
491
|
if (factory.showInViewMode) {
|
|
490
|
-
var
|
|
491
|
-
var old = decorations.find((
|
|
492
|
+
var _activeNode9, _activeNode0;
|
|
493
|
+
var old = decorations.find((_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.rootPos, (_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.rootPos, function (spec) {
|
|
492
494
|
return spec.type === factory.type;
|
|
493
495
|
});
|
|
494
496
|
decorations = decorations.remove(old);
|
|
@@ -507,8 +509,8 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
|
|
|
507
509
|
} else if (api) {
|
|
508
510
|
var _latestActiveNode5, _latestActiveNode10;
|
|
509
511
|
if (shouldRecreateHandle && (!rightSideControlsEnabled || !isViewMode)) {
|
|
510
|
-
var
|
|
511
|
-
var _oldHandle = (0, _decorationsDragHandle.findHandleDec)(decorations, (
|
|
512
|
+
var _activeNode11, _activeNode12, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
|
|
513
|
+
var _oldHandle = (0, _decorationsDragHandle.findHandleDec)(decorations, (_activeNode11 = activeNode) === null || _activeNode11 === void 0 ? void 0 : _activeNode11.pos, (_activeNode12 = activeNode) === null || _activeNode12 === void 0 ? void 0 : _activeNode12.pos);
|
|
512
514
|
decorations = decorations.remove(_oldHandle);
|
|
513
515
|
var handleDec = (0, _decorationsDragHandle.dragHandleDecoration)({
|
|
514
516
|
api: api,
|
|
@@ -522,33 +524,49 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
|
|
|
522
524
|
editorState: newState
|
|
523
525
|
});
|
|
524
526
|
decorations = decorations.add(newState.doc, [handleDec]);
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
527
|
+
}
|
|
528
|
+
if ((0, _expValEquals.expValEquals)('platform_editor_controls_reliable_anchor', 'isEnabled', true) && latestActiveNode) {
|
|
529
|
+
// Recreate the drag handle node decoration when the active node changed,
|
|
530
|
+
// its content was modified, or the document changed (e.g. Enter splits a node).
|
|
531
|
+
// This runs independently of shouldRecreateHandle so that doc changes (like pressing
|
|
532
|
+
// Enter) correctly refresh the decoration even when the handle widget doesn't move.
|
|
533
|
+
// DecorationSet.map() can misplace the decoration after node splits/inserts.
|
|
534
|
+
var needsNodeDecUpdate = activeNodeChanged || isActiveNodeModified || tr.docChanged;
|
|
535
|
+
if (needsNodeDecUpdate) {
|
|
536
|
+
var _newState$doc$nodeAt;
|
|
537
|
+
var nodeSize = (_newState$doc$nodeAt = newState.doc.nodeAt(latestActiveNode.pos)) === null || _newState$doc$nodeAt === void 0 ? void 0 : _newState$doc$nodeAt.nodeSize;
|
|
538
|
+
if (nodeSize !== undefined) {
|
|
539
|
+
// Search the full doc range: after DecorationSet.map(), the decoration may have
|
|
540
|
+
// shifted away from activeNode?.pos (e.g. a node split moves it to the new paragraph).
|
|
541
|
+
var _oldActiveNodeDec = (0, _decorationsDragHandle.findActiveDragHandleNodeDec)(decorations, 0, newState.doc.content.size);
|
|
542
|
+
// Skip remove+add if the decoration is already at the correct position.
|
|
543
|
+
// On pure text keystrokes, DecorationSet.map() keeps the decoration correctly
|
|
544
|
+
// placed, so we avoid unnecessary churn on every character typed.
|
|
545
|
+
var dragHandleDecAlreadyCorrect = _oldActiveNodeDec.length === 1 && _oldActiveNodeDec[0].from === latestActiveNode.pos && _oldActiveNodeDec[0].to === latestActiveNode.pos + nodeSize;
|
|
546
|
+
if (!dragHandleDecAlreadyCorrect) {
|
|
536
547
|
decorations = decorations.remove(_oldActiveNodeDec);
|
|
537
548
|
decorations = decorations.add(newState.doc, [(0, _decorationsDragHandle.createActiveDragHandleNodeDecoration)(latestActiveNode.pos, nodeSize)]);
|
|
538
549
|
}
|
|
539
550
|
}
|
|
551
|
+
}
|
|
540
552
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
553
|
+
// The quick-insert decoration lives on the root node (rootPos), which can change
|
|
554
|
+
// independently of the edit-mode node — e.g. when only editorSizeChanged fires but
|
|
555
|
+
// rootActiveNodeChanged is also true. So we use a separate, broader guard that
|
|
556
|
+
// includes rootActiveNodeChanged to avoid leaving the attribute on a stale root node.
|
|
557
|
+
// tr.docChanged is included for the same reason as needsNodeDecUpdate: DecorationSet.map()
|
|
558
|
+
// can misplace the decoration after a node split, so we must refresh it on every doc change.
|
|
559
|
+
var needsQuickInsertDecUpdate = activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged || tr.docChanged;
|
|
560
|
+
if (needsQuickInsertDecUpdate && latestActiveNode.rootPos !== undefined) {
|
|
561
|
+
var _newState$doc$nodeAt2;
|
|
562
|
+
var rootNodeSize = (_newState$doc$nodeAt2 = newState.doc.nodeAt(latestActiveNode.rootPos)) === null || _newState$doc$nodeAt2 === void 0 ? void 0 : _newState$doc$nodeAt2.nodeSize;
|
|
563
|
+
if (rootNodeSize !== undefined) {
|
|
564
|
+
var _oldActiveQuickInsertDec = (0, _decorationsQuickInsertButton.findActiveQuickInsertNodeDec)(decorations, 0, newState.doc.content.size);
|
|
565
|
+
// Skip remove+add if the decoration is already at the correct position.
|
|
566
|
+
// On pure text keystrokes, DecorationSet.map() keeps the decoration correctly
|
|
567
|
+
// placed, so we avoid unnecessary churn on every character typed.
|
|
568
|
+
var quickInsertDecAlreadyCorrect = _oldActiveQuickInsertDec.length === 1 && _oldActiveQuickInsertDec[0].from === latestActiveNode.rootPos && _oldActiveQuickInsertDec[0].to === latestActiveNode.rootPos + rootNodeSize;
|
|
569
|
+
if (!quickInsertDecAlreadyCorrect) {
|
|
552
570
|
decorations = decorations.remove(_oldActiveQuickInsertDec);
|
|
553
571
|
decorations = decorations.add(newState.doc, [(0, _decorationsQuickInsertButton.createActiveQuickInsertNodeDecoration)(latestActiveNode.rootPos, rootNodeSize)]);
|
|
554
572
|
}
|
|
@@ -558,8 +576,8 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
|
|
|
558
576
|
if (shouldRecreateQuickInsertButton && ((_latestActiveNode5 = latestActiveNode) === null || _latestActiveNode5 === void 0 ? void 0 : _latestActiveNode5.rootPos) !== undefined &&
|
|
559
577
|
// platform_editor_controls note: enables quick insert
|
|
560
578
|
flags.toolbarFlagsEnabled && quickInsertButtonEnabled && (!rightSideControlsEnabled || !isViewMode)) {
|
|
561
|
-
var
|
|
562
|
-
var _oldQuickInsertButton = (0, _decorationsQuickInsertButton.findQuickInsertInsertButtonDecoration)(decorations, (
|
|
579
|
+
var _activeNode13, _activeNode14, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0, _latestActiveNode1;
|
|
580
|
+
var _oldQuickInsertButton = (0, _decorationsQuickInsertButton.findQuickInsertInsertButtonDecoration)(decorations, (_activeNode13 = activeNode) === null || _activeNode13 === void 0 ? void 0 : _activeNode13.rootPos, (_activeNode14 = activeNode) === null || _activeNode14 === void 0 ? void 0 : _activeNode14.rootPos);
|
|
563
581
|
decorations = decorations.remove(_oldQuickInsertButton);
|
|
564
582
|
var quickInsertButton = (0, _decorationsQuickInsertButton.quickInsertButtonDecoration)({
|
|
565
583
|
api: api,
|
|
@@ -578,12 +596,16 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
|
|
|
578
596
|
// Update quick insert node decoration when the quick insert button is recreated but
|
|
579
597
|
// the drag handle was NOT recreated (shouldRecreateHandle was false). When
|
|
580
598
|
// shouldRecreateHandle is true, the block above already handles this.
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
599
|
+
// Also skip when the needsQuickInsertDecUpdate block above already handled the update
|
|
600
|
+
// (i.e. activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged || tr.docChanged)
|
|
601
|
+
// — running both would remove and re-add a duplicate decoration.
|
|
602
|
+
if ((0, _expValEquals.expValEquals)('platform_editor_controls_reliable_anchor', 'isEnabled', true) && !shouldRecreateHandle && !(activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged || tr.docChanged) && ((_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.rootPos) !== undefined) {
|
|
603
|
+
var _newState$doc$nodeAt3;
|
|
604
|
+
// Use full-range search (0..contentSize) rather than a point-range at activeNode.rootPos:
|
|
605
|
+
// after a doc change, DecorationSet.map() can shift the decoration to a different position,
|
|
606
|
+
// so a point-range search would miss it and leave a stale attribute on the wrong DOM node.
|
|
607
|
+
var _oldActiveQuickInsertDec2 = (0, _decorationsQuickInsertButton.findActiveQuickInsertNodeDec)(decorations, 0, newState.doc.content.size);
|
|
608
|
+
decorations = decorations.remove(_oldActiveQuickInsertDec2);
|
|
587
609
|
var _rootNodeSize = (_newState$doc$nodeAt3 = newState.doc.nodeAt(latestActiveNode.rootPos)) === null || _newState$doc$nodeAt3 === void 0 ? void 0 : _newState$doc$nodeAt3.nodeSize;
|
|
588
610
|
if (_rootNodeSize !== undefined) {
|
|
589
611
|
decorations = decorations.add(newState.doc, [(0, _decorationsQuickInsertButton.createActiveQuickInsertNodeDecoration)(latestActiveNode.rootPos, _rootNodeSize)]);
|
|
@@ -594,7 +616,7 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
|
|
|
594
616
|
_step3;
|
|
595
617
|
try {
|
|
596
618
|
var _loop3 = function _loop3() {
|
|
597
|
-
var
|
|
619
|
+
var _activeNode15, _activeNode16;
|
|
598
620
|
var factory = _step3.value;
|
|
599
621
|
if (!latestActiveNode || latestActiveNode.rootPos === undefined) {
|
|
600
622
|
return 0; // continue
|
|
@@ -608,7 +630,7 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
|
|
|
608
630
|
rootAnchorName: latestActiveNode.rootAnchorName,
|
|
609
631
|
rootNodeType: latestActiveNode.rootNodeType
|
|
610
632
|
};
|
|
611
|
-
var old = decorations.find((
|
|
633
|
+
var old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, function (spec) {
|
|
612
634
|
return spec.type === factory.type;
|
|
613
635
|
});
|
|
614
636
|
decorations = decorations.remove(old);
|
|
@@ -415,6 +415,10 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
|
|
|
415
415
|
_useState8 = (0, _slicedToArray2.default)(_useState7, 2),
|
|
416
416
|
positionStylesOld = _useState8[0],
|
|
417
417
|
setPositionStylesOld = _useState8[1];
|
|
418
|
+
// Tracks whether the initial position calculation has been performed at least once.
|
|
419
|
+
// The reliable-anchor early-return optimisation must not fire before the first calculation,
|
|
420
|
+
// otherwise positionStylesOld stays as { display: 'none' } and the handle is never shown.
|
|
421
|
+
var hasCalculatedInitialPosition = (0, _react.useRef)(false);
|
|
418
422
|
var _useState9 = (0, _react.useState)(Boolean(handleOptions === null || handleOptions === void 0 ? void 0 : handleOptions.isFocused)),
|
|
419
423
|
_useState0 = (0, _slicedToArray2.default)(_useState9, 2),
|
|
420
424
|
isFocused = _useState0[0],
|
|
@@ -901,7 +905,17 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
|
|
|
901
905
|
top: (0, _dragHandlePositions.getTopPosition)(dom, nodeType)
|
|
902
906
|
}, height);
|
|
903
907
|
}, [anchorName, getPos, view, nodeType, blockCardWidth, macroInteractionUpdates, anchorRectCache, isTopLevelNodeValue, isLayoutColumn]);
|
|
908
|
+
var isReliableAnchorEnabled = (0, _expValEquals.expValEquals)('platform_editor_controls_reliable_anchor', 'isEnabled', true);
|
|
909
|
+
var docDepForReliableAnchor = isReliableAnchorEnabled ? view.state.doc : undefined;
|
|
910
|
+
|
|
911
|
+
// Effect 1 (reliable-anchor ON): fires when non-doc deps change (e.g. blockCardWidth,
|
|
912
|
+
// anchorRectCache, macroInteractionUpdates, isTopLevelNodeValue, isLayoutColumn via
|
|
913
|
+
// calculatePositionOld) — always recalculates without the doc guard, so non-doc position
|
|
914
|
+
// changes are never incorrectly skipped.
|
|
904
915
|
(0, _react.useEffect)(function () {
|
|
916
|
+
if (!isReliableAnchorEnabled) {
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
905
919
|
var cleanUpTransitionListener;
|
|
906
920
|
if (nodeType === 'extension' || nodeType === 'embedCard') {
|
|
907
921
|
var dom = view.dom.querySelector("[".concat((0, _domAttrName.getAnchorAttrName)(), "=\"").concat(anchorName, "\"]"));
|
|
@@ -917,13 +931,62 @@ var DragHandle = exports.DragHandle = function DragHandle(_ref) {
|
|
|
917
931
|
}
|
|
918
932
|
var calcPos = requestAnimationFrame(function () {
|
|
919
933
|
setPositionStylesOld(calculatePositionOld());
|
|
934
|
+
hasCalculatedInitialPosition.current = true;
|
|
920
935
|
});
|
|
921
936
|
return function () {
|
|
922
937
|
var _cleanUpTransitionLis;
|
|
923
938
|
cancelAnimationFrame(calcPos);
|
|
924
939
|
(_cleanUpTransitionLis = cleanUpTransitionListener) === null || _cleanUpTransitionLis === void 0 || _cleanUpTransitionLis();
|
|
925
940
|
};
|
|
926
|
-
}, [calculatePositionOld, view.dom, anchorName, nodeType]);
|
|
941
|
+
}, [isReliableAnchorEnabled, calculatePositionOld, view.dom, anchorName, nodeType]);
|
|
942
|
+
|
|
943
|
+
// Effect 2 (reliable-anchor ON): fires when the doc changes — carries the DOM-attribute
|
|
944
|
+
// guard to skip recalc when a drag handle is active (pure keystroke during drag).
|
|
945
|
+
// Effect (reliable-anchor OFF): single combined effect, original behaviour.
|
|
946
|
+
(0, _react.useEffect)(function () {
|
|
947
|
+
if (isReliableAnchorEnabled) {
|
|
948
|
+
// Doc-change only effect: apply the DOM-attribute guard.
|
|
949
|
+
// React's dep comparison already ensures this only runs when docDepForReliableAnchor
|
|
950
|
+
// (i.e. view.state.doc) changed, so no manual ref tracking is needed.
|
|
951
|
+
if (!hasCalculatedInitialPosition.current) {
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
954
|
+
var _calcPos = requestAnimationFrame(function () {
|
|
955
|
+
var dom = view.dom.querySelector("[".concat((0, _domAttrName.getAnchorAttrName)(), "=\"").concat(anchorName, "\"]"));
|
|
956
|
+
if (dom !== null && dom !== void 0 && dom.hasAttribute(_consts2.ACTIVE_DRAG_HANDLE_ATTR)) {
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
setPositionStylesOld(calculatePositionOld());
|
|
960
|
+
});
|
|
961
|
+
return function () {
|
|
962
|
+
cancelAnimationFrame(_calcPos);
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// reliable-anchor OFF: original single-effect behaviour (no guard).
|
|
967
|
+
var cleanUpTransitionListener;
|
|
968
|
+
if (nodeType === 'extension' || nodeType === 'embedCard') {
|
|
969
|
+
var dom = view.dom.querySelector("[".concat((0, _domAttrName.getAnchorAttrName)(), "=\"").concat(anchorName, "\"]"));
|
|
970
|
+
if (!dom) {
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
cleanUpTransitionListener = (0, _bindEventListener.bind)(dom, {
|
|
974
|
+
type: 'transitionend',
|
|
975
|
+
listener: function listener() {
|
|
976
|
+
setPositionStylesOld(calculatePositionOld());
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
var calcPos = requestAnimationFrame(function () {
|
|
981
|
+
setPositionStylesOld(calculatePositionOld());
|
|
982
|
+
hasCalculatedInitialPosition.current = true;
|
|
983
|
+
});
|
|
984
|
+
return function () {
|
|
985
|
+
var _cleanUpTransitionLis2;
|
|
986
|
+
cancelAnimationFrame(calcPos);
|
|
987
|
+
(_cleanUpTransitionLis2 = cleanUpTransitionListener) === null || _cleanUpTransitionLis2 === void 0 || _cleanUpTransitionLis2();
|
|
988
|
+
};
|
|
989
|
+
}, [isReliableAnchorEnabled, calculatePositionOld, view.dom, anchorName, nodeType, docDepForReliableAnchor]);
|
|
927
990
|
(0, _react.useEffect)(function () {
|
|
928
991
|
if (handleOptions !== null && handleOptions !== void 0 && handleOptions.isFocused && buttonRef.current) {
|
|
929
992
|
var id = requestAnimationFrame(function () {
|
|
@@ -1192,9 +1255,15 @@ var DragHandleWithVisibility = exports.DragHandleWithVisibility = function DragH
|
|
|
1192
1255
|
// when hovering anywhere over the column, regardless of which side of the layoutSection the
|
|
1193
1256
|
// column is on. (The right-side remix button is a separate node decoration and is unaffected.)
|
|
1194
1257
|
var isLayoutColumn = nodeType === 'layoutColumn';
|
|
1258
|
+
|
|
1259
|
+
// Skip the right-side controlSide restriction for non-top-level nodes. The restriction exists
|
|
1260
|
+
// to avoid showing the drag handle and remix button simultaneously — but remix only applies to
|
|
1261
|
+
// top-level nodes, so non-top-level nodes should never be restricted.
|
|
1262
|
+
// Gated behind platform_editor_controls_reliable_anchor.
|
|
1263
|
+
var skipControlSideRestriction = (0, _expValEquals.expValEquals)('platform_editor_controls_reliable_anchor', 'isEnabled', true) && isTopLevelNode === false;
|
|
1195
1264
|
return (0, _react2.jsx)(_visibilityContainer.VisibilityContainer, {
|
|
1196
1265
|
api: api,
|
|
1197
|
-
controlSide: !isLayoutColumn && rightSideControlsEnabled ? 'left' : undefined,
|
|
1266
|
+
controlSide: !isLayoutColumn && !skipControlSideRestriction && rightSideControlsEnabled ? 'left' : undefined,
|
|
1198
1267
|
forceVisibleOnMouseOut: (0, _expValEquals.expValEquals)('platform_editor_drag_handle_keyboard_a11y', 'isEnabled', true) && !!(handleOptions !== null && handleOptions !== void 0 && handleOptions.isFocused)
|
|
1199
1268
|
}, (0, _react2.jsx)(DragHandle, {
|
|
1200
1269
|
view: view,
|
|
@@ -423,6 +423,10 @@ var GlobalStylesWrapper = exports.GlobalStylesWrapper = function GlobalStylesWra
|
|
|
423
423
|
exposure: true
|
|
424
424
|
}) ? (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? extendHoverZoneReducedNext : extendHoverZoneReduced : undefined,
|
|
425
425
|
// platform_editor_controls note: this allows drag handles to render on empty lines
|
|
426
|
-
toolbarFlagsEnabled ? undefined : withInlineNodeStyle, withDeleteLinesStyleFix, withMediaSingleStyleFix, legacyBreakoutWideLayoutStyle, headingWithIndentationInLayoutStyleFix, (0, _experiments.editorExperiment)('advanced_layouts', true) ? blockCardWithoutLayout : undefined, withDividerInPanelStyleFix, withFormatInLayoutStyleFix, (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withRelativePosStyleNext : withRelativePosStyle, topLevelNodeMarginStyles, (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withAnchorNameZindexStyleNext : withAnchorNameZindexStyle, (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && (0, _expValEquals.expValEquals)('advanced_layouts', 'isEnabled', true) ? layoutColumnExtendedHoverZone : layoutColumnWithoutHoverZone,
|
|
426
|
+
toolbarFlagsEnabled ? undefined : withInlineNodeStyle, withDeleteLinesStyleFix, withMediaSingleStyleFix, legacyBreakoutWideLayoutStyle, headingWithIndentationInLayoutStyleFix, (0, _experiments.editorExperiment)('advanced_layouts', true) ? blockCardWithoutLayout : undefined, withDividerInPanelStyleFix, withFormatInLayoutStyleFix, (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withRelativePosStyleNext : withRelativePosStyle, topLevelNodeMarginStyles, (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withAnchorNameZindexStyleNext : withAnchorNameZindexStyle, (0, _expValEquals.expValEquals)('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && (0, _expValEquals.expValEquals)('advanced_layouts', 'isEnabled', true) ? layoutColumnExtendedHoverZone : layoutColumnWithoutHoverZone, (0, _expValEquals.expValEquals)('platform_editor_controls_reliable_anchor', 'isEnabled', true) ?
|
|
427
|
+
// dragAnchorStyles sets anchor-name on ALL nodes during drag (needed for drop target positioning).
|
|
428
|
+
// staticControlsAnchorStyles handles anchor-name via node decorations for non-drag state.
|
|
429
|
+
// shouldRenderAnchors guards both: only apply anchor styles on browsers that support CSS anchor positioning.
|
|
430
|
+
shouldRenderAnchors ? isDragging ? dragAnchorStyles : staticControlsAnchorStyles : false : shouldRenderAnchors && (isDragging ? dragAnchorStyles : dragHandlerAnchorStyles)]
|
|
427
431
|
});
|
|
428
432
|
};
|
|
@@ -432,35 +432,37 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
|
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
434
|
if (shouldRemoveHandle) {
|
|
435
|
-
var _activeNode5, _activeNode6
|
|
435
|
+
var _activeNode5, _activeNode6;
|
|
436
436
|
const oldHandle = findHandleDec(decorations, (_activeNode5 = activeNode) === null || _activeNode5 === void 0 ? void 0 : _activeNode5.pos, (_activeNode6 = activeNode) === null || _activeNode6 === void 0 ? void 0 : _activeNode6.pos);
|
|
437
437
|
decorations = decorations.remove(oldHandle);
|
|
438
438
|
// When removing the handle, also remove the anchor-marker node decorations
|
|
439
439
|
// (data-active-drag-handle / data-active-quick-insert) so the DOM attributes
|
|
440
440
|
// don't linger on nodes that are no longer active.
|
|
441
|
-
|
|
442
|
-
|
|
441
|
+
// Use full-range search (0..contentSize) rather than a point-range at activeNode.pos/rootPos:
|
|
442
|
+
// after a doc change (e.g. pressing Enter splits a node), DecorationSet.map() can shift the
|
|
443
|
+
// decoration to a different position, so a point-range search would miss it and leave a stale
|
|
444
|
+
// attribute on the wrong DOM node.
|
|
445
|
+
if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true)) {
|
|
446
|
+
const oldActiveNodeDec = findActiveDragHandleNodeDec(decorations, 0, newState.doc.content.size);
|
|
443
447
|
decorations = decorations.remove(oldActiveNodeDec);
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
decorations = decorations.remove(oldActiveQuickInsertDec);
|
|
447
|
-
}
|
|
448
|
+
const oldActiveQuickInsertDec = findActiveQuickInsertNodeDec(decorations, 0, newState.doc.content.size);
|
|
449
|
+
decorations = decorations.remove(oldActiveQuickInsertDec);
|
|
448
450
|
}
|
|
449
451
|
// platform_editor_controls note: enables quick insert
|
|
450
452
|
if (flags.toolbarFlagsEnabled && quickInsertButtonEnabled) {
|
|
451
|
-
var
|
|
452
|
-
const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (
|
|
453
|
+
var _activeNode7, _activeNode8;
|
|
454
|
+
const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode7 = activeNode) === null || _activeNode7 === void 0 ? void 0 : _activeNode7.rootPos, (_activeNode8 = activeNode) === null || _activeNode8 === void 0 ? void 0 : _activeNode8.rootPos);
|
|
453
455
|
decorations = decorations.remove(oldQuickInsertButton);
|
|
454
456
|
for (const factory of nodeDecorationRegistry) {
|
|
455
|
-
var
|
|
456
|
-
const old = decorations.find((
|
|
457
|
+
var _activeNode9, _activeNode0;
|
|
458
|
+
const old = decorations.find((_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.rootPos, (_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.rootPos, spec => spec.type === factory.type);
|
|
457
459
|
decorations = decorations.remove(old);
|
|
458
460
|
}
|
|
459
461
|
if (rightSideControlsEnabled && isViewMode && fg('confluence_remix_button_right_side_block_fg')) {
|
|
460
462
|
for (const factory of nodeDecorationRegistry) {
|
|
461
463
|
if (factory.showInViewMode) {
|
|
462
|
-
var
|
|
463
|
-
const old = decorations.find((
|
|
464
|
+
var _activeNode1, _activeNode10;
|
|
465
|
+
const old = decorations.find((_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.rootPos, (_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.rootPos, spec => spec.type === factory.type);
|
|
464
466
|
decorations = decorations.remove(old);
|
|
465
467
|
}
|
|
466
468
|
}
|
|
@@ -469,8 +471,8 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
|
|
|
469
471
|
} else if (api) {
|
|
470
472
|
var _latestActiveNode5, _latestActiveNode10;
|
|
471
473
|
if (shouldRecreateHandle && (!rightSideControlsEnabled || !isViewMode)) {
|
|
472
|
-
var
|
|
473
|
-
const oldHandle = findHandleDec(decorations, (
|
|
474
|
+
var _activeNode11, _activeNode12, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
|
|
475
|
+
const oldHandle = findHandleDec(decorations, (_activeNode11 = activeNode) === null || _activeNode11 === void 0 ? void 0 : _activeNode11.pos, (_activeNode12 = activeNode) === null || _activeNode12 === void 0 ? void 0 : _activeNode12.pos);
|
|
474
476
|
decorations = decorations.remove(oldHandle);
|
|
475
477
|
const handleDec = dragHandleDecoration({
|
|
476
478
|
api,
|
|
@@ -484,33 +486,49 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
|
|
|
484
486
|
editorState: newState
|
|
485
487
|
});
|
|
486
488
|
decorations = decorations.add(newState.doc, [handleDec]);
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
489
|
+
}
|
|
490
|
+
if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && latestActiveNode) {
|
|
491
|
+
// Recreate the drag handle node decoration when the active node changed,
|
|
492
|
+
// its content was modified, or the document changed (e.g. Enter splits a node).
|
|
493
|
+
// This runs independently of shouldRecreateHandle so that doc changes (like pressing
|
|
494
|
+
// Enter) correctly refresh the decoration even when the handle widget doesn't move.
|
|
495
|
+
// DecorationSet.map() can misplace the decoration after node splits/inserts.
|
|
496
|
+
const needsNodeDecUpdate = activeNodeChanged || isActiveNodeModified || tr.docChanged;
|
|
497
|
+
if (needsNodeDecUpdate) {
|
|
498
|
+
var _newState$doc$nodeAt;
|
|
499
|
+
const nodeSize = (_newState$doc$nodeAt = newState.doc.nodeAt(latestActiveNode.pos)) === null || _newState$doc$nodeAt === void 0 ? void 0 : _newState$doc$nodeAt.nodeSize;
|
|
500
|
+
if (nodeSize !== undefined) {
|
|
501
|
+
// Search the full doc range: after DecorationSet.map(), the decoration may have
|
|
502
|
+
// shifted away from activeNode?.pos (e.g. a node split moves it to the new paragraph).
|
|
503
|
+
const oldActiveNodeDec = findActiveDragHandleNodeDec(decorations, 0, newState.doc.content.size);
|
|
504
|
+
// Skip remove+add if the decoration is already at the correct position.
|
|
505
|
+
// On pure text keystrokes, DecorationSet.map() keeps the decoration correctly
|
|
506
|
+
// placed, so we avoid unnecessary churn on every character typed.
|
|
507
|
+
const dragHandleDecAlreadyCorrect = oldActiveNodeDec.length === 1 && oldActiveNodeDec[0].from === latestActiveNode.pos && oldActiveNodeDec[0].to === latestActiveNode.pos + nodeSize;
|
|
508
|
+
if (!dragHandleDecAlreadyCorrect) {
|
|
498
509
|
decorations = decorations.remove(oldActiveNodeDec);
|
|
499
510
|
decorations = decorations.add(newState.doc, [createActiveDragHandleNodeDecoration(latestActiveNode.pos, nodeSize)]);
|
|
500
511
|
}
|
|
501
512
|
}
|
|
513
|
+
}
|
|
502
514
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
515
|
+
// The quick-insert decoration lives on the root node (rootPos), which can change
|
|
516
|
+
// independently of the edit-mode node — e.g. when only editorSizeChanged fires but
|
|
517
|
+
// rootActiveNodeChanged is also true. So we use a separate, broader guard that
|
|
518
|
+
// includes rootActiveNodeChanged to avoid leaving the attribute on a stale root node.
|
|
519
|
+
// tr.docChanged is included for the same reason as needsNodeDecUpdate: DecorationSet.map()
|
|
520
|
+
// can misplace the decoration after a node split, so we must refresh it on every doc change.
|
|
521
|
+
const needsQuickInsertDecUpdate = activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged || tr.docChanged;
|
|
522
|
+
if (needsQuickInsertDecUpdate && latestActiveNode.rootPos !== undefined) {
|
|
523
|
+
var _newState$doc$nodeAt2;
|
|
524
|
+
const rootNodeSize = (_newState$doc$nodeAt2 = newState.doc.nodeAt(latestActiveNode.rootPos)) === null || _newState$doc$nodeAt2 === void 0 ? void 0 : _newState$doc$nodeAt2.nodeSize;
|
|
525
|
+
if (rootNodeSize !== undefined) {
|
|
526
|
+
const oldActiveQuickInsertDec = findActiveQuickInsertNodeDec(decorations, 0, newState.doc.content.size);
|
|
527
|
+
// Skip remove+add if the decoration is already at the correct position.
|
|
528
|
+
// On pure text keystrokes, DecorationSet.map() keeps the decoration correctly
|
|
529
|
+
// placed, so we avoid unnecessary churn on every character typed.
|
|
530
|
+
const quickInsertDecAlreadyCorrect = oldActiveQuickInsertDec.length === 1 && oldActiveQuickInsertDec[0].from === latestActiveNode.rootPos && oldActiveQuickInsertDec[0].to === latestActiveNode.rootPos + rootNodeSize;
|
|
531
|
+
if (!quickInsertDecAlreadyCorrect) {
|
|
514
532
|
decorations = decorations.remove(oldActiveQuickInsertDec);
|
|
515
533
|
decorations = decorations.add(newState.doc, [createActiveQuickInsertNodeDecoration(latestActiveNode.rootPos, rootNodeSize)]);
|
|
516
534
|
}
|
|
@@ -520,8 +538,8 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
|
|
|
520
538
|
if (shouldRecreateQuickInsertButton && ((_latestActiveNode5 = latestActiveNode) === null || _latestActiveNode5 === void 0 ? void 0 : _latestActiveNode5.rootPos) !== undefined &&
|
|
521
539
|
// platform_editor_controls note: enables quick insert
|
|
522
540
|
flags.toolbarFlagsEnabled && quickInsertButtonEnabled && (!rightSideControlsEnabled || !isViewMode)) {
|
|
523
|
-
var
|
|
524
|
-
const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (
|
|
541
|
+
var _activeNode13, _activeNode14, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0, _latestActiveNode1;
|
|
542
|
+
const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode13 = activeNode) === null || _activeNode13 === void 0 ? void 0 : _activeNode13.rootPos, (_activeNode14 = activeNode) === null || _activeNode14 === void 0 ? void 0 : _activeNode14.rootPos);
|
|
525
543
|
decorations = decorations.remove(oldQuickInsertButton);
|
|
526
544
|
const quickInsertButton = quickInsertButtonDecoration({
|
|
527
545
|
api,
|
|
@@ -540,12 +558,16 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
|
|
|
540
558
|
// Update quick insert node decoration when the quick insert button is recreated but
|
|
541
559
|
// the drag handle was NOT recreated (shouldRecreateHandle was false). When
|
|
542
560
|
// shouldRecreateHandle is true, the block above already handles this.
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
561
|
+
// Also skip when the needsQuickInsertDecUpdate block above already handled the update
|
|
562
|
+
// (i.e. activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged || tr.docChanged)
|
|
563
|
+
// — running both would remove and re-add a duplicate decoration.
|
|
564
|
+
if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && !shouldRecreateHandle && !(activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged || tr.docChanged) && ((_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.rootPos) !== undefined) {
|
|
565
|
+
var _newState$doc$nodeAt3;
|
|
566
|
+
// Use full-range search (0..contentSize) rather than a point-range at activeNode.rootPos:
|
|
567
|
+
// after a doc change, DecorationSet.map() can shift the decoration to a different position,
|
|
568
|
+
// so a point-range search would miss it and leave a stale attribute on the wrong DOM node.
|
|
569
|
+
const oldActiveQuickInsertDec = findActiveQuickInsertNodeDec(decorations, 0, newState.doc.content.size);
|
|
570
|
+
decorations = decorations.remove(oldActiveQuickInsertDec);
|
|
549
571
|
const rootNodeSize = (_newState$doc$nodeAt3 = newState.doc.nodeAt(latestActiveNode.rootPos)) === null || _newState$doc$nodeAt3 === void 0 ? void 0 : _newState$doc$nodeAt3.nodeSize;
|
|
550
572
|
if (rootNodeSize !== undefined) {
|
|
551
573
|
decorations = decorations.add(newState.doc, [createActiveQuickInsertNodeDecoration(latestActiveNode.rootPos, rootNodeSize)]);
|
|
@@ -553,7 +575,7 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
|
|
|
553
575
|
}
|
|
554
576
|
if (rightSideControlsEnabled) {
|
|
555
577
|
for (const factory of nodeDecorationRegistry) {
|
|
556
|
-
var
|
|
578
|
+
var _activeNode15, _activeNode16;
|
|
557
579
|
if (!latestActiveNode || latestActiveNode.rootPos === undefined) {
|
|
558
580
|
continue;
|
|
559
581
|
}
|
|
@@ -566,7 +588,7 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
|
|
|
566
588
|
rootAnchorName: latestActiveNode.rootAnchorName,
|
|
567
589
|
rootNodeType: latestActiveNode.rootNodeType
|
|
568
590
|
};
|
|
569
|
-
const old = decorations.find((
|
|
591
|
+
const old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, spec => spec.type === factory.type);
|
|
570
592
|
decorations = decorations.remove(old);
|
|
571
593
|
|
|
572
594
|
// determines whether to show the decorations, see malleableUiPlugin.tsx
|
|
@@ -395,6 +395,10 @@ export const DragHandle = ({
|
|
|
395
395
|
const [positionStylesOld, setPositionStylesOld] = useState({
|
|
396
396
|
display: 'none'
|
|
397
397
|
});
|
|
398
|
+
// Tracks whether the initial position calculation has been performed at least once.
|
|
399
|
+
// The reliable-anchor early-return optimisation must not fire before the first calculation,
|
|
400
|
+
// otherwise positionStylesOld stays as { display: 'none' } and the handle is never shown.
|
|
401
|
+
const hasCalculatedInitialPosition = useRef(false);
|
|
398
402
|
const [isFocused, setIsFocused] = useState(Boolean(handleOptions === null || handleOptions === void 0 ? void 0 : handleOptions.isFocused));
|
|
399
403
|
const {
|
|
400
404
|
macroInteractionUpdates,
|
|
@@ -881,7 +885,17 @@ export const DragHandle = ({
|
|
|
881
885
|
...height
|
|
882
886
|
};
|
|
883
887
|
}, [anchorName, getPos, view, nodeType, blockCardWidth, macroInteractionUpdates, anchorRectCache, isTopLevelNodeValue, isLayoutColumn]);
|
|
888
|
+
const isReliableAnchorEnabled = expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true);
|
|
889
|
+
const docDepForReliableAnchor = isReliableAnchorEnabled ? view.state.doc : undefined;
|
|
890
|
+
|
|
891
|
+
// Effect 1 (reliable-anchor ON): fires when non-doc deps change (e.g. blockCardWidth,
|
|
892
|
+
// anchorRectCache, macroInteractionUpdates, isTopLevelNodeValue, isLayoutColumn via
|
|
893
|
+
// calculatePositionOld) — always recalculates without the doc guard, so non-doc position
|
|
894
|
+
// changes are never incorrectly skipped.
|
|
884
895
|
useEffect(() => {
|
|
896
|
+
if (!isReliableAnchorEnabled) {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
885
899
|
let cleanUpTransitionListener;
|
|
886
900
|
if (nodeType === 'extension' || nodeType === 'embedCard') {
|
|
887
901
|
const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${anchorName}"]`);
|
|
@@ -897,13 +911,62 @@ export const DragHandle = ({
|
|
|
897
911
|
}
|
|
898
912
|
const calcPos = requestAnimationFrame(() => {
|
|
899
913
|
setPositionStylesOld(calculatePositionOld());
|
|
914
|
+
hasCalculatedInitialPosition.current = true;
|
|
900
915
|
});
|
|
901
916
|
return () => {
|
|
902
917
|
var _cleanUpTransitionLis;
|
|
903
918
|
cancelAnimationFrame(calcPos);
|
|
904
919
|
(_cleanUpTransitionLis = cleanUpTransitionListener) === null || _cleanUpTransitionLis === void 0 ? void 0 : _cleanUpTransitionLis();
|
|
905
920
|
};
|
|
906
|
-
}, [calculatePositionOld, view.dom, anchorName, nodeType]);
|
|
921
|
+
}, [isReliableAnchorEnabled, calculatePositionOld, view.dom, anchorName, nodeType]);
|
|
922
|
+
|
|
923
|
+
// Effect 2 (reliable-anchor ON): fires when the doc changes — carries the DOM-attribute
|
|
924
|
+
// guard to skip recalc when a drag handle is active (pure keystroke during drag).
|
|
925
|
+
// Effect (reliable-anchor OFF): single combined effect, original behaviour.
|
|
926
|
+
useEffect(() => {
|
|
927
|
+
if (isReliableAnchorEnabled) {
|
|
928
|
+
// Doc-change only effect: apply the DOM-attribute guard.
|
|
929
|
+
// React's dep comparison already ensures this only runs when docDepForReliableAnchor
|
|
930
|
+
// (i.e. view.state.doc) changed, so no manual ref tracking is needed.
|
|
931
|
+
if (!hasCalculatedInitialPosition.current) {
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
const calcPos = requestAnimationFrame(() => {
|
|
935
|
+
const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${anchorName}"]`);
|
|
936
|
+
if (dom !== null && dom !== void 0 && dom.hasAttribute(ACTIVE_DRAG_HANDLE_ATTR)) {
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
setPositionStylesOld(calculatePositionOld());
|
|
940
|
+
});
|
|
941
|
+
return () => {
|
|
942
|
+
cancelAnimationFrame(calcPos);
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// reliable-anchor OFF: original single-effect behaviour (no guard).
|
|
947
|
+
let cleanUpTransitionListener;
|
|
948
|
+
if (nodeType === 'extension' || nodeType === 'embedCard') {
|
|
949
|
+
const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${anchorName}"]`);
|
|
950
|
+
if (!dom) {
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
cleanUpTransitionListener = bind(dom, {
|
|
954
|
+
type: 'transitionend',
|
|
955
|
+
listener: () => {
|
|
956
|
+
setPositionStylesOld(calculatePositionOld());
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
const calcPos = requestAnimationFrame(() => {
|
|
961
|
+
setPositionStylesOld(calculatePositionOld());
|
|
962
|
+
hasCalculatedInitialPosition.current = true;
|
|
963
|
+
});
|
|
964
|
+
return () => {
|
|
965
|
+
var _cleanUpTransitionLis2;
|
|
966
|
+
cancelAnimationFrame(calcPos);
|
|
967
|
+
(_cleanUpTransitionLis2 = cleanUpTransitionListener) === null || _cleanUpTransitionLis2 === void 0 ? void 0 : _cleanUpTransitionLis2();
|
|
968
|
+
};
|
|
969
|
+
}, [isReliableAnchorEnabled, calculatePositionOld, view.dom, anchorName, nodeType, docDepForReliableAnchor]);
|
|
907
970
|
useEffect(() => {
|
|
908
971
|
if (handleOptions !== null && handleOptions !== void 0 && handleOptions.isFocused && buttonRef.current) {
|
|
909
972
|
const id = requestAnimationFrame(() => {
|
|
@@ -1165,9 +1228,15 @@ export const DragHandleWithVisibility = ({
|
|
|
1165
1228
|
// when hovering anywhere over the column, regardless of which side of the layoutSection the
|
|
1166
1229
|
// column is on. (The right-side remix button is a separate node decoration and is unaffected.)
|
|
1167
1230
|
const isLayoutColumn = nodeType === 'layoutColumn';
|
|
1231
|
+
|
|
1232
|
+
// Skip the right-side controlSide restriction for non-top-level nodes. The restriction exists
|
|
1233
|
+
// to avoid showing the drag handle and remix button simultaneously — but remix only applies to
|
|
1234
|
+
// top-level nodes, so non-top-level nodes should never be restricted.
|
|
1235
|
+
// Gated behind platform_editor_controls_reliable_anchor.
|
|
1236
|
+
const skipControlSideRestriction = expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && isTopLevelNode === false;
|
|
1168
1237
|
return jsx(VisibilityContainer, {
|
|
1169
1238
|
api: api,
|
|
1170
|
-
controlSide: !isLayoutColumn && rightSideControlsEnabled ? 'left' : undefined,
|
|
1239
|
+
controlSide: !isLayoutColumn && !skipControlSideRestriction && rightSideControlsEnabled ? 'left' : undefined,
|
|
1171
1240
|
forceVisibleOnMouseOut: expValEquals('platform_editor_drag_handle_keyboard_a11y', 'isEnabled', true) && !!(handleOptions !== null && handleOptions !== void 0 && handleOptions.isFocused)
|
|
1172
1241
|
}, jsx(DragHandle, {
|
|
1173
1242
|
view: view,
|
|
@@ -514,6 +514,10 @@ export const GlobalStylesWrapper = ({
|
|
|
514
514
|
exposure: true
|
|
515
515
|
}) ? expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? extendHoverZoneReducedNext : extendHoverZoneReduced : undefined,
|
|
516
516
|
// platform_editor_controls note: this allows drag handles to render on empty lines
|
|
517
|
-
toolbarFlagsEnabled ? undefined : withInlineNodeStyle, withDeleteLinesStyleFix, withMediaSingleStyleFix, legacyBreakoutWideLayoutStyle, headingWithIndentationInLayoutStyleFix, editorExperiment('advanced_layouts', true) ? blockCardWithoutLayout : undefined, withDividerInPanelStyleFix, withFormatInLayoutStyleFix, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withRelativePosStyleNext : withRelativePosStyle, topLevelNodeMarginStyles, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withAnchorNameZindexStyleNext : withAnchorNameZindexStyle, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && expValEquals('advanced_layouts', 'isEnabled', true) ? layoutColumnExtendedHoverZone : layoutColumnWithoutHoverZone,
|
|
517
|
+
toolbarFlagsEnabled ? undefined : withInlineNodeStyle, withDeleteLinesStyleFix, withMediaSingleStyleFix, legacyBreakoutWideLayoutStyle, headingWithIndentationInLayoutStyleFix, editorExperiment('advanced_layouts', true) ? blockCardWithoutLayout : undefined, withDividerInPanelStyleFix, withFormatInLayoutStyleFix, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withRelativePosStyleNext : withRelativePosStyle, topLevelNodeMarginStyles, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withAnchorNameZindexStyleNext : withAnchorNameZindexStyle, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && expValEquals('advanced_layouts', 'isEnabled', true) ? layoutColumnExtendedHoverZone : layoutColumnWithoutHoverZone, expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) ?
|
|
518
|
+
// dragAnchorStyles sets anchor-name on ALL nodes during drag (needed for drop target positioning).
|
|
519
|
+
// staticControlsAnchorStyles handles anchor-name via node decorations for non-drag state.
|
|
520
|
+
// shouldRenderAnchors guards both: only apply anchor styles on browsers that support CSS anchor positioning.
|
|
521
|
+
shouldRenderAnchors ? isDragging ? dragAnchorStyles : staticControlsAnchorStyles : false : shouldRenderAnchors && (isDragging ? dragAnchorStyles : dragHandlerAnchorStyles)]
|
|
518
522
|
});
|
|
519
523
|
};
|
|
@@ -435,32 +435,34 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
435
435
|
}
|
|
436
436
|
}
|
|
437
437
|
if (shouldRemoveHandle) {
|
|
438
|
-
var _activeNode5, _activeNode6
|
|
438
|
+
var _activeNode5, _activeNode6;
|
|
439
439
|
var oldHandle = findHandleDec(decorations, (_activeNode5 = activeNode) === null || _activeNode5 === void 0 ? void 0 : _activeNode5.pos, (_activeNode6 = activeNode) === null || _activeNode6 === void 0 ? void 0 : _activeNode6.pos);
|
|
440
440
|
decorations = decorations.remove(oldHandle);
|
|
441
441
|
// When removing the handle, also remove the anchor-marker node decorations
|
|
442
442
|
// (data-active-drag-handle / data-active-quick-insert) so the DOM attributes
|
|
443
443
|
// don't linger on nodes that are no longer active.
|
|
444
|
-
|
|
445
|
-
|
|
444
|
+
// Use full-range search (0..contentSize) rather than a point-range at activeNode.pos/rootPos:
|
|
445
|
+
// after a doc change (e.g. pressing Enter splits a node), DecorationSet.map() can shift the
|
|
446
|
+
// decoration to a different position, so a point-range search would miss it and leave a stale
|
|
447
|
+
// attribute on the wrong DOM node.
|
|
448
|
+
if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true)) {
|
|
449
|
+
var oldActiveNodeDec = findActiveDragHandleNodeDec(decorations, 0, newState.doc.content.size);
|
|
446
450
|
decorations = decorations.remove(oldActiveNodeDec);
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
decorations = decorations.remove(oldActiveQuickInsertDec);
|
|
450
|
-
}
|
|
451
|
+
var oldActiveQuickInsertDec = findActiveQuickInsertNodeDec(decorations, 0, newState.doc.content.size);
|
|
452
|
+
decorations = decorations.remove(oldActiveQuickInsertDec);
|
|
451
453
|
}
|
|
452
454
|
// platform_editor_controls note: enables quick insert
|
|
453
455
|
if (flags.toolbarFlagsEnabled && quickInsertButtonEnabled) {
|
|
454
|
-
var
|
|
455
|
-
var oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (
|
|
456
|
+
var _activeNode7, _activeNode8;
|
|
457
|
+
var oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode7 = activeNode) === null || _activeNode7 === void 0 ? void 0 : _activeNode7.rootPos, (_activeNode8 = activeNode) === null || _activeNode8 === void 0 ? void 0 : _activeNode8.rootPos);
|
|
456
458
|
decorations = decorations.remove(oldQuickInsertButton);
|
|
457
459
|
var _iterator = _createForOfIteratorHelper(nodeDecorationRegistry),
|
|
458
460
|
_step;
|
|
459
461
|
try {
|
|
460
462
|
var _loop2 = function _loop2() {
|
|
461
|
-
var
|
|
463
|
+
var _activeNode1, _activeNode10;
|
|
462
464
|
var factory = _step.value;
|
|
463
|
-
var old = decorations.find((
|
|
465
|
+
var old = decorations.find((_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.rootPos, (_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.rootPos, function (spec) {
|
|
464
466
|
return spec.type === factory.type;
|
|
465
467
|
});
|
|
466
468
|
decorations = decorations.remove(old);
|
|
@@ -480,8 +482,8 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
480
482
|
var _loop = function _loop() {
|
|
481
483
|
var factory = _step2.value;
|
|
482
484
|
if (factory.showInViewMode) {
|
|
483
|
-
var
|
|
484
|
-
var old = decorations.find((
|
|
485
|
+
var _activeNode9, _activeNode0;
|
|
486
|
+
var old = decorations.find((_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.rootPos, (_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.rootPos, function (spec) {
|
|
485
487
|
return spec.type === factory.type;
|
|
486
488
|
});
|
|
487
489
|
decorations = decorations.remove(old);
|
|
@@ -500,8 +502,8 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
500
502
|
} else if (api) {
|
|
501
503
|
var _latestActiveNode5, _latestActiveNode10;
|
|
502
504
|
if (shouldRecreateHandle && (!rightSideControlsEnabled || !isViewMode)) {
|
|
503
|
-
var
|
|
504
|
-
var _oldHandle = findHandleDec(decorations, (
|
|
505
|
+
var _activeNode11, _activeNode12, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
|
|
506
|
+
var _oldHandle = findHandleDec(decorations, (_activeNode11 = activeNode) === null || _activeNode11 === void 0 ? void 0 : _activeNode11.pos, (_activeNode12 = activeNode) === null || _activeNode12 === void 0 ? void 0 : _activeNode12.pos);
|
|
505
507
|
decorations = decorations.remove(_oldHandle);
|
|
506
508
|
var handleDec = dragHandleDecoration({
|
|
507
509
|
api: api,
|
|
@@ -515,33 +517,49 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
515
517
|
editorState: newState
|
|
516
518
|
});
|
|
517
519
|
decorations = decorations.add(newState.doc, [handleDec]);
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
520
|
+
}
|
|
521
|
+
if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && latestActiveNode) {
|
|
522
|
+
// Recreate the drag handle node decoration when the active node changed,
|
|
523
|
+
// its content was modified, or the document changed (e.g. Enter splits a node).
|
|
524
|
+
// This runs independently of shouldRecreateHandle so that doc changes (like pressing
|
|
525
|
+
// Enter) correctly refresh the decoration even when the handle widget doesn't move.
|
|
526
|
+
// DecorationSet.map() can misplace the decoration after node splits/inserts.
|
|
527
|
+
var needsNodeDecUpdate = activeNodeChanged || isActiveNodeModified || tr.docChanged;
|
|
528
|
+
if (needsNodeDecUpdate) {
|
|
529
|
+
var _newState$doc$nodeAt;
|
|
530
|
+
var nodeSize = (_newState$doc$nodeAt = newState.doc.nodeAt(latestActiveNode.pos)) === null || _newState$doc$nodeAt === void 0 ? void 0 : _newState$doc$nodeAt.nodeSize;
|
|
531
|
+
if (nodeSize !== undefined) {
|
|
532
|
+
// Search the full doc range: after DecorationSet.map(), the decoration may have
|
|
533
|
+
// shifted away from activeNode?.pos (e.g. a node split moves it to the new paragraph).
|
|
534
|
+
var _oldActiveNodeDec = findActiveDragHandleNodeDec(decorations, 0, newState.doc.content.size);
|
|
535
|
+
// Skip remove+add if the decoration is already at the correct position.
|
|
536
|
+
// On pure text keystrokes, DecorationSet.map() keeps the decoration correctly
|
|
537
|
+
// placed, so we avoid unnecessary churn on every character typed.
|
|
538
|
+
var dragHandleDecAlreadyCorrect = _oldActiveNodeDec.length === 1 && _oldActiveNodeDec[0].from === latestActiveNode.pos && _oldActiveNodeDec[0].to === latestActiveNode.pos + nodeSize;
|
|
539
|
+
if (!dragHandleDecAlreadyCorrect) {
|
|
529
540
|
decorations = decorations.remove(_oldActiveNodeDec);
|
|
530
541
|
decorations = decorations.add(newState.doc, [createActiveDragHandleNodeDecoration(latestActiveNode.pos, nodeSize)]);
|
|
531
542
|
}
|
|
532
543
|
}
|
|
544
|
+
}
|
|
533
545
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
546
|
+
// The quick-insert decoration lives on the root node (rootPos), which can change
|
|
547
|
+
// independently of the edit-mode node — e.g. when only editorSizeChanged fires but
|
|
548
|
+
// rootActiveNodeChanged is also true. So we use a separate, broader guard that
|
|
549
|
+
// includes rootActiveNodeChanged to avoid leaving the attribute on a stale root node.
|
|
550
|
+
// tr.docChanged is included for the same reason as needsNodeDecUpdate: DecorationSet.map()
|
|
551
|
+
// can misplace the decoration after a node split, so we must refresh it on every doc change.
|
|
552
|
+
var needsQuickInsertDecUpdate = activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged || tr.docChanged;
|
|
553
|
+
if (needsQuickInsertDecUpdate && latestActiveNode.rootPos !== undefined) {
|
|
554
|
+
var _newState$doc$nodeAt2;
|
|
555
|
+
var rootNodeSize = (_newState$doc$nodeAt2 = newState.doc.nodeAt(latestActiveNode.rootPos)) === null || _newState$doc$nodeAt2 === void 0 ? void 0 : _newState$doc$nodeAt2.nodeSize;
|
|
556
|
+
if (rootNodeSize !== undefined) {
|
|
557
|
+
var _oldActiveQuickInsertDec = findActiveQuickInsertNodeDec(decorations, 0, newState.doc.content.size);
|
|
558
|
+
// Skip remove+add if the decoration is already at the correct position.
|
|
559
|
+
// On pure text keystrokes, DecorationSet.map() keeps the decoration correctly
|
|
560
|
+
// placed, so we avoid unnecessary churn on every character typed.
|
|
561
|
+
var quickInsertDecAlreadyCorrect = _oldActiveQuickInsertDec.length === 1 && _oldActiveQuickInsertDec[0].from === latestActiveNode.rootPos && _oldActiveQuickInsertDec[0].to === latestActiveNode.rootPos + rootNodeSize;
|
|
562
|
+
if (!quickInsertDecAlreadyCorrect) {
|
|
545
563
|
decorations = decorations.remove(_oldActiveQuickInsertDec);
|
|
546
564
|
decorations = decorations.add(newState.doc, [createActiveQuickInsertNodeDecoration(latestActiveNode.rootPos, rootNodeSize)]);
|
|
547
565
|
}
|
|
@@ -551,8 +569,8 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
551
569
|
if (shouldRecreateQuickInsertButton && ((_latestActiveNode5 = latestActiveNode) === null || _latestActiveNode5 === void 0 ? void 0 : _latestActiveNode5.rootPos) !== undefined &&
|
|
552
570
|
// platform_editor_controls note: enables quick insert
|
|
553
571
|
flags.toolbarFlagsEnabled && quickInsertButtonEnabled && (!rightSideControlsEnabled || !isViewMode)) {
|
|
554
|
-
var
|
|
555
|
-
var _oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (
|
|
572
|
+
var _activeNode13, _activeNode14, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0, _latestActiveNode1;
|
|
573
|
+
var _oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode13 = activeNode) === null || _activeNode13 === void 0 ? void 0 : _activeNode13.rootPos, (_activeNode14 = activeNode) === null || _activeNode14 === void 0 ? void 0 : _activeNode14.rootPos);
|
|
556
574
|
decorations = decorations.remove(_oldQuickInsertButton);
|
|
557
575
|
var quickInsertButton = quickInsertButtonDecoration({
|
|
558
576
|
api: api,
|
|
@@ -571,12 +589,16 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
571
589
|
// Update quick insert node decoration when the quick insert button is recreated but
|
|
572
590
|
// the drag handle was NOT recreated (shouldRecreateHandle was false). When
|
|
573
591
|
// shouldRecreateHandle is true, the block above already handles this.
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
592
|
+
// Also skip when the needsQuickInsertDecUpdate block above already handled the update
|
|
593
|
+
// (i.e. activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged || tr.docChanged)
|
|
594
|
+
// — running both would remove and re-add a duplicate decoration.
|
|
595
|
+
if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && !shouldRecreateHandle && !(activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged || tr.docChanged) && ((_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.rootPos) !== undefined) {
|
|
596
|
+
var _newState$doc$nodeAt3;
|
|
597
|
+
// Use full-range search (0..contentSize) rather than a point-range at activeNode.rootPos:
|
|
598
|
+
// after a doc change, DecorationSet.map() can shift the decoration to a different position,
|
|
599
|
+
// so a point-range search would miss it and leave a stale attribute on the wrong DOM node.
|
|
600
|
+
var _oldActiveQuickInsertDec2 = findActiveQuickInsertNodeDec(decorations, 0, newState.doc.content.size);
|
|
601
|
+
decorations = decorations.remove(_oldActiveQuickInsertDec2);
|
|
580
602
|
var _rootNodeSize = (_newState$doc$nodeAt3 = newState.doc.nodeAt(latestActiveNode.rootPos)) === null || _newState$doc$nodeAt3 === void 0 ? void 0 : _newState$doc$nodeAt3.nodeSize;
|
|
581
603
|
if (_rootNodeSize !== undefined) {
|
|
582
604
|
decorations = decorations.add(newState.doc, [createActiveQuickInsertNodeDecoration(latestActiveNode.rootPos, _rootNodeSize)]);
|
|
@@ -587,7 +609,7 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
587
609
|
_step3;
|
|
588
610
|
try {
|
|
589
611
|
var _loop3 = function _loop3() {
|
|
590
|
-
var
|
|
612
|
+
var _activeNode15, _activeNode16;
|
|
591
613
|
var factory = _step3.value;
|
|
592
614
|
if (!latestActiveNode || latestActiveNode.rootPos === undefined) {
|
|
593
615
|
return 0; // continue
|
|
@@ -601,7 +623,7 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
601
623
|
rootAnchorName: latestActiveNode.rootAnchorName,
|
|
602
624
|
rootNodeType: latestActiveNode.rootNodeType
|
|
603
625
|
};
|
|
604
|
-
var old = decorations.find((
|
|
626
|
+
var old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, function (spec) {
|
|
605
627
|
return spec.type === factory.type;
|
|
606
628
|
});
|
|
607
629
|
decorations = decorations.remove(old);
|
|
@@ -411,6 +411,10 @@ export var DragHandle = function DragHandle(_ref) {
|
|
|
411
411
|
_useState8 = _slicedToArray(_useState7, 2),
|
|
412
412
|
positionStylesOld = _useState8[0],
|
|
413
413
|
setPositionStylesOld = _useState8[1];
|
|
414
|
+
// Tracks whether the initial position calculation has been performed at least once.
|
|
415
|
+
// The reliable-anchor early-return optimisation must not fire before the first calculation,
|
|
416
|
+
// otherwise positionStylesOld stays as { display: 'none' } and the handle is never shown.
|
|
417
|
+
var hasCalculatedInitialPosition = useRef(false);
|
|
414
418
|
var _useState9 = useState(Boolean(handleOptions === null || handleOptions === void 0 ? void 0 : handleOptions.isFocused)),
|
|
415
419
|
_useState0 = _slicedToArray(_useState9, 2),
|
|
416
420
|
isFocused = _useState0[0],
|
|
@@ -897,7 +901,17 @@ export var DragHandle = function DragHandle(_ref) {
|
|
|
897
901
|
top: getTopPosition(dom, nodeType)
|
|
898
902
|
}, height);
|
|
899
903
|
}, [anchorName, getPos, view, nodeType, blockCardWidth, macroInteractionUpdates, anchorRectCache, isTopLevelNodeValue, isLayoutColumn]);
|
|
904
|
+
var isReliableAnchorEnabled = expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true);
|
|
905
|
+
var docDepForReliableAnchor = isReliableAnchorEnabled ? view.state.doc : undefined;
|
|
906
|
+
|
|
907
|
+
// Effect 1 (reliable-anchor ON): fires when non-doc deps change (e.g. blockCardWidth,
|
|
908
|
+
// anchorRectCache, macroInteractionUpdates, isTopLevelNodeValue, isLayoutColumn via
|
|
909
|
+
// calculatePositionOld) — always recalculates without the doc guard, so non-doc position
|
|
910
|
+
// changes are never incorrectly skipped.
|
|
900
911
|
useEffect(function () {
|
|
912
|
+
if (!isReliableAnchorEnabled) {
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
901
915
|
var cleanUpTransitionListener;
|
|
902
916
|
if (nodeType === 'extension' || nodeType === 'embedCard') {
|
|
903
917
|
var dom = view.dom.querySelector("[".concat(getAnchorAttrName(), "=\"").concat(anchorName, "\"]"));
|
|
@@ -913,13 +927,62 @@ export var DragHandle = function DragHandle(_ref) {
|
|
|
913
927
|
}
|
|
914
928
|
var calcPos = requestAnimationFrame(function () {
|
|
915
929
|
setPositionStylesOld(calculatePositionOld());
|
|
930
|
+
hasCalculatedInitialPosition.current = true;
|
|
916
931
|
});
|
|
917
932
|
return function () {
|
|
918
933
|
var _cleanUpTransitionLis;
|
|
919
934
|
cancelAnimationFrame(calcPos);
|
|
920
935
|
(_cleanUpTransitionLis = cleanUpTransitionListener) === null || _cleanUpTransitionLis === void 0 || _cleanUpTransitionLis();
|
|
921
936
|
};
|
|
922
|
-
}, [calculatePositionOld, view.dom, anchorName, nodeType]);
|
|
937
|
+
}, [isReliableAnchorEnabled, calculatePositionOld, view.dom, anchorName, nodeType]);
|
|
938
|
+
|
|
939
|
+
// Effect 2 (reliable-anchor ON): fires when the doc changes — carries the DOM-attribute
|
|
940
|
+
// guard to skip recalc when a drag handle is active (pure keystroke during drag).
|
|
941
|
+
// Effect (reliable-anchor OFF): single combined effect, original behaviour.
|
|
942
|
+
useEffect(function () {
|
|
943
|
+
if (isReliableAnchorEnabled) {
|
|
944
|
+
// Doc-change only effect: apply the DOM-attribute guard.
|
|
945
|
+
// React's dep comparison already ensures this only runs when docDepForReliableAnchor
|
|
946
|
+
// (i.e. view.state.doc) changed, so no manual ref tracking is needed.
|
|
947
|
+
if (!hasCalculatedInitialPosition.current) {
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
var _calcPos = requestAnimationFrame(function () {
|
|
951
|
+
var dom = view.dom.querySelector("[".concat(getAnchorAttrName(), "=\"").concat(anchorName, "\"]"));
|
|
952
|
+
if (dom !== null && dom !== void 0 && dom.hasAttribute(ACTIVE_DRAG_HANDLE_ATTR)) {
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
setPositionStylesOld(calculatePositionOld());
|
|
956
|
+
});
|
|
957
|
+
return function () {
|
|
958
|
+
cancelAnimationFrame(_calcPos);
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// reliable-anchor OFF: original single-effect behaviour (no guard).
|
|
963
|
+
var cleanUpTransitionListener;
|
|
964
|
+
if (nodeType === 'extension' || nodeType === 'embedCard') {
|
|
965
|
+
var dom = view.dom.querySelector("[".concat(getAnchorAttrName(), "=\"").concat(anchorName, "\"]"));
|
|
966
|
+
if (!dom) {
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
cleanUpTransitionListener = bind(dom, {
|
|
970
|
+
type: 'transitionend',
|
|
971
|
+
listener: function listener() {
|
|
972
|
+
setPositionStylesOld(calculatePositionOld());
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
var calcPos = requestAnimationFrame(function () {
|
|
977
|
+
setPositionStylesOld(calculatePositionOld());
|
|
978
|
+
hasCalculatedInitialPosition.current = true;
|
|
979
|
+
});
|
|
980
|
+
return function () {
|
|
981
|
+
var _cleanUpTransitionLis2;
|
|
982
|
+
cancelAnimationFrame(calcPos);
|
|
983
|
+
(_cleanUpTransitionLis2 = cleanUpTransitionListener) === null || _cleanUpTransitionLis2 === void 0 || _cleanUpTransitionLis2();
|
|
984
|
+
};
|
|
985
|
+
}, [isReliableAnchorEnabled, calculatePositionOld, view.dom, anchorName, nodeType, docDepForReliableAnchor]);
|
|
923
986
|
useEffect(function () {
|
|
924
987
|
if (handleOptions !== null && handleOptions !== void 0 && handleOptions.isFocused && buttonRef.current) {
|
|
925
988
|
var id = requestAnimationFrame(function () {
|
|
@@ -1188,9 +1251,15 @@ export var DragHandleWithVisibility = function DragHandleWithVisibility(_ref1) {
|
|
|
1188
1251
|
// when hovering anywhere over the column, regardless of which side of the layoutSection the
|
|
1189
1252
|
// column is on. (The right-side remix button is a separate node decoration and is unaffected.)
|
|
1190
1253
|
var isLayoutColumn = nodeType === 'layoutColumn';
|
|
1254
|
+
|
|
1255
|
+
// Skip the right-side controlSide restriction for non-top-level nodes. The restriction exists
|
|
1256
|
+
// to avoid showing the drag handle and remix button simultaneously — but remix only applies to
|
|
1257
|
+
// top-level nodes, so non-top-level nodes should never be restricted.
|
|
1258
|
+
// Gated behind platform_editor_controls_reliable_anchor.
|
|
1259
|
+
var skipControlSideRestriction = expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && isTopLevelNode === false;
|
|
1191
1260
|
return jsx(VisibilityContainer, {
|
|
1192
1261
|
api: api,
|
|
1193
|
-
controlSide: !isLayoutColumn && rightSideControlsEnabled ? 'left' : undefined,
|
|
1262
|
+
controlSide: !isLayoutColumn && !skipControlSideRestriction && rightSideControlsEnabled ? 'left' : undefined,
|
|
1194
1263
|
forceVisibleOnMouseOut: expValEquals('platform_editor_drag_handle_keyboard_a11y', 'isEnabled', true) && !!(handleOptions !== null && handleOptions !== void 0 && handleOptions.isFocused)
|
|
1195
1264
|
}, jsx(DragHandle, {
|
|
1196
1265
|
view: view,
|
|
@@ -416,6 +416,10 @@ export var GlobalStylesWrapper = function GlobalStylesWrapper(_ref) {
|
|
|
416
416
|
exposure: true
|
|
417
417
|
}) ? expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? extendHoverZoneReducedNext : extendHoverZoneReduced : undefined,
|
|
418
418
|
// platform_editor_controls note: this allows drag handles to render on empty lines
|
|
419
|
-
toolbarFlagsEnabled ? undefined : withInlineNodeStyle, withDeleteLinesStyleFix, withMediaSingleStyleFix, legacyBreakoutWideLayoutStyle, headingWithIndentationInLayoutStyleFix, editorExperiment('advanced_layouts', true) ? blockCardWithoutLayout : undefined, withDividerInPanelStyleFix, withFormatInLayoutStyleFix, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withRelativePosStyleNext : withRelativePosStyle, topLevelNodeMarginStyles, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withAnchorNameZindexStyleNext : withAnchorNameZindexStyle, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && expValEquals('advanced_layouts', 'isEnabled', true) ? layoutColumnExtendedHoverZone : layoutColumnWithoutHoverZone,
|
|
419
|
+
toolbarFlagsEnabled ? undefined : withInlineNodeStyle, withDeleteLinesStyleFix, withMediaSingleStyleFix, legacyBreakoutWideLayoutStyle, headingWithIndentationInLayoutStyleFix, editorExperiment('advanced_layouts', true) ? blockCardWithoutLayout : undefined, withDividerInPanelStyleFix, withFormatInLayoutStyleFix, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withRelativePosStyleNext : withRelativePosStyle, topLevelNodeMarginStyles, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withAnchorNameZindexStyleNext : withAnchorNameZindexStyle, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && expValEquals('advanced_layouts', 'isEnabled', true) ? layoutColumnExtendedHoverZone : layoutColumnWithoutHoverZone, expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) ?
|
|
420
|
+
// dragAnchorStyles sets anchor-name on ALL nodes during drag (needed for drop target positioning).
|
|
421
|
+
// staticControlsAnchorStyles handles anchor-name via node decorations for non-drag state.
|
|
422
|
+
// shouldRenderAnchors guards both: only apply anchor styles on browsers that support CSS anchor positioning.
|
|
423
|
+
shouldRenderAnchors ? isDragging ? dragAnchorStyles : staticControlsAnchorStyles : false : shouldRenderAnchors && (isDragging ? dragAnchorStyles : dragHandlerAnchorStyles)]
|
|
420
424
|
});
|
|
421
425
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-block-controls",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.2.0",
|
|
4
4
|
"description": "Block controls plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -15,9 +15,7 @@
|
|
|
15
15
|
"module": "dist/esm/index.js",
|
|
16
16
|
"module:es2019": "dist/es2019/index.js",
|
|
17
17
|
"types": "dist/types/index.d.ts",
|
|
18
|
-
"sideEffects": [
|
|
19
|
-
"*.compiled.css"
|
|
20
|
-
],
|
|
18
|
+
"sideEffects": ["*.compiled.css"],
|
|
21
19
|
"atlaskit:src": "src/index.ts",
|
|
22
20
|
"dependencies": {
|
|
23
21
|
"@atlaskit/browser-apis": "^1.1.0",
|
|
@@ -46,10 +44,10 @@
|
|
|
46
44
|
"@atlaskit/pragmatic-drag-and-drop": "^2.0.0",
|
|
47
45
|
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^3.0.0",
|
|
48
46
|
"@atlaskit/pragmatic-drag-and-drop-react-drop-indicator": "^4.1.0",
|
|
49
|
-
"@atlaskit/primitives": "^20.
|
|
47
|
+
"@atlaskit/primitives": "^20.5.0",
|
|
50
48
|
"@atlaskit/theme": "^26.1.0",
|
|
51
|
-
"@atlaskit/tmp-editor-statsig": "^121.
|
|
52
|
-
"@atlaskit/tokens": "^15.
|
|
49
|
+
"@atlaskit/tmp-editor-statsig": "^121.3.0",
|
|
50
|
+
"@atlaskit/tokens": "^15.5.0",
|
|
53
51
|
"@atlaskit/tooltip": "^23.1.0",
|
|
54
52
|
"@babel/runtime": "^7.0.0",
|
|
55
53
|
"@emotion/react": "^11.7.1",
|
|
@@ -59,47 +57,26 @@
|
|
|
59
57
|
"uuid": "^3.1.0"
|
|
60
58
|
},
|
|
61
59
|
"peerDependencies": {
|
|
62
|
-
"@atlaskit/editor-common": "^116.
|
|
60
|
+
"@atlaskit/editor-common": "^116.24.0",
|
|
63
61
|
"react": "^18.2.0",
|
|
64
62
|
"react-dom": "^18.2.0",
|
|
65
63
|
"react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
|
|
66
64
|
},
|
|
67
65
|
"techstack": {
|
|
68
66
|
"@atlassian/frontend": {
|
|
69
|
-
"import-structure": [
|
|
70
|
-
|
|
71
|
-
]
|
|
72
|
-
"circular-dependencies": [
|
|
73
|
-
"file-and-folder-level"
|
|
74
|
-
],
|
|
75
|
-
"code-structure": [
|
|
76
|
-
"editor-plugin"
|
|
77
|
-
]
|
|
67
|
+
"import-structure": ["atlassian-conventions"],
|
|
68
|
+
"circular-dependencies": ["file-and-folder-level"],
|
|
69
|
+
"code-structure": ["editor-plugin"]
|
|
78
70
|
},
|
|
79
71
|
"@repo/internal": {
|
|
80
72
|
"dom-events": "use-bind-event-listener",
|
|
81
|
-
"analytics": [
|
|
82
|
-
|
|
83
|
-
],
|
|
84
|
-
"
|
|
85
|
-
|
|
86
|
-
],
|
|
87
|
-
"
|
|
88
|
-
"react-context"
|
|
89
|
-
],
|
|
90
|
-
"ui-components": [
|
|
91
|
-
"lite-mode"
|
|
92
|
-
],
|
|
93
|
-
"deprecation": [
|
|
94
|
-
"no-deprecated-imports"
|
|
95
|
-
],
|
|
96
|
-
"styling": [
|
|
97
|
-
"emotion",
|
|
98
|
-
"compiled"
|
|
99
|
-
],
|
|
100
|
-
"imports": [
|
|
101
|
-
"import-no-extraneous-disable-for-examples-and-docs"
|
|
102
|
-
]
|
|
73
|
+
"analytics": ["analytics-next"],
|
|
74
|
+
"design-tokens": ["color"],
|
|
75
|
+
"theming": ["react-context"],
|
|
76
|
+
"ui-components": ["lite-mode"],
|
|
77
|
+
"deprecation": ["no-deprecated-imports"],
|
|
78
|
+
"styling": ["emotion", "compiled"],
|
|
79
|
+
"imports": ["import-no-extraneous-disable-for-examples-and-docs"]
|
|
103
80
|
}
|
|
104
81
|
},
|
|
105
82
|
"platform-feature-flags": {
|