Skip to content

Commit

Permalink
Merge branch 'main' into fix/skip-mount-animations
Browse files Browse the repository at this point in the history
  • Loading branch information
mattgperry authored Nov 30, 2023
2 parents 2cf82a0 + 8cc5349 commit 9e289b1
Show file tree
Hide file tree
Showing 22 changed files with 203 additions and 72 deletions.
1 change: 0 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"eslint.alwaysShowStatus": true,
"eslint.validate": [
"javascript",
"javascriptreact",
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@ Framer Motion adheres to [Semantic Versioning](http://semver.org/).

Undocumented APIs should be considered internal and may change without warning.

## [10.16.8] 2023-11-29

### Fixed

- Added `disableInstantAnimation` private API.
- Ensure optimised appear animations don't run post-hydration.

## [10.16.7] 2023-11-29

### Fixed

- Fixing pan events within React portal.

## [10.16.6] 2023-11-29

### Fixed

- Fixing `Reorder` component types.
- Fixing `Reorder.Item` order calculation.
- Fixing broken `dragConstraints` if component re-renders during resize.

## [10.16.5] 2023-11-14

### Fixed
Expand Down
28 changes: 28 additions & 0 deletions dev/examples/Drag-constraints-resize.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from "react"
import { motion } from "framer-motion"

const styleA = {
width: 200,
height: 200,
borderRadius: 20,
}

export function App() {
const [backgroundColor, setBackgroundColor] = React.useState("darkgray")
React.useEffect(() => {
const listener = () => {
// The re-render will have updateBlockedByResize as true and cause clearMeasurements() to be called.
setBackgroundColor("pink")
}
window.addEventListener("resize", listener)
return () => window.removeEventListener("resize", listener)
}, [backgroundColor])

return (
<motion.div
drag="x"
dragConstraints={{ right: 0, left: 0 }}
style={{ ...styleA, backgroundColor }}
/>
)
}
6 changes: 3 additions & 3 deletions dev/package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"name": "framer-motion--dev",
"version": "10.16.5",
"version": "10.16.8",
"private": true,
"scripts": {
"dev": "webpack serve --config ./webpack/config.js --hot"
},
"dependencies": {
"@react-three/drei": "^7.27.3",
"@react-three/fiber": "^8.2.2",
"framer-motion": "^10.16.5",
"framer-motion-3d": "^10.16.5",
"framer-motion": "^10.16.8",
"framer-motion-3d": "^10.16.8",
"path-browserify": "^1.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "10.16.5",
"version": "10.16.8",
"packages": [
"packages/*"
],
Expand Down
6 changes: 3 additions & 3 deletions packages/framer-motion-3d/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "framer-motion-3d",
"version": "10.16.5",
"version": "10.16.8",
"description": "A simple and powerful React animation library for @react-three/fiber",
"main": "dist/cjs/index.js",
"module": "dist/es/index.mjs",
Expand Down Expand Up @@ -46,7 +46,7 @@
"postpublish": "git push --tags"
},
"dependencies": {
"framer-motion": "^10.16.5",
"framer-motion": "^10.16.8",
"react-merge-refs": "^2.0.1"
},
"peerDependencies": {
Expand All @@ -60,5 +60,5 @@
"@react-three/test-renderer": "^9.0.0",
"@rollup/plugin-commonjs": "^22.0.1"
},
"gitHead": "bc1be3b9d0f097871f98113ec31181c955c7d4ff"
"gitHead": "d0e7fe0bb1f2b59aec9c12623d48148631f28bb8"
}
2 changes: 1 addition & 1 deletion packages/framer-motion/cypress/fixtures/appear-tests.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
["interrupt-delay-after.html","interrupt-delay-before-accelerated.html","interrupt-delay-before.html","interrupt-spring.html","interrupt-tween-opacity-waapi.html","interrupt-tween-opacity.html","interrupt-tween-transforms.html","interrupt-tween-x.html","persist.html","resync-delay.html","resync.html"]
["interrupt-delay-after.html","interrupt-delay-before-accelerated.html","interrupt-delay-before.html","interrupt-spring.html","interrupt-tween-opacity-waapi.html","interrupt-tween-opacity.html","interrupt-tween-transforms.html","interrupt-tween-x.html","persist.html","resync-delay.html","resync.html","start-after-hydration.html"]
40 changes: 40 additions & 0 deletions packages/framer-motion/cypress/integration/drag-to-reorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,44 @@ describe("Drag to reorder", () => {
})
})
})

it("Move around", () => {
const checkBox = (chain: Cypress.Chainable) => {
return chain.should(([$item]: any) => {
expectBbox($item, {
height: 68,
left: 350,
top: 174,
width: 340,
})
})
}

const moveAround = (chain: Cypress.Chainable, steps: number[]) => {
const baseY = 175
const delta = 20
chain = chain
.trigger("pointerdown", 360, baseY, { force: true })
.wait(50)
steps.forEach(step => {
Array.from({length: Math.abs(step)}).forEach(() => {
const y = step > 0 ? delta : -delta
chain = chain
.trigger("pointermove", 360, baseY + y, { force: true })
.wait(50)
})
})
return chain
.trigger("pointerup", 360, baseY, { force: true })
.wait(100)
}

const chain = checkBox(
cy.visit("?test=drag-to-reorder")
.wait(50)
.get("#Tomato")
)

checkBox(moveAround(chain, [-4, 14, -8, 4, -5, 2, -6]))
})
})
12 changes: 6 additions & 6 deletions packages/framer-motion/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "framer-motion",
"version": "10.16.5",
"version": "10.16.8",
"description": "A simple and powerful JavaScript animation library",
"main": "dist/cjs/index.js",
"module": "dist/es/index.mjs",
Expand Down Expand Up @@ -85,19 +85,19 @@
"bundlesize": [
{
"path": "./dist/size-rollup-motion.js",
"maxSize": "30.8 kB"
"maxSize": "30.91 kB"
},
{
"path": "./dist/size-rollup-m.js",
"maxSize": "5.28 kB"
"maxSize": "5.29 kB"
},
{
"path": "./dist/size-rollup-dom-animation.js",
"maxSize": "15 kB"
},
{
"path": "./dist/size-rollup-dom-max.js",
"maxSize": "26.22 kB"
"maxSize": "26.35 kB"
},
{
"path": "./dist/size-rollup-animate.js",
Expand All @@ -113,8 +113,8 @@
},
{
"path": "./dist/size-webpack-dom-max.js",
"maxSize": "31.5 kB"
"maxSize": "31.62 kB"
}
],
"gitHead": "bc1be3b9d0f097871f98113ec31181c955c7d4ff"
"gitHead": "d0e7fe0bb1f2b59aec9c12623d48148631f28bb8"
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export function startOptimizedAppearAnimation(
options: NativeAnimationOptions,
onReady?: (animation: Animation) => void
): void {
// Prevent optimised appear animations if Motion has already started animating.
if (window.HandoffAppearAnimations === false) return

const id = element.dataset[optimizedAppearDataId]

if (!id) return
Expand Down
14 changes: 8 additions & 6 deletions packages/framer-motion/src/animation/optimized-appear/types.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { Batcher } from "../../frameloop/types"
import { MotionValue } from "../../value"

type HandoffFunction = (
storeId: string,
valueName: string,
value: MotionValue,
sync: Batcher
) => number

/**
* The window global object acts as a bridge between our inline script
* triggering the optimized appear animations, and Framer Motion.
*/
declare global {
interface Window {
HandoffAppearAnimations?: (
storeId: string,
valueName: string,
value: MotionValue,
sync: Batcher
) => number
HandoffAppearAnimations?: false | HandoffFunction
}
}
40 changes: 20 additions & 20 deletions packages/framer-motion/src/components/Reorder/Group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,13 @@ export interface Props<V> {
*/
axis?: "x" | "y"

// TODO: This would be better typed as V, but that doesn't seem
// to correctly infer type from values
/**
* A callback to fire with the new value order. For instance, if the values
* are provided as a state from `useState`, this could be the set state function.
*
* @public
*/
onReorder: (newOrder: any[]) => void
onReorder: (newOrder: V[]) => void

/**
* The latest values state.
Expand All @@ -60,6 +58,10 @@ export interface Props<V> {
values: V[]
}

type ReorderGroupProps<V> = Props<V> &
Omit<HTMLMotionProps<any>, "values"> &
React.PropsWithChildren<{}>

export function ReorderGroup<V>(
{
children,
Expand All @@ -68,10 +70,8 @@ export function ReorderGroup<V>(
onReorder,
values,
...props
}: Props<V> &
Omit<HTMLMotionProps<any>, "values"> &
React.PropsWithChildren<{}>,
externalRef?: React.Ref<any>
}: ReorderGroupProps<V>,
externalRef?: React.ForwardedRef<any>
) {
const Component = useConstant(() => motion(as)) as FunctionComponent<
React.PropsWithChildren<HTMLMotionProps<any> & { ref?: React.Ref<any> }>
Expand All @@ -82,24 +82,22 @@ export function ReorderGroup<V>(

invariant(Boolean(values), "Reorder.Group must be provided a values prop")

const context: ReorderContextProps<any> = {
const context: ReorderContextProps<V> = {
axis,
registerItem: (value, layout) => {
/**
* Ensure entries can't add themselves more than once
*/
if (
layout &&
order.findIndex((entry) => value === entry.value) === -1
) {
order.push({ value, layout: layout[axis] })
order.sort(compareMin)
// If the entry was already added, update it rather than adding it again
const idx = order.findIndex((entry) => value === entry.value)
if (idx !== -1) {
order[idx].layout = layout[axis]
} else {
order.push({ value: value, layout: layout[axis] })
}
order.sort(compareMin)
},
updateOrder: (id, offset, velocity) => {
updateOrder: (item, offset, velocity) => {
if (isReordering.current) return

const newOrder = checkReorder(order, id, offset, velocity)
const newOrder = checkReorder(order, item, offset, velocity)

if (order !== newOrder) {
isReordering.current = true
Expand All @@ -125,7 +123,9 @@ export function ReorderGroup<V>(
)
}

export const Group = forwardRef(ReorderGroup)
export const Group = forwardRef(ReorderGroup) as <V>(
props: ReorderGroupProps<V> & { ref?: React.ForwardedRef<any> }
) => ReturnType<typeof ReorderGroup>

function getValue<V>(item: ItemData<V>) {
return item.value
Expand Down
25 changes: 10 additions & 15 deletions packages/framer-motion/src/components/Reorder/Item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import {
ReactHTML,
FunctionComponent,
useContext,
useEffect,
useRef,
forwardRef,
} from "react"
import { ReorderContext } from "../../context/ReorderContext"
import { Box } from "../../projection/geometry/types"
import { motion } from "../../render/dom/motion"
import { HTMLMotionProps } from "../../render/html/types"
import { useConstant } from "../../utils/use-constant"
Expand Down Expand Up @@ -45,6 +42,10 @@ function useDefaultMotionValue(value: any, defaultValue: number = 0) {
return isMotionValue(value) ? value : useMotionValue(defaultValue)
}

type ReorderItemProps<V> = Props<V> &
HTMLMotionProps<any> &
React.PropsWithChildren<{}>

export function ReorderItem<V>(
{
children,
Expand All @@ -54,8 +55,8 @@ export function ReorderItem<V>(
onDrag,
layout = true,
...props
}: Props<V> & HTMLMotionProps<any> & React.PropsWithChildren<{}>,
externalRef?: React.Ref<any>
}: ReorderItemProps<V>,
externalRef?: React.ForwardedRef<any>
) {
const Component = useConstant(() => motion(as)) as FunctionComponent<
React.PropsWithChildren<HTMLMotionProps<any> & { ref?: React.Ref<any> }>
Expand All @@ -71,16 +72,10 @@ export function ReorderItem<V>(
latestX || latestY ? 1 : "unset"
)

const measuredLayout = useRef<Box | null>(null)

invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group")

const { axis, registerItem, updateOrder } = context!

useEffect(() => {
registerItem(value, measuredLayout.current!)
}, [context])

return (
<Component
drag={axis}
Expand All @@ -95,9 +90,7 @@ export function ReorderItem<V>(

onDrag && onDrag(event, gesturePoint)
}}
onLayoutMeasure={(measured) => {
measuredLayout.current = measured
}}
onLayoutMeasure={(measured) => registerItem(value, measured)}
ref={externalRef}
ignoreStrict
>
Expand All @@ -106,4 +99,6 @@ export function ReorderItem<V>(
)
}

export const Item = forwardRef(ReorderItem)
export const Item = forwardRef(ReorderItem) as <V>(
props: ReorderItemProps<V> & { ref?: React.ForwardedRef<any> }
) => ReturnType<typeof ReorderItem>
Loading

0 comments on commit 9e289b1

Please sign in to comment.