UNPKG

@atlaskit/editor-plugin-type-ahead

Version:

Type-ahead plugin for @atlaskit/editor-core

349 lines (345 loc) 15.2 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.typeAheadPlugin = void 0; var _react = _interopRequireDefault(require("react")); var _adfSchema = require("@atlaskit/adf-schema"); var _analytics = require("@atlaskit/editor-common/analytics"); var _safePlugin = require("@atlaskit/editor-common/safe-plugin"); var _typeAhead = require("@atlaskit/editor-common/type-ahead"); var _view2 = require("@atlaskit/editor-prosemirror/view"); var _platformFeatureFlags = require("@atlaskit/platform-feature-flags"); var _closeTypeAhead = require("./pm-plugins/commands/close-type-ahead"); var _insertTypeAheadItem = require("./pm-plugins/commands/insert-type-ahead-item"); var _openTypeaheadAtCursor = require("./pm-plugins/commands/open-typeahead-at-cursor"); var _inputRules = require("./pm-plugins/input-rules"); var _insertItemPlugin = require("./pm-plugins/insert-item-plugin"); var _key = require("./pm-plugins/key"); var _main = require("./pm-plugins/main"); var _statsModifier = require("./pm-plugins/stats-modifier"); var _utils = require("./pm-plugins/utils"); var _ContentComponent = require("./ui/ContentComponent"); /** * * Revamped typeahead using decorations instead of the `typeAheadQuery` mark * * https://product-fabric.atlassian.net/wiki/spaces/E/pages/2992177582/Technical+TypeAhead+Data+Flow * * */ var createOpenAtTransaction = function createOpenAtTransaction(editorAnalyticsAPI) { return function (props) { return function (tr) { var triggerHandler = props.triggerHandler, inputMethod = props.inputMethod, query = props.query, removePrefixTriggerOnCancel = props.removePrefixTriggerOnCancel; (0, _openTypeaheadAtCursor.openTypeAheadAtCursor)({ triggerHandler: triggerHandler, inputMethod: inputMethod, query: query, removePrefixTriggerOnCancel: removePrefixTriggerOnCancel })({ tr: tr }); // This function is called from the editor-plugin-emoji and editor-plugin-type-ahead // createOpenAtTransaction <- createOpenTypeAhead <- actions.open // <- emoji-plugin (Not used) // <- type-ahead-plugin (Used) // and this caused the analytics event to be fired twice, as other places are relying on the // `onEditorViewStateUpdated` method to fire the analytics event // We want to disable this event if (!(0, _platformFeatureFlags.fg)('platform_editor_controls_patch_analytics_3')) { editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent({ action: _analytics.ACTION.INVOKED, actionSubject: _analytics.ACTION_SUBJECT.TYPEAHEAD, actionSubjectId: triggerHandler.id, attributes: { inputMethod: inputMethod }, eventType: _analytics.EVENT_TYPE.UI })(tr); } return true; }; }; }; var createOpenTypeAhead = function createOpenTypeAhead(editorViewRef, editorAnalyticsAPI) { return function (props) { if (!editorViewRef.current) { return false; } var view = editorViewRef.current; var tr = view.state.tr; createOpenAtTransaction(editorAnalyticsAPI)(props)(tr); view.dispatch(tr); return true; }; }; var createInsertTypeAheadItem = function createInsertTypeAheadItem(editorViewRef) { return function (props) { if (!editorViewRef.current) { return false; } var view = editorViewRef.current; var triggerHandler = props.triggerHandler, contentItem = props.contentItem, query = props.query, sourceListItem = props.sourceListItem, mode = props.mode; (0, _insertTypeAheadItem.insertTypeAheadItem)(view)({ handler: triggerHandler, item: contentItem, mode: mode || _typeAhead.SelectItemMode.SELECTED, query: query, sourceListItem: sourceListItem }); return true; }; }; var createFindHandlerByTrigger = function createFindHandlerByTrigger(editorViewRef) { return function (trigger) { if (!editorViewRef.current) { return null; } var view = editorViewRef.current; return (0, _utils.findHandler)(trigger, view.state); }; }; var createCloseTypeAhead = function createCloseTypeAhead(editorViewRef) { return function (options) { if (!editorViewRef.current) { return false; } var view = editorViewRef.current; var currentQuery = (0, _utils.getTypeAheadQuery)(view.state) || ''; var state = view.state; var tr = state.tr; if (options.attachCommand) { var fakeDispatch = function fakeDispatch(customTr) { tr = customTr; }; options.attachCommand(state, fakeDispatch); } (0, _closeTypeAhead.closeTypeAhead)(tr); if (options.insertCurrentQueryAsRawText && currentQuery && currentQuery.length > 0) { var handler = (0, _utils.getTypeAheadHandler)(state); if (!handler) { return false; } var text = handler.trigger.concat(currentQuery); tr.replaceSelectionWith(state.schema.text(text)); } view.dispatch(tr); if (!view.hasFocus()) { view.focus(); } return true; }; }; /** * * Revamped typeahead using decorations instead of the `typeAheadQuery` mark * * https://product-fabric.atlassian.net/wiki/spaces/E/pages/2992177582/Technical+TypeAhead+Data+Flow * * */ var typeAheadPlugin = exports.typeAheadPlugin = function typeAheadPlugin(_ref) { var _api$analytics, _api$analytics2; var api = _ref.api; var popupMountRef = { current: null }; var editorViewRef = { current: null }; return { name: 'typeAhead', marks: function marks() { // We need to keep this to make sure // All documents with typeahead marks will be loaded normally return [{ name: 'typeAheadQuery', mark: _adfSchema.typeAheadQuery }]; }, pmPlugins: function pmPlugins() { var typeAhead = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; return [{ name: 'typeAhead', plugin: function plugin(_ref2) { var dispatch = _ref2.dispatch, getIntl = _ref2.getIntl, nodeViewPortalProviderAPI = _ref2.nodeViewPortalProviderAPI; return (0, _main.createPlugin)({ getIntl: getIntl, popupMountRef: popupMountRef, reactDispatch: dispatch, typeAheadHandlers: typeAhead, nodeViewPortalProviderAPI: nodeViewPortalProviderAPI, api: api }); } }, { name: 'typeAheadEditorViewRef', plugin: function plugin() { return new _safePlugin.SafePlugin({ view: function view(_view) { editorViewRef.current = _view; return { destroy: function destroy() { editorViewRef.current = null; } }; } }); } }, { name: 'typeAheadInsertItem', plugin: _insertItemPlugin.createPlugin }, { name: 'typeAheadInputRule', plugin: function plugin(_ref3) { var schema = _ref3.schema, featureFlags = _ref3.featureFlags; return (0, _inputRules.inputRulePlugin)(schema, typeAhead, featureFlags); } }]; }, getSharedState: function getSharedState(editorState) { var _state$decorationSet, _state$decorationElem, _state$items, _state$errorInfo, _state$selectedIndex; if (!editorState) { return { query: '', isOpen: false, isAllowed: false, currentHandler: undefined, decorationSet: _view2.DecorationSet.empty, decorationElement: null, triggerHandler: undefined, items: [], errorInfo: null, selectedIndex: 0 }; } var isOpen = (0, _utils.isTypeAheadOpen)(editorState); var state = (0, _utils.getPluginState)(editorState); return { query: (0, _utils.getTypeAheadQuery)(editorState) || '', currentHandler: (0, _utils.getTypeAheadHandler)(editorState), isOpen: isOpen, isAllowed: !isOpen, decorationSet: (_state$decorationSet = state === null || state === void 0 ? void 0 : state.decorationSet) !== null && _state$decorationSet !== void 0 ? _state$decorationSet : _view2.DecorationSet.empty, decorationElement: (_state$decorationElem = state === null || state === void 0 ? void 0 : state.decorationElement) !== null && _state$decorationElem !== void 0 ? _state$decorationElem : null, triggerHandler: state === null || state === void 0 ? void 0 : state.triggerHandler, items: (_state$items = state === null || state === void 0 ? void 0 : state.items) !== null && _state$items !== void 0 ? _state$items : [], errorInfo: (_state$errorInfo = state === null || state === void 0 ? void 0 : state.errorInfo) !== null && _state$errorInfo !== void 0 ? _state$errorInfo : null, selectedIndex: (_state$selectedIndex = state === null || state === void 0 ? void 0 : state.selectedIndex) !== null && _state$selectedIndex !== void 0 ? _state$selectedIndex : 0 }; }, actions: { isOpen: _utils.isTypeAheadOpen, isAllowed: _utils.isTypeAheadAllowed, open: createOpenTypeAhead(editorViewRef, api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions), openAtTransaction: createOpenAtTransaction(api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions), findHandlerByTrigger: createFindHandlerByTrigger(editorViewRef), insert: createInsertTypeAheadItem(editorViewRef), close: createCloseTypeAhead(editorViewRef) }, contentComponent: function contentComponent(_ref4) { var editorView = _ref4.editorView, containerElement = _ref4.containerElement, popupsMountPoint = _ref4.popupsMountPoint, popupsBoundariesElement = _ref4.popupsBoundariesElement, popupsScrollableElement = _ref4.popupsScrollableElement, wrapperElement = _ref4.wrapperElement; popupMountRef.current = { popupsMountPoint: popupsMountPoint || wrapperElement || undefined, popupsBoundariesElement: popupsBoundariesElement, popupsScrollableElement: popupsScrollableElement || containerElement || undefined }; if (!editorView) { return null; } return /*#__PURE__*/_react.default.createElement(_ContentComponent.ContentComponent, { editorView: editorView, popupMountRef: popupMountRef, api: api }); }, onEditorViewStateUpdated: function onEditorViewStateUpdated(_ref5) { var originalTransaction = _ref5.originalTransaction, oldEditorState = _ref5.oldEditorState, newEditorState = _ref5.newEditorState; var oldPluginState = (0, _utils.getPluginState)(oldEditorState); var newPluginState = (0, _utils.getPluginState)(newEditorState); if (!oldPluginState || !newPluginState) { return; } var oldTriggerHandler = oldPluginState.triggerHandler; var newTriggerHandler = newPluginState.triggerHandler; var isANewHandler = oldTriggerHandler !== newTriggerHandler; if (oldTriggerHandler !== null && oldTriggerHandler !== void 0 && oldTriggerHandler.dismiss && isANewHandler) { var typeAheadMessage = originalTransaction.getMeta(_key.pluginKey); var wasItemInserted = typeAheadMessage && typeAheadMessage.action === 'INSERT_RAW_QUERY'; oldTriggerHandler.dismiss({ editorState: newEditorState, query: oldPluginState.query, stats: (oldPluginState.stats || new _statsModifier.StatsModifier()).serialize(), wasItemInserted: wasItemInserted }); } if (newTriggerHandler !== null && newTriggerHandler !== void 0 && newTriggerHandler.onOpen && isANewHandler) { if ((0, _platformFeatureFlags.fg)('platform_editor_ease_of_use_metrics')) { var _api$metrics; api === null || api === void 0 || (_api$metrics = api.metrics) === null || _api$metrics === void 0 || _api$metrics.commands.handleIntentToStartEdit({ shouldStartTimer: false, shouldPersistActiveSession: true }); } newTriggerHandler.onOpen(newEditorState); } var oldIsOpen = (0, _utils.isTypeAheadOpen)(oldEditorState); var newIsOpen = (0, _utils.isTypeAheadOpen)(newEditorState); if (newTriggerHandler && isANewHandler) { // if the typeahead opens another typeahead via the quickInsert we do NOT want to fire this analytic event (mentions and emojis) as it is already being fired from editor-plugin-analytics var isDuplicateInvokedEvent = newPluginState.inputMethod === _analytics.INPUT_METHOD.QUICK_INSERT && Object.values(_typeAhead.TypeAheadAvailableNodes).includes(newTriggerHandler.id); if (!isDuplicateInvokedEvent) { if ((0, _platformFeatureFlags.fg)('platform_editor_controls_patch_analytics_3')) { var _api$analytics3; api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 || (_api$analytics3 = _api$analytics3.actions) === null || _api$analytics3 === void 0 || _api$analytics3.fireAnalyticsEvent({ action: _analytics.ACTION.INVOKED, actionSubject: _analytics.ACTION_SUBJECT.TYPEAHEAD, actionSubjectId: newTriggerHandler.id || 'not_set', attributes: { inputMethod: newPluginState.inputMethod || _analytics.INPUT_METHOD.KEYBOARD }, eventType: _analytics.EVENT_TYPE.UI }, undefined, { context: { selection: newEditorState.selection } }); } else { var _api$analytics4; api === null || api === void 0 || (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 || (_api$analytics4 = _api$analytics4.actions) === null || _api$analytics4 === void 0 || _api$analytics4.fireAnalyticsEvent({ action: _analytics.ACTION.INVOKED, actionSubject: _analytics.ACTION_SUBJECT.TYPEAHEAD, actionSubjectId: newTriggerHandler.id || 'not_set', attributes: { inputMethod: newPluginState.inputMethod || _analytics.INPUT_METHOD.KEYBOARD }, eventType: _analytics.EVENT_TYPE.UI }); } } } else if (oldIsOpen && !newIsOpen && (0, _platformFeatureFlags.fg)('platform_editor_ease_of_use_metrics')) { var _api$metrics2; api === null || api === void 0 || api.core.actions.execute(api === null || api === void 0 || (_api$metrics2 = api.metrics) === null || _api$metrics2 === void 0 ? void 0 : _api$metrics2.commands.startActiveSessionTimer()); } } }; };