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