@atlaskit/editor-plugin-synced-block 4.2.1 → 4.2.2
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 +8 -0
- package/dist/cjs/editor-commands/index.js +5 -1
- package/dist/cjs/pm-plugins/utils/utils.js +36 -19
- package/dist/cjs/ui/CreateSyncedBlockDropdownItem.js +19 -6
- package/dist/es2019/editor-commands/index.js +5 -1
- package/dist/es2019/pm-plugins/utils/utils.js +36 -19
- package/dist/es2019/ui/CreateSyncedBlockDropdownItem.js +16 -6
- package/dist/esm/editor-commands/index.js +5 -1
- package/dist/esm/pm-plugins/utils/utils.js +36 -19
- package/dist/esm/ui/CreateSyncedBlockDropdownItem.js +17 -6
- package/dist/types/pm-plugins/utils/utils.d.ts +9 -1
- package/dist/types-ts4.5/pm-plugins/utils/utils.d.ts +9 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-synced-block
|
|
2
2
|
|
|
3
|
+
## 4.2.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`3432c4a4a074c`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3432c4a4a074c) -
|
|
8
|
+
[ux] EDITOR-2525 update block menu convert to sync block to support all fishfooding node types
|
|
9
|
+
- Updated dependencies
|
|
10
|
+
|
|
3
11
|
## 4.2.1
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.removeSyncedBlock = exports.editSyncedBlockSource = exports.createSyncedBlock = exports.copySyncedBlockReferenceToClipboard = void 0;
|
|
7
7
|
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
8
8
|
var _copyButton = require("@atlaskit/editor-common/copy-button");
|
|
9
|
+
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
9
10
|
var _utils = require("@atlaskit/editor-prosemirror/utils");
|
|
10
11
|
var _utils2 = require("../pm-plugins/utils/utils");
|
|
11
12
|
var createSyncedBlock = exports.createSyncedBlock = function createSyncedBlock(_ref) {
|
|
@@ -48,7 +49,10 @@ var createSyncedBlock = exports.createSyncedBlock = function createSyncedBlock(_
|
|
|
48
49
|
// Save the new node with empty content to backend
|
|
49
50
|
// This is so that the node can be copied and referenced without the source being saved/published
|
|
50
51
|
syncBlockStore.createBodiedSyncBlockNode(_attrs);
|
|
51
|
-
tr.replaceWith(conversionInfo.from - 1, conversionInfo.to, _newBodiedSyncBlockNode).scrollIntoView();
|
|
52
|
+
tr.replaceWith(conversionInfo.from > 0 ? conversionInfo.from - 1 : 0, conversionInfo.to, _newBodiedSyncBlockNode).scrollIntoView();
|
|
53
|
+
|
|
54
|
+
// set selection to the end of the previous selection + 1 for the position taken up by the start of the new synced block
|
|
55
|
+
tr.setSelection(_state.TextSelection.create(tr.doc, conversionInfo.to + 1));
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
// This transaction will be intercepted in filterTransaction and dispatched when saving to backend succeeds
|
|
@@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.isBodiedSyncBlockNode = exports.findSyncBlockOrBodiedSyncBlock = exports.findSyncBlock = exports.findBodiedSyncBlock = exports.canBeConvertedToSyncBlock = void 0;
|
|
7
7
|
var _model = require("@atlaskit/editor-prosemirror/model");
|
|
8
|
+
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
8
9
|
var _utils = require("@atlaskit/editor-prosemirror/utils");
|
|
9
|
-
var _editorSyncedBlockProvider = require("@atlaskit/editor-synced-block-provider");
|
|
10
10
|
var _editorTables = require("@atlaskit/editor-tables");
|
|
11
11
|
var findSyncBlock = exports.findSyncBlock = function findSyncBlock(state, selection) {
|
|
12
12
|
var syncBlock = state.schema.nodes.syncBlock;
|
|
@@ -22,11 +22,22 @@ var findSyncBlockOrBodiedSyncBlock = exports.findSyncBlockOrBodiedSyncBlock = fu
|
|
|
22
22
|
var isBodiedSyncBlockNode = exports.isBodiedSyncBlockNode = function isBodiedSyncBlockNode(node, bodiedSyncBlock) {
|
|
23
23
|
return node.type === bodiedSyncBlock;
|
|
24
24
|
};
|
|
25
|
+
var UNSUPPORTED_NODE_TYPES = new Set(['inlineExtension', 'extension', 'bodiedExtension', 'syncBlock', 'bodiedSyncBlock']);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Checks whether the selection can be converted to sync block
|
|
29
|
+
*
|
|
30
|
+
* @param selection - the current editor selection to validate for sync block conversion
|
|
31
|
+
* @returns A fragment containing the content to include in the synced block,
|
|
32
|
+
* stripping out unsupported marks (breakout on codeblock/expand/layout), as well as from and to positions,
|
|
33
|
+
* or false if conversion is not possible
|
|
34
|
+
*/
|
|
25
35
|
var canBeConvertedToSyncBlock = exports.canBeConvertedToSyncBlock = function canBeConvertedToSyncBlock(selection) {
|
|
36
|
+
var schema = selection.$from.doc.type.schema;
|
|
37
|
+
var nodes = schema.nodes;
|
|
26
38
|
var from = selection.from;
|
|
27
39
|
var to = selection.to;
|
|
28
|
-
var
|
|
29
|
-
var contentToInclude;
|
|
40
|
+
var contentToInclude = selection.content().content;
|
|
30
41
|
if (selection instanceof _editorTables.CellSelection) {
|
|
31
42
|
var table = (0, _editorTables.findTable)(selection);
|
|
32
43
|
if (!table) {
|
|
@@ -35,35 +46,41 @@ var canBeConvertedToSyncBlock = exports.canBeConvertedToSyncBlock = function can
|
|
|
35
46
|
contentToInclude = _model.Fragment.from([table.node]);
|
|
36
47
|
from = table.pos;
|
|
37
48
|
to = table.pos + table.node.nodeSize;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return false;
|
|
49
|
+
} else if (selection instanceof _state.TextSelection) {
|
|
50
|
+
var trueParent = (0, _utils.findParentNodeOfType)([nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.blockquote])(selection);
|
|
51
|
+
if (trueParent) {
|
|
52
|
+
contentToInclude = _model.Fragment.from([trueParent.node]);
|
|
53
|
+
from = trueParent.pos;
|
|
54
|
+
to = trueParent.pos + trueParent.node.nodeSize;
|
|
55
|
+
}
|
|
46
56
|
}
|
|
47
|
-
var syncBlockSchema = (0, _editorSyncedBlockProvider.getDefaultSyncBlockSchema)();
|
|
48
57
|
var canBeConverted = true;
|
|
49
58
|
selection.$from.doc.nodesBetween(from, to, function (node) {
|
|
50
|
-
if (
|
|
59
|
+
if (UNSUPPORTED_NODE_TYPES.has(node.type.name)) {
|
|
51
60
|
canBeConverted = false;
|
|
52
61
|
return false;
|
|
53
62
|
}
|
|
54
|
-
node.marks.forEach(function (mark) {
|
|
55
|
-
if (!(mark.type.name in syncBlockSchema.marks)) {
|
|
56
|
-
canBeConverted = false;
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
63
|
});
|
|
61
64
|
if (!canBeConverted) {
|
|
62
65
|
return false;
|
|
63
66
|
}
|
|
67
|
+
contentToInclude = removeBreakoutMarks(contentToInclude);
|
|
64
68
|
return {
|
|
65
69
|
contentToInclude: contentToInclude,
|
|
66
70
|
from: from,
|
|
67
71
|
to: to
|
|
68
72
|
};
|
|
73
|
+
};
|
|
74
|
+
var removeBreakoutMarks = function removeBreakoutMarks(content) {
|
|
75
|
+
var nodes = [];
|
|
76
|
+
|
|
77
|
+
// we only need to recurse at the top level, because breakout has to be on a top level
|
|
78
|
+
content.forEach(function (node) {
|
|
79
|
+
var filteredMarks = node.marks.filter(function (mark) {
|
|
80
|
+
return mark.type.name !== 'breakout';
|
|
81
|
+
});
|
|
82
|
+
var newNode = node.type.create(node.attrs, node.content, filteredMarks);
|
|
83
|
+
nodes.push(newNode);
|
|
84
|
+
});
|
|
85
|
+
return _model.Fragment.from(nodes);
|
|
69
86
|
};
|
|
@@ -1,25 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _typeof = require("@babel/runtime/helpers/typeof");
|
|
4
5
|
Object.defineProperty(exports, "__esModule", {
|
|
5
6
|
value: true
|
|
6
7
|
});
|
|
7
8
|
exports.CreateSyncedBlockDropdownItem = void 0;
|
|
8
|
-
var _react =
|
|
9
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
10
|
var _reactIntlNext = require("react-intl-next");
|
|
11
|
+
var _hooks = require("@atlaskit/editor-common/hooks");
|
|
10
12
|
var _messages = require("@atlaskit/editor-common/messages");
|
|
11
13
|
var _editorToolbar = require("@atlaskit/editor-toolbar");
|
|
12
14
|
var _lozenge = _interopRequireDefault(require("@atlaskit/lozenge"));
|
|
13
15
|
var _compiled = require("@atlaskit/primitives/compiled");
|
|
14
16
|
var _utils = require("../pm-plugins/utils/utils");
|
|
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); }
|
|
15
18
|
var CreateSyncedBlockDropdownItem = exports.CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref) {
|
|
16
|
-
var _api$selection;
|
|
17
19
|
var api = _ref.api;
|
|
18
20
|
var _useIntl = (0, _reactIntlNext.useIntl)(),
|
|
19
21
|
formatMessage = _useIntl.formatMessage;
|
|
20
|
-
var
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(api, ['selection', 'blockControls'], function (states) {
|
|
23
|
+
var _states$selectionStat, _states$blockControls;
|
|
24
|
+
return {
|
|
25
|
+
selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
|
|
26
|
+
activeNode: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.activeNode
|
|
27
|
+
};
|
|
28
|
+
}),
|
|
29
|
+
selection = _useSharedPluginState.selection,
|
|
30
|
+
activeNode = _useSharedPluginState.activeNode;
|
|
31
|
+
var isNested = activeNode && activeNode.rootPos !== activeNode.pos;
|
|
32
|
+
var canBeConverted = (0, _react.useMemo)(function () {
|
|
33
|
+
return selection && (0, _utils.canBeConvertedToSyncBlock)(selection);
|
|
34
|
+
}, [selection]);
|
|
35
|
+
if (isNested || !canBeConverted) {
|
|
23
36
|
return null;
|
|
24
37
|
}
|
|
25
38
|
var onClick = function onClick() {
|
|
@@ -37,7 +50,7 @@ var CreateSyncedBlockDropdownItem = exports.CreateSyncedBlockDropdownItem = func
|
|
|
37
50
|
}, /*#__PURE__*/_react.default.createElement(_compiled.Flex, {
|
|
38
51
|
alignItems: "center",
|
|
39
52
|
gap: "space.050"
|
|
40
|
-
}, /*#__PURE__*/_react.default.createElement(_compiled.Text, null,
|
|
53
|
+
}, /*#__PURE__*/_react.default.createElement(_compiled.Text, null, formatMessage(_messages.blockMenuMessages.createSyncedBlock)), /*#__PURE__*/_react.default.createElement(_lozenge.default, {
|
|
41
54
|
appearance: "new"
|
|
42
55
|
}, formatMessage(_messages.blockMenuMessages.newLozenge))));
|
|
43
56
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
2
2
|
import { copyDomNode, toDOM } from '@atlaskit/editor-common/copy-button';
|
|
3
|
+
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
3
4
|
import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode } from '@atlaskit/editor-prosemirror/utils';
|
|
4
5
|
import { canBeConvertedToSyncBlock, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
|
|
5
6
|
export const createSyncedBlock = ({
|
|
@@ -48,7 +49,10 @@ export const createSyncedBlock = ({
|
|
|
48
49
|
// Save the new node with empty content to backend
|
|
49
50
|
// This is so that the node can be copied and referenced without the source being saved/published
|
|
50
51
|
syncBlockStore.createBodiedSyncBlockNode(attrs);
|
|
51
|
-
tr.replaceWith(conversionInfo.from - 1, conversionInfo.to, newBodiedSyncBlockNode).scrollIntoView();
|
|
52
|
+
tr.replaceWith(conversionInfo.from > 0 ? conversionInfo.from - 1 : 0, conversionInfo.to, newBodiedSyncBlockNode).scrollIntoView();
|
|
53
|
+
|
|
54
|
+
// set selection to the end of the previous selection + 1 for the position taken up by the start of the new synced block
|
|
55
|
+
tr.setSelection(TextSelection.create(tr.doc, conversionInfo.to + 1));
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
// This transaction will be intercepted in filterTransaction and dispatched when saving to backend succeeds
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
2
3
|
import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
3
|
-
import { getDefaultSyncBlockSchema } from '@atlaskit/editor-synced-block-provider';
|
|
4
4
|
import { CellSelection, findTable } from '@atlaskit/editor-tables';
|
|
5
5
|
export const findSyncBlock = (state, selection) => {
|
|
6
6
|
const {
|
|
@@ -18,11 +18,24 @@ export const findSyncBlockOrBodiedSyncBlock = (state, selection) => {
|
|
|
18
18
|
return findSyncBlock(state, selection) || findBodiedSyncBlock(state, selection);
|
|
19
19
|
};
|
|
20
20
|
export const isBodiedSyncBlockNode = (node, bodiedSyncBlock) => node.type === bodiedSyncBlock;
|
|
21
|
+
const UNSUPPORTED_NODE_TYPES = new Set(['inlineExtension', 'extension', 'bodiedExtension', 'syncBlock', 'bodiedSyncBlock']);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Checks whether the selection can be converted to sync block
|
|
25
|
+
*
|
|
26
|
+
* @param selection - the current editor selection to validate for sync block conversion
|
|
27
|
+
* @returns A fragment containing the content to include in the synced block,
|
|
28
|
+
* stripping out unsupported marks (breakout on codeblock/expand/layout), as well as from and to positions,
|
|
29
|
+
* or false if conversion is not possible
|
|
30
|
+
*/
|
|
21
31
|
export const canBeConvertedToSyncBlock = selection => {
|
|
32
|
+
const schema = selection.$from.doc.type.schema;
|
|
33
|
+
const {
|
|
34
|
+
nodes
|
|
35
|
+
} = schema;
|
|
22
36
|
let from = selection.from;
|
|
23
37
|
let to = selection.to;
|
|
24
|
-
let
|
|
25
|
-
let contentToInclude;
|
|
38
|
+
let contentToInclude = selection.content().content;
|
|
26
39
|
if (selection instanceof CellSelection) {
|
|
27
40
|
const table = findTable(selection);
|
|
28
41
|
if (!table) {
|
|
@@ -31,35 +44,39 @@ export const canBeConvertedToSyncBlock = selection => {
|
|
|
31
44
|
contentToInclude = Fragment.from([table.node]);
|
|
32
45
|
from = table.pos;
|
|
33
46
|
to = table.pos + table.node.nodeSize;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return false;
|
|
47
|
+
} else if (selection instanceof TextSelection) {
|
|
48
|
+
const trueParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.blockquote])(selection);
|
|
49
|
+
if (trueParent) {
|
|
50
|
+
contentToInclude = Fragment.from([trueParent.node]);
|
|
51
|
+
from = trueParent.pos;
|
|
52
|
+
to = trueParent.pos + trueParent.node.nodeSize;
|
|
53
|
+
}
|
|
42
54
|
}
|
|
43
|
-
const syncBlockSchema = getDefaultSyncBlockSchema();
|
|
44
55
|
let canBeConverted = true;
|
|
45
56
|
selection.$from.doc.nodesBetween(from, to, node => {
|
|
46
|
-
if (
|
|
57
|
+
if (UNSUPPORTED_NODE_TYPES.has(node.type.name)) {
|
|
47
58
|
canBeConverted = false;
|
|
48
59
|
return false;
|
|
49
60
|
}
|
|
50
|
-
node.marks.forEach(mark => {
|
|
51
|
-
if (!(mark.type.name in syncBlockSchema.marks)) {
|
|
52
|
-
canBeConverted = false;
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
61
|
});
|
|
57
62
|
if (!canBeConverted) {
|
|
58
63
|
return false;
|
|
59
64
|
}
|
|
65
|
+
contentToInclude = removeBreakoutMarks(contentToInclude);
|
|
60
66
|
return {
|
|
61
67
|
contentToInclude,
|
|
62
68
|
from,
|
|
63
69
|
to
|
|
64
70
|
};
|
|
71
|
+
};
|
|
72
|
+
const removeBreakoutMarks = content => {
|
|
73
|
+
const nodes = [];
|
|
74
|
+
|
|
75
|
+
// we only need to recurse at the top level, because breakout has to be on a top level
|
|
76
|
+
content.forEach(node => {
|
|
77
|
+
const filteredMarks = node.marks.filter(mark => mark.type.name !== 'breakout');
|
|
78
|
+
const newNode = node.type.create(node.attrs, node.content, filteredMarks);
|
|
79
|
+
nodes.push(newNode);
|
|
80
|
+
});
|
|
81
|
+
return Fragment.from(nodes);
|
|
65
82
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { useIntl } from 'react-intl-next';
|
|
3
|
+
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
3
4
|
import { blockMenuMessages } from '@atlaskit/editor-common/messages';
|
|
4
5
|
import { SyncBlocksIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
|
|
5
6
|
import Lozenge from '@atlaskit/lozenge';
|
|
@@ -8,13 +9,22 @@ import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
|
|
|
8
9
|
export const CreateSyncedBlockDropdownItem = ({
|
|
9
10
|
api
|
|
10
11
|
}) => {
|
|
11
|
-
var _api$selection, _api$selection$shared, _api$selection$shared2;
|
|
12
12
|
const {
|
|
13
13
|
formatMessage
|
|
14
14
|
} = useIntl();
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const {
|
|
16
|
+
selection,
|
|
17
|
+
activeNode
|
|
18
|
+
} = useSharedPluginStateWithSelector(api, ['selection', 'blockControls'], states => {
|
|
19
|
+
var _states$selectionStat, _states$blockControls;
|
|
20
|
+
return {
|
|
21
|
+
selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
|
|
22
|
+
activeNode: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.activeNode
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
const isNested = activeNode && activeNode.rootPos !== activeNode.pos;
|
|
26
|
+
const canBeConverted = useMemo(() => selection && canBeConvertedToSyncBlock(selection), [selection]);
|
|
27
|
+
if (isNested || !canBeConverted) {
|
|
18
28
|
return null;
|
|
19
29
|
}
|
|
20
30
|
const onClick = () => {
|
|
@@ -32,7 +42,7 @@ export const CreateSyncedBlockDropdownItem = ({
|
|
|
32
42
|
}, /*#__PURE__*/React.createElement(Flex, {
|
|
33
43
|
alignItems: "center",
|
|
34
44
|
gap: "space.050"
|
|
35
|
-
}, /*#__PURE__*/React.createElement(Text, null,
|
|
45
|
+
}, /*#__PURE__*/React.createElement(Text, null, formatMessage(blockMenuMessages.createSyncedBlock)), /*#__PURE__*/React.createElement(Lozenge, {
|
|
36
46
|
appearance: "new"
|
|
37
47
|
}, formatMessage(blockMenuMessages.newLozenge))));
|
|
38
48
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
|
|
2
2
|
import { copyDomNode, toDOM } from '@atlaskit/editor-common/copy-button';
|
|
3
|
+
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
3
4
|
import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode } from '@atlaskit/editor-prosemirror/utils';
|
|
4
5
|
import { canBeConvertedToSyncBlock, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
|
|
5
6
|
export var createSyncedBlock = function createSyncedBlock(_ref) {
|
|
@@ -42,7 +43,10 @@ export var createSyncedBlock = function createSyncedBlock(_ref) {
|
|
|
42
43
|
// Save the new node with empty content to backend
|
|
43
44
|
// This is so that the node can be copied and referenced without the source being saved/published
|
|
44
45
|
syncBlockStore.createBodiedSyncBlockNode(_attrs);
|
|
45
|
-
tr.replaceWith(conversionInfo.from - 1, conversionInfo.to, _newBodiedSyncBlockNode).scrollIntoView();
|
|
46
|
+
tr.replaceWith(conversionInfo.from > 0 ? conversionInfo.from - 1 : 0, conversionInfo.to, _newBodiedSyncBlockNode).scrollIntoView();
|
|
47
|
+
|
|
48
|
+
// set selection to the end of the previous selection + 1 for the position taken up by the start of the new synced block
|
|
49
|
+
tr.setSelection(TextSelection.create(tr.doc, conversionInfo.to + 1));
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
// This transaction will be intercepted in filterTransaction and dispatched when saving to backend succeeds
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
2
3
|
import { findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
3
|
-
import { getDefaultSyncBlockSchema } from '@atlaskit/editor-synced-block-provider';
|
|
4
4
|
import { CellSelection, findTable } from '@atlaskit/editor-tables';
|
|
5
5
|
export var findSyncBlock = function findSyncBlock(state, selection) {
|
|
6
6
|
var syncBlock = state.schema.nodes.syncBlock;
|
|
@@ -16,11 +16,22 @@ export var findSyncBlockOrBodiedSyncBlock = function findSyncBlockOrBodiedSyncBl
|
|
|
16
16
|
export var isBodiedSyncBlockNode = function isBodiedSyncBlockNode(node, bodiedSyncBlock) {
|
|
17
17
|
return node.type === bodiedSyncBlock;
|
|
18
18
|
};
|
|
19
|
+
var UNSUPPORTED_NODE_TYPES = new Set(['inlineExtension', 'extension', 'bodiedExtension', 'syncBlock', 'bodiedSyncBlock']);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Checks whether the selection can be converted to sync block
|
|
23
|
+
*
|
|
24
|
+
* @param selection - the current editor selection to validate for sync block conversion
|
|
25
|
+
* @returns A fragment containing the content to include in the synced block,
|
|
26
|
+
* stripping out unsupported marks (breakout on codeblock/expand/layout), as well as from and to positions,
|
|
27
|
+
* or false if conversion is not possible
|
|
28
|
+
*/
|
|
19
29
|
export var canBeConvertedToSyncBlock = function canBeConvertedToSyncBlock(selection) {
|
|
30
|
+
var schema = selection.$from.doc.type.schema;
|
|
31
|
+
var nodes = schema.nodes;
|
|
20
32
|
var from = selection.from;
|
|
21
33
|
var to = selection.to;
|
|
22
|
-
var
|
|
23
|
-
var contentToInclude;
|
|
34
|
+
var contentToInclude = selection.content().content;
|
|
24
35
|
if (selection instanceof CellSelection) {
|
|
25
36
|
var table = findTable(selection);
|
|
26
37
|
if (!table) {
|
|
@@ -29,35 +40,41 @@ export var canBeConvertedToSyncBlock = function canBeConvertedToSyncBlock(select
|
|
|
29
40
|
contentToInclude = Fragment.from([table.node]);
|
|
30
41
|
from = table.pos;
|
|
31
42
|
to = table.pos + table.node.nodeSize;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return false;
|
|
43
|
+
} else if (selection instanceof TextSelection) {
|
|
44
|
+
var trueParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList, nodes.blockquote])(selection);
|
|
45
|
+
if (trueParent) {
|
|
46
|
+
contentToInclude = Fragment.from([trueParent.node]);
|
|
47
|
+
from = trueParent.pos;
|
|
48
|
+
to = trueParent.pos + trueParent.node.nodeSize;
|
|
49
|
+
}
|
|
40
50
|
}
|
|
41
|
-
var syncBlockSchema = getDefaultSyncBlockSchema();
|
|
42
51
|
var canBeConverted = true;
|
|
43
52
|
selection.$from.doc.nodesBetween(from, to, function (node) {
|
|
44
|
-
if (
|
|
53
|
+
if (UNSUPPORTED_NODE_TYPES.has(node.type.name)) {
|
|
45
54
|
canBeConverted = false;
|
|
46
55
|
return false;
|
|
47
56
|
}
|
|
48
|
-
node.marks.forEach(function (mark) {
|
|
49
|
-
if (!(mark.type.name in syncBlockSchema.marks)) {
|
|
50
|
-
canBeConverted = false;
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
57
|
});
|
|
55
58
|
if (!canBeConverted) {
|
|
56
59
|
return false;
|
|
57
60
|
}
|
|
61
|
+
contentToInclude = removeBreakoutMarks(contentToInclude);
|
|
58
62
|
return {
|
|
59
63
|
contentToInclude: contentToInclude,
|
|
60
64
|
from: from,
|
|
61
65
|
to: to
|
|
62
66
|
};
|
|
67
|
+
};
|
|
68
|
+
var removeBreakoutMarks = function removeBreakoutMarks(content) {
|
|
69
|
+
var nodes = [];
|
|
70
|
+
|
|
71
|
+
// we only need to recurse at the top level, because breakout has to be on a top level
|
|
72
|
+
content.forEach(function (node) {
|
|
73
|
+
var filteredMarks = node.marks.filter(function (mark) {
|
|
74
|
+
return mark.type.name !== 'breakout';
|
|
75
|
+
});
|
|
76
|
+
var newNode = node.type.create(node.attrs, node.content, filteredMarks);
|
|
77
|
+
nodes.push(newNode);
|
|
78
|
+
});
|
|
79
|
+
return Fragment.from(nodes);
|
|
63
80
|
};
|
|
@@ -1,18 +1,29 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { useIntl } from 'react-intl-next';
|
|
3
|
+
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
3
4
|
import { blockMenuMessages } from '@atlaskit/editor-common/messages';
|
|
4
5
|
import { SyncBlocksIcon, ToolbarDropdownItem } from '@atlaskit/editor-toolbar';
|
|
5
6
|
import Lozenge from '@atlaskit/lozenge';
|
|
6
7
|
import { Flex, Text } from '@atlaskit/primitives/compiled';
|
|
7
8
|
import { canBeConvertedToSyncBlock } from '../pm-plugins/utils/utils';
|
|
8
9
|
export var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref) {
|
|
9
|
-
var _api$selection;
|
|
10
10
|
var api = _ref.api;
|
|
11
11
|
var _useIntl = useIntl(),
|
|
12
12
|
formatMessage = _useIntl.formatMessage;
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['selection', 'blockControls'], function (states) {
|
|
14
|
+
var _states$selectionStat, _states$blockControls;
|
|
15
|
+
return {
|
|
16
|
+
selection: (_states$selectionStat = states.selectionState) === null || _states$selectionStat === void 0 ? void 0 : _states$selectionStat.selection,
|
|
17
|
+
activeNode: (_states$blockControls = states.blockControlsState) === null || _states$blockControls === void 0 ? void 0 : _states$blockControls.activeNode
|
|
18
|
+
};
|
|
19
|
+
}),
|
|
20
|
+
selection = _useSharedPluginState.selection,
|
|
21
|
+
activeNode = _useSharedPluginState.activeNode;
|
|
22
|
+
var isNested = activeNode && activeNode.rootPos !== activeNode.pos;
|
|
23
|
+
var canBeConverted = useMemo(function () {
|
|
24
|
+
return selection && canBeConvertedToSyncBlock(selection);
|
|
25
|
+
}, [selection]);
|
|
26
|
+
if (isNested || !canBeConverted) {
|
|
16
27
|
return null;
|
|
17
28
|
}
|
|
18
29
|
var onClick = function onClick() {
|
|
@@ -30,7 +41,7 @@ export var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownIte
|
|
|
30
41
|
}, /*#__PURE__*/React.createElement(Flex, {
|
|
31
42
|
alignItems: "center",
|
|
32
43
|
gap: "space.050"
|
|
33
|
-
}, /*#__PURE__*/React.createElement(Text, null,
|
|
44
|
+
}, /*#__PURE__*/React.createElement(Text, null, formatMessage(blockMenuMessages.createSyncedBlock)), /*#__PURE__*/React.createElement(Lozenge, {
|
|
34
45
|
appearance: "new"
|
|
35
46
|
}, formatMessage(blockMenuMessages.newLozenge))));
|
|
36
47
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
2
2
|
import type { NodeType, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
-
import type
|
|
3
|
+
import { type EditorState, type Selection } from '@atlaskit/editor-prosemirror/state';
|
|
4
4
|
import type { ContentNodeWithPos } from '@atlaskit/editor-prosemirror/utils';
|
|
5
5
|
export declare const findSyncBlock: (state: EditorState, selection?: Selection | null) => ContentNodeWithPos | undefined;
|
|
6
6
|
export declare const findBodiedSyncBlock: (state: EditorState, selection?: Selection | null) => ContentNodeWithPos | undefined;
|
|
@@ -11,4 +11,12 @@ export interface SyncBlockConversionInfo {
|
|
|
11
11
|
from: number;
|
|
12
12
|
to: number;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Checks whether the selection can be converted to sync block
|
|
16
|
+
*
|
|
17
|
+
* @param selection - the current editor selection to validate for sync block conversion
|
|
18
|
+
* @returns A fragment containing the content to include in the synced block,
|
|
19
|
+
* stripping out unsupported marks (breakout on codeblock/expand/layout), as well as from and to positions,
|
|
20
|
+
* or false if conversion is not possible
|
|
21
|
+
*/
|
|
14
22
|
export declare const canBeConvertedToSyncBlock: (selection: Selection) => SyncBlockConversionInfo | false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
2
2
|
import type { NodeType, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
-
import type
|
|
3
|
+
import { type EditorState, type Selection } from '@atlaskit/editor-prosemirror/state';
|
|
4
4
|
import type { ContentNodeWithPos } from '@atlaskit/editor-prosemirror/utils';
|
|
5
5
|
export declare const findSyncBlock: (state: EditorState, selection?: Selection | null) => ContentNodeWithPos | undefined;
|
|
6
6
|
export declare const findBodiedSyncBlock: (state: EditorState, selection?: Selection | null) => ContentNodeWithPos | undefined;
|
|
@@ -11,4 +11,12 @@ export interface SyncBlockConversionInfo {
|
|
|
11
11
|
from: number;
|
|
12
12
|
to: number;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Checks whether the selection can be converted to sync block
|
|
16
|
+
*
|
|
17
|
+
* @param selection - the current editor selection to validate for sync block conversion
|
|
18
|
+
* @returns A fragment containing the content to include in the synced block,
|
|
19
|
+
* stripping out unsupported marks (breakout on codeblock/expand/layout), as well as from and to positions,
|
|
20
|
+
* or false if conversion is not possible
|
|
21
|
+
*/
|
|
14
22
|
export declare const canBeConvertedToSyncBlock: (selection: Selection) => SyncBlockConversionInfo | false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-synced-block",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.2",
|
|
4
4
|
"description": "SyncedBlock plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@atlaskit/editor-tables": "^2.9.0",
|
|
43
43
|
"@atlaskit/editor-toolbar": "^0.17.0",
|
|
44
44
|
"@atlaskit/icon": "28.5.4",
|
|
45
|
-
"@atlaskit/icon-lab": "^5.
|
|
45
|
+
"@atlaskit/icon-lab": "^5.12.0",
|
|
46
46
|
"@atlaskit/logo": "^19.9.0",
|
|
47
47
|
"@atlaskit/lozenge": "^13.1.0",
|
|
48
48
|
"@atlaskit/modal-dialog": "^14.6.0",
|