@atlaskit/editor-plugin-type-ahead
Version:
Type-ahead plugin for @atlaskit/editor-core
349 lines (345 loc) • 15.2 kB
JavaScript
"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());
}
}
};
};