Created
August 1, 2017 19:09
-
-
Save kenwheeler/8067e58f15f2f06a1ef9464045b0fa32 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
open ReactNative; | |
type targetDimensions = { | |
x: float, | |
y: float, | |
width: float, | |
height: float | |
}; | |
type _state = { | |
modalTop: Animated.Value.t, | |
modalLeft: Animated.Value.t, | |
modalWidth: Animated.Value.t, | |
modalHeight: Animated.Value.t, | |
modalOpen: bool, | |
modalReady: bool, | |
modalItem: option ScheduleItem.item, | |
selectedIndex: int, | |
listOpacity: Animated.Value.t, | |
targetDimensions | |
}; | |
let easeIn x => x *. x *. x; | |
let handleTargetDimensions targetDimensions {ReasonReact.state: state} => | |
ReasonReact.Update {...state, targetDimensions}; | |
let handleModal modalOpen {ReasonReact.state: state} => ReasonReact.Update {...state, modalOpen}; | |
let handleModalReady modalReady {ReasonReact.state: state} => | |
ReasonReact.Update {...state, modalReady}; | |
let closeModal _ {ReasonReact.state: state} => ReasonReact.Update {...state, modalOpen: false}; | |
let handleItem modalItem {ReasonReact.state: state} => | |
ReasonReact.SilentUpdate {...state, modalItem}; | |
let handleIndex selectedIndex {ReasonReact.state: state} => | |
ReasonReact.Update {...state, selectedIndex}; | |
let scheduleItemPress self::(self: ReasonReact.self _ _) x y width height item index => { | |
/* Handle target dimensions */ | |
let fx = float_of_int x; | |
let fy = float_of_int y; | |
let fw = float_of_int width; | |
let fh = float_of_int height; | |
let targetDimensions = {x: fx, y: fy, width: fw, height: fh}; | |
self.update handleTargetDimensions targetDimensions; | |
/* Set modal coords based upon clicked item */ | |
Animated.Value.setValue self.state.modalLeft fx; | |
Animated.Value.setValue self.state.modalTop fy; | |
Animated.Value.setValue self.state.modalWidth fw; | |
Animated.Value.setValue self.state.modalHeight fh; | |
/* Display and give modal data */ | |
self.update handleItem (Some item); | |
self.update handleIndex index; | |
self.update handleModal true; | |
let ww = (DimensionsRe.get `window)##width - 20; | |
let wh = (DimensionsRe.get `window)##height - 90; | |
/* Define animations */ | |
let animationBatch = | |
Animated.parallel | |
[| | |
Animated.Timing.animate | |
value::self.state.listOpacity toValue::(`raw 0.) easing::easeIn duration::300. (), | |
Animated.Timing.animate | |
value::self.state.modalTop | |
toValue::(`raw (PlatformRe.os === PlatformRe.IOS ? 30. : 10.)) | |
easing::easeIn | |
duration::300. | |
(), | |
Animated.Timing.animate | |
value::self.state.modalWidth | |
toValue::(`raw (float_of_int ww)) | |
easing::easeIn | |
duration::300. | |
(), | |
Animated.Timing.animate | |
value::self.state.modalHeight | |
toValue::(`raw (float_of_int wh)) | |
easing::easeIn | |
duration::300. | |
() | |
|] | |
[%bs.raw "{stopTogether: true}"]; | |
/* Start animations */ | |
Animated.CompositeAnimation.start | |
animationBatch | |
callback::( | |
fun _ => { | |
let _id = Js.Global.setTimeout (fun _finished => self.update handleModalReady true) 100; | |
() | |
} | |
) | |
}; | |
let itemModalPress self::(self: ReasonReact.self _ _) () => { | |
let animationBatch = | |
Animated.parallel | |
[| | |
Animated.Timing.animate | |
value::self.state.listOpacity toValue::(`raw 1.) easing::easeIn duration::400. (), | |
Animated.Timing.animate | |
value::self.state.modalTop | |
toValue::(`raw self.state.targetDimensions.y) | |
easing::easeIn | |
duration::300. | |
(), | |
Animated.Timing.animate | |
value::self.state.modalWidth | |
toValue::(`raw self.state.targetDimensions.width) | |
easing::easeIn | |
duration::300. | |
(), | |
Animated.Timing.animate | |
value::self.state.modalHeight | |
toValue::(`raw self.state.targetDimensions.height) | |
easing::easeIn | |
duration::300. | |
() | |
|] | |
[%bs.raw "{stopTogether: true}"]; | |
/* Start animations */ | |
Animated.CompositeAnimation.start | |
animationBatch | |
callback::( | |
fun _finished => { | |
self.update handleModalReady false; | |
let _ = | |
Js.Global.setTimeout | |
( | |
fun () => { | |
self.update closeModal (); | |
Animated.CompositeAnimation.start | |
( | |
Animated.Timing.animate | |
value::self.state.listOpacity | |
toValue::(`raw 1.) | |
easing::easeIn | |
duration::300. | |
() | |
) | |
() | |
} | |
) | |
100; | |
() | |
} | |
) | |
() | |
}; | |
let component = ReasonReact.statefulComponent "Schedule"; | |
let styles = | |
StyleSheet.create | |
Style.( | |
{ | |
"container": style [flex 1., alignItems `center, justifyContent `center], | |
"scrollView": style [flex 1.], | |
"contentContainer": style [paddingBottom 10.], | |
"gradient": | |
style [ | |
flex 1., | |
width (float_of_int (DimensionsRe.get `window)##width), | |
paddingTop (PlatformRe.os === PlatformRe.IOS ? 20. : 0.) | |
], | |
"modal": style [position `absolute, bottom 5., right 5., width 25., height 25.] | |
} | |
); | |
let make ::loading ::data=? _children => { | |
...component, | |
initialState: fun () => { | |
listOpacity: Animated.Value.create 1., | |
modalOpen: false, | |
modalReady: false, | |
modalItem: None, | |
modalTop: Animated.Value.create 0., | |
modalLeft: Animated.Value.create 0., | |
modalHeight: Animated.Value.create 0., | |
modalWidth: Animated.Value.create 0., | |
selectedIndex: 0, | |
targetDimensions: {x: 0., y: 0., width: 0., height: 0.} | |
}, | |
render: fun self => | |
<View style=styles##container> | |
<LinearGradient colors=[|"rgb(54, 97, 115)", "rgb(85, 130, 113)"|] style=styles##gradient> | |
<Animated.View style=Style.(style [opacityAnimated self.state.listOpacity, flex 1.])> | |
<ScrollView style=styles##scrollView contentContainerStyle=styles##contentContainer> | |
{ | |
let schedule = | |
switch data { | |
| None => [||] | |
| Some d => d | |
}; | |
loading == true ? | |
<ActivityIndicator /> : | |
Array.mapi | |
( | |
fun index item => | |
<ScheduleItem | |
item | |
index | |
selectedIndex=self.state.selectedIndex | |
modalOpen=self.state.modalOpen | |
key=item##id | |
onPress=(scheduleItemPress ::self) | |
/> | |
) | |
schedule |> ReasonReact.arrayToElement | |
} | |
</ScrollView> | |
</Animated.View> | |
</LinearGradient> | |
( | |
switch self.state.modalOpen { | |
| false => ReasonReact.nullElement | |
| true => | |
<Animated.View | |
style=Style.( | |
concat [ | |
styles##modal, | |
style [ | |
position `absolute, | |
topAnimated self.state.modalTop, | |
leftAnimated self.state.modalLeft, | |
widthAnimated self.state.modalWidth, | |
heightAnimated self.state.modalHeight | |
/* transformAnimated scaleX::self.state.scale scaleY::self.state.scale () */ | |
] | |
] | |
)> | |
( | |
switch self.state.modalItem { | |
| None => ReasonReact.nullElement | |
| Some item => | |
<ItemModal | |
modalOpen=self.state.modalOpen | |
modalReady=self.state.modalReady | |
item | |
onClose=(itemModalPress ::self) | |
/> | |
} | |
) | |
</Animated.View> | |
} | |
) | |
</View> | |
}; | |
let jsComponent = | |
ReasonReact.wrapReasonForJs | |
::component | |
( | |
fun props => | |
make loading::(Js.to_bool props##data##loading) data::props##data##allSchedules [||] | |
); | |
let query = | |
GraphQLTag.gql {| | |
query allSchedules { | |
allSchedules { | |
id | |
start | |
talk { | |
title | |
speakers { | |
id | |
name | |
bio | |
photo { | |
secret | |
} | |
} | |
} | |
title | |
} | |
} | |
|}; | |
let wrappedComponent = (ReactApollo.graphql ::query) jsComponent; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment