Skip to content

Commit

Permalink
- Added support for side menu for navigation
Browse files Browse the repository at this point in the history
- Added support for props while navigating
  • Loading branch information
ojaynico committed Oct 14, 2021
1 parent 89bcaea commit 9a09f93
Showing 1 changed file with 132 additions and 97 deletions.
229 changes: 132 additions & 97 deletions src/main/kotlin/ojaynico/kotlin/react/navigator/Navigator.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ojaynico.kotlin.react.navigator

import kotlinext.js.getOwnPropertyNames
import ojaynico.kotlin.react.animatedView
import ojaynico.kotlin.react.json
import ojaynico.kotlin.react.native.Animated
Expand All @@ -10,15 +11,16 @@ import ojaynico.kotlin.react.view
import react.*
import kotlin.js.Json


external interface RouteProps : Props {
var name: String
var component: dynamic
}

val Route = functionComponent<RouteProps> { }
val Route = functionComponent<RouteProps> { }

fun buildSceneConfig(children: Array<Json>): Json {
val config = json { }
fun buildSceneConfig(children: Array<Json>, sideMenu: FunctionComponent<*>): Json {
val config = json { }

children.forEach { child ->
config[child.asDynamic().props.name] = json {
Expand All @@ -27,29 +29,46 @@ fun buildSceneConfig(children: Array<Json>): Json {
}
}


if (sideMenu != undefined) {
config["krnMenu"] = json {
key = "krnMenu"
component = sideMenu
}
}

return config.unsafeCast<Json>()
}

external interface NavigatorFunctions {
var pop: () -> Unit
var push: (routeName: String) -> Unit
val push: (routeName: String, props: dynamic) -> Unit
var showMenu: () -> Unit
}

external interface NavigationProps : Props {
var navigator: NavigatorFunctions
}

val width = Dimensions.get("window").width
val screenWidth = Dimensions.get("window").width

external interface NavigatorState : State {
var stack: List<Json>
var sceneConfig: Json
var showSideMenu: Boolean
var touchX: dynamic
}

class Navigator : RComponent<Props, NavigatorState>() {
external interface NavigatorProps : Props {
var sideMenu: FunctionComponent<*>
var menuPosition: String
var disableMenuGesture: Boolean
}

class Navigator : RComponent<NavigatorProps, NavigatorState>() {

var backAction = {
if (state.stack.size == 1) {
if (state.stack.size == 1 && !state.showSideMenu) {
BackHandler.exitApp()
} else {
handlePop()
Expand All @@ -61,10 +80,10 @@ class Navigator : RComponent<Props, NavigatorState>() {
override fun componentDidMount() {
val initialSceneName: String = props.asDynamic().children[0].props.name
setState {
sceneConfig = buildSceneConfig(props.asDynamic().children)
sceneConfig = buildSceneConfig(props.asDynamic().children, props.sideMenu)
stack = listOf(sceneConfig[initialSceneName].unsafeCast<Json>())
showSideMenu = false
}

BackHandler.addEventListener(
"hardwareBackPress",
this.backAction
Expand All @@ -78,14 +97,55 @@ class Navigator : RComponent<Props, NavigatorState>() {
)
}

private fun onSwipeLeft() {
if (state.showSideMenu) {
handlePop()

setState {
showSideMenu = !showSideMenu
}
}
}

private fun onSwipeRight() {
if (state.showSideMenu) {
handlePop()

setState {
showSideMenu = !showSideMenu
}
}
}

val _animatedValue = Animated.Value(0)

val handlePush = { sceneName: String ->
val handlePush = { sceneName: String, childProps: dynamic ->
setState {
showSideMenu = sceneName == "krnMenu"
}

setState({ navigatorState ->
navigatorState.stack = state.stack + listOf(state.sceneConfig[sceneName].unsafeCast<Json>())
state.sceneConfig[sceneName].unsafeCast<Json>()["props"] = childProps
navigatorState.stack =
if (state.stack.last()["key"].toString() == "krnMenu") {
state.stack.slice(0 until state.stack.size - 1) + listOf(state.sceneConfig[sceneName].unsafeCast<Json>())
} else {
state.stack + listOf(
state.sceneConfig[sceneName].unsafeCast<Json>()
)
}

navigatorState
}, {
_animatedValue.setValue(width)
if (sceneName == "krnMenu") {
if (props.menuPosition == "left") {
_animatedValue.setValue(-screenWidth)
} else {
_animatedValue.setValue(screenWidth)
}
} else {
_animatedValue.setValue(screenWidth)
}

Animated.asDynamic().timing(_animatedValue, json {
toValue = 0
Expand All @@ -97,11 +157,25 @@ class Navigator : RComponent<Props, NavigatorState>() {

val handlePop = {
Animated.asDynamic().timing(_animatedValue, json {
toValue = width
toValue = if (state.showSideMenu && props.menuPosition == "left") -screenWidth else screenWidth
duration = 250
useNativeDriver = true
}).start {
_animatedValue.setValue(0)

if (state.showSideMenu) {
if (props.menuPosition == "left") {
_animatedValue.setValue(screenWidth)
} else {
_animatedValue.setValue(-screenWidth)
}

setState {
showSideMenu = false
}
} else {
_animatedValue.setValue(0)
}

if (state.stack.size > 1) {
setState {
stack = stack.slice(0 until stack.size - 1)
Expand All @@ -110,11 +184,37 @@ class Navigator : RComponent<Props, NavigatorState>() {
}
}

val handleShowMenu = {
handlePush("krnMenu") {}
}

override fun RBuilder.render() {
view {
this.attrs.style = json {
flex = 1
flexDirection = "row"
this.attrs {
style = json {
flex = 1
flexDirection = "row"
}
asDynamic().onTouchStart = { e: dynamic ->
if (props.disableMenuGesture == undefined || !props.disableMenuGesture) {
val pageX = e.nativeEvent.pageX
setState {
touchX = pageX
}
}
}
asDynamic().onTouchEnd = { e: dynamic ->
if (props.disableMenuGesture == undefined || !props.disableMenuGesture) {
val positionX = e.nativeEvent.pageX
val range = screenWidth / 6

if (positionX - state.touchX > range) {
onSwipeRight()
} else if (state.touchX - positionX > range) {
onSwipeLeft()
}
}
}
}

if (!state.stack.isNullOrEmpty()) {
Expand All @@ -136,14 +236,25 @@ class Navigator : RComponent<Props, NavigatorState>() {
})
}

// Fragments
animatedView {
this.attrs.asDynamic().key = json["key"].toString()
this.attrs.style = sceneStyles
child(json.asDynamic().component.unsafeCast<FunctionComponent<NavigationProps>>()) {
attrs {
navigator = object : NavigatorFunctions {
child(json.asDynamic().component.unsafeCast<FunctionComponent<Props>>()) {

if (json["props"] != undefined) {
val childProps = json["props"].unsafeCast<JSON>()
val childPropsNames = childProps.getOwnPropertyNames()
for (propName in childPropsNames) {
this.attrs.asDynamic()[propName] = json["props"].asDynamic()[propName]
}
}

this.attrs {
asDynamic().navigator = object : NavigatorFunctions {
override var pop = handlePop
override var push = handlePush
override val push = handlePush
override var showMenu = handleShowMenu
}
}
}
Expand All @@ -154,79 +265,3 @@ class Navigator : RComponent<Props, NavigatorState>() {
}
}
}


// Functional Navigator Rendering Twice
/*val Navigator = functionComponent<Props> { props ->
val sceneConfig = buildSceneConfig(props.asDynamic().children)
val initialSceneName: String = props.asDynamic().children[0].props.name
val _animatedValue = Animated.Value(0)
val (stack, setStack) = useState(listOf(sceneConfig[initialSceneName] as Json))
useLayoutEffect(dependencies = arrayOf(stack)) {
_animatedValue.setValue(width)
Animated.asDynamic().timing(_animatedValue, json {
toValue = 0
duration = 250
useNativeDriver = true
}).start()
}
val handlePush = { sceneName: String ->
setStack(stack + sceneConfig[sceneName] as Json)
}
val handlePop = {
Animated.asDynamic().timing(_animatedValue, json {
toValue = width
duration = 250
useNativeDriver = true
}).start {
_animatedValue.setValue(0)
if (stack.size > 1) {
setStack(stack.slice(0 until stack.size - 1))
}
}
}
view {
this.attrs.style = json {
flex = 1
flexDirection = "row"
}
stack.forEachIndexed { index, json ->
val sceneStyles = StyleSheet.compose(json {
flex = 1
},
StyleSheet.asDynamic().absoluteFillObject)
if (index == stack.size - 1 && index > 0) {
sceneStyles.push(json {
transform = arrayOf(
json {
translateX = _animatedValue
}
)
})
}
animatedView {
this.attrs.asDynamic().key = json["key"].toString()
this.attrs.style = sceneStyles
child(json.asDynamic().component as FunctionComponent<NavigationProps>) {
this.attrs {
navigator = json {
push = handlePush
pop = handlePop
}
}
}
}
}
}
}*/

0 comments on commit 9a09f93

Please sign in to comment.