@atlaskit/editor-plugin-selection
Version:
Selection plugin for @atlaskit/editor-core
108 lines (106 loc) • 5.2 kB
JavaScript
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
import { selectionPluginKey } from '../types';
import { SelectionActionTypes } from './actions';
import { onCreateSelectionBetween } from './events/create-selection-between';
import { createOnKeydown } from './events/keydown';
import { onMouseOut } from './events/mouseout';
import { createPluginState, getPluginState } from './plugin-factory';
import { getDecorations, shouldRecalcDecorations } from './utils';
export const getInitialState = state => ({
decorationSet: getDecorations(state.tr),
selection: state.selection
});
export const createPlugin = (api, dispatch, dispatchAnalyticsEvent, options = {}) => {
let cursorHidden = false;
let blockSelection;
return new SafePlugin({
key: selectionPluginKey,
state: createPluginState(dispatch, getInitialState),
appendTransaction(transactions, oldEditorState, newEditorState) {
const {
tr
} = newEditorState;
let manualSelection;
let hideCursorChanged = false;
let blockSelectionChanged = false;
const needsManualSelection = editorExperiment('platform_editor_element_drag_and_drop_multiselect', true);
const needsBlockSelection = editorExperiment('platform_editor_block_menu', true, {
exposure: true
});
for (let i = transactions.length - 1; i >= 0; i--) {
const meta = transactions[i].getMeta(selectionPluginKey);
if ((meta === null || meta === void 0 ? void 0 : meta.hideCursor) !== undefined) {
const newHideCursorValue = meta.hideCursor;
if (cursorHidden !== newHideCursorValue) {
cursorHidden = newHideCursorValue;
hideCursorChanged = true;
}
}
if (needsManualSelection && meta !== null && meta !== void 0 && meta.manualSelection && !manualSelection) {
manualSelection = meta.manualSelection;
}
if (needsBlockSelection) {
if (meta !== null && meta !== void 0 && meta.setBlockSelection) {
blockSelection = meta.setBlockSelection;
blockSelectionChanged = true;
}
if (meta !== null && meta !== void 0 && meta.clearBlockSelection) {
blockSelection = undefined;
blockSelectionChanged = true;
}
}
}
if (!shouldRecalcDecorations({
oldEditorState,
newEditorState
}) && !manualSelection && !hideCursorChanged && !blockSelectionChanged) {
return;
}
tr.setMeta(selectionPluginKey, {
type: SelectionActionTypes.SET_DECORATIONS,
selection: tr.selection,
decorationSet: getDecorations(tr, manualSelection, cursorHidden, blockSelection)
});
return tr;
},
filterTransaction(tr, state) {
// Prevent single click selecting atom nodes on mobile (we want to select with long press gesture instead)
if (options.useLongPressSelection && tr.selectionSet && tr.selection instanceof NodeSelection && !tr.getMeta(selectionPluginKey)) {
return false;
}
// Prevent prosemirror's mutation observer overriding a node selection with a text selection
// for exact same range - this was cause of being unable to change dates in collab:
// https://product-fabric.atlassian.net/browse/ED-10645
if (state.selection instanceof NodeSelection && tr.selection instanceof TextSelection && state.selection.from === tr.selection.from && state.selection.to === tr.selection.to) {
return false;
}
return true;
},
props: {
createSelectionBetween: onCreateSelectionBetween,
decorations(state) {
var _api$interaction, _api$interaction$shar;
const interactionState = api === null || api === void 0 ? void 0 : (_api$interaction = api.interaction) === null || _api$interaction === void 0 ? void 0 : (_api$interaction$shar = _api$interaction.sharedState.currentState()) === null || _api$interaction$shar === void 0 ? void 0 : _api$interaction$shar.interactionState;
// Do not show selection decorations for live pages where the user has not
// interacted with the page. We do not show cursor until interaction and we do not
// want to show selections either.
if ((options.__livePage || expValEquals('platform_editor_no_cursor_on_edit_page_init', 'isEnabled', true)) && interactionState === 'hasNotHadInteraction') {
return DecorationSet.empty;
}
return getPluginState(state).decorationSet;
},
handleDOMEvents: {
keydown: createOnKeydown({
__livePage: options.__livePage
}),
// Without this event, it is not possible to click and drag to select the first node in the
// document if the node is a block with content (e.g. a panel with a paragraph inside).
mouseout: onMouseOut
}
}
});
};