@atlaskit/editor-plugin-tasks-and-decisions 0.1.0 → 0.2.1

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.
Files changed (105) hide show
  1. package/.eslintrc.js +26 -0
  2. package/CHANGELOG.md +16 -0
  3. package/dist/cjs/commands.js +288 -0
  4. package/dist/cjs/index.js +8 -1
  5. package/dist/cjs/nodeviews/decisionItem.js +117 -0
  6. package/dist/cjs/nodeviews/hooks/use-show-placeholder.js +40 -0
  7. package/dist/cjs/nodeviews/taskItem.js +211 -0
  8. package/dist/cjs/plugin.js +166 -0
  9. package/dist/cjs/pm-plugins/commands.js +96 -0
  10. package/dist/cjs/pm-plugins/helpers.js +299 -0
  11. package/dist/cjs/pm-plugins/input-rules.js +94 -0
  12. package/dist/cjs/pm-plugins/keymaps.js +376 -0
  13. package/dist/cjs/pm-plugins/main.js +263 -0
  14. package/dist/cjs/pm-plugins/plugin-key.js +8 -0
  15. package/dist/cjs/pm-plugins/types.js +11 -0
  16. package/dist/cjs/types.js +5 -0
  17. package/dist/cjs/ui/Decision/index.js +30 -0
  18. package/dist/cjs/ui/Task/index.js +81 -0
  19. package/dist/cjs/ui/Task/task-item-with-providers.js +138 -0
  20. package/dist/cjs/ui/ToolbarDecision/index.js +41 -0
  21. package/dist/cjs/ui/ToolbarTask/index.js +40 -0
  22. package/dist/cjs/utils.js +41 -0
  23. package/dist/es2019/commands.js +279 -0
  24. package/dist/es2019/index.js +1 -1
  25. package/dist/es2019/nodeviews/decisionItem.js +76 -0
  26. package/dist/es2019/nodeviews/hooks/use-show-placeholder.js +36 -0
  27. package/dist/es2019/nodeviews/taskItem.js +173 -0
  28. package/dist/es2019/plugin.js +154 -0
  29. package/dist/es2019/pm-plugins/commands.js +94 -0
  30. package/dist/es2019/pm-plugins/helpers.js +316 -0
  31. package/dist/es2019/pm-plugins/input-rules.js +91 -0
  32. package/dist/es2019/pm-plugins/keymaps.js +370 -0
  33. package/dist/es2019/pm-plugins/main.js +262 -0
  34. package/dist/es2019/pm-plugins/plugin-key.js +2 -0
  35. package/dist/es2019/pm-plugins/types.js +5 -0
  36. package/dist/es2019/types.js +1 -0
  37. package/dist/es2019/ui/Decision/index.js +26 -0
  38. package/dist/es2019/ui/Task/index.js +55 -0
  39. package/dist/es2019/ui/Task/task-item-with-providers.js +72 -0
  40. package/dist/es2019/ui/ToolbarDecision/index.js +37 -0
  41. package/dist/es2019/ui/ToolbarTask/index.js +36 -0
  42. package/dist/es2019/utils.js +32 -0
  43. package/dist/esm/commands.js +281 -0
  44. package/dist/esm/index.js +1 -1
  45. package/dist/esm/nodeviews/decisionItem.js +110 -0
  46. package/dist/esm/nodeviews/hooks/use-show-placeholder.js +34 -0
  47. package/dist/esm/nodeviews/taskItem.js +204 -0
  48. package/dist/esm/plugin.js +155 -0
  49. package/dist/esm/pm-plugins/commands.js +90 -0
  50. package/dist/esm/pm-plugins/helpers.js +285 -0
  51. package/dist/esm/pm-plugins/input-rules.js +87 -0
  52. package/dist/esm/pm-plugins/keymaps.js +368 -0
  53. package/dist/esm/pm-plugins/main.js +256 -0
  54. package/dist/esm/pm-plugins/plugin-key.js +2 -0
  55. package/dist/esm/pm-plugins/types.js +5 -0
  56. package/dist/esm/types.js +1 -0
  57. package/dist/esm/ui/Decision/index.js +23 -0
  58. package/dist/esm/ui/Task/index.js +71 -0
  59. package/dist/esm/ui/Task/task-item-with-providers.js +129 -0
  60. package/dist/esm/ui/ToolbarDecision/index.js +34 -0
  61. package/dist/esm/ui/ToolbarTask/index.js +33 -0
  62. package/dist/esm/utils.js +30 -0
  63. package/dist/types/commands.d.ts +16 -0
  64. package/dist/types/index.d.ts +2 -1
  65. package/dist/types/nodeviews/decisionItem.d.ts +10 -0
  66. package/dist/types/nodeviews/hooks/use-show-placeholder.d.ts +11 -0
  67. package/dist/types/nodeviews/taskItem.d.ts +14 -0
  68. package/dist/types/plugin.d.ts +2 -0
  69. package/dist/types/pm-plugins/commands.d.ts +15 -0
  70. package/dist/types/pm-plugins/helpers.d.ts +76 -0
  71. package/dist/types/pm-plugins/input-rules.d.ts +6 -0
  72. package/dist/types/pm-plugins/keymaps.d.ts +11 -0
  73. package/dist/types/pm-plugins/main.d.ts +7 -0
  74. package/dist/types/pm-plugins/plugin-key.d.ts +2 -0
  75. package/dist/types/pm-plugins/types.d.ts +8 -0
  76. package/dist/types/types.d.ts +49 -0
  77. package/dist/types/ui/Decision/index.d.ts +15 -0
  78. package/dist/types/ui/Task/index.d.ts +28 -0
  79. package/dist/types/ui/Task/task-item-with-providers.d.ts +29 -0
  80. package/dist/types/ui/ToolbarDecision/index.d.ts +18 -0
  81. package/dist/types/ui/ToolbarTask/index.d.ts +18 -0
  82. package/dist/types/utils.d.ts +4 -0
  83. package/dist/types-ts4.5/commands.d.ts +16 -0
  84. package/dist/types-ts4.5/index.d.ts +2 -1
  85. package/dist/types-ts4.5/nodeviews/decisionItem.d.ts +10 -0
  86. package/dist/types-ts4.5/nodeviews/hooks/use-show-placeholder.d.ts +11 -0
  87. package/dist/types-ts4.5/nodeviews/taskItem.d.ts +14 -0
  88. package/dist/types-ts4.5/plugin.d.ts +2 -0
  89. package/dist/types-ts4.5/pm-plugins/commands.d.ts +15 -0
  90. package/dist/types-ts4.5/pm-plugins/helpers.d.ts +76 -0
  91. package/dist/types-ts4.5/pm-plugins/input-rules.d.ts +6 -0
  92. package/dist/types-ts4.5/pm-plugins/keymaps.d.ts +11 -0
  93. package/dist/types-ts4.5/pm-plugins/main.d.ts +7 -0
  94. package/dist/types-ts4.5/pm-plugins/plugin-key.d.ts +2 -0
  95. package/dist/types-ts4.5/pm-plugins/types.d.ts +8 -0
  96. package/dist/types-ts4.5/types.d.ts +49 -0
  97. package/dist/types-ts4.5/ui/Decision/index.d.ts +15 -0
  98. package/dist/types-ts4.5/ui/Task/index.d.ts +28 -0
  99. package/dist/types-ts4.5/ui/Task/task-item-with-providers.d.ts +29 -0
  100. package/dist/types-ts4.5/ui/ToolbarDecision/index.d.ts +18 -0
  101. package/dist/types-ts4.5/ui/ToolbarTask/index.d.ts +18 -0
  102. package/dist/types-ts4.5/utils.d.ts +4 -0
  103. package/package.json +16 -6
  104. package/report.api.md +100 -1
  105. package/tmp/api-report-tmp.d.ts +78 -0
@@ -0,0 +1,76 @@
1
+ import React from 'react';
2
+ import ReactNodeView from '@atlaskit/editor-common/react-node-view';
3
+ import DecisionItem from '../ui/Decision';
4
+ import { useShowPlaceholder } from './hooks/use-show-placeholder';
5
+ const DecisionItemWrapper = ({
6
+ api,
7
+ editorView,
8
+ forwardRef,
9
+ isContentNodeEmpty,
10
+ getPos
11
+ }) => {
12
+ const showPlaceholder = useShowPlaceholder({
13
+ editorView,
14
+ isContentNodeEmpty,
15
+ getPos,
16
+ api
17
+ });
18
+ return /*#__PURE__*/React.createElement(DecisionItem, {
19
+ contentRef: forwardRef,
20
+ showPlaceholder: showPlaceholder
21
+ });
22
+ };
23
+ class Decision extends ReactNodeView {
24
+ isContentEmpty(node) {
25
+ return node.content.childCount === 0;
26
+ }
27
+ initWithAPI(api) {
28
+ this.api = api;
29
+ this.init();
30
+ return this;
31
+ }
32
+ createDomRef() {
33
+ const domRef = document.createElement('li');
34
+ domRef.style.listStyleType = 'none';
35
+ return domRef;
36
+ }
37
+ getContentDOM() {
38
+ const dom = document.createElement('div');
39
+ // setting a className prevents PM/Chrome mutation observer from
40
+ // incorrectly deleting nodes
41
+ dom.className = 'decision-item';
42
+ return {
43
+ dom
44
+ };
45
+ }
46
+ render(_props, forwardRef) {
47
+ const isContentNodeEmpty = this.isContentEmpty(this.node);
48
+ return /*#__PURE__*/React.createElement(DecisionItemWrapper, {
49
+ forwardRef: forwardRef,
50
+ isContentNodeEmpty: isContentNodeEmpty,
51
+ api: this.api
52
+ // The getPosHandler type is wrong, there is no `boolean` in the real implementation
53
+ // @ts-expect-error 2322: Type 'getPosHandler' is not assignable to type '() => number | undefined'.
54
+ ,
55
+ getPos: this.getPos,
56
+ editorView: this.view
57
+ });
58
+ }
59
+ viewShouldUpdate(nextNode) {
60
+ /**
61
+ * To ensure the placeholder is correctly toggled we need to allow react to re-render
62
+ * on first character insertion.
63
+ * Note: last character deletion is handled externally and automatically re-renders.
64
+ */
65
+ return this.isContentEmpty(this.node) && !!nextNode.content.childCount;
66
+ }
67
+ update(node, decorations) {
68
+ return super.update(node, decorations, undefined,
69
+ // Toggle the placeholder based on whether user input exists.
70
+ (_currentNode, _newNode) => !this.isContentEmpty(_newNode));
71
+ }
72
+ }
73
+ export const decisionItemNodeView = (portalProviderAPI, eventDispatcher, api) => (node, view, getPos) => {
74
+ const hasIntlContext = true;
75
+ return new Decision(node, view, getPos, portalProviderAPI, eventDispatcher, {}, undefined, undefined, undefined, hasIntlContext).initWithAPI(api);
76
+ };
@@ -0,0 +1,36 @@
1
+ import { useMemo } from 'react';
2
+ import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
3
+ import { TextSelection } from '@atlaskit/editor-prosemirror/state';
4
+ export const useShowPlaceholder = ({
5
+ editorView,
6
+ api,
7
+ isContentNodeEmpty,
8
+ getPos
9
+ }) => {
10
+ const {
11
+ typeAheadState
12
+ } = useSharedPluginState(api, ['typeAhead']);
13
+ const isTypeAheadOpen = Boolean(typeAheadState === null || typeAheadState === void 0 ? void 0 : typeAheadState.isOpen);
14
+ const isTypeAheadOpenedInsideItem = useMemo(() => {
15
+ var _selection$$cursor;
16
+ if (!isTypeAheadOpen) {
17
+ return false;
18
+ }
19
+ const itemPosition = getPos();
20
+ if (typeof itemPosition !== 'number') {
21
+ return false;
22
+ }
23
+ const selection = editorView.state.selection;
24
+ if (!(selection instanceof TextSelection)) {
25
+ return false;
26
+ }
27
+ const maybeItemNode = editorView.state.doc.nodeAt(itemPosition);
28
+ const maybeParentItemNode = (_selection$$cursor = selection.$cursor) === null || _selection$$cursor === void 0 ? void 0 : _selection$$cursor.node();
29
+ if (maybeItemNode && maybeParentItemNode && maybeItemNode.eq(maybeParentItemNode)) {
30
+ return true;
31
+ }
32
+ return false;
33
+ }, [isTypeAheadOpen, getPos, editorView]);
34
+ const showPlaceholder = Boolean(!isTypeAheadOpenedInsideItem && isContentNodeEmpty);
35
+ return showPlaceholder;
36
+ };
@@ -0,0 +1,173 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import React from 'react';
3
+ import { SetAttrsStep } from '@atlaskit/adf-schema/steps';
4
+ import { AnalyticsListener } from '@atlaskit/analytics-next';
5
+ import { useSharedPluginState } from '@atlaskit/editor-common/hooks';
6
+ import ReactNodeView from '@atlaskit/editor-common/react-node-view';
7
+ import TaskItem from '../ui/Task';
8
+ import { useShowPlaceholder } from './hooks/use-show-placeholder';
9
+ const TaskItemWrapper = ({
10
+ localId,
11
+ forwardRef,
12
+ isDone,
13
+ onChange,
14
+ providerFactory,
15
+ isContentNodeEmpty,
16
+ api,
17
+ getPos,
18
+ editorView
19
+ }) => {
20
+ const {
21
+ taskDecisionState
22
+ } = useSharedPluginState(api, ['taskDecision']);
23
+ const isFocused = Boolean((taskDecisionState === null || taskDecisionState === void 0 ? void 0 : taskDecisionState.focusedTaskItemLocalId) === localId);
24
+ const showPlaceholder = useShowPlaceholder({
25
+ editorView,
26
+ isContentNodeEmpty,
27
+ getPos,
28
+ api
29
+ });
30
+ return /*#__PURE__*/React.createElement(TaskItem, {
31
+ taskId: localId,
32
+ contentRef: forwardRef,
33
+ isDone: isDone,
34
+ onChange: onChange,
35
+ isFocused: isFocused,
36
+ showPlaceholder: showPlaceholder,
37
+ providers: providerFactory
38
+ });
39
+ };
40
+ class Task extends ReactNodeView {
41
+ constructor(...args) {
42
+ super(...args);
43
+ _defineProperty(this, "handleOnChange", (taskId, isChecked) => {
44
+ const {
45
+ tr
46
+ } = this.view.state;
47
+ const nodePos = this.getPos();
48
+ if (typeof nodePos !== 'number') {
49
+ return false;
50
+ }
51
+
52
+ // SetAttrsStep should be used to prevent task updates from being dropped when mapping task ticks
53
+ // from a previous version of the document, such as a published page.
54
+ tr.step(new SetAttrsStep(nodePos, {
55
+ state: isChecked ? 'DONE' : 'TODO',
56
+ localId: taskId
57
+ }));
58
+ tr.setMeta('scrollIntoView', false);
59
+ this.view.dispatch(tr);
60
+ });
61
+ /**
62
+ * Dynamically generates analytics data relating to the parent list.
63
+ *
64
+ * Required to be dynamic, as list (in prosemirror model) may have
65
+ * changed (e.g. item movements, or additional items in list).
66
+ * This node view will have not rerendered for those changes, so
67
+ * cannot render the position and listSize into the
68
+ * AnalyticsContext at initial render time.
69
+ */
70
+ _defineProperty(this, "addListAnalyticsData", event => {
71
+ try {
72
+ const nodePos = this.getPos();
73
+ if (typeof nodePos !== 'number') {
74
+ return false;
75
+ }
76
+ const resolvedPos = this.view.state.doc.resolve(nodePos);
77
+ const position = resolvedPos.index();
78
+ const listSize = resolvedPos.parent.childCount;
79
+ const listLocalId = resolvedPos.parent.attrs.localId;
80
+ event.update(payload => {
81
+ const {
82
+ attributes = {},
83
+ actionSubject
84
+ } = payload;
85
+ if (actionSubject !== 'action') {
86
+ // Not action related, ignore
87
+ return payload;
88
+ }
89
+ return {
90
+ ...payload,
91
+ attributes: {
92
+ ...attributes,
93
+ position,
94
+ listSize,
95
+ listLocalId
96
+ }
97
+ };
98
+ });
99
+ } catch (e) {
100
+ // This can occur if pos is NaN (seen it in some test cases)
101
+ // Act defensively here, and lose some analytics data rather than
102
+ // cause any user facing error.
103
+ }
104
+ });
105
+ }
106
+ initWithAPI(api) {
107
+ this.api = api;
108
+ this.init();
109
+ return this;
110
+ }
111
+ isContentEmpty(node) {
112
+ return node.content.childCount === 0;
113
+ }
114
+ createDomRef() {
115
+ const domRef = document.createElement('div');
116
+ domRef.style.listStyleType = 'none';
117
+ return domRef;
118
+ }
119
+ getContentDOM() {
120
+ const dom = document.createElement('div');
121
+ // setting a className prevents PM/Chrome mutation observer from
122
+ // incorrectly deleting nodes
123
+ dom.className = 'task-item';
124
+ return {
125
+ dom
126
+ };
127
+ }
128
+ render(props, forwardRef) {
129
+ const {
130
+ localId,
131
+ state
132
+ } = this.node.attrs;
133
+ const isContentNodeEmpty = this.isContentEmpty(this.node);
134
+ return /*#__PURE__*/React.createElement(AnalyticsListener, {
135
+ channel: "fabric-elements",
136
+ onEvent: this.addListAnalyticsData
137
+ }, /*#__PURE__*/React.createElement(TaskItemWrapper, {
138
+ localId: localId,
139
+ forwardRef: forwardRef,
140
+ isDone: state === 'DONE',
141
+ onChange: this.handleOnChange,
142
+ isContentNodeEmpty: isContentNodeEmpty,
143
+ providerFactory: props.providerFactory
144
+ // The getPosHandler type is wrong, there is no `boolean` in the real implementation
145
+ // @ts-expect-error 2322: Type 'getPosHandler' is not assignable to type '() => number | undefined'.
146
+ ,
147
+ getPos: this.getPos,
148
+ editorView: this.view,
149
+ api: this.api
150
+ }));
151
+ }
152
+ viewShouldUpdate(nextNode) {
153
+ /**
154
+ * To ensure the placeholder is correctly toggled we need to allow react to re-render
155
+ * on first character insertion.
156
+ * Note: last character deletion is handled externally and automatically re-renders.
157
+ */
158
+ return this.isContentEmpty(this.node) && !!nextNode.content.childCount;
159
+ }
160
+ update(node, decorations) {
161
+ return super.update(node, decorations, undefined, (currentNode, newNode) =>
162
+ // Toggle the placeholder based on whether user input exists
163
+ !this.isContentEmpty(newNode) && !!(currentNode.attrs.state === newNode.attrs.state));
164
+ }
165
+ }
166
+ export function taskItemNodeViewFactory(portalProviderAPI, eventDispatcher, providerFactory, api) {
167
+ return (node, view, getPos) => {
168
+ const hasIntlContext = true;
169
+ return new Task(node, view, getPos, portalProviderAPI, eventDispatcher, {
170
+ providerFactory
171
+ }, undefined, undefined, undefined, hasIntlContext).initWithAPI(api);
172
+ };
173
+ }
@@ -0,0 +1,154 @@
1
+ /** @jsx jsx */
2
+ import { css, jsx } from '@emotion/react';
3
+ import { decisionItem, decisionList, taskItem, taskList } from '@atlaskit/adf-schema';
4
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
5
+ import { MAX_INDENTATION_LEVEL } from '@atlaskit/editor-common/indentation';
6
+ import { toolbarInsertBlockMessages as insertBlockMessages } from '@atlaskit/editor-common/messages';
7
+ import { IconAction, IconDecision } from '@atlaskit/editor-common/quick-insert';
8
+ import { getListTypes, insertTaskDecisionAction, insertTaskDecisionCommand } from './commands';
9
+ import { getCurrentIndentLevel, getTaskItemIndex, isInsideTask } from './pm-plugins/helpers';
10
+ import inputRulePlugin from './pm-plugins/input-rules';
11
+ import keymap, { getIndentCommand, getUnindentCommand } from './pm-plugins/keymaps';
12
+ import { createPlugin } from './pm-plugins/main';
13
+ import { stateKey as taskPluginKey } from './pm-plugins/plugin-key';
14
+ import ToolbarDecision from './ui/ToolbarDecision';
15
+ import ToolbarTask from './ui/ToolbarTask';
16
+ const taskDecisionToolbarGroupStyles = css({
17
+ display: 'flex'
18
+ });
19
+ const addItem = (insert, listType, schema) => ({
20
+ listLocalId,
21
+ itemLocalId
22
+ }) => {
23
+ const {
24
+ list,
25
+ item
26
+ } = getListTypes(listType, schema);
27
+ return insert(list.createChecked({
28
+ localId: listLocalId
29
+ }, item.createChecked({
30
+ localId: itemLocalId
31
+ })));
32
+ };
33
+ export const tasksAndDecisionsPlugin = ({
34
+ config: {
35
+ allowNestedTasks,
36
+ consumeTabs,
37
+ useLongPressSelection
38
+ } = {},
39
+ api
40
+ }) => {
41
+ var _api$analytics, _api$analytics2, _api$analytics3;
42
+ return {
43
+ name: 'taskDecision',
44
+ nodes() {
45
+ return [{
46
+ name: 'decisionList',
47
+ node: decisionList
48
+ }, {
49
+ name: 'decisionItem',
50
+ node: decisionItem
51
+ }, {
52
+ name: 'taskList',
53
+ node: taskList
54
+ }, {
55
+ name: 'taskItem',
56
+ node: taskItem
57
+ }];
58
+ },
59
+ getSharedState(editorState) {
60
+ if (!editorState) {
61
+ return undefined;
62
+ }
63
+ const pluginState = taskPluginKey.getState(editorState);
64
+ const indentLevel = getCurrentIndentLevel(editorState.selection) || 0;
65
+ const itemIndex = getTaskItemIndex(editorState);
66
+ return {
67
+ focusedTaskItemLocalId: (pluginState === null || pluginState === void 0 ? void 0 : pluginState.focusedTaskItemLocalId) || null,
68
+ isInsideTask: isInsideTask(editorState),
69
+ indentDisabled: itemIndex === 0 || indentLevel >= MAX_INDENTATION_LEVEL,
70
+ outdentDisabled: indentLevel <= 1
71
+ };
72
+ },
73
+ actions: {
74
+ insertTaskDecision: insertTaskDecisionCommand(api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions),
75
+ indentTaskList: getIndentCommand(api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions),
76
+ outdentTaskList: getUnindentCommand(api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions)
77
+ },
78
+ pmPlugins() {
79
+ return [{
80
+ name: 'tasksAndDecisions',
81
+ plugin: ({
82
+ portalProviderAPI,
83
+ providerFactory,
84
+ eventDispatcher,
85
+ dispatch
86
+ }) => {
87
+ return createPlugin(portalProviderAPI, eventDispatcher, providerFactory, dispatch, api, useLongPressSelection);
88
+ }
89
+ }, {
90
+ name: 'tasksAndDecisionsInputRule',
91
+ plugin: ({
92
+ schema,
93
+ featureFlags
94
+ }) => {
95
+ var _api$analytics4;
96
+ return inputRulePlugin(api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions)(schema, featureFlags);
97
+ }
98
+ }, {
99
+ name: 'tasksAndDecisionsKeyMap',
100
+ plugin: ({
101
+ schema
102
+ }) => keymap(schema, api, allowNestedTasks, consumeTabs)
103
+ } // Needs to be after "save-on-enter"
104
+ ];
105
+ },
106
+
107
+ secondaryToolbarComponent({
108
+ editorView,
109
+ disabled
110
+ }) {
111
+ return jsx("div", {
112
+ css: taskDecisionToolbarGroupStyles
113
+ }, jsx(ToolbarDecision, {
114
+ editorView: editorView,
115
+ isDisabled: disabled,
116
+ isReducedSpacing: true,
117
+ editorAPI: api
118
+ }), jsx(ToolbarTask, {
119
+ editorView: editorView,
120
+ isDisabled: disabled,
121
+ isReducedSpacing: true,
122
+ editorAPI: api
123
+ }));
124
+ },
125
+ pluginsOptions: {
126
+ quickInsert: ({
127
+ formatMessage
128
+ }) => [{
129
+ id: 'action',
130
+ title: formatMessage(insertBlockMessages.action),
131
+ description: formatMessage(insertBlockMessages.actionDescription),
132
+ priority: 100,
133
+ keywords: ['checkbox', 'task', 'todo'],
134
+ keyshortcut: '[]',
135
+ icon: () => jsx(IconAction, null),
136
+ action(insert, state) {
137
+ var _api$analytics5;
138
+ return insertTaskDecisionAction(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions)(state, 'taskList', INPUT_METHOD.QUICK_INSERT, addItem(insert, 'taskList', state.schema));
139
+ }
140
+ }, {
141
+ id: 'decision',
142
+ title: formatMessage(insertBlockMessages.decision),
143
+ description: formatMessage(insertBlockMessages.decisionDescription),
144
+ priority: 900,
145
+ keyshortcut: '<>',
146
+ icon: () => jsx(IconDecision, null),
147
+ action(insert, state) {
148
+ var _api$analytics6;
149
+ return insertTaskDecisionAction(api === null || api === void 0 ? void 0 : (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions)(state, 'decisionList', INPUT_METHOD.QUICK_INSERT, addItem(insert, 'decisionList', state.schema));
150
+ }
151
+ }]
152
+ }
153
+ };
154
+ };
@@ -0,0 +1,94 @@
1
+ import { findCutBefore } from '@atlaskit/editor-common/commands';
2
+ import { findWrapping, ReplaceAroundStep } from '@atlaskit/editor-prosemirror/transform';
3
+ import { normalizeTaskItemsSelection } from '../utils';
4
+ import { getBlockRange, isActionOrDecisionItem, isActionOrDecisionList, liftBlock, subtreeHeight } from './helpers';
5
+ export const liftSelection = (state, dispatch) => {
6
+ const normalizedSelection = normalizeTaskItemsSelection(state.selection);
7
+ const {
8
+ $from,
9
+ $to
10
+ } = normalizedSelection;
11
+ const tr = liftBlock(state.tr, $from, $to);
12
+ if (dispatch && tr) {
13
+ dispatch(tr);
14
+ }
15
+ return !!tr;
16
+ };
17
+ export const wrapSelectionInTaskList = (state, dispatch) => {
18
+ const {
19
+ $from,
20
+ $to
21
+ } = normalizeTaskItemsSelection(state.selection);
22
+
23
+ // limit ui indentation to 6 levels
24
+ const {
25
+ taskList,
26
+ taskItem
27
+ } = state.schema.nodes;
28
+ const maxDepth = subtreeHeight($from, $to, [taskList, taskItem]);
29
+ if (maxDepth >= 6) {
30
+ return true;
31
+ }
32
+ const blockRange = getBlockRange($from, $to);
33
+ if (!blockRange) {
34
+ return true;
35
+ }
36
+ const wrapping = findWrapping(blockRange, state.schema.nodes.taskList);
37
+ if (!wrapping) {
38
+ return true;
39
+ }
40
+ if (dispatch) {
41
+ dispatch(state.tr.wrap(blockRange, wrapping).scrollIntoView());
42
+ }
43
+ return true;
44
+ };
45
+
46
+ /**
47
+ * Tries to move the paragraph content near the given position into the taskItem or decisionItem
48
+ * before it.
49
+ *
50
+ * Looks backwards from the given position to find the "cut point" between the last taskItem and the
51
+ * following paragraph. Then tries to move the content from that paragraph into the taskItem.
52
+ *
53
+ * @param $pos Position at the end of, or anywhere in paragraph following, the last taskItem
54
+ * @see {joinToPreviousListItem}
55
+ */
56
+ export const joinAtCut = $pos => (state, dispatch) => {
57
+ const $cut = findCutBefore($pos);
58
+ if (!$cut) {
59
+ return false;
60
+ }
61
+ const {
62
+ paragraph
63
+ } = $cut.doc.type.schema.nodes;
64
+
65
+ // find the boundary between the taskList and paragraph
66
+ if ($cut.nodeBefore && isActionOrDecisionList($cut.nodeBefore) && $cut.nodeAfter && $cut.nodeAfter.type === paragraph) {
67
+ // we'll find the boundary of a taskList
68
+ // so resolve -1 to find the inside end of the last taskItem
69
+ let $lastNode = $cut.doc.resolve($cut.pos - 1);
70
+
71
+ // might have deeply nested taskList, keep trying to find it
72
+ while (!isActionOrDecisionItem($lastNode.parent)) {
73
+ $lastNode = state.doc.resolve($lastNode.pos - 1);
74
+ }
75
+
76
+ // grab the structure between the taskItem and the paragraph
77
+ // note: structure = true in ReplaceAroundStep
78
+ const slice = state.tr.doc.slice($lastNode.pos, $cut.pos);
79
+
80
+ // collapse the range between end of last taskItem and after the paragraph
81
+ // with the gap being the paragraph's content (i.e. take that content)
82
+ //
83
+ // we pass the structure we found earlier to join the p and taskItem nodes
84
+ //
85
+ // see https://prosemirror.net/docs/ref/#transform.ReplaceStep.constructor
86
+ // see https://prosemirror.net/docs/ref/#transform.ReplaceAroundStep.constructor
87
+ const tr = state.tr.step(new ReplaceAroundStep($lastNode.pos, $cut.pos + $cut.nodeAfter.nodeSize, $cut.pos + 1, $cut.pos + $cut.nodeAfter.nodeSize - 1, slice, 0, true));
88
+ if (dispatch) {
89
+ dispatch(tr);
90
+ }
91
+ return true;
92
+ }
93
+ return false;
94
+ };