diff --git a/src/components/CategorySelector.jsx b/src/components/CategorySelector.jsx index a75021d4..e5a9dc45 100644 --- a/src/components/CategorySelector.jsx +++ b/src/components/CategorySelector.jsx @@ -59,12 +59,7 @@ const StyledCategorySelector = styled(Select, { }); const CategorySelector = forwardRef(function CategorySelector( - { - css, - handleCategoryChange, - handleCategorySelectorBlur, - menuPlacement = 'top', - }, + { css, handleCategoryChange, handleCategorySelectorBlur, menuPlacement = 'top' }, ref, ) { // update selector options when new labels become available diff --git a/src/features/loupe/BoundingBox.jsx b/src/features/loupe/BoundingBox.jsx index 7c5c3940..14a81d47 100644 --- a/src/features/loupe/BoundingBox.jsx +++ b/src/features/loupe/BoundingBox.jsx @@ -15,7 +15,12 @@ import { ContextMenuSeparator, ContextMenuItemIconLeft, } from '../../components/ContextMenu'; -import { bboxUpdated, labelsValidated, setFocus, objectsManuallyUnlocked } from '../review/reviewSlice'; +import { + bboxUpdated, + labelsValidated, + setFocus, + objectsManuallyUnlocked, +} from '../review/reviewSlice'; import { addLabelStart } from './loupeSlice'; import BoundingBoxLabel from './BoundingBoxLabel'; import { absToRel, relToAbs } from '../../app/utils'; diff --git a/src/features/loupe/BoundingBoxLabel.jsx b/src/features/loupe/BoundingBoxLabel.jsx index f3e71fce..222d28fa 100644 --- a/src/features/loupe/BoundingBoxLabel.jsx +++ b/src/features/loupe/BoundingBoxLabel.jsx @@ -89,8 +89,17 @@ const BoundingBoxLabel = forwardRef(function BoundingBoxLabel( const isAddingLabel = useSelector(selectIsAddingLabel); const open = isAddingLabel === 'from-object' && selected; const [catSelectorOpen, setCatSelectorOpen] = useState(open); + const [ignoreBlur, setIgnoreBlur] = useState(false); useEffect(() => { setCatSelectorOpen(isAddingLabel === 'from-object' && selected); + // NOTE: Firefox steals focus away from the category selector when it's opened + // via the context menu. This is a hacky workaround to prevent that from happening. + if (isAddingLabel === 'from-object' && selected) { + setIgnoreBlur(true); + setTimeout(() => { + setIgnoreBlur(false); + }, 100); + } }, [isAddingLabel, selected]); // manually focus catSelector if it's open @@ -127,7 +136,7 @@ const BoundingBoxLabel = forwardRef(function BoundingBoxLabel( const handleCategorySelectorBlur = () => { if (object.isTemp) setTempObject(null); - dispatch(addLabelEnd()); + if (!ignoreBlur) dispatch(addLabelEnd()); }; return ( @@ -146,20 +155,30 @@ const BoundingBoxLabel = forwardRef(function BoundingBoxLabel( handleCategorySelectorBlur={handleCategorySelectorBlur} menuPlacement="bottom" /> - + {displayLabel?.name} - {!object.locked && displayLabel._id !== 'fallback_label' && {conf}%} + {!object.locked && displayLabel._id !== 'fallback_label' && ( + {conf}% + )} - {showLabelButtons && !catSelectorOpen && isAuthorized && displayLabel._id !== 'fallback_label' && ( - - )} + {showLabelButtons && + !catSelectorOpen && + isAuthorized && + displayLabel._id !== 'fallback_label' && ( + + )} ); }); diff --git a/src/features/loupe/loupeSlice.js b/src/features/loupe/loupeSlice.js index 4c424738..e2644290 100644 --- a/src/features/loupe/loupeSlice.js +++ b/src/features/loupe/loupeSlice.js @@ -18,12 +18,17 @@ export const loupeSlice = createSlice({ name: 'loupe', initialState, reducers: { + toggleOpenLoupe: (state, { payload }) => { + state.open = payload; + }, - toggleOpenLoupe: (state, { payload }) => { state.open = payload; }, - - reviewModeToggled: (state) => { state.reviewMode = !state.reviewMode; }, + reviewModeToggled: (state) => { + state.reviewMode = !state.reviewMode; + }, - drawBboxStart: (state) => { state.isDrawingBbox = true; }, + drawBboxStart: (state) => { + state.isDrawingBbox = true; + }, drawBboxEnd: (state) => { if (state.isDrawingBbox) state.isDrawingBbox = false; @@ -37,7 +42,7 @@ export const loupeSlice = createSlice({ state.mouseEventOutsideOverlay = null; }, - addLabelStart: (state, { payload }) => { + addLabelStart: (state, { payload }) => { // payload can be 'from-object' or 'all-objects state.isAddingLabel = payload; }, @@ -49,14 +54,13 @@ export const loupeSlice = createSlice({ iterationOptionsChanged: (state, { payload }) => { state.iterationOptions = payload; }, - }, extraReducers: (builder) => { - builder - .addCase(clearImages, (state) => { state.open = false; }); - } - + builder.addCase(clearImages, (state) => { + state.open = false; + }); + }, }); export const { @@ -83,13 +87,12 @@ export const copyUrlToClipboard = (url) => { }; }; - // Selectors -export const selectLoupeOpen = state => state.loupe.open; -export const selectReviewMode = state => state.loupe.reviewMode; -export const selectIsDrawingBbox = state => state.loupe.isDrawingBbox; -export const selectMouseEventDetected = state => state.loupe.mouseEventOutsideOverlay; -export const selectIsAddingLabel = state => state.loupe.isAddingLabel; -export const selectIterationOptions = state => state.loupe.iterationOptions; +export const selectLoupeOpen = (state) => state.loupe.open; +export const selectReviewMode = (state) => state.loupe.reviewMode; +export const selectIsDrawingBbox = (state) => state.loupe.isDrawingBbox; +export const selectMouseEventDetected = (state) => state.loupe.mouseEventOutsideOverlay; +export const selectIsAddingLabel = (state) => state.loupe.isAddingLabel; +export const selectIterationOptions = (state) => state.loupe.iterationOptions; export default loupeSlice.reducer;