@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,48 @@
|
|
|
1
|
+
import { insertContentDeleteRange } from '@atlaskit/editor-common/utils';
|
|
2
|
+
//Case for two adjacent list items of the same indentation
|
|
3
|
+
export const joinSiblingListItems = ({
|
|
4
|
+
tr,
|
|
5
|
+
$next,
|
|
6
|
+
$head
|
|
7
|
+
}) => {
|
|
8
|
+
/* CASE 2
|
|
9
|
+
* Initial Structure:
|
|
10
|
+
*
|
|
11
|
+
* List A {
|
|
12
|
+
* ListItem B {
|
|
13
|
+
* ...Children C
|
|
14
|
+
* Paragraph D { text1 |$head||textInsertPos| } //Cant have children since that would be Case 4
|
|
15
|
+
* |childrenGInsertPos| }
|
|
16
|
+
* ListItem E { |$next|
|
|
17
|
+
* Paragraph F { text2 }
|
|
18
|
+
* ...Children G
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* Converts to:
|
|
23
|
+
*
|
|
24
|
+
* List A {
|
|
25
|
+
* ListItem B {
|
|
26
|
+
* ...Children C
|
|
27
|
+
* Paragraph C { text1text2 }
|
|
28
|
+
* ...Children G
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const listItemE = $next.parent;
|
|
35
|
+
const paragraphF = $next.nodeAfter; //ListItem must have at least one child
|
|
36
|
+
if (!paragraphF) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const beforeListItemE = $next.before();
|
|
40
|
+
const afterListItemE = $next.after();
|
|
41
|
+
const endListItemB = $head.end(-1);
|
|
42
|
+
const textInsertPos = $head.pos;
|
|
43
|
+
const childrenGInsertPos = endListItemB;
|
|
44
|
+
const textContent = paragraphF.content;
|
|
45
|
+
const childrenGContent = listItemE.content.cut(paragraphF.nodeSize);
|
|
46
|
+
insertContentDeleteRange(tr, tr => tr.doc.resolve(textInsertPos), [[textContent, textInsertPos], [childrenGContent, childrenGInsertPos]], [[beforeListItemE, afterListItemE]]);
|
|
47
|
+
return true;
|
|
48
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { isListNode } from '@atlaskit/editor-common/utils';
|
|
2
|
+
export function mergeNextListAtPosition({
|
|
3
|
+
tr,
|
|
4
|
+
listPosition
|
|
5
|
+
}) {
|
|
6
|
+
const listNodeAtPosition = tr.doc.nodeAt(listPosition);
|
|
7
|
+
if (!isListNode(listNodeAtPosition)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const listPositionResolved = tr.doc.resolve(listPosition + listNodeAtPosition.nodeSize);
|
|
11
|
+
const {
|
|
12
|
+
pos,
|
|
13
|
+
nodeAfter,
|
|
14
|
+
nodeBefore
|
|
15
|
+
} = listPositionResolved;
|
|
16
|
+
if (!isListNode(nodeBefore) || !isListNode(nodeAfter)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if ((nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.type.name) !== (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type.name)) {
|
|
20
|
+
const previousListPosition = pos - nodeBefore.nodeSize;
|
|
21
|
+
tr.setNodeMarkup(previousListPosition, nodeAfter.type);
|
|
22
|
+
}
|
|
23
|
+
tr.join(pos);
|
|
24
|
+
}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { OUTDENT_SCENARIOS } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import { JoinDirection, joinSiblingLists, normalizeListItemsSelection } from '@atlaskit/editor-common/lists';
|
|
3
|
+
import { GapCursorSelection } from '@atlaskit/editor-common/selection';
|
|
4
|
+
import { getOrderFromOrderedListNode, isListItemNode, isListNode } from '@atlaskit/editor-common/utils';
|
|
5
|
+
import { Fragment, NodeRange, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
6
|
+
import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
7
|
+
import { liftTarget, ReplaceAroundStep, ReplaceStep } from '@atlaskit/editor-prosemirror/transform';
|
|
8
|
+
import { getRestartListsAttributes, storeRestartListsAttributes } from '../utils/analytics';
|
|
9
|
+
import { findFirstParentListItemNode, findRootParentListNode } from '../utils/find';
|
|
10
|
+
import { createListNodeRange } from '../utils/selection';
|
|
11
|
+
export const outdentListItemsSelected = (tr, state, featureFlags) => {
|
|
12
|
+
const originalSelection = tr.selection;
|
|
13
|
+
const normalizedSelection = normalizeListItemsSelection({
|
|
14
|
+
selection: tr.selection,
|
|
15
|
+
doc: tr.doc
|
|
16
|
+
});
|
|
17
|
+
const rootList = findRootParentListNode(normalizedSelection.$from);
|
|
18
|
+
if (!rootList) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const commonList = normalizedSelection.$from.blockRange(rootList, isListNode);
|
|
22
|
+
if (!commonList) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
let hasNormalizedToPositionLiftedOut = false;
|
|
26
|
+
let hasNormalizedFromPositionLiftedOut = false;
|
|
27
|
+
const {
|
|
28
|
+
from: oldFrom,
|
|
29
|
+
to: oldTo
|
|
30
|
+
} = normalizedSelection;
|
|
31
|
+
const nodeRanges = splitRangeSelection(normalizedSelection);
|
|
32
|
+
nodeRanges.forEach(range => {
|
|
33
|
+
const $from = tr.doc.resolve(tr.mapping.map(range.from));
|
|
34
|
+
const $to = tr.doc.resolve(tr.mapping.map(range.to));
|
|
35
|
+
const mappedRange = $from.blockRange($to, isListNode);
|
|
36
|
+
if (!mappedRange) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (isListItemNode($from.node(mappedRange.depth - 1))) {
|
|
40
|
+
outdentRangeToParentList({
|
|
41
|
+
tr,
|
|
42
|
+
range: mappedRange
|
|
43
|
+
});
|
|
44
|
+
} else {
|
|
45
|
+
extractListItemsRangeFromList({
|
|
46
|
+
tr,
|
|
47
|
+
range: mappedRange,
|
|
48
|
+
state,
|
|
49
|
+
featureFlags
|
|
50
|
+
});
|
|
51
|
+
hasNormalizedToPositionLiftedOut = hasNormalizedToPositionLiftedOut || oldTo >= range.from && oldTo < range.to;
|
|
52
|
+
hasNormalizedFromPositionLiftedOut = hasNormalizedFromPositionLiftedOut || oldFrom >= range.from && oldFrom < range.to;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
const hasCommonListMoved = commonList.start !== tr.mapping.map(commonList.start);
|
|
56
|
+
const nextSelection = calculateNewSelection({
|
|
57
|
+
originalSelection,
|
|
58
|
+
normalizedSelection,
|
|
59
|
+
tr,
|
|
60
|
+
hasCommonListMoved,
|
|
61
|
+
hasNormalizedToPositionLiftedOut,
|
|
62
|
+
hasNormalizedFromPositionLiftedOut
|
|
63
|
+
});
|
|
64
|
+
tr.setSelection(nextSelection);
|
|
65
|
+
joinSiblingLists({
|
|
66
|
+
tr,
|
|
67
|
+
direction: JoinDirection.RIGHT
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
const calculateNewSelection = ({
|
|
71
|
+
tr,
|
|
72
|
+
originalSelection,
|
|
73
|
+
normalizedSelection,
|
|
74
|
+
hasCommonListMoved,
|
|
75
|
+
hasNormalizedToPositionLiftedOut,
|
|
76
|
+
hasNormalizedFromPositionLiftedOut
|
|
77
|
+
}) => {
|
|
78
|
+
const {
|
|
79
|
+
$from,
|
|
80
|
+
$to
|
|
81
|
+
} = normalizedSelection;
|
|
82
|
+
const isCursorSelection = normalizedSelection.empty;
|
|
83
|
+
let from = tr.mapping.map($from.pos);
|
|
84
|
+
let to = tr.mapping.map($to.pos);
|
|
85
|
+
const LIST_STRUCTURE_CHANGED_OFFSET = 2;
|
|
86
|
+
const isToFromSameListItem = $from.sameParent($to);
|
|
87
|
+
if (hasNormalizedFromPositionLiftedOut) {
|
|
88
|
+
const fromMapped = isToFromSameListItem ? $from.pos : from;
|
|
89
|
+
from = hasNormalizedFromPositionLiftedOut ? $from.pos : fromMapped;
|
|
90
|
+
from = hasCommonListMoved ? from - LIST_STRUCTURE_CHANGED_OFFSET : from;
|
|
91
|
+
from = Math.max(from, 0);
|
|
92
|
+
}
|
|
93
|
+
if (hasNormalizedToPositionLiftedOut) {
|
|
94
|
+
const toMapped = isToFromSameListItem ? $to.pos : to;
|
|
95
|
+
to = hasNormalizedToPositionLiftedOut ? $to.pos : toMapped;
|
|
96
|
+
to = hasCommonListMoved ? to - LIST_STRUCTURE_CHANGED_OFFSET : to;
|
|
97
|
+
to = Math.min(to, tr.doc.nodeSize - 2);
|
|
98
|
+
}
|
|
99
|
+
if (normalizedSelection instanceof GapCursorSelection) {
|
|
100
|
+
const nextSelectionFrom = tr.doc.resolve(from);
|
|
101
|
+
return new GapCursorSelection(nextSelectionFrom, normalizedSelection.side);
|
|
102
|
+
}
|
|
103
|
+
if (originalSelection instanceof NodeSelection) {
|
|
104
|
+
return NodeSelection.create(tr.doc, from);
|
|
105
|
+
}
|
|
106
|
+
if (isCursorSelection) {
|
|
107
|
+
return TextSelection.between(tr.doc.resolve(to), tr.doc.resolve(to), -1);
|
|
108
|
+
}
|
|
109
|
+
return TextSelection.between(tr.doc.resolve(from), tr.doc.resolve(to), -1);
|
|
110
|
+
};
|
|
111
|
+
const splitRangeSelection = selection => {
|
|
112
|
+
const commonListRange = createListNodeRange({
|
|
113
|
+
selection
|
|
114
|
+
});
|
|
115
|
+
if (!commonListRange) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
const {
|
|
119
|
+
$from,
|
|
120
|
+
$to
|
|
121
|
+
} = selection;
|
|
122
|
+
if ($from.pos === $to.pos && $from.sameParent($to)) {
|
|
123
|
+
return [{
|
|
124
|
+
from: commonListRange.start,
|
|
125
|
+
to: commonListRange.end,
|
|
126
|
+
depth: commonListRange.depth
|
|
127
|
+
}];
|
|
128
|
+
}
|
|
129
|
+
const lastListItem = findPreviousListItemSibling($from);
|
|
130
|
+
if (!lastListItem) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
const nodeRanges = [];
|
|
134
|
+
const {
|
|
135
|
+
doc
|
|
136
|
+
} = $from;
|
|
137
|
+
let previousListItem = findPreviousListItemSibling($to);
|
|
138
|
+
while (previousListItem && previousListItem.pos >= lastListItem.pos && previousListItem.pos >= commonListRange.start) {
|
|
139
|
+
const node = doc.nodeAt(previousListItem.pos);
|
|
140
|
+
if (!node || !isListItemNode(node)) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
let offset = 0;
|
|
144
|
+
if (node && node.lastChild && isListNode(node.lastChild)) {
|
|
145
|
+
offset = node.lastChild.nodeSize;
|
|
146
|
+
}
|
|
147
|
+
const start = previousListItem.pos + 1;
|
|
148
|
+
nodeRanges.push({
|
|
149
|
+
from: start,
|
|
150
|
+
to: doc.resolve(start).end() - offset,
|
|
151
|
+
depth: previousListItem.depth
|
|
152
|
+
});
|
|
153
|
+
previousListItem = findPreviousListItemSibling(previousListItem);
|
|
154
|
+
}
|
|
155
|
+
return nodeRanges;
|
|
156
|
+
};
|
|
157
|
+
const outdentRangeToParentList = ({
|
|
158
|
+
tr,
|
|
159
|
+
range
|
|
160
|
+
}) => {
|
|
161
|
+
const end = range.end;
|
|
162
|
+
const endOfList = range.$to.end(range.depth);
|
|
163
|
+
const {
|
|
164
|
+
listItem
|
|
165
|
+
} = tr.doc.type.schema.nodes;
|
|
166
|
+
if (end < endOfList) {
|
|
167
|
+
const slice = new Slice(Fragment.from(listItem.create(null, range.parent.copy())), 1, 0);
|
|
168
|
+
const step = new ReplaceAroundStep(end - 1, endOfList, end, endOfList, slice, 1, true);
|
|
169
|
+
tr.step(step);
|
|
170
|
+
range = new NodeRange(tr.doc.resolve(range.$from.pos), tr.doc.resolve(endOfList), range.depth);
|
|
171
|
+
}
|
|
172
|
+
const target = liftTarget(range);
|
|
173
|
+
if (target) {
|
|
174
|
+
tr.lift(range, target);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const extractListItemsRangeFromList = ({
|
|
178
|
+
tr,
|
|
179
|
+
range,
|
|
180
|
+
state,
|
|
181
|
+
featureFlags
|
|
182
|
+
}) => {
|
|
183
|
+
const list = range.parent;
|
|
184
|
+
const $start = tr.doc.resolve(range.start);
|
|
185
|
+
const listStart = $start.start(range.depth);
|
|
186
|
+
const listEnd = $start.end(range.depth);
|
|
187
|
+
const isAtTop = listStart === range.start;
|
|
188
|
+
const isAtBottom = listEnd === range.end;
|
|
189
|
+
const isTheEntireList = isAtTop && isAtBottom;
|
|
190
|
+
let listItemContent = isAtTop ? Fragment.empty : Fragment.from(list.copy(Fragment.empty));
|
|
191
|
+
for (let i = range.startIndex; i < range.endIndex; i++) {
|
|
192
|
+
listItemContent = listItemContent.append(list.child(i).content);
|
|
193
|
+
}
|
|
194
|
+
if (isAtTop) {
|
|
195
|
+
for (let i = 0; i < listItemContent.childCount; i++) {
|
|
196
|
+
const child = listItemContent.child(i);
|
|
197
|
+
if (child && isListNode(child) && child.type !== list.type) {
|
|
198
|
+
const newNestedList = list.type.create(null, child.content);
|
|
199
|
+
listItemContent = listItemContent.replaceChild(i, newNestedList);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
let nextList = list.copy(Fragment.empty);
|
|
204
|
+
let nextListStartNumber;
|
|
205
|
+
if (featureFlags !== null && featureFlags !== void 0 && featureFlags.restartNumberedLists) {
|
|
206
|
+
// if splitting a numbered list, keep the splitted item
|
|
207
|
+
// counter as the start of the next (second half) list (instead
|
|
208
|
+
// of reverting back to 1 as a starting number)
|
|
209
|
+
const order = getOrderFromOrderedListNode(list);
|
|
210
|
+
if (list.type.name === 'orderedList') {
|
|
211
|
+
nextListStartNumber = range.endIndex - 1 + order;
|
|
212
|
+
// @ts-ignore - [unblock prosemirror bump] assigning to readonly attrs
|
|
213
|
+
nextList.attrs = {
|
|
214
|
+
...nextList.attrs,
|
|
215
|
+
order: nextListStartNumber
|
|
216
|
+
};
|
|
217
|
+
const {
|
|
218
|
+
splitListStartNumber
|
|
219
|
+
} = getRestartListsAttributes(tr);
|
|
220
|
+
if (typeof splitListStartNumber !== 'number') {
|
|
221
|
+
storeRestartListsAttributes(tr, {
|
|
222
|
+
splitListStartNumber: nextListStartNumber
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const nextListFragment = listItemContent.append(Fragment.from(nextList));
|
|
228
|
+
if (featureFlags !== null && featureFlags !== void 0 && featureFlags.restartNumberedLists) {
|
|
229
|
+
// if the split list with nextListStartNumber is below another list
|
|
230
|
+
// with order (e.g due to multi-level indent items being lifted), track the
|
|
231
|
+
// list above's order instead, as it will be the split list's order after sibling joins
|
|
232
|
+
nextListFragment.forEach((node, _offset, index) => {
|
|
233
|
+
var _node$attrs;
|
|
234
|
+
if (node.type.name === 'orderedList' && ((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.order) === nextListStartNumber) {
|
|
235
|
+
var _prev$attrs;
|
|
236
|
+
const prev = nextListFragment.child(index - 1);
|
|
237
|
+
if ((prev === null || prev === void 0 ? void 0 : (_prev$attrs = prev.attrs) === null || _prev$attrs === void 0 ? void 0 : _prev$attrs.order) >= 0) {
|
|
238
|
+
var _prev$attrs2;
|
|
239
|
+
storeRestartListsAttributes(tr, {
|
|
240
|
+
splitListStartNumber: prev === null || prev === void 0 ? void 0 : (_prev$attrs2 = prev.attrs) === null || _prev$attrs2 === void 0 ? void 0 : _prev$attrs2.order
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
if (isTheEntireList) {
|
|
247
|
+
const slice = new Slice(listItemContent, 0, 0);
|
|
248
|
+
const step = new ReplaceStep($start.pos - 1, range.end + 1, slice, false);
|
|
249
|
+
if (featureFlags !== null && featureFlags !== void 0 && featureFlags.restartNumberedLists) {
|
|
250
|
+
storeRestartListsAttributes(tr, {
|
|
251
|
+
outdentScenario: undefined
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
tr.step(step);
|
|
255
|
+
} else if (isAtTop) {
|
|
256
|
+
const slice = new Slice(nextListFragment, 0, 1);
|
|
257
|
+
const step = new ReplaceStep($start.pos - 1, range.end, slice, false);
|
|
258
|
+
tr.step(step);
|
|
259
|
+
} else if (isAtBottom) {
|
|
260
|
+
const slice = new Slice(listItemContent, 1, 0);
|
|
261
|
+
const step = new ReplaceStep($start.pos, listEnd + 1, slice, false);
|
|
262
|
+
tr.step(step);
|
|
263
|
+
} else {
|
|
264
|
+
if (featureFlags !== null && featureFlags !== void 0 && featureFlags.restartNumberedLists) {
|
|
265
|
+
storeRestartListsAttributes(tr, {
|
|
266
|
+
outdentScenario: OUTDENT_SCENARIOS.SPLIT_LIST
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const slice = new Slice(nextListFragment, 1, 1);
|
|
270
|
+
const step = new ReplaceAroundStep($start.pos, listEnd, range.end, listEnd, slice, slice.size, false);
|
|
271
|
+
tr.step(step);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
const findPreviousListItemSibling = $pos => {
|
|
275
|
+
const doc = $pos.doc;
|
|
276
|
+
const isPositionListItem = isListNode($pos.node());
|
|
277
|
+
let listItemPosition = $pos;
|
|
278
|
+
if (!isPositionListItem) {
|
|
279
|
+
const listItem = findFirstParentListItemNode($pos);
|
|
280
|
+
if (!listItem) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
return doc.resolve(listItem.pos);
|
|
284
|
+
}
|
|
285
|
+
const resolved = doc.resolve(listItemPosition.pos);
|
|
286
|
+
const foundPosition = Selection.findFrom(resolved, -1);
|
|
287
|
+
if (!foundPosition) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
const parentListItemNode = findFirstParentListItemNode(foundPosition.$from);
|
|
291
|
+
if (!parentListItemNode) {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
return doc.resolve(parentListItemNode.pos);
|
|
295
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { GapCursorSelection } from '@atlaskit/editor-common/selection';
|
|
2
|
+
import { autoJoinTr } from '@atlaskit/editor-common/utils';
|
|
3
|
+
import { Fragment, NodeRange, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
4
|
+
import { canSplit, findWrapping, ReplaceAroundStep } from '@atlaskit/editor-prosemirror/transform';
|
|
5
|
+
import { isWrappingPossible } from '../utils/selection';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Wraps the selection in a list with the given type. If this results in
|
|
9
|
+
* two adjacent lists of the same type, those will be joined together.
|
|
10
|
+
*/
|
|
11
|
+
export function wrapInListAndJoin(nodeType, tr) {
|
|
12
|
+
wrapInList(nodeType)(tr);
|
|
13
|
+
autoJoinTr(tr, (before, after) => before.type === after.type && before.type === nodeType);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Wraps the selection in a list with the given type and attributes.
|
|
20
|
+
*
|
|
21
|
+
* Adapted from https://github.com/ProseMirror/prosemirror-schema-list/blob/master/src/schema-list.js#L64-L89
|
|
22
|
+
*/
|
|
23
|
+
export function wrapInList(listType, attrs) {
|
|
24
|
+
return function (tr) {
|
|
25
|
+
const {
|
|
26
|
+
$from,
|
|
27
|
+
$to
|
|
28
|
+
} = tr.selection;
|
|
29
|
+
let range;
|
|
30
|
+
if (tr.selection instanceof GapCursorSelection && $from.nodeAfter && isWrappingPossible(listType, tr.selection)) {
|
|
31
|
+
const nodeSize = $from.nodeAfter.nodeSize || 1;
|
|
32
|
+
range = $from.blockRange($from.doc.resolve($from.pos + nodeSize));
|
|
33
|
+
} else {
|
|
34
|
+
range = $from.blockRange($to);
|
|
35
|
+
}
|
|
36
|
+
let doJoin = false;
|
|
37
|
+
let outerRange = range;
|
|
38
|
+
if (!range) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
// This is at the top of an existing list item
|
|
42
|
+
if (range.depth >= 2 &&
|
|
43
|
+
// @ts-ignore - missing type for compatibleContent
|
|
44
|
+
$from.node(range.depth - 1).type.compatibleContent(listType) && range.startIndex === 0) {
|
|
45
|
+
// Don't do anything if this is the top of the list
|
|
46
|
+
if ($from.index(range.depth - 1) === 0) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
let $insert = tr.doc.resolve(range.start - 2);
|
|
50
|
+
outerRange = new NodeRange($insert, $insert, range.depth);
|
|
51
|
+
if (range.endIndex < range.parent.childCount) {
|
|
52
|
+
range = new NodeRange($from, tr.doc.resolve($to.end(range.depth)), range.depth);
|
|
53
|
+
}
|
|
54
|
+
doJoin = true;
|
|
55
|
+
}
|
|
56
|
+
let wrap = findWrapping(outerRange, listType, attrs, range);
|
|
57
|
+
if (!wrap) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
tr = doWrapInList(tr, range, wrap, doJoin, listType);
|
|
61
|
+
return true;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Internal function used by wrapInList
|
|
67
|
+
*
|
|
68
|
+
* Adapted from https://github.com/ProseMirror/prosemirror-schema-list/blob/master/src/schema-list.js#L91-L112
|
|
69
|
+
*/
|
|
70
|
+
function doWrapInList(tr, range, wrappers, joinBefore, listType) {
|
|
71
|
+
let content = Fragment.empty;
|
|
72
|
+
for (let i = wrappers.length - 1; i >= 0; i--) {
|
|
73
|
+
content = Fragment.from(wrappers[i].type.create(wrappers[i].attrs, content));
|
|
74
|
+
}
|
|
75
|
+
tr.step(new ReplaceAroundStep(range.start - (joinBefore ? 2 : 0), range.end, range.start, range.end, new Slice(content, 0, 0), wrappers.length, true));
|
|
76
|
+
let found = 0;
|
|
77
|
+
for (let i = 0; i < wrappers.length; i++) {
|
|
78
|
+
if (wrappers[i].type === listType) {
|
|
79
|
+
found = i + 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const splitDepth = wrappers.length - found;
|
|
83
|
+
let splitPos = range.start + wrappers.length - (joinBefore ? 2 : 0);
|
|
84
|
+
const parent = range.parent;
|
|
85
|
+
for (let i = range.startIndex, e = range.endIndex, first = true; i < e; i++, first = false) {
|
|
86
|
+
if (!first && canSplit(tr.doc, splitPos, splitDepth)) {
|
|
87
|
+
tr.split(splitPos, splitDepth);
|
|
88
|
+
splitPos += 2 * splitDepth;
|
|
89
|
+
}
|
|
90
|
+
splitPos += parent.child(i).nodeSize;
|
|
91
|
+
}
|
|
92
|
+
return tr;
|
|
93
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import { getCommonListAnalyticsAttributes, getListItemAttributes, hasValidListIndentationLevel } from '@atlaskit/editor-common/lists';
|
|
3
|
+
import { isBulletList } from '@atlaskit/editor-common/utils';
|
|
4
|
+
import { closeHistory } from '@atlaskit/editor-prosemirror/history';
|
|
5
|
+
import { indentListItemsSelected as indentListAction } from '../actions/indent-list-items-selected';
|
|
6
|
+
import { MAX_NESTED_LIST_INDENTATION } from '../types';
|
|
7
|
+
import { findFirstParentListNode } from '../utils/find';
|
|
8
|
+
import { isInsideListItem, isInsideTableCell } from '../utils/selection';
|
|
9
|
+
export const indentList = editorAnalyticsAPI => (inputMethod = INPUT_METHOD.KEYBOARD) => {
|
|
10
|
+
return function (state, dispatch) {
|
|
11
|
+
const {
|
|
12
|
+
tr,
|
|
13
|
+
selection: {
|
|
14
|
+
$from
|
|
15
|
+
}
|
|
16
|
+
} = state;
|
|
17
|
+
|
|
18
|
+
// don't indent if selection is not inside a list
|
|
19
|
+
if (!isInsideListItem(state)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Save the history, so it could undo/revert to the same state before the indent, see https://product-fabric.atlassian.net/browse/ED-14753
|
|
24
|
+
closeHistory(tr);
|
|
25
|
+
const firstListItemSelectedAttributes = getListItemAttributes($from);
|
|
26
|
+
const parentListNode = findFirstParentListNode($from);
|
|
27
|
+
if (!parentListNode || firstListItemSelectedAttributes && firstListItemSelectedAttributes.indentLevel === 0 && firstListItemSelectedAttributes.itemIndex === 0) {
|
|
28
|
+
if (isInsideTableCell(state)) {
|
|
29
|
+
// dont consume tab, as table-keymap should move cursor to next cell
|
|
30
|
+
return false;
|
|
31
|
+
} else {
|
|
32
|
+
// Even though this is a non-operation, we don't want to send this event to the browser. Because if we return false, the browser will move the focus to another place
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const currentListNode = parentListNode.node;
|
|
37
|
+
const actionSubjectId = isBulletList(currentListNode) ? ACTION_SUBJECT_ID.FORMAT_LIST_BULLET : ACTION_SUBJECT_ID.FORMAT_LIST_NUMBER;
|
|
38
|
+
indentListAction(tr);
|
|
39
|
+
const maximimunNestedLevelReached = !hasValidListIndentationLevel({
|
|
40
|
+
tr,
|
|
41
|
+
maxIndentation: MAX_NESTED_LIST_INDENTATION
|
|
42
|
+
});
|
|
43
|
+
if (maximimunNestedLevelReached || !tr.docChanged) {
|
|
44
|
+
// Even though this is a non-operation, we don't want to send this event to the browser. Because if we return false, the browser will move the focus to another place
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 ? void 0 : editorAnalyticsAPI.attachAnalyticsEvent({
|
|
48
|
+
action: ACTION.INDENTED,
|
|
49
|
+
actionSubject: ACTION_SUBJECT.LIST,
|
|
50
|
+
actionSubjectId,
|
|
51
|
+
eventType: EVENT_TYPE.TRACK,
|
|
52
|
+
attributes: {
|
|
53
|
+
...getCommonListAnalyticsAttributes(state),
|
|
54
|
+
inputMethod
|
|
55
|
+
}
|
|
56
|
+
})(tr);
|
|
57
|
+
if (dispatch) {
|
|
58
|
+
dispatch(tr);
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
};
|
|
62
|
+
};
|