@atlaskit/editor-plugin-block-menu 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @atlaskit/editor-plugin-block-menu
2
2
 
3
+ ## 1.0.10
4
+
5
+ ### Patch Changes
6
+
7
+ - [`74c42a764926a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/74c42a764926a) -
8
+ Hide copy link option when platform_editor_adf_with_localid FG is off or when selection is a
9
+ nested node
10
+ - [`614ef1a575e84`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/614ef1a575e84) -
11
+ [ux] ED-29183: Fixed p and headings with alignment not able to convert to panel, expand, block
12
+ quote
13
+
3
14
  ## 1.0.9
4
15
 
5
16
  ### 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;
@@ -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,54 @@
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
+ /**
9
+ * Determines if a node is nested (not at top-level) based on its depth and context.
10
+ *
11
+ * Simple rules:
12
+ * - Depth 0-1: Always top-level (not nested)
13
+ * - Depth 2: Top-level for blockquotes and task lists
14
+ * - Depth 3: Top-level for list items only
15
+ * - Depth 4+: Always nested
16
+ *
17
+ * @param selection - The current ProseMirror selection
18
+ * @returns true if nested, false if top-level
19
+ */
20
+ var isNestedNode = exports.isNestedNode = function isNestedNode(selection) {
21
+ if (!selection) {
22
+ return false;
23
+ }
24
+ var $from = selection.$from;
25
+ var depth = $from.depth;
26
+ if ($from.depth > 0 && selection instanceof _state.NodeSelection) {
27
+ return true;
28
+ }
29
+
30
+ // Depth 0-1: Always top-level
31
+ if (depth <= 1) {
32
+ return false;
33
+ }
34
+
35
+ // Depth 4+: Always nested
36
+ if (depth > 3) {
37
+ return true;
38
+ }
39
+
40
+ // Check parent node type for depth 2-3
41
+ var parentNode = $from.node(depth - 1);
42
+ if (!parentNode) {
43
+ return true;
44
+ }
45
+ var parentType = parentNode.type.name;
46
+
47
+ // Special cases where content is still top-level
48
+ if (parentType === 'listItem' && depth === 3 || parentType === 'blockquote' && depth === 2 || parentType === 'taskList' && depth === 2) {
49
+ return false;
50
+ }
51
+
52
+ // Everything else at depth 2-3 is nested
53
+ return true;
54
+ };
@@ -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;
@@ -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,51 @@
1
+ import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
2
+
3
+ /**
4
+ * Determines if a node is nested (not at top-level) based on its depth and context.
5
+ *
6
+ * Simple rules:
7
+ * - Depth 0-1: Always top-level (not nested)
8
+ * - Depth 2: Top-level for blockquotes and task lists
9
+ * - Depth 3: Top-level for list items only
10
+ * - Depth 4+: Always nested
11
+ *
12
+ * @param selection - The current ProseMirror selection
13
+ * @returns true if nested, false if top-level
14
+ */
15
+ export const isNestedNode = selection => {
16
+ if (!selection) {
17
+ return false;
18
+ }
19
+ const {
20
+ $from
21
+ } = selection;
22
+ const depth = $from.depth;
23
+ if ($from.depth > 0 && selection instanceof NodeSelection) {
24
+ return true;
25
+ }
26
+
27
+ // Depth 0-1: Always top-level
28
+ if (depth <= 1) {
29
+ return false;
30
+ }
31
+
32
+ // Depth 4+: Always nested
33
+ if (depth > 3) {
34
+ return true;
35
+ }
36
+
37
+ // Check parent node type for depth 2-3
38
+ const parentNode = $from.node(depth - 1);
39
+ if (!parentNode) {
40
+ return true;
41
+ }
42
+ const parentType = parentNode.type.name;
43
+
44
+ // Special cases where content is still top-level
45
+ if (parentType === 'listItem' && depth === 3 || parentType === 'blockquote' && depth === 2 || parentType === 'taskList' && depth === 2) {
46
+ return false;
47
+ }
48
+
49
+ // Everything else at depth 2-3 is nested
50
+ return true;
51
+ };
@@ -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;
@@ -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,49 @@
1
+ import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
2
+
3
+ /**
4
+ * Determines if a node is nested (not at top-level) based on its depth and context.
5
+ *
6
+ * Simple rules:
7
+ * - Depth 0-1: Always top-level (not nested)
8
+ * - Depth 2: Top-level for blockquotes and task lists
9
+ * - Depth 3: Top-level for list items only
10
+ * - Depth 4+: Always nested
11
+ *
12
+ * @param selection - The current ProseMirror selection
13
+ * @returns true if nested, false if top-level
14
+ */
15
+ export var isNestedNode = function isNestedNode(selection) {
16
+ if (!selection) {
17
+ return false;
18
+ }
19
+ var $from = selection.$from;
20
+ var depth = $from.depth;
21
+ if ($from.depth > 0 && selection instanceof NodeSelection) {
22
+ return true;
23
+ }
24
+
25
+ // Depth 0-1: Always top-level
26
+ if (depth <= 1) {
27
+ return false;
28
+ }
29
+
30
+ // Depth 4+: Always nested
31
+ if (depth > 3) {
32
+ return true;
33
+ }
34
+
35
+ // Check parent node type for depth 2-3
36
+ var parentNode = $from.node(depth - 1);
37
+ if (!parentNode) {
38
+ return true;
39
+ }
40
+ var parentType = parentNode.type.name;
41
+
42
+ // Special cases where content is still top-level
43
+ if (parentType === 'listItem' && depth === 3 || parentType === 'blockquote' && depth === 2 || parentType === 'taskList' && depth === 2) {
44
+ return false;
45
+ }
46
+
47
+ // Everything else at depth 2-3 is nested
48
+ return true;
49
+ };
@@ -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.9",
3
+ "version": "1.0.10",
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.5.0",
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
  }