@atlaskit/editor-plugin-block-menu 1.0.9 → 1.0.11
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 +21 -0
- package/dist/cjs/editor-commands/transforms/container-transforms.js +102 -4
- package/dist/cjs/ui/copy-link.js +12 -0
- package/dist/cjs/ui/utils/isNestedNode.js +60 -0
- package/dist/es2019/editor-commands/transforms/container-transforms.js +102 -4
- package/dist/es2019/ui/copy-link.js +12 -0
- package/dist/es2019/ui/utils/isNestedNode.js +57 -0
- package/dist/esm/editor-commands/transforms/container-transforms.js +102 -4
- package/dist/esm/ui/copy-link.js +12 -0
- package/dist/esm/ui/utils/isNestedNode.js +55 -0
- package/dist/types/ui/utils/isNestedNode.d.ts +14 -0
- package/dist/types-ts4.5/ui/utils/isNestedNode.d.ts +14 -0
- package/package.json +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-block-menu
|
|
2
2
|
|
|
3
|
+
## 1.0.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`fcef7ff2e1083`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/fcef7ff2e1083) -
|
|
8
|
+
Split unsupported content when converting to codeblock
|
|
9
|
+
- [`1754f5027f568`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/1754f5027f568) -
|
|
10
|
+
Fix missing copy link on table node
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
|
|
13
|
+
## 1.0.10
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [`74c42a764926a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/74c42a764926a) -
|
|
18
|
+
Hide copy link option when platform_editor_adf_with_localid FG is off or when selection is a
|
|
19
|
+
nested node
|
|
20
|
+
- [`614ef1a575e84`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/614ef1a575e84) -
|
|
21
|
+
[ux] ED-29183: Fixed p and headings with alignment not able to convert to panel, expand, block
|
|
22
|
+
quote
|
|
23
|
+
|
|
3
24
|
## 1.0.9
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
|
@@ -20,6 +20,19 @@ var convertInvalidNodeToValidNodeType = function convertInvalidNodeToValidNodeTy
|
|
|
20
20
|
});
|
|
21
21
|
return _model.Fragment.from(validTransformedContent);
|
|
22
22
|
};
|
|
23
|
+
var filterMarksForTargetNodeType = function filterMarksForTargetNodeType(content, targetNodeType) {
|
|
24
|
+
var withValidMarks = [];
|
|
25
|
+
content.forEach(function (node) {
|
|
26
|
+
if (node.marks.length > 0) {
|
|
27
|
+
var allowedMarks = targetNodeType.allowedMarks(node.marks);
|
|
28
|
+
var updatedNode = node.mark(allowedMarks);
|
|
29
|
+
withValidMarks.push(updatedNode);
|
|
30
|
+
} else {
|
|
31
|
+
withValidMarks.push(node);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
return _model.Fragment.from(withValidMarks);
|
|
35
|
+
};
|
|
23
36
|
|
|
24
37
|
/**
|
|
25
38
|
* Transform selection to container type
|
|
@@ -39,6 +52,13 @@ var transformToContainer = exports.transformToContainer = function transformToCo
|
|
|
39
52
|
if (targetNodeType === schema.nodes.blockquote) {
|
|
40
53
|
transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.heading, schema.nodes.paragraph, true);
|
|
41
54
|
}
|
|
55
|
+
|
|
56
|
+
// Preserve marks that are allowed in the target node type
|
|
57
|
+
// e.g. blocks (heading/ paragraph) with alignment need to remove alignment
|
|
58
|
+
// as panel/ blockQuote/ expands does not support alignment
|
|
59
|
+
if (sourceNode.type === schema.nodes.paragraph || sourceNode.type === schema.nodes.heading) {
|
|
60
|
+
transformedContent = filterMarksForTargetNodeType(transformedContent, targetNodeType);
|
|
61
|
+
}
|
|
42
62
|
var newNode = targetNodeType.createAndFill(targetAttrs, transformedContent);
|
|
43
63
|
if (!newNode) {
|
|
44
64
|
return null;
|
|
@@ -62,6 +82,16 @@ var transformContainerNode = exports.transformContainerNode = function transform
|
|
|
62
82
|
|
|
63
83
|
// Transform container to block type - unwrap and convert content
|
|
64
84
|
if ((0, _utils.isBlockNodeType)(targetNodeType)) {
|
|
85
|
+
// special case container to codeblock
|
|
86
|
+
if (targetNodeType.name === 'codeBlock') {
|
|
87
|
+
return transformBetweenContainerTypes({
|
|
88
|
+
tr: tr,
|
|
89
|
+
sourceNode: sourceNode,
|
|
90
|
+
sourcePos: sourcePos,
|
|
91
|
+
targetNodeType: targetNodeType,
|
|
92
|
+
targetAttrs: targetAttrs
|
|
93
|
+
});
|
|
94
|
+
}
|
|
65
95
|
return unwrapAndConvertToBlockType({
|
|
66
96
|
tr: tr,
|
|
67
97
|
sourceNode: sourceNode,
|
|
@@ -254,18 +284,29 @@ var transformBetweenContainerTypes = exports.transformBetweenContainerTypes = fu
|
|
|
254
284
|
targetNodeType = context.targetNodeType,
|
|
255
285
|
targetAttrs = context.targetAttrs;
|
|
256
286
|
|
|
287
|
+
// Special handling for codeBlock target
|
|
288
|
+
if (targetNodeType.name === 'codeBlock') {
|
|
289
|
+
var _contentSplits = splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, tr.doc.type.schema);
|
|
290
|
+
return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, _contentSplits);
|
|
291
|
+
}
|
|
292
|
+
|
|
257
293
|
// Get content validation for target container type
|
|
258
294
|
var isContentSupported = (0, _utils.getContentSupportChecker)(targetNodeType);
|
|
259
295
|
|
|
260
296
|
// Process content and collect splits
|
|
261
297
|
var contentSplits = splitContentAroundUnsupportedBlocks(sourceNode, isContentSupported, targetNodeType, targetAttrs, tr.doc.type.schema);
|
|
298
|
+
return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, contentSplits);
|
|
299
|
+
};
|
|
262
300
|
|
|
263
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Apply content splits to transaction - shared utility for replacing and inserting splits
|
|
303
|
+
*/
|
|
304
|
+
var applySplitsToTransaction = function applySplitsToTransaction(tr, sourcePos, sourceNodeSize, contentSplits) {
|
|
264
305
|
var insertPos = sourcePos;
|
|
265
306
|
contentSplits.forEach(function (splitNode, index) {
|
|
266
307
|
if (index === 0) {
|
|
267
308
|
// Replace the original node with the first split
|
|
268
|
-
tr.replaceWith(sourcePos, sourcePos +
|
|
309
|
+
tr.replaceWith(sourcePos, sourcePos + sourceNodeSize, splitNode);
|
|
269
310
|
insertPos = sourcePos + splitNode.nodeSize;
|
|
270
311
|
} else {
|
|
271
312
|
// Insert additional splits after
|
|
@@ -276,18 +317,75 @@ var transformBetweenContainerTypes = exports.transformBetweenContainerTypes = fu
|
|
|
276
317
|
return tr;
|
|
277
318
|
};
|
|
278
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Split content for codeBlock transformation, creating codeBlocks for text content
|
|
322
|
+
* and preserving unsupported blocks (like tables) separately
|
|
323
|
+
*/
|
|
324
|
+
var splitContentForCodeBlock = function splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, schema) {
|
|
325
|
+
var _sourceNode$attrs3;
|
|
326
|
+
var splits = [];
|
|
327
|
+
var children = sourceNode.content.content;
|
|
328
|
+
var currentTextContent = [];
|
|
329
|
+
|
|
330
|
+
// Handle expand title - add as first text if source is expand with title
|
|
331
|
+
if (sourceNode.type.name === 'expand' && (_sourceNode$attrs3 = sourceNode.attrs) !== null && _sourceNode$attrs3 !== void 0 && _sourceNode$attrs3.title) {
|
|
332
|
+
currentTextContent.push(sourceNode.attrs.title);
|
|
333
|
+
}
|
|
334
|
+
var flushCurrentCodeBlock = function flushCurrentCodeBlock() {
|
|
335
|
+
if (currentTextContent.length > 0) {
|
|
336
|
+
var codeText = currentTextContent.join('\n');
|
|
337
|
+
var codeBlockNode = targetNodeType.create(targetAttrs, schema.text(codeText));
|
|
338
|
+
splits.push(codeBlockNode);
|
|
339
|
+
currentTextContent = [];
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
var isCodeBlockCompatible = function isCodeBlockCompatible(node) {
|
|
343
|
+
// Only text blocks (paragraph, heading) can be converted to codeBlock text
|
|
344
|
+
return node.isTextblock || node.type.name === 'codeBlock';
|
|
345
|
+
};
|
|
346
|
+
children.forEach(function (childNode) {
|
|
347
|
+
if (isCodeBlockCompatible(childNode)) {
|
|
348
|
+
// Extract text content from compatible nodes
|
|
349
|
+
if (childNode.type.name === 'codeBlock') {
|
|
350
|
+
// If it's already a codeBlock, extract its text
|
|
351
|
+
currentTextContent.push(childNode.textContent);
|
|
352
|
+
} else if (childNode.isTextblock) {
|
|
353
|
+
// Extract text from text blocks (paragraphs, headings, etc.)
|
|
354
|
+
var text = childNode.textContent;
|
|
355
|
+
if (text.trim()) {
|
|
356
|
+
currentTextContent.push(text);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
} else if ((0, _utils.isBlockNodeForExtraction)(childNode)) {
|
|
360
|
+
// Unsupported block node (table, etc.) - flush current codeBlock, add block, continue
|
|
361
|
+
flushCurrentCodeBlock();
|
|
362
|
+
splits.push(childNode);
|
|
363
|
+
} else {
|
|
364
|
+
// Other unsupported content - try to extract text if possible
|
|
365
|
+
var _text = childNode.textContent;
|
|
366
|
+
if (_text && _text.trim()) {
|
|
367
|
+
currentTextContent.push(_text);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Flush any remaining text content as a codeBlock
|
|
373
|
+
flushCurrentCodeBlock();
|
|
374
|
+
return splits;
|
|
375
|
+
};
|
|
376
|
+
|
|
279
377
|
/**
|
|
280
378
|
* Split content around unsupported block nodes, creating separate containers
|
|
281
379
|
* for content before and after each unsupported block
|
|
282
380
|
*/
|
|
283
381
|
var splitContentAroundUnsupportedBlocks = function splitContentAroundUnsupportedBlocks(sourceNode, isContentSupported, targetNodeType, targetAttrs, schema) {
|
|
284
|
-
var _sourceNode$
|
|
382
|
+
var _sourceNode$attrs4;
|
|
285
383
|
var splits = [];
|
|
286
384
|
var children = sourceNode.content.content;
|
|
287
385
|
var currentContainerContent = [];
|
|
288
386
|
|
|
289
387
|
// Handle expand title - add as first paragraph if source is expand with title
|
|
290
|
-
if (sourceNode.type.name === 'expand' && (_sourceNode$
|
|
388
|
+
if (sourceNode.type.name === 'expand' && (_sourceNode$attrs4 = sourceNode.attrs) !== null && _sourceNode$attrs4 !== void 0 && _sourceNode$attrs4.title) {
|
|
291
389
|
var titleParagraph = schema.nodes.paragraph.create({}, schema.text(sourceNode.attrs.title));
|
|
292
390
|
currentContainerContent.push(titleParagraph);
|
|
293
391
|
}
|
package/dist/cjs/ui/copy-link.js
CHANGED
|
@@ -11,7 +11,9 @@ var _reactIntlNext = require("react-intl-next");
|
|
|
11
11
|
var _messages = require("@atlaskit/editor-common/messages");
|
|
12
12
|
var _editorToolbar = require("@atlaskit/editor-toolbar");
|
|
13
13
|
var _link = _interopRequireDefault(require("@atlaskit/icon/core/link"));
|
|
14
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
14
15
|
var _copyLink = require("./utils/copyLink");
|
|
16
|
+
var _isNestedNode = require("./utils/isNestedNode");
|
|
15
17
|
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); }
|
|
16
18
|
var CopyLinkDropdownItemContent = function CopyLinkDropdownItemContent(_ref) {
|
|
17
19
|
var api = _ref.api,
|
|
@@ -32,6 +34,16 @@ var CopyLinkDropdownItemContent = function CopyLinkDropdownItemContent(_ref) {
|
|
|
32
34
|
api === null || api === void 0 || api.core.actions.focus();
|
|
33
35
|
return (0, _copyLink.copyLink)(config === null || config === void 0 ? void 0 : config.getLinkPath, config === null || config === void 0 ? void 0 : config.blockQueryParam, api);
|
|
34
36
|
}, [config === null || config === void 0 ? void 0 : config.getLinkPath, config === null || config === void 0 ? void 0 : config.blockQueryParam, api]);
|
|
37
|
+
var checkIsNestedNode = (0, _react.useCallback)(function () {
|
|
38
|
+
var _api$selection;
|
|
39
|
+
var selection = api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || (_api$selection = _api$selection.sharedState) === null || _api$selection === void 0 || (_api$selection = _api$selection.currentState()) === null || _api$selection === void 0 ? void 0 : _api$selection.selection;
|
|
40
|
+
return (0, _isNestedNode.isNestedNode)(selection);
|
|
41
|
+
}, [api]);
|
|
42
|
+
|
|
43
|
+
// Hide copy link when `platform_editor_adf_with_localid` feature flag is off or when the node is nested
|
|
44
|
+
if (!(0, _platformFeatureFlags.fg)('platform_editor_adf_with_localid') || checkIsNestedNode()) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
35
47
|
return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownItem, {
|
|
36
48
|
onClick: handleClick,
|
|
37
49
|
elemBefore: /*#__PURE__*/_react.default.createElement(_link.default, {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isNestedNode = void 0;
|
|
7
|
+
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
8
|
+
var _editorTables = require("@atlaskit/editor-tables");
|
|
9
|
+
/**
|
|
10
|
+
* Determines if a node is nested (not at top-level) based on its depth and context.
|
|
11
|
+
*
|
|
12
|
+
* Simple rules:
|
|
13
|
+
* - Depth 0-1: Always top-level (not nested)
|
|
14
|
+
* - Depth 2: Top-level for blockquotes and task lists
|
|
15
|
+
* - Depth 3: Top-level for list items only
|
|
16
|
+
* - Depth 4+: Always nested
|
|
17
|
+
*
|
|
18
|
+
* @param selection - The current ProseMirror selection
|
|
19
|
+
* @returns true if nested, false if top-level
|
|
20
|
+
*/
|
|
21
|
+
var isNestedNode = exports.isNestedNode = function isNestedNode(selection) {
|
|
22
|
+
if (!selection) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
var $from = selection.$from;
|
|
26
|
+
var depth = $from.depth;
|
|
27
|
+
if ($from.depth > 0 && selection instanceof _state.NodeSelection) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Depth 0-1: Always top-level
|
|
32
|
+
if (depth <= 1) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Depth 4+: Always nested
|
|
37
|
+
if (depth > 3) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Special case for table selection
|
|
42
|
+
if (selection instanceof _editorTables.CellSelection) {
|
|
43
|
+
return depth > 3;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Check parent node type for depth 2-3
|
|
47
|
+
var parentNode = $from.node(depth - 1);
|
|
48
|
+
if (!parentNode) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
var parentType = parentNode.type.name;
|
|
52
|
+
|
|
53
|
+
// Special cases where content is still top-level
|
|
54
|
+
if (parentType === 'listItem' && depth === 3 || parentType === 'blockquote' && depth === 2 || parentType === 'taskList' && depth === 2) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Everything else at depth 2-3 is nested
|
|
59
|
+
return true;
|
|
60
|
+
};
|
|
@@ -12,6 +12,19 @@ const convertInvalidNodeToValidNodeType = (sourceContent, sourceNodeType, validN
|
|
|
12
12
|
});
|
|
13
13
|
return Fragment.from(validTransformedContent);
|
|
14
14
|
};
|
|
15
|
+
const filterMarksForTargetNodeType = (content, targetNodeType) => {
|
|
16
|
+
const withValidMarks = [];
|
|
17
|
+
content.forEach(node => {
|
|
18
|
+
if (node.marks.length > 0) {
|
|
19
|
+
const allowedMarks = targetNodeType.allowedMarks(node.marks);
|
|
20
|
+
const updatedNode = node.mark(allowedMarks);
|
|
21
|
+
withValidMarks.push(updatedNode);
|
|
22
|
+
} else {
|
|
23
|
+
withValidMarks.push(node);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return Fragment.from(withValidMarks);
|
|
27
|
+
};
|
|
15
28
|
|
|
16
29
|
/**
|
|
17
30
|
* Transform selection to container type
|
|
@@ -32,6 +45,13 @@ export const transformToContainer = ({
|
|
|
32
45
|
if (targetNodeType === schema.nodes.blockquote) {
|
|
33
46
|
transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.heading, schema.nodes.paragraph, true);
|
|
34
47
|
}
|
|
48
|
+
|
|
49
|
+
// Preserve marks that are allowed in the target node type
|
|
50
|
+
// e.g. blocks (heading/ paragraph) with alignment need to remove alignment
|
|
51
|
+
// as panel/ blockQuote/ expands does not support alignment
|
|
52
|
+
if (sourceNode.type === schema.nodes.paragraph || sourceNode.type === schema.nodes.heading) {
|
|
53
|
+
transformedContent = filterMarksForTargetNodeType(transformedContent, targetNodeType);
|
|
54
|
+
}
|
|
35
55
|
const newNode = targetNodeType.createAndFill(targetAttrs, transformedContent);
|
|
36
56
|
if (!newNode) {
|
|
37
57
|
return null;
|
|
@@ -56,6 +76,16 @@ export const transformContainerNode = ({
|
|
|
56
76
|
|
|
57
77
|
// Transform container to block type - unwrap and convert content
|
|
58
78
|
if (isBlockNodeType(targetNodeType)) {
|
|
79
|
+
// special case container to codeblock
|
|
80
|
+
if (targetNodeType.name === 'codeBlock') {
|
|
81
|
+
return transformBetweenContainerTypes({
|
|
82
|
+
tr,
|
|
83
|
+
sourceNode,
|
|
84
|
+
sourcePos,
|
|
85
|
+
targetNodeType,
|
|
86
|
+
targetAttrs
|
|
87
|
+
});
|
|
88
|
+
}
|
|
59
89
|
return unwrapAndConvertToBlockType({
|
|
60
90
|
tr,
|
|
61
91
|
sourceNode,
|
|
@@ -253,18 +283,29 @@ export const transformBetweenContainerTypes = context => {
|
|
|
253
283
|
targetAttrs
|
|
254
284
|
} = context;
|
|
255
285
|
|
|
286
|
+
// Special handling for codeBlock target
|
|
287
|
+
if (targetNodeType.name === 'codeBlock') {
|
|
288
|
+
const contentSplits = splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, tr.doc.type.schema);
|
|
289
|
+
return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, contentSplits);
|
|
290
|
+
}
|
|
291
|
+
|
|
256
292
|
// Get content validation for target container type
|
|
257
293
|
const isContentSupported = getContentSupportChecker(targetNodeType);
|
|
258
294
|
|
|
259
295
|
// Process content and collect splits
|
|
260
296
|
const contentSplits = splitContentAroundUnsupportedBlocks(sourceNode, isContentSupported, targetNodeType, targetAttrs, tr.doc.type.schema);
|
|
297
|
+
return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, contentSplits);
|
|
298
|
+
};
|
|
261
299
|
|
|
262
|
-
|
|
300
|
+
/**
|
|
301
|
+
* Apply content splits to transaction - shared utility for replacing and inserting splits
|
|
302
|
+
*/
|
|
303
|
+
const applySplitsToTransaction = (tr, sourcePos, sourceNodeSize, contentSplits) => {
|
|
263
304
|
let insertPos = sourcePos;
|
|
264
305
|
contentSplits.forEach((splitNode, index) => {
|
|
265
306
|
if (index === 0) {
|
|
266
307
|
// Replace the original node with the first split
|
|
267
|
-
tr.replaceWith(sourcePos, sourcePos +
|
|
308
|
+
tr.replaceWith(sourcePos, sourcePos + sourceNodeSize, splitNode);
|
|
268
309
|
insertPos = sourcePos + splitNode.nodeSize;
|
|
269
310
|
} else {
|
|
270
311
|
// Insert additional splits after
|
|
@@ -275,18 +316,75 @@ export const transformBetweenContainerTypes = context => {
|
|
|
275
316
|
return tr;
|
|
276
317
|
};
|
|
277
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Split content for codeBlock transformation, creating codeBlocks for text content
|
|
321
|
+
* and preserving unsupported blocks (like tables) separately
|
|
322
|
+
*/
|
|
323
|
+
const splitContentForCodeBlock = (sourceNode, targetNodeType, targetAttrs, schema) => {
|
|
324
|
+
var _sourceNode$attrs3;
|
|
325
|
+
const splits = [];
|
|
326
|
+
const children = sourceNode.content.content;
|
|
327
|
+
let currentTextContent = [];
|
|
328
|
+
|
|
329
|
+
// Handle expand title - add as first text if source is expand with title
|
|
330
|
+
if (sourceNode.type.name === 'expand' && (_sourceNode$attrs3 = sourceNode.attrs) !== null && _sourceNode$attrs3 !== void 0 && _sourceNode$attrs3.title) {
|
|
331
|
+
currentTextContent.push(sourceNode.attrs.title);
|
|
332
|
+
}
|
|
333
|
+
const flushCurrentCodeBlock = () => {
|
|
334
|
+
if (currentTextContent.length > 0) {
|
|
335
|
+
const codeText = currentTextContent.join('\n');
|
|
336
|
+
const codeBlockNode = targetNodeType.create(targetAttrs, schema.text(codeText));
|
|
337
|
+
splits.push(codeBlockNode);
|
|
338
|
+
currentTextContent = [];
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
const isCodeBlockCompatible = node => {
|
|
342
|
+
// Only text blocks (paragraph, heading) can be converted to codeBlock text
|
|
343
|
+
return node.isTextblock || node.type.name === 'codeBlock';
|
|
344
|
+
};
|
|
345
|
+
children.forEach(childNode => {
|
|
346
|
+
if (isCodeBlockCompatible(childNode)) {
|
|
347
|
+
// Extract text content from compatible nodes
|
|
348
|
+
if (childNode.type.name === 'codeBlock') {
|
|
349
|
+
// If it's already a codeBlock, extract its text
|
|
350
|
+
currentTextContent.push(childNode.textContent);
|
|
351
|
+
} else if (childNode.isTextblock) {
|
|
352
|
+
// Extract text from text blocks (paragraphs, headings, etc.)
|
|
353
|
+
const text = childNode.textContent;
|
|
354
|
+
if (text.trim()) {
|
|
355
|
+
currentTextContent.push(text);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
} else if (isBlockNodeForExtraction(childNode)) {
|
|
359
|
+
// Unsupported block node (table, etc.) - flush current codeBlock, add block, continue
|
|
360
|
+
flushCurrentCodeBlock();
|
|
361
|
+
splits.push(childNode);
|
|
362
|
+
} else {
|
|
363
|
+
// Other unsupported content - try to extract text if possible
|
|
364
|
+
const text = childNode.textContent;
|
|
365
|
+
if (text && text.trim()) {
|
|
366
|
+
currentTextContent.push(text);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Flush any remaining text content as a codeBlock
|
|
372
|
+
flushCurrentCodeBlock();
|
|
373
|
+
return splits;
|
|
374
|
+
};
|
|
375
|
+
|
|
278
376
|
/**
|
|
279
377
|
* Split content around unsupported block nodes, creating separate containers
|
|
280
378
|
* for content before and after each unsupported block
|
|
281
379
|
*/
|
|
282
380
|
const splitContentAroundUnsupportedBlocks = (sourceNode, isContentSupported, targetNodeType, targetAttrs, schema) => {
|
|
283
|
-
var _sourceNode$
|
|
381
|
+
var _sourceNode$attrs4;
|
|
284
382
|
const splits = [];
|
|
285
383
|
const children = sourceNode.content.content;
|
|
286
384
|
let currentContainerContent = [];
|
|
287
385
|
|
|
288
386
|
// Handle expand title - add as first paragraph if source is expand with title
|
|
289
|
-
if (sourceNode.type.name === 'expand' && (_sourceNode$
|
|
387
|
+
if (sourceNode.type.name === 'expand' && (_sourceNode$attrs4 = sourceNode.attrs) !== null && _sourceNode$attrs4 !== void 0 && _sourceNode$attrs4.title) {
|
|
290
388
|
const titleParagraph = schema.nodes.paragraph.create({}, schema.text(sourceNode.attrs.title));
|
|
291
389
|
currentContainerContent.push(titleParagraph);
|
|
292
390
|
}
|
|
@@ -3,7 +3,9 @@ import { useIntl, injectIntl } from 'react-intl-next';
|
|
|
3
3
|
import { blockMenuMessages as messages } from '@atlaskit/editor-common/messages';
|
|
4
4
|
import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
|
|
5
5
|
import LinkIcon from '@atlaskit/icon/core/link';
|
|
6
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
6
7
|
import { copyLink } from './utils/copyLink';
|
|
8
|
+
import { isNestedNode } from './utils/isNestedNode';
|
|
7
9
|
const CopyLinkDropdownItemContent = ({
|
|
8
10
|
api,
|
|
9
11
|
config
|
|
@@ -26,6 +28,16 @@ const CopyLinkDropdownItemContent = ({
|
|
|
26
28
|
api === null || api === void 0 ? void 0 : api.core.actions.focus();
|
|
27
29
|
return copyLink(config === null || config === void 0 ? void 0 : config.getLinkPath, config === null || config === void 0 ? void 0 : config.blockQueryParam, api);
|
|
28
30
|
}, [config === null || config === void 0 ? void 0 : config.getLinkPath, config === null || config === void 0 ? void 0 : config.blockQueryParam, api]);
|
|
31
|
+
const checkIsNestedNode = useCallback(() => {
|
|
32
|
+
var _api$selection, _api$selection$shared, _api$selection$shared2;
|
|
33
|
+
const selection = api === null || api === void 0 ? void 0 : (_api$selection = api.selection) === null || _api$selection === void 0 ? void 0 : (_api$selection$shared = _api$selection.sharedState) === null || _api$selection$shared === void 0 ? void 0 : (_api$selection$shared2 = _api$selection$shared.currentState()) === null || _api$selection$shared2 === void 0 ? void 0 : _api$selection$shared2.selection;
|
|
34
|
+
return isNestedNode(selection);
|
|
35
|
+
}, [api]);
|
|
36
|
+
|
|
37
|
+
// Hide copy link when `platform_editor_adf_with_localid` feature flag is off or when the node is nested
|
|
38
|
+
if (!fg('platform_editor_adf_with_localid') || checkIsNestedNode()) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
29
41
|
return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
|
|
30
42
|
onClick: handleClick,
|
|
31
43
|
elemBefore: /*#__PURE__*/React.createElement(LinkIcon, {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
|
|
2
|
+
import { CellSelection } from '@atlaskit/editor-tables';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Determines if a node is nested (not at top-level) based on its depth and context.
|
|
6
|
+
*
|
|
7
|
+
* Simple rules:
|
|
8
|
+
* - Depth 0-1: Always top-level (not nested)
|
|
9
|
+
* - Depth 2: Top-level for blockquotes and task lists
|
|
10
|
+
* - Depth 3: Top-level for list items only
|
|
11
|
+
* - Depth 4+: Always nested
|
|
12
|
+
*
|
|
13
|
+
* @param selection - The current ProseMirror selection
|
|
14
|
+
* @returns true if nested, false if top-level
|
|
15
|
+
*/
|
|
16
|
+
export const isNestedNode = selection => {
|
|
17
|
+
if (!selection) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const {
|
|
21
|
+
$from
|
|
22
|
+
} = selection;
|
|
23
|
+
const depth = $from.depth;
|
|
24
|
+
if ($from.depth > 0 && selection instanceof NodeSelection) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Depth 0-1: Always top-level
|
|
29
|
+
if (depth <= 1) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Depth 4+: Always nested
|
|
34
|
+
if (depth > 3) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Special case for table selection
|
|
39
|
+
if (selection instanceof CellSelection) {
|
|
40
|
+
return depth > 3;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check parent node type for depth 2-3
|
|
44
|
+
const parentNode = $from.node(depth - 1);
|
|
45
|
+
if (!parentNode) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
const parentType = parentNode.type.name;
|
|
49
|
+
|
|
50
|
+
// Special cases where content is still top-level
|
|
51
|
+
if (parentType === 'listItem' && depth === 3 || parentType === 'blockquote' && depth === 2 || parentType === 'taskList' && depth === 2) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Everything else at depth 2-3 is nested
|
|
56
|
+
return true;
|
|
57
|
+
};
|
|
@@ -13,6 +13,19 @@ var convertInvalidNodeToValidNodeType = function convertInvalidNodeToValidNodeTy
|
|
|
13
13
|
});
|
|
14
14
|
return Fragment.from(validTransformedContent);
|
|
15
15
|
};
|
|
16
|
+
var filterMarksForTargetNodeType = function filterMarksForTargetNodeType(content, targetNodeType) {
|
|
17
|
+
var withValidMarks = [];
|
|
18
|
+
content.forEach(function (node) {
|
|
19
|
+
if (node.marks.length > 0) {
|
|
20
|
+
var allowedMarks = targetNodeType.allowedMarks(node.marks);
|
|
21
|
+
var updatedNode = node.mark(allowedMarks);
|
|
22
|
+
withValidMarks.push(updatedNode);
|
|
23
|
+
} else {
|
|
24
|
+
withValidMarks.push(node);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return Fragment.from(withValidMarks);
|
|
28
|
+
};
|
|
16
29
|
|
|
17
30
|
/**
|
|
18
31
|
* Transform selection to container type
|
|
@@ -32,6 +45,13 @@ export var transformToContainer = function transformToContainer(_ref) {
|
|
|
32
45
|
if (targetNodeType === schema.nodes.blockquote) {
|
|
33
46
|
transformedContent = convertInvalidNodeToValidNodeType(transformedContent, schema.nodes.heading, schema.nodes.paragraph, true);
|
|
34
47
|
}
|
|
48
|
+
|
|
49
|
+
// Preserve marks that are allowed in the target node type
|
|
50
|
+
// e.g. blocks (heading/ paragraph) with alignment need to remove alignment
|
|
51
|
+
// as panel/ blockQuote/ expands does not support alignment
|
|
52
|
+
if (sourceNode.type === schema.nodes.paragraph || sourceNode.type === schema.nodes.heading) {
|
|
53
|
+
transformedContent = filterMarksForTargetNodeType(transformedContent, targetNodeType);
|
|
54
|
+
}
|
|
35
55
|
var newNode = targetNodeType.createAndFill(targetAttrs, transformedContent);
|
|
36
56
|
if (!newNode) {
|
|
37
57
|
return null;
|
|
@@ -55,6 +75,16 @@ export var transformContainerNode = function transformContainerNode(_ref2) {
|
|
|
55
75
|
|
|
56
76
|
// Transform container to block type - unwrap and convert content
|
|
57
77
|
if (isBlockNodeType(targetNodeType)) {
|
|
78
|
+
// special case container to codeblock
|
|
79
|
+
if (targetNodeType.name === 'codeBlock') {
|
|
80
|
+
return transformBetweenContainerTypes({
|
|
81
|
+
tr: tr,
|
|
82
|
+
sourceNode: sourceNode,
|
|
83
|
+
sourcePos: sourcePos,
|
|
84
|
+
targetNodeType: targetNodeType,
|
|
85
|
+
targetAttrs: targetAttrs
|
|
86
|
+
});
|
|
87
|
+
}
|
|
58
88
|
return unwrapAndConvertToBlockType({
|
|
59
89
|
tr: tr,
|
|
60
90
|
sourceNode: sourceNode,
|
|
@@ -247,18 +277,29 @@ export var transformBetweenContainerTypes = function transformBetweenContainerTy
|
|
|
247
277
|
targetNodeType = context.targetNodeType,
|
|
248
278
|
targetAttrs = context.targetAttrs;
|
|
249
279
|
|
|
280
|
+
// Special handling for codeBlock target
|
|
281
|
+
if (targetNodeType.name === 'codeBlock') {
|
|
282
|
+
var _contentSplits = splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, tr.doc.type.schema);
|
|
283
|
+
return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, _contentSplits);
|
|
284
|
+
}
|
|
285
|
+
|
|
250
286
|
// Get content validation for target container type
|
|
251
287
|
var isContentSupported = getContentSupportChecker(targetNodeType);
|
|
252
288
|
|
|
253
289
|
// Process content and collect splits
|
|
254
290
|
var contentSplits = splitContentAroundUnsupportedBlocks(sourceNode, isContentSupported, targetNodeType, targetAttrs, tr.doc.type.schema);
|
|
291
|
+
return applySplitsToTransaction(tr, sourcePos, sourceNode.nodeSize, contentSplits);
|
|
292
|
+
};
|
|
255
293
|
|
|
256
|
-
|
|
294
|
+
/**
|
|
295
|
+
* Apply content splits to transaction - shared utility for replacing and inserting splits
|
|
296
|
+
*/
|
|
297
|
+
var applySplitsToTransaction = function applySplitsToTransaction(tr, sourcePos, sourceNodeSize, contentSplits) {
|
|
257
298
|
var insertPos = sourcePos;
|
|
258
299
|
contentSplits.forEach(function (splitNode, index) {
|
|
259
300
|
if (index === 0) {
|
|
260
301
|
// Replace the original node with the first split
|
|
261
|
-
tr.replaceWith(sourcePos, sourcePos +
|
|
302
|
+
tr.replaceWith(sourcePos, sourcePos + sourceNodeSize, splitNode);
|
|
262
303
|
insertPos = sourcePos + splitNode.nodeSize;
|
|
263
304
|
} else {
|
|
264
305
|
// Insert additional splits after
|
|
@@ -269,18 +310,75 @@ export var transformBetweenContainerTypes = function transformBetweenContainerTy
|
|
|
269
310
|
return tr;
|
|
270
311
|
};
|
|
271
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Split content for codeBlock transformation, creating codeBlocks for text content
|
|
315
|
+
* and preserving unsupported blocks (like tables) separately
|
|
316
|
+
*/
|
|
317
|
+
var splitContentForCodeBlock = function splitContentForCodeBlock(sourceNode, targetNodeType, targetAttrs, schema) {
|
|
318
|
+
var _sourceNode$attrs3;
|
|
319
|
+
var splits = [];
|
|
320
|
+
var children = sourceNode.content.content;
|
|
321
|
+
var currentTextContent = [];
|
|
322
|
+
|
|
323
|
+
// Handle expand title - add as first text if source is expand with title
|
|
324
|
+
if (sourceNode.type.name === 'expand' && (_sourceNode$attrs3 = sourceNode.attrs) !== null && _sourceNode$attrs3 !== void 0 && _sourceNode$attrs3.title) {
|
|
325
|
+
currentTextContent.push(sourceNode.attrs.title);
|
|
326
|
+
}
|
|
327
|
+
var flushCurrentCodeBlock = function flushCurrentCodeBlock() {
|
|
328
|
+
if (currentTextContent.length > 0) {
|
|
329
|
+
var codeText = currentTextContent.join('\n');
|
|
330
|
+
var codeBlockNode = targetNodeType.create(targetAttrs, schema.text(codeText));
|
|
331
|
+
splits.push(codeBlockNode);
|
|
332
|
+
currentTextContent = [];
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
var isCodeBlockCompatible = function isCodeBlockCompatible(node) {
|
|
336
|
+
// Only text blocks (paragraph, heading) can be converted to codeBlock text
|
|
337
|
+
return node.isTextblock || node.type.name === 'codeBlock';
|
|
338
|
+
};
|
|
339
|
+
children.forEach(function (childNode) {
|
|
340
|
+
if (isCodeBlockCompatible(childNode)) {
|
|
341
|
+
// Extract text content from compatible nodes
|
|
342
|
+
if (childNode.type.name === 'codeBlock') {
|
|
343
|
+
// If it's already a codeBlock, extract its text
|
|
344
|
+
currentTextContent.push(childNode.textContent);
|
|
345
|
+
} else if (childNode.isTextblock) {
|
|
346
|
+
// Extract text from text blocks (paragraphs, headings, etc.)
|
|
347
|
+
var text = childNode.textContent;
|
|
348
|
+
if (text.trim()) {
|
|
349
|
+
currentTextContent.push(text);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
} else if (isBlockNodeForExtraction(childNode)) {
|
|
353
|
+
// Unsupported block node (table, etc.) - flush current codeBlock, add block, continue
|
|
354
|
+
flushCurrentCodeBlock();
|
|
355
|
+
splits.push(childNode);
|
|
356
|
+
} else {
|
|
357
|
+
// Other unsupported content - try to extract text if possible
|
|
358
|
+
var _text = childNode.textContent;
|
|
359
|
+
if (_text && _text.trim()) {
|
|
360
|
+
currentTextContent.push(_text);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Flush any remaining text content as a codeBlock
|
|
366
|
+
flushCurrentCodeBlock();
|
|
367
|
+
return splits;
|
|
368
|
+
};
|
|
369
|
+
|
|
272
370
|
/**
|
|
273
371
|
* Split content around unsupported block nodes, creating separate containers
|
|
274
372
|
* for content before and after each unsupported block
|
|
275
373
|
*/
|
|
276
374
|
var splitContentAroundUnsupportedBlocks = function splitContentAroundUnsupportedBlocks(sourceNode, isContentSupported, targetNodeType, targetAttrs, schema) {
|
|
277
|
-
var _sourceNode$
|
|
375
|
+
var _sourceNode$attrs4;
|
|
278
376
|
var splits = [];
|
|
279
377
|
var children = sourceNode.content.content;
|
|
280
378
|
var currentContainerContent = [];
|
|
281
379
|
|
|
282
380
|
// Handle expand title - add as first paragraph if source is expand with title
|
|
283
|
-
if (sourceNode.type.name === 'expand' && (_sourceNode$
|
|
381
|
+
if (sourceNode.type.name === 'expand' && (_sourceNode$attrs4 = sourceNode.attrs) !== null && _sourceNode$attrs4 !== void 0 && _sourceNode$attrs4.title) {
|
|
284
382
|
var titleParagraph = schema.nodes.paragraph.create({}, schema.text(sourceNode.attrs.title));
|
|
285
383
|
currentContainerContent.push(titleParagraph);
|
|
286
384
|
}
|
package/dist/esm/ui/copy-link.js
CHANGED
|
@@ -3,7 +3,9 @@ import { useIntl, injectIntl } from 'react-intl-next';
|
|
|
3
3
|
import { blockMenuMessages as messages } from '@atlaskit/editor-common/messages';
|
|
4
4
|
import { ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
|
|
5
5
|
import LinkIcon from '@atlaskit/icon/core/link';
|
|
6
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
6
7
|
import { copyLink } from './utils/copyLink';
|
|
8
|
+
import { isNestedNode } from './utils/isNestedNode';
|
|
7
9
|
var CopyLinkDropdownItemContent = function CopyLinkDropdownItemContent(_ref) {
|
|
8
10
|
var api = _ref.api,
|
|
9
11
|
config = _ref.config;
|
|
@@ -23,6 +25,16 @@ var CopyLinkDropdownItemContent = function CopyLinkDropdownItemContent(_ref) {
|
|
|
23
25
|
api === null || api === void 0 || api.core.actions.focus();
|
|
24
26
|
return copyLink(config === null || config === void 0 ? void 0 : config.getLinkPath, config === null || config === void 0 ? void 0 : config.blockQueryParam, api);
|
|
25
27
|
}, [config === null || config === void 0 ? void 0 : config.getLinkPath, config === null || config === void 0 ? void 0 : config.blockQueryParam, api]);
|
|
28
|
+
var checkIsNestedNode = useCallback(function () {
|
|
29
|
+
var _api$selection;
|
|
30
|
+
var selection = api === null || api === void 0 || (_api$selection = api.selection) === null || _api$selection === void 0 || (_api$selection = _api$selection.sharedState) === null || _api$selection === void 0 || (_api$selection = _api$selection.currentState()) === null || _api$selection === void 0 ? void 0 : _api$selection.selection;
|
|
31
|
+
return isNestedNode(selection);
|
|
32
|
+
}, [api]);
|
|
33
|
+
|
|
34
|
+
// Hide copy link when `platform_editor_adf_with_localid` feature flag is off or when the node is nested
|
|
35
|
+
if (!fg('platform_editor_adf_with_localid') || checkIsNestedNode()) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
26
38
|
return /*#__PURE__*/React.createElement(ToolbarDropdownItem, {
|
|
27
39
|
onClick: handleClick,
|
|
28
40
|
elemBefore: /*#__PURE__*/React.createElement(LinkIcon, {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
|
|
2
|
+
import { CellSelection } from '@atlaskit/editor-tables';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Determines if a node is nested (not at top-level) based on its depth and context.
|
|
6
|
+
*
|
|
7
|
+
* Simple rules:
|
|
8
|
+
* - Depth 0-1: Always top-level (not nested)
|
|
9
|
+
* - Depth 2: Top-level for blockquotes and task lists
|
|
10
|
+
* - Depth 3: Top-level for list items only
|
|
11
|
+
* - Depth 4+: Always nested
|
|
12
|
+
*
|
|
13
|
+
* @param selection - The current ProseMirror selection
|
|
14
|
+
* @returns true if nested, false if top-level
|
|
15
|
+
*/
|
|
16
|
+
export var isNestedNode = function isNestedNode(selection) {
|
|
17
|
+
if (!selection) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
var $from = selection.$from;
|
|
21
|
+
var depth = $from.depth;
|
|
22
|
+
if ($from.depth > 0 && selection instanceof NodeSelection) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Depth 0-1: Always top-level
|
|
27
|
+
if (depth <= 1) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Depth 4+: Always nested
|
|
32
|
+
if (depth > 3) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Special case for table selection
|
|
37
|
+
if (selection instanceof CellSelection) {
|
|
38
|
+
return depth > 3;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check parent node type for depth 2-3
|
|
42
|
+
var parentNode = $from.node(depth - 1);
|
|
43
|
+
if (!parentNode) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
var parentType = parentNode.type.name;
|
|
47
|
+
|
|
48
|
+
// Special cases where content is still top-level
|
|
49
|
+
if (parentType === 'listItem' && depth === 3 || parentType === 'blockquote' && depth === 2 || parentType === 'taskList' && depth === 2) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Everything else at depth 2-3 is nested
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Selection } from '@atlaskit/editor-prosemirror/state';
|
|
2
|
+
/**
|
|
3
|
+
* Determines if a node is nested (not at top-level) based on its depth and context.
|
|
4
|
+
*
|
|
5
|
+
* Simple rules:
|
|
6
|
+
* - Depth 0-1: Always top-level (not nested)
|
|
7
|
+
* - Depth 2: Top-level for blockquotes and task lists
|
|
8
|
+
* - Depth 3: Top-level for list items only
|
|
9
|
+
* - Depth 4+: Always nested
|
|
10
|
+
*
|
|
11
|
+
* @param selection - The current ProseMirror selection
|
|
12
|
+
* @returns true if nested, false if top-level
|
|
13
|
+
*/
|
|
14
|
+
export declare const isNestedNode: (selection: Selection | undefined) => boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Selection } from '@atlaskit/editor-prosemirror/state';
|
|
2
|
+
/**
|
|
3
|
+
* Determines if a node is nested (not at top-level) based on its depth and context.
|
|
4
|
+
*
|
|
5
|
+
* Simple rules:
|
|
6
|
+
* - Depth 0-1: Always top-level (not nested)
|
|
7
|
+
* - Depth 2: Top-level for blockquotes and task lists
|
|
8
|
+
* - Depth 3: Top-level for list items only
|
|
9
|
+
* - Depth 4+: Always nested
|
|
10
|
+
*
|
|
11
|
+
* @param selection - The current ProseMirror selection
|
|
12
|
+
* @returns true if nested, false if top-level
|
|
13
|
+
*/
|
|
14
|
+
export declare const isNestedNode: (selection: Selection | undefined) => boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-block-menu",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "BlockMenu plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@babel/runtime": "^7.0.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@atlaskit/editor-common": "^108.
|
|
49
|
+
"@atlaskit/editor-common": "^108.6.0",
|
|
50
50
|
"react": "^18.2.0",
|
|
51
51
|
"react-intl-next": "npm:react-intl@^5.18.1"
|
|
52
52
|
},
|
|
@@ -85,5 +85,10 @@
|
|
|
85
85
|
"import-no-extraneous-disable-for-examples-and-docs"
|
|
86
86
|
]
|
|
87
87
|
}
|
|
88
|
+
},
|
|
89
|
+
"platform-feature-flags": {
|
|
90
|
+
"platform_editor_adf_with_localid": {
|
|
91
|
+
"type": "boolean"
|
|
92
|
+
}
|
|
88
93
|
}
|
|
89
94
|
}
|