Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ import androidx.compose.ui.unit.dp
* @param text will be shown below the heading.
* @param modifier a [Modifier].
* @param image optional image, shown to the left of the text.
* @param trailingContent optional trailing content.
*/
@Composable
fun FloatingItemHeadingAndText(
heading: String,
text: AnnotatedString,
modifier: Modifier = Modifier,
image: @Composable () -> Unit = {}
image: @Composable () -> Unit = {},
trailingContent: @Composable () -> Unit = {},
) {
FloatingItemContainer(modifier = modifier) {
Row(
Expand All @@ -37,7 +39,7 @@ fun FloatingItemHeadingAndText(
) {
image()
Column(
modifier = Modifier.fillMaxWidth()
modifier = Modifier.weight(1.0f)
) {
Text(
text = heading,
Expand All @@ -50,7 +52,9 @@ fun FloatingItemHeadingAndText(
style = MaterialTheme.typography.bodyMedium
)
}

}
trailingContent()
}
}
}
Expand All @@ -62,18 +66,21 @@ fun FloatingItemHeadingAndText(
* @param text will be shown below the heading.
* @param modifier a [Modifier].
* @param image optional image, shown to the left of the text.
* @param trailingContent optional trailing content.
*/
@Composable
fun FloatingItemHeadingAndText(
heading: String,
text: String,
modifier: Modifier = Modifier,
image: @Composable () -> Unit = {}
image: @Composable () -> Unit = {},
trailingContent: @Composable () -> Unit = {},
) {
FloatingItemHeadingAndText(
heading = heading,
text = AnnotatedString(text),
modifier = modifier,
image = image
image = image,
trailingContent = trailingContent
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fun FloatingItemList(
Text(
modifier = Modifier.padding(start = 10.dp, top = 10.dp, end = 10.dp, bottom = 0.dp),
text = title,
style = MaterialTheme.typography.bodyMedium,
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.secondary,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ import androidx.compose.ui.unit.dp
* @param secondary optional text to show below the main text, in secondary color and smaller font.
* @param modifier a [Modifier].
* @param image optional image, shown to the left of the text.
* @param trailingContent optional trailing content.
*/
@Composable
fun FloatingItemText(
text: AnnotatedString,
modifier: Modifier = Modifier,
secondary: String? = null,
image: @Composable () -> Unit = {}
image: @Composable () -> Unit = {},
trailingContent: @Composable () -> Unit = {},
) {
FloatingItemContainer(modifier = modifier) {
Row(
Expand All @@ -35,26 +37,34 @@ fun FloatingItemText(
verticalAlignment = Alignment.CenterVertically
) {
image()
if (secondary == null) {
Text(
text = text,
textAlign = TextAlign.Start
)
} else {
Column(
modifier = Modifier.fillMaxWidth()
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp, alignment = Alignment.Start),
verticalAlignment = Alignment.CenterVertically
) {
if (secondary == null) {
Text(
modifier = Modifier.weight(1.0f),
text = text,
textAlign = TextAlign.Start
)
Text(
text = secondary,
textAlign = TextAlign.Start,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.secondary
)
} else {
Column(
modifier = Modifier.weight(1.0f)
) {
Text(
text = text,
textAlign = TextAlign.Start
)
Text(
text = secondary,
textAlign = TextAlign.Start,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.secondary
)
}
}
trailingContent()
}
}
}
Expand All @@ -67,18 +77,21 @@ fun FloatingItemText(
* @param secondary optional text to show below the main text, in secondary color and smaller font.
* @param modifier a [Modifier].
* @param image optional image, shown to the left of the text.
* @param trailingContent optional trailing content.
*/
@Composable
fun FloatingItemText(
text: String,
modifier: Modifier = Modifier,
secondary: String? = null,
image: @Composable () -> Unit = {}
image: @Composable () -> Unit = {},
trailingContent: @Composable () -> Unit = {},
) {
FloatingItemText(
text = AnnotatedString(text),
modifier = modifier,
secondary = secondary,
image = image
image = image,
trailingContent = trailingContent
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,5 @@ extension CredentialPresentmentData: @unchecked Sendable {}
extension CredentialPresentmentSelection: @unchecked Sendable {}
extension Tags: @unchecked Sendable {}
extension Tags.Editor: @unchecked Sendable {}
extension PassphraseEvaluation: @unchecked Sendable {}
extension Document.Editor: @unchecked Sendable {}
35 changes: 35 additions & 0 deletions multipaz-swiftui/src/iosMain/swift/FloatingItemCenteredText.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import SwiftUI

/// A list item view that displays centered, italicized text in a secondary color.
///
/// This component is commonly used for informational footers, empty states, or subtle hints.
public struct FloatingItemCenteredText: View {

/// The text to display, formatted as an `AttributedString`.
public var text: AttributedString

/// Creates a new centered text item using an `AttributedString`.
///
/// - Parameter text: The formatted string to display centered and italicized.
public init(text: AttributedString) {
self.text = text
}

/// Creates a new centered text item using a standard `String`.
///
/// - Parameter text: The string to display centered and italicized.
public init(text: String) {
self.text = AttributedString(text)
}

public var body: some View {
FloatingItemContainer {
Text(text)
.font(.system(size: 15))
.multilineTextAlignment(.center)
.foregroundColor(.secondary)
.italic()
.frame(maxWidth: .infinity, alignment: .center)
}
}
}
28 changes: 28 additions & 0 deletions multipaz-swiftui/src/iosMain/swift/FloatingItemContainer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import SwiftUI

/// A generic container that applies the standard list item styling.
///
/// Use this container to wrap custom content so it visually aligns with other `FloatingItem`
/// components. It applies a standard background color, full-width frame, and uniform padding.
public struct FloatingItemContainer<Content: View>: View {

/// A view builder that provides the content of the container.
public let content: () -> Content

/// Creates a new floating item container.
///
/// - Parameter content: A view builder that generates the child views to display inside the container.
public init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}

public var body: some View {
ZStack(alignment: .leading) {
content()
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(16)
// Equivalent to MaterialTheme.colorScheme.surfaceContainerLowest
.background(Color(UIColor.systemBackground))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import SwiftUI

/// A list item view that displays a bold heading with primary text below it,
/// an optional leading image, and an optional trailing view.
public struct FloatingItemHeadingAndText<ImageView: View, TrailingView: View>: View {

/// The heading string displayed in a bold font above the primary text.
public var heading: String

/// The primary text to display below the heading, formatted as an `AttributedString`.
public var text: AttributedString

/// A view builder that creates the leading image or icon.
@ViewBuilder public var image: () -> ImageView

/// A view builder that creates the trailing content, pushed to the rightmost edge.
@ViewBuilder public var trailingContent: () -> TrailingView

/// Creates a new floating item with a heading and an `AttributedString` body.
///
/// - Parameters:
/// - heading: The bold heading to display.
/// - text: The primary text to display below the heading.
/// - image: A view builder that provides a leading image or icon. Defaults to an `EmptyView`.
/// - trailingContent: A view builder that provides a trailing view. Defaults to an `EmptyView`.
public init(
heading: String,
text: AttributedString,
@ViewBuilder image: @escaping () -> ImageView = { EmptyView() },
@ViewBuilder trailingContent: @escaping () -> TrailingView = { EmptyView() }
) {
self.heading = heading
self.text = text
self.image = image
self.trailingContent = trailingContent
}

/// Creates a new floating item with a heading and a standard `String` body.
///
/// - Parameters:
/// - heading: The bold heading to display.
/// - text: The primary string to display below the heading.
/// - image: A view builder that provides a leading image or icon. Defaults to an `EmptyView`.
/// - trailingContent: A view builder that provides a trailing view. Defaults to an `EmptyView`.
public init(
heading: String,
text: String,
@ViewBuilder image: @escaping () -> ImageView = { EmptyView() },
@ViewBuilder trailingContent: @escaping () -> TrailingView = { EmptyView() }
) {
self.init(heading: heading, text: AttributedString(text), image: image, trailingContent: trailingContent)
}

public var body: some View {
FloatingItemContainer {
HStack(alignment: .center, spacing: 16) {
image()

VStack(alignment: .leading, spacing: 4) {
Text(heading)
.font(.system(size: 15))
.fontWeight(.bold)

Text(text)
.font(.system(size: 15))
.textSelection(.enabled)
}

// Pushes the trailing content to the rightmost edge
Spacer(minLength: 0)

trailingContent()
}
}
}
}
56 changes: 56 additions & 0 deletions multipaz-swiftui/src/iosMain/swift/FloatingItemList.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import SwiftUI

/// A view that presents a floating list of items, optionally accompanied by a title.
///
/// The list is styled as a rounded card with a subtle shadow. The spacing between the items
/// exposes the background color, creating a natural divider effect between each row.
public struct FloatingItemList<Content: View>: View {

/// An optional title displayed above the list container.
public var title: String?

/// A view builder that provides the content of the list.
@ViewBuilder public var content: () -> Content

/// Creates a new floating item list.
///
/// - Parameters:
/// - title: An optional title string to display above the list. Defaults to `nil`.
/// - content: A view builder that provides the items to be displayed in the list.
public init(title: String? = nil, @ViewBuilder content: @escaping () -> Content) {
self.title = title
self.content = content
}

public var body: some View {
VStack(alignment: .leading, spacing: 0) {
if let title = title {
Text(title)
.font(.subheadline) // Equivalent to bodyMedium
.fontWeight(.bold)
.foregroundColor(.secondary)
.padding(.leading, 10)
.padding(.top, 10)
.padding(.trailing, 10)
.padding(.bottom, 5) // Adjusted slightly to flow into the list naturally
}

VStack(spacing: 1) { // Spacing creates the divider effect
content()
}
// The container background acting as the divider color (outlineVariant)
.background(Color(UIColor.separator))
.clipShape(RoundedRectangle(cornerRadius: 16))
.shadow(
color: Color.black.opacity(0.10),
radius: 10,
x: 0,
y: 2
)
.padding(.leading, 10)
.padding(.trailing, 10)
.padding(.top, title == nil ? 10 : 5)
.padding(.bottom, 15)
}
}
}
Loading
Loading