Skip to content

Commit

Permalink
feat: update ToastManager to enable passing transition props and sp…
Browse files Browse the repository at this point in the history
…ecifying toast data (#814)
  • Loading branch information
cheton authored Jan 1, 2024
1 parent ea644b8 commit ce86bcd
Show file tree
Hide file tree
Showing 12 changed files with 286 additions and 108 deletions.
25 changes: 24 additions & 1 deletion packages/react-docs/pages/_app.page.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,30 @@ const App = (props) => {
useCSSBaseline
>
<PortalManager>
<ToastManager>
<ToastManager
TransitionProps={{
sx: {
'[data-toast-placement^="top"] > &:first-of-type': {
mt: '4x', // the space to the top edge of the screen
},
'[data-toast-placement^="top"] > &:not(:first-of-type)': {
mt: '2x', // the space between toasts
},
'[data-toast-placement^="bottom"] > &:last-of-type': {
mb: '4x', // the space to the bottom edge of the screen
},
'[data-toast-placement^="bottom"] > &:not(:last-of-type)': {
mb: '2x', // the space between toasts
},
'[data-toast-placement$="left"] > &': {
ml: '4x', // the space to the left edge of the screen
},
'[data-toast-placement$="right"] > &': {
mr: '4x', // the space to the right edge of the screen
},
},
}}
>
<MDXProvider components={MDXComponents}>
<Page {...props} />
<GlobalStyles />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { Box, Button, Divider, Flex, TextLabel, Toast, ToastManager, useToastManager } from '@tonic-ui/react';
import React, { useRef, useState } from 'react';

const FormGroup = (props) => (
<Box mb="4x" {...props} />
);

const ToastApp = () => {
const counterRef = useRef(0);
const toast = useToastManager();
const placement = 'bottom-right';
const isTop = placement.includes('top');

return (
<Button
onClick={() => {
const render = ({ data, onClose, placement }) => {
return (
<Toast isClosable onClose={onClose} minWidth={320}>
This is a toast message #{data.index + 1}
</Toast>
);
};

// Remove the oldest toast if there are more than 3 toasts
const toastsOfPlacement = toast.state[placement];
if (toastsOfPlacement.length >= 3) {
const oldestToast = isTop ? toastsOfPlacement[toastsOfPlacement.length - 1] : toastsOfPlacement[0];
toast.close(oldestToast.id, placement);
}

const options = {
placement: placement,
duration: 30 * 1000,
data: { // user-defined data
index: counterRef.current++,
},
};
toast(render, options);
}}
>
Notify Toast
</Button>
);
};

const App = () => {
const [edgeSpacing, setEdgeSpacing] = useState(16);
const [toastSpacing, setToastSpacing] = useState(16);

return (
<>
<FormGroup>
<Box mb="2x">
<TextLabel>
The space to the edge of the screen
</TextLabel>
</Box>
<Flex alignItems="center" columnGap="2x" mb="4x">
<input
type="range"
min={0}
max={64}
step={4}
onChange={(event) => {
const value = parseInt(event.target.value);
setEdgeSpacing(value);
}}
value={edgeSpacing}
/>
{edgeSpacing}px
</Flex>
</FormGroup>
<FormGroup>
<Box mb="2x">
<TextLabel>
The space between toasts
</TextLabel>
</Box>
<Flex alignItems="center" columnGap="2x" mb="4x">
<input
type="range"
min={0}
max={32}
step={4}
onChange={(event) => {
const value = parseInt(event.target.value);
setToastSpacing(value);
}}
value={toastSpacing}
/>
{toastSpacing}px
</Flex>
</FormGroup>
<Divider my="4x" />
<ToastManager
TransitionProps={{
sx: {
'[data-toast-placement^="top"] > &:first-of-type': {
mt: edgeSpacing, // the space to the top edge of the screen
},
'[data-toast-placement^="top"] > &:not(:first-of-type)': {
mt: toastSpacing, // the space between toasts
},
'[data-toast-placement^="bottom"] > &:last-of-type': {
mb: edgeSpacing, // the space to the bottom edge of the screen
},
'[data-toast-placement^="bottom"] > &:not(:last-of-type)': {
mb: toastSpacing, // the space between toasts
},
'[data-toast-placement$="left"] > &': {
ml: edgeSpacing, // the space to the left edge of the screen
},
'[data-toast-placement$="right"] > &': {
mr: edgeSpacing, // the space to the right edge of the screen
},
},
}}
>
<ToastApp />
</ToastManager>
</>
);
};

export default App;
79 changes: 65 additions & 14 deletions packages/react-docs/pages/components/toast-manager/index.page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,31 @@ import { TonicProvider, ToastManager } from '@tonic-ui/react';
function App() {
return (
<TonicProvider>
<ToastManager placement="bottom-right">
<ToastManager
TransitionProps={{
sx: {
'[data-toast-placement^="top"] > &:first-of-type': {
mt: '4x', // the space to the top edge of the screen
},
'[data-toast-placement^="top"] > &:not(:first-of-type)': {
mt: '2x', // the space between toasts
},
'[data-toast-placement^="bottom"] > &:last-of-type': {
mb: '4x', // the space to the bottom edge of the screen
},
'[data-toast-placement^="bottom"] > &:not(:last-of-type)': {
mb: '2x', // the space between toasts
},
'[data-toast-placement$="left"] > &': {
ml: '4x', // the space to the left edge of the screen
},
'[data-toast-placement$="right"] > &': {
mr: '4x', // the space to the right edge of the screen
},
},
}}
placement="bottom-right"
>
{/* Your app goes here */}
</ToastManager>
</TonicProvider>
Expand All @@ -29,20 +53,8 @@ function MyComponent() {
const toast = useToastManager();
const handleClickOpenToast = () => {
const render = ({ onClose, placement }) => {
const styleProps = {
'top-left': { pt: '2x', px: '4x' },
'top': { pt: '2x', px: '4x' },
'top-right': { pt: '2x', px: '4x' },
'bottom-left': { pb: '2x', px: '4x' },
'bottom': { pb: '2x', px: '4x' },
'bottom-right': { pb: '2x', px: '4x' },
}[placement];

return (
<Box
{...styleProps}
width={320}
>
<Box width={320}>
<Toast isClosable onClose={onClose}>
This is a toast notification
</Toast>
Expand Down Expand Up @@ -82,12 +94,51 @@ toast.remove(id);

See the [useToastManager](./toast-manager/useToastManager) Hook for detailed usage.

## Commonly Asked Questions

### Control the spacing between toasts

To control the spacing between toasts, make use of the `TransitionProps` prop in the `ToastManager`. Pass the `sx` prop to the transition component with your preferred spacing.

```jsx disabled
<ToastManager
TransitionProps={{
sx: {
'[data-toast-placement^="top"] > &:first-of-type': {
mt: edgeSpacing, // the space to the top edge of the screen
},
'[data-toast-placement^="top"] > &:not(:first-of-type)': {
mt: toastSpacing, // the space between toasts
},
'[data-toast-placement^="bottom"] > &:last-of-type': {
mb: edgeSpacing, // the space to the bottom edge of the screen
},
'[data-toast-placement^="bottom"] > &:not(:last-of-type)': {
mb: toastSpacing, // the space between toasts
},
'[data-toast-placement$="left"] > &': {
ml: edgeSpacing, // the space to the left edge of the screen
},
'[data-toast-placement$="right"] > &': {
mr: edgeSpacing, // the space to the right edge of the screen
},
},
}}
>
<ToastApp />
</ToastManager>
```

{render('./faq-control-spacing-between-toasts')}

## Props

### ToastManager

| Name | Type | Default | Description |
| :--- | :--- | :------ | :---------- |
| TransitionComponent | ElementType | ToastTransition | The component used for the transition. |
| TransitionProps | object | | Props applied to the [Transition](http://reactcommunity.org/react-transition-group/transition#Transition-props) element. |
| children | ReactNode \| `(context) => ReactNode` | | A function child can be used intead of a React element. This function is called with the context object. |
| containerRef | RefObject | | A `ref` to the component where the toasts will be rendered. |
| placement | string | 'bottom-right' | The default placement to place toasts. One of: 'top', 'top-right', 'top-left', 'bottom', 'bottom-left', 'bottom-right' |
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,8 @@ const App = () => {
const toast = useToastManager();
const handleClickOpenToast = useCallback(() => {
const render = ({ onClose, placement }) => {
const styleProps = {
'top-left': { pt: '2x', px: '4x' },
'top': { pt: '2x', px: '4x' },
'top-right': { pt: '2x', px: '4x' },
'bottom-left': { pb: '2x', px: '4x' },
'bottom': { pb: '2x', px: '4x' },
'bottom-right': { pb: '2x', px: '4x' },
}[placement];

return (
<Box
{...styleProps}
width={320}
>
<Box width={320}>
<Toast isClosable onClose={onClose}>
<Text>This is a toast notification</Text>
</Toast>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Create a toast at the specified placement and return the toast id.
<dl>
<dd>`message` *(Function|string)*: The toast message to render.</dd>
<dd>`[options={}]` *(Object)*: The options object.</dd>
<dd>`[options.data]` *(any)*: The user-defined data supplied to the toast.</dd>
<dd>`[options.duration=null]` *(number)*: The duration (in milliseconds) that the toast should remain on the screen. If set to null, toast will never dismiss.</dd>
<dd>`[options.id]` *(string)*: A unique ID of the toast.</dd>
<dd>`[options.placement]` *(string)*: The placement of the toast.</dd>
Expand Down Expand Up @@ -155,10 +156,10 @@ toast.setState({
'top': [
{
id: '2',
message: 'New toast message',
placement: 'top',
duration: 3000,
message: 'New toast message',
onClose: () => toast.close('2'),
placement: 'top',
}
],
'top-left': [],
Expand All @@ -178,10 +179,10 @@ toast.setState(prevState => ({
...prevState['top'],
{
id: '2',
message: 'New toast message',
placement: 'top',
duration: null,
message: 'New toast message',
onClose: () => toast.close('2', 'top'),
placement: 'top',
},
],
}));
Expand All @@ -196,10 +197,10 @@ The toast state is a placement object, each placement contains an array of objec
'top': [
{
id: '1', // A unique identifier that represents the toast message
message: ({ id, onClose, placement }) => <Toast />, // The toast message to render
placement: 'top', // The placement of the toast
duration: null, // The duration (in milliseconds) that the toast should remain on the screen. If set to null, toast will never dismiss.
message: ({ id, onClose, placement }) => <Toast />, // The toast message to render
onClose: () => toast.close(id, placement), // The function to close the toast
placement: 'top', // The placement of the toast
},
],
'top-left': [],
Expand Down
17 changes: 3 additions & 14 deletions packages/react-docs/pages/components/toast/appearances.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,21 +70,10 @@ const App = () => {
const toast = useToastManager();
const handleClickBy = (ToastNotification) => () => {
toast(({ onClose, placement }) => {
const styleProps = {
'top-left': { pt: '2x', px: '4x' },
'top': { pt: '2x', px: '4x' },
'top-right': { pt: '2x', px: '4x' },
'bottom-left': { pb: '2x', px: '4x' },
'bottom': { pb: '2x', px: '4x' },
'bottom-right': { pb: '2x', px: '4x' },
}[placement];

return (
<Box {...styleProps}>
<ToastLayout>
<ToastNotification onClose={onClose} />
</ToastLayout>
</Box>
<ToastLayout>
<ToastNotification onClose={onClose} />
</ToastLayout>
);
}, {
placement: 'bottom-right',
Expand Down
17 changes: 3 additions & 14 deletions packages/react-docs/pages/components/toast/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,10 @@ const App = () => {
const toast = useToastManager();
const handleClickBy = (ToastNotification) => () => {
toast(({ onClose, placement }) => {
const styleProps = {
'top-left': { pt: '2x', px: '4x' },
'top': { pt: '2x', px: '4x' },
'top-right': { pt: '2x', px: '4x' },
'bottom-left': { pb: '2x', px: '4x' },
'bottom': { pb: '2x', px: '4x' },
'bottom-right': { pb: '2x', px: '4x' },
}[placement];

return (
<Box {...styleProps}>
<ToastLayout>
<ToastNotification onClose={onClose} />
</ToastLayout>
</Box>
<ToastLayout>
<ToastNotification onClose={onClose} />
</ToastLayout>
);
}, {
placement: 'bottom-right',
Expand Down
Loading

0 comments on commit ce86bcd

Please sign in to comment.