Commit 3a55206a authored by tom's avatar tom

style Toast component

parent 388ec42a
import type { UseToastOptions, ToastProps } from '@chakra-ui/react';
import { createToastFn, useChakra } from '@chakra-ui/react';
import React from 'react';
import Toast from 'ui/shared/chakra/Toast';
// there is no toastComponent prop in UseToastOptions type
// but these options will be passed to createRenderToast under the hood
// and it accepts custom toastComponent
const defaultOptions: UseToastOptions & { toastComponent?: React.FC<ToastProps> } = {
toastComponent: Toast,
position: 'top-right',
isClosable: true,
containerStyle: {
margin: 3,
marginBottom: 0,
},
variant: 'subtle',
};
export default function useToastModified() {
const { theme } = useChakra();
return React.useMemo(
() => createToastFn(theme.direction, defaultOptions),
[ theme.direction ],
);
}
...@@ -31,7 +31,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>( ...@@ -31,7 +31,7 @@ export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
return ( return (
<ChakraAlert.Root ref={ ref } { ...rest }> <ChakraAlert.Root ref={ ref } { ...rest }>
{ startElement || <ChakraAlert.Indicator>{ icon || defaultIcon }</ChakraAlert.Indicator> } { startElement !== undefined || icon !== undefined ? startElement : <ChakraAlert.Indicator>{ icon || defaultIcon }</ChakraAlert.Indicator> }
{ children ? ( { children ? (
<ChakraAlert.Content> <ChakraAlert.Content>
{ title && <ChakraAlert.Title>{ title }</ChakraAlert.Title> } { title && <ChakraAlert.Title>{ title }</ChakraAlert.Title> }
......
...@@ -14,21 +14,28 @@ import { SECOND } from 'lib/consts'; ...@@ -14,21 +14,28 @@ import { SECOND } from 'lib/consts';
export const toaster = createToaster({ export const toaster = createToaster({
placement: 'top-end', placement: 'top-end',
pauseOnPageIdle: true, pauseOnPageIdle: true,
duration: 5 * SECOND, duration: 10 * SECOND,
offsets: {
top: '12px',
right: '12px',
bottom: '12px',
left: '12px',
},
}); });
export const Toaster = () => { export const Toaster = () => {
return ( return (
<Portal> <Portal>
<ChakraToaster toaster={ toaster } insetInline={{ mdDown: '4' }}> <ChakraToaster toaster={ toaster } insetInline={{ mdDown: '4' }}>
{ (toast) => ( { (toast) => {
const closable = toast.meta?.closable !== undefined ? toast.meta.closable : true;
return (
<Toast.Root width={{ md: 'sm' }}> <Toast.Root width={{ md: 'sm' }}>
{ toast.type === 'loading' ? ( { toast.type === 'loading' ? (
<Spinner size="sm" color="blue.solid"/> <Spinner size="sm" color="blue.solid"/>
) : ( ) : null }
<Toast.Indicator/> <Stack gap="0" flex="1" maxWidth="100%">
) }
<Stack gap="1" flex="1" maxWidth="100%">
{ toast.title && <Toast.Title>{ toast.title }</Toast.Title> } { toast.title && <Toast.Title>{ toast.title }</Toast.Title> }
{ toast.description && ( { toast.description && (
<Toast.Description>{ toast.description }</Toast.Description> <Toast.Description>{ toast.description }</Toast.Description>
...@@ -37,9 +44,10 @@ export const Toaster = () => { ...@@ -37,9 +44,10 @@ export const Toaster = () => {
{ toast.action && ( { toast.action && (
<Toast.ActionTrigger>{ toast.action.label }</Toast.ActionTrigger> <Toast.ActionTrigger>{ toast.action.label }</Toast.ActionTrigger>
) } ) }
{ toast.meta?.closable && <Toast.CloseTrigger/> } { closable && <Toast.CloseTrigger/> }
</Toast.Root> </Toast.Root>
) } );
} }
</ChakraToaster> </ChakraToaster>
</Portal> </Portal>
); );
......
This diff is collapsed.
...@@ -76,7 +76,8 @@ export const recipe = defineSlotRecipe({ ...@@ -76,7 +76,8 @@ export const recipe = defineSlotRecipe({
md: { md: {
root: { root: {
gap: '2', gap: '2',
p: '3', px: '3',
py: '2',
textStyle: 'md', textStyle: 'md',
}, },
indicator: { indicator: {
...@@ -87,8 +88,7 @@ export const recipe = defineSlotRecipe({ ...@@ -87,8 +88,7 @@ export const recipe = defineSlotRecipe({
}, },
defaultVariants: { defaultVariants: {
// status: 'info', visual: 'neutral',
visual: 'info',
size: 'md', size: 'md',
inline: true, inline: true,
}, },
......
...@@ -6,6 +6,7 @@ import { recipe as progressCircle } from './progress-circle.recipe'; ...@@ -6,6 +6,7 @@ import { recipe as progressCircle } from './progress-circle.recipe';
import { recipe as skeleton } from './skeleton.recipe'; import { recipe as skeleton } from './skeleton.recipe';
import { recipe as switchRecipe } from './switch.recipe'; import { recipe as switchRecipe } from './switch.recipe';
import { recipe as tabs } from './tabs.recipe'; import { recipe as tabs } from './tabs.recipe';
import { recipe as toast } from './toast.recipe';
import { recipe as tooltip } from './tooltip.recipe'; import { recipe as tooltip } from './tooltip.recipe';
export const recipes = { export const recipes = {
...@@ -20,5 +21,6 @@ export const slotRecipes = { ...@@ -20,5 +21,6 @@ export const slotRecipes = {
progressCircle, progressCircle,
'switch': switchRecipe, 'switch': switchRecipe,
tabs, tabs,
toast,
tooltip, tooltip,
}; };
import { defineSlotRecipe } from '@chakra-ui/react';
export const recipe = defineSlotRecipe({
slots: [ 'root', 'title', 'description', 'indicator', 'actionTrigger', 'closeTrigger' ],
base: {
root: {
width: 'full',
display: 'flex',
alignItems: 'flex-start',
position: 'relative',
gap: '3',
py: '3',
ps: '6',
pe: '3',
borderRadius: 'md',
translate: 'var(--x) var(--y)',
scale: 'var(--scale)',
zIndex: 'var(--z-index)',
height: 'var(--height)',
opacity: 'var(--opacity)',
willChange: 'translate, opacity, scale',
transition:
'translate 400ms, scale 400ms, opacity 400ms, height 400ms, box-shadow 200ms',
transitionTimingFunction: 'cubic-bezier(0.21, 1.02, 0.73, 1)',
_closed: {
transition: 'translate 400ms, scale 400ms, opacity 200ms',
transitionTimingFunction: 'cubic-bezier(0.06, 0.71, 0.55, 1)',
},
bg: 'alert.bg.neutral',
color: 'alert.fg',
boxShadow: 'xl',
'--toast-trigger-bg': 'colors.bg.muted',
'&[data-type=warning]': {
color: 'alert.fg',
bg: 'alert.bg.warning',
'--toast-trigger-bg': '{white/10}',
'--toast-border-color': '{white/40}',
},
'&[data-type=success]': {
color: 'alert.fg',
bg: 'alert.bg.success',
'--toast-trigger-bg': '{white/10}',
'--toast-border-color': '{white/40}',
},
'&[data-type=error]': {
color: 'alert.fg',
bg: 'alert.bg.error',
'--toast-trigger-bg': '{white/10}',
'--toast-border-color': '{white/40}',
},
'&[data-type=info]': {
color: 'alert.fg',
bg: 'alert.bg.info',
'--toast-trigger-bg': '{white/10}',
'--toast-border-color': '{white/40}',
},
},
title: {
fontWeight: '700',
textStyle: 'md',
marginEnd: '0',
},
description: {
display: 'inline',
textStyle: 'md',
},
indicator: {
flexShrink: '0',
boxSize: '5',
},
actionTrigger: {
textStyle: 'sm',
fontWeight: 'medium',
height: '8',
px: '3',
borderRadius: 'base',
alignSelf: 'center',
borderWidth: '1px',
borderColor: 'var(--toast-border-color, inherit)',
transition: 'background 200ms',
_hover: {
bg: 'var(--toast-trigger-bg)',
},
},
closeTrigger: {
position: 'static',
alignSelf: 'center',
top: '0',
insetEnd: '0',
padding: '2px',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
color: '{currentColor}',
borderRadius: 'none',
boxSize: '6',
transition: 'background 200ms',
cursor: 'pointer',
},
},
});
/* eslint-disable react/jsx-no-bind */
import { Heading, HStack, Link, Tabs, VStack } from '@chakra-ui/react'; import { Heading, HStack, Link, Tabs, VStack } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
...@@ -7,6 +8,7 @@ import { useColorMode } from 'toolkit/chakra/color-mode'; ...@@ -7,6 +8,7 @@ import { useColorMode } from 'toolkit/chakra/color-mode';
import { ProgressCircleRing, ProgressCircleRoot } from 'toolkit/chakra/progress-circle'; import { ProgressCircleRing, ProgressCircleRoot } from 'toolkit/chakra/progress-circle';
import { Skeleton } from 'toolkit/chakra/skeleton'; import { Skeleton } from 'toolkit/chakra/skeleton';
import { Switch } from 'toolkit/chakra/switch'; import { Switch } from 'toolkit/chakra/switch';
import { toaster } from 'toolkit/chakra/toaster';
import { Tooltip } from 'toolkit/chakra/tooltip'; import { Tooltip } from 'toolkit/chakra/tooltip';
import ContentLoader from 'ui/shared/ContentLoader'; import ContentLoader from 'ui/shared/ContentLoader';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
...@@ -17,7 +19,7 @@ const ChakraShowcases = () => { ...@@ -17,7 +19,7 @@ const ChakraShowcases = () => {
return ( return (
<> <>
<PageTitle title="Chakra UI Showcase"/> <PageTitle title="Chakra UI Showcase"/>
<Switch onCheckedChange={ colorMode.toggleColorMode } checked={ colorMode.colorMode === 'dark' } mb={ 4 }> <Switch onCheckedChange={ colorMode.toggleColorMode } checked={ colorMode.colorMode === 'dark' } mb={ 10 }>
Color mode: { colorMode.colorMode } Color mode: { colorMode.colorMode }
</Switch> </Switch>
...@@ -106,9 +108,17 @@ const ChakraShowcases = () => { ...@@ -106,9 +108,17 @@ const ChakraShowcases = () => {
<Heading textStyle="heading.md" mb={ 2 }>Alerts</Heading> <Heading textStyle="heading.md" mb={ 2 }>Alerts</Heading>
<HStack gap={ 4 } whiteSpace="nowrap"> <HStack gap={ 4 } whiteSpace="nowrap">
<Alert visual="info" title="Info"> Alert content </Alert> <Alert visual="info" title="Info"> Alert content </Alert>
<Alert visual="neutral" title="Neutral"> Alert content </Alert>
<Alert visual="warning" title="Warning"> Alert content </Alert> <Alert visual="warning" title="Warning"> Alert content </Alert>
<Alert visual="success" title="Success"> Alert content </Alert> <Alert visual="success" title="Success"> Alert content </Alert>
<Alert visual="error" title="Error"> Alert content </Alert> <Alert visual="error" title="Error" startElement={ null }> Alert content </Alert>
</HStack>
</section>
<section>
<Heading textStyle="heading.md" mb={ 2 }>Toasts</Heading>
<HStack gap={ 4 } whiteSpace="nowrap">
<Button onClick={ () => toaster.success({ title: 'Success', description: 'Toast content' }) }>Success</Button>
</HStack> </HStack>
</section> </section>
</VStack> </VStack>
......
import type { ToastProps, AlertStatus } from '@chakra-ui/react';
import { Alert, AlertTitle, AlertDescription, CloseButton } from '@chakra-ui/react';
import { chakra } from '@chakra-ui/system';
import React from 'react';
function getBgColor(status?: AlertStatus) {
switch (status) {
case 'success':
return 'green.100';
case 'error':
return 'red.100';
case 'warning':
return 'orange.100';
case 'info':
default:
return 'blue.100';
}
}
const Toast = ({ onClose, title, description, id, isClosable, status, icon }: ToastProps) => {
const ids = id ?
{
root: `toast-${ id }`,
title: `toast-${ id }-title`,
description: `toast-${ id }-description`,
} :
undefined;
const bgColor = getBgColor(status);
return (
<Alert
id={ ids?.root }
alignItems="start"
borderRadius="md"
boxShadow="lg"
paddingY={ 4 }
paddingLeft={ 6 }
paddingRight="72px"
color="gray.700"
bgColor={ bgColor }
textAlign="start"
width="auto"
maxWidth="400px"
>
<chakra.div flex="1" maxWidth="100%">
{ title && <AlertTitle id={ ids?.title } display="flex" alignItems="center">{ icon }{ title }</AlertTitle> }
{ description && (
<AlertDescription id={ ids?.description } display="block">
{ description }
</AlertDescription>
) }
</chakra.div>
{ isClosable && (
<CloseButton
size="md"
borderRadius="base"
color="gray.700"
onClick={ onClose }
position="absolute"
insetEnd={ 4 }
top={ 4 }
/>
) }
</Alert>
);
};
export default Toast;
...@@ -4,22 +4,20 @@ import React from 'react'; ...@@ -4,22 +4,20 @@ import React from 'react';
import config from 'configs/app'; import config from 'configs/app';
import { Alert } from 'toolkit/chakra/alert'; import { Alert } from 'toolkit/chakra/alert';
// TODO @tom2drum fix this alert
const MaintenanceAlert = () => { const MaintenanceAlert = () => {
if (!config.UI.maintenanceAlert.message) { if (!config.UI.maintenanceAlert.message) {
return null; return null;
} }
return ( return (
<Alert status="info" colorScheme="gray" py={ 3 } borderRadius="md"> <Alert visual="neutral">
{ /* <AlertIcon display={{ base: 'none', lg: 'flex' }}/> */ }
<Box <Box
dangerouslySetInnerHTML={{ __html: config.UI.maintenanceAlert.message }} dangerouslySetInnerHTML={{ __html: config.UI.maintenanceAlert.message }}
css={{ css={{
'& a': { '& a': {
color: 'link', color: 'link.primary',
_hover: { _hover: {
color: 'link_hovered', color: 'link.primary.hover',
}, },
}, },
}} }}
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment