diff --git a/package.json b/package.json index 6a3d290..80d6ba5 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "apollo-server-core": "^2.12.0", "apollo-server-env": "^2.4.3", "dataloader": "^2.0.0", + "emotion": "^10.0.27", "emotion-theming": "^10.0.27", "graphql": "^15.0.0", "immer": "^6.0.3", diff --git a/src/ItemList.js b/src/ItemList.js index aec43f8..3d7af94 100644 --- a/src/ItemList.js +++ b/src/ItemList.js @@ -1,4 +1,5 @@ import React from "react"; +import { css } from "emotion"; import { CSSTransition, TransitionGroup } from "react-transition-group"; import { Box, @@ -8,6 +9,7 @@ import { PseudoBox, Skeleton, Tooltip, + useTheme, } from "@chakra-ui/core"; import "./ItemList.css"; @@ -39,7 +41,11 @@ function ItemList({ items, outfitState, dispatchToOutfit }) { ); } -function ItemListSkeleton({ count }) { +export function ItemListContainer({ children }) { + return {children}; +} + +export function ItemListSkeleton({ count }) { return ( {Array.from({ length: count }).map((_, i) => ( @@ -51,28 +57,39 @@ function ItemListSkeleton({ count }) { ); } -function Item({ item, outfitState, dispatchToOutfit }) { - const { wornItemIds, allItemIds } = outfitState; - - const isWorn = wornItemIds.includes(item.id); +export function Item({ item, outfitState, dispatchToOutfit }) { + const { allItemIds } = outfitState; const isInOutfit = allItemIds.includes(item.id); + const theme = useTheme(); return ( - - dispatchToOutfit({ - type: isWorn ? "unwearItem" : "wearItem", - itemId: item.id, - }) + border="1px" + borderColor="transparent" + className={ + "item-container " + + css` + input:active + & { + border-color: ${theme.colors.green["800"]}; + } + input:focus + & { + border-style: dotted; + border-color: ${theme.colors.gray["400"]}; + } + ` } > - + - {item.name} + {item.name} {isInOutfit && ( @@ -105,7 +122,7 @@ function Item({ item, outfitState, dispatchToOutfit }) { /> )} - + ); } @@ -119,53 +136,61 @@ function ItemSkeleton() { ); } -function ItemThumbnail({ src, isWorn }) { +function ItemThumbnail({ src }) { + const theme = useTheme(); return ( - - + ); } -function ItemName({ children, isWorn }) { +function ItemName({ children }) { + const theme = useTheme(); + return ( - {children} - + ); } export default ItemList; -export { ItemListSkeleton }; diff --git a/src/ItemsPanel.js b/src/ItemsPanel.js index 88c1213..99c1dde 100644 --- a/src/ItemsPanel.js +++ b/src/ItemsPanel.js @@ -8,11 +8,12 @@ import { IconButton, PseudoBox, Skeleton, + VisuallyHidden, } from "@chakra-ui/core"; import { CSSTransition, TransitionGroup } from "react-transition-group"; import { Delay, Heading1, Heading2 } from "./util"; -import ItemList, { ItemListSkeleton } from "./ItemList"; +import { ItemListContainer, Item, ItemListSkeleton } from "./ItemList"; import "./ItemsPanel.css"; @@ -48,7 +49,8 @@ function ItemsPanel({ outfitState, loading, dispatchToOutfit }) { > {zoneLabel} - { + const itemId = e.target.value; + dispatchToOutfit({ type: "wearItem", itemId }); + }; + + const onToggle = (e) => { + // Clicking the radio button when already selected deselects it - this is + // how you can select none! + const itemId = e.target.value; + if (outfitState.wornItemIds.includes(itemId)) { + // We need the event handler to finish before this, so that simulated + // events don't just come back around and undo it - but we can't just + // solve that with `preventDefault`, because it breaks the radio's + // intended visual updates when we unwear. So, we `setTimeout` to do it + // after all event handlers resolve! + setTimeout(() => dispatchToOutfit({ type: "unwearItem", itemId }), 0); + } + }; + + return ( + + + {items.map((item) => ( + { + e.style.height = e.offsetHeight + "px"; + }} + > + + + ))} + + + ); +} + function OutfitHeading({ outfitState, dispatchToOutfit }) { return ( diff --git a/yarn.lock b/yarn.lock index 78b0f1f..e0c7e9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3974,6 +3974,16 @@ create-ecdh@^4.0.0: bn.js "^4.1.0" elliptic "^6.0.0" +create-emotion@^10.0.27: + version "10.0.27" + resolved "https://registry.yarnpkg.com/create-emotion/-/create-emotion-10.0.27.tgz#cb4fa2db750f6ca6f9a001a33fbf1f6c46789503" + integrity sha512-fIK73w82HPPn/RsAij7+Zt8eCE8SptcJ3WoRMfxMtjteYxud8GDTKKld7MYwAX2TVhrw29uR1N/bVGxeStHILg== + dependencies: + "@emotion/cache" "^10.0.27" + "@emotion/serialize" "^0.11.15" + "@emotion/sheet" "0.9.4" + "@emotion/utils" "0.11.3" + create-hash@^1.1.0, create-hash@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" @@ -4684,6 +4694,14 @@ emotion-theming@^10.0.27: "@emotion/weak-memoize" "0.2.5" hoist-non-react-statics "^3.3.0" +emotion@^10.0.27: + version "10.0.27" + resolved "https://registry.yarnpkg.com/emotion/-/emotion-10.0.27.tgz#f9ca5df98630980a23c819a56262560562e5d75e" + integrity sha512-2xdDzdWWzue8R8lu4G76uWX5WhyQuzATon9LmNeCy/2BHVC6dsEpfhN1a0qhELgtDVdjyEA6J8Y/VlI5ZnaH0g== + dependencies: + babel-plugin-emotion "^10.0.27" + create-emotion "^10.0.27" + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"