feat: useBoolean hook (#2314)

* feat: Add useBoolean hook and example usage

* More example usage

* chore: More useBoolean conversion
This commit is contained in:
Tom Moor
2021-07-15 15:27:03 -04:00
committed by GitHub
parent 8884da8a4b
commit 31714efb0b
11 changed files with 87 additions and 73 deletions

View File

@@ -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 (
<DocumentLink
ref={ref}
@@ -147,7 +145,7 @@ function DocumentListItem(props: Props, ref) {
document={document}
showPin={showPin}
onOpen={handleMenuOpen}
onClose={handleMenuClosed}
onClose={handleMenuClose}
modal={false}
/>
</Actions>

View File

@@ -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 && (
<CollectionSortMenuWithMargin
collection={collection}
onOpen={() => setMenuOpen(true)}
onClose={() => setMenuOpen(false)}
onOpen={handleMenuOpen}
onClose={handleMenuClose}
/>
)}
<CollectionMenu
collection={collection}
onOpen={() => setMenuOpen(true)}
onClose={() => setMenuOpen(false)}
onOpen={handleMenuOpen}
onClose={handleMenuClose}
/>
</>
}

View File

@@ -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(
<Fade>
<DocumentMenu
document={document}
onOpen={() => setMenuOpen(true)}
onClose={() => setMenuOpen(false)}
onOpen={handleMenuOpen}
onClose={handleMenuClose}
/>
</Fade>
) : undefined

23
app/hooks/useBoolean.js Normal file
View File

@@ -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];
}

View File

@@ -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) {
<>
<Guide
isOpen={keyboardShortcutsOpen}
onRequestClose={() => setKeyboardShortcutsOpen(false)}
onRequestClose={handleKeyboardShortcutsClose}
title={t("Keyboard shortcuts")}
>
<KeyboardShortcuts />
@@ -102,7 +105,7 @@ function AccountMenu(props: Props) {
<MenuItem {...menu} as={Link} to={settings()}>
{t("Settings")}
</MenuItem>
<MenuItem {...menu} onClick={() => setKeyboardShortcutsOpen(true)}>
<MenuItem {...menu} onClick={handleKeyboardShortcutsOpen}>
{t("Keyboard shortcuts")}
</MenuItem>
<MenuItem {...menu} href={developers()} target="_blank">

View File

@@ -39,6 +39,7 @@ import Tabs from "components/Tabs";
import Tooltip from "components/Tooltip";
import Collection from "../models/Collection";
import { updateCollectionUrl } from "../utils/routeHelpers";
import useBoolean from "hooks/useBoolean";
import useCurrentTeam from "hooks/useCurrentTeam";
import useImportDocument from "hooks/useImportDocument";
import useStores from "hooks/useStores";
@@ -54,7 +55,11 @@ function CollectionScene() {
const team = useCurrentTeam();
const [isFetching, setFetching] = React.useState();
const [error, setError] = React.useState();
const [permissionsModalOpen, setPermissionsModalOpen] = React.useState(false);
const [
permissionsModalOpen,
handlePermissionsModalOpen,
handlePermissionsModalClose,
] = useBoolean();
const id = params.id || "";
const collection: ?Collection =
@@ -102,14 +107,6 @@ function CollectionScene() {
load();
}, [collections, isFetching, collection, error, id, can]);
const handlePermissionsModalOpen = React.useCallback(() => {
setPermissionsModalOpen(true);
}, []);
const handlePermissionsModalClose = React.useCallback(() => {
setPermissionsModalOpen(false);
}, []);
const handleRejection = React.useCallback(() => {
ui.showToast(
t("Document not supported try Markdown, Plain text, HTML, or Word"),

View File

@@ -17,6 +17,7 @@ import AddGroupsToCollection from "./AddGroupsToCollection";
import AddPeopleToCollection from "./AddPeopleToCollection";
import CollectionGroupMemberListItem from "./components/CollectionGroupMemberListItem";
import MemberListItem from "./components/MemberListItem";
import useBoolean from "hooks/useBoolean";
import useCurrentUser from "hooks/useCurrentUser";
import useStores from "hooks/useStores";
@@ -34,8 +35,16 @@ function CollectionPermissions({ collection }: Props) {
users,
groups,
} = useStores();
const [addGroupModalOpen, setAddGroupModalOpen] = React.useState(false);
const [addMemberModalOpen, setAddMemberModalOpen] = React.useState(false);
const [
addGroupModalOpen,
handleAddGroupModalOpen,
handleAddGroupModalClose,
] = useBoolean();
const [
addMemberModalOpen,
handleAddMemberModalOpen,
handleAddMemberModalClose,
] = useBoolean();
const handleRemoveUser = React.useCallback(
async (user) => {
@@ -183,7 +192,7 @@ function CollectionPermissions({ collection }: Props) {
<Actions>
<Button
type="button"
onClick={() => setAddGroupModalOpen(true)}
onClick={handleAddGroupModalOpen}
icon={<PlusIcon />}
neutral
>
@@ -191,7 +200,7 @@ function CollectionPermissions({ collection }: Props) {
</Button>{" "}
<Button
type="button"
onClick={() => setAddMemberModalOpen(true)}
onClick={handleAddMemberModalOpen}
icon={<PlusIcon />}
neutral
>
@@ -244,24 +253,24 @@ function CollectionPermissions({ collection }: Props) {
title={t(`Add groups to {{ collectionName }}`, {
collectionName: collection.name,
})}
onRequestClose={() => setAddGroupModalOpen(false)}
onRequestClose={handleAddGroupModalClose}
isOpen={addGroupModalOpen}
>
<AddGroupsToCollection
collection={collection}
onSubmit={() => setAddGroupModalOpen(false)}
onSubmit={handleAddGroupModalClose}
/>
</Modal>
<Modal
title={t(`Add people to {{ collectionName }}`, {
collectionName: collection.name,
})}
onRequestClose={() => setAddMemberModalOpen(false)}
onRequestClose={handleAddMemberModalClose}
isOpen={addMemberModalOpen}
>
<AddPeopleToCollection
collection={collection}
onSubmit={() => setAddMemberModalOpen(false)}
onSubmit={handleAddMemberModalClose}
/>
</Modal>
</Flex>

View File

@@ -8,20 +8,15 @@ import KeyboardShortcuts from "scenes/KeyboardShortcuts";
import Guide from "components/Guide";
import NudeButton from "components/NudeButton";
import Tooltip from "components/Tooltip";
import useBoolean from "hooks/useBoolean";
function KeyboardShortcutsButton() {
const { t } = useTranslation();
const [keyboardShortcutsOpen, setKeyboardShortcutsOpen] = React.useState(
false
);
const handleCloseKeyboardShortcuts = React.useCallback(() => {
setKeyboardShortcutsOpen(false);
}, []);
const handleOpenKeyboardShortcuts = React.useCallback(() => {
setKeyboardShortcutsOpen(true);
}, []);
const [
keyboardShortcutsOpen,
handleOpenKeyboardShortcuts,
handleCloseKeyboardShortcuts,
] = useBoolean();
return (
<>

View File

@@ -14,6 +14,7 @@ import Modal from "components/Modal";
import PaginatedList from "components/PaginatedList";
import Scene from "components/Scene";
import Subheading from "components/Subheading";
import useBoolean from "hooks/useBoolean";
import useCurrentTeam from "hooks/useCurrentTeam";
import useStores from "hooks/useStores";
import GroupMenu from "menus/GroupMenu";
@@ -23,15 +24,11 @@ function Groups() {
const { policies, groups } = useStores();
const team = useCurrentTeam();
const can = policies.abilities(team.id);
const [newGroupModalOpen, setNewGroupModalOpen] = React.useState(false);
const handleNewGroupModalOpen = React.useCallback(() => {
setNewGroupModalOpen(true);
}, []);
const handleNewGroupModalClose = React.useCallback(() => {
setNewGroupModalOpen(false);
}, []);
const [
newGroupModalOpen,
handleNewGroupModalOpen,
handleNewGroupModalClose,
] = useBoolean();
return (
<Scene

View File

@@ -18,6 +18,7 @@ import Modal from "components/Modal";
import Scene from "components/Scene";
import PeopleTable from "./components/PeopleTable";
import UserStatusFilter from "./components/UserStatusFilter";
import useBoolean from "hooks/useBoolean";
import useCurrentTeam from "hooks/useCurrentTeam";
import useQuery from "hooks/useQuery";
import useStores from "hooks/useStores";
@@ -26,7 +27,11 @@ function People(props) {
const topRef = React.useRef();
const location = useLocation();
const history = useHistory();
const [inviteModalOpen, setInviteModalOpen] = React.useState(false);
const [
inviteModalOpen,
handleInviteModalOpen,
handleInviteModalClose,
] = useBoolean();
const team = useCurrentTeam();
const { users, policies } = useStores();
const { t } = useTranslation();
@@ -96,14 +101,6 @@ function People(props) {
userIds,
]);
const handleInviteModalOpen = React.useCallback(() => {
setInviteModalOpen(true);
}, []);
const handleInviteModalClose = React.useCallback(() => {
setInviteModalOpen(false);
}, []);
const handleFilter = React.useCallback(
(filter) => {
if (filter) {

View File

@@ -13,6 +13,7 @@ import PaginatedList from "components/PaginatedList";
import Scene from "components/Scene";
import Subheading from "components/Subheading";
import TokenListItem from "./components/TokenListItem";
import useBoolean from "hooks/useBoolean";
import useCurrentTeam from "hooks/useCurrentTeam";
import useStores from "hooks/useStores";
@@ -20,17 +21,9 @@ function Tokens() {
const team = useCurrentTeam();
const { t } = useTranslation();
const { apiKeys, policies } = useStores();
const [newModalOpen, setNewModalOpen] = React.useState(false);
const [newModalOpen, handleNewModalOpen, handleNewModalClose] = useBoolean();
const can = policies.abilities(team.id);
const handleNewModalOpen = React.useCallback(() => {
setNewModalOpen(true);
}, []);
const handleNewModalClose = React.useCallback(() => {
setNewModalOpen(false);
}, []);
return (
<Scene
title={t("API Tokens")}