@atlaskit/editor-plugin-tasks-and-decisions 0.2.15 → 0.3.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/CHANGELOG.md +19 -0
- package/dist/cjs/pm-plugins/helpers.js +22 -0
- package/dist/cjs/pm-plugins/keymaps.js +60 -5
- package/dist/es2019/pm-plugins/helpers.js +22 -1
- package/dist/es2019/pm-plugins/keymaps.js +64 -7
- package/dist/esm/pm-plugins/helpers.js +22 -1
- package/dist/esm/pm-plugins/keymaps.js +62 -7
- package/dist/types/index.d.ts +1 -1
- package/dist/types/pm-plugins/helpers.d.ts +4 -0
- package/dist/types-ts4.5/index.d.ts +1 -1
- package/dist/types-ts4.5/pm-plugins/helpers.d.ts +4 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-tasks-and-decisions
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#68790](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/68790) [`c6d8affc52d1`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/c6d8affc52d1) - Support maybeAdd plugins in usePreset. Add typing support for universal preset.
|
|
8
|
+
|
|
9
|
+
Now when using the editor API with the universal preset
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#69144](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/69144) [`10e7328aea8c`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/10e7328aea8c) - [ux] ED-21844 Add fix for enter keybind behaviour for nested actions inside list and relevant testcases
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
|
|
16
|
+
## 0.2.16
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies
|
|
21
|
+
|
|
3
22
|
## 0.2.15
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.findFirstParentListNode = findFirstParentListNode;
|
|
6
7
|
exports.focusCheckboxAndUpdateSelection = focusCheckboxAndUpdateSelection;
|
|
7
8
|
exports.getAllTaskItemsDataInRootTaskList = getAllTaskItemsDataInRootTaskList;
|
|
8
9
|
exports.getCurrentIndentLevel = exports.getBlockRange = void 0;
|
|
@@ -296,4 +297,25 @@ function removeCheckboxFocus(view) {
|
|
|
296
297
|
dispatch(tr.setMeta(_pluginKey.stateKey, {
|
|
297
298
|
action: _types.ACTIONS.FOCUS_BY_LOCALID
|
|
298
299
|
}));
|
|
300
|
+
}
|
|
301
|
+
function findFirstParentListNode($pos) {
|
|
302
|
+
var currentNode = $pos.doc.nodeAt($pos.pos);
|
|
303
|
+
var listNodePosition = null;
|
|
304
|
+
if ((0, _utils.isListNode)(currentNode)) {
|
|
305
|
+
listNodePosition = $pos.pos;
|
|
306
|
+
} else {
|
|
307
|
+
var result = (0, _utils2.findParentNodeClosestToPos)($pos, _utils.isListNode);
|
|
308
|
+
listNodePosition = result && result.pos;
|
|
309
|
+
}
|
|
310
|
+
if (listNodePosition == null) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
var node = $pos.doc.nodeAt(listNodePosition);
|
|
314
|
+
if (!node) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
node: node,
|
|
319
|
+
pos: listNodePosition
|
|
320
|
+
};
|
|
299
321
|
}
|
|
@@ -222,7 +222,38 @@ var unindentTaskOrUnwrapTaskDecisionFollowing = function unindentTaskOrUnwrapTas
|
|
|
222
222
|
return false;
|
|
223
223
|
};
|
|
224
224
|
var deleteForwards = (0, _commands.autoJoin)((0, _commands.chainCommands)((0, _utils.deleteEmptyParagraphAndMoveBlockUp)(_helpers.isActionOrDecisionList), joinTaskDecisionFollowing, unindentTaskOrUnwrapTaskDecisionFollowing), ['taskList', 'decisionList']);
|
|
225
|
+
var deleteExtraListItem = function deleteExtraListItem(tr, $from) {
|
|
226
|
+
/*
|
|
227
|
+
After we replace actionItem with empty list item if there's the anomaly of extra empty list item
|
|
228
|
+
the cursor moves inside the first taskItem of splitted taskList
|
|
229
|
+
so the extra list item present above the list item containing taskList & cursor
|
|
230
|
+
*/
|
|
231
|
+
|
|
232
|
+
var $currentFrom = tr.selection.$from;
|
|
233
|
+
var listItemContainingActionList = tr.doc.resolve($currentFrom.start($currentFrom.depth - 2));
|
|
234
|
+
var emptyListItem = tr.doc.resolve(listItemContainingActionList.before() - 1);
|
|
235
|
+
tr.delete(emptyListItem.start(), listItemContainingActionList.pos);
|
|
236
|
+
};
|
|
237
|
+
var processNestedActionItem = function processNestedActionItem(tr, $from, previousListItemPos) {
|
|
238
|
+
var parentListNode = (0, _helpers.findFirstParentListNode)($from);
|
|
239
|
+
var previousChildCountOfList = parentListNode === null || parentListNode === void 0 ? void 0 : parentListNode.node.childCount;
|
|
240
|
+
var currentParentListNode = (0, _helpers.findFirstParentListNode)(tr.doc.resolve(tr.mapping.map($from.pos)));
|
|
241
|
+
var currentChildCountOfList = currentParentListNode === null || currentParentListNode === void 0 ? void 0 : currentParentListNode.node.childCount;
|
|
242
|
+
|
|
243
|
+
/*
|
|
244
|
+
While replacing range with empty list item an extra list item gets created in some of the scenarios
|
|
245
|
+
After splitting only one extra listItem should be created else an extra listItem is created
|
|
246
|
+
*/
|
|
247
|
+
if (previousChildCountOfList && currentChildCountOfList && previousChildCountOfList + 1 !== currentChildCountOfList) {
|
|
248
|
+
deleteExtraListItem(tr, $from);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Set custom selection for nested action inside lists using previosuly calculated previousListItem position
|
|
252
|
+
var stableResolvedPos = tr.doc.resolve(previousListItemPos);
|
|
253
|
+
tr.setSelection(_state.TextSelection.create(tr.doc, stableResolvedPos.after() + 2));
|
|
254
|
+
};
|
|
225
255
|
var splitListItemWith = function splitListItemWith(tr, content, $from, setSelection) {
|
|
256
|
+
var _frag$firstChild;
|
|
226
257
|
var origDoc = tr.doc;
|
|
227
258
|
|
|
228
259
|
// split just before the current item
|
|
@@ -230,7 +261,13 @@ var splitListItemWith = function splitListItemWith(tr, content, $from, setSelect
|
|
|
230
261
|
var container = $from.node($from.depth - 2);
|
|
231
262
|
var posInList = $from.index($from.depth - 1);
|
|
232
263
|
var shouldSplit = !(!(0, _helpers.isActionOrDecisionList)(container) && posInList === 0);
|
|
233
|
-
|
|
264
|
+
var frag = _model.Fragment.from(content);
|
|
265
|
+
var isNestedActionInsideLists = frag.childCount === 1 && ((_frag$firstChild = frag.firstChild) === null || _frag$firstChild === void 0 ? void 0 : _frag$firstChild.type.name) === 'listItem';
|
|
266
|
+
|
|
267
|
+
/* We don't split the list item if it's nested inside lists
|
|
268
|
+
to have consistent behaviour and their resolution.
|
|
269
|
+
*/
|
|
270
|
+
if (shouldSplit && !isNestedActionInsideLists) {
|
|
234
271
|
// this only splits a node to delete it, so we probably don't need a random uuid
|
|
235
272
|
// but generate one anyway for correctness
|
|
236
273
|
tr = tr.split($from.pos, 1, [{
|
|
@@ -240,19 +277,25 @@ var splitListItemWith = function splitListItemWith(tr, content, $from, setSelect
|
|
|
240
277
|
}
|
|
241
278
|
}]);
|
|
242
279
|
}
|
|
280
|
+
/*
|
|
281
|
+
In case of nested action inside lists we explicitly set the cursor
|
|
282
|
+
We need to insert it relatively to previous doc structure
|
|
283
|
+
So we calculate the position of previous list item and save that position
|
|
284
|
+
(The cursor can be placed easily next to list item)
|
|
285
|
+
*/
|
|
286
|
+
var previousListItemPos = isNestedActionInsideLists ? $from.start($from.depth - 2) : 0;
|
|
287
|
+
|
|
243
288
|
// and delete the action at the current pos
|
|
244
289
|
// we can do this because we know either first new child will be taskItem or nothing at all
|
|
245
|
-
|
|
290
|
+
|
|
246
291
|
tr = tr.replace(tr.mapping.map($from.start() - 2), tr.mapping.map($from.end() + 2), frag.size ? new _model.Slice(frag, 0, 0) : _model.Slice.empty);
|
|
247
292
|
|
|
248
293
|
// put cursor inside paragraph
|
|
249
|
-
if (setSelection) {
|
|
294
|
+
if (setSelection && !isNestedActionInsideLists) {
|
|
250
295
|
tr = tr.setSelection(new _state.TextSelection(tr.doc.resolve($from.pos + 1 - (shouldSplit ? 0 : 2))));
|
|
251
296
|
}
|
|
252
|
-
|
|
253
297
|
// lift list up if the node after the initial one was a taskList
|
|
254
298
|
// which means it would have empty placeholder content if we just immediately delete it
|
|
255
|
-
//
|
|
256
299
|
// if it's a taskItem then it can stand alone, so it's fine
|
|
257
300
|
var $oldAfter = origDoc.resolve($from.after());
|
|
258
301
|
|
|
@@ -273,14 +316,26 @@ var splitListItemWith = function splitListItemWith(tr, content, $from, setSelect
|
|
|
273
316
|
tr = tr.deleteRange(pos - 3, pos - 1);
|
|
274
317
|
}
|
|
275
318
|
}
|
|
319
|
+
if (isNestedActionInsideLists) {
|
|
320
|
+
processNestedActionItem(tr, $from, previousListItemPos);
|
|
321
|
+
}
|
|
276
322
|
return tr;
|
|
277
323
|
};
|
|
324
|
+
var creatParentListItemFragement = function creatParentListItemFragement(state) {
|
|
325
|
+
return state.schema.nodes.listItem.create({}, state.schema.nodes.paragraph.create());
|
|
326
|
+
};
|
|
278
327
|
var splitListItem = function splitListItem(state, dispatch) {
|
|
279
328
|
var tr = state.tr,
|
|
280
329
|
$from = state.selection.$from;
|
|
281
330
|
var paragraph = state.schema.nodes.paragraph;
|
|
331
|
+
var listItem = state.schema.nodes.listItem;
|
|
282
332
|
if (actionDecisionFollowsOrNothing($from)) {
|
|
283
333
|
if (dispatch) {
|
|
334
|
+
if ((0, _utils2.hasParentNodeOfType)(listItem)(tr.selection)) {
|
|
335
|
+
// if we're inside a list item, then we pass in a fragment containing a new list item not a paragraph
|
|
336
|
+
dispatch(splitListItemWith(tr, creatParentListItemFragement(state), $from, true));
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
284
339
|
dispatch(splitListItemWith(tr, paragraph.createChecked(), $from, true));
|
|
285
340
|
}
|
|
286
341
|
return true;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findFarthestParentNode } from '@atlaskit/editor-common/utils';
|
|
1
|
+
import { findFarthestParentNode, isListNode } from '@atlaskit/editor-common/utils';
|
|
2
2
|
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
3
3
|
import { liftTarget } from '@atlaskit/editor-prosemirror/transform';
|
|
4
4
|
import { findParentNodeClosestToPos, hasParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
@@ -313,4 +313,25 @@ export function removeCheckboxFocus(view) {
|
|
|
313
313
|
dispatch(tr.setMeta(stateKey, {
|
|
314
314
|
action: ACTIONS.FOCUS_BY_LOCALID
|
|
315
315
|
}));
|
|
316
|
+
}
|
|
317
|
+
export function findFirstParentListNode($pos) {
|
|
318
|
+
const currentNode = $pos.doc.nodeAt($pos.pos);
|
|
319
|
+
let listNodePosition = null;
|
|
320
|
+
if (isListNode(currentNode)) {
|
|
321
|
+
listNodePosition = $pos.pos;
|
|
322
|
+
} else {
|
|
323
|
+
const result = findParentNodeClosestToPos($pos, isListNode);
|
|
324
|
+
listNodePosition = result && result.pos;
|
|
325
|
+
}
|
|
326
|
+
if (listNodePosition == null) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
const node = $pos.doc.nodeAt(listNodePosition);
|
|
330
|
+
if (!node) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
node,
|
|
335
|
+
pos: listNodePosition
|
|
336
|
+
};
|
|
316
337
|
}
|
|
@@ -8,11 +8,11 @@ import { autoJoin, chainCommands } from '@atlaskit/editor-prosemirror/commands';
|
|
|
8
8
|
import { keymap } from '@atlaskit/editor-prosemirror/keymap';
|
|
9
9
|
import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
10
10
|
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
11
|
-
import { findParentNodeOfType, findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
|
|
11
|
+
import { findParentNodeOfType, findParentNodeOfTypeClosestToPos, hasParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
12
12
|
import { insertTaskDecisionWithAnalytics } from '../commands';
|
|
13
13
|
import { normalizeTaskItemsSelection } from '../utils';
|
|
14
14
|
import { joinAtCut, liftSelection, wrapSelectionInTaskList } from './commands';
|
|
15
|
-
import { getBlockRange, getCurrentIndentLevel, getTaskItemIndex, isActionOrDecisionItem, isActionOrDecisionList, isEmptyTaskDecision, isInsideDecision, isInsideTask, isInsideTaskOrDecisionItem, isTable, liftBlock, walkOut } from './helpers';
|
|
15
|
+
import { findFirstParentListNode, getBlockRange, getCurrentIndentLevel, getTaskItemIndex, isActionOrDecisionItem, isActionOrDecisionList, isEmptyTaskDecision, isInsideDecision, isInsideTask, isInsideTaskOrDecisionItem, isTable, liftBlock, walkOut } from './helpers';
|
|
16
16
|
const indentationAnalytics = (curIndentLevel, direction, inputMethod) => ({
|
|
17
17
|
action: ACTION.FORMATTED,
|
|
18
18
|
actionSubject: ACTION_SUBJECT.TEXT,
|
|
@@ -197,7 +197,38 @@ const unindentTaskOrUnwrapTaskDecisionFollowing = (state, dispatch) => {
|
|
|
197
197
|
return false;
|
|
198
198
|
};
|
|
199
199
|
const deleteForwards = autoJoin(chainCommands(deleteEmptyParagraphAndMoveBlockUp(isActionOrDecisionList), joinTaskDecisionFollowing, unindentTaskOrUnwrapTaskDecisionFollowing), ['taskList', 'decisionList']);
|
|
200
|
+
const deleteExtraListItem = (tr, $from) => {
|
|
201
|
+
/*
|
|
202
|
+
After we replace actionItem with empty list item if there's the anomaly of extra empty list item
|
|
203
|
+
the cursor moves inside the first taskItem of splitted taskList
|
|
204
|
+
so the extra list item present above the list item containing taskList & cursor
|
|
205
|
+
*/
|
|
206
|
+
|
|
207
|
+
const $currentFrom = tr.selection.$from;
|
|
208
|
+
const listItemContainingActionList = tr.doc.resolve($currentFrom.start($currentFrom.depth - 2));
|
|
209
|
+
const emptyListItem = tr.doc.resolve(listItemContainingActionList.before() - 1);
|
|
210
|
+
tr.delete(emptyListItem.start(), listItemContainingActionList.pos);
|
|
211
|
+
};
|
|
212
|
+
const processNestedActionItem = (tr, $from, previousListItemPos) => {
|
|
213
|
+
const parentListNode = findFirstParentListNode($from);
|
|
214
|
+
const previousChildCountOfList = parentListNode === null || parentListNode === void 0 ? void 0 : parentListNode.node.childCount;
|
|
215
|
+
const currentParentListNode = findFirstParentListNode(tr.doc.resolve(tr.mapping.map($from.pos)));
|
|
216
|
+
const currentChildCountOfList = currentParentListNode === null || currentParentListNode === void 0 ? void 0 : currentParentListNode.node.childCount;
|
|
217
|
+
|
|
218
|
+
/*
|
|
219
|
+
While replacing range with empty list item an extra list item gets created in some of the scenarios
|
|
220
|
+
After splitting only one extra listItem should be created else an extra listItem is created
|
|
221
|
+
*/
|
|
222
|
+
if (previousChildCountOfList && currentChildCountOfList && previousChildCountOfList + 1 !== currentChildCountOfList) {
|
|
223
|
+
deleteExtraListItem(tr, $from);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Set custom selection for nested action inside lists using previosuly calculated previousListItem position
|
|
227
|
+
const stableResolvedPos = tr.doc.resolve(previousListItemPos);
|
|
228
|
+
tr.setSelection(TextSelection.create(tr.doc, stableResolvedPos.after() + 2));
|
|
229
|
+
};
|
|
200
230
|
const splitListItemWith = (tr, content, $from, setSelection) => {
|
|
231
|
+
var _frag$firstChild;
|
|
201
232
|
const origDoc = tr.doc;
|
|
202
233
|
|
|
203
234
|
// split just before the current item
|
|
@@ -205,7 +236,13 @@ const splitListItemWith = (tr, content, $from, setSelection) => {
|
|
|
205
236
|
const container = $from.node($from.depth - 2);
|
|
206
237
|
const posInList = $from.index($from.depth - 1);
|
|
207
238
|
const shouldSplit = !(!isActionOrDecisionList(container) && posInList === 0);
|
|
208
|
-
|
|
239
|
+
const frag = Fragment.from(content);
|
|
240
|
+
const isNestedActionInsideLists = frag.childCount === 1 && ((_frag$firstChild = frag.firstChild) === null || _frag$firstChild === void 0 ? void 0 : _frag$firstChild.type.name) === 'listItem';
|
|
241
|
+
|
|
242
|
+
/* We don't split the list item if it's nested inside lists
|
|
243
|
+
to have consistent behaviour and their resolution.
|
|
244
|
+
*/
|
|
245
|
+
if (shouldSplit && !isNestedActionInsideLists) {
|
|
209
246
|
// this only splits a node to delete it, so we probably don't need a random uuid
|
|
210
247
|
// but generate one anyway for correctness
|
|
211
248
|
tr = tr.split($from.pos, 1, [{
|
|
@@ -215,19 +252,25 @@ const splitListItemWith = (tr, content, $from, setSelection) => {
|
|
|
215
252
|
}
|
|
216
253
|
}]);
|
|
217
254
|
}
|
|
255
|
+
/*
|
|
256
|
+
In case of nested action inside lists we explicitly set the cursor
|
|
257
|
+
We need to insert it relatively to previous doc structure
|
|
258
|
+
So we calculate the position of previous list item and save that position
|
|
259
|
+
(The cursor can be placed easily next to list item)
|
|
260
|
+
*/
|
|
261
|
+
const previousListItemPos = isNestedActionInsideLists ? $from.start($from.depth - 2) : 0;
|
|
262
|
+
|
|
218
263
|
// and delete the action at the current pos
|
|
219
264
|
// we can do this because we know either first new child will be taskItem or nothing at all
|
|
220
|
-
|
|
265
|
+
|
|
221
266
|
tr = tr.replace(tr.mapping.map($from.start() - 2), tr.mapping.map($from.end() + 2), frag.size ? new Slice(frag, 0, 0) : Slice.empty);
|
|
222
267
|
|
|
223
268
|
// put cursor inside paragraph
|
|
224
|
-
if (setSelection) {
|
|
269
|
+
if (setSelection && !isNestedActionInsideLists) {
|
|
225
270
|
tr = tr.setSelection(new TextSelection(tr.doc.resolve($from.pos + 1 - (shouldSplit ? 0 : 2))));
|
|
226
271
|
}
|
|
227
|
-
|
|
228
272
|
// lift list up if the node after the initial one was a taskList
|
|
229
273
|
// which means it would have empty placeholder content if we just immediately delete it
|
|
230
|
-
//
|
|
231
274
|
// if it's a taskItem then it can stand alone, so it's fine
|
|
232
275
|
const $oldAfter = origDoc.resolve($from.after());
|
|
233
276
|
|
|
@@ -248,8 +291,14 @@ const splitListItemWith = (tr, content, $from, setSelection) => {
|
|
|
248
291
|
tr = tr.deleteRange(pos - 3, pos - 1);
|
|
249
292
|
}
|
|
250
293
|
}
|
|
294
|
+
if (isNestedActionInsideLists) {
|
|
295
|
+
processNestedActionItem(tr, $from, previousListItemPos);
|
|
296
|
+
}
|
|
251
297
|
return tr;
|
|
252
298
|
};
|
|
299
|
+
const creatParentListItemFragement = state => {
|
|
300
|
+
return state.schema.nodes.listItem.create({}, state.schema.nodes.paragraph.create());
|
|
301
|
+
};
|
|
253
302
|
const splitListItem = (state, dispatch) => {
|
|
254
303
|
let {
|
|
255
304
|
tr,
|
|
@@ -264,8 +313,16 @@ const splitListItem = (state, dispatch) => {
|
|
|
264
313
|
}
|
|
265
314
|
}
|
|
266
315
|
} = state;
|
|
316
|
+
const {
|
|
317
|
+
listItem
|
|
318
|
+
} = state.schema.nodes;
|
|
267
319
|
if (actionDecisionFollowsOrNothing($from)) {
|
|
268
320
|
if (dispatch) {
|
|
321
|
+
if (hasParentNodeOfType(listItem)(tr.selection)) {
|
|
322
|
+
// if we're inside a list item, then we pass in a fragment containing a new list item not a paragraph
|
|
323
|
+
dispatch(splitListItemWith(tr, creatParentListItemFragement(state), $from, true));
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
269
326
|
dispatch(splitListItemWith(tr, paragraph.createChecked(), $from, true));
|
|
270
327
|
}
|
|
271
328
|
return true;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findFarthestParentNode } from '@atlaskit/editor-common/utils';
|
|
1
|
+
import { findFarthestParentNode, isListNode } from '@atlaskit/editor-common/utils';
|
|
2
2
|
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
3
3
|
import { liftTarget } from '@atlaskit/editor-prosemirror/transform';
|
|
4
4
|
import { findParentNodeClosestToPos, hasParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
@@ -282,4 +282,25 @@ export function removeCheckboxFocus(view) {
|
|
|
282
282
|
dispatch(tr.setMeta(stateKey, {
|
|
283
283
|
action: ACTIONS.FOCUS_BY_LOCALID
|
|
284
284
|
}));
|
|
285
|
+
}
|
|
286
|
+
export function findFirstParentListNode($pos) {
|
|
287
|
+
var currentNode = $pos.doc.nodeAt($pos.pos);
|
|
288
|
+
var listNodePosition = null;
|
|
289
|
+
if (isListNode(currentNode)) {
|
|
290
|
+
listNodePosition = $pos.pos;
|
|
291
|
+
} else {
|
|
292
|
+
var result = findParentNodeClosestToPos($pos, isListNode);
|
|
293
|
+
listNodePosition = result && result.pos;
|
|
294
|
+
}
|
|
295
|
+
if (listNodePosition == null) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
var node = $pos.doc.nodeAt(listNodePosition);
|
|
299
|
+
if (!node) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
node: node,
|
|
304
|
+
pos: listNodePosition
|
|
305
|
+
};
|
|
285
306
|
}
|
|
@@ -11,11 +11,11 @@ import { autoJoin, chainCommands } from '@atlaskit/editor-prosemirror/commands';
|
|
|
11
11
|
import { keymap } from '@atlaskit/editor-prosemirror/keymap';
|
|
12
12
|
import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
|
|
13
13
|
import { TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
14
|
-
import { findParentNodeOfType, findParentNodeOfTypeClosestToPos } from '@atlaskit/editor-prosemirror/utils';
|
|
14
|
+
import { findParentNodeOfType, findParentNodeOfTypeClosestToPos, hasParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
|
|
15
15
|
import { insertTaskDecisionWithAnalytics } from '../commands';
|
|
16
16
|
import { normalizeTaskItemsSelection } from '../utils';
|
|
17
17
|
import { joinAtCut, liftSelection, wrapSelectionInTaskList } from './commands';
|
|
18
|
-
import { getBlockRange, getCurrentIndentLevel, getTaskItemIndex, isActionOrDecisionItem, isActionOrDecisionList, isEmptyTaskDecision, isInsideDecision, isInsideTask, isInsideTaskOrDecisionItem, isTable, liftBlock, walkOut } from './helpers';
|
|
18
|
+
import { findFirstParentListNode, getBlockRange, getCurrentIndentLevel, getTaskItemIndex, isActionOrDecisionItem, isActionOrDecisionList, isEmptyTaskDecision, isInsideDecision, isInsideTask, isInsideTaskOrDecisionItem, isTable, liftBlock, walkOut } from './helpers';
|
|
19
19
|
var indentationAnalytics = function indentationAnalytics(curIndentLevel, direction, inputMethod) {
|
|
20
20
|
return {
|
|
21
21
|
action: ACTION.FORMATTED,
|
|
@@ -214,7 +214,38 @@ var unindentTaskOrUnwrapTaskDecisionFollowing = function unindentTaskOrUnwrapTas
|
|
|
214
214
|
return false;
|
|
215
215
|
};
|
|
216
216
|
var deleteForwards = autoJoin(chainCommands(deleteEmptyParagraphAndMoveBlockUp(isActionOrDecisionList), joinTaskDecisionFollowing, unindentTaskOrUnwrapTaskDecisionFollowing), ['taskList', 'decisionList']);
|
|
217
|
+
var deleteExtraListItem = function deleteExtraListItem(tr, $from) {
|
|
218
|
+
/*
|
|
219
|
+
After we replace actionItem with empty list item if there's the anomaly of extra empty list item
|
|
220
|
+
the cursor moves inside the first taskItem of splitted taskList
|
|
221
|
+
so the extra list item present above the list item containing taskList & cursor
|
|
222
|
+
*/
|
|
223
|
+
|
|
224
|
+
var $currentFrom = tr.selection.$from;
|
|
225
|
+
var listItemContainingActionList = tr.doc.resolve($currentFrom.start($currentFrom.depth - 2));
|
|
226
|
+
var emptyListItem = tr.doc.resolve(listItemContainingActionList.before() - 1);
|
|
227
|
+
tr.delete(emptyListItem.start(), listItemContainingActionList.pos);
|
|
228
|
+
};
|
|
229
|
+
var processNestedActionItem = function processNestedActionItem(tr, $from, previousListItemPos) {
|
|
230
|
+
var parentListNode = findFirstParentListNode($from);
|
|
231
|
+
var previousChildCountOfList = parentListNode === null || parentListNode === void 0 ? void 0 : parentListNode.node.childCount;
|
|
232
|
+
var currentParentListNode = findFirstParentListNode(tr.doc.resolve(tr.mapping.map($from.pos)));
|
|
233
|
+
var currentChildCountOfList = currentParentListNode === null || currentParentListNode === void 0 ? void 0 : currentParentListNode.node.childCount;
|
|
234
|
+
|
|
235
|
+
/*
|
|
236
|
+
While replacing range with empty list item an extra list item gets created in some of the scenarios
|
|
237
|
+
After splitting only one extra listItem should be created else an extra listItem is created
|
|
238
|
+
*/
|
|
239
|
+
if (previousChildCountOfList && currentChildCountOfList && previousChildCountOfList + 1 !== currentChildCountOfList) {
|
|
240
|
+
deleteExtraListItem(tr, $from);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Set custom selection for nested action inside lists using previosuly calculated previousListItem position
|
|
244
|
+
var stableResolvedPos = tr.doc.resolve(previousListItemPos);
|
|
245
|
+
tr.setSelection(TextSelection.create(tr.doc, stableResolvedPos.after() + 2));
|
|
246
|
+
};
|
|
217
247
|
var splitListItemWith = function splitListItemWith(tr, content, $from, setSelection) {
|
|
248
|
+
var _frag$firstChild;
|
|
218
249
|
var origDoc = tr.doc;
|
|
219
250
|
|
|
220
251
|
// split just before the current item
|
|
@@ -222,7 +253,13 @@ var splitListItemWith = function splitListItemWith(tr, content, $from, setSelect
|
|
|
222
253
|
var container = $from.node($from.depth - 2);
|
|
223
254
|
var posInList = $from.index($from.depth - 1);
|
|
224
255
|
var shouldSplit = !(!isActionOrDecisionList(container) && posInList === 0);
|
|
225
|
-
|
|
256
|
+
var frag = Fragment.from(content);
|
|
257
|
+
var isNestedActionInsideLists = frag.childCount === 1 && ((_frag$firstChild = frag.firstChild) === null || _frag$firstChild === void 0 ? void 0 : _frag$firstChild.type.name) === 'listItem';
|
|
258
|
+
|
|
259
|
+
/* We don't split the list item if it's nested inside lists
|
|
260
|
+
to have consistent behaviour and their resolution.
|
|
261
|
+
*/
|
|
262
|
+
if (shouldSplit && !isNestedActionInsideLists) {
|
|
226
263
|
// this only splits a node to delete it, so we probably don't need a random uuid
|
|
227
264
|
// but generate one anyway for correctness
|
|
228
265
|
tr = tr.split($from.pos, 1, [{
|
|
@@ -232,19 +269,25 @@ var splitListItemWith = function splitListItemWith(tr, content, $from, setSelect
|
|
|
232
269
|
}
|
|
233
270
|
}]);
|
|
234
271
|
}
|
|
272
|
+
/*
|
|
273
|
+
In case of nested action inside lists we explicitly set the cursor
|
|
274
|
+
We need to insert it relatively to previous doc structure
|
|
275
|
+
So we calculate the position of previous list item and save that position
|
|
276
|
+
(The cursor can be placed easily next to list item)
|
|
277
|
+
*/
|
|
278
|
+
var previousListItemPos = isNestedActionInsideLists ? $from.start($from.depth - 2) : 0;
|
|
279
|
+
|
|
235
280
|
// and delete the action at the current pos
|
|
236
281
|
// we can do this because we know either first new child will be taskItem or nothing at all
|
|
237
|
-
|
|
282
|
+
|
|
238
283
|
tr = tr.replace(tr.mapping.map($from.start() - 2), tr.mapping.map($from.end() + 2), frag.size ? new Slice(frag, 0, 0) : Slice.empty);
|
|
239
284
|
|
|
240
285
|
// put cursor inside paragraph
|
|
241
|
-
if (setSelection) {
|
|
286
|
+
if (setSelection && !isNestedActionInsideLists) {
|
|
242
287
|
tr = tr.setSelection(new TextSelection(tr.doc.resolve($from.pos + 1 - (shouldSplit ? 0 : 2))));
|
|
243
288
|
}
|
|
244
|
-
|
|
245
289
|
// lift list up if the node after the initial one was a taskList
|
|
246
290
|
// which means it would have empty placeholder content if we just immediately delete it
|
|
247
|
-
//
|
|
248
291
|
// if it's a taskItem then it can stand alone, so it's fine
|
|
249
292
|
var $oldAfter = origDoc.resolve($from.after());
|
|
250
293
|
|
|
@@ -265,14 +308,26 @@ var splitListItemWith = function splitListItemWith(tr, content, $from, setSelect
|
|
|
265
308
|
tr = tr.deleteRange(pos - 3, pos - 1);
|
|
266
309
|
}
|
|
267
310
|
}
|
|
311
|
+
if (isNestedActionInsideLists) {
|
|
312
|
+
processNestedActionItem(tr, $from, previousListItemPos);
|
|
313
|
+
}
|
|
268
314
|
return tr;
|
|
269
315
|
};
|
|
316
|
+
var creatParentListItemFragement = function creatParentListItemFragement(state) {
|
|
317
|
+
return state.schema.nodes.listItem.create({}, state.schema.nodes.paragraph.create());
|
|
318
|
+
};
|
|
270
319
|
var splitListItem = function splitListItem(state, dispatch) {
|
|
271
320
|
var tr = state.tr,
|
|
272
321
|
$from = state.selection.$from;
|
|
273
322
|
var paragraph = state.schema.nodes.paragraph;
|
|
323
|
+
var listItem = state.schema.nodes.listItem;
|
|
274
324
|
if (actionDecisionFollowsOrNothing($from)) {
|
|
275
325
|
if (dispatch) {
|
|
326
|
+
if (hasParentNodeOfType(listItem)(tr.selection)) {
|
|
327
|
+
// if we're inside a list item, then we pass in a fragment containing a new list item not a paragraph
|
|
328
|
+
dispatch(splitListItemWith(tr, creatParentListItemFragement(state), $from, true));
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
276
331
|
dispatch(splitListItemWith(tr, paragraph.createChecked(), $from, true));
|
|
277
332
|
}
|
|
278
333
|
return true;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { tasksAndDecisionsPlugin } from './plugin';
|
|
2
|
-
export type { TasksAndDecisionsPlugin } from './types';
|
|
2
|
+
export type { TasksAndDecisionsPlugin, TaskDecisionPluginOptions, TaskAndDecisionsSharedState, TaskDecisionListType, AddItemTransactionCreator, } from './types';
|
|
@@ -74,3 +74,7 @@ export declare function getTaskItemDataToFocus(view: EditorView, direction: 'nex
|
|
|
74
74
|
} | undefined;
|
|
75
75
|
export declare function focusCheckboxAndUpdateSelection(view: EditorView, taskItemData: TaskItemData): void;
|
|
76
76
|
export declare function removeCheckboxFocus(view: EditorView): void;
|
|
77
|
+
export declare function findFirstParentListNode($pos: ResolvedPos): {
|
|
78
|
+
pos: number;
|
|
79
|
+
node: Node;
|
|
80
|
+
} | null;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { tasksAndDecisionsPlugin } from './plugin';
|
|
2
|
-
export type { TasksAndDecisionsPlugin } from './types';
|
|
2
|
+
export type { TasksAndDecisionsPlugin, TaskDecisionPluginOptions, TaskAndDecisionsSharedState, TaskDecisionListType, AddItemTransactionCreator, } from './types';
|
|
@@ -74,3 +74,7 @@ export declare function getTaskItemDataToFocus(view: EditorView, direction: 'nex
|
|
|
74
74
|
} | undefined;
|
|
75
75
|
export declare function focusCheckboxAndUpdateSelection(view: EditorView, taskItemData: TaskItemData): void;
|
|
76
76
|
export declare function removeCheckboxFocus(view: EditorView): void;
|
|
77
|
+
export declare function findFirstParentListNode($pos: ResolvedPos): {
|
|
78
|
+
pos: number;
|
|
79
|
+
node: Node;
|
|
80
|
+
} | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-tasks-and-decisions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Tasks and decisions plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@atlaskit/adf-schema": "^35.3.0",
|
|
37
37
|
"@atlaskit/analytics-namespaced-context": "^6.9.0",
|
|
38
38
|
"@atlaskit/analytics-next": "^9.1.0",
|
|
39
|
-
"@atlaskit/editor-common": "^
|
|
39
|
+
"@atlaskit/editor-common": "^77.1.0",
|
|
40
40
|
"@atlaskit/editor-plugin-analytics": "^0.4.0",
|
|
41
41
|
"@atlaskit/editor-plugin-type-ahead": "^0.9.0",
|
|
42
42
|
"@atlaskit/editor-prosemirror": "1.1.0",
|