Add find and replace hooks for desktop app
This commit is contained in:
@@ -177,6 +177,7 @@ function Input(
|
|||||||
labelHidden,
|
labelHidden,
|
||||||
onFocus,
|
onFocus,
|
||||||
onBlur,
|
onBlur,
|
||||||
|
onRequestSubmit,
|
||||||
children,
|
children,
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { ResizingHeightContainer } from "~/components/ResizingHeightContainer";
|
|||||||
import Tooltip from "~/components/Tooltip";
|
import Tooltip from "~/components/Tooltip";
|
||||||
import useKeyDown from "~/hooks/useKeyDown";
|
import useKeyDown from "~/hooks/useKeyDown";
|
||||||
import useOnClickOutside from "~/hooks/useOnClickOutside";
|
import useOnClickOutside from "~/hooks/useOnClickOutside";
|
||||||
|
import Desktop from "~/utils/Desktop";
|
||||||
import { altDisplay, isModKey, metaDisplay } from "~/utils/keyboard";
|
import { altDisplay, isModKey, metaDisplay } from "~/utils/keyboard";
|
||||||
import { useEditor } from "./EditorContext";
|
import { useEditor } from "./EditorContext";
|
||||||
|
|
||||||
@@ -39,14 +40,40 @@ export default function FindAndReplace({ readOnly }: Props) {
|
|||||||
const [regexEnabled, setRegex] = React.useState(false);
|
const [regexEnabled, setRegex] = React.useState(false);
|
||||||
const [searchTerm, setSearchTerm] = React.useState("");
|
const [searchTerm, setSearchTerm] = React.useState("");
|
||||||
const [replaceTerm, setReplaceTerm] = React.useState("");
|
const [replaceTerm, setReplaceTerm] = React.useState("");
|
||||||
|
|
||||||
const popover = usePopoverState();
|
const popover = usePopoverState();
|
||||||
|
const { show } = popover;
|
||||||
|
|
||||||
|
// Hooks for desktop app menu items
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!Desktop.bridge) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("onFindInPage" in Desktop.bridge) {
|
||||||
|
Desktop.bridge.onFindInPage(() => {
|
||||||
|
selectionRef.current = window.getSelection()?.toString();
|
||||||
|
show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ("onReplaceInPage" in Desktop.bridge) {
|
||||||
|
Desktop.bridge.onReplaceInPage(() => {
|
||||||
|
setShowReplace(true);
|
||||||
|
show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [show]);
|
||||||
|
|
||||||
|
// Close handlers
|
||||||
useKeyDown("Escape", popover.hide);
|
useKeyDown("Escape", popover.hide);
|
||||||
useOnClickOutside(popover.unstable_referenceRef, popover.hide);
|
useOnClickOutside(popover.unstable_referenceRef, popover.hide);
|
||||||
|
|
||||||
|
// Keyboard shortcuts
|
||||||
useKeyDown(
|
useKeyDown(
|
||||||
(ev) => isModKey(ev) && !popover.visible && ev.code === "KeyF",
|
(ev) =>
|
||||||
|
isModKey(ev) &&
|
||||||
|
!popover.visible &&
|
||||||
|
ev.code === "KeyF" &&
|
||||||
|
// Handler is through AppMenu on desktop app
|
||||||
|
!Desktop.isElectron(),
|
||||||
(ev) => {
|
(ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
selectionRef.current = window.getSelection()?.toString();
|
selectionRef.current = window.getSelection()?.toString();
|
||||||
@@ -72,6 +99,7 @@ export default function FindAndReplace({ readOnly }: Props) {
|
|||||||
{ allowInInput: true }
|
{ allowInInput: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
const handleMore = React.useCallback(
|
const handleMore = React.useCallback(
|
||||||
() => setShowReplace((state) => !state),
|
() => setShowReplace((state) => !state),
|
||||||
[]
|
[]
|
||||||
@@ -122,18 +150,24 @@ export default function FindAndReplace({ readOnly }: Props) {
|
|||||||
|
|
||||||
const handleReplace = React.useCallback(
|
const handleReplace = React.useCallback(
|
||||||
(ev) => {
|
(ev) => {
|
||||||
|
if (readOnly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
editor.commands.replace({ text: replaceTerm });
|
editor.commands.replace({ text: replaceTerm });
|
||||||
},
|
},
|
||||||
[editor.commands, replaceTerm]
|
[editor.commands, readOnly, replaceTerm]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleReplaceAll = React.useCallback(
|
const handleReplaceAll = React.useCallback(
|
||||||
(ev) => {
|
(ev) => {
|
||||||
|
if (readOnly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
editor.commands.replaceAll({ text: replaceTerm });
|
editor.commands.replaceAll({ text: replaceTerm });
|
||||||
},
|
},
|
||||||
[editor.commands, replaceTerm]
|
[editor.commands, readOnly, replaceTerm]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChangeFind = React.useCallback(
|
const handleChangeFind = React.useCallback(
|
||||||
@@ -281,7 +315,7 @@ export default function FindAndReplace({ readOnly }: Props) {
|
|||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
<ResizingHeightContainer>
|
<ResizingHeightContainer>
|
||||||
{showReplace && (
|
{showReplace && !readOnly && (
|
||||||
<Flex gap={8}>
|
<Flex gap={8}>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
maxLength={255}
|
maxLength={255}
|
||||||
|
|||||||
10
app/typings/window.d.ts
vendored
10
app/typings/window.d.ts
vendored
@@ -96,6 +96,16 @@ declare global {
|
|||||||
* Go forward in history, if possible
|
* Go forward in history, if possible
|
||||||
*/
|
*/
|
||||||
goForward: () => void;
|
goForward: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a callback to be called when the application wants to open the find in page dialog.
|
||||||
|
*/
|
||||||
|
onFindInPage: (callback: () => void) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a callback to be called when the application wants to open the replace in page dialog.
|
||||||
|
*/
|
||||||
|
onReplaceInPage: (callback: () => void) => void;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user