@atlaskit/editor-plugin-toolbar
Version:
Toolbar plugin for @atlaskit/editor-core
147 lines • 9.5 kB
JavaScript
import React, { useCallback, useMemo } from 'react';
import { useIntl } from 'react-intl';
import { ACTION_SUBJECT, ACTION_SUBJECT_ID } from '@atlaskit/editor-common/analytics';
import { isSSR } from '@atlaskit/editor-common/core-utils';
import { ErrorBoundary } from '@atlaskit/editor-common/error-boundary';
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
import { logException } from '@atlaskit/editor-common/monitoring';
import { EditorToolbarProvider, EditorToolbarUIProvider, shouldShowSelectionToolbar } from '@atlaskit/editor-common/toolbar';
import { Popup } from '@atlaskit/editor-common/ui';
import { calculateToolbarPositionTrackHead, calculateToolbarPositionOnCellSelection } from '@atlaskit/editor-common/utils';
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
import { AllSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
import { akEditorFloatingDialogZIndex } from '@atlaskit/editor-shared-styles';
import { ToolbarSection, ToolbarButtonGroup, ToolbarDropdownItemSection, useToolbarUI } from '@atlaskit/editor-toolbar';
import { ToolbarModelRenderer } from '@atlaskit/editor-toolbar-model';
import { fg } from '@atlaskit/platform-feature-flags';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
import { SELECTION_TOOLBAR_LABEL } from '../consts';
import { getKeyboardNavigationConfig } from './keyboard-config';
import { getDomRefFromSelection } from './utils';
var isToolbarComponent = function isToolbarComponent(component) {
return component.type === 'toolbar' && component.key === 'inline-text-toolbar';
};
var usePluginState = function usePluginState(api) {
return useSharedPluginStateWithSelector(api, ['connectivity', 'userPreferences', 'toolbar', 'selection', 'userIntent', 'editorViewMode'], function (state) {
var _state$connectivitySt, _state$userPreference, _state$toolbarState, _state$toolbarState2, _state$selectionState, _state$userIntentStat, _state$editorViewMode;
return {
connectivityStateMode: (_state$connectivitySt = state.connectivityState) === null || _state$connectivitySt === void 0 ? void 0 : _state$connectivitySt.mode,
editorToolbarDockingPreference: (_state$userPreference = state.userPreferencesState) === null || _state$userPreference === void 0 || (_state$userPreference = _state$userPreference.preferences) === null || _state$userPreference === void 0 ? void 0 : _state$userPreference.toolbarDockingPosition,
shouldShowToolbar: (_state$toolbarState = state.toolbarState) === null || _state$toolbarState === void 0 ? void 0 : _state$toolbarState.shouldShowToolbar,
selectedNode: (_state$toolbarState2 = state.toolbarState) === null || _state$toolbarState2 === void 0 ? void 0 : _state$toolbarState2.selectedNode,
selection: (_state$selectionState = state.selectionState) === null || _state$selectionState === void 0 ? void 0 : _state$selectionState.selection,
currentUserIntent: (_state$userIntentStat = state.userIntentState) === null || _state$userIntentStat === void 0 ? void 0 : _state$userIntentStat.currentUserIntent,
editorViewMode: (_state$editorViewMode = state.editorViewModeState) === null || _state$editorViewMode === void 0 ? void 0 : _state$editorViewMode.mode
};
});
};
var useOnPositionCalculated = function useOnPositionCalculated(editorView) {
var onPositionCalculated = useCallback(function (position) {
try {
// Show special position on cell selection only when editor controls experiment is enabled
var isEditorControlsEnabled = expValEquals('platform_editor_controls', 'cohort', 'variant1');
var isCellSelection = ('$anchorCell' in editorView.state.selection);
if (isCellSelection && isEditorControlsEnabled) {
return calculateToolbarPositionOnCellSelection(SELECTION_TOOLBAR_LABEL)(editorView, position);
}
return calculateToolbarPositionTrackHead(SELECTION_TOOLBAR_LABEL)(editorView, position);
} catch (error) {
logException(error, {
location: 'editor-plugin-toolbar/selectionToolbar'
});
return position;
}
}, [editorView]);
return onPositionCalculated;
};
export var SelectionToolbar = function SelectionToolbar(_ref) {
var _api$toolbar$actions$, _api$toolbar, _api$toolbar2;
var api = _ref.api,
editorView = _ref.editorView,
mountPoint = _ref.mountPoint,
disableSelectionToolbarWhenPinned = _ref.disableSelectionToolbarWhenPinned;
var _usePluginState = usePluginState(api),
connectivityStateMode = _usePluginState.connectivityStateMode,
editorToolbarDockingPreference = _usePluginState.editorToolbarDockingPreference,
currentUserIntent = _usePluginState.currentUserIntent,
shouldShowToolbar = _usePluginState.shouldShowToolbar,
editorViewMode = _usePluginState.editorViewMode;
var contextualFormattingEnabled = (_api$toolbar$actions$ = api === null || api === void 0 || (_api$toolbar = api.toolbar) === null || _api$toolbar === void 0 ? void 0 : _api$toolbar.actions.contextualFormattingMode()) !== null && _api$toolbar$actions$ !== void 0 ? _api$toolbar$actions$ : 'always-pinned';
var selectionToolbarConfigEnabled = shouldShowSelectionToolbar(contextualFormattingEnabled, editorToolbarDockingPreference);
var intl = useIntl();
var components = api === null || api === void 0 || (_api$toolbar2 = api.toolbar) === null || _api$toolbar2 === void 0 ? void 0 : _api$toolbar2.actions.getComponents();
var toolbar = components === null || components === void 0 ? void 0 : components.find(function (component) {
return isToolbarComponent(component);
});
var keyboardNavigation = useMemo(function () {
return getKeyboardNavigationConfig(editorView, intl, api);
}, [editorView, intl, api]);
var _useToolbarUI = useToolbarUI(),
isDisabled = _useToolbarUI.isDisabled;
var isOffline = isOfflineMode(connectivityStateMode);
var isTextSelection = !editorView.state.selection.empty && editorView.state.selection instanceof TextSelection;
var isAllSelection = !editorView.state.selection.empty && editorView.state.selection instanceof AllSelection;
var isCellSelection = !editorView.state.selection.empty && '$anchorCell' in editorView.state.selection;
var onPositionCalculated = useOnPositionCalculated(editorView);
if (selectionToolbarConfigEnabled && disableSelectionToolbarWhenPinned || !components || !toolbar) {
return null;
}
if (!(isTextSelection || isCellSelection || isAllSelection) || currentUserIntent === 'dragging' || !shouldShowToolbar || currentUserIntent === 'blockMenuOpen' && editorExperiment('platform_editor_block_menu', true) ||
// hide toolbar when user intent is not default, except when it's dragHandleSelected without cell selection
currentUserIntent && currentUserIntent !== 'default' && !(currentUserIntent === 'dragHandleSelected' && !isCellSelection) || isSSR()) {
return null;
}
return /*#__PURE__*/React.createElement(Popup
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
, {
offset: [0, 10],
target: getDomRefFromSelection(editorView),
onPositionCalculated: onPositionCalculated,
mountTo: mountPoint,
zIndex: fg('platform_editor_sel_toolbar_stacking_fix') ? akEditorFloatingDialogZIndex : undefined
}, /*#__PURE__*/React.createElement(EditorToolbarProvider, {
editorView: editorView,
editorToolbarDockingPreference: editorToolbarDockingPreference,
editorViewMode: editorViewMode !== null && editorViewMode !== void 0 ? editorViewMode : 'edit',
isOffline: isOffline
}, /*#__PURE__*/React.createElement(EditorToolbarUIProvider, {
api: api,
isDisabled: isDisabled
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
fireAnalyticsEvent: function fireAnalyticsEvent(payload) {
var _api$analytics;
api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.fireAnalyticsEvent(payload);
},
keyboardNavigation: keyboardNavigation
}, /*#__PURE__*/React.createElement(ToolbarModelRenderer, {
toolbar: toolbar,
components: components
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
,
fallbacks: {
section: ToolbarSection,
menuSection: ToolbarDropdownItemSection,
group: ToolbarButtonGroup
}
}))));
};
export var SelectionToolbarWithErrorBoundary = function SelectionToolbarWithErrorBoundary(_ref2) {
var _api$analytics2;
var api = _ref2.api,
editorView = _ref2.editorView,
mountPoint = _ref2.mountPoint,
disableSelectionToolbarWhenPinned = _ref2.disableSelectionToolbarWhenPinned;
return /*#__PURE__*/React.createElement(ErrorBoundary, {
component: ACTION_SUBJECT.TOOLBAR,
componentId: ACTION_SUBJECT_ID.SELECTION_TOOLBAR,
dispatchAnalyticsEvent: api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions.fireAnalyticsEvent,
fallbackComponent: null
}, /*#__PURE__*/React.createElement(SelectionToolbar, {
api: api,
editorView: editorView,
mountPoint: mountPoint,
disableSelectionToolbarWhenPinned: disableSelectionToolbarWhenPinned
}));
};