diff --git a/.changeset/ten-rabbits-reflect.md b/.changeset/ten-rabbits-reflect.md new file mode 100644 index 00000000..661e2a20 --- /dev/null +++ b/.changeset/ten-rabbits-reflect.md @@ -0,0 +1,5 @@ +--- +'@project44-manifest/react': minor +--- + +toast position support diff --git a/packages/react/src/components/Toast/Toast.types.ts b/packages/react/src/components/Toast/Toast.types.ts index 430cf202..7a3b9d3d 100644 --- a/packages/react/src/components/Toast/Toast.types.ts +++ b/packages/react/src/components/Toast/Toast.types.ts @@ -3,6 +3,14 @@ import type { IconButton } from '../button'; export type ToastVariant = 'default' | 'error' | 'info' | 'success' | 'warning'; +export type ToastPosition = + | 'bottom-center' + | 'bottom-left' + | 'bottom-right' + | 'top-center' + | 'top-left' + | 'top-right'; + export type ToastElement = 'div'; export interface ToastProps { @@ -48,6 +56,10 @@ export interface ToastProps { * @default 'default' */ variant?: ToastVariant; + /** + * The position + */ + position?: ToastPosition; } export interface ToastOptions { @@ -95,4 +107,8 @@ export interface ToastOptions { * The display variant of the toast. */ variant?: ToastVariant; + /** + * The position + */ + position?: ToastPosition; } diff --git a/packages/react/src/components/Toast/index.ts b/packages/react/src/components/Toast/index.ts index f2aa8580..bfe0e97e 100644 --- a/packages/react/src/components/Toast/index.ts +++ b/packages/react/src/components/Toast/index.ts @@ -1,3 +1,3 @@ export { Toast } from './Toast'; -export type { ToastOptions, ToastProps, ToastVariant } from './Toast.types'; +export type { ToastOptions, ToastPosition, ToastProps, ToastVariant } from './Toast.types'; export { toast } from './utils'; diff --git a/packages/react/src/components/Toast/story/Toast.stories.tsx b/packages/react/src/components/Toast/story/Toast.stories.tsx index 5b7ab9b8..b4e6e8e2 100644 --- a/packages/react/src/components/Toast/story/Toast.stories.tsx +++ b/packages/react/src/components/Toast/story/Toast.stories.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Button, toast, Toaster, ToasterProps } from '../../..'; +import { Button, ButtonGroup, Flex, toast, Toaster, ToasterProps, ToastPosition } from '../../..'; export default { title: 'Components/Toast', @@ -123,3 +123,49 @@ export const Error = (args: ToasterProps) => { ); }; + +export const Positioning = (args: ToasterProps) => { + const handlePress = (position: ToastPosition): void => { + toast.info('Lorem ipsum dolor', { + description: + 'consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + duration: 1000, + position, + }); + }; + + const handleTopLeft = React.useCallback(() => { + handlePress('top-left'); + }, []); + const handleTopCenter = React.useCallback(() => { + handlePress('top-center'); + }, []); + const handleTopRight = React.useCallback(() => { + handlePress('top-right'); + }, []); + const handleBottomLeft = React.useCallback(() => { + handlePress('bottom-left'); + }, []); + const handleBottomCenter = React.useCallback(() => { + handlePress('bottom-center'); + }, []); + const handleBottomRight = React.useCallback(() => { + handlePress('bottom-right'); + }, []); + + return ( + + + + + + + + + + + + + + ); +}; diff --git a/packages/react/src/components/Toaster/Toaster.tsx b/packages/react/src/components/Toaster/Toaster.tsx index 9bdda5a9..3780d8b3 100644 --- a/packages/react/src/components/Toaster/Toaster.tsx +++ b/packages/react/src/components/Toaster/Toaster.tsx @@ -6,14 +6,7 @@ import type { ToastOptions } from '../Toast'; import { Toast } from '../Toast'; import { StyledToaster } from './Toaster.styles'; import type { ToasterElement, ToasterProps } from './Toaster.types'; - -const getPositionStyle = (offset: number): React.CSSProperties => ({ - position: 'absolute', - right: 0, - top: 0, - transform: `translateY(${Number(offset)}px)`, - transition: 'all 200ms cubic-bezier(0.4, 0.14, 0.3, 1)', -}); +import { getPositionStyle } from './utils'; export const Toaster = React.forwardRef((props, forwardedRef) => { const { as, className: classNameProp, css: cssProp, duration = 5000, ...other } = props; @@ -56,19 +49,19 @@ export const Toaster = React.forwardRef((props, forwardedRef) => { message, onDismiss, variant, + position = 'top-right', } = rest as ToastOptions; const offset = handlers.calculateOffset(toast, { gutter: 8, - defaultPosition: 'top-right', + defaultPosition: position, }); - const styles = getPositionStyle(offset); + const styles = getPositionStyle(position as string, offset); const toastRef = (element: HTMLElement | null) => { if (element && typeof toast.height !== 'number') { const { height } = element.getBoundingClientRect(); - updateHeight(toast.id, height); } }; diff --git a/packages/react/src/components/Toaster/utils.ts b/packages/react/src/components/Toaster/utils.ts new file mode 100644 index 00000000..4bb0dc5d --- /dev/null +++ b/packages/react/src/components/Toaster/utils.ts @@ -0,0 +1,55 @@ +export const getPositionStyle = (position: string, offset: number): React.CSSProperties => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const styles: React.CSSProperties = { + position: 'absolute', + transition: 'all 200ms cubic-bezier(0.4, 0.14, 0.3, 1)', + } as React.CSSProperties; + + switch (position) { + case 'top-left': + return { + ...styles, + top: 0, + left: 0, + transform: `translateY(${offset}px)`, + }; + + case 'top-center': + return { + ...styles, + top: 0, + left: '50%', + transform: `translate(-50%,${offset}px)`, + }; + case 'top-right': + return { + ...styles, + top: 0, + right: 0, + transform: `translateY(${offset}px)`, + }; + case 'bottom-left': + return { + ...styles, + bottom: 0, + left: 0, + transform: `translateY(${-offset}px)`, + }; + case 'bottom-center': + return { + ...styles, + bottom: 0, + left: '50%', + transform: `translate(-50%, ${-offset}px)`, + }; + case 'bottom-right': + return { + ...styles, + bottom: 0, + right: 0, + transform: `translateY(${-offset}px)`, + }; + default: + return styles; + } +};