@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.
- package/.eslintrc.js +26 -0
- package/CHANGELOG.md +16 -0
- package/dist/cjs/commands.js +288 -0
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/nodeviews/decisionItem.js +117 -0
- package/dist/cjs/nodeviews/hooks/use-show-placeholder.js +40 -0
- package/dist/cjs/nodeviews/taskItem.js +211 -0
- package/dist/cjs/plugin.js +166 -0
- package/dist/cjs/pm-plugins/commands.js +96 -0
- package/dist/cjs/pm-plugins/helpers.js +299 -0
- package/dist/cjs/pm-plugins/input-rules.js +94 -0
- package/dist/cjs/pm-plugins/keymaps.js +376 -0
- package/dist/cjs/pm-plugins/main.js +263 -0
- package/dist/cjs/pm-plugins/plugin-key.js +8 -0
- package/dist/cjs/pm-plugins/types.js +11 -0
- package/dist/cjs/types.js +5 -0
- package/dist/cjs/ui/Decision/index.js +30 -0
- package/dist/cjs/ui/Task/index.js +81 -0
- package/dist/cjs/ui/Task/task-item-with-providers.js +138 -0
- package/dist/cjs/ui/ToolbarDecision/index.js +41 -0
- package/dist/cjs/ui/ToolbarTask/index.js +40 -0
- package/dist/cjs/utils.js +41 -0
- package/dist/es2019/commands.js +279 -0
- package/dist/es2019/index.js +1 -1
- package/dist/es2019/nodeviews/decisionItem.js +76 -0
- package/dist/es2019/nodeviews/hooks/use-show-placeholder.js +36 -0
- package/dist/es2019/nodeviews/taskItem.js +173 -0
- package/dist/es2019/plugin.js +154 -0
- package/dist/es2019/pm-plugins/commands.js +94 -0
- package/dist/es2019/pm-plugins/helpers.js +316 -0
- package/dist/es2019/pm-plugins/input-rules.js +91 -0
- package/dist/es2019/pm-plugins/keymaps.js +370 -0
- package/dist/es2019/pm-plugins/main.js +262 -0
- package/dist/es2019/pm-plugins/plugin-key.js +2 -0
- package/dist/es2019/pm-plugins/types.js +5 -0
- package/dist/es2019/types.js +1 -0
- package/dist/es2019/ui/Decision/index.js +26 -0
- package/dist/es2019/ui/Task/index.js +55 -0
- package/dist/es2019/ui/Task/task-item-with-providers.js +72 -0
- package/dist/es2019/ui/ToolbarDecision/index.js +37 -0
- package/dist/es2019/ui/ToolbarTask/index.js +36 -0
- package/dist/es2019/utils.js +32 -0
- package/dist/esm/commands.js +281 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/nodeviews/decisionItem.js +110 -0
- package/dist/esm/nodeviews/hooks/use-show-placeholder.js +34 -0
- package/dist/esm/nodeviews/taskItem.js +204 -0
- package/dist/esm/plugin.js +155 -0
- package/dist/esm/pm-plugins/commands.js +90 -0
- package/dist/esm/pm-plugins/helpers.js +285 -0
- package/dist/esm/pm-plugins/input-rules.js +87 -0
- package/dist/esm/pm-plugins/keymaps.js +368 -0
- package/dist/esm/pm-plugins/main.js +256 -0
- package/dist/esm/pm-plugins/plugin-key.js +2 -0
- package/dist/esm/pm-plugins/types.js +5 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/ui/Decision/index.js +23 -0
- package/dist/esm/ui/Task/index.js +71 -0
- package/dist/esm/ui/Task/task-item-with-providers.js +129 -0
- package/dist/esm/ui/ToolbarDecision/index.js +34 -0
- package/dist/esm/ui/ToolbarTask/index.js +33 -0
- package/dist/esm/utils.js +30 -0
- package/dist/types/commands.d.ts +16 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/nodeviews/decisionItem.d.ts +10 -0
- package/dist/types/nodeviews/hooks/use-show-placeholder.d.ts +11 -0
- package/dist/types/nodeviews/taskItem.d.ts +14 -0
- package/dist/types/plugin.d.ts +2 -0
- package/dist/types/pm-plugins/commands.d.ts +15 -0
- package/dist/types/pm-plugins/helpers.d.ts +76 -0
- package/dist/types/pm-plugins/input-rules.d.ts +6 -0
- package/dist/types/pm-plugins/keymaps.d.ts +11 -0
- package/dist/types/pm-plugins/main.d.ts +7 -0
- package/dist/types/pm-plugins/plugin-key.d.ts +2 -0
- package/dist/types/pm-plugins/types.d.ts +8 -0
- package/dist/types/types.d.ts +49 -0
- package/dist/types/ui/Decision/index.d.ts +15 -0
- package/dist/types/ui/Task/index.d.ts +28 -0
- package/dist/types/ui/Task/task-item-with-providers.d.ts +29 -0
- package/dist/types/ui/ToolbarDecision/index.d.ts +18 -0
- package/dist/types/ui/ToolbarTask/index.d.ts +18 -0
- package/dist/types/utils.d.ts +4 -0
- package/dist/types-ts4.5/commands.d.ts +16 -0
- package/dist/types-ts4.5/index.d.ts +2 -1
- package/dist/types-ts4.5/nodeviews/decisionItem.d.ts +10 -0
- package/dist/types-ts4.5/nodeviews/hooks/use-show-placeholder.d.ts +11 -0
- package/dist/types-ts4.5/nodeviews/taskItem.d.ts +14 -0
- package/dist/types-ts4.5/plugin.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/commands.d.ts +15 -0
- package/dist/types-ts4.5/pm-plugins/helpers.d.ts +76 -0
- package/dist/types-ts4.5/pm-plugins/input-rules.d.ts +6 -0
- package/dist/types-ts4.5/pm-plugins/keymaps.d.ts +11 -0
- package/dist/types-ts4.5/pm-plugins/main.d.ts +7 -0
- package/dist/types-ts4.5/pm-plugins/plugin-key.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/types.d.ts +8 -0
- package/dist/types-ts4.5/types.d.ts +49 -0
- package/dist/types-ts4.5/ui/Decision/index.d.ts +15 -0
- package/dist/types-ts4.5/ui/Task/index.d.ts +28 -0
- package/dist/types-ts4.5/ui/Task/task-item-with-providers.d.ts +29 -0
- package/dist/types-ts4.5/ui/ToolbarDecision/index.d.ts +18 -0
- package/dist/types-ts4.5/ui/ToolbarTask/index.d.ts +18 -0
- package/dist/types-ts4.5/utils.d.ts +4 -0
- package/package.json +16 -6
- package/report.api.md +100 -1
- 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
|
+
};
|