mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-12 17:55:02 -05:00
feat(ui): switch tab on drag over tab button
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
||||
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
||||
import { dropTargetForExternal } from '@atlaskit/pragmatic-drag-and-drop/external/adapter';
|
||||
import { useTimeoutCallback } from 'common/hooks/useTimeoutCallback';
|
||||
import type { RefObject } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useCallbackOnDragEnter = (cb: () => void, ref: RefObject<HTMLElement>, delay = 300) => {
|
||||
const [run, cancel] = useTimeoutCallback(cb, delay);
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
return combine(
|
||||
dropTargetForElements({
|
||||
element,
|
||||
onDragEnter: run,
|
||||
onDragLeave: cancel,
|
||||
}),
|
||||
dropTargetForExternal({
|
||||
element,
|
||||
onDragEnter: run,
|
||||
onDragLeave: cancel,
|
||||
})
|
||||
);
|
||||
}, [cancel, ref, run]);
|
||||
};
|
||||
21
invokeai/frontend/web/src/common/hooks/useTimeoutCallback.ts
Normal file
21
invokeai/frontend/web/src/common/hooks/useTimeoutCallback.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
|
||||
export const useTimeoutCallback = (callback: () => void, delay: number, onCancel?: () => void) => {
|
||||
const timeoutRef = useRef<number | null>(null);
|
||||
const cancel = useCallback(() => {
|
||||
if (timeoutRef.current !== null) {
|
||||
window.clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = null;
|
||||
onCancel?.();
|
||||
}
|
||||
}, [onCancel]);
|
||||
const callWithTimeout = useCallback(() => {
|
||||
cancel();
|
||||
timeoutRef.current = window.setTimeout(() => {
|
||||
callback();
|
||||
timeoutRef.current = null;
|
||||
}, delay);
|
||||
}, [callback, cancel, delay]);
|
||||
const api = useMemo(() => [callWithTimeout, cancel] as const, [callWithTimeout, cancel]);
|
||||
return api;
|
||||
};
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useCallbackOnDragEnter } from 'common/hooks/useCallbackOnDragEnter';
|
||||
import { selectActiveTab } from 'features/ui/store/uiSelectors';
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import type { TabName } from 'features/ui/store/uiTypes';
|
||||
import { forwardRef, memo, type ReactElement, useCallback } from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
import { memo, useCallback, useRef } from 'react';
|
||||
|
||||
const sx: SystemStyleObject = {
|
||||
'&[data-selected=true]': {
|
||||
@@ -12,32 +14,32 @@ const sx: SystemStyleObject = {
|
||||
},
|
||||
};
|
||||
|
||||
export const TabButton = memo(
|
||||
forwardRef(({ tab, icon, label }: { tab: TabName; icon: ReactElement; label: string }, ref) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const activeTabName = useAppSelector(selectActiveTab);
|
||||
const onClick = useCallback(() => {
|
||||
dispatch(setActiveTab(tab));
|
||||
}, [dispatch, tab]);
|
||||
export const TabButton = memo(({ tab, icon, label }: { tab: TabName; icon: ReactElement; label: string }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const activeTabName = useAppSelector(selectActiveTab);
|
||||
const selectTab = useCallback(() => {
|
||||
dispatch(setActiveTab(tab));
|
||||
}, [dispatch, tab]);
|
||||
useCallbackOnDragEnter(selectTab, ref, 300);
|
||||
|
||||
return (
|
||||
<Tooltip label={label} placement="end">
|
||||
<IconButton
|
||||
ref={ref}
|
||||
p={0}
|
||||
onClick={onClick}
|
||||
icon={icon}
|
||||
size="md"
|
||||
fontSize="24px"
|
||||
variant="appTab"
|
||||
data-selected={activeTabName === tab}
|
||||
aria-label={label}
|
||||
data-testid={label}
|
||||
sx={sx}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
})
|
||||
);
|
||||
return (
|
||||
<Tooltip label={label} placement="end">
|
||||
<IconButton
|
||||
p={0}
|
||||
ref={ref}
|
||||
onClick={selectTab}
|
||||
icon={icon}
|
||||
size="md"
|
||||
fontSize="24px"
|
||||
variant="appTab"
|
||||
data-selected={activeTabName === tab}
|
||||
aria-label={label}
|
||||
data-testid={label}
|
||||
sx={sx}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
||||
TabButton.displayName = 'TabButton';
|
||||
|
||||
Reference in New Issue
Block a user