UNPKG

@atlaskit/editor-plugin-hyperlink

Version:

Hyperlink plugin for @atlaskit/editor-core

316 lines (312 loc) 19.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.HyperlinkAddToolbarWithState = HyperlinkAddToolbarWithState; exports.getToolbarConfig = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _react = _interopRequireWildcard(require("react")); var _adfSchema = require("@atlaskit/adf-schema"); var _analytics = require("@atlaskit/editor-common/analytics"); var _card = require("@atlaskit/editor-common/card"); var _hooks = require("@atlaskit/editor-common/hooks"); var _link2 = require("@atlaskit/editor-common/link"); var _messages = require("@atlaskit/editor-common/messages"); var _toolbarFlagCheck = require("@atlaskit/editor-common/toolbar-flag-check"); var _ui = require("@atlaskit/editor-common/ui"); var _userIntent = require("@atlaskit/editor-common/user-intent"); var _utils = require("@atlaskit/editor-common/utils"); var _editorPluginConnectivity = require("@atlaskit/editor-plugin-connectivity"); var _state = require("@atlaskit/editor-prosemirror/state"); var _utils2 = require("@atlaskit/editor-prosemirror/utils"); var _edit = _interopRequireDefault(require("@atlaskit/icon/core/edit")); var _linkBroken = _interopRequireDefault(require("@atlaskit/icon/core/link-broken")); var _linkExternal = _interopRequireDefault(require("@atlaskit/icon/core/link-external")); var _experiments = require("@atlaskit/tmp-editor-statsig/experiments"); var _commands = require("../../editor-commands/commands"); var _main = require("../../pm-plugins/main"); var _toolbarButtons = require("../../pm-plugins/toolbar-buttons"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } 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; } /* type guard for edit links */ function isEditLink(linkMark) { return linkMark.pos !== undefined; } var dispatchAnalytics = function dispatchAnalytics(dispatch, state, analyticsBuilder, editorAnalyticsApi) { if (dispatch) { var tr = state.tr; editorAnalyticsApi === null || editorAnalyticsApi === void 0 || editorAnalyticsApi.attachAnalyticsEvent(analyticsBuilder(_analytics.ACTION_SUBJECT_ID.HYPERLINK))(tr); dispatch(tr); } }; var visitHyperlink = function visitHyperlink(editorAnalyticsApi) { return function (state, dispatch) { dispatchAnalytics(dispatch, state, _analytics.buildVisitedLinkPayload, editorAnalyticsApi); return true; }; }; function getLinkText(activeLinkMark, state) { if (!activeLinkMark.node) { return undefined; } var textToUrl = (0, _utils.normalizeUrl)(activeLinkMark.node.text); var linkMark = activeLinkMark.node.marks.find(function (mark) { return mark.type === state.schema.marks.link; }); var linkHref = linkMark && linkMark.attrs.href; if (textToUrl === linkHref) { return undefined; } return activeLinkMark.node.text; } var selector = function selector(states) { var _states$hyperlinkStat, _states$hyperlinkStat2, _states$hyperlinkStat3; return { timesViewed: (_states$hyperlinkStat = states.hyperlinkState) === null || _states$hyperlinkStat === void 0 ? void 0 : _states$hyperlinkStat.timesViewed, inputMethod: (_states$hyperlinkStat2 = states.hyperlinkState) === null || _states$hyperlinkStat2 === void 0 ? void 0 : _states$hyperlinkStat2.inputMethod, searchSessionId: (_states$hyperlinkStat3 = states.hyperlinkState) === null || _states$hyperlinkStat3 === void 0 ? void 0 : _states$hyperlinkStat3.searchSessionId }; }; function HyperlinkAddToolbarWithState(_ref) { var _pluginInjectionApi$c; var _ref$linkPickerOption = _ref.linkPickerOptions, linkPickerOptions = _ref$linkPickerOption === void 0 ? {} : _ref$linkPickerOption, onSubmit = _ref.onSubmit, displayText = _ref.displayText, displayUrl = _ref.displayUrl, providerFactory = _ref.providerFactory, view = _ref.view, onCancel = _ref.onCancel, invokeMethod = _ref.invokeMethod, lpLinkPicker = _ref.lpLinkPicker, onClose = _ref.onClose, onEscapeCallback = _ref.onEscapeCallback, onClickAwayCallback = _ref.onClickAwayCallback, pluginInjectionApi = _ref.pluginInjectionApi; var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(pluginInjectionApi, ['hyperlink'], selector), timesViewed = _useSharedPluginState.timesViewed, inputMethod = _useSharedPluginState.inputMethod, searchSessionId = _useSharedPluginState.searchSessionId; // This is constant rather than dynamic - because if someone's already got a hyperlink toolbar open, // we don't want to dynamically change it on them as this would cause data loss if they've already // started typing in the fields. var isOffline = (0, _react.useRef)((0, _editorPluginConnectivity.isOfflineMode)(pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c = pluginInjectionApi.connectivity) === null || _pluginInjectionApi$c === void 0 || (_pluginInjectionApi$c = _pluginInjectionApi$c.sharedState.currentState()) === null || _pluginInjectionApi$c === void 0 ? void 0 : _pluginInjectionApi$c.mode)); return /*#__PURE__*/_react.default.createElement(_link2.HyperlinkAddToolbar, { linkPickerOptions: linkPickerOptions, onSubmit: onSubmit, displayText: displayText, displayUrl: displayUrl, providerFactory: providerFactory, view: view, onCancel: onCancel, invokeMethod: invokeMethod, lpLinkPicker: lpLinkPicker, onClose: onClose, onEscapeCallback: onEscapeCallback, onClickAwayCallback: onClickAwayCallback, timesViewed: timesViewed, inputMethod: inputMethod, searchSessionId: searchSessionId, isOffline: isOffline.current }); } var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig(options, pluginInjectionApi) { return function (state, intl, providerFactory) { var _pluginInjectionApi$c2, _pluginInjectionApi$a, _options$lpLinkPicker; if (options.disableFloatingToolbar) { return; } var isToolbarAIFCEnabled = Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar); var linkState = _main.stateKey.getState(state); var activeLinkMark = linkState === null || linkState === void 0 ? void 0 : linkState.activeLinkMark; // If range selection, we don't show hyperlink floating toolbar. // Text Formattting toolbar is shown instaed. if (state.selection instanceof _state.TextSelection && state.selection.to !== state.selection.from && (activeLinkMark === null || activeLinkMark === void 0 ? void 0 : activeLinkMark.type) === 'EDIT' && (0, _experiments.editorExperiment)('platform_editor_controls', 'variant1')) { return; } var formatMessage = intl.formatMessage; var editorCardActions = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c2 = pluginInjectionApi.card) === null || _pluginInjectionApi$c2 === void 0 ? void 0 : _pluginInjectionApi$c2.actions; var editorAnalyticsApi = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 ? void 0 : _pluginInjectionApi$a.actions; var lpLinkPicker = (_options$lpLinkPicker = options.lpLinkPicker) !== null && _options$lpLinkPicker !== void 0 ? _options$lpLinkPicker : true; if (activeLinkMark) { var hyperLinkToolbar = { title: 'Hyperlink floating controls', nodeType: [state.schema.nodes.text, state.schema.nodes.paragraph, state.schema.nodes.heading, state.schema.nodes.taskItem, state.schema.nodes.decisionItem, state.schema.nodes.caption].filter(function (nodeType) { return !!nodeType; }), // Use only the node types existing in the schema ED-6745 align: 'left', className: activeLinkMark.type.match('INSERT|EDIT_INSERTED') ? 'hyperlink-floating-toolbar' : '', // getDomRef by default uses view.state.selection.from to position the toolbar. // However, when the user clicks in right after the link the view.state.selection.from references to the dom after the selection. // So instead we want to use the activeLinkMark.pos which has been calculated as the position before the click so that would be the link node getDomRef: activeLinkMark && (activeLinkMark.type === 'EDIT_INSERTED' || activeLinkMark.type === 'EDIT') && (0, _experiments.editorExperiment)('platform_editor_controls', 'variant1') ? function (view) { var domRef = (0, _utils2.findDomRefAtPos)(activeLinkMark.pos, view.domAtPos.bind(view)); return domRef instanceof HTMLElement ? domRef : undefined; } : undefined }; switch (activeLinkMark.type) { case 'EDIT': { var _pluginInjectionApi$c3, _cardActions$getStart, _cardActions$getEndin; var pos = activeLinkMark.pos, node = activeLinkMark.node; var linkMark = node.marks.filter(function (mark) { return mark.type === state.schema.marks.link; }); var link = linkMark[0] && linkMark[0].attrs.href; var isValidUrl = (0, _adfSchema.isSafeUrl)(link); var labelOpenLink = formatMessage(isValidUrl ? _messages.linkMessages.openLink : _messages.linkToolbarMessages.unableToOpenLink); // TODO: ED-14403 - investigate why these are not translating? var labelUnlink = formatMessage(_messages.linkToolbarMessages.unlink); var editLink = formatMessage(_messages.linkToolbarMessages.editLink); var metadata = { url: link, title: '' }; if (activeLinkMark.node.text) { metadata.title = activeLinkMark.node.text; } var areAnyNewToolbarFlagsEnabled = (0, _toolbarFlagCheck.areToolbarFlagsEnabled)(Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : pluginInjectionApi.toolbar)); var cardActions = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c3 = pluginInjectionApi.card) === null || _pluginInjectionApi$c3 === void 0 ? void 0 : _pluginInjectionApi$c3.actions; var startingToolbarItems = (_cardActions$getStart = cardActions === null || cardActions === void 0 ? void 0 : cardActions.getStartingToolbarItems(intl, link, (0, _commands.editInsertedLink)(editorAnalyticsApi), metadata, areAnyNewToolbarFlagsEnabled ? state : undefined)) !== null && _cardActions$getStart !== void 0 ? _cardActions$getStart : [{ id: 'editor.link.edit', testId: 'editor.link.edit', type: 'button', onClick: (0, _commands.editInsertedLink)(editorAnalyticsApi), title: editLink, showTitle: areAnyNewToolbarFlagsEnabled ? false : true, metadata: metadata, icon: areAnyNewToolbarFlagsEnabled ? _edit.default : undefined }, { type: 'separator' }]; var openLinkButton = { id: 'editor.link.openLink', testId: 'editor.link.openLink', type: 'button', disabled: !isValidUrl, target: '_blank', href: isValidUrl ? link : undefined, onClick: visitHyperlink(editorAnalyticsApi), title: labelOpenLink, icon: _linkExternal.default, className: 'hyperlink-open-link', metadata: metadata, tabIndex: null }; var unlinkButton = { id: 'editor.link.unlink', testId: 'editor.link.unlink', type: 'button', onClick: (0, _card.commandWithMetadata)((0, _commands.removeLink)(pos, editorAnalyticsApi), { inputMethod: _analytics.INPUT_METHOD.FLOATING_TB }), title: labelUnlink, icon: _linkBroken.default, tabIndex: null }; var items = [].concat((0, _toConsumableArray2.default)(startingToolbarItems), (0, _toConsumableArray2.default)(areAnyNewToolbarFlagsEnabled ? [unlinkButton, { type: 'separator', fullHeight: true }, openLinkButton, { type: 'separator', fullHeight: true }] : [openLinkButton, { type: 'separator' }, unlinkButton, { type: 'separator' }]), [{ type: 'copy-button', items: [{ state: state, formatMessage: formatMessage, markType: state.schema.marks.link }] }], (0, _toConsumableArray2.default)((_cardActions$getEndin = cardActions === null || cardActions === void 0 ? void 0 : cardActions.getEndingToolbarItems(intl, link)) !== null && _cardActions$getEndin !== void 0 ? _cardActions$getEndin : [])); return _objectSpread(_objectSpread({}, hyperLinkToolbar), {}, { height: 32, width: 250, items: items, scrollable: true }); } case 'EDIT_INSERTED': case 'INSERT': { var _link; if (isEditLink(activeLinkMark) && activeLinkMark.node) { var _linkMark = activeLinkMark.node.marks.filter(function (mark) { return mark.type === state.schema.marks.link; }); _link = _linkMark[0] && _linkMark[0].attrs.href; } var displayText = isEditLink(activeLinkMark) ? getLinkText(activeLinkMark, state) : linkState.activeText; var popupHeight = lpLinkPicker ? _ui.LINKPICKER_HEIGHT_IN_PX : _ui.RECENT_SEARCH_HEIGHT_IN_PX; // Removing popupWidth to ensure that we the popup always positions setting positon left instead of flipping to position right // inside of a narrow space like Preview panel var popupWidth = !lpLinkPicker ? undefined : _ui.RECENT_SEARCH_WIDTH_IN_PX; return _objectSpread(_objectSpread({}, hyperLinkToolbar), {}, { preventPopupOverflow: true, height: popupHeight, width: popupWidth, items: [{ type: 'custom', fallback: [], disableArrowNavigation: true, render: function render(view, idx) { if (!view) { return null; } var Toolbar = /*#__PURE__*/_react.default.createElement(HyperlinkAddToolbarWithState, { pluginInjectionApi: pluginInjectionApi, view: view, key: idx, linkPickerOptions: options === null || options === void 0 ? void 0 : options.linkPicker, lpLinkPicker: lpLinkPicker, displayUrl: _link, displayText: displayText || '', providerFactory: providerFactory // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , onCancel: function onCancel() { return view.focus(); }, onEscapeCallback: (0, _commands.onEscapeCallback)(editorCardActions), onClickAwayCallback: _commands.onClickAwayCallback // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , onSubmit: function onSubmit(href) { var _toolbarKey$getState$, _toolbarKey$getState; var title = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; var displayText = arguments.length > 2 ? arguments[2] : undefined; var inputMethod = arguments.length > 3 ? arguments[3] : undefined; var analytic = arguments.length > 4 ? arguments[4] : undefined; var isEdit = isEditLink(activeLinkMark); var action = isEdit ? _analytics.ACTION.UPDATED : _analytics.ACTION.INSERTED; var skipAnalytics = (_toolbarKey$getState$ = (_toolbarKey$getState = _toolbarButtons.toolbarKey.getState(state)) === null || _toolbarKey$getState === void 0 ? void 0 : _toolbarKey$getState.skipAnalytics) !== null && _toolbarKey$getState$ !== void 0 ? _toolbarKey$getState$ : false; var command = isEdit ? (0, _card.commandWithMetadata)((0, _commands.updateLink)(href, displayText || title, activeLinkMark.pos), { action: action, inputMethod: inputMethod, sourceEvent: analytic }) : (0, _commands.insertLinkWithAnalytics)(inputMethod, activeLinkMark.from, activeLinkMark.to, href, editorCardActions, editorAnalyticsApi, title, displayText, skipAnalytics, analytic); command(view.state, view.dispatch, view); view.focus(); } }); return isToolbarAIFCEnabled ? /*#__PURE__*/_react.default.createElement(_userIntent.UserIntentPopupWrapper, { api: pluginInjectionApi }, Toolbar) : Toolbar; } }] }); } } } return; }; };