Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example usage #1

Open
jonlaing opened this issue May 24, 2022 · 4 comments
Open

Add example usage #1

jonlaing opened this issue May 24, 2022 · 4 comments

Comments

@jonlaing
Copy link

Hi, thanks for putting the work into these bindings! I'm new to Rescript so it wasn't immediately obvious to me how to use it, but I was able to figure it out by reading your code and playing around with it for a bit. Anyway, I thought it might be helpful to other people if there was at least one example of using it.

Also, you might want to update the README to reflect what bindings are working. Seems to me that at least Animated.View and withSpring are working as intended.

Anyway, here is the little toy example I wrote to try out the bindings. I have it working in Expo 44.0 on both iOS and Android:

open ReactNative

let styles = {
  open Style

  StyleSheet.create({
    "container": viewStyle(
      ~alignItems=#center,
      ~justifyContent=#center,
      ~borderRadius=4.0,
      ~position=#relative,
      ~backgroundColor="transparent",
      (),
    ),
    "presser": viewStyle(~flex=1.0, ()),
    "backer": viewStyle(
      ~position=#absolute,
      ~top=0.0->dp,
      ~left=0.0->dp,
      ~right=0.0->dp,
      ~bottom=0.0->dp,
      ~borderRadius=4.0,
      (),
    ),
    "textContainer": viewStyle(
      ~paddingVertical=5.0->dp,
      ~paddingHorizontal=10.0->dp,
      (),
      ~backgroundColor="transparent",
    ),
  })
}

@genType("Selectable") @react.component
let make = (~title, ~selected, ~color, ~onPress) => {
  let scale = ResAnimated.useSharedValue(0.0)

  React.useEffect1(_ => {
    open ResAnimated
    switch selected {
    | true =>
      scale.value = withSpring(
        ~toValue=1.0,
        ~userConfig=Spring.makeConfig(
          ~damping=15.0,
          ~mass=1.0,
          ~stiffness=250.0,
          ~overshootClamping=false,
          ~restSpeedThreshold=0.001,
          ~restDisplacementThreshold=0.001,
          (),
        ),
        (),
      )
    | false =>
      scale.value = withTiming(
        ~toValue=0.0,
        ~userConfig=Some(#Duration({Timing.duration: 250.0})),
        (),
      )
    }
    None
  }, [selected])

  let animatedStyle = {
    ResAnimated.useAnimatedStyle(() =>
      Style.style(~transform=[Style.scale(~scale=scale.value)], ~opacity=scale.value, ())
    )
  }

  <View style={styles["container"]}>
    <ResAnimated.View
      style={
        open ReactNative
        open Style
        array([styles["backer"], viewStyle(~backgroundColor=color, ()), animatedStyle])
      }
    />
    <Pressable style={_ => styles["presser"]} onPress={_ => onPress(!selected)}>
      {_ => <>
        <View style={styles["textContainer"]}> <Text> {title->React.string} </Text> </View>
      </>}
    </Pressable>
  </View>
}
@jonlaing
Copy link
Author

Oh, since I will probably be using this library quite a bit in the coming weeks, I'm happy to work on it and submit pull requests, if that's alright with you.

@reck753
Copy link
Owner

reck753 commented May 24, 2022

Hi @jonlaing! You are welcome!

Glad you found about this honestly. I started working on these binding some time ago as you can see and I just laid down some basics and core stuff I needed in a project of mine.
I planned on expanding it as I stumble upon something I need, since I was the only one using it. But now, since there is someone else in need of these, I will try to find some time to work on them again.

As for the example, I already started working on something that I wanted to publish, but never found time to wrap it up. I will try to do it in upcoming days. It is a drawer sheet example with ReScript Gesture Handler as well.

Your pull requests will be more than welcome! Please let me know if you stumble upon some issue or a question, I will gladly try to help!

Thanks!

@reck753
Copy link
Owner

reck753 commented May 24, 2022

Oh, one more thing... If by some chance I miss the message or pull request, feel free to tag me after a day or two, to remind me!

@reck753
Copy link
Owner

reck753 commented May 24, 2022

@jonlaing Here is some draft of that example, just so it's here if someone stumbles on this until I manage to publish it:

open ReactNative
open ReactNativeSafeAreaContext

// `Portal` is a binding of `@gorhom/portal`
module DrawerPortal = Portal

let styles = StyleSheet.create({
  "backdrop": Style.viewStyle(~backgroundColor="black", ()),
  "container": Style.viewStyle(~padding=12.->Style.dp, ~backgroundColor="lightgrey", ()),
  "absoluteFillObject": StyleSheet.absoluteFillObject,
})

let drawerWidthPct = 75.

@genType @react.component
let make = (
  ~isOpened: bool,
  ~onClose: unit => unit,
  ~children: React.element,
  ~position: [#left | #right]=#left,
  ~containerStyle: option<Style.t>=?,
  ~includeSafeAreaInsets: bool=true,
) => {
  let {top, bottom, left, right} = useSafeAreaInsets()
  let {width, height} = Dimensions.useWindowDimensions()
  let smallerAxisSize = Js.Math.min_float(height, width)
  let widerScreen = smallerAxisSize >= 600.
  let maximumWidth = widerScreen ? 320. : 280.

  let drawerPctWidth = width *. (drawerWidthPct /. 100.)
  // For wider screens, we want to cap the drawer width
  let cappedWidth = drawerPctWidth > maximumWidth ? maximumWidth : drawerPctWidth

  let startPosition = position === #left ? -.cappedWidth : width
  let endPosition = position === #left ? 0. : width -. cappedWidth

  // Currently this variable name won't make sense, because value is 0 or 1,
  // but it is going to be refactored to be x-axis value from 0 to constrainedWidth
  let transitionX = ResAnimated.useSharedValue(0.)
  React.useEffect2(() => {
    transitionX.value = isOpened ? ResAnimated.withTiming(1.) : ResAnimated.withTiming(0.)
    None
  }, (transitionX, isOpened))

  let drawerStyle = ResAnimated.useAnimatedStyle(() => {
    let translateX = ResAnimated.interpolate(
      transitionX.value,
      [0., 1.],
      [startPosition, endPosition],
      None,
    )
    Style.viewStyle(~transform=[Style.translateX(~translateX)], ())
  })

  let backdropStyle = ResAnimated.useAnimatedStyle(() => {
    let opacity = ResAnimated.interpolate(transitionX.value, [0., 1.], [0., 0.7], None)
    Style.viewStyle(~opacity, ())
  })

  // `Directions` from ReScript Gesture Handler
  let flingDirection = switch position {
  | #left => Directions.Direction.make(Directions.directions.left)
  | #right => Directions.Direction.make(Directions.directions.right)
  }

  let safeAreaPadding = Style.viewStyle(
    ~paddingTop=top->Style.dp,
    ~paddingBottom=bottom->Style.dp,
    ~paddingLeft=left->Style.dp,
    ~paddingRight=right->Style.dp,
    (),
  )

  // `Fling` and `Gesture` from ReScript Gesture Handler
  let flingGesture1 =
    Gesture.makeFling()
    ->Fling.direction(flingDirection)
    ->Fling.onEnd((_event, _success) => ResAnimated.runOnJS(onClose)(.))

  // We need two different ones for some reason. By having only one applied on two different `GestureDetectors`, will
  // make one of them not to work properly
  let flingGesture2 =
    Gesture.makeFling()
    ->Fling.direction(flingDirection)
    ->Fling.onEnd((_event, _success) => ResAnimated.runOnJS(onClose)(.))

  // `GestureDetector` and `Gesture` from ReScript Gesture Handler
  <DrawerPortal>
    <GestureDetector gesture={flingGesture1->Gesture.fling}>
      <ResAnimated.View
        style={Style.array([styles["backdrop"], styles["absoluteFillObject"], backdropStyle])}
        pointerEvents={isOpened ? #auto : #none}
      />
    </GestureDetector>
    <GestureDetector gesture={flingGesture2->Gesture.fling}>
      <ResAnimated.View
        style={Style.arrayOption([
          Some(styles["container"]),
          Some(styles["absoluteFillObject"]),
          Some(
            Style.viewStyle(~width=drawerWidthPct->Style.pct, ~maxWidth=maximumWidth->Style.dp, ()),
          ),
          includeSafeAreaInsets ? Some(safeAreaPadding) : None,
          containerStyle,
          Some(drawerStyle),
        ])}>
        {children}
      </ResAnimated.View>
    </GestureDetector>
  </DrawerPortal>
}

NOTE: Currently, the version of Gesture Handler used in the ReScript binding makes this code not work on Android (I think, or some other platform), so be careful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants