aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-10-30 11:18:11 +0100
committerArmand Philippot <git@armandphilippot.com>2023-11-11 18:15:27 +0100
commit84a679b0e48ed76eee2fa44d3caac83591aa3c8c (patch)
tree1d418a6c514ff8a04b84ba35c98736e8450f968c /src/components
parent60c49f18389ff625177a57277ef8f292a31097bf (diff)
feat(hooks): add useBoolean and useToggle hooks
Diffstat (limited to 'src/components')
-rw-r--r--src/components/molecules/forms/flipping-label/flipping-label.stories.tsx7
-rw-r--r--src/components/organisms/forms/ackee-toggle/ackee-toggle.tsx17
-rw-r--r--src/components/organisms/forms/comment-form/comment-form.tsx17
-rw-r--r--src/components/organisms/forms/contact-form/contact-form.tsx17
-rw-r--r--src/components/organisms/layout/comment.tsx13
-rw-r--r--src/components/organisms/toolbar/main-nav.stories.tsx8
-rw-r--r--src/components/organisms/toolbar/search.stories.tsx16
-rw-r--r--src/components/organisms/toolbar/settings.stories.tsx8
-rw-r--r--src/components/organisms/toolbar/toolbar.tsx53
9 files changed, 72 insertions, 84 deletions
diff --git a/src/components/molecules/forms/flipping-label/flipping-label.stories.tsx b/src/components/molecules/forms/flipping-label/flipping-label.stories.tsx
index c3c4f9a..906a488 100644
--- a/src/components/molecules/forms/flipping-label/flipping-label.stories.tsx
+++ b/src/components/molecules/forms/flipping-label/flipping-label.stories.tsx
@@ -1,5 +1,5 @@
import type { ComponentMeta, ComponentStory } from '@storybook/react';
-import { useCallback, useState } from 'react';
+import { useToggle } from '../../../../utils/hooks';
import { Button, Icon } from '../../../atoms';
import { FlippingLabel } from './flipping-label';
@@ -74,11 +74,10 @@ const Template: ComponentStory<typeof FlippingLabel> = ({
isActive,
...args
}) => {
- const [active, setActive] = useState<boolean>(isActive);
- const updateState = useCallback(() => setActive((prev) => !prev), []);
+ const [active, toggle] = useToggle(isActive);
return (
- <Button kind="neutral" onClick={updateState} shape="initial" type="button">
+ <Button kind="neutral" onClick={toggle} shape="initial" type="button">
<FlippingLabel {...args} isActive={active} />
</Button>
);
diff --git a/src/components/organisms/forms/ackee-toggle/ackee-toggle.tsx b/src/components/organisms/forms/ackee-toggle/ackee-toggle.tsx
index 9493095..2fea0a7 100644
--- a/src/components/organisms/forms/ackee-toggle/ackee-toggle.tsx
+++ b/src/components/organisms/forms/ackee-toggle/ackee-toggle.tsx
@@ -1,7 +1,7 @@
/* eslint-disable max-statements */
-import { type FC, useState, useCallback } from 'react';
+import type { FC } from 'react';
import { useIntl } from 'react-intl';
-import { useAckee } from '../../../../utils/hooks';
+import { useAckee, useBoolean } from '../../../../utils/hooks';
import { Legend, List, ListItem } from '../../../atoms';
import {
Switch,
@@ -25,7 +25,11 @@ export type AckeeToggleProps = Omit<
export const AckeeToggle: FC<AckeeToggleProps> = ({ direction, ...props }) => {
const intl = useIntl();
const [tracking, toggleTracking] = useAckee();
- const [isTooltipOpened, setIsTooltipOpened] = useState(false);
+ const {
+ deactivate: closeTooltip,
+ state: isTooltipOpened,
+ toggle: toggleTooltip,
+ } = useBoolean(false);
const ackeeLabel = intl.formatMessage({
defaultMessage: 'Tracking:',
@@ -64,13 +68,6 @@ export const AckeeToggle: FC<AckeeToggleProps> = ({ direction, ...props }) => {
{ id: 'ackee-partial' as const, label: partialLabel, value: 'partial' },
] satisfies [SwitchOption, SwitchOption];
- const closeTooltip = useCallback(() => {
- setIsTooltipOpened(false);
- }, []);
- const toggleTooltip = useCallback(() => {
- setIsTooltipOpened((prev) => !prev);
- }, []);
-
return (
<Switch
{...props}
diff --git a/src/components/organisms/forms/comment-form/comment-form.tsx b/src/components/organisms/forms/comment-form/comment-form.tsx
index b5f2d64..9059cbc 100644
--- a/src/components/organisms/forms/comment-form/comment-form.tsx
+++ b/src/components/organisms/forms/comment-form/comment-form.tsx
@@ -10,6 +10,7 @@ import {
useId,
} from 'react';
import { useIntl } from 'react-intl';
+import { useBoolean } from '../../../../utils/hooks';
import {
Button,
Form,
@@ -77,15 +78,19 @@ export const CommentForm: FC<CommentFormProps> = ({
};
}, [parentId]);
const [data, setData] = useState(emptyForm);
- const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
+ const {
+ activate: activateNotice,
+ deactivate: deactivateNotice,
+ state: isSubmitting,
+ } = useBoolean(false);
/**
* Reset all the form fields.
*/
const resetForm = useCallback(() => {
setData(emptyForm);
- setIsSubmitting(false);
- }, [emptyForm]);
+ deactivateNotice();
+ }, [deactivateNotice, emptyForm]);
const nameLabel = intl.formatMessage({
defaultMessage: 'Name:',
@@ -160,10 +165,10 @@ export const CommentForm: FC<CommentFormProps> = ({
const sendForm = useCallback(
(e: FormEvent) => {
e.preventDefault();
- setIsSubmitting(true);
- saveComment(data, resetForm).then(() => setIsSubmitting(false));
+ activateNotice();
+ saveComment(data, resetForm).then(() => deactivateNotice());
},
- [data, resetForm, saveComment]
+ [activateNotice, data, deactivateNotice, resetForm, saveComment]
);
return (
diff --git a/src/components/organisms/forms/contact-form/contact-form.tsx b/src/components/organisms/forms/contact-form/contact-form.tsx
index 89fd331..ed23aad 100644
--- a/src/components/organisms/forms/contact-form/contact-form.tsx
+++ b/src/components/organisms/forms/contact-form/contact-form.tsx
@@ -9,6 +9,7 @@ import {
useMemo,
} from 'react';
import { useIntl } from 'react-intl';
+import { useBoolean } from '../../../../utils/hooks';
import { Button, Form, Input, Label, Spinner, TextArea } from '../../../atoms';
import { LabelledField } from '../../../molecules';
import styles from './contact-form.module.scss';
@@ -56,15 +57,19 @@ export const ContactForm: FC<ContactFormProps> = ({
};
}, []);
const [data, setData] = useState(emptyForm);
- const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
+ const {
+ activate: activateNotice,
+ deactivate: deactivateNotice,
+ state: isSubmitting,
+ } = useBoolean(false);
/**
* Reset all the form fields.
*/
const resetForm = useCallback(() => {
setData(emptyForm);
- setIsSubmitting(false);
- }, [emptyForm]);
+ deactivateNotice();
+ }, [deactivateNotice, emptyForm]);
const updateForm = useCallback(
(e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
@@ -135,10 +140,10 @@ export const ContactForm: FC<ContactFormProps> = ({
const submitHandler = useCallback(
async (e: FormEvent) => {
e.preventDefault();
- setIsSubmitting(true);
- await sendMail(data, resetForm).then(() => setIsSubmitting(false));
+ activateNotice();
+ await sendMail(data, resetForm).then(() => deactivateNotice());
},
- [data, resetForm, sendMail]
+ [activateNotice, data, deactivateNotice, resetForm, sendMail]
);
return (
diff --git a/src/components/organisms/layout/comment.tsx b/src/components/organisms/layout/comment.tsx
index db7cb3a..adbb2cc 100644
--- a/src/components/organisms/layout/comment.tsx
+++ b/src/components/organisms/layout/comment.tsx
@@ -1,11 +1,11 @@
/* eslint-disable max-statements */
import NextImage from 'next/image';
import Script from 'next/script';
-import { type FC, useCallback, useState } from 'react';
+import type { FC } from 'react';
import { useIntl } from 'react-intl';
import type { Comment as CommentSchema, WithContext } from 'schema-dts';
import type { SingleComment } from '../../../types';
-import { useSettings } from '../../../utils/hooks';
+import { useSettings, useToggle } from '../../../utils/hooks';
import { Button, Link, Time } from '../../atoms';
import {
Card,
@@ -49,12 +49,7 @@ export const UserComment: FC<UserCommentProps> = ({
}) => {
const intl = useIntl();
const { website } = useSettings();
- const [isReplying, setIsReplying] = useState<boolean>(false);
-
- const handleReply = useCallback(
- () => setIsReplying((prevState) => !prevState),
- []
- );
+ const [isReplying, toggleIsReplying] = useToggle(false);
if (!approved) {
return (
@@ -170,7 +165,7 @@ export const UserComment: FC<UserCommentProps> = ({
{canReply ? (
<CardFooter>
<CardActions>
- <Button kind="tertiary" onClick={handleReply}>
+ <Button kind="tertiary" onClick={toggleIsReplying}>
{buttonLabel}
</Button>
</CardActions>
diff --git a/src/components/organisms/toolbar/main-nav.stories.tsx b/src/components/organisms/toolbar/main-nav.stories.tsx
index d79addf..31e2b65 100644
--- a/src/components/organisms/toolbar/main-nav.stories.tsx
+++ b/src/components/organisms/toolbar/main-nav.stories.tsx
@@ -1,5 +1,5 @@
import type { ComponentMeta, ComponentStory } from '@storybook/react';
-import { useCallback, useState } from 'react';
+import { useToggle } from '../../../utils/hooks';
import { MainNavItem } from './main-nav';
/**
@@ -61,11 +61,7 @@ const Template: ComponentStory<typeof MainNavItem> = ({
setIsActive: _setIsActive,
...args
}) => {
- const [isOpen, setIsOpen] = useState<boolean>(isActive);
-
- const toggle = useCallback(() => {
- setIsOpen((prevState) => !prevState);
- }, []);
+ const [isOpen, toggle] = useToggle(isActive);
return <MainNavItem isActive={isOpen} setIsActive={toggle} {...args} />;
};
diff --git a/src/components/organisms/toolbar/search.stories.tsx b/src/components/organisms/toolbar/search.stories.tsx
index 2c8dd10..0f211bd 100644
--- a/src/components/organisms/toolbar/search.stories.tsx
+++ b/src/components/organisms/toolbar/search.stories.tsx
@@ -1,5 +1,5 @@
-import { ComponentMeta, ComponentStory } from '@storybook/react';
-import { useState } from 'react';
+import type { ComponentMeta, ComponentStory } from '@storybook/react';
+import { useToggle } from '../../../utils/hooks';
import { Search } from './search';
/**
@@ -66,17 +66,9 @@ const Template: ComponentStory<typeof Search> = ({
setIsActive: _setIsActive,
...args
}) => {
- const [isOpen, setIsOpen] = useState<boolean>(isActive);
+ const [isOpen, toggle] = useToggle(isActive);
- return (
- <Search
- isActive={isOpen}
- setIsActive={() => {
- setIsOpen(!isOpen);
- }}
- {...args}
- />
- );
+ return <Search isActive={isOpen} setIsActive={toggle} {...args} />;
};
/**
diff --git a/src/components/organisms/toolbar/settings.stories.tsx b/src/components/organisms/toolbar/settings.stories.tsx
index 793c521..c1fe37d 100644
--- a/src/components/organisms/toolbar/settings.stories.tsx
+++ b/src/components/organisms/toolbar/settings.stories.tsx
@@ -1,5 +1,5 @@
import type { ComponentMeta, ComponentStory } from '@storybook/react';
-import { useCallback, useState } from 'react';
+import { useToggle } from '../../../utils/hooks';
import { Settings } from './settings';
/**
@@ -66,11 +66,7 @@ const Template: ComponentStory<typeof Settings> = ({
setIsActive: _setIsActive,
...args
}) => {
- const [isOpen, setIsOpen] = useState<boolean>(isActive);
-
- const toggle = useCallback(() => {
- setIsOpen((prevState) => !prevState);
- }, []);
+ const [isOpen, toggle] = useToggle(isActive);
return <Settings isActive={isOpen} setIsActive={toggle} {...args} />;
};
diff --git a/src/components/organisms/toolbar/toolbar.tsx b/src/components/organisms/toolbar/toolbar.tsx
index c400285..c0be464 100644
--- a/src/components/organisms/toolbar/toolbar.tsx
+++ b/src/components/organisms/toolbar/toolbar.tsx
@@ -1,6 +1,10 @@
/* eslint-disable max-statements */
-import { type FC, useState, useCallback } from 'react';
-import { useOnClickOutside, useRouteChange } from '../../../utils/hooks';
+import type { FC } from 'react';
+import {
+ useBoolean,
+ useOnClickOutside,
+ useRouteChange,
+} from '../../../utils/hooks';
import { MainNavItem, type MainNavItemProps } from './main-nav';
import { Search, type SearchProps } from './search';
import { Settings } from './settings';
@@ -27,54 +31,53 @@ export const Toolbar: FC<ToolbarProps> = ({
nav,
searchPage,
}) => {
- const [isNavOpened, setIsNavOpened] = useState<boolean>(false);
- const [isSearchOpened, setIsSearchOpened] = useState<boolean>(false);
- const [isSettingsOpened, setIsSettingsOpened] = useState<boolean>(false);
+ const {
+ deactivate: deactivateMainNav,
+ state: isMainNavOpen,
+ toggle: toggleMainNav,
+ } = useBoolean(false);
+ const {
+ deactivate: deactivateSearch,
+ state: isSearchOpen,
+ toggle: toggleSearch,
+ } = useBoolean(false);
+ const {
+ deactivate: deactivateSettings,
+ state: isSettingsOpen,
+ toggle: toggleSettings,
+ } = useBoolean(false);
const mainNavRef = useOnClickOutside<HTMLDivElement>(
- () => isNavOpened && setIsNavOpened(false)
+ () => isMainNavOpen && deactivateMainNav()
);
const searchRef = useOnClickOutside<HTMLDivElement>(
- () => isSearchOpened && setIsSearchOpened(false)
+ () => isSearchOpen && deactivateSearch()
);
const settingsRef = useOnClickOutside<HTMLDivElement>(
- () => isSettingsOpened && setIsSettingsOpened(false)
+ () => isSettingsOpen && deactivateSettings()
);
- const toggleMainNav = useCallback(
- () => setIsNavOpened((prevState) => !prevState),
- []
- );
- const toggleSearch = useCallback(
- () => setIsSearchOpened((prevState) => !prevState),
- []
- );
- const toggleSettings = useCallback(
- () => setIsSettingsOpened((prevState) => !prevState),
- []
- );
-
- useRouteChange(() => setIsSearchOpened(false));
+ useRouteChange(deactivateSearch);
return (
<div className={`${styles.wrapper} ${className}`}>
<MainNavItem
className={styles.modal}
- isActive={isNavOpened}
+ isActive={isMainNavOpen}
items={nav}
ref={mainNavRef}
setIsActive={toggleMainNav}
/>
<Search
className={`${styles.modal} ${styles['modal--search']}`}
- isActive={isSearchOpened}
+ isActive={isSearchOpen}
ref={searchRef}
searchPage={searchPage}
setIsActive={toggleSearch}
/>
<Settings
className={`${styles.modal} ${styles['modal--settings']}`}
- isActive={isSettingsOpened}
+ isActive={isSettingsOpen}
ref={settingsRef}
setIsActive={toggleSettings}
/>