UNPKG

@atlaskit/editor-plugin-annotation

Version:

Annotation plugin for @atlaskit/editor-core

355 lines (351 loc) 13.9 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; import { AnnotationTypes } from '@atlaskit/adf-schema'; import { INPUT_METHOD, RESOLVE_METHOD } from '@atlaskit/editor-common/analytics'; import { NodeSelection } from '@atlaskit/editor-prosemirror/state'; import { createCommand } from '../pm-plugins/plugin-factory'; import { ACTIONS } from '../pm-plugins/types'; import { getPluginState, inlineCommentPluginKey, isSelectionValid, isSupportedBlockNode } from '../pm-plugins/utils'; import { AnnotationSelectionType } from '../types'; import transform from './transform'; import { resetUserIntent, setUserIntent } from './utils'; export var updateInlineCommentResolvedState = function updateInlineCommentResolvedState(editorAnalyticsAPI) { return function (partialNewState, resolveMethod) { var command = { type: ACTIONS.UPDATE_INLINE_COMMENT_STATE, data: partialNewState }; var allResolved = Object.values(partialNewState).every(function (state) { return state; }); // TODO: EDF-716 - This is nuking the scroll into view which occurs // when a comment is resolved. The problem is this is called when either a collab user or the current user resolves a comment. // and the problem is since cc is just dispatching an event through the provider to resolve the comment and this // is not comming via NCS, we have not way to know if this is a local or remote transaction. // To quickly fix this problem to unblock live pages this will just stop the transaction causing a scroll when the // resolve method is set. var transformer = function transformer(tr, state) { return resolveMethod === RESOLVE_METHOD.CONSUMER ? tr.setMeta('scrollIntoView', false) : tr; }; if (resolveMethod && allResolved) { return createCommand(command, function (tr, state) { return transformer(transform.addResolveAnalytics(editorAnalyticsAPI)(resolveMethod)(tr, state), state); }); } return createCommand(command, transformer); }; }; export var closeComponent = function closeComponent() { return createCommand({ type: ACTIONS.CLOSE_COMPONENT }); }; export var clearDirtyMark = function clearDirtyMark() { return createCommand({ type: ACTIONS.INLINE_COMMENT_CLEAR_DIRTY_MARK }); }; export var setInlineCommentsFetched = function setInlineCommentsFetched() { return createCommand({ type: ACTIONS.SET_INLINE_COMMENTS_FETCHED }); }; export var flushPendingSelections = function flushPendingSelections(editorAnalyticsAPI) { return function (canSetAsSelectedAnnotations, errorReason) { var command = { type: ACTIONS.FLUSH_PENDING_SELECTIONS, data: { canSetAsSelectedAnnotations: canSetAsSelectedAnnotations } }; if (!!errorReason) { return createCommand(command, function (tr, state) { return transform.addPreemptiveGateErrorAnalytics(editorAnalyticsAPI)(errorReason)(tr, state); }); } return createCommand(command); }; }; export var setPendingSelectedAnnotation = function setPendingSelectedAnnotation(id) { return createCommand({ type: ACTIONS.SET_PENDING_SELECTIONS, data: { selectedAnnotations: [{ id: id, type: AnnotationTypes.INLINE_COMMENT }] } }); }; var removeInlineCommentFromNode = function removeInlineCommentFromNode(id) { var supportedBlockNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; var state = arguments.length > 2 ? arguments[2] : undefined; var dispatch = arguments.length > 3 ? arguments[3] : undefined; var tr = state.tr, selection = state.selection; if (selection instanceof NodeSelection && isSupportedBlockNode(selection.node, supportedBlockNodes)) { var $from = selection.$from; var currNode = selection.node; var from = $from.start(); // for media annotation, the selection is on media Single if (currNode.type === state.schema.nodes.mediaSingle && currNode.firstChild) { currNode = currNode.firstChild; from = from + 1; } var annotationMarkType = state.schema.marks.annotation; var hasAnnotation = currNode.marks.some(function (mark) { return mark.type === annotationMarkType; }); if (!hasAnnotation) { return false; } tr.removeNodeMark(from, annotationMarkType.create({ id: id, type: AnnotationTypes.INLINE_COMMENT })); if (dispatch) { dispatch(tr); } return true; } return false; }; export var removeInlineCommentNearSelection = function removeInlineCommentNearSelection(id) { var supportedNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; return function (state, dispatch) { var tr = state.tr, $from = state.selection.$from; if (removeInlineCommentFromNode(id, supportedNodes, state, dispatch)) { return true; } var annotationMarkType = state.schema.marks.annotation; var hasAnnotation = $from.marks().some(function (mark) { return mark.type === annotationMarkType; }); if (!hasAnnotation) { return false; } // just remove entire mark from around the node tr.removeMark($from.start(), $from.end(), annotationMarkType.create({ id: id, type: AnnotationTypes.INLINE_COMMENT })); if (dispatch) { dispatch(tr); } return true; }; }; export var removeInlineCommentFromDoc = function removeInlineCommentFromDoc(editorAnalyticsAPI) { return function (id) { var supportedNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; return function (state, dispatch) { var tr = state.tr; state.doc.descendants(function (node, pos) { // Inline comment on mediaInline is not supported as part of comments on media project // Thus, we skip the decoration for mediaInline node if (node.type.name === 'mediaInline') { return false; } var isSupportedBlockNode = node.isBlock && (supportedNodes === null || supportedNodes === void 0 ? void 0 : supportedNodes.includes(node.type.name)); node.marks.filter(function (mark) { return mark.type === state.schema.marks.annotation && mark.attrs.id === id; }).forEach(function (mark) { if (isSupportedBlockNode) { tr.removeNodeMark(pos, mark); } else { tr.removeMark(pos, pos + node.nodeSize, mark); } }); }); if (dispatch) { dispatch(transform.addDeleteAnalytics(editorAnalyticsAPI)(tr, state)); return true; } return false; }; }; }; var getDraftCommandAction = function getDraftCommandAction(drafting, targetType, targetNodeId, supportedBlockNodes, isOpeningMediaCommentFromToolbar) { return function (editorState) { // validate selection only when entering draft mode if (drafting && isSelectionValid(editorState) !== AnnotationSelectionType.VALID) { return false; } return { type: ACTIONS.SET_INLINE_COMMENT_DRAFT_STATE, data: { drafting: drafting, editorState: editorState, targetType: targetType, supportedBlockNodes: supportedBlockNodes, targetNodeId: targetNodeId, isOpeningMediaCommentFromToolbar: isOpeningMediaCommentFromToolbar } }; }; }; /** * Show active inline comments for a given block node, otherwise, * return false if the node has no comments or no unresolved comments. * @param supportedBlockNodes * @example */ export var showInlineCommentForBlockNode = function showInlineCommentForBlockNode() { var supportedBlockNodes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; return function (node, viewMethod, isOpeningMediaCommentFromToolbar) { return function (state, dispatch) { var pluginState = getPluginState(state); var annotation = state.schema.marks.annotation; if (node && node.isBlock && supportedBlockNodes.includes(node.type.name)) { var unresolvedAnnotationMarks = ((node === null || node === void 0 ? void 0 : node.marks) || []).filter(function (mark) { return mark.type === annotation && !(pluginState !== null && pluginState !== void 0 && pluginState.annotations[mark.attrs.id]); }).map(function (mark) { return { id: mark.attrs.id, type: mark.attrs.annotationType }; }); if (unresolvedAnnotationMarks.length) { if (dispatch) { // bypass createCommand with setMeta // so that external plugins can be aware of if there are active(unresolved) comments associated with the node // i.e. media plugin can use the return result (true/false) to show toggle create comment component dispatch(state.tr.setMeta(inlineCommentPluginKey, { type: ACTIONS.SET_SELECTED_ANNOTATION, data: { selectedAnnotations: unresolvedAnnotationMarks, selectAnnotationMethod: viewMethod, isOpeningMediaCommentFromToolbar: isOpeningMediaCommentFromToolbar } })); return true; } } } return false; }; }; }; export var setInlineCommentDraftState = function setInlineCommentDraftState(editorAnalyticsAPI) { var supportedBlockNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; var api = arguments.length > 2 ? arguments[2] : undefined; return function (drafting) { var inputMethod = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : INPUT_METHOD.TOOLBAR; var targetType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'inline'; var targetNodeId = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : undefined; var isOpeningMediaCommentFromToolbar = arguments.length > 4 ? arguments[4] : undefined; var commandAction = getDraftCommandAction(drafting, targetType, targetNodeId, supportedBlockNodes, isOpeningMediaCommentFromToolbar); if (Boolean(api === null || api === void 0 ? void 0 : api.toolbar)) { return function (state, dispatch) { var tr = transform.handleDraftState(editorAnalyticsAPI)(drafting, inputMethod)(state.tr, state); var newPluginState = commandAction(state); if (tr && newPluginState) { tr.setMeta(inlineCommentPluginKey, newPluginState); if (drafting) { setUserIntent(api, tr); } else { resetUserIntent(api, tr); } if (dispatch) { dispatch(tr); } } else { return false; } return true; }; } else { return createCommand(commandAction, transform.handleDraftState(editorAnalyticsAPI)(drafting, inputMethod)); } }; }; export var addInlineComment = function addInlineComment(editorAnalyticsAPI, editorAPI) { return function (id, supportedBlockNodes) { var commandAction = function commandAction(editorState) { return { type: ACTIONS.ADD_INLINE_COMMENT, data: { drafting: false, inlineComments: _defineProperty({}, id, false), // Auto make the newly inserted comment selected. // We move the selection to the head of the comment selection. selectedAnnotations: [{ id: id, type: AnnotationTypes.INLINE_COMMENT }], editorState: editorState } }; }; if (Boolean(editorAPI === null || editorAPI === void 0 ? void 0 : editorAPI.toolbar)) { return function (state, dispatch) { var tr = transform.addInlineComment(editorAnalyticsAPI, editorAPI)(id, supportedBlockNodes)(state.tr, state); tr.setMeta(inlineCommentPluginKey, commandAction(state)); resetUserIntent(editorAPI, tr); if (dispatch) { dispatch(tr); return true; } return false; }; } else { return createCommand(commandAction, transform.addInlineComment(editorAnalyticsAPI, editorAPI)(id, supportedBlockNodes)); } }; }; export var updateMouseState = function updateMouseState(mouseData) { return createCommand({ type: ACTIONS.INLINE_COMMENT_UPDATE_MOUSE_STATE, data: { mouseData: mouseData } }); }; export var setSelectedAnnotation = function setSelectedAnnotation(id) { return createCommand({ type: ACTIONS.SET_SELECTED_ANNOTATION, data: { selectedAnnotations: [{ id: id, type: AnnotationTypes.INLINE_COMMENT }] } }); }; export var setHoveredAnnotation = function setHoveredAnnotation(id) { return createCommand({ type: ACTIONS.SET_HOVERED_ANNOTATION, data: { hoveredAnnotations: [{ id: id, type: AnnotationTypes.INLINE_COMMENT }] } }); }; export var createAnnotation = function createAnnotation(editorAnalyticsAPI, editorAPI) { return function (id) { var annotationType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : AnnotationTypes.INLINE_COMMENT; var supportedBlockNodes = arguments.length > 2 ? arguments[2] : undefined; return function (state, dispatch) { // don't try to add if there are is no temp highlight bookmarked var _ref = getPluginState(state) || {}, bookmark = _ref.bookmark; if (!bookmark || !dispatch) { return false; } if (annotationType === AnnotationTypes.INLINE_COMMENT) { return addInlineComment(editorAnalyticsAPI, editorAPI)(id, supportedBlockNodes)(state, dispatch); } return false; }; }; }; export var setInlineCommentsVisibility = function setInlineCommentsVisibility(isVisible) { return createCommand({ type: ACTIONS.INLINE_COMMENT_SET_VISIBLE, data: { isVisible: isVisible } }); };