mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): fix queue item count badge positioning
Previously this badge, floating over the queue menu button next to the invoke button, was rendered within the existing layout. When I initially positioned it, the app layout interfered - it would extend into an area reserved for a flex gap, which cut off the badge. As a (bad) workaround, I had shifted the whole app down a few pixels to make room for it. What I should have done is what I've done in this commit - render the badge in a portal to take it out of the layout so we don't need that extra vertical padding. Sleekified some styling a bit too.
This commit is contained in:
parent
64e60a7fde
commit
ce55a96125
@ -19,8 +19,6 @@ export const CanvasEditor = memo(() => {
|
|||||||
<Flex
|
<Flex
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
layerStyle="first"
|
|
||||||
p={2}
|
|
||||||
borderRadius="base"
|
borderRadius="base"
|
||||||
position="relative"
|
position="relative"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
|
@ -82,10 +82,11 @@ export const StageComponent = memo(({ asPreview = false }: Props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex position="relative" w="full" h="full" bg={dynamicGrid ? 'base.850' : 'base.900'}>
|
<Flex position="relative" w="full" h="full" bg={dynamicGrid ? 'base.850' : 'base.900'} borderRadius="base">
|
||||||
{!dynamicGrid && (
|
{!dynamicGrid && (
|
||||||
<Flex
|
<Flex
|
||||||
position="absolute"
|
position="absolute"
|
||||||
|
borderRadius="base"
|
||||||
bgImage={TRANSPARENCY_CHECKER_PATTERN}
|
bgImage={TRANSPARENCY_CHECKER_PATTERN}
|
||||||
top={0}
|
top={0}
|
||||||
right={0}
|
right={0}
|
||||||
@ -102,9 +103,6 @@ export const StageComponent = memo(({ asPreview = false }: Props) => {
|
|||||||
left={0}
|
left={0}
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
borderRadius="base"
|
borderRadius="base"
|
||||||
border={1}
|
|
||||||
borderStyle="solid"
|
|
||||||
borderColor="base.700"
|
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
data-testid="control-layers-canvas"
|
data-testid="control-layers-canvas"
|
||||||
/>
|
/>
|
||||||
|
@ -59,7 +59,7 @@ const GalleryPanelContent = () => {
|
|||||||
}, [boardSearchText.length, boardSearchDisclosure, boardsListPanel, dispatch]);
|
}, [boardSearchText.length, boardSearchDisclosure, boardsListPanel, dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex ref={ref} position="relative" flexDirection="column" h="full" w="full" pt={2} tabIndex={-1}>
|
<Flex ref={ref} position="relative" flexDirection="column" h="full" w="full" tabIndex={-1}>
|
||||||
<Flex alignItems="center" gap={0}>
|
<Flex alignItems="center" gap={0}>
|
||||||
<GalleryHeader />
|
<GalleryHeader />
|
||||||
<Flex alignItems="center" justifyContent="space-between" w="full">
|
<Flex alignItems="center" justifyContent="space-between" w="full">
|
||||||
|
@ -21,7 +21,7 @@ export const ImageViewer = memo(() => {
|
|||||||
<Flex
|
<Flex
|
||||||
ref={ref}
|
ref={ref}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
layerStyle="first"
|
layerStyle="body"
|
||||||
borderRadius="base"
|
borderRadius="base"
|
||||||
position="absolute"
|
position="absolute"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
@ -29,7 +29,6 @@ export const ImageViewer = memo(() => {
|
|||||||
right={0}
|
right={0}
|
||||||
bottom={0}
|
bottom={0}
|
||||||
left={0}
|
left={0}
|
||||||
p={2}
|
|
||||||
rowGap={4}
|
rowGap={4}
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
|
@ -7,16 +7,18 @@ import {
|
|||||||
MenuDivider,
|
MenuDivider,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
MenuList,
|
MenuList,
|
||||||
|
Portal,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
} from '@invoke-ai/ui-library';
|
} from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import type { Coordinate } from 'features/controlLayers/store/types';
|
||||||
import { useClearQueueConfirmationAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
|
import { useClearQueueConfirmationAlertDialog } from 'features/queue/components/ClearQueueConfirmationAlertDialog';
|
||||||
import { useClearQueue } from 'features/queue/hooks/useClearQueue';
|
import { useClearQueue } from 'features/queue/hooks/useClearQueue';
|
||||||
import { usePauseProcessor } from 'features/queue/hooks/usePauseProcessor';
|
import { usePauseProcessor } from 'features/queue/hooks/usePauseProcessor';
|
||||||
import { useResumeProcessor } from 'features/queue/hooks/useResumeProcessor';
|
import { useResumeProcessor } from 'features/queue/hooks/useResumeProcessor';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiPauseFill, PiPlayFill, PiTrashSimpleBold } from 'react-icons/pi';
|
import { PiPauseFill, PiPlayFill, PiTrashSimpleBold } from 'react-icons/pi';
|
||||||
import { RiListCheck, RiPlayList2Fill } from 'react-icons/ri';
|
import { RiListCheck, RiPlayList2Fill } from 'react-icons/ri';
|
||||||
@ -26,6 +28,8 @@ export const QueueActionsMenuButton = memo(() => {
|
|||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [badgePos, setBadgePos] = useState<Coordinate | null>(null);
|
||||||
|
const menuButtonRef = useRef<HTMLButtonElement>(null);
|
||||||
const dialogState = useClearQueueConfirmationAlertDialog();
|
const dialogState = useClearQueueConfirmationAlertDialog();
|
||||||
const isPauseEnabled = useFeatureStatus('pauseQueue');
|
const isPauseEnabled = useFeatureStatus('pauseQueue');
|
||||||
const isResumeEnabled = useFeatureStatus('resumeQueue');
|
const isResumeEnabled = useFeatureStatus('resumeQueue');
|
||||||
@ -49,10 +53,17 @@ export const QueueActionsMenuButton = memo(() => {
|
|||||||
dispatch(setActiveTab('queue'));
|
dispatch(setActiveTab('queue'));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (menuButtonRef.current) {
|
||||||
|
const { x, y } = menuButtonRef.current.getBoundingClientRect();
|
||||||
|
setBadgePos({ x: x - 10, y: y - 10 });
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box pos="relative">
|
<Box pos="relative">
|
||||||
<Menu isOpen={isOpen} onOpen={onOpen} onClose={onClose} placement="bottom-end">
|
<Menu isOpen={isOpen} onOpen={onOpen} onClose={onClose} placement="bottom-end">
|
||||||
<MenuButton as={IconButton} aria-label="Queue Actions Menu" icon={<RiListCheck />} />
|
<MenuButton ref={menuButtonRef} as={IconButton} aria-label="Queue Actions Menu" icon={<RiListCheck />} />
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
isDestructive
|
isDestructive
|
||||||
@ -89,10 +100,18 @@ export const QueueActionsMenuButton = memo(() => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
{queueSize > 0 && (
|
{queueSize > 0 && badgePos !== null && (
|
||||||
<Badge pos="absolute" insetInlineStart={-3} insetBlockStart={-1.5} colorScheme="invokeYellow" zIndex="docked">
|
<Portal>
|
||||||
{queueSize}
|
<Badge
|
||||||
</Badge>
|
pos="absolute"
|
||||||
|
insetInlineStart={badgePos.x}
|
||||||
|
insetBlockStart={badgePos.y}
|
||||||
|
colorScheme="invokeYellow"
|
||||||
|
zIndex="docked"
|
||||||
|
>
|
||||||
|
{queueSize}
|
||||||
|
</Badge>
|
||||||
|
</Portal>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -11,7 +11,7 @@ import { QueueActionsMenuButton } from './QueueActionsMenuButton';
|
|||||||
const QueueControls = () => {
|
const QueueControls = () => {
|
||||||
const isPrependEnabled = useFeatureStatus('prependQueue');
|
const isPrependEnabled = useFeatureStatus('prependQueue');
|
||||||
return (
|
return (
|
||||||
<Flex w="full" position="relative" borderRadius="base" gap={2} pt={2} flexDir="column">
|
<Flex w="full" position="relative" borderRadius="base" gap={2} flexDir="column">
|
||||||
<ButtonGroup size="lg" isAttached={false}>
|
<ButtonGroup size="lg" isAttached={false}>
|
||||||
{isPrependEnabled && <QueueFrontButton />}
|
{isPrependEnabled && <QueueFrontButton />}
|
||||||
<InvokeQueueBackButton />
|
<InvokeQueueBackButton />
|
||||||
|
@ -18,7 +18,7 @@ export const VerticalNavBar = memo(() => {
|
|||||||
const customNavComponent = useStore($customNavComponent);
|
const customNavComponent = useStore($customNavComponent);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDir="column" alignItems="center" pt={4} pb={2} gap={4}>
|
<Flex flexDir="column" alignItems="center" py={2} gap={4}>
|
||||||
<InvokeAILogoComponent />
|
<InvokeAILogoComponent />
|
||||||
<Flex gap={4} pt={6} h="full" flexDir="column">
|
<Flex gap={4} pt={6} h="full" flexDir="column">
|
||||||
<TabMountGate tab="generation">
|
<TabMountGate tab="generation">
|
||||||
|
Loading…
Reference in New Issue
Block a user