From 6671a97181f053606276b297deb868453f4bd190 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Tue, 12 Nov 2024 13:18:52 +0500 Subject: [PATCH] editor: get rid of `framer-motion` --- packages/editor/package.json | 2 - .../src/components/action-sheet/index.tsx | 206 ++++++++++++------ .../editor/src/extensions/image/component.tsx | 10 +- packages/editor/styles/styles.css | 9 + 4 files changed, 147 insertions(+), 80 deletions(-) diff --git a/packages/editor/package.json b/packages/editor/package.json index b118575ce..6ce8235e4 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -92,7 +92,6 @@ "@types/react-dom": "^18.3.0", "@types/react-modal": "^3.16.3 ", "@types/tinycolor2": "^1.4.6", - "framer-motion": "^11.5.4", "happy-dom": "^15.7.4", "isomorphic-fetch": "^3.0.0", "nodemon": "^3.1.7", @@ -110,7 +109,6 @@ "@mdi/js": ">=7.2.96", "@theme-ui/components": ">=0.16.0", "@theme-ui/core": ">=0.16.0", - "framer-motion": ">=11", "react": ">=18", "react-dom": ">=18", "react-modal": ">=3", diff --git a/packages/editor/src/components/action-sheet/index.tsx b/packages/editor/src/components/action-sheet/index.tsx index eb67dfa0d..41348b3c8 100644 --- a/packages/editor/src/components/action-sheet/index.tsx +++ b/packages/editor/src/components/action-sheet/index.tsx @@ -28,29 +28,13 @@ import { MenuItem, Icon, MenuButton, MenuSeparator } from "@notesnook/ui"; import { Box, Button, Flex, Text } from "@theme-ui/components"; import { Icons } from "../../toolbar/icons.js"; import Modal from "react-modal"; -import { - motion, - PanInfo, - useMotionValue, - useTransform, - useAnimation -} from "framer-motion"; import { useTheme } from "@emotion/react"; import { EmotionThemeProvider, Theme } from "@notesnook/theme"; -const AnimatedFlex = motion.create(Flex); - type ActionSheetHistoryItem = { title?: string; items?: MenuItem[]; }; -const TRANSITION = { - type: "spring", - stiffness: 300, - damping: 30, - mass: 0.2, - duration: 300 -}; function useHistory(initial: T) { const [current, setCurrent] = useState(initial); @@ -101,25 +85,127 @@ export function ActionSheetPresenter( } = props; const theme = useTheme() as Theme; const contentRef = useRef(); + const pressed = useRef(false); + const startY = useRef(0); + const reverse = false; + const threshold = 50; + const sheetTransition = "transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1)"; + const animationRef = useRef(0); + const masterOffset = useRef(0); + const sheetRef = useRef(null); + const overlayRef = useRef(null); - const y = useMotionValue(0); - const opacity = useTransform( - y, - [0, contentRef.current?.offsetHeight || window.innerHeight], - [1, 0] - ); - const animation = useAnimation(); + const onSwipeMove = (event: React.TouchEvent): void => { + if (pressed.current) { + const offset = event.touches[0].clientY - startY.current; + move(offset); + } + }; + + const onMouseMove = ( + event: React.MouseEvent + ): void => { + event.stopPropagation(); + if (pressed.current) { + if (reverse) { + const offset = event.clientY - startY.current; + move(offset); + } else { + const offset = event.clientY - startY.current; + move(offset); + } + } + }; + + const move = (offset: number): boolean => { + if (!reverse && offset > 0) { + masterOffset.current = offset; + animationRef.current = requestAnimationFrame(updatePosition); + return true; + } else if (reverse && offset < 0) { + masterOffset.current = offset; + animationRef.current = requestAnimationFrame(updatePosition); + return true; + } + return false; + }; + + const updatePosition = (): boolean => { + if (animationRef.current !== undefined) { + if (sheetRef.current) { + sheetRef.current.style.transform = `translate3d(0, ${masterOffset.current}px, 0)`; + if (overlayRef.current) + overlayRef.current.style.opacity = `${ + 1 - masterOffset.current / sheetRef.current.offsetHeight + }`; + return true; + } + return false; + } + return false; + }; + + const onSwipeStart = (event: React.TouchEvent): void => { + if (sheetRef?.current) sheetRef.current.style.transition = "none"; + startY.current = event.touches[0].clientY; + changePressed(true); + }; + + const onMouseStart = ( + event: React.MouseEvent + ): void => { + if (sheetRef?.current) sheetRef.current.style.transition = "none"; + startY.current = event.clientY; + changePressed(true); + }; + + const changePressed = (x: boolean): void => { + pressed.current = x; + }; + + const requestSheetDown = React.useCallback((): boolean => { + if (sheetRef.current) { + sheetRef.current.style.transition = sheetTransition; + sheetRef.current.style.transform = reverse + ? "translate3d(0, -101%, 0)" + : "translate3d(0, 101%, 0)"; + if (overlayRef.current) overlayRef.current.style.opacity = `0`; + return true; + } + return false; + }, [reverse, sheetTransition]); + + const requestSheetUp = React.useCallback((): boolean => { + if (sheetRef.current) { + sheetRef.current.style.transform = `translate3d(0, 0%, 0)`; + if (overlayRef.current) overlayRef.current.style.opacity = `1`; + return true; + } + return false; + }, []); + + const onSwipeEnd = (): void => { + cancelAnimationFrame(animationRef.current); + changePressed(false); + if (Math.abs(masterOffset.current) > threshold) { + // setShow(false); + requestSheetDown(); + console.log("CLSOING", masterOffset.current); + setTimeout(() => { + if (onClose) onClose(); + }, 300); + } else { + requestSheetUp(); + } + masterOffset.current = 0; + }; const onBeforeClose = useCallback(() => { - const height = contentRef.current?.offsetHeight || window.innerHeight; + requestSheetDown(); setTimeout(() => { - onClose?.(); - }, TRANSITION.duration - 50); - animation.start({ - transition: TRANSITION, - y: height + 100 - }); - }, [animation, onClose]); + if (onClose) onClose(); + }, 300); + }, [onClose, requestSheetDown]); const handleBackPress = useCallback( (event: Event) => { @@ -156,7 +242,7 @@ export function ActionSheetPresenter( onRequestClose={() => onBeforeClose()} portalClassName={"bottom-sheet-presenter-portal"} onAfterOpen={() => { - setTimeout(() => animation.start({ transition: TRANSITION, y: 0 })); + setTimeout(() => requestSheetUp()); }} overlayElement={(overlayElementProps, contentEl) => { return ( @@ -172,12 +258,13 @@ export function ActionSheetPresenter( tabIndex={-1} > {blocking && ( - )} > - {draggable && ( - { - y.set(Math.max(y.get() + delta.y, 0)); - }} - onDragEnd={(_, { velocity }: PanInfo) => { - if (velocity.y >= 500) { - onClose?.(); - return; - } - const sheetEl = contentRef.current; - if (!sheetEl) return; - - const contentHeight = sheetEl.offsetHeight; - const threshold = 30; - const closingHeight = (contentHeight * threshold) / 100; - - if (y.get() >= closingHeight) { - onBeforeClose(); - } else { - setTimeout(() => - animation.start({ transition: TRANSITION, y: 0 }) - ); - } - }} - dragConstraints={{ top: 0, bottom: 0 }} - dragMomentum={false} - dragElastic={false} + - + )} {children} - + ); } diff --git a/packages/editor/src/extensions/image/component.tsx b/packages/editor/src/extensions/image/component.tsx index 9841c8299..8c6b44fbf 100644 --- a/packages/editor/src/extensions/image/component.tsx +++ b/packages/editor/src/extensions/image/component.tsx @@ -38,13 +38,10 @@ import { toBlobURL, toDataURL } from "../../utils/downloader.js"; -import { motion } from "framer-motion"; import { useObserver } from "../../hooks/use-observer.js"; import { Attachment, ImageAlignmentOptions } from "../attachment/index.js"; import { DataURL } from "@notesnook/common"; -export const AnimatedImage = motion.create(Image); - export function ImageComponent( props: ReactNodeViewProps> ) { @@ -252,11 +249,8 @@ export function ImageComponent( /> )} -