diff --git a/src/components/app-bars/ImageViewerAppBar/ImageViewerAppBar.tsx b/src/components/app-bars/ImageViewerAppBar/ImageViewerAppBar.tsx
index 4c7a5c49..0eddc8f5 100644
--- a/src/components/app-bars/ImageViewerAppBar/ImageViewerAppBar.tsx
+++ b/src/components/app-bars/ImageViewerAppBar/ImageViewerAppBar.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { ArrowBack } from "@mui/icons-material";
@@ -10,26 +10,32 @@ import {
Typography,
} from "@mui/material";
-import { useDialog } from "hooks";
+import { useDialogHotkey } from "hooks";
import { ExitAnnotatorDialog } from "components/dialogs";
import { LogoLoader } from "components/styled-components";
+import { HotkeyContext } from "utils/common/enums";
export const ImageViewerAppBar = () => {
+ const [returnToProject, setReturnToProject] = useState(false);
const {
onClose: onCloseExitAnnotatorDialog,
onOpen: onOpenExitAnnotatorDialog,
- open: openExitAnnotatorDialog,
- } = useDialog();
+ open: ExitAnnotatorDialogOpen,
+ } = useDialogHotkey(HotkeyContext.ConfirmationDialog);
const navigate = useNavigate();
const onReturnToMainProject = () => {
- onCloseExitAnnotatorDialog();
- navigate("/");
+ setReturnToProject(true);
};
+ useEffect(() => {
+ //NOTE: Wait until ExitAnnotatorDialogOpen is finished updating. Otherwise unmounted component access warning
+ if (returnToProject && !ExitAnnotatorDialogOpen) navigate("/");
+ }, [returnToProject, ExitAnnotatorDialogOpen, navigate]);
+
return (
<>
{
>
);
diff --git a/src/components/app-bars/ProjectToolbar/ProjectToolbar.tsx b/src/components/app-bars/ProjectToolbar/ProjectToolbar.tsx
index aa7f1709..eae96f53 100644
--- a/src/components/app-bars/ProjectToolbar/ProjectToolbar.tsx
+++ b/src/components/app-bars/ProjectToolbar/ProjectToolbar.tsx
@@ -45,12 +45,12 @@ import {
useThingSelection,
} from "hooks";
import { TooltipTitle } from "components/tooltips";
-import { HotkeyView } from "utils/common/enums";
+import { HotkeyContext } from "utils/common/enums";
import { useNavigate } from "react-router-dom";
import { selectActiveCategories } from "store/project/reselectors";
import { imageViewerSlice } from "store/imageViewer";
import { TooltipButton } from "components/styled-components/TooltipButton/TooltipButton";
-import { DialogWithAction } from "components/dialogs";
+import { ConfirmationDialog } from "components/dialogs";
import { ImageCategoryMenu } from "components/menus";
import { Partition } from "utils/models/enums";
import { dataSlice } from "store/data";
@@ -78,7 +78,7 @@ export const ProjectToolbar = () => {
onClose: handleCloseDeleteImagesDialog,
onOpen: onOpenDeleteImagesDialog,
open: deleteImagesDialogisOpen,
- } = useDialogHotkey(HotkeyView.DialogWithAction);
+ } = useDialogHotkey(HotkeyContext.ConfirmationDialog);
const handleDelete = () => {
dispatch(
@@ -96,20 +96,11 @@ export const ProjectToolbar = () => {
selectedThingIds: allSelectedThingIds,
})
);
- dispatch(
- applicationSettingsSlice.actions.unregisterHotkeyView({
- hotkeyView: HotkeyView.MainImageGridAppBar,
- })
- );
+
navigate("/imageviewer");
};
const handleNavigateMeasurements = () => {
- dispatch(
- applicationSettingsSlice.actions.unregisterHotkeyView({
- hotkeyView: HotkeyView.MainImageGridAppBar,
- })
- );
navigate("/measurements");
};
@@ -211,7 +202,7 @@ export const ProjectToolbar = () => {
)}
- {
inputRef={inputRef}
size="small"
sx={{ ml: 5 }}
+ variant="standard"
+ inputProps={{ min: 0, style: { textAlign: "center" } }}
/>
)}
diff --git a/src/components/cards/ExampleProjectCard/ExampleProjectCard.tsx b/src/components/cards/ExampleProjectCard/ExampleProjectCard.tsx
index ea5331bc..29795987 100644
--- a/src/components/cards/ExampleProjectCard/ExampleProjectCard.tsx
+++ b/src/components/cards/ExampleProjectCard/ExampleProjectCard.tsx
@@ -125,10 +125,7 @@ export const ExampleProjectCard = ({
);
if (!deserializedProject) return;
- const project = deserializedProject.project;
- const data = deserializedProject.data;
-
- const classifier = deserializedProject.classifier;
+ const { project, data, classifier } = deserializedProject;
batch(() => {
// loadPercent will be set to 1 here
diff --git a/src/components/dialogs/BaseExampleDialog/index.ts b/src/components/dialogs/BaseExampleDialog/index.ts
deleted file mode 100644
index 598dcbf1..00000000
--- a/src/components/dialogs/BaseExampleDialog/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { BaseExampleDialog } from "./BaseExampleDialog";
diff --git a/src/components/dialogs/CategoryDialog/CategoryDialog.tsx b/src/components/dialogs/CategoryDialog/CategoryDialog.tsx
index 9a766da3..5c1b0f64 100644
--- a/src/components/dialogs/CategoryDialog/CategoryDialog.tsx
+++ b/src/components/dialogs/CategoryDialog/CategoryDialog.tsx
@@ -1,6 +1,6 @@
import { TextField, Box } from "@mui/material";
-import { DialogWithAction } from "../DialogWithAction";
+import { ConfirmationDialog } from "../ConfirmationDialog";
import { useCategoryValidation } from "hooks/useCategoryValidation/useCategoryValidation";
import { ColorIcon } from "components/controls";
@@ -37,7 +37,9 @@ export const CategoryDialog = ({
} = useCategoryValidation({ kind, initName, initColor });
const handleConfirm = () => {
- onConfirm(name, color, kind);
+ if (!isInvalidName) {
+ onConfirm(name, color, kind);
+ }
};
const handleClose = () => {
@@ -46,7 +48,7 @@ export const CategoryDialog = ({
};
return (
- void;
diff --git a/src/components/dialogs/UpdateCategoryDialog/UpdateCategoryDialog.tsx b/src/components/dialogs/CategoryDialog/UpdateCategoryDialog.tsx
similarity index 94%
rename from src/components/dialogs/UpdateCategoryDialog/UpdateCategoryDialog.tsx
rename to src/components/dialogs/CategoryDialog/UpdateCategoryDialog.tsx
index 23ee5d23..1f1bb45b 100644
--- a/src/components/dialogs/UpdateCategoryDialog/UpdateCategoryDialog.tsx
+++ b/src/components/dialogs/CategoryDialog/UpdateCategoryDialog.tsx
@@ -1,6 +1,6 @@
import { useDispatch } from "react-redux";
import { dataSlice } from "store/data/dataSlice";
-import { CategoryDialog } from "../CategoryDialog";
+import { CategoryDialog } from "./CategoryDialog";
import { Category } from "store/data/types";
type UpdateCategoriesDialogProps = {
diff --git a/src/components/dialogs/CategoryDialog/index.ts b/src/components/dialogs/CategoryDialog/index.ts
index 3b8d775a..4677acd5 100644
--- a/src/components/dialogs/CategoryDialog/index.ts
+++ b/src/components/dialogs/CategoryDialog/index.ts
@@ -1 +1,2 @@
-export { CategoryDialog } from "./CategoryDialog";
+export { CreateCategoryDialog } from "./CreateCategoryDialog";
+export { UpdateCategoryDialog } from "./UpdateCategoryDialog";
diff --git a/src/components/dialogs/DialogWithAction/DialogWithAction.stories.tsx b/src/components/dialogs/ConfirmationDialog/ConfirmationDialog.stories.tsx
similarity index 72%
rename from src/components/dialogs/DialogWithAction/DialogWithAction.stories.tsx
rename to src/components/dialogs/ConfirmationDialog/ConfirmationDialog.stories.tsx
index db0e1adc..25cf943a 100644
--- a/src/components/dialogs/DialogWithAction/DialogWithAction.stories.tsx
+++ b/src/components/dialogs/ConfirmationDialog/ConfirmationDialog.stories.tsx
@@ -1,9 +1,10 @@
+// ignore-no-logs
import type { Meta, StoryObj } from "@storybook/react";
-import { DialogWithActionSHK } from "./DialogWithActionSHK";
+import { ConfirmationDialogSHK } from "./ConfirmationDialogSHK";
-const meta: Meta = {
- title: "Common/DialogWithAction",
- component: DialogWithActionSHK,
+const meta: Meta = {
+ title: "Common/ConfirmationDialog",
+ component: ConfirmationDialogSHK,
tags: ["autodocs"],
parameters: {
docs: {
@@ -16,11 +17,11 @@ const meta: Meta = {
};
export default meta;
-type Story = StoryObj;
+type Story = StoryObj;
export const Default: Story = {
args: {
- title: "Dialog with Action",
+ title: "Confirmation Dialog",
content: "Dialog content",
onConfirm: () => {
console.log("I'm confirmed");
@@ -34,7 +35,7 @@ export const Default: Story = {
export const AltText: Story = {
args: {
- title: "Dialog with Action",
+ title: "Confirmation Dialog",
content: "Dialog content",
onConfirm: () => {
console.log("I'm confirmed");
@@ -49,7 +50,7 @@ export const AltText: Story = {
export const WithReject: Story = {
args: {
- title: "Dialog with Action",
+ title: "Confirmation Dialog",
content: "Dialog content",
onConfirm: () => {
console.log("I'm confirmed");
diff --git a/src/components/dialogs/DialogWithAction/DialogWithAction.tsx b/src/components/dialogs/ConfirmationDialog/ConfirmationDialog.tsx
similarity index 73%
rename from src/components/dialogs/DialogWithAction/DialogWithAction.tsx
rename to src/components/dialogs/ConfirmationDialog/ConfirmationDialog.tsx
index 0672808c..89d0a0b8 100644
--- a/src/components/dialogs/DialogWithAction/DialogWithAction.tsx
+++ b/src/components/dialogs/ConfirmationDialog/ConfirmationDialog.tsx
@@ -10,10 +10,10 @@ import {
} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
import { useHotkeys } from "hooks";
-import { HotkeyView } from "utils/common/enums";
+import { HotkeyContext } from "utils/common/enums";
import { ReactElement } from "react";
-type DialogWithActionProps = Omit<
+type ConfirmationDialogProps = Omit<
DialogProps,
"children" | "open" | "content"
> & {
@@ -26,34 +26,40 @@ type DialogWithActionProps = Omit<
confirmText?: string;
rejectText?: string;
confirmDisabled?: boolean;
+ disableHotkeyOnInput?: boolean;
};
-export const DialogWithAction = ({
+export const ConfirmationDialog = ({
title,
content,
onConfirm,
onClose: handleClose,
- onReject: handleReject,
+ onReject,
confirmText = "Confirm",
rejectText = "Reject",
isOpen,
confirmDisabled,
+ disableHotkeyOnInput,
...rest
-}: DialogWithActionProps) => {
+}: ConfirmationDialogProps) => {
const handleConfirm = () => {
+ handleClose();
onConfirm();
+ };
+ const handleReject = () => {
+ onReject && onReject();
handleClose();
};
useHotkeys(
"enter",
() => {
- handleConfirm();
+ !confirmDisabled && handleConfirm();
},
- HotkeyView.DialogWithAction,
- { enableOnTags: ["INPUT"], enabled: isOpen },
- [handleConfirm]
+ HotkeyContext.ConfirmationDialog,
+ { enableOnTags: disableHotkeyOnInput ? [] : ["INPUT"], enabled: isOpen },
+ [handleConfirm, confirmDisabled]
);
return (
@@ -77,26 +83,26 @@ export const DialogWithAction = ({
- {content && {content}}
+ {content && {content}}
+
+
+ {onReject && (
+
+ )}
- {handleReject ? (
-
- ) : (
- <>>
- )}
-
);
diff --git a/src/components/dialogs/DialogWithAction/DialogWithActionSHK.tsx b/src/components/dialogs/ConfirmationDialog/ConfirmationDialogSHK.tsx
similarity index 75%
rename from src/components/dialogs/DialogWithAction/DialogWithActionSHK.tsx
rename to src/components/dialogs/ConfirmationDialog/ConfirmationDialogSHK.tsx
index e556113e..627ba631 100644
--- a/src/components/dialogs/DialogWithAction/DialogWithActionSHK.tsx
+++ b/src/components/dialogs/ConfirmationDialog/ConfirmationDialogSHK.tsx
@@ -1,9 +1,9 @@
import { useHotkeys } from "hooks";
-import { HotkeyView } from "utils/common/enums";
-import { DialogWithAction } from "./DialogWithAction";
+import { HotkeyContext } from "utils/common/enums";
+import { ConfirmationDialog } from "./ConfirmationDialog";
-type DialogWithActionHKProps = {
+type ConfirmationDialogProps = {
title: string;
content: string;
onConfirm: () => void;
@@ -16,7 +16,7 @@ type DialogWithActionHKProps = {
//NOTE: SHK : Sans Hotkey for storybook
-export const DialogWithActionSHK = ({
+export const ConfirmationDialogSHK = ({
title,
content,
onConfirm,
@@ -25,7 +25,7 @@ export const DialogWithActionSHK = ({
confirmText = "Confirm",
rejectText = "Reject",
isOpen,
-}: DialogWithActionHKProps) => {
+}: ConfirmationDialogProps) => {
const handleConfirm = () => {
onConfirm();
@@ -37,13 +37,13 @@ export const DialogWithActionSHK = ({
() => {
handleConfirm();
},
- HotkeyView.DeleteCategoryDialog,
+ HotkeyContext.ConfirmationDialog,
{ enableOnTags: ["INPUT"] },
[handleConfirm]
);
return (
- void;
+ returnToProject: () => void;
onClose: () => void;
open: boolean;
};
export const ExitAnnotatorDialog = ({
- onReturnToProject,
+ returnToProject,
onClose,
open,
}: ExitAnnotatorDialogProps) => {
@@ -19,7 +19,7 @@ export const ExitAnnotatorDialog = ({
const activeImageId = useSelector(selectActiveImageId);
- const onSaveChanges = () => {
+ const handleSaveChanges = () => {
batch(() => {
dispatch(
imageViewerSlice.actions.setActiveImageId({
@@ -30,11 +30,10 @@ export const ExitAnnotatorDialog = ({
dispatch(dataSlice.actions.reconcile({ keepChanges: true }));
dispatch(imageViewerSlice.actions.setImageStack({ imageIds: [] }));
});
-
- onReturnToProject();
+ returnToProject();
};
- const onDiscardChanges = () => {
+ const handleDiscardChanges = () => {
batch(() => {
dispatch(
imageViewerSlice.actions.setActiveImageId({
@@ -44,19 +43,22 @@ export const ExitAnnotatorDialog = ({
);
dispatch(dataSlice.actions.reconcile({ keepChanges: false }));
});
+ returnToProject();
+ };
- onReturnToProject();
+ const handleClose = () => {
+ onClose();
};
return (
-
);
diff --git a/src/components/dialogs/ExportAnnotationsDialog/ExportAnnotationsDialog.tsx b/src/components/dialogs/ExportAnnotationsDialog/ExportAnnotationsDialog.tsx
index 904275e3..83edf73f 100644
--- a/src/components/dialogs/ExportAnnotationsDialog/ExportAnnotationsDialog.tsx
+++ b/src/components/dialogs/ExportAnnotationsDialog/ExportAnnotationsDialog.tsx
@@ -1,11 +1,7 @@
import React, { ChangeEvent, useState } from "react";
import { Grid, TextField } from "@mui/material";
-
-import { useHotkeys } from "hooks";
-
-import { HotkeyView } from "utils/common/enums";
-import { DialogWithAction } from "../DialogWithAction";
+import { ConfirmationDialog } from "../ConfirmationDialog";
type ExportAnnotationsDialogProps = {
onClose: () => void;
@@ -22,28 +18,17 @@ export const ExportAnnotationsDialog = ({
}: ExportAnnotationsDialogProps) => {
const [projectName, setProjectName] = useState(defaultName);
- const onCancel = () => {
+ const handleCancel = () => {
setProjectName(defaultName);
onClose();
};
- const onNameChange = (event: ChangeEvent) => {
+ const handleNameChange = (event: ChangeEvent) => {
setProjectName(event.target.value);
};
- useHotkeys(
- "enter",
- () => {
- handleSave(projectName);
- onClose();
- },
- HotkeyView.ExportAnnotationsDialog,
- { enableOnTags: ["INPUT"] },
- [handleSave]
- );
-
return (
-
@@ -54,13 +39,14 @@ export const ExportAnnotationsDialog = ({
id="name"
label="Project file name"
margin="dense"
+ variant="standard"
defaultValue={projectName}
- onChange={onNameChange}
+ onChange={handleNameChange}
/>
}
- onClose={onCancel}
+ onClose={handleCancel}
onConfirm={() => handleSave(projectName)}
confirmText="Export Annotations"
isOpen={open}
diff --git a/src/components/dialogs/FallbackDialog/FallBackDialog.tsx b/src/components/dialogs/FallbackDialog/FallBackDialog.tsx
index 85d51e03..dce28829 100644
--- a/src/components/dialogs/FallbackDialog/FallBackDialog.tsx
+++ b/src/components/dialogs/FallbackDialog/FallBackDialog.tsx
@@ -27,7 +27,7 @@ import { useDialogHotkey } from "hooks";
import { SaveFittedModelDialog, SaveProjectDialog } from "components/dialogs";
-import { HotkeyView } from "utils/common/enums";
+import { HotkeyContext } from "utils/common/enums";
import { ModelStatus } from "utils/models/enums";
import { APPLICATION_COLORS } from "utils/common/constants";
@@ -74,7 +74,7 @@ export const FallBackDialog = (props: any) => {
onClose: onSaveProjectDialogClose,
onOpen: onSaveProjectDialogOpen,
open: openSaveProjectDialog,
- } = useDialogHotkey(HotkeyView.SaveProjectDialog);
+ } = useDialogHotkey(HotkeyContext.ConfirmationDialog);
// const routePath = useLocation().pathname;
// const inAnnotator = routePath === "/annotator";
@@ -83,13 +83,13 @@ export const FallBackDialog = (props: any) => {
onClose: onSaveClassifierDialogClose,
onOpen: onSaveClassifierDialogOpen,
open: openSaveClassifierDialog,
- } = useDialogHotkey(HotkeyView.SaveFittedModelDialog);
+ } = useDialogHotkey(HotkeyContext.ConfirmationDialog);
const {
onClose: onSaveSegmenterDialogClose,
onOpen: onSaveSegmenterDialogOpen,
open: openSaveSegmenterDialog,
- } = useDialogHotkey(HotkeyView.SaveFittedModelDialog);
+ } = useDialogHotkey(HotkeyContext.ConfirmationDialog);
const selectedClassifierModel = useSelector(selectClassifierSelectedModel);
const classifierModelStatus = useSelector(selectClassifierModelStatus);
diff --git a/src/components/dialogs/ImportTensorflowModelDialog/CloudUpload.tsx b/src/components/dialogs/ImportTensorflowModelDialog/CloudUpload.tsx
index 8ce85a26..593d6718 100644
--- a/src/components/dialogs/ImportTensorflowModelDialog/CloudUpload.tsx
+++ b/src/components/dialogs/ImportTensorflowModelDialog/CloudUpload.tsx
@@ -3,11 +3,9 @@ import React, { useState } from "react";
import {
Button,
Checkbox,
- DialogContent,
FormControl,
FormControlLabel,
InputAdornment,
- MenuItem,
TextField,
Typography,
} from "@mui/material";
@@ -103,63 +101,62 @@ export const CloudUpload = ({
return (
<>
-
- {"Upload a model from the internet."}
-
-
+ {errMessage}
+
+
+ {successMessage}
+
+
+ }
+ label="From TF Hub?"
+ />
+
+
>
);
};
diff --git a/src/components/dialogs/ImportTensorflowModelDialog/ImportTensorflowModelDialog.tsx b/src/components/dialogs/ImportTensorflowModelDialog/ImportTensorflowModelDialog.tsx
index 11852928..e3597bc3 100644
--- a/src/components/dialogs/ImportTensorflowModelDialog/ImportTensorflowModelDialog.tsx
+++ b/src/components/dialogs/ImportTensorflowModelDialog/ImportTensorflowModelDialog.tsx
@@ -7,6 +7,7 @@ import {
Collapse,
Dialog,
DialogActions,
+ DialogContent,
DialogTitle,
IconButton,
Tab,
@@ -30,7 +31,7 @@ import { Cellpose } from "utils/models/Cellpose";
import { ModelTask } from "utils/models/enums";
import { availableClassifierModels } from "utils/models/availableClassificationModels";
import { availableSegmenterModels } from "utils/models/availableSegmentationModels";
-import { HotkeyView } from "utils/common/enums";
+import { HotkeyContext } from "utils/common/enums";
import { Shape } from "store/data/types";
import { selectProjectImageChannels } from "store/project/selectors";
import { useSelector } from "react-redux";
@@ -76,7 +77,7 @@ type ImportTensorflowModelDialogProps = {
loadedModel?: Model;
open: boolean;
modelTask: ModelTask;
- dispatchFunction: (model: Model, inputShape: Shape) => void;
+ dispatchFunction: (model: Model, inputShape: Shape) => Promise;
};
export const ImportTensorflowModelDialog = ({
@@ -117,14 +118,14 @@ export const ImportTensorflowModelDialog = ({
}
}, []);
- const dispatchModelToStore = () => {
+ const dispatchModelToStore = async () => {
if (!selectedModel) {
process.env.NODE_ENV !== "production" &&
console.warn("Attempting to dispatch undefined model");
return;
}
- dispatchFunction(selectedModel, inputShape);
+ await dispatchFunction(selectedModel, inputShape);
setCloudWarning(false);
setInvalidModel(false);
@@ -144,10 +145,12 @@ export const ImportTensorflowModelDialog = ({
useHotkeys(
"enter",
- () => dispatchModelToStore(),
- HotkeyView.ImportTensorflowModelDialog,
- { enableOnTags: ["INPUT"] },
- [dispatchModelToStore]
+ (event) => {
+ selectedModel && !invalidModel && dispatchModelToStore();
+ },
+ HotkeyContext.ConfirmationDialog,
+
+ [dispatchModelToStore, selectedModel, invalidModel]
);
useEffect(() => {
@@ -232,51 +235,51 @@ export const ImportTensorflowModelDialog = ({
disabled={modelTask === ModelTask.Segmentation}
/>
-
-
- model.name === selectedModel.name
- ) + ""
- : "-1"
- }
- setModel={onModelChange}
- error={invalidModel}
- errorText={
- !selectedModel
- ? "Select a Model"
- : invalidModel
- ? `Model requires ${selectedModel.requiredChannels}-channel images`
- : ""
- }
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ model.name === selectedModel.name
+ ) + ""
+ : "-1"
+ }
+ setModel={onModelChange}
+ error={invalidModel}
+ errorText={
+ !selectedModel
+ ? "Select a Model"
+ : invalidModel
+ ? `Model requires ${selectedModel.requiredChannels}-channel images`
+ : ""
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+