~ minimum code to add color chooser 🔦

This commit is contained in:
Will Holcomb
2021-03-18 23:18:10 -04:00
committed by Alec LaLonde
parent 0555deb028
commit f00bee9b41
29 changed files with 1218 additions and 215 deletions

View File

@@ -0,0 +1,3 @@
import { chakra } from '@chakra-ui/react';
export const SVG = chakra('svg');

View File

@@ -9,6 +9,7 @@ export { MetaTile, MetaTileBody, MetaTileHeader } from './MetaTile';
export { ResponsiveText } from './ResponsiveText';
export { SelectSearch, selectStyles } from './SelectSearch';
export { SelectTimeZone } from './SelectTimeZone';
export { SVG } from './SVG'
export { theme as MetaTheme } from './theme';
export { H1, P } from './typography';
export { EmailIcon } from '@chakra-ui/icons';
@@ -20,6 +21,7 @@ export {
ButtonGroup,
ButtonProps,
Center,
ChakraProps,
ChakraProvider,
Container,
CSSReset,

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 141.91 137.27" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="linearGradient1365" x2="1" gradientTransform="matrix(0 49.54 49.54 0 119.75 520.38)" gradientUnits="userSpaceOnUse">
<stop stop-color="#020121" offset="0"/>
<stop stop-color="#2f3e57" offset=".69367"/>
<stop stop-color="#2f3e57" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(-20.653 -9.4842)">
<g transform="matrix(2.7709 0 0 -2.7709 -240.2 1588.7)">
<path d="m118.35 569.3-20.208-22.702c-1.084-1.212-0.224-3.223 1.402-3.223h12.945l-17.785-19.634c-1.256-1.405-0.259-3.365 1.625-3.365h46.848c1.883 0 2.881 1.96 1.625 3.365l-17.786 19.634h12.946c1.625 0 2.486 2.024 1.402 3.236l-20.208 22.664c-0.38 0.425-0.904 0.64-1.426 0.64-0.507 0-1.011-0.203-1.38-0.615" fill="url(#linearGradient1365)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 922 B

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 0 131.71 131.7" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="linearGradient1387" x2="1" gradientTransform="matrix(0 -51.25 -51.25 0 620.06 570.38)" gradientUnits="userSpaceOnUse">
<stop stop-color="#9cce62" offset="0"/>
<stop stop-color="#009b5b" offset=".98876"/>
<stop stop-color="#009b5b" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(-19.625 -6.5215)">
<g transform="matrix(2.5699 0 0 -2.5699 -1508 1472.3)">
<path d="m594.44 544.76c0-14.129 11.496-25.625 25.625-25.625 14.13 0 25.626 11.496 25.626 25.625 0 14.13-11.496 25.624-25.626 25.624-14.129 0-25.625-11.494-25.625-25.624m3.622 0c0 12.133 9.87 22.004 22.003 22.004s22.004-9.871 22.004-22.004-9.871-22.004-22.004-22.004-22.003 9.871-22.003 22.004m8.701 0c0-7.347 5.955-13.303 13.303-13.303 7.347 0 13.302 5.956 13.302 13.303 0 7.346-5.955 13.303-13.302 13.303-7.348 0-13.303-5.957-13.303-13.303" fill="url(#linearGradient1387)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 114.02 136.76" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="linearGradient1005" x2="1" gradientTransform="matrix(0 -49.659 -49.659 0 369.88 421.96)" gradientUnits="userSpaceOnUse">
<stop stop-color="#f15236" offset="0"/>
<stop stop-color="#94136e" offset=".91424"/>
<stop stop-color="#94136e" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(-19.777 -8.3514)">
<g transform="matrix(2.7539 0 0 -2.7539 -941.82 1170.4)">
<path d="m362.63 421.88c2.821-3.011 3.426-9.684 1.671-12.411s-4.115-2.372-5.27-0.152c-1.121 2.151 0.665 5.857 0.665 5.857-10.873-9.792-10.513-22.177-10.513-22.177 0-11.432 9.268-20.701 20.701-20.701 11.434 0 20.702 9.269 20.702 20.701 0 9.513-6.058 16.705-6.058 16.705 1.549-3.357-1.026-7.03-4.644-0.864-1.548 2.637-2.383 5.601-5.517 8.766-3.644 3.681-7.527 4.352-10.162 4.352-0.594 0-1.125-0.034-1.575-0.076" fill="url(#linearGradient1005)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 133.06 137.7" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<defs>
<linearGradient id="linearGradient1049" x2="1" gradientTransform="matrix(0 -48.66 -48.66 0 873.93 421.04)" gradientUnits="userSpaceOnUse">
<stop stop-color="#a4aec4" offset="0"/>
<stop stop-color="#dca8a9" offset="1"/>
</linearGradient>
</defs>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="translate(-15.651 -3.0238)">
<g transform="matrix(2.8298 0 0 -2.8298 -2390.8 1194.5)">
<path d="m868.89 420.34c-1.987-0.58-3.794-1.494-5.074-3.216-0.093-0.124-0.277-0.204-0.434-0.252-2.363-0.712-4.61-1.663-6.614-3.132-0.115-0.084-0.259-0.136-0.396-0.177-1.277-0.38-2.554-0.759-3.833-1.128-0.279-0.08-0.44-0.217-0.535-0.512-0.955-2.95-1.482-5.973-1.573-9.069-0.069-2.313 0.125-4.611 0.581-6.878 0.044-0.219 0.09-0.437 0.139-0.654 0.414-1.863 0.992-3.691 1.738-5.447 0.703-1.756 1.664-3.44 2.718-5.007 0.435-0.647 0.895-1.279 1.378-1.892 0.776-0.987 1.613-1.927 2.504-2.811 1.372-1.359 2.879-2.58 4.491-3.642 1.974-1.3 4.047-2.414 6.324-3.103 1.117-0.339 2.236-0.678 3.358-1.004 0.16-0.046 0.349-0.047 0.511-0.01 4.83 1.079 9.11 3.251 12.842 6.499 1.624 1.414 3.082 2.986 4.37 4.712 1.836 2.46 3.267 5.142 4.283 8.037 1.357 3.867 1.942 7.85 1.726 11.952-0.15 2.854-0.674 5.63-1.542 8.348-0.077 0.242-0.198 0.381-0.439 0.453-0.741 0.221-1.482 0.44-2.214 0.687-0.873 0.296-1.843 0.329-2.59 0.973-0.232 0.199-0.495 0.365-0.758 0.519-1.69 0.982-3.482 1.726-5.354 2.286-0.2 0.06-0.384 0.221-0.539 0.372-0.422 0.405-0.789 0.873-1.241 1.237-1.461 1.183-3.184 1.818-5 2.173-1.294 0.252-2.587 0.386-3.876 0.386-1.661 0-3.315-0.222-4.951-0.7" fill="url(#linearGradient1049)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 136.2 130.09" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<defs>
<linearGradient id="linearGradient1439" x2="1" gradientTransform="matrix(0 -49.577 -49.577 0 1118.7 569.96)" gradientUnits="userSpaceOnUse">
<stop stop-color="#09b9f2" offset="0"/>
<stop stop-color="#09b9f2" offset=".080645"/>
<stop stop-color="#09b9f2" offset=".2827"/>
<stop stop-color="#04468b" offset="1"/>
</linearGradient>
</defs>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="translate(-19.169 -4.0977)">
<g transform="matrix(2.624 0 0 -2.624 -2848.2 1499.6)">
<path d="m1116.8 568.76-6.352-12.871c-0.314-0.636-0.92-1.076-1.621-1.177l-14.205-2.065c-1.765-0.256-2.47-2.426-1.192-3.672l10.278-10.019c0.507-0.494 0.739-1.207 0.619-1.905l-2.426-14.146c-0.302-1.759 1.544-3.101 3.124-2.27l12.704 6.679c0.626 0.33 1.376 0.33 2.003 0l12.705-6.679c1.579-0.831 3.425 0.511 3.124 2.27l-2.427 14.146c-0.119 0.698 0.112 1.411 0.619 1.905l10.279 10.019c1.277 1.246 0.572 3.416-1.194 3.672l-14.204 2.065c-0.701 0.101-1.307 0.541-1.621 1.177l-6.352 12.871c-0.395 0.8-1.163 1.2-1.931 1.2-0.767 0-1.535-0.4-1.93-1.2" fill="url(#linearGradient1439)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

Before

Width:  |  Height:  |  Size: 236 KiB

After

Width:  |  Height:  |  Size: 236 KiB

View File

@@ -0,0 +1,137 @@
/* eslint no-bitwise: "off" */
import { Box, ChakraProps, Flex, SVG } from '@metafam/ds';
import { FlexContainer } from 'components/Container';
import {
colors,
getPersonalityInfo,
images,
} from 'graphql/getPersonalityInfo';
import { PersonalityOption } from 'graphql/types';
import React, { useEffect, useState } from 'react';
// This is just verbose, so I am pulling it out to
// save space in the main template
const maskImageStyle = (
({ url }: { url: string }): Record<string, string> => ({
maskImage: `url(${url})`,
maskSize: 'contain',
maskPosition: 'center',
maskRepeat: 'no-repeat',
WebkitMaskImage: `url(${url})`,
WebkitMaskSize: 'contain',
WebkitMaskPosition: 'center',
WebkitMaskRepeat: 'no-repeat',
})
);
/* The color bar is below the attribute selection screen,
* and shows an equally proportioned set of colors with
* monochrome icons above them and a term for the
* combination below.
*/
export const ColorBar = (
(
{ mask = 0, ...props }:
ChakraProps & { mask: number | undefined },
): JSX.Element => {
const [parts, setParts] = (
useState<Array<PersonalityOption>>([])
);
const [types, setTypes] = (
useState<Record<number, PersonalityOption>>({})
);
const load = async () => {
const { parts: ps, types: ts } = (
await getPersonalityInfo()
)
setParts(ps)
setTypes(ts)
}
useEffect(() => { load() }, []);
return (
<Flex
direction='column'
maxW='100%'
className="color-bar"
{...props}
>
<Flex maxW='100%' minH='1.5rem' mb='1rem'>
{parts.map((part) => {
const set = ((mask & part.mask) !== 0)
return (
!set // if the bit isn't set
? ( null ) // return null for map to work
: (
<Flex
key={part.mask}
grow={1} justify='center'
opacity={0.75}
>
<Box
bgColor='white'
h={6} w={6}
title={part.name}
style={maskImageStyle({ url: images[part.mask] })}
/>
</Flex>
)
)
})}
</Flex>
<Flex
minH='calc(1.5rem + 4px)' maxW='100%'
border='2px' borderRadius={3}
>
{parts.map((part) => (
((mask & part.mask) === 0)
? ( null )
: (
<Flex
key={part.mask}
grow={1}
h='1.5rem'
>
<SVG
viewBox='0 0 100 100'
preserveAspectRatio='none'
w='100%'
>
<defs>
<linearGradient
id="shading"
gradientTransform="rotate(90)"
>
<stop
offset="5%"
stopColor="black" stopOpacity={0.5}
/>
<stop
offset="95%"
stopColor="white" stopOpacity={0.25}
/>
</linearGradient>
</defs>
<rect
width='100%' height='100%'
fill={colors[part.mask]}
/>
<rect
width='100%' height='100%'
fill='url(#shading)'
/>
</SVG>
</Flex>
)
))}
</Flex>
<FlexContainer mt={1}>
<q>{types?.[mask]?.name}</q>
</FlexContainer>
</Flex>
)
}
);

View File

@@ -1,56 +1,73 @@
import { Avatar, Box, HStack, Image, Text, VStack } from '@metafam/ds';
import {
Avatar, Box, Flex, HStack, Link, Text, VStack,
} from '@metafam/ds';
import { PlayerFragmentFragment } from 'graphql/autogen/types';
import React from 'react';
import { getPersonalityInfo } from 'graphql/getPersonalityInfo';
import { PersonalityOption } from 'graphql/types';
import React, { useEffect } from 'react';
import {
getPlayerDescription,
getPlayerImage,
getPlayerName,
} from 'utils/playerHelpers';
import { PersonalityTypes } from '../../../graphql/types';
import { FlexContainer } from '../../Container';
import { ProfileSection } from '../../ProfileSection';
import { ColorBar } from '../ColorBar';
import { PlayerContacts } from '../PlayerContacts';
import { PlayerBrightId } from './PlayerBrightId';
import { PlayerCollab } from './PlayerCollab';
const BIO_LENGTH = 240;
const MAX_BIO_LENGTH = 240;
type Props = { player: PlayerFragmentFragment };
export const PlayerHero: React.FC<Props> = ({ player }) => {
const description = getPlayerDescription(player);
const [show, setShow] = React.useState(
getPlayerDescription(player).length < BIO_LENGTH,
description.length <= MAX_BIO_LENGTH,
);
const [types, setTypes] = React.useState<{
[any: string]: PersonalityOption;
}>();
const mask = player?.ColorAspect?.mask;
const type = mask && types?.[mask];
const loadTypes = async () => {
const { types: list } = await getPersonalityInfo();
setTypes(list);
};
useEffect(() => { loadTypes(); }, []);
return (
<ProfileSection>
<VStack spacing={8}>
<Avatar
w={{ base: '32', md: '56' }}
h={{ base: '32', md: '56' }}
w={{ base: 32, md: 56 }}
h={{ base: 32, md: 56 }}
src={getPlayerImage(player)}
name={getPlayerName(player)}
/>
<Box textAlign="center">
<Text fontSize="xl" fontFamily="heading" mb="1">
<Text fontSize="xl" fontFamily="heading" mb={1}>
{getPlayerName(player)}
</Text>
<PlayerBrightId player={player} />
</Box>
<Box>
<Text>
{`${getPlayerDescription(player).substring(
0,
show ? getPlayerDescription(player).length : BIO_LENGTH,
)}${show ? '' : '...'} `}
{getPlayerDescription(player).length > BIO_LENGTH && (
{show
? description
: `${description.substring(0, MAX_BIO_LENGTH - 9)}`
}
{description.length > MAX_BIO_LENGTH && (
<Text
as="span"
fontFamily="body"
fontSize="xs"
color="cyanText"
cursor="pointer"
onClick={() => setShow(!show)}
onClick={() => setShow(s => !s)}
pl={1}
>
Read {show ? 'less' : 'more'}
</Text>
@@ -58,38 +75,50 @@ export const PlayerHero: React.FC<Props> = ({ player }) => {
</Text>
</Box>
<HStack mt="2">
<HStack mt={2}>
<PlayerContacts player={player} />
</HStack>
<Box w="100%">
<PlayerCollab player={player} />
</Box>
{player.EnneagramType && (
<HStack spacing={4}>
<Image
w="100%"
maxW="4rem"
src={PersonalityTypes[player.EnneagramType.name].image}
alt={player.EnneagramType.name}
style={{ mixBlendMode: 'color-dodge' }}
/>
<FlexContainer align="stretch">
<Text color="white" fontWeight="bold">
{player.EnneagramType.name}
</Text>
<Text color="blueLight">{player.EnneagramType.description}</Text>
</FlexContainer>
</HStack>
)}
{player.playerType?.title ? (
<FlexContainer align="stretch">
<Text color="white" fontWeight="bold">
{player.playerType.title.toUpperCase()}
{type && types && (
<Flex direction="column" id="color" mb={0} w="100%">
<Text
fontSize="xs" color="blueLight"
casing="uppercase"
mb={3}
textAlign="left"
>
Color Disposition
</Text>
<Link
isExternal
href={`//dysbulic.github.io/5-color-radar/#/combos/${type.mask.toString(2)}`}
maxH='6rem'
>
<Flex justify="center">
<ColorBar mask={type.mask}/>
</Flex>
</Link>
<Text color="blueLight" mt={4} style={{ textIndent: 16 }}>
{type.description}
</Text>
</Flex>
)}
{player.playerType?.title && (
<FlexContainer align="stretch">
<Text
color="white" fontWeight="bold"
casing="uppercase"
>
{player.playerType.title}
</Text>
<Text color="blueLight" style={{ textIndent: 16 }}>
{player.playerType.description}
</Text>
<Text color="blueLight">{player.playerType.description}</Text>
</FlexContainer>
) : null}
)}
</VStack>
</ProfileSection>
);
};
}

View File

@@ -1,56 +1,63 @@
/* eslint no-bitwise: "off" */
import {
HStack,
Button,
Flex,
Image,
MetaButton,
MetaHeading,
SimpleGrid,
Text,
useToast,
} from '@metafam/ds';
import { FlexContainer } from 'components/Container';
import { MetaLink } from 'components/Link';
import { ColorBar } from 'components/Player/ColorBar';
import { useSetupFlow } from 'contexts/SetupContext';
import { useUpdateAboutYouMutation } from 'graphql/autogen/types';
import { PersonalityType } from 'graphql/types';
import { images as BaseImages } from 'graphql/getPersonalityInfo';
import { PersonalityOption } from 'graphql/types';
import { useUser } from 'lib/hooks';
import React, { useState } from 'react';
import React, { useCallback } from 'react';
export type SetupPersonalityTypeProps = {
personalityTypeChoices: Array<PersonalityType>;
personalityType: PersonalityType | undefined;
setPersonalityType: React.Dispatch<
React.SetStateAction<PersonalityType | undefined>
// keyed on a bitmask of the format 0bWUBRG
personalityTypes: { [x: number]: PersonalityOption };
colorMask: number | undefined;
setColorMask: React.Dispatch<
React.SetStateAction<number | undefined>
>;
};
export const SetupPersonalityType: React.FC<SetupPersonalityTypeProps> = ({
personalityTypeChoices,
personalityType,
setPersonalityType,
export const SetupPersonalityType: (
React.FC<SetupPersonalityTypeProps>
) = ({
personalityTypes, colorMask, setColorMask,
}) => {
const { onNextPress, nextButtonLabel } = useSetupFlow();
const { user } = useUser({ redirectTo: '/' });
const toast = useToast();
const [updateAboutYouRes, updateAboutYou] = (
useUpdateAboutYouMutation()
);
const [updateAboutYouRes, updateAboutYou] = useUpdateAboutYouMutation();
const [loading, setLoading] = useState(false);
const handleNextPress = async () => {
const handleNextPress = useCallback(async () => {
if (!user) return;
setLoading(true);
if (user.player?.EnneagramType?.name !== personalityType?.name) {
if (user.player?.ColorAspect?.mask !== colorMask) {
const { error } = await updateAboutYou({
playerId: user.id,
input: {
enneagram: personalityType?.name,
color_mask: colorMask,
},
});
if (error) {
console.warn(error); // eslint-disable-line no-console
toast({
title: 'Error',
description: 'Unable to update personality type. The octo is sad 😢',
description: (
'Unable to update personality type. The octo is sad. 😢'
),
status: 'error',
isClosable: true,
});
@@ -60,64 +67,130 @@ export const SetupPersonalityType: React.FC<SetupPersonalityTypeProps> = ({
}
onNextPress();
}, [colorMask, onNextPress, toast, updateAboutYou, user]);
// mask should always only have at most a single bit set
const toggleMaskElement = (mask = 0): void => {
setColorMask((current = 0) => {
if ((mask & current) > 0) { // if the bit in mask is set
return current & ~mask; // unset it
}
return current | mask; // otherwise set it
})
};
return (
<FlexContainer>
<MetaHeading mb={5} textAlign="center">
Personality Type
</MetaHeading>
<Text mb={10}>
{`Please select your personality type below. Not sure what type you are? `}
<MetaLink href="https://enneagramtest.net/" isExternal>
Take a quick test.
</MetaLink>
</Text>
<SimpleGrid columns={[1, null, 2, 3]} spacing="8">
{personalityTypeChoices.map((p: PersonalityType) => (
<HStack
key={p.id}
p={6}
spacing={4}
bgColor={
personalityType && personalityType.id === p.id
? 'purpleBoxDark'
: 'purpleBoxLight'
}
borderRadius="0.5rem"
_hover={{ bgColor: 'purpleBoxDark' }}
transition="background 0.25s"
cursor="pointer"
onClick={() => setPersonalityType(p)}
border="2px"
borderColor={
personalityType && personalityType.id === p.id
? 'purple.400'
: 'transparent'
}
<FlexContainer maxW='100%'>
<Flex direction='column'>
<MetaHeading mb={5} textAlign="center">
Person&#xAD;ality Type
</MetaHeading>
<Text mb={10}>
Please select your personality components below.
Not sure what type you are?
<Text as="span"> Take </Text>
<MetaLink
href="//dysbulic.github.io/5-color-radar/#/explore/"
isExternal
>
<Image
w="100%"
maxW="4rem"
src={p.image}
alt={p.name}
style={{ mixBlendMode: 'color-dodge' }}
/>
<FlexContainer align="stretch">
<Text color="white" fontWeight="bold">
{p.name}
</Text>
<Text color="blueLight">{p.description}</Text>
</FlexContainer>
</HStack>
))}
</SimpleGrid>
a quick exam
</MetaLink>
<Text as="span"> or </Text>
<MetaLink
href="//dysbulic.github.io/5-color-radar/#/test/"
isExternal
>
a longer quiz
</MetaLink>
.
</Text>
</Flex>
<FlexContainer
grow={1} spacing={8} maxW='70rem'
direction='row' wrap='wrap'
id="colors"
>
{Object.entries(BaseImages)
.reverse().map(
([orig, image], idx) => {
const option = personalityTypes[parseInt(orig, 10)]
const { mask = 0 } = (option ?? {})
const selected = (((colorMask ?? 0) & mask) > 0)
return (
<Button
key={mask}
display="flex"
direction="row"
p={6} m={2} h="auto" spacing={4}
borderRadius={8}
cursor="pointer"
onClick={() => toggleMaskElement(mask)}
autoFocus={idx === 0} // Doesn't work
ref={input => {
if (idx === 0 && !input?.getAttribute('focused-once')) {
input?.focus()
input?.setAttribute('focused-once', 'true')
}
}}
onKeyPress={(e) => {
if (e.key === 'Enter') {
handleNextPress()
e.preventDefault()
}
}}
transition="background 0.25s, filter 0.5s"
bgColor={
selected ? 'purpleBoxDark' : 'purpleBoxLight'
}
_hover={{
filter: 'hue-rotate(25deg)',
}}
_focus={{
borderColor: '#FFFFFF55',
outline: 'none',
}}
_active={{ bg: (
selected ? 'purpleBoxDark' : 'purpleBoxLight'
) }}
borderWidth={2}
borderColor={
selected ? 'purple.400' : 'transparent'
}
>
<Image
w="100%" maxW={16} h={16}
src={image} alt={option.name}
filter="drop-shadow(0px 0px 3px black)"
/>
<FlexContainer align="stretch" ml={2}>
<Text
color="white"
casing="uppercase" textAlign="left"
>
{option.name}
</Text>
<Text
color="blueLight" fontWeight="normal"
whiteSpace="initial" textAlign="left"
>
{option.description}
</Text>
</FlexContainer>
</Button>
)
},
)
}
</FlexContainer>
<ColorBar mask={colorMask} w="min(100vw, 30rem)"/>
<MetaButton
onClick={handleNextPress}
mt={10}
isDisabled={!personalityType}
isLoading={updateAboutYouRes.fetching || loading}
isDisabled={colorMask === undefined}
isLoading={updateAboutYouRes.fetching}
loadingText="Saving"
>
{nextButtonLabel}

View File

@@ -9,9 +9,10 @@ export const PlayerFragment = gql`
ethereum_address
availability_hours
timezone
EnneagramType {
description
ColorAspect {
name
description
mask
}
playerType {
description

View File

@@ -0,0 +1,73 @@
import AmbitionAltImg from 'assets/colors/Ambition.svg';
import BalanceAltImg from 'assets/colors/Balance.svg';
import ChaosAltImg from 'assets/colors/Chaos.svg';
import JusticeAltImg from 'assets/colors/Justice.svg';
import WisdomAltImg from 'assets/colors/Wisdom.svg';
import gql from 'fake-tag';
import { isPow2 } from 'utils/mathHelper';
import { ColorAspect } from './autogen/types';
import { client } from './client';
import { PersonalityOption } from './types';
const AspectsQuery = gql`
query GetAspects {
ColorAspect {
mask
name
description
}
}
`;
export const images: {
[x: number]: string
} = {
0b10000: JusticeAltImg,
0b01000: WisdomAltImg,
0b00100: AmbitionAltImg,
0b00010: ChaosAltImg,
0b00001: BalanceAltImg,
};
export const colors: {
[x: number]: string
} = {
0b10000: '#c4aab4',
0b01000: '#0273b2',
0b00100: '#141a36',
0b00010: '#b72d5b',
0b00001: '#36ae60',
};
export const getPersonalityInfo = async (): Promise<{
parts: Array<PersonalityOption>;
types: { [any: string]: PersonalityOption };
}> => {
const { data, error } = await (
client
.query(AspectsQuery)
.toPromise()
);
if (error) throw error;
if (!data) throw new Error("data isn't set");
const parts: Array<PersonalityOption> = [];
const types: { [x: number]: PersonalityOption } = {};
data.ColorAspect.forEach((aspect: ColorAspect) => {
const option = {
name: aspect.name,
description: aspect.description,
mask: aspect.mask,
};
types[aspect.mask] = option;
// pure colors are powers of 2 (only 1 bit set)
if (isPow2(aspect.mask)) {
parts.push(option);
}
});
return { parts, types };
};

View File

@@ -1,3 +0,0 @@
import { PersonalityTypes } from 'graphql/types';
export const getPersonalityTypes = () => Object.values(PersonalityTypes);

View File

@@ -1,15 +1,5 @@
import { MetaTheme } from '@metafam/ds';
import AchieverImage from 'assets/achiever.png';
import ChallengerImage from 'assets/challenger.png';
import EnthusiastImage from 'assets/enthusiast.png';
import HelperImage from 'assets/helper.png';
import IndividualistImage from 'assets/individualist.png';
import InvestigatorImage from 'assets/investigator.png';
import LoyalistImage from 'assets/loyalist.png';
import PeacemakerImage from 'assets/peacemaker.png';
import ReformerImage from 'assets/reformer.png';
import {
EnneagramType_Enum,
Me,
Member,
Moloch,
@@ -27,12 +17,10 @@ export type Skill = {
category: string;
};
export type PersonalityType = {
id: string;
name: EnneagramType_Enum;
label: string;
description: string;
image: string;
export type PersonalityOption = {
mask: number;
name: string;
description?: string | null | undefined;
};
export type Membership = Pick<Member, 'id'> & {
@@ -46,74 +34,6 @@ export type MeType =
| null
| undefined;
export const PersonalityTypes: {
[any: string]: PersonalityType;
} = {
[EnneagramType_Enum.Reformer]: {
id: '1',
name: EnneagramType_Enum.Reformer,
label: 'The Reformer',
description: 'Principled, Purposeful, Self-Controlled, and Perfectionistic',
image: ReformerImage,
},
[EnneagramType_Enum.Helper]: {
id: '2',
name: EnneagramType_Enum.Helper,
label: 'The Helper',
description: 'Demonstrative, Generous, People-Pleasing, and Possessive',
image: HelperImage,
},
[EnneagramType_Enum.Achiever]: {
id: '3',
name: EnneagramType_Enum.Achiever,
label: 'The Achiever',
description: 'Adaptive, Excelling, Driven, and Image-Conscious',
image: AchieverImage,
},
[EnneagramType_Enum.Individualist]: {
id: '4',
name: EnneagramType_Enum.Individualist,
label: 'The Individualist',
description: 'Expressive, Dramatic, Self-Absorbed, and Temperamental',
image: IndividualistImage,
},
[EnneagramType_Enum.Investigator]: {
id: '5',
name: EnneagramType_Enum.Investigator,
label: 'The Investigator',
description: 'Perceptive, Innovative, Secretive, and Isolated',
image: InvestigatorImage,
},
[EnneagramType_Enum.Loyalist]: {
id: '6',
name: EnneagramType_Enum.Loyalist,
label: 'The Loyalist',
description: 'Engaging, Responsible, Anxious, and Suspicious',
image: LoyalistImage,
},
[EnneagramType_Enum.Enthusiast]: {
id: '7',
name: EnneagramType_Enum.Enthusiast,
label: 'The Enthusiast',
description: 'Spontaneous, Versatile, Distractible, and Scattered',
image: EnthusiastImage,
},
[EnneagramType_Enum.Challenger]: {
id: '8',
name: EnneagramType_Enum.Challenger,
label: 'The Challenger',
description: 'Self-Confident, Decisive, Willful, and Confrontational',
image: ChallengerImage,
},
[EnneagramType_Enum.Peacemaker]: {
id: '9',
name: EnneagramType_Enum.Peacemaker,
label: 'The Peacemaker',
description: 'Receptive, Reassuring, Agreeable, and Complacent',
image: PeacemakerImage,
},
};
export const SkillColors: Record<SkillCategory_Enum, string> = {
[SkillCategory_Enum.Community]: MetaTheme.colors.green['700'],
[SkillCategory_Enum.Design]: MetaTheme.colors.pink['700'],

View File

@@ -1,18 +1,20 @@
import { SetupPersonalityType } from 'components/Setup/SetupPersonalityType';
import { SetupProfile } from 'components/Setup/SetupProfile';
import { SetupContextProvider } from 'contexts/SetupContext';
import { getPersonalityTypes } from 'graphql/getPersonalityTypes';
import { PersonalityType, PersonalityTypes } from 'graphql/types';
import { getPersonalityInfo } from 'graphql/getPersonalityInfo';
import { useUser } from 'lib/hooks';
import { InferGetStaticPropsType } from 'next';
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
export const getStaticProps = async () => {
const personalityTypeChoices = await getPersonalityTypes();
const { types: personalityTypes, parts: personalityParts } = (
await getPersonalityInfo()
);
return {
props: {
personalityTypeChoices,
personalityParts,
personalityTypes,
hideAppDrawer: true,
},
};
@@ -22,24 +24,32 @@ type Props = InferGetStaticPropsType<typeof getStaticProps>;
const PersonalityTypeSetup: React.FC<Props> = (props) => {
const { personalityTypeChoices } = props;
const [personalityType, setPersonalityType] = useState<PersonalityType>();
const { personalityTypes } = props;
const { user } = useUser({ redirectTo: '/' });
const [colorMask, setColorMask] = (
useState<number | undefined>(user?.player?.ColorAspect?.mask)
);
if (user?.player) {
const { player } = user;
if (player.EnneagramType && !personalityType) {
setPersonalityType(PersonalityTypes[player.EnneagramType.name]);
const load = () => {
const { player } = user ?? {};
if (player) {
if (colorMask === undefined && player.ColorAspect !== null) {
setColorMask(player.ColorAspect?.mask);
}
}
}
useEffect(load, [user, colorMask]);
return (
<SetupContextProvider>
<SetupProfile>
<SetupPersonalityType
personalityTypeChoices={personalityTypeChoices}
personalityType={personalityType}
setPersonalityType={setPersonalityType} />
<SetupPersonalityType
{...{
personalityTypes,
colorMask,
setColorMask,
}}
/>
</SetupProfile>
</SetupContextProvider>
);

View File

@@ -0,0 +1,5 @@
// true if the number is a power of 2
export const isPow2 = (int: number): boolean => (
// eslint-disable-next-line no-bitwise
int > 0 && (int & (int - 1)) === 0
);