diff --git a/app/components/DocumentListItem.js b/app/components/DocumentListItem.js
index 08986eb81..610a1b48d 100644
--- a/app/components/DocumentListItem.js
+++ b/app/components/DocumentListItem.js
@@ -15,6 +15,7 @@ import Flex from "components/Flex";
import Highlight from "components/Highlight";
import StarButton, { AnimatedStar } from "components/Star";
import Tooltip from "components/Tooltip";
+import useBoolean from "hooks/useBoolean";
import useCurrentTeam from "hooks/useCurrentTeam";
import useCurrentUser from "hooks/useCurrentUser";
import useStores from "hooks/useStores";
@@ -46,7 +47,7 @@ function DocumentListItem(props: Props, ref) {
const { policies } = useStores();
const currentUser = useCurrentUser();
const currentTeam = useCurrentTeam();
- const [menuOpen, setMenuOpen] = React.useState(false);
+ const [menuOpen, handleMenuOpen, handleMenuClose] = useBoolean();
const {
document,
showNestedDocuments,
@@ -66,9 +67,6 @@ function DocumentListItem(props: Props, ref) {
!document.isDraft && !document.isArchived && !document.isTemplate;
const can = policies.abilities(currentTeam.id);
- const handleMenuOpen = React.useCallback(() => setMenuOpen(true), []);
- const handleMenuClosed = React.useCallback(() => setMenuOpen(false), []);
-
return (
diff --git a/app/components/Sidebar/components/CollectionLink.js b/app/components/Sidebar/components/CollectionLink.js
index 5366fcd51..25284b124 100644
--- a/app/components/Sidebar/components/CollectionLink.js
+++ b/app/components/Sidebar/components/CollectionLink.js
@@ -12,6 +12,7 @@ import DropCursor from "./DropCursor";
import DropToImport from "./DropToImport";
import EditableTitle from "./EditableTitle";
import SidebarLink from "./SidebarLink";
+import useBoolean from "hooks/useBoolean";
import useStores from "hooks/useStores";
import CollectionMenu from "menus/CollectionMenu";
import CollectionSortMenu from "menus/CollectionSortMenu";
@@ -35,7 +36,7 @@ function CollectionLink({
isDraggingAnyCollection,
onChangeDragging,
}: Props) {
- const [menuOpen, setMenuOpen] = React.useState(false);
+ const [menuOpen, handleMenuOpen, handleMenuClose] = useBoolean();
const handleTitleChange = React.useCallback(
async (name: string) => {
@@ -163,14 +164,14 @@ function CollectionLink({
{can.update && (
setMenuOpen(true)}
- onClose={() => setMenuOpen(false)}
+ onOpen={handleMenuOpen}
+ onClose={handleMenuClose}
/>
)}
setMenuOpen(true)}
- onClose={() => setMenuOpen(false)}
+ onOpen={handleMenuOpen}
+ onClose={handleMenuClose}
/>
>
}
diff --git a/app/components/Sidebar/components/DocumentLink.js b/app/components/Sidebar/components/DocumentLink.js
index 689f04fc0..d813c08ea 100644
--- a/app/components/Sidebar/components/DocumentLink.js
+++ b/app/components/Sidebar/components/DocumentLink.js
@@ -12,6 +12,7 @@ import DropCursor from "./DropCursor";
import DropToImport from "./DropToImport";
import EditableTitle from "./EditableTitle";
import SidebarLink from "./SidebarLink";
+import useBoolean from "hooks/useBoolean";
import useStores from "hooks/useStores";
import DocumentMenu from "menus/DocumentMenu";
import { type NavigationNode } from "types";
@@ -120,7 +121,7 @@ function DocumentLink(
[documents, document]
);
- const [menuOpen, setMenuOpen] = React.useState(false);
+ const [menuOpen, handleMenuOpen, handleMenuClose] = useBoolean();
const isMoving = documents.movingDocumentId === node.id;
const manualSort = collection?.sort.field === "index";
@@ -245,8 +246,8 @@ function DocumentLink(
setMenuOpen(true)}
- onClose={() => setMenuOpen(false)}
+ onOpen={handleMenuOpen}
+ onClose={handleMenuClose}
/>
) : undefined
diff --git a/app/hooks/useBoolean.js b/app/hooks/useBoolean.js
new file mode 100644
index 000000000..252a5aebc
--- /dev/null
+++ b/app/hooks/useBoolean.js
@@ -0,0 +1,23 @@
+// @flow
+import * as React from "react";
+
+type InitialState = boolean | (() => boolean);
+
+/**
+ * React hook to manage booleans
+ *
+ * @param initialState the initial boolean state value
+ */
+export default function useBoolean(initialState: InitialState = false) {
+ const [value, setValue] = React.useState(initialState);
+
+ const setTrue = React.useCallback(() => {
+ setValue(true);
+ }, []);
+
+ const setFalse = React.useCallback(() => {
+ setValue(false);
+ }, []);
+
+ return [value, setTrue, setFalse];
+}
diff --git a/app/menus/AccountMenu.js b/app/menus/AccountMenu.js
index 8a2034bb0..e8bcdb579 100644
--- a/app/menus/AccountMenu.js
+++ b/app/menus/AccountMenu.js
@@ -19,6 +19,7 @@ import MenuItem, { MenuAnchor } from "components/ContextMenu/MenuItem";
import Separator from "components/ContextMenu/Separator";
import Flex from "components/Flex";
import Guide from "components/Guide";
+import useBoolean from "hooks/useBoolean";
import usePrevious from "hooks/usePrevious";
import useStores from "hooks/useStores";
@@ -78,9 +79,11 @@ function AccountMenu(props: Props) {
const { auth, ui } = useStores();
const previousTheme = usePrevious(ui.theme);
const { t } = useTranslation();
- const [keyboardShortcutsOpen, setKeyboardShortcutsOpen] = React.useState(
- false
- );
+ const [
+ keyboardShortcutsOpen,
+ handleKeyboardShortcutsOpen,
+ handleKeyboardShortcutsClose,
+ ] = useBoolean();
React.useEffect(() => {
if (ui.theme !== previousTheme) {
@@ -92,7 +95,7 @@ function AccountMenu(props: Props) {
<>
setKeyboardShortcutsOpen(false)}
+ onRequestClose={handleKeyboardShortcutsClose}
title={t("Keyboard shortcuts")}
>
@@ -102,7 +105,7 @@ function AccountMenu(props: Props) {
-