UNPKG

@atlaskit/editor-plugin-type-ahead

Version:

Type-ahead plugin for @atlaskit/editor-core

117 lines (116 loc) 3.75 kB
import { InsertTypeAheadStep } from '@atlaskit/adf-schema/steps'; import { SafePlugin } from '@atlaskit/editor-common/safe-plugin'; import { closest } from '@atlaskit/editor-common/utils'; import { DecorationSet } from '@atlaskit/editor-prosemirror/view'; import { fg } from '@atlaskit/platform-feature-flags'; import { ACTIONS } from './actions'; import { TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE } from './constants'; import { factoryDecorations } from './decorations'; import { isInsertionTransaction } from './isInsertionTransaction'; import { pluginKey } from './key'; import { createReducer } from './reducer'; const hasValidTypeAheadStep = tr => { const steps = tr.steps.filter(step => step instanceof InsertTypeAheadStep); // There are some cases, like collab rebase, where the steps are re-applied // We should not re open the type-ahead for those cases if (steps.length === 1) { return steps[0]; } return null; }; export function createPlugin({ reactDispatch, popupMountRef, typeAheadHandlers, getIntl, nodeViewPortalProviderAPI, api }) { const intl = getIntl(); const { createDecorations, removeDecorations } = factoryDecorations({ intl, nodeViewPortalProviderAPI, popupMountRef, api }); const reducer = createReducer({ createDecorations, removeDecorations, typeAheadHandlers, popupMountRef }); return new SafePlugin({ key: pluginKey, state: { init() { return { typeAheadHandlers, query: '', decorationSet: DecorationSet.empty, decorationElement: null, items: [], errorInfo: null, selectedIndex: -1, stats: null, inputMethod: null, removePrefixTriggerOnCancel: undefined }; }, apply(tr, currentPluginState, oldEditorState, state) { const customStep = hasValidTypeAheadStep(tr); const nextPluginState = reducer(tr, currentPluginState, customStep); if (currentPluginState !== nextPluginState) { reactDispatch(pluginKey, nextPluginState); } return nextPluginState; } }, appendTransaction(transactions, _oldState, newState) { const insertItemCallback = isInsertionTransaction(transactions, ACTIONS.INSERT_RAW_QUERY); if (insertItemCallback) { const tr = insertItemCallback(newState); if (tr) { if (fg('platform_editor_ease_of_use_metrics')) { var _api$metrics; api === null || api === void 0 ? void 0 : (_api$metrics = api.metrics) === null || _api$metrics === void 0 ? void 0 : _api$metrics.commands.startActiveSessionTimer()({ tr }); } return tr; } } }, view() { return { update(editorView) {} }; }, props: { decorations: state => { var _pluginKey$getState; return (_pluginKey$getState = pluginKey.getState(state)) === null || _pluginKey$getState === void 0 ? void 0 : _pluginKey$getState.decorationSet; }, handleDOMEvents: { compositionend: (view, event) => { return false; }, click: (view, event) => { const { target } = event; // ProseMirror view listen to any click event inside of it // When this event is coming from the typeahead // we should tell to ProseMirror to sit down and relax // cuz we know what we are doing (I hope) if (target instanceof HTMLElement && closest(target, `[data-type-ahead=${TYPE_AHEAD_DECORATION_DATA_ATTRIBUTE}]`)) { return true; } return false; } } } }); }