diff --git a/example/storybook-nativewind/.storybook/preview.js b/example/storybook-nativewind/.storybook/preview.js index 648d13247..a6a29b2c6 100644 --- a/example/storybook-nativewind/.storybook/preview.js +++ b/example/storybook-nativewind/.storybook/preview.js @@ -95,7 +95,7 @@ export const parameters = { 'Recipes', ['Linear Gradient'], 'More', - ['FAQ', 'Releases', 'Roadmap', 'Troubleshooting'], + ['FAQs', 'Releases', 'Roadmap', 'Troubleshooting'], ], ], icons: [ diff --git a/example/storybook-nativewind/package.json b/example/storybook-nativewind/package.json index 798cde6eb..56a95f033 100644 --- a/example/storybook-nativewind/package.json +++ b/example/storybook-nativewind/package.json @@ -32,7 +32,7 @@ "@gluestack-style/animation-resolver": "^1.0.4", "@gluestack-style/react": "^1.0.57", "@gluestack-ui/config": "^1.1.19", - "@gluestack-ui/themed": "^1.1.34", + "@gluestack-ui/themed": "^1.1.35", "@gluestack/design-system": "^0.5.36", "@gorhom/bottom-sheet": "^5.0.0-alpha.10", "@legendapp/motion": "^2.2.0", diff --git a/example/storybook-nativewind/public/images/benchmarks-v2.png b/example/storybook-nativewind/public/images/benchmarks-v2.png new file mode 100644 index 000000000..626a1bf48 Binary files /dev/null and b/example/storybook-nativewind/public/images/benchmarks-v2.png differ diff --git a/example/storybook-nativewind/src/components/Toast/Toast.tsx b/example/storybook-nativewind/src/components/Toast/Toast.tsx index 262ed4164..4f3ac5fed 100644 --- a/example/storybook-nativewind/src/components/Toast/Toast.tsx +++ b/example/storybook-nativewind/src/components/Toast/Toast.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Icon, CloseIcon } from '@/components/ui/icon'; import { VStack } from '@/components/ui/vstack'; import { Pressable } from '@/components/ui/pressable'; @@ -26,31 +26,46 @@ const ToastFigmaStory = ({ _placement = 'top', _colorMode, ...props }: any) => { ); }; -const ToastBasic = ({ placement = 'top', ...props }: any) => { +const ToastBasic = ({ ...props }: any) => { const toast = useToast(); + const [toastId, setToastId] = useState(0); + + const handleToast = () => { + if (!toast.isActive(toastId)) { + showNewToast(); + } + }; + + const showNewToast = () => { + const newId = Math.random(); + setToastId(newId); + + toast.show({ + id: newId, + placement: props.placement, + duration: 3000, + // duration: null, + render: ({ id }) => { + const uniqueToastId = `toast-${id}`; + return ( + + + Hello World Toast + + Please create a support ticket from the support page + + + toast.close(id)}> + + + + ); + }, + }); + }; + return ( - ); diff --git a/example/storybook-nativewind/src/components/Toast/index.nw.stories.mdx b/example/storybook-nativewind/src/components/Toast/index.nw.stories.mdx index ae7bd4014..53daa3f03 100644 --- a/example/storybook-nativewind/src/components/Toast/index.nw.stories.mdx +++ b/example/storybook-nativewind/src/components/Toast/index.nw.stories.mdx @@ -61,32 +61,41 @@ This is an illustration of **Toast** component. showArgsController={true} metaData={{ code: ` - function Example() { + function Example() { const toast = useToast(); + const [toastId, setToastId] = React.useState(0); + const handleToast = () => { + if (!toast.isActive(toastId)) { + showNewToast(); + } + }; + const showNewToast = () => { + const newId = Math.random(); + setToastId(newId); + toast.show({ + id: newId, + placement: 'top', + duration: 3000, + // duration: null, + render: ({ id }) => { + const uniqueToastId = "toast-" + id; + return ( + + Toast Heading + + Lorem ipsum dolor sit amet consectetur. + + + ); + }, + }); + }; return ( - ); - }; + } `, transformCode: (code) => { return transformedCode(code, 'function', 'Example'); @@ -405,64 +414,61 @@ The Examples section provides visual representations of the different variants o showArgsController={false} metaData={{ code: ` - function Example() { + function Example() { const toast = useToast(); + const [toastId, setToastId] = React.useState(0); + const handleToast = () => { + if (!toast.isActive(toastId)) { + showNewToast(); + } + }; + const showNewToast = () => { + const newId = Math.random(); + setToastId(newId); + toast.show({ + id: newId, + placement: 'top', + duration: 3000, + render: ({ id }) => { + const uniqueToastId = "toast-" + id; + return ( + + + + + Toast Heading + + Lorem ipsum dolor sit amet consectetur. + + + + + + toast.close(id)}> + + + + + ); + }, + }); + }; return ( - - toast.close(id)}> - - - - - ); - }, - }); - }} - > - Show Toast + ); - }; + } `, transformCode: (code) => { return transformedCode(code, 'function', 'Example'); @@ -583,68 +589,74 @@ The Examples section provides visual representations of the different variants o metaData={{ code: ` function Example() { - const toast = useToast(); - return ( - - - - - - ); - }, - }); - }} - > - Show Toast - - ); + Not now + + + + + + ); + }, + }); }; + return ( + + ); + } `, transformCode: (code) => { return transformedCode(code, 'function', 'Example'); diff --git a/example/storybook-nativewind/src/core-components/nativewind/tooltip/index.tsx b/example/storybook-nativewind/src/core-components/nativewind/tooltip/index.tsx index 68c0d06d2..17b9dc8ee 100644 --- a/example/storybook-nativewind/src/core-components/nativewind/tooltip/index.tsx +++ b/example/storybook-nativewind/src/core-components/nativewind/tooltip/index.tsx @@ -105,27 +105,6 @@ export const TooltipContent = React.forwardRef( return ( + -# FAQ +# FAQs ## Migration and Compatibility diff --git a/example/storybook-nativewind/src/home/overview/Introduction/index.nw.stories.mdx b/example/storybook-nativewind/src/home/overview/Introduction/index.nw.stories.mdx index f9de57510..fc13e2d7f 100644 --- a/example/storybook-nativewind/src/home/overview/Introduction/index.nw.stories.mdx +++ b/example/storybook-nativewind/src/home/overview/Introduction/index.nw.stories.mdx @@ -105,9 +105,3 @@ Stay updated about our company and collaborate on enterprise-level projects. ## All Components Explore 30+ components for React, Next.js & React Native [See All](/ui/docs/components/all-components). - -# FAQs - -### What are the new components in the pipeline? - -We're currently working on adding new components which include Table, Tabs and many more. We've already prepared API drafts for some of these components, which you can find in our documentation. You can check out our new component development roadmap [here](/ui/docs/home/more/roadmap). diff --git a/example/storybook-nativewind/src/home/performance/benchmarks/index.stories.mdx b/example/storybook-nativewind/src/home/performance/benchmarks/index.stories.mdx index 290eb308c..52f8d5dda 100644 --- a/example/storybook-nativewind/src/home/performance/benchmarks/index.stories.mdx +++ b/example/storybook-nativewind/src/home/performance/benchmarks/index.stories.mdx @@ -71,10 +71,17 @@ Creating a layout with HStack, VStack, Image, and Text to display a list of 28 i }} /> -> Note: This test was taken from [Tamagui](https://github.com/tamagui/tamagui/tree/24ac758bbe5d6ff2f67f25071df4286e0594f578/starters/benchmark). +> Note: This test was taken from Tamagui. # Web We are working on dedicated benchmarks for the web. Stay tuned for more updates! -In the meantime, we've built the landing page for version 2 using `gluestack-ui v2`, achieving a Lighthouse score of 85+. \ No newline at end of file +In the meantime, we have built the landing page for v2 using gluestack-ui v2. You can check out the lighthouse score below: + +gluestack-ui v2 lighthouse score +
\ No newline at end of file diff --git a/example/storybook-nativewind/src/hooks/useMediaQuery/index.nw.stories.mdx b/example/storybook-nativewind/src/hooks/useMediaQuery/index.nw.stories.mdx index 7a8250442..e2abbc2d4 100644 --- a/example/storybook-nativewind/src/hooks/useMediaQuery/index.nw.stories.mdx +++ b/example/storybook-nativewind/src/hooks/useMediaQuery/index.nw.stories.mdx @@ -54,10 +54,10 @@ import Wrapper from '../../core-components/nativewind/Wrapper'; }, { minWidth: 769, - maxWidth: 1300, + maxWidth: 1440, }, { - minWidth: 1301, + minWidth: 1441, }, ]); return ( diff --git a/example/storybook-nativewind/src/hooks/useMediaQuery/useMediaQuery.tsx b/example/storybook-nativewind/src/hooks/useMediaQuery/useMediaQuery.tsx index 42c9d6d01..4e2adb325 100644 --- a/example/storybook-nativewind/src/hooks/useMediaQuery/useMediaQuery.tsx +++ b/example/storybook-nativewind/src/hooks/useMediaQuery/useMediaQuery.tsx @@ -16,10 +16,10 @@ const UseMediaQueryBasic = ({ ...props }: any) => { }, { minWidth: 769, - maxWidth: 1300, + maxWidth: 1440, }, { - minWidth: 1301, + minWidth: 1441, }, ]); diff --git a/packages/config/package.json b/packages/config/package.json index e38e70a40..3c98fc02f 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -66,14 +66,14 @@ "@gluestack-ui/switch": "0.1.22", "@gluestack-ui/tabs": "0.1.16", "@gluestack-ui/textarea": "0.1.23", - "@gluestack-ui/themed": "1.1.34", - "@gluestack-ui/toast": "1.0.5", + "@gluestack-ui/themed": "1.1.35", + "@gluestack-ui/toast": "1.0.6", "@gluestack-ui/tooltip": "0.1.31", "@legendapp/motion": "latest" }, "peerDependencies": { "@gluestack-style/react": ">=1.0.57", - "@gluestack-ui/themed": ">=1.1.34" + "@gluestack-ui/themed": ">=1.1.35" }, "release-it": { "git": { diff --git a/packages/themed/CHANGELOG.md b/packages/themed/CHANGELOG.md index da9efb4f5..9c8435039 100644 --- a/packages/themed/CHANGELOG.md +++ b/packages/themed/CHANGELOG.md @@ -1,5 +1,12 @@ # @gluestack-ui/themed +## 1.1.35 + +### Patch Changes + +- Updated dependencies + - @gluestack-ui/toast@1.0.6 + ## 1.1.34 ### Patch Changes diff --git a/packages/themed/package.json b/packages/themed/package.json index f859f084d..1f787c397 100644 --- a/packages/themed/package.json +++ b/packages/themed/package.json @@ -1,6 +1,6 @@ { "name": "@gluestack-ui/themed", - "version": "1.1.34", + "version": "1.1.35", "main": "build/index.js", "types": "build/index.d.ts", "module": "build/index", @@ -65,7 +65,7 @@ "@gluestack-ui/switch": "0.1.22", "@gluestack-ui/tabs": "0.1.16", "@gluestack-ui/textarea": "0.1.23", - "@gluestack-ui/toast": "1.0.5", + "@gluestack-ui/toast": "1.0.6", "@gluestack-ui/tooltip": "0.1.31", "@legendapp/motion": "latest" }, diff --git a/packages/unstyled/toast/CHANGELOG.md b/packages/unstyled/toast/CHANGELOG.md index 34c5616ec..f3c80e975 100644 --- a/packages/unstyled/toast/CHANGELOG.md +++ b/packages/unstyled/toast/CHANGELOG.md @@ -1,5 +1,16 @@ # @gluestack-ui/toast +## 1.0.6 + +### Patch Changes + +- ### Fixes + + - Fixed `isActive` always returning false by [Andrew M](https://github.com/evelant). + - Removed almost all instances of `any`, replaced with actual types by [Andrew M](https://github.com/evelant). + - Fixed duplicate toasts when calling `show` with an existing id by [Andrew M](https://github.com/evelant). + - Removed unnecessary `@ts-ignore` usages by [Andrew M](https://github.com/evelant). + ## 1.0.5 ### Patch Changes diff --git a/packages/unstyled/toast/package.json b/packages/unstyled/toast/package.json index e5b35007b..ac7e09c6a 100644 --- a/packages/unstyled/toast/package.json +++ b/packages/unstyled/toast/package.json @@ -15,7 +15,7 @@ "ios", "nextjs" ], - "version": "1.0.5", + "version": "1.0.6", "main": "lib/commonjs/index", "module": "lib/module/index", "types": "lib/typescript/index.d.ts", diff --git a/packages/unstyled/toast/src/OverlayAnimatePresence.tsx b/packages/unstyled/toast/src/OverlayAnimatePresence.tsx index 517c7b26c..7f85c7b20 100644 --- a/packages/unstyled/toast/src/OverlayAnimatePresence.tsx +++ b/packages/unstyled/toast/src/OverlayAnimatePresence.tsx @@ -1,14 +1,14 @@ /* eslint-disable react-hooks/exhaustive-deps */ +import { ExitAnimationContext } from '@gluestack-ui/overlay'; import React, { forwardRef } from 'react'; import { Animated } from 'react-native'; -import { ExitAnimationContext } from '@gluestack-ui/overlay'; -const defaultTransitionConfig: any = { +const defaultTransitionConfig = { type: 'timing', useNativeDriver: true, duration: 0, delay: 0, -}; +} as const; export const OverlayAnimatePresence = forwardRef( ({ children, visible = false, AnimatePresence, onExit }: any, ref?: any) => { @@ -27,8 +27,6 @@ export const OverlayAnimatePresence = forwardRef( if (AnimatePresence) { Animated.sequence([ - // @ts-ignore - delay is present in defaultTransitionConfig - //@ts-ignore Animated[transition.type ?? 'timing'](animateValue, { toValue: startAnimation, useNativeDriver: true, diff --git a/packages/unstyled/toast/src/Toast.tsx b/packages/unstyled/toast/src/Toast.tsx index dd574ccea..cc152466d 100644 --- a/packages/unstyled/toast/src/Toast.tsx +++ b/packages/unstyled/toast/src/Toast.tsx @@ -1,10 +1,10 @@ -import React, { useState, useMemo } from 'react'; -import { ToastList } from './ToastList'; -import type { IToastInfo, IToast, IToastProps } from './types'; -import { ToastContext } from './ToastContext'; +import React, { useMemo, useState } from 'react'; import { View } from 'react-native'; +import { ToastContext } from './ToastContext'; +import { ToastList } from './ToastList'; +import type { IToast, IToastInfo, IToastProps } from './types'; export const ToastProvider = ({ children }: { children: any }) => { - const [toastInfo, setToastInfo] = useState({}); + const [toastInfo, setToastInfo] = useState({} as IToastInfo); const [visibleToasts, setVisibleToasts] = useState<{ [key in string]: boolean; }>({}); @@ -18,7 +18,7 @@ export const ToastProvider = ({ children }: { children: any }) => { }, [setVisibleToasts]); const hideToast = React.useCallback( - (id: any) => { + (id: string) => { setVisibleToasts((prevVisibleToasts) => ({ ...prevVisibleToasts, [id]: false, @@ -28,10 +28,14 @@ export const ToastProvider = ({ children }: { children: any }) => { ); const isActive = React.useCallback( - (id: any) => { - for (const toastPosition of Object.keys(toastInfo)) { + (id: string) => { + for (const toastPosition of Object.keys( + toastInfo + ) as (keyof typeof toastInfo)[]) { const positionArray: Array = toastInfo[toastPosition]; - return positionArray.findIndex((toastData) => toastData.id === id) > -1; + if (positionArray.findIndex((toastData) => toastData.id === id) > -1) { + return true; + } } return false; }, @@ -39,9 +43,11 @@ export const ToastProvider = ({ children }: { children: any }) => { ); const removeToast = React.useCallback( - (id: any) => { + (id: string) => { setToastInfo((prev) => { - for (const toastPosition of Object.keys(prev)) { + for (const toastPosition of Object.keys( + prev + ) as (keyof typeof prev)[]) { const positionArray: Array = prev[toastPosition]; const isToastPresent = positionArray.findIndex((toastData) => toastData.id === id) > -1; @@ -65,30 +71,34 @@ export const ToastProvider = ({ children }: { children: any }) => { ); const setToast = React.useCallback( - (props: IToastProps): number => { + (props: IToastProps): string => { const { placement = 'bottom', render, - id = toastIndex.current++, + id = `${toastIndex.current++}`, duration = 5000, } = props; if (render) { const component = render({ id }); - setToastInfo((prev: any) => { + setToastInfo((prev: IToastInfo): IToastInfo => { return { ...prev, [placement]: [ - ...(prev[placement] ? prev[placement] : []), + ...(prev[placement] ? prev[placement] : []).filter( + (t) => t.id !== id + ), { component, id, config: props }, ], }; }); - setVisibleToasts((toasts: any) => { + setVisibleToasts((toasts: { [key: string]: boolean }) => { return { - ...toasts, + ...Object.fromEntries( + Object.entries(toasts).filter(([key]) => key !== id) + ), [id]: true, }; }); diff --git a/packages/unstyled/toast/src/ToastContext.ts b/packages/unstyled/toast/src/ToastContext.ts index 1914d25fb..90d49d042 100644 --- a/packages/unstyled/toast/src/ToastContext.ts +++ b/packages/unstyled/toast/src/ToastContext.ts @@ -1,16 +1,16 @@ import { createContext } from 'react'; -import type { IToastContext } from './types'; +import type { IToastContext, IToastInfo } from './types'; export const ToastContext = createContext({ - toastInfo: {}, + toastInfo: {} as IToastInfo, setToastInfo: () => {}, - setToast: () => {}, + setToast: () => '', removeToast: () => {}, hideAll: () => {}, isActive: () => false, visibleToasts: {}, setVisibleToasts: () => {}, hideToast: () => {}, - AnimationWrapper: null, - AnimatePresence: null, + AnimationWrapper: { current: null }, + AnimatePresence: { current: null }, }); diff --git a/packages/unstyled/toast/src/ToastList.tsx b/packages/unstyled/toast/src/ToastList.tsx index a827eaf13..5ef7567b8 100644 --- a/packages/unstyled/toast/src/ToastList.tsx +++ b/packages/unstyled/toast/src/ToastList.tsx @@ -1,12 +1,11 @@ /* eslint-disable react-native/no-inline-styles */ -import React from 'react'; -import { ToastContext } from './ToastContext'; -import { Overlay } from '@gluestack-ui/overlay'; -import { SafeAreaView } from 'react-native'; -import { View, Platform } from 'react-native'; import { useKeyboardBottomInset } from '@gluestack-ui/hooks'; -import type { IToast } from './types'; +import { Overlay } from '@gluestack-ui/overlay'; +import React from 'react'; +import { Platform, SafeAreaView, View } from 'react-native'; import { OverlayAnimatePresence } from './OverlayAnimatePresence'; +import { ToastContext } from './ToastContext'; +import type { IToast, ToastPlacement } from './types'; const initialAnimationOffset = 24; const transitionConfig: any = { @@ -61,12 +60,12 @@ export const ToastList = () => { AnimationWrapper, AnimatePresence: ContextAnimatePresence, } = React.useContext(ToastContext); - const AnimationView = AnimationWrapper.current; - const AnimatePresence = ContextAnimatePresence.current; + const AnimationView = AnimationWrapper?.current; + const AnimatePresence = ContextAnimatePresence?.current; const bottomInset = useKeyboardBottomInset() * 2; const getPositions = () => { - return Object.keys(toastInfo); + return Object.keys(toastInfo) as (keyof typeof toastInfo)[]; }; let hasToastOnOverlay = false; @@ -76,7 +75,7 @@ export const ToastList = () => { return getPositions().length > 0 ? ( - {getPositions().map((position: string) => { + {getPositions().map((position: ToastPlacement) => { if (Object.keys(POSITIONS).includes(position)) return ( { style={{ justifyContent: 'center', margin: 'auto', + //@ts-expect-error it is properly defined above per-platform position: toastPositionStyle, pointerEvents: 'box-none', - //@ts-ignore ...POSITIONS[position], }} > diff --git a/packages/unstyled/toast/src/types.ts b/packages/unstyled/toast/src/types.ts index e46187442..e6d6e034b 100644 --- a/packages/unstyled/toast/src/types.ts +++ b/packages/unstyled/toast/src/types.ts @@ -1,4 +1,17 @@ -import type { ReactNode } from 'react'; +import type { + Dispatch, + MutableRefObject, + ReactNode, + SetStateAction, +} from 'react'; + +export type ToastPlacement = + | 'top' + | 'top right' + | 'top left' + | 'bottom' + | 'bottom left' + | 'bottom right'; export interface InterfaceToastProps { /** @@ -9,7 +22,7 @@ export interface InterfaceToastProps { /** * The `id` of the toast. Mostly used when you need to prevent duplicate. By default, we generate a unique `id` for each toast */ - id?: any; + id?: string; /** * Callback function to run side effects after the toast has closed. */ @@ -18,17 +31,11 @@ export interface InterfaceToastProps { * The placement of the toast. Defaults to bottom * @default bottom */ - placement?: - | 'top' - | 'top right' - | 'top left' - | 'bottom' - | 'bottom left' - | 'bottom right'; + placement?: ToastPlacement; /** - * Render a component toast component. Any component passed will receive 2 props: `id` and `onClose`. + * Render a component toast component. Any component passed will receive 1 prop: `id` */ - render?: (props: any) => ReactNode; + render?: (props: ToastComponentProps) => ReactNode; /** * If true and the keyboard is opened, the Toast will move up equivalent to the keyboard height. * @default false @@ -39,33 +46,41 @@ export interface InterfaceToastProps { * container Style object for the toast * @default 0 */ - containerStyle?: any; + containerStyle?: React.CSSProperties; +} + +export interface ToastComponentProps { + id: string; } -export type IToast = { - id: number; - component: any; +export interface VisibleToasts { + [key: string]: boolean; +} + +export interface IToast { + id: string; + component: ReactNode; config?: IToastProps; -}; +} export type IToastInfo = { - [key in any]: Array; + [key in ToastPlacement]: Array; }; export type IToastContext = { toastInfo: IToastInfo; - setToastInfo: any; - setToast: (props: IToastProps) => any; - removeToast: (id: any) => void; + setToastInfo: Dispatch>; + setToast: (props: IToastProps) => string; + removeToast: (id: string) => void; hideAll: () => void; - isActive: (id: any) => boolean; - visibleToasts: any; - setVisibleToasts: any; - hideToast: (id: any) => void; + isActive: (id: string) => boolean; + visibleToasts: VisibleToasts; + setVisibleToasts: Dispatch>; + hideToast: (id: string) => void; avoidKeyboard?: boolean; bottomInset?: number; - AnimationWrapper?: any; - AnimatePresence?: any; + AnimationWrapper: MutableRefObject; + AnimatePresence: MutableRefObject; }; export type IToastComponentType< diff --git a/packages/unstyled/tooltip/src/Tooltip.tsx b/packages/unstyled/tooltip/src/Tooltip.tsx index 99436d551..cd9df0b5c 100644 --- a/packages/unstyled/tooltip/src/Tooltip.tsx +++ b/packages/unstyled/tooltip/src/Tooltip.tsx @@ -19,7 +19,7 @@ function Tooltip( defaultIsOpen = false, onClose, onOpen, - openDelay = 0, + openDelay = 350, closeDelay = 0, placement = 'bottom', children, diff --git a/packages/unstyled/tooltip/src/TooltipContent.tsx b/packages/unstyled/tooltip/src/TooltipContent.tsx index 0d74f4259..7b0841a63 100644 --- a/packages/unstyled/tooltip/src/TooltipContent.tsx +++ b/packages/unstyled/tooltip/src/TooltipContent.tsx @@ -21,15 +21,17 @@ export function TooltipContent( shouldOverlapWithTrigger, } = value; const overlayRef = React.useRef(null); - const { overlayProps } = useOverlayPosition({ - placement, - targetRef, - overlayRef, - crossOffset, - offset, - shouldOverlapWithTrigger, - shouldFlip, - }); + const { overlayProps, placement: calculatedPlacement } = useOverlayPosition( + { + placement, + targetRef, + overlayRef, + crossOffset, + offset, + shouldOverlapWithTrigger, + shouldFlip, + } + ); if (Object.keys(overlayProps.style).length === 0) { overlayProps.style = { @@ -39,14 +41,52 @@ export function TooltipContent( } const mergedRef = mergeRefs([ref, overlayRef]); + const initialAnimatedStyles = { + opacity: 0, + scale: 0.9, + y: + calculatedPlacement === 'top' + ? 6 + : calculatedPlacement === 'bottom' + ? -6 + : 0, + x: + calculatedPlacement === 'right' + ? -6 + : calculatedPlacement === 'left' + ? 6 + : 0, + }; + + const animatedStyles = { + opacity: 1, + y: 0, + scale: 1, + x: 0, + }; + + const exitAnimatedStyles = { + opacity: 0, + x: 0, + y: 0, + }; + return (