fix(ui): unable to use text inputs within draggable

This commit is contained in:
psychedelicious
2024-11-14 15:05:11 -08:00
parent 4d08d00ad8
commit c5b8efe03b
5 changed files with 95 additions and 19 deletions

View File

@@ -4,6 +4,7 @@ import { attachClosestEdge, extractClosestEdge } from '@atlaskit/pragmatic-drag-
import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types';
import { singleCanvasEntityDndSource } from 'features/dnd/dnd';
import { type DndListTargetState, idle } from 'features/dnd/types';
import { firefoxDndFix } from 'features/dnd/util';
import type { RefObject } from 'react';
import { useEffect, useState } from 'react';
@@ -17,6 +18,7 @@ export const useCanvasEntityListDnd = (ref: RefObject<HTMLElement>, entityIdenti
return;
}
return combine(
firefoxDndFix(element),
draggable({
element,
getInitialData() {

View File

@@ -1,3 +1,4 @@
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
import { draggable } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import type { ImageProps, SystemStyleObject } from '@invoke-ai/ui-library';
import { Image } from '@invoke-ai/ui-library';
@@ -5,6 +6,7 @@ import { useAppStore } from 'app/store/nanostores/store';
import { singleImageDndSource } from 'features/dnd/dnd';
import type { DndDragPreviewSingleImageState } from 'features/dnd/DndDragPreviewSingleImage';
import { createSingleImageDragPreview, setSingleImageDragPreview } from 'features/dnd/DndDragPreviewSingleImage';
import { firefoxDndFix } from 'features/dnd/util';
import { useImageContextMenu } from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
import { memo, useEffect, useState } from 'react';
import type { ImageDTO } from 'services/api/types';
@@ -35,25 +37,28 @@ export const DndImage = memo(({ imageDTO, asThumbnail, ...rest }: Props) => {
if (!element) {
return;
}
return draggable({
element,
getInitialData: () => singleImageDndSource.getData({ imageDTO }, imageDTO.image_name),
onDragStart: () => {
setIsDragging(true);
},
onDrop: () => {
setIsDragging(false);
},
onGenerateDragPreview: (args) => {
if (singleImageDndSource.typeGuard(args.source.data)) {
setSingleImageDragPreview({
singleImageDndData: args.source.data,
onGenerateDragPreviewArgs: args,
setDragPreviewState,
});
}
},
});
return combine(
firefoxDndFix(element),
draggable({
element,
getInitialData: () => singleImageDndSource.getData({ imageDTO }, imageDTO.image_name),
onDragStart: () => {
setIsDragging(true);
},
onDrop: () => {
setIsDragging(false);
},
onGenerateDragPreview: (args) => {
if (singleImageDndSource.typeGuard(args.source.data)) {
setSingleImageDragPreview({
singleImageDndData: args.source.data,
onGenerateDragPreviewArgs: args,
setDragPreviewState,
});
}
},
})
);
}, [imageDTO, element, store]);
useImageContextMenu(imageDTO, element);

View File

@@ -1,6 +1,7 @@
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 'lodash-es';
import type { CSSProperties } from 'react';
/**
@@ -44,3 +45,67 @@ export function triggerPostMoveFlash(element: HTMLElement, backgroundColor: CSSP
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();
};
};

View File

@@ -12,6 +12,7 @@ import type { DndDragPreviewMultipleImageState } from 'features/dnd/DndDragPrevi
import { createMultipleImageDragPreview, setMultipleImageDragPreview } from 'features/dnd/DndDragPreviewMultipleImage';
import type { DndDragPreviewSingleImageState } from 'features/dnd/DndDragPreviewSingleImage';
import { createSingleImageDragPreview, setSingleImageDragPreview } from 'features/dnd/DndDragPreviewSingleImage';
import { firefoxDndFix } from 'features/dnd/util';
import { useImageContextMenu } from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
import { GalleryImageHoverIcons } from 'features/gallery/components/ImageGrid/GalleryImageHoverIcons';
import { getGalleryImageDataTestId } from 'features/gallery/components/ImageGrid/getGalleryImageDataTestId';
@@ -115,6 +116,7 @@ export const GalleryImage = memo(({ imageDTO }: Props) => {
return;
}
return combine(
firefoxDndFix(element),
draggable({
element,
getInitialData: () => {

View File

@@ -4,6 +4,7 @@ import { attachClosestEdge, extractClosestEdge } from '@atlaskit/pragmatic-drag-
import { singleWorkflowFieldDndSource } from 'features/dnd/dnd';
import type { DndListTargetState } from 'features/dnd/types';
import { idle } from 'features/dnd/types';
import { firefoxDndFix } from 'features/dnd/util';
import type { FieldIdentifier } from 'features/nodes/types/field';
import type { RefObject } from 'react';
import { useEffect, useState } from 'react';
@@ -18,6 +19,7 @@ export const useLinearViewFieldDnd = (ref: RefObject<HTMLElement>, fieldIdentifi
return;
}
return combine(
firefoxDndFix(element),
draggable({
element,
getInitialData() {