UNPKG

@atlaskit/editor-plugin-hyperlink

Version:

Hyperlink plugin for @atlaskit/editor-core

242 lines (240 loc) 10.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.stateKey = exports.plugin = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _uuid = _interopRequireDefault(require("uuid")); var _link3 = require("@atlaskit/editor-common/link"); var _safePlugin = require("@atlaskit/editor-common/safe-plugin"); var _utils = require("@atlaskit/editor-common/utils"); var _state = require("@atlaskit/editor-prosemirror/state"); var _view = require("@atlaskit/editor-prosemirror/view"); 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; } 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) { (0, _defineProperty2.default)(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; } // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead var mapTransactionToState = function mapTransactionToState(state, tr) { if (!state) { return undefined; } else if (state.type === _link3.InsertStatus.EDIT_LINK_TOOLBAR || state.type === _link3.InsertStatus.EDIT_INSERTED_TOOLBAR) { var _tr$mapping$mapResult = tr.mapping.mapResult(state.pos, 1), pos = _tr$mapping$mapResult.pos, deleted = _tr$mapping$mapResult.deleted; var node = tr.doc.nodeAt(pos); // If the position was not deleted & it is still a link if (!deleted && !!node.type.schema.marks.link.isInSet(node.marks)) { if (node === state.node && pos === state.pos) { return state; } return _objectSpread(_objectSpread({}, state), {}, { pos: pos, node: node }); } // If the position has been deleted, then require a navigation to show the toolbar again return; } else if (state.type === _link3.InsertStatus.INSERT_LINK_TOOLBAR) { return _objectSpread(_objectSpread({}, state), {}, { from: tr.mapping.map(state.from), to: tr.mapping.map(state.to) }); } return; }; var toState = function toState(state, action, editorState) { // Show insert or edit toolbar if (!state) { switch (action) { case _link3.LinkAction.SHOW_INSERT_TOOLBAR: { var _editorState$selectio = editorState.selection, from = _editorState$selectio.from, to = _editorState$selectio.to; if ((0, _utils.canLinkBeCreatedInRange)(from, to)(editorState)) { return { type: _link3.InsertStatus.INSERT_LINK_TOOLBAR, from: from, to: to }; } return undefined; } case _link3.LinkAction.SELECTION_CHANGE: // If the user has moved their cursor, see if they're in a link var link = (0, _link3.getActiveLinkMark)(editorState); if (link) { return _objectSpread(_objectSpread({}, link), {}, { type: _link3.InsertStatus.EDIT_LINK_TOOLBAR }); } return undefined; default: return undefined; } } // Update toolbar state if selection changes, or if toolbar is hidden if (state.type === _link3.InsertStatus.EDIT_LINK_TOOLBAR) { switch (action) { case _link3.LinkAction.EDIT_INSERTED_TOOLBAR: { var _link = (0, _link3.getActiveLinkMark)(editorState); if (_link) { if (_link.pos === state.pos && _link.node === state.node) { return _objectSpread(_objectSpread({}, state), {}, { type: _link3.InsertStatus.EDIT_INSERTED_TOOLBAR }); } return _objectSpread(_objectSpread({}, _link), {}, { type: _link3.InsertStatus.EDIT_INSERTED_TOOLBAR }); } return undefined; } case _link3.LinkAction.SELECTION_CHANGE: var _link2 = (0, _link3.getActiveLinkMark)(editorState); if (_link2) { if (_link2.pos === state.pos && _link2.node === state.node) { // Make sure we return the same object, if it's the same link return state; } return _objectSpread(_objectSpread({}, _link2), {}, { type: _link3.InsertStatus.EDIT_LINK_TOOLBAR }); } return undefined; case _link3.LinkAction.HIDE_TOOLBAR: return undefined; default: return state; } } // Remove toolbar if user changes selection or toolbar is hidden if (state.type === _link3.InsertStatus.INSERT_LINK_TOOLBAR) { switch (action) { case _link3.LinkAction.SELECTION_CHANGE: case _link3.LinkAction.HIDE_TOOLBAR: return undefined; default: return state; } } return; }; var getActiveText = function getActiveText(selection) { var currentSlice = selection.content(); if (currentSlice.size === 0) { return; } if (currentSlice.content.childCount === 1 && currentSlice.content.firstChild && selection instanceof _state.TextSelection) { return currentSlice.content.firstChild.textContent; } return; }; var stateKey = exports.stateKey = new _state.PluginKey('hyperlinkPlugin'); var plugin = exports.plugin = function plugin(dispatch, intl, editorAppearance, _pluginInjectionApi, _onClickCallback, __livePage) { return new _safePlugin.SafePlugin({ state: { init: function init(_, state) { var canInsertLink = (0, _utils.canLinkBeCreatedInRange)(state.selection.from, state.selection.to)(state); return { activeText: getActiveText(state.selection), canInsertLink: canInsertLink, timesViewed: 0, activeLinkMark: toState(undefined, _link3.LinkAction.SELECTION_CHANGE, state), editorAppearance: editorAppearance }; }, apply: function apply(tr, pluginState, oldState, newState) { var state = pluginState; var action = tr.getMeta(stateKey) && tr.getMeta(stateKey).type; var inputMethod = tr.getMeta(stateKey) && tr.getMeta(stateKey).inputMethod; if (tr.docChanged) { state = { activeText: state.activeText, canInsertLink: (0, _utils.canLinkBeCreatedInRange)(newState.selection.from, newState.selection.to)(newState), timesViewed: state.timesViewed, inputMethod: inputMethod, activeLinkMark: mapTransactionToState(state.activeLinkMark, tr), editorAppearance: editorAppearance }; } if (action) { var stateForAnalytics = [_link3.LinkAction.SHOW_INSERT_TOOLBAR, _link3.LinkAction.EDIT_INSERTED_TOOLBAR].includes(action) ? { timesViewed: ++state.timesViewed, // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead searchSessionId: (0, _uuid.default)() } : { timesViewed: state.timesViewed, searchSessionId: state.searchSessionId }; state = _objectSpread({ activeText: state.activeText, canInsertLink: state.canInsertLink, inputMethod: inputMethod, activeLinkMark: toState(state.activeLinkMark, action, newState), editorAppearance: editorAppearance }, stateForAnalytics); } var hasPositionChanged = oldState.selection.from !== newState.selection.from || oldState.selection.to !== newState.selection.to; if (tr.selectionSet && hasPositionChanged) { state = { activeText: getActiveText(newState.selection), canInsertLink: (0, _utils.canLinkBeCreatedInRange)(newState.selection.from, newState.selection.to)(newState), activeLinkMark: toState(state.activeLinkMark, _link3.LinkAction.SELECTION_CHANGE, newState), timesViewed: state.timesViewed, searchSessionId: state.searchSessionId, inputMethod: inputMethod, editorAppearance: editorAppearance }; } if (!(0, _utils.shallowEqual)(state, pluginState)) { dispatch(stateKey, state); } return state; } }, key: stateKey, props: { decorations: function decorations() { return _view.DecorationSet.empty; }, handleDOMEvents: { // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any mouseup: function mouseup(_, event) { // this prevents redundant selection transaction when clicking on link // link state will be update on slection change which happens on mousedown if (isLinkDirectTarget(event)) { event.preventDefault(); return true; } return false; }, // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-explicit-any mousedown: function mousedown(view, event) { // since link clicks are disallowed by browsers inside contenteditable // so we need to handle shift+click selection ourselves in this case if (!event.shiftKey || !isLinkDirectTarget(event)) { return false; } var state = view.state; var $anchor = state.selection.$anchor; var newPosition = view.posAtCoords({ left: event.clientX, top: event.clientY }); if ((newPosition === null || newPosition === void 0 ? void 0 : newPosition.pos) != null && newPosition.pos !== $anchor.pos) { var tr = state.tr.setSelection(_state.TextSelection.create(state.doc, $anchor.pos, newPosition.pos)); view.dispatch(tr); return true; } return false; } } } }); }; function isLinkDirectTarget(event) { return (event === null || event === void 0 ? void 0 : event.target) instanceof HTMLElement && event.target.tagName === 'A'; }