UNPKG

@atlaskit/editor-plugin-find-replace

Version:

find replace plugin for @atlaskit/editor-core

168 lines (166 loc) 6.96 kB
import React, { useLayoutEffect, useState } from 'react'; import { TRIGGER_METHOD } from '@atlaskit/editor-common/analytics'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { blur, toggleMatchCase } from '../pm-plugins/commands'; import { activateWithAnalytics, cancelSearchWithAnalytics, findNextWithAnalytics, findPrevWithAnalytics, findWithAnalytics, replaceAllWithAnalytics, replaceWithAnalytics } from '../pm-plugins/commands-with-analytics'; import FindReplaceDropdown from './FindReplaceDropdown'; import FindReplaceToolbarButton from './FindReplaceToolbarButton'; // light implementation of useSharedPluginState(). This is due to findreplace // being the only plugin that previously used WithPluginState with // debounce=false. That was implemented because of text sync issues // between editor & findreplace dialog. // To address under ENGHEALTH-5853 const useSharedPluginStateNoDebounce = api => { var _api$findReplace; const [state, setState] = useState(api === null || api === void 0 ? void 0 : (_api$findReplace = api.findReplace) === null || _api$findReplace === void 0 ? void 0 : _api$findReplace.sharedState.currentState()); useLayoutEffect(() => { var _api$findReplace2; const unsub = api === null || api === void 0 ? void 0 : (_api$findReplace2 = api.findReplace) === null || _api$findReplace2 === void 0 ? void 0 : _api$findReplace2.sharedState.onChange(({ nextSharedState }) => { setState(nextSharedState); }); return () => { unsub === null || unsub === void 0 ? void 0 : unsub(); }; }, [api]); return { findReplaceState: state }; }; const FindReplaceToolbarButtonWithState = ({ popupsBoundariesElement, popupsMountPoint, popupsScrollableElement, isToolbarReducedSpacing, editorView, containerElement, dispatchAnalyticsEvent, takeFullWidth, api, isButtonHidden, doesNotHaveButton }) => { var _api$analytics, _matches$index; const editorAnalyticsAPI = api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions; const { findReplaceState } = useSharedPluginStateNoDebounce(api); const shouldMatchCase = findReplaceState === null || findReplaceState === void 0 ? void 0 : findReplaceState.shouldMatchCase; const isActive = findReplaceState === null || findReplaceState === void 0 ? void 0 : findReplaceState.isActive; const findText = findReplaceState === null || findReplaceState === void 0 ? void 0 : findReplaceState.findText; const replaceText = findReplaceState === null || findReplaceState === void 0 ? void 0 : findReplaceState.replaceText; const index = findReplaceState === null || findReplaceState === void 0 ? void 0 : findReplaceState.index; const matches = findReplaceState === null || findReplaceState === void 0 ? void 0 : findReplaceState.matches; const shouldFocus = findReplaceState === null || findReplaceState === void 0 ? void 0 : findReplaceState.shouldFocus; if (!editorView) { return null; } // we need the editor to be in focus for scrollIntoView() to work // so we focus it while we run the command, then put focus back into // whatever element was previously focused in find replace component const runWithEditorFocused = fn => { const activeElement = document.activeElement; editorView.focus(); fn(); activeElement === null || activeElement === void 0 ? void 0 : activeElement.focus(); }; const dispatchCommand = cmd => { const { state, dispatch } = editorView; cmd(state, dispatch, editorView); }; const handleActivate = () => { runWithEditorFocused(() => dispatchCommand(activateWithAnalytics(editorAnalyticsAPI)({ triggerMethod: TRIGGER_METHOD.TOOLBAR }))); }; const handleFind = keyword => { runWithEditorFocused(() => dispatchCommand(findWithAnalytics(editorAnalyticsAPI)({ editorView, containerElement, keyword }))); }; const handleFindNext = ({ triggerMethod }) => { runWithEditorFocused(() => dispatchCommand(findNextWithAnalytics(editorAnalyticsAPI, editorView)({ triggerMethod }))); }; const handleFindPrev = ({ triggerMethod }) => { runWithEditorFocused(() => dispatchCommand(findPrevWithAnalytics(editorAnalyticsAPI, editorView)({ triggerMethod }))); }; const handleReplace = ({ triggerMethod, replaceText }) => { runWithEditorFocused(() => dispatchCommand(replaceWithAnalytics(editorAnalyticsAPI)({ triggerMethod, replaceText }))); }; const handleReplaceAll = ({ replaceText }) => { runWithEditorFocused(() => dispatchCommand(replaceAllWithAnalytics(editorAnalyticsAPI)({ replaceText }))); }; const handleFindBlur = () => { dispatchCommand(blur()); }; const handleCancel = ({ triggerMethod }) => { dispatchCommand(cancelSearchWithAnalytics(editorAnalyticsAPI)({ triggerMethod })); editorView.focus(); }; const handleToggleMatchCase = () => { dispatchCommand(toggleMatchCase()); }; if (shouldMatchCase === undefined || isActive === undefined || findText === undefined || replaceText === undefined || index === undefined || matches === undefined || shouldFocus === undefined) { return null; } const DropDownComponent = doesNotHaveButton ? FindReplaceDropdown : FindReplaceToolbarButton; return /*#__PURE__*/React.createElement(DropDownComponent, { shouldMatchCase: shouldMatchCase, onToggleMatchCase: handleToggleMatchCase, isActive: isActive, findText: findText, index: index, numMatches: matches.length, isReplaceable: expValEquals('platform_editor_find_and_replace_improvements', 'isEnabled', true) ? (_matches$index = matches[index]) === null || _matches$index === void 0 ? void 0 : _matches$index.canReplace : undefined, numReplaceable: expValEquals('platform_editor_find_and_replace_improvements', 'isEnabled', true) ? // eslint-disable-next-line @atlassian/perf-linting/no-expensive-computations-in-render -- Ignored via go/ees017 (to be fixed) matches.filter(match => match.canReplace === true).length : undefined, replaceText: replaceText, shouldFocus: shouldFocus, popupsBoundariesElement: popupsBoundariesElement, popupsMountPoint: popupsMountPoint, popupsScrollableElement: popupsScrollableElement, isReducedSpacing: !!isToolbarReducedSpacing, dispatchAnalyticsEvent: dispatchAnalyticsEvent, onFindBlur: handleFindBlur, onCancel: handleCancel, onActivate: handleActivate, onFind: handleFind, onFindNext: handleFindNext, onFindPrev: handleFindPrev, onReplace: handleReplace, onReplaceAll: handleReplaceAll, takeFullWidth: !!takeFullWidth, isButtonHidden: isButtonHidden }); }; const _default_1 = /*#__PURE__*/React.memo(FindReplaceToolbarButtonWithState); export default _default_1;