UNPKG

@atlaskit/editor-plugin-find-replace

Version:

find replace plugin for @atlaskit/editor-core

269 lines (266 loc) 12.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.FIND_DEBOUNCE_MS = void 0; var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireDefault(require("react")); var _react2 = require("@emotion/react"); var _debounce = _interopRequireDefault(require("lodash/debounce")); var _rafSchd = _interopRequireDefault(require("raf-schd")); var _reactIntl = require("react-intl"); var _analytics = require("@atlaskit/editor-common/analytics"); var _messages = require("@atlaskit/editor-common/messages"); var _form = require("@atlaskit/form"); var _textLetterCase = _interopRequireDefault(require("@atlaskit/icon-lab/core/text-letter-case")); var _textStyle = _interopRequireDefault(require("@atlaskit/icon/core/text-style")); var _textfield = _interopRequireDefault(require("@atlaskit/textfield")); var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals"); var _FindReplaceTooltipButton = require("./FindReplaceTooltipButton"); var _uiStyles = require("./ui-styles"); function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } /* eslint-disable @atlaskit/design-system/consistent-css-prop-usage */ /** * @jsxRuntime classic * @jsx jsx */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766 var FIND_DEBOUNCE_MS = exports.FIND_DEBOUNCE_MS = 100; // eslint-disable-next-line @repo/internal/react/no-class-components var Find = /*#__PURE__*/function (_React$Component) { function Find(props) { var _this; (0, _classCallCheck2.default)(this, Find); _this = _callSuper(this, Find, [props]); (0, _defineProperty2.default)(_this, "findTextfieldRef", /*#__PURE__*/_react.default.createRef()); (0, _defineProperty2.default)(_this, "isComposing", false); (0, _defineProperty2.default)(_this, "syncFindText", function (onSynced) { var _this$state; // If the external prop findText changes and we aren't in a composition we should update to // use the external prop value. // // An example of where this may happen is when a find occurs through the user selecting some text // and pressing Mod-f. if (!_this.isComposing && _this.props.findText !== ((_this$state = _this.state) === null || _this$state === void 0 ? void 0 : _this$state.localFindText)) { _this.updateFindValue(_this.props.findText || '', onSynced); } }); (0, _defineProperty2.default)(_this, "focusFindTextfield", function () { var input = _this.findTextfieldRef.current; if (_this.props.shouldFocus && input) { input.select(); } }); (0, _defineProperty2.default)(_this, "handleFindChange", function (event) { _this.updateFindValue(event.target.value); }); // debounce (vs throttle) to not block typing inside find input while onFind runs (0, _defineProperty2.default)(_this, "debouncedFind", (0, _debounce.default)(function (value) { _this.props.onFind(value); }, FIND_DEBOUNCE_MS)); (0, _defineProperty2.default)(_this, "updateFindValue", function (value, onSynced) { _this.setState({ localFindText: value }, function () { if (_this.isComposing) { return; } onSynced && onSynced(); _this.debouncedFind(value); }); _this.props.setFindTyped(true); }); // throtlle between animation frames gives better experience on Enter compared to arbitrary value // it adjusts based on performance (and document size) (0, _defineProperty2.default)(_this, "handleFindKeyDownThrottled", (0, _rafSchd.default)(function (event) { if (event.key === 'Enter') { if (event.shiftKey) { _this.props.onFindPrev({ triggerMethod: _analytics.TRIGGER_METHOD.KEYBOARD }); } else { _this.props.onFindNext({ triggerMethod: _analytics.TRIGGER_METHOD.KEYBOARD }); } } else if (event.key === 'ArrowDown') { // we want to move focus between find & replace texfields when user hits up/down arrows _this.props.onArrowDown(); } })); (0, _defineProperty2.default)(_this, "handleFindKeyDown", function (event) { if (_this.isComposing) { return; } event.persist(); _this.handleFindKeyDownThrottled(event); }); (0, _defineProperty2.default)(_this, "handleFindKeyUp", function () { _this.handleFindKeyDownThrottled.cancel(); }); (0, _defineProperty2.default)(_this, "handleFindNextClick", function () { if (_this.isComposing) { return; } _this.props.onFindNext({ triggerMethod: _analytics.TRIGGER_METHOD.BUTTON }); }); (0, _defineProperty2.default)(_this, "handleFindPrevClick", function () { if (_this.isComposing) { return; } _this.props.onFindPrev({ triggerMethod: _analytics.TRIGGER_METHOD.BUTTON }); }); (0, _defineProperty2.default)(_this, "handleCompositionStart", function () { _this.isComposing = true; }); (0, _defineProperty2.default)(_this, "handleCompositionEnd", function (event) { _this.isComposing = false; // type for React.CompositionEvent doesn't set type for target correctly _this.updateFindValue(event.target.value); }); (0, _defineProperty2.default)(_this, "clearSearch", function () { _this.props.onCancel({ triggerMethod: _analytics.TRIGGER_METHOD.BUTTON }); }); (0, _defineProperty2.default)(_this, "handleMatchCaseClick", function () { if (_this.props.onToggleMatchCase) { _this.props.onToggleMatchCase(); _this.props.onFind(_this.props.findText); } }); (0, _defineProperty2.default)(_this, "matchCaseIconEle", function (iconProps) { return (0, _expValEquals.expValEquals)('platform_editor_find_and_replace_improvements', 'isEnabled', true) ? (0, _react2.jsx)(_textLetterCase.default, { label: iconProps.label, size: "small" }) : (0, _react2.jsx)(_textStyle.default, { label: _this.matchCase }); }); var formatMessage = props.intl.formatMessage; _this.find = formatMessage(_messages.findReplaceMessages.find); _this.noResultsFound = formatMessage(_messages.findReplaceMessages.noResultsFound); _this.matchCase = formatMessage(_messages.findReplaceMessages.matchCase); // We locally manage the value of the input inside this component in order to support compositions. // This requires some additional work inside componentDidUpdate to ensure we support changes that // occur to this value which do not originate from this component. _this.state = { localFindText: '' }; return _this; } (0, _inherits2.default)(Find, _React$Component); return (0, _createClass2.default)(Find, [{ key: "componentDidMount", value: function componentDidMount() { var _this2 = this; this.props.onFindTextfieldRefSet(this.findTextfieldRef); // focus initially on dialog mount if there is no find text provided if (!this.props.findText) { // Wait for findTextfieldRef to become available then focus setTimeout(function () { _this2.focusFindTextfield(); }, 100); } this.syncFindText(function () { // focus after input is synced if find text provided if (_this2.props.findText) { _this2.focusFindTextfield(); } }); } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { var _this$state2, _this3 = this; // focus on update if find text did not change if (this.props.findText === ((_this$state2 = this.state) === null || _this$state2 === void 0 ? void 0 : _this$state2.localFindText)) { this.focusFindTextfield(); } if (this.props.findText !== prevProps.findText && this.props.shouldFocus) { this.syncFindText(function () { // focus after input is synced if find text provided if (_this3.props.findText) { _this3.focusFindTextfield(); } }); } } }, { key: "componentWillUnmount", value: function componentWillUnmount() { this.debouncedFind.cancel(); this.handleFindKeyDownThrottled.cancel(); } }, { key: "render", value: function render() { var _this$props = this.props, findText = _this$props.findText, count = _this$props.count, shouldMatchCase = _this$props.shouldMatchCase, formatMessage = _this$props.intl.formatMessage; var resultsCount = formatMessage(_messages.findReplaceMessages.resultsCount, { selectedMatchPosition: count.index + 1, totalResultsCount: count.total }); var elemAfterInput = // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 (0, _react2.jsx)("div", { css: _uiStyles.afterInputSection }, (0, _react2.jsx)("div", { "aria-live": "polite" }, findText && // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 (0, _react2.jsx)("span", { "data-testid": "textfield-count", css: [_uiStyles.countStyles, _uiStyles.countStylesAlternateStyles] }, count.total === 0 ? this.noResultsFound : resultsCount)), (0, _react2.jsx)("div", { css: _uiStyles.matchCaseSection }, (0, _react2.jsx)(_FindReplaceTooltipButton.FindReplaceTooltipButton, { title: this.matchCase, appearance: "default", icon: this.matchCaseIconEle, iconLabel: this.matchCase, onClick: this.handleMatchCaseClick, isPressed: shouldMatchCase }))); return ( // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 (0, _react2.jsx)("div", { css: [_uiStyles.sectionWrapperStyles, _uiStyles.sectionWrapperStylesAlternate] }, (0, _react2.jsx)("div", { css: _uiStyles.textFieldWrapper }, (0, _react2.jsx)(_form.Label, { htmlFor: "find-text-field" }, this.find), (0, _react2.jsx)(_textfield.default, { name: "find", id: "find-text-field", testId: "find-field", appearance: "standard", value: this.state.localFindText, ref: this.findTextfieldRef, autoComplete: "off", onChange: this.handleFindChange, onKeyDown: this.handleFindKeyDown, onKeyUp: this.handleFindKeyUp, onBlur: this.props.onFindBlur, onCompositionStart: this.handleCompositionStart, onCompositionEnd: this.handleCompositionEnd, elemAfterInput: elemAfterInput }))) ); } }]); }(_react.default.Component); // eslint-disable-next-line @typescript-eslint/ban-types var _default_1 = (0, _reactIntl.injectIntl)(Find); var _default = exports.default = _default_1;