From 56527da73ed1802907068f950e6a3b2e33f34db1 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 29 Dec 2023 14:05:56 +1100 Subject: [PATCH] feat(ui): memoize all components --- .../frontend/web/.storybook/ReduxInit.tsx | 8 +- .../src/common/components/IAIDropOverlay.tsx | 2 +- .../common/components/IAIImageFallback.tsx | 67 +++--- .../InvAutosizeTextarea.tsx | 61 ++--- .../common/components/InvButton/InvButton.tsx | 39 ++-- .../InvButtonGroup/InvButtonGroup.tsx | 16 +- .../InvConfirmationAlertDialog.tsx | 102 ++++----- .../InvContextMenu/InvContextMenu.tsx | 162 +++++++------- .../components/InvControl/InvControl.tsx | 108 ++++----- .../components/InvControl/InvControlGroup.tsx | 23 +- .../common/components/InvControl/InvLabel.tsx | 54 ++--- .../InvIconButton/InvIconButton.tsx | 43 ++-- .../common/components/InvInput/InvInput.tsx | 10 +- .../common/components/InvMenu/InvMenuItem.tsx | 55 ++--- .../common/components/InvMenu/InvMenuList.tsx | 29 ++- .../InvNumberInput/InvNumberInput.tsx | 208 +++++++++--------- .../InvNumberInput/InvNumberInputField.tsx | 8 +- .../InvNumberInput/InvNumberInputStepper.tsx | 34 +-- .../InvRangeSlider/InvRangeSlider.tsx | 187 ++++++++-------- .../InvRangeSlider/InvRangeSliderMark.tsx | 80 +++---- .../components/InvSelect/CustomMenuList.tsx | 108 ++++----- .../components/InvSelect/CustomOption.tsx | 95 ++++---- .../InvSelect/InvSelectFallback.tsx | 7 +- .../InvSingleAccordion/InvSingleAccordion.tsx | 7 +- .../common/components/InvSlider/InvSlider.tsx | 8 +- .../components/InvSlider/InvSliderMark.tsx | 80 +++---- .../src/common/components/InvTabs/InvTab.tsx | 9 +- .../components/InvTextarea/InvTextarea.tsx | 46 ++-- .../components/InvTooltip/InvTooltip.tsx | 33 +-- .../components/DeleteImageButton.tsx | 7 +- .../components/DynamicPromptsPreviewModal.tsx | 7 +- .../ShowDynamicPromptsPreviewButton.tsx | 7 +- .../features/embedding/AddEmbeddingButton.tsx | 7 +- .../features/embedding/EmbeddingPopover.tsx | 7 +- .../features/embedding/EmbeddingSelect.tsx | 111 +++++----- .../SyncModels/SyncModelsButton.tsx | 41 ++-- .../SyncModels/SyncModelsIconButton.tsx | 45 ++-- .../features/nodes/components/flow/Flow.tsx | 8 +- .../components/Core/ParamHeight.tsx | 8 +- .../components/Core/ParamNegativePrompt.tsx | 8 +- .../components/Core/ParamPositivePrompt.tsx | 8 +- .../parameters/components/Core/ParamWidth.tsx | 8 +- .../ImageSize/AspectRatioPreviewWrapper.tsx | 7 +- .../ImageSize/AspectRatioSelect.tsx | 8 +- .../ImageSize/LockAspectRatioButton.tsx | 8 +- .../ImageSize/SetOptimalSizeButton.tsx | 8 +- .../ImageSize/SwapDimensionsButton.tsx | 8 +- .../Prompts/PromptOverlayButtonWrapper.tsx | 8 +- .../parameters/components/Prompts/Prompts.tsx | 7 +- .../components/Seed/ParamSeedNumberInput.tsx | 8 +- .../components/Seed/ParamSeedRandomize.tsx | 8 +- .../components/Seed/ParamSeedShuffle.tsx | 8 +- .../VAEModel/ParamVAEModelSelect.tsx | 4 +- .../components/InvokeQueueBackButton.tsx | 8 +- .../components/QueueActionsMenuButton.tsx | 7 +- .../queue/components/QueueControls.tsx | 3 +- .../ParamSDXLNegativeStylePrompt.tsx | 8 +- .../ParamSDXLPositiveStylePrompt.tsx | 8 +- .../SDXLPrompts/SDXLConcatButton.tsx | 8 +- .../components/SDXLPrompts/SDXLPrompts.tsx | 7 +- .../AdvancedSettingsAccordion.tsx | 7 +- .../GenerationSettingsAccordion.tsx | 7 +- .../ImageSettingsAccordion.tsx | 13 +- .../SettingsModal/SettingsLanguageSelect.tsx | 8 +- .../SettingsModal/SettingsLogLevelSelect.tsx | 8 +- 65 files changed, 1165 insertions(+), 970 deletions(-) diff --git a/invokeai/frontend/web/.storybook/ReduxInit.tsx b/invokeai/frontend/web/.storybook/ReduxInit.tsx index 36583ca121..9d46432603 100644 --- a/invokeai/frontend/web/.storybook/ReduxInit.tsx +++ b/invokeai/frontend/web/.storybook/ReduxInit.tsx @@ -1,4 +1,4 @@ -import { PropsWithChildren, useEffect } from 'react'; +import { PropsWithChildren, memo, useEffect } from 'react'; import { modelChanged } from '../src/features/parameters/store/generationSlice'; import { useAppDispatch } from '../src/app/store/storeHooks'; import { useGlobalModifiersInit } from '../src/common/hooks/useGlobalModifiers'; @@ -6,7 +6,7 @@ import { useGlobalModifiersInit } from '../src/common/hooks/useGlobalModifiers'; * Initializes some state for storybook. Must be in a different component * so that it is run inside the redux context. */ -export const ReduxInit = (props: PropsWithChildren) => { +export const ReduxInit = memo((props: PropsWithChildren) => { const dispatch = useAppDispatch(); useGlobalModifiersInit(); useEffect(() => { @@ -20,4 +20,6 @@ export const ReduxInit = (props: PropsWithChildren) => { }, []); return props.children; -}; +}); + +ReduxInit.displayName = 'ReduxInit'; diff --git a/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx b/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx index 3d2bf887aa..809c00653b 100644 --- a/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx +++ b/invokeai/frontend/web/src/common/components/IAIDropOverlay.tsx @@ -22,7 +22,7 @@ const exit: AnimationProps['exit'] = { transition: { duration: 0.1 }, }; -export const IAIDropOverlay = (props: Props) => { +const IAIDropOverlay = (props: Props) => { const { isOver, label = 'Drop' } = props; const motionId = useRef(uuidv4()); return ( diff --git a/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx b/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx index 1c7775eb57..04354f5e87 100644 --- a/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx +++ b/invokeai/frontend/web/src/common/components/IAIImageFallback.tsx @@ -1,6 +1,6 @@ import type { As, FlexProps, StyleProps } from '@chakra-ui/react'; import { Flex, Icon, Skeleton, Spinner } from '@chakra-ui/react'; -import { useMemo } from 'react'; +import { memo, useMemo } from 'react'; import { FaImage } from 'react-icons/fa'; import type { ImageDTO } from 'services/api/types'; @@ -8,7 +8,7 @@ import { InvText } from './InvText/wrapper'; type Props = { image: ImageDTO | undefined }; -export const IAILoadingImageFallback = (props: Props) => { +export const IAILoadingImageFallback = memo((props: Props) => { if (props.image) { return ( { ); -}; +}); +IAILoadingImageFallback.displayName = 'IAILoadingImageFallback'; type IAINoImageFallbackProps = FlexProps & { label?: string; @@ -41,7 +42,7 @@ type IAINoImageFallbackProps = FlexProps & { boxSize?: StyleProps['boxSize']; }; -export const IAINoContentFallback = (props: IAINoImageFallbackProps) => { +export const IAINoContentFallback = memo((props: IAINoImageFallbackProps) => { const { icon = FaImage, boxSize = 16, sx, ...rest } = props; const styles = useMemo( @@ -67,37 +68,39 @@ export const IAINoContentFallback = (props: IAINoImageFallbackProps) => { {props.label && {props.label}} ); -}; +}); +IAINoContentFallback.displayName = 'IAINoContentFallback'; type IAINoImageFallbackWithSpinnerProps = FlexProps & { label?: string; }; -export const IAINoContentFallbackWithSpinner = ( - props: IAINoImageFallbackWithSpinnerProps -) => { - const { sx, ...rest } = props; - const styles = useMemo( - () => ({ - w: 'full', - h: 'full', - alignItems: 'center', - justifyContent: 'center', - borderRadius: 'base', - flexDir: 'column', - gap: 2, - userSelect: 'none', - opacity: 0.7, - color: 'base.500', - ...sx, - }), - [sx] - ); +export const IAINoContentFallbackWithSpinner = memo( + (props: IAINoImageFallbackWithSpinnerProps) => { + const { sx, ...rest } = props; + const styles = useMemo( + () => ({ + w: 'full', + h: 'full', + alignItems: 'center', + justifyContent: 'center', + borderRadius: 'base', + flexDir: 'column', + gap: 2, + userSelect: 'none', + opacity: 0.7, + color: 'base.500', + ...sx, + }), + [sx] + ); - return ( - - - {props.label && {props.label}} - - ); -}; + return ( + + + {props.label && {props.label}} + + ); + } +); +IAINoContentFallbackWithSpinner.displayName = 'IAINoContentFallbackWithSpinner'; diff --git a/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvAutosizeTextarea.tsx b/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvAutosizeTextarea.tsx index 455cdee090..431a901bb2 100644 --- a/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvAutosizeTextarea.tsx +++ b/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvAutosizeTextarea.tsx @@ -2,36 +2,39 @@ import { Box, forwardRef, Textarea as ChakraTextarea } from '@chakra-ui/react'; import { useGlobalModifiersSetters } from 'common/hooks/useGlobalModifiers'; import { stopPastePropagation } from 'common/util/stopPastePropagation'; import type { KeyboardEvent } from 'react'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import ResizeTextarea from 'react-textarea-autosize'; import type { InvAutosizeTextareaProps } from './types'; -export const InvAutosizeTextarea = forwardRef< - InvAutosizeTextareaProps, - typeof ResizeTextarea ->((props: InvAutosizeTextareaProps, ref) => { - const { setShift } = useGlobalModifiersSetters(); - const onKeyUpDown = useCallback( - (e: KeyboardEvent) => { - setShift(e.shiftKey); - }, - [setShift] - ); - return ( - - - - ); -}); +export const InvAutosizeTextarea = memo( + forwardRef( + (props: InvAutosizeTextareaProps, ref) => { + const { setShift } = useGlobalModifiersSetters(); + const onKeyUpDown = useCallback( + (e: KeyboardEvent) => { + setShift(e.shiftKey); + }, + [setShift] + ); + return ( + + + + ); + } + ) +); + +InvAutosizeTextarea.displayName = 'InvAutosizeTextarea'; diff --git a/invokeai/frontend/web/src/common/components/InvButton/InvButton.tsx b/invokeai/frontend/web/src/common/components/InvButton/InvButton.tsx index 3e857d61f1..ffd2bbca86 100644 --- a/invokeai/frontend/web/src/common/components/InvButton/InvButton.tsx +++ b/invokeai/frontend/web/src/common/components/InvButton/InvButton.tsx @@ -1,24 +1,33 @@ import { Button, forwardRef } from '@chakra-ui/react'; import { InvTooltip } from 'common/components/InvTooltip/InvTooltip'; +import { memo } from 'react'; import type { InvButtonProps } from './types'; -export const InvButton = forwardRef( - ({ isChecked, tooltip, children, ...rest }: InvButtonProps, ref) => { - if (tooltip) { +export const InvButton = memo( + forwardRef( + ({ isChecked, tooltip, children, ...rest }: InvButtonProps, ref) => { + if (tooltip) { + return ( + + + + ); + } + return ( - - - + ); } - - return ( - - ); - } + ) ); + +InvButton.displayName = 'InvButton'; diff --git a/invokeai/frontend/web/src/common/components/InvButtonGroup/InvButtonGroup.tsx b/invokeai/frontend/web/src/common/components/InvButtonGroup/InvButtonGroup.tsx index ca9167a0bf..6b31b3e516 100644 --- a/invokeai/frontend/web/src/common/components/InvButtonGroup/InvButtonGroup.tsx +++ b/invokeai/frontend/web/src/common/components/InvButtonGroup/InvButtonGroup.tsx @@ -1,10 +1,14 @@ import { ButtonGroup, forwardRef } from '@chakra-ui/react'; +import { memo } from 'react'; import type { InvButtonGroupProps } from './types'; -export const InvButtonGroup = forwardRef< - InvButtonGroupProps, - typeof ButtonGroup ->(({ isAttached = true, ...rest }: InvButtonGroupProps, ref) => { - return ; -}); +export const InvButtonGroup = memo( + forwardRef( + ({ isAttached = true, ...rest }: InvButtonGroupProps, ref) => { + return ; + } + ) +); + +InvButtonGroup.displayName = 'InvButtonGroup'; diff --git a/invokeai/frontend/web/src/common/components/InvConfirmationAlertDialog/InvConfirmationAlertDialog.tsx b/invokeai/frontend/web/src/common/components/InvConfirmationAlertDialog/InvConfirmationAlertDialog.tsx index 58edb74025..b3c8ebb8a1 100644 --- a/invokeai/frontend/web/src/common/components/InvConfirmationAlertDialog/InvConfirmationAlertDialog.tsx +++ b/invokeai/frontend/web/src/common/components/InvConfirmationAlertDialog/InvConfirmationAlertDialog.tsx @@ -7,7 +7,7 @@ import { InvAlertDialogOverlay, } from 'common/components/InvAlertDialog/wrapper'; import { InvButton } from 'common/components/InvButton/InvButton'; -import { useCallback, useRef } from 'react'; +import { memo, useCallback, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import type { InvConfirmationAlertDialogProps } from './types'; @@ -16,59 +16,61 @@ import type { InvConfirmationAlertDialogProps } from './types'; * This component is a wrapper around InvAlertDialog that provides a confirmation dialog. * Its state must be managed externally using chakra's `useDisclosure()` hook. */ -export const InvConfirmationAlertDialog = ( - props: InvConfirmationAlertDialogProps -) => { - const { t } = useTranslation(); +export const InvConfirmationAlertDialog = memo( + (props: InvConfirmationAlertDialogProps) => { + const { t } = useTranslation(); - const { - acceptCallback, - cancelCallback, - acceptButtonText = t('common.accept'), - cancelButtonText = t('common.cancel'), - children, - title, - isOpen, - onClose, - } = props; + const { + acceptCallback, + cancelCallback, + acceptButtonText = t('common.accept'), + cancelButtonText = t('common.cancel'), + children, + title, + isOpen, + onClose, + } = props; - const cancelRef = useRef(null); + const cancelRef = useRef(null); - const handleAccept = useCallback(() => { - acceptCallback(); - onClose(); - }, [acceptCallback, onClose]); + const handleAccept = useCallback(() => { + acceptCallback(); + onClose(); + }, [acceptCallback, onClose]); - const handleCancel = useCallback(() => { - cancelCallback && cancelCallback(); - onClose(); - }, [cancelCallback, onClose]); + const handleCancel = useCallback(() => { + cancelCallback && cancelCallback(); + onClose(); + }, [cancelCallback, onClose]); - return ( - - - - - {title} - + return ( + + + + + {title} + - {children} + {children} - - - {cancelButtonText} - - - {acceptButtonText} - - - - - - ); -}; + + + {cancelButtonText} + + + {acceptButtonText} + + + + + + ); + } +); + +InvConfirmationAlertDialog.displayName = 'InvConfirmationAlertDialog'; diff --git a/invokeai/frontend/web/src/common/components/InvContextMenu/InvContextMenu.tsx b/invokeai/frontend/web/src/common/components/InvContextMenu/InvContextMenu.tsx index f2322fca52..20025b0b56 100644 --- a/invokeai/frontend/web/src/common/components/InvContextMenu/InvContextMenu.tsx +++ b/invokeai/frontend/web/src/common/components/InvContextMenu/InvContextMenu.tsx @@ -16,7 +16,7 @@ import type { MenuButtonProps, MenuProps, PortalProps } from '@chakra-ui/react'; import { Portal, useEventListener } from '@chakra-ui/react'; import { InvMenu, InvMenuButton } from 'common/components/InvMenu/wrapper'; import { useGlobalMenuCloseTrigger } from 'common/hooks/useGlobalMenuCloseTrigger'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { memo, useCallback, useEffect, useRef, useState } from 'react'; export interface InvContextMenuProps { renderMenu: () => JSX.Element | null; @@ -26,89 +26,91 @@ export interface InvContextMenuProps { menuButtonProps?: MenuButtonProps; } -export const InvContextMenu = ( - props: InvContextMenuProps -) => { - const [isOpen, setIsOpen] = useState(false); - const [isRendered, setIsRendered] = useState(false); - const [isDeferredOpen, setIsDeferredOpen] = useState(false); - const [position, setPosition] = useState<[number, number]>([0, 0]); - const targetRef = useRef(null); +export const InvContextMenu = memo( + (props: InvContextMenuProps) => { + const [isOpen, setIsOpen] = useState(false); + const [isRendered, setIsRendered] = useState(false); + const [isDeferredOpen, setIsDeferredOpen] = useState(false); + const [position, setPosition] = useState<[number, number]>([0, 0]); + const targetRef = useRef(null); - useEffect(() => { - if (isOpen) { - setTimeout(() => { - setIsRendered(true); + useEffect(() => { + if (isOpen) { setTimeout(() => { - setIsDeferredOpen(true); + setIsRendered(true); + setTimeout(() => { + setIsDeferredOpen(true); + }); }); - }); - } else { - setIsDeferredOpen(false); - const timeout = setTimeout(() => { - setIsRendered(isOpen); - }, 1000); - return () => clearTimeout(timeout); - } - }, [isOpen]); + } else { + setIsDeferredOpen(false); + const timeout = setTimeout(() => { + setIsRendered(isOpen); + }, 1000); + return () => clearTimeout(timeout); + } + }, [isOpen]); - const onClose = useCallback(() => { - setIsOpen(false); - setIsDeferredOpen(false); - setIsRendered(false); - }, []); - - // This is the change from the original chakra-ui-contextmenu - // Close all menus when the globalContextMenuCloseTrigger changes - useGlobalMenuCloseTrigger(onClose); - - useEventListener('contextmenu', (e) => { - if ( - targetRef.current?.contains(e.target as HTMLElement) || - e.target === targetRef.current - ) { - e.preventDefault(); - setIsOpen(true); - setPosition([e.pageX, e.pageY]); - } else { + const onClose = useCallback(() => { setIsOpen(false); - } - }); + setIsDeferredOpen(false); + setIsRendered(false); + }, []); - const onCloseHandler = useCallback(() => { - props.menuProps?.onClose?.(); - setIsOpen(false); - }, [props.menuProps]); + // This is the change from the original chakra-ui-contextmenu + // Close all menus when the globalContextMenuCloseTrigger changes + useGlobalMenuCloseTrigger(onClose); - return ( - <> - {props.children(targetRef)} - {isRendered && ( - - - - {props.renderMenu()} - - - )} - - ); -}; + useEventListener('contextmenu', (e) => { + if ( + targetRef.current?.contains(e.target as HTMLElement) || + e.target === targetRef.current + ) { + e.preventDefault(); + setIsOpen(true); + setPosition([e.pageX, e.pageY]); + } else { + setIsOpen(false); + } + }); + + const onCloseHandler = useCallback(() => { + props.menuProps?.onClose?.(); + setIsOpen(false); + }, [props.menuProps]); + + return ( + <> + {props.children(targetRef)} + {isRendered && ( + + + + {props.renderMenu()} + + + )} + + ); + } +); + +InvContextMenu.displayName = 'InvContextMenu'; diff --git a/invokeai/frontend/web/src/common/components/InvControl/InvControl.tsx b/invokeai/frontend/web/src/common/components/InvControl/InvControl.tsx index 62e11d77b3..4223c147ef 100644 --- a/invokeai/frontend/web/src/common/components/InvControl/InvControl.tsx +++ b/invokeai/frontend/web/src/common/components/InvControl/InvControl.tsx @@ -5,71 +5,75 @@ import { forwardRef, } from '@chakra-ui/react'; import { InvControlGroupContext } from 'common/components/InvControl/InvControlGroup'; -import { useContext } from 'react'; +import { memo, useContext } from 'react'; import { InvLabel } from './InvLabel'; import type { InvControlProps } from './types'; -export const InvControl = forwardRef( - (props: InvControlProps, ref) => { - const { - children, - helperText, - feature, - orientation, - renderInfoPopoverInPortal = true, - isDisabled, - labelProps, - label, - ...formControlProps - } = props; +export const InvControl = memo( + forwardRef( + (props: InvControlProps, ref) => { + const { + children, + helperText, + feature, + orientation, + renderInfoPopoverInPortal = true, + isDisabled, + labelProps, + label, + ...formControlProps + } = props; - const ctx = useContext(InvControlGroupContext); + const ctx = useContext(InvControlGroupContext); + + if (helperText) { + return ( + + + {label && ( + + {label} + + )} + {children} + + {helperText} + + ); + } - if (helperText) { return ( - - {label && ( - - {label} - - )} - {children} - - {helperText} + {label && ( + + {label} + + )} + {children} ); } - - return ( - - {label && ( - - {label} - - )} - {children} - - ); - } + ) ); + +InvControl.displayName = 'InvControl'; diff --git a/invokeai/frontend/web/src/common/components/InvControl/InvControlGroup.tsx b/invokeai/frontend/web/src/common/components/InvControl/InvControlGroup.tsx index f79452ec93..41f7653947 100644 --- a/invokeai/frontend/web/src/common/components/InvControl/InvControlGroup.tsx +++ b/invokeai/frontend/web/src/common/components/InvControl/InvControlGroup.tsx @@ -1,6 +1,6 @@ import type { FormLabelProps } from '@chakra-ui/react'; import type { PropsWithChildren } from 'react'; -import { createContext } from 'react'; +import { createContext, memo } from 'react'; export type InvControlGroupProps = { labelProps?: FormLabelProps; @@ -10,13 +10,14 @@ export type InvControlGroupProps = { export const InvControlGroupContext = createContext({}); -export const InvControlGroup = ({ - children, - ...context -}: PropsWithChildren) => { - return ( - - {children} - - ); -}; +export const InvControlGroup = memo( + ({ children, ...context }: PropsWithChildren) => { + return ( + + {children} + + ); + } +); + +InvControlGroup.displayName = 'InvControlGroup'; diff --git a/invokeai/frontend/web/src/common/components/InvControl/InvLabel.tsx b/invokeai/frontend/web/src/common/components/InvControl/InvLabel.tsx index e218df9ced..1f6dfd8b4a 100644 --- a/invokeai/frontend/web/src/common/components/InvControl/InvLabel.tsx +++ b/invokeai/frontend/web/src/common/components/InvControl/InvLabel.tsx @@ -4,7 +4,7 @@ import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover'; import { InvControlGroupContext } from 'common/components/InvControl/InvControlGroup'; -import { useContext } from 'react'; +import { memo, useContext } from 'react'; import type { InvLabelProps } from './types'; @@ -13,31 +13,35 @@ const selector = createSelector( ({ system }) => system.shouldEnableInformationalPopovers ); -export const InvLabel = forwardRef( - ( - { feature, renderInfoPopoverInPortal, children, ...rest }: InvLabelProps, - ref - ) => { - const shouldEnableInformationalPopovers = useAppSelector(selector); - const ctx = useContext(InvControlGroupContext); - if (feature && shouldEnableInformationalPopovers) { +export const InvLabel = memo( + forwardRef( + ( + { feature, renderInfoPopoverInPortal, children, ...rest }: InvLabelProps, + ref + ) => { + const shouldEnableInformationalPopovers = useAppSelector(selector); + const ctx = useContext(InvControlGroupContext); + if (feature && shouldEnableInformationalPopovers) { + return ( + + + + {children} + + + + ); + } return ( - - - - {children} - - - + + {children} + ); } - return ( - - {children} - - ); - } + ) ); + +InvLabel.displayName = 'InvLabel'; diff --git a/invokeai/frontend/web/src/common/components/InvIconButton/InvIconButton.tsx b/invokeai/frontend/web/src/common/components/InvIconButton/InvIconButton.tsx index ce1ca908f4..747658ee43 100644 --- a/invokeai/frontend/web/src/common/components/InvIconButton/InvIconButton.tsx +++ b/invokeai/frontend/web/src/common/components/InvIconButton/InvIconButton.tsx @@ -1,27 +1,32 @@ import { forwardRef, IconButton } from '@chakra-ui/react'; import type { InvIconButtonProps } from 'common/components/InvIconButton/types'; import { InvTooltip } from 'common/components/InvTooltip/InvTooltip'; +import { memo } from 'react'; + +export const InvIconButton = memo( + forwardRef( + ({ isChecked, tooltip, ...rest }: InvIconButtonProps, ref) => { + if (tooltip) { + return ( + + + + ); + } -export const InvIconButton = forwardRef( - ({ isChecked, tooltip, ...rest }: InvIconButtonProps, ref) => { - if (tooltip) { return ( - - - + ); } - - return ( - - ); - } + ) ); + +InvIconButton.displayName = 'InvIconButton'; diff --git a/invokeai/frontend/web/src/common/components/InvInput/InvInput.tsx b/invokeai/frontend/web/src/common/components/InvInput/InvInput.tsx index 9170e2c7b3..21018faa0c 100644 --- a/invokeai/frontend/web/src/common/components/InvInput/InvInput.tsx +++ b/invokeai/frontend/web/src/common/components/InvInput/InvInput.tsx @@ -2,12 +2,12 @@ import { forwardRef, Input } from '@chakra-ui/react'; import { useGlobalModifiersSetters } from 'common/hooks/useGlobalModifiers'; import { stopPastePropagation } from 'common/util/stopPastePropagation'; import type { KeyboardEvent } from 'react'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import type { InvInputProps } from './types'; -export const InvInput = forwardRef( - (props: InvInputProps, ref) => { +export const InvInput = memo( + forwardRef((props: InvInputProps, ref) => { const { setShift } = useGlobalModifiersSetters(); const onKeyUpDown = useCallback( (e: KeyboardEvent) => { @@ -24,5 +24,7 @@ export const InvInput = forwardRef( {...props} /> ); - } + }) ); + +InvInput.displayName = 'InvInput'; diff --git a/invokeai/frontend/web/src/common/components/InvMenu/InvMenuItem.tsx b/invokeai/frontend/web/src/common/components/InvMenu/InvMenuItem.tsx index 62a8ba05f6..fee0950e08 100644 --- a/invokeai/frontend/web/src/common/components/InvMenu/InvMenuItem.tsx +++ b/invokeai/frontend/web/src/common/components/InvMenu/InvMenuItem.tsx @@ -4,6 +4,7 @@ import { keyframes, MenuItem as ChakraMenuItem, } from '@chakra-ui/react'; +import {memo} from'react' import type { InvMenuItemProps } from './types'; @@ -16,29 +17,33 @@ const spin = keyframes` } `; -export const InvMenuItem = forwardRef( - (props: InvMenuItemProps, ref) => { - const { - isDestructive = false, - isLoading = false, - isDisabled, - icon, - ...rest - } = props; - return ( - - ) : ( - icon - ) - } - isDisabled={isLoading || isDisabled} - data-destructive={isDestructive} - {...rest} - /> - ); - } +export const InvMenuItem = memo( + forwardRef( + (props: InvMenuItemProps, ref) => { + const { + isDestructive = false, + isLoading = false, + isDisabled, + icon, + ...rest + } = props; + return ( + + ) : ( + icon + ) + } + isDisabled={isLoading || isDisabled} + data-destructive={isDestructive} + {...rest} + /> + ); + } + ) ); + +InvMenuItem.displayName = 'InvMenuItem'; diff --git a/invokeai/frontend/web/src/common/components/InvMenu/InvMenuList.tsx b/invokeai/frontend/web/src/common/components/InvMenu/InvMenuList.tsx index 0d40e41fae..aa5c34a8ff 100644 --- a/invokeai/frontend/web/src/common/components/InvMenu/InvMenuList.tsx +++ b/invokeai/frontend/web/src/common/components/InvMenu/InvMenuList.tsx @@ -3,20 +3,25 @@ import { MenuList as ChakraMenuList, Portal, } from '@chakra-ui/react'; +import { memo } from 'react'; import { menuListMotionProps } from './constants'; import type { InvMenuListProps } from './types'; -export const InvMenuList = forwardRef( - (props: InvMenuListProps, ref) => { - return ( - - - - ); - } +export const InvMenuList = memo( + forwardRef( + (props: InvMenuListProps, ref) => { + return ( + + + + ); + } + ) ); + +InvMenuList.displayName = 'InvMenuList'; diff --git a/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInput.tsx b/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInput.tsx index 6f9211c37e..7e662c87f1 100644 --- a/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInput.tsx +++ b/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInput.tsx @@ -5,7 +5,7 @@ import { roundToMultiple } from 'common/util/roundDownToMultiple'; import { stopPastePropagation } from 'common/util/stopPastePropagation'; import { clamp } from 'lodash-es'; import type { FocusEventHandler } from 'react'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { InvNumberInputField } from './InvNumberInputField'; import { InvNumberInputStepper } from './InvNumberInputStepper'; @@ -13,109 +13,121 @@ import type { InvNumberInputProps } from './types'; const isValidCharacter = (char: string) => /^[0-9\-.]$/i.test(char); -export const InvNumberInput = forwardRef< - InvNumberInputProps, - typeof ChakraNumberInput ->((props: InvNumberInputProps, ref) => { - const { - value, - min = 0, - max, - step: _step = 1, - fineStep: _fineStep, - onChange: _onChange, - numberInputFieldProps, - ...rest - } = props; +export const InvNumberInput = memo( + forwardRef( + (props: InvNumberInputProps, ref) => { + const { + value, + min = 0, + max, + step: _step = 1, + fineStep: _fineStep, + onChange: _onChange, + numberInputFieldProps, + ...rest + } = props; - const [valueAsString, setValueAsString] = useState(String(value)); - const [valueAsNumber, setValueAsNumber] = useState(value); - const modifiers = useStore($modifiers); - const step = useMemo( - () => (modifiers.shift ? _fineStep ?? _step : _step), - [modifiers.shift, _fineStep, _step] - ); - const isInteger = useMemo( - () => Number.isInteger(_step) && Number.isInteger(_fineStep ?? 1), - [_step, _fineStep] - ); + const [valueAsString, setValueAsString] = useState(String(value)); + const [valueAsNumber, setValueAsNumber] = useState(value); + const modifiers = useStore($modifiers); + const step = useMemo( + () => (modifiers.shift ? _fineStep ?? _step : _step), + [modifiers.shift, _fineStep, _step] + ); + const isInteger = useMemo( + () => Number.isInteger(_step) && Number.isInteger(_fineStep ?? 1), + [_step, _fineStep] + ); - const inputMode = useMemo( - () => (isInteger ? 'numeric' : 'decimal'), - [isInteger] - ); + const inputMode = useMemo( + () => (isInteger ? 'numeric' : 'decimal'), + [isInteger] + ); - const precision = useMemo(() => (isInteger ? 0 : 3), [isInteger]); + const precision = useMemo(() => (isInteger ? 0 : 3), [isInteger]); - const onChange = useCallback( - (valueAsString: string, valueAsNumber: number) => { - setValueAsString(valueAsString); - if (isNaN(valueAsNumber)) { - return; - } - setValueAsNumber(valueAsNumber); - _onChange(valueAsNumber); - }, - [_onChange] - ); + const onChange = useCallback( + (valueAsString: string, valueAsNumber: number) => { + setValueAsString(valueAsString); + if (isNaN(valueAsNumber)) { + return; + } + setValueAsNumber(valueAsNumber); + _onChange(valueAsNumber); + }, + [_onChange] + ); - // This appears to be unnecessary? Cannot figure out what it did but leaving it here in case - // it was important. - // const onClickStepper = useCallback( - // () => _onChange(Number(valueAsString)), - // [_onChange, valueAsString] - // ); + // This appears to be unnecessary? Cannot figure out what it did but leaving it here in case + // it was important. + // const onClickStepper = useCallback( + // () => _onChange(Number(valueAsString)), + // [_onChange, valueAsString] + // ); - const onBlur: FocusEventHandler = useCallback( - (e) => { - console.log('blur!'); - if (!e.target.value) { - // If the input is empty, we set it to the minimum value - onChange(String(min), min); - } else { - // Otherwise, we round the value to the nearest multiple if integer, else 3 decimals - const roundedValue = isInteger - ? roundToMultiple(valueAsNumber, _fineStep ?? _step) - : Number(valueAsNumber.toFixed(precision)); - // Clamp to min/max - const clampedValue = clamp(roundedValue, min, max); - onChange(String(clampedValue), clampedValue); - } - }, - [_fineStep, _step, isInteger, max, min, onChange, precision, valueAsNumber] - ); + const onBlur: FocusEventHandler = useCallback( + (e) => { + console.log('blur!'); + if (!e.target.value) { + // If the input is empty, we set it to the minimum value + onChange(String(min), min); + } else { + // Otherwise, we round the value to the nearest multiple if integer, else 3 decimals + const roundedValue = isInteger + ? roundToMultiple(valueAsNumber, _fineStep ?? _step) + : Number(valueAsNumber.toFixed(precision)); + // Clamp to min/max + const clampedValue = clamp(roundedValue, min, max); + onChange(String(clampedValue), clampedValue); + } + }, + [ + _fineStep, + _step, + isInteger, + max, + min, + onChange, + precision, + valueAsNumber, + ] + ); - /** - * When `value` changes (e.g. from a diff source than this component), we need - * to update the internal `valueAsString`, but only if the actual value is different - * from the current value. - */ - useEffect(() => { - if (value !== valueAsNumber) { - setValueAsString(String(value)); - setValueAsNumber(value); + /** + * When `value` changes (e.g. from a diff source than this component), we need + * to update the internal `valueAsString`, but only if the actual value is different + * from the current value. + */ + useEffect(() => { + if (value !== valueAsNumber) { + setValueAsString(String(value)); + setValueAsNumber(value); + } + }, [value, valueAsNumber]); + + return ( + + + + + ); } - }, [value, valueAsNumber]); + ) +); - return ( - - - - - ); -}); +InvNumberInput.displayName = 'InvNumberInput'; diff --git a/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInputField.tsx b/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInputField.tsx index 018cb00dd9..bd453f5d3e 100644 --- a/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInputField.tsx +++ b/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInputField.tsx @@ -1,11 +1,11 @@ import { NumberInputField as ChakraNumberInputField } from '@chakra-ui/react'; import { useGlobalModifiersSetters } from 'common/hooks/useGlobalModifiers'; import type { KeyboardEventHandler } from 'react'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import type { InvNumberInputFieldProps } from './types'; -export const InvNumberInputField = (props: InvNumberInputFieldProps) => { +export const InvNumberInputField = memo((props: InvNumberInputFieldProps) => { const { onKeyUp, onKeyDown, children, ...rest } = props; const { setShift } = useGlobalModifiersSetters(); @@ -29,4 +29,6 @@ export const InvNumberInputField = (props: InvNumberInputFieldProps) => { {children} ); -}; +}); + +InvNumberInputField.displayName = 'InvNumberInputField'; diff --git a/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInputStepper.tsx b/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInputStepper.tsx index 697c4cc946..99b1944cb2 100644 --- a/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInputStepper.tsx +++ b/invokeai/frontend/web/src/common/components/InvNumberInput/InvNumberInputStepper.tsx @@ -5,22 +5,26 @@ import { NumberIncrementStepper as ChakraNumberIncrementStepper, NumberInputStepper as ChakraNumberInputStepper, } from '@chakra-ui/react'; +import { memo } from 'react'; import { FaMinus, FaPlus } from 'react-icons/fa6'; import type { InvNumberInputStepperProps } from './types'; -export const InvNumberInputStepper = forwardRef< - InvNumberInputStepperProps, - typeof ChakraNumberInputStepper ->((props: InvNumberInputStepperProps, ref) => { - return ( - - - - - - - - - ); -}); +export const InvNumberInputStepper = memo( + forwardRef( + (props: InvNumberInputStepperProps, ref) => { + return ( + + + + + + + + + ); + } + ) +); + +InvNumberInputStepper.displayName = 'InvNumberInputStepper'; diff --git a/invokeai/frontend/web/src/common/components/InvRangeSlider/InvRangeSlider.tsx b/invokeai/frontend/web/src/common/components/InvRangeSlider/InvRangeSlider.tsx index b86a62d5bf..102557e797 100644 --- a/invokeai/frontend/web/src/common/components/InvRangeSlider/InvRangeSlider.tsx +++ b/invokeai/frontend/web/src/common/components/InvRangeSlider/InvRangeSlider.tsx @@ -10,100 +10,113 @@ import type { InvFormattedMark } from 'common/components/InvSlider/types'; import { InvTooltip } from 'common/components/InvTooltip/InvTooltip'; import { $modifiers } from 'common/hooks/useGlobalModifiers'; import { AnimatePresence } from 'framer-motion'; -import { useCallback, useMemo, useState } from 'react'; +import { memo, useCallback, useMemo, useState } from 'react'; import { InvRangeSliderMark } from './InvRangeSliderMark'; import type { InvRangeSliderProps } from './types'; -export const InvRangeSlider = (props: InvRangeSliderProps) => { - const { - value, - min, - max, - step: _step = 1, - fineStep: _fineStep, - onChange, - onReset, - formatValue = (v: number) => v.toString(), - marks: _marks, - withThumbTooltip: withTooltip = false, - ...sliderProps - } = props; - const [isMouseOverSlider, setIsMouseOverSlider] = useState(false); - const [isChanging, setIsChanging] = useState(false); +export const InvRangeSlider = memo( + forwardRef( + (props: InvRangeSliderProps, ref) => { + const { + value, + min, + max, + step: _step = 1, + fineStep: _fineStep, + onChange, + onReset, + formatValue = (v: number) => v.toString(), + marks: _marks, + withThumbTooltip: withTooltip = false, + ...sliderProps + } = props; + const [isMouseOverSlider, setIsMouseOverSlider] = useState(false); + const [isChanging, setIsChanging] = useState(false); - const modifiers = useStore($modifiers); - const step = useMemo( - () => (modifiers.shift ? _fineStep ?? _step : _step), - [modifiers.shift, _fineStep, _step] - ); - const controlProps = useFormControl({}); + const modifiers = useStore($modifiers); + const step = useMemo( + () => (modifiers.shift ? _fineStep ?? _step : _step), + [modifiers.shift, _fineStep, _step] + ); + const controlProps = useFormControl({}); - const labels = useMemo( - () => value.map(formatValue), - [formatValue, value] - ); + const labels = useMemo( + () => value.map(formatValue), + [formatValue, value] + ); - const onMouseEnter = useCallback(() => setIsMouseOverSlider(true), []); - const onMouseLeave = useCallback(() => setIsMouseOverSlider(false), []); - const onChangeStart = useCallback(() => setIsChanging(true), []); - const onChangeEnd = useCallback(() => setIsChanging(false), []); + const onMouseEnter = useCallback(() => setIsMouseOverSlider(true), []); + const onMouseLeave = useCallback(() => setIsMouseOverSlider(false), []); + const onChangeStart = useCallback(() => setIsChanging(true), []); + const onChangeEnd = useCallback(() => setIsChanging(false), []); - const marks = useMemo(() => { - if (_marks === true) { - return [min, max].map((m) => ({ value: m, label: formatValue(m) })); - } - if (_marks) { - return _marks?.map((m) => ({ value: m, label: formatValue(m) })); - } - return []; - }, [_marks, formatValue, max, min]); + const marks = useMemo(() => { + if (_marks === true) { + return [min, max].map((m) => ({ value: m, label: formatValue(m) })); + } + if (_marks) { + return _marks?.map((m) => ({ value: m, label: formatValue(m) })); + } + return []; + }, [_marks, formatValue, max, min]); - return ( - - - {marks?.length && - (isMouseOverSlider || isChanging) && - marks.map((m, i) => ( - + + {marks?.length && + (isMouseOverSlider || isChanging) && + marks.map((m, i) => ( + + ))} + + + + + + + + - ))} - - - - - - - - - - - - - - ); -}; + + + + + + ); + } + ) +); diff --git a/invokeai/frontend/web/src/common/components/InvRangeSlider/InvRangeSliderMark.tsx b/invokeai/frontend/web/src/common/components/InvRangeSlider/InvRangeSliderMark.tsx index 77e1209f74..ddb872ec30 100644 --- a/invokeai/frontend/web/src/common/components/InvRangeSlider/InvRangeSliderMark.tsx +++ b/invokeai/frontend/web/src/common/components/InvRangeSlider/InvRangeSliderMark.tsx @@ -2,55 +2,55 @@ import { RangeSliderMark as ChakraRangeSliderMark } from '@chakra-ui/react'; import type { InvRangeSliderMarkProps } from 'common/components/InvRangeSlider/types'; import { sliderMarkAnimationConstants } from 'common/components/InvSlider/InvSliderMark'; import { motion } from 'framer-motion'; +import { memo } from 'react'; + +export const InvRangeSliderMark = memo( + ({ value, label, index, total }: InvRangeSliderMarkProps) => { + if (index === 0) { + return ( + + {label} + + ); + } + + if (index === total - 1) { + return ( + + {label} + + ); + } -export const InvRangeSliderMark = ({ - value, - label, - index, - total, -}: InvRangeSliderMarkProps) => { - if (index === 0) { return ( {label} ); } +); - if (index === total - 1) { - return ( - - {label} - - ); - } - - return ( - - {label} - - ); -}; +InvRangeSliderMark.displayName = 'InvRangeSliderMark'; diff --git a/invokeai/frontend/web/src/common/components/InvSelect/CustomMenuList.tsx b/invokeai/frontend/web/src/common/components/InvSelect/CustomMenuList.tsx index 4d8bd37697..f944447605 100644 --- a/invokeai/frontend/web/src/common/components/InvSelect/CustomMenuList.tsx +++ b/invokeai/frontend/web/src/common/components/InvSelect/CustomMenuList.tsx @@ -6,7 +6,7 @@ import { cloneDeep, merge } from 'lodash-es'; import type { UseOverlayScrollbarsParams } from 'overlayscrollbars-react'; import { useOverlayScrollbars } from 'overlayscrollbars-react'; import type { PropsWithChildren } from 'react'; -import { useEffect, useRef, useState } from 'react'; +import { memo, useEffect, useRef, useState } from 'react'; import type { InvSelectOption } from './types'; @@ -25,59 +25,61 @@ const osParams = merge( overlayScrollbarsParamsOverrides ); -const Scrollable = ( - props: PropsWithChildren<{ viewport: HTMLDivElement | null }> -) => { - const { children, viewport } = props; +const Scrollable = memo( + (props: PropsWithChildren<{ viewport: HTMLDivElement | null }>) => { + const { children, viewport } = props; - const targetRef = useRef(null); - const [initialize, getInstance] = useOverlayScrollbars(osParams); + const targetRef = useRef(null); + const [initialize, getInstance] = useOverlayScrollbars(osParams); - useEffect(() => { - if (targetRef.current && viewport) { - initialize({ - target: targetRef.current, - elements: { - viewport, - }, - }); - } - return () => getInstance()?.destroy(); - }, [viewport, initialize, getInstance]); + useEffect(() => { + if (targetRef.current && viewport) { + initialize({ + target: targetRef.current, + elements: { + viewport, + }, + }); + } + return () => getInstance()?.destroy(); + }, [viewport, initialize, getInstance]); - return ( - - {children} - - ); -}; - -export const CustomMenuList = ({ - children, - innerRef, - ...other -}: CustomMenuListProps) => { - const [viewport, setViewport] = useState(null); - - useEffect(() => { - if (!innerRef || !(innerRef instanceof Function)) { - return; - } - innerRef(viewport); - }, [innerRef, viewport]); - - return ( - - + return ( + {children} - - - ); -}; + + ); + } +); + +Scrollable.displayName = 'Scrollable'; + +export const CustomMenuList = memo( + ({ children, innerRef, ...other }: CustomMenuListProps) => { + const [viewport, setViewport] = useState(null); + + useEffect(() => { + if (!innerRef || !(innerRef instanceof Function)) { + return; + } + innerRef(viewport); + }, [innerRef, viewport]); + + return ( + + + {children} + + + ); + } +); + +CustomMenuList.displayName = 'CustomMenuList'; diff --git a/invokeai/frontend/web/src/common/components/InvSelect/CustomOption.tsx b/invokeai/frontend/web/src/common/components/InvSelect/CustomOption.tsx index 36c20c0848..e0372a4763 100644 --- a/invokeai/frontend/web/src/common/components/InvSelect/CustomOption.tsx +++ b/invokeai/frontend/web/src/common/components/InvSelect/CustomOption.tsx @@ -3,6 +3,7 @@ import type { GroupBase, OptionProps } from 'chakra-react-select'; import { chakraComponents } from 'chakra-react-select'; import { InvText } from 'common/components/InvText/wrapper'; import { InvTooltip } from 'common/components/InvTooltip/InvTooltip'; +import { memo } from 'react'; import type { InvSelectOption } from './types'; @@ -12,58 +13,62 @@ type CustomOptionProps = OptionProps< GroupBase >; -export const CustomOption = ({ children, ...props }: CustomOptionProps) => { - // On large lists, perf really takes a hit :/ - // This improves it drastically and doesn't seem to break anything... - delete props.innerProps.onMouseMove; - delete props.innerProps.onMouseOver; +export const CustomOption = memo( + ({ children, ...props }: CustomOptionProps) => { + // On large lists, perf really takes a hit :/ + // This improves it drastically and doesn't seem to break anything... + delete props.innerProps.onMouseMove; + delete props.innerProps.onMouseOver; + + if (props.data.icon) { + return ( + + + + + {props.data.icon} + + + {children} + {props.data.description && ( + + {props.data.description} + + )} + + + + + ); + } - if (props.data.icon) { return ( - - - {props.data.icon} - - - {children} - {props.data.description && ( - - {props.data.description} - - )} - + + {children} + {props.data.description && ( + + {props.data.description} + + )} ); } +); - return ( - - - - {children} - {props.data.description && ( - - {props.data.description} - - )} - - - - ); -}; +CustomOption.displayName = 'CustomOption'; diff --git a/invokeai/frontend/web/src/common/components/InvSelect/InvSelectFallback.tsx b/invokeai/frontend/web/src/common/components/InvSelect/InvSelectFallback.tsx index d9f9407e0e..fe95399653 100644 --- a/invokeai/frontend/web/src/common/components/InvSelect/InvSelectFallback.tsx +++ b/invokeai/frontend/web/src/common/components/InvSelect/InvSelectFallback.tsx @@ -1,12 +1,15 @@ import { Flex } from '@chakra-ui/react'; import { InvText } from 'common/components/InvText/wrapper'; +import { memo } from 'react'; import type { InvSelectFallbackProps } from './types'; -export const InvSelectFallback = (props: InvSelectFallbackProps) => ( +export const InvSelectFallback = memo((props: InvSelectFallbackProps) => ( {props.label} -); +)); + +InvSelectFallback.displayName = 'InvSelectFallback'; diff --git a/invokeai/frontend/web/src/common/components/InvSingleAccordion/InvSingleAccordion.tsx b/invokeai/frontend/web/src/common/components/InvSingleAccordion/InvSingleAccordion.tsx index c115e892fe..ca39561362 100644 --- a/invokeai/frontend/web/src/common/components/InvSingleAccordion/InvSingleAccordion.tsx +++ b/invokeai/frontend/web/src/common/components/InvSingleAccordion/InvSingleAccordion.tsx @@ -4,10 +4,11 @@ import { InvAccordionItem, InvAccordionPanel, } from 'common/components/InvAccordion/wrapper'; +import { memo } from 'react'; import type { InvSingleAccordionProps } from './types'; -export const InvSingleAccordion = (props: InvSingleAccordionProps) => { +export const InvSingleAccordion = memo((props: InvSingleAccordionProps) => { return ( { ); -}; +}); + +InvSingleAccordion.displayName = 'InvSingleAccordion'; diff --git a/invokeai/frontend/web/src/common/components/InvSlider/InvSlider.tsx b/invokeai/frontend/web/src/common/components/InvSlider/InvSlider.tsx index 5e518fb9c4..4fbbe8bf35 100644 --- a/invokeai/frontend/web/src/common/components/InvSlider/InvSlider.tsx +++ b/invokeai/frontend/web/src/common/components/InvSlider/InvSlider.tsx @@ -10,12 +10,12 @@ import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput' import { InvTooltip } from 'common/components/InvTooltip/InvTooltip'; import { $modifiers } from 'common/hooks/useGlobalModifiers'; import { AnimatePresence } from 'framer-motion'; -import { useCallback, useMemo, useState } from 'react'; +import { memo, useCallback, useMemo, useState } from 'react'; import { InvSliderMark } from './InvSliderMark'; import type { InvFormattedMark, InvSliderProps } from './types'; -export const InvSlider = (props: InvSliderProps) => { +export const InvSlider = memo((props: InvSliderProps) => { const { value, min, @@ -112,4 +112,6 @@ export const InvSlider = (props: InvSliderProps) => { )} ); -}; +}); + +InvSlider.displayName = 'InvSlider'; diff --git a/invokeai/frontend/web/src/common/components/InvSlider/InvSliderMark.tsx b/invokeai/frontend/web/src/common/components/InvSlider/InvSliderMark.tsx index c56bf91a2b..8222bc2057 100644 --- a/invokeai/frontend/web/src/common/components/InvSlider/InvSliderMark.tsx +++ b/invokeai/frontend/web/src/common/components/InvSlider/InvSliderMark.tsx @@ -3,6 +3,7 @@ import type { SystemStyleObject } from '@chakra-ui/styled-system'; import type { InvSliderMarkProps } from 'common/components/InvSlider/types'; import type { MotionProps } from 'framer-motion'; import { motion } from 'framer-motion'; +import { memo } from 'react'; const initialFirstLast: MotionProps['initial'] = { opacity: 0, y: 10 }; const initialOther = { ...initialFirstLast, x: '-50%' }; @@ -41,54 +42,53 @@ export const sliderMarkAnimationConstants = { lastMarkStyle, }; -export const InvSliderMark = ({ - value, - label, - index, - total, -}: InvSliderMarkProps) => { - if (index === 0) { +export const InvSliderMark = memo( + ({ value, label, index, total }: InvSliderMarkProps) => { + if (index === 0) { + return ( + + {label} + + ); + } + + if (index === total - 1) { + return ( + + {label} + + ); + } + return ( {label} ); } +); - if (index === total - 1) { - return ( - - {label} - - ); - } - - return ( - - {label} - - ); -}; +InvSliderMark.displayName = 'InvSliderMark'; diff --git a/invokeai/frontend/web/src/common/components/InvTabs/InvTab.tsx b/invokeai/frontend/web/src/common/components/InvTabs/InvTab.tsx index d2677ffe75..eb6cbe5533 100644 --- a/invokeai/frontend/web/src/common/components/InvTabs/InvTab.tsx +++ b/invokeai/frontend/web/src/common/components/InvTabs/InvTab.tsx @@ -2,9 +2,10 @@ import { Spacer } from '@chakra-ui/layout'; import { forwardRef, Tab as ChakraTab } from '@chakra-ui/react'; import { InvBadge } from 'common/components/InvBadge/wrapper'; import type { InvTabProps } from 'common/components/InvTabs/types'; +import { memo } from 'react'; -export const InvTab = forwardRef( - (props: InvTabProps, ref) => { +export const InvTab = memo( + forwardRef((props: InvTabProps, ref) => { const { children, badges, ...rest } = props; return ( @@ -17,5 +18,7 @@ export const InvTab = forwardRef( ))} ); - } + }) ); + +InvTab.displayName = 'InvTab'; diff --git a/invokeai/frontend/web/src/common/components/InvTextarea/InvTextarea.tsx b/invokeai/frontend/web/src/common/components/InvTextarea/InvTextarea.tsx index d3802cdecd..050c5aa515 100644 --- a/invokeai/frontend/web/src/common/components/InvTextarea/InvTextarea.tsx +++ b/invokeai/frontend/web/src/common/components/InvTextarea/InvTextarea.tsx @@ -2,28 +2,32 @@ import { forwardRef, Textarea as ChakraTextarea } from '@chakra-ui/react'; import { useGlobalModifiersSetters } from 'common/hooks/useGlobalModifiers'; import { stopPastePropagation } from 'common/util/stopPastePropagation'; import type { KeyboardEvent } from 'react'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import type { InvTextareaProps } from './types'; -export const InvTextarea = forwardRef( - (props: InvTextareaProps, ref) => { - const { ...rest } = props; - const { setShift } = useGlobalModifiersSetters(); - const onKeyUpDown = useCallback( - (e: KeyboardEvent) => { - setShift(e.shiftKey); - }, - [setShift] - ); - return ( - - ); - } +export const InvTextarea = memo( + forwardRef( + (props: InvTextareaProps, ref) => { + const { ...rest } = props; + const { setShift } = useGlobalModifiersSetters(); + const onKeyUpDown = useCallback( + (e: KeyboardEvent) => { + setShift(e.shiftKey); + }, + [setShift] + ); + return ( + + ); + } + ) ); + +InvTextarea.displayName = 'InvTextarea'; diff --git a/invokeai/frontend/web/src/common/components/InvTooltip/InvTooltip.tsx b/invokeai/frontend/web/src/common/components/InvTooltip/InvTooltip.tsx index 3887f4c360..226681e4b0 100644 --- a/invokeai/frontend/web/src/common/components/InvTooltip/InvTooltip.tsx +++ b/invokeai/frontend/web/src/common/components/InvTooltip/InvTooltip.tsx @@ -1,19 +1,24 @@ import { forwardRef, Tooltip as ChakraTooltip } from '@chakra-ui/react'; +import { memo } from 'react'; import type { InvTooltipProps } from './types'; -export const InvTooltip = forwardRef( - (props: InvTooltipProps, ref) => { - const { children, hasArrow = true, placement = 'top', ...rest } = props; - return ( - - {children} - - ); - } +export const InvTooltip = memo( + forwardRef( + (props: InvTooltipProps, ref) => { + const { children, hasArrow = true, placement = 'top', ...rest } = props; + return ( + + {children} + + ); + } + ) ); + +InvTooltip.displayName = 'InvTooltip'; diff --git a/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageButton.tsx b/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageButton.tsx index 64858b2d2d..8660e140f4 100644 --- a/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageButton.tsx +++ b/invokeai/frontend/web/src/features/deleteImageModal/components/DeleteImageButton.tsx @@ -1,6 +1,7 @@ import { useAppSelector } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import type { InvIconButtonProps } from 'common/components/InvIconButton/types'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { FaTrash } from 'react-icons/fa'; @@ -8,7 +9,7 @@ type DeleteImageButtonProps = Omit & { onClick: () => void; }; -export const DeleteImageButton = (props: DeleteImageButtonProps) => { +export const DeleteImageButton = memo((props: DeleteImageButtonProps) => { const { onClick, isDisabled } = props; const { t } = useTranslation(); const isConnected = useAppSelector((state) => state.system.isConnected); @@ -23,4 +24,6 @@ export const DeleteImageButton = (props: DeleteImageButtonProps) => { colorScheme="error" /> ); -}; +}); + +DeleteImageButton.displayName = 'DeleteImageButton'; diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx index 016e693072..68869f0898 100644 --- a/invokeai/frontend/web/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx @@ -8,13 +8,14 @@ import { InvModalOverlay, } from 'common/components/InvModal/wrapper'; import { useDynamicPromptsModal } from 'features/dynamicPrompts/hooks/useDynamicPromptsModal'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import ParamDynamicPromptsMaxPrompts from './ParamDynamicPromptsMaxPrompts'; import ParamDynamicPromptsPreview from './ParamDynamicPromptsPreview'; import ParamDynamicPromptsSeedBehaviour from './ParamDynamicPromptsSeedBehaviour'; -export const DynamicPromptsModal = () => { +export const DynamicPromptsModal = memo(() => { const { t } = useTranslation(); const { isOpen, onClose } = useDynamicPromptsModal(); @@ -41,4 +42,6 @@ export const DynamicPromptsModal = () => { ); -}; +}); + +DynamicPromptsModal.displayName = 'DynamicPromptsModal'; diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx index 1cd97fbae6..55fa1068a5 100644 --- a/invokeai/frontend/web/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx +++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton.tsx @@ -1,10 +1,11 @@ import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import { InvTooltip } from 'common/components/InvTooltip/InvTooltip'; import { useDynamicPromptsModal } from 'features/dynamicPrompts/hooks/useDynamicPromptsModal'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { BsBracesAsterisk } from 'react-icons/bs'; -export const ShowDynamicPromptsPreviewButton = () => { +export const ShowDynamicPromptsPreviewButton = memo(() => { const { t } = useTranslation(); const { isOpen, onOpen } = useDynamicPromptsModal(); return ( @@ -19,4 +20,6 @@ export const ShowDynamicPromptsPreviewButton = () => { /> ); -}; +}); + +ShowDynamicPromptsPreviewButton.displayName = 'ShowDynamicPromptsPreviewButton'; diff --git a/invokeai/frontend/web/src/features/embedding/AddEmbeddingButton.tsx b/invokeai/frontend/web/src/features/embedding/AddEmbeddingButton.tsx index 0f8c0c9cda..ab6e443954 100644 --- a/invokeai/frontend/web/src/features/embedding/AddEmbeddingButton.tsx +++ b/invokeai/frontend/web/src/features/embedding/AddEmbeddingButton.tsx @@ -1,5 +1,6 @@ import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import { InvTooltip } from 'common/components/InvTooltip/InvTooltip'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { FaCode } from 'react-icons/fa'; @@ -8,7 +9,7 @@ type Props = { onOpen: () => void; }; -export const AddEmbeddingButton = (props: Props) => { +export const AddEmbeddingButton = memo((props: Props) => { const { onOpen, isOpen } = props; const { t } = useTranslation(); return ( @@ -22,4 +23,6 @@ export const AddEmbeddingButton = (props: Props) => { /> ); -}; +}); + +AddEmbeddingButton.displayName = 'AddEmbeddingButton'; diff --git a/invokeai/frontend/web/src/features/embedding/EmbeddingPopover.tsx b/invokeai/frontend/web/src/features/embedding/EmbeddingPopover.tsx index d49de1db0c..c95a1e9b23 100644 --- a/invokeai/frontend/web/src/features/embedding/EmbeddingPopover.tsx +++ b/invokeai/frontend/web/src/features/embedding/EmbeddingPopover.tsx @@ -6,9 +6,10 @@ import { } from 'common/components/InvPopover/wrapper'; import { EmbeddingSelect } from 'features/embedding/EmbeddingSelect'; import type { EmbeddingPopoverProps } from 'features/embedding/types'; +import { memo } from 'react'; import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants'; -export const EmbeddingPopover = (props: EmbeddingPopoverProps) => { +export const EmbeddingPopover = memo((props: EmbeddingPopoverProps) => { const { onSelect, isOpen, @@ -43,4 +44,6 @@ export const EmbeddingPopover = (props: EmbeddingPopoverProps) => { ); -}; +}); + +EmbeddingPopover.displayName = 'EmbeddingPopover'; diff --git a/invokeai/frontend/web/src/features/embedding/EmbeddingSelect.tsx b/invokeai/frontend/web/src/features/embedding/EmbeddingSelect.tsx index 2a4d3fb38f..625fd9fc81 100644 --- a/invokeai/frontend/web/src/features/embedding/EmbeddingSelect.tsx +++ b/invokeai/frontend/web/src/features/embedding/EmbeddingSelect.tsx @@ -6,74 +6,75 @@ import { InvSelectFallback } from 'common/components/InvSelect/InvSelectFallback import { useGroupedModelInvSelect } from 'common/components/InvSelect/useGroupedModelInvSelect'; import type { EmbeddingSelectProps } from 'features/embedding/types'; import { t } from 'i18next'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import type { TextualInversionModelConfigEntity } from 'services/api/endpoints/models'; import { useGetTextualInversionModelsQuery } from 'services/api/endpoints/models'; const noOptionsMessage = () => t('embedding.noMatchingEmbedding'); -export const EmbeddingSelect = ({ - onSelect, - onClose, -}: EmbeddingSelectProps) => { - const { t } = useTranslation(); +export const EmbeddingSelect = memo( + ({ onSelect, onClose }: EmbeddingSelectProps) => { + const { t } = useTranslation(); - const currentBaseModel = useAppSelector( - (state) => state.generation.model?.base_model - ); + const currentBaseModel = useAppSelector( + (state) => state.generation.model?.base_model + ); - const getIsDisabled = ( - embedding: TextualInversionModelConfigEntity - ): boolean => { - const isCompatible = currentBaseModel === embedding.base_model; - const hasMainModel = Boolean(currentBaseModel); - return !hasMainModel || !isCompatible; - }; - const { data, isLoading } = useGetTextualInversionModelsQuery(); + const getIsDisabled = ( + embedding: TextualInversionModelConfigEntity + ): boolean => { + const isCompatible = currentBaseModel === embedding.base_model; + const hasMainModel = Boolean(currentBaseModel); + return !hasMainModel || !isCompatible; + }; + const { data, isLoading } = useGetTextualInversionModelsQuery(); - const _onChange = useCallback( - (embedding: TextualInversionModelConfigEntity | null) => { - if (!embedding) { - return; - } - onSelect(embedding.model_name); - }, - [onSelect] - ); + const _onChange = useCallback( + (embedding: TextualInversionModelConfigEntity | null) => { + if (!embedding) { + return; + } + onSelect(embedding.model_name); + }, + [onSelect] + ); - const { options, onChange } = useGroupedModelInvSelect({ - modelEntities: data, - getIsDisabled, - onChange: _onChange, - }); + const { options, onChange } = useGroupedModelInvSelect({ + modelEntities: data, + getIsDisabled, + onChange: _onChange, + }); - if (isLoading) { - return ; + if (isLoading) { + return ; + } + + if (options.length === 0) { + return ; + } + + return ( + + + + ); } +); - if (options.length === 0) { - return ; - } - - return ( - - - - ); -}; +EmbeddingSelect.displayName = 'EmbeddingSelect'; const selectStyles: ChakraProps['sx'] = { w: 'full', diff --git a/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsButton.tsx b/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsButton.tsx index 2e61cb82b2..8bd63b40c4 100644 --- a/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsButton.tsx +++ b/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsButton.tsx @@ -1,29 +1,34 @@ import { InvButton } from 'common/components/InvButton/InvButton'; import type { InvButtonProps } from 'common/components/InvButton/types'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { FaSync } from 'react-icons/fa'; import { useSyncModels } from './useSyncModels'; -export const SyncModelsButton = (props: Omit) => { - const { t } = useTranslation(); - const { syncModels, isLoading } = useSyncModels(); - const isSyncModelEnabled = useFeatureStatus('syncModels').isFeatureEnabled; +export const SyncModelsButton = memo( + (props: Omit) => { + const { t } = useTranslation(); + const { syncModels, isLoading } = useSyncModels(); + const isSyncModelEnabled = useFeatureStatus('syncModels').isFeatureEnabled; - if (!isSyncModelEnabled) { - return null; + if (!isSyncModelEnabled) { + return null; + } + + return ( + } + minW="max-content" + {...props} + > + {t('modelManager.syncModels')} + + ); } +); - return ( - } - minW="max-content" - {...props} - > - {t('modelManager.syncModels')} - - ); -}; +SyncModelsButton.displayName = 'SyncModelsButton'; diff --git a/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsIconButton.tsx b/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsIconButton.tsx index b83ba0eabe..790484d753 100644 --- a/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsIconButton.tsx +++ b/invokeai/frontend/web/src/features/modelManager/components/SyncModels/SyncModelsIconButton.tsx @@ -1,32 +1,35 @@ import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import type { InvIconButtonProps } from 'common/components/InvIconButton/types'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { FaSync } from 'react-icons/fa'; import { useSyncModels } from './useSyncModels'; -export const SyncModelsIconButton = ( - props: Omit -) => { - const { t } = useTranslation(); - const { syncModels, isLoading } = useSyncModels(); - const isSyncModelEnabled = useFeatureStatus('syncModels').isFeatureEnabled; +export const SyncModelsIconButton = memo( + (props: Omit) => { + const { t } = useTranslation(); + const { syncModels, isLoading } = useSyncModels(); + const isSyncModelEnabled = useFeatureStatus('syncModels').isFeatureEnabled; - if (!isSyncModelEnabled) { - return null; + if (!isSyncModelEnabled) { + return null; + } + + return ( + } + tooltip={t('modelManager.syncModels')} + aria-label={t('modelManager.syncModels')} + isLoading={isLoading} + onClick={syncModels} + size="sm" + variant="ghost" + {...props} + /> + ); } +); - return ( - } - tooltip={t('modelManager.syncModels')} - aria-label={t('modelManager.syncModels')} - isLoading={isLoading} - onClick={syncModels} - size="sm" - variant="ghost" - {...props} - /> - ); -}; +SyncModelsIconButton.displayName = 'SyncModelsIconButton'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx index 2876382d39..1470746470 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx @@ -24,7 +24,7 @@ import { import { $flow } from 'features/nodes/store/reactFlowInstance'; import { bumpGlobalMenuCloseTrigger } from 'features/ui/store/uiSlice'; import type { CSSProperties, MouseEvent } from 'react'; -import { useCallback, useMemo, useRef } from 'react'; +import { memo, useCallback, useMemo, useRef } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import type { OnConnect, @@ -77,7 +77,7 @@ const selector = createMemoizedSelector(stateSelector, ({ nodes }) => { const snapGrid: [number, number] = [25, 25]; -export const Flow = () => { +export const Flow = memo(() => { const dispatch = useAppDispatch(); const nodes = useAppSelector((state) => state.nodes.nodes); const edges = useAppSelector((state) => state.nodes.edges); @@ -287,4 +287,6 @@ export const Flow = () => { ); -}; +}); + +Flow.displayName = 'Flow'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamHeight.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamHeight.tsx index 5e499eed7d..9d2e6204af 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamHeight.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamHeight.tsx @@ -5,7 +5,7 @@ import { InvControl } from 'common/components/InvControl/InvControl'; import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { heightChanged } from 'features/parameters/store/generationSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( @@ -32,7 +32,7 @@ const selector = createMemoizedSelector( } ); -export const ParamHeight = () => { +export const ParamHeight = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const { initial, height, min, max, inputMax, step, fineStep } = @@ -71,4 +71,6 @@ export const ParamHeight = () => { /> ); -}; +}); + +ParamHeight.displayName = 'ParamHeight'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx index a7731b4f20..ba82e92f3d 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx @@ -6,10 +6,10 @@ import { EmbeddingPopover } from 'features/embedding/EmbeddingPopover'; import { usePrompt } from 'features/embedding/usePrompt'; import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper'; import { setNegativePrompt } from 'features/parameters/store/generationSlice'; -import { useCallback, useRef } from 'react'; +import { memo, useCallback, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -export const ParamNegativePrompt = () => { +export const ParamNegativePrompt = memo(() => { const dispatch = useAppDispatch(); const prompt = useAppSelector((state) => state.generation.negativePrompt); const textareaRef = useRef(null); @@ -55,4 +55,6 @@ export const ParamNegativePrompt = () => { ); -}; +}); + +ParamNegativePrompt.displayName = 'ParamNegativePrompt'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx index 5d9a8b8021..3c607804d7 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx @@ -8,12 +8,12 @@ import { usePrompt } from 'features/embedding/usePrompt'; import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper'; import { setPositivePrompt } from 'features/parameters/store/generationSlice'; import { SDXLConcatButton } from 'features/sdxl/components/SDXLPrompts/SDXLConcatButton'; -import { useCallback, useRef } from 'react'; +import { memo, useCallback, useRef } from 'react'; import type { HotkeyCallback } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; -export const ParamPositivePrompt = () => { +export const ParamPositivePrompt = memo(() => { const dispatch = useAppDispatch(); const prompt = useAppSelector((state) => state.generation.positivePrompt); const baseModel = useAppSelector((state) => state.generation.model) @@ -79,4 +79,6 @@ export const ParamPositivePrompt = () => { ); -}; +}); + +ParamPositivePrompt.displayName = 'ParamPositivePrompt'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamWidth.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamWidth.tsx index 5f71c595e2..ee2da16ae8 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamWidth.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamWidth.tsx @@ -5,7 +5,7 @@ import { InvControl } from 'common/components/InvControl/InvControl'; import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput'; import { InvSlider } from 'common/components/InvSlider/InvSlider'; import { widthChanged } from 'features/parameters/store/generationSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( @@ -31,7 +31,7 @@ const selector = createMemoizedSelector( }; } ); -export const ParamWidth = () => { +export const ParamWidth = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const { initial, width, min, max, inputMax, step, fineStep } = @@ -70,4 +70,6 @@ export const ParamWidth = () => { /> ); -}; +}); + +ParamWidth.displayName = 'ParamWidth'; diff --git a/invokeai/frontend/web/src/features/parameters/components/ImageSize/AspectRatioPreviewWrapper.tsx b/invokeai/frontend/web/src/features/parameters/components/ImageSize/AspectRatioPreviewWrapper.tsx index 26e7fadb11..846e802cb1 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ImageSize/AspectRatioPreviewWrapper.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ImageSize/AspectRatioPreviewWrapper.tsx @@ -1,9 +1,12 @@ import { useAppSelector } from 'app/store/storeHooks'; import { AspectRatioPreview } from 'common/components/AspectRatioPreview/AspectRatioPreview'; +import { memo } from 'react'; -export const AspectRatioPreviewWrapper = () => { +export const AspectRatioPreviewWrapper = memo(() => { const width = useAppSelector((state) => state.generation.width); const height = useAppSelector((state) => state.generation.height); return ; -}; +}); + +AspectRatioPreviewWrapper.displayName = 'AspectRatioPreviewWrapper'; diff --git a/invokeai/frontend/web/src/features/parameters/components/ImageSize/AspectRatioSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/ImageSize/AspectRatioSelect.tsx index f256703223..e4559782f3 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ImageSize/AspectRatioSelect.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ImageSize/AspectRatioSelect.tsx @@ -7,14 +7,14 @@ import type { InvSelectOption } from 'common/components/InvSelect/types'; import { ASPECT_RATIO_OPTIONS } from 'features/parameters/components/ImageSize/constants'; import { isAspectRatioID } from 'features/parameters/components/ImageSize/types'; import { aspectRatioSelected } from 'features/parameters/store/generationSlice'; -import { useCallback, useMemo } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { LockAspectRatioButton } from './LockAspectRatioButton'; import { SetOptimalSizeButton } from './SetOptimalSizeButton'; import { SwapDimensionsButton } from './SwapDimensionsButton'; -export const AspectRatioSelect = () => { +export const AspectRatioSelect = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const aspectRatioID = useAppSelector( @@ -49,6 +49,8 @@ export const AspectRatioSelect = () => { ); -}; +}); + +AspectRatioSelect.displayName = 'AspectRatioSelect'; const selectStyles: SystemStyleObject = { minW: 48 }; diff --git a/invokeai/frontend/web/src/features/parameters/components/ImageSize/LockAspectRatioButton.tsx b/invokeai/frontend/web/src/features/parameters/components/ImageSize/LockAspectRatioButton.tsx index 43aa56407a..39c8ea46d1 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ImageSize/LockAspectRatioButton.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ImageSize/LockAspectRatioButton.tsx @@ -1,11 +1,11 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import { isLockedToggled } from 'features/parameters/store/generationSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { FaLock, FaLockOpen } from 'react-icons/fa6'; -export const LockAspectRatioButton = () => { +export const LockAspectRatioButton = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const isLocked = useAppSelector( @@ -24,4 +24,6 @@ export const LockAspectRatioButton = () => { icon={isLocked ? : } /> ); -}; +}); + +LockAspectRatioButton.displayName = 'LockAspectRatioButton'; diff --git a/invokeai/frontend/web/src/features/parameters/components/ImageSize/SetOptimalSizeButton.tsx b/invokeai/frontend/web/src/features/parameters/components/ImageSize/SetOptimalSizeButton.tsx index 974f333221..dc3defcc9f 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ImageSize/SetOptimalSizeButton.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ImageSize/SetOptimalSizeButton.tsx @@ -1,11 +1,11 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import { sizeReset } from 'features/parameters/store/generationSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { IoSparkles } from 'react-icons/io5'; -export const SetOptimalSizeButton = () => { +export const SetOptimalSizeButton = memo(() => { const { t } = useTranslation(); const optimalDimension = useAppSelector((state) => state.generation.model?.base_model === 'sdxl' ? 1024 : 512 @@ -24,4 +24,6 @@ export const SetOptimalSizeButton = () => { icon={} /> ); -}; +}); + +SetOptimalSizeButton.displayName = 'SetOptimalSizeButton'; diff --git a/invokeai/frontend/web/src/features/parameters/components/ImageSize/SwapDimensionsButton.tsx b/invokeai/frontend/web/src/features/parameters/components/ImageSize/SwapDimensionsButton.tsx index 6bab43824b..ed5e51e5e6 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ImageSize/SwapDimensionsButton.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ImageSize/SwapDimensionsButton.tsx @@ -1,11 +1,11 @@ import { useAppDispatch } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import { dimensionsSwapped } from 'features/parameters/store/generationSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { IoSwapVertical } from 'react-icons/io5'; -export const SwapDimensionsButton = () => { +export const SwapDimensionsButton = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const onClick = useCallback(() => { @@ -20,4 +20,6 @@ export const SwapDimensionsButton = () => { icon={} /> ); -}; +}); + +SwapDimensionsButton.displayName = 'SwapDimensionsButton'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/PromptOverlayButtonWrapper.tsx b/invokeai/frontend/web/src/features/parameters/components/Prompts/PromptOverlayButtonWrapper.tsx index 0767e145aa..c8189ec1a1 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Prompts/PromptOverlayButtonWrapper.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Prompts/PromptOverlayButtonWrapper.tsx @@ -1,7 +1,7 @@ import { Flex } from '@chakra-ui/layout'; -import type { PropsWithChildren } from 'react'; +import { memo, type PropsWithChildren } from 'react'; -export const PromptOverlayButtonWrapper = (props: PropsWithChildren) => ( +export const PromptOverlayButtonWrapper = memo((props: PropsWithChildren) => ( ( > {props.children} -); +)); + +PromptOverlayButtonWrapper.displayName = 'PromptOverlayButtonWrapper'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx b/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx index 342546eb50..a7df3b7259 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx @@ -1,12 +1,15 @@ import { Flex } from '@chakra-ui/react'; import { ParamNegativePrompt } from 'features/parameters/components/Core/ParamNegativePrompt'; import { ParamPositivePrompt } from 'features/parameters/components/Core/ParamPositivePrompt'; +import { memo } from 'react'; -export const Prompts = () => { +export const Prompts = memo(() => { return ( ); -}; +}); + +Prompts.displayName = 'Prompts'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx b/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx index 587b407e10..b52408c0f7 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx @@ -3,10 +3,10 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvControl } from 'common/components/InvControl/InvControl'; import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput'; import { setSeed } from 'features/parameters/store/generationSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -export const ParamSeedNumberInput = () => { +export const ParamSeedNumberInput = memo(() => { const seed = useAppSelector((state) => state.generation.seed); const shouldRandomizeSeed = useAppSelector( (state) => state.generation.shouldRandomizeSeed @@ -34,4 +34,6 @@ export const ParamSeedNumberInput = () => { /> ); -}; +}); + +ParamSeedNumberInput.displayName = 'ParamSeedNumberInput'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedRandomize.tsx b/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedRandomize.tsx index db3517706f..b01d964820 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedRandomize.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedRandomize.tsx @@ -4,10 +4,10 @@ import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSwitch } from 'common/components/InvSwitch/wrapper'; import { setShouldRandomizeSeed } from 'features/parameters/store/generationSlice'; import type { ChangeEvent } from 'react'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -export const ParamSeedRandomize = () => { +export const ParamSeedRandomize = memo(() => { const dispatch = useAppDispatch(); const { t } = useTranslation(); @@ -29,4 +29,6 @@ export const ParamSeedRandomize = () => { /> ); -}; +}); + +ParamSeedRandomize.displayName = 'ParamSeedRandomize'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedShuffle.tsx b/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedShuffle.tsx index 7049da8578..a2c0cdb0a0 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedShuffle.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedShuffle.tsx @@ -4,11 +4,11 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvButton } from 'common/components/InvButton/InvButton'; import randomInt from 'common/util/randomInt'; import { setSeed } from 'features/parameters/store/generationSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { FaShuffle } from 'react-icons/fa6'; -export const ParamSeedShuffle = () => { +export const ParamSeedShuffle = memo(() => { const dispatch = useAppDispatch(); const shouldRandomizeSeed = useAppSelector( (state: RootState) => state.generation.shouldRandomizeSeed @@ -31,4 +31,6 @@ export const ParamSeedShuffle = () => { {t('parameters.shuffle')} ); -}; +}); + +ParamSeedShuffle.displayName = 'ParamSeedShuffle'; diff --git a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx index 585aa2c35c..1ede92e12d 100644 --- a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx @@ -6,7 +6,7 @@ import { InvSelect } from 'common/components/InvSelect/InvSelect'; import { useGroupedModelInvSelect } from 'common/components/InvSelect/useGroupedModelInvSelect'; import { vaeSelected } from 'features/parameters/store/generationSlice'; import { pick } from 'lodash-es'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import type { VaeModelConfigEntity } from 'services/api/endpoints/models'; import { useGetVaeModelsQuery } from 'services/api/endpoints/models'; @@ -60,4 +60,4 @@ const ParamVAEModelSelect = () => { ); }; -export default ParamVAEModelSelect; +export default memo(ParamVAEModelSelect); diff --git a/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx b/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx index cdbab8db78..a139e435d2 100644 --- a/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx @@ -8,7 +8,7 @@ import { InvNumberInput } from 'common/components/InvNumberInput/InvNumberInput' import type { InvNumberInputFieldProps } from 'common/components/InvNumberInput/types'; import { setIterations } from 'features/parameters/store/generationSlice'; import { useQueueBack } from 'features/queue/hooks/useQueueBack'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { IoSparkles } from 'react-icons/io5'; import { QueueButtonTooltip } from './QueueButtonTooltip'; @@ -40,7 +40,7 @@ const selector = createMemoizedSelector([stateSelector], (state) => { }; }); -export const InvokeQueueBackButton = () => { +export const InvokeQueueBackButton = memo(() => { const { queueBack, isLoading, isDisabled } = useQueueBack(); const { iterations, step, fineStep } = useAppSelector(selector); const dispatch = useAppDispatch(); @@ -89,4 +89,6 @@ export const InvokeQueueBackButton = () => { ); -}; +}); + +InvokeQueueBackButton.displayName = 'InvokeQueueBackButton'; diff --git a/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx b/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx index a0168ea7bc..efbf6dd284 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueActionsMenuButton.tsx @@ -9,12 +9,13 @@ import { useCancelCurrentQueueItem } from 'features/queue/hooks/useCancelCurrent import { usePauseProcessor } from 'features/queue/hooks/usePauseProcessor'; import { useResumeProcessor } from 'features/queue/hooks/useResumeProcessor'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { FaPause, FaPlay, FaTimes } from 'react-icons/fa'; import { FaList } from 'react-icons/fa6'; import { useGetQueueStatusQuery } from 'services/api/endpoints/queue'; -export const QueueActionsMenuButton = () => { +export const QueueActionsMenuButton = memo(() => { const { isOpen, onOpen, onClose } = useDisclosure(); const { t } = useTranslation(); const isPauseEnabled = useFeatureStatus('pauseQueue').isFeatureEnabled; @@ -100,4 +101,6 @@ export const QueueActionsMenuButton = () => { )} ); -}; +}); + +QueueActionsMenuButton.displayName = 'QueueActionsMenuButton'; diff --git a/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx b/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx index 36350b799f..5c9b840309 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx @@ -4,6 +4,7 @@ import ClearQueueButton from 'features/queue/components/ClearQueueButton'; import QueueFrontButton from 'features/queue/components/QueueFrontButton'; import ProgressBar from 'features/system/components/ProgressBar'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; +import { memo } from 'react'; import { InvokeQueueBackButton } from './InvokeQueueBackButton'; import { QueueActionsMenuButton } from './QueueActionsMenuButton'; @@ -35,7 +36,7 @@ const QueueControls = () => { ); }; -export default QueueControls; +export default memo(QueueControls); // const QueueCounts = () => { // const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx index 4e104ef409..a1491cab18 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx @@ -6,11 +6,11 @@ import { EmbeddingPopover } from 'features/embedding/EmbeddingPopover'; import { usePrompt } from 'features/embedding/usePrompt'; import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper'; import { setNegativeStylePromptSDXL } from 'features/sdxl/store/sdxlSlice'; -import { useCallback, useRef } from 'react'; +import { memo, useCallback, useRef } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; -export const ParamSDXLNegativeStylePrompt = () => { +export const ParamSDXLNegativeStylePrompt = memo(() => { const dispatch = useAppDispatch(); const prompt = useAppSelector((state) => state.sdxl.negativeStylePrompt); const textareaRef = useRef(null); @@ -65,4 +65,6 @@ export const ParamSDXLNegativeStylePrompt = () => { ); -}; +}); + +ParamSDXLNegativeStylePrompt.displayName = 'ParamSDXLNegativeStylePrompt'; diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx index 063217777a..83952dbe7c 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx @@ -6,10 +6,10 @@ import { EmbeddingPopover } from 'features/embedding/EmbeddingPopover'; import { usePrompt } from 'features/embedding/usePrompt'; import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper'; import { setPositiveStylePromptSDXL } from 'features/sdxl/store/sdxlSlice'; -import { useCallback, useRef } from 'react'; +import { memo, useCallback, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -export const ParamSDXLPositiveStylePrompt = () => { +export const ParamSDXLPositiveStylePrompt = memo(() => { const dispatch = useAppDispatch(); const prompt = useAppSelector((state) => state.sdxl.positiveStylePrompt); const textareaRef = useRef(null); @@ -55,4 +55,6 @@ export const ParamSDXLPositiveStylePrompt = () => { ); -}; +}); + +ParamSDXLPositiveStylePrompt.displayName = 'ParamSDXLPositiveStylePrompt'; diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLConcatButton.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLConcatButton.tsx index 1ffa86371f..046d372358 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLConcatButton.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLConcatButton.tsx @@ -2,11 +2,11 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { InvIconButton } from 'common/components/InvIconButton/InvIconButton'; import { InvTooltip } from 'common/components/InvTooltip/InvTooltip'; import { setShouldConcatSDXLStylePrompt } from 'features/sdxl/store/sdxlSlice'; -import { useCallback, useMemo } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { FaLink, FaUnlink } from 'react-icons/fa'; -export const SDXLConcatButton = () => { +export const SDXLConcatButton = memo(() => { const shouldConcatSDXLStylePrompt = useAppSelector( (state) => state.sdxl.shouldConcatSDXLStylePrompt ); @@ -38,4 +38,6 @@ export const SDXLConcatButton = () => { /> ); -}; +}); + +SDXLConcatButton.displayName = 'SDXLConcatButton'; diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLPrompts.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLPrompts.tsx index 27760879ec..c0b530bc69 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLPrompts.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/SDXLPrompts.tsx @@ -2,11 +2,12 @@ import { Flex } from '@chakra-ui/react'; import { useAppSelector } from 'app/store/storeHooks'; import { ParamNegativePrompt } from 'features/parameters/components/Core/ParamNegativePrompt'; import { ParamPositivePrompt } from 'features/parameters/components/Core/ParamPositivePrompt'; +import { memo } from 'react'; import { ParamSDXLNegativeStylePrompt } from './ParamSDXLNegativeStylePrompt'; import { ParamSDXLPositiveStylePrompt } from './ParamSDXLPositiveStylePrompt'; -export const SDXLPrompts = () => { +export const SDXLPrompts = memo(() => { const shouldConcatSDXLStylePrompt = useAppSelector( (state) => state.sdxl.shouldConcatSDXLStylePrompt ); @@ -18,4 +19,6 @@ export const SDXLPrompts = () => { {!shouldConcatSDXLStylePrompt && } ); -}; +}); + +SDXLPrompts.displayName = 'SDXLPrompts'; diff --git a/invokeai/frontend/web/src/features/settingsAccordions/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx index a68a9ba287..91b581174a 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/AdvancedSettingsAccordion/AdvancedSettingsAccordion.tsx @@ -8,6 +8,7 @@ import ParamSeamlessXAxis from 'features/parameters/components/Seamless/ParamSea import ParamSeamlessYAxis from 'features/parameters/components/Seamless/ParamSeamlessYAxis'; import ParamVAEModelSelect from 'features/parameters/components/VAEModel/ParamVAEModelSelect'; import ParamVAEPrecision from 'features/parameters/components/VAEModel/ParamVAEPrecision'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; const labelProps: InvLabelProps = { @@ -18,7 +19,7 @@ const labelProps2: InvLabelProps = { flexGrow: 1, }; -export const AdvancedSettingsAccordion = () => { +export const AdvancedSettingsAccordion = memo(() => { const { t } = useTranslation(); return ( @@ -41,4 +42,6 @@ export const AdvancedSettingsAccordion = () => { ); -}; +}); + +AdvancedSettingsAccordion.displayName = 'AdvancedSettingsAccordion'; diff --git a/invokeai/frontend/web/src/features/settingsAccordions/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx index c3b5735769..2abfd99c58 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx @@ -21,6 +21,7 @@ import ParamScheduler from 'features/parameters/components/Core/ParamScheduler'; import ParamSteps from 'features/parameters/components/Core/ParamSteps'; import ParamMainModelSelect from 'features/parameters/components/MainModel/ParamMainModelSelect'; import { size, truncate } from 'lodash-es'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; const labelProps: InvLabelProps = { @@ -43,7 +44,7 @@ const badgesSelector = createMemoizedSelector( } ); -export const GenerationSettingsAccordion = () => { +export const GenerationSettingsAccordion = memo(() => { const { t } = useTranslation(); const { loraTabBadges, accordionBadges } = useAppSelector(badgesSelector); @@ -86,4 +87,6 @@ export const GenerationSettingsAccordion = () => { ); -}; +}); + +GenerationSettingsAccordion.displayName = 'GenerationSettingsAccordion'; diff --git a/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSettingsAccordion.tsx index d260028ea4..8727559e92 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/ImageSettingsAccordion/ImageSettingsAccordion.tsx @@ -23,6 +23,7 @@ import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSee import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedShuffle'; import type { InvokeTabName } from 'features/ui/store/tabMap'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createMemoizedSelector( @@ -49,7 +50,7 @@ const scalingLabelProps: InvLabelProps = { w: '4.5rem', }; -export const ImageSettingsAccordion = () => { +export const ImageSettingsAccordion = memo(() => { const { t } = useTranslation(); const { badges, activeTabName } = useAppSelector(selector); @@ -95,9 +96,11 @@ export const ImageSettingsAccordion = () => { ); -}; +}); -const WidthHeight = (props: { activeTabName: InvokeTabName }) => { +ImageSettingsAccordion.displayName = 'ImageSettingsAccordion'; + +const WidthHeight = memo((props: { activeTabName: InvokeTabName }) => { if (props.activeTabName === 'unifiedCanvas') { return ( <> @@ -113,4 +116,6 @@ const WidthHeight = (props: { activeTabName: InvokeTabName }) => { ); -}; +}); + +WidthHeight.displayName = 'WidthHeight'; diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx index 202a2ae067..1e0d9178d7 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx @@ -5,10 +5,10 @@ import type { InvSelectOnChange } from 'common/components/InvSelect/types'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { languageChanged } from 'features/system/store/systemSlice'; import { isLanguage } from 'features/system/store/types'; -import { useCallback, useMemo } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -export const SettingsLanguageSelect = () => { +export const SettingsLanguageSelect = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const language = useAppSelector((state) => state.system.language); @@ -58,4 +58,6 @@ export const SettingsLanguageSelect = () => { ); -}; +}); + +SettingsLanguageSelect.displayName = 'SettingsLanguageSelect'; diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx index c39bbe5e23..c50ea0b360 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLogLevelSelect.tsx @@ -4,10 +4,10 @@ import { InvControl } from 'common/components/InvControl/InvControl'; import { InvSelect } from 'common/components/InvSelect/InvSelect'; import type { InvSelectOnChange } from 'common/components/InvSelect/types'; import { consoleLogLevelChanged } from 'features/system/store/systemSlice'; -import { useCallback, useMemo } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -export const SettingsLogLevelSelect = () => { +export const SettingsLogLevelSelect = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const consoleLogLevel = useAppSelector( @@ -43,4 +43,6 @@ export const SettingsLogLevelSelect = () => { ); -}; +}); + +SettingsLogLevelSelect.displayName = 'SettingsLogLevelSelect';