@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 +13 -0
- package/dist/cjs/pm-plugins/decorations-remix-button.js +67 -0
- package/dist/cjs/pm-plugins/main.js +36 -8
- package/dist/cjs/pm-plugins/utils/widget-positions.js +21 -1
- package/dist/cjs/ui/consts.js +9 -1
- package/dist/cjs/ui/remix-button.js +163 -0
- package/dist/es2019/pm-plugins/decorations-remix-button.js +56 -0
- package/dist/es2019/pm-plugins/main.js +36 -8
- package/dist/es2019/pm-plugins/utils/widget-positions.js +20 -0
- package/dist/es2019/ui/consts.js +8 -0
- package/dist/es2019/ui/remix-button.js +144 -0
- package/dist/esm/pm-plugins/decorations-remix-button.js +59 -0
- package/dist/esm/pm-plugins/main.js +36 -8
- package/dist/esm/pm-plugins/utils/widget-positions.js +20 -0
- package/dist/esm/ui/consts.js +8 -0
- package/dist/esm/ui/remix-button.js +154 -0
- package/dist/types/pm-plugins/decorations-remix-button.d.ts +21 -0
- package/dist/types/pm-plugins/utils/widget-positions.d.ts +10 -0
- package/dist/types/ui/consts.d.ts +8 -0
- package/dist/types/ui/remix-button.d.ts +17 -0
- package/dist/types/ui/utils/anchor-name.d.ts +1 -1
- package/dist/types-ts4.5/pm-plugins/decorations-remix-button.d.ts +21 -0
- package/dist/types-ts4.5/pm-plugins/utils/widget-positions.d.ts +10 -0
- package/dist/types-ts4.5/ui/consts.d.ts +8 -0
- package/dist/types-ts4.5/ui/remix-button.d.ts +17 -0
- package/dist/types-ts4.5/ui/utils/anchor-name.d.ts +1 -1
- package/package.json +5 -3
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remix block control: positioned at the right edge of the block.
|
|
3
|
+
* Uses anchor(anchorName end) or getRightPositionForRootElement (same coordinate system
|
|
4
|
+
* as left controls). The widget span has no position:relative so position:absolute
|
|
5
|
+
* uses the same containing block as the node.
|
|
6
|
+
*/
|
|
7
|
+
/* @jsxRuntime classic */
|
|
8
|
+
/**
|
|
9
|
+
* @jsxRuntime classic
|
|
10
|
+
* @jsx jsx
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
14
|
+
|
|
15
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
|
16
|
+
import { css, jsx } from '@emotion/react';
|
|
17
|
+
import { bind } from 'bind-event-listener';
|
|
18
|
+
import { IconButton } from '@atlaskit/button/new';
|
|
19
|
+
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
20
|
+
import RandomizeIcon from '@atlaskit/icon-lab/core/randomize';
|
|
21
|
+
import { getTopPosition } from '../pm-plugins/utils/drag-handle-positions';
|
|
22
|
+
import { getRightPositionForRootElement } from '../pm-plugins/utils/widget-positions';
|
|
23
|
+
import { REMIX_BUTTON_DIMENSIONS, REMIX_BUTTON_RIGHT_OFFSET, rootElementGap, topPositionAdjustment } from './consts';
|
|
24
|
+
import { refreshAnchorName } from './utils/anchor-name';
|
|
25
|
+
import { getAnchorAttrName } from './utils/dom-attr-name';
|
|
26
|
+
import { VisibilityContainer } from './visibility-container';
|
|
27
|
+
const containerBaseStyles = css({
|
|
28
|
+
position: 'absolute',
|
|
29
|
+
zIndex: 100,
|
|
30
|
+
display: 'flex',
|
|
31
|
+
alignItems: 'center',
|
|
32
|
+
justifyContent: 'center'
|
|
33
|
+
});
|
|
34
|
+
export const RemixButton = ({
|
|
35
|
+
view,
|
|
36
|
+
api,
|
|
37
|
+
getPos,
|
|
38
|
+
anchorName,
|
|
39
|
+
rootAnchorName,
|
|
40
|
+
rootNodeType
|
|
41
|
+
}) => {
|
|
42
|
+
const {
|
|
43
|
+
macroInteractionUpdates
|
|
44
|
+
} = useSharedPluginStateWithSelector(api, ['featureFlags'], states => {
|
|
45
|
+
var _states$featureFlagsS;
|
|
46
|
+
return {
|
|
47
|
+
macroInteractionUpdates: (_states$featureFlagsS = states.featureFlagsState) === null || _states$featureFlagsS === void 0 ? void 0 : _states$featureFlagsS.macroInteractionUpdates
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
const [positionStyles, setPositionStyles] = useState({
|
|
51
|
+
display: 'none'
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Same positioning pattern as quick insert / drag handle: anchor(start/end) or offset-based left/top
|
|
55
|
+
const calculatePosition = useCallback(() => {
|
|
56
|
+
var _innerContainer, _dom$offsetLeft;
|
|
57
|
+
const safeAnchorName = refreshAnchorName({
|
|
58
|
+
getPos,
|
|
59
|
+
view,
|
|
60
|
+
anchorName: rootAnchorName !== null && rootAnchorName !== void 0 ? rootAnchorName : anchorName
|
|
61
|
+
});
|
|
62
|
+
const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${safeAnchorName}"]`);
|
|
63
|
+
if (!dom) {
|
|
64
|
+
return {
|
|
65
|
+
display: 'none'
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const hasResizer = rootNodeType === 'table' || rootNodeType === 'mediaSingle';
|
|
69
|
+
const isExtension = rootNodeType === 'extension' || rootNodeType === 'bodiedExtension';
|
|
70
|
+
const isBlockCard = rootNodeType === 'blockCard';
|
|
71
|
+
const isEmbedCard = rootNodeType === 'embedCard';
|
|
72
|
+
const isMacroInteractionUpdates = macroInteractionUpdates && isExtension;
|
|
73
|
+
let innerContainer = null;
|
|
74
|
+
if (dom) {
|
|
75
|
+
if (isEmbedCard) {
|
|
76
|
+
innerContainer = dom.querySelector('.rich-media-item');
|
|
77
|
+
} else if (hasResizer) {
|
|
78
|
+
innerContainer = dom.querySelector('.resizer-item');
|
|
79
|
+
} else if (isExtension) {
|
|
80
|
+
innerContainer = dom.querySelector('.extension-container[data-layout]');
|
|
81
|
+
} else if (isBlockCard) {
|
|
82
|
+
innerContainer = dom.querySelector('.datasourceView-content-inner-wrap');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const isEdgeCase = (hasResizer || isExtension || isEmbedCard || isBlockCard) && innerContainer;
|
|
86
|
+
|
|
87
|
+
// Check anchor first (no reflow). Only call expensive getRightPositionForRootElement when fallback needed.
|
|
88
|
+
const supportsAnchorRight = CSS.supports('left', `anchor(${safeAnchorName} end)`) && CSS.supports('top', `anchor(${safeAnchorName} start)`);
|
|
89
|
+
if (supportsAnchorRight && !isEdgeCase) {
|
|
90
|
+
return {
|
|
91
|
+
left: `calc(anchor(${safeAnchorName} end) - ${REMIX_BUTTON_DIMENSIONS.width}px - ${rootElementGap(rootNodeType)}px + ${REMIX_BUTTON_RIGHT_OFFSET}px)`,
|
|
92
|
+
top: `calc(anchor(${safeAnchorName} start) + ${topPositionAdjustment(rootNodeType)}px)`,
|
|
93
|
+
height: `${REMIX_BUTTON_DIMENSIONS.height}px`,
|
|
94
|
+
bottom: 'unset'
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Fallback: offset-based (triggers reflow). When isEdgeCase add dom.offsetLeft (same as left controls).
|
|
98
|
+
const rightEdgeLeft = getRightPositionForRootElement(dom, rootNodeType, REMIX_BUTTON_DIMENSIONS, (_innerContainer = innerContainer) !== null && _innerContainer !== void 0 ? _innerContainer : undefined, isMacroInteractionUpdates);
|
|
99
|
+
return {
|
|
100
|
+
left: isEdgeCase ? `calc(${(_dom$offsetLeft = dom === null || dom === void 0 ? void 0 : dom.offsetLeft) !== null && _dom$offsetLeft !== void 0 ? _dom$offsetLeft : 0}px + (${rightEdgeLeft}) + ${REMIX_BUTTON_RIGHT_OFFSET}px)` : `calc(${rightEdgeLeft} + ${REMIX_BUTTON_RIGHT_OFFSET}px)`,
|
|
101
|
+
top: getTopPosition(dom, rootNodeType),
|
|
102
|
+
height: `${REMIX_BUTTON_DIMENSIONS.height}px`,
|
|
103
|
+
bottom: 'unset'
|
|
104
|
+
};
|
|
105
|
+
}, [view, getPos, anchorName, rootAnchorName, rootNodeType, macroInteractionUpdates]);
|
|
106
|
+
|
|
107
|
+
// Recompute button position on mount and when extension/embedCard layout changes (e.g. expand/collapse).
|
|
108
|
+
// For extension/embedCard we listen to transitionend so position updates after CSS transitions finish.
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
let cleanUpTransitionListener;
|
|
111
|
+
if (rootNodeType === 'extension' || rootNodeType === 'embedCard') {
|
|
112
|
+
const anchorDom = view.dom.querySelector(`[${getAnchorAttrName()}="${rootAnchorName !== null && rootAnchorName !== void 0 ? rootAnchorName : anchorName}"]`);
|
|
113
|
+
if (anchorDom) {
|
|
114
|
+
cleanUpTransitionListener = bind(anchorDom, {
|
|
115
|
+
type: 'transitionend',
|
|
116
|
+
listener: () => setPositionStyles(calculatePosition())
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const id = requestAnimationFrame(() => setPositionStyles(calculatePosition()));
|
|
121
|
+
return () => {
|
|
122
|
+
var _cleanUpTransitionLis;
|
|
123
|
+
cancelAnimationFrame(id);
|
|
124
|
+
(_cleanUpTransitionLis = cleanUpTransitionListener) === null || _cleanUpTransitionLis === void 0 ? void 0 : _cleanUpTransitionLis();
|
|
125
|
+
};
|
|
126
|
+
}, [calculatePosition, view.dom, rootAnchorName, rootNodeType, anchorName]);
|
|
127
|
+
return jsx(VisibilityContainer, {
|
|
128
|
+
api: api
|
|
129
|
+
}, jsx("div", {
|
|
130
|
+
css: containerBaseStyles
|
|
131
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Dynamic positioning (left, top, height) calculated at runtime
|
|
132
|
+
,
|
|
133
|
+
style: positionStyles,
|
|
134
|
+
"data-testid": "block-ctrl-remix-button"
|
|
135
|
+
}, jsx(IconButton, {
|
|
136
|
+
spacing: "compact",
|
|
137
|
+
appearance: "subtle",
|
|
138
|
+
label: "Remix",
|
|
139
|
+
icon: () => jsx(RandomizeIcon, {
|
|
140
|
+
label: ""
|
|
141
|
+
}),
|
|
142
|
+
onMouseDown: e => e.preventDefault()
|
|
143
|
+
})));
|
|
144
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
var TYPE_REMIX_BUTTON = 'REMIX_BUTTON';
|
|
9
|
+
export var findRemixButtonDecoration = function findRemixButtonDecoration(decorations, from, to) {
|
|
10
|
+
return decorations.find(from, to, function (spec) {
|
|
11
|
+
return spec.type === TYPE_REMIX_BUTTON;
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
/** Right-edge Remix button: same gutter as left controls (side: -4) but positioned at block right edge. */
|
|
15
|
+
export var remixButtonDecoration = function remixButtonDecoration(_ref) {
|
|
16
|
+
var api = _ref.api,
|
|
17
|
+
formatMessage = _ref.formatMessage,
|
|
18
|
+
rootPos = _ref.rootPos,
|
|
19
|
+
anchorName = _ref.anchorName,
|
|
20
|
+
nodeType = _ref.nodeType,
|
|
21
|
+
nodeViewPortalProviderAPI = _ref.nodeViewPortalProviderAPI,
|
|
22
|
+
rootAnchorName = _ref.rootAnchorName,
|
|
23
|
+
rootNodeType = _ref.rootNodeType;
|
|
24
|
+
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
25
|
+
var key = uuid();
|
|
26
|
+
var widgetSpec = {
|
|
27
|
+
side: -4,
|
|
28
|
+
type: TYPE_REMIX_BUTTON,
|
|
29
|
+
destroy: function destroy(_) {
|
|
30
|
+
if (fg('platform_editor_fix_widget_destroy')) {
|
|
31
|
+
nodeViewPortalProviderAPI.remove(key);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
return Decoration.widget(rootPos, function (view, getPos) {
|
|
36
|
+
var doc = getDocument();
|
|
37
|
+
if (!doc) {
|
|
38
|
+
throw new Error('Document not available');
|
|
39
|
+
}
|
|
40
|
+
var element = doc.createElement('span');
|
|
41
|
+
element.style.display = 'block';
|
|
42
|
+
element.contentEditable = 'false';
|
|
43
|
+
element.setAttribute('data-blocks-remix-button-container', 'true');
|
|
44
|
+
element.setAttribute('data-testid', 'block-ctrl-remix-button-container');
|
|
45
|
+
nodeViewPortalProviderAPI.render(function () {
|
|
46
|
+
return /*#__PURE__*/createElement(RemixButton, {
|
|
47
|
+
api: api,
|
|
48
|
+
getPos: getPos,
|
|
49
|
+
formatMessage: formatMessage,
|
|
50
|
+
view: view,
|
|
51
|
+
nodeType: nodeType,
|
|
52
|
+
anchorName: anchorName,
|
|
53
|
+
rootAnchorName: rootAnchorName,
|
|
54
|
+
rootNodeType: rootNodeType !== null && rootNodeType !== void 0 ? rootNodeType : nodeType
|
|
55
|
+
});
|
|
56
|
+
}, element, key, undefined, true);
|
|
57
|
+
return element;
|
|
58
|
+
}, widgetSpec);
|
|
59
|
+
};
|
|
@@ -26,6 +26,7 @@ import { dragHandleDecoration, emptyParagraphNodeDecorations, findHandleDec } fr
|
|
|
26
26
|
import { dropTargetDecorations, findDropTargetDecs } from './decorations-drop-target';
|
|
27
27
|
import { getActiveDropTargetDecorations } from './decorations-drop-target-active';
|
|
28
28
|
import { findQuickInsertInsertButtonDecoration, quickInsertButtonDecoration } from './decorations-quick-insert-button';
|
|
29
|
+
import { findRemixButtonDecoration, remixButtonDecoration } from './decorations-remix-button';
|
|
29
30
|
import { handleMouseDown } from './handle-mouse-down';
|
|
30
31
|
import { handleMouseOver } from './handle-mouse-over';
|
|
31
32
|
import { boundKeydownHandler } from './keymap';
|
|
@@ -432,12 +433,17 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
432
433
|
var _activeNode7, _activeNode8;
|
|
433
434
|
var oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode7 = activeNode) === null || _activeNode7 === void 0 ? void 0 : _activeNode7.rootPos, (_activeNode8 = activeNode) === null || _activeNode8 === void 0 ? void 0 : _activeNode8.rootPos);
|
|
434
435
|
decorations = decorations.remove(oldQuickInsertButton);
|
|
436
|
+
if (expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
|
|
437
|
+
var _activeNode9, _activeNode0;
|
|
438
|
+
var oldRemixButton = findRemixButtonDecoration(decorations, (_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.rootPos, (_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.rootPos);
|
|
439
|
+
decorations = decorations.remove(oldRemixButton);
|
|
440
|
+
}
|
|
435
441
|
}
|
|
436
442
|
} else if (api) {
|
|
437
443
|
var _latestActiveNode5;
|
|
438
444
|
if (shouldRecreateHandle) {
|
|
439
|
-
var
|
|
440
|
-
var _oldHandle = findHandleDec(decorations, (
|
|
445
|
+
var _activeNode1, _activeNode10, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
|
|
446
|
+
var _oldHandle = findHandleDec(decorations, (_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.pos, (_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.pos);
|
|
441
447
|
decorations = decorations.remove(_oldHandle);
|
|
442
448
|
var handleDec = dragHandleDecoration({
|
|
443
449
|
api: api,
|
|
@@ -455,8 +461,8 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
455
461
|
if (shouldRecreateQuickInsertButton && ((_latestActiveNode5 = latestActiveNode) === null || _latestActiveNode5 === void 0 ? void 0 : _latestActiveNode5.rootPos) !== undefined &&
|
|
456
462
|
// platform_editor_controls note: enables quick insert
|
|
457
463
|
flags.toolbarFlagsEnabled) {
|
|
458
|
-
var
|
|
459
|
-
var _oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (
|
|
464
|
+
var _activeNode11, _activeNode12, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0;
|
|
465
|
+
var _oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode11 = activeNode) === null || _activeNode11 === void 0 ? void 0 : _activeNode11.rootPos, (_activeNode12 = activeNode) === null || _activeNode12 === void 0 ? void 0 : _activeNode12.rootPos);
|
|
460
466
|
decorations = decorations.remove(_oldQuickInsertButton);
|
|
461
467
|
var quickInsertButton = quickInsertButtonDecoration({
|
|
462
468
|
api: api,
|
|
@@ -471,6 +477,28 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
471
477
|
editorState: newState
|
|
472
478
|
});
|
|
473
479
|
decorations = decorations.add(newState.doc, [quickInsertButton]);
|
|
480
|
+
if (expValEquals('confluence_remix_icon_right_side', 'isEnabled', true)) {
|
|
481
|
+
var _activeNode13, _activeNode14, _latestActiveNode1, _latestActiveNode10, _latestActiveNode11, _latestActiveNode12, _latestActiveNode13;
|
|
482
|
+
var _oldRemixButton = findRemixButtonDecoration(decorations, (_activeNode13 = activeNode) === null || _activeNode13 === void 0 ? void 0 : _activeNode13.rootPos, (_activeNode14 = activeNode) === null || _activeNode14 === void 0 ? void 0 : _activeNode14.rootPos);
|
|
483
|
+
decorations = decorations.remove(_oldRemixButton);
|
|
484
|
+
var remixButton = remixButtonDecoration({
|
|
485
|
+
api: api,
|
|
486
|
+
formatMessage: formatMessage,
|
|
487
|
+
anchorName: (_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.anchorName,
|
|
488
|
+
nodeType: (_latestActiveNode10 = latestActiveNode) === null || _latestActiveNode10 === void 0 ? void 0 : _latestActiveNode10.nodeType,
|
|
489
|
+
nodeViewPortalProviderAPI: nodeViewPortalProviderAPI,
|
|
490
|
+
rootPos: (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.rootPos,
|
|
491
|
+
rootAnchorName: (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.rootAnchorName,
|
|
492
|
+
rootNodeType: (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.rootNodeType,
|
|
493
|
+
editorState: newState
|
|
494
|
+
});
|
|
495
|
+
decorations = decorations.add(newState.doc, [remixButton]);
|
|
496
|
+
} else {
|
|
497
|
+
var _activeNode15, _activeNode16;
|
|
498
|
+
// Remove remix decoration when experiment is off so it disappears when flag is toggled
|
|
499
|
+
var _oldRemixButton2 = findRemixButtonDecoration(decorations, (_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos);
|
|
500
|
+
decorations = decorations.remove(_oldRemixButton2);
|
|
501
|
+
}
|
|
474
502
|
}
|
|
475
503
|
}
|
|
476
504
|
|
|
@@ -518,12 +546,12 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
|
|
|
518
546
|
var newActiveNode;
|
|
519
547
|
// platform_editor_controls note: enables quick insert
|
|
520
548
|
if (flags.toolbarFlagsEnabled) {
|
|
521
|
-
var
|
|
549
|
+
var _latestActiveNode14, _latestActiveNode15;
|
|
522
550
|
// remove isEmptyDoc check and let decorations render and determine their own visibility
|
|
523
|
-
newActiveNode = !(meta !== null && meta !== void 0 && meta.activeNode) && findHandleDec(decorations, (
|
|
551
|
+
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;
|
|
524
552
|
} else {
|
|
525
|
-
var
|
|
526
|
-
newActiveNode = isEmptyDoc || !(meta !== null && meta !== void 0 && meta.activeNode) && findHandleDec(decorations, (
|
|
553
|
+
var _latestActiveNode16, _latestActiveNode17;
|
|
554
|
+
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;
|
|
527
555
|
}
|
|
528
556
|
var isMenuOpenNew = isMenuOpen;
|
|
529
557
|
if (expValEqualsNoExposure('platform_editor_block_menu', 'isEnabled', true)) {
|
|
@@ -17,4 +17,24 @@ export var getLeftPositionForRootElement = function getLeftPositionForRootElemen
|
|
|
17
17
|
var relativeSpan = macroInteractionUpdates ? dom.querySelector('span.relative') : null;
|
|
18
18
|
var leftAdjustment = relativeSpan ? relativeSpan.offsetLeft : 0;
|
|
19
19
|
return getComputedStyle(innerContainer).transform === 'none' ? "".concat(innerContainer.offsetLeft + leftAdjustment - rootElementGap(nodeType) - widgetDimensions.width, "px") : "".concat(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 var getRightPositionForRootElement = function getRightPositionForRootElement(dom, nodeType, widgetDimensions, innerContainer, macroInteractionUpdates) {
|
|
29
|
+
if (!dom) {
|
|
30
|
+
return 'auto';
|
|
31
|
+
}
|
|
32
|
+
var relativeSpan = macroInteractionUpdates ? dom.querySelector('span.relative') : null;
|
|
33
|
+
var leftAdjustment = relativeSpan ? relativeSpan.offsetLeft : 0;
|
|
34
|
+
var gap = rootElementGap(nodeType);
|
|
35
|
+
var width = widgetDimensions.width;
|
|
36
|
+
if (!innerContainer) {
|
|
37
|
+
return "".concat(dom.offsetLeft + leftAdjustment + dom.offsetWidth - gap - width, "px");
|
|
38
|
+
}
|
|
39
|
+
return getComputedStyle(innerContainer).transform === 'none' ? "".concat(innerContainer.offsetLeft + leftAdjustment + innerContainer.offsetWidth - gap - width, "px") : "".concat(innerContainer.offsetLeft + leftAdjustment + innerContainer.offsetWidth / 2 - gap - width, "px");
|
|
20
40
|
};
|
package/dist/esm/ui/consts.js
CHANGED
|
@@ -37,6 +37,14 @@ export var QUICK_INSERT_DIMENSIONS = {
|
|
|
37
37
|
height: QUICK_INSERT_HEIGHT
|
|
38
38
|
};
|
|
39
39
|
export var QUICK_INSERT_LEFT_OFFSET = 16;
|
|
40
|
+
export var REMIX_BUTTON_HEIGHT = 24;
|
|
41
|
+
export var REMIX_BUTTON_WIDTH = 24;
|
|
42
|
+
export var REMIX_BUTTON_DIMENSIONS = {
|
|
43
|
+
width: REMIX_BUTTON_WIDTH,
|
|
44
|
+
height: REMIX_BUTTON_HEIGHT
|
|
45
|
+
};
|
|
46
|
+
/** Extra offset to the right for the right-side Remix button (px) */
|
|
47
|
+
export var REMIX_BUTTON_RIGHT_OFFSET = 55;
|
|
40
48
|
var nodeTypeExcludeList = ['embedCard', 'mediaSingle', 'table'];
|
|
41
49
|
var breakoutResizableNodes = ['expand', 'layoutSection', 'codeBlock'];
|
|
42
50
|
export var dragHandleGap = function dragHandleGap(nodeType, parentNodeType) {
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
/**
|
|
3
|
+
* Remix block control: positioned at the right edge of the block.
|
|
4
|
+
* Uses anchor(anchorName end) or getRightPositionForRootElement (same coordinate system
|
|
5
|
+
* as left controls). The widget span has no position:relative so position:absolute
|
|
6
|
+
* uses the same containing block as the node.
|
|
7
|
+
*/
|
|
8
|
+
/* @jsxRuntime classic */
|
|
9
|
+
/**
|
|
10
|
+
* @jsxRuntime classic
|
|
11
|
+
* @jsx jsx
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
|
|
17
|
+
import { css, jsx } from '@emotion/react';
|
|
18
|
+
import { bind } from 'bind-event-listener';
|
|
19
|
+
import { IconButton } from '@atlaskit/button/new';
|
|
20
|
+
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
21
|
+
import RandomizeIcon from '@atlaskit/icon-lab/core/randomize';
|
|
22
|
+
import { getTopPosition } from '../pm-plugins/utils/drag-handle-positions';
|
|
23
|
+
import { getRightPositionForRootElement } from '../pm-plugins/utils/widget-positions';
|
|
24
|
+
import { REMIX_BUTTON_DIMENSIONS, REMIX_BUTTON_RIGHT_OFFSET, rootElementGap, topPositionAdjustment } from './consts';
|
|
25
|
+
import { refreshAnchorName } from './utils/anchor-name';
|
|
26
|
+
import { getAnchorAttrName } from './utils/dom-attr-name';
|
|
27
|
+
import { VisibilityContainer } from './visibility-container';
|
|
28
|
+
var containerBaseStyles = css({
|
|
29
|
+
position: 'absolute',
|
|
30
|
+
zIndex: 100,
|
|
31
|
+
display: 'flex',
|
|
32
|
+
alignItems: 'center',
|
|
33
|
+
justifyContent: 'center'
|
|
34
|
+
});
|
|
35
|
+
export var RemixButton = function RemixButton(_ref) {
|
|
36
|
+
var view = _ref.view,
|
|
37
|
+
api = _ref.api,
|
|
38
|
+
getPos = _ref.getPos,
|
|
39
|
+
anchorName = _ref.anchorName,
|
|
40
|
+
rootAnchorName = _ref.rootAnchorName,
|
|
41
|
+
rootNodeType = _ref.rootNodeType;
|
|
42
|
+
var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['featureFlags'], function (states) {
|
|
43
|
+
var _states$featureFlagsS;
|
|
44
|
+
return {
|
|
45
|
+
macroInteractionUpdates: (_states$featureFlagsS = states.featureFlagsState) === null || _states$featureFlagsS === void 0 ? void 0 : _states$featureFlagsS.macroInteractionUpdates
|
|
46
|
+
};
|
|
47
|
+
}),
|
|
48
|
+
macroInteractionUpdates = _useSharedPluginState.macroInteractionUpdates;
|
|
49
|
+
var _useState = useState({
|
|
50
|
+
display: 'none'
|
|
51
|
+
}),
|
|
52
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
53
|
+
positionStyles = _useState2[0],
|
|
54
|
+
setPositionStyles = _useState2[1];
|
|
55
|
+
|
|
56
|
+
// Same positioning pattern as quick insert / drag handle: anchor(start/end) or offset-based left/top
|
|
57
|
+
var calculatePosition = useCallback(function () {
|
|
58
|
+
var _dom$offsetLeft;
|
|
59
|
+
var safeAnchorName = refreshAnchorName({
|
|
60
|
+
getPos: getPos,
|
|
61
|
+
view: view,
|
|
62
|
+
anchorName: rootAnchorName !== null && rootAnchorName !== void 0 ? rootAnchorName : anchorName
|
|
63
|
+
});
|
|
64
|
+
var dom = view.dom.querySelector("[".concat(getAnchorAttrName(), "=\"").concat(safeAnchorName, "\"]"));
|
|
65
|
+
if (!dom) {
|
|
66
|
+
return {
|
|
67
|
+
display: 'none'
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
var hasResizer = rootNodeType === 'table' || rootNodeType === 'mediaSingle';
|
|
71
|
+
var isExtension = rootNodeType === 'extension' || rootNodeType === 'bodiedExtension';
|
|
72
|
+
var isBlockCard = rootNodeType === 'blockCard';
|
|
73
|
+
var isEmbedCard = rootNodeType === 'embedCard';
|
|
74
|
+
var isMacroInteractionUpdates = macroInteractionUpdates && isExtension;
|
|
75
|
+
var innerContainer = null;
|
|
76
|
+
if (dom) {
|
|
77
|
+
if (isEmbedCard) {
|
|
78
|
+
innerContainer = dom.querySelector('.rich-media-item');
|
|
79
|
+
} else if (hasResizer) {
|
|
80
|
+
innerContainer = dom.querySelector('.resizer-item');
|
|
81
|
+
} else if (isExtension) {
|
|
82
|
+
innerContainer = dom.querySelector('.extension-container[data-layout]');
|
|
83
|
+
} else if (isBlockCard) {
|
|
84
|
+
innerContainer = dom.querySelector('.datasourceView-content-inner-wrap');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
var isEdgeCase = (hasResizer || isExtension || isEmbedCard || isBlockCard) && innerContainer;
|
|
88
|
+
|
|
89
|
+
// Check anchor first (no reflow). Only call expensive getRightPositionForRootElement when fallback needed.
|
|
90
|
+
var supportsAnchorRight = CSS.supports('left', "anchor(".concat(safeAnchorName, " end)")) && CSS.supports('top', "anchor(".concat(safeAnchorName, " start)"));
|
|
91
|
+
if (supportsAnchorRight && !isEdgeCase) {
|
|
92
|
+
return {
|
|
93
|
+
left: "calc(anchor(".concat(safeAnchorName, " end) - ").concat(REMIX_BUTTON_DIMENSIONS.width, "px - ").concat(rootElementGap(rootNodeType), "px + ").concat(REMIX_BUTTON_RIGHT_OFFSET, "px)"),
|
|
94
|
+
top: "calc(anchor(".concat(safeAnchorName, " start) + ").concat(topPositionAdjustment(rootNodeType), "px)"),
|
|
95
|
+
height: "".concat(REMIX_BUTTON_DIMENSIONS.height, "px"),
|
|
96
|
+
bottom: 'unset'
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// Fallback: offset-based (triggers reflow). When isEdgeCase add dom.offsetLeft (same as left controls).
|
|
100
|
+
var rightEdgeLeft = getRightPositionForRootElement(dom, rootNodeType, REMIX_BUTTON_DIMENSIONS, innerContainer !== null && innerContainer !== void 0 ? innerContainer : undefined, isMacroInteractionUpdates);
|
|
101
|
+
return {
|
|
102
|
+
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(REMIX_BUTTON_RIGHT_OFFSET, "px)") : "calc(".concat(rightEdgeLeft, " + ").concat(REMIX_BUTTON_RIGHT_OFFSET, "px)"),
|
|
103
|
+
top: getTopPosition(dom, rootNodeType),
|
|
104
|
+
height: "".concat(REMIX_BUTTON_DIMENSIONS.height, "px"),
|
|
105
|
+
bottom: 'unset'
|
|
106
|
+
};
|
|
107
|
+
}, [view, getPos, anchorName, rootAnchorName, rootNodeType, macroInteractionUpdates]);
|
|
108
|
+
|
|
109
|
+
// Recompute button position on mount and when extension/embedCard layout changes (e.g. expand/collapse).
|
|
110
|
+
// For extension/embedCard we listen to transitionend so position updates after CSS transitions finish.
|
|
111
|
+
useEffect(function () {
|
|
112
|
+
var cleanUpTransitionListener;
|
|
113
|
+
if (rootNodeType === 'extension' || rootNodeType === 'embedCard') {
|
|
114
|
+
var anchorDom = view.dom.querySelector("[".concat(getAnchorAttrName(), "=\"").concat(rootAnchorName !== null && rootAnchorName !== void 0 ? rootAnchorName : anchorName, "\"]"));
|
|
115
|
+
if (anchorDom) {
|
|
116
|
+
cleanUpTransitionListener = bind(anchorDom, {
|
|
117
|
+
type: 'transitionend',
|
|
118
|
+
listener: function listener() {
|
|
119
|
+
return setPositionStyles(calculatePosition());
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
var id = requestAnimationFrame(function () {
|
|
125
|
+
return setPositionStyles(calculatePosition());
|
|
126
|
+
});
|
|
127
|
+
return function () {
|
|
128
|
+
var _cleanUpTransitionLis;
|
|
129
|
+
cancelAnimationFrame(id);
|
|
130
|
+
(_cleanUpTransitionLis = cleanUpTransitionListener) === null || _cleanUpTransitionLis === void 0 || _cleanUpTransitionLis();
|
|
131
|
+
};
|
|
132
|
+
}, [calculatePosition, view.dom, rootAnchorName, rootNodeType, anchorName]);
|
|
133
|
+
return jsx(VisibilityContainer, {
|
|
134
|
+
api: api
|
|
135
|
+
}, jsx("div", {
|
|
136
|
+
css: containerBaseStyles
|
|
137
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Dynamic positioning (left, top, height) calculated at runtime
|
|
138
|
+
,
|
|
139
|
+
style: positionStyles,
|
|
140
|
+
"data-testid": "block-ctrl-remix-button"
|
|
141
|
+
}, jsx(IconButton, {
|
|
142
|
+
spacing: "compact",
|
|
143
|
+
appearance: "subtle",
|
|
144
|
+
label: "Remix",
|
|
145
|
+
icon: function icon() {
|
|
146
|
+
return jsx(RandomizeIcon, {
|
|
147
|
+
label: ""
|
|
148
|
+
});
|
|
149
|
+
},
|
|
150
|
+
onMouseDown: function onMouseDown(e) {
|
|
151
|
+
return e.preventDefault();
|
|
152
|
+
}
|
|
153
|
+
})));
|
|
154
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type IntlShape } from 'react-intl-next';
|
|
2
|
+
import type { PortalProviderAPI } from '@atlaskit/editor-common/portal';
|
|
3
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
4
|
+
import { type EditorState } from '@atlaskit/editor-prosemirror/state';
|
|
5
|
+
import { Decoration, type DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
6
|
+
import type { BlockControlsPlugin } from '../blockControlsPluginType';
|
|
7
|
+
export declare const findRemixButtonDecoration: (decorations: DecorationSet, from?: number, to?: number) => Decoration[];
|
|
8
|
+
type RemixButtonDecorationParams = {
|
|
9
|
+
anchorName: string;
|
|
10
|
+
api: ExtractInjectionAPI<BlockControlsPlugin>;
|
|
11
|
+
editorState: EditorState;
|
|
12
|
+
formatMessage: IntlShape['formatMessage'];
|
|
13
|
+
nodeType: string;
|
|
14
|
+
nodeViewPortalProviderAPI: PortalProviderAPI;
|
|
15
|
+
rootAnchorName?: string;
|
|
16
|
+
rootNodeType?: string;
|
|
17
|
+
rootPos: number;
|
|
18
|
+
};
|
|
19
|
+
/** Right-edge Remix button: same gutter as left controls (side: -4) but positioned at block right edge. */
|
|
20
|
+
export declare const remixButtonDecoration: ({ api, formatMessage, rootPos, anchorName, nodeType, nodeViewPortalProviderAPI, rootAnchorName, rootNodeType, }: RemixButtonDecorationParams) => Decoration;
|
|
21
|
+
export {};
|
|
@@ -2,3 +2,13 @@ export declare const getLeftPositionForRootElement: (dom: HTMLElement | null, no
|
|
|
2
2
|
height: number;
|
|
3
3
|
width: number;
|
|
4
4
|
}, innerContainer?: HTMLElement | null, macroInteractionUpdates?: boolean) => string;
|
|
5
|
+
/**
|
|
6
|
+
* Left position (in px) for a widget on the right edge of the block.
|
|
7
|
+
* Mirrors getLeftPositionForRootElement: when innerContainer is set (table, mediaSingle,
|
|
8
|
+
* extension, blockCard, embedCard) use it for the right edge so the button aligns to the
|
|
9
|
+
* content box and does not overlap; otherwise use the block (dom).
|
|
10
|
+
*/
|
|
11
|
+
export declare const getRightPositionForRootElement: (dom: HTMLElement | null, nodeType: string, widgetDimensions: {
|
|
12
|
+
height: number;
|
|
13
|
+
width: number;
|
|
14
|
+
}, innerContainer?: HTMLElement | null, macroInteractionUpdates?: boolean) => string;
|
|
@@ -30,6 +30,14 @@ export declare const QUICK_INSERT_DIMENSIONS: {
|
|
|
30
30
|
height: number;
|
|
31
31
|
};
|
|
32
32
|
export declare const QUICK_INSERT_LEFT_OFFSET = 16;
|
|
33
|
+
export declare const REMIX_BUTTON_HEIGHT = 24;
|
|
34
|
+
export declare const REMIX_BUTTON_WIDTH = 24;
|
|
35
|
+
export declare const REMIX_BUTTON_DIMENSIONS: {
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
};
|
|
39
|
+
/** Extra offset to the right for the right-side Remix button (px) */
|
|
40
|
+
export declare const REMIX_BUTTON_RIGHT_OFFSET = 55;
|
|
33
41
|
export declare const dragHandleGap: (nodeType: string, parentNodeType?: string) => number;
|
|
34
42
|
export declare const rootElementGap: (nodeType: string) => number;
|
|
35
43
|
export declare const getNestedNodeLeftPaddingMargin: (nodeType?: string) => "8px" | "16px" | "20px" | "24px" | "28px" | "40px";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx } from '@emotion/react';
|
|
2
|
+
import type { IntlShape } from 'react-intl-next';
|
|
3
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
4
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
5
|
+
import type { BlockControlsPlugin } from '../blockControlsPluginType';
|
|
6
|
+
type RemixButtonProps = {
|
|
7
|
+
anchorName: string;
|
|
8
|
+
api: ExtractInjectionAPI<BlockControlsPlugin>;
|
|
9
|
+
formatMessage: IntlShape['formatMessage'];
|
|
10
|
+
getPos: () => number | undefined;
|
|
11
|
+
nodeType: string;
|
|
12
|
+
rootAnchorName?: string;
|
|
13
|
+
rootNodeType: string;
|
|
14
|
+
view: EditorView;
|
|
15
|
+
};
|
|
16
|
+
export declare const RemixButton: ({ view, api, getPos, anchorName, rootAnchorName, rootNodeType, }: RemixButtonProps) => jsx.JSX.Element;
|
|
17
|
+
export {};
|
|
@@ -8,5 +8,5 @@ type RefreshAnchorNameParams = {
|
|
|
8
8
|
* Checks for plugin state for latest anchorName based on the position, returns
|
|
9
9
|
* provided anchorName if available
|
|
10
10
|
*/
|
|
11
|
-
export declare const refreshAnchorName: ({ getPos, view, anchorName }: RefreshAnchorNameParams) => string;
|
|
11
|
+
export declare const refreshAnchorName: ({ getPos, view, anchorName, }: RefreshAnchorNameParams) => string;
|
|
12
12
|
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type IntlShape } from 'react-intl-next';
|
|
2
|
+
import type { PortalProviderAPI } from '@atlaskit/editor-common/portal';
|
|
3
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
4
|
+
import { type EditorState } from '@atlaskit/editor-prosemirror/state';
|
|
5
|
+
import { Decoration, type DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
6
|
+
import type { BlockControlsPlugin } from '../blockControlsPluginType';
|
|
7
|
+
export declare const findRemixButtonDecoration: (decorations: DecorationSet, from?: number, to?: number) => Decoration[];
|
|
8
|
+
type RemixButtonDecorationParams = {
|
|
9
|
+
anchorName: string;
|
|
10
|
+
api: ExtractInjectionAPI<BlockControlsPlugin>;
|
|
11
|
+
editorState: EditorState;
|
|
12
|
+
formatMessage: IntlShape['formatMessage'];
|
|
13
|
+
nodeType: string;
|
|
14
|
+
nodeViewPortalProviderAPI: PortalProviderAPI;
|
|
15
|
+
rootAnchorName?: string;
|
|
16
|
+
rootNodeType?: string;
|
|
17
|
+
rootPos: number;
|
|
18
|
+
};
|
|
19
|
+
/** Right-edge Remix button: same gutter as left controls (side: -4) but positioned at block right edge. */
|
|
20
|
+
export declare const remixButtonDecoration: ({ api, formatMessage, rootPos, anchorName, nodeType, nodeViewPortalProviderAPI, rootAnchorName, rootNodeType, }: RemixButtonDecorationParams) => Decoration;
|
|
21
|
+
export {};
|
|
@@ -2,3 +2,13 @@ export declare const getLeftPositionForRootElement: (dom: HTMLElement | null, no
|
|
|
2
2
|
height: number;
|
|
3
3
|
width: number;
|
|
4
4
|
}, innerContainer?: HTMLElement | null, macroInteractionUpdates?: boolean) => string;
|
|
5
|
+
/**
|
|
6
|
+
* Left position (in px) for a widget on the right edge of the block.
|
|
7
|
+
* Mirrors getLeftPositionForRootElement: when innerContainer is set (table, mediaSingle,
|
|
8
|
+
* extension, blockCard, embedCard) use it for the right edge so the button aligns to the
|
|
9
|
+
* content box and does not overlap; otherwise use the block (dom).
|
|
10
|
+
*/
|
|
11
|
+
export declare const getRightPositionForRootElement: (dom: HTMLElement | null, nodeType: string, widgetDimensions: {
|
|
12
|
+
height: number;
|
|
13
|
+
width: number;
|
|
14
|
+
}, innerContainer?: HTMLElement | null, macroInteractionUpdates?: boolean) => string;
|
|
@@ -30,6 +30,14 @@ export declare const QUICK_INSERT_DIMENSIONS: {
|
|
|
30
30
|
height: number;
|
|
31
31
|
};
|
|
32
32
|
export declare const QUICK_INSERT_LEFT_OFFSET = 16;
|
|
33
|
+
export declare const REMIX_BUTTON_HEIGHT = 24;
|
|
34
|
+
export declare const REMIX_BUTTON_WIDTH = 24;
|
|
35
|
+
export declare const REMIX_BUTTON_DIMENSIONS: {
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
};
|
|
39
|
+
/** Extra offset to the right for the right-side Remix button (px) */
|
|
40
|
+
export declare const REMIX_BUTTON_RIGHT_OFFSET = 55;
|
|
33
41
|
export declare const dragHandleGap: (nodeType: string, parentNodeType?: string) => number;
|
|
34
42
|
export declare const rootElementGap: (nodeType: string) => number;
|
|
35
43
|
export declare const getNestedNodeLeftPaddingMargin: (nodeType?: string) => "8px" | "16px" | "20px" | "24px" | "28px" | "40px";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx } from '@emotion/react';
|
|
2
|
+
import type { IntlShape } from 'react-intl-next';
|
|
3
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
4
|
+
import type { EditorView } from '@atlaskit/editor-prosemirror/view';
|
|
5
|
+
import type { BlockControlsPlugin } from '../blockControlsPluginType';
|
|
6
|
+
type RemixButtonProps = {
|
|
7
|
+
anchorName: string;
|
|
8
|
+
api: ExtractInjectionAPI<BlockControlsPlugin>;
|
|
9
|
+
formatMessage: IntlShape['formatMessage'];
|
|
10
|
+
getPos: () => number | undefined;
|
|
11
|
+
nodeType: string;
|
|
12
|
+
rootAnchorName?: string;
|
|
13
|
+
rootNodeType: string;
|
|
14
|
+
view: EditorView;
|
|
15
|
+
};
|
|
16
|
+
export declare const RemixButton: ({ view, api, getPos, anchorName, rootAnchorName, rootNodeType, }: RemixButtonProps) => jsx.JSX.Element;
|
|
17
|
+
export {};
|
|
@@ -8,5 +8,5 @@ type RefreshAnchorNameParams = {
|
|
|
8
8
|
* Checks for plugin state for latest anchorName based on the position, returns
|
|
9
9
|
* provided anchorName if available
|
|
10
10
|
*/
|
|
11
|
-
export declare const refreshAnchorName: ({ getPos, view, anchorName }: RefreshAnchorNameParams) => string;
|
|
11
|
+
export declare const refreshAnchorName: ({ getPos, view, anchorName, }: RefreshAnchorNameParams) => string;
|
|
12
12
|
export {};
|