diff --git a/src/components/app-bars/GridItemActionBar/GridItemActionBar.tsx b/src/components/app-bars/GridItemActionBar/GridItemActionBar.tsx deleted file mode 100644 index 1e1b699d..00000000 --- a/src/components/app-bars/GridItemActionBar/GridItemActionBar.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import React from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { useNavigate } from "react-router-dom"; - -import { - AppBar, - Chip, - IconButton, - Slide, - Toolbar, - Tooltip, - Typography, - Box, -} from "@mui/material"; - -import { - Clear as ClearIcon, - Delete as DeleteIcon, - Deselect as DeselectIcon, - Gesture as GestureIcon, - LabelOutlined as LabelOutlinedIcon, - SelectAll as SelectAllIcon, -} from "@mui/icons-material"; - -import { ImageCategoryMenu } from "components/menus"; -import { TooltipTitle } from "components/tooltips"; - -import { applicationSettingsSlice } from "store/applicationSettings"; - -import { HotkeyView } from "utils/common/enums"; -import { dataSlice } from "store/data/dataSlice"; -import { Partition } from "utils/models/enums"; -import { selectActiveCategories } from "store/project/reselectors"; - -type GridItemActionBarProps = { - allSelected: boolean; - selectedThings: string[]; - selectAllThings: any; - deselectAllThings: any; - handleOpenDeleteDialog: any; - onOpenImageViewer: any; -}; - -export const GridItemActionBar = ({ - allSelected, - selectedThings, - selectAllThings, - deselectAllThings, - handleOpenDeleteDialog, - onOpenImageViewer: handleOpenImageViewer, -}: GridItemActionBarProps) => { - const dispatch = useDispatch(); - const navigate = useNavigate(); - - const [categoryMenuAnchorEl, setCategoryMenuAnchorEl] = - React.useState(null); - - const categories = useSelector(selectActiveCategories); - - const handleSelectAllObjects = () => { - selectAllThings(); - }; - - const handleDeselectAllObjects = () => { - deselectAllThings(); - }; - - const onOpenCategoriesMenu = (event: React.MouseEvent) => { - setCategoryMenuAnchorEl(event.currentTarget); - }; - - const onCloseCategoryMenu = () => { - setCategoryMenuAnchorEl(null); - }; - const handleUpdateCategories = (categoryId: string) => { - const updates = selectedThings.map((thingId) => ({ - id: thingId, - categoryId: categoryId, - partition: Partition.Unassigned, - })); - dispatch( - dataSlice.actions.updateThings({ - updates, - isPermanent: true, - }) - ); - }; - - const handleNavigateImageViewer = () => { - handleOpenImageViewer(); - dispatch( - applicationSettingsSlice.actions.unregisterHotkeyView({ - hotkeyView: HotkeyView.MainImageGridAppBar, - }) - ); - navigate("/imageviewer"); - }; - - return ( - <> - 0}> - - - - theme.spacing(2) }} - edge="start" - color="inherit" - onClick={handleDeselectAllObjects} - > - - - - - - {`${selectedThings.length} selected `} - - - - - } - label="Categorize" - onClick={onOpenCategoriesMenu} - variant="outlined" - style={{ marginRight: 15 }} - /> - } - label="Annotate" - onClick={handleNavigateImageViewer} - variant="outlined" - /> - - {!allSelected ? ( - - - - - - ) : ( - - - - - - )} - - - - - - - - - - - - - ); -}; diff --git a/src/components/app-bars/GridItemActionBar/index.ts b/src/components/app-bars/GridItemActionBar/index.ts deleted file mode 100644 index d4a29b3c..00000000 --- a/src/components/app-bars/GridItemActionBar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { GridItemActionBar } from "./GridItemActionBar"; diff --git a/src/components/app-bars/ProjectToolbar/ProjectToolbar.tsx b/src/components/app-bars/ProjectToolbar/ProjectToolbar.tsx index adc7d228..aa7f1709 100644 --- a/src/components/app-bars/ProjectToolbar/ProjectToolbar.tsx +++ b/src/components/app-bars/ProjectToolbar/ProjectToolbar.tsx @@ -5,18 +5,26 @@ import { Slider, Toolbar, Box, - Button, Typography, TextField, FormControl, Menu, + Tooltip, + IconButton, + Chip, + Divider, + Badge, } from "@mui/material"; import { - ZoomOut as ZoomOutIcon, ZoomIn as ZoomInIcon, - Search as SearchIcon, Add as AddIcon, Remove as RemoveIcon, + Delete as DeleteIcon, + Deselect as DeselectIcon, + Gesture as GestureIcon, + LabelOutlined as LabelOutlinedIcon, + SelectAll as SelectAllIcon, + Straighten as StraightenIcon, } from "@mui/icons-material"; import { LogoLoader } from "components/styled-components"; @@ -25,24 +33,205 @@ import { applicationSettingsSlice } from "store/applicationSettings"; import { projectSlice } from "store/project"; import { SortSelection } from "components/styled-components"; import { + selectActiveKindId, selectLoadMessage, selectLoadPercent, selectProjectName, } from "store/project/selectors"; -import { useMenu, useMobileView } from "hooks"; +import { + useDialogHotkey, + useMenu, + useMobileView, + useThingSelection, +} from "hooks"; +import { TooltipTitle } from "components/tooltips"; +import { HotkeyView } 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 { ImageCategoryMenu } from "components/menus"; +import { Partition } from "utils/models/enums"; +import { dataSlice } from "store/data"; +import { pluralize } from "utils/common/helpers"; const minZoom = 0.6; const maxZoom = 4; export const ProjectToolbar = () => { const dispatch = useDispatch(); + const activeKind = useSelector(selectActiveKindId); const loadPercent = useSelector(selectLoadPercent); - const loadMessage = useSelector(selectLoadMessage); - const projectName = useSelector(selectProjectName); - const [value, setValue] = useState(1); - const [newProjectName, setNewProjectName] = useState(projectName); - const inputRef = useRef(null); const isMobile = useMobileView(); + const navigate = useNavigate(); + + const { + allSelected, + unfilteredSelectedThings, + allSelectedThingIds, + handleDeselectAll, + handleSelectAll, + } = useThingSelection(); + + const { + onClose: handleCloseDeleteImagesDialog, + onOpen: onOpenDeleteImagesDialog, + open: deleteImagesDialogisOpen, + } = useDialogHotkey(HotkeyView.DialogWithAction); + + const handleDelete = () => { + dispatch( + dataSlice.actions.deleteThings({ + thingIds: unfilteredSelectedThings, + disposeColorTensors: true, + isPermanent: true, + }) + ); + }; + + const handleNavigateImageViewer = () => { + dispatch( + imageViewerSlice.actions.prepareImageViewer({ + selectedThingIds: allSelectedThingIds, + }) + ); + dispatch( + applicationSettingsSlice.actions.unregisterHotkeyView({ + hotkeyView: HotkeyView.MainImageGridAppBar, + }) + ); + navigate("/imageviewer"); + }; + + const handleNavigateMeasurements = () => { + dispatch( + applicationSettingsSlice.actions.unregisterHotkeyView({ + hotkeyView: HotkeyView.MainImageGridAppBar, + }) + ); + navigate("/measurements"); + }; + + return ( + <> + + + + + + + + {isMobile ? ( + + ) : ( + <> + + + + + + + + + + + + + + + + + + + + + + + } + label="Annotate" + onClick={handleNavigateImageViewer} + variant="outlined" + sx={{ marginRight: 1 }} + disabled={allSelectedThingIds.length === 0} + /> + + + + + } + label="Measurements" + onClick={handleNavigateMeasurements} + variant="outlined" + /> + + + + )} + + + + + ); +}; + +const ZoomControl = () => { + const dispatch = useDispatch(); + const [value, setValue] = useState(1); const { onOpen, onClose, open, anchorEl } = useMenu(); const handleSizeChange = (event: Event, newValue: number | number[]) => { @@ -74,6 +263,99 @@ export const ProjectToolbar = () => { ); }; + return ( + <> + + + + + + + + + + + + ); +}; + +const CategorizeChip = ({ + unfilteredSelectedThings, +}: { + unfilteredSelectedThings: string[]; +}) => { + const dispatch = useDispatch(); + const categories = useSelector(selectActiveCategories); + const [categoryMenuAnchorEl, setCategoryMenuAnchorEl] = + React.useState(null); + const onOpenCategoriesMenu = (event: React.MouseEvent) => { + setCategoryMenuAnchorEl(event.currentTarget); + }; + + const onCloseCategoryMenu = () => { + setCategoryMenuAnchorEl(null); + }; + const handleUpdateCategories = (categoryId: string) => { + const updates = unfilteredSelectedThings.map((thingId) => ({ + id: thingId, + categoryId: categoryId, + partition: Partition.Unassigned, + })); + dispatch( + dataSlice.actions.updateThings({ + updates, + isPermanent: true, + }) + ); + }; + return ( + <> + + + } + label="Categorize" + onClick={onOpenCategoriesMenu} + variant="outlined" + sx={{ marginRight: 1 }} + disabled={unfilteredSelectedThings.length === 0} + /> + + + + + ); +}; + +const ProjectTextField = () => { + const dispatch = useDispatch(); + + const loadMessage = useSelector(selectLoadMessage); + const projectName = useSelector(selectProjectName); + const [newProjectName, setNewProjectName] = useState(projectName); + const inputRef = useRef(null); + const handleTextFieldBlur = () => { if (projectName === newProjectName) return; dispatch(projectSlice.actions.setProjectName({ name: newProjectName })); @@ -95,9 +377,7 @@ export const ProjectToolbar = () => { }; return ( - - - + <> {loadMessage ? ( {loadMessage} @@ -115,58 +395,6 @@ export const ProjectToolbar = () => { /> )} - - - - {isMobile ? ( - <> - - - - - - - - - - ) : ( - <> - - - - - - - )} - + ); }; diff --git a/src/components/app-bars/index.ts b/src/components/app-bars/index.ts index 3eee7495..75cf6d62 100644 --- a/src/components/app-bars/index.ts +++ b/src/components/app-bars/index.ts @@ -2,6 +2,5 @@ export { ImageViewerAppBar } from "./ImageViewerAppBar"; export { ProjectAppBar } from "./ProjectAppBar"; export { FitSegmenterDialogAppBar } from "./FitSegmenterDialogAppBar"; export { FitClassifierDialogAppBar } from "./FitClassifierDialogAppBar"; -export { GridItemActionBar } from "./GridItemActionBar"; export { AlertBar } from "./AlertBar"; export { MeasurementsAppBar } from "./MeasurementsAppBar"; diff --git a/src/components/drawers/ImageToolDrawer/ImageToolDrawer.tsx b/src/components/drawers/ImageToolDrawer/ImageToolDrawer.tsx index 0247612b..d1dbc047 100644 --- a/src/components/drawers/ImageToolDrawer/ImageToolDrawer.tsx +++ b/src/components/drawers/ImageToolDrawer/ImageToolDrawer.tsx @@ -71,6 +71,7 @@ const imageTools: Record = { description: "-", options: , hotkey: "M", + mobile: true, }, learning: { icon: (color) => , diff --git a/src/components/image-grids/ImageGrid/ImageGrid.tsx b/src/components/image-grids/ImageGrid/ImageGrid.tsx index eb595d04..b73f24e4 100644 --- a/src/components/image-grids/ImageGrid/ImageGrid.tsx +++ b/src/components/image-grids/ImageGrid/ImageGrid.tsx @@ -3,24 +3,14 @@ import { useDispatch, useSelector } from "react-redux"; import { Container, Grid } from "@mui/material"; -import { useDialogHotkey } from "hooks"; - import { projectSlice } from "store/project"; -import { HotkeyView } from "utils/common/enums"; -import { imageViewerSlice } from "store/imageViewer"; -import { DialogWithAction } from "components/dialogs"; import { selectThingsOfKind } from "store/data"; import { ProjectGridItem } from "../ProjectGridItem"; import { useSortFunction } from "hooks/useSortFunction/useSortFunction"; -import { GridItemActionBar } from "components/app-bars"; import { DropBox } from "components/styled-components/DropBox/DropBox"; -import { - selectActiveKindId, - selectThingFilters, -} from "store/project/selectors"; +import { selectThingFilters } from "store/project/selectors"; import { isFiltered } from "utils/common/helpers"; -import { dataSlice } from "store/data/dataSlice"; import { selectActiveSelectedThingIds } from "store/project/reselectors"; import { AnnotationObject, ImageObject } from "store/data/types"; @@ -31,7 +21,6 @@ const max_images = 1000; //number of images from the project that we'll show export const ImageGrid = ({ kind }: { kind: string }) => { const dispatch = useDispatch(); - const activeKind = useSelector(selectActiveKindId); const things = useSelector(selectThingsOfKind)(kind); const thingFilters = useSelector(selectThingFilters)[kind]; const selectedThingIds = useSelector(selectActiveSelectedThingIds); @@ -39,38 +28,6 @@ export const ImageGrid = ({ kind }: { kind: string }) => { //const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu(); - const { - onClose: handleCloseDeleteImagesDialog, - onOpen: onOpenDeleteImagesDialog, - open: deleteImagesDialogisOpen, - } = useDialogHotkey(HotkeyView.DialogWithAction); - - const handleSelectAll = useCallback(() => { - dispatch( - projectSlice.actions.selectThings({ - ids: things.map((thing) => thing.id), - }) - ); - }, [things, dispatch]); - - const handleDeselectAll = () => { - dispatch( - projectSlice.actions.deselectThings({ - ids: things.map((thing) => thing.id), - }) - ); - }; - - const handleDelete = () => { - dispatch( - dataSlice.actions.deleteThings({ - thingIds: selectedThingIds, - disposeColorTensors: true, - isPermanent: true, - }) - ); - }; - const handleSelectThing = useCallback( (id: string, selected: boolean) => { if (selected) { @@ -82,10 +39,6 @@ export const ImageGrid = ({ kind }: { kind: string }) => { [dispatch] ); - const handleOpenImageViewer = () => { - dispatch(imageViewerSlice.actions.prepareImageViewer({ selectedThingIds })); - }; - // useHotkeys("esc", () => handleDeselectAll(), HotkeyView.ProjectView, { // enabled: tabIndex === 0, // }); @@ -114,73 +67,29 @@ export const ImageGrid = ({ kind }: { kind: string }) => { })} maxWidth={false} > -
{ - dispatch( - projectSlice.actions.deselectThings({ ids: selectedThingIds }) - ); + - - {things - .slice(0, max_images) - .sort(sortFunction) - .map((thing: ImageObject | AnnotationObject) => ( - - ))} - - - {/**/} -
+ {things + .slice(0, max_images) + .sort(sortFunction) + .map((thing: ImageObject | AnnotationObject) => ( + + ))} + - {kind === activeKind && ( - - )} - - 1 ? "s" : "" - }?`} - content={`Objects will be deleted from the project. ${ - kind === "Image" - ? "Associated annotations will also be removed." - : "" - } `} - onConfirm={handleDelete} - isOpen={deleteImagesDialogisOpen} - onClose={handleCloseDeleteImagesDialog} - /> ); diff --git a/src/components/styled-components/SortSelection/SortSelection.tsx b/src/components/styled-components/SortSelection/SortSelection.tsx index 625bacfa..0a2ca729 100644 --- a/src/components/styled-components/SortSelection/SortSelection.tsx +++ b/src/components/styled-components/SortSelection/SortSelection.tsx @@ -28,7 +28,7 @@ export const SortSelection = () => {