@atlaskit/editor-plugin-list 0.2.0 → 1.0.0
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/.eslintrc.js +14 -0
- package/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/dist/cjs/actions/conversions.js +153 -0
- package/dist/cjs/actions/indent-list-items-selected.js +125 -0
- package/dist/cjs/actions/indent-list.js +49 -0
- package/dist/cjs/actions/join-list-items-forward.js +59 -0
- package/dist/cjs/actions/join-list-items-scenarios/index.js +40 -0
- package/dist/cjs/actions/join-list-items-scenarios/join-list-item-with-paragraph.js +88 -0
- package/dist/cjs/actions/join-list-items-scenarios/join-list-item-with-parent-nested-list.js +85 -0
- package/dist/cjs/actions/join-list-items-scenarios/join-nested-list-with-parent-list-item.js +79 -0
- package/dist/cjs/actions/join-list-items-scenarios/join-paragraph-with-list.js +45 -0
- package/dist/cjs/actions/join-list-items-scenarios/join-sibling-list-items.js +56 -0
- package/dist/cjs/actions/merge-lists.js +27 -0
- package/dist/cjs/actions/outdent-list-items-selected.js +291 -0
- package/dist/cjs/actions/wrap-and-join-lists.js +100 -0
- package/dist/cjs/commands/indent-list.js +71 -0
- package/dist/cjs/commands/index.js +350 -0
- package/dist/cjs/commands/isFirstChildOfParent.js +12 -0
- package/dist/cjs/commands/join-list-item-forward.js +61 -0
- package/dist/cjs/commands/listBackspace.js +284 -0
- package/dist/cjs/commands/outdent-list.js +70 -0
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/messages.js +37 -0
- package/dist/cjs/plugin.js +133 -0
- package/dist/cjs/pm-plugins/input-rules/create-list-input-rule.js +63 -0
- package/dist/cjs/pm-plugins/input-rules/index.js +38 -0
- package/dist/cjs/pm-plugins/input-rules/wrapping-join-rule.js +60 -0
- package/dist/cjs/pm-plugins/keymap.js +27 -0
- package/dist/cjs/pm-plugins/main.js +166 -0
- package/dist/cjs/transforms.js +99 -0
- package/dist/cjs/types.js +4 -1
- package/dist/cjs/utils/analytics.js +22 -0
- package/dist/cjs/utils/find.js +68 -0
- package/dist/cjs/utils/indentation.js +22 -0
- package/dist/cjs/utils/mark.js +40 -0
- package/dist/cjs/utils/node.js +16 -0
- package/dist/cjs/utils/selection.js +95 -0
- package/dist/es2019/actions/conversions.js +160 -0
- package/dist/es2019/actions/indent-list-items-selected.js +124 -0
- package/dist/es2019/actions/indent-list.js +44 -0
- package/dist/es2019/actions/join-list-items-forward.js +54 -0
- package/dist/es2019/actions/join-list-items-scenarios/index.js +5 -0
- package/dist/es2019/actions/join-list-items-scenarios/join-list-item-with-paragraph.js +74 -0
- package/dist/es2019/actions/join-list-items-scenarios/join-list-item-with-parent-nested-list.js +77 -0
- package/dist/es2019/actions/join-list-items-scenarios/join-nested-list-with-parent-list-item.js +71 -0
- package/dist/es2019/actions/join-list-items-scenarios/join-paragraph-with-list.js +37 -0
- package/dist/es2019/actions/join-list-items-scenarios/join-sibling-list-items.js +48 -0
- package/dist/es2019/actions/merge-lists.js +24 -0
- package/dist/es2019/actions/outdent-list-items-selected.js +295 -0
- package/dist/es2019/actions/wrap-and-join-lists.js +93 -0
- package/dist/es2019/commands/indent-list.js +62 -0
- package/dist/es2019/commands/index.js +326 -0
- package/dist/es2019/commands/isFirstChildOfParent.js +7 -0
- package/dist/es2019/commands/join-list-item-forward.js +53 -0
- package/dist/es2019/commands/listBackspace.js +276 -0
- package/dist/es2019/commands/outdent-list.js +60 -0
- package/dist/es2019/index.js +1 -1
- package/dist/es2019/messages.js +29 -0
- package/dist/es2019/plugin.js +121 -0
- package/dist/es2019/pm-plugins/input-rules/create-list-input-rule.js +56 -0
- package/dist/es2019/pm-plugins/input-rules/index.js +35 -0
- package/dist/es2019/pm-plugins/input-rules/wrapping-join-rule.js +55 -0
- package/dist/es2019/pm-plugins/keymap.js +19 -0
- package/dist/es2019/pm-plugins/main.js +156 -0
- package/dist/es2019/transforms.js +101 -0
- package/dist/es2019/types.js +1 -1
- package/dist/es2019/utils/analytics.js +12 -0
- package/dist/es2019/utils/find.js +61 -0
- package/dist/es2019/utils/indentation.js +15 -0
- package/dist/es2019/utils/mark.js +30 -0
- package/dist/es2019/utils/node.js +12 -0
- package/dist/es2019/utils/selection.js +96 -0
- package/dist/esm/actions/conversions.js +147 -0
- package/dist/esm/actions/indent-list-items-selected.js +117 -0
- package/dist/esm/actions/indent-list.js +43 -0
- package/dist/esm/actions/join-list-items-forward.js +52 -0
- package/dist/esm/actions/join-list-items-scenarios/index.js +5 -0
- package/dist/esm/actions/join-list-items-scenarios/join-list-item-with-paragraph.js +81 -0
- package/dist/esm/actions/join-list-items-scenarios/join-list-item-with-parent-nested-list.js +78 -0
- package/dist/esm/actions/join-list-items-scenarios/join-nested-list-with-parent-list-item.js +72 -0
- package/dist/esm/actions/join-list-items-scenarios/join-paragraph-with-list.js +38 -0
- package/dist/esm/actions/join-list-items-scenarios/join-sibling-list-items.js +49 -0
- package/dist/esm/actions/merge-lists.js +21 -0
- package/dist/esm/actions/outdent-list-items-selected.js +283 -0
- package/dist/esm/actions/wrap-and-join-lists.js +94 -0
- package/dist/esm/commands/indent-list.js +63 -0
- package/dist/esm/commands/index.js +324 -0
- package/dist/esm/commands/isFirstChildOfParent.js +5 -0
- package/dist/esm/commands/join-list-item-forward.js +53 -0
- package/dist/esm/commands/listBackspace.js +275 -0
- package/dist/esm/commands/outdent-list.js +62 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/messages.js +29 -0
- package/dist/esm/plugin.js +126 -0
- package/dist/esm/pm-plugins/input-rules/create-list-input-rule.js +57 -0
- package/dist/esm/pm-plugins/input-rules/index.js +32 -0
- package/dist/esm/pm-plugins/input-rules/wrapping-join-rule.js +54 -0
- package/dist/esm/pm-plugins/keymap.js +19 -0
- package/dist/esm/pm-plugins/main.js +156 -0
- package/dist/esm/transforms.js +91 -0
- package/dist/esm/types.js +1 -1
- package/dist/esm/utils/analytics.js +12 -0
- package/dist/esm/utils/find.js +59 -0
- package/dist/esm/utils/indentation.js +15 -0
- package/dist/esm/utils/mark.js +33 -0
- package/dist/esm/utils/node.js +10 -0
- package/dist/esm/utils/selection.js +81 -0
- package/dist/types/actions/conversions.d.ts +6 -0
- package/dist/types/actions/indent-list-items-selected.d.ts +2 -0
- package/dist/types/actions/indent-list.d.ts +2 -0
- package/dist/types/actions/join-list-items-forward.d.ts +13 -0
- package/dist/types/actions/join-list-items-scenarios/index.d.ts +5 -0
- package/dist/types/actions/join-list-items-scenarios/join-list-item-with-paragraph.d.ts +9 -0
- package/dist/types/actions/join-list-items-scenarios/join-list-item-with-parent-nested-list.d.ts +9 -0
- package/dist/types/actions/join-list-items-scenarios/join-nested-list-with-parent-list-item.d.ts +9 -0
- package/dist/types/actions/join-list-items-scenarios/join-paragraph-with-list.d.ts +9 -0
- package/dist/types/actions/join-list-items-scenarios/join-sibling-list-items.d.ts +9 -0
- package/dist/types/actions/merge-lists.d.ts +7 -0
- package/dist/types/actions/outdent-list-items-selected.d.ts +3 -0
- package/dist/types/actions/wrap-and-join-lists.d.ts +17 -0
- package/dist/types/commands/indent-list.d.ts +6 -0
- package/dist/types/commands/index.d.ts +16 -0
- package/dist/types/commands/isFirstChildOfParent.d.ts +2 -0
- package/dist/types/commands/join-list-item-forward.d.ts +3 -0
- package/dist/types/commands/listBackspace.d.ts +10 -0
- package/dist/types/commands/outdent-list.d.ts +6 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/messages.d.ts +27 -0
- package/dist/types/plugin.d.ts +2 -0
- package/dist/types/pm-plugins/input-rules/create-list-input-rule.d.ts +11 -0
- package/dist/types/pm-plugins/input-rules/index.d.ts +5 -0
- package/dist/types/pm-plugins/input-rules/wrapping-join-rule.d.ts +13 -0
- package/dist/types/pm-plugins/keymap.d.ts +5 -0
- package/dist/types/pm-plugins/main.d.ts +11 -0
- package/dist/types/transforms.d.ts +4 -0
- package/dist/types/types.d.ts +4 -6
- package/dist/types/utils/analytics.d.ts +5 -0
- package/dist/types/utils/find.d.ts +10 -0
- package/dist/types/utils/indentation.d.ts +2 -0
- package/dist/types/utils/mark.d.ts +8 -0
- package/dist/types/utils/node.d.ts +2 -0
- package/dist/types/utils/selection.d.ts +14 -0
- package/dist/types-ts4.5/actions/conversions.d.ts +6 -0
- package/dist/types-ts4.5/actions/indent-list-items-selected.d.ts +2 -0
- package/dist/types-ts4.5/actions/indent-list.d.ts +2 -0
- package/dist/types-ts4.5/actions/join-list-items-forward.d.ts +16 -0
- package/dist/types-ts4.5/actions/join-list-items-scenarios/index.d.ts +5 -0
- package/dist/types-ts4.5/actions/join-list-items-scenarios/join-list-item-with-paragraph.d.ts +9 -0
- package/dist/types-ts4.5/actions/join-list-items-scenarios/join-list-item-with-parent-nested-list.d.ts +9 -0
- package/dist/types-ts4.5/actions/join-list-items-scenarios/join-nested-list-with-parent-list-item.d.ts +9 -0
- package/dist/types-ts4.5/actions/join-list-items-scenarios/join-paragraph-with-list.d.ts +9 -0
- package/dist/types-ts4.5/actions/join-list-items-scenarios/join-sibling-list-items.d.ts +9 -0
- package/dist/types-ts4.5/actions/merge-lists.d.ts +7 -0
- package/dist/types-ts4.5/actions/outdent-list-items-selected.d.ts +3 -0
- package/dist/types-ts4.5/actions/wrap-and-join-lists.d.ts +17 -0
- package/dist/types-ts4.5/commands/indent-list.d.ts +6 -0
- package/dist/types-ts4.5/commands/index.d.ts +16 -0
- package/dist/types-ts4.5/commands/isFirstChildOfParent.d.ts +2 -0
- package/dist/types-ts4.5/commands/join-list-item-forward.d.ts +3 -0
- package/dist/types-ts4.5/commands/listBackspace.d.ts +13 -0
- package/dist/types-ts4.5/commands/outdent-list.d.ts +6 -0
- package/dist/types-ts4.5/index.d.ts +2 -1
- package/dist/types-ts4.5/messages.d.ts +27 -0
- package/dist/types-ts4.5/plugin.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/input-rules/create-list-input-rule.d.ts +11 -0
- package/dist/types-ts4.5/pm-plugins/input-rules/index.d.ts +5 -0
- package/dist/types-ts4.5/pm-plugins/input-rules/wrapping-join-rule.d.ts +13 -0
- package/dist/types-ts4.5/pm-plugins/keymap.d.ts +5 -0
- package/dist/types-ts4.5/pm-plugins/main.d.ts +11 -0
- package/dist/types-ts4.5/transforms.d.ts +4 -0
- package/dist/types-ts4.5/types.d.ts +4 -6
- package/dist/types-ts4.5/utils/analytics.d.ts +5 -0
- package/dist/types-ts4.5/utils/find.d.ts +10 -0
- package/dist/types-ts4.5/utils/indentation.d.ts +2 -0
- package/dist/types-ts4.5/utils/mark.d.ts +8 -0
- package/dist/types-ts4.5/utils/node.d.ts +2 -0
- package/dist/types-ts4.5/utils/selection.d.ts +14 -0
- package/package.json +8 -5
- package/report.api.md +6 -2
- package/tmp/api-report-tmp.d.ts +4 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { joinSiblingLists } from '@atlaskit/editor-common/lists';
|
|
2
|
+
import { GapCursorSelection } from '@atlaskit/editor-common/selection';
|
|
3
|
+
import { isEmptyParagraph, isListNode } from '@atlaskit/editor-common/utils';
|
|
4
|
+
import { NodeRange } from '@atlaskit/editor-prosemirror/model';
|
|
5
|
+
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
6
|
+
import { findWrapping } from '@atlaskit/editor-prosemirror/transform';
|
|
7
|
+
import { findParentNodeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
|
|
8
|
+
import { findFirstParentListNode } from '../utils/find';
|
|
9
|
+
export function convertListType({
|
|
10
|
+
tr,
|
|
11
|
+
nextListNodeType
|
|
12
|
+
}) {
|
|
13
|
+
const {
|
|
14
|
+
doc,
|
|
15
|
+
selection: {
|
|
16
|
+
$from,
|
|
17
|
+
$to
|
|
18
|
+
}
|
|
19
|
+
} = tr;
|
|
20
|
+
let listRange;
|
|
21
|
+
if (tr.selection instanceof GapCursorSelection) {
|
|
22
|
+
var _$from$nodeAfter;
|
|
23
|
+
const nodeSize = ((_$from$nodeAfter = $from.nodeAfter) === null || _$from$nodeAfter === void 0 ? void 0 : _$from$nodeAfter.nodeSize) || 1;
|
|
24
|
+
listRange = $from.blockRange($from.doc.resolve($from.pos + nodeSize));
|
|
25
|
+
} else {
|
|
26
|
+
listRange = $from.blockRange($to, isListNode);
|
|
27
|
+
}
|
|
28
|
+
if (listRange) {
|
|
29
|
+
return convertSelectedList({
|
|
30
|
+
tr,
|
|
31
|
+
nextListNodeType
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
let nodeRangeAroundList = $from.blockRange($to);
|
|
35
|
+
if (!nodeRangeAroundList) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const parentNode = nodeRangeAroundList.parent;
|
|
39
|
+
const {
|
|
40
|
+
startIndex,
|
|
41
|
+
endIndex,
|
|
42
|
+
depth
|
|
43
|
+
} = nodeRangeAroundList;
|
|
44
|
+
|
|
45
|
+
// Checking for invalid nodes to prevent conversion
|
|
46
|
+
// eg. a panel cannot be wrapped in a list so return
|
|
47
|
+
// It will skip this check if the selection begins within a list
|
|
48
|
+
// This is to match the behaviour of the toolbar buttons being disabled
|
|
49
|
+
if (!findFirstParentListNode($from)) {
|
|
50
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
51
|
+
const position = nodeRangeAroundList.$from.posAtIndex(i, depth);
|
|
52
|
+
const resolvedPosition = doc.resolve(position);
|
|
53
|
+
const currentChild = parentNode.child(i);
|
|
54
|
+
const currentNodeRange = resolvedPosition.blockRange(tr.doc.resolve(position + currentChild.nodeSize));
|
|
55
|
+
if (currentNodeRange && !isListNode(currentChild) && !findWrapping(currentNodeRange, nextListNodeType)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Checking for any non list nodes and wrapping them in a list
|
|
62
|
+
// so they can be converted
|
|
63
|
+
tr.doc.nodesBetween(nodeRangeAroundList.start, nodeRangeAroundList.end, (node, pos) => {
|
|
64
|
+
// Skip over any nodes that are part of a list
|
|
65
|
+
if (findFirstParentListNode(tr.doc.resolve(tr.mapping.map(pos)))) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The following applies to suitable nodes that are not within a list
|
|
70
|
+
const currentNodeNotWrappedInList = node;
|
|
71
|
+
const isNotAnEmptyParagraphAndIsParagraphOrLeafNode = !isEmptyParagraph(currentNodeNotWrappedInList) && (!node.type.isBlock || node.type.name === 'paragraph');
|
|
72
|
+
if (isNotAnEmptyParagraphAndIsParagraphOrLeafNode && nodeRangeAroundList) {
|
|
73
|
+
const remainingNodeRange = new NodeRange(tr.doc.resolve(tr.mapping.map(pos)), tr.doc.resolve(tr.mapping.map(pos) + currentNodeNotWrappedInList.nodeSize), nodeRangeAroundList.depth);
|
|
74
|
+
convertAroundList({
|
|
75
|
+
tr,
|
|
76
|
+
nextListNodeType,
|
|
77
|
+
nodeRange: remainingNodeRange
|
|
78
|
+
});
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
convertSelectedList({
|
|
83
|
+
tr,
|
|
84
|
+
nextListNodeType
|
|
85
|
+
});
|
|
86
|
+
if (tr.docChanged) {
|
|
87
|
+
joinSiblingLists({
|
|
88
|
+
tr,
|
|
89
|
+
forceListType: nextListNodeType
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const convertSelectedList = ({
|
|
94
|
+
tr,
|
|
95
|
+
nextListNodeType
|
|
96
|
+
}) => {
|
|
97
|
+
const {
|
|
98
|
+
selection,
|
|
99
|
+
selection: {
|
|
100
|
+
from,
|
|
101
|
+
to
|
|
102
|
+
}
|
|
103
|
+
} = tr;
|
|
104
|
+
const {
|
|
105
|
+
codeBlock
|
|
106
|
+
} = tr.doc.type.schema.nodes;
|
|
107
|
+
// get the positions of all the leaf nodes within the selection
|
|
108
|
+
const nodePositions = [];
|
|
109
|
+
if (selection instanceof TextSelection && selection.$cursor || selection instanceof GapCursorSelection) {
|
|
110
|
+
nodePositions.push(from);
|
|
111
|
+
} else {
|
|
112
|
+
// nodesBetween doesn't return leaf nodes that are outside of from and to
|
|
113
|
+
tr.doc.nodesBetween(from, to, (node, pos) => {
|
|
114
|
+
// isLeaf is false for empty codeBlock so adding additional check for childCount
|
|
115
|
+
if (!node.isLeaf && !(node.type === codeBlock && node.childCount === 0)) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
nodePositions.push(pos);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// use those positions to get the closest parent list nodes
|
|
123
|
+
nodePositions.reduce((acc, pos) => {
|
|
124
|
+
const closestParentListNode = findParentNodeClosestToPos(tr.doc.resolve(pos), isListNode);
|
|
125
|
+
if (!closestParentListNode) {
|
|
126
|
+
return acc;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// don't add duplicates if the parent has already been added into the array
|
|
130
|
+
const existingParent = acc.find(node => {
|
|
131
|
+
return node.pos === closestParentListNode.pos && node.start === closestParentListNode.start && node.depth === closestParentListNode.depth;
|
|
132
|
+
});
|
|
133
|
+
if (!existingParent) {
|
|
134
|
+
acc.push(closestParentListNode);
|
|
135
|
+
}
|
|
136
|
+
return acc;
|
|
137
|
+
}, []).forEach(item => {
|
|
138
|
+
tr.setNodeMarkup(item.pos, nextListNodeType);
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
const convertAroundList = ({
|
|
142
|
+
tr,
|
|
143
|
+
nextListNodeType,
|
|
144
|
+
nodeRange
|
|
145
|
+
}) => {
|
|
146
|
+
for (let i = nodeRange.endIndex - 1; i >= nodeRange.startIndex; i--) {
|
|
147
|
+
// @ts-ignore posAtIndex is a public API but has no type yet
|
|
148
|
+
const position = nodeRange.$from.posAtIndex(i, nodeRange.depth);
|
|
149
|
+
const resolvedPos = tr.doc.resolve(position + 1);
|
|
150
|
+
const range = resolvedPos.blockRange(resolvedPos);
|
|
151
|
+
if (!range) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const wrappings = findWrapping(range, nextListNodeType);
|
|
155
|
+
if (!range || !wrappings) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
tr.wrap(range, wrappings);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { normalizeListItemsSelection } from '@atlaskit/editor-common/lists';
|
|
2
|
+
import { GapCursorSelection } from '@atlaskit/editor-common/selection';
|
|
3
|
+
import { isListItemNode, isListNode } from '@atlaskit/editor-common/utils';
|
|
4
|
+
import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
5
|
+
import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
6
|
+
import { findFirstParentListItemNode } from '../utils/find';
|
|
7
|
+
export const indentListItemsSelected = tr => {
|
|
8
|
+
const originalSelection = tr.selection;
|
|
9
|
+
const normalizedSelection = normalizeListItemsSelection({
|
|
10
|
+
selection: originalSelection,
|
|
11
|
+
doc: tr.doc
|
|
12
|
+
});
|
|
13
|
+
const {
|
|
14
|
+
$from,
|
|
15
|
+
$to
|
|
16
|
+
} = normalizedSelection;
|
|
17
|
+
const range = calculateRange({
|
|
18
|
+
selection: normalizedSelection
|
|
19
|
+
});
|
|
20
|
+
if (!range) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const listItemsSelected = {
|
|
24
|
+
from: findFirstParentListItemNode($from),
|
|
25
|
+
to: findFirstParentListItemNode($to)
|
|
26
|
+
};
|
|
27
|
+
if (listItemsSelected.from === null || listItemsSelected.to === null) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const resolvedPos = tr.doc.resolve(listItemsSelected.from.pos);
|
|
31
|
+
const listItemIndex = resolvedPos.index();
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
const positionListItemPosition = resolvedPos.posAtIndex(listItemIndex - 1);
|
|
34
|
+
const previousListItem = tr.doc.nodeAt(positionListItemPosition);
|
|
35
|
+
if (!previousListItem || !isListItemNode(previousListItem)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
if (isListItemNode(previousListItem) && listItemIndex === 0) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const listItemSelectedCommonParent = range.parent;
|
|
42
|
+
const previousNestedList = isListNode(previousListItem.lastChild) ? previousListItem.lastChild : null;
|
|
43
|
+
const listNodeType = previousNestedList ? previousNestedList.type : listItemSelectedCommonParent.type;
|
|
44
|
+
const nestedList = listItemsSelected.to.node.lastChild;
|
|
45
|
+
const nestedItemsOffset = nestedList && isListNode(nestedList) ? nestedList.nodeSize : 0;
|
|
46
|
+
const from = listItemsSelected.from.pos;
|
|
47
|
+
const to = listItemsSelected.to.pos + listItemsSelected.to.node.nodeSize - nestedItemsOffset;
|
|
48
|
+
const [sliceSelected, nestedListItemsLeftover] = createIndentedListItemsSlice({
|
|
49
|
+
tr,
|
|
50
|
+
listNodeType,
|
|
51
|
+
range,
|
|
52
|
+
from,
|
|
53
|
+
to
|
|
54
|
+
});
|
|
55
|
+
const hasPreviousNestedList = Boolean(previousNestedList);
|
|
56
|
+
const start = from - 1;
|
|
57
|
+
tr.replaceRange(hasPreviousNestedList ? start - 1 : start, range.end, sliceSelected);
|
|
58
|
+
const leftoverContentPosition = tr.mapping.map(to) - 2;
|
|
59
|
+
if (nestedListItemsLeftover.openStart === 0) {
|
|
60
|
+
tr.insert(leftoverContentPosition, nestedListItemsLeftover.content);
|
|
61
|
+
} else {
|
|
62
|
+
tr.replace(leftoverContentPosition - nestedListItemsLeftover.openStart, leftoverContentPosition - nestedListItemsLeftover.openStart, nestedListItemsLeftover);
|
|
63
|
+
}
|
|
64
|
+
const nextSelection = calculateNewSelection({
|
|
65
|
+
originalSelection,
|
|
66
|
+
normalizedSelection,
|
|
67
|
+
tr,
|
|
68
|
+
hasPreviousNestedList
|
|
69
|
+
});
|
|
70
|
+
tr.setSelection(nextSelection);
|
|
71
|
+
};
|
|
72
|
+
const calculateRange = ({
|
|
73
|
+
selection
|
|
74
|
+
}) => {
|
|
75
|
+
const {
|
|
76
|
+
$from,
|
|
77
|
+
$to
|
|
78
|
+
} = selection;
|
|
79
|
+
const range = $from.blockRange($to, isListNode);
|
|
80
|
+
if (!range) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return range;
|
|
84
|
+
};
|
|
85
|
+
const calculateNewSelection = ({
|
|
86
|
+
tr,
|
|
87
|
+
normalizedSelection,
|
|
88
|
+
originalSelection,
|
|
89
|
+
hasPreviousNestedList
|
|
90
|
+
}) => {
|
|
91
|
+
const offset = hasPreviousNestedList ? 2 : 0;
|
|
92
|
+
const {
|
|
93
|
+
$from,
|
|
94
|
+
$to
|
|
95
|
+
} = normalizedSelection;
|
|
96
|
+
if (normalizedSelection instanceof GapCursorSelection) {
|
|
97
|
+
const nextSelectionFrom = tr.doc.resolve($from.pos - offset);
|
|
98
|
+
return new GapCursorSelection(nextSelectionFrom, normalizedSelection.side);
|
|
99
|
+
}
|
|
100
|
+
if (originalSelection instanceof NodeSelection) {
|
|
101
|
+
return NodeSelection.create(tr.doc, $from.pos - offset);
|
|
102
|
+
}
|
|
103
|
+
const {
|
|
104
|
+
$from: nextSelectionFrom
|
|
105
|
+
} = Selection.near(tr.doc.resolve($from.pos - offset));
|
|
106
|
+
const {
|
|
107
|
+
$to: nextSelectionTo
|
|
108
|
+
} = Selection.near(tr.doc.resolve($to.pos - offset), -1);
|
|
109
|
+
return new TextSelection(nextSelectionFrom, nextSelectionTo);
|
|
110
|
+
};
|
|
111
|
+
const createIndentedListItemsSlice = ({
|
|
112
|
+
tr,
|
|
113
|
+
from,
|
|
114
|
+
to,
|
|
115
|
+
listNodeType,
|
|
116
|
+
range
|
|
117
|
+
}) => {
|
|
118
|
+
const listItemsSlice = tr.doc.slice(from, to - 2);
|
|
119
|
+
const listFragment = Fragment.from(listNodeType.create(null, listItemsSlice.content));
|
|
120
|
+
const nonSelectedListItemsSlice = tr.doc.slice(to, range.end - 2);
|
|
121
|
+
const openStart = tr.doc.slice(from - 1, range.end).openStart;
|
|
122
|
+
const slice = new Slice(listFragment, openStart, 0);
|
|
123
|
+
return [slice, nonSelectedListItemsSlice];
|
|
124
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
2
|
+
import { ReplaceAroundStep } from '@atlaskit/editor-prosemirror/transform';
|
|
3
|
+
|
|
4
|
+
// adapted from https://github.com/ProseMirror/prosemirror-schema-list/blob/master/src/schema-list.js#L206:L231
|
|
5
|
+
export const indentList = tr => {
|
|
6
|
+
const {
|
|
7
|
+
$from,
|
|
8
|
+
$to
|
|
9
|
+
} = tr.selection;
|
|
10
|
+
const {
|
|
11
|
+
listItem
|
|
12
|
+
} = tr.doc.type.schema.nodes;
|
|
13
|
+
const range = $from.blockRange($to, node => !!node.childCount && !!node.firstChild && node.firstChild.type === listItem);
|
|
14
|
+
if (!range) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// get the index of the selected list item in the list it is part of
|
|
19
|
+
const startIndex = range.startIndex;
|
|
20
|
+
if (startIndex === 0) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// get the parent list of the list item(s) in the selected range
|
|
25
|
+
const parent = range.parent;
|
|
26
|
+
|
|
27
|
+
// get the list immediately before the selection start
|
|
28
|
+
const previousListItem = parent.child(startIndex - 1);
|
|
29
|
+
if (previousListItem.type !== listItem) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// if that list was nested, join the selected list items into the same
|
|
34
|
+
// nested list; if not, create a new child list of the same type and
|
|
35
|
+
// nest it under the current level
|
|
36
|
+
const isPreviousListNested = previousListItem.lastChild && ['bulletList', 'orderedList'].includes(previousListItem.lastChild.type.name);
|
|
37
|
+
const inner = Fragment.from(isPreviousListNested ? listItem.create() : undefined);
|
|
38
|
+
const nextListNodeType = isPreviousListNested ? previousListItem.lastChild.type : parent.type;
|
|
39
|
+
const nextListNodeContent = Fragment.from(nextListNodeType.create(null, inner));
|
|
40
|
+
const slice = new Slice(Fragment.from(listItem.create(null, nextListNodeContent)), isPreviousListNested ? 3 : 1, 0);
|
|
41
|
+
const before = range.start;
|
|
42
|
+
const after = range.end;
|
|
43
|
+
tr.step(new ReplaceAroundStep(before - (isPreviousListNested ? 3 : 1), after, before, after, slice, 1, true));
|
|
44
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { LIST_TEXT_SCENARIOS } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import { isListItemNode, isListNode, isParagraphNode } from '@atlaskit/editor-common/utils';
|
|
3
|
+
import { isPosInsideList, isPosInsideParagraph } from '../utils/selection';
|
|
4
|
+
import { joinListItemWithParagraph, joinListItemWithParentNestedList, joinNestedListWithParentListItem, joinParagrapWithList, joinSiblingListItems } from './join-list-items-scenarios';
|
|
5
|
+
export const calcJoinListScenario = (walkNode, $head) => {
|
|
6
|
+
const {
|
|
7
|
+
$pos: $next,
|
|
8
|
+
foundNode: nextFoundNode
|
|
9
|
+
} = walkNode;
|
|
10
|
+
const headParent = $head.parent;
|
|
11
|
+
const headGrandParent = $head.node(-1);
|
|
12
|
+
const headInList = isPosInsideList($head);
|
|
13
|
+
const headInParagraph = isPosInsideParagraph($head);
|
|
14
|
+
const headInLastNonListChild = headGrandParent && headGrandParent.lastChild && (headGrandParent.lastChild === headParent || headGrandParent.childCount > 1 && headGrandParent.child(headGrandParent.childCount - 2) === headParent &&
|
|
15
|
+
//find the second last child if a list item may be the last child
|
|
16
|
+
isListNode(headGrandParent.lastChild));
|
|
17
|
+
const nextInList = isPosInsideList($next);
|
|
18
|
+
const nextInParagraph = isPosInsideParagraph($next);
|
|
19
|
+
if (!headInList && headInParagraph && nextInList) {
|
|
20
|
+
return [LIST_TEXT_SCENARIOS.JOIN_LIST_ITEM_WITH_PARAGRAPH, joinListItemWithParagraph];
|
|
21
|
+
}
|
|
22
|
+
if (!nextFoundNode || !headInList || !headInParagraph || !headInLastNonListChild) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
if (!nextInList && nextInParagraph) {
|
|
26
|
+
return [LIST_TEXT_SCENARIOS.JOIN_PARAGRAPH_WITH_LIST, joinParagrapWithList];
|
|
27
|
+
}
|
|
28
|
+
if (!nextInList) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const nextNodeAfter = $next.nodeAfter;
|
|
32
|
+
const nextGrandParent = $next.node(-1);
|
|
33
|
+
const headGreatGrandParent = $head.node(-2);
|
|
34
|
+
const nextInListItem = isListItemNode($next.parent);
|
|
35
|
+
const nextNodeAfterListItem = isListItemNode(nextNodeAfter);
|
|
36
|
+
const nextListItemHasFirstChildParagraph = nextNodeAfter &&
|
|
37
|
+
//Redundant check but the linter complains otherwise
|
|
38
|
+
nextNodeAfterListItem && isParagraphNode(nextNodeAfter.firstChild);
|
|
39
|
+
if (!nextInListItem && nextListItemHasFirstChildParagraph) {
|
|
40
|
+
return [LIST_TEXT_SCENARIOS.JOIN_DESCENDANT_TO_PARENT, joinNestedListWithParentListItem];
|
|
41
|
+
}
|
|
42
|
+
if (!nextInListItem) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const nextParentSiblingOfHeadParent = nextGrandParent && nextGrandParent === headGreatGrandParent;
|
|
46
|
+
const nextNodeAfterIsParagraph = isParagraphNode(nextNodeAfter);
|
|
47
|
+
if (!nextNodeAfterIsParagraph) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
if (nextParentSiblingOfHeadParent) {
|
|
51
|
+
return [LIST_TEXT_SCENARIOS.JOIN_SIBLINGS, joinSiblingListItems];
|
|
52
|
+
}
|
|
53
|
+
return [LIST_TEXT_SCENARIOS.JOIN_PARENT_SIBLING_TO_PARENT_CHILD, joinListItemWithParentNestedList];
|
|
54
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { joinParagrapWithList } from './join-paragraph-with-list';
|
|
2
|
+
export { joinSiblingListItems } from './join-sibling-list-items';
|
|
3
|
+
export { joinNestedListWithParentListItem } from './join-nested-list-with-parent-list-item';
|
|
4
|
+
export { joinListItemWithParentNestedList } from './join-list-item-with-parent-nested-list';
|
|
5
|
+
export { joinListItemWithParagraph } from './join-list-item-with-paragraph';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { insertContentDeleteRange, isListNode } from '@atlaskit/editor-common/utils';
|
|
2
|
+
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
3
|
+
// Case for when a users selection is at the end of a paragraph, the paragraph
|
|
4
|
+
// is followed by a list, and they delete forward
|
|
5
|
+
export const joinListItemWithParagraph = ({
|
|
6
|
+
tr,
|
|
7
|
+
$next,
|
|
8
|
+
$head
|
|
9
|
+
}) => {
|
|
10
|
+
// For empty paragraphs before a list
|
|
11
|
+
if ($head.parent.content.size < 1) {
|
|
12
|
+
insertContentDeleteRange(tr, tr => tr.doc.resolve($head.pos), [], [[$head.pos - 1, $head.pos]]);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
const paragraphPosition = $head.pos;
|
|
16
|
+
const list = tr.doc.nodeAt($next.pos - 1);
|
|
17
|
+
const firstListItem = tr.doc.nodeAt($next.pos);
|
|
18
|
+
if (!list || !firstListItem) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const firstChildNodeOfFirstListItem = firstListItem.firstChild;
|
|
22
|
+
if (!firstChildNodeOfFirstListItem) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const lastChildOfFirstListItem = firstListItem.lastChild;
|
|
26
|
+
const firstGrandchildOfFirstListItem = firstChildNodeOfFirstListItem.firstChild;
|
|
27
|
+
const firstListItemHasOneChildWithNoNestedLists = hasSingleChild(firstListItem) && firstChildNodeOfFirstListItem.childCount < 2 && $next.nodeAfter;
|
|
28
|
+
const firstListItemContainsParagraphAndNestedList = !hasSingleChild(firstListItem) && lastChildOfFirstListItem && isListNode(lastChildOfFirstListItem);
|
|
29
|
+
const insertions = [];
|
|
30
|
+
const deletions = [];
|
|
31
|
+
|
|
32
|
+
// For lists that only have one list item with no children - need to remove remaining list
|
|
33
|
+
if (hasSingleChild(list) && hasSingleChild(firstListItem) && $next.nodeAfter) {
|
|
34
|
+
deletions.push([tr.mapping.map($next.pos - 1), tr.mapping.map($next.pos + $next.nodeAfter.nodeSize + 1)]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// For first list items that have a paragraph and a list
|
|
38
|
+
if (firstListItemContainsParagraphAndNestedList) {
|
|
39
|
+
const firstListItemNestedList = Fragment.from(lastChildOfFirstListItem.content);
|
|
40
|
+
insertions.push([firstListItemNestedList, tr.mapping.map($next.pos)]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// For first list item has one child & no nested lists OR first list items that have a paragraph and a list
|
|
44
|
+
if (firstListItemHasOneChildWithNoNestedLists || firstListItemContainsParagraphAndNestedList) {
|
|
45
|
+
deletions.push([tr.mapping.map($next.pos), tr.mapping.map($next.pos + firstListItem.nodeSize - 1)]);
|
|
46
|
+
const firstListItemText = Fragment.from(firstChildNodeOfFirstListItem.content);
|
|
47
|
+
insertions.push([firstListItemText, paragraphPosition]);
|
|
48
|
+
insertContentDeleteRange(tr, tr => tr.doc.resolve($head.pos), insertions, deletions);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// For any first list items that have multiple children (eg. multiple paragraphs)
|
|
53
|
+
if (firstListItem.childCount > 1) {
|
|
54
|
+
insertions.push([Fragment.from(firstChildNodeOfFirstListItem.content), paragraphPosition]);
|
|
55
|
+
deletions.push([tr.mapping.map($next.pos + 1), tr.mapping.map($next.pos + firstChildNodeOfFirstListItem.nodeSize + 1)]);
|
|
56
|
+
insertContentDeleteRange(tr, tr => tr.doc.resolve($head.pos), insertions, deletions);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// For any remaining first list items that have a single child (eg. single paragraph, multiple lines of text)
|
|
61
|
+
if (firstGrandchildOfFirstListItem && firstGrandchildOfFirstListItem.type.name === 'hardBreak') {
|
|
62
|
+
const nodeSizeOfGrandchild = firstGrandchildOfFirstListItem ? firstGrandchildOfFirstListItem.nodeSize : 0;
|
|
63
|
+
deletions.push([tr.mapping.map($next.pos + 2), tr.mapping.map($next.pos + 2 + nodeSizeOfGrandchild)]);
|
|
64
|
+
} else {
|
|
65
|
+
insertions.push([Fragment.from(firstChildNodeOfFirstListItem.content), paragraphPosition]);
|
|
66
|
+
const nodeSizeOfFirstChild = firstChildNodeOfFirstListItem.nodeSize;
|
|
67
|
+
deletions.push([tr.mapping.map($next.pos), tr.mapping.map($next.pos + 2 + nodeSizeOfFirstChild)]);
|
|
68
|
+
}
|
|
69
|
+
insertContentDeleteRange(tr, tr => tr.doc.resolve($head.pos), insertions, deletions);
|
|
70
|
+
return true;
|
|
71
|
+
};
|
|
72
|
+
const hasSingleChild = node => {
|
|
73
|
+
return node.childCount === 1;
|
|
74
|
+
};
|
package/dist/es2019/actions/join-list-items-scenarios/join-list-item-with-parent-nested-list.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { insertContentDeleteRange, isListNode } from '@atlaskit/editor-common/utils';
|
|
2
|
+
//Case for two adjacent list items with the first being of greater indentation
|
|
3
|
+
export const joinListItemWithParentNestedList = ({
|
|
4
|
+
tr,
|
|
5
|
+
$next,
|
|
6
|
+
$head
|
|
7
|
+
}) => {
|
|
8
|
+
/* CASE 4
|
|
9
|
+
* Initial Structure:
|
|
10
|
+
*
|
|
11
|
+
* List A {
|
|
12
|
+
* ListItem B {
|
|
13
|
+
* Paragraph C { text1 }
|
|
14
|
+
* ...Children D
|
|
15
|
+
* List E {
|
|
16
|
+
* ...
|
|
17
|
+
* List F { //May be multiple levels of lists
|
|
18
|
+
* ...Children G
|
|
19
|
+
* ListItem H { //Last node of the block
|
|
20
|
+
* ...Children I
|
|
21
|
+
* Paragraph J { text2 |$head||textInsertPos| } |childrenMInsertPos| //Cant have children since this ListItem is the last of the block
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ...
|
|
25
|
+
* |childrenOInsertPos| }
|
|
26
|
+
* }
|
|
27
|
+
* ListItem K { |$next|
|
|
28
|
+
* Paragraph L { text3 }
|
|
29
|
+
* ...Children M
|
|
30
|
+
* List? N {
|
|
31
|
+
* ...Children O
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* Converts to:
|
|
37
|
+
*
|
|
38
|
+
* List A {
|
|
39
|
+
* ListItem B {
|
|
40
|
+
* Paragraph C { text1 }
|
|
41
|
+
* ...Children D
|
|
42
|
+
* List E {
|
|
43
|
+
* ...
|
|
44
|
+
* List F {
|
|
45
|
+
* ...Children G
|
|
46
|
+
* ListItem H {
|
|
47
|
+
* ...Children I
|
|
48
|
+
* Paragraph J { text2text3 }
|
|
49
|
+
* ...Children M
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* ...
|
|
53
|
+
* ...Children O
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
* }
|
|
57
|
+
*
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
const listItemK = $next.parent; //List must have at least one child
|
|
61
|
+
if (!listItemK.firstChild || !listItemK.lastChild) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
const beforeListItemK = $next.before();
|
|
65
|
+
const afterListItemB = $next.before();
|
|
66
|
+
const afterListItemK = $next.after();
|
|
67
|
+
const containsChildrenO = isListNode(listItemK.lastChild);
|
|
68
|
+
const textInsertPos = $head.pos;
|
|
69
|
+
const childrenMInsertPos = $head.pos + 1;
|
|
70
|
+
const childrenOInsertPos = afterListItemB - 2;
|
|
71
|
+
const textContent = listItemK.firstChild.content;
|
|
72
|
+
const childrenMContent = containsChildrenO ? listItemK.content.cut(listItemK.firstChild.nodeSize, listItemK.nodeSize - listItemK.lastChild.nodeSize - 2 //Get the position before
|
|
73
|
+
) : listItemK.content.cut(listItemK.firstChild.nodeSize);
|
|
74
|
+
const childrenOContent = listItemK.lastChild.content;
|
|
75
|
+
insertContentDeleteRange(tr, tr => tr.doc.resolve(textInsertPos), containsChildrenO ? [[textContent, textInsertPos], [childrenMContent, childrenMInsertPos], [childrenOContent, childrenOInsertPos]] : [[textContent, textInsertPos], [childrenMContent, childrenMInsertPos]], [[beforeListItemK, afterListItemK]]);
|
|
76
|
+
return true;
|
|
77
|
+
};
|
package/dist/es2019/actions/join-list-items-scenarios/join-nested-list-with-parent-list-item.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { insertContentDeleteRange, isListNode } from '@atlaskit/editor-common/utils';
|
|
2
|
+
//Case for two adjacent list items with the first being of lower indentation
|
|
3
|
+
export const joinNestedListWithParentListItem = ({
|
|
4
|
+
tr,
|
|
5
|
+
$next,
|
|
6
|
+
$head
|
|
7
|
+
}) => {
|
|
8
|
+
/* CASE 3
|
|
9
|
+
* Initial Structure:
|
|
10
|
+
*
|
|
11
|
+
* List A {
|
|
12
|
+
* ListItem B {
|
|
13
|
+
* ...Children C
|
|
14
|
+
* Paragraph D { text1 |$head||textInsertPos| } |childrenHInsertPos|
|
|
15
|
+
* List E { |$next||childrenJInsertPos|
|
|
16
|
+
* ListItem F {
|
|
17
|
+
* Paragraph G { text2 }
|
|
18
|
+
* ...Children H
|
|
19
|
+
* List? I {
|
|
20
|
+
* ...Children J
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ...Children K
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
* Converts to:
|
|
29
|
+
*
|
|
30
|
+
* List A {
|
|
31
|
+
* ListItem B {
|
|
32
|
+
* ...Children C
|
|
33
|
+
* Paragraph D { text1text2 }
|
|
34
|
+
* ...Children H
|
|
35
|
+
* List E {
|
|
36
|
+
* ...Children J
|
|
37
|
+
* ...Children K
|
|
38
|
+
* }
|
|
39
|
+
* }
|
|
40
|
+
* }
|
|
41
|
+
*
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
const listE = $next.parent;
|
|
45
|
+
const listItemF = $next.nodeAfter; //We know next is before a ListItem. ListItem must have at least one child
|
|
46
|
+
if (!listItemF || !listItemF.lastChild) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const paragraphG = listItemF.firstChild; //ListItem must have at least one child
|
|
50
|
+
if (!paragraphG) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const beforeListE = $next.before();
|
|
54
|
+
const beforeListItemF = $next.pos;
|
|
55
|
+
const afterParagraphD = $head.after();
|
|
56
|
+
const afterListE = $next.after();
|
|
57
|
+
const afterListItemF = tr.doc.resolve($next.pos + 1).after(); //List must always have at least one listItem
|
|
58
|
+
|
|
59
|
+
const containsChildrenJ = isListNode(listItemF.lastChild);
|
|
60
|
+
const shouldRemoveListE = listE.childCount === 1 && !containsChildrenJ; //Assures no Children J and K
|
|
61
|
+
|
|
62
|
+
const textInsertPos = $head.pos;
|
|
63
|
+
const childrenHInsertPos = afterParagraphD;
|
|
64
|
+
const childrenJInsertPos = $next.pos;
|
|
65
|
+
const textContent = paragraphG.content;
|
|
66
|
+
const childrenHContent = containsChildrenJ ? listItemF.content.cut(paragraphG.nodeSize, listItemF.nodeSize - listItemF.lastChild.nodeSize - 2) : listItemF.content.cut(paragraphG.nodeSize); //If Children J doesn't exist then Children H will include the last node
|
|
67
|
+
const childrenJContent = listItemF.lastChild.content; //Will be invalid if there are no Children J but it will be unused
|
|
68
|
+
|
|
69
|
+
insertContentDeleteRange(tr, tr => tr.doc.resolve(textInsertPos), containsChildrenJ ? [[textContent, textInsertPos], [childrenHContent, childrenHInsertPos], [childrenJContent, childrenJInsertPos]] : [[textContent, textInsertPos], [childrenHContent, childrenHInsertPos]], [shouldRemoveListE ? [beforeListE, afterListE] : [beforeListItemF, afterListItemF]]);
|
|
70
|
+
return true;
|
|
71
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { insertContentDeleteRange } from '@atlaskit/editor-common/utils';
|
|
2
|
+
//Case for two adjacent nodes with the first being a list item and the last being a paragraph
|
|
3
|
+
export const joinParagrapWithList = ({
|
|
4
|
+
tr,
|
|
5
|
+
$next,
|
|
6
|
+
$head
|
|
7
|
+
}) => {
|
|
8
|
+
/* CASE 1
|
|
9
|
+
* Initial Structure:
|
|
10
|
+
*
|
|
11
|
+
* List A {
|
|
12
|
+
* ListItem B {
|
|
13
|
+
* ...Children C
|
|
14
|
+
* Paragraph D { text1 |$head||textInsertPos| }
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
* Paragraph E { |$next| text 2 }
|
|
18
|
+
*
|
|
19
|
+
* Converts to:
|
|
20
|
+
*
|
|
21
|
+
* List A {
|
|
22
|
+
* ListItem B {
|
|
23
|
+
* ...Children C
|
|
24
|
+
* Paragraph D { text1text2 }
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const paragraphE = $next.parent;
|
|
31
|
+
const beforeParagraphE = $next.before();
|
|
32
|
+
const afterParagraphE = $next.after();
|
|
33
|
+
const textInsertPos = $head.pos;
|
|
34
|
+
const textContent = paragraphE.content;
|
|
35
|
+
insertContentDeleteRange(tr, tr => tr.doc.resolve(textInsertPos), [[textContent, textInsertPos]], [[beforeParagraphE, afterParagraphE]]);
|
|
36
|
+
return true;
|
|
37
|
+
};
|