@chakra-ui/react#useTheme TypeScript Examples
The following examples show how to use
@chakra-ui/react#useTheme.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ReactSelectCustomization.tsx From ke with MIT License | 6 votes |
function SingleValue<OptionType>({ ...props }: SingleValueProps<OptionType>): JSX.Element {
const { singleValue } = useStyles()
const { className, isDisabled } = props
const theme = useTheme()
return (
<ClassNames>
{({ cx: emotionCx, css }) => (
<selectComponents.SingleValue
{...props}
className={emotionCx(className, {
[css(chakraCss((singleValue as any)?._disabled)(theme))]: isDisabled,
})}
/>
)}
</ClassNames>
)
}
Example #2
Source File: ContentContainer.tsx From coindrop with GNU General Public License v3.0 | 6 votes |
ContentContainer: FC<ContentContainerProps> = ({ boxProps, children }) => {
const theme = useTheme();
return (
<Container
my={24}
maxW={theme.breakpoints.xl}
// eslint-disable-next-line react/jsx-props-no-spreading
{...boxProps}
>
{children}
</Container>
);
}
Example #3
Source File: HeaderFooterContainer.tsx From coindrop with GNU General Public License v3.0 | 6 votes |
HeaderFooterContainer: FC = ({ children }) => {
const theme = useTheme();
return (
<Container
maxW={theme.breakpoints.xl}
>
{children}
</Container>
);
}
Example #4
Source File: DefaultLayoutHOC.tsx From coindrop with GNU General Public License v3.0 | 6 votes |
withDefaultLayout = (Component: ComponentType): FunctionComponent<Props> => {
const WrappedComponent: FunctionComponent = (props: Props) => {
const theme = useTheme();
return (
<Container
maxW={theme.breakpoints.lg}
mx="auto"
px={4}
mb={6}
>
<Navbar />
<Box mb={[2, 0]} />
<hr />
<Component {...props} />
<Footer />
</Container>
);
};
return WrappedComponent;
}
Example #5
Source File: Logo.tsx From coindrop with GNU General Public License v3.0 | 6 votes |
PiggyLogo: FC<IconProps> = (iconProps) => {
const theme = useTheme();
const { colorMode } = useColorMode();
const logoOutlineColor = useColorModeValue(theme.colors.gray['800'], theme.colors.gray['900']);
// eslint-disable-next-line react/jsx-props-no-spreading
return colorMode === 'light'
? <PiggyLogoIcon color={logoOutlineColor} {...iconProps} />
: <PiggyLogoIconDarkMode color={logoOutlineColor} {...iconProps} />;
}
Example #6
Source File: Logo.tsx From coindrop with GNU General Public License v3.0 | 6 votes |
Logo: FC<Props> = ({ text = 'coindrop', subtitle }) => {
const theme = useTheme();
const fontColor = useColorModeValue(theme.colors.gray['600'], theme.colors.gray['50']);
const LogoText = () => (
<Text
fontSize={["4xl", "5xl"]}
fontFamily="Changa, system-ui, sans-serif"
fontWeight={500}
color={fontColor}
ml={2}
lineHeight={["1.5rem", "2.5rem"]}
>
{text}
</Text>
);
return (
<Flex
ml={1}
mr={2}
mb={[2, 0]}
align="center"
>
<PiggyLogo boxSize={["48px", "64px"]} />
<Box>
<LogoText />
{subtitle && (
<Text textAlign="center">
{subtitle}
</Text>
)}
</Box>
</Flex>
);
}
Example #7
Source File: PiggybankListItem.tsx From coindrop with GNU General Public License v3.0 | 5 votes |
PiggybankListItem: FunctionComponent<Props> = ({ id }) => {
const { colors } = useTheme();
const [isLoading, setIsLoading] = useState(false);
const hoverBg = useColorModeValue(colors.gray['100'], colors.gray['600']);
const activeBg = useColorModeValue(colors.gray['200'], colors.gray['700']);
return (
<Box
onClick={() => setIsLoading(true)}
cursor="pointer"
mt={3}
bg={isLoading ? hoverBg : undefined}
_hover={{
bg: hoverBg,
textDecoration: "none",
}}
_active={{
bg: activeBg,
}}
>
<NextLink href={`/${id}`} passHref>
<a id={`link-to-coindrop-${id}`}>
<Box
py={5}
shadow="md"
borderWidth="1px"
borderRadius="10px"
>
{isLoading ? (
<Flex align="center" justify="center">
<Spinner boxSize="32px" />
<Heading ml={2} fontSize="xl">
Loading
</Heading>
</Flex>
) : (
<Flex
justify="space-between"
align="center"
mx={4}
>
<Heading fontSize="xl" wordBreak="break-word">
coindrop.to/
{id}
</Heading>
<ChevronRightIcon boxSize="32px" />
</Flex>
)}
</Box>
</a>
</NextLink>
</Box>
);
}
Example #8
Source File: PaymentMethodButton.tsx From coindrop with GNU General Public License v3.0 | 5 votes |
PaymentMethodButton: FunctionComponent<Props> = ({
paymentMethod,
paymentMethodValue,
isPreferred,
accentColor,
}) => {
const theme = useTheme();
const { isOpen, onOpen, onClose } = useDisclosure();
const paymentMethodDisplayName = paymentMethodNames[paymentMethod];
const Icon = paymentMethodIcons[paymentMethod];
if (!paymentMethodDisplayName || !Icon) return null;
const textColor = useColorModeValue("gray.800", "white");
const bgColor = useColorModeValue("gray.50", "gray.700");
const bgColorHover = useColorModeValue("gray.100", "gray.600");
const bgColorActive = useColorModeValue("gray.200", "gray.500");
const borderColor = isPreferred
? theme.colors[accentColor]['500']
: useColorModeValue("gray.300", "gray.500");
const borderColorHover = isPreferred
? theme.colors[accentColor]['500']
: useColorModeValue("gray.300", "gray.500");
return (
<>
<PaymentMethodButtonModal
isOpen={isOpen}
onClose={onClose}
paymentMethod={paymentMethod}
paymentMethodDisplayName={paymentMethodDisplayName}
paymentMethodValue={paymentMethodValue}
/>
<Box
as="button"
id={`payment-method-button-${paymentMethod}`}
onClick={onOpen}
lineHeight="1.2"
transition="all 0.2s cubic-bezier(.08,.52,.52,1)"
borderWidth={isPreferred ? "2px" : "1px"}
rounded="6px"
fontSize="18px"
fontWeight="semibold"
bg={bgColor}
borderColor={borderColor}
p={4}
m={2}
shadow="md"
color={textColor}
_hover={{
bg: bgColorHover,
transform: "scale(0.98)",
borderColor: borderColorHover,
}}
_active={{
bg: bgColorActive,
transform: "scale(0.96)",
borderColor: borderColorHover,
}}
_focus={{
outline: "none",
}}
>
<Flex align="center">
<Icon boxSize="32px" />
<Text ml={2}>{paymentMethodDisplayName}</Text>
</Flex>
{isPreferred && (
<Text
fontSize="xs"
color={textColor}
>
Preferred
</Text>
)}
</Box>
</>
);
}
Example #9
Source File: faq.tsx From coindrop with GNU General Public License v3.0 | 5 votes |
FAQ: FunctionComponent = () => {
const theme = useTheme();
const panelBgColor = useColorModeValue("gray.50", undefined);
return (
<Box>
<Box my={6}>
<Heading as="h1" textAlign="center">
FAQ
</Heading>
<Text textAlign="center">
Frequently Asked Questions
</Text>
</Box>
<Container maxW={theme.breakpoints.lg}>
<Accordion defaultIndex={-1} allowToggle>
{accordionText.map(({title, body}) => (
<AccordionItem key={title}>
<AccordionButton>
<Box flex="1" textAlign="left">
{title}
</Box>
<AccordionIcon />
</AccordionButton>
<AccordionPanel>
<Box
p={4}
bg={panelBgColor}
>
{body}
</Box>
</AccordionPanel>
</AccordionItem>
))}
</Accordion>
</Container>
<Text textAlign="center" mt={4} fontSize="sm">
{"Do you have a question that's not answered here? Send it to "}
<Link href={`mailto:${coindropEmail}`} isExternal>
{coindropEmail}
</Link>
</Text>
</Box>
);
}
Example #10
Source File: ColorPickerControl.tsx From openchakra with MIT License | 4 votes |
ColorPickerControl = (props: ColorPickerPropType) => {
const theme = useTheme()
const themeColors: any = omit(theme.colors, [
'transparent',
'current',
'black',
'white',
])
const { setValue, setValueFromEvent } = useForm()
const value = usePropsSelector(props.name)
let propsIconButton: any = { bg: value }
if (value && themeColors[value]) {
propsIconButton = { colorScheme: value }
}
return (
<>
<Popover placement="bottom">
<PopoverTrigger>
{props.gradient ? (
<IconButton
mr={2}
boxShadow="md"
border={props.gradientColor ? 'none' : '2px solid grey'}
isRound
aria-label="Color"
size="xs"
colorScheme={props.gradientColor}
bg={props.gradientColor}
/>
) : (
<IconButton
mr={2}
boxShadow="md"
border={value ? 'none' : '2px solid grey'}
isRound
aria-label="Color"
size="xs"
{...propsIconButton}
/>
)}
</PopoverTrigger>
<Portal>
<PopoverContent width="200px">
<PopoverArrow />
<PopoverBody>
{props.withFullColor ? (
<Tabs size="sm" variant="soft-rounded" colorScheme="green">
<TabList>
<Tab>Theme</Tab>
<Tab>All</Tab>
</TabList>
<TabPanels mt={4}>
<TabPanel p={0}>
{props.gradient ? (
<HuesPickerControl
name={props.name}
themeColors={themeColors}
enableHues
setValue={setValue}
gradient={true}
index={props.index}
updateGradient={props.updateGradient}
/>
) : (
<HuesPickerControl
name={props.name}
themeColors={themeColors}
enableHues
setValue={setValue}
gradient={props.gradient}
/>
)}
</TabPanel>
<TabPanel p={0}>
<Box position="relative" height="150px">
<ColorPicker
color={props.gradient ? props.gradientColor : value}
onChange={(color: any) => {
props.gradient
? props.updateGradient!(
`#${color.hex}`,
props.index!,
)
: setValue(props.name, `#${color.hex}`)
}}
/>
);
</Box>
</TabPanel>
</TabPanels>
</Tabs>
) : props.gradient ? (
<HuesPickerControl
name={props.name}
themeColors={themeColors}
enableHues
setValue={setValue}
gradient={true}
index={props.index}
updateGradient={props.updateGradient}
/>
) : (
<HuesPickerControl
name={props.name}
themeColors={themeColors}
enableHues={false}
setValue={setValue}
gradient={props.gradient}
/>
)}
</PopoverBody>
</PopoverContent>
</Portal>
</Popover>
{props.gradient ? (
<Input
width="100px"
size="sm"
name={props.name}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
props.gradient
? props.updateGradient!(e.target.value, props.index!)
: setValue(props.name, e.target.value)
}}
value={props.gradientColor}
/>
) : (
<Input
width="100px"
size="sm"
name={props.name}
onChange={setValueFromEvent}
value={value}
/>
)}
</>
)
}
Example #11
Source File: TextPanel.tsx From openchakra with MIT License | 4 votes |
TextPanel = () => {
const { setValue, setValueFromEvent } = useForm()
const theme = useTheme()
const fontWeight = usePropsSelector('fontWeight')
const fontStyle = usePropsSelector('fontStyle')
const textAlign = usePropsSelector('textAlign')
const fontSize = usePropsSelector('fontSize')
const letterSpacing = usePropsSelector('letterSpacing')
const lineHeight = usePropsSelector('lineHeight')
return (
<>
<FormControl label="Style">
<IconButton
mr={1}
aria-label="bold"
icon={<GoBold />}
onClick={() => {
setValue('fontWeight', fontWeight ? null : 'bold')
}}
size="xs"
colorScheme={fontWeight ? 'whatsapp' : 'gray'}
variant={fontWeight ? 'solid' : 'outline'}
>
Bold
</IconButton>
<IconButton
aria-label="italic"
icon={<GoItalic />}
onClick={() => {
setValue('fontStyle', fontStyle === 'italic' ? null : 'italic')
}}
size="xs"
colorScheme={fontStyle === 'italic' ? 'whatsapp' : 'gray'}
variant={fontStyle === 'italic' ? 'solid' : 'outline'}
>
Italic
</IconButton>
</FormControl>
<FormControl label="Text align">
<ButtonGroup size="xs" isAttached>
<IconButton
aria-label="bold"
icon={<MdFormatAlignLeft />}
onClick={() => {
setValue('textAlign', 'left')
}}
colorScheme={textAlign === 'left' ? 'whatsapp' : 'gray'}
variant={textAlign === 'left' ? 'solid' : 'outline'}
/>
<IconButton
aria-label="italic"
icon={<MdFormatAlignCenter />}
onClick={() => {
setValue('textAlign', 'center')
}}
colorScheme={textAlign === 'center' ? 'whatsapp' : 'gray'}
variant={textAlign === 'center' ? 'solid' : 'outline'}
/>
<IconButton
aria-label="italic"
icon={<MdFormatAlignRight />}
onClick={() => {
setValue('textAlign', 'right')
}}
colorScheme={textAlign === 'right' ? 'whatsapp' : 'gray'}
variant={textAlign === 'right' ? 'solid' : 'outline'}
/>
<IconButton
aria-label="italic"
icon={<MdFormatAlignJustify />}
onClick={() => {
setValue('textAlign', 'justify')
}}
colorScheme={textAlign === 'justify' ? 'whatsapp' : 'gray'}
variant={textAlign === 'justify' ? 'solid' : 'outline'}
/>
</ButtonGroup>
</FormControl>
<FormControl label="Font size" htmlFor="fontSize">
<InputSuggestion
value={fontSize}
handleChange={setValueFromEvent}
name="fontSize"
>
{Object.keys(theme.fontSizes).map(option => (
<ComboboxOption key={option} value={option} />
))}
</InputSuggestion>
</FormControl>
<ColorsControl withFullColor enableHues name="color" label="Color" />
<FormControl label="Line height" htmlFor="lineHeight">
<InputSuggestion
value={lineHeight}
handleChange={setValueFromEvent}
name="lineHeight"
>
{Object.keys(theme.lineHeights).map(option => (
<ComboboxOption key={option} value={option} />
))}
</InputSuggestion>
</FormControl>
<FormControl label="Letter spacing" htmlFor="letterSpacing">
<InputSuggestion
value={letterSpacing}
handleChange={setValueFromEvent}
name="letterSpacing"
>
{Object.keys(theme.letterSpacings).map(option => (
<ComboboxOption key={option} value={option} />
))}
</InputSuggestion>
</FormControl>
</>
)
}
Example #12
Source File: Post.tsx From coindrop with GNU General Public License v3.0 | 4 votes |
Post: FunctionComponent<PostTypePostHydrate> = ({
author,
datePublished,
dateModified,
title,
description,
images,
slug,
content,
}) => {
const { avatar: authorAvatar, handle: authorHandle, url: authorUrl } = authors[author];
const theme = useTheme();
return (
<>
<NextSeo
title={`${title} | Coindrop blog`}
description={description}
/>
<ArticleJsonLd
url={`https://coindrop.to/blog/${slug}`}
title={title}
images={images}
datePublished={datePublished}
dateModified={dateModified}
authorName={author}
publisherName="Coindrop"
publisherLogo="https://coindrop.to/piggy-256.png" // TODO: change this to a valid AMP logo size https://developers.google.com/search/docs/data-types/article
description={description}
/>
<article>
<Heading
as="h1"
size="2xl"
textAlign="center"
my={6}
>
{title}
</Heading>
<Flex
direction={["column", null, "row"]}
align={["initial", null, "center"]}
alignItems="center"
justify="center"
mb={4}
>
<Text
id="publish-date"
mb={[4, null, "auto"]}
mt={[0, null, "auto"]}
textAlign="center"
mr={[null, null, 12]}
>
{`${dayjs(datePublished).format('dddd, MMMM D, YYYY')} (${dayjs(datePublished).fromNow()})`}
</Text>
<Flex
id="author"
align="center"
justify="center"
>
<Avatar name={author} src={authorAvatar} size="sm" />
<Flex direction="column" ml={1} align="flex-start">
<Text fontSize="sm">
<Text>
{author}
</Text>
<Link href={authorUrl} isExternal>
{authorHandle}
</Link>
</Text>
</Flex>
</Flex>
</Flex>
<hr />
<Container
mt={8}
maxW={theme.breakpoints.md}
>
{content}
{dateModified && (
<>
<Text
id="modified-date"
fontSize="sm"
textAlign="center"
>
{`Last updated ${dayjs(dateModified).format('dddd, MMMM D, YYYY')} (${dayjs(dateModified).fromNow()})`}
</Text>
<Text
fontSize="xs"
textAlign="center"
>
<Link
href={`https://github.com/remjx/coindrop/commits/master/blog/posts/${slug}/index.mdx`}
>
<u>View edits</u>
</Link>
</Text>
</>
)}
</Container>
</article>
</>
);
}
Example #13
Source File: CompetitorComparisonTable.tsx From coindrop with GNU General Public License v3.0 | 4 votes |
CompetitorComparisonTable: FunctionComponent = () => {
const theme = useTheme();
const green = useColorModeValue(theme.colors.green['500'], theme.colors.green['300']);
const red = useColorModeValue(theme.colors.red['500'], theme.colors.red['300']);
const orange = useColorModeValue(theme.colors.orange['500'], theme.colors.orange['300']);
const logoOutlineColor = useColorModeValue(theme.colors.gray['800'], theme.colors.gray['900']);
const StyledTd: FunctionComponent<{ value: string | number }> = ({ value }) => {
let backgroundColor;
switch (value) {
case 'Unlimited':
case 'Any':
case 'None':
case 'Freemium':
case 'Yes':
backgroundColor = green;
break;
case '$9/mo':
backgroundColor = orange;
break;
default:
backgroundColor = red;
}
return (
<td style={{backgroundColor, color: "white"}}>
{value}
</td>
);
};
return (
<Box>
<Flex
id="full-width-comparison-table"
justify="center"
textAlign="center"
display={['none', 'none', 'flex']}
>
<table className={styles.comparisontable}>
<thead>
<tr>
<th> </th>
<th>
<Flex align="center">
{coindropData.displayName}
<PiggyLogoIcon ml={1} size="19px" color={logoOutlineColor} />
</Flex>
</th>
{competitorData.map(obj => (
<th key={obj.id}>
{obj.displayName}
{obj.icon}
</th>
))}
</tr>
</thead>
<tbody>
<tr>
<td>Fees</td>
{data.map(obj => (
<StyledTd value={obj.fees} key={obj.id} />
))}
</tr>
<tr>
<td>Payment methods</td>
{data.map(obj => (
<StyledTd value={obj.paymentMethods} key={obj.id} />
))}
</tr>
<tr>
<td># Pages per account</td>
{data.map(obj => (
<StyledTd value={obj.numPagesPerAccount} key={obj.id} />
))}
</tr>
<tr>
<td>Open-source</td>
{data.map(obj => (
<StyledTd value={obj.isOpenSource} key={obj.id} />
))}
</tr>
<tr>
<td>Subscribers</td>
{data.map(obj => (
<StyledTd value={obj.subscriberFeatures} key={obj.id} />
))}
</tr>
</tbody>
</table>
</Flex>
{competitorData.map(obj => (
<Flex
key={obj.id}
id={`partial-width-comparison-table-${obj.id}`}
justify="center"
textAlign="center"
display={['flex', 'flex', 'none']}
>
<table className={styles.comparisontable}>
<thead>
<tr>
<th> </th>
<th>
<Flex align="center">
Coindrop
<PiggyLogoIcon ml={1} size="19px" color={logoOutlineColor} />
</Flex>
</th>
<th>
{obj.displayName}
{obj.icon}
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Fees</td>
<StyledTd value={coindropData.fees} />
<StyledTd value={obj.fees} />
</tr>
<tr>
<td>Payment methods</td>
<StyledTd value={coindropData.paymentMethods} />
<StyledTd value={obj.paymentMethods} />
</tr>
<tr>
<td># Pages per account</td>
<StyledTd value={coindropData.numPagesPerAccount} />
<StyledTd value={obj.numPagesPerAccount} />
</tr>
<tr>
<td>Open-source</td>
<StyledTd value={coindropData.isOpenSource} />
<StyledTd value={obj.isOpenSource} />
</tr>
<tr>
<td>Subscribers</td>
<StyledTd value={coindropData.subscriberFeatures} />
<StyledTd value={obj.subscriberFeatures} />
</tr>
</tbody>
</table>
</Flex>
))}
</Box>
);
}
Example #14
Source File: EditPiggybankModal.tsx From coindrop with GNU General Public License v3.0 | 4 votes |
EditPiggybankModal: FunctionComponent<Props> = ({ isOpen, onClose }) => {
const [isSubmitting, setIsSubmitting] = useState(false);
const { colors } = useTheme();
const { user } = useUser();
const { colorMode } = useColorMode();
const accentColorLevelInitial = getAccentColorLevelInitial(colorMode);
const accentColorLevelHover = getAccentColorLevelHover(colorMode);
const { push: routerPush, query: { piggybankName } } = useRouter();
const initialPiggybankId = Array.isArray(piggybankName) ? piggybankName[0] : piggybankName;
const { piggybankDbData } = useContext(PublicPiggybankDataContext);
const { avatar_storage_id: currentAvatarStorageId } = piggybankDbData;
const initialPaymentMethodsDataFieldArray = convertPaymentMethodsDataToFieldArray(piggybankDbData.paymentMethods);
const initialAccentColor = piggybankDbData.accentColor ?? 'orange';
const {
register,
handleSubmit,
setValue,
watch,
control,
formState: { isDirty },
} = useForm({
defaultValues: {
piggybankId: initialPiggybankId,
accentColor: initialAccentColor,
website: piggybankDbData.website ?? '',
name: piggybankDbData.name ?? '',
verb: piggybankDbData.verb ?? 'pay',
paymentMethods: sortByIsPreferredThenAlphabetical(initialPaymentMethodsDataFieldArray),
},
});
const paymentMethodsFieldArrayName = "paymentMethods";
const { fields, append, remove } = useFieldArray({
control,
name: paymentMethodsFieldArrayName,
});
const {
accentColor: watchedAccentColor,
piggybankId: watchedPiggybankId,
} = watch(["accentColor", "piggybankId"]);
const isAccentColorDirty = initialAccentColor !== watchedAccentColor;
const isUrlUnchanged = initialPiggybankId === watchedPiggybankId;
const { isPiggybankIdAvailable, setIsAddressTouched } = useContext(AdditionalValidation);
const onSubmit = async (formData) => {
try {
setIsSubmitting(true);
const dataToSubmit = {
...formData,
paymentMethods: convertPaymentMethodsFieldArrayToDbMap(formData.paymentMethods ?? []),
owner_uid: user.id,
avatar_storage_id: currentAvatarStorageId ?? null,
};
if (isUrlUnchanged) {
await db.collection('piggybanks').doc(initialPiggybankId).set(dataToSubmit);
mutate(['publicPiggybankData', initialPiggybankId], dataToSubmit);
} else {
await axios.post(
'/api/createPiggybank',
{
oldPiggybankName: initialPiggybankId,
newPiggybankName: formData.piggybankId,
piggybankData: dataToSubmit,
},
{
headers: {
token: user.token,
},
},
);
try {
await db.collection('piggybanks').doc(initialPiggybankId).delete();
} catch (err) {
console.log('error deleting old Coindrop page');
}
routerPush(`/${formData.piggybankId}`);
}
fetch(`/${initialPiggybankId}`, { headers: { isToForceStaticRegeneration: "true" }});
onClose();
} catch (error) {
setIsSubmitting(false);
// TODO: handle errors
throw error;
}
};
const handleAccentColorChange = (e) => {
e.preventDefault();
setValue("accentColor", e.target.dataset.colorname);
};
useEffect(() => {
register("accentColor");
}, [register]);
const formControlTopMargin = 2;
return (
<Modal
isOpen={isOpen}
onClose={onClose}
size="xl"
closeOnOverlayClick={false}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Configure</ModalHeader>
<ModalCloseButton />
<form id="configure-coindrop-form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<AvatarInput />
<FormControl
isRequired
mt={formControlTopMargin}
>
<FormLabel htmlFor="input-piggybankId">URL</FormLabel>
<EditUrlInput
register={register}
value={watchedPiggybankId}
/>
</FormControl>
<FormControl
mt={formControlTopMargin}
>
<FormLabel
htmlFor="input-accentColor"
>
Theme
</FormLabel>
<Flex wrap="wrap" justify="center">
{themeColorOptions.map(colorName => {
const isColorSelected = watchedAccentColor === colorName;
const accentColorInitial = colors[colorName][accentColorLevelInitial];
const accentColorHover = colors[colorName][accentColorLevelHover];
return (
<Box
key={colorName}
as="button"
bg={isColorSelected ? accentColorHover : accentColorInitial}
_hover={{
bg: accentColorHover,
}}
w="36px"
h="36px"
borderRadius="50%"
mx={1}
my={1}
onClick={handleAccentColorChange}
data-colorname={colorName}
>
{isColorSelected && (
<CheckIcon color="#FFF" />
)}
</Box>
);
})}
</Flex>
</FormControl>
<FormControl
isRequired
mt={formControlTopMargin}
>
<FormLabel
htmlFor="input-name"
>
Name
</FormLabel>
<Input
id="input-name"
name="name"
ref={register}
/>
</FormControl>
<FormControl
isRequired
mt={formControlTopMargin}
>
<FormLabel
htmlFor="input-verb"
>
Payment action name
</FormLabel>
<Select
id="input-verb"
name="verb"
ref={register}
>
<option value="pay">Pay</option>
<option value="donate to">Donate to</option>
<option value="support">Support</option>
<option value="tip">Tip</option>
</Select>
</FormControl>
<FormControl
mt={formControlTopMargin}
>
<FormLabel
htmlFor="input-website"
>
Website
</FormLabel>
<Input
id="input-website"
name="website"
ref={register}
placeholder="http://"
type="url"
/>
</FormControl>
<FormControl
mt={formControlTopMargin}
>
<FormLabel
htmlFor="input-paymentmethods"
>
Payment Methods
</FormLabel>
<PaymentMethodsInput
fields={fields}
control={control}
register={register}
remove={remove}
append={append}
fieldArrayName={paymentMethodsFieldArrayName}
/>
</FormControl>
</ModalBody>
<Flex
id="modal-footer"
justify="space-between"
m={6}
>
<DeleteButton
piggybankName={initialPiggybankId}
/>
<Flex>
<Button
variant="ghost"
onClick={onClose}
>
Cancel
</Button>
<Button
id="save-configuration-btn"
colorScheme="green"
mx={1}
type="submit"
isLoading={isSubmitting}
loadingText="Saving"
isDisabled={
(
!isDirty
&& !isAccentColorDirty // controlled accentColor field is not showing up in formState.dirtyFields
)
|| !isPiggybankIdAvailable
|| !initialPiggybankId
}
onClick={() => setIsAddressTouched(true)}
>
Save
</Button>
</Flex>
</Flex>
</form>
</ModalContent>
</Modal>
);
}
Example #15
Source File: PaymentMethodsInput.tsx From coindrop with GNU General Public License v3.0 | 4 votes |
PaymentMethodsInput: FC<Props> = ({ fieldArrayName, fields, control, register, remove, append }) => {
const { colors } = useTheme();
const paymentMethodsDataWatch: PaymentMethod[] = useWatch({
control,
name: fieldArrayName,
});
const [openAccordionItemIndex, setOpenAccordionItemIndex] = useState(-1);
useEffect(() => {
if (
paymentMethodsDataWatch[paymentMethodsDataWatch.length - 1]?.paymentMethodId === "default-blank"
|| paymentMethodsDataWatch[paymentMethodsDataWatch.length - 1]?.address === ""
) {
setOpenAccordionItemIndex(paymentMethodsDataWatch.length - 1);
}
}, [paymentMethodsDataWatch]);
const containsInvalidAddress = paymentMethodsDataWatch.some(paymentMethod => !paymentMethod.address);
const { isAddressTouched, setIsAddressTouched } = useContext(AdditionalValidation);
// optgroup not compatible with Chakra dark mode: https://github.com/chakra-ui/chakra-ui/issues/2853
// const optionsGroup = (category: Category) => {
// const optgroupLabels: Record<Category, string> = {
// "digital-wallet": 'Digital Wallets',
// "digital-asset": "Digital Assets",
// "subscription-platform": "Subscription Platforms",
// };
// return (
// <optgroup label={optgroupLabels[category]}>
// {paymentMethods
// .filter(paymentMethod => paymentMethod.category === category)
// .sort((a, b) => (a.id < b.id ? -1 : 1))
// .map(({ id, displayName }) => (
// <option
// key={id}
// value={id}
// style={{display: paymentMethodsDataWatch.map(paymentMethodDataWatch => paymentMethodDataWatch.paymentMethodId).includes(id) ? "none" : undefined }}
// >
// {displayName}
// </option>
// ))}
// </optgroup>
// );
// };
return (
<>
{fields.length < 1
? 'No payment methods defined yet.'
: (
<Accordion
allowToggle
defaultIndex={-1}
index={openAccordionItemIndex}
>
{
fields
.map((item, index) => {
const watchedData = paymentMethodsDataWatch.find(watchedPaymentMethod => watchedPaymentMethod.id === item.id);
const PaymentMethodIcon = paymentMethodIcons[watchedData?.paymentMethodId];
return (
<AccordionItem
key={item.id}
>
<AccordionButton
onClick={() => {
setIsAddressTouched(true);
if (openAccordionItemIndex !== index && !paymentMethodsDataWatch.find(paymentMethod => paymentMethod.address === "")) {
setOpenAccordionItemIndex(index);
} else {
setOpenAccordionItemIndex(undefined);
}
}}
>
<Flex flex="1" textAlign="left" align="center">
<Flex mr={1} align="center">
{PaymentMethodIcon ? <PaymentMethodIcon mr={2} /> : <QuestionOutlineIcon mr={2} />}
{paymentMethodNames[watchedData?.paymentMethodId] ?? 'Add payment method'}
</Flex>
{watchedData?.isPreferred && (
<Flex>
<StarIcon
ml={2}
size="16px"
color={colors.yellow['400']}
/>
<Text
as="span"
fontSize="xs"
ml={1}
>
<i>Preferred</i>
</Text>
</Flex>
)}
</Flex>
<AccordionIcon />
</AccordionButton>
<AccordionPanel pb={4} id={`accordion-panel-${watchedData.paymentMethodId}`}>
<input
ref={register()}
name={`${fieldArrayName}[${index}].id`}
defaultValue={item.id}
style={{display: 'none'}}
/>
<Box
display={paymentMethodNames[watchedData?.paymentMethodId] ? "none" : "block"}
data-cy={`select-payment-method-container-${watchedData.paymentMethodId}`}
>
<Select
name={`${fieldArrayName}[${index}].paymentMethodId`}
ref={register()}
defaultValue={paymentMethodNames[item.paymentMethodId] ? item.paymentMethodId : 'default-blank'}
isInvalid={containsInvalidAddress && isAddressTouched}
onChange={() => setIsAddressTouched(false)}
>
<option hidden disabled value="default-blank">Select...</option>
{/* optgroup not compatible with Chakra dark mode: https://github.com/chakra-ui/chakra-ui/issues/2853 */}
{Object.entries(paymentMethodNames)
.sort((a, b) => {
const [aId] = a;
const [bId] = b;
return aId < bId ? -1 : 1;
})
.map(([paymentMethodId, paymentMethodDisplayName]) => (
<option
key={paymentMethodId}
value={paymentMethodId}
style={{display: paymentMethodsDataWatch.map(paymentMethod => paymentMethod.paymentMethodId).includes(paymentMethodId) ? "none" : undefined }}
>
{paymentMethodDisplayName}
</option>
))}
</Select>
</Box>
<Box
mx={3}
display={paymentMethodNames[watchedData?.paymentMethodId] ? "block" : "none"}
>
<FormControl isRequired>
<FormLabel htmlFor={`${fieldArrayName}[${index}].address`}>Address</FormLabel>
<Input
id={`address-input-${watchedData.paymentMethodId}`}
name={`${fieldArrayName}[${index}].address`}
ref={register()}
defaultValue={item.address}
isInvalid={containsInvalidAddress && isAddressTouched}
/>
<Box>
<Checkbox
name={`${fieldArrayName}[${index}].isPreferred`}
ref={register()}
defaultValue={item?.isPreferred ? 1 : 0}
defaultIsChecked={item?.isPreferred}
mt={1}
colorScheme="yellow"
>
Preferred
</Checkbox>
</Box>
</FormControl>
</Box>
<Flex
justify={watchedData?.paymentMethodId === 'default-blank' ? 'space-between' : 'flex-end'}
mt={3}
wrap="wrap"
align="center"
>
{watchedData?.paymentMethodId === 'default-blank' && (
<Text fontSize="xs" ml={1}>
<Link href="/blog/payment-method-request" isExternal>
Payment method not listed?
</Link>
</Text>
)}
<Button
onClick={() => {
remove(index);
setIsAddressTouched(false);
}}
leftIcon={<MinusIcon />}
size="sm"
>
{'Remove '}
{/* {paymentMethodNames[watchedData?.paymentMethodId]} */}
</Button>
</Flex>
</AccordionPanel>
</AccordionItem>
);
})
}
</Accordion>
)}
<Flex
justify="center"
mt={2}
>
<Button
id="add-payment-method-button"
onClick={() => {
append({});
setIsAddressTouched(false);
}}
leftIcon={<AddIcon />}
variant="ghost"
size="sm"
isDisabled={
(
fields.length > 0
&& !paymentMethodNames[paymentMethodsDataWatch[paymentMethodsDataWatch.length - 1]?.paymentMethodId]
)
|| containsInvalidAddress
}
>
Add payment method
</Button>
</Flex>
</>
);
}
Example #16
Source File: PublicPiggybankPage.tsx From coindrop with GNU General Public License v3.0 | 4 votes |
PublicPiggybankPage: FunctionComponent = () => {
const { query: { piggybankName } } = useRouter();
const { piggybankDbData } = useContext(PublicPiggybankDataContext);
const theme = useTheme();
const { user } = useUser();
const { colorMode } = useColorMode();
const accentColorLevelInitial = getAccentColorLevelInitial(colorMode);
const accentColorLevelHover = getAccentColorLevelHover(colorMode);
const {
name,
website,
accentColor = "orange",
verb,
owner_uid,
} = piggybankDbData;
const accentColorInitial = theme.colors[accentColor][accentColorLevelInitial];
const accentColorHover = theme.colors[accentColor][accentColorLevelHover];
const pagePaymentMethodsDataEntries = Object.entries(piggybankDbData.paymentMethods ?? {});
const preferredAddresses = pagePaymentMethodsDataEntries.filter(([, paymentMethodData]: any) => paymentMethodData.isPreferred);
const otherAddresses = pagePaymentMethodsDataEntries.filter(([, paymentMethodData]: any) => !paymentMethodData.isPreferred);
const WrapGroup: FunctionComponent = ({ children }) => (
<Wrap
justify="center"
>
{children}
</Wrap>
);
type PaymentMethodButtonsFromEntriesProps = {
entries: PaymentMethodDbObjEntry[]
}
const PaymentMethodButtonsFromEntries: FunctionComponent<PaymentMethodButtonsFromEntriesProps> = ({ entries }) => (
<WrapGroup>
{entries
.sort(sortArrayByEntriesKeyAlphabetical)
.map(([paymentMethodId, paymentMethodData]) => (
<WrapItem key={paymentMethodId}>
<PaymentMethodButton
key={paymentMethodId}
paymentMethod={paymentMethodId}
paymentMethodValue={paymentMethodData.address}
isPreferred={paymentMethodData.isPreferred}
accentColor={accentColor}
/>
</WrapItem>
))}
</WrapGroup>
);
const piggybankExists = !!owner_uid;
const initialSetupComplete = name && accentColor && verb && pagePaymentMethodsDataEntries.length > 0;
return (
<>
<NextSeo
title={`${name ?? piggybankName}'s Coindrop (coindrop.to/${piggybankName})`}
description={`Send money to ${name} with no fees`}
/>
<Container
maxW={theme.breakpoints.lg}
mx="auto"
>
{user?.id
&& user.id === owner_uid
&& (
<>
<DataRefetcher />
<ManagePiggybankBar
editButtonOptions={
initialSetupComplete
? ({
text: 'Configure',
color: undefined,
icon: <SettingsIcon />,
}) : ({
text: 'Set up',
color: 'green',
icon: <SettingsIcon />,
})
}
initialSetupComplete={initialSetupComplete}
/>
</>
)}
{initialSetupComplete ? (
<Box
mb={6}
>
<Box
padding="10px"
my={2}
mx={3}
>
<Center>
<Avatar />
</Center>
<Heading textAlign="center">
Choose a payment method to
{` ${verb} `}
{website ? (
<Link href={website} target="_blank" rel="noreferrer">
<Heading
as="span"
color={accentColorInitial}
textDecoration="underline"
css={css`
&:hover {
color: ${accentColorHover};
}
`}
>
{name}
</Heading>
</Link>
) : (
<Heading
as="span"
color={accentColorInitial}
>
{name}
</Heading>
)}
:
</Heading>
</Box>
<PaymentMethodButtonsFromEntries
entries={preferredAddresses}
/>
<PaymentMethodButtonsFromEntries
entries={otherAddresses}
/>
<PoweredByCoindropLink />
</Box>
) : (
<Heading mt={4} textAlign="center">
{piggybankExists ? 'This Coindrop has not been set up yet.' : 'This Coindrop does not exist'}
{/* TODO: Include action buttons to log in or landing page */}
</Heading>
)}
</Container>
</>
);
}