Last active
May 10, 2022 03:51
-
-
Save vmarcosp/866ed8770727640b7b79af6ab125da7f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module FramerMotion = { | |
type animateValue | |
type controlStatus = [#visible | #hidden] | |
type controls = {start: (. controlStatus) => unit} | |
type animate = [#controlled(controls) | #visible | #hidden] | |
@deriving(abstract) | |
type transition = { | |
@optional duration: float, | |
@optional delay: float, | |
@optional staggeredChildren: float, | |
@optional delayChildren: float, | |
} | |
@deriving(abstract) | |
type variant = { | |
@optional opacity: float, | |
@optional scale: int, | |
@optional x: int, | |
@optional y: int, | |
@optional transition: transition, | |
} | |
type variants = { | |
hidden: variant, | |
visible: variant, | |
} | |
@module("framer-motion") | |
external useAnimation: unit => controls = "useAnimation" | |
} | |
module Components = { | |
module Option = Belt.Option | |
type motionElement | |
type initial = [#hidden | #visible] | |
@deriving(abstract) | |
type motionProps = { | |
@optional className: string, | |
@optional @as("ref") innerRef: ReactDOM.domRef, | |
@optional id: string, | |
@optional href: string, | |
@optional src: string, | |
@optional target: string, | |
@optional initial: initial, | |
@optional variants: FramerMotion.variants, | |
@optional animate: FramerMotion.animateValue, | |
} | |
external identity: 'a => FramerMotion.animateValue = "%identity" | |
@module("react") | |
external createElement: ( | |
motionElement, | |
motionProps, | |
React.element, | |
) => React.element = "createElement" | |
@module("react") | |
external createVoidElement: (motionElement, motionProps) => React.element = | |
"createElement" | |
let unwrapAnimate = v => | |
switch v { | |
| #controlled(v) => identity(v) | |
| v => identity(v) | |
} | |
module type IMakeElement = { | |
let element: motionElement | |
} | |
module MakeElement = (M: IMakeElement) => { | |
@react.component | |
let make = ( | |
~className=?, | |
~initial=?, | |
~variants=?, | |
~animate: option<FramerMotion.animate>=?, | |
~innerRef=?, | |
~id=?, | |
~href=?, | |
~children, | |
) => { | |
let props = motionProps( | |
~className?, | |
~innerRef?, | |
~id?, | |
~initial?, | |
~animate=?animate->Option.map(unwrapAnimate), | |
~variants?, | |
~href?, | |
(), | |
) | |
createElement(M.element, props, children) | |
} | |
} | |
module MakeVoidElement = (M: IMakeElement) => { | |
@react.component | |
let make = ( | |
~className=?, | |
~initial=?, | |
~variants=?, | |
~animate: option<FramerMotion.animate>=?, | |
~innerRef=?, | |
~id=?, | |
~src=?, | |
) => { | |
let props = motionProps( | |
~className?, | |
~innerRef?, | |
~id?, | |
~initial?, | |
~animate=?animate->Option.map(unwrapAnimate), | |
~variants?, | |
~src?, | |
(), | |
) | |
createVoidElement(M.element, props) | |
} | |
} | |
} | |
module Div = Components.MakeElement({ | |
@module("framer-motion") @scope("motion") | |
external element: Components.motionElement = "div" | |
}) | |
module Header = Components.MakeElement({ | |
@module("framer-motion") @scope("motion") | |
external element: Components.motionElement = "header" | |
}) | |
module Section = Components.MakeElement({ | |
@module("framer-motion") @scope("motion") | |
external element: Components.motionElement = "section" | |
}) | |
module H1 = Components.MakeElement({ | |
@module("framer-motion") @scope("motion") | |
external element: Components.motionElement = "h1" | |
}) | |
module H2 = Components.MakeElement({ | |
@module("framer-motion") @scope("motion") | |
external element: Components.motionElement = "h2" | |
}) | |
module P = Components.MakeElement({ | |
@module("framer-motion") @scope("motion") | |
external element: Components.motionElement = "p" | |
}) | |
module A = Components.MakeElement({ | |
@module("framer-motion") @scope("motion") | |
external element: Components.motionElement = "a" | |
}) | |
module Li = Components.MakeElement({ | |
@module("framer-motion") @scope("motion") | |
external element: Components.motionElement = "li" | |
}) | |
module Img = Components.MakeVoidElement({ | |
@module("framer-motion") @scope("motion") | |
external element: Components.motionElement = "img" | |
}) | |
// Usage | |
module Usage = { | |
open Render | |
let variants = delay => { | |
open FramerMotion | |
{ | |
hidden: variant(~opacity=0.0, ~y=20, ()), | |
visible: variant( | |
~opacity=1.0, | |
~y=0, | |
~transition=transition(~delay, ~duration=0.6, ()), | |
() | |
), | |
} | |
} | |
@react.component | |
let make = () => { | |
let controls = FramerMotion.useAnimation() | |
let (innerRef, inView) = IntersectionObserver.useInView() | |
React.useEffect1(() => { | |
if inView { | |
controls.start(. #visible) | |
} | |
None | |
}, [inView]) | |
<section ref=innerRef> | |
<Motion.H1 | |
initial=#hidden | |
animate=#controlled(controls) | |
variants={variants(1.75)} | |
> | |
{Content.heroText} | |
</Motion.H1> | |
<Motion.A | |
initial=#hidden | |
animate=#controlled(controls) | |
variants={variants(2.0)} | |
className=button> | |
{"SAIBA MAIS"->str} | |
</Motion.A> | |
</section> | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment