From ea20ea6784c0409247ff541e25dea22e3872f215 Mon Sep 17 00:00:00 2001 From: MTG2000 Date: Sun, 16 Jan 2022 20:51:24 +0200 Subject: [PATCH] feat: Created an animated Tip button --- src/App.tsx | 6 +- .../Login_ExternalWalletCard.stories.tsx | 2 +- .../Login/Login_NativeWalletCard.stories.tsx | 2 +- .../Login_ScanningWalletCard.stories.tsx | 2 +- .../Login/Login_SuccessCard.stories.tsx | 2 +- .../ModalsContainer/ModalsContainer.tsx | 1 + .../TipButton/TipButton.stories.tsx | 17 ++ src/Components/TipButton/TipButton.tsx | 176 ++++++++++++++++++ src/Components/TipButton/tipbutton.style.css | 79 ++++++++ src/index.css | 10 + .../Claim_CopySignatureCard.stories.tsx | 2 +- .../Claim_FundWithdrawCard.stories.tsx | 2 +- .../Claim_GenerateSignatureCard.stories.tsx | 2 +- .../Claim_SubmittedCard.stories.tsx | 2 +- .../ProjectCard/ProjectCard.stories.tsx | 9 +- .../ProjectPage/ProjectCard/ProjectCard.tsx | 13 +- src/pages/ProjectPage/Tip/TipCard.stories.tsx | 2 +- src/pages/ProjectPage/Tip/TipCard.tsx | 6 +- src/redux/features/modals.slice.ts | 9 +- src/redux/features/theme.slice.ts | 4 +- src/utils/helperFunctions.ts | 4 + src/utils/hooks/index.ts | 1 + src/utils/hooks/usePressHolder.ts | 40 ++++ .../utils/storybookDecorators.tsx | 9 +- 24 files changed, 375 insertions(+), 27 deletions(-) create mode 100644 src/Components/TipButton/TipButton.stories.tsx create mode 100644 src/Components/TipButton/TipButton.tsx create mode 100644 src/Components/TipButton/tipbutton.style.css create mode 100644 src/utils/hooks/usePressHolder.ts rename .storybook/helpers.tsx => src/utils/storybookDecorators.tsx (68%) diff --git a/src/App.tsx b/src/App.tsx index aa2b627..1023ffc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect } from "react"; import Navbar from "src/Components/Navbar/Navbar"; import ExplorePage from "src/pages/ExplorePage"; import ModalsContainer from "src/Components/Modals/ModalsContainer/ModalsContainer"; @@ -23,10 +23,10 @@ function App() { console.log("error:webln.enable()", err); }); } - }, []); + }, [dispatch]); useResizeListener(() => { - dispatch(setIsMobileScreen(document.body.clientWidth < 768)); + // dispatch(setIsMobileScreen(/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent))); }, [dispatch]) diff --git a/src/Components/Modals/Login/Login_ExternalWalletCard.stories.tsx b/src/Components/Modals/Login/Login_ExternalWalletCard.stories.tsx index 4951f66..199c44a 100644 --- a/src/Components/Modals/Login/Login_ExternalWalletCard.stories.tsx +++ b/src/Components/Modals/Login/Login_ExternalWalletCard.stories.tsx @@ -2,7 +2,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import Login_ExternalWalletCard from './Login_ExternalWalletCard'; -import { ModalsDecorator } from '.storybook/helpers' +import { ModalsDecorator } from 'src/utils/storybookDecorators' export default { title: 'Login/External Wallet Card', diff --git a/src/Components/Modals/Login/Login_NativeWalletCard.stories.tsx b/src/Components/Modals/Login/Login_NativeWalletCard.stories.tsx index 99e4558..b906943 100644 --- a/src/Components/Modals/Login/Login_NativeWalletCard.stories.tsx +++ b/src/Components/Modals/Login/Login_NativeWalletCard.stories.tsx @@ -2,7 +2,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import Login_NativeWalletCard from './Login_NativeWalletCard'; -import { ModalsDecorator } from '.storybook/helpers' +import { ModalsDecorator } from 'src/utils/storybookDecorators' export default { title: 'Login/Native Wallet Card', diff --git a/src/Components/Modals/Login/Login_ScanningWalletCard.stories.tsx b/src/Components/Modals/Login/Login_ScanningWalletCard.stories.tsx index ac083f8..e69260d 100644 --- a/src/Components/Modals/Login/Login_ScanningWalletCard.stories.tsx +++ b/src/Components/Modals/Login/Login_ScanningWalletCard.stories.tsx @@ -2,7 +2,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import Login_ScanningWalletCard from './Login_ScanningWalletCard'; -import { ModalsDecorator } from '.storybook/helpers' +import { ModalsDecorator } from 'src/utils/storybookDecorators' export default { title: 'Login/Scanning Wallet Card', diff --git a/src/Components/Modals/Login/Login_SuccessCard.stories.tsx b/src/Components/Modals/Login/Login_SuccessCard.stories.tsx index b333777..bdb286f 100644 --- a/src/Components/Modals/Login/Login_SuccessCard.stories.tsx +++ b/src/Components/Modals/Login/Login_SuccessCard.stories.tsx @@ -2,7 +2,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import Login_SuccessCard from './Login_SuccessCard'; -import { ModalsDecorator } from '.storybook/helpers' +import { ModalsDecorator } from 'src/utils/storybookDecorators' export default { title: 'Login/Success Card', diff --git a/src/Components/Modals/ModalsContainer/ModalsContainer.tsx b/src/Components/Modals/ModalsContainer/ModalsContainer.tsx index 113fa7b..535ba7d 100644 --- a/src/Components/Modals/ModalsContainer/ModalsContainer.tsx +++ b/src/Components/Modals/ModalsContainer/ModalsContainer.tsx @@ -71,6 +71,7 @@ export default function ModalsContainer() { {openModals.map((modal, idx) => { const Child = ALL_MODALS[modal.Modal]; + return ( diff --git a/src/Components/TipButton/TipButton.stories.tsx b/src/Components/TipButton/TipButton.stories.tsx new file mode 100644 index 0000000..cccca96 --- /dev/null +++ b/src/Components/TipButton/TipButton.stories.tsx @@ -0,0 +1,17 @@ +import { ComponentStory, ComponentMeta } from '@storybook/react'; + +import TipButton from './TipButton'; +import { centerDecorator } from 'src/utils/storybookDecorators' + +export default { + title: 'Shared/Tip Button', + component: TipButton, + decorators: [ + centerDecorator + ] +} as ComponentMeta; + +const Template: ComponentStory = (args) => { }} />; + +export const Default = Template.bind({}); + diff --git a/src/Components/TipButton/TipButton.tsx b/src/Components/TipButton/TipButton.tsx new file mode 100644 index 0000000..6cdbab0 --- /dev/null +++ b/src/Components/TipButton/TipButton.tsx @@ -0,0 +1,176 @@ +import { MdLocalFireDepartment } from 'react-icons/md' +import Button from 'src/Components/Button/Button' +import { useAppSelector, usePressHolder } from 'src/utils/hooks' +import _throttle from 'lodash.throttle' +import { ComponentProps, useEffect, useState } from 'react' +import './tipbutton.style.css' +import { random, randomItem } from 'src/utils/helperFunctions' + + +interface Particle { + id: string, + offsetX: number, + color: '#ff6a00' | '#ff7717' | '#ff6217' | '#ff8217' | '#ff5717' + animation: 'fly-spark-1' | 'fly-spark-2', + animationSpeed: 1 | 2 | 3, + scale: number +} + +type Props = { + onTip: (tip: number) => void +} & Omit, 'children'> + +export default function TipButton({ onTip = () => { }, ...props }: Props) { + const [tipCnt, setTipCnt] = useState(0) + const [sparks, setSparks] = useState([]); + const [wasActive, setWasActive] = useState(false); + + const isMobileScreen = useAppSelector(s => s.theme.isMobileScreen) + + // useEffect(() => { + // setInterval(() => { + // setTipCnt(s => s + 1) + + // const newSpark = { + // id: Math.random().toString(), + // offsetX: random(1, 99), + // animation: randomItem('fly-spark-1', 'fly-spark-2'), + // animationSpeed: randomItem(1, 1.5, 2), + // color: randomItem('#ff6a00', '#ff7717', '#ff6217', '#ff8217', '#ff5717'), + // scale: random(1, 2) + // }; + // setTimeout(() => { + // setSparks(s => { + // return s.filter(spark => spark.id !== newSpark.id) + // }) + + // }, newSpark.animationSpeed * 1000) + + // setSparks(oldSparks => [...oldSparks, newSpark]) + + // }, 300); + // }, []) + + + const { onPressDown, onPressUp } = usePressHolder(_throttle(() => { + const incStep = (Math.ceil((tipCnt + 1) / 100) + 1) ** 2; + setTipCnt(s => s + incStep) + + const newSpark = { + id: Math.random().toString(), + offsetX: random(1, 99), + animation: randomItem('fly-spark-1', 'fly-spark-2'), + animationSpeed: randomItem(1, 1.5, 2), + color: randomItem('#ff6a00', '#ff7717', '#ff6217', '#ff8217', '#ff5717'), + scale: random(1.2, 2.2) + }; + + // if on mobile screen, reduce number of sparks particles to 60% + if (!isMobileScreen || Math.random() > .4) { + setSparks(oldSparks => [...oldSparks, newSpark]) + setTimeout(() => { + setSparks(s => { + return s.filter(spark => spark.id !== newSpark.id) + }) + + }, newSpark.animationSpeed * 1000) + } + + }, 100), 100); + + const handlePressDown = () => { + setWasActive(true); + onPressDown(); + } + + const handlePressUp = (event?: any) => { + + if (!wasActive) return; + + setWasActive(false); + if (event?.preventDefault) event.preventDefault(); + onPressUp(); + if (tipCnt === 0) + onTip(10); + else + setTimeout(() => { + setSparks([]); + onTip(tipCnt); + setTipCnt(0); + }, 1000) + } + + return ( + + + ) +} diff --git a/src/Components/TipButton/tipbutton.style.css b/src/Components/TipButton/tipbutton.style.css new file mode 100644 index 0000000..06c1d8a --- /dev/null +++ b/src/Components/TipButton/tipbutton.style.css @@ -0,0 +1,79 @@ +.tip-button { + --scale: 0; + transition: background-color 1s; + background-color: hsl(25, 100%, max(calc((95 - var(--scale) / 4) * 1%), 63%)); +} + +.tip-counter { + --pos-y: 0; + + position: absolute; + left: 50%; + bottom: 110%; + color: hsl(25, 100%, 50%); + font-weight: bold; + font-size: 21px; + will-change: transform; + opacity: min(calc(var(--scale) * 1), 1); + transform: translate(-50%, max(calc(-1px * var(--scale) / 10), -30px)) + scale(calc(1 + min(calc(var(--scale) / 150), 2))); + text-shadow: 0 0 4px hsl(25, 100%, 50%); +} + +.spark { + position: absolute; + bottom: 46%; + left: calc(var(--offsetX) * 1%); + transform: scale(var(--scale)); + opacity: 0; + will-change: transform; + + animation-name: fly-spark-1; + animation-duration: calc(var(--animationSpeed) * 1s); + animation-timing-function: linear; + animation-iteration-count: 1; + animation-fill-mode: forwards; + filter: drop-shadow(0 0 4px); +} + +@keyframes fly-spark-1 { + 0% { + transform: translate(0, 0) scale(var(--scale)); + opacity: 1; + } + + 33% { + transform: translate(12px, -70px) scale(var(--scale)); + } + + 66% { + transform: translate(0, -140px) scale(var(--scale)); + opacity: 0.6; + } + + 100% { + transform: translate(6px, -200px) scale(var(--scale)); + opacity: 0; + } +} + +@keyframes fly-spark-2 { + 0% { + transform: translate(0, 0) scale(var(--scale)); + opacity: 1; + } + + 50% { + transform: translate(-10px, -80px) scale(var(--scale)); + } + + 80% { + transform: translate(-4px, -140px) scale(var(--scale)); + opacity: 0.6; + } + + 100% { + transform: translate(-6px, -160px) scale(var(--scale)); + opacity: 0; + } +} diff --git a/src/index.css b/src/index.css index 6f52d9e..a91f6ff 100644 --- a/src/index.css +++ b/src/index.css @@ -76,6 +76,16 @@ svg { background-color: #999; } +.noselect { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + .no-scrollbar { -ms-overflow-style: none; /* Internet Explorer 10+ */ scrollbar-width: none; /* Firefox */ diff --git a/src/pages/ProjectPage/ClaimProject/Claim_CopySignatureCard.stories.tsx b/src/pages/ProjectPage/ClaimProject/Claim_CopySignatureCard.stories.tsx index b94b7ec..a7c4042 100644 --- a/src/pages/ProjectPage/ClaimProject/Claim_CopySignatureCard.stories.tsx +++ b/src/pages/ProjectPage/ClaimProject/Claim_CopySignatureCard.stories.tsx @@ -2,7 +2,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import Claim_CopySignatureCard from './Claim_CopySignatureCard'; -import { ModalsDecorator } from '.storybook/helpers' +import { ModalsDecorator } from 'src/utils/storybookDecorators' export default { title: 'Claim/Copy Signature Card', diff --git a/src/pages/ProjectPage/ClaimProject/Claim_FundWithdrawCard.stories.tsx b/src/pages/ProjectPage/ClaimProject/Claim_FundWithdrawCard.stories.tsx index 64cd298..21b4b09 100644 --- a/src/pages/ProjectPage/ClaimProject/Claim_FundWithdrawCard.stories.tsx +++ b/src/pages/ProjectPage/ClaimProject/Claim_FundWithdrawCard.stories.tsx @@ -2,7 +2,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import Claim_FundWithdrawCard from './Claim_FundWithdrawCard'; -import { ModalsDecorator } from '.storybook/helpers' +import { ModalsDecorator } from 'src/utils/storybookDecorators' export default { title: 'Claim/Fund Withdraw Card', diff --git a/src/pages/ProjectPage/ClaimProject/Claim_GenerateSignatureCard.stories.tsx b/src/pages/ProjectPage/ClaimProject/Claim_GenerateSignatureCard.stories.tsx index 249279b..6e5016e 100644 --- a/src/pages/ProjectPage/ClaimProject/Claim_GenerateSignatureCard.stories.tsx +++ b/src/pages/ProjectPage/ClaimProject/Claim_GenerateSignatureCard.stories.tsx @@ -2,7 +2,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import Claim_GenerateSignatureCard from './Claim_GenerateSignatureCard'; -import { ModalsDecorator } from '.storybook/helpers' +import { ModalsDecorator } from 'src/utils/storybookDecorators' export default { title: 'Claim/Generate Signature Card', diff --git a/src/pages/ProjectPage/ClaimProject/Claim_SubmittedCard.stories.tsx b/src/pages/ProjectPage/ClaimProject/Claim_SubmittedCard.stories.tsx index b2f9810..3c22cae 100644 --- a/src/pages/ProjectPage/ClaimProject/Claim_SubmittedCard.stories.tsx +++ b/src/pages/ProjectPage/ClaimProject/Claim_SubmittedCard.stories.tsx @@ -2,7 +2,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import Claim_SubmittedCard from './Claim_SubmittedCard'; -import { ModalsDecorator } from '.storybook/helpers' +import { ModalsDecorator } from 'src/utils/storybookDecorators' export default { title: 'Claim/Submitted Card', diff --git a/src/pages/ProjectPage/ProjectCard/ProjectCard.stories.tsx b/src/pages/ProjectPage/ProjectCard/ProjectCard.stories.tsx index bf012c5..13f2dda 100644 --- a/src/pages/ProjectPage/ProjectCard/ProjectCard.stories.tsx +++ b/src/pages/ProjectPage/ProjectCard/ProjectCard.stories.tsx @@ -3,7 +3,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import ProjectCard from './ProjectCard'; import ProjectCardSkeleton from './ProjectCard.Skeleton'; -import { ModalsDecorator } from '.storybook/helpers' +import { ModalsDecorator } from 'src/utils/storybookDecorators' export default { title: 'Project/Project Card', @@ -14,7 +14,12 @@ export default { const Template: ComponentStory = (args) => ; -export const Default = Template.bind({}); +export const Default = Template.bind({ +}); + +Default.args = { + projectId: '3' +} diff --git a/src/pages/ProjectPage/ProjectCard/ProjectCard.tsx b/src/pages/ProjectPage/ProjectCard/ProjectCard.tsx index 7f7525c..bd5b5de 100644 --- a/src/pages/ProjectPage/ProjectCard/ProjectCard.tsx +++ b/src/pages/ProjectPage/ProjectCard/ProjectCard.tsx @@ -12,6 +12,7 @@ import { requestProvider } from 'webln'; import { PROJECT_BY_ID_QUERY, PROJECT_BY_ID_RES, PROJECT_BY_ID_VARS } from './query' import { AiFillThunderbolt } from 'react-icons/ai'; import ProjectCardSkeleton from './ProjectCard.Skeleton' +import TipButton from 'src/Components/TipButton/TipButton'; interface Props extends ModalCard { @@ -46,6 +47,7 @@ export default function ProjectCard({ onClose, direction, projectId, ...props }: const onConnectWallet = async () => { try { + const webln = await requestProvider(); if (webln) { dispatch(connectWallet(webln)); @@ -59,15 +61,16 @@ export default function ProjectCard({ onClose, direction, projectId, ...props }: } } - const onTip = () => { + const onTip = (tip?: number) => { + if (!isWalletConnected) { - dispatch(scheduleModal({ Modal: 'TipCard' })) + dispatch(scheduleModal({ Modal: 'TipCard', props: { tipValue: tip } })) dispatch(openModal({ Modal: 'Login_ScanningWalletCard' })) } else - dispatch(openModal({ Modal: 'TipCard' })) + dispatch(openModal({ Modal: 'TipCard', props: { tipValue: tip } })) } @@ -111,7 +114,7 @@ export default function ProjectCard({ onClose, direction, projectId, ...props }:
{isWalletConnected ? - + : } @@ -121,7 +124,7 @@ export default function ProjectCard({ onClose, direction, projectId, ...props }:
{isWalletConnected ? - + : } diff --git a/src/pages/ProjectPage/Tip/TipCard.stories.tsx b/src/pages/ProjectPage/Tip/TipCard.stories.tsx index e50823c..0d6ec93 100644 --- a/src/pages/ProjectPage/Tip/TipCard.stories.tsx +++ b/src/pages/ProjectPage/Tip/TipCard.stories.tsx @@ -2,7 +2,7 @@ import { ComponentStory, ComponentMeta } from '@storybook/react'; import TipCard from './TipCard'; -import { ModalsDecorator } from '.storybook/helpers' +import { ModalsDecorator } from 'src/utils/storybookDecorators' export default { title: 'Tip/Tip Card', diff --git a/src/pages/ProjectPage/Tip/TipCard.tsx b/src/pages/ProjectPage/Tip/TipCard.tsx index b59d2dc..7656c5b 100644 --- a/src/pages/ProjectPage/Tip/TipCard.tsx +++ b/src/pages/ProjectPage/Tip/TipCard.tsx @@ -49,10 +49,10 @@ mutation Mutation($paymentRequest: String!, $preimage: String!) { `; interface Props extends ModalCard { - + tipValue?: number; } -export default function TipCard({ onClose, direction, ...props }: Props) { +export default function TipCard({ onClose, direction, tipValue, ...props }: Props) { const { width, height } = useWindowSize() const { isWalletConnected, webln } = useAppSelector(state => ({ @@ -63,7 +63,7 @@ export default function TipCard({ onClose, direction, ...props }: Props) { const dispatch = useAppDispatch(); const [selectedOption, setSelectedOption] = useState(10); - const [voteAmount, setVoteAmount] = useState(10); + const [voteAmount, setVoteAmount] = useState(tipValue ?? 10); const [paymentStatus, setPaymentStatus] = useState(PaymentStatus.DEFAULT); const [vote, { data }] = useMutation(VOTE, { diff --git a/src/redux/features/modals.slice.ts b/src/redux/features/modals.slice.ts index 0782156..d9d2710 100644 --- a/src/redux/features/modals.slice.ts +++ b/src/redux/features/modals.slice.ts @@ -34,13 +34,16 @@ export const ALL_MODALS = { Claim_FundWithdrawCard, } - type ExcludeBaseModalProps = Omit type ModalProps = ExcludeBaseModalProps> +type NonNullableObject = { + [K in keyof T]-?: NonNullable +} + type ModalAction = U extends any ? - {} extends ModalProps ? + {} extends NonNullableObject> ? { Modal: U } : { Modal: U, props: ModalProps } @@ -48,6 +51,7 @@ type ModalAction = never; + interface OpenModal { Modal: ModalAction['Modal'], props?: any; @@ -110,7 +114,6 @@ export const modalSlice = createSlice({ ) { state.direction = Direction.START; state.isOpen = true; - let props: any = {}; if ('props' in action.payload) props = { ...action.payload.props } diff --git a/src/redux/features/theme.slice.ts b/src/redux/features/theme.slice.ts index 8aed41d..30ba139 100644 --- a/src/redux/features/theme.slice.ts +++ b/src/redux/features/theme.slice.ts @@ -7,9 +7,11 @@ interface StoreState { const initialState = { navHeight: 88, - isMobileScreen: false, + isMobileScreen: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) || window.innerWidth < 480, } as StoreState; + + export const themeSlice = createSlice({ name: "theme", initialState, diff --git a/src/utils/helperFunctions.ts b/src/utils/helperFunctions.ts index cf52fec..3fc658d 100644 --- a/src/utils/helperFunctions.ts +++ b/src/utils/helperFunctions.ts @@ -1,3 +1,7 @@ export function random(min: number, max: number) { return Math.random() * (max - min) + min; } + +export function randomItem(...args: any[]) { + return args[Math.floor(Math.random() * args.length)]; +} diff --git a/src/utils/hooks/index.ts b/src/utils/hooks/index.ts index 6c01a5f..14a37bb 100644 --- a/src/utils/hooks/index.ts +++ b/src/utils/hooks/index.ts @@ -1,2 +1,3 @@ export * from "./storeHooks"; export * from "./useResizeListener"; +export * from "./usePressHolder"; diff --git a/src/utils/hooks/usePressHolder.ts b/src/utils/hooks/usePressHolder.ts new file mode 100644 index 0000000..cd4da2e --- /dev/null +++ b/src/utils/hooks/usePressHolder.ts @@ -0,0 +1,40 @@ +import { useRef } from "react"; + + +export const usePressHolder = (onHold: () => any, holdThreshold: number = 400) => { + const ref = useRef({ + cntr: 0, + timerID: 0, + previousTimestamp: -1 + }); + + const onPressDown = () => { + requestAnimationFrame(timer) + } + + + const onPressUp = () => { + + cancelAnimationFrame(ref.current.timerID); + ref.current.cntr = 0; + ref.current.previousTimestamp = -1; + } + + function timer(timestamp: number) { + if (ref.current.previousTimestamp === -1) ref.current.previousTimestamp = timestamp; + + const dt = timestamp - ref.current.previousTimestamp; + ref.current.previousTimestamp = timestamp; + + if (ref.current.cntr < holdThreshold) { + ref.current.cntr += dt; + } else { + onHold(); + } + + ref.current.timerID = requestAnimationFrame(timer); + } + + return { onPressUp, onPressDown } + +} \ No newline at end of file diff --git a/.storybook/helpers.tsx b/src/utils/storybookDecorators.tsx similarity index 68% rename from .storybook/helpers.tsx rename to src/utils/storybookDecorators.tsx index df8b9c6..b2ef481 100644 --- a/.storybook/helpers.tsx +++ b/src/utils/storybookDecorators.tsx @@ -1,7 +1,8 @@ +import { DecoratorFn } from '@storybook/react'; import { AnimatePresence, motion } from 'framer-motion'; import Modal from 'src/Components/Modals/Modal/Modal'; -export const ModalsDecorator = (Story: any) => { +export const ModalsDecorator: DecoratorFn = (Story) => { const onClose = () => { }; return ( { ); +} + +export const centerDecorator: DecoratorFn = (Story) => { + return
+ +
} \ No newline at end of file