mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-14 20:25:04 -05:00
112 lines
3.5 KiB
TypeScript
112 lines
3.5 KiB
TypeScript
import type { GetOffsetFn } from '@atlaskit/pragmatic-drag-and-drop/dist/types/public-utils/element/custom-native-drag-preview/types';
|
|
import type { Input } from '@atlaskit/pragmatic-drag-and-drop/types';
|
|
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
|
import { noop } from 'es-toolkit/compat';
|
|
import type { CSSProperties } from 'react';
|
|
|
|
/**
|
|
* The size of the image drag preview in theme units.
|
|
*/
|
|
export const DND_IMAGE_DRAG_PREVIEW_SIZE = 32 satisfies SystemStyleObject['w'];
|
|
|
|
/**
|
|
* A drag preview offset function that works like the provided `preserveOffsetOnSource`, except when either the X or Y
|
|
* offset is outside the container, in which case it centers the preview in the container.
|
|
*/
|
|
export function preserveOffsetOnSourceFallbackCentered({
|
|
element,
|
|
input,
|
|
}: {
|
|
element: HTMLElement;
|
|
input: Input;
|
|
}): GetOffsetFn {
|
|
return ({ container }) => {
|
|
const sourceRect = element.getBoundingClientRect();
|
|
const containerRect = container.getBoundingClientRect();
|
|
|
|
let offsetX = input.clientX - sourceRect.x;
|
|
let offsetY = input.clientY - sourceRect.y;
|
|
|
|
if (offsetY > containerRect.height || offsetX > containerRect.width) {
|
|
offsetX = containerRect.width / 2;
|
|
offsetY = containerRect.height / 2;
|
|
}
|
|
|
|
return { x: offsetX, y: offsetY };
|
|
};
|
|
}
|
|
|
|
// Based on https://github.com/atlassian/pragmatic-drag-and-drop/blob/main/packages/flourish/src/trigger-post-move-flash.tsx
|
|
// That package has a lot of extra deps so we just copied the function here
|
|
export function triggerPostMoveFlash(element: HTMLElement, backgroundColor: CSSProperties['backgroundColor']) {
|
|
element.animate([{ backgroundColor }, {}], {
|
|
duration: 700,
|
|
easing: 'cubic-bezier(0.25, 0.1, 0.25, 1.0)',
|
|
iterations: 1,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Firefox has a bug where input or textarea elements with draggable parents do not allow selection of their text.
|
|
*
|
|
* This helper function implements a workaround by setting the draggable attribute to false when the mouse is over a
|
|
* input or textarea child of the draggable. It reverts the attribute on mouse out.
|
|
*
|
|
* The fix is only applied for Firefox, and should be used in every `pragmatic-drag-and-drop` `draggable`.
|
|
*
|
|
* See:
|
|
* - https://github.com/atlassian/pragmatic-drag-and-drop/issues/111
|
|
* - https://bugzilla.mozilla.org/show_bug.cgi?id=1853069
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* useEffect(() => {
|
|
* const element = ref.current;
|
|
* if (!element) {
|
|
* return;
|
|
* }
|
|
* return combine(
|
|
* firefoxDndFix(element),
|
|
* // The rest of the draggable setup is the same
|
|
* draggable({
|
|
* element,
|
|
* // ...
|
|
* }),
|
|
* );
|
|
*```
|
|
* @param element The draggable element
|
|
* @returns A cleanup function that removes the event listeners
|
|
*/
|
|
export const firefoxDndFix = (element: HTMLElement): (() => void) => {
|
|
if (!navigator.userAgent.includes('Firefox')) {
|
|
return noop;
|
|
}
|
|
|
|
const abortController = new AbortController();
|
|
|
|
element.addEventListener(
|
|
'mouseover',
|
|
(event) => {
|
|
if (event.target instanceof HTMLTextAreaElement || event.target instanceof HTMLInputElement) {
|
|
element.setAttribute('draggable', 'false');
|
|
}
|
|
},
|
|
{ signal: abortController.signal }
|
|
);
|
|
|
|
element.addEventListener(
|
|
'mouseout',
|
|
(event) => {
|
|
if (event.target instanceof HTMLTextAreaElement || event.target instanceof HTMLInputElement) {
|
|
element.setAttribute('draggable', 'true');
|
|
}
|
|
},
|
|
{ signal: abortController.signal }
|
|
);
|
|
|
|
return () => {
|
|
element.setAttribute('draggable', 'true');
|
|
abortController.abort();
|
|
};
|
|
};
|