From a04980b1116cff870e25d062f376d860240ab62e Mon Sep 17 00:00:00 2001 From: Aryan Shah Date: Mon, 25 Aug 2025 11:58:52 +0530 Subject: [PATCH 1/3] Added all the custom and reusable hooks in /hooks Signed-off-by: Aryan Shah --- src/custom/Helpers/Dimension/index.tsx | 3 - src/custom/ResourceDetailFormatters/index.ts | 2 +- .../TeamTable/TeamTableConfiguration.tsx | 2 +- src/custom/UniversalFilter.tsx | 1 - src/custom/UsersTable/UsersTable.tsx | 2 +- src/custom/Workspaces/DesignTable.tsx | 4 +- src/custom/Workspaces/EnvironmentTable.tsx | 4 +- src/custom/Workspaces/WorkspaceTeamsTable.tsx | 2 +- src/custom/Workspaces/WorkspaceViewsTable.tsx | 6 +- src/custom/Workspaces/index.ts | 14 +- src/custom/index.tsx | 2 +- src/hooks/data/index.ts | 1 + .../data}/useResourceCleanData.ts | 9 +- src/hooks/index.ts | 4 + src/hooks/readme.md | 148 ++++++++++++++++++ src/hooks/ui/index.ts | 1 + .../ui/useWindowDImensions.ts} | 4 +- src/hooks/utils/index.ts | 5 + src/hooks/utils/useDebounce.ts | 44 ++++++ src/hooks/utils/useDebouncedCallback.ts | 49 ++++++ src/hooks/utils/useStateCB.ts | 51 ++++++ src/hooks/utils/useTimeOut.ts | 36 +++++ src/hooks/utils/useToggle.ts | 24 +++ src/hooks/workspaces/index.ts | 4 + .../workspaces}/useDesignAssignment.tsx | 6 +- .../workspaces}/useEnvironmentAssignment.tsx | 4 +- .../workspaces}/useTeamAssignment.tsx | 4 +- .../workspaces}/useViewsAssignment.tsx | 6 +- tsconfig.json | 6 + 29 files changed, 413 insertions(+), 35 deletions(-) delete mode 100644 src/custom/Helpers/Dimension/index.tsx create mode 100644 src/hooks/data/index.ts rename src/{custom/ResourceDetailFormatters => hooks/data}/useResourceCleanData.ts (97%) create mode 100644 src/hooks/readme.md create mode 100644 src/hooks/ui/index.ts rename src/{custom/Helpers/Dimension/windowSize.tsx => hooks/ui/useWindowDImensions.ts} (93%) create mode 100644 src/hooks/utils/index.ts create mode 100644 src/hooks/utils/useDebounce.ts create mode 100644 src/hooks/utils/useDebouncedCallback.ts create mode 100644 src/hooks/utils/useStateCB.ts create mode 100644 src/hooks/utils/useTimeOut.ts create mode 100644 src/hooks/utils/useToggle.ts create mode 100644 src/hooks/workspaces/index.ts rename src/{custom/Workspaces/hooks => hooks/workspaces}/useDesignAssignment.tsx (96%) rename src/{custom/Workspaces/hooks => hooks/workspaces}/useEnvironmentAssignment.tsx (97%) rename src/{custom/Workspaces/hooks => hooks/workspaces}/useTeamAssignment.tsx (96%) rename src/{custom/Workspaces/hooks => hooks/workspaces}/useViewsAssignment.tsx (95%) diff --git a/src/custom/Helpers/Dimension/index.tsx b/src/custom/Helpers/Dimension/index.tsx deleted file mode 100644 index aefbaf894..000000000 --- a/src/custom/Helpers/Dimension/index.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { useWindowDimensions } from './windowSize'; - -export { useWindowDimensions }; diff --git a/src/custom/ResourceDetailFormatters/index.ts b/src/custom/ResourceDetailFormatters/index.ts index bd66d708b..4bf22602a 100644 --- a/src/custom/ResourceDetailFormatters/index.ts +++ b/src/custom/ResourceDetailFormatters/index.ts @@ -1,3 +1,4 @@ +import { useResourceCleanData } from 'hooks/data'; import { KeyValueInRow, NumberStateFormatter } from './Component'; import { OperatorDataFormatter } from './Details'; import { @@ -14,7 +15,6 @@ import { TableDataFormatter, TextWithLinkFormatter } from './Formatter'; -import { useResourceCleanData } from './useResourceCleanData'; import { convertToReadableUnit, extractPodVolumnTables, splitCamelCaseString } from './utils'; export { diff --git a/src/custom/TeamTable/TeamTableConfiguration.tsx b/src/custom/TeamTable/TeamTableConfiguration.tsx index 46ce3404a..93c45ecd6 100644 --- a/src/custom/TeamTable/TeamTableConfiguration.tsx +++ b/src/custom/TeamTable/TeamTableConfiguration.tsx @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { useWindowDimensions } from 'hooks/ui'; import { MUIDataTableColumn, MUIDataTableMeta } from 'mui-datatables'; import { useState } from 'react'; import { DeleteIcon, EditIcon } from '../../icons'; @@ -7,7 +8,6 @@ import { CHARCOAL, useTheme } from '../../theme'; import { CustomTooltip } from '../CustomTooltip'; import { FormatId } from '../FormatId'; import { ConditionalTooltip } from '../Helpers/CondtionalTooltip'; -import { useWindowDimensions } from '../Helpers/Dimension'; import { ColView, updateVisibleColumns } from '../Helpers/ResponsiveColumns/responsive-coulmns.tsx'; import { IconWrapper } from '../ResponsiveDataTable'; import { TooltipIcon } from '../TooltipIconButton'; diff --git a/src/custom/UniversalFilter.tsx b/src/custom/UniversalFilter.tsx index 1ba0594da..c8547ff92 100644 --- a/src/custom/UniversalFilter.tsx +++ b/src/custom/UniversalFilter.tsx @@ -9,7 +9,6 @@ import { Paper } from '../base/Paper'; import { Select } from '../base/Select'; import { FilterIcon } from '../icons'; import { useTheme } from '../theme'; -import { darkModalGradient, lightModalGradient } from '../theme/colors/colors'; import PopperListener from './PopperListener'; import { TooltipIcon } from './TooltipIconButton'; diff --git a/src/custom/UsersTable/UsersTable.tsx b/src/custom/UsersTable/UsersTable.tsx index 81eff4bcf..b2b0fd1b0 100644 --- a/src/custom/UsersTable/UsersTable.tsx +++ b/src/custom/UsersTable/UsersTable.tsx @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Theme } from '@mui/material'; +import { useWindowDimensions } from 'hooks/ui'; import { MUIDataTableColumn, MUIDataTableMeta } from 'mui-datatables'; import { useRef, useState } from 'react'; import { Box, Tooltip } from '../../base'; @@ -8,7 +9,6 @@ import Github from '../../icons/Github/GithubIcon'; import Google from '../../icons/Google/GoogleIcon'; import LogoutIcon from '../../icons/Logout/LogOutIcon'; import { CHARCOAL, SistentThemeProviderWithoutBaseLine } from '../../theme'; -import { useWindowDimensions } from '../Helpers/Dimension'; import { ColView, updateVisibleColumns diff --git a/src/custom/Workspaces/DesignTable.tsx b/src/custom/Workspaces/DesignTable.tsx index 395de1718..b4b5b95f3 100644 --- a/src/custom/Workspaces/DesignTable.tsx +++ b/src/custom/Workspaces/DesignTable.tsx @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { useWindowDimensions } from 'hooks/ui'; +import { useDesignAssignment } from 'hooks/workspaces'; import _ from 'lodash'; import React, { useEffect, useRef, useState } from 'react'; import { Box } from '../../base'; @@ -12,13 +14,11 @@ import { } from '../CatalogDesignTable'; import { Pattern } from '../CustomCatalog/CustomCard'; import { CustomColumnVisibilityControl } from '../CustomColumnVisibilityControl'; -import { useWindowDimensions } from '../Helpers/Dimension'; import { updateVisibleColumns } from '../Helpers/ResponsiveColumns/responsive-coulmns.tsx/responsive-column'; import PromptComponent from '../Prompt'; import SearchBar from '../SearchBar'; import { VIEW_VISIBILITY } from '../VisibilityChipMenu/VisibilityChipMenu'; import AssignmentModal from './AssignmentModal'; -import useDesignAssignment from './hooks/useDesignAssignment'; import { L5EditIcon, TableHeader } from './styles'; export interface DesignTableProps { workspaceId: string; diff --git a/src/custom/Workspaces/EnvironmentTable.tsx b/src/custom/Workspaces/EnvironmentTable.tsx index 83dd67011..cfe89b7a2 100644 --- a/src/custom/Workspaces/EnvironmentTable.tsx +++ b/src/custom/Workspaces/EnvironmentTable.tsx @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import { useWindowDimensions } from 'hooks/ui'; +import { useEnvironmentAssignment } from 'hooks/workspaces'; import { MUIDataTableColumn, MUIDataTableMeta } from 'mui-datatables'; import React, { useState } from 'react'; import { Accordion, AccordionDetails, AccordionSummary, Typography } from '../../base'; @@ -8,7 +10,6 @@ import { useTheme } from '../../theme'; import { CustomColumnVisibilityControl } from '../CustomColumnVisibilityControl'; import { CustomTooltip } from '../CustomTooltip'; import { ConditionalTooltip } from '../Helpers/CondtionalTooltip'; -import { useWindowDimensions } from '../Helpers/Dimension'; import { ColView, updateVisibleColumns @@ -17,7 +18,6 @@ import ResponsiveDataTable, { IconWrapper } from '../ResponsiveDataTable'; import SearchBar from '../SearchBar'; import { TooltipIcon } from '../TooltipIconButton'; import AssignmentModal from './AssignmentModal'; -import useEnvironmentAssignment from './hooks/useEnvironmentAssignment'; import { CellStyle, CustomBodyRenderStyle, diff --git a/src/custom/Workspaces/WorkspaceTeamsTable.tsx b/src/custom/Workspaces/WorkspaceTeamsTable.tsx index 1c4043f9b..294a8af82 100644 --- a/src/custom/Workspaces/WorkspaceTeamsTable.tsx +++ b/src/custom/Workspaces/WorkspaceTeamsTable.tsx @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import AddIcon from '@mui/icons-material/Add'; +import { useTeamAssignment } from 'hooks/workspaces'; import { useState } from 'react'; import { Button } from '../../base'; import { TeamsIcon } from '../../icons'; @@ -9,7 +10,6 @@ import SearchBar from '../SearchBar'; import { TeamTableConfiguration } from '../TeamTable'; import TeamTable from '../TeamTable/TeamTable'; import AssignmentModal from './AssignmentModal'; -import useTeamAssignment from './hooks/useTeamAssignment'; import { TableHeader, TableRightActionHeader } from './styles'; export interface TeamsTableProps { diff --git a/src/custom/Workspaces/WorkspaceViewsTable.tsx b/src/custom/Workspaces/WorkspaceViewsTable.tsx index ff4a29709..a66b135a7 100644 --- a/src/custom/Workspaces/WorkspaceViewsTable.tsx +++ b/src/custom/Workspaces/WorkspaceViewsTable.tsx @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Lock, Public } from '@mui/icons-material'; import RemoveCircleIcon from '@mui/icons-material/RemoveCircle'; +import { useWindowDimensions } from 'hooks/ui'; +import { useViewsAssignment } from 'hooks/workspaces'; import { MUIDataTableColumn, MUIDataTableMeta } from 'mui-datatables'; import React, { useState } from 'react'; import { Box } from '../../base'; @@ -12,7 +14,6 @@ import { CustomColumnVisibilityControl } from '../CustomColumnVisibilityControl' import { CustomTooltip } from '../CustomTooltip'; import { ErrorBoundary } from '../ErrorBoundary'; import { ConditionalTooltip } from '../Helpers/CondtionalTooltip'; -import { useWindowDimensions } from '../Helpers/Dimension'; import { ColView, updateVisibleColumns @@ -23,7 +24,6 @@ import { TooltipIcon } from '../TooltipIconButton'; import { UserTableAvatarInfo } from '../UsersTable'; import VisibilityChipMenu, { VIEW_VISIBILITY } from '../VisibilityChipMenu/VisibilityChipMenu'; import AssignmentModal from './AssignmentModal'; -import useViewAssignment from './hooks/useViewsAssignment'; import { CellStyle, CustomBodyRenderStyle, L5EditIcon, TableHeader } from './styles'; interface ViewsTableProps { @@ -270,7 +270,7 @@ const WorkspaceViewsTable: React.FC = ({ } ]; - const viewAssignment = useViewAssignment({ + const viewAssignment = useViewsAssignment({ workspaceId, useGetViewsOfWorkspaceQuery, useUnassignViewFromWorkspaceMutation, diff --git a/src/custom/Workspaces/index.ts b/src/custom/Workspaces/index.ts index 52c295604..565c3080e 100644 --- a/src/custom/Workspaces/index.ts +++ b/src/custom/Workspaces/index.ts @@ -1,17 +1,19 @@ +import { + useDesignAssignment, + useEnvironmentAssignment, + useTeamAssignment, + useViewsAssignment +} from 'hooks/workspaces'; import AssignmentModal from './AssignmentModal'; import DesignTable from './DesignTable'; import EnvironmentTable from './EnvironmentTable'; +import { L5DeleteIcon, L5EditIcon } from './styles'; import WorkspaceCard from './WorkspaceCard'; import WorkspaceContentMoveModal from './WorkspaceContentMoveModal'; import WorkspaceEnvironmentSelection from './WorkspaceEnvironmentSelection'; import WorkspaceRecentActivityModal from './WorkspaceRecentActivityModal'; import WorkspaceTeamsTable from './WorkspaceTeamsTable'; import WorkspaceViewsTable from './WorkspaceViewsTable'; -import useDesignAssignment from './hooks/useDesignAssignment'; -import useEnvironmentAssignment from './hooks/useEnvironmentAssignment'; -import useTeamAssignment from './hooks/useTeamAssignment'; -import useViewAssignment from './hooks/useViewsAssignment'; -import { L5DeleteIcon, L5EditIcon } from './styles'; export { AssignmentModal, @@ -22,7 +24,7 @@ export { useDesignAssignment, useEnvironmentAssignment, useTeamAssignment, - useViewAssignment, + useViewsAssignment, WorkspaceCard, WorkspaceContentMoveModal, WorkspaceEnvironmentSelection, diff --git a/src/custom/index.tsx b/src/custom/index.tsx index 8cea123bb..fe7a4eb2b 100644 --- a/src/custom/index.tsx +++ b/src/custom/index.tsx @@ -1,3 +1,4 @@ +import { useWindowDimensions } from '../hooks/ui'; import { ActionButton } from './ActionButton'; import { BBChart } from './BBChart'; import { BookmarkNotification } from './BookmarkNotification'; @@ -31,7 +32,6 @@ import { import { FeedbackButton } from './Feedback'; import { FlipCard, FlipCardProps } from './FlipCard'; import { FormatId } from './FormatId'; -import { useWindowDimensions } from './Helpers/Dimension'; import { useNotificationHandler } from './Helpers/Notification'; import { ColView, updateVisibleColumns } from './Helpers/ResponsiveColumns/responsive-coulmns.tsx'; import { LearningCard } from './LearningCard'; diff --git a/src/hooks/data/index.ts b/src/hooks/data/index.ts new file mode 100644 index 000000000..cc4ac66aa --- /dev/null +++ b/src/hooks/data/index.ts @@ -0,0 +1 @@ +export { default as useResourceCleanData } from './useResourceCleanData'; diff --git a/src/custom/ResourceDetailFormatters/useResourceCleanData.ts b/src/hooks/data/useResourceCleanData.ts similarity index 97% rename from src/custom/ResourceDetailFormatters/useResourceCleanData.ts rename to src/hooks/data/useResourceCleanData.ts index 3e1778f58..c129e1f95 100644 --- a/src/custom/ResourceDetailFormatters/useResourceCleanData.ts +++ b/src/hooks/data/useResourceCleanData.ts @@ -1,9 +1,12 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import _ from 'lodash'; import moment from 'moment'; -import { GetResourceCleanDataProps, NumberState } from './types'; +import { + GetResourceCleanDataProps, + NumberState +} from '../../custom/ResourceDetailFormatters/types'; -export const useResourceCleanData = () => { +const useResourceCleanData = () => { const structureNumberStates = (parsedStatus: any, parsedSpec: any): NumberState[] => { const numberStates: NumberState[] = []; @@ -182,3 +185,5 @@ export const useResourceCleanData = () => { return { getResourceCleanData, structureNumberStates, getAge, getStatus, joinwithEqual }; }; + +export default useResourceCleanData; diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 855448bb6..5c33bc1b5 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1 +1,5 @@ +export * from './data'; +export * from './ui'; export * from './useRoomActivity'; +export * from './utils'; +export * from './workspaces'; diff --git a/src/hooks/readme.md b/src/hooks/readme.md new file mode 100644 index 000000000..b832bd772 --- /dev/null +++ b/src/hooks/readme.md @@ -0,0 +1,148 @@ +# Sistent Hooks + +This directory contains all reusable custom React hooks for the Sistent design system. The hooks are organized into logical categories for better maintainability and discoverability. + +## Directory Structure + +``` +src/hooks/ +├── data/ # Data management and API-related hooks +├── ui/ # UI interaction and state management hooks +├── utils/ # General utility hooks +├── workspaces/ # Workspace-specific hooks +└── index.ts # Main exports +``` + +## Categories + +### Data Hooks (`./data/`) + +Hooks for managing data, API interactions, and data processing. + +- `useResourceCleanData` - Resource data formatting and processing for Kubernetes resources + +### UI Hooks (`./ui/`) + +Hooks for managing UI state, interactions, and visual components. + +- `useWindowDimensions` - Window dimension tracking with debounced resize handling + +### Utility Hooks (`./utils/`) + +General-purpose utility hooks for common patterns. + +- `useDebounce` - Debounce values with configurable delay +- `useDebouncedCallback` - Debounce function calls with configurable delay +- `usePreventUserFromLeavingPage` - Prevent users from leaving pages with unsaved changes +- `useStateCB` - State management with callback support for change tracking +- `useToggle` - Simple boolean state toggle management +- `useTimeout` - Manage timeouts with automatic cleanup + +### Workspace Hooks (`./workspaces/`) + +Hooks specific to workspace functionality and management. + +- `useDesignAssignment` - Design assignment management for workspaces +- `useEnvironmentAssignment` - Environment assignment management for workspaces +- `useTeamAssignment` - Team assignment management for workspaces +- `useViewsAssignment` - Views assignment management for workspaces + +### Import Options + +With the configured path mapping, you have multiple import options: + +#### Option 1: Category-based imports (Recommended) + +```typescript +// Import from specific categories +import { useDebounce, useToggle, useTimeout } from 'hooks/utils'; +import { useWindowDimensions } from 'hooks/ui'; +import { useDesignAssignment } from 'hooks/workspaces'; +``` + +#### Option 2: Absolute path imports + +```typescript +// Using absolute paths with @ alias +import { useDebounce } from '@/src/hooks/utils'; +import { useWindowDimensions } from '@/src/hooks/ui'; +``` + +#### Option 3: Traditional relative imports + +```typescript +// Relative imports (still work) +import { useDebounce } from 'hooks/utils'; +``` + +## Adding New Hooks + +When adding new hooks, please follow these guidelines: + +1. **Categorization**: Place hooks in the appropriate directory based on their primary function +2. **Naming**: Use descriptive names starting with "use" following React conventions +3. **TypeScript**: Provide full type definitions for parameters and return values +4. **Documentation**: Include JSDoc comments describing the hook's purpose and usage +5. **Exports**: Add the hook to the appropriate category's index.ts file +6. **Default Export**: Use default exports with the pattern: + + ```typescript + function useMyHook() { + // implementation + } + + export default useMyHook; + ``` + +## Examples + +### Basic Usage + +```typescript +import { useDebounce, useToggle } from 'hooks/utils'; + +function MyComponent() { + const [searchTerm, setSearchTerm] = useState(''); + const [isOpen, toggleOpen] = useToggle(false); + const debouncedSearchTerm = useDebounce(searchTerm, 300); + + // Use debouncedSearchTerm for API calls + // Use toggleOpen for modal state +} +``` + +## Best Practices + +1. **Keep hooks focused**: Each hook should have a single, well-defined responsibility +2. **Use TypeScript**: Always provide type definitions for better developer experience +3. **Handle cleanup**: Ensure proper cleanup of effects, timers, and event listeners +4. **Avoid `any` types**: Use `unknown` or specific types instead of `any` +5. **Default exports**: Follow the established pattern of default exports +6. **Consistent imports**: Use the path mapping for cleaner, more maintainable imports +7. **Test thoroughly**: Write comprehensive tests for all hooks +8. **Document edge cases**: Include documentation for error handling and edge cases + +## File Structure Example + +```typescript +// src/hooks/utils/useMyHook.ts +import { useState, useEffect } from 'react'; + +/** + * Custom hook for managing something + * @param initialValue - The initial value + * @returns The managed state and setter + */ +function useMyHook(initialValue: T): [T, (value: T) => void] { + // Implementation +} + +export default useMyHook; +``` + +```typescript +// src/hooks/utils/index.ts +export { default as useDebounce } from './useDebounce'; +export { default as useMyHook } from './useMyHook'; +// ... other exports +``` diff --git a/src/hooks/ui/index.ts b/src/hooks/ui/index.ts new file mode 100644 index 000000000..e7413199d --- /dev/null +++ b/src/hooks/ui/index.ts @@ -0,0 +1 @@ +export { default as useWindowDimensions } from './useWindowDImensions'; diff --git a/src/custom/Helpers/Dimension/windowSize.tsx b/src/hooks/ui/useWindowDImensions.ts similarity index 93% rename from src/custom/Helpers/Dimension/windowSize.tsx rename to src/hooks/ui/useWindowDImensions.ts index e396e9896..6a5f47ee1 100644 --- a/src/custom/Helpers/Dimension/windowSize.tsx +++ b/src/hooks/ui/useWindowDImensions.ts @@ -18,7 +18,7 @@ function getWindowDimensions(): WindowDimensions { * * @returns {WindowDimensions} { width, height } */ -export function useWindowDimensions(): WindowDimensions { +function useWindowDimensions(): WindowDimensions { const [windowDimensions, setWindowDimensions] = React.useState(getWindowDimensions()); React.useEffect(() => { @@ -47,6 +47,8 @@ export function useWindowDimensions(): WindowDimensions { return windowDimensions; } +export default useWindowDimensions; + /** * Represents the width and height of the window. */ diff --git a/src/hooks/utils/index.ts b/src/hooks/utils/index.ts new file mode 100644 index 000000000..b3fe1b575 --- /dev/null +++ b/src/hooks/utils/index.ts @@ -0,0 +1,5 @@ +export { default as useDebounce } from './useDebounce'; +export { default as useDebouncedCallback } from './useDebouncedCallback'; +export { default as useStateCB } from './useStateCB'; +export { default as useTimeOut } from './useTimeOut'; +export { default as useToggle } from './useToggle'; diff --git a/src/hooks/utils/useDebounce.ts b/src/hooks/utils/useDebounce.ts new file mode 100644 index 000000000..482a7274c --- /dev/null +++ b/src/hooks/utils/useDebounce.ts @@ -0,0 +1,44 @@ +import { useEffect, useState } from 'react'; + +/** + * Custom hook for debouncing values + * + * This hook delays updating the returned value until after the specified delay + * has passed since the last time the input value changed. + * + * @template T - The type of the value being debounced + * @param value - The value to debounce + * @param delay - The debounce delay in milliseconds (default: 300) + * @returns The debounced value that updates after the delay period + * + * @example + * ```typescript + * const [searchTerm, setSearchTerm] = useState(''); + * const debouncedSearchTerm = useDebounce(searchTerm, 500); + * + * useEffect(() => { + * if (debouncedSearchTerm) { + * // Perform search API call + * searchAPI(debouncedSearchTerm); + * } + * }, [debouncedSearchTerm]); + * ``` + */ +export function useDebounce(value: T, delay: number = 300): T { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedValue(value); + }, delay); + + // Cleanup function to clear timeout if value changes before delay + return () => { + clearTimeout(handler); + }; + }, [value, delay]); + + return debouncedValue; +} + +export default useDebounce; diff --git a/src/hooks/utils/useDebouncedCallback.ts b/src/hooks/utils/useDebouncedCallback.ts new file mode 100644 index 000000000..aa149b915 --- /dev/null +++ b/src/hooks/utils/useDebouncedCallback.ts @@ -0,0 +1,49 @@ +import { useCallback, useEffect, useRef } from 'react'; + +/** + * Custom hook for debouncing function calls + * + * This hook returns a debounced version of the provided callback function. + * The debounced function will only execute after the specified delay has + * passed since the last time it was called. + * + * @template T - The type of the callback function parameters + * @param callback - The function to debounce + * @param delay - The debounce delay in milliseconds (default: 300) + * @returns A debounced version of the callback function + * + */ +export function useDebouncedCallback unknown>( + callback: T, + delay: number = 300 +): (...args: Parameters) => void { + const timeoutRef = useRef(null); + + const debouncedFunction = useCallback( + (...args: Parameters) => { + // Clear any existing timeout + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + // Set a new timeout to call the callback + timeoutRef.current = setTimeout(() => { + callback(...args); + }, delay); + }, + [callback, delay] + ); + + // Clean up timeout on unmount + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, []); + + return debouncedFunction; +} + +export default useDebouncedCallback; diff --git a/src/hooks/utils/useStateCB.ts b/src/hooks/utils/useStateCB.ts new file mode 100644 index 000000000..483d5e9e0 --- /dev/null +++ b/src/hooks/utils/useStateCB.ts @@ -0,0 +1,51 @@ +import React from 'react'; + +type StateCallback = (state: T) => void; +type SetStateCallback = (state: T, callback?: StateCallback) => void; +type GetStateRefValue = () => T; + +/** + * Custom hook that provides state management with callback support + * + * This hook extends React's useState to support: + * - Callback execution after state updates + * - Change tracking callback for all state changes + * - Access to current state value via ref + * + * @template T - The type of the state value + * @param initState - Initial state value + * @param changeTrackCB - Optional callback that runs on every state change + * @returns Tuple containing [state, setState, getStateRefValue] + */ +function useStateCB( + initState: T, + changeTrackCB?: StateCallback +): [T, SetStateCallback, GetStateRefValue] { + const [state, _setState] = React.useState(initState); + const stateRef = React.useRef(initState); + + const callbackRef = React.useRef | undefined>(); + const changeTrackCBRef = React.useRef | undefined>(changeTrackCB); + const isFirstCBCall = React.useRef(true); + + React.useEffect(() => { + if (isFirstCBCall.current) { + isFirstCBCall.current = false; + } else { + callbackRef.current?.(state); + changeTrackCBRef.current?.(state); + } + }, [state]); + + const setState: SetStateCallback = (newState: T, callback?: StateCallback) => { + callbackRef.current = callback; + stateRef.current = newState; + _setState(newState); + }; + + const getStateRefValue: GetStateRefValue = () => stateRef.current; + + return [state, setState, getStateRefValue]; +} + +export default useStateCB; diff --git a/src/hooks/utils/useTimeOut.ts b/src/hooks/utils/useTimeOut.ts new file mode 100644 index 000000000..e139d924a --- /dev/null +++ b/src/hooks/utils/useTimeOut.ts @@ -0,0 +1,36 @@ +import { useEffect, useRef } from 'react'; + +/** + * Custom hook for managing timeouts + * + * This hook provides a declarative way to handle timeouts in React components. + * The callback is automatically updated without restarting the timeout, and + * the timeout is automatically cleaned up when the component unmounts or delay changes. + * + * @param callback - The callback function to execute after the delay + * @param delay - The delay in milliseconds, or null to disable the timeout + */ +export function useTimeout(callback: () => void, delay: number | null): void { + const savedCallback = useRef<(() => void) | undefined>(); + + // Remember the latest callback + useEffect(() => { + savedCallback.current = callback; + }, [callback]); + + // Set up the timeout + useEffect(() => { + function tick(): void { + if (savedCallback.current) { + savedCallback.current(); + } + } + + if (delay !== null) { + const id: NodeJS.Timeout = setTimeout(tick, delay); + return () => clearTimeout(id); + } + }, [delay]); +} + +export default useTimeout; diff --git a/src/hooks/utils/useToggle.ts b/src/hooks/utils/useToggle.ts new file mode 100644 index 000000000..26ceb794c --- /dev/null +++ b/src/hooks/utils/useToggle.ts @@ -0,0 +1,24 @@ +import { useCallback, useState } from 'react'; + +/** + * Custom hook for managing boolean toggle state + * + * This hook provides a convenient way to manage boolean state with toggle functionality. + * It returns the current boolean value, a toggle function, and a setter function. + * + * @param initialValue - The initial boolean value (defaults to false) + * @returns A tuple of [value, toggleValue, setValue] + */ +function useToggle( + initialValue: boolean = false +): [boolean, () => void, (value: boolean | ((prev: boolean) => boolean)) => void] { + const [value, setValue] = useState(initialValue); + + const toggleValue = useCallback((): void => { + setValue((prev: boolean) => !prev); + }, []); + + return [value, toggleValue, setValue]; +} + +export default useToggle; diff --git a/src/hooks/workspaces/index.ts b/src/hooks/workspaces/index.ts new file mode 100644 index 000000000..4be0ec3fc --- /dev/null +++ b/src/hooks/workspaces/index.ts @@ -0,0 +1,4 @@ +export { default as useDesignAssignment } from './useDesignAssignment'; +export { default as useEnvironmentAssignment } from './useEnvironmentAssignment'; +export { default as useTeamAssignment } from './useTeamAssignment'; +export { default as useViewsAssignment } from './useViewsAssignment'; diff --git a/src/custom/Workspaces/hooks/useDesignAssignment.tsx b/src/hooks/workspaces/useDesignAssignment.tsx similarity index 96% rename from src/custom/Workspaces/hooks/useDesignAssignment.tsx rename to src/hooks/workspaces/useDesignAssignment.tsx index d7c3a60d3..aeb85a9aa 100644 --- a/src/custom/Workspaces/hooks/useDesignAssignment.tsx +++ b/src/hooks/workspaces/useDesignAssignment.tsx @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect, useState } from 'react'; -import { Pattern } from '../../CustomCatalog/CustomCard'; -import { withDefaultPageArgs } from '../../PerformersSection/PerformersSection'; -import { AssignmentHookResult } from '../types'; +import { Pattern } from '../../custom/CustomCatalog/CustomCard'; +import { withDefaultPageArgs } from '../../custom/PerformersSection/PerformersSection'; +import { AssignmentHookResult } from '../../custom/Workspaces/types'; interface AddedAndRemovedDesigns { addedDesignsIds: string[]; diff --git a/src/custom/Workspaces/hooks/useEnvironmentAssignment.tsx b/src/hooks/workspaces/useEnvironmentAssignment.tsx similarity index 97% rename from src/custom/Workspaces/hooks/useEnvironmentAssignment.tsx rename to src/hooks/workspaces/useEnvironmentAssignment.tsx index 1e34ea422..70ddf2853 100644 --- a/src/custom/Workspaces/hooks/useEnvironmentAssignment.tsx +++ b/src/hooks/workspaces/useEnvironmentAssignment.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect, useState } from 'react'; -import { withDefaultPageArgs } from '../../PerformersSection/PerformersSection'; -import { AssignmentHookResult, Environment } from '../types'; +import { withDefaultPageArgs } from '../../custom/PerformersSection/PerformersSection'; +import { AssignmentHookResult, Environment } from '../../custom/Workspaces/types'; interface UseEnvironmentAssignmentProps { workspaceId: string; diff --git a/src/custom/Workspaces/hooks/useTeamAssignment.tsx b/src/hooks/workspaces/useTeamAssignment.tsx similarity index 96% rename from src/custom/Workspaces/hooks/useTeamAssignment.tsx rename to src/hooks/workspaces/useTeamAssignment.tsx index e3f177595..45a77d744 100644 --- a/src/custom/Workspaces/hooks/useTeamAssignment.tsx +++ b/src/hooks/workspaces/useTeamAssignment.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect, useState } from 'react'; -import { withDefaultPageArgs } from '../../PerformersSection/PerformersSection'; -import { AssignmentHookResult, Team } from '../types'; +import { withDefaultPageArgs } from '../../custom/PerformersSection/PerformersSection'; +import { AssignmentHookResult, Team } from '../../custom/Workspaces/types'; interface UseTeamAssignmentProps { workspaceId: string; diff --git a/src/custom/Workspaces/hooks/useViewsAssignment.tsx b/src/hooks/workspaces/useViewsAssignment.tsx similarity index 95% rename from src/custom/Workspaces/hooks/useViewsAssignment.tsx rename to src/hooks/workspaces/useViewsAssignment.tsx index 9f274bcf0..ffe3ceb65 100644 --- a/src/custom/Workspaces/hooks/useViewsAssignment.tsx +++ b/src/hooks/workspaces/useViewsAssignment.tsx @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect, useState } from 'react'; -import { Pattern } from '../../CustomCatalog/CustomCard'; -import { withDefaultPageArgs } from '../../PerformersSection/PerformersSection'; -import { AssignmentHookResult } from '../types'; +import { Pattern } from '../../custom/CustomCatalog/CustomCard'; +import { withDefaultPageArgs } from '../../custom/PerformersSection/PerformersSection'; +import { AssignmentHookResult } from '../../custom/Workspaces/types'; interface AddedAndRemovedViews { addedviewsIds: string[]; diff --git a/tsconfig.json b/tsconfig.json index dd728e1c8..6757be258 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,12 @@ "resolveJsonModule": true, "allowSyntheticDefaultImports": true, "importHelpers": true, + "baseUrl": ".", + "paths": { + "@/src/*": ["src/*"], + "@/site/*": ["site/*"], + "hooks/*": ["src/hooks/*"] + } }, "include": [ "." From d7518f4316ce9c17e51ddff7d6bb7fd9baca2ede Mon Sep 17 00:00:00 2001 From: Aryan Shah <149894557+ARYANSHAH1567@users.noreply.github.com> Date: Mon, 25 Aug 2025 12:05:38 +0530 Subject: [PATCH 2/3] Update UniversalFilter.tsx Signed-off-by: Aryan Shah <149894557+ARYANSHAH1567@users.noreply.github.com> --- src/custom/UniversalFilter.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/custom/UniversalFilter.tsx b/src/custom/UniversalFilter.tsx index c8547ff92..1ba0594da 100644 --- a/src/custom/UniversalFilter.tsx +++ b/src/custom/UniversalFilter.tsx @@ -9,6 +9,7 @@ import { Paper } from '../base/Paper'; import { Select } from '../base/Select'; import { FilterIcon } from '../icons'; import { useTheme } from '../theme'; +import { darkModalGradient, lightModalGradient } from '../theme/colors/colors'; import PopperListener from './PopperListener'; import { TooltipIcon } from './TooltipIconButton'; From 96a5a3c18270acf3ef3c7496b0ccebfc86eedb11 Mon Sep 17 00:00:00 2001 From: Aryan Shah Date: Mon, 25 Aug 2025 19:17:33 +0530 Subject: [PATCH 3/3] Removed extra hooks Signed-off-by: Aryan Shah --- src/hooks/index.ts | 1 - src/hooks/readme.md | 148 ------------------------ src/hooks/utils/index.ts | 5 - src/hooks/utils/useDebounce.ts | 44 ------- src/hooks/utils/useDebouncedCallback.ts | 49 -------- src/hooks/utils/useStateCB.ts | 51 -------- src/hooks/utils/useTimeOut.ts | 36 ------ src/hooks/utils/useToggle.ts | 24 ---- 8 files changed, 358 deletions(-) delete mode 100644 src/hooks/readme.md delete mode 100644 src/hooks/utils/index.ts delete mode 100644 src/hooks/utils/useDebounce.ts delete mode 100644 src/hooks/utils/useDebouncedCallback.ts delete mode 100644 src/hooks/utils/useStateCB.ts delete mode 100644 src/hooks/utils/useTimeOut.ts delete mode 100644 src/hooks/utils/useToggle.ts diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 5c33bc1b5..43c2c2391 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,5 +1,4 @@ export * from './data'; export * from './ui'; export * from './useRoomActivity'; -export * from './utils'; export * from './workspaces'; diff --git a/src/hooks/readme.md b/src/hooks/readme.md deleted file mode 100644 index b832bd772..000000000 --- a/src/hooks/readme.md +++ /dev/null @@ -1,148 +0,0 @@ -# Sistent Hooks - -This directory contains all reusable custom React hooks for the Sistent design system. The hooks are organized into logical categories for better maintainability and discoverability. - -## Directory Structure - -``` -src/hooks/ -├── data/ # Data management and API-related hooks -├── ui/ # UI interaction and state management hooks -├── utils/ # General utility hooks -├── workspaces/ # Workspace-specific hooks -└── index.ts # Main exports -``` - -## Categories - -### Data Hooks (`./data/`) - -Hooks for managing data, API interactions, and data processing. - -- `useResourceCleanData` - Resource data formatting and processing for Kubernetes resources - -### UI Hooks (`./ui/`) - -Hooks for managing UI state, interactions, and visual components. - -- `useWindowDimensions` - Window dimension tracking with debounced resize handling - -### Utility Hooks (`./utils/`) - -General-purpose utility hooks for common patterns. - -- `useDebounce` - Debounce values with configurable delay -- `useDebouncedCallback` - Debounce function calls with configurable delay -- `usePreventUserFromLeavingPage` - Prevent users from leaving pages with unsaved changes -- `useStateCB` - State management with callback support for change tracking -- `useToggle` - Simple boolean state toggle management -- `useTimeout` - Manage timeouts with automatic cleanup - -### Workspace Hooks (`./workspaces/`) - -Hooks specific to workspace functionality and management. - -- `useDesignAssignment` - Design assignment management for workspaces -- `useEnvironmentAssignment` - Environment assignment management for workspaces -- `useTeamAssignment` - Team assignment management for workspaces -- `useViewsAssignment` - Views assignment management for workspaces - -### Import Options - -With the configured path mapping, you have multiple import options: - -#### Option 1: Category-based imports (Recommended) - -```typescript -// Import from specific categories -import { useDebounce, useToggle, useTimeout } from 'hooks/utils'; -import { useWindowDimensions } from 'hooks/ui'; -import { useDesignAssignment } from 'hooks/workspaces'; -``` - -#### Option 2: Absolute path imports - -```typescript -// Using absolute paths with @ alias -import { useDebounce } from '@/src/hooks/utils'; -import { useWindowDimensions } from '@/src/hooks/ui'; -``` - -#### Option 3: Traditional relative imports - -```typescript -// Relative imports (still work) -import { useDebounce } from 'hooks/utils'; -``` - -## Adding New Hooks - -When adding new hooks, please follow these guidelines: - -1. **Categorization**: Place hooks in the appropriate directory based on their primary function -2. **Naming**: Use descriptive names starting with "use" following React conventions -3. **TypeScript**: Provide full type definitions for parameters and return values -4. **Documentation**: Include JSDoc comments describing the hook's purpose and usage -5. **Exports**: Add the hook to the appropriate category's index.ts file -6. **Default Export**: Use default exports with the pattern: - - ```typescript - function useMyHook() { - // implementation - } - - export default useMyHook; - ``` - -## Examples - -### Basic Usage - -```typescript -import { useDebounce, useToggle } from 'hooks/utils'; - -function MyComponent() { - const [searchTerm, setSearchTerm] = useState(''); - const [isOpen, toggleOpen] = useToggle(false); - const debouncedSearchTerm = useDebounce(searchTerm, 300); - - // Use debouncedSearchTerm for API calls - // Use toggleOpen for modal state -} -``` - -## Best Practices - -1. **Keep hooks focused**: Each hook should have a single, well-defined responsibility -2. **Use TypeScript**: Always provide type definitions for better developer experience -3. **Handle cleanup**: Ensure proper cleanup of effects, timers, and event listeners -4. **Avoid `any` types**: Use `unknown` or specific types instead of `any` -5. **Default exports**: Follow the established pattern of default exports -6. **Consistent imports**: Use the path mapping for cleaner, more maintainable imports -7. **Test thoroughly**: Write comprehensive tests for all hooks -8. **Document edge cases**: Include documentation for error handling and edge cases - -## File Structure Example - -```typescript -// src/hooks/utils/useMyHook.ts -import { useState, useEffect } from 'react'; - -/** - * Custom hook for managing something - * @param initialValue - The initial value - * @returns The managed state and setter - */ -function useMyHook(initialValue: T): [T, (value: T) => void] { - // Implementation -} - -export default useMyHook; -``` - -```typescript -// src/hooks/utils/index.ts -export { default as useDebounce } from './useDebounce'; -export { default as useMyHook } from './useMyHook'; -// ... other exports -``` diff --git a/src/hooks/utils/index.ts b/src/hooks/utils/index.ts deleted file mode 100644 index b3fe1b575..000000000 --- a/src/hooks/utils/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default as useDebounce } from './useDebounce'; -export { default as useDebouncedCallback } from './useDebouncedCallback'; -export { default as useStateCB } from './useStateCB'; -export { default as useTimeOut } from './useTimeOut'; -export { default as useToggle } from './useToggle'; diff --git a/src/hooks/utils/useDebounce.ts b/src/hooks/utils/useDebounce.ts deleted file mode 100644 index 482a7274c..000000000 --- a/src/hooks/utils/useDebounce.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { useEffect, useState } from 'react'; - -/** - * Custom hook for debouncing values - * - * This hook delays updating the returned value until after the specified delay - * has passed since the last time the input value changed. - * - * @template T - The type of the value being debounced - * @param value - The value to debounce - * @param delay - The debounce delay in milliseconds (default: 300) - * @returns The debounced value that updates after the delay period - * - * @example - * ```typescript - * const [searchTerm, setSearchTerm] = useState(''); - * const debouncedSearchTerm = useDebounce(searchTerm, 500); - * - * useEffect(() => { - * if (debouncedSearchTerm) { - * // Perform search API call - * searchAPI(debouncedSearchTerm); - * } - * }, [debouncedSearchTerm]); - * ``` - */ -export function useDebounce(value: T, delay: number = 300): T { - const [debouncedValue, setDebouncedValue] = useState(value); - - useEffect(() => { - const handler = setTimeout(() => { - setDebouncedValue(value); - }, delay); - - // Cleanup function to clear timeout if value changes before delay - return () => { - clearTimeout(handler); - }; - }, [value, delay]); - - return debouncedValue; -} - -export default useDebounce; diff --git a/src/hooks/utils/useDebouncedCallback.ts b/src/hooks/utils/useDebouncedCallback.ts deleted file mode 100644 index aa149b915..000000000 --- a/src/hooks/utils/useDebouncedCallback.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { useCallback, useEffect, useRef } from 'react'; - -/** - * Custom hook for debouncing function calls - * - * This hook returns a debounced version of the provided callback function. - * The debounced function will only execute after the specified delay has - * passed since the last time it was called. - * - * @template T - The type of the callback function parameters - * @param callback - The function to debounce - * @param delay - The debounce delay in milliseconds (default: 300) - * @returns A debounced version of the callback function - * - */ -export function useDebouncedCallback unknown>( - callback: T, - delay: number = 300 -): (...args: Parameters) => void { - const timeoutRef = useRef(null); - - const debouncedFunction = useCallback( - (...args: Parameters) => { - // Clear any existing timeout - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - } - - // Set a new timeout to call the callback - timeoutRef.current = setTimeout(() => { - callback(...args); - }, delay); - }, - [callback, delay] - ); - - // Clean up timeout on unmount - useEffect(() => { - return () => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current); - } - }; - }, []); - - return debouncedFunction; -} - -export default useDebouncedCallback; diff --git a/src/hooks/utils/useStateCB.ts b/src/hooks/utils/useStateCB.ts deleted file mode 100644 index 483d5e9e0..000000000 --- a/src/hooks/utils/useStateCB.ts +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; - -type StateCallback = (state: T) => void; -type SetStateCallback = (state: T, callback?: StateCallback) => void; -type GetStateRefValue = () => T; - -/** - * Custom hook that provides state management with callback support - * - * This hook extends React's useState to support: - * - Callback execution after state updates - * - Change tracking callback for all state changes - * - Access to current state value via ref - * - * @template T - The type of the state value - * @param initState - Initial state value - * @param changeTrackCB - Optional callback that runs on every state change - * @returns Tuple containing [state, setState, getStateRefValue] - */ -function useStateCB( - initState: T, - changeTrackCB?: StateCallback -): [T, SetStateCallback, GetStateRefValue] { - const [state, _setState] = React.useState(initState); - const stateRef = React.useRef(initState); - - const callbackRef = React.useRef | undefined>(); - const changeTrackCBRef = React.useRef | undefined>(changeTrackCB); - const isFirstCBCall = React.useRef(true); - - React.useEffect(() => { - if (isFirstCBCall.current) { - isFirstCBCall.current = false; - } else { - callbackRef.current?.(state); - changeTrackCBRef.current?.(state); - } - }, [state]); - - const setState: SetStateCallback = (newState: T, callback?: StateCallback) => { - callbackRef.current = callback; - stateRef.current = newState; - _setState(newState); - }; - - const getStateRefValue: GetStateRefValue = () => stateRef.current; - - return [state, setState, getStateRefValue]; -} - -export default useStateCB; diff --git a/src/hooks/utils/useTimeOut.ts b/src/hooks/utils/useTimeOut.ts deleted file mode 100644 index e139d924a..000000000 --- a/src/hooks/utils/useTimeOut.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useEffect, useRef } from 'react'; - -/** - * Custom hook for managing timeouts - * - * This hook provides a declarative way to handle timeouts in React components. - * The callback is automatically updated without restarting the timeout, and - * the timeout is automatically cleaned up when the component unmounts or delay changes. - * - * @param callback - The callback function to execute after the delay - * @param delay - The delay in milliseconds, or null to disable the timeout - */ -export function useTimeout(callback: () => void, delay: number | null): void { - const savedCallback = useRef<(() => void) | undefined>(); - - // Remember the latest callback - useEffect(() => { - savedCallback.current = callback; - }, [callback]); - - // Set up the timeout - useEffect(() => { - function tick(): void { - if (savedCallback.current) { - savedCallback.current(); - } - } - - if (delay !== null) { - const id: NodeJS.Timeout = setTimeout(tick, delay); - return () => clearTimeout(id); - } - }, [delay]); -} - -export default useTimeout; diff --git a/src/hooks/utils/useToggle.ts b/src/hooks/utils/useToggle.ts deleted file mode 100644 index 26ceb794c..000000000 --- a/src/hooks/utils/useToggle.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useCallback, useState } from 'react'; - -/** - * Custom hook for managing boolean toggle state - * - * This hook provides a convenient way to manage boolean state with toggle functionality. - * It returns the current boolean value, a toggle function, and a setter function. - * - * @param initialValue - The initial boolean value (defaults to false) - * @returns A tuple of [value, toggleValue, setValue] - */ -function useToggle( - initialValue: boolean = false -): [boolean, () => void, (value: boolean | ((prev: boolean) => boolean)) => void] { - const [value, setValue] = useState(initialValue); - - const toggleValue = useCallback((): void => { - setValue((prev: boolean) => !prev); - }, []); - - return [value, toggleValue, setValue]; -} - -export default useToggle;