1 /*
<lambda>null2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.compose.material3.adaptive.navigation
18 
19 import androidx.annotation.FloatRange
20 import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
21 import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
22 import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
23 import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldDefaults
24 import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
25 import androidx.compose.material3.adaptive.layout.MutableThreePaneScaffoldState
26 import androidx.compose.material3.adaptive.layout.PaneScaffoldDirective
27 import androidx.compose.material3.adaptive.layout.SupportingPaneScaffold
28 import androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldDefaults
29 import androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldRole
30 import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies
31 import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem
32 import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole
33 import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState
34 import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue
35 import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective
36 import androidx.compose.material3.adaptive.layout.calculateThreePaneScaffoldValue
37 import androidx.compose.runtime.Composable
38 import androidx.compose.runtime.Stable
39 import androidx.compose.runtime.derivedStateOf
40 import androidx.compose.runtime.getValue
41 import androidx.compose.runtime.mutableStateListOf
42 import androidx.compose.runtime.mutableStateOf
43 import androidx.compose.runtime.saveable.Saver
44 import androidx.compose.runtime.saveable.listSaver
45 import androidx.compose.runtime.saveable.rememberSaveable
46 import androidx.compose.runtime.setValue
47 import androidx.compose.ui.util.fastMap
48 import kotlin.collections.removeLast as removeLastKt
49 
50 /**
51  * The common interface of the default navigation implementations for different three-pane
52  * scaffolds.
53  *
54  * In general, we suggest you to use [rememberListDetailPaneScaffoldNavigator] or
55  * [rememberSupportingPaneScaffoldNavigator] to get remembered default instances of this interface
56  * for [ListDetailPaneScaffold] and [SupportingPaneScaffold], respectively. Those default
57  * implementations work independently from any navigation frameworks.
58  *
59  * If you need to integrate with existing navigation frameworks or implement your own custom
60  * navigation logic, usually creating whole new APIs that's tailored for your own solution will be
61  * recommended, instead of implementing this interface. But we recommend you refer to the API design
62  * and the default implementation to get better understanding and address the intricacies of
63  * navigation in an adaptive scenario.
64  *
65  * @param T the type representing the content key/id for a navigation destination. This type must be
66  *   storable in a Bundle. Used to customize navigation behavior (for example,
67  *   [BackNavigationBehavior]). If this customization is unneeded, you can pass [Any].
68  */
69 @ExperimentalMaterial3AdaptiveApi
70 @Stable
71 interface ThreePaneScaffoldNavigator<T> {
72     /**
73      * The current layout directives that the associated three pane scaffold needs to follow. It's
74      * supposed to be automatically updated when the window configuration changes.
75      */
76     val scaffoldDirective: PaneScaffoldDirective
77 
78     /**
79      * The current state of the associated three pane scaffold, used to query the transition between
80      * layout states.
81      */
82     val scaffoldState: ThreePaneScaffoldState
83 
84     /**
85      * The current layout value of the associated three pane scaffold, which represents unique
86      * layout states of the scaffold.
87      */
88     val scaffoldValue: ThreePaneScaffoldValue
89 
90     /**
91      * Returns the scaffold value associated with the previous destination, assuming there is a
92      * previous destination to navigate back to. If not, this is the same as [scaffoldValue].
93      *
94      * @param backNavigationBehavior the behavior describing which backstack entries may be skipped
95      *   during the back navigation. See [BackNavigationBehavior].
96      */
97     fun peekPreviousScaffoldValue(
98         backNavigationBehavior: BackNavigationBehavior =
99             BackNavigationBehavior.PopUntilScaffoldValueChange
100     ): ThreePaneScaffoldValue
101 
102     /**
103      * The current destination as tracked by the navigator.
104      *
105      * Implementors of this interface should ensure this value is updated whenever a navigation
106      * operation is performed.
107      */
108     val currentDestination: ThreePaneScaffoldDestinationItem<T>?
109 
110     /**
111      * Indicates if the navigator should be aware of pane destination history when deciding the
112      * result [ThreePaneScaffoldValue] by a navigation operation. If the value is `false`, only the
113      * current destination will be considered in the scaffold value calculation.
114      *
115      * @see calculateThreePaneScaffoldValue for more detailed explanation about history awareness.
116      */
117     var isDestinationHistoryAware: Boolean
118 
119     /**
120      * Navigates to a new destination, possibly with an animation, and suspends until the animation
121      * is complete. The new destination is supposed to have the highest priority when calculating
122      * the new [scaffoldValue].
123      *
124      * Implementors of this interface should ensure the new destination pane will be expanded or
125      * adapted in a reasonable way so it provides users the sense that the new destination is the
126      * pane currently being used.
127      *
128      * @param pane the new destination pane.
129      * @param contentKey the optional key or id representing the content of the new destination.
130      */
131     suspend fun navigateTo(pane: ThreePaneScaffoldRole, contentKey: T? = null)
132 
133     /**
134      * Returns `true` if there is a previous destination to navigate back to.
135      *
136      * Implementors of this interface should ensure the logic of this function is consistent with
137      * [navigateBack].
138      *
139      * @param backNavigationBehavior the behavior describing which backstack entries may be skipped
140      *   during the back navigation. See [BackNavigationBehavior].
141      */
142     fun canNavigateBack(
143         backNavigationBehavior: BackNavigationBehavior =
144             BackNavigationBehavior.PopUntilScaffoldValueChange
145     ): Boolean
146 
147     /**
148      * Navigates to the previous destination, possibly with an animation, and suspends until the
149      * animation is complete. Returns `true` if there is a previous destination to navigate back to.
150      *
151      * Implementors of this interface should ensure the logic of this function is consistent with
152      * [canNavigateBack].
153      *
154      * @param backNavigationBehavior the behavior describing which backstack entries may be skipped
155      *   during the back navigation. See [BackNavigationBehavior].
156      */
157     suspend fun navigateBack(
158         backNavigationBehavior: BackNavigationBehavior =
159             BackNavigationBehavior.PopUntilScaffoldValueChange
160     ): Boolean
161 
162     /**
163      * Seeks the [scaffoldState] transition to the previous destination, as in a predictive back
164      * animation.
165      *
166      * This does not affect the current [scaffoldValue] or backstack. To do so, call [navigateBack]
167      * when the back navigation action is finalized.
168      *
169      * @param backNavigationBehavior the behavior describing which backstack entries may be skipped
170      *   during the back navigation. See [BackNavigationBehavior].
171      * @param fraction the progress fraction of the transition of backwards navigation.
172      */
173     suspend fun seekBack(
174         backNavigationBehavior: BackNavigationBehavior =
175             BackNavigationBehavior.PopUntilScaffoldValueChange,
176         @FloatRange(from = 0.0, to = 1.0) fraction: Float = 1.0f,
177     )
178 }
179 
180 /**
181  * Returns a remembered default implementation of [ThreePaneScaffoldNavigator] for
182  * [ListDetailPaneScaffold], which will be updated automatically when the input values change. The
183  * default navigator is supposed to be used independently from any navigation frameworks and handles
184  * the navigation purely inside the [ListDetailPaneScaffold].
185  *
186  * @param T the type representing the content key/id for a navigation destination. This type must be
187  *   storable in a Bundle. Used to customize navigation behavior (for example,
188  *   [BackNavigationBehavior]). If this customization is unneeded, you can pass [Any].
189  * @param scaffoldDirective the current layout directives to follow. The default value will be
190  *   calculated with [calculatePaneScaffoldDirective] using
191  *   [WindowAdaptiveInfo][androidx.compose.material3.adaptive.WindowAdaptiveInfo] retrieved from the
192  *   current context.
193  * @param adaptStrategies adaptation strategies of each pane.
194  * @param isDestinationHistoryAware `true` if the scaffold value calculation should be aware of the
195  *   full destination history, instead of just the current destination. See
196  *   [calculateThreePaneScaffoldValue] for more relevant details.
197  * @param initialDestinationHistory the initial pane destination history of the scaffold, by default
198  *   it will be just the list pane.
199  */
200 @ExperimentalMaterial3AdaptiveApi
201 @Composable
rememberListDetailPaneScaffoldNavigatornull202 fun <T> rememberListDetailPaneScaffoldNavigator(
203     scaffoldDirective: PaneScaffoldDirective =
204         calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()),
205     adaptStrategies: ThreePaneScaffoldAdaptStrategies =
206         ListDetailPaneScaffoldDefaults.adaptStrategies(),
207     isDestinationHistoryAware: Boolean = true,
208     initialDestinationHistory: List<ThreePaneScaffoldDestinationItem<T>> =
209         DefaultListDetailPaneHistory,
210 ): ThreePaneScaffoldNavigator<T> =
211     rememberThreePaneScaffoldNavigator(
212         scaffoldDirective,
213         adaptStrategies,
214         isDestinationHistoryAware,
215         initialDestinationHistory
216     )
217 
218 /**
219  * Returns a remembered default implementation of [ThreePaneScaffoldNavigator] for
220  * [ListDetailPaneScaffold], which will be updated automatically when the input values change. The
221  * default navigator is supposed to be used independently from any navigation frameworks and handles
222  * the navigation purely inside the [ListDetailPaneScaffold].
223  *
224  * @param scaffoldDirective the current layout directives to follow. The default value will be
225  *   calculated with [calculatePaneScaffoldDirective] using
226  *   [WindowAdaptiveInfo][androidx.compose.material3.adaptive.WindowAdaptiveInfo] retrieved from the
227  *   current context.
228  * @param adaptStrategies adaptation strategies of each pane.
229  * @param isDestinationHistoryAware `true` if the scaffold value calculation should be aware of the
230  *   full destination history, instead of just the current destination. See
231  *   [calculateThreePaneScaffoldValue] for more relevant details.
232  */
233 @ExperimentalMaterial3AdaptiveApi
234 @Composable
235 fun rememberListDetailPaneScaffoldNavigator(
236     scaffoldDirective: PaneScaffoldDirective =
237         calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()),
238     adaptStrategies: ThreePaneScaffoldAdaptStrategies =
239         ListDetailPaneScaffoldDefaults.adaptStrategies(),
240     isDestinationHistoryAware: Boolean = true,
241 ): ThreePaneScaffoldNavigator<Any> =
242     rememberListDetailPaneScaffoldNavigator<Any>(
243         scaffoldDirective,
244         adaptStrategies,
245         isDestinationHistoryAware,
246     )
247 
248 /**
249  * Returns a remembered default implementation of [ThreePaneScaffoldNavigator] for
250  * [SupportingPaneScaffold], which will be updated automatically when the input values change. The
251  * default navigator is supposed to be used independently from any navigation frameworks and handles
252  * the navigation purely inside the [SupportingPaneScaffold].
253  *
254  * @param T the type representing the content key/id for a navigation destination. This type must be
255  *   storable in a Bundle. Used to customize navigation behavior (for example,
256  *   [BackNavigationBehavior]). If this customization is unneeded, you can pass [Any].
257  * @param scaffoldDirective the current layout directives to follow. The default value will be
258  *   calculated with [calculatePaneScaffoldDirective] using
259  *   [WindowAdaptiveInfo][androidx.compose.material3.adaptive.WindowAdaptiveInfo] retrieved from the
260  *   current context.
261  * @param adaptStrategies adaptation strategies of each pane.
262  * @param isDestinationHistoryAware `true` if the scaffold value calculation should be aware of the
263  *   full destination history, instead of just the current destination. See
264  *   [calculateThreePaneScaffoldValue] for more relevant details.
265  * @param initialDestinationHistory the initial destination history of the scaffold, by default it
266  *   will be just the main pane.
267  */
268 @ExperimentalMaterial3AdaptiveApi
269 @Composable
270 fun <T> rememberSupportingPaneScaffoldNavigator(
271     scaffoldDirective: PaneScaffoldDirective =
272         calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()),
273     adaptStrategies: ThreePaneScaffoldAdaptStrategies =
274         SupportingPaneScaffoldDefaults.adaptStrategies(),
275     isDestinationHistoryAware: Boolean = true,
276     initialDestinationHistory: List<ThreePaneScaffoldDestinationItem<T>> =
277         DefaultSupportingPaneHistory,
278 ): ThreePaneScaffoldNavigator<T> =
279     rememberThreePaneScaffoldNavigator(
280         scaffoldDirective,
281         adaptStrategies,
282         isDestinationHistoryAware,
283         initialDestinationHistory
284     )
285 
286 /**
287  * Returns a remembered default implementation of [ThreePaneScaffoldNavigator] for
288  * [SupportingPaneScaffold], which will be updated automatically when the input values change. The
289  * default navigator is supposed to be used independently from any navigation frameworks and handles
290  * the navigation purely inside the [SupportingPaneScaffold].
291  *
292  * @param scaffoldDirective the current layout directives to follow. The default value will be
293  *   calculated with [calculatePaneScaffoldDirective] using
294  *   [WindowAdaptiveInfo][androidx.compose.material3.adaptive.WindowAdaptiveInfo] retrieved from the
295  *   current context.
296  * @param adaptStrategies adaptation strategies of each pane.
297  * @param isDestinationHistoryAware `true` if the scaffold value calculation should be aware of the
298  *   full destination history, instead of just the current destination. See
299  *   [calculateThreePaneScaffoldValue] for more relevant details.
300  */
301 @ExperimentalMaterial3AdaptiveApi
302 @Composable
303 fun rememberSupportingPaneScaffoldNavigator(
304     scaffoldDirective: PaneScaffoldDirective =
305         calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()),
306     adaptStrategies: ThreePaneScaffoldAdaptStrategies =
307         SupportingPaneScaffoldDefaults.adaptStrategies(),
308     isDestinationHistoryAware: Boolean = true,
309 ): ThreePaneScaffoldNavigator<Any> =
310     rememberSupportingPaneScaffoldNavigator<Any>(
311         scaffoldDirective,
312         adaptStrategies,
313         isDestinationHistoryAware,
314     )
315 
316 @ExperimentalMaterial3AdaptiveApi
317 @Composable
318 internal fun <T> rememberThreePaneScaffoldNavigator(
319     scaffoldDirective: PaneScaffoldDirective,
320     adaptStrategies: ThreePaneScaffoldAdaptStrategies,
321     isDestinationHistoryAware: Boolean,
322     initialDestinationHistory: List<ThreePaneScaffoldDestinationItem<T>>
323 ): ThreePaneScaffoldNavigator<T> =
324     rememberSaveable(
325             saver =
326                 DefaultThreePaneScaffoldNavigator.saver(
327                     scaffoldDirective,
328                     adaptStrategies,
329                     isDestinationHistoryAware
330                 )
331         ) {
332             DefaultThreePaneScaffoldNavigator(
333                 initialDestinationHistory = initialDestinationHistory,
334                 initialScaffoldDirective = scaffoldDirective,
335                 initialAdaptStrategies = adaptStrategies,
336                 initialIsDestinationHistoryAware = isDestinationHistoryAware
337             )
338         }
<lambda>null339         .apply {
340             this.scaffoldDirective = scaffoldDirective
341             this.adaptStrategies = adaptStrategies
342             this.isDestinationHistoryAware = isDestinationHistoryAware
343         }
344 
345 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
346 internal class DefaultThreePaneScaffoldNavigator<T>(
347     initialDestinationHistory: List<ThreePaneScaffoldDestinationItem<T>>,
348     initialScaffoldDirective: PaneScaffoldDirective,
349     initialAdaptStrategies: ThreePaneScaffoldAdaptStrategies,
350     initialIsDestinationHistoryAware: Boolean
351 ) : ThreePaneScaffoldNavigator<T> {
352 
353     private val destinationHistory =
<lambda>null354         mutableStateListOf<ThreePaneScaffoldDestinationItem<T>>().apply {
355             addAll(initialDestinationHistory)
356         }
357 
358     override var scaffoldDirective by mutableStateOf(initialScaffoldDirective)
359 
360     override var isDestinationHistoryAware by mutableStateOf(initialIsDestinationHistoryAware)
361 
362     var adaptStrategies by mutableStateOf(initialAdaptStrategies)
363 
364     override val currentDestination
365         get() = destinationHistory.lastOrNull()
366 
<lambda>null367     override val scaffoldValue by derivedStateOf {
368         calculateScaffoldValue(destinationHistory.lastIndex)
369     }
370 
371     // Must be updated whenever `destinationHistory` changes to keep in sync.
372     override val scaffoldState = MutableThreePaneScaffoldState(scaffoldValue)
373 
peekPreviousScaffoldValuenull374     override fun peekPreviousScaffoldValue(
375         backNavigationBehavior: BackNavigationBehavior
376     ): ThreePaneScaffoldValue {
377         val index = getPreviousDestinationIndex(backNavigationBehavior)
378         return if (index == -1) scaffoldValue else calculateScaffoldValue(index)
379     }
380 
navigateTonull381     override suspend fun navigateTo(pane: ThreePaneScaffoldRole, contentKey: T?) {
382         destinationHistory.add(ThreePaneScaffoldDestinationItem(pane, contentKey))
383         animateStateToCurrentScaffoldValue()
384     }
385 
canNavigateBacknull386     override fun canNavigateBack(backNavigationBehavior: BackNavigationBehavior): Boolean =
387         getPreviousDestinationIndex(backNavigationBehavior) >= 0
388 
389     override suspend fun navigateBack(
390         backNavigationBehavior: BackNavigationBehavior,
391     ): Boolean {
392         val previousDestinationIndex = getPreviousDestinationIndex(backNavigationBehavior)
393         if (previousDestinationIndex < 0) {
394             destinationHistory.clear()
395             animateStateToCurrentScaffoldValue()
396             return false
397         }
398         val targetSize = previousDestinationIndex + 1
399         while (destinationHistory.size > targetSize) {
400             destinationHistory.removeLastKt()
401         }
402         animateStateToCurrentScaffoldValue()
403         return true
404     }
405 
seekBacknull406     override suspend fun seekBack(backNavigationBehavior: BackNavigationBehavior, fraction: Float) {
407         if (fraction == 0f) {
408             animateStateToCurrentScaffoldValue()
409         } else {
410             val previousScaffoldValue = peekPreviousScaffoldValue(backNavigationBehavior)
411             scaffoldState.seekTo(fraction, previousScaffoldValue, isPredictiveBackInProgress = true)
412         }
413     }
414 
animateStateToCurrentScaffoldValuenull415     private suspend fun animateStateToCurrentScaffoldValue() {
416         scaffoldState.animateTo(scaffoldValue)
417     }
418 
getPreviousDestinationIndexnull419     private fun getPreviousDestinationIndex(backNavBehavior: BackNavigationBehavior): Int {
420         if (destinationHistory.size <= 1) {
421             // No previous destination
422             return -1
423         }
424         when (backNavBehavior) {
425             BackNavigationBehavior.PopLatest -> return destinationHistory.lastIndex - 1
426             BackNavigationBehavior.PopUntilScaffoldValueChange ->
427                 for (previousDestinationIndex in destinationHistory.lastIndex - 1 downTo 0) {
428                     val previousValue = calculateScaffoldValue(previousDestinationIndex)
429                     if (previousValue != scaffoldValue) {
430                         return previousDestinationIndex
431                     }
432                 }
433             BackNavigationBehavior.PopUntilCurrentDestinationChange ->
434                 for (previousDestinationIndex in destinationHistory.lastIndex - 1 downTo 0) {
435                     val destination = destinationHistory[previousDestinationIndex].pane
436                     if (destination != currentDestination?.pane) {
437                         return previousDestinationIndex
438                     }
439                 }
440             BackNavigationBehavior.PopUntilContentChange ->
441                 for (previousDestinationIndex in destinationHistory.lastIndex - 1 downTo 0) {
442                     val contentKey = destinationHistory[previousDestinationIndex].contentKey
443                     if (contentKey != currentDestination?.contentKey) {
444                         return previousDestinationIndex
445                     }
446                     // A scaffold value change also counts as a content change.
447                     val previousValue = calculateScaffoldValue(previousDestinationIndex)
448                     if (previousValue != scaffoldValue) {
449                         return previousDestinationIndex
450                     }
451                 }
452         }
453 
454         return -1
455     }
456 
calculateScaffoldValuenull457     private fun calculateScaffoldValue(destinationIndex: Int) =
458         if (destinationIndex == -1) {
459             calculateThreePaneScaffoldValue(
460                 maxHorizontalPartitions = scaffoldDirective.maxHorizontalPartitions,
461                 maxVerticalPartitions = scaffoldDirective.maxVerticalPartitions,
462                 adaptStrategies = adaptStrategies,
463                 currentDestination = null
464             )
465         } else if (isDestinationHistoryAware) {
466             calculateThreePaneScaffoldValue(
467                 maxHorizontalPartitions = scaffoldDirective.maxHorizontalPartitions,
468                 maxVerticalPartitions = scaffoldDirective.maxVerticalPartitions,
469                 adaptStrategies = adaptStrategies,
470                 destinationHistory = destinationHistory.subList(0, destinationIndex + 1)
471             )
472         } else {
473             calculateThreePaneScaffoldValue(
474                 maxHorizontalPartitions = scaffoldDirective.maxHorizontalPartitions,
475                 maxVerticalPartitions = scaffoldDirective.maxVerticalPartitions,
476                 adaptStrategies = adaptStrategies,
477                 currentDestination = destinationHistory[destinationIndex]
478             )
479         }
480 
481     companion object {
482         /** To keep destination history saved */
savernull483         fun <T> saver(
484             initialScaffoldDirective: PaneScaffoldDirective,
485             initialAdaptStrategies: ThreePaneScaffoldAdaptStrategies,
486             initialDestinationHistoryAware: Boolean
487         ): Saver<DefaultThreePaneScaffoldNavigator<T>, *> {
488             val destinationItemSaver = destinationItemSaver<T>()
489             return listSaver(
490                 save = {
491                     it.destinationHistory.fastMap { destination ->
492                         with(destinationItemSaver) { save(destination) }
493                     }
494                 },
495                 restore = {
496                     DefaultThreePaneScaffoldNavigator(
497                         initialDestinationHistory =
498                             it.fastMap { savedDestination ->
499                                 destinationItemSaver.restore(savedDestination!!)!!
500                             },
501                         initialScaffoldDirective = initialScaffoldDirective,
502                         initialAdaptStrategies = initialAdaptStrategies,
503                         initialIsDestinationHistoryAware = initialDestinationHistoryAware
504                     )
505                 }
506             )
507         }
508     }
509 }
510 
511 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
destinationItemSavernull512 internal fun <T> destinationItemSaver(): Saver<ThreePaneScaffoldDestinationItem<T>, Any> =
513     listSaver(
514         save = { listOf(it.pane, it.contentKey) },
<lambda>null515         restore = {
516             @Suppress("UNCHECKED_CAST")
517             (ThreePaneScaffoldDestinationItem(
518                 pane = it[0] as ThreePaneScaffoldRole,
519                 contentKey = it[1] as T?
520             ))
521         }
522     )
523 
524 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
525 private val DefaultListDetailPaneHistory: List<ThreePaneScaffoldDestinationItem<Nothing>> =
526     listOf(ThreePaneScaffoldDestinationItem(ListDetailPaneScaffoldRole.List))
527 
528 @OptIn(ExperimentalMaterial3AdaptiveApi::class)
529 private val DefaultSupportingPaneHistory: List<ThreePaneScaffoldDestinationItem<Nothing>> =
530     listOf(ThreePaneScaffoldDestinationItem(SupportingPaneScaffoldRole.Main))
531