diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cd44b37..106f139 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,7 +21,7 @@ kotlinxCoroutines = "1.9.0" kotlinxDatetime = "0.6.1" lifecycle-runtime = "2.8.6" tunjidStateHolder = "1.1.0" -tunjidComposables = "0.0.4" +tunjidComposables = "0.0.5" junit = "4.13.2" runner = "1.0.2" espressoCore = "3.0.2" diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptivePaneStrategy.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptivePaneStrategy.kt deleted file mode 100644 index c53f882..0000000 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptivePaneStrategy.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.tunjid.treenav.adaptive - -import androidx.compose.animation.EnterTransition -import androidx.compose.animation.ExitTransition -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import com.tunjid.treenav.Node - -/** - * Provides adaptive strategy in panes [Pane] for a given navigation destination [Destination]. - */ -@Stable -class AdaptivePaneStrategy internal constructor( - val transitions: AdaptivePaneScope.() -> AdaptivePaneScope.Transitions, - /** - * Defines what route to show in the secondary panel alongside this route - */ - val paneMapper: @Composable (Destination) -> Map, - val render: @Composable AdaptivePaneScope.(Destination) -> Unit -) - -/** - * Allows for defining the adaptation strategy in panes [Pane] for a given navigation destination [Destination]. - * - * @param transitions the transitions to run within each [AdaptivePaneScope]. - * @param paneMapping provides the mapping of panes to destinations for a given destination [Destination]. - * @param render defines the Composable rendered for each destination - * in a given [AdaptivePaneScope]. - */ -fun adaptivePaneStrategy( - transitions: AdaptivePaneScope.() -> AdaptivePaneScope.Transitions = { NoTransition }, - paneMapping: @Composable (Destination) -> Map = { emptyMap() }, - render: @Composable AdaptivePaneScope.(Destination) -> Unit -) = AdaptivePaneStrategy( - paneMapper = paneMapping, - transitions = transitions, - render = render -) - -private val NoTransition = AdaptivePaneScope.Transitions( - enter = EnterTransition.None, - exit = ExitTransition.None, -) \ No newline at end of file diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/ThreePane.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/ThreePane.kt deleted file mode 100644 index 0d71440..0000000 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/ThreePane.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.tunjid.treenav.adaptive.threepane - -import androidx.compose.animation.EnterTransition -import androidx.compose.animation.ExitTransition -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.runtime.Composable -import com.tunjid.treenav.Node -import com.tunjid.treenav.adaptive.Adaptation.Swap -import com.tunjid.treenav.adaptive.AdaptivePaneScope -import com.tunjid.treenav.adaptive.AdaptivePaneStrategy -import com.tunjid.treenav.adaptive.adaptivePaneStrategy - -/** - * A layout in the hierarchy that hosts an [AdaptivePaneStrategy] - */ -enum class ThreePane { - Primary, - TransientPrimary, - Secondary, - Tertiary, - Overlay; - - companion object { - val PrimaryToSecondary = Swap( - from = Primary, - to = Secondary - ) - - val SecondaryToPrimary = Swap( - from = Secondary, - to = Primary - ) - - val PrimaryToTransient = Swap( - from = Primary, - to = TransientPrimary - ) - } -} - -fun threePaneAdaptiveNodeConfiguration( - transitions: AdaptivePaneScope.() -> AdaptivePaneScope.Transitions = { - val state = paneState - when (state.pane) { - ThreePane.Primary, - ThreePane.Secondary -> when (state.adaptations) { - ThreePane.PrimaryToSecondary, - ThreePane.SecondaryToPrimary -> NoTransition - - else -> DefaultTransition - } - - ThreePane.TransientPrimary -> when (state.adaptations) { - ThreePane.PrimaryToTransient -> when (state.pane) { - ThreePane.Secondary -> DefaultTransition - else -> NoTransition - } - - else -> DefaultTransition - } - - else -> NoTransition - } - }, - paneMapping: @Composable (R) -> Map = { - mapOf(ThreePane.Primary to it) - }, - render: @Composable AdaptivePaneScope.(R) -> Unit -) = adaptivePaneStrategy( - paneMapping = paneMapping, - transitions = transitions, - render = render -) - -private val RouteTransitionAnimationSpec: FiniteAnimationSpec = tween( - durationMillis = 700 -) - -private val DefaultTransition = AdaptivePaneScope.Transitions( - enter = fadeIn( - animationSpec = RouteTransitionAnimationSpec, - ), - exit = fadeOut( - animationSpec = RouteTransitionAnimationSpec - ) -) - -private val NoTransition = AdaptivePaneScope.Transitions( - enter = EnterTransition.None, - exit = ExitTransition.None, -) \ No newline at end of file diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptivePaneScope.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PaneScope.kt similarity index 72% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptivePaneScope.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PaneScope.kt index b4ef94e..57ffe05 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptivePaneScope.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PaneScope.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.tunjid.treenav.adaptive +package com.tunjid.treenav.compose import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.AnimatedVisibilityScope @@ -32,15 +32,15 @@ import kotlin.jvm.JvmInline * Scope for adaptive content that can show up in an arbitrary pane. */ @Stable -interface AdaptivePaneScope : AnimatedVisibilityScope { +interface PaneScope : AnimatedVisibilityScope { /** - * Provides information about the adaptive context that created this [AdaptivePaneScope]. + * Provides information about the adaptive context that created this [PaneScope]. */ - val paneState: AdaptivePaneState + val paneState: PaneState /** - * Whether or not this [AdaptivePaneScope] is active in its current pane. It is inactive when + * Whether or not this [PaneScope] is active in its current pane. It is inactive when * it is animating out of its [AnimatedVisibilityScope]. */ val isActive: Boolean @@ -55,14 +55,14 @@ interface AdaptivePaneScope : AnimatedVisibilityScope } /** - * An implementation of [AdaptivePaneScope] that supports animations and shared elements + * An implementation of [PaneScope] that supports animations and shared elements */ @Stable -internal class AnimatedAdaptivePaneScope( - paneState: AdaptivePaneState, +internal class AnimatedPaneScope( + paneState: PaneState, activeState: State, val animatedContentScope: AnimatedContentScope -) : AdaptivePaneScope, AnimatedVisibilityScope by animatedContentScope { +) : PaneScope, AnimatedVisibilityScope by animatedContentScope { override var paneState by mutableStateOf(paneState) @@ -73,14 +73,14 @@ internal class AnimatedAdaptivePaneScope( * Information about content in a pane */ @Stable -sealed interface AdaptivePaneState { +sealed interface PaneState { val currentDestination: Destination? val pane: Pane? val adaptations: Set } /** - * [Slot] based implementation of [AdaptivePaneState] + * [Slot] based implementation of [PaneState] */ internal data class SlotPaneState( val slot: Slot?, @@ -88,10 +88,10 @@ internal data class SlotPaneState( override val currentDestination: Destination?, override val pane: Pane?, override val adaptations: Set, -) : AdaptivePaneState +) : PaneState /** - * A spot taken by an [AdaptivePaneStrategy] that may be moved in from pane to pane. + * A spot taken by an [PaneStrategy] that may be moved in from pane to pane. */ @JvmInline internal value class Slot internal constructor(val index: Int) diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PaneStrategy.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PaneStrategy.kt new file mode 100644 index 0000000..937e043 --- /dev/null +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PaneStrategy.kt @@ -0,0 +1,45 @@ +package com.tunjid.treenav.compose + +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import com.tunjid.treenav.Node + +/** + * Provides the logic used to select, configure and place a navigation [Destination] for each + * pane [Pane] for the current active navigation [Destination]. + */ +@Stable +class PaneStrategy internal constructor( + val transitions: PaneScope.() -> PaneScope.Transitions, + /** + * Defines what route to show in the secondary panel alongside this route + */ + val paneMapper: @Composable (Destination) -> Map, + val render: @Composable PaneScope.(Destination) -> Unit +) + +/** + * Allows for defining the logic used to select, configure and place a navigation + * [Destination] for each pane [Pane] for the current active navigation [Destination]. + * + * @param transitions the transitions to run within each [PaneScope]. + * @param paneMapping provides the mapping of panes to destinations for a given destination [Destination]. + * @param render defines the Composable rendered for each destination + * in a given [PaneScope]. + */ +fun paneStrategy( + transitions: PaneScope.() -> PaneScope.Transitions = { NoTransition }, + paneMapping: @Composable (Destination) -> Map = { emptyMap() }, + render: @Composable PaneScope.(Destination) -> Unit +) = PaneStrategy( + paneMapper = paneMapping, + transitions = transitions, + render = render +) + +private val NoTransition = PaneScope.Transitions( + enter = EnterTransition.None, + exit = ExitTransition.None, +) \ No newline at end of file diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavHost.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PanedNavHost.kt similarity index 70% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavHost.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PanedNavHost.kt index bd43da9..ab83491 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavHost.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PanedNavHost.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.tunjid.treenav.adaptive +package com.tunjid.treenav.compose import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable @@ -24,18 +24,18 @@ import com.tunjid.treenav.Node /** * Creates a host for adaptive navigation for panes [Pane] and destinations [Destination]. * - * @param state the [AdaptiveNavHostState] producing the [AdaptiveNavHostScope] that provides - * context about the panes in [AdaptiveNavHost]. + * @param state the [PanedNavHostState] producing the [PanedNavHostScope] that provides + * context about the panes in [PanedNavHost]. * @param modifier The modifier to be applied to the layout. - * @param content [AdaptiveNavHostScope] receiving lambda allowing for placing each pane in its + * @param content [PanedNavHostScope] receiving lambda allowing for placing each pane in its * appropriate slot. * */ @Composable -fun AdaptiveNavHost( - state: AdaptiveNavHostState, +fun PanedNavHost( + state: PanedNavHostState, modifier: Modifier = Modifier, - content: @Composable AdaptiveNavHostScope.() -> Unit + content: @Composable PanedNavHostScope.() -> Unit ) { Box( modifier = modifier diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavHostConfiguration.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PanedNavHostConfiguration.kt similarity index 70% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavHostConfiguration.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PanedNavHostConfiguration.kt index 30956dd..856e335 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavHostConfiguration.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PanedNavHostConfiguration.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.tunjid.treenav.adaptive +package com.tunjid.treenav.compose import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable @@ -27,7 +27,7 @@ import androidx.compose.ui.Modifier import com.tunjid.treenav.Node /** - * Class for configuring an [AdaptiveNavHost] for adapting different navigation + * Class for configuring a [PanedNavHost] for selecting, adapting and placing navigation * destinations into different panes from an arbitrary [navigationState]. * * @param navigationState the navigation state to be adapted into various panes. @@ -36,10 +36,10 @@ import com.tunjid.treenav.Node * panes available. */ @Stable -class AdaptiveNavHostConfiguration internal constructor( +class PanedNavHostConfiguration internal constructor( val navigationState: State, val destinationTransform: (NavigationState) -> Destination, - val strategyTransform: (destination: Destination) -> AdaptivePaneStrategy + val strategyTransform: (destination: Destination) -> PaneStrategy ) { internal val currentDestination: State = derivedStateOf { destinationTransform(navigationState.value) @@ -47,7 +47,7 @@ class AdaptiveNavHostConfiguration adaptiveNavHostConfiguration( +fun panedNavHostConfiguration( navigationState: State, destinationTransform: (NavigationState) -> Destination, - strategyTransform: (destination: Destination) -> AdaptivePaneStrategy -) = AdaptiveNavHostConfiguration( + strategyTransform: (destination: Destination) -> PaneStrategy +) = PanedNavHostConfiguration( navigationState = navigationState, destinationTransform = destinationTransform, strategyTransform = strategyTransform, ) /** - * Creates a new [AdaptiveNavHost] by delegating to [this] and rendering destinations into different panes. + * Creates a new [PanedNavHost] by delegating to [this] and rendering destinations into different panes. * - * @param destinationTransform a transform of [AdaptiveNavHostConfiguration.navigationState] + * @param destinationTransform a transform of [PanedNavHostConfiguration.navigationState] * to its current destination. It is read inside a [derivedStateOf] block, so reads of snapshot * state objects will be observed. * @param strategyTransform provides the strategy used to adapt the current destination to the * panes available. */ -fun AdaptiveNavHostConfiguration.delegated( +fun PanedNavHostConfiguration< + Pane, + NavigationState, + Destination + >.delegated( destinationTransform: (NavigationState) -> Destination = this@delegated.destinationTransform, - strategyTransform: (destination: Destination) -> AdaptivePaneStrategy -) = adaptiveNavHostConfiguration( + strategyTransform: (destination: Destination) -> PaneStrategy +) = panedNavHostConfiguration( navigationState = this@delegated.navigationState, destinationTransform = destinationTransform, strategyTransform = strategyTransform, @@ -90,8 +94,12 @@ fun AdaptiveNavHostConfigurat * The current destination in a given [paneScope]. */ @Composable -internal fun AdaptiveNavHostConfiguration.Destination( - paneScope: AdaptivePaneScope +internal fun PanedNavHostConfiguration< + Pane, + *, + Destination + >.Destination( + paneScope: PaneScope ) { val current = remember(paneScope.paneState.currentDestination) { paneScope.paneState.currentDestination @@ -105,17 +113,21 @@ internal fun AdaptiveNavHostConfiguration AdaptiveNavHostConfiguration.paneMapping(): Map { +internal fun PanedNavHostConfiguration< + Pane, + *, + Destination + >.paneMapping(): Map { val current by currentDestination return current.let { strategyTransform(it).paneMapper(it) diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PanedNavigationState.kt similarity index 88% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PanedNavigationState.kt index 5c1e978..c8bd66d 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/AdaptiveNavigationState.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/PanedNavigationState.kt @@ -1,11 +1,11 @@ -package com.tunjid.treenav.adaptive +package com.tunjid.treenav.compose import com.tunjid.treenav.Node /** * State providing details about data in each pane [Pane] it hosts. */ -interface AdaptiveNavigationState { +interface PanedNavigationState { /** * The current [Destination] in this [pane]. @@ -25,7 +25,8 @@ interface AdaptiveNavigationState { } /** - * A description of the process that the layout undertook to adapt to its new configuration. + * A description of the process that the layout undertook to adapt to the present + * pane in its new configuration. */ sealed class Adaptation { diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SavedStateAdaptiveNavHostState.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/SavedStatePanedNavHostState.kt similarity index 85% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SavedStateAdaptiveNavHostState.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/SavedStatePanedNavHostState.kt index 51ca896..d1e88c7 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SavedStateAdaptiveNavHostState.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/SavedStatePanedNavHostState.kt @@ -1,4 +1,4 @@ -package com.tunjid.treenav.adaptive +package com.tunjid.treenav.compose import androidx.compose.animation.AnimatedContent import androidx.compose.animation.ContentTransform @@ -25,8 +25,8 @@ import androidx.lifecycle.compose.currentStateAsState import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner import com.tunjid.treenav.Node import com.tunjid.treenav.Order -import com.tunjid.treenav.adaptive.lifecycle.DestinationViewModelStoreCreator -import com.tunjid.treenav.adaptive.lifecycle.rememberDestinationLifecycleOwner +import com.tunjid.treenav.compose.lifecycle.DestinationViewModelStoreCreator +import com.tunjid.treenav.compose.lifecycle.rememberDestinationLifecycleOwner import com.tunjid.treenav.traverse @@ -34,20 +34,20 @@ import com.tunjid.treenav.traverse * A host for adaptive navigation for panes [Pane] and destinations [Destination]. */ @Stable -interface AdaptiveNavHostState { +interface PanedNavHostState { /** - * Creates the scope that provides context about individual panes [Pane] in an [AdaptiveNavHost]. + * Creates the scope that provides context about individual panes [Pane] in an [PanedNavHost]. */ @Composable - fun scope(): AdaptiveNavHostScope + fun scope(): PanedNavHostScope } /** - * Scope that provides context about individual panes [Pane] in an [AdaptiveNavHost]. + * Scope that provides context about individual panes [Pane] in an [PanedNavHost]. */ @Stable -interface AdaptiveNavHostScope { +interface PanedNavHostScope { @Composable fun Destination( @@ -64,29 +64,29 @@ interface AdaptiveNavHostScope { } /** - * An implementation of an [AdaptiveNavHostState] that provides a [SaveableStateHolder] for each + * An implementation of an [PanedNavHostState] that provides a [SaveableStateHolder] for each * navigation destination that shows up in its panes. * - * @param panes a list of panes that is possible to show in the [AdaptiveNavHost] in all + * @param panes a list of panes that is possible to show in the [PanedNavHost] in all * possible configurations. The panes should consist of enum class instances, or a sealed class * hierarchy of kotlin objects. - * @param configuration the [AdaptiveNavHostConfiguration] that applies adaptive semantics and - * strategies for each navigation destination shown in the [AdaptiveNavHost]. + * @param configuration the [PanedNavHostConfiguration] that applies adaptive semantics and + * strategies for each navigation destination shown in the [PanedNavHost]. */ @Stable -class SavedStateAdaptiveNavHostState( +class SavedStatePanedNavHostState( private val panes: List, - private val configuration: AdaptiveNavHostConfiguration, -) : AdaptiveNavHostState { + private val configuration: PanedNavHostConfiguration, +) : PanedNavHostState { @Composable - override fun scope(): AdaptiveNavHostScope { + override fun scope(): PanedNavHostScope { val navigationState by configuration.navigationState val panesToNodes = configuration.paneMapping() val saveableStateHolder = rememberSaveableStateHolder() val adaptiveContentScope = remember { - SavedStateAdaptiveNavHostScope( + SavedStatePanedNavHostScope( panes = panes, navHostConfiguration = configuration, initialPanesToNodes = panesToNodes, @@ -106,12 +106,12 @@ class SavedStateAdaptiveNavHostState( companion object { @Stable - private class SavedStateAdaptiveNavHostScope( + private class SavedStatePanedNavHostScope( panes: List, initialPanesToNodes: Map, saveableStateHolder: SaveableStateHolder, - val navHostConfiguration: AdaptiveNavHostConfiguration, - ) : AdaptiveNavHostScope, SaveableStateHolder by saveableStateHolder { + val navHostConfiguration: PanedNavHostConfiguration, + ) : PanedNavHostScope, SaveableStateHolder by saveableStateHolder { private val destinationViewModelStoreCreator = DestinationViewModelStoreCreator( rootNodeProvider = navHostConfiguration.navigationState::value @@ -123,7 +123,7 @@ class SavedStateAdaptiveNavHostState( ).toSet() var adaptiveNavigationState by mutableStateOf( - value = SlotBasedAdaptiveNavigationState.initial(slots = slots) + value = SlotBasedPanedNavigationState.initial(slots = slots) .adaptTo( slots = slots, panesToNodes = initialPanesToNodes, @@ -189,7 +189,7 @@ class SavedStateAdaptiveNavHostState( } ) { targetPaneState -> val scope = remember { - AnimatedAdaptivePaneScope( + AnimatedPaneScope( paneState = targetPaneState, activeState = derivedStateOf { val activePaneState = adaptiveNavigationState.paneStateFor(slot) @@ -235,13 +235,13 @@ class SavedStateAdaptiveNavHostState( ) { destinationLifecycleOwner.update( hostLifecycleState = hostLifecycleState, - adaptivePaneScope = scope, + paneScope = scope, adaptiveNavigationState = adaptiveNavigationState ) onDispose { destinationLifecycleOwner.update( hostLifecycleState = hostLifecycleState, - adaptivePaneScope = scope, + paneScope = scope, adaptiveNavigationState = adaptiveNavigationState ) } @@ -274,7 +274,7 @@ class SavedStateAdaptiveNavHostState( } private inline fun updateAdaptiveNavigationState( - block: SlotBasedAdaptiveNavigationState.() -> SlotBasedAdaptiveNavigationState + block: SlotBasedPanedNavigationState.() -> SlotBasedPanedNavigationState ) { adaptiveNavigationState = adaptiveNavigationState.block() } diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/SlotBasedPanedNavigationState.kt similarity index 89% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/SlotBasedPanedNavigationState.kt index 2a8a891..b2a548a 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationState.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/SlotBasedPanedNavigationState.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.tunjid.treenav.adaptive +package com.tunjid.treenav.compose import androidx.compose.runtime.Immutable import com.tunjid.treenav.Node -import com.tunjid.treenav.adaptive.Adaptation.Change.contains +import com.tunjid.treenav.compose.Adaptation.Change.contains /** * Data structure for managing navigation as it adapts to various layout configurations */ @Immutable -internal data class SlotBasedAdaptiveNavigationState( +internal data class SlotBasedPanedNavigationState( /** * Moves between panes within a navigation sequence. */ @@ -49,11 +49,11 @@ internal data class SlotBasedAdaptiveNavigationState( * A set of node ids that are animating out. */ val destinationIdsAnimatingOut: Set, -) : AdaptiveNavigationState { +) : PanedNavigationState { companion object { internal fun initial( slots: Collection, - ): SlotBasedAdaptiveNavigationState = SlotBasedAdaptiveNavigationState( + ): SlotBasedPanedNavigationState = SlotBasedPanedNavigationState( swapAdaptations = emptySet(), panesToDestinations = emptyMap(), destinationIdsToAdaptiveSlots = slots.associateBy( @@ -67,7 +67,7 @@ internal data class SlotBasedAdaptiveNavigationState( internal fun paneStateFor( slot: Slot - ): AdaptivePaneState { + ): PaneState { val node = destinationFor(slot) val pane = node?.let(::paneFor) return SlotPaneState( @@ -121,11 +121,11 @@ internal data class SlotBasedAdaptiveNavigationState( * A method that adapts changes in navigation to different panes while allowing for them * to be animated easily. */ -internal fun SlotBasedAdaptiveNavigationState.adaptTo( +internal fun SlotBasedPanedNavigationState.adaptTo( slots: Set, panesToNodes: Map, backStackIds: Set, -): SlotBasedAdaptiveNavigationState { +): SlotBasedPanedNavigationState { val previous = this val previouslyUsedSlots = previous.destinationIdsToAdaptiveSlots @@ -178,7 +178,7 @@ internal fun SlotBasedAdaptiveNavigationState.adaptTo( nodeIdsToAdaptiveSlots[nodeId] = availableSlots.first().also(availableSlots::remove) } - return SlotBasedAdaptiveNavigationState( + return SlotBasedPanedNavigationState( // If the values of the nodes to panes are the same, no swaps occurred. swapAdaptations = when (previous.panesToDestinations.mapValues { it.value?.id }) { panesToNodes.mapValues { it.value?.id } -> previous.swapAdaptations @@ -198,7 +198,7 @@ internal fun SlotBasedAdaptiveNavigationState.adaptTo( /** * Checks if any of the new routes coming in has any conflicts with those animating out. */ -internal fun SlotBasedAdaptiveNavigationState.hasConflictingRoutes(): Boolean = +internal fun SlotBasedPanedNavigationState.hasConflictingRoutes(): Boolean = panesToDestinations.keys .map(::destinationFor) .any { @@ -206,9 +206,9 @@ internal fun SlotBasedAdaptiveNavigationState SlotBasedAdaptiveNavigationState.prune(): SlotBasedAdaptiveNavigationState = +internal fun SlotBasedPanedNavigationState.prune(): SlotBasedPanedNavigationState = copy( destinationIdsToAdaptiveSlots = destinationIdsToAdaptiveSlots.filter { (routeId) -> if (routeId == null) return@filter false diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/lifecycle/DestinationLifecycleOwner.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/lifecycle/DestinationLifecycleOwner.kt similarity index 82% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/lifecycle/DestinationLifecycleOwner.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/lifecycle/DestinationLifecycleOwner.kt index a506e0d..16e2a2a 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/lifecycle/DestinationLifecycleOwner.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/lifecycle/DestinationLifecycleOwner.kt @@ -1,4 +1,4 @@ -package com.tunjid.treenav.adaptive.lifecycle +package com.tunjid.treenav.compose.lifecycle import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable @@ -9,8 +9,8 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.compose.LocalLifecycleOwner import com.tunjid.treenav.Node -import com.tunjid.treenav.adaptive.AdaptivePaneScope -import com.tunjid.treenav.adaptive.SlotBasedAdaptiveNavigationState +import com.tunjid.treenav.compose.PaneScope +import com.tunjid.treenav.compose.SlotBasedPanedNavigationState @Composable internal fun rememberDestinationLifecycleOwner( @@ -41,10 +41,10 @@ internal class DestinationLifecycleOwner( fun update( hostLifecycleState: State, - adaptivePaneScope: AdaptivePaneScope<*, *>, - adaptiveNavigationState: SlotBasedAdaptiveNavigationState<*, *>, + paneScope: PaneScope<*, *>, + adaptiveNavigationState: SlotBasedPanedNavigationState<*, *>, ) { - val active = adaptivePaneScope.isActive + val active = paneScope.isActive val exists = adaptiveNavigationState.backStackIds.contains( destination.id ) diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/lifecycle/DestinationViewModelStoreCreator.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/lifecycle/DestinationViewModelStoreCreator.kt similarity index 97% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/lifecycle/DestinationViewModelStoreCreator.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/lifecycle/DestinationViewModelStoreCreator.kt index 596073a..b18205f 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/lifecycle/DestinationViewModelStoreCreator.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/lifecycle/DestinationViewModelStoreCreator.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.tunjid.treenav.adaptive.lifecycle +package com.tunjid.treenav.compose.lifecycle import androidx.compose.runtime.Stable import androidx.lifecycle.ViewModelStore diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/moveablesharedelement/BoundsTransformDeferredAnimation.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/moveablesharedelement/BoundsTransformDeferredAnimation.kt similarity index 99% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/moveablesharedelement/BoundsTransformDeferredAnimation.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/moveablesharedelement/BoundsTransformDeferredAnimation.kt index 0902923..09b1c8e 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/moveablesharedelement/BoundsTransformDeferredAnimation.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/moveablesharedelement/BoundsTransformDeferredAnimation.kt @@ -1,4 +1,4 @@ -package com.tunjid.scaffold.treenav.adaptive.moveablesharedelement +package com.tunjid.treenav.compose.moveablesharedelement import androidx.compose.animation.BoundsTransform diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/moveablesharedelement/MovableSharedElementState.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/moveablesharedelement/MovableSharedElementState.kt similarity index 95% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/moveablesharedelement/MovableSharedElementState.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/moveablesharedelement/MovableSharedElementState.kt index 65a8aa3..9e97c2b 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/moveablesharedelement/MovableSharedElementState.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/moveablesharedelement/MovableSharedElementState.kt @@ -34,20 +34,21 @@ import androidx.compose.ui.unit.toOffset import androidx.compose.ui.unit.toSize import androidx.compose.ui.util.fastRoundToInt import com.tunjid.treenav.Node -import com.tunjid.treenav.adaptive.AdaptivePaneScope -import com.tunjid.treenav.adaptive.AdaptivePaneState +import com.tunjid.treenav.compose.PaneScope +import com.tunjid.treenav.compose.PaneState +import com.tunjid.treenav.compose.moveablesharedelement.BoundsTransformDeferredAnimation import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.first @Stable @OptIn(ExperimentalSharedTransitionApi::class) internal class MovableSharedElementState( - paneScope: AdaptivePaneScope, + paneScope: PaneScope, sharedTransitionScope: SharedTransitionScope, sharedElement: @Composable (State, Modifier) -> Unit, onRemoved: () -> Unit, private val boundsTransform: BoundsTransform, - private val canAnimateOnStartingFrames: AdaptivePaneState.() -> Boolean + private val canAnimateOnStartingFrames: PaneState.() -> Boolean ) : SharedElementOverlay, SharedTransitionScope by sharedTransitionScope { var paneScope by mutableStateOf(paneScope) @@ -97,7 +98,7 @@ internal class MovableSharedElementState( } private fun updatePaneStateSeen( - paneState: AdaptivePaneState<*, *> + paneState: PaneState<*, *> ) { panesKeysToSeenCount[paneState.key] = Unit } @@ -231,4 +232,4 @@ internal class MovableSharedElementState( } } -private val AdaptivePaneState<*, *>.key get() = "${currentDestination?.id}-$pane" +private val PaneState<*, *>.key get() = "${currentDestination?.id}-$pane" diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/moveablesharedelement/MovableSharedElements.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/moveablesharedelement/MovableSharedElements.kt similarity index 89% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/moveablesharedelement/MovableSharedElements.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/moveablesharedelement/MovableSharedElements.kt index 7d2b5ff..8ca6fc3 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/moveablesharedelement/MovableSharedElements.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/moveablesharedelement/MovableSharedElements.kt @@ -17,16 +17,16 @@ import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.drawscope.ContentDrawScope import com.tunjid.treenav.Node -import com.tunjid.treenav.adaptive.AdaptiveNavHost -import com.tunjid.treenav.adaptive.AdaptivePaneScope -import com.tunjid.treenav.adaptive.AdaptivePaneState +import com.tunjid.treenav.compose.PanedNavHost +import com.tunjid.treenav.compose.PaneScope +import com.tunjid.treenav.compose.PaneState internal interface SharedElementOverlay { fun ContentDrawScope.drawInOverlay() } /** - * Creates movable shared elements that may be shared amongst different [AdaptivePaneScope] + * Creates movable shared elements that may be shared amongst different [PaneScope] * instances. */ interface MovableSharedElementScope { @@ -50,13 +50,13 @@ interface MovableSharedElementScope { } /** - * State for managing movable shared elements within a single [AdaptiveNavHost]. + * State for managing movable shared elements within a single [PanedNavHost]. */ @OptIn(ExperimentalSharedTransitionApi::class) @Stable class MovableSharedElementHostState( private val sharedTransitionScope: SharedTransitionScope, - private val canAnimateOnStartingFrames: (AdaptivePaneState) -> Boolean, + private val canAnimateOnStartingFrames: (PaneState) -> Boolean, ) { // TODO: This should be unnecessary. Figure out a way to participate arbitrarily in the @@ -86,11 +86,11 @@ class MovableSharedElementHostState( keysToMovableSharedElements.contains(key) /** - * Provides a movable shared element that can be rendered in a given [AdaptivePaneScope]. + * Provides a movable shared element that can be rendered in a given [PaneScope]. * It is the callers responsibility to perform other verifications on the ability - * of the calling [AdaptivePaneScope] to render the movable shared element. + * of the calling [PaneScope] to render the movable shared element. */ - fun AdaptivePaneScope.createOrUpdateSharedElement( + fun PaneScope.createOrUpdateSharedElement( key: Any, boundsTransform: BoundsTransform, sharedElement: @Composable (S, Modifier) -> Unit, @@ -113,14 +113,14 @@ class MovableSharedElementHostState( /** * An implementation of [MovableSharedElementScope] that ensures shared elements are only rendered - * in an [AdaptivePaneScope] when it is active. + * in an [PaneScope] when it is active. * * Other implementations of [MovableSharedElementScope] may delegate to this for their own * movable shared element implementations. */ @Stable internal class AdaptiveMovableSharedElementScope( - paneScope: AdaptivePaneScope, + paneScope: PaneScope, private val movableSharedElementHostState: MovableSharedElementHostState, ) : MovableSharedElementScope { diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/threepane/ThreePane.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/threepane/ThreePane.kt new file mode 100644 index 0000000..28349c4 --- /dev/null +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/threepane/ThreePane.kt @@ -0,0 +1,152 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.tunjid.treenav.compose.threepane + +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.core.FiniteAnimationSpec +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.runtime.Composable +import com.tunjid.treenav.Node +import com.tunjid.treenav.compose.Adaptation.Swap +import com.tunjid.treenav.compose.PaneScope +import com.tunjid.treenav.compose.PaneStrategy +import com.tunjid.treenav.compose.paneStrategy + +/** + * A [PaneStrategy] for apps that display up to 3 major panes as once. + * It also provides extra panes for transient content. + */ +enum class ThreePane { + /** + * The pane for the foremost content the user is interacting with. This is typically the + * actual navigation destination. + */ + Primary, + + /** + * A optional pane for placing content from the [Primary] pane, if a preview of the previous + * navigation destinations is occurring. The primary content is rendered here, while + * the previous primary content is rendered in the [Primary] pane. + */ + TransientPrimary, + + /** + * An optional pane for displaying a navigation destination alongside the [Primary] pane. + * This is useful for list-detail, or supporting panels flows. + */ + Secondary, + + /** + * An optional pane to display an navigation destination alongside both the [Primary] and + * [Secondary] panes. This is typically used when the screen is large enough to display 3 + * panes simultaneously. + */ + Tertiary, + + /** + * An optional pane for showing dialogs, or context sheets over existing panes. + */ + Overlay; + + companion object { + val PrimaryToSecondary = Swap( + from = Primary, + to = Secondary + ) + + val SecondaryToPrimary = Swap( + from = Secondary, + to = Primary + ) + + val PrimaryToTransient = Swap( + from = Primary, + to = TransientPrimary + ) + } +} + +/** + * A strategy for selectively running animations in list detail flows. When: + * - A navigation destination moves between the [ThreePane.Primary] and [ThreePane.Secondary] + * panes, the pane animations are not run to provide a seamless movement experience. + * - A navigation destination moves between the [ThreePane.Primary] and + * [ThreePane.TransientPrimary] panes, the pane animations are not run. + * + * @param enterTransition the transition to run for the entering pane when permitted. + * @param exitTransition the transition to run for the exiting pane when permitted. + * @param paneMapping the mapping of panes to navigation destinations. + * @param render the Composable for rendering the current destination. + */ +fun threePaneListDetailStrategy( + enterTransition: PaneScope.() -> EnterTransition = { DefaultFadeIn }, + exitTransition: PaneScope.() -> ExitTransition = { DefaultFadeOut }, + paneMapping: @Composable (R) -> Map = { + mapOf(ThreePane.Primary to it) + }, + render: @Composable PaneScope.(R) -> Unit +): PaneStrategy = paneStrategy( + paneMapping = paneMapping, + transitions = { + val state = paneState + when (state.pane) { + ThreePane.Primary, + ThreePane.Secondary -> when { + ThreePane.PrimaryToSecondary in state.adaptations + || ThreePane.SecondaryToPrimary in state.adaptations + -> NoTransition + + else -> PaneScope.Transitions( + enter = enterTransition(), + exit = exitTransition() + ) + } + + ThreePane.TransientPrimary -> when { + ThreePane.PrimaryToTransient in state.adaptations -> NoTransition + + else -> PaneScope.Transitions( + enter = enterTransition(), + exit = exitTransition() + ) + } + + else -> NoTransition + } + }, + render = render +) + +private val RouteTransitionAnimationSpec: FiniteAnimationSpec = tween( + durationMillis = 700 +) + +private val DefaultFadeIn = fadeIn( + animationSpec = RouteTransitionAnimationSpec, +) + +private val DefaultFadeOut = fadeOut( + animationSpec = RouteTransitionAnimationSpec, +) + +private val NoTransition = PaneScope.Transitions( + enter = EnterTransition.None, + exit = ExitTransition.None, +) \ No newline at end of file diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/threepane/configurations/MovableSharedElementConfiguration.kt similarity index 77% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/threepane/configurations/MovableSharedElementConfiguration.kt index 9dcc9fe..ad63802 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/MovableSharedElementConfiguration.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/threepane/configurations/MovableSharedElementConfiguration.kt @@ -1,4 +1,4 @@ -package com.tunjid.treenav.adaptive.threepane.configurations +package com.tunjid.treenav.compose.threepane.configurations import androidx.compose.animation.BoundsTransform import androidx.compose.animation.ExperimentalSharedTransitionApi @@ -11,28 +11,33 @@ import com.tunjid.scaffold.treenav.adaptive.moveablesharedelement.AdaptiveMovabl import com.tunjid.scaffold.treenav.adaptive.moveablesharedelement.MovableSharedElementHostState import com.tunjid.scaffold.treenav.adaptive.moveablesharedelement.MovableSharedElementScope import com.tunjid.treenav.Node -import com.tunjid.treenav.adaptive.AdaptiveNavHost -import com.tunjid.treenav.adaptive.AdaptiveNavHostConfiguration -import com.tunjid.treenav.adaptive.AdaptivePaneScope -import com.tunjid.treenav.adaptive.AdaptivePaneState -import com.tunjid.treenav.adaptive.AdaptivePaneStrategy -import com.tunjid.treenav.adaptive.delegated -import com.tunjid.treenav.adaptive.threepane.ThreePane +import com.tunjid.treenav.compose.PanedNavHost +import com.tunjid.treenav.compose.PanedNavHostConfiguration +import com.tunjid.treenav.compose.PaneScope +import com.tunjid.treenav.compose.PaneState +import com.tunjid.treenav.compose.PaneStrategy +import com.tunjid.treenav.compose.delegated +import com.tunjid.treenav.compose.threepane.ThreePane /** - * An [AdaptiveNavHostConfiguration] that applies semantics of movable shared elements to + * An [PanedNavHostConfiguration] that applies semantics of movable shared elements to * [ThreePane] layouts. * * @param movableSharedElementHostState the host state for coordinating movable shared elements. - * There should be one instance of this per [AdaptiveNavHost]. + * There should be one instance of this per [PanedNavHost]. */ -fun AdaptiveNavHostConfiguration.movableSharedElementConfiguration( +fun PanedNavHostConfiguration< + ThreePane, + NavigationState, + Destination + >.threePanedMovableSharedElementConfiguration( movableSharedElementHostState: MovableSharedElementHostState, -): AdaptiveNavHostConfiguration = +): PanedNavHostConfiguration = delegated { destination -> - val originalStrategy = this@movableSharedElementConfiguration.strategyTransform(destination) - AdaptivePaneStrategy( + val originalStrategy = + this@threePanedMovableSharedElementConfiguration.strategyTransform(destination) + PaneStrategy( transitions = originalStrategy.transitions, paneMapper = originalStrategy.paneMapper, render = { paneDestination -> @@ -56,7 +61,7 @@ fun AdaptiveNavHostConfiguration AdaptivePaneScope.movableSharedElementScope(): MovableSharedElementScope { +fun PaneScope.movableSharedElementScope(): MovableSharedElementScope { check(this is ThreePaneMovableSharedElementScope) { """ The current AdaptivePaneScope (${this::class.qualifiedName}) is not an instance of @@ -73,7 +78,7 @@ private class ThreePaneMovableSharedElementScope( private val hostState: MovableSharedElementHostState, private val delegate: AdaptiveMovableSharedElementScope, ) : MovableSharedElementScope, - AdaptivePaneScope by delegate.paneScope { + PaneScope by delegate.paneScope { @OptIn(ExperimentalSharedTransitionApi::class) override fun movableSharedElementOf( key: Any, @@ -115,9 +120,9 @@ private class ThreePaneMovableSharedElementScope( } } -fun AdaptivePaneState?.canAnimateOnStartingFrames() = +fun PaneState?.canAnimateOnStartingFrames() = this?.pane != ThreePane.TransientPrimary -private val AdaptivePaneScope.isPreviewingBack: Boolean +private val PaneScope.isPreviewingBack: Boolean get() = paneState.pane == ThreePane.Primary && paneState.adaptations.contains(ThreePane.PrimaryToTransient) \ No newline at end of file diff --git a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/ThreePaneAdaptiveConfiguration.kt b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/threepane/configurations/ThreePaneAdaptiveConfiguration.kt similarity index 70% rename from library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/ThreePaneAdaptiveConfiguration.kt rename to library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/threepane/configurations/ThreePaneAdaptiveConfiguration.kt index 0a7e883..4960c44 100644 --- a/library/compose/src/commonMain/kotlin/com/tunjid/treenav/adaptive/threepane/configurations/ThreePaneAdaptiveConfiguration.kt +++ b/library/compose/src/commonMain/kotlin/com/tunjid/treenav/compose/threepane/configurations/ThreePaneAdaptiveConfiguration.kt @@ -1,27 +1,31 @@ -package com.tunjid.treenav.adaptive.threepane.configurations +package com.tunjid.treenav.compose.threepane.configurations import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import com.tunjid.treenav.Node -import com.tunjid.treenav.adaptive.AdaptiveNavHostConfiguration -import com.tunjid.treenav.adaptive.adaptivePaneStrategy -import com.tunjid.treenav.adaptive.delegated -import com.tunjid.treenav.adaptive.threepane.ThreePane +import com.tunjid.treenav.compose.PanedNavHostConfiguration +import com.tunjid.treenav.compose.paneStrategy +import com.tunjid.treenav.compose.delegated +import com.tunjid.treenav.compose.threepane.ThreePane /** - * An [AdaptiveNavHostConfiguration] that selectively displays panes for a [ThreePane] layout + * An [PanedNavHostConfiguration] that selectively displays panes for a [ThreePane] layout * based on the space available determined by the [windowWidthDpState]. * * @param windowWidthDpState provides the current width of the display in Dp. */ -fun AdaptiveNavHostConfiguration.threePaneAdaptiveConfiguration( +fun PanedNavHostConfiguration< + ThreePane, + NavigationState, + Destination + >.threePanedNavHostConfiguration( windowWidthDpState: State, secondaryPaneBreakPoint: State = mutableStateOf(SECONDARY_PANE_MIN_WIDTH_BREAKPOINT_DP), tertiaryPaneBreakPoint: State = mutableStateOf(TERTIARY_PANE_MIN_WIDTH_BREAKPOINT_DP), -): AdaptiveNavHostConfiguration = delegated { node -> - val originalStrategy = this@threePaneAdaptiveConfiguration.strategyTransform(node) - adaptivePaneStrategy( +): PanedNavHostConfiguration = delegated { node -> + val originalStrategy = this@threePanedNavHostConfiguration.strategyTransform(node) + paneStrategy( render = originalStrategy.render, transitions = originalStrategy.transitions, paneMapping = { inner -> diff --git a/library/compose/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt b/library/compose/src/commonTest/kotlin/com/tunjid/treenav/compose/SlotBasedAdaptiveNavigationStateTest.kt similarity index 98% rename from library/compose/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt rename to library/compose/src/commonTest/kotlin/com/tunjid/treenav/compose/SlotBasedAdaptiveNavigationStateTest.kt index d395002..f2236f7 100644 --- a/library/compose/src/commonTest/kotlin/com/tunjid/treenav/adaptive/SlotBasedAdaptiveNavigationStateTest.kt +++ b/library/compose/src/commonTest/kotlin/com/tunjid/treenav/compose/SlotBasedAdaptiveNavigationStateTest.kt @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.tunjid.treenav.adaptive +package com.tunjid.treenav.compose import com.tunjid.treenav.Node -import com.tunjid.treenav.adaptive.threepane.ThreePane +import com.tunjid.treenav.compose.threepane.ThreePane import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -44,7 +44,7 @@ data class TestNode(val name: String) : Node { class SlotBasedAdaptiveNavigationStateTest { - private lateinit var subject: SlotBasedAdaptiveNavigationState + private lateinit var subject: SlotBasedPanedNavigationState private lateinit var panes: List private lateinit var slots: Set @@ -53,7 +53,7 @@ class SlotBasedAdaptiveNavigationStateTest { fun setup() { panes = ThreePane.entries.toList() slots = List(size = panes.size, init = ::Slot).toSet() - subject = SlotBasedAdaptiveNavigationState.initial( + subject = SlotBasedPanedNavigationState.initial( slots = slots ) } @@ -531,7 +531,7 @@ class SlotBasedAdaptiveNavigationStateTest { } } - private fun SlotBasedAdaptiveNavigationState.testAdaptTo( + private fun SlotBasedPanedNavigationState.testAdaptTo( panesToNodes: Map ) = adaptTo( slots = slots, diff --git a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/DemoApp.kt b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/DemoApp.kt index d367e66..4b2e0e0 100644 --- a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/DemoApp.kt +++ b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/DemoApp.kt @@ -39,24 +39,24 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity -import com.tunjid.demo.common.ui.SampleAppState.Companion.rememberAdaptiveNavHostState -import com.tunjid.demo.common.ui.chat.chatAdaptiveConfiguration -import com.tunjid.demo.common.ui.chatrooms.chatRoomPaneConfiguration +import com.tunjid.demo.common.ui.SampleAppState.Companion.rememberPanedNavHostState +import com.tunjid.demo.common.ui.chat.chatPaneStrategy +import com.tunjid.demo.common.ui.chatrooms.chatRoomPaneStrategy import com.tunjid.demo.common.ui.data.NavigationRepository import com.tunjid.demo.common.ui.data.SampleDestination -import com.tunjid.demo.common.ui.profile.profileAdaptiveConfiguration -import com.tunjid.demo.common.ui.settings.settingsPaneConfiguration +import com.tunjid.demo.common.ui.profile.profilePaneStrategy +import com.tunjid.demo.common.ui.me.mePaneStrategy import com.tunjid.scaffold.treenav.adaptive.moveablesharedelement.MovableSharedElementHostState import com.tunjid.treenav.MultiStackNav -import com.tunjid.treenav.adaptive.AdaptiveNavHost -import com.tunjid.treenav.adaptive.AdaptiveNavHostConfiguration -import com.tunjid.treenav.adaptive.AdaptivePaneState -import com.tunjid.treenav.adaptive.SavedStateAdaptiveNavHostState -import com.tunjid.treenav.adaptive.adaptiveNavHostConfiguration -import com.tunjid.treenav.adaptive.threepane.ThreePane -import com.tunjid.treenav.adaptive.threepane.configurations.canAnimateOnStartingFrames -import com.tunjid.treenav.adaptive.threepane.configurations.movableSharedElementConfiguration -import com.tunjid.treenav.adaptive.threepane.configurations.threePaneAdaptiveConfiguration +import com.tunjid.treenav.compose.PanedNavHost +import com.tunjid.treenav.compose.PanedNavHostConfiguration +import com.tunjid.treenav.compose.PaneState +import com.tunjid.treenav.compose.SavedStatePanedNavHostState +import com.tunjid.treenav.compose.panedNavHostConfiguration +import com.tunjid.treenav.compose.threepane.ThreePane +import com.tunjid.treenav.compose.threepane.configurations.canAnimateOnStartingFrames +import com.tunjid.treenav.compose.threepane.configurations.threePanedMovableSharedElementConfiguration +import com.tunjid.treenav.compose.threepane.configurations.threePanedNavHostConfiguration import com.tunjid.treenav.current import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -75,7 +75,7 @@ fun SampleApp( icon = { Icon(it.icon, contentDescription = it.title) }, label = { Text(it.title) }, selected = it == appState.currentNavigation.current, - onClick = { } + onClick = { appState.setTab(it) } ) } } @@ -86,16 +86,16 @@ fun SampleApp( val movableSharedElementHostState = remember { MovableSharedElementHostState( sharedTransitionScope = this, - canAnimateOnStartingFrames = AdaptivePaneState::canAnimateOnStartingFrames + canAnimateOnStartingFrames = PaneState::canAnimateOnStartingFrames ) } - AdaptiveNavHost( - state = appState.rememberAdaptiveNavHostState { + PanedNavHost( + state = appState.rememberPanedNavHostState { this - .threePaneAdaptiveConfiguration( + .threePanedNavHostConfiguration( windowWidthDpState = windowWidthDp ) - .movableSharedElementConfiguration( + .threePanedMovableSharedElementConfiguration( movableSharedElementHostState = movableSharedElementHostState ) }, @@ -144,21 +144,27 @@ class SampleAppState( ) val currentNavigation by navigationState - private val adaptiveNavHostConfiguration = sampleAppAdaptiveConfiguration( + private val adaptiveNavHostConfiguration = sampleAppNavHostConfiguration( navigationState ) + fun setTab(destination: SampleDestination.NavTabs) { + navigationRepository.navigate { + it.copy(currentIndex = destination.ordinal) + } + } + companion object { @Composable - fun SampleAppState.rememberAdaptiveNavHostState( - configurationBlock: AdaptiveNavHostConfiguration< + fun SampleAppState.rememberPanedNavHostState( + configurationBlock: PanedNavHostConfiguration< ThreePane, MultiStackNav, SampleDestination - >.() -> AdaptiveNavHostConfiguration - ): SavedStateAdaptiveNavHostState { + >.() -> PanedNavHostConfiguration + ): SavedStatePanedNavHostState { val adaptiveNavHostState = remember { - SavedStateAdaptiveNavHostState( + SavedStatePanedNavHostState( panes = ThreePane.entries.toList(), configuration = adaptiveNavHostConfiguration.configurationBlock(), ) @@ -176,9 +182,9 @@ class SampleAppState( } } -private fun sampleAppAdaptiveConfiguration( +private fun sampleAppNavHostConfiguration( multiStackNavState: State -) = adaptiveNavHostConfiguration( +) = panedNavHostConfiguration( navigationState = multiStackNavState, destinationTransform = { multiStackNav -> multiStackNav.current as? SampleDestination ?: throw IllegalArgumentException( @@ -187,13 +193,13 @@ private fun sampleAppAdaptiveConfiguration( }, strategyTransform = { destination -> when (destination) { - SampleDestination.NavTabs.ChatRooms -> chatRoomPaneConfiguration() + SampleDestination.NavTabs.ChatRooms -> chatRoomPaneStrategy() - is SampleDestination.Chat -> chatAdaptiveConfiguration(destination) + SampleDestination.NavTabs.Me -> mePaneStrategy() - SampleDestination.NavTabs.Settings -> settingsPaneConfiguration() + is SampleDestination.Chat -> chatPaneStrategy() - is SampleDestination.Profile -> profileAdaptiveConfiguration(destination) + is SampleDestination.Profile -> profilePaneStrategy() } } ) diff --git a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chat/Configuration.kt b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chat/Strategy.kt similarity index 81% rename from sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chat/Configuration.kt rename to sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chat/Strategy.kt index 98455ad..74ea0a2 100644 --- a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chat/Configuration.kt +++ b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chat/Strategy.kt @@ -25,14 +25,19 @@ import com.tunjid.demo.common.ui.data.ChatsRepository import com.tunjid.demo.common.ui.data.ProfileRepository import com.tunjid.demo.common.ui.data.SampleDestination import com.tunjid.demo.common.ui.data.SampleDestination.NavTabs -import com.tunjid.treenav.adaptive.threepane.ThreePane -import com.tunjid.treenav.adaptive.threepane.configurations.movableSharedElementScope -import com.tunjid.treenav.adaptive.threepane.threePaneAdaptiveNodeConfiguration +import com.tunjid.treenav.compose.threepane.ThreePane +import com.tunjid.treenav.compose.threepane.configurations.movableSharedElementScope +import com.tunjid.treenav.compose.threepane.threePaneListDetailStrategy -fun chatAdaptiveConfiguration( - destination: SampleDestination.Chat -) = threePaneAdaptiveNodeConfiguration( - render = { +fun chatPaneStrategy() = threePaneListDetailStrategy( + paneMapping = { destination -> + mapOf( + ThreePane.Primary to destination, + ThreePane.Secondary to NavTabs.ChatRooms, + ) + }, + render = { destination -> + check(destination is SampleDestination.Chat) val scope = LocalLifecycleOwner.current.lifecycle.coroutineScope val viewModel = viewModel { ChatViewModel( @@ -55,10 +60,4 @@ fun chatAdaptiveConfiguration( ) } }, - paneMapping = { - mapOf( - ThreePane.Primary to it, - ThreePane.Secondary to NavTabs.ChatRooms, - ) - } ) diff --git a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chatrooms/Configuration.kt b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chatrooms/Strategy.kt similarity index 89% rename from sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chatrooms/Configuration.kt rename to sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chatrooms/Strategy.kt index 0dbfdb2..bc4aa7f 100644 --- a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chatrooms/Configuration.kt +++ b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/chatrooms/Strategy.kt @@ -22,10 +22,10 @@ import androidx.lifecycle.coroutineScope import androidx.lifecycle.viewmodel.compose.viewModel import com.tunjid.demo.common.ui.data.ChatsRepository import com.tunjid.demo.common.ui.data.SampleDestination -import com.tunjid.treenav.adaptive.threepane.threePaneAdaptiveNodeConfiguration +import com.tunjid.treenav.compose.threepane.threePaneListDetailStrategy -fun chatRoomPaneConfiguration( -) = threePaneAdaptiveNodeConfiguration( +fun chatRoomPaneStrategy( +) = threePaneListDetailStrategy( render = { val scope = LocalLifecycleOwner.current.lifecycle.coroutineScope val viewModel = viewModel { diff --git a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/data/NavigationRepository.kt b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/data/NavigationRepository.kt index 6cc1e51..1e29e27 100644 --- a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/data/NavigationRepository.kt +++ b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/data/NavigationRepository.kt @@ -18,6 +18,7 @@ package com.tunjid.demo.common.ui.data import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.List +import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Settings import com.tunjid.mutator.Mutation import com.tunjid.mutator.coroutines.mapToManyMutations @@ -36,14 +37,14 @@ sealed interface SampleDestination : Node { val title: String, ) : SampleDestination { ChatRooms("Chat Rooms"), - Settings("Settings"); + Me("Me"); override val id: String get() = title val icon get() = when (this) { ChatRooms -> Icons.AutoMirrored.Filled.List - Settings -> Icons.Default.Settings + Me -> Icons.Default.Person } } @@ -107,6 +108,12 @@ private val InitialNavState = MultiStackNav( children = listOf( SampleDestination.NavTabs.ChatRooms, ) - ) + ), + StackNav( + name = "me", + children = listOf( + SampleDestination.NavTabs.Me, + ) + ), ) ) \ No newline at end of file diff --git a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/me/Strategy.kt b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/me/Strategy.kt new file mode 100644 index 0000000..fed9a3f --- /dev/null +++ b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/me/Strategy.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.tunjid.demo.common.ui.me + +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.coroutineScope +import androidx.lifecycle.viewmodel.compose.viewModel +import com.tunjid.demo.common.ui.data.SampleDestination +import com.tunjid.demo.common.ui.profile.ProfileScreen +import com.tunjid.demo.common.ui.profile.ProfileViewModel +import com.tunjid.treenav.compose.threepane.configurations.movableSharedElementScope +import com.tunjid.treenav.compose.threepane.threePaneListDetailStrategy + +fun mePaneStrategy( +) = threePaneListDetailStrategy( + render = { + val scope = LocalLifecycleOwner.current.lifecycle.coroutineScope + val viewModel = viewModel { + ProfileViewModel( + coroutineScope = scope, + profileName = null, + ) + } + ProfileScreen( + movableSharedElementScope = movableSharedElementScope(), + state = viewModel.state.collectAsStateWithLifecycle().value, + onAction = viewModel.accept + ) + } +) \ No newline at end of file diff --git a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/ProfileScreen.kt b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/ProfileScreen.kt index 19ff986..61a91c3 100644 --- a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/ProfileScreen.kt +++ b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/ProfileScreen.kt @@ -20,9 +20,9 @@ import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -30,6 +30,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight @@ -57,8 +58,10 @@ fun ProfileScreen( ProfileHeader( state = state, movableSharedElementScope = movableSharedElementScope, - onBackPressed = { - onAction(Action.Navigation.Pop) + onBackPressed = remember(state.profileName) { + if (state.profileName != null) return@remember { + onAction(Action.Navigation.Pop) + } else null }, modifier = Modifier .fillMaxWidth() @@ -82,20 +85,18 @@ private fun ProfileHeader( state: State, movableSharedElementScope: MovableSharedElementScope, modifier: Modifier = Modifier, - onBackPressed: () -> Unit + onBackPressed: (() -> Unit)?, ) { - Box { - Box( + Box( + modifier = Modifier.heightIn(min = 400.dp) + ) { + ProfilePhoto( + state = state, + movableSharedElementScope = movableSharedElementScope, modifier = modifier - ) { - ProfilePhoto( - state = state, - movableSharedElementScope = movableSharedElementScope, - modifier = Modifier.fillMaxSize() - ) - } + ) SampleTopAppBar( - title = "Profile", + title = if (state.profileName == null) "Me" else "Profile", onBackPressed = onBackPressed, ) } diff --git a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/ProfileViewModel.kt b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/ProfileViewModel.kt index b7345d1..2a6788a 100644 --- a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/ProfileViewModel.kt +++ b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/ProfileViewModel.kt @@ -22,7 +22,6 @@ import com.tunjid.demo.common.ui.data.NavigationAction import com.tunjid.demo.common.ui.data.NavigationRepository import com.tunjid.demo.common.ui.data.Profile import com.tunjid.demo.common.ui.data.ProfileRepository -import com.tunjid.demo.common.ui.data.SampleDestination import com.tunjid.demo.common.ui.data.navigationAction import com.tunjid.demo.common.ui.data.navigationMutations import com.tunjid.mutator.Mutation @@ -37,14 +36,14 @@ class ProfileViewModel( coroutineScope: LifecycleCoroutineScope, profileRepository: ProfileRepository = ProfileRepository, navigationRepository: NavigationRepository = NavigationRepository, - destination: SampleDestination.Profile, + profileName: String?, ) : ViewModel() { private val mutator = coroutineScope.actionStateFlowMutator( initialState = State( - profileName = destination.profileName + profileName = profileName ), inputs = listOf( - profileRepository.profileMutations(destination) + profileRepository.profileMutations(profileName) ), actionTransform = { actions -> actions.toMutationStream( @@ -65,9 +64,10 @@ class ProfileViewModel( } private fun ProfileRepository.profileMutations( - destination: SampleDestination.Profile, + profileName: String?, ): Flow> = - profileFor(destination.profileName).mapToMutation { copy(profile = it) } + (profileName?.let(::profileFor) ?: me) + .mapToMutation { copy(profile = it) } data class State( val profileName: String? = null, diff --git a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/Configuration.kt b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/Strategy.kt similarity index 75% rename from sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/Configuration.kt rename to sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/Strategy.kt index c69500d..1780f64 100644 --- a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/Configuration.kt +++ b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/profile/Strategy.kt @@ -22,19 +22,26 @@ import androidx.lifecycle.coroutineScope import androidx.lifecycle.viewmodel.compose.viewModel import com.tunjid.demo.common.ui.data.SampleDestination import com.tunjid.demo.common.ui.data.SampleDestination.NavTabs -import com.tunjid.treenav.adaptive.threepane.ThreePane -import com.tunjid.treenav.adaptive.threepane.configurations.movableSharedElementScope -import com.tunjid.treenav.adaptive.threepane.threePaneAdaptiveNodeConfiguration +import com.tunjid.treenav.compose.threepane.ThreePane +import com.tunjid.treenav.compose.threepane.configurations.movableSharedElementScope +import com.tunjid.treenav.compose.threepane.threePaneListDetailStrategy -fun profileAdaptiveConfiguration( - destination: SampleDestination.Profile -) = threePaneAdaptiveNodeConfiguration( - render = { +fun profilePaneStrategy() = threePaneListDetailStrategy( + paneMapping = { destination -> + check(destination is SampleDestination.Profile) + mapOf( + ThreePane.Primary to destination, + ThreePane.Secondary to destination.roomName?.let(SampleDestination::Chat), + ThreePane.Tertiary to destination.roomName?.let { NavTabs.ChatRooms }, + ) + }, + render = { destination -> + check(destination is SampleDestination.Profile) val scope = LocalLifecycleOwner.current.lifecycle.coroutineScope val viewModel = viewModel { ProfileViewModel( coroutineScope = scope, - destination = destination, + profileName = destination.profileName, ) } ProfileScreen( @@ -43,11 +50,4 @@ fun profileAdaptiveConfiguration( onAction = viewModel.accept ) }, - paneMapping = { - mapOf( - ThreePane.Primary to destination, - ThreePane.Secondary to destination.roomName?.let(SampleDestination::Chat), - ThreePane.Tertiary to destination.roomName?.let { NavTabs.ChatRooms }, - ) - } ) \ No newline at end of file diff --git a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/settings/Configuration.kt b/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/settings/Configuration.kt deleted file mode 100644 index 8ecee66..0000000 --- a/sample/common/src/commonMain/kotlin/com/tunjid/demo/common/ui/settings/Configuration.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.tunjid.demo.common.ui.settings - -import com.tunjid.demo.common.ui.data.SampleDestination -import com.tunjid.treenav.adaptive.threepane.threePaneAdaptiveNodeConfiguration - -fun settingsPaneConfiguration( -) = threePaneAdaptiveNodeConfiguration( - render = { } -) \ No newline at end of file