@atlaskit/editor-plugin-block-menu 6.0.47 → 6.0.48
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 +10 -0
- package/dist/cjs/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +29 -8
- package/dist/cjs/editor-commands/transform-node-utils/wrapStep.js +31 -4
- package/dist/cjs/pm-plugins/experiences/block-menu-experiences.js +27 -0
- package/dist/cjs/pm-plugins/experiences/experience-check-utils.js +41 -3
- package/dist/es2019/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +22 -3
- package/dist/es2019/editor-commands/transform-node-utils/wrapStep.js +27 -2
- package/dist/es2019/pm-plugins/experiences/block-menu-experiences.js +28 -1
- package/dist/es2019/pm-plugins/experiences/experience-check-utils.js +38 -2
- package/dist/esm/editor-commands/transform-node-utils/steps/wrapMixedContentStep.js +24 -3
- package/dist/esm/editor-commands/transform-node-utils/wrapStep.js +29 -2
- package/dist/esm/pm-plugins/experiences/block-menu-experiences.js +28 -1
- package/dist/esm/pm-plugins/experiences/experience-check-utils.js +41 -2
- package/dist/types/editor-commands/transform-node-utils/wrapStep.d.ts +3 -0
- package/dist/types/pm-plugins/experiences/experience-check-utils.d.ts +13 -1
- package/dist/types-ts4.5/editor-commands/transform-node-utils/wrapStep.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/experiences/experience-check-utils.d.ts +13 -1
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-block-menu
|
|
2
2
|
|
|
3
|
+
## 6.0.48
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`3cfeff169fe0d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3cfeff169fe0d) -
|
|
8
|
+
[ux] Persist width marks for supported node types when transforming
|
|
9
|
+
- [`8d553b883996b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8d553b883996b) -
|
|
10
|
+
Add block menu transform experience tracking
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
|
|
3
13
|
## 6.0.47
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -6,21 +6,23 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.wrapMixedContentStep = void 0;
|
|
8
8
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
|
+
var _utils = require("@atlaskit/editor-common/utils");
|
|
9
10
|
var _model = require("@atlaskit/editor-prosemirror/model");
|
|
10
11
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
11
12
|
var _marks = require("../marks");
|
|
12
13
|
var _types = require("../types");
|
|
13
|
-
var
|
|
14
|
+
var _utils2 = require("../utils");
|
|
14
15
|
/**
|
|
15
16
|
* Creates a layout section with two columns, where the first column contains the provided content.
|
|
17
|
+
* Preserves breakout marks if provided.
|
|
16
18
|
*/
|
|
17
|
-
var createLayoutSection = function createLayoutSection(content, layoutSection, layoutColumn) {
|
|
19
|
+
var createLayoutSection = function createLayoutSection(content, layoutSection, layoutColumn, marks) {
|
|
18
20
|
var columnOne = layoutColumn.createAndFill({}, (0, _marks.removeDisallowedMarks)(content, layoutColumn));
|
|
19
21
|
var columnTwo = layoutColumn.createAndFill();
|
|
20
22
|
if (!columnOne || !columnTwo) {
|
|
21
23
|
return null;
|
|
22
24
|
}
|
|
23
|
-
return layoutSection.createAndFill({}, [columnOne, columnTwo]);
|
|
25
|
+
return layoutSection.createAndFill({}, [columnOne, columnTwo], marks);
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
/**
|
|
@@ -140,6 +142,25 @@ var wrapMixedContentStep = exports.wrapMixedContentStep = function wrapMixedCont
|
|
|
140
142
|
var _schema$nodes = schema.nodes,
|
|
141
143
|
layoutSection = _schema$nodes.layoutSection,
|
|
142
144
|
layoutColumn = _schema$nodes.layoutColumn;
|
|
145
|
+
|
|
146
|
+
// [FEATURE FLAG: platform_editor_preserve_breakout_on_transform]
|
|
147
|
+
// Preserves breakout mark width when transforming to layoutSection from resizable nodes.
|
|
148
|
+
// This ensures that custom width settings are maintained during block type transformations.
|
|
149
|
+
// To clean up: remove the if-else block and keep only the flag-on behavior (lines 151-159).
|
|
150
|
+
var breakoutMark;
|
|
151
|
+
if ((0, _platformFeatureFlags.fg)('platform_editor_preserve_breakout_on_transform')) {
|
|
152
|
+
// NEW BEHAVIOR: Preserve breakout mark when both source and target support resizing
|
|
153
|
+
var sourceSupportsBreakout = _utils.breakoutResizableNodes.includes(fromNode.type.name);
|
|
154
|
+
var targetSupportsBreakout = _utils.breakoutResizableNodes.includes(targetNodeTypeName);
|
|
155
|
+
var shouldPreserveBreakout = sourceSupportsBreakout && targetSupportsBreakout;
|
|
156
|
+
if (shouldPreserveBreakout) {
|
|
157
|
+
breakoutMark = fromNode.marks.find(function (mark) {
|
|
158
|
+
return mark.type.name === 'breakout';
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// else: OLD BEHAVIOR - no breakout mark preservation (to be removed when flag is cleaned up)
|
|
163
|
+
|
|
143
164
|
var result = [];
|
|
144
165
|
var currentContainerContent = [];
|
|
145
166
|
var hasCreatedContainer = false;
|
|
@@ -149,7 +170,7 @@ var wrapMixedContentStep = exports.wrapMixedContentStep = function wrapMixedCont
|
|
|
149
170
|
}
|
|
150
171
|
var container = null;
|
|
151
172
|
if (isLayout) {
|
|
152
|
-
container = createLayoutSection(currentContainerContent, layoutSection, layoutColumn);
|
|
173
|
+
container = createLayoutSection(currentContainerContent, layoutSection, layoutColumn, breakoutMark ? [breakoutMark] : undefined);
|
|
153
174
|
} else if (isCodeblock) {
|
|
154
175
|
container = createTextContentContainer(currentContainerContent, targetNodeType, schema);
|
|
155
176
|
} else {
|
|
@@ -171,10 +192,10 @@ var wrapMixedContentStep = exports.wrapMixedContentStep = function wrapMixedCont
|
|
|
171
192
|
(_currentContainerCont = currentContainerContent).push.apply(_currentContainerCont, (0, _toConsumableArray2.default)((0, _marks.removeDisallowedMarks)([node], validationType)));
|
|
172
193
|
};
|
|
173
194
|
var handleCodeblockTextNode = function handleCodeblockTextNode(node) {
|
|
174
|
-
currentContainerContent.push((0,
|
|
195
|
+
currentContainerContent.push((0, _utils2.createTextContent)(node));
|
|
175
196
|
};
|
|
176
197
|
var handleConvertibleTextNode = function handleConvertibleTextNode(node) {
|
|
177
|
-
var paragraph = (0,
|
|
198
|
+
var paragraph = (0, _utils2.convertTextNodeToParagraph)(node, schema);
|
|
178
199
|
if (paragraph) {
|
|
179
200
|
currentContainerContent.push(paragraph);
|
|
180
201
|
}
|
|
@@ -188,11 +209,11 @@ var wrapMixedContentStep = exports.wrapMixedContentStep = function wrapMixedCont
|
|
|
188
209
|
handleWrappableNode(node);
|
|
189
210
|
return;
|
|
190
211
|
}
|
|
191
|
-
if ((0,
|
|
212
|
+
if ((0, _utils2.isTextNode)(node) && isCodeblock) {
|
|
192
213
|
handleCodeblockTextNode(node);
|
|
193
214
|
return;
|
|
194
215
|
}
|
|
195
|
-
if ((0,
|
|
216
|
+
if ((0, _utils2.isTextNode)(node)) {
|
|
196
217
|
handleConvertibleTextNode(node);
|
|
197
218
|
return;
|
|
198
219
|
}
|
|
@@ -4,17 +4,22 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.wrapStep = void 0;
|
|
7
|
+
var _utils = require("@atlaskit/editor-common/utils");
|
|
7
8
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
8
9
|
var _marks = require("./marks");
|
|
9
|
-
var
|
|
10
|
+
var _utils2 = require("./utils");
|
|
10
11
|
/**
|
|
11
12
|
* Wraps nodes into the target container type.
|
|
12
13
|
* When wrapping into expand, any expand children are converted to nestedExpand
|
|
13
14
|
* since expand cannot be a direct child of expand.
|
|
15
|
+
*
|
|
16
|
+
* Preserves breakout marks when both source and target nodes support resizing
|
|
17
|
+
* (codeBlock, expand, layoutSection).
|
|
14
18
|
*/
|
|
15
19
|
var wrapStep = exports.wrapStep = function wrapStep(nodes, context) {
|
|
16
20
|
var schema = context.schema,
|
|
17
|
-
targetNodeTypeName = context.targetNodeTypeName
|
|
21
|
+
targetNodeTypeName = context.targetNodeTypeName,
|
|
22
|
+
fromNode = context.fromNode;
|
|
18
23
|
|
|
19
24
|
// When wrapping into expand, convert any expand children to nestedExpand
|
|
20
25
|
// since expand cannot be a direct child of expand
|
|
@@ -22,7 +27,7 @@ var wrapStep = exports.wrapStep = function wrapStep(nodes, context) {
|
|
|
22
27
|
if (targetNodeTypeName === 'expand') {
|
|
23
28
|
processedNodes = nodes.map(function (node) {
|
|
24
29
|
if (node.type.name === 'expand') {
|
|
25
|
-
var nestedExpandNode = (0,
|
|
30
|
+
var nestedExpandNode = (0, _utils2.convertExpandToNestedExpand)(node, schema);
|
|
26
31
|
return nestedExpandNode !== null && nestedExpandNode !== void 0 ? nestedExpandNode : node;
|
|
27
32
|
}
|
|
28
33
|
return node;
|
|
@@ -39,6 +44,28 @@ var wrapStep = exports.wrapStep = function wrapStep(nodes, context) {
|
|
|
39
44
|
var nodeAttrs = isExpandType && (0, _platformFeatureFlags.fg)('platform_editor_block_menu_expand_localid_fix') ? {
|
|
40
45
|
localId: crypto.randomUUID()
|
|
41
46
|
} : {};
|
|
42
|
-
|
|
47
|
+
|
|
48
|
+
// [FEATURE FLAG: platform_editor_preserve_breakout_on_transform]
|
|
49
|
+
// Preserves breakout mark width when transforming between resizable nodes (codeBlock, expand, layoutSection).
|
|
50
|
+
// This ensures that custom width settings are maintained during block type transformations.
|
|
51
|
+
// To clean up: remove the if-else block and keep only the flag-on behavior (lines 47-58).
|
|
52
|
+
var marks;
|
|
53
|
+
if ((0, _platformFeatureFlags.fg)('platform_editor_preserve_breakout_on_transform')) {
|
|
54
|
+
// NEW BEHAVIOR: Preserve breakout marks when both source and target support resizing
|
|
55
|
+
var sourceSupportsBreakout = _utils.breakoutResizableNodes.includes(fromNode.type.name);
|
|
56
|
+
var targetSupportsBreakout = _utils.breakoutResizableNodes.includes(targetNodeType.name);
|
|
57
|
+
var shouldPreserveBreakout = sourceSupportsBreakout && targetSupportsBreakout;
|
|
58
|
+
if (shouldPreserveBreakout) {
|
|
59
|
+
var breakoutMark = fromNode.marks.find(function (mark) {
|
|
60
|
+
return mark.type.name === 'breakout';
|
|
61
|
+
});
|
|
62
|
+
if (breakoutMark) {
|
|
63
|
+
marks = [breakoutMark];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// else: OLD BEHAVIOR - no breakout mark preservation (to be removed when flag is cleaned up)
|
|
68
|
+
|
|
69
|
+
var outputNode = targetNodeType.createAndFill(nodeAttrs, (0, _marks.removeDisallowedMarks)(processedNodes, schema.nodes[targetNodeTypeName]), marks);
|
|
43
70
|
return outputNode ? [outputNode] : nodes;
|
|
44
71
|
};
|
|
@@ -91,6 +91,17 @@ var getBlockMenuExperiencesPlugin = exports.getBlockMenuExperiencesPlugin = func
|
|
|
91
91
|
observeConfig: actionObserveConfig
|
|
92
92
|
})]
|
|
93
93
|
});
|
|
94
|
+
var blockTransformExperience = new _experiences.Experience(_experiences.EXPERIENCE_ID.MENU_ACTION, {
|
|
95
|
+
action: _analytics.ACTION.TRANSFORMED,
|
|
96
|
+
actionSubjectId: _analytics.ACTION_SUBJECT_ID.TRANSFORM_BLOCK,
|
|
97
|
+
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
|
|
98
|
+
checks: [new _experiences.ExperienceCheckTimeout({
|
|
99
|
+
durationMs: TIMEOUT_DURATION
|
|
100
|
+
}), new _experiences.ExperienceCheckDomMutation({
|
|
101
|
+
onDomMutation: _experienceCheckUtils.handleTransformDomMutation,
|
|
102
|
+
observeConfig: actionObserveConfig
|
|
103
|
+
})]
|
|
104
|
+
});
|
|
94
105
|
var handleMenuOpened = function handleMenuOpened(method) {
|
|
95
106
|
// Don't start if block menu is already visible
|
|
96
107
|
if ((0, _experienceCheckUtils.isBlockMenuVisible)(getPopupsTarget())) {
|
|
@@ -100,7 +111,20 @@ var getBlockMenuExperiencesPlugin = exports.getBlockMenuExperiencesPlugin = func
|
|
|
100
111
|
method: method
|
|
101
112
|
});
|
|
102
113
|
};
|
|
114
|
+
var handleTransformActioned = function handleTransformActioned(target) {
|
|
115
|
+
if (!target.closest('[data-testid="editor-turn-into-menu--content"]')) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
var turnIntoButton = target.closest('button');
|
|
119
|
+
if (turnIntoButton && turnIntoButton instanceof HTMLElement && !turnIntoButton.hasAttribute('disabled') && turnIntoButton.getAttribute('aria-disabled') !== 'true') {
|
|
120
|
+
blockTransformExperience.start();
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
};
|
|
103
124
|
var handleItemActioned = function handleItemActioned(target) {
|
|
125
|
+
if (handleTransformActioned(target)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
104
128
|
var button = target.closest('button[data-testid]');
|
|
105
129
|
if (!button || !(button instanceof HTMLButtonElement) || button.disabled || button.getAttribute('aria-disabled') === 'true') {
|
|
106
130
|
return;
|
|
@@ -180,6 +204,9 @@ var getBlockMenuExperiencesPlugin = exports.getBlockMenuExperiencesPlugin = func
|
|
|
180
204
|
blockDeleteExperience.abort({
|
|
181
205
|
reason: ABORT_REASON.EDITOR_DESTROYED
|
|
182
206
|
});
|
|
207
|
+
blockTransformExperience.abort({
|
|
208
|
+
reason: ABORT_REASON.EDITOR_DESTROYED
|
|
209
|
+
});
|
|
183
210
|
editorView = undefined;
|
|
184
211
|
unbindClickListener();
|
|
185
212
|
unbindKeydownListener();
|
|
@@ -4,9 +4,10 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.isDragHandleElement = exports.isBlockMenuVisible = exports.handleMoveDomMutation = exports.handleMenuOpenDomMutation = exports.handleDeleteDomMutation = exports.getParentDOMAtSelection = void 0;
|
|
7
|
+
exports.isDragHandleElement = exports.isBlockMenuVisible = exports.handleTransformDomMutation = exports.handleMoveDomMutation = exports.handleMenuOpenDomMutation = exports.handleDeleteDomMutation = exports.getParentDOMAtSelection = void 0;
|
|
8
8
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
9
|
var _experiences = require("@atlaskit/editor-common/experiences");
|
|
10
|
+
var _editorTables = require("@atlaskit/editor-tables");
|
|
10
11
|
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
11
12
|
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
12
13
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
@@ -38,19 +39,31 @@ var isBlockMenuVisible = exports.isBlockMenuVisible = function isBlockMenuVisibl
|
|
|
38
39
|
* from the provided editor view.
|
|
39
40
|
*
|
|
40
41
|
* @param editorView - The editor view from which to get the parent DOM element
|
|
41
|
-
* @returns The parent HTMLElement at the selection
|
|
42
|
+
* @returns The parent HTMLElement of the block at the selection, or null if not found
|
|
42
43
|
*/
|
|
43
44
|
var getParentDOMAtSelection = exports.getParentDOMAtSelection = function getParentDOMAtSelection(editorView) {
|
|
44
45
|
if (!editorView) {
|
|
45
46
|
return null;
|
|
46
47
|
}
|
|
47
48
|
var selection = editorView.state.selection;
|
|
49
|
+
if (selection instanceof _editorTables.CellSelection) {
|
|
50
|
+
// $anchorCell resolves inside the row (before the cell), so
|
|
51
|
+
// $anchorCell.depth is the row's depth. Table is one level up.
|
|
52
|
+
var $cell = selection.$anchorCell;
|
|
53
|
+
var tableDepth = $cell.depth - 1;
|
|
54
|
+
if (tableDepth > 0) {
|
|
55
|
+
var dom = editorView.nodeDOM($cell.before(tableDepth));
|
|
56
|
+
if (dom instanceof HTMLElement && dom.parentElement) {
|
|
57
|
+
return dom.parentElement;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
48
61
|
var from = selection.from;
|
|
49
62
|
var nodeDOM = editorView.nodeDOM(from);
|
|
50
63
|
if (nodeDOM instanceof HTMLElement) {
|
|
51
64
|
return nodeDOM.parentElement;
|
|
52
65
|
}
|
|
53
|
-
return null;
|
|
66
|
+
return editorView.dom || null;
|
|
54
67
|
};
|
|
55
68
|
var isBlockMenuAddedInMutation = function isBlockMenuAddedInMutation(_ref) {
|
|
56
69
|
var type = _ref.type,
|
|
@@ -144,4 +157,29 @@ var handleDeleteDomMutation = exports.handleDeleteDomMutation = function handleD
|
|
|
144
157
|
};
|
|
145
158
|
}
|
|
146
159
|
return undefined;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Handles DOM mutations to determine if a transform action was performed.
|
|
164
|
+
*
|
|
165
|
+
* Transforms replace one block type with another (e.g. paragraph → heading),
|
|
166
|
+
* producing childList mutations with both removed (old node) and added (new node).
|
|
167
|
+
*
|
|
168
|
+
* @param options.mutations - The list of DOM mutation records
|
|
169
|
+
* @returns An ExperienceCheckResult indicating success if a transform was detected, otherwise undefined
|
|
170
|
+
*/
|
|
171
|
+
var handleTransformDomMutation = exports.handleTransformDomMutation = function handleTransformDomMutation(_ref5) {
|
|
172
|
+
var mutations = _ref5.mutations;
|
|
173
|
+
var hasRemovedNodes = mutations.some(function (m) {
|
|
174
|
+
return m.type === 'childList' && m.removedNodes.length > 0;
|
|
175
|
+
});
|
|
176
|
+
var hasAddedNodes = mutations.some(function (m) {
|
|
177
|
+
return m.type === 'childList' && m.addedNodes.length > 0;
|
|
178
|
+
});
|
|
179
|
+
if (hasRemovedNodes && hasAddedNodes) {
|
|
180
|
+
return {
|
|
181
|
+
status: 'success'
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
147
185
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { breakoutResizableNodes } from '@atlaskit/editor-common/utils';
|
|
1
2
|
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
2
3
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
4
|
import { removeDisallowedMarks } from '../marks';
|
|
@@ -6,14 +7,15 @@ import { convertTextNodeToParagraph, createTextContent, isTextNode } from '../ut
|
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Creates a layout section with two columns, where the first column contains the provided content.
|
|
10
|
+
* Preserves breakout marks if provided.
|
|
9
11
|
*/
|
|
10
|
-
const createLayoutSection = (content, layoutSection, layoutColumn) => {
|
|
12
|
+
const createLayoutSection = (content, layoutSection, layoutColumn, marks) => {
|
|
11
13
|
const columnOne = layoutColumn.createAndFill({}, removeDisallowedMarks(content, layoutColumn));
|
|
12
14
|
const columnTwo = layoutColumn.createAndFill();
|
|
13
15
|
if (!columnOne || !columnTwo) {
|
|
14
16
|
return null;
|
|
15
17
|
}
|
|
16
|
-
return layoutSection.createAndFill({}, [columnOne, columnTwo]);
|
|
18
|
+
return layoutSection.createAndFill({}, [columnOne, columnTwo], marks);
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -136,6 +138,23 @@ export const wrapMixedContentStep = (nodes, context) => {
|
|
|
136
138
|
layoutSection,
|
|
137
139
|
layoutColumn
|
|
138
140
|
} = schema.nodes;
|
|
141
|
+
|
|
142
|
+
// [FEATURE FLAG: platform_editor_preserve_breakout_on_transform]
|
|
143
|
+
// Preserves breakout mark width when transforming to layoutSection from resizable nodes.
|
|
144
|
+
// This ensures that custom width settings are maintained during block type transformations.
|
|
145
|
+
// To clean up: remove the if-else block and keep only the flag-on behavior (lines 151-159).
|
|
146
|
+
let breakoutMark;
|
|
147
|
+
if (fg('platform_editor_preserve_breakout_on_transform')) {
|
|
148
|
+
// NEW BEHAVIOR: Preserve breakout mark when both source and target support resizing
|
|
149
|
+
const sourceSupportsBreakout = breakoutResizableNodes.includes(fromNode.type.name);
|
|
150
|
+
const targetSupportsBreakout = breakoutResizableNodes.includes(targetNodeTypeName);
|
|
151
|
+
const shouldPreserveBreakout = sourceSupportsBreakout && targetSupportsBreakout;
|
|
152
|
+
if (shouldPreserveBreakout) {
|
|
153
|
+
breakoutMark = fromNode.marks.find(mark => mark.type.name === 'breakout');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// else: OLD BEHAVIOR - no breakout mark preservation (to be removed when flag is cleaned up)
|
|
157
|
+
|
|
139
158
|
const result = [];
|
|
140
159
|
let currentContainerContent = [];
|
|
141
160
|
let hasCreatedContainer = false;
|
|
@@ -145,7 +164,7 @@ export const wrapMixedContentStep = (nodes, context) => {
|
|
|
145
164
|
}
|
|
146
165
|
let container = null;
|
|
147
166
|
if (isLayout) {
|
|
148
|
-
container = createLayoutSection(currentContainerContent, layoutSection, layoutColumn);
|
|
167
|
+
container = createLayoutSection(currentContainerContent, layoutSection, layoutColumn, breakoutMark ? [breakoutMark] : undefined);
|
|
149
168
|
} else if (isCodeblock) {
|
|
150
169
|
container = createTextContentContainer(currentContainerContent, targetNodeType, schema);
|
|
151
170
|
} else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { breakoutResizableNodes } from '@atlaskit/editor-common/utils';
|
|
1
2
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
2
3
|
import { removeDisallowedMarks } from './marks';
|
|
3
4
|
import { convertExpandToNestedExpand } from './utils';
|
|
@@ -6,11 +7,15 @@ import { convertExpandToNestedExpand } from './utils';
|
|
|
6
7
|
* Wraps nodes into the target container type.
|
|
7
8
|
* When wrapping into expand, any expand children are converted to nestedExpand
|
|
8
9
|
* since expand cannot be a direct child of expand.
|
|
10
|
+
*
|
|
11
|
+
* Preserves breakout marks when both source and target nodes support resizing
|
|
12
|
+
* (codeBlock, expand, layoutSection).
|
|
9
13
|
*/
|
|
10
14
|
export const wrapStep = (nodes, context) => {
|
|
11
15
|
const {
|
|
12
16
|
schema,
|
|
13
|
-
targetNodeTypeName
|
|
17
|
+
targetNodeTypeName,
|
|
18
|
+
fromNode
|
|
14
19
|
} = context;
|
|
15
20
|
|
|
16
21
|
// When wrapping into expand, convert any expand children to nestedExpand
|
|
@@ -36,6 +41,26 @@ export const wrapStep = (nodes, context) => {
|
|
|
36
41
|
const nodeAttrs = isExpandType && fg('platform_editor_block_menu_expand_localid_fix') ? {
|
|
37
42
|
localId: crypto.randomUUID()
|
|
38
43
|
} : {};
|
|
39
|
-
|
|
44
|
+
|
|
45
|
+
// [FEATURE FLAG: platform_editor_preserve_breakout_on_transform]
|
|
46
|
+
// Preserves breakout mark width when transforming between resizable nodes (codeBlock, expand, layoutSection).
|
|
47
|
+
// This ensures that custom width settings are maintained during block type transformations.
|
|
48
|
+
// To clean up: remove the if-else block and keep only the flag-on behavior (lines 47-58).
|
|
49
|
+
let marks;
|
|
50
|
+
if (fg('platform_editor_preserve_breakout_on_transform')) {
|
|
51
|
+
// NEW BEHAVIOR: Preserve breakout marks when both source and target support resizing
|
|
52
|
+
const sourceSupportsBreakout = breakoutResizableNodes.includes(fromNode.type.name);
|
|
53
|
+
const targetSupportsBreakout = breakoutResizableNodes.includes(targetNodeType.name);
|
|
54
|
+
const shouldPreserveBreakout = sourceSupportsBreakout && targetSupportsBreakout;
|
|
55
|
+
if (shouldPreserveBreakout) {
|
|
56
|
+
const breakoutMark = fromNode.marks.find(mark => mark.type.name === 'breakout');
|
|
57
|
+
if (breakoutMark) {
|
|
58
|
+
marks = [breakoutMark];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// else: OLD BEHAVIOR - no breakout mark preservation (to be removed when flag is cleaned up)
|
|
63
|
+
|
|
64
|
+
const outputNode = targetNodeType.createAndFill(nodeAttrs, removeDisallowedMarks(processedNodes, schema.nodes[targetNodeTypeName]), marks);
|
|
40
65
|
return outputNode ? [outputNode] : nodes;
|
|
41
66
|
};
|
|
@@ -4,7 +4,7 @@ import { BLOCK_MENU_ACTION_TEST_ID } from '@atlaskit/editor-common/block-menu';
|
|
|
4
4
|
import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getPopupContainerFromEditorView } from '@atlaskit/editor-common/experiences';
|
|
5
5
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
6
6
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
7
|
-
import { getParentDOMAtSelection, handleDeleteDomMutation, handleMenuOpenDomMutation, handleMoveDomMutation, isBlockMenuVisible, isDragHandleElement } from './experience-check-utils';
|
|
7
|
+
import { getParentDOMAtSelection, handleDeleteDomMutation, handleMenuOpenDomMutation, handleMoveDomMutation, handleTransformDomMutation, isBlockMenuVisible, isDragHandleElement } from './experience-check-utils';
|
|
8
8
|
const TIMEOUT_DURATION = 1000;
|
|
9
9
|
const pluginKey = new PluginKey('blockMenuExperiences');
|
|
10
10
|
const START_METHOD = {
|
|
@@ -82,6 +82,17 @@ export const getBlockMenuExperiencesPlugin = ({
|
|
|
82
82
|
observeConfig: actionObserveConfig
|
|
83
83
|
})]
|
|
84
84
|
});
|
|
85
|
+
const blockTransformExperience = new Experience(EXPERIENCE_ID.MENU_ACTION, {
|
|
86
|
+
action: ACTION.TRANSFORMED,
|
|
87
|
+
actionSubjectId: ACTION_SUBJECT_ID.TRANSFORM_BLOCK,
|
|
88
|
+
dispatchAnalyticsEvent,
|
|
89
|
+
checks: [new ExperienceCheckTimeout({
|
|
90
|
+
durationMs: TIMEOUT_DURATION
|
|
91
|
+
}), new ExperienceCheckDomMutation({
|
|
92
|
+
onDomMutation: handleTransformDomMutation,
|
|
93
|
+
observeConfig: actionObserveConfig
|
|
94
|
+
})]
|
|
95
|
+
});
|
|
85
96
|
const handleMenuOpened = method => {
|
|
86
97
|
// Don't start if block menu is already visible
|
|
87
98
|
if (isBlockMenuVisible(getPopupsTarget())) {
|
|
@@ -91,7 +102,20 @@ export const getBlockMenuExperiencesPlugin = ({
|
|
|
91
102
|
method
|
|
92
103
|
});
|
|
93
104
|
};
|
|
105
|
+
const handleTransformActioned = target => {
|
|
106
|
+
if (!target.closest('[data-testid="editor-turn-into-menu--content"]')) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const turnIntoButton = target.closest('button');
|
|
110
|
+
if (turnIntoButton && turnIntoButton instanceof HTMLElement && !turnIntoButton.hasAttribute('disabled') && turnIntoButton.getAttribute('aria-disabled') !== 'true') {
|
|
111
|
+
blockTransformExperience.start();
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
};
|
|
94
115
|
const handleItemActioned = target => {
|
|
116
|
+
if (handleTransformActioned(target)) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
95
119
|
const button = target.closest('button[data-testid]');
|
|
96
120
|
if (!button || !(button instanceof HTMLButtonElement) || button.disabled || button.getAttribute('aria-disabled') === 'true') {
|
|
97
121
|
return;
|
|
@@ -171,6 +195,9 @@ export const getBlockMenuExperiencesPlugin = ({
|
|
|
171
195
|
blockDeleteExperience.abort({
|
|
172
196
|
reason: ABORT_REASON.EDITOR_DESTROYED
|
|
173
197
|
});
|
|
198
|
+
blockTransformExperience.abort({
|
|
199
|
+
reason: ABORT_REASON.EDITOR_DESTROYED
|
|
200
|
+
});
|
|
174
201
|
editorView = undefined;
|
|
175
202
|
unbindClickListener();
|
|
176
203
|
unbindKeydownListener();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { popupWithNestedElement } from '@atlaskit/editor-common/experiences';
|
|
2
|
+
import { CellSelection } from '@atlaskit/editor-tables';
|
|
3
|
+
|
|
2
4
|
/**
|
|
3
5
|
* Checks if the given element or any of its ancestors is a drag handle element.
|
|
4
6
|
*
|
|
@@ -27,7 +29,7 @@ export const isBlockMenuVisible = popupsTarget => {
|
|
|
27
29
|
* from the provided editor view.
|
|
28
30
|
*
|
|
29
31
|
* @param editorView - The editor view from which to get the parent DOM element
|
|
30
|
-
* @returns The parent HTMLElement at the selection
|
|
32
|
+
* @returns The parent HTMLElement of the block at the selection, or null if not found
|
|
31
33
|
*/
|
|
32
34
|
export const getParentDOMAtSelection = editorView => {
|
|
33
35
|
if (!editorView) {
|
|
@@ -36,12 +38,24 @@ export const getParentDOMAtSelection = editorView => {
|
|
|
36
38
|
const {
|
|
37
39
|
selection
|
|
38
40
|
} = editorView.state;
|
|
41
|
+
if (selection instanceof CellSelection) {
|
|
42
|
+
// $anchorCell resolves inside the row (before the cell), so
|
|
43
|
+
// $anchorCell.depth is the row's depth. Table is one level up.
|
|
44
|
+
const $cell = selection.$anchorCell;
|
|
45
|
+
const tableDepth = $cell.depth - 1;
|
|
46
|
+
if (tableDepth > 0) {
|
|
47
|
+
const dom = editorView.nodeDOM($cell.before(tableDepth));
|
|
48
|
+
if (dom instanceof HTMLElement && dom.parentElement) {
|
|
49
|
+
return dom.parentElement;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
39
53
|
const from = selection.from;
|
|
40
54
|
const nodeDOM = editorView.nodeDOM(from);
|
|
41
55
|
if (nodeDOM instanceof HTMLElement) {
|
|
42
56
|
return nodeDOM.parentElement;
|
|
43
57
|
}
|
|
44
|
-
return null;
|
|
58
|
+
return editorView.dom || null;
|
|
45
59
|
};
|
|
46
60
|
const isBlockMenuAddedInMutation = ({
|
|
47
61
|
type,
|
|
@@ -122,4 +136,26 @@ export const handleDeleteDomMutation = ({
|
|
|
122
136
|
};
|
|
123
137
|
}
|
|
124
138
|
return undefined;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Handles DOM mutations to determine if a transform action was performed.
|
|
143
|
+
*
|
|
144
|
+
* Transforms replace one block type with another (e.g. paragraph → heading),
|
|
145
|
+
* producing childList mutations with both removed (old node) and added (new node).
|
|
146
|
+
*
|
|
147
|
+
* @param options.mutations - The list of DOM mutation records
|
|
148
|
+
* @returns An ExperienceCheckResult indicating success if a transform was detected, otherwise undefined
|
|
149
|
+
*/
|
|
150
|
+
export const handleTransformDomMutation = ({
|
|
151
|
+
mutations
|
|
152
|
+
}) => {
|
|
153
|
+
const hasRemovedNodes = mutations.some(m => m.type === 'childList' && m.removedNodes.length > 0);
|
|
154
|
+
const hasAddedNodes = mutations.some(m => m.type === 'childList' && m.addedNodes.length > 0);
|
|
155
|
+
if (hasRemovedNodes && hasAddedNodes) {
|
|
156
|
+
return {
|
|
157
|
+
status: 'success'
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
125
161
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
+
import { breakoutResizableNodes } from '@atlaskit/editor-common/utils';
|
|
2
3
|
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
3
4
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
4
5
|
import { removeDisallowedMarks } from '../marks';
|
|
@@ -7,14 +8,15 @@ import { convertTextNodeToParagraph, createTextContent, isTextNode } from '../ut
|
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Creates a layout section with two columns, where the first column contains the provided content.
|
|
11
|
+
* Preserves breakout marks if provided.
|
|
10
12
|
*/
|
|
11
|
-
var createLayoutSection = function createLayoutSection(content, layoutSection, layoutColumn) {
|
|
13
|
+
var createLayoutSection = function createLayoutSection(content, layoutSection, layoutColumn, marks) {
|
|
12
14
|
var columnOne = layoutColumn.createAndFill({}, removeDisallowedMarks(content, layoutColumn));
|
|
13
15
|
var columnTwo = layoutColumn.createAndFill();
|
|
14
16
|
if (!columnOne || !columnTwo) {
|
|
15
17
|
return null;
|
|
16
18
|
}
|
|
17
|
-
return layoutSection.createAndFill({}, [columnOne, columnTwo]);
|
|
19
|
+
return layoutSection.createAndFill({}, [columnOne, columnTwo], marks);
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
/**
|
|
@@ -134,6 +136,25 @@ export var wrapMixedContentStep = function wrapMixedContentStep(nodes, context)
|
|
|
134
136
|
var _schema$nodes = schema.nodes,
|
|
135
137
|
layoutSection = _schema$nodes.layoutSection,
|
|
136
138
|
layoutColumn = _schema$nodes.layoutColumn;
|
|
139
|
+
|
|
140
|
+
// [FEATURE FLAG: platform_editor_preserve_breakout_on_transform]
|
|
141
|
+
// Preserves breakout mark width when transforming to layoutSection from resizable nodes.
|
|
142
|
+
// This ensures that custom width settings are maintained during block type transformations.
|
|
143
|
+
// To clean up: remove the if-else block and keep only the flag-on behavior (lines 151-159).
|
|
144
|
+
var breakoutMark;
|
|
145
|
+
if (fg('platform_editor_preserve_breakout_on_transform')) {
|
|
146
|
+
// NEW BEHAVIOR: Preserve breakout mark when both source and target support resizing
|
|
147
|
+
var sourceSupportsBreakout = breakoutResizableNodes.includes(fromNode.type.name);
|
|
148
|
+
var targetSupportsBreakout = breakoutResizableNodes.includes(targetNodeTypeName);
|
|
149
|
+
var shouldPreserveBreakout = sourceSupportsBreakout && targetSupportsBreakout;
|
|
150
|
+
if (shouldPreserveBreakout) {
|
|
151
|
+
breakoutMark = fromNode.marks.find(function (mark) {
|
|
152
|
+
return mark.type.name === 'breakout';
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// else: OLD BEHAVIOR - no breakout mark preservation (to be removed when flag is cleaned up)
|
|
157
|
+
|
|
137
158
|
var result = [];
|
|
138
159
|
var currentContainerContent = [];
|
|
139
160
|
var hasCreatedContainer = false;
|
|
@@ -143,7 +164,7 @@ export var wrapMixedContentStep = function wrapMixedContentStep(nodes, context)
|
|
|
143
164
|
}
|
|
144
165
|
var container = null;
|
|
145
166
|
if (isLayout) {
|
|
146
|
-
container = createLayoutSection(currentContainerContent, layoutSection, layoutColumn);
|
|
167
|
+
container = createLayoutSection(currentContainerContent, layoutSection, layoutColumn, breakoutMark ? [breakoutMark] : undefined);
|
|
147
168
|
} else if (isCodeblock) {
|
|
148
169
|
container = createTextContentContainer(currentContainerContent, targetNodeType, schema);
|
|
149
170
|
} else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { breakoutResizableNodes } from '@atlaskit/editor-common/utils';
|
|
1
2
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
2
3
|
import { removeDisallowedMarks } from './marks';
|
|
3
4
|
import { convertExpandToNestedExpand } from './utils';
|
|
@@ -6,10 +7,14 @@ import { convertExpandToNestedExpand } from './utils';
|
|
|
6
7
|
* Wraps nodes into the target container type.
|
|
7
8
|
* When wrapping into expand, any expand children are converted to nestedExpand
|
|
8
9
|
* since expand cannot be a direct child of expand.
|
|
10
|
+
*
|
|
11
|
+
* Preserves breakout marks when both source and target nodes support resizing
|
|
12
|
+
* (codeBlock, expand, layoutSection).
|
|
9
13
|
*/
|
|
10
14
|
export var wrapStep = function wrapStep(nodes, context) {
|
|
11
15
|
var schema = context.schema,
|
|
12
|
-
targetNodeTypeName = context.targetNodeTypeName
|
|
16
|
+
targetNodeTypeName = context.targetNodeTypeName,
|
|
17
|
+
fromNode = context.fromNode;
|
|
13
18
|
|
|
14
19
|
// When wrapping into expand, convert any expand children to nestedExpand
|
|
15
20
|
// since expand cannot be a direct child of expand
|
|
@@ -34,6 +39,28 @@ export var wrapStep = function wrapStep(nodes, context) {
|
|
|
34
39
|
var nodeAttrs = isExpandType && fg('platform_editor_block_menu_expand_localid_fix') ? {
|
|
35
40
|
localId: crypto.randomUUID()
|
|
36
41
|
} : {};
|
|
37
|
-
|
|
42
|
+
|
|
43
|
+
// [FEATURE FLAG: platform_editor_preserve_breakout_on_transform]
|
|
44
|
+
// Preserves breakout mark width when transforming between resizable nodes (codeBlock, expand, layoutSection).
|
|
45
|
+
// This ensures that custom width settings are maintained during block type transformations.
|
|
46
|
+
// To clean up: remove the if-else block and keep only the flag-on behavior (lines 47-58).
|
|
47
|
+
var marks;
|
|
48
|
+
if (fg('platform_editor_preserve_breakout_on_transform')) {
|
|
49
|
+
// NEW BEHAVIOR: Preserve breakout marks when both source and target support resizing
|
|
50
|
+
var sourceSupportsBreakout = breakoutResizableNodes.includes(fromNode.type.name);
|
|
51
|
+
var targetSupportsBreakout = breakoutResizableNodes.includes(targetNodeType.name);
|
|
52
|
+
var shouldPreserveBreakout = sourceSupportsBreakout && targetSupportsBreakout;
|
|
53
|
+
if (shouldPreserveBreakout) {
|
|
54
|
+
var breakoutMark = fromNode.marks.find(function (mark) {
|
|
55
|
+
return mark.type.name === 'breakout';
|
|
56
|
+
});
|
|
57
|
+
if (breakoutMark) {
|
|
58
|
+
marks = [breakoutMark];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// else: OLD BEHAVIOR - no breakout mark preservation (to be removed when flag is cleaned up)
|
|
63
|
+
|
|
64
|
+
var outputNode = targetNodeType.createAndFill(nodeAttrs, removeDisallowedMarks(processedNodes, schema.nodes[targetNodeTypeName]), marks);
|
|
38
65
|
return outputNode ? [outputNode] : nodes;
|
|
39
66
|
};
|
|
@@ -4,7 +4,7 @@ import { BLOCK_MENU_ACTION_TEST_ID } from '@atlaskit/editor-common/block-menu';
|
|
|
4
4
|
import { Experience, EXPERIENCE_ID, ExperienceCheckDomMutation, ExperienceCheckTimeout, getPopupContainerFromEditorView } from '@atlaskit/editor-common/experiences';
|
|
5
5
|
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
6
6
|
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
7
|
-
import { getParentDOMAtSelection, handleDeleteDomMutation, handleMenuOpenDomMutation, handleMoveDomMutation, isBlockMenuVisible, isDragHandleElement } from './experience-check-utils';
|
|
7
|
+
import { getParentDOMAtSelection, handleDeleteDomMutation, handleMenuOpenDomMutation, handleMoveDomMutation, handleTransformDomMutation, isBlockMenuVisible, isDragHandleElement } from './experience-check-utils';
|
|
8
8
|
var TIMEOUT_DURATION = 1000;
|
|
9
9
|
var pluginKey = new PluginKey('blockMenuExperiences');
|
|
10
10
|
var START_METHOD = {
|
|
@@ -85,6 +85,17 @@ export var getBlockMenuExperiencesPlugin = function getBlockMenuExperiencesPlugi
|
|
|
85
85
|
observeConfig: actionObserveConfig
|
|
86
86
|
})]
|
|
87
87
|
});
|
|
88
|
+
var blockTransformExperience = new Experience(EXPERIENCE_ID.MENU_ACTION, {
|
|
89
|
+
action: ACTION.TRANSFORMED,
|
|
90
|
+
actionSubjectId: ACTION_SUBJECT_ID.TRANSFORM_BLOCK,
|
|
91
|
+
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
|
|
92
|
+
checks: [new ExperienceCheckTimeout({
|
|
93
|
+
durationMs: TIMEOUT_DURATION
|
|
94
|
+
}), new ExperienceCheckDomMutation({
|
|
95
|
+
onDomMutation: handleTransformDomMutation,
|
|
96
|
+
observeConfig: actionObserveConfig
|
|
97
|
+
})]
|
|
98
|
+
});
|
|
88
99
|
var handleMenuOpened = function handleMenuOpened(method) {
|
|
89
100
|
// Don't start if block menu is already visible
|
|
90
101
|
if (isBlockMenuVisible(getPopupsTarget())) {
|
|
@@ -94,7 +105,20 @@ export var getBlockMenuExperiencesPlugin = function getBlockMenuExperiencesPlugi
|
|
|
94
105
|
method: method
|
|
95
106
|
});
|
|
96
107
|
};
|
|
108
|
+
var handleTransformActioned = function handleTransformActioned(target) {
|
|
109
|
+
if (!target.closest('[data-testid="editor-turn-into-menu--content"]')) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
var turnIntoButton = target.closest('button');
|
|
113
|
+
if (turnIntoButton && turnIntoButton instanceof HTMLElement && !turnIntoButton.hasAttribute('disabled') && turnIntoButton.getAttribute('aria-disabled') !== 'true') {
|
|
114
|
+
blockTransformExperience.start();
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
};
|
|
97
118
|
var handleItemActioned = function handleItemActioned(target) {
|
|
119
|
+
if (handleTransformActioned(target)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
98
122
|
var button = target.closest('button[data-testid]');
|
|
99
123
|
if (!button || !(button instanceof HTMLButtonElement) || button.disabled || button.getAttribute('aria-disabled') === 'true') {
|
|
100
124
|
return;
|
|
@@ -174,6 +198,9 @@ export var getBlockMenuExperiencesPlugin = function getBlockMenuExperiencesPlugi
|
|
|
174
198
|
blockDeleteExperience.abort({
|
|
175
199
|
reason: ABORT_REASON.EDITOR_DESTROYED
|
|
176
200
|
});
|
|
201
|
+
blockTransformExperience.abort({
|
|
202
|
+
reason: ABORT_REASON.EDITOR_DESTROYED
|
|
203
|
+
});
|
|
177
204
|
editorView = undefined;
|
|
178
205
|
unbindClickListener();
|
|
179
206
|
unbindKeydownListener();
|
|
@@ -3,6 +3,8 @@ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol
|
|
|
3
3
|
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
4
4
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
5
5
|
import { popupWithNestedElement } from '@atlaskit/editor-common/experiences';
|
|
6
|
+
import { CellSelection } from '@atlaskit/editor-tables';
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Checks if the given element or any of its ancestors is a drag handle element.
|
|
8
10
|
*
|
|
@@ -31,19 +33,31 @@ export var isBlockMenuVisible = function isBlockMenuVisible(popupsTarget) {
|
|
|
31
33
|
* from the provided editor view.
|
|
32
34
|
*
|
|
33
35
|
* @param editorView - The editor view from which to get the parent DOM element
|
|
34
|
-
* @returns The parent HTMLElement at the selection
|
|
36
|
+
* @returns The parent HTMLElement of the block at the selection, or null if not found
|
|
35
37
|
*/
|
|
36
38
|
export var getParentDOMAtSelection = function getParentDOMAtSelection(editorView) {
|
|
37
39
|
if (!editorView) {
|
|
38
40
|
return null;
|
|
39
41
|
}
|
|
40
42
|
var selection = editorView.state.selection;
|
|
43
|
+
if (selection instanceof CellSelection) {
|
|
44
|
+
// $anchorCell resolves inside the row (before the cell), so
|
|
45
|
+
// $anchorCell.depth is the row's depth. Table is one level up.
|
|
46
|
+
var $cell = selection.$anchorCell;
|
|
47
|
+
var tableDepth = $cell.depth - 1;
|
|
48
|
+
if (tableDepth > 0) {
|
|
49
|
+
var dom = editorView.nodeDOM($cell.before(tableDepth));
|
|
50
|
+
if (dom instanceof HTMLElement && dom.parentElement) {
|
|
51
|
+
return dom.parentElement;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
41
55
|
var from = selection.from;
|
|
42
56
|
var nodeDOM = editorView.nodeDOM(from);
|
|
43
57
|
if (nodeDOM instanceof HTMLElement) {
|
|
44
58
|
return nodeDOM.parentElement;
|
|
45
59
|
}
|
|
46
|
-
return null;
|
|
60
|
+
return editorView.dom || null;
|
|
47
61
|
};
|
|
48
62
|
var isBlockMenuAddedInMutation = function isBlockMenuAddedInMutation(_ref) {
|
|
49
63
|
var type = _ref.type,
|
|
@@ -137,4 +151,29 @@ export var handleDeleteDomMutation = function handleDeleteDomMutation(_ref4) {
|
|
|
137
151
|
};
|
|
138
152
|
}
|
|
139
153
|
return undefined;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Handles DOM mutations to determine if a transform action was performed.
|
|
158
|
+
*
|
|
159
|
+
* Transforms replace one block type with another (e.g. paragraph → heading),
|
|
160
|
+
* producing childList mutations with both removed (old node) and added (new node).
|
|
161
|
+
*
|
|
162
|
+
* @param options.mutations - The list of DOM mutation records
|
|
163
|
+
* @returns An ExperienceCheckResult indicating success if a transform was detected, otherwise undefined
|
|
164
|
+
*/
|
|
165
|
+
export var handleTransformDomMutation = function handleTransformDomMutation(_ref5) {
|
|
166
|
+
var mutations = _ref5.mutations;
|
|
167
|
+
var hasRemovedNodes = mutations.some(function (m) {
|
|
168
|
+
return m.type === 'childList' && m.removedNodes.length > 0;
|
|
169
|
+
});
|
|
170
|
+
var hasAddedNodes = mutations.some(function (m) {
|
|
171
|
+
return m.type === 'childList' && m.addedNodes.length > 0;
|
|
172
|
+
});
|
|
173
|
+
if (hasRemovedNodes && hasAddedNodes) {
|
|
174
|
+
return {
|
|
175
|
+
status: 'success'
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return undefined;
|
|
140
179
|
};
|
|
@@ -3,5 +3,8 @@ import type { TransformStep } from './types';
|
|
|
3
3
|
* Wraps nodes into the target container type.
|
|
4
4
|
* When wrapping into expand, any expand children are converted to nestedExpand
|
|
5
5
|
* since expand cannot be a direct child of expand.
|
|
6
|
+
*
|
|
7
|
+
* Preserves breakout marks when both source and target nodes support resizing
|
|
8
|
+
* (codeBlock, expand, layoutSection).
|
|
6
9
|
*/
|
|
7
10
|
export declare const wrapStep: TransformStep;
|
|
@@ -19,7 +19,7 @@ export declare const isBlockMenuVisible: (popupsTarget: HTMLElement | undefined)
|
|
|
19
19
|
* from the provided editor view.
|
|
20
20
|
*
|
|
21
21
|
* @param editorView - The editor view from which to get the parent DOM element
|
|
22
|
-
* @returns The parent HTMLElement at the selection
|
|
22
|
+
* @returns The parent HTMLElement of the block at the selection, or null if not found
|
|
23
23
|
*/
|
|
24
24
|
export declare const getParentDOMAtSelection: (editorView?: EditorView) => HTMLElement | null;
|
|
25
25
|
/**
|
|
@@ -60,3 +60,15 @@ export declare const handleMoveDomMutation: ({ mutations, }: {
|
|
|
60
60
|
export declare const handleDeleteDomMutation: ({ mutations, }: {
|
|
61
61
|
mutations: MutationRecord[];
|
|
62
62
|
}) => ExperienceCheckResult | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Handles DOM mutations to determine if a transform action was performed.
|
|
65
|
+
*
|
|
66
|
+
* Transforms replace one block type with another (e.g. paragraph → heading),
|
|
67
|
+
* producing childList mutations with both removed (old node) and added (new node).
|
|
68
|
+
*
|
|
69
|
+
* @param options.mutations - The list of DOM mutation records
|
|
70
|
+
* @returns An ExperienceCheckResult indicating success if a transform was detected, otherwise undefined
|
|
71
|
+
*/
|
|
72
|
+
export declare const handleTransformDomMutation: ({ mutations, }: {
|
|
73
|
+
mutations: MutationRecord[];
|
|
74
|
+
}) => ExperienceCheckResult | undefined;
|
|
@@ -3,5 +3,8 @@ import type { TransformStep } from './types';
|
|
|
3
3
|
* Wraps nodes into the target container type.
|
|
4
4
|
* When wrapping into expand, any expand children are converted to nestedExpand
|
|
5
5
|
* since expand cannot be a direct child of expand.
|
|
6
|
+
*
|
|
7
|
+
* Preserves breakout marks when both source and target nodes support resizing
|
|
8
|
+
* (codeBlock, expand, layoutSection).
|
|
6
9
|
*/
|
|
7
10
|
export declare const wrapStep: TransformStep;
|
|
@@ -19,7 +19,7 @@ export declare const isBlockMenuVisible: (popupsTarget: HTMLElement | undefined)
|
|
|
19
19
|
* from the provided editor view.
|
|
20
20
|
*
|
|
21
21
|
* @param editorView - The editor view from which to get the parent DOM element
|
|
22
|
-
* @returns The parent HTMLElement at the selection
|
|
22
|
+
* @returns The parent HTMLElement of the block at the selection, or null if not found
|
|
23
23
|
*/
|
|
24
24
|
export declare const getParentDOMAtSelection: (editorView?: EditorView) => HTMLElement | null;
|
|
25
25
|
/**
|
|
@@ -60,3 +60,15 @@ export declare const handleMoveDomMutation: ({ mutations, }: {
|
|
|
60
60
|
export declare const handleDeleteDomMutation: ({ mutations, }: {
|
|
61
61
|
mutations: MutationRecord[];
|
|
62
62
|
}) => ExperienceCheckResult | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Handles DOM mutations to determine if a transform action was performed.
|
|
65
|
+
*
|
|
66
|
+
* Transforms replace one block type with another (e.g. paragraph → heading),
|
|
67
|
+
* producing childList mutations with both removed (old node) and added (new node).
|
|
68
|
+
*
|
|
69
|
+
* @param options.mutations - The list of DOM mutation records
|
|
70
|
+
* @returns An ExperienceCheckResult indicating success if a transform was detected, otherwise undefined
|
|
71
|
+
*/
|
|
72
|
+
export declare const handleTransformDomMutation: ({ mutations, }: {
|
|
73
|
+
mutations: MutationRecord[];
|
|
74
|
+
}) => ExperienceCheckResult | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-block-menu",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.48",
|
|
4
4
|
"description": "BlockMenu plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
"@atlaskit/platform-feature-flags-react": "^0.4.0",
|
|
47
47
|
"@atlaskit/primitives": "^18.0.0",
|
|
48
48
|
"@atlaskit/prosemirror-history": "^0.2.0",
|
|
49
|
-
"@atlaskit/tmp-editor-statsig": "^29.
|
|
49
|
+
"@atlaskit/tmp-editor-statsig": "^29.1.0",
|
|
50
50
|
"@atlaskit/tokens": "^11.0.0",
|
|
51
51
|
"@babel/runtime": "^7.0.0",
|
|
52
52
|
"bind-event-listener": "^3.0.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"@atlaskit/editor-common": "^111.
|
|
55
|
+
"@atlaskit/editor-common": "^111.17.0",
|
|
56
56
|
"react": "^18.2.0",
|
|
57
57
|
"react-intl-next": "npm:react-intl@^5.18.1"
|
|
58
58
|
},
|
|
@@ -104,6 +104,9 @@
|
|
|
104
104
|
},
|
|
105
105
|
"platform_editor_table_transform_selection_fix": {
|
|
106
106
|
"type": "boolean"
|
|
107
|
+
},
|
|
108
|
+
"platform_editor_preserve_breakout_on_transform": {
|
|
109
|
+
"type": "boolean"
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
}
|