@atlaskit/editor-plugin-hyperlink
Version:
Hyperlink plugin for @atlaskit/editor-core
242 lines (240 loc) • 10.5 kB
JavaScript
;
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';
}