zone badges on items

These are nice! :)

The `hideSimpleZones` option I'm not sure about yet, but I figure that:
1. For a new user just doing simple outfits, I feel like the double data on the items page just looks silly, so I want to streamline for that
2. But I _do_ want to let the user think about zone complexity when things _are_ multi-zone.

I did also consider just hiding the zone badge for the header you're under, but I figured the consistency of having the item and its badges look the same in all the places in the list was more important.
This commit is contained in:
Emi Matchu 2020-09-01 01:18:24 -07:00
parent 3a6e3fac8e
commit dec9d76601
4 changed files with 94 additions and 3 deletions

View file

@ -8,10 +8,16 @@ import {
Image, Image,
Skeleton, Skeleton,
Tooltip, Tooltip,
Wrap,
useColorModeValue, useColorModeValue,
useTheme, useTheme,
} from "@chakra-ui/core"; } from "@chakra-ui/core";
import { EditIcon, DeleteIcon, InfoIcon } from "@chakra-ui/icons"; import {
EditIcon,
DeleteIcon,
InfoIcon,
NotAllowedIcon,
} from "@chakra-ui/icons";
import loadable from "@loadable/component"; import loadable from "@loadable/component";
import { safeImageUrl } from "../util"; import { safeImageUrl } from "../util";
@ -39,9 +45,27 @@ const LoadableItemSupportDrawer = loadable(() =>
* wearing/unwearing items being noticeably slower on lower-power * wearing/unwearing items being noticeably slower on lower-power
* devices. * devices.
*/ */
function Item({ item, itemNameId, isWorn, isInOutfit, dispatchToOutfit }) { function Item({
item,
itemNameId,
isWorn,
isInOutfit,
dispatchToOutfit,
hideSimpleZones = false,
}) {
const [supportDrawerIsOpen, setSupportDrawerIsOpen] = React.useState(false); const [supportDrawerIsOpen, setSupportDrawerIsOpen] = React.useState(false);
const occupiedZoneLabels = getZoneLabels(
item.appearanceOn.layers.map((l) => l.zone)
);
const restrictedZoneLabels = getZoneLabels(
item.appearanceOn.restrictedZones.filter((z) => z.isCommonlyUsedByItems)
);
const zonesAreSimple =
occupiedZoneLabels.length <= 1 && restrictedZoneLabels.length === 0;
const shouldHideZones = hideSimpleZones && zonesAreSimple;
const shouldShowZones = !shouldHideZones;
return ( return (
<> <>
<ItemContainer> <ItemContainer>
@ -55,7 +79,36 @@ function Item({ item, itemNameId, isWorn, isInOutfit, dispatchToOutfit }) {
<ItemName id={itemNameId} isWorn={isWorn}> <ItemName id={itemNameId} isWorn={isWorn}>
{item.name} {item.name}
</ItemName> </ItemName>
<Box>{item.isNc && <Badge colorScheme="cyan">NC</Badge>}</Box> <Wrap spacing="2" marginTop="1">
{shouldShowZones &&
occupiedZoneLabels.map((zoneLabel) => (
<Badge key={zoneLabel}>{getZoneShorthand(zoneLabel)}</Badge>
))}
{shouldShowZones &&
restrictedZoneLabels.map((zoneLabel) => (
<Tooltip
label={
<Box textAlign="center">
Restricted: This isn't a {zoneLabel} item, but you can't
wear {zoneLabel} items with it
</Box>
}
placement="top"
openDelay={250}
>
<Badge
key={zoneLabel}
display="flex"
flexDirection="row"
alignItems="center"
>
{getZoneShorthand(zoneLabel)}
<NotAllowedIcon marginLeft="1" />
</Badge>
</Tooltip>
))}
{item.isNc && <Badge colorScheme="cyan">NC</Badge>}
</Wrap>
</Box> </Box>
<Box flex="0 0 auto"> <Box flex="0 0 auto">
<SupportOnly> <SupportOnly>
@ -328,6 +381,32 @@ export function ItemListSkeleton({ count }) {
); );
} }
/**
* getZoneLabels returns the set of labels for the given zones. Sometimes an
* item occupies multiple zones of the same name, so it's especially important
* to de-duplicate them here!
*/
function getZoneLabels(zones) {
let labels = zones.map((z) => z.label);
labels = new Set(labels);
labels = [...labels].sort();
return labels;
}
/**
* getZoneShorthand returns a potentially shortened version of the zone label,
* to make the Item badges a bit less bulky!
*/
function getZoneShorthand(zoneLabel) {
return zoneLabel
.replace("Background Item", "BG Item")
.replace("Foreground Item", "FG Item")
.replace("Lower-body", "Lower")
.replace("Upper-body", "Upper")
.replace("Transient", "Trans")
.replace("Biology", "Bio");
}
/** /**
* containerHasFocus is a common CSS selector, for the case where our parent * containerHasFocus is a common CSS selector, for the case where our parent
* .item-container is hovered or the adjacent hidden radio/checkbox is * .item-container is hovered or the adjacent hidden radio/checkbox is

View file

@ -123,6 +123,7 @@ function ItemZoneGroup({ zoneLabel, items, outfitState, dispatchToOutfit }) {
isWorn={outfitState.wornItemIds.includes(item.id)} isWorn={outfitState.wornItemIds.includes(item.id)}
isInOutfit={outfitState.allItemIds.includes(item.id)} isInOutfit={outfitState.allItemIds.includes(item.id)}
dispatchToOutfit={dispatchToOutfit} dispatchToOutfit={dispatchToOutfit}
hideSimpleZones
/> />
</label> </label>
</CSSTransition> </CSSTransition>

View file

@ -229,6 +229,7 @@ function useSearchResults(query, outfitState) {
id id
name name
thumbnailUrl thumbnailUrl
isNc
appearanceOn(speciesId: $speciesId, colorId: $colorId) { appearanceOn(speciesId: $speciesId, colorId: $colorId) {
# This enables us to quickly show the item when the user clicks it! # This enables us to quickly show the item when the user clicks it!
@ -242,6 +243,11 @@ function useSearchResults(query, outfitState) {
label @client label @client
} }
} }
restrictedZones {
id
label @client
isCommonlyUsedByItems @client
}
} }
} }
} }

View file

@ -51,6 +51,11 @@ function useOutfitState() {
label @client label @client
} }
} }
restrictedZones {
id
label @client
isCommonlyUsedByItems @client
}
} }
} }