@atlaskit/editor-plugin-block-controls 8.4.3 → 8.5.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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # @atlaskit/editor-plugin-block-controls
2
2
 
3
+ ## 8.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`db19daf3a712a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/db19daf3a712a) -
8
+ [ux] Add remix button to platform
9
+
10
+ ## 8.4.4
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+
3
16
  ## 8.4.3
4
17
 
5
18
  ### Patch Changes
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.remixButtonDecoration = exports.findRemixButtonDecoration = void 0;
8
+ var _react = require("react");
9
+ var _uuid = _interopRequireDefault(require("uuid"));
10
+ var _browserApis = require("@atlaskit/browser-apis");
11
+ var _view = require("@atlaskit/editor-prosemirror/view");
12
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
13
+ var _remixButton = require("../ui/remix-button");
14
+ // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
15
+
16
+ var TYPE_REMIX_BUTTON = 'REMIX_BUTTON';
17
+ var findRemixButtonDecoration = exports.findRemixButtonDecoration = function findRemixButtonDecoration(decorations, from, to) {
18
+ return decorations.find(from, to, function (spec) {
19
+ return spec.type === TYPE_REMIX_BUTTON;
20
+ });
21
+ };
22
+ /** Right-edge Remix button: same gutter as left controls (side: -4) but positioned at block right edge. */
23
+ var remixButtonDecoration = exports.remixButtonDecoration = function remixButtonDecoration(_ref) {
24
+ var api = _ref.api,
25
+ formatMessage = _ref.formatMessage,
26
+ rootPos = _ref.rootPos,
27
+ anchorName = _ref.anchorName,
28
+ nodeType = _ref.nodeType,
29
+ nodeViewPortalProviderAPI = _ref.nodeViewPortalProviderAPI,
30
+ rootAnchorName = _ref.rootAnchorName,
31
+ rootNodeType = _ref.rootNodeType;
32
+ // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
33
+ var key = (0, _uuid.default)();
34
+ var widgetSpec = {
35
+ side: -4,
36
+ type: TYPE_REMIX_BUTTON,
37
+ destroy: function destroy(_) {
38
+ if ((0, _platformFeatureFlags.fg)('platform_editor_fix_widget_destroy')) {
39
+ nodeViewPortalProviderAPI.remove(key);
40
+ }
41
+ }
42
+ };
43
+ return _view.Decoration.widget(rootPos, function (view, getPos) {
44
+ var doc = (0, _browserApis.getDocument)();
45
+ if (!doc) {
46
+ throw new Error('Document not available');
47
+ }
48
+ var element = doc.createElement('span');
49
+ element.style.display = 'block';
50
+ element.contentEditable = 'false';
51
+ element.setAttribute('data-blocks-remix-button-container', 'true');
52
+ element.setAttribute('data-testid', 'block-ctrl-remix-button-container');
53
+ nodeViewPortalProviderAPI.render(function () {
54
+ return /*#__PURE__*/(0, _react.createElement)(_remixButton.RemixButton, {
55
+ api: api,
56
+ getPos: getPos,
57
+ formatMessage: formatMessage,
58
+ view: view,
59
+ nodeType: nodeType,
60
+ anchorName: anchorName,
61
+ rootAnchorName: rootAnchorName,
62
+ rootNodeType: rootNodeType !== null && rootNodeType !== void 0 ? rootNodeType : nodeType
63
+ });
64
+ }, element, key, undefined, true);
65
+ return element;
66
+ }, widgetSpec);
67
+ };
@@ -31,6 +31,7 @@ var _decorationsDragHandle = require("./decorations-drag-handle");
31
31
  var _decorationsDropTarget = require("./decorations-drop-target");
32
32
  var _decorationsDropTargetActive = require("./decorations-drop-target-active");
33
33
  var _decorationsQuickInsertButton = require("./decorations-quick-insert-button");
34
+ var _decorationsRemixButton = require("./decorations-remix-button");
34
35
  var _handleMouseDown = require("./handle-mouse-down");
35
36
  var _handleMouseOver = require("./handle-mouse-over");
36
37
  var _keymap = require("./keymap");
@@ -439,12 +440,17 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
439
440
  var _activeNode7, _activeNode8;
440
441
  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);
441
442
  decorations = decorations.remove(oldQuickInsertButton);
443
+ if ((0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true)) {
444
+ var _activeNode9, _activeNode0;
445
+ var oldRemixButton = (0, _decorationsRemixButton.findRemixButtonDecoration)(decorations, (_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.rootPos, (_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.rootPos);
446
+ decorations = decorations.remove(oldRemixButton);
447
+ }
442
448
  }
443
449
  } else if (api) {
444
450
  var _latestActiveNode5;
445
451
  if (shouldRecreateHandle) {
446
- var _activeNode9, _activeNode0, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
447
- var _oldHandle = (0, _decorationsDragHandle.findHandleDec)(decorations, (_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.pos, (_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.pos);
452
+ var _activeNode1, _activeNode10, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
453
+ var _oldHandle = (0, _decorationsDragHandle.findHandleDec)(decorations, (_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.pos, (_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.pos);
448
454
  decorations = decorations.remove(_oldHandle);
449
455
  var handleDec = (0, _decorationsDragHandle.dragHandleDecoration)({
450
456
  api: api,
@@ -462,8 +468,8 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
462
468
  if (shouldRecreateQuickInsertButton && ((_latestActiveNode5 = latestActiveNode) === null || _latestActiveNode5 === void 0 ? void 0 : _latestActiveNode5.rootPos) !== undefined &&
463
469
  // platform_editor_controls note: enables quick insert
464
470
  flags.toolbarFlagsEnabled) {
465
- var _activeNode1, _activeNode10, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0;
466
- var _oldQuickInsertButton = (0, _decorationsQuickInsertButton.findQuickInsertInsertButtonDecoration)(decorations, (_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.rootPos, (_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.rootPos);
471
+ var _activeNode11, _activeNode12, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0;
472
+ var _oldQuickInsertButton = (0, _decorationsQuickInsertButton.findQuickInsertInsertButtonDecoration)(decorations, (_activeNode11 = activeNode) === null || _activeNode11 === void 0 ? void 0 : _activeNode11.rootPos, (_activeNode12 = activeNode) === null || _activeNode12 === void 0 ? void 0 : _activeNode12.rootPos);
467
473
  decorations = decorations.remove(_oldQuickInsertButton);
468
474
  var quickInsertButton = (0, _decorationsQuickInsertButton.quickInsertButtonDecoration)({
469
475
  api: api,
@@ -478,6 +484,28 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
478
484
  editorState: newState
479
485
  });
480
486
  decorations = decorations.add(newState.doc, [quickInsertButton]);
487
+ if ((0, _expValEquals.expValEquals)('confluence_remix_icon_right_side', 'isEnabled', true)) {
488
+ var _activeNode13, _activeNode14, _latestActiveNode1, _latestActiveNode10, _latestActiveNode11, _latestActiveNode12, _latestActiveNode13;
489
+ var _oldRemixButton = (0, _decorationsRemixButton.findRemixButtonDecoration)(decorations, (_activeNode13 = activeNode) === null || _activeNode13 === void 0 ? void 0 : _activeNode13.rootPos, (_activeNode14 = activeNode) === null || _activeNode14 === void 0 ? void 0 : _activeNode14.rootPos);
490
+ decorations = decorations.remove(_oldRemixButton);
491
+ var remixButton = (0, _decorationsRemixButton.remixButtonDecoration)({
492
+ api: api,
493
+ formatMessage: formatMessage,
494
+ anchorName: (_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.anchorName,
495
+ nodeType: (_latestActiveNode10 = latestActiveNode) === null || _latestActiveNode10 === void 0 ? void 0 : _latestActiveNode10.nodeType,
496
+ nodeViewPortalProviderAPI: nodeViewPortalProviderAPI,
497
+ rootPos: (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.rootPos,
498
+ rootAnchorName: (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.rootAnchorName,
499
+ rootNodeType: (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.rootNodeType,
500
+ editorState: newState
501
+ });
502
+ decorations = decorations.add(newState.doc, [remixButton]);
503
+ } else {
504
+ var _activeNode15, _activeNode16;
505
+ // Remove remix decoration when experiment is off so it disappears when flag is toggled
506
+ var _oldRemixButton2 = (0, _decorationsRemixButton.findRemixButtonDecoration)(decorations, (_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos);
507
+ decorations = decorations.remove(_oldRemixButton2);
508
+ }
481
509
  }
482
510
  }
483
511
 
@@ -525,12 +553,12 @@ var _apply = exports.apply = function apply(api, formatMessage, tr, currentState
525
553
  var newActiveNode;
526
554
  // platform_editor_controls note: enables quick insert
527
555
  if (flags.toolbarFlagsEnabled) {
528
- var _latestActiveNode1, _latestActiveNode10;
556
+ var _latestActiveNode14, _latestActiveNode15;
529
557
  // remove isEmptyDoc check and let decorations render and determine their own visibility
530
- newActiveNode = !(meta !== null && meta !== void 0 && meta.activeNode) && (0, _decorationsDragHandle.findHandleDec)(decorations, (_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.pos, (_latestActiveNode10 = latestActiveNode) === null || _latestActiveNode10 === void 0 ? void 0 : _latestActiveNode10.pos).length === 0 ? null : latestActiveNode;
558
+ newActiveNode = !(meta !== null && meta !== void 0 && meta.activeNode) && (0, _decorationsDragHandle.findHandleDec)(decorations, (_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.pos, (_latestActiveNode15 = latestActiveNode) === null || _latestActiveNode15 === void 0 ? void 0 : _latestActiveNode15.pos).length === 0 ? null : latestActiveNode;
531
559
  } else {
532
- var _latestActiveNode11, _latestActiveNode12;
533
- newActiveNode = isEmptyDoc || !(meta !== null && meta !== void 0 && meta.activeNode) && (0, _decorationsDragHandle.findHandleDec)(decorations, (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.pos, (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.pos).length === 0 ? null : latestActiveNode;
560
+ var _latestActiveNode16, _latestActiveNode17;
561
+ newActiveNode = isEmptyDoc || !(meta !== null && meta !== void 0 && meta.activeNode) && (0, _decorationsDragHandle.findHandleDec)(decorations, (_latestActiveNode16 = latestActiveNode) === null || _latestActiveNode16 === void 0 ? void 0 : _latestActiveNode16.pos, (_latestActiveNode17 = latestActiveNode) === null || _latestActiveNode17 === void 0 ? void 0 : _latestActiveNode17.pos).length === 0 ? null : latestActiveNode;
534
562
  }
535
563
  var isMenuOpenNew = isMenuOpen;
536
564
  if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu', 'isEnabled', true)) {
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.getLeftPositionForRootElement = void 0;
6
+ exports.getRightPositionForRootElement = exports.getLeftPositionForRootElement = void 0;
7
7
  var _consts = require("../../ui/consts");
8
8
  // Adapted from `src/pm-plugins/utils/drag-handle-positions.ts`
9
9
  // CHANGES - removed parentNodeType, use only for positioning widgets for top level element
@@ -22,4 +22,24 @@ var getLeftPositionForRootElement = exports.getLeftPositionForRootElement = func
22
22
  var relativeSpan = macroInteractionUpdates ? dom.querySelector('span.relative') : null;
23
23
  var leftAdjustment = relativeSpan ? relativeSpan.offsetLeft : 0;
24
24
  return getComputedStyle(innerContainer).transform === 'none' ? "".concat(innerContainer.offsetLeft + leftAdjustment - (0, _consts.rootElementGap)(nodeType) - widgetDimensions.width, "px") : "".concat(innerContainer.offsetLeft + leftAdjustment - innerContainer.offsetWidth / 2 - (0, _consts.rootElementGap)(nodeType) - widgetDimensions.width, "px");
25
+ };
26
+
27
+ /**
28
+ * Left position (in px) for a widget on the right edge of the block.
29
+ * Mirrors getLeftPositionForRootElement: when innerContainer is set (table, mediaSingle,
30
+ * extension, blockCard, embedCard) use it for the right edge so the button aligns to the
31
+ * content box and does not overlap; otherwise use the block (dom).
32
+ */
33
+ var getRightPositionForRootElement = exports.getRightPositionForRootElement = function getRightPositionForRootElement(dom, nodeType, widgetDimensions, innerContainer, macroInteractionUpdates) {
34
+ if (!dom) {
35
+ return 'auto';
36
+ }
37
+ var relativeSpan = macroInteractionUpdates ? dom.querySelector('span.relative') : null;
38
+ var leftAdjustment = relativeSpan ? relativeSpan.offsetLeft : 0;
39
+ var gap = (0, _consts.rootElementGap)(nodeType);
40
+ var width = widgetDimensions.width;
41
+ if (!innerContainer) {
42
+ return "".concat(dom.offsetLeft + leftAdjustment + dom.offsetWidth - gap - width, "px");
43
+ }
44
+ return getComputedStyle(innerContainer).transform === 'none' ? "".concat(innerContainer.offsetLeft + leftAdjustment + innerContainer.offsetWidth - gap - width, "px") : "".concat(innerContainer.offsetLeft + leftAdjustment + innerContainer.offsetWidth / 2 - gap - width, "px");
25
45
  };
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.topPositionAdjustment = exports.spacingBetweenNodesForPreview = exports.spaceLookupMap = exports.rootElementGap = exports.nodeMargins = exports.getNestedNodeLeftPaddingMargin = exports.dropTargetMarginMap = exports.dragHandleGap = exports.STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER = exports.STICKY_CONTROLS_TOP_MARGIN = exports.QUICK_INSERT_WIDTH = exports.QUICK_INSERT_LEFT_OFFSET = exports.QUICK_INSERT_HEIGHT = exports.QUICK_INSERT_DIMENSIONS = exports.DRAG_HANDLE_ZINDEX = exports.DRAG_HANDLE_PARAGRAPH_TOP_ADJUSTMENT = exports.DRAG_HANDLE_NARROW_GAP = exports.DRAG_HANDLE_MAX_WIDTH_PLUS_GAP = exports.DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH = exports.DRAG_HANDLE_MAX_GAP = exports.DRAG_HANDLE_LAYOUT_SECTION_TOP_ADJUSTMENT = exports.DRAG_HANDLE_HEIGHT = exports.DRAG_HANDLE_H6_TOP_ADJUSTMENT = exports.DRAG_HANDLE_H5_TOP_ADJUSTMENT = exports.DRAG_HANDLE_H4_TOP_ADJUSTMENT = exports.DRAG_HANDLE_H3_TOP_ADJUSTMENT = exports.DRAG_HANDLE_H2_TOP_ADJUSTMENT = exports.DRAG_HANDLE_H1_TOP_ADJUSTMENT = exports.DRAG_HANDLE_DIVIDER_TOP_ADJUSTMENT = exports.DRAG_HANDLE_DEFAULT_GAP = exports.DRAG_HANDLE_BORDER_RADIUS = exports.DEFAULT_COLUMN_DISTRIBUTIONS = void 0;
7
+ exports.topPositionAdjustment = exports.spacingBetweenNodesForPreview = exports.spaceLookupMap = exports.rootElementGap = exports.nodeMargins = exports.getNestedNodeLeftPaddingMargin = exports.dropTargetMarginMap = exports.dragHandleGap = exports.STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER = exports.STICKY_CONTROLS_TOP_MARGIN = exports.REMIX_BUTTON_WIDTH = exports.REMIX_BUTTON_RIGHT_OFFSET = exports.REMIX_BUTTON_HEIGHT = exports.REMIX_BUTTON_DIMENSIONS = exports.QUICK_INSERT_WIDTH = exports.QUICK_INSERT_LEFT_OFFSET = exports.QUICK_INSERT_HEIGHT = exports.QUICK_INSERT_DIMENSIONS = exports.DRAG_HANDLE_ZINDEX = exports.DRAG_HANDLE_PARAGRAPH_TOP_ADJUSTMENT = exports.DRAG_HANDLE_NARROW_GAP = exports.DRAG_HANDLE_MAX_WIDTH_PLUS_GAP = exports.DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH = exports.DRAG_HANDLE_MAX_GAP = exports.DRAG_HANDLE_LAYOUT_SECTION_TOP_ADJUSTMENT = exports.DRAG_HANDLE_HEIGHT = exports.DRAG_HANDLE_H6_TOP_ADJUSTMENT = exports.DRAG_HANDLE_H5_TOP_ADJUSTMENT = exports.DRAG_HANDLE_H4_TOP_ADJUSTMENT = exports.DRAG_HANDLE_H3_TOP_ADJUSTMENT = exports.DRAG_HANDLE_H2_TOP_ADJUSTMENT = exports.DRAG_HANDLE_H1_TOP_ADJUSTMENT = exports.DRAG_HANDLE_DIVIDER_TOP_ADJUSTMENT = exports.DRAG_HANDLE_DEFAULT_GAP = exports.DRAG_HANDLE_BORDER_RADIUS = exports.DEFAULT_COLUMN_DISTRIBUTIONS = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
9
  var _styles = require("@atlaskit/editor-common/styles");
10
10
  var _utils = require("@atlaskit/editor-common/utils");
@@ -44,6 +44,14 @@ var QUICK_INSERT_DIMENSIONS = exports.QUICK_INSERT_DIMENSIONS = {
44
44
  height: QUICK_INSERT_HEIGHT
45
45
  };
46
46
  var QUICK_INSERT_LEFT_OFFSET = exports.QUICK_INSERT_LEFT_OFFSET = 16;
47
+ var REMIX_BUTTON_HEIGHT = exports.REMIX_BUTTON_HEIGHT = 24;
48
+ var REMIX_BUTTON_WIDTH = exports.REMIX_BUTTON_WIDTH = 24;
49
+ var REMIX_BUTTON_DIMENSIONS = exports.REMIX_BUTTON_DIMENSIONS = {
50
+ width: REMIX_BUTTON_WIDTH,
51
+ height: REMIX_BUTTON_HEIGHT
52
+ };
53
+ /** Extra offset to the right for the right-side Remix button (px) */
54
+ var REMIX_BUTTON_RIGHT_OFFSET = exports.REMIX_BUTTON_RIGHT_OFFSET = 55;
47
55
  var nodeTypeExcludeList = ['embedCard', 'mediaSingle', 'table'];
48
56
  var breakoutResizableNodes = ['expand', 'layoutSection', 'codeBlock'];
49
57
  var dragHandleGap = exports.dragHandleGap = function dragHandleGap(nodeType, parentNodeType) {
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.RemixButton = void 0;
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _react = _interopRequireWildcard(require("react"));
11
+ var _react2 = require("@emotion/react");
12
+ var _bindEventListener = require("bind-event-listener");
13
+ var _new = require("@atlaskit/button/new");
14
+ var _hooks = require("@atlaskit/editor-common/hooks");
15
+ var _randomize = _interopRequireDefault(require("@atlaskit/icon-lab/core/randomize"));
16
+ var _dragHandlePositions = require("../pm-plugins/utils/drag-handle-positions");
17
+ var _widgetPositions = require("../pm-plugins/utils/widget-positions");
18
+ var _consts = require("./consts");
19
+ var _anchorName = require("./utils/anchor-name");
20
+ var _domAttrName = require("./utils/dom-attr-name");
21
+ var _visibilityContainer = require("./visibility-container");
22
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
23
+ /**
24
+ * Remix block control: positioned at the right edge of the block.
25
+ * Uses anchor(anchorName end) or getRightPositionForRootElement (same coordinate system
26
+ * as left controls). The widget span has no position:relative so position:absolute
27
+ * uses the same containing block as the node.
28
+ */
29
+ /* @jsxRuntime classic */
30
+ /**
31
+ * @jsxRuntime classic
32
+ * @jsx jsx
33
+ */
34
+
35
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
36
+
37
+ var containerBaseStyles = (0, _react2.css)({
38
+ position: 'absolute',
39
+ zIndex: 100,
40
+ display: 'flex',
41
+ alignItems: 'center',
42
+ justifyContent: 'center'
43
+ });
44
+ var RemixButton = exports.RemixButton = function RemixButton(_ref) {
45
+ var view = _ref.view,
46
+ api = _ref.api,
47
+ getPos = _ref.getPos,
48
+ anchorName = _ref.anchorName,
49
+ rootAnchorName = _ref.rootAnchorName,
50
+ rootNodeType = _ref.rootNodeType;
51
+ var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['featureFlags'], function (states) {
52
+ var _states$featureFlagsS;
53
+ return {
54
+ macroInteractionUpdates: (_states$featureFlagsS = states.featureFlagsState) === null || _states$featureFlagsS === void 0 ? void 0 : _states$featureFlagsS.macroInteractionUpdates
55
+ };
56
+ }),
57
+ macroInteractionUpdates = _useSharedPluginState.macroInteractionUpdates;
58
+ var _useState = (0, _react.useState)({
59
+ display: 'none'
60
+ }),
61
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
62
+ positionStyles = _useState2[0],
63
+ setPositionStyles = _useState2[1];
64
+
65
+ // Same positioning pattern as quick insert / drag handle: anchor(start/end) or offset-based left/top
66
+ var calculatePosition = (0, _react.useCallback)(function () {
67
+ var _dom$offsetLeft;
68
+ var safeAnchorName = (0, _anchorName.refreshAnchorName)({
69
+ getPos: getPos,
70
+ view: view,
71
+ anchorName: rootAnchorName !== null && rootAnchorName !== void 0 ? rootAnchorName : anchorName
72
+ });
73
+ var dom = view.dom.querySelector("[".concat((0, _domAttrName.getAnchorAttrName)(), "=\"").concat(safeAnchorName, "\"]"));
74
+ if (!dom) {
75
+ return {
76
+ display: 'none'
77
+ };
78
+ }
79
+ var hasResizer = rootNodeType === 'table' || rootNodeType === 'mediaSingle';
80
+ var isExtension = rootNodeType === 'extension' || rootNodeType === 'bodiedExtension';
81
+ var isBlockCard = rootNodeType === 'blockCard';
82
+ var isEmbedCard = rootNodeType === 'embedCard';
83
+ var isMacroInteractionUpdates = macroInteractionUpdates && isExtension;
84
+ var innerContainer = null;
85
+ if (dom) {
86
+ if (isEmbedCard) {
87
+ innerContainer = dom.querySelector('.rich-media-item');
88
+ } else if (hasResizer) {
89
+ innerContainer = dom.querySelector('.resizer-item');
90
+ } else if (isExtension) {
91
+ innerContainer = dom.querySelector('.extension-container[data-layout]');
92
+ } else if (isBlockCard) {
93
+ innerContainer = dom.querySelector('.datasourceView-content-inner-wrap');
94
+ }
95
+ }
96
+ var isEdgeCase = (hasResizer || isExtension || isEmbedCard || isBlockCard) && innerContainer;
97
+
98
+ // Check anchor first (no reflow). Only call expensive getRightPositionForRootElement when fallback needed.
99
+ var supportsAnchorRight = CSS.supports('left', "anchor(".concat(safeAnchorName, " end)")) && CSS.supports('top', "anchor(".concat(safeAnchorName, " start)"));
100
+ if (supportsAnchorRight && !isEdgeCase) {
101
+ return {
102
+ left: "calc(anchor(".concat(safeAnchorName, " end) - ").concat(_consts.REMIX_BUTTON_DIMENSIONS.width, "px - ").concat((0, _consts.rootElementGap)(rootNodeType), "px + ").concat(_consts.REMIX_BUTTON_RIGHT_OFFSET, "px)"),
103
+ top: "calc(anchor(".concat(safeAnchorName, " start) + ").concat((0, _consts.topPositionAdjustment)(rootNodeType), "px)"),
104
+ height: "".concat(_consts.REMIX_BUTTON_DIMENSIONS.height, "px"),
105
+ bottom: 'unset'
106
+ };
107
+ }
108
+ // Fallback: offset-based (triggers reflow). When isEdgeCase add dom.offsetLeft (same as left controls).
109
+ var rightEdgeLeft = (0, _widgetPositions.getRightPositionForRootElement)(dom, rootNodeType, _consts.REMIX_BUTTON_DIMENSIONS, innerContainer !== null && innerContainer !== void 0 ? innerContainer : undefined, isMacroInteractionUpdates);
110
+ return {
111
+ left: isEdgeCase ? "calc(".concat((_dom$offsetLeft = dom === null || dom === void 0 ? void 0 : dom.offsetLeft) !== null && _dom$offsetLeft !== void 0 ? _dom$offsetLeft : 0, "px + (").concat(rightEdgeLeft, ") + ").concat(_consts.REMIX_BUTTON_RIGHT_OFFSET, "px)") : "calc(".concat(rightEdgeLeft, " + ").concat(_consts.REMIX_BUTTON_RIGHT_OFFSET, "px)"),
112
+ top: (0, _dragHandlePositions.getTopPosition)(dom, rootNodeType),
113
+ height: "".concat(_consts.REMIX_BUTTON_DIMENSIONS.height, "px"),
114
+ bottom: 'unset'
115
+ };
116
+ }, [view, getPos, anchorName, rootAnchorName, rootNodeType, macroInteractionUpdates]);
117
+
118
+ // Recompute button position on mount and when extension/embedCard layout changes (e.g. expand/collapse).
119
+ // For extension/embedCard we listen to transitionend so position updates after CSS transitions finish.
120
+ (0, _react.useEffect)(function () {
121
+ var cleanUpTransitionListener;
122
+ if (rootNodeType === 'extension' || rootNodeType === 'embedCard') {
123
+ var anchorDom = view.dom.querySelector("[".concat((0, _domAttrName.getAnchorAttrName)(), "=\"").concat(rootAnchorName !== null && rootAnchorName !== void 0 ? rootAnchorName : anchorName, "\"]"));
124
+ if (anchorDom) {
125
+ cleanUpTransitionListener = (0, _bindEventListener.bind)(anchorDom, {
126
+ type: 'transitionend',
127
+ listener: function listener() {
128
+ return setPositionStyles(calculatePosition());
129
+ }
130
+ });
131
+ }
132
+ }
133
+ var id = requestAnimationFrame(function () {
134
+ return setPositionStyles(calculatePosition());
135
+ });
136
+ return function () {
137
+ var _cleanUpTransitionLis;
138
+ cancelAnimationFrame(id);
139
+ (_cleanUpTransitionLis = cleanUpTransitionListener) === null || _cleanUpTransitionLis === void 0 || _cleanUpTransitionLis();
140
+ };
141
+ }, [calculatePosition, view.dom, rootAnchorName, rootNodeType, anchorName]);
142
+ return (0, _react2.jsx)(_visibilityContainer.VisibilityContainer, {
143
+ api: api
144
+ }, (0, _react2.jsx)("div", {
145
+ css: containerBaseStyles
146
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Dynamic positioning (left, top, height) calculated at runtime
147
+ ,
148
+ style: positionStyles,
149
+ "data-testid": "block-ctrl-remix-button"
150
+ }, (0, _react2.jsx)(_new.IconButton, {
151
+ spacing: "compact",
152
+ appearance: "subtle",
153
+ label: "Remix",
154
+ icon: function icon() {
155
+ return (0, _react2.jsx)(_randomize.default, {
156
+ label: ""
157
+ });
158
+ },
159
+ onMouseDown: function onMouseDown(e) {
160
+ return e.preventDefault();
161
+ }
162
+ })));
163
+ };
@@ -0,0 +1,56 @@
1
+ import { createElement } from 'react';
2
+ // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
3
+ import uuid from 'uuid';
4
+ import { getDocument } from '@atlaskit/browser-apis';
5
+ import { Decoration } from '@atlaskit/editor-prosemirror/view';
6
+ import { fg } from '@atlaskit/platform-feature-flags';
7
+ import { RemixButton } from '../ui/remix-button';
8
+ const TYPE_REMIX_BUTTON = 'REMIX_BUTTON';
9
+ export const findRemixButtonDecoration = (decorations, from, to) => {
10
+ return decorations.find(from, to, spec => spec.type === TYPE_REMIX_BUTTON);
11
+ };
12
+ /** Right-edge Remix button: same gutter as left controls (side: -4) but positioned at block right edge. */
13
+ export const remixButtonDecoration = ({
14
+ api,
15
+ formatMessage,
16
+ rootPos,
17
+ anchorName,
18
+ nodeType,
19
+ nodeViewPortalProviderAPI,
20
+ rootAnchorName,
21
+ rootNodeType
22
+ }) => {
23
+ // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
24
+ const key = uuid();
25
+ const widgetSpec = {
26
+ side: -4,
27
+ type: TYPE_REMIX_BUTTON,
28
+ destroy: _ => {
29
+ if (fg('platform_editor_fix_widget_destroy')) {
30
+ nodeViewPortalProviderAPI.remove(key);
31
+ }
32
+ }
33
+ };
34
+ return Decoration.widget(rootPos, (view, getPos) => {
35
+ const doc = getDocument();
36
+ if (!doc) {
37
+ throw new Error('Document not available');
38
+ }
39
+ const element = doc.createElement('span');
40
+ element.style.display = 'block';
41
+ element.contentEditable = 'false';
42
+ element.setAttribute('data-blocks-remix-button-container', 'true');
43
+ element.setAttribute('data-testid', 'block-ctrl-remix-button-container');
44
+ nodeViewPortalProviderAPI.render(() => /*#__PURE__*/createElement(RemixButton, {
45
+ api,
46
+ getPos,
47
+ formatMessage,
48
+ view,
49
+ nodeType,
50
+ anchorName,
51
+ rootAnchorName,
52
+ rootNodeType: rootNodeType !== null && rootNodeType !== void 0 ? rootNodeType : nodeType
53
+ }), element, key, undefined, true);
54
+ return element;
55
+ }, widgetSpec);
56
+ };
@@ -23,6 +23,7 @@ import { dragHandleDecoration, emptyParagraphNodeDecorations, findHandleDec } fr
23
23
  import { dropTargetDecorations, findDropTargetDecs } from './decorations-drop-target';
24
24
  import { getActiveDropTargetDecorations } from './decorations-drop-target-active';
25
25
  import { findQuickInsertInsertButtonDecoration, quickInsertButtonDecoration } from './decorations-quick-insert-button';
26
+ import { findRemixButtonDecoration, remixButtonDecoration } from './decorations-remix-button';
26
27
  import { handleMouseDown } from './handle-mouse-down';
27
28
  import { handleMouseOver } from './handle-mouse-over';
28
29
  import { boundKeydownHandler } from './keymap';
@@ -436,12 +437,17 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
436
437
  var _activeNode7, _activeNode8;
437
438
  const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode7 = activeNode) === null || _activeNode7 === void 0 ? void 0 : _activeNode7.rootPos, (_activeNode8 = activeNode) === null || _activeNode8 === void 0 ? void 0 : _activeNode8.rootPos);
438
439
  decorations = decorations.remove(oldQuickInsertButton);
440
+ if (expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
441
+ var _activeNode9, _activeNode0;
442
+ const oldRemixButton = findRemixButtonDecoration(decorations, (_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.rootPos, (_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.rootPos);
443
+ decorations = decorations.remove(oldRemixButton);
444
+ }
439
445
  }
440
446
  } else if (api) {
441
447
  var _latestActiveNode5;
442
448
  if (shouldRecreateHandle) {
443
- var _activeNode9, _activeNode0, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
444
- const oldHandle = findHandleDec(decorations, (_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.pos, (_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.pos);
449
+ var _activeNode1, _activeNode10, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
450
+ const oldHandle = findHandleDec(decorations, (_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.pos, (_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.pos);
445
451
  decorations = decorations.remove(oldHandle);
446
452
  const handleDec = dragHandleDecoration({
447
453
  api,
@@ -459,8 +465,8 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
459
465
  if (shouldRecreateQuickInsertButton && ((_latestActiveNode5 = latestActiveNode) === null || _latestActiveNode5 === void 0 ? void 0 : _latestActiveNode5.rootPos) !== undefined &&
460
466
  // platform_editor_controls note: enables quick insert
461
467
  flags.toolbarFlagsEnabled) {
462
- var _activeNode1, _activeNode10, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0;
463
- const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.rootPos, (_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.rootPos);
468
+ var _activeNode11, _activeNode12, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0;
469
+ const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode11 = activeNode) === null || _activeNode11 === void 0 ? void 0 : _activeNode11.rootPos, (_activeNode12 = activeNode) === null || _activeNode12 === void 0 ? void 0 : _activeNode12.rootPos);
464
470
  decorations = decorations.remove(oldQuickInsertButton);
465
471
  const quickInsertButton = quickInsertButtonDecoration({
466
472
  api,
@@ -475,6 +481,28 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
475
481
  editorState: newState
476
482
  });
477
483
  decorations = decorations.add(newState.doc, [quickInsertButton]);
484
+ if (expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
485
+ var _activeNode13, _activeNode14, _latestActiveNode1, _latestActiveNode10, _latestActiveNode11, _latestActiveNode12, _latestActiveNode13;
486
+ const oldRemixButton = findRemixButtonDecoration(decorations, (_activeNode13 = activeNode) === null || _activeNode13 === void 0 ? void 0 : _activeNode13.rootPos, (_activeNode14 = activeNode) === null || _activeNode14 === void 0 ? void 0 : _activeNode14.rootPos);
487
+ decorations = decorations.remove(oldRemixButton);
488
+ const remixButton = remixButtonDecoration({
489
+ api,
490
+ formatMessage,
491
+ anchorName: (_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.anchorName,
492
+ nodeType: (_latestActiveNode10 = latestActiveNode) === null || _latestActiveNode10 === void 0 ? void 0 : _latestActiveNode10.nodeType,
493
+ nodeViewPortalProviderAPI,
494
+ rootPos: (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.rootPos,
495
+ rootAnchorName: (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.rootAnchorName,
496
+ rootNodeType: (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.rootNodeType,
497
+ editorState: newState
498
+ });
499
+ decorations = decorations.add(newState.doc, [remixButton]);
500
+ } else {
501
+ var _activeNode15, _activeNode16;
502
+ // Remove remix decoration when experiment is off so it disappears when flag is toggled
503
+ const oldRemixButton = findRemixButtonDecoration(decorations, (_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos);
504
+ decorations = decorations.remove(oldRemixButton);
505
+ }
478
506
  }
479
507
  }
480
508
 
@@ -523,12 +551,12 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
523
551
  let newActiveNode;
524
552
  // platform_editor_controls note: enables quick insert
525
553
  if (flags.toolbarFlagsEnabled) {
526
- var _latestActiveNode1, _latestActiveNode10;
554
+ var _latestActiveNode14, _latestActiveNode15;
527
555
  // remove isEmptyDoc check and let decorations render and determine their own visibility
528
- newActiveNode = !(meta !== null && meta !== void 0 && meta.activeNode) && findHandleDec(decorations, (_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.pos, (_latestActiveNode10 = latestActiveNode) === null || _latestActiveNode10 === void 0 ? void 0 : _latestActiveNode10.pos).length === 0 ? null : latestActiveNode;
556
+ newActiveNode = !(meta !== null && meta !== void 0 && meta.activeNode) && findHandleDec(decorations, (_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.pos, (_latestActiveNode15 = latestActiveNode) === null || _latestActiveNode15 === void 0 ? void 0 : _latestActiveNode15.pos).length === 0 ? null : latestActiveNode;
529
557
  } else {
530
- var _latestActiveNode11, _latestActiveNode12;
531
- newActiveNode = isEmptyDoc || !(meta !== null && meta !== void 0 && meta.activeNode) && findHandleDec(decorations, (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.pos, (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.pos).length === 0 ? null : latestActiveNode;
558
+ var _latestActiveNode16, _latestActiveNode17;
559
+ newActiveNode = isEmptyDoc || !(meta !== null && meta !== void 0 && meta.activeNode) && findHandleDec(decorations, (_latestActiveNode16 = latestActiveNode) === null || _latestActiveNode16 === void 0 ? void 0 : _latestActiveNode16.pos, (_latestActiveNode17 = latestActiveNode) === null || _latestActiveNode17 === void 0 ? void 0 : _latestActiveNode17.pos).length === 0 ? null : latestActiveNode;
532
560
  }
533
561
  let isMenuOpenNew = isMenuOpen;
534
562
  if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
@@ -17,4 +17,24 @@ export const getLeftPositionForRootElement = (dom, nodeType, widgetDimensions, i
17
17
  const relativeSpan = macroInteractionUpdates ? dom.querySelector('span.relative') : null;
18
18
  const leftAdjustment = relativeSpan ? relativeSpan.offsetLeft : 0;
19
19
  return getComputedStyle(innerContainer).transform === 'none' ? `${innerContainer.offsetLeft + leftAdjustment - rootElementGap(nodeType) - widgetDimensions.width}px` : `${innerContainer.offsetLeft + leftAdjustment - innerContainer.offsetWidth / 2 - rootElementGap(nodeType) - widgetDimensions.width}px`;
20
+ };
21
+
22
+ /**
23
+ * Left position (in px) for a widget on the right edge of the block.
24
+ * Mirrors getLeftPositionForRootElement: when innerContainer is set (table, mediaSingle,
25
+ * extension, blockCard, embedCard) use it for the right edge so the button aligns to the
26
+ * content box and does not overlap; otherwise use the block (dom).
27
+ */
28
+ export const getRightPositionForRootElement = (dom, nodeType, widgetDimensions, innerContainer, macroInteractionUpdates) => {
29
+ if (!dom) {
30
+ return 'auto';
31
+ }
32
+ const relativeSpan = macroInteractionUpdates ? dom.querySelector('span.relative') : null;
33
+ const leftAdjustment = relativeSpan ? relativeSpan.offsetLeft : 0;
34
+ const gap = rootElementGap(nodeType);
35
+ const width = widgetDimensions.width;
36
+ if (!innerContainer) {
37
+ return `${dom.offsetLeft + leftAdjustment + dom.offsetWidth - gap - width}px`;
38
+ }
39
+ return getComputedStyle(innerContainer).transform === 'none' ? `${innerContainer.offsetLeft + leftAdjustment + innerContainer.offsetWidth - gap - width}px` : `${innerContainer.offsetLeft + leftAdjustment + innerContainer.offsetWidth / 2 - gap - width}px`;
20
40
  };
@@ -35,6 +35,14 @@ export const QUICK_INSERT_DIMENSIONS = {
35
35
  height: QUICK_INSERT_HEIGHT
36
36
  };
37
37
  export const QUICK_INSERT_LEFT_OFFSET = 16;
38
+ export const REMIX_BUTTON_HEIGHT = 24;
39
+ export const REMIX_BUTTON_WIDTH = 24;
40
+ export const REMIX_BUTTON_DIMENSIONS = {
41
+ width: REMIX_BUTTON_WIDTH,
42
+ height: REMIX_BUTTON_HEIGHT
43
+ };
44
+ /** Extra offset to the right for the right-side Remix button (px) */
45
+ export const REMIX_BUTTON_RIGHT_OFFSET = 55;
38
46
  const nodeTypeExcludeList = ['embedCard', 'mediaSingle', 'table'];
39
47
  const breakoutResizableNodes = ['expand', 'layoutSection', 'codeBlock'];
40
48
  export const dragHandleGap = (nodeType, parentNodeType) => {