@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,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
|
+
}
|