From 38eb167c66b9521f705670033a3057251d713038 Mon Sep 17 00:00:00 2001 From: Nikita Gorin <36075690+NikitaCG@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:14:13 +0300 Subject: [PATCH] refactor: private hooks directory (#1091) --- src/components/Checkbox/Checkbox.tsx | 2 +- src/components/DropdownMenu/constants.ts | 2 +- .../DropdownMenu/hooks/usePopupVisibility.ts | 2 +- src/components/Modal/Modal.tsx | 2 +- src/components/Popover/hooks/useOpen.ts | 2 +- src/components/Popup/Popup.tsx | 5 ++-- src/components/Radio/Radio.tsx | 2 +- src/components/RadioButton/RadioButton.tsx | 2 +- .../RadioButton/RadioButtonOption.tsx | 2 +- src/components/RadioGroup/RadioGroup.tsx | 2 +- src/components/Switch/Switch.tsx | 2 +- .../TableColumnSetup/TableColumnSetup.tsx | 2 +- src/components/Toaster/Toast/Toast.tsx | 2 +- src/components/Tooltip/Tooltip.tsx | 2 +- .../controls/TextInput/TextInput.tsx | 2 +- .../useConditionallyControlledState/index.ts | 1 - src/components/utils/useForceUpdate.ts | 8 ------ src/components/utils/useOnFocusOutside.ts | 2 +- src/components/utils/usePreviousValue.ts | 9 ------ src/hooks/private/index.ts | 11 ++++++++ src/hooks/private/useBoolean/README.md | 20 +++++++++++++ src/hooks/private/useBoolean/index.ts | 2 ++ .../private/useBoolean}/useBoolean.ts | 2 ++ src/hooks/private/useCheckbox/README.md | 28 +++++++++++++++++++ src/hooks/private/useCheckbox/index.ts | 2 ++ .../private/useCheckbox}/useCheckbox.ts | 14 +++++++--- src/hooks/private/useCloseOnTimeout/README.md | 17 +++++++++++ src/hooks/private/useCloseOnTimeout/index.ts | 2 ++ .../useCloseOnTimeout}/useCloseOnTimeout.ts | 14 +++++++--- .../useConditionallyControlledState/README.md | 16 +++++------ .../useConditionallyControlledState/index.ts | 2 ++ .../useConditionallyControlledState.ts | 2 +- src/hooks/private/useElementSize/README.md | 14 ++++++++++ src/hooks/private/useElementSize/index.ts | 2 ++ .../private/useElementSize}/useElementSize.ts | 4 +-- src/hooks/private/useHover/README.md | 13 +++++++++ src/hooks/private/useHover/index.ts | 2 ++ .../private/useHover}/useHover.ts | 8 ++---- src/hooks/private/usePopper/README.md | 21 ++++++++++++++ src/hooks/private/usePopper/index.ts | 10 +++++++ .../private/usePopper}/usePopper.ts | 19 ++++++++++++- src/hooks/private/useRadio/README.md | 27 ++++++++++++++++++ src/hooks/private/useRadio/index.ts | 2 ++ .../private/useRadio}/useRadio.ts | 14 +++++++--- src/hooks/private/useRadioGroup/README.md | 24 ++++++++++++++++ src/hooks/private/useRadioGroup/index.ts | 2 ++ .../private/useRadioGroup}/useRadioGroup.ts | 26 +++++++++++++++-- src/hooks/private/useRestoreFocus/README.md | 17 +++++++++++ src/hooks/private/useRestoreFocus/index.ts | 2 ++ .../useRestoreFocus}/useRestoreFocus.tsx | 13 +++++++-- .../private/useStateWithCallback/README.md | 17 +++++++++++ .../private/useStateWithCallback/index.ts | 1 + .../useStateWithCallback.ts | 2 +- src/hooks/private/useUpdateEffect/README.md | 3 ++ src/hooks/private/useUpdateEffect/index.ts | 1 + .../useUpdateEffect}/useUpdateEffect.ts | 0 56 files changed, 358 insertions(+), 71 deletions(-) delete mode 100644 src/components/utils/useConditionallyControlledState/index.ts delete mode 100644 src/components/utils/useForceUpdate.ts delete mode 100644 src/components/utils/usePreviousValue.ts create mode 100644 src/hooks/private/index.ts create mode 100644 src/hooks/private/useBoolean/README.md create mode 100644 src/hooks/private/useBoolean/index.ts rename src/{components/utils => hooks/private/useBoolean}/useBoolean.ts (87%) create mode 100644 src/hooks/private/useCheckbox/README.md create mode 100644 src/hooks/private/useCheckbox/index.ts rename src/{components/utils => hooks/private/useCheckbox}/useCheckbox.ts (83%) create mode 100644 src/hooks/private/useCloseOnTimeout/README.md create mode 100644 src/hooks/private/useCloseOnTimeout/index.ts rename src/{components/utils => hooks/private/useCloseOnTimeout}/useCloseOnTimeout.ts (61%) rename src/{components/utils => hooks/private}/useConditionallyControlledState/README.md (59%) create mode 100644 src/hooks/private/useConditionallyControlledState/index.ts rename src/{components/utils => hooks/private}/useConditionallyControlledState/useConditionallyControlledState.ts (89%) create mode 100644 src/hooks/private/useElementSize/README.md create mode 100644 src/hooks/private/useElementSize/index.ts rename src/{components/utils => hooks/private/useElementSize}/useElementSize.ts (94%) create mode 100644 src/hooks/private/useHover/README.md create mode 100644 src/hooks/private/useHover/index.ts rename src/{components/utils => hooks/private/useHover}/useHover.ts (70%) create mode 100644 src/hooks/private/usePopper/README.md create mode 100644 src/hooks/private/usePopper/index.ts rename src/{components/utils => hooks/private/usePopper}/usePopper.ts (79%) create mode 100644 src/hooks/private/useRadio/README.md create mode 100644 src/hooks/private/useRadio/index.ts rename src/{components/utils => hooks/private/useRadio}/useRadio.ts (80%) create mode 100644 src/hooks/private/useRadioGroup/README.md create mode 100644 src/hooks/private/useRadioGroup/index.ts rename src/{components/utils => hooks/private/useRadioGroup}/useRadioGroup.ts (66%) create mode 100644 src/hooks/private/useRestoreFocus/README.md create mode 100644 src/hooks/private/useRestoreFocus/index.ts rename src/{components/utils => hooks/private/useRestoreFocus}/useRestoreFocus.tsx (92%) create mode 100644 src/hooks/private/useStateWithCallback/README.md create mode 100644 src/hooks/private/useStateWithCallback/index.ts rename src/{components/utils => hooks/private/useStateWithCallback}/useStateWithCallback.ts (92%) create mode 100644 src/hooks/private/useUpdateEffect/README.md create mode 100644 src/hooks/private/useUpdateEffect/index.ts rename src/{components/utils => hooks/private/useUpdateEffect}/useUpdateEffect.ts (100%) diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index b17a51655c..ae41e5f7d9 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -1,10 +1,10 @@ import React from 'react'; +import {useCheckbox} from '../../hooks/private'; import {ControlLabel} from '../ControlLabel'; import type {ControlLabelSize} from '../ControlLabel'; import type {ControlProps, DOMProps, QAProps} from '../types'; import {block} from '../utils/cn'; -import {useCheckbox} from '../utils/useCheckbox'; import {CheckboxDashIcon} from './CheckboxDashIcon'; import {CheckboxTickIcon} from './CheckboxTickIcon'; diff --git a/src/components/DropdownMenu/constants.ts b/src/components/DropdownMenu/constants.ts index 2ebbe106c4..2a4c773236 100644 --- a/src/components/DropdownMenu/constants.ts +++ b/src/components/DropdownMenu/constants.ts @@ -1,4 +1,4 @@ -import type {PopperPlacement} from '../utils/usePopper'; +import type {PopperPlacement} from '../../hooks/private'; import type {DropdownMenuListItem} from './types'; diff --git a/src/components/DropdownMenu/hooks/usePopupVisibility.ts b/src/components/DropdownMenu/hooks/usePopupVisibility.ts index 54449bf16d..f133f42746 100644 --- a/src/components/DropdownMenu/hooks/usePopupVisibility.ts +++ b/src/components/DropdownMenu/hooks/usePopupVisibility.ts @@ -1,6 +1,6 @@ import React from 'react'; -import {useConditionallyControlledState} from '../../utils/useConditionallyControlledState'; +import {useConditionallyControlledState} from '../../../hooks/private'; export function usePopupVisibility( visible?: boolean, diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 1ee8f39665..358ccda1ba 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -3,6 +3,7 @@ import React from 'react'; import {CSSTransition} from 'react-transition-group'; import {useBodyScrollLock} from '../../hooks'; +import {useRestoreFocus} from '../../hooks/private'; import {Portal} from '../Portal'; import type {DOMProps, QAProps} from '../types'; import {FocusTrap} from '../utils/FocusTrap'; @@ -11,7 +12,6 @@ import type {LayerCloseReason} from '../utils/layer-manager'; import {useLayer} from '../utils/layer-manager'; import type {LayerExtendableProps} from '../utils/layer-manager/LayerManager'; import {getCSSTransitionClassNames} from '../utils/transition'; -import {useRestoreFocus} from '../utils/useRestoreFocus'; import './Modal.scss'; diff --git a/src/components/Popover/hooks/useOpen.ts b/src/components/Popover/hooks/useOpen.ts index aed58b7622..c4c79ff823 100644 --- a/src/components/Popover/hooks/useOpen.ts +++ b/src/components/Popover/hooks/useOpen.ts @@ -1,6 +1,6 @@ import React from 'react'; -import {useUpdateEffect} from '../../utils/useUpdateEffect'; +import {useUpdateEffect} from '../../../hooks/private'; import {PopoverBehavior, delayByBehavior} from '../config'; export type UseOpenProps = { diff --git a/src/components/Popup/Popup.tsx b/src/components/Popup/Popup.tsx index d715b79626..5dda652f56 100644 --- a/src/components/Popup/Popup.tsx +++ b/src/components/Popup/Popup.tsx @@ -3,6 +3,8 @@ import React from 'react'; import {CSSTransition} from 'react-transition-group'; import {useForkRef} from '../../hooks'; +import {usePopper, useRestoreFocus} from '../../hooks/private'; +import type {PopperAnchorRef, PopperPlacement, PopperProps} from '../../hooks/private'; import {Portal} from '../Portal'; import type {DOMProps, QAProps} from '../types'; import {FocusTrap, useParentFocusTrap} from '../utils/FocusTrap'; @@ -10,9 +12,6 @@ import {block} from '../utils/cn'; import {useLayer} from '../utils/layer-manager'; import type {LayerExtendableProps} from '../utils/layer-manager/LayerManager'; import {getCSSTransitionClassNames} from '../utils/transition'; -import {usePopper} from '../utils/usePopper'; -import type {PopperAnchorRef, PopperPlacement, PopperProps} from '../utils/usePopper'; -import {useRestoreFocus} from '../utils/useRestoreFocus'; import {PopupArrow} from './PopupArrow'; diff --git a/src/components/Radio/Radio.tsx b/src/components/Radio/Radio.tsx index 276bda4e43..aa1b1d423a 100644 --- a/src/components/Radio/Radio.tsx +++ b/src/components/Radio/Radio.tsx @@ -1,10 +1,10 @@ import React from 'react'; +import {useRadio} from '../../hooks/private'; import {ControlLabel} from '../ControlLabel'; import type {ControlLabelSize} from '../ControlLabel'; import type {ControlProps, DOMProps, QAProps} from '../types'; import {block} from '../utils/cn'; -import {useRadio} from '../utils/useRadio'; import './Radio.scss'; diff --git a/src/components/RadioButton/RadioButton.tsx b/src/components/RadioButton/RadioButton.tsx index 3731949556..207e3532c8 100644 --- a/src/components/RadioButton/RadioButton.tsx +++ b/src/components/RadioButton/RadioButton.tsx @@ -1,8 +1,8 @@ import React from 'react'; +import {useRadioGroup} from '../../hooks/private'; import type {ControlGroupOption, ControlGroupProps, DOMProps, QAProps} from '../types'; import {block} from '../utils/cn'; -import {useRadioGroup} from '../utils/useRadioGroup'; import {RadioButtonOption as Option} from './RadioButtonOption'; diff --git a/src/components/RadioButton/RadioButtonOption.tsx b/src/components/RadioButton/RadioButtonOption.tsx index 4f892acbfb..0a7e7b8915 100644 --- a/src/components/RadioButton/RadioButtonOption.tsx +++ b/src/components/RadioButton/RadioButtonOption.tsx @@ -1,9 +1,9 @@ import React from 'react'; +import {useRadio} from '../../hooks/private'; import type {ControlProps} from '../types'; import {block} from '../utils/cn'; import {isIcon} from '../utils/common'; -import {useRadio} from '../utils/useRadio'; const b = block('radio-button'); diff --git a/src/components/RadioGroup/RadioGroup.tsx b/src/components/RadioGroup/RadioGroup.tsx index 8e18c85c96..11b95ef1ec 100644 --- a/src/components/RadioGroup/RadioGroup.tsx +++ b/src/components/RadioGroup/RadioGroup.tsx @@ -1,10 +1,10 @@ import React from 'react'; +import {useRadioGroup} from '../../hooks/private'; import {Radio} from '../Radio'; import type {RadioProps, RadioSize} from '../Radio'; import type {ControlGroupOption, ControlGroupProps, DOMProps, QAProps} from '../types'; import {block} from '../utils/cn'; -import {useRadioGroup} from '../utils/useRadioGroup'; import './RadioGroup.scss'; diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index 3b190446c1..b298807d1d 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -1,10 +1,10 @@ import React from 'react'; +import {useCheckbox} from '../../hooks/private'; import {ControlLabel} from '../ControlLabel'; import type {ControlLabelSize} from '../ControlLabel'; import type {ControlProps, DOMProps, QAProps} from '../types'; import {block} from '../utils/cn'; -import {useCheckbox} from '../utils/useCheckbox'; import './Switch.scss'; diff --git a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx index 5efdae28e2..5b696b2715 100644 --- a/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx +++ b/src/components/Table/hoc/withTableSettings/TableColumnSetup/TableColumnSetup.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {Gear} from '@gravity-ui/icons'; -import type {PopperPlacement} from '../../../../../components/utils/usePopper'; +import type {PopperPlacement} from '../../../../../hooks/private'; import {useActionHandlers} from '../../../../../hooks/useActionHandlers'; import {Button} from '../../../../Button'; import {Icon} from '../../../../Icon'; diff --git a/src/components/Toaster/Toast/Toast.tsx b/src/components/Toaster/Toast/Toast.tsx index 106bf7b713..633d2bf06f 100644 --- a/src/components/Toaster/Toast/Toast.tsx +++ b/src/components/Toaster/Toast/Toast.tsx @@ -2,11 +2,11 @@ import React from 'react'; import {CircleCheck, CircleInfo, Thunderbolt, TriangleExclamation, Xmark} from '@gravity-ui/icons'; +import {useCloseOnTimeout} from '../../../hooks/private'; import {Button} from '../../Button'; import {Icon} from '../../Icon'; import type {IconProps} from '../../Icon'; import {block} from '../../utils/cn'; -import {useCloseOnTimeout} from '../../utils/useCloseOnTimeout'; import i18n from '../i18n'; import type {InternalToastProps, ToastAction, ToastType} from '../types'; diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index f65c68557f..fc29f45db9 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -2,11 +2,11 @@ import React from 'react'; import {KeyCode} from '../../constants'; import {useForkRef} from '../../hooks'; +import {useBoolean} from '../../hooks/private'; import {Popup} from '../Popup'; import type {PopupPlacement} from '../Popup'; import type {DOMProps} from '../types'; import {block} from '../utils/cn'; -import {useBoolean} from '../utils/useBoolean'; import './Tooltip.scss'; diff --git a/src/components/controls/TextInput/TextInput.tsx b/src/components/controls/TextInput/TextInput.tsx index ffbd1cf488..d9d1aa8ce9 100644 --- a/src/components/controls/TextInput/TextInput.tsx +++ b/src/components/controls/TextInput/TextInput.tsx @@ -3,10 +3,10 @@ import React from 'react'; import {TriangleExclamation} from '@gravity-ui/icons'; import {useForkRef, useUniqId} from '../../../hooks'; +import {useElementSize} from '../../../hooks/private'; import {Icon} from '../../Icon'; import {Popover} from '../../Popover'; import {block} from '../../utils/cn'; -import {useElementSize} from '../../utils/useElementSize'; import {ClearButton, mapTextInputSizeToButtonSize} from '../common'; import {OuterAdditionalContent} from '../common/OuterAdditionalContent/OuterAdditionalContent'; import type { diff --git a/src/components/utils/useConditionallyControlledState/index.ts b/src/components/utils/useConditionallyControlledState/index.ts deleted file mode 100644 index 8540faa5a5..0000000000 --- a/src/components/utils/useConditionallyControlledState/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {useConditionallyControlledState} from './useConditionallyControlledState'; diff --git a/src/components/utils/useForceUpdate.ts b/src/components/utils/useForceUpdate.ts deleted file mode 100644 index ab2d8b26f4..0000000000 --- a/src/components/utils/useForceUpdate.ts +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; - -export function useForceUpdate() { - const [, setState] = React.useState({}); - return React.useCallback(() => { - setState({}); - }, []); -} diff --git a/src/components/utils/useOnFocusOutside.ts b/src/components/utils/useOnFocusOutside.ts index 9c421c36dd..98c6412bf4 100644 --- a/src/components/utils/useOnFocusOutside.ts +++ b/src/components/utils/useOnFocusOutside.ts @@ -13,7 +13,7 @@ interface UseOnFocusOutsideProps { } /** - * @deprecated use useFocusWithin instead + * @deprecated use useFocusWithin instead, drop on next major * * Calls callback on focus element outside of some React sub-tree * diff --git a/src/components/utils/usePreviousValue.ts b/src/components/utils/usePreviousValue.ts deleted file mode 100644 index d31abd6800..0000000000 --- a/src/components/utils/usePreviousValue.ts +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; - -export function usePreviousValue(value: T) { - const ref = React.useRef(); - React.useEffect(() => { - ref.current = value; - }, [value]); - return ref.current; -} diff --git a/src/hooks/private/index.ts b/src/hooks/private/index.ts new file mode 100644 index 0000000000..c8e2f6843a --- /dev/null +++ b/src/hooks/private/index.ts @@ -0,0 +1,11 @@ +export * from './useBoolean'; +export * from './useCheckbox'; +export * from './useCloseOnTimeout'; +export * from './useConditionallyControlledState'; +export * from './useElementSize'; +export * from './useHover'; +export * from './usePopper'; +export * from './useRadio'; +export * from './useRadioGroup'; +export * from './useRestoreFocus'; +export * from './useUpdateEffect'; diff --git a/src/hooks/private/useBoolean/README.md b/src/hooks/private/useBoolean/README.md new file mode 100644 index 0000000000..a16f9775ac --- /dev/null +++ b/src/hooks/private/useBoolean/README.md @@ -0,0 +1,20 @@ +# useBoolean + +The `useBoolean` hook makes typical handlers for boolean state + +## Properties + +| Name | Description | Type | Default | +| :----------- | :------------ | :------------------------: | :-----: | +| initialState | Boolean state | `boolean - (() => boolean` | | + +## Result + +``` +[ + value, // boolean + setTrueCallback, // () => void + setFalseCallback, // () => void + toggleBooleanStateCallback, // () => void +] +``` diff --git a/src/hooks/private/useBoolean/index.ts b/src/hooks/private/useBoolean/index.ts new file mode 100644 index 0000000000..7ddef5a9be --- /dev/null +++ b/src/hooks/private/useBoolean/index.ts @@ -0,0 +1,2 @@ +export {useBoolean} from './useBoolean'; +export type {UseBooleanResult} from './useBoolean'; diff --git a/src/components/utils/useBoolean.ts b/src/hooks/private/useBoolean/useBoolean.ts similarity index 87% rename from src/components/utils/useBoolean.ts rename to src/hooks/private/useBoolean/useBoolean.ts index 99ec98c834..bab3cab5bc 100644 --- a/src/components/utils/useBoolean.ts +++ b/src/hooks/private/useBoolean/useBoolean.ts @@ -4,6 +4,8 @@ type SetTrue = () => void; type SetFalse = () => void; type Toggle = () => void; +export type UseBooleanResult = [boolean, SetTrue, SetFalse, Toggle]; + export function useBoolean( initialState: boolean | (() => boolean), ): [boolean, SetTrue, SetFalse, Toggle] { diff --git a/src/hooks/private/useCheckbox/README.md b/src/hooks/private/useCheckbox/README.md new file mode 100644 index 0000000000..cfb7f4671f --- /dev/null +++ b/src/hooks/private/useCheckbox/README.md @@ -0,0 +1,28 @@ +# useCheckbox + +The `useCheckbox` hook need to generate props for checkbox control + +## Properties + +| Name | Description | Type | Default | +| :------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----: | +| name | Name | `string` | | +| value | Value | `string` | | +| checked | Checked flag | `boolean` | | +| defaultChecked | Default checked value | `boolean` | | +| indeterminate | Indeterminate flag | `boolean` | | +| controlRef | Ref-link on control element | `React.Ref` | | +| controlProps | Another control props | `Omit` | | +| disabled | Disabled flag | `boolean` | | +| onUpdate | OnUpdate callback | `(checked: boolean) => void` | | +| onChange | OnChange callback | `(event: React.ChangeEvent) => void` | | +| onFocus | OnFocus callback | `(event: React.FocusEvent) => void` | | +| onBlur | OnBlur callback | `(event: React.FocusEvent) => void` | | +| id | ID attribute | `(event: React.FocusEvent) => void` | | + +## Result + +| Name | Description | Type | +| :--------- | :--------------------------------- | :-----------------------------------------------: | +| checked | Checked state | `boolean` | +| inputProps | Props to pass to the input element | `React.InputHTMLAttributes & React.RefAttributes` | diff --git a/src/hooks/private/useCheckbox/index.ts b/src/hooks/private/useCheckbox/index.ts new file mode 100644 index 0000000000..7f097d36e2 --- /dev/null +++ b/src/hooks/private/useCheckbox/index.ts @@ -0,0 +1,2 @@ +export {useCheckbox} from './useCheckbox'; +export type {UseCheckboxProps, UseCheckboxResult} from './useCheckbox'; diff --git a/src/components/utils/useCheckbox.ts b/src/hooks/private/useCheckbox/useCheckbox.ts similarity index 83% rename from src/components/utils/useCheckbox.ts rename to src/hooks/private/useCheckbox/useCheckbox.ts index 7fcac924df..78471c2af5 100644 --- a/src/components/utils/useCheckbox.ts +++ b/src/hooks/private/useCheckbox/useCheckbox.ts @@ -1,9 +1,15 @@ import React from 'react'; -import {useForkRef} from '../../hooks'; -import type {ControlProps} from '../types'; +import {useForkRef} from '../..'; +import type {ControlProps} from '../../../components/types'; +import {eventBroker} from '../../../components/utils/event-broker'; -import {eventBroker} from './event-broker'; +export type UseCheckboxProps = ControlProps; + +export type UseCheckboxResult = { + checked: boolean; + inputProps: React.InputHTMLAttributes & React.RefAttributes; +}; export function useCheckbox({ name, @@ -19,7 +25,7 @@ export function useCheckbox({ onFocus, onBlur, disabled, -}: ControlProps) { +}: UseCheckboxProps): UseCheckboxResult { const innerControlRef = React.useRef(null); const [checkedState, setCheckedState] = React.useState(defaultChecked ?? false); const isControlled = typeof checked === 'boolean'; diff --git a/src/hooks/private/useCloseOnTimeout/README.md b/src/hooks/private/useCloseOnTimeout/README.md new file mode 100644 index 0000000000..5a5b328436 --- /dev/null +++ b/src/hooks/private/useCloseOnTimeout/README.md @@ -0,0 +1,17 @@ +# useCloseOnTimeout + +The `useCloseOnTimeout` hook invokes callback after given amount of time unless mouse is on the element + +## Properties + +| Name | Description | Type | Default | +| :------ | :---------------- | :------------: | :-----: | +| onClose | On close callback | `VoidFunction` | | +| timeout | Timeout | `number` | | + +## Result + +| Name | Description | Type | +| :----------- | :------------------- | :-----------------------: | +| onMouseOver | OnMouseOver handler | `React.MouseEventHandler` | +| onMouseLeave | OnMouseLeave handler | `React.MouseEventHandler` | diff --git a/src/hooks/private/useCloseOnTimeout/index.ts b/src/hooks/private/useCloseOnTimeout/index.ts new file mode 100644 index 0000000000..61f1871ac2 --- /dev/null +++ b/src/hooks/private/useCloseOnTimeout/index.ts @@ -0,0 +1,2 @@ +export {useCloseOnTimeout} from './useCloseOnTimeout'; +export type {UseCloseOnTimeoutProps, UseCloseOnTimeoutResult} from './useCloseOnTimeout'; diff --git a/src/components/utils/useCloseOnTimeout.ts b/src/hooks/private/useCloseOnTimeout/useCloseOnTimeout.ts similarity index 61% rename from src/components/utils/useCloseOnTimeout.ts rename to src/hooks/private/useCloseOnTimeout/useCloseOnTimeout.ts index e7ebf97ad8..3b08252a0f 100644 --- a/src/components/utils/useCloseOnTimeout.ts +++ b/src/hooks/private/useCloseOnTimeout/useCloseOnTimeout.ts @@ -1,17 +1,23 @@ -import {useTimeout} from '../../hooks'; +import {useTimeout} from '../..'; +import {useHover} from '../useHover'; -import {useHover} from './useHover'; - -interface UseCloseOnTimeoutProps { +export interface UseCloseOnTimeoutProps { onClose: VoidFunction; timeout?: number; } +export interface UseCloseOnTimeoutResult { + onMouseOver: React.MouseEventHandler; + onMouseLeave: React.MouseEventHandler; +} + /** * Invokes callback after given amount of time unless mouse is on the element * * @param onClose * @param timeout + * + * @return mouse event handlers */ export function useCloseOnTimeout({onClose, timeout}: UseCloseOnTimeoutProps) { const [onMouseOver, onMouseLeave, isHovering] = useHover(); diff --git a/src/components/utils/useConditionallyControlledState/README.md b/src/hooks/private/useConditionallyControlledState/README.md similarity index 59% rename from src/components/utils/useConditionallyControlledState/README.md rename to src/hooks/private/useConditionallyControlledState/README.md index 0a46f58fac..ac37e6d030 100644 --- a/src/components/utils/useConditionallyControlledState/README.md +++ b/src/hooks/private/useConditionallyControlledState/README.md @@ -2,16 +2,16 @@ React hook used to create a conditionally controlled state, such state can either be controlled or uncontrolled -### Arguments +## Properties -| Name | Type | Default | Description | -| :----------- | :-------------------------------------------------------------------- | :---------------------------------------------------- | :--------------------------------------------------------------- | -| property | `StateType` | `-` | State value | -| setProperty | `Dispatch> or ((value: StateType) => void)` | `-` | State setter or callback, which should be called on state change | -| initialState | `StateType` or `() => StateType` | `-` | Initial state or state initializer | -| isControlled | `() => boolean` | `property !== undefined && setProperty !== undefined` | Returns if state should be controlled | +| Name | Description | Type | Default | +| :----------- | :--------------------------------------------------------------- | :-------------------------------------------------------------------: | :---------------------------------------------------: | +| property | State value | `StateType` | | +| setProperty | State setter or callback, which should be called on state change | `Dispatch> or ((value: StateType) => void)` | | +| initialState | Initial state or state initializer | `StateType` or `() => StateType` | | +| isControlled | Returns if state should be controlled | `() => boolean` | `property !== undefined && setProperty !== undefined` | -### Returns +## Result | Name | Type | Description | | :---- | :------------------------------------------------- | :--------------------- | diff --git a/src/hooks/private/useConditionallyControlledState/index.ts b/src/hooks/private/useConditionallyControlledState/index.ts new file mode 100644 index 0000000000..4883cd53c0 --- /dev/null +++ b/src/hooks/private/useConditionallyControlledState/index.ts @@ -0,0 +1,2 @@ +export {useConditionallyControlledState} from './useConditionallyControlledState'; +export type {UseConditionallyControlledStateResult} from './useConditionallyControlledState'; diff --git a/src/components/utils/useConditionallyControlledState/useConditionallyControlledState.ts b/src/hooks/private/useConditionallyControlledState/useConditionallyControlledState.ts similarity index 89% rename from src/components/utils/useConditionallyControlledState/useConditionallyControlledState.ts rename to src/hooks/private/useConditionallyControlledState/useConditionallyControlledState.ts index 2f8d2f791f..0bb61b59ef 100644 --- a/src/components/utils/useConditionallyControlledState/useConditionallyControlledState.ts +++ b/src/hooks/private/useConditionallyControlledState/useConditionallyControlledState.ts @@ -1,6 +1,6 @@ import {useStateWithCallback} from '../useStateWithCallback'; -type UseConditionallyControlledStateResult = [ +export type UseConditionallyControlledStateResult = [ T, React.Dispatch>, ]; diff --git a/src/hooks/private/useElementSize/README.md b/src/hooks/private/useElementSize/README.md new file mode 100644 index 0000000000..3b1f17c063 --- /dev/null +++ b/src/hooks/private/useElementSize/README.md @@ -0,0 +1,14 @@ +# useElementSize + +The `useElementSize` hook is needed to get target element size + +## Properties + +| Name | Description | Type | Default | +| :--- | :------------------------------------------------------------------------------------ | :---------------: | :-----: | +| ref | Target element | `React.RefObject` | | +| key | key for element, can be used, when it is needed to force reassign observer to element | `string` | | + +## Result + +Element sizes, `{width, height}` diff --git a/src/hooks/private/useElementSize/index.ts b/src/hooks/private/useElementSize/index.ts new file mode 100644 index 0000000000..cfaa78691c --- /dev/null +++ b/src/hooks/private/useElementSize/index.ts @@ -0,0 +1,2 @@ +export {useElementSize} from './useElementSize'; +export type {UseElementSizeResult} from './useElementSize'; diff --git a/src/components/utils/useElementSize.ts b/src/hooks/private/useElementSize/useElementSize.ts similarity index 94% rename from src/components/utils/useElementSize.ts rename to src/hooks/private/useElementSize/useElementSize.ts index e974934255..bd8dfeea87 100644 --- a/src/components/utils/useElementSize.ts +++ b/src/hooks/private/useElementSize/useElementSize.ts @@ -6,7 +6,7 @@ import throttle from 'lodash/throttle'; const RESIZE_THROTTLE = 16; const ROUND_PRESICION = 2; -interface ElementSize { +export interface UseElementSizeResult { width: number; height: number; } @@ -18,7 +18,7 @@ export function useElementSize( // https://github.com/WICG/resize-observer/issues/65 key?: string, ) { - const [size, setSize] = React.useState({ + const [size, setSize] = React.useState({ width: 0, height: 0, }); diff --git a/src/hooks/private/useHover/README.md b/src/hooks/private/useHover/README.md new file mode 100644 index 0000000000..7373acbdc1 --- /dev/null +++ b/src/hooks/private/useHover/README.md @@ -0,0 +1,13 @@ +# useHover + +The `useHover` hook makes hover state with callbacks for change it + +## Result + +``` +[ + onMouseEnter, // React.MouseEventHandler + onMouseLeave, // React.MouseEventHandler + isHovering, // boolean +] +``` diff --git a/src/hooks/private/useHover/index.ts b/src/hooks/private/useHover/index.ts new file mode 100644 index 0000000000..14a1b1b02e --- /dev/null +++ b/src/hooks/private/useHover/index.ts @@ -0,0 +1,2 @@ +export {useHover} from './useHover'; +export type {UseHoverResult} from './useHover'; diff --git a/src/components/utils/useHover.ts b/src/hooks/private/useHover/useHover.ts similarity index 70% rename from src/components/utils/useHover.ts rename to src/hooks/private/useHover/useHover.ts index 801d22cfc6..af6939c56c 100644 --- a/src/components/utils/useHover.ts +++ b/src/hooks/private/useHover/useHover.ts @@ -1,10 +1,8 @@ import React from 'react'; -export function useHover(): [ - React.MouseEventHandler, - React.MouseEventHandler, - boolean, -] { +export type UseHoverResult = [React.MouseEventHandler, React.MouseEventHandler, boolean]; + +export function useHover(): UseHoverResult { const [isHovering, setIsHovering] = React.useState(false); const onMouseEnter: React.MouseEventHandler = React.useCallback(() => { setIsHovering(true); diff --git a/src/hooks/private/usePopper/README.md b/src/hooks/private/usePopper/README.md new file mode 100644 index 0000000000..57ef0a4ff1 --- /dev/null +++ b/src/hooks/private/usePopper/README.md @@ -0,0 +1,21 @@ +# usePopper + +The `usePopper` hook wrap usePopper props and add few another helpful props + +## Properties + +| Name | Description | Type | Default | +| :---------- | :---------------------------------------------- | :-------------------------------------: | :-----: | +| anchorRef | Ref-link for anchor element | `React.RefObject` | | +| placement | Popper placement | `popper.Placement - popper.Placement[]` | | +| offset | Offset modifier | `[number, number]` | | +| modifiers | Popper modifiers | `Modifier[]` | | +| strategy | Popper position strategy | `popper.PositioningStrategy` | | +| altBoundary | Flag for check the reference's boundary context | `boolean` | | + +## Result + +- attributes +- styles +- setPopperRef +- setArrowRef diff --git a/src/hooks/private/usePopper/index.ts b/src/hooks/private/usePopper/index.ts new file mode 100644 index 0000000000..675724cf45 --- /dev/null +++ b/src/hooks/private/usePopper/index.ts @@ -0,0 +1,10 @@ +export {usePopper} from './usePopper'; +export type { + UsePopperProps, + PopperProps, + UsePopperResult, + PopperPlacement, + PopperOffset, + PopperModifiers, + PopperAnchorRef, +} from './usePopper'; diff --git a/src/components/utils/usePopper.ts b/src/hooks/private/usePopper/usePopper.ts similarity index 79% rename from src/components/utils/usePopper.ts rename to src/hooks/private/usePopper/usePopper.ts index e251a13774..2eed8f1393 100644 --- a/src/components/utils/usePopper.ts +++ b/src/hooks/private/usePopper/usePopper.ts @@ -18,6 +18,23 @@ export interface PopperProps { altBoundary?: boolean; } +export interface UsePopperProps extends PopperProps {} + +export interface UsePopperResult { + attributes: { + [key: string]: + | { + [key: string]: string; + } + | undefined; + }; + styles: { + [key: string]: React.CSSProperties; + }; + setPopperRef: React.Dispatch>; + setArrowRef: React.Dispatch>; +} + const DEFAULT_PLACEMENT: PopperPlacement = [ 'bottom-start', 'bottom', @@ -40,7 +57,7 @@ export function usePopper({ modifiers = [], strategy, altBoundary, -}: PopperProps) { +}: UsePopperProps): UsePopperResult { const [popperElement, setPopperElement] = React.useState(null); const [arrowElement, setArrowElement] = React.useState(null); const placements = Array.isArray(placement) ? placement : [placement]; diff --git a/src/hooks/private/useRadio/README.md b/src/hooks/private/useRadio/README.md new file mode 100644 index 0000000000..8b3aab1e04 --- /dev/null +++ b/src/hooks/private/useRadio/README.md @@ -0,0 +1,27 @@ +# useRadio + +The `useRadio` hook need to generate props for radio group control + +## Properties + +| Name | Description | Type | Default | +| :------------- | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----: | +| name | Name | `string` | | +| value | Value | `string` | | +| checked | Checked flag | `boolean` | | +| defaultChecked | Default checked value | `boolean` | | +| controlRef | Ref-link on control element | `React.Ref` | | +| controlProps | Another control props | `Omit` | | +| disabled | Disabled flag | `boolean` | | +| onUpdate | OnUpdate callback | `(checked: boolean) => void` | | +| onChange | OnChange callback | `(event: React.ChangeEvent) => void` | | +| onFocus | OnFocus callback | `(event: React.FocusEvent) => void` | | +| onBlur | OnBlur callback | `(event: React.FocusEvent) => void` | | +| id | ID attribute | `(event: React.FocusEvent) => void` | | + +## Result + +| Name | Description | Type | +| :--------- | :--------------------------------- | :-----------------------------------------------: | +| checked | Checked state | `boolean` | +| inputProps | Props to pass to the input element | `React.InputHTMLAttributes & React.RefAttributes` | diff --git a/src/hooks/private/useRadio/index.ts b/src/hooks/private/useRadio/index.ts new file mode 100644 index 0000000000..1e3fe515f4 --- /dev/null +++ b/src/hooks/private/useRadio/index.ts @@ -0,0 +1,2 @@ +export {useRadio} from './useRadio'; +export type {UseRadioProps, UseRadioResult} from './useRadio'; diff --git a/src/components/utils/useRadio.ts b/src/hooks/private/useRadio/useRadio.ts similarity index 80% rename from src/components/utils/useRadio.ts rename to src/hooks/private/useRadio/useRadio.ts index a6876946f7..93c87b76c0 100644 --- a/src/components/utils/useRadio.ts +++ b/src/hooks/private/useRadio/useRadio.ts @@ -1,9 +1,15 @@ import React from 'react'; -import {useForkRef, useUniqId} from '../../hooks'; -import type {ControlProps} from '../types'; +import {useForkRef, useUniqId} from '../..'; +import type {ControlProps} from '../../../components/types'; +import {eventBroker} from '../../../components/utils/event-broker'; -import {eventBroker} from './event-broker'; +export type UseRadioProps = ControlProps; + +export type UseRadioResult = { + checked: boolean; + inputProps: React.InputHTMLAttributes & React.RefAttributes; +}; export function useRadio({ name, @@ -18,7 +24,7 @@ export function useRadio({ onFocus, onBlur, id, -}: ControlProps) { +}: UseRadioProps): UseRadioResult { const controlId = useUniqId(); const innerControlRef = React.useRef(null); const [checkedState, setCheckedState] = React.useState(defaultChecked ?? false); diff --git a/src/hooks/private/useRadioGroup/README.md b/src/hooks/private/useRadioGroup/README.md new file mode 100644 index 0000000000..f3170c01ca --- /dev/null +++ b/src/hooks/private/useRadioGroup/README.md @@ -0,0 +1,24 @@ +# useRadioGroup + +The `useRadioGroup` hook need to generate props for radio group control + +## Properties + +| Name | Description | Type | Default | +| :----------- | :---------------- | :--------------------------------------------------------------------------------------------: | :-----: | +| name | Radio group name | `string` | | +| value | Value | `string` | | +| defaultValue | Default value | `string` | | +| options | Options | `{value: string; content?: React.ReactNode; children?: React.ReactNode; disabled?: boolean}[]` | [] | +| disabled | Disabled flag | `boolean` | | +| onUpdate | OnUpdate callback | `(value: string) => void` | | +| onChange | OnChange callback | `(event: React.ChangeEvent) => void` | | +| onFocus | OnFocus callback | `(event: React.FocusEvent) => void` | | +| onBlur | OnBlur callback | `(event: React.FocusEvent) => void` | | + +## Result + +| Name | Description | Type | +| :------------- | :--------------------------------- | :------------------------------------------------------------------------------------------: | +| containerProps | Props to pass to the input element | `{'aria-label'?: string;'aria-labelledby'?: string; role: string; 'aria-disabled': boolean;` | +| optionsProps | Options props | `OptionsProps` | diff --git a/src/hooks/private/useRadioGroup/index.ts b/src/hooks/private/useRadioGroup/index.ts new file mode 100644 index 0000000000..8d8f70753f --- /dev/null +++ b/src/hooks/private/useRadioGroup/index.ts @@ -0,0 +1,2 @@ +export {useRadioGroup} from './useRadioGroup'; +export type {UseRadioGroupProps, UseRadioGroupResult} from './useRadioGroup'; diff --git a/src/components/utils/useRadioGroup.ts b/src/hooks/private/useRadioGroup/useRadioGroup.ts similarity index 66% rename from src/components/utils/useRadioGroup.ts rename to src/hooks/private/useRadioGroup/useRadioGroup.ts index ce56525f13..09ea31eae8 100644 --- a/src/components/utils/useRadioGroup.ts +++ b/src/hooks/private/useRadioGroup/useRadioGroup.ts @@ -1,9 +1,29 @@ import React from 'react'; -import {useUniqId} from '../../hooks'; -import type {ControlGroupProps} from '../types'; +import {useUniqId} from '../..'; +import type {ControlGroupOption, ControlGroupProps} from '../../../components/types'; -export function useRadioGroup(props: ControlGroupProps) { +interface OptionsProps + extends Omit< + ControlGroupProps, + 'options' | 'defaultValue' | 'aria-label' | 'aria-labelledby' | 'onUpdate' | 'value' + > { + value: string; + checked: boolean; + content: ControlGroupOption['content']; +} + +export type UseRadioGroupProps = ControlGroupProps; + +export type UseRadioGroupResult = { + containerProps: Pick & { + role: string; + 'aria-disabled': ControlGroupProps['disabled']; + }; + optionsProps: OptionsProps[]; +}; + +export function useRadioGroup(props: UseRadioGroupProps): UseRadioGroupResult { const { name, value, diff --git a/src/hooks/private/useRestoreFocus/README.md b/src/hooks/private/useRestoreFocus/README.md new file mode 100644 index 0000000000..a640bcd6c2 --- /dev/null +++ b/src/hooks/private/useRestoreFocus/README.md @@ -0,0 +1,17 @@ +# useRestoreFocus + +The `useRestoreFocus` hook restore focus + +## Properties + +| Name | Description | Type | Default | +| :-------------- | :------------------------- | :---------------: | :-----: | +| enabled | Enabled flag | `boolean` | | +| restoreFocusRef | Ref-link for restore focus | `React.RefObject` | | +| focusTrapped | Focus trapped flag | `boolean` | | + +## Result + +| Name | Description | Type | +| :------ | :--------------- | :---------------------------------: | +| onFocus | OnFocus callback | `(event: React.FocusEvent) => void` | diff --git a/src/hooks/private/useRestoreFocus/index.ts b/src/hooks/private/useRestoreFocus/index.ts new file mode 100644 index 0000000000..a1c50d98e0 --- /dev/null +++ b/src/hooks/private/useRestoreFocus/index.ts @@ -0,0 +1,2 @@ +export {useRestoreFocus} from './useRestoreFocus'; +export type {UseRestoreFocusProps, UseRestoreFocusResult} from './useRestoreFocus'; diff --git a/src/components/utils/useRestoreFocus.tsx b/src/hooks/private/useRestoreFocus/useRestoreFocus.tsx similarity index 92% rename from src/components/utils/useRestoreFocus.tsx rename to src/hooks/private/useRestoreFocus/useRestoreFocus.tsx index 20e0fe3959..6bdd05502e 100644 --- a/src/components/utils/useRestoreFocus.tsx +++ b/src/hooks/private/useRestoreFocus/useRestoreFocus.tsx @@ -2,12 +2,21 @@ import React from 'react'; import {isFocusable, isTabbable} from 'tabbable'; -interface UseRestoreFocusProps { +export interface UseRestoreFocusProps { enabled: boolean; restoreFocusRef?: React.RefObject; focusTrapped?: boolean; } -export function useRestoreFocus({enabled, restoreFocusRef, focusTrapped}: UseRestoreFocusProps) { + +export interface UseRestoreFocusResult { + onFocus: (event: React.FocusEvent) => void; +} + +export function useRestoreFocus({ + enabled, + restoreFocusRef, + focusTrapped, +}: UseRestoreFocusProps): UseRestoreFocusResult { const ref = React.useRef(null); const initialActiveElementRef = React.useRef(null); diff --git a/src/hooks/private/useStateWithCallback/README.md b/src/hooks/private/useStateWithCallback/README.md new file mode 100644 index 0000000000..65d48802ac --- /dev/null +++ b/src/hooks/private/useStateWithCallback/README.md @@ -0,0 +1,17 @@ +# useStateWithCallback + +The `useStateWithCallback` hook a wrapper that adds a callback call when the wrapped state changes + +## Properties + +| Name | Description | Type | Default | +| :----------- | :------------------- | :--------------------: | :-----: | +| initialState | initial state | `any` | | +| callback | callback for wrapper | `(value: any) => void` | | + +## Result + +| Name | Description | Type | +| :-------------- | :----------------------------------------- | :-------------------------------------------: | +| state | Target state | `any` | +| setWithCallback | State setter with the transmitted callback | `(nextValue: T or ((value: T) => T)) => void` | diff --git a/src/hooks/private/useStateWithCallback/index.ts b/src/hooks/private/useStateWithCallback/index.ts new file mode 100644 index 0000000000..a73031d32b --- /dev/null +++ b/src/hooks/private/useStateWithCallback/index.ts @@ -0,0 +1 @@ +export {useStateWithCallback} from './useStateWithCallback'; diff --git a/src/components/utils/useStateWithCallback.ts b/src/hooks/private/useStateWithCallback/useStateWithCallback.ts similarity index 92% rename from src/components/utils/useStateWithCallback.ts rename to src/hooks/private/useStateWithCallback/useStateWithCallback.ts index 6d8227f4fa..47b8d66481 100644 --- a/src/components/utils/useStateWithCallback.ts +++ b/src/hooks/private/useStateWithCallback/useStateWithCallback.ts @@ -1,6 +1,6 @@ import React from 'react'; -import {isFunction} from './typeCheckers'; +import {isFunction} from '../../../components/utils/typeCheckers'; export function useStateWithCallback( initialValue: T, diff --git a/src/hooks/private/useUpdateEffect/README.md b/src/hooks/private/useUpdateEffect/README.md new file mode 100644 index 0000000000..8b6aa5fc9c --- /dev/null +++ b/src/hooks/private/useUpdateEffect/README.md @@ -0,0 +1,3 @@ +# useUpdateEffect + +The `useUpdateEffect` hook that ignores the first invocation (e.g. on mount). The signature is exactly the same as the useEffect hook. diff --git a/src/hooks/private/useUpdateEffect/index.ts b/src/hooks/private/useUpdateEffect/index.ts new file mode 100644 index 0000000000..76f940123a --- /dev/null +++ b/src/hooks/private/useUpdateEffect/index.ts @@ -0,0 +1 @@ +export {useUpdateEffect} from './useUpdateEffect'; diff --git a/src/components/utils/useUpdateEffect.ts b/src/hooks/private/useUpdateEffect/useUpdateEffect.ts similarity index 100% rename from src/components/utils/useUpdateEffect.ts rename to src/hooks/private/useUpdateEffect/useUpdateEffect.ts