Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(SnapCard): LATCH-2380 Added SnapCard component #417

Merged
merged 1 commit into from
Dec 16, 2024
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
4 changes: 4 additions & 0 deletions MisticaCatalog/MisticaCatalog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
BB9A70292CE0D94900B2767F /* CardList.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB9A70282CE0D93D00B2767F /* CardList.swift */; };
BB9A702B2CE0E34600B2767F /* PosterCardCatalogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB9A702A2CE0E34300B2767F /* PosterCardCatalogView.swift */; };
BB9A702E2CE3B3BE00B2767F /* airpods.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = BB9A702D2CE3B3BE00B2767F /* airpods.mp4 */; };
BBEAB72E2CFF5A0C00AE8D8F /* SnapCardCatalogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBEAB72D2CFF5A0600AE8D8F /* SnapCardCatalogView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -180,6 +181,7 @@
BB9A70282CE0D93D00B2767F /* CardList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardList.swift; sourceTree = "<group>"; };
BB9A702A2CE0E34300B2767F /* PosterCardCatalogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PosterCardCatalogView.swift; sourceTree = "<group>"; };
BB9A702D2CE3B3BE00B2767F /* airpods.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = airpods.mp4; sourceTree = "<group>"; };
BBEAB72D2CFF5A0600AE8D8F /* SnapCardCatalogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapCardCatalogView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -231,6 +233,7 @@
18E48553287F19EB0052A6F2 /* RadioButtonCatalogView.swift */,
244D00C52C491D4600424AA5 /* SkeletonsCatalogView.swift */,
18E48550287F19EB0052A6F2 /* SnackbarCatalogView.swift */,
BBEAB72D2CFF5A0600AE8D8F /* SnapCardCatalogView.swift */,
18E4854E287F19EB0052A6F2 /* StepperCatalogVIew.swift */,
18E48559287F19EB0052A6F2 /* TabsCatalogView.swift */,
18E4855B287F19EB0052A6F2 /* TagCatalogView.swift */,
Expand Down Expand Up @@ -570,6 +573,7 @@
18E4858A287F19EB0052A6F2 /* BadgeCatalogView.swift in Sources */,
18E48583287F19EB0052A6F2 /* RadioButtonCatalogView.swift in Sources */,
BB9A702B2CE0E34600B2767F /* PosterCardCatalogView.swift in Sources */,
BBEAB72E2CFF5A0C00AE8D8F /* SnapCardCatalogView.swift in Sources */,
18E4858F287F19EB0052A6F2 /* UICatalogScrollContentIndicatorViewController.swift in Sources */,
18E4857B287F19EB0052A6F2 /* CatalogList.swift in Sources */,
18E48586287F19EB0052A6F2 /* ButtonCatalogView.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion MisticaCatalog/Source/Catalog/Cards/CardList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private extension CardRow {
case .posterCard:
PosterCardCatalogView()
case .snapCard:
notImplementedView
SnapCardCatalogView()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ struct PosterCardCatalogView: View {
NavigationLink("Show Poster Card") {
posterCard()
.padding(.horizontal, 16)
.navigationBarTitle("DataCard")
.navigationBarTitle("PosterCard")
.navigationBarTitleDisplayMode(.inline)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
//
// SnapCardCatalogView.swift
//
// Made with ❤️ by Novum
//
// Copyright © Telefonica. All rights reserved.
//

import Foundation
import MisticaSwiftUI
import SwiftUI

struct SnapCardCatalogView: View {
@State var title: String = "AirPods 4"
@State var subTitle: String = ""
@State var description: String = "The next evolution of sound and comfort"
@State var aspectRatio: AspectRatio = .ratio7to10
@State var assetType: AssetType = .none
@State var hasSlot: Bool = false
@State var isInverse: Bool = false

enum AspectRatio: String, CaseIterable, Identifiable {
var id: Self { self }

case ratio1to1 = "1:1"
case ratio7to10 = "7:10"
case ratio9to10 = "9:10"
case ratio16to9 = "16:9"

var toSnapCardAspectRatio: SnapCardAspectRatio {
switch self {
case .ratio1to1: .ratio1to1
case .ratio7to10: .ratio7to10
case .ratio9to10: .ratio9to10
case .ratio16to9: .ratio16to9
}
}
}

enum AssetType: String, CaseIterable, Identifiable, Equatable {
var id: Self { self }

case none
case icon = "Icon"
case circledIcon = "Circled icon"
case avatar = "Avatar"

func toSnapCardAssetType() -> SnapCardAssetType {
switch self {
case .none:
return .none
case .icon:
return .icon(image: Image(systemName: "apple.logo"), foregroundColor: nil, backgroundColor: nil)
case .circledIcon:
return .icon(image: Image(systemName: "apple.logo"), foregroundColor: .brand, backgroundColor: .brandLow)
case .avatar:
return .avatar(Image("netflix-logo"))
}
}
}

var body: some View {
VStack {
List {
section("Title") { TextField("Title", text: $title) }
section("Subtitle") { TextField("Subtitle", text: $subTitle) }
section("Description") { TextField("Description", text: $description) }
section("Aspect ratio") {
Picker("Aspect Ratio", selection: $aspectRatio) {
ForEach(AspectRatio.allCases) {
Text($0.rawValue.capitalized)
}
}
.pickerStyle(.segmented)
}
section("Asset type") {
Picker("Asset type", selection: $assetType) {
ForEach(AssetType.allCases) {
Text($0.rawValue.capitalized)
}
}
}
section("Options") {
VStack {
Toggle("Has slot", isOn: $hasSlot)
Toggle("Inverse", isOn: $isInverse)
}
}

NavigationLink("Show Snap Card") {
snapCard()
.padding(.horizontal, 16)
.navigationBarTitle("SnapCard")
.navigationBarTitleDisplayMode(.inline)
}
}
}
}

@ViewBuilder
func snapCard() -> some View {
VStack {
SnapCard(
themeVariant: isInverse ? .inverse : .none,
aspectRatio: aspectRatio.toSnapCardAspectRatio,
assetType: assetType.toSnapCardAssetType(),
title: title,
subTitle: subTitle.isEmpty ? nil : subTitle,
description: description.isEmpty ? nil : description,
slot: {
if hasSlot {
HStack(alignment: .center) {
Spacer()
Text("This is a slot")
Spacer()
}
.padding()
.background(Color.green)
} else {
EmptyView()
}
}
)
}
.padding()
}
}

#Preview {
NavigationView {
SnapCardCatalogView()
}
.misticaNavigationViewStyle()
}
29 changes: 27 additions & 2 deletions Sources/MisticaSwiftUI/Components/Cards/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
* [How to use a PosterCard](#how-to-use-a-postercard)
* [Adding extra content to a PosterCard](#adding-extra-content-to-a-postercard)

* [SnapCard](#snapcard)
* [How to use a SnapCard](#how-to-use-a-snapcard)

* [Accessibility](#accessibility)

## DataCard
Expand Down Expand Up @@ -57,7 +60,7 @@ You can use the default initializers depending on your needs. For example, a bas

```swift
PosterCard(
mediaType: .image,
mediaType: .image(Image("airpods"), topActions: .dismiss {}),
title: title,
subtitle: subtitle,
description: description
Expand All @@ -83,7 +86,7 @@ The extra View will be placed below the descriptionTitle property, keeping the s

```swift
PosterCard(
mediaType: .image,
mediaType: .image(Image("airpods"), topActions: .dismiss {}),
title: title,
subtitle: subtitle,
description: description,
Expand Down Expand Up @@ -133,3 +136,25 @@ PosterCard(
### Conclusion

`PosterCard` is a highly flexible and customizable component for displaying various types of media in your SwiftUI apps. Whether you need a simple image card or a fully interactive video card, `PosterCard` provides the tools to make it happen.

## SnapCard

Snap Cards are quick content-minimal elements. Their aim is to be used to read content quickly, which acts as an entry point to more detailed information.

If you're using multiple cards on the same screen, use the same type to keep the overall visual hierarchy.

![SnapCard](./docs/images/snap-card.png)

### How to use a SnapCard

You can use the default initializers depending on your needs. For example, a basic configuration for an image-based PosterCard:

```swift
SnapCard(
assetType: .avatar(Image("avatar-icon")),
title: "title",
subTitle: "subtitle",
description: "description",
slot: { Text("Extra Content!") }
)
```
Loading