@atlaskit/editor-plugin-toolbar
Version:
Toolbar plugin for @atlaskit/editor-core
194 lines (193 loc) • 7.13 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getSelectionToolbarOpenExperiencePlugin = void 0;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _analytics = require("@atlaskit/editor-common/analytics");
var _experiences = require("@atlaskit/editor-common/experiences");
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
var _state = require("@atlaskit/editor-prosemirror/state");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var pluginKey = new _state.PluginKey('selectionToolbarOpenExperience');
var START_METHOD = {
MOUSE_UP: 'mouseUp',
KEY_DOWN: 'keyDown'
};
var ABORT_REASON = {
SELECTION_CLEARED: 'selectionCleared',
BLOCK_MENU_OPENED: 'blockMenuOpened',
EDITOR_DESTROYED: 'editorDestroyed'
};
/**
* This experience tracks when the selection toolbar is opened.
*
* Start: When user makes a selection via mouseup or shift+arrow key down
* Success: When the selection toolbar is added to the DOM within 1000ms of start
* Failure: When 1000ms passes without the selection toolbar being added to the DOM
* Abort: When selection transitions to empty or block menu is opened
*
* @see https://hello.atlassian.net/wiki/spaces/EDITOR/pages/6262117789/Experience+tracking+Selection+toolbar+open
*/
var getSelectionToolbarOpenExperiencePlugin = exports.getSelectionToolbarOpenExperiencePlugin = function getSelectionToolbarOpenExperiencePlugin(_ref) {
var refs = _ref.refs,
dispatchAnalyticsEvent = _ref.dispatchAnalyticsEvent;
var editorView;
var targetEl;
var shiftArrowKeyPressed = false;
var mouseDownPos;
var getTarget = function getTarget() {
if (!targetEl) {
var _editorView;
targetEl = refs.popupsMountPoint || (0, _experiences.getPopupContainerFromEditorView)((_editorView = editorView) === null || _editorView === void 0 ? void 0 : _editorView.dom);
}
return targetEl;
};
var experience = new _experiences.Experience(_experiences.EXPERIENCE_ID.TOOLBAR_OPEN, {
actionSubjectId: _analytics.ACTION_SUBJECT_ID.SELECTION_TOOLBAR,
dispatchAnalyticsEvent: dispatchAnalyticsEvent,
checks: [new _experiences.ExperienceCheckTimeout({
durationMs: 1000,
onTimeout: function onTimeout() {
var _editorView2;
if (isBlockMenuWithinNode(getTarget())) {
return {
status: 'abort',
reason: ABORT_REASON.BLOCK_MENU_OPENED
};
} else if (isSelectionWithoutTextContent((_editorView2 = editorView) === null || _editorView2 === void 0 ? void 0 : _editorView2.state.selection)) {
return {
status: 'abort',
reason: ABORT_REASON.SELECTION_CLEARED
};
}
}
}), new _experiences.ExperienceCheckDomMutation({
onDomMutation: function onDomMutation(_ref2) {
var mutations = _ref2.mutations;
if (mutations.some(isSelectionToolbarAddedInMutation)) {
return {
status: 'success'
};
}
},
observeConfig: function observeConfig() {
return {
target: getTarget(),
options: {
childList: true
}
};
}
})]
});
var shouldSkipExperienceStart = function shouldSkipExperienceStart(selection) {
if (isSelectionWithoutTextContent(selection) || isSelectionWithinCodeBlock(selection)) {
return true;
}
var target = getTarget();
return isSelectionToolbarWithinNode(target) || isBlockMenuWithinNode(target) && (0, _platformFeatureFlags.fg)('platform_editor_toolbar_open_experience_fix');
};
return new _safePlugin.SafePlugin({
key: pluginKey,
state: {
init: function init() {
return {};
},
apply: function apply(_tr, pluginState, oldState, newState) {
if (!oldState.selection.empty && isSelectionWithoutTextContent(newState.selection)) {
experience.abort({
reason: ABORT_REASON.SELECTION_CLEARED
});
}
if (shiftArrowKeyPressed && !newState.selection.eq(oldState.selection) && !isSelectionWithoutTextContent(newState.selection)) {
experience.start({
method: START_METHOD.KEY_DOWN
});
shiftArrowKeyPressed = false;
}
return pluginState;
}
},
props: {
handleDOMEvents: {
mousedown: function mousedown(_view, e) {
mouseDownPos = {
x: e.clientX,
y: e.clientY
};
},
mouseup: function mouseup(view, e) {
if (!mouseDownPos || shouldSkipExperienceStart(view.state.selection)) {
return;
}
if (e.clientX !== mouseDownPos.x || e.clientY !== mouseDownPos.y) {
experience.start({
method: START_METHOD.MOUSE_UP
});
}
},
dblclick: function dblclick(view) {
if (shouldSkipExperienceStart(view.state.selection)) {
return;
}
experience.start({
method: START_METHOD.MOUSE_UP
});
},
keydown: function keydown(_view, _ref3) {
var shiftKey = _ref3.shiftKey,
key = _ref3.key;
shiftArrowKeyPressed = shiftKey && key.includes('Arrow') && !isSelectionToolbarWithinNode(getTarget());
},
keyup: function keyup() {
shiftArrowKeyPressed = false;
}
}
},
view: function view(_view2) {
editorView = _view2;
return {
destroy: function destroy() {
experience.abort({
reason: ABORT_REASON.EDITOR_DESTROYED
});
}
};
}
});
};
var isSelectionToolbarAddedInMutation = function isSelectionToolbarAddedInMutation(_ref4) {
var type = _ref4.type,
addedNodes = _ref4.addedNodes;
return type === 'childList' && (0, _toConsumableArray2.default)(addedNodes).some(isSelectionToolbarWithinNode);
};
var isSelectionToolbarWithinNode = function isSelectionToolbarWithinNode(node) {
return (0, _experiences.containsPopupWithNestedElement)(node, '[data-testid="editor-floating-toolbar"]');
};
var isBlockMenuWithinNode = function isBlockMenuWithinNode(node) {
return (0, _experiences.containsPopupWithNestedElement)(node, '[data-testid="editor-block-menu"]');
};
var isSelectionWithoutTextContent = function isSelectionWithoutTextContent(selection) {
if (!selection || selection.empty) {
return true;
}
var hasText = false;
selection.$from.doc.nodesBetween(selection.from, selection.to, function (node) {
if (hasText) {
return false;
}
if (node.isText && node.text && node.text.length > 0) {
hasText = true;
return false;
}
return true;
});
return !hasText;
};
var isSelectionWithinCodeBlock = function isSelectionWithinCodeBlock(selection) {
var $from = selection.$from,
$to = selection.$to;
return $from.sameParent($to) && $from.parent.type.name === 'codeBlock';
};