Add 'Find and replace' option to menu on mobile (#6368)

This commit is contained in:
Tom Moor
2024-01-10 05:07:05 -08:00
committed by GitHub
parent 7d7d0fd9ca
commit 6e1347c2a7
4 changed files with 62 additions and 4 deletions

View File

@@ -25,10 +25,18 @@ import { altDisplay, isModKey, metaDisplay } from "~/utils/keyboard";
import { useEditor } from "./EditorContext";
type Props = {
open: boolean;
onOpen: () => void;
onClose: () => void;
readOnly?: boolean;
};
export default function FindAndReplace({ readOnly }: Props) {
export default function FindAndReplace({
readOnly,
open,
onOpen,
onClose,
}: Props) {
const editor = useEditor();
const finalFocusRef = React.useRef<HTMLElement>(
editor.view.dom.parentElement
@@ -46,6 +54,12 @@ export default function FindAndReplace({ readOnly }: Props) {
const popover = usePopoverState();
const { show } = popover;
React.useEffect(() => {
if (open) {
show();
}
}, [open]);
// Hooks for desktop app menu items
React.useEffect(() => {
if (!Desktop.bridge) {
@@ -209,6 +223,7 @@ export default function FindAndReplace({ readOnly }: Props) {
React.useEffect(() => {
if (popover.visible) {
onOpen();
const startSearchText = selectionRef.current || searchTerm;
editor.commands.find({
@@ -225,6 +240,7 @@ export default function FindAndReplace({ readOnly }: Props) {
setSearchTerm(selectionRef.current);
}
} else {
onClose();
setShowReplace(false);
editor.commands.clearSearch();
}

View File

@@ -1,4 +1,5 @@
import escapeRegExp from "lodash/escapeRegExp";
import { observable } from "mobx";
import { Node } from "prosemirror-model";
import { Command, Plugin, PluginKey } from "prosemirror-state";
import { Decoration, DecorationSet } from "prosemirror-view";
@@ -68,6 +69,11 @@ export default class FindAndReplaceExtension extends Extension {
* Clear the current search
*/
clearSearch: () => this.clear(),
/**
* Open the find and replace UI
*/
openFindAndReplace: () => this.openFindAndReplace(),
};
}
@@ -142,6 +148,13 @@ export default class FindAndReplaceExtension extends Extension {
};
}
public openFindAndReplace(): Command {
return (state, dispatch) => {
dispatch?.(state.tr.setMeta(pluginKey, { open: true }));
return true;
};
}
private get findRegExp() {
try {
return RegExp(
@@ -275,6 +288,9 @@ export default class FindAndReplaceExtension extends Extension {
const action = tr.getMeta(pluginKey);
if (action) {
if (action.open) {
this.open = true;
}
return this.createDeco(tr.doc);
}
@@ -295,9 +311,21 @@ export default class FindAndReplaceExtension extends Extension {
}
public widget = ({ readOnly }: WidgetProps) => (
<FindAndReplace readOnly={readOnly} />
<FindAndReplace
readOnly={readOnly}
open={this.open}
onOpen={() => {
this.open = true;
}}
onClose={() => {
this.open = false;
}}
/>
);
@observable
private open = false;
private results: { from: number; to: number }[] = [];
private currentResultIndex = 0;
private searchTerm = "";

View File

@@ -1,5 +1,5 @@
import { observer } from "mobx-react";
import { EditIcon, RestoreIcon } from "outline-icons";
import { EditIcon, RestoreIcon, SearchIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
@@ -64,6 +64,7 @@ type Props = {
showToggleEmbeds?: boolean;
showPin?: boolean;
label?: (props: MenuButtonHTMLProps) => React.ReactNode;
onFindAndReplace?: () => void;
onRename?: () => void;
onOpen?: () => void;
onClose?: () => void;
@@ -76,6 +77,7 @@ function DocumentMenu({
showToggleEmbeds,
showDisplayOptions,
label,
onFindAndReplace,
onRename,
onOpen,
onClose,
@@ -259,6 +261,13 @@ function DocumentMenu({
actionToMenuItem(subscribeDocument, context),
actionToMenuItem(unsubscribeDocument, context),
...(isMobile ? [actionToMenuItem(shareDocument, context)] : []),
{
type: "button",
title: `${t("Find and replace")}`,
visible: !!onFindAndReplace && isMobile,
onClick: () => onFindAndReplace?.(),
icon: <SearchIcon />,
},
{
type: "separator",
},

View File

@@ -20,7 +20,10 @@ import Badge from "~/components/Badge";
import Button from "~/components/Button";
import Collaborators from "~/components/Collaborators";
import DocumentBreadcrumb from "~/components/DocumentBreadcrumb";
import { useEditingFocus } from "~/components/DocumentContext";
import {
useDocumentContext,
useEditingFocus,
} from "~/components/DocumentContext";
import Header from "~/components/Header";
import EmojiIcon from "~/components/Icons/EmojiIcon";
import Star from "~/components/Star";
@@ -94,6 +97,7 @@ function DocumentHeader({
const isMobile = useMobile();
const isRevision = !!revision;
const isEditingFocus = useEditingFocus();
const { editor } = useDocumentContext();
// We cache this value for as long as the component is mounted so that if you
// apply a template there is still the option to replace it until the user
@@ -344,6 +348,7 @@ function DocumentHeader({
neutral
/>
)}
onFindAndReplace={editor?.commands.openFindAndReplace}
showToggleEmbeds={canToggleEmbeds}
showDisplayOptions
/>