@atlaskit/editor-plugin-annotation 0.1.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 +18 -0
- package/CHANGELOG.md +1 -0
- package/LICENSE.md +13 -0
- package/README.md +30 -0
- package/dist/cjs/commands/index.js +150 -0
- package/dist/cjs/commands/transform.js +86 -0
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/nodeviews/index.js +59 -0
- package/dist/cjs/plugin.js +132 -0
- package/dist/cjs/pm-plugins/inline-comment.js +246 -0
- package/dist/cjs/pm-plugins/keymap.js +15 -0
- package/dist/cjs/pm-plugins/plugin-factory.js +107 -0
- package/dist/cjs/pm-plugins/reducer.js +84 -0
- package/dist/cjs/pm-plugins/types.js +17 -0
- package/dist/cjs/toolbar.js +59 -0
- package/dist/cjs/types.js +20 -0
- package/dist/cjs/ui/AnnotationViewWrapper.js +39 -0
- package/dist/cjs/ui/InlineCommentView.js +149 -0
- package/dist/cjs/utils.js +372 -0
- package/dist/es2019/commands/index.js +123 -0
- package/dist/es2019/commands/transform.js +64 -0
- package/dist/es2019/index.js +1 -0
- package/dist/es2019/nodeviews/index.js +31 -0
- package/dist/es2019/plugin.js +127 -0
- package/dist/es2019/pm-plugins/inline-comment.js +181 -0
- package/dist/es2019/pm-plugins/keymap.js +9 -0
- package/dist/es2019/pm-plugins/plugin-factory.js +108 -0
- package/dist/es2019/pm-plugins/reducer.js +94 -0
- package/dist/es2019/pm-plugins/types.js +11 -0
- package/dist/es2019/toolbar.js +53 -0
- package/dist/es2019/types.js +14 -0
- package/dist/es2019/ui/AnnotationViewWrapper.js +15 -0
- package/dist/es2019/ui/InlineCommentView.js +145 -0
- package/dist/es2019/utils.js +334 -0
- package/dist/esm/commands/index.js +143 -0
- package/dist/esm/commands/transform.js +80 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/nodeviews/index.js +52 -0
- package/dist/esm/plugin.js +120 -0
- package/dist/esm/pm-plugins/inline-comment.js +239 -0
- package/dist/esm/pm-plugins/keymap.js +9 -0
- package/dist/esm/pm-plugins/plugin-factory.js +101 -0
- package/dist/esm/pm-plugins/reducer.js +77 -0
- package/dist/esm/pm-plugins/types.js +11 -0
- package/dist/esm/toolbar.js +52 -0
- package/dist/esm/types.js +14 -0
- package/dist/esm/ui/AnnotationViewWrapper.js +32 -0
- package/dist/esm/ui/InlineCommentView.js +142 -0
- package/dist/esm/utils.js +345 -0
- package/dist/types/commands/index.d.ts +15 -0
- package/dist/types/commands/transform.d.ts +11 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/nodeviews/index.d.ts +11 -0
- package/dist/types/plugin.d.ts +6 -0
- package/dist/types/pm-plugins/inline-comment.d.ts +3 -0
- package/dist/types/pm-plugins/keymap.d.ts +3 -0
- package/dist/types/pm-plugins/plugin-factory.d.ts +2 -0
- package/dist/types/pm-plugins/reducer.d.ts +3 -0
- package/dist/types/pm-plugins/types.d.ts +78 -0
- package/dist/types/toolbar.d.ts +5 -0
- package/dist/types/types.d.ts +100 -0
- package/dist/types/ui/AnnotationViewWrapper.d.ts +10 -0
- package/dist/types/ui/InlineCommentView.d.ts +12 -0
- package/dist/types/utils.d.ts +44 -0
- package/dist/types-ts4.5/commands/index.d.ts +15 -0
- package/dist/types-ts4.5/commands/transform.d.ts +11 -0
- package/dist/types-ts4.5/index.d.ts +3 -0
- package/dist/types-ts4.5/nodeviews/index.d.ts +11 -0
- package/dist/types-ts4.5/plugin.d.ts +6 -0
- package/dist/types-ts4.5/pm-plugins/inline-comment.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/keymap.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/plugin-factory.d.ts +2 -0
- package/dist/types-ts4.5/pm-plugins/reducer.d.ts +3 -0
- package/dist/types-ts4.5/pm-plugins/types.d.ts +78 -0
- package/dist/types-ts4.5/toolbar.d.ts +5 -0
- package/dist/types-ts4.5/types.d.ts +102 -0
- package/dist/types-ts4.5/ui/AnnotationViewWrapper.d.ts +10 -0
- package/dist/types-ts4.5/ui/InlineCommentView.d.ts +12 -0
- package/dist/types-ts4.5/utils.d.ts +44 -0
- package/package.json +106 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.InlineCommentView = InlineCommentView;
|
|
8
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _react = _interopRequireDefault(require("react"));
|
|
10
|
+
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
11
|
+
var _utils = require("@atlaskit/editor-prosemirror/utils");
|
|
12
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
13
|
+
var _commands = require("../commands");
|
|
14
|
+
var _types = require("../types");
|
|
15
|
+
var _utils2 = require("../utils");
|
|
16
|
+
var _AnnotationViewWrapper = require("./AnnotationViewWrapper");
|
|
17
|
+
var findPosForDOM = function findPosForDOM(sel) {
|
|
18
|
+
var $from = sel.$from,
|
|
19
|
+
from = sel.from;
|
|
20
|
+
|
|
21
|
+
// Retrieve current TextNode
|
|
22
|
+
var index = $from.index();
|
|
23
|
+
var node = index < $from.parent.childCount && $from.parent.child(index);
|
|
24
|
+
|
|
25
|
+
// Right edge of a mark.
|
|
26
|
+
if (!node && $from.nodeBefore && $from.nodeBefore.isText && $from.nodeBefore.marks.find(function (mark) {
|
|
27
|
+
return mark.type.name === 'annotation';
|
|
28
|
+
})) {
|
|
29
|
+
return from - 1;
|
|
30
|
+
}
|
|
31
|
+
return from;
|
|
32
|
+
};
|
|
33
|
+
function InlineCommentView(_ref) {
|
|
34
|
+
var providers = _ref.providers,
|
|
35
|
+
editorView = _ref.editorView,
|
|
36
|
+
editorAnalyticsAPI = _ref.editorAnalyticsAPI,
|
|
37
|
+
dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent;
|
|
38
|
+
// As inlineComment is the only annotation present, this function is not generic
|
|
39
|
+
var inlineCommentProvider = providers.inlineComment;
|
|
40
|
+
var state = editorView.state,
|
|
41
|
+
dispatch = editorView.dispatch;
|
|
42
|
+
var CreateComponent = inlineCommentProvider.createComponent,
|
|
43
|
+
ViewComponent = inlineCommentProvider.viewComponent;
|
|
44
|
+
var inlineCommentState = (0, _utils2.getPluginState)(state);
|
|
45
|
+
var _ref2 = inlineCommentState || {},
|
|
46
|
+
bookmark = _ref2.bookmark,
|
|
47
|
+
selectedAnnotations = _ref2.selectedAnnotations,
|
|
48
|
+
annotations = _ref2.annotations,
|
|
49
|
+
isInlineCommentViewClosed = _ref2.isInlineCommentViewClosed;
|
|
50
|
+
var annotationsList = (0, _utils2.getAllAnnotations)(editorView.state.doc);
|
|
51
|
+
var selection = (0, _utils2.getSelectionPositions)(state, inlineCommentState);
|
|
52
|
+
var position = findPosForDOM(selection);
|
|
53
|
+
var dom;
|
|
54
|
+
try {
|
|
55
|
+
dom = (0, _utils.findDomRefAtPos)(position, editorView.domAtPos.bind(editorView));
|
|
56
|
+
} catch (error) {
|
|
57
|
+
// eslint-disable-next-line no-console
|
|
58
|
+
console.warn(error);
|
|
59
|
+
if (dispatchAnalyticsEvent) {
|
|
60
|
+
var payload = {
|
|
61
|
+
action: _analytics.ACTION.ERRORED,
|
|
62
|
+
actionSubject: _analytics.ACTION_SUBJECT.CONTENT_COMPONENT,
|
|
63
|
+
eventType: _analytics.EVENT_TYPE.OPERATIONAL,
|
|
64
|
+
attributes: {
|
|
65
|
+
component: _analytics.CONTENT_COMPONENT.INLINE_COMMENT,
|
|
66
|
+
selection: selection.toJSON(),
|
|
67
|
+
position: position,
|
|
68
|
+
docSize: editorView.state.doc.nodeSize,
|
|
69
|
+
error: error.toString()
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
dispatchAnalyticsEvent(payload);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!dom) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Create Component
|
|
80
|
+
if (bookmark) {
|
|
81
|
+
if (!CreateComponent) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
//getting all text between bookmarked positions
|
|
86
|
+
var textSelection = state.doc.textBetween(selection.from, selection.to);
|
|
87
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
88
|
+
"data-testid": _types.AnnotationTestIds.floatingComponent,
|
|
89
|
+
"data-editor-popup": "true"
|
|
90
|
+
}, /*#__PURE__*/_react.default.createElement(CreateComponent, {
|
|
91
|
+
dom: dom,
|
|
92
|
+
textSelection: textSelection,
|
|
93
|
+
onCreate: function onCreate(id) {
|
|
94
|
+
(0, _commands.createAnnotation)(editorAnalyticsAPI)(id)(editorView.state, editorView.dispatch);
|
|
95
|
+
!editorView.hasFocus() && editorView.focus();
|
|
96
|
+
},
|
|
97
|
+
onClose: function onClose() {
|
|
98
|
+
(0, _commands.setInlineCommentDraftState)(editorAnalyticsAPI)(false)(editorView.state, editorView.dispatch);
|
|
99
|
+
!editorView.hasFocus() && editorView.focus();
|
|
100
|
+
}
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// View Component
|
|
105
|
+
var activeAnnotations = (selectedAnnotations === null || selectedAnnotations === void 0 ? void 0 : selectedAnnotations.filter(function (mark) {
|
|
106
|
+
return annotations && annotations[mark.id] === false;
|
|
107
|
+
})) || [];
|
|
108
|
+
if (!ViewComponent || activeAnnotations.length === 0) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
var onAnnotationViewed = function onAnnotationViewed() {
|
|
112
|
+
if (!dispatchAnalyticsEvent) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// fire analytics
|
|
116
|
+
var payload = {
|
|
117
|
+
action: _analytics.ACTION.VIEWED,
|
|
118
|
+
actionSubject: _analytics.ACTION_SUBJECT.ANNOTATION,
|
|
119
|
+
actionSubjectId: _analytics.ACTION_SUBJECT_ID.INLINE_COMMENT,
|
|
120
|
+
eventType: _analytics.EVENT_TYPE.TRACK,
|
|
121
|
+
attributes: {
|
|
122
|
+
overlap: activeAnnotations.length ? activeAnnotations.length - 1 : 0
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
dispatchAnalyticsEvent(payload);
|
|
126
|
+
};
|
|
127
|
+
if ((0, _platformFeatureFlags.getBooleanFF)('platform.editor.annotation.decouple-inline-comment-closed_flmox') && isInlineCommentViewClosed || !selectedAnnotations) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
return /*#__PURE__*/_react.default.createElement(_AnnotationViewWrapper.AnnotationViewWrapper, {
|
|
131
|
+
"data-editor-popup": "true",
|
|
132
|
+
"data-testid": _types.AnnotationTestIds.floatingComponent,
|
|
133
|
+
key: (0, _utils2.getAnnotationViewKey)(activeAnnotations),
|
|
134
|
+
onViewed: onAnnotationViewed
|
|
135
|
+
}, /*#__PURE__*/_react.default.createElement(ViewComponent, {
|
|
136
|
+
annotationsList: annotationsList,
|
|
137
|
+
annotations: activeAnnotations,
|
|
138
|
+
dom: dom,
|
|
139
|
+
onDelete: function onDelete(id) {
|
|
140
|
+
return (0, _commands.removeInlineCommentNearSelection)(id)(state, dispatch);
|
|
141
|
+
},
|
|
142
|
+
onResolve: function onResolve(id) {
|
|
143
|
+
return (0, _commands.updateInlineCommentResolvedState)(editorAnalyticsAPI)((0, _defineProperty2.default)({}, id, true), _analytics.RESOLVE_METHOD.COMPONENT)(editorView.state, editorView.dispatch);
|
|
144
|
+
},
|
|
145
|
+
onClose: function onClose() {
|
|
146
|
+
(0, _commands.closeComponent)()(editorView.state, editorView.dispatch);
|
|
147
|
+
}
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.addDraftDecoration = void 0;
|
|
7
|
+
exports.annotationExists = annotationExists;
|
|
8
|
+
Object.defineProperty(exports, "containsAnyAnnotations", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function get() {
|
|
11
|
+
return _utils.containsAnyAnnotations;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
exports.getPluginState = exports.getDraftCommandAnalyticsPayload = exports.getAnnotationViewKey = exports.getAllAnnotations = exports.findAnnotationsInSelection = void 0;
|
|
15
|
+
exports.getSelectionPositions = getSelectionPositions;
|
|
16
|
+
Object.defineProperty(exports, "hasAnnotationMark", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function get() {
|
|
19
|
+
return _utils.hasAnnotationMark;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
exports.hasInvalidNodes = void 0;
|
|
23
|
+
exports.hasInvalidWhitespaceNode = hasInvalidWhitespaceNode;
|
|
24
|
+
exports.inlineCommentPluginKey = void 0;
|
|
25
|
+
exports.isSelectedAnnotationsChanged = isSelectedAnnotationsChanged;
|
|
26
|
+
exports.isSelectionValid = void 0;
|
|
27
|
+
exports.stripNonExistingAnnotations = stripNonExistingAnnotations;
|
|
28
|
+
exports.surroundingMarks = void 0;
|
|
29
|
+
var _adfSchema = require("@atlaskit/adf-schema");
|
|
30
|
+
var _analytics = require("@atlaskit/editor-common/analytics");
|
|
31
|
+
var _styles = require("@atlaskit/editor-common/styles");
|
|
32
|
+
var _utils = require("@atlaskit/editor-common/utils");
|
|
33
|
+
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
34
|
+
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
35
|
+
var _types = require("./types");
|
|
36
|
+
function sum(arr, f) {
|
|
37
|
+
return arr.reduce(function (val, x) {
|
|
38
|
+
return val + f(x);
|
|
39
|
+
}, 0);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Finds the marks in the nodes to the left and right.
|
|
43
|
+
* @param $pos Position to center search around
|
|
44
|
+
*/
|
|
45
|
+
var surroundingMarks = exports.surroundingMarks = function surroundingMarks($pos) {
|
|
46
|
+
var nodeBefore = $pos.nodeBefore,
|
|
47
|
+
nodeAfter = $pos.nodeAfter;
|
|
48
|
+
var markNodeBefore = nodeBefore && $pos.doc.nodeAt(Math.max(0, $pos.pos - nodeBefore.nodeSize - 1));
|
|
49
|
+
var markNodeAfter = nodeAfter && $pos.doc.nodeAt($pos.pos + nodeAfter.nodeSize);
|
|
50
|
+
return [markNodeBefore && markNodeBefore.marks || [], markNodeAfter && markNodeAfter.marks || []];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Finds annotation marks, and returns their IDs.
|
|
55
|
+
* @param marks Array of marks to search in
|
|
56
|
+
*/
|
|
57
|
+
var filterAnnotationIds = function filterAnnotationIds(marks) {
|
|
58
|
+
if (!marks.length) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
var annotation = marks[0].type.schema.marks.annotation;
|
|
62
|
+
return marks.filter(function (mark) {
|
|
63
|
+
return mark.type === annotation;
|
|
64
|
+
}).map(function (mark) {
|
|
65
|
+
return mark.attrs.id;
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Re-orders the annotation array based on the order in the document.
|
|
71
|
+
*
|
|
72
|
+
* This places the marks that do not appear in the surrounding nodes
|
|
73
|
+
* higher in the list. That is, the inner-most one appears first.
|
|
74
|
+
*
|
|
75
|
+
* Undo, for example, can re-order annotation marks in the document.
|
|
76
|
+
* @param annotations annotation metadata
|
|
77
|
+
* @param $from location to look around (usually the selection)
|
|
78
|
+
*/
|
|
79
|
+
var reorderAnnotations = function reorderAnnotations(annotations, $from) {
|
|
80
|
+
var idSet = surroundingMarks($from).map(filterAnnotationIds);
|
|
81
|
+
annotations.sort(function (a, b) {
|
|
82
|
+
return sum(idSet, function (ids) {
|
|
83
|
+
return ids.indexOf(a.id);
|
|
84
|
+
}) - sum(idSet, function (ids) {
|
|
85
|
+
return ids.indexOf(b.id);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
var getAllAnnotations = exports.getAllAnnotations = function getAllAnnotations(doc) {
|
|
90
|
+
var allAnnotationIds = new Set();
|
|
91
|
+
doc.descendants(function (node) {
|
|
92
|
+
node.marks.filter(function (mark) {
|
|
93
|
+
return mark.type.name === 'annotation';
|
|
94
|
+
})
|
|
95
|
+
// filter out annotations with invalid attributes as they cause errors when interacting with them
|
|
96
|
+
.filter(validateAnnotationMark).forEach(function (m) {
|
|
97
|
+
return allAnnotationIds.add(m.attrs.id);
|
|
98
|
+
});
|
|
99
|
+
return true;
|
|
100
|
+
});
|
|
101
|
+
return Array.from(allAnnotationIds);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/*
|
|
105
|
+
* verifies if annotation mark contains valid attributes
|
|
106
|
+
*/
|
|
107
|
+
var validateAnnotationMark = function validateAnnotationMark(annotationMark) {
|
|
108
|
+
var _ref = annotationMark.attrs,
|
|
109
|
+
id = _ref.id,
|
|
110
|
+
annotationType = _ref.annotationType;
|
|
111
|
+
return validateAnnotationId(id) && validateAnnotationType(annotationType);
|
|
112
|
+
function validateAnnotationId(id) {
|
|
113
|
+
if (!id || typeof id !== 'string') {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
var invalidIds = ['null', 'undefined'];
|
|
117
|
+
return !invalidIds.includes(id.toLowerCase());
|
|
118
|
+
}
|
|
119
|
+
function validateAnnotationType(type) {
|
|
120
|
+
if (!type || typeof type !== 'string') {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
var allowedTypes = Object.values(_adfSchema.AnnotationTypes);
|
|
124
|
+
return allowedTypes.includes(type);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/*
|
|
129
|
+
* add decoration for the comment selection in draft state
|
|
130
|
+
* (when creating new comment)
|
|
131
|
+
*/
|
|
132
|
+
var addDraftDecoration = exports.addDraftDecoration = function addDraftDecoration(start, end) {
|
|
133
|
+
return _view.Decoration.inline(start, end, {
|
|
134
|
+
class: "".concat(_styles.AnnotationSharedClassNames.draft)
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
var getAnnotationViewKey = exports.getAnnotationViewKey = function getAnnotationViewKey(annotations) {
|
|
138
|
+
var keys = annotations.map(function (mark) {
|
|
139
|
+
return mark.id;
|
|
140
|
+
}).join('_');
|
|
141
|
+
return "view-annotation-wrapper_".concat(keys);
|
|
142
|
+
};
|
|
143
|
+
var findAnnotationsInSelection = exports.findAnnotationsInSelection = function findAnnotationsInSelection(selection, doc) {
|
|
144
|
+
var empty = selection.empty,
|
|
145
|
+
$anchor = selection.$anchor,
|
|
146
|
+
anchor = selection.anchor;
|
|
147
|
+
// Only detect annotations on caret selection
|
|
148
|
+
if (!empty || !doc) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
var node = doc.nodeAt(anchor);
|
|
152
|
+
if (!node && !$anchor.nodeBefore) {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
var annotationMark = doc.type.schema.marks.annotation;
|
|
156
|
+
var nodeBefore = $anchor.nodeBefore;
|
|
157
|
+
var anchorAnnotationMarks = node && node.marks || [];
|
|
158
|
+
var marks = [];
|
|
159
|
+
if (annotationMark.isInSet(anchorAnnotationMarks)) {
|
|
160
|
+
marks = anchorAnnotationMarks;
|
|
161
|
+
} else if (nodeBefore && annotationMark.isInSet(nodeBefore.marks)) {
|
|
162
|
+
marks = nodeBefore.marks;
|
|
163
|
+
}
|
|
164
|
+
var annotations = marks.filter(function (mark) {
|
|
165
|
+
return mark.type.name === 'annotation';
|
|
166
|
+
}).map(function (mark) {
|
|
167
|
+
return {
|
|
168
|
+
id: mark.attrs.id,
|
|
169
|
+
type: mark.attrs.annotationType
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
reorderAnnotations(annotations, $anchor);
|
|
173
|
+
return annotations;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* get selection from position to apply new comment for
|
|
178
|
+
* @return bookmarked positions if they exists, otherwise current selection positions
|
|
179
|
+
*/
|
|
180
|
+
function getSelectionPositions(editorState, inlineCommentState) {
|
|
181
|
+
var _ref2 = inlineCommentState || {},
|
|
182
|
+
bookmark = _ref2.bookmark;
|
|
183
|
+
// get positions via saved bookmark if it is available
|
|
184
|
+
// this is to make comments box positioned relative to temporary highlight rather then current selection
|
|
185
|
+
if (bookmark) {
|
|
186
|
+
return bookmark.resolve(editorState.doc);
|
|
187
|
+
}
|
|
188
|
+
return editorState.selection;
|
|
189
|
+
}
|
|
190
|
+
var inlineCommentPluginKey = exports.inlineCommentPluginKey = new _state.PluginKey('inlineCommentPluginKey');
|
|
191
|
+
var getPluginState = exports.getPluginState = function getPluginState(state) {
|
|
192
|
+
return inlineCommentPluginKey.getState(state);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* get number of unique annotations within current selection
|
|
197
|
+
*/
|
|
198
|
+
var getAnnotationsInSelectionCount = function getAnnotationsInSelectionCount(state) {
|
|
199
|
+
var _state$selection = state.selection,
|
|
200
|
+
from = _state$selection.from,
|
|
201
|
+
to = _state$selection.to;
|
|
202
|
+
var annotations = (0, _utils.getAnnotationIdsFromRange)({
|
|
203
|
+
from: from,
|
|
204
|
+
to: to
|
|
205
|
+
}, state.doc, state.schema);
|
|
206
|
+
return annotations.length;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* get payload for the open/close analytics event
|
|
211
|
+
*/
|
|
212
|
+
var getDraftCommandAnalyticsPayload = exports.getDraftCommandAnalyticsPayload = function getDraftCommandAnalyticsPayload(drafting, inputMethod) {
|
|
213
|
+
var payload = function payload(state) {
|
|
214
|
+
var attributes = {};
|
|
215
|
+
if (drafting) {
|
|
216
|
+
attributes = {
|
|
217
|
+
inputMethod: inputMethod,
|
|
218
|
+
overlap: getAnnotationsInSelectionCount(state)
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
action: drafting ? _analytics.ACTION.OPENED : _analytics.ACTION.CLOSED,
|
|
223
|
+
actionSubject: _analytics.ACTION_SUBJECT.ANNOTATION,
|
|
224
|
+
actionSubjectId: _analytics.ACTION_SUBJECT_ID.INLINE_COMMENT,
|
|
225
|
+
eventType: _analytics.EVENT_TYPE.TRACK,
|
|
226
|
+
attributes: attributes
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
return payload;
|
|
230
|
+
};
|
|
231
|
+
var isSelectionValid = exports.isSelectionValid = function isSelectionValid(state) {
|
|
232
|
+
var selection = state.selection;
|
|
233
|
+
var _ref3 = getPluginState(state) || {},
|
|
234
|
+
disallowOnWhitespace = _ref3.disallowOnWhitespace;
|
|
235
|
+
if (selection.empty || !(selection instanceof _state.TextSelection || selection instanceof _state.AllSelection)) {
|
|
236
|
+
return _types.AnnotationSelectionType.INVALID;
|
|
237
|
+
}
|
|
238
|
+
var containsInvalidNodes = hasInvalidNodes(state);
|
|
239
|
+
|
|
240
|
+
// A selection that only covers 1 pos, and is an invalid node
|
|
241
|
+
// e.g. a text selection over a mention
|
|
242
|
+
if (containsInvalidNodes && selection.to - selection.from === 1) {
|
|
243
|
+
return _types.AnnotationSelectionType.INVALID;
|
|
244
|
+
}
|
|
245
|
+
if (containsInvalidNodes) {
|
|
246
|
+
return _types.AnnotationSelectionType.DISABLED;
|
|
247
|
+
}
|
|
248
|
+
if (disallowOnWhitespace && hasInvalidWhitespaceNode(selection, state.schema)) {
|
|
249
|
+
return _types.AnnotationSelectionType.INVALID;
|
|
250
|
+
}
|
|
251
|
+
if (isEmptyTextSelection(selection, state.schema)) {
|
|
252
|
+
return _types.AnnotationSelectionType.INVALID;
|
|
253
|
+
}
|
|
254
|
+
return _types.AnnotationSelectionType.VALID;
|
|
255
|
+
};
|
|
256
|
+
var hasInvalidNodes = exports.hasInvalidNodes = function hasInvalidNodes(state) {
|
|
257
|
+
var selection = state.selection,
|
|
258
|
+
doc = state.doc,
|
|
259
|
+
schema = state.schema;
|
|
260
|
+
return !(0, _utils.canApplyAnnotationOnRange)({
|
|
261
|
+
from: selection.from,
|
|
262
|
+
to: selection.to
|
|
263
|
+
}, doc, schema);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Checks if selection contains only empty text
|
|
268
|
+
* e.g. when you select across multiple empty paragraphs
|
|
269
|
+
*/
|
|
270
|
+
function isEmptyTextSelection(selection, schema) {
|
|
271
|
+
var _schema$nodes = schema.nodes,
|
|
272
|
+
text = _schema$nodes.text,
|
|
273
|
+
paragraph = _schema$nodes.paragraph;
|
|
274
|
+
var hasContent = false;
|
|
275
|
+
selection.content().content.descendants(function (node) {
|
|
276
|
+
// for empty paragraph - consider empty (nothing to comment on)
|
|
277
|
+
if (node.type === paragraph && !node.content.size) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
// for not a text or nonempty text - consider nonempty (can comment if the node is supported for annotations)
|
|
281
|
+
if (node.type !== text || !node.textContent) {
|
|
282
|
+
hasContent = true;
|
|
283
|
+
}
|
|
284
|
+
return !hasContent;
|
|
285
|
+
});
|
|
286
|
+
return !hasContent;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Checks if any of the nodes in a given selection are completely whitespace
|
|
291
|
+
* This is to conform to Confluence annotation specifications
|
|
292
|
+
*/
|
|
293
|
+
function hasInvalidWhitespaceNode(selection, schema) {
|
|
294
|
+
var foundInvalidWhitespace = false;
|
|
295
|
+
var content = selection.content().content;
|
|
296
|
+
content.descendants(function (node) {
|
|
297
|
+
if ((0, _utils.isText)(node, schema)) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
if (node.textContent.trim() === '') {
|
|
301
|
+
// Trailing new lines do not result in the annotation spanning into
|
|
302
|
+
// the trailing new line so can be ignored when looking for invalid
|
|
303
|
+
// whitespace nodes.
|
|
304
|
+
var nodeIsTrailingNewLine =
|
|
305
|
+
// it is the final node
|
|
306
|
+
node.eq(content.lastChild) &&
|
|
307
|
+
// and there are multiple nodes
|
|
308
|
+
!node.eq(content.firstChild) &&
|
|
309
|
+
// and it is a paragraph node
|
|
310
|
+
(0, _utils.isParagraph)(node, schema);
|
|
311
|
+
if (!nodeIsTrailingNewLine) {
|
|
312
|
+
foundInvalidWhitespace = true;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return !foundInvalidWhitespace;
|
|
316
|
+
});
|
|
317
|
+
return foundInvalidWhitespace;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/*
|
|
321
|
+
* verifies that the annotation exists by the given id
|
|
322
|
+
*/
|
|
323
|
+
function annotationExists(annotationId, state) {
|
|
324
|
+
var commentsPluginState = getPluginState(state);
|
|
325
|
+
return !!(commentsPluginState !== null && commentsPluginState !== void 0 && commentsPluginState.annotations) && Object.keys(commentsPluginState.annotations).includes(annotationId);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/*
|
|
329
|
+
* remove annotations that dont exsist in plugin state from slice
|
|
330
|
+
*/
|
|
331
|
+
function stripNonExistingAnnotations(slice, state) {
|
|
332
|
+
if (!slice.content.size) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
slice.content.forEach(function (node) {
|
|
336
|
+
stripNonExistingAnnotationsFromNode(node, state);
|
|
337
|
+
node.content.descendants(function (node) {
|
|
338
|
+
stripNonExistingAnnotationsFromNode(node, state);
|
|
339
|
+
return true;
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/*
|
|
345
|
+
* remove annotations that dont exsist in plugin state
|
|
346
|
+
* from node
|
|
347
|
+
*/
|
|
348
|
+
function stripNonExistingAnnotationsFromNode(node, state) {
|
|
349
|
+
if ((0, _utils.hasAnnotationMark)(node, state)) {
|
|
350
|
+
node.marks = node.marks.filter(function (mark) {
|
|
351
|
+
if (mark.type.name === 'annotation') {
|
|
352
|
+
return annotationExists(mark.attrs.id, state);
|
|
353
|
+
}
|
|
354
|
+
return true;
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
return node;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Compares two sets of annotationInfos to see if the annotations have changed
|
|
362
|
+
* This function assumes annotations will have unique id's for simplicity
|
|
363
|
+
*/
|
|
364
|
+
function isSelectedAnnotationsChanged(oldSelectedAnnotations, newSelectedAnnotations) {
|
|
365
|
+
return newSelectedAnnotations.length !== oldSelectedAnnotations.length ||
|
|
366
|
+
// assuming annotations have unique id's for simplicity
|
|
367
|
+
newSelectedAnnotations.some(function (annotation) {
|
|
368
|
+
return !oldSelectedAnnotations.find(function (pluginStateAnnotation) {
|
|
369
|
+
return annotation.id === pluginStateAnnotation.id && annotation.type === pluginStateAnnotation.type;
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { AnnotationTypes } from '@atlaskit/adf-schema';
|
|
2
|
+
import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
3
|
+
import { createCommand } from '../pm-plugins/plugin-factory';
|
|
4
|
+
import { ACTIONS } from '../pm-plugins/types';
|
|
5
|
+
import { AnnotationSelectionType } from '../types';
|
|
6
|
+
import { getPluginState, isSelectionValid } from '../utils';
|
|
7
|
+
import transform from './transform';
|
|
8
|
+
export const updateInlineCommentResolvedState = editorAnalyticsAPI => (partialNewState, resolveMethod) => {
|
|
9
|
+
const command = {
|
|
10
|
+
type: ACTIONS.UPDATE_INLINE_COMMENT_STATE,
|
|
11
|
+
data: partialNewState
|
|
12
|
+
};
|
|
13
|
+
const allResolved = Object.values(partialNewState).every(state => state);
|
|
14
|
+
if (resolveMethod && allResolved) {
|
|
15
|
+
return createCommand(command, transform.addResolveAnalytics(editorAnalyticsAPI)(resolveMethod));
|
|
16
|
+
}
|
|
17
|
+
return createCommand(command);
|
|
18
|
+
};
|
|
19
|
+
export const closeComponent = () => createCommand({
|
|
20
|
+
type: ACTIONS.CLOSE_COMPONENT
|
|
21
|
+
});
|
|
22
|
+
export const clearDirtyMark = () => createCommand({
|
|
23
|
+
type: ACTIONS.INLINE_COMMENT_CLEAR_DIRTY_MARK
|
|
24
|
+
});
|
|
25
|
+
export const removeInlineCommentNearSelection = id => (state, dispatch) => {
|
|
26
|
+
const {
|
|
27
|
+
tr,
|
|
28
|
+
selection: {
|
|
29
|
+
$from
|
|
30
|
+
}
|
|
31
|
+
} = state;
|
|
32
|
+
const {
|
|
33
|
+
annotation: annotationMarkType
|
|
34
|
+
} = state.schema.marks;
|
|
35
|
+
const hasAnnotation = $from.marks().some(mark => mark.type === annotationMarkType);
|
|
36
|
+
if (!hasAnnotation) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// just remove entire mark from around the node
|
|
41
|
+
tr.removeMark($from.start(), $from.end(), annotationMarkType.create({
|
|
42
|
+
id,
|
|
43
|
+
type: AnnotationTypes.INLINE_COMMENT
|
|
44
|
+
}));
|
|
45
|
+
if (dispatch) {
|
|
46
|
+
dispatch(tr);
|
|
47
|
+
}
|
|
48
|
+
return true;
|
|
49
|
+
};
|
|
50
|
+
const getDraftCommandAction = drafting => {
|
|
51
|
+
return editorState => {
|
|
52
|
+
// validate selection only when entering draft mode
|
|
53
|
+
if (drafting && isSelectionValid(editorState) !== AnnotationSelectionType.VALID) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
type: ACTIONS.SET_INLINE_COMMENT_DRAFT_STATE,
|
|
58
|
+
data: {
|
|
59
|
+
drafting,
|
|
60
|
+
editorState
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
export const setInlineCommentDraftState = editorAnalyticsAPI => (drafting, inputMethod = INPUT_METHOD.TOOLBAR) => {
|
|
66
|
+
const commandAction = getDraftCommandAction(drafting);
|
|
67
|
+
return createCommand(commandAction, transform.addOpenCloseAnalytics(editorAnalyticsAPI)(drafting, inputMethod));
|
|
68
|
+
};
|
|
69
|
+
export const addInlineComment = editorAnalyticsAPI => id => {
|
|
70
|
+
const commandAction = editorState => ({
|
|
71
|
+
type: ACTIONS.ADD_INLINE_COMMENT,
|
|
72
|
+
data: {
|
|
73
|
+
drafting: false,
|
|
74
|
+
inlineComments: {
|
|
75
|
+
[id]: false
|
|
76
|
+
},
|
|
77
|
+
// Auto make the newly inserted comment selected.
|
|
78
|
+
// We move the selection to the head of the comment selection.
|
|
79
|
+
selectedAnnotations: [{
|
|
80
|
+
id,
|
|
81
|
+
type: AnnotationTypes.INLINE_COMMENT
|
|
82
|
+
}],
|
|
83
|
+
editorState
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return createCommand(commandAction, transform.addInlineComment(editorAnalyticsAPI)(id));
|
|
87
|
+
};
|
|
88
|
+
export const updateMouseState = mouseData => createCommand({
|
|
89
|
+
type: ACTIONS.INLINE_COMMENT_UPDATE_MOUSE_STATE,
|
|
90
|
+
data: {
|
|
91
|
+
mouseData
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
export const setSelectedAnnotation = id => createCommand({
|
|
95
|
+
type: ACTIONS.SET_SELECTED_ANNOTATION,
|
|
96
|
+
data: {
|
|
97
|
+
selectedAnnotations: [{
|
|
98
|
+
id,
|
|
99
|
+
type: AnnotationTypes.INLINE_COMMENT
|
|
100
|
+
}]
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
export const createAnnotation = editorAnalyticsAPI => (id, annotationType = AnnotationTypes.INLINE_COMMENT) => (state, dispatch) => {
|
|
104
|
+
// don't try to add if there are is no temp highlight bookmarked
|
|
105
|
+
const {
|
|
106
|
+
bookmark
|
|
107
|
+
} = getPluginState(state) || {};
|
|
108
|
+
if (!bookmark || !dispatch) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (annotationType === AnnotationTypes.INLINE_COMMENT) {
|
|
112
|
+
return addInlineComment(editorAnalyticsAPI)(id)(state, dispatch);
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
};
|
|
116
|
+
export const setInlineCommentsVisibility = isVisible => {
|
|
117
|
+
return createCommand({
|
|
118
|
+
type: ACTIONS.INLINE_COMMENT_SET_VISIBLE,
|
|
119
|
+
data: {
|
|
120
|
+
isVisible
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
};
|