Skip to content

Commit b028975

Browse files
authored
Merge branch 'main' into j-circuitx-navigation
2 parents 2d4055d + 69f1d60 commit b028975

19 files changed

Lines changed: 410 additions & 242 deletions

File tree

.github/CONTRIBUTING.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ checkout.
88
Circuit is a Kotlin Multiplatform project, so ensure you have your environment set up
99
accordingly: https://www.jetbrains.com/help/kotlin-multiplatform-dev/multiplatform-setup.html
1010

11-
The primary project is `circuit`. The primary sample is `samples/star`.
12-
1311
This project is written in Kotlin and should only use Kotlin.
1412

1513
Code formatting is checked via [Spotless](https://github.com/diffplug/spotless). To run the

.github/workflows/mkdocs-requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ Markdown==3.8
77
MarkupSafe==3.0.2
88
mkdocs==1.6.1
99
mkdocs-macros-plugin==1.3.7
10-
mkdocs-material==9.6.11
10+
mkdocs-material==9.6.12
1111
mkdocs-material-extensions==1.3.1
1212
Pygments==2.19.1
13-
pymdown-extensions==10.14.3
13+
pymdown-extensions==10.15
1414
python-dateutil==2.9.0.post0
1515
PyYAML==6.0.2
1616
repackage==0.7.3

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ Changelog
44
Unreleased
55
----------
66

7+
- Fix the provided `Modifier` not being used in `NavigatorDefaults.EmptyDecoration`
8+
- [docs] Add more alternative state designs.
9+
710
0.27.1
811
------
912

circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavigableCircuitContent.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import androidx.compose.animation.shrinkHorizontally
2020
import androidx.compose.animation.slideInHorizontally
2121
import androidx.compose.animation.slideOutHorizontally
2222
import androidx.compose.animation.togetherWith
23+
import androidx.compose.foundation.layout.Box
2324
import androidx.compose.runtime.Composable
2425
import androidx.compose.runtime.CompositionLocalProvider
2526
import androidx.compose.runtime.DisposableEffect
@@ -445,7 +446,7 @@ public object NavigatorDefaults {
445446
modifier: Modifier,
446447
content: @Composable (T) -> Unit,
447448
) {
448-
content(args.first())
449+
Box(modifier = modifier) { content(args.first()) }
449450
}
450451
}
451452
}

docs/circuitx.md

Lines changed: 6 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -12,225 +12,9 @@ These packages differ from Circuit's core artifacts in a few ways:
1212
- These artifacts are under the `com.slack.circuitx` package prefix.
1313
- These artifacts may be platform-specific where appropriate.
1414

15-
## Android
16-
17-
The `circuitx-android` artifact contains Android-specific extensions for Circuit.
18-
19-
```kotlin
20-
dependencies {
21-
implementation("com.slack.circuit:circuitx-android:<version>")
22-
}
23-
```
24-
25-
### Navigation
26-
27-
It can be important for Circuit to be able to navigate to Android targets, such as other activities
28-
or custom tabs. To support this, decorate your existing `Navigator` instance
29-
with `rememberAndroidScreenAwareNavigator()`.
30-
31-
```kotlin
32-
class MainActivity : Activity {
33-
override fun onCreate(savedInstanceState: Bundle?) {
34-
setContent {
35-
val backStack = rememberSaveableBackStack(root = HomeScreen)
36-
val navigator = rememberAndroidScreenAwareNavigator(
37-
rememberCircuitNavigator(backstack), // Decorated navigator
38-
this@MainActivity
39-
)
40-
CircuitCompositionLocals(circuit) {
41-
NavigableCircuitContent(navigator, backstack)
42-
}
43-
}
44-
}
45-
}
46-
```
47-
48-
`rememberAndroidScreenAwareNavigator()` has two overloads - one that accepts a `Context` and one
49-
that accepts an `AndroidScreenStarter`. The former is just a shorthand for the latter that only
50-
supports `IntentScreen`. You can also implement your own starter that supports other screen types.
51-
52-
`AndroidScreen` is the base `Screen` type that this navigator and `AndroidScreenStarter` interact
53-
with. There is a built-in `IntentScreen` implementation that wraps an `Intent` and an
54-
options `Bundle` to pass to `startActivity()`. Custom `AndroidScreens` can be implemented separately
55-
and route through here, but you should be sure to implement your own `AndroidScreenStarter` to
56-
handle them accordingly.
57-
58-
## Effects
59-
60-
CircuitX provides some effects for use with logging/analytics. These effects are typically used in
61-
Circuit presenters for tracking `impressions` and will run only once until forgotten based on the
62-
current circuit-retained strategy.
63-
64-
```kotlin
65-
dependencies {
66-
implementation("com.slack.circuit:circuitx-effects:<version>")
67-
}
68-
```
69-
70-
### ImpressionEffect
71-
72-
`ImpressionEffect` is a simple single fire side effect useful for logging or analytics.
73-
This `impression` will run only once until it is forgotten based on the current `RetainedStateRegistry`.
74-
75-
```kotlin
76-
ImpressionEffect {
77-
// Impression
78-
}
79-
```
80-
81-
### LaunchedImpressionEffect
82-
83-
This is useful for async single fire side effects like logging or analytics. This effect will run a
84-
suspendable `impression` once until it is forgotten based on the `RetainedStateRegistry`.
85-
86-
```kotlin
87-
LaunchedImpressionEffect {
88-
// Impression
89-
}
90-
```
91-
92-
### RememberImpressionNavigator
93-
94-
A `LaunchedImpressionEffect` that is useful for async single fire side effects like logging or
95-
analytics that need to be navigation aware. This will run the `impression` again if it re-enters
96-
the composition after a navigation event.
97-
98-
```kotlin
99-
val navigator = rememberImpressionNavigator(
100-
navigator = Navigator()
101-
) {
102-
// Impression
103-
}
104-
```
105-
106-
## Gesture Navigation
107-
108-
CircuitX provides `NavDecoration` implementation which support navigation through appropriate
109-
gestures on certain platforms.
110-
111-
```kotlin
112-
dependencies {
113-
implementation("com.slack.circuit:circuitx-gesture-navigation:<version>")
114-
}
115-
```
116-
117-
To enable gesture navigation support, you can use the use the `GestureNavigationDecoration`function:
118-
119-
```kotlin
120-
NavigableCircuitContent(
121-
navigator = navigator,
122-
backStack = backstack,
123-
decoration = GestureNavigationDecoration(
124-
// Pop the back stack once the user has gone 'back'
125-
navigator::pop
126-
)
127-
)
128-
```
129-
130-
### Android
131-
132-
On Android, this supports the [Predictive back gesture](https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture) which is available on Android 14 and later (API level 34+). On older platforms, Circuit's default
133-
`NavDecoration` decoration is used instead.
134-
135-
<figure>
136-
<video controls width="300" loop=true>
137-
<source src="../images/gesturenav_android.mp4" type="video/mp4" />
138-
</video>
139-
<figcaption><a href="https://github.com/slackhq/circuit/tree/main/samples/star">Star sample</a> running on an Android 14 device</figcaption>
140-
</figure>
141-
142-
### iOS
143-
144-
On iOS, this simulates iOS's 'Interactive Pop Gesture' in Compose UI, allowing the user to swipe Circuit UIs away. As this is
145-
a simulation of the native behavior, it does not match the native functionality perfectly. However, it is a good approximation.
146-
147-
<figure>
148-
<video controls width="300" loop=true>
149-
<source src="../images/gesturenav_ios.mp4" type="video/mp4" />
150-
</video>
151-
<figcaption><a href="https://github.com/chrisbanes/tivi">Tivi</a> app running on iPhone</figcaption>
152-
</figure>
153-
154-
### Other platforms
155-
156-
On other platforms we defer to Circuit's default `NavDecoration` decoration.
157-
158-
## Overlays
159-
160-
CircuitX provides a few out-of-the-box `Overlay` implementations that you can use to build common
161-
UIs.
162-
163-
```kotlin
164-
dependencies {
165-
implementation("com.slack.circuit:circuitx-overlays:<version>")
166-
}
167-
```
168-
169-
### `BottomSheetOverlay`
170-
171-
`BottomSheetOverlay` is an overlay that shows a bottom sheet with a strongly-typed API for the input
172-
model to the sheet content and result type. This allows you to easily use a bottom sheet to prompt
173-
for user input and suspend the underlying Circuit content until that result is returned.
174-
175-
```kotlin
176-
/** A hypothetical bottom sheet of available actions when long-pressing a list item. */
177-
suspend fun OverlayHost.showLongPressActionsSheet(): Action {
178-
return show(
179-
BottomSheetOverlay(
180-
model = listOfActions()
181-
) { actions, overlayNavigator ->
182-
ActionsSheet(
183-
actions,
184-
overlayNavigator::finish // Finish the overlay with the clicked Action
185-
)
186-
}
187-
)
188-
}
189-
190-
@Composable
191-
fun ActionsSheet(actions: List<Action>, onActionClicked: (Action) -> Unit) {
192-
Column {
193-
actions.forEach { action ->
194-
TextButton(onClick = { onActionClicked(action) }) {
195-
Text(action.title)
196-
}
197-
}
198-
}
199-
}
200-
```
201-
202-
### Dialog Overlays
203-
204-
`alertDialogOverlay` is function that returns an Overlay that shows a simple confirmation dialog with configurable inputs. This acts more or less as an `Overlay` shim over the Material 3 `AlertDialog` API.
205-
206-
```kotlin
207-
/** A hypothetical confirmation dialog. */
208-
suspend fun OverlayHost.showConfirmationDialog(): Action {
209-
return show(
210-
alertDialogOverlay(
211-
titleText = { Text("Are you sure?") },
212-
confirmButton = { onClick -> Button(onClick = onClick) { Text("Yes") } },
213-
dismissButton = { onClick -> Button(onClick = onClick) { Text("No") } },
214-
)
215-
)
216-
}
217-
```
218-
219-
There are also more generic `BasicAlertDialog` and `BasicDialog` implementations that allow more customization.
220-
221-
### `FullScreenOverlay`
222-
223-
Sometimes it's useful to have a full-screen overlay that can be used to show a screen in full above
224-
the current content. This API is fairly simple to use and just takes a `Screen` input of what
225-
content you want to show in the overlay.
226-
227-
```kotlin
228-
overlayHost.showFullScreenOverlay(
229-
ImageViewerScreen(id = url, url = url, placeholderKey = name)
230-
)
231-
```
232-
233-
!!! info "When to use `FullScreenOverlay` vs navigating to a `Screen`?"
234-
While they achieve similar results, the key difference is that `FullScreenOverlay` is
235-
inherently an ephemeral UI that is _controlled_ by an underlying primary UI. It cannot
236-
navigate elsewhere and it does not participate in the backstack.
15+
| Artifact | Documentation |
16+
|--------------------------------------------------|------------------------------------------------------|
17+
| `com.slack.circuitx:circuitx-android` | [Android](circuitx/android.md) |
18+
| `com.slack.circuitx:circuitx-effects` | [Effects](circuitx/effects.md) |
19+
| `com.slack.circuitx:circuitx-gesture-navigation` | [Gesture Navigation](circuitx/gesture-navigation.md) |
20+
| `com.slack.circuitx:circuitx-overlays` | [Overlays](circuitx/overlays.md) |

docs/circuitx/android.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
The `circuitx-android` artifact contains Android-specific extensions for Circuit.
2+
3+
```kotlin
4+
dependencies {
5+
implementation("com.slack.circuit:circuitx-android:<version>")
6+
}
7+
```
8+
9+
### Navigation
10+
11+
It can be important for Circuit to be able to navigate to Android targets, such as other activities
12+
or custom tabs. To support this, decorate your existing `Navigator` instance
13+
with `rememberAndroidScreenAwareNavigator()`.
14+
15+
```kotlin
16+
class MainActivity : Activity {
17+
override fun onCreate(savedInstanceState: Bundle?) {
18+
setContent {
19+
val backStack = rememberSaveableBackStack(root = HomeScreen)
20+
val navigator = rememberAndroidScreenAwareNavigator(
21+
rememberCircuitNavigator(backstack), // Decorated navigator
22+
this@MainActivity
23+
)
24+
CircuitCompositionLocals(circuit) {
25+
NavigableCircuitContent(navigator, backstack)
26+
}
27+
}
28+
}
29+
}
30+
```
31+
32+
`rememberAndroidScreenAwareNavigator()` has two overloads - one that accepts a `Context` and one
33+
that accepts an `AndroidScreenStarter`. The former is just a shorthand for the latter that only
34+
supports `IntentScreen`. You can also implement your own starter that supports other screen types.
35+
36+
`AndroidScreen` is the base `Screen` type that this navigator and `AndroidScreenStarter` interact
37+
with. There is a built-in `IntentScreen` implementation that wraps an `Intent` and an
38+
options `Bundle` to pass to `startActivity()`. Custom `AndroidScreens` can be implemented separately
39+
and route through here, but you should be sure to implement your own `AndroidScreenStarter` to
40+
handle them accordingly.

docs/circuitx/effects.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
CircuitX provides some effects for use with logging/analytics. These effects are typically used in
2+
Circuit presenters for tracking `impressions` and will run only once until forgotten based on the
3+
current circuit-retained strategy.
4+
5+
```kotlin
6+
dependencies {
7+
implementation("com.slack.circuit:circuitx-effects:<version>")
8+
}
9+
```
10+
11+
### ImpressionEffect
12+
13+
`ImpressionEffect` is a simple single fire side effect useful for logging or analytics.
14+
This `impression` will run only once until it is forgotten based on the current `RetainedStateRegistry`.
15+
16+
```kotlin
17+
ImpressionEffect {
18+
// Impression
19+
}
20+
```
21+
22+
### LaunchedImpressionEffect
23+
24+
This is useful for async single fire side effects like logging or analytics. This effect will run a
25+
suspendable `impression` once until it is forgotten based on the `RetainedStateRegistry`.
26+
27+
```kotlin
28+
LaunchedImpressionEffect {
29+
// Impression
30+
}
31+
```
32+
33+
### RememberImpressionNavigator
34+
35+
A `LaunchedImpressionEffect` that is useful for async single fire side effects like logging or
36+
analytics that need to be navigation aware. This will run the `impression` again if it re-enters
37+
the composition after a navigation event.
38+
39+
```kotlin
40+
val navigator = rememberImpressionNavigator(
41+
navigator = Navigator()
42+
) {
43+
// Impression
44+
}
45+
```

0 commit comments

Comments
 (0)