diff --git a/src/ItemList.css b/src/ItemList.css
new file mode 100644
index 0000000..a8c39d7
--- /dev/null
+++ b/src/ItemList.css
@@ -0,0 +1,12 @@
+.item-list-row-exit {
+ opacity: 1;
+ height: auto;
+}
+
+.item-list-row-exit-active {
+ opacity: 0;
+ height: 0 !important;
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ transition: all 0.5s;
+}
diff --git a/src/ItemList.js b/src/ItemList.js
index 7080802..3412f52 100644
--- a/src/ItemList.js
+++ b/src/ItemList.js
@@ -1,19 +1,42 @@
import React from "react";
-import { Box, Image, PseudoBox, Stack, Skeleton } from "@chakra-ui/core";
+import { CSSTransition, TransitionGroup } from "react-transition-group";
+import {
+ Box,
+ Flex,
+ IconButton,
+ Image,
+ PseudoBox,
+ Stack,
+ Skeleton,
+ Tooltip,
+} from "@chakra-ui/core";
-function ItemList({ items, wornItemIds, dispatchToOutfit }) {
+import "./ItemList.css";
+
+function ItemList({ items, outfitState, dispatchToOutfit }) {
return (
-
- {items.map((item) => (
-
-
-
- ))}
-
+
+
+ {items.map((item) => (
+ {
+ e.style.height = e.offsetHeight + "px";
+ }}
+ >
+
+
+
+
+ ))}
+
+
);
}
@@ -29,7 +52,12 @@ function ItemListSkeleton({ count }) {
);
}
-function Item({ item, isWorn, dispatchToOutfit }) {
+function Item({ item, outfitState, dispatchToOutfit }) {
+ const { wornItemIds, allItemIds } = outfitState;
+
+ const isWorn = wornItemIds.includes(item.id);
+ const isInOutfit = allItemIds.includes(item.id);
+
return (
{item.name}
+
+ {isInOutfit && (
+
+ {
+ e.stopPropagation();
+ dispatchToOutfit({ type: "removeItem", itemId: item.id });
+ }}
+ opacity="0"
+ transitionProperty="opacity color"
+ transitionDuration="0.2s"
+ _groupHover={{
+ opacity: 1,
+ transitionDuration: "0.5s",
+ }}
+ _hover={{
+ opacity: 1,
+ color: "gray.800",
+ backgroundColor: "gray.200",
+ }}
+ _focus={{
+ opacity: 1,
+ color: "gray.800",
+ backgroundColor: "gray.200",
+ }}
+ />
+
+ )}
);
}
diff --git a/src/ItemsPanel.css b/src/ItemsPanel.css
new file mode 100644
index 0000000..cd5fa7b
--- /dev/null
+++ b/src/ItemsPanel.css
@@ -0,0 +1,12 @@
+.items-panel-zone-exit {
+ opacity: 1;
+ height: auto;
+}
+
+.items-panel-zone-exit-active {
+ opacity: 0;
+ height: 0 !important;
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ transition: all 0.5s;
+}
diff --git a/src/ItemsPanel.js b/src/ItemsPanel.js
new file mode 100644
index 0000000..6429243
--- /dev/null
+++ b/src/ItemsPanel.js
@@ -0,0 +1,115 @@
+import React from "react";
+import {
+ Box,
+ Editable,
+ EditablePreview,
+ EditableInput,
+ Flex,
+ IconButton,
+ PseudoBox,
+ Skeleton,
+} from "@chakra-ui/core";
+import { CSSTransition, TransitionGroup } from "react-transition-group";
+
+import { Delay, Heading1, Heading2 } from "./util";
+import ItemList, { ItemListSkeleton } from "./ItemList";
+
+import "./ItemsPanel.css";
+
+function ItemsPanel({ outfitState, loading, dispatchToOutfit }) {
+ const { zonesAndItems, wornItemIds } = outfitState;
+
+ return (
+
+
+
+ {loading &&
+ [1, 2, 3].map((i) => (
+
+
+
+
+
+
+ ))}
+ {!loading && (
+
+ {zonesAndItems.map(({ zone, items }) => (
+ {
+ e.style.height = e.offsetHeight + "px";
+ }}
+ >
+
+ {zone.label}
+
+
+
+ ))}
+
+ )}
+
+
+ );
+}
+
+function OutfitHeading({ outfitState, dispatchToOutfit }) {
+ return (
+
+
+
+
+ dispatchToOutfit({ type: "rename", outfitName: value })
+ }
+ >
+ {({ isEditing, onRequestEdit }) => (
+ <>
+
+
+ {!isEditing && (
+
+ )}
+ >
+ )}
+
+
+
+
+ );
+}
+
+function OutfitNameEditButton({ onRequestEdit }) {
+ return (
+
+
+
+ );
+}
+
+export default ItemsPanel;
diff --git a/src/SearchPanel.js b/src/SearchPanel.js
index b47332f..9408a6c 100644
--- a/src/SearchPanel.js
+++ b/src/SearchPanel.js
@@ -94,7 +94,7 @@ function SearchResults({ query, outfitState, dispatchToOutfit }) {
return (
);
diff --git a/src/WardrobePage.js b/src/WardrobePage.js
index 440f6c0..5a5ab29 100644
--- a/src/WardrobePage.js
+++ b/src/WardrobePage.js
@@ -1,9 +1,6 @@
import React from "react";
import {
Box,
- Editable,
- EditablePreview,
- EditableInput,
Grid,
Icon,
IconButton,
@@ -11,14 +8,10 @@ import {
InputGroup,
InputLeftElement,
InputRightElement,
- PseudoBox,
- Skeleton,
- Stack,
useToast,
} from "@chakra-ui/core";
-import { Delay, Heading1, Heading2 } from "./util";
-import ItemList, { ItemListSkeleton } from "./ItemList";
+import ItemsPanel from "./ItemsPanel";
import OutfitPreview from "./OutfitPreview";
import SearchPanel from "./SearchPanel";
import useOutfitState from "./useOutfitState.js";
@@ -129,90 +122,4 @@ function SearchToolbar({ query, onChange }) {
);
}
-function ItemsPanel({ outfitState, loading, dispatchToOutfit }) {
- const { zonesAndItems, wornItemIds } = outfitState;
-
- return (
-
-
-
- {loading &&
- [1, 2, 3].map((i) => (
-
-
-
-
-
-
- ))}
- {!loading &&
- zonesAndItems.map(({ zone, items }) => (
-
- {zone.label}
- i.id)
- .filter((id) => wornItemIds.includes(id))}
- dispatchToOutfit={dispatchToOutfit}
- />
-
- ))}
-
-
- );
-}
-
-function OutfitHeading({ outfitState, dispatchToOutfit }) {
- return (
-
-
-
-
- dispatchToOutfit({ type: "rename", outfitName: value })
- }
- >
- {({ isEditing, onRequestEdit }) => (
- <>
-
-
- {!isEditing && (
-
- )}
- >
- )}
-
-
-
-
- );
-}
-
-function OutfitNameEditButton({ onRequestEdit }) {
- return (
-
-
-
- );
-}
-
export default WardrobePage;
diff --git a/src/useOutfitState.js b/src/useOutfitState.js
index 51b115a..76412a7 100644
--- a/src/useOutfitState.js
+++ b/src/useOutfitState.js
@@ -133,6 +133,15 @@ const outfitStateReducer = (apolloClient) => (baseState, action) => {
wornItemIds.delete(itemId);
closetedItemIds.add(itemId);
});
+ case "removeItem":
+ return produce(baseState, (state) => {
+ const { wornItemIds, closetedItemIds } = state;
+ const { itemId } = action;
+
+ // Remove this item from both the worn set and the closet.
+ wornItemIds.delete(itemId);
+ closetedItemIds.delete(itemId);
+ });
default:
throw new Error(`unexpected action ${action}`);
}