@atlaskit/editor-plugin-block-menu 4.0.1 → 4.0.3
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 +17 -0
- package/afm-cc/tsconfig.json +3 -0
- package/afm-dev-agents/tsconfig.json +3 -0
- package/afm-jira/tsconfig.json +3 -0
- package/afm-passionfruit/tsconfig.json +3 -0
- package/afm-post-office/tsconfig.json +3 -0
- package/afm-rovo-extension/tsconfig.json +3 -0
- package/afm-townsquare/tsconfig.json +3 -0
- package/dist/cjs/blockMenuPlugin.js +1 -1
- package/dist/cjs/editor-commands/formatNode.js +128 -49
- package/dist/es2019/blockMenuPlugin.js +1 -1
- package/dist/es2019/editor-commands/formatNode.js +82 -2
- package/dist/esm/blockMenuPlugin.js +1 -1
- package/dist/esm/editor-commands/formatNode.js +128 -48
- package/dist/types/editor-commands/formatNode.d.ts +14 -2
- package/dist/types-ts4.5/editor-commands/formatNode.d.ts +14 -2
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-block-menu
|
|
2
2
|
|
|
3
|
+
## 4.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`c158b1ba4f0fd`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c158b1ba4f0fd) -
|
|
8
|
+
ED-29388: fix converting empty list
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
|
|
11
|
+
## 4.0.2
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [`9cf29da7572b3`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9cf29da7572b3) -
|
|
16
|
+
[ux] Fires Element converted event when Turn into menu is used to transform a node from one node
|
|
17
|
+
type to another.
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
|
|
3
20
|
## 4.0.1
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/afm-cc/tsconfig.json
CHANGED
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
{
|
|
24
24
|
"path": "../../../design-system/dropdown-menu/afm-dev-agents/tsconfig.json"
|
|
25
25
|
},
|
|
26
|
+
{
|
|
27
|
+
"path": "../../editor-plugin-analytics/afm-dev-agents/tsconfig.json"
|
|
28
|
+
},
|
|
26
29
|
{
|
|
27
30
|
"path": "../../editor-plugin-block-controls/afm-dev-agents/tsconfig.json"
|
|
28
31
|
},
|
package/afm-jira/tsconfig.json
CHANGED
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
{
|
|
24
24
|
"path": "../../../design-system/dropdown-menu/afm-passionfruit/tsconfig.json"
|
|
25
25
|
},
|
|
26
|
+
{
|
|
27
|
+
"path": "../../editor-plugin-analytics/afm-passionfruit/tsconfig.json"
|
|
28
|
+
},
|
|
26
29
|
{
|
|
27
30
|
"path": "../../editor-plugin-block-controls/afm-passionfruit/tsconfig.json"
|
|
28
31
|
},
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
{
|
|
24
24
|
"path": "../../../design-system/dropdown-menu/afm-post-office/tsconfig.json"
|
|
25
25
|
},
|
|
26
|
+
{
|
|
27
|
+
"path": "../../editor-plugin-analytics/afm-post-office/tsconfig.json"
|
|
28
|
+
},
|
|
26
29
|
{
|
|
27
30
|
"path": "../../editor-plugin-block-controls/afm-post-office/tsconfig.json"
|
|
28
31
|
},
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
{
|
|
24
24
|
"path": "../../../design-system/dropdown-menu/afm-rovo-extension/tsconfig.json"
|
|
25
25
|
},
|
|
26
|
+
{
|
|
27
|
+
"path": "../../editor-plugin-analytics/afm-rovo-extension/tsconfig.json"
|
|
28
|
+
},
|
|
26
29
|
{
|
|
27
30
|
"path": "../../editor-plugin-block-controls/afm-rovo-extension/tsconfig.json"
|
|
28
31
|
},
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
{
|
|
24
24
|
"path": "../../../design-system/dropdown-menu/afm-townsquare/tsconfig.json"
|
|
25
25
|
},
|
|
26
|
+
{
|
|
27
|
+
"path": "../../editor-plugin-analytics/afm-townsquare/tsconfig.json"
|
|
28
|
+
},
|
|
26
29
|
{
|
|
27
30
|
"path": "../../editor-plugin-block-controls/afm-townsquare/tsconfig.json"
|
|
28
31
|
},
|
|
@@ -38,7 +38,7 @@ var blockMenuPlugin = exports.blockMenuPlugin = function blockMenuPlugin(_ref) {
|
|
|
38
38
|
},
|
|
39
39
|
commands: {
|
|
40
40
|
formatNode: function formatNode(targetType) {
|
|
41
|
-
return (0, _formatNode2.formatNode)(targetType);
|
|
41
|
+
return (0, _formatNode2.formatNode)(api)(targetType);
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
getSharedState: function getSharedState(editorState) {
|
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.formatNode = void 0;
|
|
6
|
+
exports.formatNodeSelectEmptyList = exports.formatNode = void 0;
|
|
7
|
+
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
8
|
+
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
7
9
|
var _utils = require("@atlaskit/editor-prosemirror/utils");
|
|
8
10
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
9
11
|
var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
|
|
10
12
|
var _selection = require("./selection");
|
|
11
13
|
var _layoutTransforms = require("./transforms/layout-transforms");
|
|
12
14
|
var _transformNodeToTargetType = require("./transforms/transformNodeToTargetType");
|
|
15
|
+
var _utils2 = require("./transforms/utils");
|
|
13
16
|
/**
|
|
14
17
|
* Handles formatting when selection is empty by inserting a new target node
|
|
15
18
|
*/
|
|
@@ -49,64 +52,140 @@ var formatNodeWhenSelectionEmpty = function formatNodeWhenSelectionEmpty(tr, tar
|
|
|
49
52
|
return tr;
|
|
50
53
|
};
|
|
51
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Handles formatting when an empty list is selected
|
|
57
|
+
* Converting an empty list to a target node, will remove the list and replace with an empty target node
|
|
58
|
+
*/
|
|
59
|
+
var formatNodeSelectEmptyList = exports.formatNodeSelectEmptyList = function formatNodeSelectEmptyList(tr, targetType, listNode, schema) {
|
|
60
|
+
var nodes = schema.nodes;
|
|
61
|
+
var headingLevel = 1;
|
|
62
|
+
var finalTargetType = targetType;
|
|
63
|
+
if (targetType.startsWith('heading')) {
|
|
64
|
+
var levelString = targetType.slice(-1);
|
|
65
|
+
var level = parseInt(levelString, 10);
|
|
66
|
+
if (!isNaN(level) && level >= 1 && level <= 6) {
|
|
67
|
+
headingLevel = level;
|
|
68
|
+
finalTargetType = 'heading';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
var replaceNode = null;
|
|
72
|
+
if (finalTargetType === 'layoutSection') {
|
|
73
|
+
var emptyPara = nodes.paragraph.createAndFill();
|
|
74
|
+
if (emptyPara) {
|
|
75
|
+
replaceNode = (0, _layoutTransforms.createDefaultLayoutSection)(schema, emptyPara);
|
|
76
|
+
}
|
|
77
|
+
} else if (finalTargetType === 'heading') {
|
|
78
|
+
replaceNode = nodes.heading.createAndFill({
|
|
79
|
+
level: headingLevel
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
replaceNode = nodes[finalTargetType].createAndFill();
|
|
83
|
+
}
|
|
84
|
+
if (replaceNode) {
|
|
85
|
+
tr.replaceWith(listNode.pos, listNode.pos + listNode.node.nodeSize, replaceNode);
|
|
86
|
+
tr.setSelection(new _state.TextSelection(tr.doc.resolve(listNode.pos)));
|
|
87
|
+
}
|
|
88
|
+
return tr;
|
|
89
|
+
};
|
|
90
|
+
|
|
52
91
|
/**
|
|
53
92
|
* Formats the current node or selection to the specified target type
|
|
93
|
+
* @param api - The editor API injection that provides access to analytics and other plugin actions
|
|
54
94
|
* @param targetType - The target node type to convert to
|
|
55
95
|
*/
|
|
56
|
-
var formatNode = exports.formatNode = function formatNode(
|
|
57
|
-
return function (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
96
|
+
var formatNode = exports.formatNode = function formatNode(api) {
|
|
97
|
+
return function (targetType) {
|
|
98
|
+
return function (_ref) {
|
|
99
|
+
var tr = _ref.tr;
|
|
100
|
+
var selection = tr.selection;
|
|
101
|
+
var schema = tr.doc.type.schema;
|
|
102
|
+
var nodes = schema.nodes;
|
|
62
103
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
104
|
+
// Find the node to format from the current selection
|
|
105
|
+
var nodeToFormat;
|
|
106
|
+
var nodePos = selection.from;
|
|
66
107
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
108
|
+
// when selection is empty, we insert a empty target node
|
|
109
|
+
if (selection.empty && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_block_menu_empty_line', 'isEnabled', true)) {
|
|
110
|
+
var listNodes = [];
|
|
111
|
+
// need to find if there is any list node in the current selection
|
|
112
|
+
// As when select a empty list, selection is empty, but we want to convert the list instead of inserting a target node
|
|
113
|
+
// findSelectedNodeOfType does not work when selection is empty, so we use nodesBetween
|
|
114
|
+
tr.doc.nodesBetween(selection.from, selection.to, function (node, pos) {
|
|
115
|
+
if ((0, _utils2.isListNodeType)(node.type)) {
|
|
116
|
+
listNodes.push({
|
|
117
|
+
node: node,
|
|
118
|
+
pos: pos
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
// get the first list node as when click on drag handle if there are list node
|
|
123
|
+
// can only select one list at a time, so we just need to find the first one
|
|
124
|
+
if (listNodes.length > 0 && (0, _platformFeatureFlags.fg)('platform_editor_block_menu_patch_2')) {
|
|
125
|
+
var firstChild = listNodes[0];
|
|
126
|
+
return formatNodeSelectEmptyList(tr, targetType, firstChild, schema);
|
|
127
|
+
} else {
|
|
128
|
+
return formatNodeWhenSelectionEmpty(tr, targetType, nodePos, schema);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
71
131
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
132
|
+
// Try to find the current node from selection
|
|
133
|
+
var selectedNode = (0, _utils.findSelectedNodeOfType)([nodes.paragraph, nodes.heading, nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.layoutSection])(selection);
|
|
134
|
+
if (selectedNode) {
|
|
135
|
+
nodeToFormat = selectedNode.node;
|
|
136
|
+
nodePos = selectedNode.pos;
|
|
137
|
+
} else {
|
|
138
|
+
// Try to find parent node (including list parents)
|
|
139
|
+
var parentNode = (0, _utils.findParentNodeOfType)([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
|
|
140
|
+
if (parentNode) {
|
|
141
|
+
nodeToFormat = parentNode.node;
|
|
142
|
+
nodePos = parentNode.pos;
|
|
143
|
+
var paragraphOrHeadingNode = (0, _utils.findParentNodeOfType)([nodes.paragraph, nodes.heading])(selection);
|
|
144
|
+
// Special case: if we found a listItem, check if we need the parent list instead
|
|
145
|
+
if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
|
|
146
|
+
var listParent = (0, _utils.findParentNodeOfType)([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
|
|
147
|
+
if (listParent) {
|
|
148
|
+
// For list transformations, we want the list parent, not the listItem
|
|
149
|
+
nodeToFormat = listParent.node;
|
|
150
|
+
nodePos = listParent.pos;
|
|
151
|
+
}
|
|
152
|
+
} else if (parentNode.node.type !== nodes.blockquote && paragraphOrHeadingNode) {
|
|
153
|
+
nodeToFormat = paragraphOrHeadingNode.node;
|
|
154
|
+
nodePos = paragraphOrHeadingNode.pos;
|
|
91
155
|
}
|
|
92
|
-
} else if (parentNode.node.type !== nodes.blockquote && paragraphOrHeadingNode) {
|
|
93
|
-
nodeToFormat = paragraphOrHeadingNode.node;
|
|
94
|
-
nodePos = paragraphOrHeadingNode.pos;
|
|
95
156
|
}
|
|
96
157
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
nodePos = selection.$from.pos;
|
|
101
|
-
}
|
|
102
|
-
try {
|
|
103
|
-
var newTr = (0, _transformNodeToTargetType.transformNodeToTargetType)(tr, nodeToFormat, nodePos, targetType);
|
|
104
|
-
if (newTr && (0, _platformFeatureFlags.fg)('platform_editor_block_menu_selection_fix')) {
|
|
105
|
-
return (0, _selection.setSelectionAfterTransform)(newTr, nodePos, targetType);
|
|
158
|
+
if (!nodeToFormat) {
|
|
159
|
+
nodeToFormat = selection.$from.node();
|
|
160
|
+
nodePos = selection.$from.pos;
|
|
106
161
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
162
|
+
try {
|
|
163
|
+
var _nodeToFormat$attrs;
|
|
164
|
+
var newTr = (0, _transformNodeToTargetType.transformNodeToTargetType)(tr, nodeToFormat, nodePos, targetType);
|
|
165
|
+
var sourceTypeName = nodeToFormat.type.name;
|
|
166
|
+
if (sourceTypeName === 'heading' && (_nodeToFormat$attrs = nodeToFormat.attrs) !== null && _nodeToFormat$attrs !== void 0 && _nodeToFormat$attrs.level) {
|
|
167
|
+
sourceTypeName = "heading".concat(nodeToFormat.attrs.level);
|
|
168
|
+
}
|
|
169
|
+
if (newTr && sourceTypeName !== targetType) {
|
|
170
|
+
var _api$analytics;
|
|
171
|
+
api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.attachAnalyticsEvent({
|
|
172
|
+
action: _analytics.ACTION.CONVERTED,
|
|
173
|
+
actionSubject: _analytics.ACTION_SUBJECT.ELEMENT,
|
|
174
|
+
eventType: _analytics.EVENT_TYPE.TRACK,
|
|
175
|
+
attributes: {
|
|
176
|
+
from: sourceTypeName,
|
|
177
|
+
to: targetType,
|
|
178
|
+
inputMethod: _analytics.INPUT_METHOD.BLOCK_MENU
|
|
179
|
+
}
|
|
180
|
+
})(newTr);
|
|
181
|
+
}
|
|
182
|
+
if (newTr && (0, _platformFeatureFlags.fg)('platform_editor_block_menu_selection_fix')) {
|
|
183
|
+
return (0, _selection.setSelectionAfterTransform)(newTr, nodePos, targetType);
|
|
184
|
+
}
|
|
185
|
+
return newTr;
|
|
186
|
+
} catch (_unused) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
111
190
|
};
|
|
112
191
|
};
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { ACTION, ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
1
3
|
import { findParentNodeOfType, findSelectedNodeOfType, safeInsert as pmSafeInsert } from '@atlaskit/editor-prosemirror/utils';
|
|
2
4
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
5
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
4
6
|
import { setSelectionAfterTransform } from './selection';
|
|
5
7
|
import { createDefaultLayoutSection } from './transforms/layout-transforms';
|
|
6
8
|
import { transformNodeToTargetType } from './transforms/transformNodeToTargetType';
|
|
9
|
+
import { isListNodeType } from './transforms/utils';
|
|
10
|
+
|
|
7
11
|
/**
|
|
8
12
|
* Handles formatting when selection is empty by inserting a new target node
|
|
9
13
|
*/
|
|
@@ -47,11 +51,50 @@ const formatNodeWhenSelectionEmpty = (tr, targetType, nodePos, schema) => {
|
|
|
47
51
|
return tr;
|
|
48
52
|
};
|
|
49
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Handles formatting when an empty list is selected
|
|
56
|
+
* Converting an empty list to a target node, will remove the list and replace with an empty target node
|
|
57
|
+
*/
|
|
58
|
+
export const formatNodeSelectEmptyList = (tr, targetType, listNode, schema) => {
|
|
59
|
+
const {
|
|
60
|
+
nodes
|
|
61
|
+
} = schema;
|
|
62
|
+
let headingLevel = 1;
|
|
63
|
+
let finalTargetType = targetType;
|
|
64
|
+
if (targetType.startsWith('heading')) {
|
|
65
|
+
const levelString = targetType.slice(-1);
|
|
66
|
+
const level = parseInt(levelString, 10);
|
|
67
|
+
if (!isNaN(level) && level >= 1 && level <= 6) {
|
|
68
|
+
headingLevel = level;
|
|
69
|
+
finalTargetType = 'heading';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
let replaceNode = null;
|
|
73
|
+
if (finalTargetType === 'layoutSection') {
|
|
74
|
+
const emptyPara = nodes.paragraph.createAndFill();
|
|
75
|
+
if (emptyPara) {
|
|
76
|
+
replaceNode = createDefaultLayoutSection(schema, emptyPara);
|
|
77
|
+
}
|
|
78
|
+
} else if (finalTargetType === 'heading') {
|
|
79
|
+
replaceNode = nodes.heading.createAndFill({
|
|
80
|
+
level: headingLevel
|
|
81
|
+
});
|
|
82
|
+
} else {
|
|
83
|
+
replaceNode = nodes[finalTargetType].createAndFill();
|
|
84
|
+
}
|
|
85
|
+
if (replaceNode) {
|
|
86
|
+
tr.replaceWith(listNode.pos, listNode.pos + listNode.node.nodeSize, replaceNode);
|
|
87
|
+
tr.setSelection(new TextSelection(tr.doc.resolve(listNode.pos)));
|
|
88
|
+
}
|
|
89
|
+
return tr;
|
|
90
|
+
};
|
|
91
|
+
|
|
50
92
|
/**
|
|
51
93
|
* Formats the current node or selection to the specified target type
|
|
94
|
+
* @param api - The editor API injection that provides access to analytics and other plugin actions
|
|
52
95
|
* @param targetType - The target node type to convert to
|
|
53
96
|
*/
|
|
54
|
-
export const formatNode = targetType => {
|
|
97
|
+
export const formatNode = api => targetType => {
|
|
55
98
|
return ({
|
|
56
99
|
tr
|
|
57
100
|
}) => {
|
|
@@ -69,7 +112,26 @@ export const formatNode = targetType => {
|
|
|
69
112
|
|
|
70
113
|
// when selection is empty, we insert a empty target node
|
|
71
114
|
if (selection.empty && expValEqualsNoExposure('platform_editor_block_menu_empty_line', 'isEnabled', true)) {
|
|
72
|
-
|
|
115
|
+
const listNodes = [];
|
|
116
|
+
// need to find if there is any list node in the current selection
|
|
117
|
+
// As when select a empty list, selection is empty, but we want to convert the list instead of inserting a target node
|
|
118
|
+
// findSelectedNodeOfType does not work when selection is empty, so we use nodesBetween
|
|
119
|
+
tr.doc.nodesBetween(selection.from, selection.to, (node, pos) => {
|
|
120
|
+
if (isListNodeType(node.type)) {
|
|
121
|
+
listNodes.push({
|
|
122
|
+
node,
|
|
123
|
+
pos
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
// get the first list node as when click on drag handle if there are list node
|
|
128
|
+
// can only select one list at a time, so we just need to find the first one
|
|
129
|
+
if (listNodes.length > 0 && fg('platform_editor_block_menu_patch_2')) {
|
|
130
|
+
const firstChild = listNodes[0];
|
|
131
|
+
return formatNodeSelectEmptyList(tr, targetType, firstChild, schema);
|
|
132
|
+
} else {
|
|
133
|
+
return formatNodeWhenSelectionEmpty(tr, targetType, nodePos, schema);
|
|
134
|
+
}
|
|
73
135
|
}
|
|
74
136
|
|
|
75
137
|
// Try to find the current node from selection
|
|
@@ -103,7 +165,25 @@ export const formatNode = targetType => {
|
|
|
103
165
|
nodePos = selection.$from.pos;
|
|
104
166
|
}
|
|
105
167
|
try {
|
|
168
|
+
var _nodeToFormat$attrs;
|
|
106
169
|
const newTr = transformNodeToTargetType(tr, nodeToFormat, nodePos, targetType);
|
|
170
|
+
let sourceTypeName = nodeToFormat.type.name;
|
|
171
|
+
if (sourceTypeName === 'heading' && (_nodeToFormat$attrs = nodeToFormat.attrs) !== null && _nodeToFormat$attrs !== void 0 && _nodeToFormat$attrs.level) {
|
|
172
|
+
sourceTypeName = `heading${nodeToFormat.attrs.level}`;
|
|
173
|
+
}
|
|
174
|
+
if (newTr && sourceTypeName !== targetType) {
|
|
175
|
+
var _api$analytics, _api$analytics$action;
|
|
176
|
+
api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$action = _api$analytics.actions) === null || _api$analytics$action === void 0 ? void 0 : _api$analytics$action.attachAnalyticsEvent({
|
|
177
|
+
action: ACTION.CONVERTED,
|
|
178
|
+
actionSubject: ACTION_SUBJECT.ELEMENT,
|
|
179
|
+
eventType: EVENT_TYPE.TRACK,
|
|
180
|
+
attributes: {
|
|
181
|
+
from: sourceTypeName,
|
|
182
|
+
to: targetType,
|
|
183
|
+
inputMethod: INPUT_METHOD.BLOCK_MENU
|
|
184
|
+
}
|
|
185
|
+
})(newTr);
|
|
186
|
+
}
|
|
107
187
|
if (newTr && fg('platform_editor_block_menu_selection_fix')) {
|
|
108
188
|
return setSelectionAfterTransform(newTr, nodePos, targetType);
|
|
109
189
|
}
|
|
@@ -31,7 +31,7 @@ export var blockMenuPlugin = function blockMenuPlugin(_ref) {
|
|
|
31
31
|
},
|
|
32
32
|
commands: {
|
|
33
33
|
formatNode: function formatNode(targetType) {
|
|
34
|
-
return _formatNode(targetType);
|
|
34
|
+
return _formatNode(api)(targetType);
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
getSharedState: function getSharedState(editorState) {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { ACTION, ACTION_SUBJECT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
1
3
|
import { findParentNodeOfType, findSelectedNodeOfType, safeInsert as pmSafeInsert } from '@atlaskit/editor-prosemirror/utils';
|
|
2
4
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
5
|
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
4
6
|
import { setSelectionAfterTransform } from './selection';
|
|
5
7
|
import { createDefaultLayoutSection } from './transforms/layout-transforms';
|
|
6
8
|
import { transformNodeToTargetType } from './transforms/transformNodeToTargetType';
|
|
9
|
+
import { isListNodeType } from './transforms/utils';
|
|
10
|
+
|
|
7
11
|
/**
|
|
8
12
|
* Handles formatting when selection is empty by inserting a new target node
|
|
9
13
|
*/
|
|
@@ -43,64 +47,140 @@ var formatNodeWhenSelectionEmpty = function formatNodeWhenSelectionEmpty(tr, tar
|
|
|
43
47
|
return tr;
|
|
44
48
|
};
|
|
45
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Handles formatting when an empty list is selected
|
|
52
|
+
* Converting an empty list to a target node, will remove the list and replace with an empty target node
|
|
53
|
+
*/
|
|
54
|
+
export var formatNodeSelectEmptyList = function formatNodeSelectEmptyList(tr, targetType, listNode, schema) {
|
|
55
|
+
var nodes = schema.nodes;
|
|
56
|
+
var headingLevel = 1;
|
|
57
|
+
var finalTargetType = targetType;
|
|
58
|
+
if (targetType.startsWith('heading')) {
|
|
59
|
+
var levelString = targetType.slice(-1);
|
|
60
|
+
var level = parseInt(levelString, 10);
|
|
61
|
+
if (!isNaN(level) && level >= 1 && level <= 6) {
|
|
62
|
+
headingLevel = level;
|
|
63
|
+
finalTargetType = 'heading';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
var replaceNode = null;
|
|
67
|
+
if (finalTargetType === 'layoutSection') {
|
|
68
|
+
var emptyPara = nodes.paragraph.createAndFill();
|
|
69
|
+
if (emptyPara) {
|
|
70
|
+
replaceNode = createDefaultLayoutSection(schema, emptyPara);
|
|
71
|
+
}
|
|
72
|
+
} else if (finalTargetType === 'heading') {
|
|
73
|
+
replaceNode = nodes.heading.createAndFill({
|
|
74
|
+
level: headingLevel
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
replaceNode = nodes[finalTargetType].createAndFill();
|
|
78
|
+
}
|
|
79
|
+
if (replaceNode) {
|
|
80
|
+
tr.replaceWith(listNode.pos, listNode.pos + listNode.node.nodeSize, replaceNode);
|
|
81
|
+
tr.setSelection(new TextSelection(tr.doc.resolve(listNode.pos)));
|
|
82
|
+
}
|
|
83
|
+
return tr;
|
|
84
|
+
};
|
|
85
|
+
|
|
46
86
|
/**
|
|
47
87
|
* Formats the current node or selection to the specified target type
|
|
88
|
+
* @param api - The editor API injection that provides access to analytics and other plugin actions
|
|
48
89
|
* @param targetType - The target node type to convert to
|
|
49
90
|
*/
|
|
50
|
-
export var formatNode = function formatNode(
|
|
51
|
-
return function (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
91
|
+
export var formatNode = function formatNode(api) {
|
|
92
|
+
return function (targetType) {
|
|
93
|
+
return function (_ref) {
|
|
94
|
+
var tr = _ref.tr;
|
|
95
|
+
var selection = tr.selection;
|
|
96
|
+
var schema = tr.doc.type.schema;
|
|
97
|
+
var nodes = schema.nodes;
|
|
56
98
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
99
|
+
// Find the node to format from the current selection
|
|
100
|
+
var nodeToFormat;
|
|
101
|
+
var nodePos = selection.from;
|
|
60
102
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
103
|
+
// when selection is empty, we insert a empty target node
|
|
104
|
+
if (selection.empty && expValEqualsNoExposure('platform_editor_block_menu_empty_line', 'isEnabled', true)) {
|
|
105
|
+
var listNodes = [];
|
|
106
|
+
// need to find if there is any list node in the current selection
|
|
107
|
+
// As when select a empty list, selection is empty, but we want to convert the list instead of inserting a target node
|
|
108
|
+
// findSelectedNodeOfType does not work when selection is empty, so we use nodesBetween
|
|
109
|
+
tr.doc.nodesBetween(selection.from, selection.to, function (node, pos) {
|
|
110
|
+
if (isListNodeType(node.type)) {
|
|
111
|
+
listNodes.push({
|
|
112
|
+
node: node,
|
|
113
|
+
pos: pos
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
// get the first list node as when click on drag handle if there are list node
|
|
118
|
+
// can only select one list at a time, so we just need to find the first one
|
|
119
|
+
if (listNodes.length > 0 && fg('platform_editor_block_menu_patch_2')) {
|
|
120
|
+
var firstChild = listNodes[0];
|
|
121
|
+
return formatNodeSelectEmptyList(tr, targetType, firstChild, schema);
|
|
122
|
+
} else {
|
|
123
|
+
return formatNodeWhenSelectionEmpty(tr, targetType, nodePos, schema);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
65
126
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
127
|
+
// Try to find the current node from selection
|
|
128
|
+
var selectedNode = findSelectedNodeOfType([nodes.paragraph, nodes.heading, nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.layoutSection])(selection);
|
|
129
|
+
if (selectedNode) {
|
|
130
|
+
nodeToFormat = selectedNode.node;
|
|
131
|
+
nodePos = selectedNode.pos;
|
|
132
|
+
} else {
|
|
133
|
+
// Try to find parent node (including list parents)
|
|
134
|
+
var parentNode = findParentNodeOfType([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
|
|
135
|
+
if (parentNode) {
|
|
136
|
+
nodeToFormat = parentNode.node;
|
|
137
|
+
nodePos = parentNode.pos;
|
|
138
|
+
var paragraphOrHeadingNode = findParentNodeOfType([nodes.paragraph, nodes.heading])(selection);
|
|
139
|
+
// Special case: if we found a listItem, check if we need the parent list instead
|
|
140
|
+
if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
|
|
141
|
+
var listParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
|
|
142
|
+
if (listParent) {
|
|
143
|
+
// For list transformations, we want the list parent, not the listItem
|
|
144
|
+
nodeToFormat = listParent.node;
|
|
145
|
+
nodePos = listParent.pos;
|
|
146
|
+
}
|
|
147
|
+
} else if (parentNode.node.type !== nodes.blockquote && paragraphOrHeadingNode) {
|
|
148
|
+
nodeToFormat = paragraphOrHeadingNode.node;
|
|
149
|
+
nodePos = paragraphOrHeadingNode.pos;
|
|
85
150
|
}
|
|
86
|
-
} else if (parentNode.node.type !== nodes.blockquote && paragraphOrHeadingNode) {
|
|
87
|
-
nodeToFormat = paragraphOrHeadingNode.node;
|
|
88
|
-
nodePos = paragraphOrHeadingNode.pos;
|
|
89
151
|
}
|
|
90
152
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
nodePos = selection.$from.pos;
|
|
95
|
-
}
|
|
96
|
-
try {
|
|
97
|
-
var newTr = transformNodeToTargetType(tr, nodeToFormat, nodePos, targetType);
|
|
98
|
-
if (newTr && fg('platform_editor_block_menu_selection_fix')) {
|
|
99
|
-
return setSelectionAfterTransform(newTr, nodePos, targetType);
|
|
153
|
+
if (!nodeToFormat) {
|
|
154
|
+
nodeToFormat = selection.$from.node();
|
|
155
|
+
nodePos = selection.$from.pos;
|
|
100
156
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
157
|
+
try {
|
|
158
|
+
var _nodeToFormat$attrs;
|
|
159
|
+
var newTr = transformNodeToTargetType(tr, nodeToFormat, nodePos, targetType);
|
|
160
|
+
var sourceTypeName = nodeToFormat.type.name;
|
|
161
|
+
if (sourceTypeName === 'heading' && (_nodeToFormat$attrs = nodeToFormat.attrs) !== null && _nodeToFormat$attrs !== void 0 && _nodeToFormat$attrs.level) {
|
|
162
|
+
sourceTypeName = "heading".concat(nodeToFormat.attrs.level);
|
|
163
|
+
}
|
|
164
|
+
if (newTr && sourceTypeName !== targetType) {
|
|
165
|
+
var _api$analytics;
|
|
166
|
+
api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || (_api$analytics = _api$analytics.actions) === null || _api$analytics === void 0 || _api$analytics.attachAnalyticsEvent({
|
|
167
|
+
action: ACTION.CONVERTED,
|
|
168
|
+
actionSubject: ACTION_SUBJECT.ELEMENT,
|
|
169
|
+
eventType: EVENT_TYPE.TRACK,
|
|
170
|
+
attributes: {
|
|
171
|
+
from: sourceTypeName,
|
|
172
|
+
to: targetType,
|
|
173
|
+
inputMethod: INPUT_METHOD.BLOCK_MENU
|
|
174
|
+
}
|
|
175
|
+
})(newTr);
|
|
176
|
+
}
|
|
177
|
+
if (newTr && fg('platform_editor_block_menu_selection_fix')) {
|
|
178
|
+
return setSelectionAfterTransform(newTr, nodePos, targetType);
|
|
179
|
+
}
|
|
180
|
+
return newTr;
|
|
181
|
+
} catch (_unused) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
105
185
|
};
|
|
106
186
|
};
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
import type { EditorCommand } from '@atlaskit/editor-common/types';
|
|
1
|
+
import type { EditorCommand, ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
2
|
+
import { type Schema, type Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import { type Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
4
|
+
import type { BlockMenuPlugin } from '../blockMenuPluginType';
|
|
2
5
|
import type { FormatNodeTargetType } from './transforms/types';
|
|
6
|
+
/**
|
|
7
|
+
* Handles formatting when an empty list is selected
|
|
8
|
+
* Converting an empty list to a target node, will remove the list and replace with an empty target node
|
|
9
|
+
*/
|
|
10
|
+
export declare const formatNodeSelectEmptyList: (tr: Transaction, targetType: FormatNodeTargetType, listNode: {
|
|
11
|
+
node: PMNode;
|
|
12
|
+
pos: number;
|
|
13
|
+
}, schema: Schema) => Transaction;
|
|
3
14
|
/**
|
|
4
15
|
* Formats the current node or selection to the specified target type
|
|
16
|
+
* @param api - The editor API injection that provides access to analytics and other plugin actions
|
|
5
17
|
* @param targetType - The target node type to convert to
|
|
6
18
|
*/
|
|
7
|
-
export declare const formatNode: (targetType: FormatNodeTargetType) => EditorCommand;
|
|
19
|
+
export declare const formatNode: (api?: ExtractInjectionAPI<BlockMenuPlugin>) => (targetType: FormatNodeTargetType) => EditorCommand;
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
import type { EditorCommand } from '@atlaskit/editor-common/types';
|
|
1
|
+
import type { EditorCommand, ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
2
|
+
import { type Schema, type Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
import { type Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
4
|
+
import type { BlockMenuPlugin } from '../blockMenuPluginType';
|
|
2
5
|
import type { FormatNodeTargetType } from './transforms/types';
|
|
6
|
+
/**
|
|
7
|
+
* Handles formatting when an empty list is selected
|
|
8
|
+
* Converting an empty list to a target node, will remove the list and replace with an empty target node
|
|
9
|
+
*/
|
|
10
|
+
export declare const formatNodeSelectEmptyList: (tr: Transaction, targetType: FormatNodeTargetType, listNode: {
|
|
11
|
+
node: PMNode;
|
|
12
|
+
pos: number;
|
|
13
|
+
}, schema: Schema) => Transaction;
|
|
3
14
|
/**
|
|
4
15
|
* Formats the current node or selection to the specified target type
|
|
16
|
+
* @param api - The editor API injection that provides access to analytics and other plugin actions
|
|
5
17
|
* @param targetType - The target node type to convert to
|
|
6
18
|
*/
|
|
7
|
-
export declare const formatNode: (targetType: FormatNodeTargetType) => EditorCommand;
|
|
19
|
+
export declare const formatNode: (api?: ExtractInjectionAPI<BlockMenuPlugin>) => (targetType: FormatNodeTargetType) => EditorCommand;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-block-menu",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"description": "BlockMenu plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@atlaskit/css": "^0.14.0",
|
|
32
32
|
"@atlaskit/dropdown-menu": "^16.3.0",
|
|
33
|
+
"@atlaskit/editor-plugin-analytics": "^6.1.0",
|
|
33
34
|
"@atlaskit/editor-plugin-block-controls": "^7.1.0",
|
|
34
35
|
"@atlaskit/editor-plugin-decorations": "^6.1.0",
|
|
35
36
|
"@atlaskit/editor-plugin-selection": "^6.0.0",
|
|
@@ -39,15 +40,15 @@
|
|
|
39
40
|
"@atlaskit/editor-tables": "^2.9.0",
|
|
40
41
|
"@atlaskit/editor-toolbar": "^0.10.0",
|
|
41
42
|
"@atlaskit/icon": "^28.3.0",
|
|
42
|
-
"@atlaskit/icon-lab": "^5.
|
|
43
|
+
"@atlaskit/icon-lab": "^5.8.0",
|
|
43
44
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
44
45
|
"@atlaskit/primitives": "^14.15.0",
|
|
45
46
|
"@atlaskit/tmp-editor-statsig": "^12.32.0",
|
|
46
|
-
"@atlaskit/tokens": "^6.
|
|
47
|
+
"@atlaskit/tokens": "^6.4.0",
|
|
47
48
|
"@babel/runtime": "^7.0.0"
|
|
48
49
|
},
|
|
49
50
|
"peerDependencies": {
|
|
50
|
-
"@atlaskit/editor-common": "^110.
|
|
51
|
+
"@atlaskit/editor-common": "^110.3.0",
|
|
51
52
|
"react": "^18.2.0",
|
|
52
53
|
"react-intl-next": "npm:react-intl@^5.18.1"
|
|
53
54
|
},
|